@vtex/faststore 0.0.7 → 0.0.8
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.
- package/CHANGELOG.md +43 -0
- package/dist/actions.json +152 -1
- package/dist/catalog.json +1264 -142
- package/dist/cli/index.d.ts +512 -63
- package/dist/cli/index.js +2098 -686
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/server-runtime.js +44 -24
- package/dist/cli/server-runtime.js.map +1 -1
- package/dist/client.d.ts +5 -1
- package/dist/client.js +5 -1
- package/dist/components.d.ts +34 -7
- package/dist/components.js +38 -11
- package/dist/content-types.json +523 -0
- package/dist/core/dist/define-component.d.ts +6 -1
- package/dist/docs/account-access-gate.md +4 -4
- package/dist/docs/account-address-book.md +519 -0
- package/dist/docs/account-entry-point.md +1 -1
- package/dist/docs/account-menu.md +5 -5
- package/dist/docs/alert-dialog.md +2 -2
- package/dist/docs/billing-address.md +17 -9
- package/dist/docs/card-entry.md +37 -6
- package/dist/docs/cardholder-document.md +16 -14
- package/dist/docs/cart-item.md +1 -1
- package/dist/docs/cart.md +8 -8
- package/dist/docs/client-profile-fieldset.md +7 -5
- package/dist/docs/copy-text.md +84 -0
- package/dist/docs/document-field.md +8 -8
- package/dist/docs/footer-link-group.md +2 -2
- package/dist/docs/header-root.md +1 -1
- package/dist/docs/incentives.md +2 -2
- package/dist/docs/logo.md +5 -5
- package/dist/docs/my-account-profile.md +4 -4
- package/dist/docs/navigation-link-list.md +2 -2
- package/dist/docs/navigation-link.md +3 -3
- package/dist/docs/order-cancel.md +52 -0
- package/dist/docs/order-delivery-address.md +63 -0
- package/dist/docs/order-delivery-group.md +83 -0
- package/dist/docs/order-details-back.md +41 -0
- package/dist/docs/order-details-header.md +48 -0
- package/dist/docs/order-list-row.md +46 -0
- package/dist/docs/order-list-search.md +101 -0
- package/dist/docs/order-list-views.md +97 -0
- package/dist/docs/order-list.md +94 -0
- package/dist/docs/order-payment.md +94 -0
- package/dist/docs/order-placed-date.md +49 -0
- package/dist/docs/order-placed-payment-summary.md +13 -15
- package/dist/docs/order-placed.md +225 -0
- package/dist/docs/order-progress.md +88 -0
- package/dist/docs/order-status-badge.md +91 -0
- package/dist/docs/order-summary.md +53 -0
- package/dist/docs/payment-method-badges.md +14 -3
- package/dist/docs/place-order.md +13 -9
- package/dist/docs/saved-cards.md +2 -2
- package/dist/docs/select.md +19 -6
- package/dist/docs/session.md +1 -1
- package/dist/docs/shipping-address.md +1 -1
- package/dist/docs/sign-in-redirect.md +6 -6
- package/dist/docs/toast.md +7 -3
- package/dist/index.d.ts +6 -2
- package/dist/index.js +2 -1
- package/dist/internal/client.d.ts +6 -1
- package/dist/internal/client.js +7 -2
- package/dist/internal/server.d.ts +6 -2
- package/dist/internal/server.js +6 -2
- package/dist/matter/account-access-gate/account-access-gate.d.ts +2 -1
- package/dist/matter/account-access-gate/account-access-gate.js +19 -16
- package/dist/matter/account-access-gate/account-access-gate.js.map +1 -1
- package/dist/matter/account-address-book/account-address-book-delete.d.ts +60 -0
- package/dist/matter/account-address-book/account-address-book-delete.js +184 -0
- package/dist/matter/account-address-book/account-address-book-delete.js.map +1 -0
- package/dist/matter/account-address-book/account-address-book-deliverability.d.ts +19 -0
- package/dist/matter/account-address-book/account-address-book-deliverability.js +50 -0
- package/dist/matter/account-address-book/account-address-book-deliverability.js.map +1 -0
- package/dist/matter/account-address-book/account-address-book-form-parts.d.ts +42 -0
- package/dist/matter/account-address-book/account-address-book-form-parts.js +146 -0
- package/dist/matter/account-address-book/account-address-book-form-parts.js.map +1 -0
- package/dist/matter/account-address-book/account-address-book-profile-notice.d.ts +25 -0
- package/dist/matter/account-address-book/account-address-book-profile-notice.js +72 -0
- package/dist/matter/account-address-book/account-address-book-profile-notice.js.map +1 -0
- package/dist/matter/account-address-book/account-address-book.d.ts +120 -0
- package/dist/matter/account-address-book/account-address-book.js +469 -0
- package/dist/matter/account-address-book/account-address-book.js.map +1 -0
- package/dist/matter/account-address-book/account-address-country-switch.js +59 -0
- package/dist/matter/account-address-book/account-address-country-switch.js.map +1 -0
- package/dist/matter/account-address-book/account-address-country.d.ts +36 -0
- package/dist/matter/account-address-book/account-address-country.js +151 -0
- package/dist/matter/account-address-book/account-address-country.js.map +1 -0
- package/dist/matter/account-address-book/account-address-first-line.js +11 -0
- package/dist/matter/account-address-book/account-address-first-line.js.map +1 -0
- package/dist/matter/account-address-book/account-address-form-context.d.ts +55 -0
- package/dist/matter/account-address-book/account-address-form-context.js +14 -0
- package/dist/matter/account-address-book/account-address-form-context.js.map +1 -0
- package/dist/matter/account-address-book/account-address-form.d.ts +85 -0
- package/dist/matter/account-address-book/account-address-form.js +946 -0
- package/dist/matter/account-address-book/account-address-form.js.map +1 -0
- package/dist/matter/account-address-book/account-address-postal-autofill.js +189 -0
- package/dist/matter/account-address-book/account-address-postal-autofill.js.map +1 -0
- package/dist/matter/account-address-book/account-address-record.d.ts +26 -0
- package/dist/matter/account-address-book/account-address-record.js +138 -0
- package/dist/matter/account-address-book/account-address-record.js.map +1 -0
- package/dist/matter/account-entry-point/account-entry-point.js +11 -11
- package/dist/matter/account-entry-point/account-entry-point.js.map +1 -1
- package/dist/matter/account-menu/account-menu.d.ts +7 -3
- package/dist/matter/account-menu/account-menu.js +69 -28
- package/dist/matter/account-menu/account-menu.js.map +1 -1
- package/dist/matter/billing-address/billing-address.d.ts +30 -17
- package/dist/matter/billing-address/billing-address.js +128 -33
- package/dist/matter/billing-address/billing-address.js.map +1 -1
- package/dist/matter/breadcrumb/breadcrumb.js +2 -2
- package/dist/matter/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/matter/card-entry/card-capture.js +4 -3
- package/dist/matter/card-entry/card-capture.js.map +1 -1
- package/dist/matter/card-entry/card-entry.d.ts +5 -3
- package/dist/matter/card-entry/card-entry.js +77 -27
- package/dist/matter/card-entry/card-entry.js.map +1 -1
- package/dist/matter/cardholder-document/cardholder-document.d.ts +10 -9
- package/dist/matter/cardholder-document/cardholder-document.js +36 -16
- package/dist/matter/cardholder-document/cardholder-document.js.map +1 -1
- package/dist/matter/cart/cart.d.ts +7 -2
- package/dist/matter/cart/cart.js +29 -26
- package/dist/matter/cart/cart.js.map +1 -1
- package/dist/matter/cart-item/cart-item.js +2 -2
- package/dist/matter/cart-item/cart-item.js.map +1 -1
- package/dist/matter/client-preferences/client-preferences-fieldset.js +1 -1
- package/dist/matter/client-profile-form/client-profile-fieldset.js +8 -5
- package/dist/matter/client-profile-form/client-profile-fieldset.js.map +1 -1
- package/dist/matter/identity-edit/identity-edit.js +3 -3
- package/dist/matter/identity-edit/identity-edit.js.map +1 -1
- package/dist/matter/identity-summary/identity-summary.js +2 -2
- package/dist/matter/identity-summary/identity-summary.js.map +1 -1
- package/dist/matter/my-account-profile/my-account-profile.d.ts +3 -3
- package/dist/matter/my-account-profile/my-account-profile.js +4 -4
- package/dist/matter/my-account-profile/my-account-profile.js.map +1 -1
- package/dist/matter/order-cancel/order-cancel.d.ts +110 -0
- package/dist/matter/order-cancel/order-cancel.js +344 -0
- package/dist/matter/order-cancel/order-cancel.js.map +1 -0
- package/dist/matter/order-delivery-address/order-delivery-address.d.ts +73 -0
- package/dist/matter/order-delivery-address/order-delivery-address.js +172 -0
- package/dist/matter/order-delivery-address/order-delivery-address.js.map +1 -0
- package/dist/matter/order-delivery-group/order-delivery-group.d.ts +128 -0
- package/dist/matter/order-delivery-group/order-delivery-group.js +408 -0
- package/dist/matter/order-delivery-group/order-delivery-group.js.map +1 -0
- package/dist/matter/order-details-header/order-details-header.d.ts +104 -0
- package/dist/matter/order-details-header/order-details-header.js +190 -0
- package/dist/matter/order-details-header/order-details-header.js.map +1 -0
- package/dist/matter/order-list/customer-orders-filter.d.ts +12 -0
- package/dist/matter/order-list/customer-orders-filter.js +30 -0
- package/dist/matter/order-list/customer-orders-filter.js.map +1 -0
- package/dist/matter/order-list/order-list.d.ts +121 -0
- package/dist/matter/order-list/order-list.js +395 -0
- package/dist/matter/order-list/order-list.js.map +1 -0
- package/dist/matter/order-list-row/order-list-row.d.ts +44 -0
- package/dist/matter/order-list-row/order-list-row.js +127 -0
- package/dist/matter/order-list-row/order-list-row.js.map +1 -0
- package/dist/matter/order-list-search/order-list-search.d.ts +72 -0
- package/dist/matter/order-list-search/order-list-search.js +296 -0
- package/dist/matter/order-list-search/order-list-search.js.map +1 -0
- package/dist/matter/order-list-views/order-list-views.d.ts +65 -0
- package/dist/matter/order-list-views/order-list-views.js +154 -0
- package/dist/matter/order-list-views/order-list-views.js.map +1 -0
- package/dist/matter/order-payment/order-payment.d.ts +83 -0
- package/dist/matter/order-payment/order-payment.js +258 -0
- package/dist/matter/order-payment/order-payment.js.map +1 -0
- package/dist/matter/order-placed/order-placed-deliveries.d.ts +145 -0
- package/dist/matter/order-placed/order-placed-deliveries.js +380 -0
- package/dist/matter/order-placed/order-placed-deliveries.js.map +1 -0
- package/dist/matter/order-placed/order-placed.d.ts +122 -0
- package/dist/matter/order-placed/order-placed.js +452 -0
- package/dist/matter/order-placed/order-placed.js.map +1 -0
- package/dist/matter/order-placed-date/order-placed-date.d.ts +45 -0
- package/dist/matter/order-placed-date/order-placed-date.js +92 -0
- package/dist/matter/order-placed-date/order-placed-date.js.map +1 -0
- package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.d.ts +5 -8
- package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js +29 -81
- package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js.map +1 -1
- package/dist/matter/order-progress/order-progress.d.ts +85 -0
- package/dist/matter/order-progress/order-progress.js +203 -0
- package/dist/matter/order-progress/order-progress.js.map +1 -0
- package/dist/matter/order-summary/order-summary.d.ts +81 -0
- package/dist/matter/order-summary/order-summary.js +192 -0
- package/dist/matter/order-summary/order-summary.js.map +1 -0
- package/dist/matter/payment/cardholder-document-rule.d.ts +35 -0
- package/dist/matter/payment/cardholder-document-rule.js +35 -0
- package/dist/matter/payment/cardholder-document-rule.js.map +1 -0
- package/dist/matter/payment/payment-card-capture.js +38 -23
- package/dist/matter/payment/payment-card-capture.js.map +1 -1
- package/dist/matter/payment/payment-controller.js +11 -0
- package/dist/matter/payment/payment-controller.js.map +1 -1
- package/dist/matter/payment/payment-method-badges.js +4 -2
- package/dist/matter/payment/payment-method-badges.js.map +1 -1
- package/dist/matter/payment/payment.js +11 -0
- package/dist/matter/payment/payment.js.map +1 -1
- package/dist/matter/payment/use-payment-method-selection.d.ts +39 -0
- package/dist/matter/payment/use-payment-method-selection.js +30 -2
- package/dist/matter/payment/use-payment-method-selection.js.map +1 -1
- package/dist/matter/payment/use-payment-submit-gate.d.ts +16 -2
- package/dist/matter/payment/use-payment-submit-gate.js +42 -14
- package/dist/matter/payment/use-payment-submit-gate.js.map +1 -1
- package/dist/matter/place-order/place-order.d.ts +12 -7
- package/dist/matter/place-order/place-order.js +126 -103
- package/dist/matter/place-order/place-order.js.map +1 -1
- package/dist/matter/pricing/pricing.js +5 -5
- package/dist/matter/pricing/pricing.js.map +1 -1
- package/dist/matter/region/region.js +1 -1
- package/dist/matter/saved-cards/saved-cards.js +26 -15
- package/dist/matter/saved-cards/saved-cards.js.map +1 -1
- package/dist/matter/session/session.js +2 -2
- package/dist/matter/session/session.js.map +1 -1
- package/dist/matter/shipping-address/country-descriptors.d.ts +1 -0
- package/dist/matter/shipping-address/country-descriptors.js +23 -1
- package/dist/matter/shipping-address/country-descriptors.js.map +1 -1
- package/dist/matter/shipping-address/shipping-address.js +62 -14
- package/dist/matter/shipping-address/shipping-address.js.map +1 -1
- package/dist/matter/shipping-simulator/shipping-simulator.d.ts +1 -1
- package/dist/matter/shipping-simulator/shipping-simulator.js.map +1 -1
- package/dist/matter/sign-in-redirect/sign-in-redirect.js +48 -10
- package/dist/matter/sign-in-redirect/sign-in-redirect.js.map +1 -1
- package/dist/matter/variant-selector/variant-selector.js +2 -2
- package/dist/matter/variant-selector/variant-selector.js.map +1 -1
- package/dist/particles/address-summary/address-summary.js +2 -2
- package/dist/particles/address-summary/address-summary.js.map +1 -1
- package/dist/particles/alert-dialog/alert-dialog.d.ts +5 -1
- package/dist/particles/alert-dialog/alert-dialog.js +5 -2
- package/dist/particles/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/particles/copy-text/copy-text.d.ts +28 -0
- package/dist/particles/copy-text/copy-text.js +140 -0
- package/dist/particles/copy-text/copy-text.js.map +1 -0
- package/dist/particles/document-field/document-field.d.ts +34 -5
- package/dist/particles/document-field/document-field.js +115 -19
- package/dist/particles/document-field/document-field.js.map +1 -1
- package/dist/particles/field/field.js +1 -1
- package/dist/particles/field/field.js.map +1 -1
- package/dist/particles/footer-link-group/footer-link-group.d.ts +6 -2
- package/dist/particles/footer-link-group/footer-link-group.js +18 -7
- package/dist/particles/footer-link-group/footer-link-group.js.map +1 -1
- package/dist/particles/incentives/incentives.d.ts +6 -2
- package/dist/particles/incentives/incentives.js +7 -2
- package/dist/particles/incentives/incentives.js.map +1 -1
- package/dist/particles/logo/logo.d.ts +3 -2
- package/dist/particles/logo/logo.js +20 -8
- package/dist/particles/logo/logo.js.map +1 -1
- package/dist/particles/navigation-link/navigation-link.d.ts +2 -3
- package/dist/particles/navigation-link/navigation-link.js +4 -4
- package/dist/particles/navigation-link/navigation-link.js.map +1 -1
- package/dist/particles/navigation-link-list/navigation-link-list.d.ts +6 -2
- package/dist/particles/navigation-link-list/navigation-link-list.js +18 -7
- package/dist/particles/navigation-link-list/navigation-link-list.js.map +1 -1
- package/dist/particles/order-details-back/order-details-back.d.ts +28 -0
- package/dist/particles/order-details-back/order-details-back.js +52 -0
- package/dist/particles/order-details-back/order-details-back.js.map +1 -0
- package/dist/particles/order-status-badge/order-status-badge.d.ts +19 -0
- package/dist/particles/order-status-badge/order-status-badge.js +50 -0
- package/dist/particles/order-status-badge/order-status-badge.js.map +1 -0
- package/dist/particles/phone-field/phone-field.js +2 -2
- package/dist/particles/phone-field/phone-field.js.map +1 -1
- package/dist/particles/select/select.js +6 -6
- package/dist/particles/select/select.js.map +1 -1
- package/dist/particles/toast/toast.d.ts +1 -1
- package/dist/particles/toast/toast.js +32 -6
- package/dist/particles/toast/toast.js.map +1 -1
- package/dist/schema.graphql +1014 -28
- package/dist/sdk/actions/address-action-provider.d.ts +16 -0
- package/dist/sdk/actions/address-action-provider.js +20 -0
- package/dist/sdk/actions/address-action-provider.js.map +1 -0
- package/dist/sdk/actions/analytics-action-provider.js +1 -1
- package/dist/sdk/actions/index.d.ts +3 -1
- package/dist/sdk/actions/index.js +3 -1
- package/dist/sdk/actions/order-action-provider.d.ts +12 -0
- package/dist/sdk/actions/order-action-provider.js +16 -0
- package/dist/sdk/actions/order-action-provider.js.map +1 -0
- package/dist/sdk/actions/types.d.ts +6 -2
- package/dist/sdk/address/address-fields.js +38 -0
- package/dist/sdk/address/address-fields.js.map +1 -0
- package/dist/sdk/address/checkout-readiness.js +17 -0
- package/dist/sdk/address/checkout-readiness.js.map +1 -0
- package/dist/sdk/address/constants.js +60 -0
- package/dist/sdk/address/constants.js.map +1 -0
- package/dist/sdk/address/country-display.js +19 -0
- package/dist/sdk/address/country-display.js.map +1 -0
- package/dist/sdk/address/country-rules.js +30 -0
- package/dist/sdk/address/country-rules.js.map +1 -0
- package/dist/sdk/address/format-address.js +18 -0
- package/dist/sdk/address/format-address.js.map +1 -0
- package/dist/sdk/address/iso-countries.js +262 -0
- package/dist/sdk/address/iso-countries.js.map +1 -0
- package/dist/sdk/address/layouts.js +56 -0
- package/dist/sdk/address/layouts.js.map +1 -0
- package/dist/sdk/address/messages.js +97 -0
- package/dist/sdk/address/messages.js.map +1 -0
- package/dist/sdk/address/operations.d.ts +54 -0
- package/dist/sdk/address/operations.js +83 -0
- package/dist/sdk/address/operations.js.map +1 -0
- package/dist/sdk/address/postal-code-lookup-operations.js +23 -0
- package/dist/sdk/address/postal-code-lookup-operations.js.map +1 -0
- package/dist/sdk/address/postal-code.js +11 -0
- package/dist/sdk/address/postal-code.js.map +1 -0
- package/dist/sdk/address/schema.js +14 -0
- package/dist/sdk/address/schema.js.map +1 -0
- package/dist/sdk/address/server-fields.js +25 -0
- package/dist/sdk/address/server-fields.js.map +1 -0
- package/dist/sdk/address/switch-country.js +57 -0
- package/dist/sdk/address/switch-country.js.map +1 -0
- package/dist/sdk/address/types.d.ts +26 -0
- package/dist/sdk/address/use-address-actions.d.ts +29 -0
- package/dist/sdk/address/use-address-actions.js +247 -0
- package/dist/sdk/address/use-address-actions.js.map +1 -0
- package/dist/sdk/address/use-address-by-postal-code.js +57 -0
- package/dist/sdk/address/use-address-by-postal-code.js.map +1 -0
- package/dist/sdk/address/validate-address.js +62 -0
- package/dist/sdk/address/validate-address.js.map +1 -0
- package/dist/sdk/cache/storefront-cache.js +29 -4
- package/dist/sdk/cache/storefront-cache.js.map +1 -1
- package/dist/sdk/client/fetch-page-data.d.ts +11 -0
- package/dist/sdk/client/fetch-page-data.js +12 -1
- package/dist/sdk/client/fetch-page-data.js.map +1 -1
- package/dist/sdk/content/create-content-runtime.js +1 -8
- package/dist/sdk/content/create-content-runtime.js.map +1 -1
- package/dist/sdk/data/failed-fields.js +23 -0
- package/dist/sdk/data/failed-fields.js.map +1 -0
- package/dist/sdk/data/index.js +2 -2
- package/dist/sdk/data/page-reactive-data-root.d.ts +45 -7
- package/dist/sdk/data/page-reactive-data-root.js +88 -12
- package/dist/sdk/data/page-reactive-data-root.js.map +1 -1
- package/dist/sdk/data/reactive-list-item-data-provider.js +2 -2
- package/dist/sdk/generated/fragment-types.js +2 -0
- package/dist/sdk/generated/fragment-types.js.map +1 -1
- package/dist/sdk/generated/gql.js +3 -1
- package/dist/sdk/generated/gql.js.map +1 -1
- package/dist/sdk/generated/graphql.d.ts +8 -0
- package/dist/sdk/generated/graphql.js +212 -1
- package/dist/sdk/generated/graphql.js.map +1 -1
- package/dist/sdk/i18n/messages/en-US.js +138 -1
- package/dist/sdk/i18n/messages/en-US.json +138 -1
- package/dist/sdk/i18n/messages/en-US.json.d.ts +137 -0
- package/dist/sdk/i18n/messages/es-ES.js +138 -1
- package/dist/sdk/i18n/messages/es-ES.json +138 -1
- package/dist/sdk/i18n/messages/pt-BR.js +138 -1
- package/dist/sdk/i18n/messages/pt-BR.json +138 -1
- package/dist/sdk/navigation/current-page.js +30 -0
- package/dist/sdk/navigation/current-page.js.map +1 -0
- package/dist/sdk/navigation/not-found-log.d.ts +28 -0
- package/dist/sdk/navigation/not-found-log.js +133 -0
- package/dist/sdk/navigation/not-found-log.js.map +1 -0
- package/dist/sdk/navigation/path.js +25 -0
- package/dist/sdk/navigation/path.js.map +1 -0
- package/dist/sdk/navigation/redirect.d.ts +11 -0
- package/dist/sdk/navigation/redirect.js +22 -0
- package/dist/sdk/navigation/redirect.js.map +1 -0
- package/dist/sdk/navigation/report.js +70 -0
- package/dist/sdk/navigation/report.js.map +1 -0
- package/dist/sdk/navigation/resolve-storefront-href.d.ts +12 -0
- package/dist/sdk/navigation/resolve-storefront-href.js +214 -0
- package/dist/sdk/navigation/resolve-storefront-href.js.map +1 -0
- package/dist/sdk/navigation/route-identity-provider.d.ts +11 -0
- package/dist/sdk/navigation/route-identity-provider.js +20 -0
- package/dist/sdk/navigation/route-identity-provider.js.map +1 -0
- package/dist/sdk/navigation/route-map-provider.d.ts +19 -0
- package/dist/sdk/navigation/route-map-provider.js +29 -0
- package/dist/sdk/navigation/route-map-provider.js.map +1 -0
- package/dist/sdk/navigation/storefront-link.d.ts +16 -0
- package/dist/sdk/navigation/storefront-link.js +66 -0
- package/dist/sdk/navigation/storefront-link.js.map +1 -0
- package/dist/sdk/navigation/types.d.ts +45 -0
- package/dist/sdk/navigation/use-storefront-navigation.d.ts +26 -0
- package/dist/sdk/navigation/use-storefront-navigation.js +73 -0
- package/dist/sdk/navigation/use-storefront-navigation.js.map +1 -0
- package/dist/sdk/navigation/use-storefront-route.d.ts +26 -0
- package/dist/sdk/navigation/use-storefront-route.js +87 -0
- package/dist/sdk/navigation/use-storefront-route.js.map +1 -0
- package/dist/sdk/navigation/use-storefront-search-params.d.ts +11 -0
- package/dist/sdk/navigation/use-storefront-search-params.js +16 -0
- package/dist/sdk/navigation/use-storefront-search-params.js.map +1 -0
- package/dist/sdk/orders/completion-badge.d.ts +8 -0
- package/dist/sdk/orders/completion-badge.js +47 -0
- package/dist/sdk/orders/completion-badge.js.map +1 -0
- package/dist/sdk/orders/order-operations.d.ts +19 -0
- package/dist/sdk/orders/order-operations.js +21 -0
- package/dist/sdk/orders/order-operations.js.map +1 -0
- package/dist/sdk/orders/use-order-actions.d.ts +23 -0
- package/dist/sdk/orders/use-order-actions.js +62 -0
- package/dist/sdk/orders/use-order-actions.js.map +1 -0
- package/dist/sdk/payment/gateway-origin.js +26 -13
- package/dist/sdk/payment/gateway-origin.js.map +1 -1
- package/dist/sdk/payment/gateway.d.ts +14 -2
- package/dist/sdk/payment/gateway.js +32 -14
- package/dist/sdk/payment/gateway.js.map +1 -1
- package/dist/sdk/payment/operations.js +11 -0
- package/dist/sdk/payment/operations.js.map +1 -1
- package/dist/sdk/payment/submission-registry.d.ts +2 -1
- package/dist/sdk/payment/submission-registry.js +12 -8
- package/dist/sdk/payment/submission-registry.js.map +1 -1
- package/dist/sdk/payment/use-payment-actions.d.ts +5 -3
- package/dist/sdk/payment/use-payment-actions.js +5 -2
- package/dist/sdk/payment/use-payment-actions.js.map +1 -1
- package/dist/sdk/payment/use-place-order.d.ts +6 -3
- package/dist/sdk/payment/use-place-order.js +333 -143
- package/dist/sdk/payment/use-place-order.js.map +1 -1
- package/dist/sdk/seo/page-alternates.d.ts +55 -0
- package/dist/sdk/seo/page-alternates.js +132 -0
- package/dist/sdk/seo/page-alternates.js.map +1 -0
- package/dist/utils/own-value.js +19 -0
- package/dist/utils/own-value.js.map +1 -0
- package/dist/utils/safe-href.d.ts +4 -4
- package/dist/utils/safe-href.js +31 -22
- package/dist/utils/safe-href.js.map +1 -1
- package/package.json +5 -1
- package/skills/storefront-asset/SKILL.md +111 -0
- package/skills/storefront-asset/references/api-side.md +153 -0
- package/skills/storefront-asset/references/findings.md +40 -0
- package/skills/storefront-asset/references/ui-side.md +94 -0
- package/src/styles/tailwind.css +27 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cart-item.js","names":["BaseNumberField"],"sources":["../../../src/matter/cart-item/cart-item.tsx"],"sourcesContent":["'use client';\n\nimport { NumberField as BaseNumberField } from '@base-ui/react/number-field';\nimport { defineComponent } from '@vtex/fs-core';\nimport { type ComponentPropsWithRef, createContext, type ReactNode, useContext, useState } from 'react';\nimport { NavigationLink, type NavigationLinkProps } from '../../particles/navigation-link/navigation-link.js';\nimport { useActionsOverride } from '../../sdk/actions/actions-override-context.js';\nimport type { ComponentActions } from '../../sdk/actions/types.js';\nimport type { CartItemEntity } from '../../sdk/cart/operations.js';\nimport { useStorefrontContext } from '../../sdk/context/storefront-context.js';\nimport { useDataOverride } from '../../sdk/data/data-override-context.js';\nimport { graphql } from '../../sdk/generated/gql.js';\nimport { resolveSafeHref } from '../../utils/safe-href.js';\nimport { formatPrice } from '../cart/cart.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type CartItemRootProps = ComponentPropsWithRef<'div'> & {\n /**\n * Per-line slice delivered by the `reactive-list-item` runtime (`ReactiveListItemDataProvider`) —\n * the resolved `CartItem` entity read from the normalized cache via `useFragment(CartItem:<id>)`.\n * Injected through the iteration context in a compiled page; the `$data` prop is retained for test\n * injection.\n */\n $data?: CartItemEntity | null;\n};\n\nexport type CartItemImageProps = ComponentPropsWithRef<'img'> & {\n objectFit?: 'contain' | 'cover' | 'fill' | 'none';\n};\n\n// The destination is the line's own product page, so `href` is not authorable. `$content` goes with it\n// because the leaf declares no content model: a product URL is commerce data, not copy. `target` goes\n// too, so the same-tab rule is enforced by the type rather than merely absent from the schema — an\n// image-only link cannot carry the \"(opens in new tab)\" cue a `_blank` destination needs.\nexport type CartItemProductLinkProps = Omit<NavigationLinkProps, 'href' | '$content' | 'target'>;\n\nexport type CartItemNameProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemVariantProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemSkuProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemPriceProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemListPriceProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemDiscountPercentageProps = ComponentPropsWithRef<'span'> & {\n template?: string;\n};\n\nexport type CartItemLineTotalProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemOwnTotalProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemAggregateTotalProps = ComponentPropsWithRef<'span'>;\n\n/**\n * Per-line slice delivered by the `reactive-list-item` runtime: the value the wrapper publishes\n * through `DataOverrideContext`, read via `useDataOverride`. Only the field the stepper converts with\n * is typed; the resolved cache slice carries the full fragment selection.\n */\nexport type CartItemQuantitySlice = {\n variant: { unitMultiplier: number } | null;\n};\n\nexport type CartItemQuantityProps = Omit<ComponentPropsWithRef<'div'>, 'defaultValue' | 'value'> & {\n /** Minimum, in purchasable units — not in the unit shown to the shopper. */\n min?: number;\n /** Maximum, in purchasable units. Unbounded when unset. */\n max?: number;\n /** Step, in purchasable units: one step moves the display by one unit multiplier. */\n step?: number;\n removeOnDecrement?: boolean;\n /** Snap-notice template; `{value}` is replaced with the corrected displayed amount. */\n validationMessage?: string;\n children?: ReactNode;\n /** Per-line slice from the reactive-list-item runtime; injected/overridden by the wrapper. */\n $data?: CartItemQuantitySlice | null;\n /** Injected by the compiler from `actions: { cart: [...] }`; the cart write surface. */\n $actions?: ComponentActions;\n};\n\nexport type CartItemQuantityValueProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemRemoveProps = ComponentPropsWithRef<'button'> & {\n /** Injected by the compiler from `actions: { cart: ['removeItem'] }`; the cart write surface. */\n $actions?: ComponentActions;\n};\n\nexport type CartItemAvailableProps = ComponentPropsWithRef<'div'>;\n\nexport type CartItemUnavailableProps = ComponentPropsWithRef<'div'>;\n\nexport type CartItemGiftProps = ComponentPropsWithRef<'span'>;\n\n// ---------------------------------------------------------------------------\n// GraphQL Fragment — typed via @graphql-codegen client-preset (T7)\n//\n// CartItemRoot owns the core-scalar slice of each cart line (ADR-0012 Option B). Fields are inlined\n// (no `...` spread) so the compiler's list-field derivation finds the iterated `items` list by its\n// direct child field and re-roots this fragment to `CartItemRootData_CartItem on CartItem` for the\n// per-line `useFragment` subscription. `variant.availability` stays here — it is a cross-cutting core\n// scalar (CartCheckoutGuard, CartItemAvailable/Unavailable read it), not an optional display leaf.\n// `gift` stays here for the same reason: CartItemGift, CartItemRemove's suppression, and the row's\n// own state hook all read it, so one selection serves three consumers.\n//\n// Every field selected here MUST also be selected by the mutation fragment (`CartItemFragment` in\n// sdk/cart/operations.ts). ReactiveListItemDataProvider resolves `complete ? data : fallbackData`,\n// so a field present here but missing there makes every line incomplete after a cart write and\n// degrades the whole list to the cart-level slice, which carries no product or prices.\n// ---------------------------------------------------------------------------\n\nconst fragment = graphql(`\n fragment CartItemRootData on Query {\n cart {\n items {\n __typename\n id\n quantity\n price {\n amount\n currencyCode\n }\n listPrice {\n amount\n currencyCode\n }\n sellingPrice {\n amount\n currencyCode\n }\n gift\n ownTotal {\n ...MoneyFragment\n }\n total {\n ...MoneyFragment\n }\n variant {\n __typename\n variantId\n sku\n name\n image {\n url\n altText\n }\n availability\n }\n product {\n __typename\n productId\n title\n detailUrl\n categories {\n categoryId\n name\n }\n brand {\n brandId\n name\n }\n }\n }\n }\n }\n`);\n\n// ADR-0011: the unit multiplier is optional per-line data owned by the stepper that converts with\n// it, not by the core root — a cart line showing a read-only quantity never fetches it. Fields are\n// inlined (no `...` spread) so the derived per-item fragment is self-contained for `useFragment`, and\n// it drills the identical cart { items { ... } } path as the other per-item matters so it merges\n// un-aliased (ADR-0012).\nconst cartItemQuantityFragment = graphql(`\n fragment CartItemQuantityData on Query {\n cart {\n items {\n __typename\n id\n variant {\n __typename\n unitMultiplier\n }\n }\n }\n }\n`);\n\n// ---------------------------------------------------------------------------\n// Cart item context — provided by CartItemRoot with its resolved per-line slice\n// ---------------------------------------------------------------------------\n\nconst CartItemContext = createContext<CartItemEntity | null>(null);\n\nfunction useCartItem(): CartItemEntity {\n const item = useContext(CartItemContext);\n if (!item) {\n throw new Error('Cart item components must be used inside a <CartItemRoot> subtree.');\n }\n return item;\n}\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const CartItemRootDefinition = defineComponent<CartItemRootProps>({\n name: 'CartItemRoot',\n importPath: '@vtex/faststore/components',\n component: CartItemRoot,\n fragment,\n fetchStrategy: 'reactive-list-item',\n // Placement: requires CartList's `cart-item` iteration context and provides `cart-item-data` to the\n // core particles below. The two tokens differ, so it does not self-satisfy — the context-constraints\n // walk checks `requiresContext` before pushing this node's own `providesContext`.\n requiresContext: 'cart-item',\n providesContext: 'cart-item-data',\n editor: { label: 'Cart Item Root', category: 'commerce' },\n schema: {},\n guideline:\n 'Per-line data provider for the cart line-item template: place it directly inside CartList, wrapping the CartItem* particles (CartItemImage, CartItemName, CartItemPrice, the CartItemQuantity stepper, CartItemRemove, …). It owns the core per-item fields and delivers each line its own slice, so the core particles must sit inside it. Must be inside CartList. See cart-item.md.',\n});\n\nexport const CartItemImageDefinition = defineComponent<CartItemImageProps>({\n name: 'CartItemImage',\n importPath: '@vtex/faststore/components',\n component: CartItemImage,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Image', category: 'commerce' },\n schema: {\n objectFit: { type: ['contain', 'cover', 'fill', 'none'] as const, default: 'contain', label: 'Object Fit' },\n },\n guideline: 'Renders the product image from the cart item. Must be inside CartItemRoot.',\n});\n\nexport const CartItemProductLinkDefinition = defineComponent<CartItemProductLinkProps>({\n name: 'CartItemProductLink',\n importPath: '@vtex/faststore/components',\n component: CartItemProductLink,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Product Link', category: 'commerce' },\n schema: {},\n guideline:\n 'Links the cart line image to its product page, using the product URL the line already carries: place it inside CartItemRoot wrapping CartItemImage. Whether a line links is decided by placing this element, so there is no prop to toggle it — a checkout line simply omits it. Renders its children unwrapped when the line has no image or no usable product URL, so it never leaves an empty or unsafe link. Navigation is same-tab, and the leaf adds no fields to the page query. Must be inside CartItemRoot. See cart-item.md.',\n});\n\nexport const CartItemNameDefinition = defineComponent<CartItemNameProps>({\n name: 'CartItemName',\n importPath: '@vtex/faststore/components',\n component: CartItemName,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Name', category: 'commerce' },\n schema: {},\n guideline: 'Renders the product name from the cart item. Must be inside CartItemRoot.',\n});\n\nexport const CartItemVariantDefinition = defineComponent<CartItemVariantProps>({\n name: 'CartItemVariant',\n importPath: '@vtex/faststore/components',\n component: CartItemVariant,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Variant', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the selected variant name of the cart item (e.g. \"Midnight Black\"); renders null when the variant has no name. Use as a subtitle under CartItemName, typically alongside CartItemSku. Must be inside CartItemRoot.',\n});\n\nexport const CartItemSkuDefinition = defineComponent<CartItemSkuProps>({\n name: 'CartItemSku',\n importPath: '@vtex/faststore/components',\n component: CartItemSku,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item SKU', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the SKU code of the cart item variant (e.g. \"WNC-400X\"); renders null when the variant has no SKU. Use as a subtitle under CartItemName, typically alongside CartItemVariant. Must be inside CartItemRoot.',\n});\n\nexport const CartItemPriceDefinition = defineComponent<CartItemPriceProps>({\n name: 'CartItemPrice',\n importPath: '@vtex/faststore/components',\n component: CartItemPrice,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Price', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the selling price of the cart item, formatted from the active storefront context. Must be inside CartItemRoot.',\n});\n\nexport const CartItemListPriceDefinition = defineComponent<CartItemListPriceProps>({\n name: 'CartItemListPrice',\n importPath: '@vtex/faststore/components',\n component: CartItemListPrice,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item List Price', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the original list price of the cart item (strikethrough); renders null when no discount is active. Use alongside CartItemPrice to show promotional pricing. Must be inside CartItemRoot.',\n});\n\nexport const CartItemDiscountPercentageDefinition = defineComponent<CartItemDiscountPercentageProps>({\n name: 'CartItemDiscountPercentage',\n importPath: '@vtex/faststore/components',\n component: CartItemDiscountPercentage,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Discount %', category: 'commerce' },\n schema: {\n template: { type: 'string', default: '-{percentage}%', label: 'Template' },\n },\n guideline:\n 'Per-line discount badge showing the integer listPrice-vs-sellingPrice percentage (e.g. \"-20%\"); use the template prop to customize the format ({percentage} is the integer percent). Renders null when the line has no discount (sellingPrice >= listPrice), so it only appears on promotional lines. Place alongside CartItemPrice/CartItemListPrice. Must be inside CartItemRoot.',\n});\n\nexport const CartItemLineTotalDefinition = defineComponent<CartItemLineTotalProps>({\n name: 'CartItemLineTotal',\n importPath: '@vtex/faststore/components',\n component: CartItemLineTotal,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Line Total', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the per-line total (selling price × quantity) formatted from the active storefront context. Must be inside CartItemRoot.',\n});\n\nexport const CartItemOwnTotalDefinition = defineComponent<CartItemOwnTotalProps>({\n name: 'CartItemOwnTotal',\n importPath: '@vtex/faststore/components',\n component: CartItemOwnTotal,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Own Total', category: 'commerce' },\n schema: {},\n guideline:\n \"Renders the line's own total (excludes bundle/composition children), formatted from the active storefront context. Equals CartItemAggregateTotal on lines with no compositions. Must be inside CartItemRoot. See cart-item.md.\",\n});\n\nexport const CartItemAggregateTotalDefinition = defineComponent<CartItemAggregateTotalProps>({\n name: 'CartItemAggregateTotal',\n importPath: '@vtex/faststore/components',\n component: CartItemAggregateTotal,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Aggregate Total', category: 'commerce' },\n schema: {},\n guideline:\n \"Renders the line's aggregate total including all nested composition children, formatted from the active storefront context — the price the shopper actually pays for a bundled line. Equals CartItemOwnTotal on lines with no compositions. Must be inside CartItemRoot. See cart-item.md.\",\n});\n\nexport const CartItemQuantityDefinition = defineComponent<CartItemQuantityProps>({\n name: 'CartItemQuantity',\n importPath: '@vtex/faststore/components',\n component: CartItemQuantity,\n fragment: cartItemQuantityFragment,\n fetchStrategy: 'reactive-list-item',\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Quantity', category: 'commerce' },\n schema: {\n min: { type: 'number', default: 1, label: 'Min' },\n max: { type: 'number', label: 'Max' },\n step: { type: 'number', default: 1, label: 'Step' },\n removeOnDecrement: { type: 'boolean', default: false, label: 'Remove On Decrement' },\n validationMessage: { type: 'string', default: 'Quantity adjusted to {value}.', label: 'Validation Message' },\n },\n actions: { cart: ['increaseQuantity', 'decreaseQuantity', 'removeItem'] },\n guideline:\n \"Quantity stepper for a cart item, shown in the unit the merchant sells in: the displayed amount is the line's quantity times its unit multiplier, so a SKU sold in 500 g portions reads 1.5 rather than 3. min, max, and step are configured in purchasable units and converted for display, so one template serves SKUs with different multipliers. A typed amount that is out of bounds or off the multiplier grid snaps to the nearest valid value on commit and renders validationMessage. Set removeOnDecrement=true to remove the item when quantity reaches the minimum. Renders nothing on a gift line — a gift's quantity is promotion-controlled and cannot be stepped or removed, so use CartItemQuantityValue to show a gift's fixed quantity. Must be inside CartItemRoot. See cart-item.md.\",\n});\n\nexport const CartItemQuantityValueDefinition = defineComponent<CartItemQuantityValueProps>({\n name: 'CartItemQuantityValue',\n importPath: '@vtex/faststore/components',\n component: CartItemQuantityValue,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Quantity Value', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the read-only quantity count of the cart item as text (e.g. a \"Qty\" badge). Distinct from the editable CartItemQuantity stepper. Must be inside CartItemRoot.',\n});\n\nexport const CartItemRemoveDefinition = defineComponent<CartItemRemoveProps>({\n name: 'CartItemRemove',\n importPath: '@vtex/faststore/components',\n component: CartItemRemove,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Remove', category: 'commerce' },\n schema: {},\n actions: { cart: ['removeItem'] },\n guideline:\n 'Button that removes the current line item from the cart. Must be inside CartItemRoot. Also composes inside CartItemUnavailable as a remove-on-alert control. See cart-item.md.',\n});\n\nexport const CartItemAvailableDefinition = defineComponent<CartItemAvailableProps>({\n name: 'CartItemAvailable',\n importPath: '@vtex/faststore/components',\n component: CartItemAvailable,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Available', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders its children only when the current cart item is in stock (availability AVAILABLE). Wrap price, quantity stepper, and line total so they hide for unavailable items. Must be inside CartItemRoot.',\n});\n\nexport const CartItemUnavailableDefinition = defineComponent<CartItemUnavailableProps>({\n name: 'CartItemUnavailable',\n importPath: '@vtex/faststore/components',\n component: CartItemUnavailable,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Unavailable', category: 'commerce' },\n schema: {},\n guideline:\n 'Status badge shown only when the current cart item is NOT available. Renders a built-in English label per state (OUT_OF_STOCK → \"Out of stock\", UNAVAILABLE → \"Cannot be delivered\", otherwise \"Unavailable\"); pass children to override the text — children REPLACE the label, so a remove-on-alert composition nests an explicit label plus a CartItemRemove. Emits data-fs-cart-item-unavailable-state for per-state styling. Must be inside CartItemRoot. See cart-item.md.',\n});\n\nexport const CartItemGiftDefinition = defineComponent<CartItemGiftProps>({\n name: 'CartItemGift',\n importPath: '@vtex/faststore/components',\n component: CartItemGift,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Gift', category: 'commerce' },\n schema: {},\n guideline:\n 'Free marker for a promotional gift line: renders only when the current cart item is a gift, showing a built-in \"Free\" label; pass children to override the text — children REPLACE the label. Place it beside CartItemPrice/CartItemListPrice, whose struck-through list price it complements. It is an inline marker, not a wrapper for a gift-only subtree. On a gift line CartItemRemove suppresses itself and CartItemRoot marks the row with data-fs-cart-item-gift-line, so neither needs composing differently. Must be inside CartItemRoot. See cart-item.md.',\n});\n\n// ---------------------------------------------------------------------------\n// Implementations\n// ---------------------------------------------------------------------------\n\nexport function CartItemRoot({ $data: $dataProp, className, children, ...props }: CartItemRootProps) {\n // Read the resolved per-line slice through the DataOverrideContext seam the reactive-list-item\n // wrapper publishes (or via $data in tests), then re-provide it to the core particles — the same\n // \"read my slice, then provide React context to descendants\" shape CartRoot uses for the cart.\n const slice = useDataOverride<CartItemEntity | null | undefined>($dataProp) ?? null;\n\n return (\n <CartItemContext value={slice}>\n <div\n {...props}\n className={className}\n data-fs-cart-item-root=\"\"\n {...(slice?.gift ? { 'data-fs-cart-item-gift-line': '' } : {})}\n >\n {children}\n </div>\n </CartItemContext>\n );\n}\n\nexport function CartItemImage({ className, objectFit = 'contain', ...props }: CartItemImageProps) {\n const item = useCartItem();\n const image = item.variant.image;\n\n if (!image) return null;\n\n return (\n // biome-ignore lint/performance/noImgElement: Cart item image URL is dynamic; matter stays Next-agnostic.\n <img\n {...props}\n src={image.url}\n alt={image.altText ?? item.variant.name}\n className={className}\n style={{ objectFit, ...props.style }}\n data-fs-cart-item-image=\"\"\n data-fs-image=\"\"\n />\n );\n}\n\nexport function CartItemProductLink({ className, children, ...props }: Readonly<CartItemProductLinkProps>) {\n const item = useCartItem();\n // The particle gates only CMS-authored destinations, so a platform URL arriving on the `href` prop\n // is this leaf's own to validate. `detailUrl` is `String!`, which excludes null but not an empty or\n // hostile value. Either way the leaf degrades to a passthrough rather than self-hiding the way the\n // data-bearing leaves do: returning null would take the image with it.\n const destination = resolveSafeHref(item.product.detailUrl);\n\n if (!destination.safe) {\n // A blank URL is ordinary, so it stays quiet. Anything else was actively refused, and the reason\n // is logged the way the particle logs a refused authored destination — never the value itself.\n if (destination.reason !== 'blank') {\n console.warn('[CartItemProductLink] product URL rejected', { reason: destination.reason });\n }\n return children;\n }\n\n // The anchor is named by the wrapped image's alt text (`altText ?? variant.name`). With no image\n // there is nothing to render or name; with a blank alt the anchor would be a focus stop with no\n // accessible name. Either way, degrade to the unwrapped children rather than emit an unnamed link.\n const image = item.variant.image;\n if (!image || !(image.altText ?? item.variant.name ?? '').trim()) return children;\n\n return (\n <NavigationLink {...props} href={destination.href} className={className} data-fs-cart-item-product-link=\"\">\n {children}\n </NavigationLink>\n );\n}\n\nexport function CartItemName({ className, children, ...props }: CartItemNameProps) {\n const item = useCartItem();\n\n return (\n <span {...props} className={className} data-fs-cart-item-name=\"\" data-fs-text=\"\">\n {children ?? item.product.title}\n </span>\n );\n}\n\nexport function CartItemVariant({ className, children, ...props }: CartItemVariantProps) {\n const item = useCartItem();\n const { name } = item.variant;\n if (!name) return null;\n\n return (\n <span {...props} className={className} data-fs-cart-item-variant=\"\">\n {children ?? name}\n </span>\n );\n}\n\nexport function CartItemSku({ className, children, ...props }: CartItemSkuProps) {\n const item = useCartItem();\n const { sku } = item.variant;\n if (!sku) return null;\n\n return (\n <span {...props} className={className} data-fs-cart-item-sku=\"\">\n {children ?? sku}\n </span>\n );\n}\n\nexport function CartItemPrice({ className, children, ...props }: CartItemPriceProps) {\n const item = useCartItem();\n const { locale, currency } = useStorefrontContext();\n\n return (\n <span {...props} className={className} data-fs-cart-item-price=\"\" data-fs-text=\"\">\n {children ?? formatPrice(item.sellingPrice.amount, locale, currency)}\n </span>\n );\n}\n\nexport function CartItemListPrice({ className, children, ...props }: CartItemListPriceProps) {\n const item = useCartItem();\n const { locale, currency } = useStorefrontContext();\n\n if (item.listPrice.amount <= item.sellingPrice.amount) {\n return null;\n }\n\n return (\n <span {...props} className={className} data-fs-cart-item-list-price=\"\" data-fs-text=\"\">\n {children ?? formatPrice(item.listPrice.amount, locale, currency)}\n </span>\n );\n}\n\nexport function CartItemDiscountPercentage({\n template = '-{percentage}%',\n className,\n children,\n ...props\n}: CartItemDiscountPercentageProps) {\n const item = useCartItem();\n\n const listPrice = Number(item.listPrice.amount);\n const sellingPrice = Number(item.sellingPrice.amount);\n\n if (listPrice <= sellingPrice) {\n return null;\n }\n\n const percent = Math.round(((listPrice - sellingPrice) / listPrice) * 100);\n const text = template.replaceAll('{percentage}', String(percent));\n\n return (\n <span {...props} className={className} data-fs-cart-item-discount-percentage=\"\" data-fs-text=\"\">\n {children ?? text}\n </span>\n );\n}\n\nexport function CartItemLineTotal({ className, children, ...props }: CartItemLineTotalProps) {\n const item = useCartItem();\n const { locale, currency } = useStorefrontContext();\n\n return (\n <span {...props} className={className} data-fs-cart-item-line-total=\"\">\n {children ?? formatPrice(item.sellingPrice.amount * item.quantity, locale, currency)}\n </span>\n );\n}\n\nexport function CartItemOwnTotal({ className, children, ...props }: CartItemOwnTotalProps) {\n const item = useCartItem();\n const { locale, currency } = useStorefrontContext();\n\n return (\n <span {...props} className={className} data-fs-cart-item-own-total=\"\" data-fs-text=\"\">\n {children ?? formatPrice(Number(item.ownTotal.amount), locale, currency)}\n </span>\n );\n}\n\nexport function CartItemAggregateTotal({ className, children, ...props }: CartItemAggregateTotalProps) {\n const item = useCartItem();\n const { locale, currency } = useStorefrontContext();\n\n return (\n <span {...props} className={className} data-fs-cart-item-aggregate-total=\"\" data-fs-text=\"\">\n {children ?? formatPrice(Number(item.total.amount), locale, currency)}\n </span>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Quantity stepper — the unit multiplier converts between the purchasable count the order form and\n// the cart mutations use, and the measure the merchant sells in. `1` is the identity, so a SKU sold\n// by the unit behaves exactly as it did before the multiplier was read.\n// ---------------------------------------------------------------------------\n\n/** Why a committed amount had to be corrected; drives both the notice and its styling hook. */\ntype QuantityAdjustment = { reason: 'min' | 'max' | 'multiple'; value: number };\n\nconst MAX_DISPLAY_DECIMALS = 6;\n\n/**\n * A multiplier the platform cannot sell in — zero, negative, or non-finite — is treated as `1`, which\n * is also the fallback when the optional slice is absent (the stepper composed with no own provider).\n */\nfunction effectiveMultiplier(slice: CartItemQuantitySlice | null | undefined): number {\n const multiplier = slice?.variant?.unitMultiplier ?? 1;\n return Number.isFinite(multiplier) && multiplier > 0 ? multiplier : 1;\n}\n\n/**\n * Precision of the multiplier, so a displayed amount never carries binary-representation noise.\n * Derived through `toFixed` rather than `String`: `String` switches to exponential notation below\n * 1e-6, where it carries no decimal point and would report zero places, yielding a step of 0 that no\n * control can move. The normal-path precisions (0.5, 0.1, 0.333) are covered by tests; the\n * exponential branch is not, since it needs a multiplier no SKU can have.\n */\nfunction decimalPlacesOf(multiplier: number): number {\n const [, decimals = ''] = multiplier.toFixed(MAX_DISPLAY_DECIMALS).split('.');\n // Trailing zeros carry no precision; strip them with a linear scan rather than a `/0+$/` regex,\n // which SonarQube flags as super-linear (harmless here — the input is a bounded toFixed string).\n let end = decimals.length;\n while (end > 0 && decimals[end - 1] === '0') end -= 1;\n return end;\n}\n\nfunction roundTo(value: number, decimals: number): number {\n return Number(value.toFixed(decimals));\n}\n\nfunction resolveAdjustment(\n entered: number,\n snappedDisplay: number,\n multiplier: number,\n minDisplay: number,\n maxDisplay: number | undefined,\n): QuantityAdjustment | null {\n if (maxDisplay != null && entered > maxDisplay) return { reason: 'max', value: snappedDisplay };\n if (entered < minDisplay) return { reason: 'min', value: snappedDisplay };\n // Tolerance is relative to the multiplier, so recovering the count through a division never reads\n // as an off-grid entry.\n if (Math.abs(entered - snappedDisplay) > multiplier * 1e-6) return { reason: 'multiple', value: snappedDisplay };\n return null;\n}\n\nexport function CartItemQuantity({\n className,\n children,\n min = 1,\n max,\n step,\n removeOnDecrement,\n validationMessage = 'Quantity adjusted to {value}.',\n $data: $dataProp,\n $actions: $actionsProp,\n ...props\n}: CartItemQuantityProps) {\n const item = useCartItem();\n const slice = useDataOverride<CartItemQuantitySlice | null | undefined>($dataProp);\n const $actions = useActionsOverride($actionsProp);\n const [adjustment, setAdjustment] = useState<QuantityAdjustment | null>(null);\n\n // A gift line has no interactive quantity control, the same way CartItemRemove has no remove\n // control: a promotion-controlled quantity the shopper cannot validly change, and — with\n // removeOnDecrement — a path that would otherwise dispatch removeItem on a gift, discarding a\n // benefit T6 already sealed off from the remove button. Suppressing the stepper closes that path\n // under any composition. A store showing a gift's fixed quantity composes CartItemQuantityValue.\n if (item.gift) return null;\n\n const cart = $actions?.cart;\n\n const multiplier = effectiveMultiplier(slice);\n const decimals = decimalPlacesOf(multiplier);\n // min, max, and step are counts of purchasable units, and a cart line holds a whole number of\n // them, so a fractional bound has no valid value to land on. Left unrounded, a fractional `min` or\n // `max` becomes the clamp result and leaks into the delta — `decreaseQuantity({ by: 2.5 })` — which\n // the cart mutations reject. Rounding all three keeps every reachable value, and every dispatched\n // delta, an integer.\n const stepUnits = Math.max(1, Math.round(step ?? 1));\n const minUnits = Math.round(min);\n const maxUnits = max == null ? undefined : Math.round(max);\n const displayStep = roundTo(stepUnits * multiplier, decimals);\n const minDisplay = roundTo(minUnits * multiplier, decimals);\n const maxDisplay = maxUnits == null ? undefined : roundTo(maxUnits * multiplier, decimals);\n\n // Commit rather than change: `onValueChange` fires per parseable keystroke, so enforcing there\n // writes to the cart for every digit typed and snaps a value the shopper has not finished entering.\n function handleValueCommitted(value: number | null) {\n if (value == null) return;\n\n if (removeOnDecrement && value < minDisplay) {\n setAdjustment(null);\n void cart?.removeItem?.({ id: item.id });\n return;\n }\n\n const snapped = Math.min(Math.max(Math.round(value / multiplier), minUnits), maxUnits ?? Number.POSITIVE_INFINITY);\n const snappedDisplay = roundTo(snapped * multiplier, decimals);\n setAdjustment(resolveAdjustment(value, snappedDisplay, multiplier, minDisplay, maxDisplay));\n\n const delta = snapped - item.quantity;\n if (delta > 0) {\n void cart?.increaseQuantity?.({ id: item.id, by: delta });\n } else if (delta < 0) {\n void cart?.decreaseQuantity?.({ id: item.id, by: -delta });\n }\n }\n\n const effectiveMin = removeOnDecrement ? 0 : minDisplay;\n const computedClassName = removeOnDecrement ? (className ? `group ${className}` : 'group') : className;\n\n return (\n <BaseNumberField.Root\n {...props}\n value={roundTo(item.quantity * multiplier, decimals)}\n onValueCommitted={handleValueCommitted}\n // A shopper editing the field again makes the previous correction stale, so it is dropped as\n // soon as they type. Step and scrub reasons are excluded: those land on the grid by\n // construction and never raise a notice, so clearing on them would be dead code.\n onValueChange={(_value, details) => {\n if (details.reason === 'input-change' || details.reason === 'input-paste' || details.reason === 'input-clear') {\n setAdjustment(null);\n }\n }}\n min={effectiveMin}\n max={maxDisplay}\n step={displayStep}\n smallStep={displayStep}\n largeStep={roundTo(displayStep * 10, decimals)}\n snapOnStep\n // Base UI clamps text entry to min/max on its own, which would correct an out-of-bounds amount\n // before this component ever saw it — and a correction nobody can observe cannot be explained.\n allowOutOfRange\n className={computedClassName}\n data-fs-cart-item-quantity=\"\"\n {...(multiplier === 1 ? {} : { 'data-fs-cart-item-quantity-multiplier': String(multiplier) })}\n {...(adjustment\n ? {\n 'data-fs-cart-item-quantity-adjusted': '',\n 'data-fs-cart-item-quantity-adjusted-reason': adjustment.reason,\n }\n : {})}\n {...(removeOnDecrement && item.quantity <= minUnits ? { 'data-fs-cart-item-at-min': '' } : {})}\n >\n {children}\n {adjustment ? (\n <div role=\"alert\" data-fs-cart-item-quantity-validation=\"\">\n {validationMessage.replaceAll('{value}', String(adjustment.value))}\n </div>\n ) : null}\n </BaseNumberField.Root>\n );\n}\n\nexport function CartItemQuantityValue({ className, children, ...props }: CartItemQuantityValueProps) {\n const item = useCartItem();\n\n return (\n <span {...props} className={className} data-fs-cart-item-quantity-value=\"\">\n {children ?? String(item.quantity)}\n </span>\n );\n}\n\nexport function CartItemRemove({\n className,\n children,\n onClick,\n $actions: $actionsProp,\n ...props\n}: CartItemRemoveProps) {\n const $actions = useActionsOverride($actionsProp);\n const item = useCartItem();\n\n // A gift line has no remove control: the promotion granted the item, and this phase does not let a\n // shopper discard it. Suppressed here rather than by composition, because CartList renders one\n // template for every line — an author cannot leave the leaf out of gift rows alone. Covers the\n // remove-on-alert composition too; an unavailable gift is recovered by the cart-level\n // CartRemoveUnavailable batch and by server-side cart validation.\n if (item.gift) return null;\n\n function handleClick(e: React.MouseEvent<HTMLButtonElement>) {\n void $actions?.cart?.removeItem?.({ id: item.id });\n onClick?.(e);\n }\n\n return (\n <button\n {...props}\n type=\"button\"\n className={className}\n onClick={handleClick}\n data-fs-cart-item-remove=\"\"\n data-fs-button=\"\"\n >\n {children}\n </button>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Availability gates — read `variant.availability` from the core slice (F4).\n// UNKNOWN and any future non-available state fall back to the generic \"Unavailable\".\n// ---------------------------------------------------------------------------\n\nconst UNAVAILABLE_LABELS: Record<string, string> = {\n OUT_OF_STOCK: 'Out of stock',\n UNAVAILABLE: 'Cannot be delivered',\n};\n\nfunction unavailableLabel(availability: string): string {\n return UNAVAILABLE_LABELS[availability] ?? 'Unavailable';\n}\n\nexport function CartItemAvailable({ className, children, ...props }: CartItemAvailableProps) {\n const item = useCartItem();\n if (item.variant.availability !== 'AVAILABLE') return null;\n\n return (\n <div {...props} className={className} data-fs-cart-item-available=\"\">\n {children}\n </div>\n );\n}\n\nexport function CartItemUnavailable({ className, children, ...props }: CartItemUnavailableProps) {\n const item = useCartItem();\n const { availability } = item.variant;\n if (availability === 'AVAILABLE') return null;\n\n return (\n <div\n {...props}\n className={className}\n data-fs-cart-item-unavailable=\"\"\n data-fs-cart-item-unavailable-state={availability.toLowerCase()}\n >\n {children ?? unavailableLabel(availability)}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// F9 — gift marker. Inline, so `children` override the label rather than nesting a subtree; a gift\n// row's other needs are met by the row's own `data-fs-cart-item-gift-line` hook. \"Free\" is\n// operational copy, so it ships as a built-in default like the unavailability labels above, and moves\n// to the localization catalog rather than to a content model.\n// ---------------------------------------------------------------------------\n\nexport function CartItemGift({ className, children, ...props }: CartItemGiftProps) {\n const item = useCartItem();\n if (!item.gift) return null;\n\n return (\n <span {...props} className={className} data-fs-cart-item-gift=\"\" data-fs-text=\"\">\n {children ?? 'Free'}\n </span>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cart-item.js","names":["BaseNumberField"],"sources":["../../../src/matter/cart-item/cart-item.tsx"],"sourcesContent":["'use client';\n\nimport { NumberField as BaseNumberField } from '@base-ui/react/number-field';\nimport { defineComponent } from '@vtex/fs-core';\nimport { type ComponentPropsWithRef, createContext, type ReactNode, useContext, useState } from 'react';\nimport { NavigationLink, type NavigationLinkProps } from '../../particles/navigation-link/navigation-link.js';\nimport { useActionsOverride } from '../../sdk/actions/actions-override-context.js';\nimport type { ComponentActions } from '../../sdk/actions/types.js';\nimport type { CartItemEntity } from '../../sdk/cart/operations.js';\nimport { useStorefrontContext } from '../../sdk/context/storefront-context.js';\nimport { useDataOverride } from '../../sdk/data/data-override-context.js';\nimport { graphql } from '../../sdk/generated/gql.js';\nimport { resolveSafeHref } from '../../utils/safe-href.js';\nimport { formatPrice } from '../cart/cart.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type CartItemRootProps = ComponentPropsWithRef<'div'> & {\n /**\n * Per-line slice delivered by the `reactive-list-item` runtime (`ReactiveListItemDataProvider`) —\n * the resolved `CartItem` entity read from the normalized cache via `useFragment(CartItem:<id>)`.\n * Injected through the iteration context in a compiled page; the `$data` prop is retained for test\n * injection.\n */\n $data?: CartItemEntity | null;\n};\n\nexport type CartItemImageProps = ComponentPropsWithRef<'img'> & {\n objectFit?: 'contain' | 'cover' | 'fill' | 'none';\n};\n\n// The destination is the line's own product page, so `href` is not authorable. `$content` goes with it\n// because the leaf declares no content model: a product URL is commerce data, not copy. `target` goes\n// too, so the same-tab rule is enforced by the type rather than merely absent from the schema — an\n// image-only link cannot carry the \"(opens in new tab)\" cue a `_blank` destination needs.\nexport type CartItemProductLinkProps = Omit<NavigationLinkProps, 'href' | '$content' | 'target'>;\n\nexport type CartItemNameProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemVariantProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemSkuProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemPriceProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemListPriceProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemDiscountPercentageProps = ComponentPropsWithRef<'span'> & {\n template?: string;\n};\n\nexport type CartItemLineTotalProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemOwnTotalProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemAggregateTotalProps = ComponentPropsWithRef<'span'>;\n\n/**\n * Per-line slice delivered by the `reactive-list-item` runtime: the value the wrapper publishes\n * through `DataOverrideContext`, read via `useDataOverride`. Only the field the stepper converts with\n * is typed; the resolved cache slice carries the full fragment selection.\n */\nexport type CartItemQuantitySlice = {\n variant: { unitMultiplier: number } | null;\n};\n\nexport type CartItemQuantityProps = Omit<ComponentPropsWithRef<'div'>, 'defaultValue' | 'value'> & {\n /** Minimum, in purchasable units — not in the unit shown to the shopper. */\n min?: number;\n /** Maximum, in purchasable units. Unbounded when unset. */\n max?: number;\n /** Step, in purchasable units: one step moves the display by one unit multiplier. */\n step?: number;\n removeOnDecrement?: boolean;\n /** Snap-notice template; `{value}` is replaced with the corrected displayed amount. */\n validationMessage?: string;\n children?: ReactNode;\n /** Per-line slice from the reactive-list-item runtime; injected/overridden by the wrapper. */\n $data?: CartItemQuantitySlice | null;\n /** Injected by the compiler from `actions: { cart: [...] }`; the cart write surface. */\n $actions?: ComponentActions;\n};\n\nexport type CartItemQuantityValueProps = ComponentPropsWithRef<'span'>;\n\nexport type CartItemRemoveProps = ComponentPropsWithRef<'button'> & {\n /** Injected by the compiler from `actions: { cart: ['removeItem'] }`; the cart write surface. */\n $actions?: ComponentActions;\n};\n\nexport type CartItemAvailableProps = ComponentPropsWithRef<'div'>;\n\nexport type CartItemUnavailableProps = ComponentPropsWithRef<'div'>;\n\nexport type CartItemGiftProps = ComponentPropsWithRef<'span'>;\n\n// ---------------------------------------------------------------------------\n// GraphQL Fragment — typed via @graphql-codegen client-preset (T7)\n//\n// CartItemRoot owns the core-scalar slice of each cart line (ADR-0012 Option B). Fields are inlined\n// (no `...` spread) so the compiler's list-field derivation finds the iterated `items` list by its\n// direct child field and re-roots this fragment to `CartItemRootData_CartItem on CartItem` for the\n// per-line `useFragment` subscription. `variant.availability` stays here — it is a cross-cutting core\n// scalar (CartCheckoutGuard, CartItemAvailable/Unavailable read it), not an optional display leaf.\n// `gift` stays here for the same reason: CartItemGift, CartItemRemove's suppression, and the row's\n// own state hook all read it, so one selection serves three consumers.\n//\n// Every field selected here MUST also be selected by the mutation fragment (`CartItemFragment` in\n// sdk/cart/operations.ts). ReactiveListItemDataProvider resolves `complete ? data : fallbackData`,\n// so a field present here but missing there makes every line incomplete after a cart write and\n// degrades the whole list to the cart-level slice, which carries no product or prices.\n// ---------------------------------------------------------------------------\n\nconst fragment = graphql(`\n fragment CartItemRootData on Query {\n cart {\n items {\n __typename\n id\n quantity\n price {\n amount\n currencyCode\n }\n listPrice {\n amount\n currencyCode\n }\n sellingPrice {\n amount\n currencyCode\n }\n gift\n ownTotal {\n ...MoneyFragment\n }\n total {\n ...MoneyFragment\n }\n variant {\n __typename\n variantId\n sku\n name\n image {\n url\n altText\n }\n availability\n }\n product {\n __typename\n productId\n title\n detailUrl\n categories {\n categoryId\n name\n }\n brand {\n brandId\n name\n }\n }\n }\n }\n }\n`);\n\n// ADR-0011: the unit multiplier is optional per-line data owned by the stepper that converts with\n// it, not by the core root — a cart line showing a read-only quantity never fetches it. Fields are\n// inlined (no `...` spread) so the derived per-item fragment is self-contained for `useFragment`, and\n// it drills the identical cart { items { ... } } path as the other per-item matters so it merges\n// un-aliased (ADR-0012).\nconst cartItemQuantityFragment = graphql(`\n fragment CartItemQuantityData on Query {\n cart {\n items {\n __typename\n id\n variant {\n __typename\n unitMultiplier\n }\n }\n }\n }\n`);\n\n// ---------------------------------------------------------------------------\n// Cart item context — provided by CartItemRoot with its resolved per-line slice\n// ---------------------------------------------------------------------------\n\nconst CartItemContext = createContext<CartItemEntity | null>(null);\n\nfunction useCartItem(): CartItemEntity {\n const item = useContext(CartItemContext);\n if (!item) {\n throw new Error('Cart item components must be used inside a <CartItemRoot> subtree.');\n }\n return item;\n}\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const CartItemRootDefinition = defineComponent<CartItemRootProps>({\n name: 'CartItemRoot',\n importPath: '@vtex/faststore/components',\n component: CartItemRoot,\n fragment,\n fetchStrategy: 'reactive-list-item',\n // Placement: requires CartList's `cart-item` iteration context and provides `cart-item-data` to the\n // core particles below. The two tokens differ, so it does not self-satisfy — the context-constraints\n // walk checks `requiresContext` before pushing this node's own `providesContext`.\n requiresContext: 'cart-item',\n providesContext: 'cart-item-data',\n editor: { label: 'Cart Item Root', category: 'commerce' },\n schema: {},\n guideline:\n 'Per-line data provider for the cart line-item template: place it directly inside CartList, wrapping the CartItem* particles (CartItemImage, CartItemName, CartItemPrice, the CartItemQuantity stepper, CartItemRemove, …). It owns the core per-item fields and delivers each line its own slice, so the core particles must sit inside it. Must be inside CartList. See cart-item.md.',\n});\n\nexport const CartItemImageDefinition = defineComponent<CartItemImageProps>({\n name: 'CartItemImage',\n importPath: '@vtex/faststore/components',\n component: CartItemImage,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Image', category: 'commerce' },\n schema: {\n objectFit: { type: ['contain', 'cover', 'fill', 'none'] as const, default: 'contain', label: 'Object Fit' },\n },\n guideline: 'Renders the product image from the cart item. Must be inside CartItemRoot.',\n});\n\nexport const CartItemProductLinkDefinition = defineComponent<CartItemProductLinkProps>({\n name: 'CartItemProductLink',\n importPath: '@vtex/faststore/components',\n component: CartItemProductLink,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Product Link', category: 'commerce' },\n schema: {},\n guideline:\n 'Links the cart line image to its product page, using the product URL the line already carries: place it inside CartItemRoot wrapping CartItemImage. Whether a line links is decided by placing this element, so there is no prop to toggle it — a checkout line simply omits it. Renders its children unwrapped when the line has no image or no usable product URL, so it never leaves an empty or unsafe link. Navigation is same-tab, and the leaf adds no fields to the page query. Must be inside CartItemRoot. See cart-item.md.',\n});\n\nexport const CartItemNameDefinition = defineComponent<CartItemNameProps>({\n name: 'CartItemName',\n importPath: '@vtex/faststore/components',\n component: CartItemName,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Name', category: 'commerce' },\n schema: {},\n guideline: 'Renders the product name from the cart item. Must be inside CartItemRoot.',\n});\n\nexport const CartItemVariantDefinition = defineComponent<CartItemVariantProps>({\n name: 'CartItemVariant',\n importPath: '@vtex/faststore/components',\n component: CartItemVariant,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Variant', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the selected variant name of the cart item (e.g. \"Midnight Black\"); renders null when the variant has no name. Use as a subtitle under CartItemName, typically alongside CartItemSku. Must be inside CartItemRoot.',\n});\n\nexport const CartItemSkuDefinition = defineComponent<CartItemSkuProps>({\n name: 'CartItemSku',\n importPath: '@vtex/faststore/components',\n component: CartItemSku,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item SKU', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the SKU code of the cart item variant (e.g. \"WNC-400X\"); renders null when the variant has no SKU. Use as a subtitle under CartItemName, typically alongside CartItemVariant. Must be inside CartItemRoot.',\n});\n\nexport const CartItemPriceDefinition = defineComponent<CartItemPriceProps>({\n name: 'CartItemPrice',\n importPath: '@vtex/faststore/components',\n component: CartItemPrice,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Price', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the selling price of the cart item, formatted from the active storefront context. Must be inside CartItemRoot.',\n});\n\nexport const CartItemListPriceDefinition = defineComponent<CartItemListPriceProps>({\n name: 'CartItemListPrice',\n importPath: '@vtex/faststore/components',\n component: CartItemListPrice,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item List Price', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the original list price of the cart item (strikethrough); renders null when no discount is active. Use alongside CartItemPrice to show promotional pricing. Must be inside CartItemRoot.',\n});\n\nexport const CartItemDiscountPercentageDefinition = defineComponent<CartItemDiscountPercentageProps>({\n name: 'CartItemDiscountPercentage',\n importPath: '@vtex/faststore/components',\n component: CartItemDiscountPercentage,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Discount %', category: 'commerce' },\n schema: {\n template: { type: 'string', default: '-{percentage}%', label: 'Template' },\n },\n guideline:\n 'Per-line discount badge showing the integer listPrice-vs-sellingPrice percentage (e.g. \"-20%\"); use the template prop to customize the format ({percentage} is the integer percent). Renders null when the line has no discount (sellingPrice >= listPrice), so it only appears on promotional lines. Place alongside CartItemPrice/CartItemListPrice. Must be inside CartItemRoot.',\n});\n\nexport const CartItemLineTotalDefinition = defineComponent<CartItemLineTotalProps>({\n name: 'CartItemLineTotal',\n importPath: '@vtex/faststore/components',\n component: CartItemLineTotal,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Line Total', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the per-line total (selling price × quantity) formatted from the active storefront context. Must be inside CartItemRoot.',\n});\n\nexport const CartItemOwnTotalDefinition = defineComponent<CartItemOwnTotalProps>({\n name: 'CartItemOwnTotal',\n importPath: '@vtex/faststore/components',\n component: CartItemOwnTotal,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Own Total', category: 'commerce' },\n schema: {},\n guideline:\n \"Renders the line's own total (excludes bundle/composition children), formatted from the active storefront context. Equals CartItemAggregateTotal on lines with no compositions. Must be inside CartItemRoot. See cart-item.md.\",\n});\n\nexport const CartItemAggregateTotalDefinition = defineComponent<CartItemAggregateTotalProps>({\n name: 'CartItemAggregateTotal',\n importPath: '@vtex/faststore/components',\n component: CartItemAggregateTotal,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Aggregate Total', category: 'commerce' },\n schema: {},\n guideline:\n \"Renders the line's aggregate total including all nested composition children, formatted from the active storefront context — the price the shopper actually pays for a bundled line. Equals CartItemOwnTotal on lines with no compositions. Must be inside CartItemRoot. See cart-item.md.\",\n});\n\nexport const CartItemQuantityDefinition = defineComponent<CartItemQuantityProps>({\n name: 'CartItemQuantity',\n importPath: '@vtex/faststore/components',\n component: CartItemQuantity,\n fragment: cartItemQuantityFragment,\n fetchStrategy: 'reactive-list-item',\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Quantity', category: 'commerce' },\n schema: {\n min: { type: 'number', default: 1, label: 'Min' },\n max: { type: 'number', label: 'Max' },\n step: { type: 'number', default: 1, label: 'Step' },\n removeOnDecrement: { type: 'boolean', default: false, label: 'Remove On Decrement' },\n validationMessage: { type: 'string', default: 'Quantity adjusted to {value}.', label: 'Validation Message' },\n },\n actions: { cart: ['increaseQuantity', 'decreaseQuantity', 'removeItem'] },\n guideline:\n \"Quantity stepper for a cart item, shown in the unit the merchant sells in: the displayed amount is the line's quantity times its unit multiplier, so a SKU sold in 500 g portions reads 1.5 rather than 3. min, max, and step are configured in purchasable units and converted for display, so one template serves SKUs with different multipliers. A typed amount that is out of bounds or off the multiplier grid snaps to the nearest valid value on commit and renders validationMessage. Set removeOnDecrement=true to remove the item when quantity reaches the minimum. Renders nothing on a gift line — a gift's quantity is promotion-controlled and cannot be stepped or removed, so use CartItemQuantityValue to show a gift's fixed quantity. Must be inside CartItemRoot. See cart-item.md.\",\n});\n\nexport const CartItemQuantityValueDefinition = defineComponent<CartItemQuantityValueProps>({\n name: 'CartItemQuantityValue',\n importPath: '@vtex/faststore/components',\n component: CartItemQuantityValue,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Quantity Value', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders the read-only quantity count of the cart item as text (e.g. a \"Qty\" badge). Distinct from the editable CartItemQuantity stepper. Must be inside CartItemRoot.',\n});\n\nexport const CartItemRemoveDefinition = defineComponent<CartItemRemoveProps>({\n name: 'CartItemRemove',\n importPath: '@vtex/faststore/components',\n component: CartItemRemove,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Remove', category: 'commerce' },\n schema: {},\n actions: { cart: ['removeItem'] },\n guideline:\n 'Button that removes the current line item from the cart. Must be inside CartItemRoot. Also composes inside CartItemUnavailable as a remove-on-alert control. See cart-item.md.',\n});\n\nexport const CartItemAvailableDefinition = defineComponent<CartItemAvailableProps>({\n name: 'CartItemAvailable',\n importPath: '@vtex/faststore/components',\n component: CartItemAvailable,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Available', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders its children only when the current cart item is in stock (availability AVAILABLE). Wrap price, quantity stepper, and line total so they hide for unavailable items. Must be inside CartItemRoot.',\n});\n\nexport const CartItemUnavailableDefinition = defineComponent<CartItemUnavailableProps>({\n name: 'CartItemUnavailable',\n importPath: '@vtex/faststore/components',\n component: CartItemUnavailable,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Unavailable', category: 'commerce' },\n schema: {},\n guideline:\n 'Status badge shown only when the current cart item is NOT available. Renders a built-in English label per state (OUT_OF_STOCK → \"Out of stock\", UNAVAILABLE → \"Cannot be delivered\", otherwise \"Unavailable\"); pass children to override the text — children REPLACE the label, so a remove-on-alert composition nests an explicit label plus a CartItemRemove. Emits data-fs-cart-item-unavailable-state for per-state styling. Must be inside CartItemRoot. See cart-item.md.',\n});\n\nexport const CartItemGiftDefinition = defineComponent<CartItemGiftProps>({\n name: 'CartItemGift',\n importPath: '@vtex/faststore/components',\n component: CartItemGift,\n requiresContext: 'cart-item-data',\n editor: { label: 'Cart Item Gift', category: 'commerce' },\n schema: {},\n guideline:\n 'Free marker for a promotional gift line: renders only when the current cart item is a gift, showing a built-in \"Free\" label; pass children to override the text — children REPLACE the label. Place it beside CartItemPrice/CartItemListPrice, whose struck-through list price it complements. It is an inline marker, not a wrapper for a gift-only subtree. On a gift line CartItemRemove suppresses itself and CartItemRoot marks the row with data-fs-cart-item-gift-line, so neither needs composing differently. Must be inside CartItemRoot. See cart-item.md.',\n});\n\n// ---------------------------------------------------------------------------\n// Implementations\n// ---------------------------------------------------------------------------\n\nexport function CartItemRoot({ $data: $dataProp, className, children, ...props }: CartItemRootProps) {\n // Read the resolved per-line slice through the DataOverrideContext seam the reactive-list-item\n // wrapper publishes (or via $data in tests), then re-provide it to the core particles — the same\n // \"read my slice, then provide React context to descendants\" shape CartRoot uses for the cart.\n const slice = useDataOverride<CartItemEntity | null | undefined>($dataProp) ?? null;\n\n return (\n <CartItemContext value={slice}>\n <div\n {...props}\n className={className}\n data-fs-cart-item-root=\"\"\n {...(slice?.gift ? { 'data-fs-cart-item-gift-line': '' } : {})}\n >\n {children}\n </div>\n </CartItemContext>\n );\n}\n\nexport function CartItemImage({ className, objectFit = 'contain', ...props }: CartItemImageProps) {\n const item = useCartItem();\n const image = item.variant.image;\n\n if (!image) return null;\n\n return (\n // biome-ignore lint/performance/noImgElement: Cart item image URL is dynamic; matter stays Next-agnostic.\n <img\n {...props}\n src={image.url}\n alt={image.altText ?? item.variant.name}\n className={className}\n style={{ objectFit, ...props.style }}\n data-fs-cart-item-image=\"\"\n data-fs-image=\"\"\n />\n );\n}\n\nexport function CartItemProductLink({ className, children, ...props }: Readonly<CartItemProductLinkProps>) {\n const item = useCartItem();\n // The particle gates only CMS-authored destinations, so a platform URL arriving on the `href` prop\n // is this leaf's own to validate. `detailUrl` is `String!`, which excludes null but not an empty or\n // hostile value. Either way the leaf degrades to a passthrough rather than self-hiding the way the\n // data-bearing leaves do: returning null would take the image with it.\n const destination = resolveSafeHref(item.product.detailUrl);\n\n if (!destination.safe) {\n // A blank URL is ordinary, so it stays quiet. Anything else was actively refused, and the reason\n // is logged the way the particle logs a refused authored destination — never the value itself.\n if (destination.reason !== 'blank') {\n console.warn('[CartItemProductLink] product URL rejected', { reason: destination.reason });\n }\n return children;\n }\n\n // The anchor is named by the wrapped image's alt text (`altText ?? variant.name`). With no image\n // there is nothing to render or name; with a blank alt the anchor would be a focus stop with no\n // accessible name. Either way, degrade to the unwrapped children rather than emit an unnamed link.\n const image = item.variant.image;\n if (!image || !(image.altText ?? item.variant.name ?? '').trim()) return children;\n\n return (\n <NavigationLink {...props} href={destination.href} className={className} data-fs-cart-item-product-link=\"\">\n {children}\n </NavigationLink>\n );\n}\n\nexport function CartItemName({ className, children, ...props }: CartItemNameProps) {\n const item = useCartItem();\n\n return (\n <span {...props} className={className} data-fs-cart-item-name=\"\" data-fs-text=\"\">\n {children ?? item.product.title}\n </span>\n );\n}\n\nexport function CartItemVariant({ className, children, ...props }: CartItemVariantProps) {\n const item = useCartItem();\n const { name } = item.variant;\n if (!name) return null;\n\n return (\n <span {...props} className={className} data-fs-cart-item-variant=\"\">\n {children ?? name}\n </span>\n );\n}\n\nexport function CartItemSku({ className, children, ...props }: CartItemSkuProps) {\n const item = useCartItem();\n const { sku } = item.variant;\n if (!sku) return null;\n\n return (\n <span {...props} className={className} data-fs-cart-item-sku=\"\">\n {children ?? sku}\n </span>\n );\n}\n\nexport function CartItemPrice({ className, children, ...props }: CartItemPriceProps) {\n const item = useCartItem();\n const { locale, currency } = useStorefrontContext();\n\n return (\n <span {...props} className={className} data-fs-cart-item-price=\"\" data-fs-text=\"\">\n {children ?? formatPrice(item.sellingPrice.amount, locale, currency)}\n </span>\n );\n}\n\nexport function CartItemListPrice({ className, children, ...props }: CartItemListPriceProps) {\n const item = useCartItem();\n const { locale, currency } = useStorefrontContext();\n\n // `Decimal` arrives as a string (\"100.00\"), whatever the generated type says, and strings compare\n // character by character: \"100.00\" <= \"90.00\" holds. Compare the numbers, as the discount leaf does.\n if (Number(item.listPrice.amount) <= Number(item.sellingPrice.amount)) {\n return null;\n }\n\n return (\n <span {...props} className={className} data-fs-cart-item-list-price=\"\" data-fs-text=\"\">\n {children ?? formatPrice(item.listPrice.amount, locale, currency)}\n </span>\n );\n}\n\nexport function CartItemDiscountPercentage({\n template = '-{percentage}%',\n className,\n children,\n ...props\n}: CartItemDiscountPercentageProps) {\n const item = useCartItem();\n\n const listPrice = Number(item.listPrice.amount);\n const sellingPrice = Number(item.sellingPrice.amount);\n\n if (listPrice <= sellingPrice) {\n return null;\n }\n\n const percent = Math.round(((listPrice - sellingPrice) / listPrice) * 100);\n const text = template.replaceAll('{percentage}', String(percent));\n\n return (\n <span {...props} className={className} data-fs-cart-item-discount-percentage=\"\" data-fs-text=\"\">\n {children ?? text}\n </span>\n );\n}\n\nexport function CartItemLineTotal({ className, children, ...props }: CartItemLineTotalProps) {\n const item = useCartItem();\n const { locale, currency } = useStorefrontContext();\n\n return (\n <span {...props} className={className} data-fs-cart-item-line-total=\"\">\n {children ?? formatPrice(item.sellingPrice.amount * item.quantity, locale, currency)}\n </span>\n );\n}\n\nexport function CartItemOwnTotal({ className, children, ...props }: CartItemOwnTotalProps) {\n const item = useCartItem();\n const { locale, currency } = useStorefrontContext();\n\n return (\n <span {...props} className={className} data-fs-cart-item-own-total=\"\" data-fs-text=\"\">\n {children ?? formatPrice(Number(item.ownTotal.amount), locale, currency)}\n </span>\n );\n}\n\nexport function CartItemAggregateTotal({ className, children, ...props }: CartItemAggregateTotalProps) {\n const item = useCartItem();\n const { locale, currency } = useStorefrontContext();\n\n return (\n <span {...props} className={className} data-fs-cart-item-aggregate-total=\"\" data-fs-text=\"\">\n {children ?? formatPrice(Number(item.total.amount), locale, currency)}\n </span>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Quantity stepper — the unit multiplier converts between the purchasable count the order form and\n// the cart mutations use, and the measure the merchant sells in. `1` is the identity, so a SKU sold\n// by the unit behaves exactly as it did before the multiplier was read.\n// ---------------------------------------------------------------------------\n\n/** Why a committed amount had to be corrected; drives both the notice and its styling hook. */\ntype QuantityAdjustment = { reason: 'min' | 'max' | 'multiple'; value: number };\n\nconst MAX_DISPLAY_DECIMALS = 6;\n\n/**\n * A multiplier the platform cannot sell in — zero, negative, or non-finite — is treated as `1`, which\n * is also the fallback when the optional slice is absent (the stepper composed with no own provider).\n */\nfunction effectiveMultiplier(slice: CartItemQuantitySlice | null | undefined): number {\n const multiplier = slice?.variant?.unitMultiplier ?? 1;\n return Number.isFinite(multiplier) && multiplier > 0 ? multiplier : 1;\n}\n\n/**\n * Precision of the multiplier, so a displayed amount never carries binary-representation noise.\n * Derived through `toFixed` rather than `String`: `String` switches to exponential notation below\n * 1e-6, where it carries no decimal point and would report zero places, yielding a step of 0 that no\n * control can move. The normal-path precisions (0.5, 0.1, 0.333) are covered by tests; the\n * exponential branch is not, since it needs a multiplier no SKU can have.\n */\nfunction decimalPlacesOf(multiplier: number): number {\n const [, decimals = ''] = multiplier.toFixed(MAX_DISPLAY_DECIMALS).split('.');\n // Trailing zeros carry no precision; strip them with a linear scan rather than a `/0+$/` regex,\n // which SonarQube flags as super-linear (harmless here — the input is a bounded toFixed string).\n let end = decimals.length;\n while (end > 0 && decimals[end - 1] === '0') end -= 1;\n return end;\n}\n\nfunction roundTo(value: number, decimals: number): number {\n return Number(value.toFixed(decimals));\n}\n\nfunction resolveAdjustment(\n entered: number,\n snappedDisplay: number,\n multiplier: number,\n minDisplay: number,\n maxDisplay: number | undefined,\n): QuantityAdjustment | null {\n if (maxDisplay != null && entered > maxDisplay) return { reason: 'max', value: snappedDisplay };\n if (entered < minDisplay) return { reason: 'min', value: snappedDisplay };\n // Tolerance is relative to the multiplier, so recovering the count through a division never reads\n // as an off-grid entry.\n if (Math.abs(entered - snappedDisplay) > multiplier * 1e-6) return { reason: 'multiple', value: snappedDisplay };\n return null;\n}\n\nexport function CartItemQuantity({\n className,\n children,\n min = 1,\n max,\n step,\n removeOnDecrement,\n validationMessage = 'Quantity adjusted to {value}.',\n $data: $dataProp,\n $actions: $actionsProp,\n ...props\n}: CartItemQuantityProps) {\n const item = useCartItem();\n const slice = useDataOverride<CartItemQuantitySlice | null | undefined>($dataProp);\n const $actions = useActionsOverride($actionsProp);\n const [adjustment, setAdjustment] = useState<QuantityAdjustment | null>(null);\n\n // A gift line has no interactive quantity control, the same way CartItemRemove has no remove\n // control: a promotion-controlled quantity the shopper cannot validly change, and — with\n // removeOnDecrement — a path that would otherwise dispatch removeItem on a gift, discarding a\n // benefit T6 already sealed off from the remove button. Suppressing the stepper closes that path\n // under any composition. A store showing a gift's fixed quantity composes CartItemQuantityValue.\n if (item.gift) return null;\n\n const cart = $actions?.cart;\n\n const multiplier = effectiveMultiplier(slice);\n const decimals = decimalPlacesOf(multiplier);\n // min, max, and step are counts of purchasable units, and a cart line holds a whole number of\n // them, so a fractional bound has no valid value to land on. Left unrounded, a fractional `min` or\n // `max` becomes the clamp result and leaks into the delta — `decreaseQuantity({ by: 2.5 })` — which\n // the cart mutations reject. Rounding all three keeps every reachable value, and every dispatched\n // delta, an integer.\n const stepUnits = Math.max(1, Math.round(step ?? 1));\n const minUnits = Math.round(min);\n const maxUnits = max == null ? undefined : Math.round(max);\n const displayStep = roundTo(stepUnits * multiplier, decimals);\n const minDisplay = roundTo(minUnits * multiplier, decimals);\n const maxDisplay = maxUnits == null ? undefined : roundTo(maxUnits * multiplier, decimals);\n\n // Commit rather than change: `onValueChange` fires per parseable keystroke, so enforcing there\n // writes to the cart for every digit typed and snaps a value the shopper has not finished entering.\n function handleValueCommitted(value: number | null) {\n if (value == null) return;\n\n if (removeOnDecrement && value < minDisplay) {\n setAdjustment(null);\n void cart?.removeItem?.({ id: item.id });\n return;\n }\n\n const snapped = Math.min(Math.max(Math.round(value / multiplier), minUnits), maxUnits ?? Number.POSITIVE_INFINITY);\n const snappedDisplay = roundTo(snapped * multiplier, decimals);\n setAdjustment(resolveAdjustment(value, snappedDisplay, multiplier, minDisplay, maxDisplay));\n\n const delta = snapped - item.quantity;\n if (delta > 0) {\n void cart?.increaseQuantity?.({ id: item.id, by: delta });\n } else if (delta < 0) {\n void cart?.decreaseQuantity?.({ id: item.id, by: -delta });\n }\n }\n\n const effectiveMin = removeOnDecrement ? 0 : minDisplay;\n const computedClassName = removeOnDecrement ? (className ? `group ${className}` : 'group') : className;\n\n return (\n <BaseNumberField.Root\n {...props}\n value={roundTo(item.quantity * multiplier, decimals)}\n onValueCommitted={handleValueCommitted}\n // A shopper editing the field again makes the previous correction stale, so it is dropped as\n // soon as they type. Step and scrub reasons are excluded: those land on the grid by\n // construction and never raise a notice, so clearing on them would be dead code.\n onValueChange={(_value, details) => {\n if (details.reason === 'input-change' || details.reason === 'input-paste' || details.reason === 'input-clear') {\n setAdjustment(null);\n }\n }}\n min={effectiveMin}\n max={maxDisplay}\n step={displayStep}\n smallStep={displayStep}\n largeStep={roundTo(displayStep * 10, decimals)}\n snapOnStep\n // Base UI clamps text entry to min/max on its own, which would correct an out-of-bounds amount\n // before this component ever saw it — and a correction nobody can observe cannot be explained.\n allowOutOfRange\n className={computedClassName}\n data-fs-cart-item-quantity=\"\"\n {...(multiplier === 1 ? {} : { 'data-fs-cart-item-quantity-multiplier': String(multiplier) })}\n {...(adjustment\n ? {\n 'data-fs-cart-item-quantity-adjusted': '',\n 'data-fs-cart-item-quantity-adjusted-reason': adjustment.reason,\n }\n : {})}\n {...(removeOnDecrement && item.quantity <= minUnits ? { 'data-fs-cart-item-at-min': '' } : {})}\n >\n {children}\n {adjustment ? (\n <div role=\"alert\" data-fs-cart-item-quantity-validation=\"\">\n {validationMessage.replaceAll('{value}', String(adjustment.value))}\n </div>\n ) : null}\n </BaseNumberField.Root>\n );\n}\n\nexport function CartItemQuantityValue({ className, children, ...props }: CartItemQuantityValueProps) {\n const item = useCartItem();\n\n return (\n <span {...props} className={className} data-fs-cart-item-quantity-value=\"\">\n {children ?? String(item.quantity)}\n </span>\n );\n}\n\nexport function CartItemRemove({\n className,\n children,\n onClick,\n $actions: $actionsProp,\n ...props\n}: CartItemRemoveProps) {\n const $actions = useActionsOverride($actionsProp);\n const item = useCartItem();\n\n // A gift line has no remove control: the promotion granted the item, and this phase does not let a\n // shopper discard it. Suppressed here rather than by composition, because CartList renders one\n // template for every line — an author cannot leave the leaf out of gift rows alone. Covers the\n // remove-on-alert composition too; an unavailable gift is recovered by the cart-level\n // CartRemoveUnavailable batch and by server-side cart validation.\n if (item.gift) return null;\n\n function handleClick(e: React.MouseEvent<HTMLButtonElement>) {\n void $actions?.cart?.removeItem?.({ id: item.id });\n onClick?.(e);\n }\n\n return (\n <button\n {...props}\n type=\"button\"\n className={className}\n onClick={handleClick}\n data-fs-cart-item-remove=\"\"\n data-fs-button=\"\"\n >\n {children}\n </button>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Availability gates — read `variant.availability` from the core slice (F4).\n// UNKNOWN and any future non-available state fall back to the generic \"Unavailable\".\n// ---------------------------------------------------------------------------\n\nconst UNAVAILABLE_LABELS: Record<string, string> = {\n OUT_OF_STOCK: 'Out of stock',\n UNAVAILABLE: 'Cannot be delivered',\n};\n\nfunction unavailableLabel(availability: string): string {\n return UNAVAILABLE_LABELS[availability] ?? 'Unavailable';\n}\n\nexport function CartItemAvailable({ className, children, ...props }: CartItemAvailableProps) {\n const item = useCartItem();\n if (item.variant.availability !== 'AVAILABLE') return null;\n\n return (\n <div {...props} className={className} data-fs-cart-item-available=\"\">\n {children}\n </div>\n );\n}\n\nexport function CartItemUnavailable({ className, children, ...props }: CartItemUnavailableProps) {\n const item = useCartItem();\n const { availability } = item.variant;\n if (availability === 'AVAILABLE') return null;\n\n return (\n <div\n {...props}\n className={className}\n data-fs-cart-item-unavailable=\"\"\n data-fs-cart-item-unavailable-state={availability.toLowerCase()}\n >\n {children ?? unavailableLabel(availability)}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// F9 — gift marker. Inline, so `children` override the label rather than nesting a subtree; a gift\n// row's other needs are met by the row's own `data-fs-cart-item-gift-line` hook. \"Free\" is\n// operational copy, so it ships as a built-in default like the unavailability labels above, and moves\n// to the localization catalog rather than to a content model.\n// ---------------------------------------------------------------------------\n\nexport function CartItemGift({ className, children, ...props }: CartItemGiftProps) {\n const item = useCartItem();\n if (!item.gift) return null;\n\n return (\n <span {...props} className={className} data-fs-cart-item-gift=\"\" data-fs-text=\"\">\n {children ?? 'Free'}\n </span>\n );\n}\n"],"mappings":"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"}
|
|
@@ -6,11 +6,11 @@ import { Field, FieldBinding, FieldsetFieldContext, useFieldsetField, useFieldse
|
|
|
6
6
|
import { useActionsOverride } from "../../sdk/actions/actions-override-context.js";
|
|
7
7
|
import { useDataOverride } from "../../sdk/data/data-override-context.js";
|
|
8
8
|
import { useSession } from "../session/session.js";
|
|
9
|
+
import { useFormRegistry } from "../../particles/form/form.js";
|
|
9
10
|
import { Icon } from "../../particles/icon/icon.js";
|
|
10
11
|
import { useEntityDataResolved } from "../../sdk/data/entity-resolution-context.js";
|
|
11
12
|
import { useReactiveDataSettled } from "../../sdk/data/use-reactive-data-settled.js";
|
|
12
13
|
import { Fieldset } from "../../particles/fieldset/fieldset.js";
|
|
13
|
-
import { useFormRegistry } from "../../particles/form/form.js";
|
|
14
14
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
15
15
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
16
16
|
import { useFormContext } from "react-hook-form";
|
|
@@ -5,14 +5,14 @@ import { Field, FieldBinding, FieldsetFieldContext, useFieldsetField, useFieldse
|
|
|
5
5
|
import { useActionsOverride } from "../../sdk/actions/actions-override-context.js";
|
|
6
6
|
import { useDataOverride } from "../../sdk/data/data-override-context.js";
|
|
7
7
|
import { useSession } from "../session/session.js";
|
|
8
|
+
import { useFormRegistry } from "../../particles/form/form.js";
|
|
8
9
|
import { useEntityDataResolved } from "../../sdk/data/entity-resolution-context.js";
|
|
9
10
|
import { useReactiveDataSettled } from "../../sdk/data/use-reactive-data-settled.js";
|
|
10
11
|
import { Fieldset } from "../../particles/fieldset/fieldset.js";
|
|
11
12
|
import { DOCUMENTS, DocumentField, isSupportedDocumentType } from "../../particles/document-field/document-field.js";
|
|
12
|
-
import { useFormRegistry } from "../../particles/form/form.js";
|
|
13
13
|
import { PhoneField, toE164 } from "../../particles/phone-field/phone-field.js";
|
|
14
14
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
15
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
15
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
16
16
|
import { useFormContext } from "react-hook-form";
|
|
17
17
|
//#region src/matter/client-profile-form/client-profile-fieldset.tsx
|
|
18
18
|
/**
|
|
@@ -284,7 +284,7 @@ function ClientProfileFieldset({ country = "US", documentType, skeletonCount = C
|
|
|
284
284
|
return update(payload);
|
|
285
285
|
}, []);
|
|
286
286
|
const toPayload = useCallback((values) => {
|
|
287
|
-
const { country: activeCountry, getFieldState } = latest.current;
|
|
287
|
+
const { country: activeCountry, documentType: activeDocumentType, getFieldState } = latest.current;
|
|
288
288
|
const isDirty = (key) => getFieldState(`clientProfile.${key}`).isDirty;
|
|
289
289
|
const payload = {};
|
|
290
290
|
if ("firstName" in values && isDirty("firstName")) payload.firstName = String(values.firstName ?? "").trim();
|
|
@@ -293,7 +293,10 @@ function ClientProfileFieldset({ country = "US", documentType, skeletonCount = C
|
|
|
293
293
|
const raw = String(values.phone ?? "").trim();
|
|
294
294
|
payload.phone = raw ? toE164(raw, activeCountry) : "";
|
|
295
295
|
}
|
|
296
|
-
if ("document" in values && isDirty("document")
|
|
296
|
+
if ("document" in values && isDirty("document") && isSupportedDocumentType(activeDocumentType)) {
|
|
297
|
+
const rawDocument = typeof values.document === "string" ? values.document : "";
|
|
298
|
+
payload.document = DOCUMENTS[activeDocumentType].sanitize(rawDocument);
|
|
299
|
+
}
|
|
297
300
|
return payload;
|
|
298
301
|
}, []);
|
|
299
302
|
useEffect(() => registerFieldset({
|
|
@@ -382,7 +385,7 @@ function ProfileTextControl({ fieldKey, attr, autoComplete, defaultLabel, label,
|
|
|
382
385
|
children: /* @__PURE__ */ jsx(Field.Root, {
|
|
383
386
|
className,
|
|
384
387
|
"data-fs-client-profile-fieldset-field": attr,
|
|
385
|
-
children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
388
|
+
children: children ?? /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
386
389
|
/* @__PURE__ */ jsx(Field.Label, {}),
|
|
387
390
|
/* @__PURE__ */ jsx(Field.Control, {}),
|
|
388
391
|
/* @__PURE__ */ jsx(Field.Error, {})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-profile-fieldset.js","names":[],"sources":["../../../src/matter/client-profile-form/client-profile-fieldset.tsx"],"sourcesContent":["'use client';\n\nimport { defineComponent } from '@vtex/fs-core';\nimport {\n type ComponentPropsWithRef,\n createContext,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useFormContext } from 'react-hook-form';\nimport type { DocumentType } from '../../particles/document-field/document-field.js';\nimport { DOCUMENTS, DocumentField, isSupportedDocumentType } from '../../particles/document-field/document-field.js';\nimport type { FieldRule } from '../../particles/field/field.js';\nimport {\n Field,\n FieldBinding,\n FieldsetFieldContext,\n useFieldsetField,\n useFieldsetFieldNames,\n useNoAncestorFieldset,\n} from '../../particles/field/field.js';\nimport { Fieldset } from '../../particles/fieldset/fieldset.js';\nimport { useFormRegistry } from '../../particles/form/form.js';\nimport type { PhoneCountry } from '../../particles/phone-field/phone-field.js';\nimport { PhoneField, toE164 } from '../../particles/phone-field/phone-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 { useEntityDataResolved } from '../../sdk/data/entity-resolution-context.js';\nimport { useReactiveDataSettled } from '../../sdk/data/use-reactive-data-settled.js';\nimport type { ClientProfileUpdateInput } from '../../sdk/identity/operations.js';\nimport { useSession } from '../session/session.js';\n\n/**\n * `ClientProfileFieldset` — the composable, one-Save write of the `ClientProfile` sibling entity\n * (RFC: Composable Entity Forms, `Proposal > Design > The fieldset`). It registers itself with the\n * enclosing `FormRoot` under the `clientProfile` namespace and dispatches\n * `$actions.identity.clientProfileUpdate` for its own submit slot; it renders no `<form>` of its own.\n *\n * The four controls below are the smallest placeable unit: each calls `useFieldsetField` for its full\n * field name, renders a `FieldBinding` carrying the field's static facts, and — inside it — either the\n * default anatomy or the author's own children. No control's field contract carries a blocking\n * `RegisterOptions` key (no `required`, `min`, `max`, `minLength`, `maxLength`, `pattern`); requiredness\n * on the phone control rides on its own `requirement` prop instead, which only ever gates required\n * validation — a field the store does not want is omitted from the page, not switched off by a prop.\n *\n * The fieldset's context name is `client-profile-fieldset`, not `client-profile`: the shipped\n * `ClientProfilePhoneControl` (`matter/client-profile/client-profile.tsx`) already requires\n * `client-profile` and writes on blur, so reusing that name here would let an on-blur control compile\n * inside this batched fieldset and write immediately. `ClientProfileFieldsetPhoneControl` carries the\n * same container prefix as the context it requires, naming the collision rather than hiding it.\n */\n\n// ---------------------------------------------------------------------------\n// GraphQL Fragment — colocated on the fieldset, its own data root\n// ---------------------------------------------------------------------------\n\nconst fragment = /* GraphQL */ `\n fragment ClientProfileFieldsetData on Query {\n clientProfile {\n __typename\n id\n firstName\n lastName\n phone\n document\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** The `$data` slice forwarded by the reactive runtime — the ClientProfile sibling entity, unwrapped. */\nexport type ClientProfileFieldsetData = {\n firstName?: string | null;\n lastName?: string | null;\n phone?: string | null;\n document?: string | null;\n} | null;\n\n/** The four keys this fieldset registers under its namespace. */\ntype ProfileFieldKey = 'firstName' | 'lastName' | 'phone' | 'document';\n\n/** What the fieldset publishes to its controls. Carries no live value — react-hook-form owns those. */\ntype ClientProfileFieldsetContextValue = Readonly<{\n namespace: 'clientProfile';\n /** Seeds, resolved from `$data`. Empty string, never null, so a control never seeds `undefined`. */\n saved: Readonly<Record<ProfileFieldKey, string>>;\n /** True when the session reports `canEditData: false`. */\n readOnly: boolean;\n /** Phone mask/validation country, from the fieldset's own prop. */\n country: PhoneCountry;\n /** Locale document type; the document control renders only when supported. */\n documentType: string | undefined;\n /** The fieldset's own validation rules, per key. Present and empty today (Decision 6). */\n rules: Readonly<Partial<Record<ProfileFieldKey, FieldRule>>>;\n}>;\n\n/** No control publishes a rule today — a blocking `RegisterOptions` key would reopen the same submit\n * block for a read-only session (Decision 6): `readOnly` disables a control's rendered `<input>` via a\n * plain HTML attribute, never react-hook-form's own `disabled` registration option (`field.tsx`'s\n * `useController` call takes no `disabled`), so a required field would stay fully registered and\n * validated while the shopper has no way to focus or edit it. Kept as a stable empty object so the\n * context value's identity does not churn on every render. */\nconst NO_RULES: Readonly<Partial<Record<ProfileFieldKey, FieldRule>>> = {};\n\n/**\n * Rows `ClientProfileFieldsetSkeleton` draws when nobody says otherwise: one per control in the full\n * anatomy — first name, last name, phone, document. It is a default and not a derivation, because\n * the composed children are opaque here. A page that composes fewer sets `skeletonCount` to what it\n * composed, or the form jumps by the difference when the controls take the skeleton's place.\n */\nconst CLIENT_PROFILE_SKELETON_ROWS = 4;\n\nconst CLIENT_PROFILE_FIELDSET_UNSET = Symbol('ClientProfileFieldsetContextUnset');\n\nconst ClientProfileFieldsetContext = createContext<\n ClientProfileFieldsetContextValue | typeof CLIENT_PROFILE_FIELDSET_UNSET\n>(CLIENT_PROFILE_FIELDSET_UNSET);\n\nfunction useClientProfileFieldsetContext(): ClientProfileFieldsetContextValue {\n const ctx = useContext(ClientProfileFieldsetContext);\n if (ctx === CLIENT_PROFILE_FIELDSET_UNSET) {\n throw new Error('ClientProfileFieldset controls must be used inside ClientProfileFieldset.');\n }\n return ctx;\n}\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport type ClientProfileFieldsetProps = {\n /** Phone mask/validation country, provided to the phone control via context. */\n country?: PhoneCountry;\n /** Locale document type; the document control renders only when supported. */\n documentType?: string;\n /** Placeholder rows the loading skeleton draws. Set it to the number of controls composed here. */\n skeletonCount?: number;\n /** Announced politely while the profile resolves. Blank means no announcement. */\n loadingLabel?: string;\n /** Announced politely once the controls mount. Blank means no announcement. */\n loadedLabel?: string;\n /** Accessible name of the `<fieldset>` group. Blank means no name is exposed. */\n groupLabel?: string;\n className?: string;\n children?: ReactNode;\n /** `ClientProfileFieldsetData` slice forwarded by the reactive runtime. */\n $data?: ClientProfileFieldsetData;\n /** Compiler-injected; carries `identity.clientProfileUpdate`. */\n $actions?: ComponentActions;\n};\n\ntype ProfileTextControlProps = { label?: string; placeholder?: string; className?: string; children?: ReactNode };\n\nexport type ClientProfileFirstNameControlProps = ProfileTextControlProps;\nexport type ClientProfileLastNameControlProps = ProfileTextControlProps;\n\nexport type ClientProfileFieldsetPhoneControlProps = {\n label?: string;\n placeholder?: string;\n /**\n * Raises the field to required. The composite owns it; the contract carries no rule (Decision 6),\n * so here the raise is the whole of the resolution. Unset is no opinion, which leaves the phone\n * optional.\n */\n requirement?: 'mandatory' | 'optional';\n errorInvalid?: string;\n errorRequired?: string;\n className?: string;\n /** Given, replaces PhoneField's default Field.Label + PhoneFieldInput + Field.Error anatomy. */\n children?: ReactNode;\n};\n\nexport type ClientProfileDocumentControlProps = {\n label?: string;\n placeholder?: string;\n errorMessage?: string;\n className?: string;\n /** Given, replaces DocumentField's default Field.Label + DocumentFieldInput + Field.Error anatomy. */\n children?: ReactNode;\n};\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const ClientProfileFieldsetDefinition = defineComponent<ClientProfileFieldsetProps>({\n name: 'ClientProfileFieldset',\n importPath: '@vtex/faststore/components',\n component: ClientProfileFieldset,\n fragment,\n fetchStrategy: 'reactive',\n skeleton: ClientProfileFieldsetSkeleton,\n requiresContext: ['form', 'session'],\n providesContext: 'client-profile-fieldset',\n actions: { identity: ['clientProfileUpdate'] },\n editor: { label: 'Client Profile Fieldset', category: 'identity' },\n schema: {\n country: { type: 'string', default: 'US', label: 'Phone country (US or BR)' },\n documentType: { type: 'string', label: 'Document type (e.g. CPF)' },\n skeletonCount: { type: 'number', default: 4, label: 'Skeleton rows' },\n loadingLabel: { type: 'string', default: 'Loading your details', label: 'Loading announcement' },\n loadedLabel: { type: 'string', default: 'Your details are ready', label: 'Loaded announcement' },\n groupLabel: { type: 'string', default: 'Your details', label: 'Accessible group name' },\n },\n guideline:\n 'Registers a batched write of the ClientProfile entity with the enclosing FormRoot; renders one Fieldset.Root and no <form>. Must be inside a FormRoot with a SessionRoot ancestor. Compose ClientProfileFirstNameControl / ClientProfileLastNameControl / ClientProfileFieldsetPhoneControl / ClientProfileDocumentControl as children, in any order. The controls mount only once the profile has resolved, with skeletonCount placeholder rows in their place until then — set it to the number of controls composed, or the form jumps by the difference. Style the announcement sr-only. Dispatches only the fields the shopper changed. groupLabel names the group as its accessible name; blank emits no aria-label, and a composed FieldsetLegend wins over it.',\n});\n\nexport const ClientProfileFirstNameControlDefinition = defineComponent<ClientProfileFirstNameControlProps>({\n name: 'ClientProfileFirstNameControl',\n importPath: '@vtex/faststore/components',\n component: ClientProfileFirstNameControl,\n requiresContext: 'client-profile-fieldset',\n providesContext: 'field',\n editor: { label: 'Client Profile First Name Control', category: 'identity' },\n schema: {\n // No default (Decision 1): absence means the platform's value, applied at render time by\n // the control itself, not baked into the schema.\n label: { type: 'string', label: 'Label' },\n placeholder: { type: 'string', label: 'Placeholder' },\n },\n guideline:\n \"The bound first-name control; must be a descendant of ClientProfileFieldset. Renders Field.Root > Field.Label + Field.Control + Field.Error by default, or the composed children in the author's own order.\",\n});\n\nexport const ClientProfileLastNameControlDefinition = defineComponent<ClientProfileLastNameControlProps>({\n name: 'ClientProfileLastNameControl',\n importPath: '@vtex/faststore/components',\n component: ClientProfileLastNameControl,\n requiresContext: 'client-profile-fieldset',\n providesContext: 'field',\n editor: { label: 'Client Profile Last Name Control', category: 'identity' },\n schema: {\n // No default (Decision 1): absence means the platform's value, applied at render time by\n // the control itself, not baked into the schema.\n label: { type: 'string', label: 'Label' },\n placeholder: { type: 'string', label: 'Placeholder' },\n },\n guideline:\n \"The bound last-name control; must be a descendant of ClientProfileFieldset. Renders Field.Root > Field.Label + Field.Control + Field.Error by default, or the composed children in the author's own order.\",\n});\n\nexport const ClientProfileFieldsetPhoneControlDefinition = defineComponent<ClientProfileFieldsetPhoneControlProps>({\n name: 'ClientProfileFieldsetPhoneControl',\n importPath: '@vtex/faststore/components',\n component: ClientProfileFieldsetPhoneControl,\n requiresContext: 'client-profile-fieldset',\n // Provides both names (mirrors shipping-address.tsx:595's Decision 7): 'field' is what every\n // fieldset Control declares (RFC:503), but this control also composes PhoneField, whose\n // PhoneFieldInput part requires 'phone-field' — a page composing\n // ClientProfileFieldsetPhoneControl > PhoneFieldInput would otherwise fail the build with a\n // context-constraint violation, since PhoneField itself (the only 'phone-field' provider) never\n // appears in the page schema (finding F1).\n providesContext: ['field', 'phone-field'],\n editor: { label: 'Client Profile Fieldset Phone Control', category: 'identity' },\n schema: {\n // No default (Decision 1): absence means the platform's value, applied at render time by\n // the control itself, not baked into the schema.\n label: { type: 'string', label: 'Label' },\n placeholder: { type: 'string', label: 'Placeholder' },\n // No default: an unset requirement is no opinion, not a raise to mandatory.\n requirement: { type: ['mandatory', 'optional'] as const, label: 'Requirement' },\n // No default (Decision 1): absence means the platform's value, applied at render time by\n // the control itself, not baked into the schema.\n errorInvalid: { type: 'string', label: 'Invalid error message' },\n errorRequired: { type: 'string', label: 'Required error message' },\n },\n guideline:\n \"The bound phone control; must be a descendant of ClientProfileFieldset. Distinct from ClientProfilePhoneControl (matter/client-profile), which writes on blur and requires the client-profile context — the two cannot compile into each other's container. Composes the PhoneField particle: recomposable (RFC:503) — given children, they replace PhoneField's default Field.Label + PhoneFieldInput + Field.Error anatomy, in any order; childless keeps today's order. Its className styles PhoneField's own root, the node that lays out label, input and error — not the wrapper, which carries only the data-fs-client-profile-fieldset-field=\\\"phone\\\" styling hook. A store that does not want a phone field omits this element from the page: requirement only governs required validation, never visibility.\",\n});\n\nexport const ClientProfileDocumentControlDefinition = defineComponent<ClientProfileDocumentControlProps>({\n name: 'ClientProfileDocumentControl',\n importPath: '@vtex/faststore/components',\n component: ClientProfileDocumentControl,\n requiresContext: 'client-profile-fieldset',\n // Provides both names, same reasoning as ClientProfileFieldsetPhoneControlDefinition above: this\n // control composes DocumentField, whose DocumentFieldInput part requires 'document-field' — a page\n // composing ClientProfileDocumentControl > DocumentFieldInput would otherwise fail the build\n // (finding F1).\n providesContext: ['field', 'document-field'],\n editor: { label: 'Client Profile Document Control', category: 'identity' },\n schema: {\n // No default (Decision 1): absence means the platform's value, applied at render time by\n // the control itself, not baked into the schema.\n label: { type: 'string', label: 'Label' },\n placeholder: { type: 'string', label: 'Placeholder' },\n errorMessage: { type: 'string', label: 'Error message' },\n },\n guideline:\n \"The bound tax-document control; must be a descendant of ClientProfileFieldset. Renders nothing, and registers no field name, when the fieldset's documentType is unsupported. Composes the DocumentField particle: recomposable (RFC:503) — given children, they replace DocumentField's default Field.Label + DocumentFieldInput + Field.Error anatomy, in any order; childless keeps today's order.\",\n});\n\n// ---------------------------------------------------------------------------\n// Fieldset root\n// ---------------------------------------------------------------------------\n\n/**\n * Registers a batched write of the `ClientProfile` entity with the enclosing `FormRoot`\n * (`registerFieldset`, namespace `clientProfile`) on mount, unconditionally — so this fieldset keeps its\n * original dispatch slot even while its own query is still resolving. Its children — the seed values and\n * the field-name registry its controls need — render only once the entity has resolved; until then the\n * `Fieldset.Root` carries `-pending` and renders `ClientProfileFieldsetSkeleton` (Decision 5).\n * `dispatch` and `toPayload` are `useCallback([])`-stable and read `$actions`/`country`/`documentType`\n * through a ref, so the registration effect runs exactly once per mount regardless of how often the\n * parent re-renders.\n *\n * \"Resolved\" is two conditions, because `$data` alone cannot answer it. Under the reactive runtime the\n * slice is defined from the first render — `useReactiveEntity` hands down `fallbackData` while the cache\n * read is incomplete — so an empty server answer looks exactly like a resolved one, and the controls\n * would mount and seed `''` before the shopper's real profile arrives. `useSeedAdoption` re-seeds a\n * clean field with `defaultValue` on every render, but once the field turns dirty no later seed\n * ever reaches it again: a shopper who starts typing before the real profile arrives locks that\n * empty seed in for good, and even a shopper who does not type sees the field flash from `''` to\n * the real data. `useReactiveDataSettled` supplies the missing half: the page query's own\n * in-flight state, latched so it only ever goes pending → settled. The `$data === undefined` half still\n * carries trees with no reactive root above them, where the slice arrives as a plain prop.\n *\n * Both ends of that wait are spoken: the fieldset carries `aria-busy` while it lasts and holds a polite\n * live region that announces `loadingLabel` and then `loadedLabel`. Style the region `sr-only` — it is\n * text meant for a screen reader, not for the page.\n */\nexport function ClientProfileFieldset({\n country = 'US',\n documentType,\n skeletonCount = CLIENT_PROFILE_SKELETON_ROWS,\n loadingLabel = 'Loading your details',\n loadedLabel = 'Your details are ready',\n groupLabel = 'Your details',\n className,\n children,\n $data: $dataProp,\n $actions: $actionsProp,\n}: ClientProfileFieldsetProps) {\n // Nesting fieldsets has no legitimate use: useFieldsetField resolves the *nearest*\n // FieldsetFieldContext, not the one owning a control's actual namespace, so a control nested two\n // fieldsets deep would silently register under the wrong one and its write would be discarded on\n // submit. Runtime registry integrity, mirroring the namespace-collision throw FormRoot already ships\n // (form.tsx:280-288).\n useNoAncestorFieldset('ClientProfileFieldset');\n\n const $data = useDataOverride($dataProp);\n\n const $actions = useActionsOverride($actionsProp);\n // Three conditions, not two. `useReactiveDataSettled` latches one way and never re-arms, so on its\n // own it answers \"has this page ever resolved\", not \"is what I am holding right now the server's\n // answer\" — after the page query's entity ref moves to one this cache never stored, or its root is\n // skipped for want of a variable, the slice silently regresses to `fallbackData` while the latch\n // still reads settled. `useEntityDataResolved` is the per-entity half: unlatched, so it follows the\n // slice back down and returns this fieldset to its skeleton instead of leaving live controls seeded\n // from a placeholder.\n const settled = useReactiveDataSettled();\n const sliceResolved = useEntityDataResolved();\n const resolved = settled && sliceResolved && $data !== undefined;\n // Also enforces the SessionRoot ancestor: useSession() throws when rendered outside one.\n const { canEditData } = useSession();\n const readOnly = !canEditData;\n\n // Runs before useFormContext() below: useFormRegistry() throws the friendly \"Form parts must be used\n // inside FormRoot.\" error when no FormRoot is above, so a FormRoot-less render fails there rather than\n // reaching the getFieldState destructure, which throws an unhelpful TypeError instead (useFormContext()\n // resolves to null outside a FormRoot, and destructuring null throws).\n const { registerFieldset } = useFormRegistry();\n\n // Decision 3, as later narrowed: this fieldset still reads dirtiness imperatively, but the rule is no\n // longer the form's. `FormRoot` subscribes `dirtyFields` in render and decides which fieldsets to\n // dispatch from it, by the fields each one has mounted, on react-hook-form's documented contract.\n //\n // The imperative read survives here because it asks a different question in a different place: which\n // of *this* fieldset's own fields changed, resolved inside `toPayload` at submit time, by full\n // registered field name — the shape `getFieldState` documents. `useFormContext()` adds no\n // `formState` subscription of its own; one is created only when a caller reads a formState key off\n // the `useForm`/`useFormState` proxy (or hands that already-subscribed object to `getFieldState`'s\n // second argument). Calling `getFieldState(name)` with no second argument reads the current internal\n // state directly, so this fieldset root still does not re-render on a keystroke.\n const { getFieldState } = useFormContext();\n\n const latest = useRef({ $actions, country, documentType, readOnly, getFieldState });\n useEffect(() => {\n latest.current = { $actions, country, documentType, readOnly, getFieldState };\n }, [$actions, country, documentType, readOnly, getFieldState]);\n\n const { registry: fieldsetRegistry, getFieldNames } = useFieldsetFieldNames('clientProfile');\n\n const dispatch = useCallback(async (payload: Record<string, unknown>) => {\n // Defence-in-depth: the UI disables every input while read-only, but `FormClear` sets values (and\n // marks them dirty) on every registered field regardless of that disabled attribute — react-hook-\n // form's own `useController` calls never receive a `disabled` registration option, so it has no\n // notion of \"disabled\" of its own. Guarding here keeps the write from reaching the network even if\n // some other composition dirties this fieldset while canEditData is false (the server also refuses,\n // but a client-side guard should not depend on that).\n if (latest.current.readOnly) {\n throw new Error('ClientProfileFieldset cannot dispatch while the session is read-only.');\n }\n const update = latest.current.$actions?.identity?.clientProfileUpdate;\n // Throwing is the contract's failure path: FormRoot catches it and records this fieldset's failure\n // (form.tsx:264-266). Returning [] would report an unqualified success for a write that never\n // happened.\n if (!update) throw new Error('ClientProfileFieldset requires $actions.identity.clientProfileUpdate.');\n return update(payload as ClientProfileUpdateInput);\n }, []);\n\n const toPayload = useCallback((values: Record<string, unknown>): Record<string, unknown> => {\n const { country: activeCountry, getFieldState } = latest.current;\n // FR-5: a key is emitted only when the shopper actually changed it — presence in `values` alone\n // (every registered field is always present) is not enough. `dirtyFields` is not read directly;\n // `getFieldState` is called with no second argument, which reads react-hook-form's current\n // internal state without subscribing to it (Decision 3).\n const isDirty = (key: ProfileFieldKey) => getFieldState(`clientProfile.${key}`).isDirty;\n const payload: Record<string, string> = {};\n if ('firstName' in values && isDirty('firstName')) payload.firstName = String(values.firstName ?? '').trim();\n if ('lastName' in values && isDirty('lastName')) payload.lastName = String(values.lastName ?? '').trim();\n if ('phone' in values && isDirty('phone')) {\n const raw = String(values.phone ?? '').trim();\n // toE164 on a blank value returns the bare country code, which would write \"+55\" over the\n // shopper's number and fail every later save.\n payload.phone = raw ? toE164(raw, activeCountry) : '';\n }\n if ('document' in values && isDirty('document')) {\n payload.document = String(values.document ?? '').replace(/\\D/g, '');\n }\n return payload;\n }, []);\n\n useEffect(\n () =>\n registerFieldset({\n namespace: 'clientProfile',\n getFieldNames,\n dispatch,\n toPayload,\n // Decision 1: the register is a machine identifier, the kebab-case ASCII rendering of this\n // registration's own `namespace` (`clientProfile` → `client-profile`) — not authored per\n // page (Decision 2).\n label: 'client-profile',\n }),\n [registerFieldset, getFieldNames, dispatch, toPayload],\n );\n\n const saved = useMemo<Readonly<Record<ProfileFieldKey, string>>>(\n () => ({\n firstName: $data?.firstName ?? '',\n lastName: $data?.lastName ?? '',\n phone: $data?.phone ?? '',\n document: $data?.document ?? '',\n }),\n [$data],\n );\n\n const contextValue = useMemo<ClientProfileFieldsetContextValue>(\n () => ({ namespace: 'clientProfile', saved, readOnly, country, documentType, rules: NO_RULES }),\n [saved, readOnly, country, documentType],\n );\n\n // Assistive technology only reports a change inside a region that was already in the tree, so a\n // region mounted together with its text announces nothing. The region therefore sits in both\n // branches and starts empty, with the wait written on the pass after mount and replaced — not\n // cleared — once the controls are up: the skeleton is `aria-hidden`, so without this second\n // message the form would materialise in silence (WCAG 2.1 SC 4.1.3).\n const [announcement, setAnnouncement] = useState('');\n const announced = resolved ? loadedLabel : loadingLabel;\n useEffect(() => {\n setAnnouncement(announced);\n }, [announced]);\n\n return (\n <Fieldset.Root\n className={className}\n disabled={readOnly}\n aria-busy={resolved ? undefined : true}\n // FR-12: blank (or whitespace-only) means no name is exposed, mirroring the loadingLabel/\n // loadedLabel convention. On the root itself (FR-13), so it is identical whether the fieldset\n // is pending or resolved; a page-composed FieldsetLegend still wins via aria-labelledby.\n {...(groupLabel?.trim() ? { 'aria-label': groupLabel } : {})}\n data-fs-client-profile-fieldset=\"\"\n {...(resolved ? {} : { 'data-fs-client-profile-fieldset-pending': '' })}\n {...(readOnly ? { 'data-fs-client-profile-fieldset-readonly': '' } : {})}\n >\n {resolved ? (\n <FieldsetFieldContext.Provider value={fieldsetRegistry}>\n <ClientProfileFieldsetContext.Provider value={contextValue}>{children}</ClientProfileFieldsetContext.Provider>\n </FieldsetFieldContext.Provider>\n ) : (\n <ClientProfileFieldsetSkeleton count={skeletonCount} />\n )}\n <output aria-live=\"polite\" data-fs-client-profile-fieldset-announcement=\"\">\n {announcement}\n </output>\n </Fieldset.Root>\n );\n}\n\n/**\n * The loading state of the fieldset, rendered in place of the controls until the entity resolves and\n * declared as the definition's `skeleton`. Purely visual: the wait is spoken by the fieldset's own live\n * region, so this is hidden from assistive technology rather than announced twice.\n */\nexport function ClientProfileFieldsetSkeleton({\n count = CLIENT_PROFILE_SKELETON_ROWS,\n className,\n ...props\n}: ComponentPropsWithRef<'div'> & { count?: number }) {\n return (\n <div {...props} className={className} data-fs-client-profile-fieldset-skeleton=\"\" aria-hidden=\"true\">\n {Array.from({ length: count }, (_, i) => (\n <span key={i} data-fs-client-profile-fieldset-skeleton-field=\"\" />\n ))}\n </div>\n );\n}\n\nClientProfileFieldset.Skeleton = ClientProfileFieldsetSkeleton;\n\n// ---------------------------------------------------------------------------\n// Text controls — first name, last name\n// ---------------------------------------------------------------------------\n\nfunction ProfileTextControl({\n fieldKey,\n attr,\n autoComplete,\n defaultLabel,\n label,\n placeholder,\n className,\n children,\n}: {\n fieldKey: 'firstName' | 'lastName';\n attr: string;\n autoComplete: string;\n defaultLabel: string;\n label?: string;\n placeholder?: string;\n className?: string;\n children?: ReactNode;\n}) {\n const { namespace, saved, readOnly, rules } = useClientProfileFieldsetContext();\n const name = useFieldsetField(namespace, fieldKey);\n\n return (\n <FieldBinding\n name={name}\n label={label ?? defaultLabel}\n placeholder={placeholder}\n defaultValue={saved[fieldKey]}\n rule={rules[fieldKey]}\n disabled={readOnly}\n autoComplete={autoComplete}\n >\n <Field.Root className={className} data-fs-client-profile-fieldset-field={attr}>\n {children ?? (\n <>\n <Field.Label />\n <Field.Control />\n <Field.Error />\n </>\n )}\n </Field.Root>\n </FieldBinding>\n );\n}\n\nexport function ClientProfileFirstNameControl({\n label,\n placeholder,\n className,\n children,\n}: ClientProfileFirstNameControlProps) {\n return (\n <ProfileTextControl\n fieldKey=\"firstName\"\n attr=\"first-name\"\n autoComplete=\"given-name\"\n defaultLabel=\"First name\"\n label={label}\n placeholder={placeholder}\n className={className}\n >\n {children}\n </ProfileTextControl>\n );\n}\n\nexport function ClientProfileLastNameControl({\n label,\n placeholder,\n className,\n children,\n}: ClientProfileLastNameControlProps) {\n return (\n <ProfileTextControl\n fieldKey=\"lastName\"\n attr=\"last-name\"\n autoComplete=\"family-name\"\n defaultLabel=\"Last name\"\n label={label}\n placeholder={placeholder}\n className={className}\n >\n {children}\n </ProfileTextControl>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Phone control — the fieldset-bound counterpart of ClientProfilePhoneControl\n// ---------------------------------------------------------------------------\n\n/**\n * Composes `PhoneField` inside a `FieldBinding`, wrapped in a `data-fs-client-profile-fieldset-field=\"phone\"`\n * node — the same styling hook attribute the text controls carry on their own `Field.Root`.\n * `useFieldsetField` runs unconditionally, before `requirement` is even read — there is no suppressed\n * state for this control to second-guess; `requirement` only ever raises the field to required,\n * and left unset it raises nothing.\n */\nexport function ClientProfileFieldsetPhoneControl({\n label,\n placeholder,\n requirement,\n errorInvalid,\n errorRequired,\n className,\n children,\n}: ClientProfileFieldsetPhoneControlProps) {\n const { namespace, saved, readOnly, rules, country } = useClientProfileFieldsetContext();\n const name = useFieldsetField(namespace, 'phone');\n\n const binding = (\n <FieldBinding\n name={name}\n label={label}\n placeholder={placeholder}\n defaultValue={saved.phone}\n rule={rules.phone}\n disabled={readOnly}\n >\n <PhoneField\n country={country}\n requirement={requirement}\n errorInvalid={errorInvalid}\n errorRequired={errorRequired}\n className={className}\n >\n {children}\n </PhoneField>\n </FieldBinding>\n );\n\n // The wrapper carries no className of its own (Decision 1): className lands on PhoneField's own\n // Field.Root, the node that actually stacks label, input and error, so it needs no flex slot here.\n return <div data-fs-client-profile-fieldset-field=\"phone\">{binding}</div>;\n}\n\n// ---------------------------------------------------------------------------\n// Document control — self-gates on documentType, through an inner component\n// ---------------------------------------------------------------------------\n\ntype ClientProfileDocumentControlInnerProps = ClientProfileDocumentControlProps & { documentType: DocumentType };\n\n/**\n * `useFieldsetField` (through `useClientProfileFieldsetContext`'s namespace) lives only in this inner\n * component, mounted only once `ClientProfileDocumentControl` has already decided the document type is\n * supported. Splitting the decision from the registration this way (Decision 8) keeps a control that\n * renders nothing from registering a field name `FormClear` would otherwise dirty and dispatch.\n */\nfunction ClientProfileDocumentControlInner({\n documentType,\n label,\n placeholder,\n errorMessage,\n className,\n children,\n}: ClientProfileDocumentControlInnerProps) {\n const { namespace, saved, readOnly, rules } = useClientProfileFieldsetContext();\n const name = useFieldsetField(namespace, 'document');\n\n return (\n <FieldBinding\n name={name}\n label={label ?? DOCUMENTS[documentType].name}\n placeholder={placeholder}\n defaultValue={saved.document}\n rule={rules.document}\n disabled={readOnly}\n >\n <DocumentField\n documentType={documentType}\n errorMessage={errorMessage}\n className={className}\n data-fs-client-profile-fieldset-field=\"document\"\n >\n {children}\n </DocumentField>\n </FieldBinding>\n );\n}\n\nexport function ClientProfileDocumentControl({\n label,\n placeholder,\n errorMessage,\n className,\n children,\n}: ClientProfileDocumentControlProps) {\n const { documentType } = useClientProfileFieldsetContext();\n if (!isSupportedDocumentType(documentType)) return null;\n\n return (\n <ClientProfileDocumentControlInner\n documentType={documentType}\n label={label}\n placeholder={placeholder}\n errorMessage={errorMessage}\n className={className}\n >\n {children}\n </ClientProfileDocumentControlInner>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client-profile-fieldset.js","names":[],"sources":["../../../src/matter/client-profile-form/client-profile-fieldset.tsx"],"sourcesContent":["'use client';\n\nimport { defineComponent } from '@vtex/fs-core';\nimport {\n type ComponentPropsWithRef,\n createContext,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useFormContext } from 'react-hook-form';\nimport type { DocumentType } from '../../particles/document-field/document-field.js';\nimport { DOCUMENTS, DocumentField, isSupportedDocumentType } from '../../particles/document-field/document-field.js';\nimport type { FieldRule } from '../../particles/field/field.js';\nimport {\n Field,\n FieldBinding,\n FieldsetFieldContext,\n useFieldsetField,\n useFieldsetFieldNames,\n useNoAncestorFieldset,\n} from '../../particles/field/field.js';\nimport { Fieldset } from '../../particles/fieldset/fieldset.js';\nimport { useFormRegistry } from '../../particles/form/form.js';\nimport type { PhoneCountry } from '../../particles/phone-field/phone-field.js';\nimport { PhoneField, toE164 } from '../../particles/phone-field/phone-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 { useEntityDataResolved } from '../../sdk/data/entity-resolution-context.js';\nimport { useReactiveDataSettled } from '../../sdk/data/use-reactive-data-settled.js';\nimport type { ClientProfileUpdateInput } from '../../sdk/identity/operations.js';\nimport { useSession } from '../session/session.js';\n\n/**\n * `ClientProfileFieldset` — the composable, one-Save write of the `ClientProfile` sibling entity\n * (RFC: Composable Entity Forms, `Proposal > Design > The fieldset`). It registers itself with the\n * enclosing `FormRoot` under the `clientProfile` namespace and dispatches\n * `$actions.identity.clientProfileUpdate` for its own submit slot; it renders no `<form>` of its own.\n *\n * The four controls below are the smallest placeable unit: each calls `useFieldsetField` for its full\n * field name, renders a `FieldBinding` carrying the field's static facts, and — inside it — either the\n * default anatomy or the author's own children. No control's field contract carries a blocking\n * `RegisterOptions` key (no `required`, `min`, `max`, `minLength`, `maxLength`, `pattern`); requiredness\n * on the phone control rides on its own `requirement` prop instead, which only ever gates required\n * validation — a field the store does not want is omitted from the page, not switched off by a prop.\n *\n * The fieldset's context name is `client-profile-fieldset`, not `client-profile`: the shipped\n * `ClientProfilePhoneControl` (`matter/client-profile/client-profile.tsx`) already requires\n * `client-profile` and writes on blur, so reusing that name here would let an on-blur control compile\n * inside this batched fieldset and write immediately. `ClientProfileFieldsetPhoneControl` carries the\n * same container prefix as the context it requires, naming the collision rather than hiding it.\n */\n\n// ---------------------------------------------------------------------------\n// GraphQL Fragment — colocated on the fieldset, its own data root\n// ---------------------------------------------------------------------------\n\nconst fragment = /* GraphQL */ `\n fragment ClientProfileFieldsetData on Query {\n clientProfile {\n __typename\n id\n firstName\n lastName\n phone\n document\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** The `$data` slice forwarded by the reactive runtime — the ClientProfile sibling entity, unwrapped. */\nexport type ClientProfileFieldsetData = {\n firstName?: string | null;\n lastName?: string | null;\n phone?: string | null;\n document?: string | null;\n} | null;\n\n/** The four keys this fieldset registers under its namespace. */\ntype ProfileFieldKey = 'firstName' | 'lastName' | 'phone' | 'document';\n\n/** What the fieldset publishes to its controls. Carries no live value — react-hook-form owns those. */\ntype ClientProfileFieldsetContextValue = Readonly<{\n namespace: 'clientProfile';\n /** Seeds, resolved from `$data`. Empty string, never null, so a control never seeds `undefined`. */\n saved: Readonly<Record<ProfileFieldKey, string>>;\n /** True when the session reports `canEditData: false`. */\n readOnly: boolean;\n /** Phone mask/validation country, from the fieldset's own prop. */\n country: PhoneCountry;\n /** Locale document type; the document control renders only when supported. */\n documentType: string | undefined;\n /** The fieldset's own validation rules, per key. Present and empty today (Decision 6). */\n rules: Readonly<Partial<Record<ProfileFieldKey, FieldRule>>>;\n}>;\n\n/** No control publishes a rule today — a blocking `RegisterOptions` key would reopen the same submit\n * block for a read-only session (Decision 6): `readOnly` disables a control's rendered `<input>` via a\n * plain HTML attribute, never react-hook-form's own `disabled` registration option (`field.tsx`'s\n * `useController` call takes no `disabled`), so a required field would stay fully registered and\n * validated while the shopper has no way to focus or edit it. Kept as a stable empty object so the\n * context value's identity does not churn on every render. */\nconst NO_RULES: Readonly<Partial<Record<ProfileFieldKey, FieldRule>>> = {};\n\n/**\n * Rows `ClientProfileFieldsetSkeleton` draws when nobody says otherwise: one per control in the full\n * anatomy — first name, last name, phone, document. It is a default and not a derivation, because\n * the composed children are opaque here. A page that composes fewer sets `skeletonCount` to what it\n * composed, or the form jumps by the difference when the controls take the skeleton's place.\n */\nconst CLIENT_PROFILE_SKELETON_ROWS = 4;\n\nconst CLIENT_PROFILE_FIELDSET_UNSET = Symbol('ClientProfileFieldsetContextUnset');\n\nconst ClientProfileFieldsetContext = createContext<\n ClientProfileFieldsetContextValue | typeof CLIENT_PROFILE_FIELDSET_UNSET\n>(CLIENT_PROFILE_FIELDSET_UNSET);\n\nfunction useClientProfileFieldsetContext(): ClientProfileFieldsetContextValue {\n const ctx = useContext(ClientProfileFieldsetContext);\n if (ctx === CLIENT_PROFILE_FIELDSET_UNSET) {\n throw new Error('ClientProfileFieldset controls must be used inside ClientProfileFieldset.');\n }\n return ctx;\n}\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport type ClientProfileFieldsetProps = {\n /** Phone mask/validation country, provided to the phone control via context. */\n country?: PhoneCountry;\n /** Locale document type; the document control renders only when supported. */\n documentType?: string;\n /** Placeholder rows the loading skeleton draws. Set it to the number of controls composed here. */\n skeletonCount?: number;\n /** Announced politely while the profile resolves. Blank means no announcement. */\n loadingLabel?: string;\n /** Announced politely once the controls mount. Blank means no announcement. */\n loadedLabel?: string;\n /** Accessible name of the `<fieldset>` group. Blank means no name is exposed. */\n groupLabel?: string;\n className?: string;\n children?: ReactNode;\n /** `ClientProfileFieldsetData` slice forwarded by the reactive runtime. */\n $data?: ClientProfileFieldsetData;\n /** Compiler-injected; carries `identity.clientProfileUpdate`. */\n $actions?: ComponentActions;\n};\n\ntype ProfileTextControlProps = { label?: string; placeholder?: string; className?: string; children?: ReactNode };\n\nexport type ClientProfileFirstNameControlProps = ProfileTextControlProps;\nexport type ClientProfileLastNameControlProps = ProfileTextControlProps;\n\nexport type ClientProfileFieldsetPhoneControlProps = {\n label?: string;\n placeholder?: string;\n /**\n * Raises the field to required. The composite owns it; the contract carries no rule (Decision 6),\n * so here the raise is the whole of the resolution. Unset is no opinion, which leaves the phone\n * optional.\n */\n requirement?: 'mandatory' | 'optional';\n errorInvalid?: string;\n errorRequired?: string;\n className?: string;\n /** Given, replaces PhoneField's default Field.Label + PhoneFieldInput + Field.Error anatomy. */\n children?: ReactNode;\n};\n\nexport type ClientProfileDocumentControlProps = {\n label?: string;\n placeholder?: string;\n errorMessage?: string;\n className?: string;\n /** Given, replaces DocumentField's default Field.Label + DocumentFieldInput + Field.Error anatomy. */\n children?: ReactNode;\n};\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const ClientProfileFieldsetDefinition = defineComponent<ClientProfileFieldsetProps>({\n name: 'ClientProfileFieldset',\n importPath: '@vtex/faststore/components',\n component: ClientProfileFieldset,\n fragment,\n fetchStrategy: 'reactive',\n skeleton: ClientProfileFieldsetSkeleton,\n requiresContext: ['form', 'session'],\n providesContext: 'client-profile-fieldset',\n actions: { identity: ['clientProfileUpdate'] },\n editor: { label: 'Client Profile Fieldset', category: 'identity' },\n schema: {\n country: { type: 'string', default: 'US', label: 'Phone country (US or BR)' },\n documentType: { type: 'string', label: 'Document type (e.g. CPF)' },\n skeletonCount: { type: 'number', default: 4, label: 'Skeleton rows' },\n loadingLabel: { type: 'string', default: 'Loading your details', label: 'Loading announcement' },\n loadedLabel: { type: 'string', default: 'Your details are ready', label: 'Loaded announcement' },\n groupLabel: { type: 'string', default: 'Your details', label: 'Accessible group name' },\n },\n guideline:\n 'Registers a batched write of the ClientProfile entity with the enclosing FormRoot; renders one Fieldset.Root and no <form>. Must be inside a FormRoot with a SessionRoot ancestor. Compose ClientProfileFirstNameControl / ClientProfileLastNameControl / ClientProfileFieldsetPhoneControl / ClientProfileDocumentControl as children, in any order. The controls mount only once the profile has resolved, with skeletonCount placeholder rows in their place until then — set it to the number of controls composed, or the form jumps by the difference. Style the announcement sr-only. Dispatches only the fields the shopper changed. groupLabel names the group as its accessible name; blank emits no aria-label, and a composed FieldsetLegend wins over it.',\n});\n\nexport const ClientProfileFirstNameControlDefinition = defineComponent<ClientProfileFirstNameControlProps>({\n name: 'ClientProfileFirstNameControl',\n importPath: '@vtex/faststore/components',\n component: ClientProfileFirstNameControl,\n requiresContext: 'client-profile-fieldset',\n providesContext: 'field',\n editor: { label: 'Client Profile First Name Control', category: 'identity' },\n schema: {\n // No default (Decision 1): absence means the platform's value, applied at render time by\n // the control itself, not baked into the schema.\n label: { type: 'string', label: 'Label' },\n placeholder: { type: 'string', label: 'Placeholder' },\n },\n guideline:\n \"The bound first-name control; must be a descendant of ClientProfileFieldset. Renders Field.Root > Field.Label + Field.Control + Field.Error by default, or the composed children in the author's own order.\",\n});\n\nexport const ClientProfileLastNameControlDefinition = defineComponent<ClientProfileLastNameControlProps>({\n name: 'ClientProfileLastNameControl',\n importPath: '@vtex/faststore/components',\n component: ClientProfileLastNameControl,\n requiresContext: 'client-profile-fieldset',\n providesContext: 'field',\n editor: { label: 'Client Profile Last Name Control', category: 'identity' },\n schema: {\n // No default (Decision 1): absence means the platform's value, applied at render time by\n // the control itself, not baked into the schema.\n label: { type: 'string', label: 'Label' },\n placeholder: { type: 'string', label: 'Placeholder' },\n },\n guideline:\n \"The bound last-name control; must be a descendant of ClientProfileFieldset. Renders Field.Root > Field.Label + Field.Control + Field.Error by default, or the composed children in the author's own order.\",\n});\n\nexport const ClientProfileFieldsetPhoneControlDefinition = defineComponent<ClientProfileFieldsetPhoneControlProps>({\n name: 'ClientProfileFieldsetPhoneControl',\n importPath: '@vtex/faststore/components',\n component: ClientProfileFieldsetPhoneControl,\n requiresContext: 'client-profile-fieldset',\n // Provides both names (mirrors shipping-address.tsx:595's Decision 7): 'field' is what every\n // fieldset Control declares (RFC:503), but this control also composes PhoneField, whose\n // PhoneFieldInput part requires 'phone-field' — a page composing\n // ClientProfileFieldsetPhoneControl > PhoneFieldInput would otherwise fail the build with a\n // context-constraint violation, since PhoneField itself (the only 'phone-field' provider) never\n // appears in the page schema (finding F1).\n providesContext: ['field', 'phone-field'],\n editor: { label: 'Client Profile Fieldset Phone Control', category: 'identity' },\n schema: {\n // No default (Decision 1): absence means the platform's value, applied at render time by\n // the control itself, not baked into the schema.\n label: { type: 'string', label: 'Label' },\n placeholder: { type: 'string', label: 'Placeholder' },\n // No default: an unset requirement is no opinion, not a raise to mandatory.\n requirement: { type: ['mandatory', 'optional'] as const, label: 'Requirement' },\n // No default (Decision 1): absence means the platform's value, applied at render time by\n // the control itself, not baked into the schema.\n errorInvalid: { type: 'string', label: 'Invalid error message' },\n errorRequired: { type: 'string', label: 'Required error message' },\n },\n guideline:\n \"The bound phone control; must be a descendant of ClientProfileFieldset. Distinct from ClientProfilePhoneControl (matter/client-profile), which writes on blur and requires the client-profile context — the two cannot compile into each other's container. Composes the PhoneField particle: recomposable (RFC:503) — given children, they replace PhoneField's default Field.Label + PhoneFieldInput + Field.Error anatomy, in any order; childless keeps today's order. Its className styles PhoneField's own root, the node that lays out label, input and error — not the wrapper, which carries only the data-fs-client-profile-fieldset-field=\\\"phone\\\" styling hook. A store that does not want a phone field omits this element from the page: requirement only governs required validation, never visibility.\",\n});\n\nexport const ClientProfileDocumentControlDefinition = defineComponent<ClientProfileDocumentControlProps>({\n name: 'ClientProfileDocumentControl',\n importPath: '@vtex/faststore/components',\n component: ClientProfileDocumentControl,\n requiresContext: 'client-profile-fieldset',\n // Provides both names, same reasoning as ClientProfileFieldsetPhoneControlDefinition above: this\n // control composes DocumentField, whose DocumentFieldInput part requires 'document-field' — a page\n // composing ClientProfileDocumentControl > DocumentFieldInput would otherwise fail the build\n // (finding F1).\n providesContext: ['field', 'document-field'],\n editor: { label: 'Client Profile Document Control', category: 'identity' },\n schema: {\n // No default (Decision 1): absence means the platform's value, applied at render time by\n // the control itself, not baked into the schema.\n label: { type: 'string', label: 'Label' },\n placeholder: { type: 'string', label: 'Placeholder' },\n errorMessage: { type: 'string', label: 'Error message' },\n },\n guideline:\n \"The bound tax-document control; must be a descendant of ClientProfileFieldset. Renders nothing, and registers no field name, when the fieldset's documentType is unsupported. Composes the DocumentField particle: recomposable (RFC:503) — given children, they replace DocumentField's default Field.Label + DocumentFieldInput + Field.Error anatomy, in any order; childless keeps today's order.\",\n});\n\n// ---------------------------------------------------------------------------\n// Fieldset root\n// ---------------------------------------------------------------------------\n\n/**\n * Registers a batched write of the `ClientProfile` entity with the enclosing `FormRoot`\n * (`registerFieldset`, namespace `clientProfile`) on mount, unconditionally — so this fieldset keeps its\n * original dispatch slot even while its own query is still resolving. Its children — the seed values and\n * the field-name registry its controls need — render only once the entity has resolved; until then the\n * `Fieldset.Root` carries `-pending` and renders `ClientProfileFieldsetSkeleton` (Decision 5).\n * `dispatch` and `toPayload` are `useCallback([])`-stable and read `$actions`/`country`/`documentType`\n * through a ref, so the registration effect runs exactly once per mount regardless of how often the\n * parent re-renders.\n *\n * \"Resolved\" is two conditions, because `$data` alone cannot answer it. Under the reactive runtime the\n * slice is defined from the first render — `useReactiveEntity` hands down `fallbackData` while the cache\n * read is incomplete — so an empty server answer looks exactly like a resolved one, and the controls\n * would mount and seed `''` before the shopper's real profile arrives. `useSeedAdoption` re-seeds a\n * clean field with `defaultValue` on every render, but once the field turns dirty no later seed\n * ever reaches it again: a shopper who starts typing before the real profile arrives locks that\n * empty seed in for good, and even a shopper who does not type sees the field flash from `''` to\n * the real data. `useReactiveDataSettled` supplies the missing half: the page query's own\n * in-flight state, latched so it only ever goes pending → settled. The `$data === undefined` half still\n * carries trees with no reactive root above them, where the slice arrives as a plain prop.\n *\n * Both ends of that wait are spoken: the fieldset carries `aria-busy` while it lasts and holds a polite\n * live region that announces `loadingLabel` and then `loadedLabel`. Style the region `sr-only` — it is\n * text meant for a screen reader, not for the page.\n */\nexport function ClientProfileFieldset({\n country = 'US',\n documentType,\n skeletonCount = CLIENT_PROFILE_SKELETON_ROWS,\n loadingLabel = 'Loading your details',\n loadedLabel = 'Your details are ready',\n groupLabel = 'Your details',\n className,\n children,\n $data: $dataProp,\n $actions: $actionsProp,\n}: ClientProfileFieldsetProps) {\n // Nesting fieldsets has no legitimate use: useFieldsetField resolves the *nearest*\n // FieldsetFieldContext, not the one owning a control's actual namespace, so a control nested two\n // fieldsets deep would silently register under the wrong one and its write would be discarded on\n // submit. Runtime registry integrity, mirroring the namespace-collision throw FormRoot already ships\n // (form.tsx:280-288).\n useNoAncestorFieldset('ClientProfileFieldset');\n\n const $data = useDataOverride($dataProp);\n\n const $actions = useActionsOverride($actionsProp);\n // Three conditions, not two. `useReactiveDataSettled` latches one way and never re-arms, so on its\n // own it answers \"has this page ever resolved\", not \"is what I am holding right now the server's\n // answer\" — after the page query's entity ref moves to one this cache never stored, or its root is\n // skipped for want of a variable, the slice silently regresses to `fallbackData` while the latch\n // still reads settled. `useEntityDataResolved` is the per-entity half: unlatched, so it follows the\n // slice back down and returns this fieldset to its skeleton instead of leaving live controls seeded\n // from a placeholder.\n const settled = useReactiveDataSettled();\n const sliceResolved = useEntityDataResolved();\n const resolved = settled && sliceResolved && $data !== undefined;\n // Also enforces the SessionRoot ancestor: useSession() throws when rendered outside one.\n const { canEditData } = useSession();\n const readOnly = !canEditData;\n\n // Runs before useFormContext() below: useFormRegistry() throws the friendly \"Form parts must be used\n // inside FormRoot.\" error when no FormRoot is above, so a FormRoot-less render fails there rather than\n // reaching the getFieldState destructure, which throws an unhelpful TypeError instead (useFormContext()\n // resolves to null outside a FormRoot, and destructuring null throws).\n const { registerFieldset } = useFormRegistry();\n\n // Decision 3, as later narrowed: this fieldset still reads dirtiness imperatively, but the rule is no\n // longer the form's. `FormRoot` subscribes `dirtyFields` in render and decides which fieldsets to\n // dispatch from it, by the fields each one has mounted, on react-hook-form's documented contract.\n //\n // The imperative read survives here because it asks a different question in a different place: which\n // of *this* fieldset's own fields changed, resolved inside `toPayload` at submit time, by full\n // registered field name — the shape `getFieldState` documents. `useFormContext()` adds no\n // `formState` subscription of its own; one is created only when a caller reads a formState key off\n // the `useForm`/`useFormState` proxy (or hands that already-subscribed object to `getFieldState`'s\n // second argument). Calling `getFieldState(name)` with no second argument reads the current internal\n // state directly, so this fieldset root still does not re-render on a keystroke.\n const { getFieldState } = useFormContext();\n\n const latest = useRef({ $actions, country, documentType, readOnly, getFieldState });\n useEffect(() => {\n latest.current = { $actions, country, documentType, readOnly, getFieldState };\n }, [$actions, country, documentType, readOnly, getFieldState]);\n\n const { registry: fieldsetRegistry, getFieldNames } = useFieldsetFieldNames('clientProfile');\n\n const dispatch = useCallback(async (payload: Record<string, unknown>) => {\n // Defence-in-depth: the UI disables every input while read-only, but `FormClear` sets values (and\n // marks them dirty) on every registered field regardless of that disabled attribute — react-hook-\n // form's own `useController` calls never receive a `disabled` registration option, so it has no\n // notion of \"disabled\" of its own. Guarding here keeps the write from reaching the network even if\n // some other composition dirties this fieldset while canEditData is false (the server also refuses,\n // but a client-side guard should not depend on that).\n if (latest.current.readOnly) {\n throw new Error('ClientProfileFieldset cannot dispatch while the session is read-only.');\n }\n const update = latest.current.$actions?.identity?.clientProfileUpdate;\n // Throwing is the contract's failure path: FormRoot catches it and records this fieldset's failure\n // (form.tsx:264-266). Returning [] would report an unqualified success for a write that never\n // happened.\n if (!update) throw new Error('ClientProfileFieldset requires $actions.identity.clientProfileUpdate.');\n return update(payload as ClientProfileUpdateInput);\n }, []);\n\n const toPayload = useCallback((values: Record<string, unknown>): Record<string, unknown> => {\n const { country: activeCountry, documentType: activeDocumentType, getFieldState } = latest.current;\n // FR-5: a key is emitted only when the shopper actually changed it — presence in `values` alone\n // (every registered field is always present) is not enough. `dirtyFields` is not read directly;\n // `getFieldState` is called with no second argument, which reads react-hook-form's current\n // internal state without subscribing to it (Decision 3).\n const isDirty = (key: ProfileFieldKey) => getFieldState(`clientProfile.${key}`).isDirty;\n const payload: Record<string, string> = {};\n if ('firstName' in values && isDirty('firstName')) payload.firstName = String(values.firstName ?? '').trim();\n if ('lastName' in values && isDirty('lastName')) payload.lastName = String(values.lastName ?? '').trim();\n if ('phone' in values && isDirty('phone')) {\n const raw = String(values.phone ?? '').trim();\n // toE164 on a blank value returns the bare country code, which would write \"+55\" over the\n // shopper's number and fail every later save.\n payload.phone = raw ? toE164(raw, activeCountry) : '';\n }\n // The type's own sanitiser (digits for CPF, SSN and SIN, letters kept for RFC). Omitted when the type is\n // not supported: a value for a control that is no longer shown is never sent.\n if ('document' in values && isDirty('document') && isSupportedDocumentType(activeDocumentType)) {\n // The control only ever writes a string (the masked input's own value); `values` is typed `unknown`\n // because the fieldset contract carries every field's value, so it is narrowed here, not stringified.\n const rawDocument = typeof values.document === 'string' ? values.document : '';\n payload.document = DOCUMENTS[activeDocumentType].sanitize(rawDocument);\n }\n return payload;\n }, []);\n\n useEffect(\n () =>\n registerFieldset({\n namespace: 'clientProfile',\n getFieldNames,\n dispatch,\n toPayload,\n // Decision 1: the register is a machine identifier, the kebab-case ASCII rendering of this\n // registration's own `namespace` (`clientProfile` → `client-profile`) — not authored per\n // page (Decision 2).\n label: 'client-profile',\n }),\n [registerFieldset, getFieldNames, dispatch, toPayload],\n );\n\n const saved = useMemo<Readonly<Record<ProfileFieldKey, string>>>(\n () => ({\n firstName: $data?.firstName ?? '',\n lastName: $data?.lastName ?? '',\n phone: $data?.phone ?? '',\n document: $data?.document ?? '',\n }),\n [$data],\n );\n\n const contextValue = useMemo<ClientProfileFieldsetContextValue>(\n () => ({ namespace: 'clientProfile', saved, readOnly, country, documentType, rules: NO_RULES }),\n [saved, readOnly, country, documentType],\n );\n\n // Assistive technology only reports a change inside a region that was already in the tree, so a\n // region mounted together with its text announces nothing. The region therefore sits in both\n // branches and starts empty, with the wait written on the pass after mount and replaced — not\n // cleared — once the controls are up: the skeleton is `aria-hidden`, so without this second\n // message the form would materialise in silence (WCAG 2.1 SC 4.1.3).\n const [announcement, setAnnouncement] = useState('');\n const announced = resolved ? loadedLabel : loadingLabel;\n useEffect(() => {\n setAnnouncement(announced);\n }, [announced]);\n\n return (\n <Fieldset.Root\n className={className}\n disabled={readOnly}\n aria-busy={resolved ? undefined : true}\n // FR-12: blank (or whitespace-only) means no name is exposed, mirroring the loadingLabel/\n // loadedLabel convention. On the root itself (FR-13), so it is identical whether the fieldset\n // is pending or resolved; a page-composed FieldsetLegend still wins via aria-labelledby.\n {...(groupLabel?.trim() ? { 'aria-label': groupLabel } : {})}\n data-fs-client-profile-fieldset=\"\"\n {...(resolved ? {} : { 'data-fs-client-profile-fieldset-pending': '' })}\n {...(readOnly ? { 'data-fs-client-profile-fieldset-readonly': '' } : {})}\n >\n {resolved ? (\n <FieldsetFieldContext.Provider value={fieldsetRegistry}>\n <ClientProfileFieldsetContext.Provider value={contextValue}>{children}</ClientProfileFieldsetContext.Provider>\n </FieldsetFieldContext.Provider>\n ) : (\n <ClientProfileFieldsetSkeleton count={skeletonCount} />\n )}\n <output aria-live=\"polite\" data-fs-client-profile-fieldset-announcement=\"\">\n {announcement}\n </output>\n </Fieldset.Root>\n );\n}\n\n/**\n * The loading state of the fieldset, rendered in place of the controls until the entity resolves and\n * declared as the definition's `skeleton`. Purely visual: the wait is spoken by the fieldset's own live\n * region, so this is hidden from assistive technology rather than announced twice.\n */\nexport function ClientProfileFieldsetSkeleton({\n count = CLIENT_PROFILE_SKELETON_ROWS,\n className,\n ...props\n}: ComponentPropsWithRef<'div'> & { count?: number }) {\n return (\n <div {...props} className={className} data-fs-client-profile-fieldset-skeleton=\"\" aria-hidden=\"true\">\n {Array.from({ length: count }, (_, i) => (\n <span key={i} data-fs-client-profile-fieldset-skeleton-field=\"\" />\n ))}\n </div>\n );\n}\n\nClientProfileFieldset.Skeleton = ClientProfileFieldsetSkeleton;\n\n// ---------------------------------------------------------------------------\n// Text controls — first name, last name\n// ---------------------------------------------------------------------------\n\nfunction ProfileTextControl({\n fieldKey,\n attr,\n autoComplete,\n defaultLabel,\n label,\n placeholder,\n className,\n children,\n}: {\n fieldKey: 'firstName' | 'lastName';\n attr: string;\n autoComplete: string;\n defaultLabel: string;\n label?: string;\n placeholder?: string;\n className?: string;\n children?: ReactNode;\n}) {\n const { namespace, saved, readOnly, rules } = useClientProfileFieldsetContext();\n const name = useFieldsetField(namespace, fieldKey);\n\n return (\n <FieldBinding\n name={name}\n label={label ?? defaultLabel}\n placeholder={placeholder}\n defaultValue={saved[fieldKey]}\n rule={rules[fieldKey]}\n disabled={readOnly}\n autoComplete={autoComplete}\n >\n <Field.Root className={className} data-fs-client-profile-fieldset-field={attr}>\n {children ?? (\n <>\n <Field.Label />\n <Field.Control />\n <Field.Error />\n </>\n )}\n </Field.Root>\n </FieldBinding>\n );\n}\n\nexport function ClientProfileFirstNameControl({\n label,\n placeholder,\n className,\n children,\n}: ClientProfileFirstNameControlProps) {\n return (\n <ProfileTextControl\n fieldKey=\"firstName\"\n attr=\"first-name\"\n autoComplete=\"given-name\"\n defaultLabel=\"First name\"\n label={label}\n placeholder={placeholder}\n className={className}\n >\n {children}\n </ProfileTextControl>\n );\n}\n\nexport function ClientProfileLastNameControl({\n label,\n placeholder,\n className,\n children,\n}: ClientProfileLastNameControlProps) {\n return (\n <ProfileTextControl\n fieldKey=\"lastName\"\n attr=\"last-name\"\n autoComplete=\"family-name\"\n defaultLabel=\"Last name\"\n label={label}\n placeholder={placeholder}\n className={className}\n >\n {children}\n </ProfileTextControl>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Phone control — the fieldset-bound counterpart of ClientProfilePhoneControl\n// ---------------------------------------------------------------------------\n\n/**\n * Composes `PhoneField` inside a `FieldBinding`, wrapped in a `data-fs-client-profile-fieldset-field=\"phone\"`\n * node — the same styling hook attribute the text controls carry on their own `Field.Root`.\n * `useFieldsetField` runs unconditionally, before `requirement` is even read — there is no suppressed\n * state for this control to second-guess; `requirement` only ever raises the field to required,\n * and left unset it raises nothing.\n */\nexport function ClientProfileFieldsetPhoneControl({\n label,\n placeholder,\n requirement,\n errorInvalid,\n errorRequired,\n className,\n children,\n}: ClientProfileFieldsetPhoneControlProps) {\n const { namespace, saved, readOnly, rules, country } = useClientProfileFieldsetContext();\n const name = useFieldsetField(namespace, 'phone');\n\n const binding = (\n <FieldBinding\n name={name}\n label={label}\n placeholder={placeholder}\n defaultValue={saved.phone}\n rule={rules.phone}\n disabled={readOnly}\n >\n <PhoneField\n country={country}\n requirement={requirement}\n errorInvalid={errorInvalid}\n errorRequired={errorRequired}\n className={className}\n >\n {children}\n </PhoneField>\n </FieldBinding>\n );\n\n // The wrapper carries no className of its own (Decision 1): className lands on PhoneField's own\n // Field.Root, the node that actually stacks label, input and error, so it needs no flex slot here.\n return <div data-fs-client-profile-fieldset-field=\"phone\">{binding}</div>;\n}\n\n// ---------------------------------------------------------------------------\n// Document control — self-gates on documentType, through an inner component\n// ---------------------------------------------------------------------------\n\ntype ClientProfileDocumentControlInnerProps = ClientProfileDocumentControlProps & { documentType: DocumentType };\n\n/**\n * `useFieldsetField` (through `useClientProfileFieldsetContext`'s namespace) lives only in this inner\n * component, mounted only once `ClientProfileDocumentControl` has already decided the document type is\n * supported. Splitting the decision from the registration this way (Decision 8) keeps a control that\n * renders nothing from registering a field name `FormClear` would otherwise dirty and dispatch.\n */\nfunction ClientProfileDocumentControlInner({\n documentType,\n label,\n placeholder,\n errorMessage,\n className,\n children,\n}: ClientProfileDocumentControlInnerProps) {\n const { namespace, saved, readOnly, rules } = useClientProfileFieldsetContext();\n const name = useFieldsetField(namespace, 'document');\n\n return (\n <FieldBinding\n name={name}\n label={label ?? DOCUMENTS[documentType].name}\n placeholder={placeholder}\n defaultValue={saved.document}\n rule={rules.document}\n disabled={readOnly}\n >\n <DocumentField\n documentType={documentType}\n errorMessage={errorMessage}\n className={className}\n data-fs-client-profile-fieldset-field=\"document\"\n >\n {children}\n </DocumentField>\n </FieldBinding>\n );\n}\n\nexport function ClientProfileDocumentControl({\n label,\n placeholder,\n errorMessage,\n className,\n children,\n}: ClientProfileDocumentControlProps) {\n const { documentType } = useClientProfileFieldsetContext();\n if (!isSupportedDocumentType(documentType)) return null;\n\n return (\n <ClientProfileDocumentControlInner\n documentType={documentType}\n label={label}\n placeholder={placeholder}\n errorMessage={errorMessage}\n className={className}\n >\n {children}\n </ClientProfileDocumentControlInner>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DA,MAAM,WAAyB;;;;;;;;;;;;;;;;;;AAiD/B,MAAM,WAAkE,CAAC;;;;;;;AAQzE,MAAM,+BAA+B;AAErC,MAAM,gCAAgC,OAAO,mCAAmC;AAEhF,MAAM,+BAA+B,cAEnC,6BAA6B;AAE/B,SAAS,kCAAqE;CAC5E,MAAM,MAAM,WAAW,4BAA4B;CACnD,IAAI,QAAQ,+BACV,MAAM,IAAI,MAAM,2EAA2E;CAE7F,OAAO;AACT;AA6DA,MAAa,kCAAkC,gBAA4C;CACzF,MAAM;CACN,YAAY;CACZ,WAAW;CACX;CACA,eAAe;CACf,UAAU;CACV,iBAAiB,CAAC,QAAQ,SAAS;CACnC,iBAAiB;CACjB,SAAS,EAAE,UAAU,CAAC,qBAAqB,EAAE;CAC7C,QAAQ;EAAE,OAAO;EAA2B,UAAU;CAAW;CACjE,QAAQ;EACN,SAAS;GAAE,MAAM;GAAU,SAAS;GAAM,OAAO;EAA2B;EAC5E,cAAc;GAAE,MAAM;GAAU,OAAO;EAA2B;EAClE,eAAe;GAAE,MAAM;GAAU,SAAS;GAAG,OAAO;EAAgB;EACpE,cAAc;GAAE,MAAM;GAAU,SAAS;GAAwB,OAAO;EAAuB;EAC/F,aAAa;GAAE,MAAM;GAAU,SAAS;GAA0B,OAAO;EAAsB;EAC/F,YAAY;GAAE,MAAM;GAAU,SAAS;GAAgB,OAAO;EAAwB;CACxF;CACA,WACE;AACJ,CAAC;AAED,MAAa,0CAA0C,gBAAoD;CACzG,MAAM;CACN,YAAY;CACZ,WAAW;CACX,iBAAiB;CACjB,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAAqC,UAAU;CAAW;CAC3E,QAAQ;EAGN,OAAO;GAAE,MAAM;GAAU,OAAO;EAAQ;EACxC,aAAa;GAAE,MAAM;GAAU,OAAO;EAAc;CACtD;CACA,WACE;AACJ,CAAC;AAED,MAAa,yCAAyC,gBAAmD;CACvG,MAAM;CACN,YAAY;CACZ,WAAW;CACX,iBAAiB;CACjB,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAAoC,UAAU;CAAW;CAC1E,QAAQ;EAGN,OAAO;GAAE,MAAM;GAAU,OAAO;EAAQ;EACxC,aAAa;GAAE,MAAM;GAAU,OAAO;EAAc;CACtD;CACA,WACE;AACJ,CAAC;AAED,MAAa,8CAA8C,gBAAwD;CACjH,MAAM;CACN,YAAY;CACZ,WAAW;CACX,iBAAiB;CAOjB,iBAAiB,CAAC,SAAS,aAAa;CACxC,QAAQ;EAAE,OAAO;EAAyC,UAAU;CAAW;CAC/E,QAAQ;EAGN,OAAO;GAAE,MAAM;GAAU,OAAO;EAAQ;EACxC,aAAa;GAAE,MAAM;GAAU,OAAO;EAAc;EAEpD,aAAa;GAAE,MAAM,CAAC,aAAa,UAAU;GAAY,OAAO;EAAc;EAG9E,cAAc;GAAE,MAAM;GAAU,OAAO;EAAwB;EAC/D,eAAe;GAAE,MAAM;GAAU,OAAO;EAAyB;CACnE;CACA,WACE;AACJ,CAAC;AAED,MAAa,yCAAyC,gBAAmD;CACvG,MAAM;CACN,YAAY;CACZ,WAAW;CACX,iBAAiB;CAKjB,iBAAiB,CAAC,SAAS,gBAAgB;CAC3C,QAAQ;EAAE,OAAO;EAAmC,UAAU;CAAW;CACzE,QAAQ;EAGN,OAAO;GAAE,MAAM;GAAU,OAAO;EAAQ;EACxC,aAAa;GAAE,MAAM;GAAU,OAAO;EAAc;EACpD,cAAc;GAAE,MAAM;GAAU,OAAO;EAAgB;CACzD;CACA,WACE;AACJ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BD,SAAgB,sBAAsB,EACpC,UAAU,MACV,cACA,gBAAgB,8BAChB,eAAe,wBACf,cAAc,0BACd,aAAa,gBACb,WACA,UACA,OAAO,WACP,UAAU,gBACmB;CAM7B,sBAAsB,uBAAuB;CAE7C,MAAM,QAAQ,gBAAgB,SAAS;CAEvC,MAAM,WAAW,mBAAmB,YAAY;CAQhD,MAAM,UAAU,uBAAuB;CACvC,MAAM,gBAAgB,sBAAsB;CAC5C,MAAM,WAAW,WAAW,iBAAiB,UAAU,KAAA;CAEvD,MAAM,EAAE,gBAAgB,WAAW;CACnC,MAAM,WAAW,CAAC;CAMlB,MAAM,EAAE,qBAAqB,gBAAgB;CAa7C,MAAM,EAAE,kBAAkB,eAAe;CAEzC,MAAM,SAAS,OAAO;EAAE;EAAU;EAAS;EAAc;EAAU;CAAc,CAAC;CAClF,gBAAgB;EACd,OAAO,UAAU;GAAE;GAAU;GAAS;GAAc;GAAU;EAAc;CAC9E,GAAG;EAAC;EAAU;EAAS;EAAc;EAAU;CAAa,CAAC;CAE7D,MAAM,EAAE,UAAU,kBAAkB,kBAAkB,sBAAsB,eAAe;CAE3F,MAAM,WAAW,YAAY,OAAO,YAAqC;EAOvE,IAAI,OAAO,QAAQ,UACjB,MAAM,IAAI,MAAM,uEAAuE;EAEzF,MAAM,SAAS,OAAO,QAAQ,UAAU,UAAU;EAIlD,IAAI,CAAC,QAAQ,MAAM,IAAI,MAAM,uEAAuE;EACpG,OAAO,OAAO,OAAmC;CACnD,GAAG,CAAC,CAAC;CAEL,MAAM,YAAY,aAAa,WAA6D;EAC1F,MAAM,EAAE,SAAS,eAAe,cAAc,oBAAoB,kBAAkB,OAAO;EAK3F,MAAM,WAAW,QAAyB,cAAc,iBAAiB,KAAK,CAAC,CAAC;EAChF,MAAM,UAAkC,CAAC;EACzC,IAAI,eAAe,UAAU,QAAQ,WAAW,GAAG,QAAQ,YAAY,OAAO,OAAO,aAAa,EAAE,CAAC,CAAC,KAAK;EAC3G,IAAI,cAAc,UAAU,QAAQ,UAAU,GAAG,QAAQ,WAAW,OAAO,OAAO,YAAY,EAAE,CAAC,CAAC,KAAK;EACvG,IAAI,WAAW,UAAU,QAAQ,OAAO,GAAG;GACzC,MAAM,MAAM,OAAO,OAAO,SAAS,EAAE,CAAC,CAAC,KAAK;GAG5C,QAAQ,QAAQ,MAAM,OAAO,KAAK,aAAa,IAAI;EACrD;EAGA,IAAI,cAAc,UAAU,QAAQ,UAAU,KAAK,wBAAwB,kBAAkB,GAAG;GAG9F,MAAM,cAAc,OAAO,OAAO,aAAa,WAAW,OAAO,WAAW;GAC5E,QAAQ,WAAW,UAAU,mBAAmB,CAAC,SAAS,WAAW;EACvE;EACA,OAAO;CACT,GAAG,CAAC,CAAC;CAEL,gBAEI,iBAAiB;EACf,WAAW;EACX;EACA;EACA;EAIA,OAAO;CACT,CAAC,GACH;EAAC;EAAkB;EAAe;EAAU;CAAS,CACvD;CAEA,MAAM,QAAQ,eACL;EACL,WAAW,OAAO,aAAa;EAC/B,UAAU,OAAO,YAAY;EAC7B,OAAO,OAAO,SAAS;EACvB,UAAU,OAAO,YAAY;CAC/B,IACA,CAAC,KAAK,CACR;CAEA,MAAM,eAAe,eACZ;EAAE,WAAW;EAAiB;EAAO;EAAU;EAAS;EAAc,OAAO;CAAS,IAC7F;EAAC;EAAO;EAAU;EAAS;CAAY,CACzC;CAOA,MAAM,CAAC,cAAc,mBAAmB,SAAS,EAAE;CACnD,MAAM,YAAY,WAAW,cAAc;CAC3C,gBAAgB;EACd,gBAAgB,SAAS;CAC3B,GAAG,CAAC,SAAS,CAAC;CAEd,OACE,qBAAC,SAAS,MAAV;EACa;EACX,UAAU;EACV,aAAW,WAAW,KAAA,IAAY;EAIlC,GAAK,YAAY,KAAK,IAAI,EAAE,cAAc,WAAW,IAAI,CAAC;EAC1D,mCAAgC;EAChC,GAAK,WAAW,CAAC,IAAI,EAAE,2CAA2C,GAAG;EACrE,GAAK,WAAW,EAAE,4CAA4C,GAAG,IAAI,CAAC;EAVxE,UAAA,CAYG,WACC,oBAAC,qBAAqB,UAAtB;GAA+B,OAAO;GACpC,UAAA,oBAAC,6BAA6B,UAA9B;IAAuC,OAAO;IAAe;GAAgD,CAAA;EAChF,CAAA,IAE/B,oBAAC,+BAAD,EAA+B,OAAO,cAAgB,CAAA,GAExD,oBAAC,UAAD;GAAQ,aAAU;GAAS,gDAA6C;GACrE,UAAA;EACK,CAAA,CACK;;AAEnB;;;;;;AAOA,SAAgB,8BAA8B,EAC5C,QAAQ,8BACR,WACA,GAAG,SACiD;CACpD,OACE,oBAAC,OAAD;EAAK,GAAI;EAAkB;EAAW,4CAAyC;EAAG,eAAY;EAC3F,UAAA,MAAM,KAAK,EAAE,QAAQ,MAAM,IAAI,GAAG,MACjC,oBAAC,QAAD,EAAc,kDAA+C,GAAI,GAAtD,CAAsD,CAClE;CACE,CAAA;AAET;AAEA,sBAAsB,WAAW;AAMjC,SAAS,mBAAmB,EAC1B,UACA,MACA,cACA,cACA,OACA,aACA,WACA,YAUC;CACD,MAAM,EAAE,WAAW,OAAO,UAAU,UAAU,gCAAgC;CAC9E,MAAM,OAAO,iBAAiB,WAAW,QAAQ;CAEjD,OACE,oBAAC,cAAD;EACQ;EACN,OAAO,SAAS;EACH;EACb,cAAc,MAAM;EACpB,MAAM,MAAM;EACZ,UAAU;EACI;EAEd,UAAA,oBAAC,MAAM,MAAP;GAAuB;GAAW,yCAAuC;GACtE,UAAA,YACC,qBAAA,YAAA,EAAA,UAAA;IACE,oBAAC,MAAM,OAAP,CAAc,CAAA;IACd,oBAAC,MAAM,SAAP,CAAgB,CAAA;IAChB,oBAAC,MAAM,OAAP,CAAc,CAAA;GACd,EAAA,CAAA;EAEM,CAAA;CACA,CAAA;AAElB;AAEA,SAAgB,8BAA8B,EAC5C,OACA,aACA,WACA,YACqC;CACrC,OACE,oBAAC,oBAAD;EACE,UAAS;EACT,MAAK;EACL,cAAa;EACb,cAAa;EACN;EACM;EACF;EAEV;CACiB,CAAA;AAExB;AAEA,SAAgB,6BAA6B,EAC3C,OACA,aACA,WACA,YACoC;CACpC,OACE,oBAAC,oBAAD;EACE,UAAS;EACT,MAAK;EACL,cAAa;EACb,cAAa;EACN;EACM;EACF;EAEV;CACiB,CAAA;AAExB;;;;;;;;AAaA,SAAgB,kCAAkC,EAChD,OACA,aACA,aACA,cACA,eACA,WACA,YACyC;CACzC,MAAM,EAAE,WAAW,OAAO,UAAU,OAAO,YAAY,gCAAgC;CACvF,MAAM,OAAO,iBAAiB,WAAW,OAAO;CAEhD,MAAM,UACJ,oBAAC,cAAD;EACQ;EACC;EACM;EACb,cAAc,MAAM;EACpB,MAAM,MAAM;EACZ,UAAU;EAEV,UAAA,oBAAC,YAAD;GACW;GACI;GACC;GACC;GACJ;GAEV;EACS,CAAA;CACA,CAAA;CAKhB,OAAO,oBAAC,OAAD;EAAK,yCAAsC;EAAS,UAAA;CAAa,CAAA;AAC1E;;;;;;;AAcA,SAAS,kCAAkC,EACzC,cACA,OACA,aACA,cACA,WACA,YACyC;CACzC,MAAM,EAAE,WAAW,OAAO,UAAU,UAAU,gCAAgC;CAC9E,MAAM,OAAO,iBAAiB,WAAW,UAAU;CAEnD,OACE,oBAAC,cAAD;EACQ;EACN,OAAO,SAAS,UAAU,aAAa,CAAC;EAC3B;EACb,cAAc,MAAM;EACpB,MAAM,MAAM;EACZ,UAAU;EAEV,UAAA,oBAAC,eAAD;GACgB;GACA;GACH;GACX,yCAAsC;GAErC;EACY,CAAA;CACH,CAAA;AAElB;AAEA,SAAgB,6BAA6B,EAC3C,OACA,aACA,cACA,WACA,YACoC;CACpC,MAAM,EAAE,iBAAiB,gCAAgC;CACzD,IAAI,CAAC,wBAAwB,YAAY,GAAG,OAAO;CAEnD,OACE,oBAAC,mCAAD;EACgB;EACP;EACM;EACC;EACH;EAEV;CACgC,CAAA;AAEvC"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { defineComponent } from "../../core/dist/define-component.js";
|
|
3
3
|
import "../../core/dist/index.js";
|
|
4
4
|
import { createContext, useContext, useMemo, useState } from "react";
|
|
5
|
-
import { Fragment, jsx } from "react/jsx-runtime";
|
|
5
|
+
import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
|
|
6
6
|
//#region src/matter/identity-edit/identity-edit.tsx
|
|
7
7
|
const IdentityEditContext = createContext(void 0);
|
|
8
8
|
function useIdentityEditContext() {
|
|
@@ -100,13 +100,13 @@ function IdentityEditController({ children }) {
|
|
|
100
100
|
function IdentitySummaryView({ children }) {
|
|
101
101
|
const { mode } = useIdentityEditContext();
|
|
102
102
|
if (mode !== "view") return null;
|
|
103
|
-
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
103
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children });
|
|
104
104
|
}
|
|
105
105
|
/** Renders its children only while the section shows the form. */
|
|
106
106
|
function IdentityEditForm({ children }) {
|
|
107
107
|
const { mode } = useIdentityEditContext();
|
|
108
108
|
if (mode !== "edit") return null;
|
|
109
|
-
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
109
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children });
|
|
110
110
|
}
|
|
111
111
|
/** Opens the form. Renders `children` or the `label` fallback. */
|
|
112
112
|
function IdentityEditButton({ label = "editar", className, children }) {
|