@shopify/hydrogen 2026.10.0-preview.4 → 2026.10.0
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/dist/cli/index.mjs +23 -12
- package/dist/client/client.d.mts +18 -15
- package/dist/client/client.mjs +8 -3
- package/dist/client/client.mjs.map +1 -1
- package/dist/client/errors.d.mts +35 -13
- package/dist/client/errors.mjs +23 -13
- package/dist/client/errors.mjs.map +1 -1
- package/dist/client/types.d.mts +140 -90
- package/dist/core/analytics/cart-tracker.d.mts +19 -10
- package/dist/core/analytics/cart-tracker.mjs +18 -10
- package/dist/core/analytics/cart-tracker.mjs.map +1 -1
- package/dist/core/analytics/events.d.mts +17 -11
- package/dist/core/analytics/events.mjs +13 -4
- package/dist/core/analytics/events.mjs.map +1 -1
- package/dist/core/analytics/types.d.mts +207 -118
- package/dist/core/analytics/utils/flatten-connection.d.mts +11 -9
- package/dist/core/analytics/utils/flatten-connection.mjs +7 -6
- package/dist/core/analytics/utils/flatten-connection.mjs.map +1 -1
- package/dist/core/cache/fetch-with-cache.d.mts +24 -4
- package/dist/core/cache/fetch-with-cache.mjs +17 -2
- package/dist/core/cache/fetch-with-cache.mjs.map +1 -1
- package/dist/core/cache/key.d.mts +2 -0
- package/dist/core/cache/key.mjs.map +1 -1
- package/dist/core/cache/run-with-cache.d.mts +56 -8
- package/dist/core/cache/run-with-cache.mjs +17 -6
- package/dist/core/cache/run-with-cache.mjs.map +1 -1
- package/dist/core/cache/store.d.mts +8 -6
- package/dist/core/cache/store.mjs.map +1 -1
- package/dist/core/cache/strategies.d.mts +27 -2
- package/dist/core/cache/strategies.mjs +6 -0
- package/dist/core/cache/strategies.mjs.map +1 -1
- package/dist/core/cart/actions.d.mts +37 -30
- package/dist/core/cart/actions.mjs +21 -9
- package/dist/core/cart/actions.mjs.map +1 -1
- package/dist/core/cart/attach-quantity-input.d.mts +8 -7
- package/dist/core/cart/attach-quantity-input.mjs +8 -7
- package/dist/core/cart/attach-quantity-input.mjs.map +1 -1
- package/dist/core/cart/buyer-identity-sync.d.mts +7 -6
- package/dist/core/cart/buyer-identity-sync.mjs.map +1 -1
- package/dist/core/cart/cart.d.mts +59 -46
- package/dist/core/cart/cart.mjs +46 -29
- package/dist/core/cart/cart.mjs.map +1 -1
- package/dist/core/cart/cookie.d.mts +8 -4
- package/dist/core/cart/cookie.mjs +8 -4
- package/dist/core/cart/cookie.mjs.map +1 -1
- package/dist/core/cart/form.d.mts +34 -20
- package/dist/core/cart/form.mjs +6 -4
- package/dist/core/cart/form.mjs.map +1 -1
- package/dist/core/cart/get-cart.d.mts +27 -15
- package/dist/core/cart/get-cart.mjs +13 -8
- package/dist/core/cart/get-cart.mjs.map +1 -1
- package/dist/core/cart/quantity.d.mts +16 -5
- package/dist/core/cart/quantity.mjs +16 -5
- package/dist/core/cart/quantity.mjs.map +1 -1
- package/dist/core/cart/queries.d.mts +25 -19
- package/dist/core/cart/queries.mjs +15 -1
- package/dist/core/cart/queries.mjs.map +1 -1
- package/dist/core/cart/server-handlers.d.mts +82 -45
- package/dist/core/cart/server-handlers.mjs +4 -0
- package/dist/core/cart/server-handlers.mjs.map +1 -1
- package/dist/core/cart/state.d.mts +110 -64
- package/dist/core/cart/state.mjs +24 -6
- package/dist/core/cart/state.mjs.map +1 -1
- package/dist/core/collection/collection.d.mts +52 -53
- package/dist/core/collection/collection.mjs +5 -3
- package/dist/core/collection/collection.mjs.map +1 -1
- package/dist/core/collection/reconciler.d.mts +23 -16
- package/dist/core/collection/reconciler.mjs +8 -4
- package/dist/core/collection/reconciler.mjs.map +1 -1
- package/dist/core/collection/state.d.mts +31 -26
- package/dist/core/collection/state.mjs +4 -3
- package/dist/core/collection/state.mjs.map +1 -1
- package/dist/core/collection/url.d.mts +70 -29
- package/dist/core/collection/url.mjs +63 -25
- package/dist/core/collection/url.mjs.map +1 -1
- package/dist/core/constants.d.mts +1 -0
- package/dist/core/constants.mjs +1 -0
- package/dist/core/constants.mjs.map +1 -1
- package/dist/core/logging/logging.d.mts +14 -8
- package/dist/core/logging/logging.mjs +15 -10
- package/dist/core/logging/logging.mjs.map +1 -1
- package/dist/core/logging/types.d.mts +28 -11
- package/dist/core/logging/types.mjs.map +1 -1
- package/dist/core/money/format.d.mts +6 -4
- package/dist/core/money/format.mjs +19 -9
- package/dist/core/money/format.mjs.map +1 -1
- package/dist/core/money/types.d.mts +41 -25
- package/dist/core/predictive-search/form.d.mts +37 -7
- package/dist/core/predictive-search/form.mjs +21 -3
- package/dist/core/predictive-search/form.mjs.map +1 -1
- package/dist/core/predictive-search/queries.d.mts +18 -30
- package/dist/core/predictive-search/queries.mjs +7 -3
- package/dist/core/predictive-search/queries.mjs.map +1 -1
- package/dist/core/predictive-search/search.d.mts +25 -27
- package/dist/core/predictive-search/search.mjs +20 -19
- package/dist/core/predictive-search/search.mjs.map +1 -1
- package/dist/core/predictive-search/server-handlers.d.mts +25 -26
- package/dist/core/predictive-search/server-handlers.mjs +4 -0
- package/dist/core/predictive-search/server-handlers.mjs.map +1 -1
- package/dist/core/predictive-search/store.d.mts +65 -24
- package/dist/core/predictive-search/store.mjs +12 -1
- package/dist/core/predictive-search/store.mjs.map +1 -1
- package/dist/core/predictive-search/url.d.mts +36 -37
- package/dist/core/predictive-search/url.mjs +14 -6
- package/dist/core/predictive-search/url.mjs.map +1 -1
- package/dist/core/product/form.d.mts +63 -16
- package/dist/core/product/form.mjs +8 -2
- package/dist/core/product/form.mjs.map +1 -1
- package/dist/core/product/options.d.mts +8 -10
- package/dist/core/product/options.mjs +8 -10
- package/dist/core/product/options.mjs.map +1 -1
- package/dist/core/product/product-form.d.mts +77 -29
- package/dist/core/product/product-form.mjs +32 -10
- package/dist/core/product/product-form.mjs.map +1 -1
- package/dist/core/product/state.d.mts +55 -32
- package/dist/core/product/url.d.mts +19 -13
- package/dist/core/product/url.mjs +10 -10
- package/dist/core/product/url.mjs.map +1 -1
- package/dist/core/request-context.d.mts +77 -19
- package/dist/core/request-context.mjs +5 -1
- package/dist/core/request-context.mjs.map +1 -1
- package/dist/core/request-routing/handle-shopify-redirects.d.mts +12 -4
- package/dist/core/request-routing/handle-shopify-redirects.mjs +9 -2
- package/dist/core/request-routing/handle-shopify-redirects.mjs.map +1 -1
- package/dist/core/request-routing/handle-shopify-routes.d.mts +5 -5
- package/dist/core/request-routing/handle-shopify-routes.mjs +7 -5
- package/dist/core/request-routing/handle-shopify-routes.mjs.map +1 -1
- package/dist/core/request-routing/interceptors/proxy.mjs +79 -43
- package/dist/core/request-routing/interceptors/proxy.mjs.map +1 -1
- package/dist/core/request-routing/interceptors/ucp-profile-proxy.mjs +69 -0
- package/dist/core/request-routing/interceptors/ucp-profile-proxy.mjs.map +1 -0
- package/dist/core/request-routing/interceptors/url-redirects.mjs +6 -1
- package/dist/core/request-routing/interceptors/url-redirects.mjs.map +1 -1
- package/dist/core/request-routing/interceptors/well-known.mjs +1 -1
- package/dist/core/request-routing/interceptors/well-known.mjs.map +1 -1
- package/dist/core/request-routing/registered-routes.d.mts +12 -0
- package/dist/core/request-routing/registered-routes.mjs +12 -0
- package/dist/core/request-routing/registered-routes.mjs.map +1 -1
- package/dist/core/request-routing/route-types.d.mts +43 -18
- package/dist/core/shop-pay/shop-pay.d.mts +71 -35
- package/dist/core/shop-pay/shop-pay.mjs +36 -10
- package/dist/core/shop-pay/shop-pay.mjs.map +1 -1
- package/dist/core/shopify-scripts/index.d.mts +15 -5
- package/dist/core/shopify-scripts/index.mjs +15 -5
- package/dist/core/shopify-scripts/index.mjs.map +1 -1
- package/dist/core/shopify-scripts/initialize.d.mts +12 -4
- package/dist/core/shopify-scripts/initialize.mjs +12 -4
- package/dist/core/shopify-scripts/initialize.mjs.map +1 -1
- package/dist/core/shopify-scripts/types.d.mts +58 -25
- package/dist/core/standard-routes/build.d.mts +11 -12
- package/dist/core/standard-routes/build.mjs +11 -12
- package/dist/core/standard-routes/build.mjs.map +1 -1
- package/dist/core/standard-routes/types.d.mts +30 -24
- package/dist/core/url.mjs +2 -1
- package/dist/core/url.mjs.map +1 -1
- package/dist/customer-account/client.d.mts +36 -69
- package/dist/customer-account/client.mjs +7 -8
- package/dist/customer-account/client.mjs.map +1 -1
- package/dist/customer-account/errors.d.mts +45 -36
- package/dist/customer-account/errors.mjs +21 -36
- package/dist/customer-account/errors.mjs.map +1 -1
- package/dist/customer-account/graphql.d.mts +11 -23
- package/dist/customer-account/graphql.mjs +3 -9
- package/dist/customer-account/graphql.mjs.map +1 -1
- package/dist/customer-account/session.d.mts +130 -100
- package/dist/customer-account/session.mjs +32 -10
- package/dist/customer-account/session.mjs.map +1 -1
- package/dist/customer-account/type-resolver.d.mts +7 -8
- package/dist/development/client/client.d.mts +18 -15
- package/dist/development/client/client.mjs +8 -3
- package/dist/development/client/client.mjs.map +1 -1
- package/dist/development/client/errors.d.mts +35 -13
- package/dist/development/client/errors.mjs +23 -13
- package/dist/development/client/errors.mjs.map +1 -1
- package/dist/development/client/types.d.mts +140 -90
- package/dist/development/core/analytics/cart-tracker.d.mts +19 -10
- package/dist/development/core/analytics/cart-tracker.mjs +18 -10
- package/dist/development/core/analytics/cart-tracker.mjs.map +1 -1
- package/dist/development/core/analytics/events.d.mts +17 -11
- package/dist/development/core/analytics/events.mjs +13 -4
- package/dist/development/core/analytics/events.mjs.map +1 -1
- package/dist/development/core/analytics/types.d.mts +207 -118
- package/dist/development/core/analytics/utils/flatten-connection.d.mts +11 -9
- package/dist/development/core/analytics/utils/flatten-connection.mjs +7 -6
- package/dist/development/core/analytics/utils/flatten-connection.mjs.map +1 -1
- package/dist/development/core/cache/fetch-with-cache.d.mts +24 -4
- package/dist/development/core/cache/fetch-with-cache.mjs +17 -2
- package/dist/development/core/cache/fetch-with-cache.mjs.map +1 -1
- package/dist/development/core/cache/key.d.mts +2 -0
- package/dist/development/core/cache/key.mjs.map +1 -1
- package/dist/development/core/cache/run-with-cache.d.mts +56 -8
- package/dist/development/core/cache/run-with-cache.mjs +17 -6
- package/dist/development/core/cache/run-with-cache.mjs.map +1 -1
- package/dist/development/core/cache/store.d.mts +8 -6
- package/dist/development/core/cache/store.mjs.map +1 -1
- package/dist/development/core/cache/strategies.d.mts +27 -2
- package/dist/development/core/cache/strategies.mjs +6 -0
- package/dist/development/core/cache/strategies.mjs.map +1 -1
- package/dist/development/core/cart/actions.d.mts +37 -30
- package/dist/development/core/cart/actions.mjs +21 -9
- package/dist/development/core/cart/actions.mjs.map +1 -1
- package/dist/development/core/cart/attach-quantity-input.d.mts +8 -7
- package/dist/development/core/cart/attach-quantity-input.mjs +8 -7
- package/dist/development/core/cart/attach-quantity-input.mjs.map +1 -1
- package/dist/development/core/cart/buyer-identity-sync.d.mts +6 -5
- package/dist/development/core/cart/buyer-identity-sync.mjs.map +1 -1
- package/dist/development/core/cart/cart.d.mts +59 -46
- package/dist/development/core/cart/cart.mjs +46 -29
- package/dist/development/core/cart/cart.mjs.map +1 -1
- package/dist/development/core/cart/cookie.d.mts +8 -4
- package/dist/development/core/cart/cookie.mjs +8 -4
- package/dist/development/core/cart/cookie.mjs.map +1 -1
- package/dist/development/core/cart/form.d.mts +34 -20
- package/dist/development/core/cart/form.mjs +6 -4
- package/dist/development/core/cart/form.mjs.map +1 -1
- package/dist/development/core/cart/get-cart.d.mts +27 -15
- package/dist/development/core/cart/get-cart.mjs +13 -8
- package/dist/development/core/cart/get-cart.mjs.map +1 -1
- package/dist/development/core/cart/quantity.d.mts +16 -5
- package/dist/development/core/cart/quantity.mjs +16 -5
- package/dist/development/core/cart/quantity.mjs.map +1 -1
- package/dist/development/core/cart/queries.d.mts +25 -19
- package/dist/development/core/cart/queries.mjs +15 -1
- package/dist/development/core/cart/queries.mjs.map +1 -1
- package/dist/development/core/cart/server-handlers.d.mts +82 -45
- package/dist/development/core/cart/server-handlers.mjs +4 -0
- package/dist/development/core/cart/server-handlers.mjs.map +1 -1
- package/dist/development/core/cart/state.d.mts +110 -64
- package/dist/development/core/cart/state.mjs +24 -6
- package/dist/development/core/cart/state.mjs.map +1 -1
- package/dist/development/core/collection/collection.d.mts +52 -53
- package/dist/development/core/collection/collection.mjs +5 -3
- package/dist/development/core/collection/collection.mjs.map +1 -1
- package/dist/development/core/collection/reconciler.d.mts +23 -16
- package/dist/development/core/collection/reconciler.mjs +8 -4
- package/dist/development/core/collection/reconciler.mjs.map +1 -1
- package/dist/development/core/collection/state.d.mts +31 -26
- package/dist/development/core/collection/state.mjs +4 -3
- package/dist/development/core/collection/state.mjs.map +1 -1
- package/dist/development/core/collection/url.d.mts +70 -29
- package/dist/development/core/collection/url.mjs +63 -25
- package/dist/development/core/collection/url.mjs.map +1 -1
- package/dist/development/core/constants.mjs.map +1 -1
- package/dist/development/core/logging/logging.d.mts +14 -8
- package/dist/development/core/logging/logging.mjs +15 -10
- package/dist/development/core/logging/logging.mjs.map +1 -1
- package/dist/development/core/logging/types.d.mts +28 -11
- package/dist/development/core/logging/types.mjs.map +1 -1
- package/dist/development/core/money/format.d.mts +6 -4
- package/dist/development/core/money/format.mjs +19 -9
- package/dist/development/core/money/format.mjs.map +1 -1
- package/dist/development/core/money/types.d.mts +41 -25
- package/dist/development/core/predictive-search/form.d.mts +37 -7
- package/dist/development/core/predictive-search/form.mjs +21 -3
- package/dist/development/core/predictive-search/form.mjs.map +1 -1
- package/dist/development/core/predictive-search/queries.d.mts +18 -30
- package/dist/development/core/predictive-search/queries.mjs +7 -3
- package/dist/development/core/predictive-search/queries.mjs.map +1 -1
- package/dist/development/core/predictive-search/search.d.mts +25 -27
- package/dist/development/core/predictive-search/search.mjs +20 -19
- package/dist/development/core/predictive-search/search.mjs.map +1 -1
- package/dist/development/core/predictive-search/server-handlers.d.mts +25 -26
- package/dist/development/core/predictive-search/server-handlers.mjs +4 -0
- package/dist/development/core/predictive-search/server-handlers.mjs.map +1 -1
- package/dist/development/core/predictive-search/store.d.mts +65 -24
- package/dist/development/core/predictive-search/store.mjs +12 -1
- package/dist/development/core/predictive-search/store.mjs.map +1 -1
- package/dist/development/core/predictive-search/url.d.mts +36 -37
- package/dist/development/core/predictive-search/url.mjs +14 -6
- package/dist/development/core/predictive-search/url.mjs.map +1 -1
- package/dist/development/core/product/form.d.mts +63 -16
- package/dist/development/core/product/form.mjs +8 -2
- package/dist/development/core/product/form.mjs.map +1 -1
- package/dist/development/core/product/options.d.mts +8 -10
- package/dist/development/core/product/options.mjs +8 -10
- package/dist/development/core/product/options.mjs.map +1 -1
- package/dist/development/core/product/product-form.d.mts +77 -29
- package/dist/development/core/product/product-form.mjs +32 -10
- package/dist/development/core/product/product-form.mjs.map +1 -1
- package/dist/development/core/product/state.d.mts +55 -32
- package/dist/development/core/product/url.d.mts +19 -13
- package/dist/development/core/product/url.mjs +10 -10
- package/dist/development/core/product/url.mjs.map +1 -1
- package/dist/development/core/request-context.d.mts +77 -19
- package/dist/development/core/request-context.mjs +5 -1
- package/dist/development/core/request-context.mjs.map +1 -1
- package/dist/development/core/request-routing/handle-shopify-redirects.d.mts +12 -4
- package/dist/development/core/request-routing/handle-shopify-redirects.mjs +9 -2
- package/dist/development/core/request-routing/handle-shopify-redirects.mjs.map +1 -1
- package/dist/development/core/request-routing/handle-shopify-routes.development.d.mts +9 -1
- package/dist/development/core/request-routing/handle-shopify-routes.development.mjs +7 -0
- package/dist/development/core/request-routing/handle-shopify-routes.development.mjs.map +1 -1
- package/dist/development/core/request-routing/handle-shopify-routes.mjs +7 -5
- package/dist/development/core/request-routing/handle-shopify-routes.mjs.map +1 -1
- package/dist/development/core/request-routing/interceptors/proxy.mjs +79 -43
- package/dist/development/core/request-routing/interceptors/proxy.mjs.map +1 -1
- package/dist/development/core/request-routing/interceptors/ucp-profile-proxy.mjs +69 -0
- package/dist/development/core/request-routing/interceptors/ucp-profile-proxy.mjs.map +1 -0
- package/dist/development/core/request-routing/interceptors/url-redirects.mjs +6 -1
- package/dist/development/core/request-routing/interceptors/url-redirects.mjs.map +1 -1
- package/dist/development/core/request-routing/interceptors/well-known.mjs +1 -1
- package/dist/development/core/request-routing/interceptors/well-known.mjs.map +1 -1
- package/dist/development/core/request-routing/registered-routes.d.mts +12 -0
- package/dist/development/core/request-routing/registered-routes.mjs +12 -0
- package/dist/development/core/request-routing/registered-routes.mjs.map +1 -1
- package/dist/development/core/request-routing/route-types.d.mts +43 -18
- package/dist/development/core/shop-pay/shop-pay.d.mts +71 -35
- package/dist/development/core/shop-pay/shop-pay.mjs +36 -10
- package/dist/development/core/shop-pay/shop-pay.mjs.map +1 -1
- package/dist/development/core/shopify-scripts/index.d.mts +15 -5
- package/dist/development/core/shopify-scripts/index.mjs +15 -5
- package/dist/development/core/shopify-scripts/index.mjs.map +1 -1
- package/dist/development/core/shopify-scripts/initialize.d.mts +12 -4
- package/dist/development/core/shopify-scripts/initialize.mjs +12 -4
- package/dist/development/core/shopify-scripts/initialize.mjs.map +1 -1
- package/dist/development/core/shopify-scripts/types.d.mts +58 -25
- package/dist/development/core/standard-routes/build.d.mts +11 -12
- package/dist/development/core/standard-routes/build.mjs +11 -12
- package/dist/development/core/standard-routes/build.mjs.map +1 -1
- package/dist/development/core/standard-routes/types.d.mts +30 -24
- package/dist/development/core/url.mjs +2 -1
- package/dist/development/core/url.mjs.map +1 -1
- package/dist/development/customer-account/session.d.mts +45 -54
- package/dist/development/customer-account/session.mjs +20 -4
- package/dist/development/customer-account/session.mjs.map +1 -1
- package/dist/development/globals.d.mts +20 -11
- package/dist/development/graphql/generated/storefront-api-types.d.mts +127 -1
- package/dist/development/graphql/graphql.d.mts +17 -16
- package/dist/development/graphql/graphql.mjs +7 -8
- package/dist/development/graphql/graphql.mjs.map +1 -1
- package/dist/development/graphql/type-resolver.d.mts +15 -8
- package/dist/development/react/cart.d.mts +78 -32
- package/dist/development/react/cart.mjs +56 -29
- package/dist/development/react/cart.mjs.map +1 -1
- package/dist/development/react/collection.d.mts +40 -22
- package/dist/development/react/collection.mjs +27 -7
- package/dist/development/react/collection.mjs.map +1 -1
- package/dist/development/react/predictive-search.d.mts +65 -50
- package/dist/development/react/predictive-search.mjs +23 -21
- package/dist/development/react/predictive-search.mjs.map +1 -1
- package/dist/development/react/product.d.mts +45 -16
- package/dist/development/react/product.mjs +13 -11
- package/dist/development/react/product.mjs.map +1 -1
- package/dist/development/react/shop-pay.d.mts +15 -0
- package/dist/development/react/shop-pay.mjs +9 -0
- package/dist/development/react/shop-pay.mjs.map +1 -1
- package/dist/development/react/shopify-scripts.d.mts +23 -2
- package/dist/development/react/shopify-scripts.mjs +9 -0
- package/dist/development/react/shopify-scripts.mjs.map +1 -1
- package/dist/development/vue/cart.d.mts +28 -23
- package/dist/development/vue/cart.mjs +42 -25
- package/dist/development/vue/cart.mjs.map +1 -1
- package/dist/development/vue/collection.d.mts +20 -12
- package/dist/development/vue/collection.mjs +15 -7
- package/dist/development/vue/collection.mjs.map +1 -1
- package/dist/development/vue/index.d.mts +2 -2
- package/dist/development/vue/index.mjs +2 -2
- package/dist/development/vue/predictive-search.d.mts +31 -36
- package/dist/development/vue/predictive-search.mjs +16 -22
- package/dist/development/vue/predictive-search.mjs.map +1 -1
- package/dist/development/vue/product.d.mts +16 -15
- package/dist/development/vue/product.mjs +11 -11
- package/dist/development/vue/product.mjs.map +1 -1
- package/dist/development/vue/shop-pay.d.mts +14 -17
- package/dist/development/vue/shop-pay.mjs +8 -0
- package/dist/development/vue/shop-pay.mjs.map +1 -1
- package/dist/development/vue/shopify-scripts.d.mts +11 -0
- package/dist/development/vue/shopify-scripts.mjs +6 -0
- package/dist/development/vue/shopify-scripts.mjs.map +1 -1
- package/dist/globals.d.mts +20 -11
- package/dist/graphql/generated/storefront-api-types.d.mts +127 -1
- package/dist/graphql/graphql.d.mts +17 -16
- package/dist/graphql/graphql.mjs +7 -8
- package/dist/graphql/graphql.mjs.map +1 -1
- package/dist/graphql/type-resolver.d.mts +15 -8
- package/dist/react/cart.d.mts +78 -32
- package/dist/react/cart.mjs +56 -29
- package/dist/react/cart.mjs.map +1 -1
- package/dist/react/collection.d.mts +40 -22
- package/dist/react/collection.mjs +27 -7
- package/dist/react/collection.mjs.map +1 -1
- package/dist/react/predictive-search.d.mts +65 -50
- package/dist/react/predictive-search.mjs +23 -21
- package/dist/react/predictive-search.mjs.map +1 -1
- package/dist/react/product.d.mts +45 -16
- package/dist/react/product.mjs +13 -11
- package/dist/react/product.mjs.map +1 -1
- package/dist/react/shop-pay.d.mts +15 -0
- package/dist/react/shop-pay.mjs +9 -0
- package/dist/react/shop-pay.mjs.map +1 -1
- package/dist/react/shopify-scripts.d.mts +23 -2
- package/dist/react/shopify-scripts.mjs +9 -0
- package/dist/react/shopify-scripts.mjs.map +1 -1
- package/dist/ts-plugin/index.cjs +6 -0
- package/dist/ts-plugin/index.cjs.map +1 -1
- package/dist/ts-plugin/index.d.cts +6 -0
- package/dist/vite/local-https.d.mts +51 -17
- package/dist/vite/local-https.mjs +26 -5
- package/dist/vite/local-https.mjs.map +1 -1
- package/dist/vue/cart.d.mts +28 -23
- package/dist/vue/cart.mjs +42 -25
- package/dist/vue/cart.mjs.map +1 -1
- package/dist/vue/collection.d.mts +20 -12
- package/dist/vue/collection.mjs +15 -7
- package/dist/vue/collection.mjs.map +1 -1
- package/dist/vue/index.d.mts +2 -2
- package/dist/vue/index.mjs +2 -2
- package/dist/vue/predictive-search.d.mts +31 -36
- package/dist/vue/predictive-search.mjs +16 -22
- package/dist/vue/predictive-search.mjs.map +1 -1
- package/dist/vue/product.d.mts +16 -15
- package/dist/vue/product.mjs +11 -11
- package/dist/vue/product.mjs.map +1 -1
- package/dist/vue/shop-pay.d.mts +14 -17
- package/dist/vue/shop-pay.mjs +8 -0
- package/dist/vue/shop-pay.mjs.map +1 -1
- package/dist/vue/shopify-scripts.d.mts +11 -0
- package/dist/vue/shopify-scripts.mjs +6 -0
- package/dist/vue/shopify-scripts.mjs.map +1 -1
- package/package.json +3 -1
- package/skills/hydrogen-analytics/SKILL.md +1 -1
- package/skills/hydrogen-cart-ui/SKILL.md +28 -2
- package/skills/hydrogen-collection-browser/SKILL.md +9 -1
- package/skills/hydrogen-collection-browser/references/nextjs.md +1 -1
- package/skills/hydrogen-collection-browser/references/nuxt.md +1 -1
- package/skills/hydrogen-collection-browser/references/react.md +151 -32
- package/skills/hydrogen-customer-account/SKILL.md +2 -0
- package/skills/hydrogen-request-handlers/SKILL.md +21 -11
- package/skills/hydrogen-setup/references/analytics.md +14 -8
- package/skills/hydrogen-setup/steps/1-inspect-app.md +2 -2
- package/skills/hydrogen-setup/steps/6-product-detail-page.md +21 -11
- package/skills/hydrogen-storefront-client/SKILL.md +1 -1
- package/skills/hydrogen-storefront-client/references/caching.md +2 -0
- package/skills/hydrogen-variant-form/SKILL.md +25 -7
- package/skills/hydrogen-variant-form/references/nextjs.md +39 -4
- package/skills/hydrogen-variant-form/references/nuxt.md +83 -42
- package/skills/hydrogen-variant-form/references/react.md +45 -9
|
@@ -10,47 +10,69 @@ type PredictiveSearchContextValue = {
|
|
|
10
10
|
actions: PredictiveSearchActions;
|
|
11
11
|
searchAction?: string;
|
|
12
12
|
};
|
|
13
|
-
/** Props for the {@link PredictiveSearchProvider} component. */
|
|
14
|
-
type PredictiveSearchProviderProps = CreatePredictiveSearchStoreOptions & {
|
|
15
|
-
children?: ReactNode; /** Form action URL for progressive enhancement. Used by {@link usePredictiveSearchForm} to set the form's `action` attribute so the search works without JavaScript. Falls back to `"/search"` when omitted. */
|
|
16
|
-
searchAction?: string;
|
|
17
|
-
};
|
|
18
13
|
/**
|
|
19
|
-
*
|
|
14
|
+
* Store options, the search page path, and the content that uses the predictive search hooks.
|
|
20
15
|
*
|
|
21
|
-
*
|
|
22
|
-
* replaced by a version that provides the extracted search term.
|
|
16
|
+
* Changing a store option clears the current term and results. The provider compares `types` and `searchableFields` by value, which lets you pass new arrays on each render.
|
|
23
17
|
*/
|
|
24
|
-
|
|
25
|
-
/**
|
|
18
|
+
interface PredictiveSearchProviderProps extends CreatePredictiveSearchStoreOptions {
|
|
19
|
+
/** Content that uses the predictive search hooks. */
|
|
20
|
+
children?: ReactNode;
|
|
21
|
+
/** Search page path that forms from `usePredictiveSearchForm` submit to. Defaults to `/search`. */
|
|
22
|
+
searchAction?: string;
|
|
23
|
+
}
|
|
24
|
+
/** Submit behavior for the search form. */
|
|
25
|
+
interface PredictiveSearchFormSubmitOptions {
|
|
26
|
+
/** Pass `true` to run the search in the browser on submit and stay on the current page. Defaults to `false`. */
|
|
27
|
+
preventDefault?: boolean;
|
|
28
|
+
/** Runs on submit with the event and the search term. Call `event.preventDefault()` to cancel both the page navigation and the browser search. */
|
|
26
29
|
onSubmit?: (event: SubmitEvent<HTMLFormElement>, term: string) => void;
|
|
27
|
-
}
|
|
30
|
+
}
|
|
28
31
|
/**
|
|
29
|
-
*
|
|
30
|
-
* {@link usePredictiveSearchForm}'s `register` method.
|
|
32
|
+
* Form attributes and submit behavior for the search form.
|
|
31
33
|
*
|
|
32
|
-
*
|
|
33
|
-
* form registration.
|
|
34
|
+
* Any standard form attribute works. Attributes that you pass override the default action, method, and role.
|
|
34
35
|
*/
|
|
35
|
-
type
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
type PredictiveSearchFormPropsOptions = Omit<FormHTMLAttributes<HTMLFormElement>, "onSubmit"> & PredictiveSearchFormSubmitOptions;
|
|
37
|
+
/**
|
|
38
|
+
* Input attributes and a change callback for the search input.
|
|
39
|
+
*
|
|
40
|
+
* Any standard input attribute works except the name, type, autocomplete, autocapitalize, and spell check attributes, which the register function sets.
|
|
41
|
+
*/
|
|
42
|
+
type PredictiveSearchQueryInputPropsOptions = Omit<InputHTMLAttributes<HTMLInputElement>, "autoCapitalize" | "autoComplete" | "name" | "onChange" | "spellCheck" | "type"> & PredictiveSearchQueryInputChangeOptions;
|
|
43
|
+
/** Change behavior for the search input. */
|
|
44
|
+
interface PredictiveSearchQueryInputChangeOptions {
|
|
45
|
+
/** Runs on every change with the event and the input value. Call `event.preventDefault()` to skip the search for that change. */
|
|
46
|
+
onChange?: (event: ChangeEvent<HTMLInputElement>, term: string) => void;
|
|
47
|
+
}
|
|
38
48
|
type PredictiveSearchFormField = Parameters<PredictiveSearchFormRegister$1>[0];
|
|
39
|
-
/**
|
|
49
|
+
/** Returns the search input attributes, merged with the attributes that you pass. */
|
|
40
50
|
type PredictiveSearchFormRegister = (field: PredictiveSearchFormField, options?: PredictiveSearchQueryInputPropsOptions) => InputHTMLAttributes<HTMLInputElement>;
|
|
41
|
-
/**
|
|
51
|
+
/** Functions that return attributes for a predictive search form and its search input. */
|
|
42
52
|
type PredictiveSearchFormResult = {
|
|
43
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* Returns the attributes for the search form.
|
|
55
|
+
*
|
|
56
|
+
* By default, submitting the form opens the search page at the provider's search action.
|
|
57
|
+
* Pass `preventDefault: true` to run the search in the browser and stay on the current page.
|
|
58
|
+
*/
|
|
59
|
+
formProps(options?: PredictiveSearchFormPropsOptions): FormHTMLAttributes<HTMLFormElement>;
|
|
60
|
+
/**
|
|
61
|
+
* Returns the attributes for the search input, which searches as the customer types.
|
|
62
|
+
*
|
|
63
|
+
* Pass `query`. Any other field name throws an error.
|
|
64
|
+
*/
|
|
44
65
|
register: PredictiveSearchFormRegister;
|
|
45
66
|
};
|
|
46
67
|
/**
|
|
47
|
-
*
|
|
48
|
-
* hooks via React context.
|
|
68
|
+
* Runs predictive search for the components inside the provider. Wrap your search UI in the provider. Inside the provider, `usePredictiveSearch` reads the results, `usePredictiveSearchActions` searches and clears, and `usePredictiveSearchForm` builds the search form.
|
|
49
69
|
*
|
|
50
|
-
*
|
|
51
|
-
* on mount and destroys it on unmount.
|
|
70
|
+
* The provider throws an error when you omit the `fetch` prop in a runtime without a global `fetch`.
|
|
52
71
|
*
|
|
53
|
-
* @
|
|
72
|
+
* @param props - The store options, the search page path, and the content that uses the predictive search hooks.
|
|
73
|
+
* @returns The provider element that wraps your search UI.
|
|
74
|
+
* @throws {Error} When neither the fetch prop nor a global fetch function exists.
|
|
75
|
+
* @publicDocs
|
|
54
76
|
*/
|
|
55
77
|
declare function PredictiveSearchProvider({
|
|
56
78
|
children,
|
|
@@ -66,43 +88,36 @@ declare function PredictiveSearchProvider({
|
|
|
66
88
|
unavailableProducts
|
|
67
89
|
}: PredictiveSearchProviderProps): _$react.FunctionComponentElement<_$react.ProviderProps<PredictiveSearchContextValue | null>>;
|
|
68
90
|
/**
|
|
69
|
-
*
|
|
91
|
+
* Returns the search term, status, results, and error, and re-renders the component on each change.
|
|
70
92
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* selected value changes (reference equality by default, or a custom
|
|
74
|
-
* `isEqual`).
|
|
93
|
+
* Pass a selector to read one value, such as the status, and re-render only when that value changes. The hook compares selected values by reference unless you pass an equality function.
|
|
94
|
+
* The hook throws an error outside a `PredictiveSearchProvider`.
|
|
75
95
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* @throws {Error} When called outside a PredictiveSearchProvider.
|
|
96
|
+
* @throws {Error} When you call the hook outside a PredictiveSearchProvider.
|
|
97
|
+
* @publicDocs
|
|
79
98
|
*/
|
|
80
99
|
declare function usePredictiveSearch<TData extends PredictiveSearchData = PredictiveSearchData>(): PredictiveSearchState<TData>;
|
|
81
100
|
declare function usePredictiveSearch<TData extends PredictiveSearchData = PredictiveSearchData, S = PredictiveSearchState<TData>>(selector: (state: PredictiveSearchState<TData>) => S, isEqual?: (a: S, b: S) => boolean): S;
|
|
82
101
|
/**
|
|
83
|
-
* Returns
|
|
84
|
-
* current store. Their identity is stable across re-renders, even
|
|
85
|
-
* when a config prop change recreates the store.
|
|
102
|
+
* Returns methods that search for a term and clear the results of the provider's predictive search. Use the hook in components that start searches without reading results.
|
|
86
103
|
*
|
|
87
|
-
*
|
|
104
|
+
* The methods keep the same identity across re-renders and keep working after a store option changes.
|
|
105
|
+
* The hook throws an error outside a `PredictiveSearchProvider`.
|
|
88
106
|
*
|
|
89
|
-
* @
|
|
107
|
+
* @returns Methods that search for a term and clear the results.
|
|
108
|
+
* @throws {Error} When you call the hook outside a PredictiveSearchProvider.
|
|
109
|
+
* @publicDocs
|
|
90
110
|
*/
|
|
91
111
|
declare function usePredictiveSearchActions(): PredictiveSearchActions;
|
|
92
112
|
/**
|
|
93
|
-
* Returns
|
|
94
|
-
* search form.
|
|
95
|
-
*
|
|
96
|
-
* `formProps()` generates form element attributes including the search
|
|
97
|
-
* action URL. `register("query")` generates input attributes and calls the
|
|
98
|
-
* store's search action on every change event. The store debounces requests
|
|
99
|
-
* and resets state for terms shorter than `minTermLength`.
|
|
100
|
-
* Both support an optional callback that receives the event and extracted
|
|
101
|
-
* term, and respect `event.preventDefault()` to cancel the automatic behavior.
|
|
113
|
+
* Returns functions that connect a search form and its search input to the provider's predictive search. The form submits to the search page without JavaScript, and the search input searches as the customer types.
|
|
102
114
|
*
|
|
103
|
-
*
|
|
115
|
+
* Spread `formProps()` on the form and `register("query")` on the input. In the `onSubmit` and `onChange` callbacks, call `event.preventDefault()` to skip the automatic submission or search.
|
|
116
|
+
* The hook throws an error outside a `PredictiveSearchProvider`.
|
|
104
117
|
*
|
|
105
|
-
* @
|
|
118
|
+
* @returns Functions that return the search form attributes and the search input attributes.
|
|
119
|
+
* @throws {Error} When you call the hook outside a PredictiveSearchProvider.
|
|
120
|
+
* @publicDocs
|
|
106
121
|
*/
|
|
107
122
|
declare function usePredictiveSearchForm(): PredictiveSearchFormResult;
|
|
108
123
|
//#endregion
|
|
@@ -8,13 +8,14 @@ import { createContext, createElement, useCallback, useContext, useEffect, useIn
|
|
|
8
8
|
const CONFIG_ARRAY_SEPARATOR = "\0";
|
|
9
9
|
const PredictiveSearchContext = createContext(null);
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
* hooks via React context.
|
|
11
|
+
* Runs predictive search for the components inside the provider. Wrap your search UI in the provider. Inside the provider, `usePredictiveSearch` reads the results, `usePredictiveSearchActions` searches and clears, and `usePredictiveSearchForm` builds the search form.
|
|
13
12
|
*
|
|
14
|
-
*
|
|
15
|
-
* on mount and destroys it on unmount.
|
|
13
|
+
* The provider throws an error when you omit the `fetch` prop in a runtime without a global `fetch`.
|
|
16
14
|
*
|
|
17
|
-
* @
|
|
15
|
+
* @param props - The store options, the search page path, and the content that uses the predictive search hooks.
|
|
16
|
+
* @returns The provider element that wraps your search UI.
|
|
17
|
+
* @throws {Error} When neither the fetch prop nor a global fetch function exists.
|
|
18
|
+
* @publicDocs
|
|
18
19
|
*/
|
|
19
20
|
function PredictiveSearchProvider({ children, predictiveSearchEndpoint, searchAction, debounceInMs, minTermLength, fetch, limit, limitScope, types, searchableFields, unavailableProducts }) {
|
|
20
21
|
const store = useMemo(() => createPredictiveSearchStore({
|
|
@@ -74,6 +75,11 @@ function useRequiredContext(hookName) {
|
|
|
74
75
|
if (!context) throw new Error(`${hookName} must be used inside a <PredictiveSearchProvider>.`);
|
|
75
76
|
return context;
|
|
76
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* @param selector - Function that picks the value to return from the search state.
|
|
80
|
+
* @param isEqual - Function that compares two selected values. Return `true` to skip the re-render.
|
|
81
|
+
* @returns The search state, or the value that your selector picks.
|
|
82
|
+
*/
|
|
77
83
|
function usePredictiveSearch(selector, isEqual) {
|
|
78
84
|
const store = useRequiredStore("usePredictiveSearch");
|
|
79
85
|
const cachedRef = useRef(null);
|
|
@@ -107,31 +113,27 @@ function usePredictiveSearch(selector, isEqual) {
|
|
|
107
113
|
return useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
|
|
108
114
|
}
|
|
109
115
|
/**
|
|
110
|
-
* Returns
|
|
111
|
-
* current store. Their identity is stable across re-renders, even
|
|
112
|
-
* when a config prop change recreates the store.
|
|
116
|
+
* Returns methods that search for a term and clear the results of the provider's predictive search. Use the hook in components that start searches without reading results.
|
|
113
117
|
*
|
|
114
|
-
*
|
|
118
|
+
* The methods keep the same identity across re-renders and keep working after a store option changes.
|
|
119
|
+
* The hook throws an error outside a `PredictiveSearchProvider`.
|
|
115
120
|
*
|
|
116
|
-
* @
|
|
121
|
+
* @returns Methods that search for a term and clear the results.
|
|
122
|
+
* @throws {Error} When you call the hook outside a PredictiveSearchProvider.
|
|
123
|
+
* @publicDocs
|
|
117
124
|
*/
|
|
118
125
|
function usePredictiveSearchActions() {
|
|
119
126
|
return useRequiredContext("usePredictiveSearchActions").actions;
|
|
120
127
|
}
|
|
121
128
|
/**
|
|
122
|
-
* Returns
|
|
123
|
-
* search form.
|
|
124
|
-
*
|
|
125
|
-
* `formProps()` generates form element attributes including the search
|
|
126
|
-
* action URL. `register("query")` generates input attributes and calls the
|
|
127
|
-
* store's search action on every change event. The store debounces requests
|
|
128
|
-
* and resets state for terms shorter than `minTermLength`.
|
|
129
|
-
* Both support an optional callback that receives the event and extracted
|
|
130
|
-
* term, and respect `event.preventDefault()` to cancel the automatic behavior.
|
|
129
|
+
* Returns functions that connect a search form and its search input to the provider's predictive search. The form submits to the search page without JavaScript, and the search input searches as the customer types.
|
|
131
130
|
*
|
|
132
|
-
*
|
|
131
|
+
* Spread `formProps()` on the form and `register("query")` on the input. In the `onSubmit` and `onChange` callbacks, call `event.preventDefault()` to skip the automatic submission or search.
|
|
132
|
+
* The hook throws an error outside a `PredictiveSearchProvider`.
|
|
133
133
|
*
|
|
134
|
-
* @
|
|
134
|
+
* @returns Functions that return the search form attributes and the search input attributes.
|
|
135
|
+
* @throws {Error} When you call the hook outside a PredictiveSearchProvider.
|
|
136
|
+
* @publicDocs
|
|
135
137
|
*/
|
|
136
138
|
function usePredictiveSearchForm() {
|
|
137
139
|
const { searchAction, actions } = useRequiredContext("usePredictiveSearchForm");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"predictive-search.mjs","names":[],"sources":["../../src/react/predictive-search.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n createElement,\n useCallback,\n useContext,\n useEffect,\n useInsertionEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n type ChangeEvent,\n type FormHTMLAttributes,\n type InputHTMLAttributes,\n type ReactNode,\n type SubmitEvent,\n} from \"react\";\n\nimport {\n createPredictiveSearchFormRegister,\n createPredictiveSearchStore,\n getPredictiveSearchFormAttributes,\n readPredictiveSearchFormTerm,\n type CreatePredictiveSearchStoreOptions,\n type PredictiveSearchActions,\n type PredictiveSearchFormRegister as CorePredictiveSearchFormRegister,\n type PredictiveSearchData,\n type PredictiveSearchState,\n type PredictiveSearchStore,\n} from \"../core/predictive-search\";\n\nconst CONFIG_ARRAY_SEPARATOR = \"\\u0000\";\n\ntype PredictiveSearchContextValue = {\n store: PredictiveSearchStore;\n actions: PredictiveSearchActions;\n searchAction?: string;\n};\n\nconst PredictiveSearchContext = createContext<PredictiveSearchContextValue | null>(null);\n\n/** Props for the {@link PredictiveSearchProvider} component. */\nexport type PredictiveSearchProviderProps = CreatePredictiveSearchStoreOptions & {\n children?: ReactNode;\n /** Form action URL for progressive enhancement. Used by {@link usePredictiveSearchForm} to set the form's `action` attribute so the search works without JavaScript. Falls back to `\"/search\"` when omitted. */\n searchAction?: string;\n};\n\nexport type { PredictiveSearchActions };\n\n/**\n * Options for the form props builder returned by {@link usePredictiveSearchForm}.\n *\n * Accepts all standard form HTML attributes except `onSubmit`, which is\n * replaced by a version that provides the extracted search term.\n */\nexport type PredictiveSearchFormPropsOptions = Omit<\n FormHTMLAttributes<HTMLFormElement>,\n \"onSubmit\"\n> & {\n /** When `true`, prevents the native form submission and triggers a client-side search instead. */\n preventDefault?: boolean;\n /** Called on submit with the submit event and the extracted search term. Calling `event.preventDefault()` cancels both the native submission and Hydrogen's client-side search. */\n onSubmit?: (event: SubmitEvent<HTMLFormElement>, term: string) => void;\n};\n\n/**\n * Options for the query input props builder returned by\n * {@link usePredictiveSearchForm}'s `register` method.\n *\n * Accepts all standard input HTML attributes except those controlled by the\n * form registration.\n */\nexport type PredictiveSearchQueryInputPropsOptions = Omit<\n InputHTMLAttributes<HTMLInputElement>,\n \"autoCapitalize\" | \"autoComplete\" | \"name\" | \"onChange\" | \"spellCheck\" | \"type\"\n> & {\n /** Called on change with the change event and the current input value. Call `event.preventDefault()` to skip the automatic search trigger. */\n onChange?: (event: ChangeEvent<HTMLInputElement>, term: string) => void;\n};\n\ntype PredictiveSearchFormField = Parameters<CorePredictiveSearchFormRegister>[0];\n\n/** Generates input element attributes for a named form field. */\nexport type PredictiveSearchFormRegister = (\n field: PredictiveSearchFormField,\n options?: PredictiveSearchQueryInputPropsOptions,\n) => InputHTMLAttributes<HTMLInputElement>;\n\n/** Return type of {@link usePredictiveSearchForm}, providing methods to build a progressively-enhanced search form. */\nexport type PredictiveSearchFormResult = {\n /** Generates form element attributes including the search action and submit handler. */\n formProps(options?: PredictiveSearchFormPropsOptions): FormHTMLAttributes<HTMLFormElement>;\n /** Generates input element attributes for a named form field and wires up the search trigger. */\n register: PredictiveSearchFormRegister;\n};\n\n/**\n * Creates and manages a predictive search store, providing it to descendant\n * hooks via React context.\n *\n * Recreates the store when configuration props change. Connects the store\n * on mount and destroys it on unmount.\n *\n * @throws {Error} When no `fetch` implementation is available (neither passed as a prop nor available on `globalThis`).\n */\nexport function PredictiveSearchProvider({\n children,\n predictiveSearchEndpoint,\n searchAction,\n debounceInMs,\n minTermLength,\n fetch,\n limit,\n limitScope,\n types,\n searchableFields,\n unavailableProducts,\n}: PredictiveSearchProviderProps) {\n const typesKey = getArrayKey(types);\n const searchableFieldsKey = getArrayKey(searchableFields);\n\n const store = useMemo(\n () =>\n createPredictiveSearchStore({\n predictiveSearchEndpoint,\n debounceInMs,\n minTermLength,\n fetch,\n limit,\n limitScope,\n types,\n searchableFields,\n unavailableProducts,\n }),\n // oxlint-disable-next-line react-hooks/exhaustive-deps -- array keys are the semantic dependencies\n [\n predictiveSearchEndpoint,\n debounceInMs,\n minTermLength,\n fetch,\n limit,\n limitScope,\n typesKey,\n searchableFieldsKey,\n unavailableProducts,\n ],\n );\n\n const storeRef = useRef(store);\n // Actions outlive store swaps by reading the committed store at call time. Only an insertion\n // effect is both commit-only and early enough: a render-phase write can point actions at a\n // store from a render React never commits, and layout and passive effects run child-first,\n // so a child effect searching in the same commit would still see the old store.\n useInsertionEffect(() => {\n storeRef.current = store;\n }, [store]);\n const actions = useMemo<PredictiveSearchActions>(\n () => ({\n search: (term) => storeRef.current.search(term),\n clear: () => storeRef.current.clear(),\n }),\n [],\n );\n\n useEffect(() => {\n store.connect();\n return () => store.destroy();\n }, [store]);\n\n const contextValue = useMemo(\n () => ({ store, actions, searchAction }),\n [store, actions, searchAction],\n );\n\n return createElement(PredictiveSearchContext.Provider, { value: contextValue }, children);\n}\n\nfunction getArrayKey(values: readonly string[] | undefined): string {\n return values?.join(CONFIG_ARRAY_SEPARATOR) ?? \"\";\n}\n\nfunction useRequiredStore<TData extends PredictiveSearchData>(\n hookName: string,\n): PredictiveSearchStore<TData> {\n const context = useContext(PredictiveSearchContext);\n if (!context) {\n throw new Error(`${hookName} must be used inside a <PredictiveSearchProvider>.`);\n }\n\n // oxlint-disable-next-line @typescript-eslint/consistent-type-assertions -- context stores the runtime object while hooks preserve caller-provided result typing\n return context.store as PredictiveSearchStore<TData>;\n}\n\nfunction useRequiredContext(hookName: string): PredictiveSearchContextValue {\n const context = useContext(PredictiveSearchContext);\n if (!context) {\n throw new Error(`${hookName} must be used inside a <PredictiveSearchProvider>.`);\n }\n\n return context;\n}\n\n/**\n * Subscribes to the predictive search store's state.\n *\n * Without arguments, returns the full {@link PredictiveSearchState}. With a\n * `selector`, returns a derived value that only triggers re-renders when the\n * selected value changes (reference equality by default, or a custom\n * `isEqual`).\n *\n * Must be used inside a {@link PredictiveSearchProvider}.\n *\n * @throws {Error} When called outside a PredictiveSearchProvider.\n */\nexport function usePredictiveSearch<\n TData extends PredictiveSearchData = PredictiveSearchData,\n>(): PredictiveSearchState<TData>;\nexport function usePredictiveSearch<\n TData extends PredictiveSearchData = PredictiveSearchData,\n S = PredictiveSearchState<TData>,\n>(selector: (state: PredictiveSearchState<TData>) => S, isEqual?: (a: S, b: S) => boolean): S;\nexport function usePredictiveSearch<\n TData extends PredictiveSearchData = PredictiveSearchData,\n S = PredictiveSearchState<TData>,\n>(\n selector?: (state: PredictiveSearchState<TData>) => S,\n isEqual?: (a: S, b: S) => boolean,\n): PredictiveSearchState<TData> | S {\n const store = useRequiredStore<TData>(\"usePredictiveSearch\");\n const cachedRef = useRef<{\n state: unknown;\n selector: typeof selector;\n value: PredictiveSearchState<TData> | S;\n } | null>(null);\n\n const getSnapshot = () => {\n const state = store.getState();\n\n if (\n cachedRef.current &&\n cachedRef.current.state === state &&\n cachedRef.current.selector === selector\n ) {\n return cachedRef.current.value;\n }\n\n if (!selector) {\n cachedRef.current = { state, selector, value: state };\n return state;\n }\n\n const next = selector(state);\n if (cachedRef.current && isEqual?.(cachedRef.current.value as S, next)) {\n cachedRef.current = { state, selector, value: cachedRef.current.value };\n return cachedRef.current.value;\n }\n\n cachedRef.current = { state, selector, value: next };\n return next;\n };\n\n return useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Returns `search` and `clear` methods that always target the provider's\n * current store. Their identity is stable across re-renders, even\n * when a config prop change recreates the store.\n *\n * Must be used inside a {@link PredictiveSearchProvider}.\n *\n * @throws {Error} When called outside a PredictiveSearchProvider.\n */\nexport function usePredictiveSearchActions(): PredictiveSearchActions {\n return useRequiredContext(\"usePredictiveSearchActions\").actions;\n}\n\n/**\n * Returns `formProps` and `register` for building a progressively-enhanced\n * search form.\n *\n * `formProps()` generates form element attributes including the search\n * action URL. `register(\"query\")` generates input attributes and calls the\n * store's search action on every change event. The store debounces requests\n * and resets state for terms shorter than `minTermLength`.\n * Both support an optional callback that receives the event and extracted\n * term, and respect `event.preventDefault()` to cancel the automatic behavior.\n *\n * Must be used inside a {@link PredictiveSearchProvider}.\n *\n * @throws {Error} When called outside a PredictiveSearchProvider.\n */\nexport function usePredictiveSearchForm(): PredictiveSearchFormResult {\n const { searchAction, actions } = useRequiredContext(\"usePredictiveSearchForm\");\n const coreRegister = useMemo(() => createPredictiveSearchFormRegister(), []);\n\n const register = useCallback<PredictiveSearchFormRegister>(\n (field, props = {}) => {\n const { onChange, ...attributes } = props;\n const coreAttributes = coreRegister(field);\n\n return {\n ...attributes,\n ...coreAttributes,\n onChange: (event: ChangeEvent<HTMLInputElement>) => {\n const term = event.currentTarget.value;\n onChange?.(event, term);\n if (event.defaultPrevented) return;\n void actions.search(term);\n },\n };\n },\n [coreRegister, actions],\n );\n\n const formProps = useCallback(\n (props: PredictiveSearchFormPropsOptions = {}): FormHTMLAttributes<HTMLFormElement> => {\n const { onSubmit, preventDefault, ...attributes } = props;\n\n return {\n ...getPredictiveSearchFormAttributes(searchAction),\n ...attributes,\n onSubmit: (event: SubmitEvent<HTMLFormElement>) => {\n const term = readPredictiveSearchFormTerm(new FormData(event.currentTarget));\n onSubmit?.(event, term);\n if (event.defaultPrevented) return;\n if (!preventDefault) return;\n event.preventDefault();\n void actions.search(term);\n },\n };\n },\n [searchAction, actions],\n );\n\n return { formProps, register };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"predictive-search.mjs","names":[],"sources":["../../src/react/predictive-search.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n createElement,\n useCallback,\n useContext,\n useEffect,\n useInsertionEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n type ChangeEvent,\n type FormHTMLAttributes,\n type InputHTMLAttributes,\n type ReactNode,\n type SubmitEvent,\n} from \"react\";\n\nimport {\n createPredictiveSearchFormRegister,\n createPredictiveSearchStore,\n getPredictiveSearchFormAttributes,\n readPredictiveSearchFormTerm,\n type CreatePredictiveSearchStoreOptions,\n type PredictiveSearchActions,\n type PredictiveSearchFormRegister as CorePredictiveSearchFormRegister,\n type PredictiveSearchData,\n type PredictiveSearchState,\n type PredictiveSearchStore,\n} from \"../core/predictive-search\";\n\nconst CONFIG_ARRAY_SEPARATOR = \"\\u0000\";\n\ntype PredictiveSearchContextValue = {\n store: PredictiveSearchStore;\n actions: PredictiveSearchActions;\n searchAction?: string;\n};\n\nconst PredictiveSearchContext = createContext<PredictiveSearchContextValue | null>(null);\n\n/**\n * Store options, the search page path, and the content that uses the predictive search hooks.\n *\n * Changing a store option clears the current term and results. The provider compares `types` and `searchableFields` by value, which lets you pass new arrays on each render.\n */\nexport interface PredictiveSearchProviderProps extends CreatePredictiveSearchStoreOptions {\n /** Content that uses the predictive search hooks. */\n children?: ReactNode;\n /** Search page path that forms from `usePredictiveSearchForm` submit to. Defaults to `/search`. */\n searchAction?: string;\n}\n\nexport type { PredictiveSearchActions };\n\n/** Submit behavior for the search form. */\ninterface PredictiveSearchFormSubmitOptions {\n /** Pass `true` to run the search in the browser on submit and stay on the current page. Defaults to `false`. */\n preventDefault?: boolean;\n /** Runs on submit with the event and the search term. Call `event.preventDefault()` to cancel both the page navigation and the browser search. */\n onSubmit?: (event: SubmitEvent<HTMLFormElement>, term: string) => void;\n}\n\n/**\n * Form attributes and submit behavior for the search form.\n *\n * Any standard form attribute works. Attributes that you pass override the default action, method, and role.\n */\nexport type PredictiveSearchFormPropsOptions = Omit<\n FormHTMLAttributes<HTMLFormElement>,\n \"onSubmit\"\n> &\n PredictiveSearchFormSubmitOptions;\n\n/**\n * Input attributes and a change callback for the search input.\n *\n * Any standard input attribute works except the name, type, autocomplete, autocapitalize, and spell check attributes, which the register function sets.\n */\nexport type PredictiveSearchQueryInputPropsOptions = Omit<\n InputHTMLAttributes<HTMLInputElement>,\n \"autoCapitalize\" | \"autoComplete\" | \"name\" | \"onChange\" | \"spellCheck\" | \"type\"\n> &\n PredictiveSearchQueryInputChangeOptions;\n\n/** Change behavior for the search input. */\ninterface PredictiveSearchQueryInputChangeOptions {\n /** Runs on every change with the event and the input value. Call `event.preventDefault()` to skip the search for that change. */\n onChange?: (event: ChangeEvent<HTMLInputElement>, term: string) => void;\n}\n\ntype PredictiveSearchFormField = Parameters<CorePredictiveSearchFormRegister>[0];\n\n/** Returns the search input attributes, merged with the attributes that you pass. */\nexport type PredictiveSearchFormRegister = (\n field: PredictiveSearchFormField,\n options?: PredictiveSearchQueryInputPropsOptions,\n) => InputHTMLAttributes<HTMLInputElement>;\n\n/** Functions that return attributes for a predictive search form and its search input. */\nexport type PredictiveSearchFormResult = {\n /**\n * Returns the attributes for the search form.\n *\n * By default, submitting the form opens the search page at the provider's search action.\n * Pass `preventDefault: true` to run the search in the browser and stay on the current page.\n */\n formProps(options?: PredictiveSearchFormPropsOptions): FormHTMLAttributes<HTMLFormElement>;\n /**\n * Returns the attributes for the search input, which searches as the customer types.\n *\n * Pass `query`. Any other field name throws an error.\n */\n register: PredictiveSearchFormRegister;\n};\n\n/**\n * Runs predictive search for the components inside the provider. Wrap your search UI in the provider. Inside the provider, `usePredictiveSearch` reads the results, `usePredictiveSearchActions` searches and clears, and `usePredictiveSearchForm` builds the search form.\n *\n * The provider throws an error when you omit the `fetch` prop in a runtime without a global `fetch`.\n *\n * @param props - The store options, the search page path, and the content that uses the predictive search hooks.\n * @returns The provider element that wraps your search UI.\n * @throws {Error} When neither the fetch prop nor a global fetch function exists.\n * @publicDocs\n */\nexport function PredictiveSearchProvider({\n children,\n predictiveSearchEndpoint,\n searchAction,\n debounceInMs,\n minTermLength,\n fetch,\n limit,\n limitScope,\n types,\n searchableFields,\n unavailableProducts,\n}: PredictiveSearchProviderProps) {\n const typesKey = getArrayKey(types);\n const searchableFieldsKey = getArrayKey(searchableFields);\n\n const store = useMemo(\n () =>\n createPredictiveSearchStore({\n predictiveSearchEndpoint,\n debounceInMs,\n minTermLength,\n fetch,\n limit,\n limitScope,\n types,\n searchableFields,\n unavailableProducts,\n }),\n // oxlint-disable-next-line react-hooks/exhaustive-deps -- array keys are the semantic dependencies\n [\n predictiveSearchEndpoint,\n debounceInMs,\n minTermLength,\n fetch,\n limit,\n limitScope,\n typesKey,\n searchableFieldsKey,\n unavailableProducts,\n ],\n );\n\n const storeRef = useRef(store);\n // Actions outlive store swaps by reading the committed store at call time. Only an insertion\n // effect is both commit-only and early enough: a render-phase write can point actions at a\n // store from a render React never commits, and layout and passive effects run child-first,\n // so a child effect searching in the same commit would still see the old store.\n useInsertionEffect(() => {\n storeRef.current = store;\n }, [store]);\n const actions = useMemo<PredictiveSearchActions>(\n () => ({\n search: (term) => storeRef.current.search(term),\n clear: () => storeRef.current.clear(),\n }),\n [],\n );\n\n useEffect(() => {\n store.connect();\n return () => store.destroy();\n }, [store]);\n\n const contextValue = useMemo(\n () => ({ store, actions, searchAction }),\n [store, actions, searchAction],\n );\n\n return createElement(PredictiveSearchContext.Provider, { value: contextValue }, children);\n}\n\nfunction getArrayKey(values: readonly string[] | undefined): string {\n return values?.join(CONFIG_ARRAY_SEPARATOR) ?? \"\";\n}\n\nfunction useRequiredStore<TData extends PredictiveSearchData>(\n hookName: string,\n): PredictiveSearchStore<TData> {\n const context = useContext(PredictiveSearchContext);\n if (!context) {\n throw new Error(`${hookName} must be used inside a <PredictiveSearchProvider>.`);\n }\n\n // oxlint-disable-next-line @typescript-eslint/consistent-type-assertions -- context stores the runtime object while hooks preserve caller-provided result typing\n return context.store as PredictiveSearchStore<TData>;\n}\n\nfunction useRequiredContext(hookName: string): PredictiveSearchContextValue {\n const context = useContext(PredictiveSearchContext);\n if (!context) {\n throw new Error(`${hookName} must be used inside a <PredictiveSearchProvider>.`);\n }\n\n return context;\n}\n\n/**\n * Returns the search term, status, results, and error, and re-renders the component on each change.\n *\n * Pass a selector to read one value, such as the status, and re-render only when that value changes. The hook compares selected values by reference unless you pass an equality function.\n * The hook throws an error outside a `PredictiveSearchProvider`.\n *\n * @throws {Error} When you call the hook outside a PredictiveSearchProvider.\n * @publicDocs\n */\nexport function usePredictiveSearch<\n TData extends PredictiveSearchData = PredictiveSearchData,\n>(): PredictiveSearchState<TData>;\nexport function usePredictiveSearch<\n TData extends PredictiveSearchData = PredictiveSearchData,\n S = PredictiveSearchState<TData>,\n>(selector: (state: PredictiveSearchState<TData>) => S, isEqual?: (a: S, b: S) => boolean): S;\n/**\n * @param selector - Function that picks the value to return from the search state.\n * @param isEqual - Function that compares two selected values. Return `true` to skip the re-render.\n * @returns The search state, or the value that your selector picks.\n */\nexport function usePredictiveSearch<\n TData extends PredictiveSearchData = PredictiveSearchData,\n S = PredictiveSearchState<TData>,\n>(\n selector?: (state: PredictiveSearchState<TData>) => S,\n isEqual?: (a: S, b: S) => boolean,\n): PredictiveSearchState<TData> | S {\n const store = useRequiredStore<TData>(\"usePredictiveSearch\");\n const cachedRef = useRef<{\n state: unknown;\n selector: typeof selector;\n value: PredictiveSearchState<TData> | S;\n } | null>(null);\n\n const getSnapshot = () => {\n const state = store.getState();\n\n if (\n cachedRef.current &&\n cachedRef.current.state === state &&\n cachedRef.current.selector === selector\n ) {\n return cachedRef.current.value;\n }\n\n if (!selector) {\n cachedRef.current = { state, selector, value: state };\n return state;\n }\n\n const next = selector(state);\n if (cachedRef.current && isEqual?.(cachedRef.current.value as S, next)) {\n cachedRef.current = { state, selector, value: cachedRef.current.value };\n return cachedRef.current.value;\n }\n\n cachedRef.current = { state, selector, value: next };\n return next;\n };\n\n return useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Returns methods that search for a term and clear the results of the provider's predictive search. Use the hook in components that start searches without reading results.\n *\n * The methods keep the same identity across re-renders and keep working after a store option changes.\n * The hook throws an error outside a `PredictiveSearchProvider`.\n *\n * @returns Methods that search for a term and clear the results.\n * @throws {Error} When you call the hook outside a PredictiveSearchProvider.\n * @publicDocs\n */\nexport function usePredictiveSearchActions(): PredictiveSearchActions {\n return useRequiredContext(\"usePredictiveSearchActions\").actions;\n}\n\n/**\n * Returns functions that connect a search form and its search input to the provider's predictive search. The form submits to the search page without JavaScript, and the search input searches as the customer types.\n *\n * Spread `formProps()` on the form and `register(\"query\")` on the input. In the `onSubmit` and `onChange` callbacks, call `event.preventDefault()` to skip the automatic submission or search.\n * The hook throws an error outside a `PredictiveSearchProvider`.\n *\n * @returns Functions that return the search form attributes and the search input attributes.\n * @throws {Error} When you call the hook outside a PredictiveSearchProvider.\n * @publicDocs\n */\nexport function usePredictiveSearchForm(): PredictiveSearchFormResult {\n const { searchAction, actions } = useRequiredContext(\"usePredictiveSearchForm\");\n const coreRegister = useMemo(() => createPredictiveSearchFormRegister(), []);\n\n const register = useCallback<PredictiveSearchFormRegister>(\n (field, props = {}) => {\n const { onChange, ...attributes } = props;\n const coreAttributes = coreRegister(field);\n\n return {\n ...attributes,\n ...coreAttributes,\n onChange: (event: ChangeEvent<HTMLInputElement>) => {\n const term = event.currentTarget.value;\n onChange?.(event, term);\n if (event.defaultPrevented) return;\n void actions.search(term);\n },\n };\n },\n [coreRegister, actions],\n );\n\n const formProps = useCallback(\n (props: PredictiveSearchFormPropsOptions = {}): FormHTMLAttributes<HTMLFormElement> => {\n const { onSubmit, preventDefault, ...attributes } = props;\n\n return {\n ...getPredictiveSearchFormAttributes(searchAction),\n ...attributes,\n onSubmit: (event: SubmitEvent<HTMLFormElement>) => {\n const term = readPredictiveSearchFormTerm(new FormData(event.currentTarget));\n onSubmit?.(event, term);\n if (event.defaultPrevented) return;\n if (!preventDefault) return;\n event.preventDefault();\n void actions.search(term);\n },\n };\n },\n [searchAction, actions],\n );\n\n return { formProps, register };\n}\n\n/**\n * Returns the search term, status, results, and error, and re-renders the component on each change.\n *\n * Pass a selector to read one value, such as the status, and re-render only when that value changes. The hook compares selected values by reference unless you pass an equality function.\n * The hook throws an error outside a `PredictiveSearchProvider`.\n *\n * @throws {Error} When you call the hook outside a PredictiveSearchProvider.\n * @publicDocs\n */\nexport type UsePredictiveSearchForDocs =\n /**\n * @param selector - Function that picks the value to return from the search state.\n * @param isEqual - Function that compares two selected values. Return `true` to skip the re-render.\n * @returns The search state, or the value that your selector picks.\n */\n <S = PredictiveSearchState>(\n selector?: (state: PredictiveSearchState) => S,\n isEqual?: (a: S, b: S) => boolean,\n ) => S;\n"],"mappings":";;;;;;;AAgCA,MAAM,yBAAyB;AAQ/B,MAAM,0BAA0B,cAAmD,KAAK;;;;;;;;;;;AAuFxF,SAAgB,yBAAyB,EACvC,UACA,0BACA,cACA,cACA,eACA,OACA,OACA,YACA,OACA,kBACA,uBACgC;CAIhC,MAAM,QAAQ,cAEV,4BAA4B;EAC1B;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,EAEJ;EACE;EACA;EACA;EACA;EACA;EACA;EAvBa,YAAY,MAwBjB;EAvBgB,YAAY,iBAwBjB;EACnB;EACD,CACF;CAED,MAAM,WAAW,OAAO,MAAM;AAK9B,0BAAyB;AACvB,WAAS,UAAU;IAClB,CAAC,MAAM,CAAC;CACX,MAAM,UAAU,eACP;EACL,SAAS,SAAS,SAAS,QAAQ,OAAO,KAAK;EAC/C,aAAa,SAAS,QAAQ,OAAO;EACtC,GACD,EAAE,CACH;AAED,iBAAgB;AACd,QAAM,SAAS;AACf,eAAa,MAAM,SAAS;IAC3B,CAAC,MAAM,CAAC;CAEX,MAAM,eAAe,eACZ;EAAE;EAAO;EAAS;EAAc,GACvC;EAAC;EAAO;EAAS;EAAa,CAC/B;AAED,QAAO,cAAc,wBAAwB,UAAU,EAAE,OAAO,cAAc,EAAE,SAAS;;AAG3F,SAAS,YAAY,QAA+C;AAClE,QAAO,QAAQ,KAAK,uBAAuB,IAAI;;AAGjD,SAAS,iBACP,UAC8B;CAC9B,MAAM,UAAU,WAAW,wBAAwB;AACnD,KAAI,CAAC,QACH,OAAM,IAAI,MAAM,GAAG,SAAS,oDAAoD;AAIlF,QAAO,QAAQ;;AAGjB,SAAS,mBAAmB,UAAgD;CAC1E,MAAM,UAAU,WAAW,wBAAwB;AACnD,KAAI,CAAC,QACH,OAAM,IAAI,MAAM,GAAG,SAAS,oDAAoD;AAGlF,QAAO;;;;;;;AAwBT,SAAgB,oBAId,UACA,SACkC;CAClC,MAAM,QAAQ,iBAAwB,sBAAsB;CAC5D,MAAM,YAAY,OAIR,KAAK;CAEf,MAAM,oBAAoB;EACxB,MAAM,QAAQ,MAAM,UAAU;AAE9B,MACE,UAAU,WACV,UAAU,QAAQ,UAAU,SAC5B,UAAU,QAAQ,aAAa,SAE/B,QAAO,UAAU,QAAQ;AAG3B,MAAI,CAAC,UAAU;AACb,aAAU,UAAU;IAAE;IAAO;IAAU,OAAO;IAAO;AACrD,UAAO;;EAGT,MAAM,OAAO,SAAS,MAAM;AAC5B,MAAI,UAAU,WAAW,UAAU,UAAU,QAAQ,OAAY,KAAK,EAAE;AACtE,aAAU,UAAU;IAAE;IAAO;IAAU,OAAO,UAAU,QAAQ;IAAO;AACvE,UAAO,UAAU,QAAQ;;AAG3B,YAAU,UAAU;GAAE;GAAO;GAAU,OAAO;GAAM;AACpD,SAAO;;AAGT,QAAO,qBAAqB,MAAM,WAAW,aAAa,YAAY;;;;;;;;;;;;AAaxE,SAAgB,6BAAsD;AACpE,QAAO,mBAAmB,6BAA6B,CAAC;;;;;;;;;;;;AAa1D,SAAgB,0BAAsD;CACpE,MAAM,EAAE,cAAc,YAAY,mBAAmB,0BAA0B;CAC/E,MAAM,eAAe,cAAc,oCAAoC,EAAE,EAAE,CAAC;CAE5E,MAAM,WAAW,aACd,OAAO,QAAQ,EAAE,KAAK;EACrB,MAAM,EAAE,UAAU,GAAG,eAAe;EACpC,MAAM,iBAAiB,aAAa,MAAM;AAE1C,SAAO;GACL,GAAG;GACH,GAAG;GACH,WAAW,UAAyC;IAClD,MAAM,OAAO,MAAM,cAAc;AACjC,eAAW,OAAO,KAAK;AACvB,QAAI,MAAM,iBAAkB;AAC5B,IAAK,QAAQ,OAAO,KAAK;;GAE5B;IAEH,CAAC,cAAc,QAAQ,CACxB;AAsBD,QAAO;EAAE,WApBS,aACf,QAA0C,EAAE,KAA0C;GACrF,MAAM,EAAE,UAAU,gBAAgB,GAAG,eAAe;AAEpD,UAAO;IACL,GAAG,kCAAkC,aAAa;IAClD,GAAG;IACH,WAAW,UAAwC;KACjD,MAAM,OAAO,6BAA6B,IAAI,SAAS,MAAM,cAAc,CAAC;AAC5E,gBAAW,OAAO,KAAK;AACvB,SAAI,MAAM,iBAAkB;AAC5B,SAAI,CAAC,eAAgB;AACrB,WAAM,gBAAgB;AACtB,KAAK,QAAQ,OAAO,KAAK;;IAE5B;KAEH,CAAC,cAAc,QAAQ,CAGP;EAAE;EAAU"}
|
package/dist/react/product.d.mts
CHANGED
|
@@ -5,63 +5,92 @@ import { ProductFormRegister } from "../core/product/form.mjs";
|
|
|
5
5
|
import { FormHTMLAttributes, ReactNode, SubmitEvent } from "react";
|
|
6
6
|
|
|
7
7
|
//#region src/react/product.d.ts
|
|
8
|
-
/** Options for
|
|
8
|
+
/** Options for the `useProductForm` hook. */
|
|
9
9
|
interface UseProductFormOptions<TProduct extends ProductInput> {
|
|
10
|
+
/** Runs after each resolved or unresolved selection. Invalid selections skip the callback. */
|
|
10
11
|
onSelect?: (result: ValidProductSelectionResult<TProduct>) => void;
|
|
11
12
|
}
|
|
12
|
-
/**
|
|
13
|
+
/** Product form state and bindings that the product form hooks return. */
|
|
13
14
|
interface UseProductFormResult<TProduct extends ProductInput> {
|
|
15
|
+
/** Each product option and its values, with the selection, existence, and availability of each value. */
|
|
14
16
|
options: ProductFormOptions<TProduct>;
|
|
17
|
+
/** Variant that matches the current selection, or `null` when the selection is partial or your query didn't load that variant. */
|
|
15
18
|
selectedVariant: ProductVariantFrom<TProduct> | null;
|
|
19
|
+
/** Returns the props for a product form field. Spread the props on the matching input or button. */
|
|
16
20
|
register: ProductFormRegister;
|
|
21
|
+
/**
|
|
22
|
+
* Returns props for the product form element. Submitting the form adds the selected variant to the cart, and the form posts to the cart endpoint before JavaScript loads.
|
|
23
|
+
* Call `preventDefault()` in `beforeSubmit` to cancel the submission. The `afterSubmit` callback runs after the cart store handles the submission. Cart errors appear in the errors field. When the cart store throws an error, the hook logs the error and skips `afterSubmit`.
|
|
24
|
+
*/
|
|
17
25
|
formProps: (opts?: {
|
|
18
26
|
beforeSubmit?: (e: SubmitEvent<HTMLFormElement>) => void;
|
|
19
27
|
afterSubmit?: (e: SubmitEvent<HTMLFormElement>) => void;
|
|
20
28
|
}) => FormHTMLAttributes<HTMLFormElement>;
|
|
29
|
+
/** Cart errors and warnings to show near the add-to-cart button. */
|
|
21
30
|
errors: ProductFormErrors;
|
|
31
|
+
/** First cart line that holds the selected variant, or `null` when no cart line holds the variant. */
|
|
22
32
|
matchedLineItem: CartLine | null;
|
|
23
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Whether the cart is handling a form submission. Disable the add-to-cart button while the value is `true`.
|
|
35
|
+
*/
|
|
24
36
|
pending: boolean;
|
|
37
|
+
/** Selects an option value and returns the result. Resolved and unresolved selections run the `onSelect` callback. */
|
|
25
38
|
selectOption: (name: string, value: string) => VariantSelectionResult<ProductVariantFrom<TProduct>>;
|
|
26
39
|
}
|
|
27
|
-
/** Props for the
|
|
40
|
+
/** Props for the product provider from `createProductComponents`. */
|
|
28
41
|
interface ProductProviderProps<TProduct extends ProductInput> {
|
|
42
|
+
/**
|
|
43
|
+
* The product from your Storefront API query.
|
|
44
|
+
* The provider reloads the product into the store when the product ID or the selected or first available variant ID changes. The customer's selection persists through re-renders that keep both IDs.
|
|
45
|
+
*/
|
|
29
46
|
product: TProduct;
|
|
47
|
+
/**
|
|
48
|
+
* Runs after each resolved or unresolved selection. Invalid selections skip the callback. Navigate to the new product URL in this callback, and keep navigation out of option controls.
|
|
49
|
+
* Make the callback safe to run twice. A click on a hydrated option link runs the callback and also follows the link.
|
|
50
|
+
*/
|
|
30
51
|
onSelect?: (result: ValidProductSelectionResult<TProduct>) => void;
|
|
52
|
+
/** Content that reads product state through the product hooks. */
|
|
31
53
|
children?: ReactNode;
|
|
32
54
|
}
|
|
33
|
-
/**
|
|
55
|
+
/** Product state that the `useProduct` hook from `createProductComponents` returns. */
|
|
34
56
|
interface UseProductResult<TProduct extends ProductInput> {
|
|
57
|
+
/** Each product option and its values, with the selection, existence, and availability of each value. */
|
|
35
58
|
options: ProductFormOptions<TProduct>;
|
|
59
|
+
/** Variant that matches the current selection, or `null` when the selection is partial or your query didn't load that variant. */
|
|
36
60
|
selectedVariant: ProductVariantFrom<TProduct> | null;
|
|
61
|
+
/** Selects an option value and returns the result. Resolved and unresolved selections run the `onSelect` callback. */
|
|
37
62
|
selectOption: (name: string, value: string) => VariantSelectionResult<ProductVariantFrom<TProduct>>;
|
|
63
|
+
/** Cart errors and warnings to show near the add-to-cart button. */
|
|
38
64
|
errors: ProductFormErrors;
|
|
65
|
+
/** First cart line that holds the selected variant, or `null` when no cart line holds the variant. */
|
|
39
66
|
matchedLineItem: CartLine | null;
|
|
40
67
|
}
|
|
41
68
|
/**
|
|
42
|
-
*
|
|
69
|
+
* Returns product form state and bindings from a product form store that you manage, and re-renders when the state changes.
|
|
43
70
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
71
|
+
* Create the store with `createProductFormStore`. Call `connect()` on mount, `hydrate()` when the product data changes, and `destroy()` on unmount. To skip the store setup, use the product provider from `createProductComponents`.
|
|
72
|
+
*
|
|
73
|
+
* @param store The store that `createProductFormStore` returns.
|
|
74
|
+
* @param options An `onSelect` callback that runs after each resolved or unresolved selection.
|
|
75
|
+
* @returns The option state, the selected variant, cart errors, the matching cart line, the pending state, the register function, and the form props.
|
|
76
|
+
* @publicDocs
|
|
48
77
|
*/
|
|
49
78
|
declare function useProductForm<TProduct extends ProductInput>(store: ProductFormStore<TProduct>, options?: UseProductFormOptions<TProduct>): UseProductFormResult<TProduct>;
|
|
50
79
|
/**
|
|
51
|
-
* Creates a
|
|
80
|
+
* Creates a product provider and product hooks typed to your product query.
|
|
81
|
+
*
|
|
82
|
+
* The provider creates the product form store, reloads the product into the store when the product changes, and destroys the store on unmount. The `useProduct` hook returns the option state and variant selection. The `useProductForm` hook adds field props, form props, and the pending state.
|
|
52
83
|
*
|
|
53
|
-
*
|
|
54
|
-
* - `ProductProvider` manages store lifecycle (creation, hydration, cleanup)
|
|
55
|
-
* - `useProduct` provides read-only state and variant selection
|
|
56
|
-
* - `useProductForm` provides form-binding utilities (register, formProps, pending)
|
|
84
|
+
* Pass your Storefront API product query type as the type argument to type the selected variant and option values. Render the provider inside a cart provider. The provider throws an error without a cart provider ancestor, and each hook throws an error outside the product provider.
|
|
57
85
|
*
|
|
58
|
-
*
|
|
86
|
+
* @returns The typed `ProductProvider` component and the `useProduct` and `useProductForm` hooks.
|
|
59
87
|
*
|
|
60
88
|
* @example
|
|
61
89
|
* ```ts
|
|
62
90
|
* const { ProductProvider, useProduct, useProductForm } =
|
|
63
91
|
* createProductComponents<MyProductType>();
|
|
64
92
|
* ```
|
|
93
|
+
* @publicDocs
|
|
65
94
|
*/
|
|
66
95
|
declare function createProductComponents<TProduct extends ProductInput>(): {
|
|
67
96
|
ProductProvider: (props: ProductProviderProps<TProduct>) => ReactNode;
|
package/dist/react/product.mjs
CHANGED
|
@@ -9,12 +9,14 @@ import { createContext, createElement, useCallback, useContext, useEffect, useMe
|
|
|
9
9
|
//#region src/react/product.tsx
|
|
10
10
|
const log = getLogger("product");
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* Returns product form state and bindings from a product form store that you manage, and re-renders when the state changes.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* Create the store with `createProductFormStore`. Call `connect()` on mount, `hydrate()` when the product data changes, and `destroy()` on unmount. To skip the store setup, use the product provider from `createProductComponents`.
|
|
15
|
+
*
|
|
16
|
+
* @param store The store that `createProductFormStore` returns.
|
|
17
|
+
* @param options An `onSelect` callback that runs after each resolved or unresolved selection.
|
|
18
|
+
* @returns The option state, the selected variant, cart errors, the matching cart line, the pending state, the register function, and the form props.
|
|
19
|
+
* @publicDocs
|
|
18
20
|
*/
|
|
19
21
|
function useProductForm(store, options) {
|
|
20
22
|
const onSelectRef = useRef(options?.onSelect);
|
|
@@ -52,20 +54,20 @@ function useProductForm(store, options) {
|
|
|
52
54
|
};
|
|
53
55
|
}
|
|
54
56
|
/**
|
|
55
|
-
* Creates a
|
|
57
|
+
* Creates a product provider and product hooks typed to your product query.
|
|
58
|
+
*
|
|
59
|
+
* The provider creates the product form store, reloads the product into the store when the product changes, and destroys the store on unmount. The `useProduct` hook returns the option state and variant selection. The `useProductForm` hook adds field props, form props, and the pending state.
|
|
56
60
|
*
|
|
57
|
-
*
|
|
58
|
-
* - `ProductProvider` manages store lifecycle (creation, hydration, cleanup)
|
|
59
|
-
* - `useProduct` provides read-only state and variant selection
|
|
60
|
-
* - `useProductForm` provides form-binding utilities (register, formProps, pending)
|
|
61
|
+
* Pass your Storefront API product query type as the type argument to type the selected variant and option values. Render the provider inside a cart provider. The provider throws an error without a cart provider ancestor, and each hook throws an error outside the product provider.
|
|
61
62
|
*
|
|
62
|
-
*
|
|
63
|
+
* @returns The typed `ProductProvider` component and the `useProduct` and `useProductForm` hooks.
|
|
63
64
|
*
|
|
64
65
|
* @example
|
|
65
66
|
* ```ts
|
|
66
67
|
* const { ProductProvider, useProduct, useProductForm } =
|
|
67
68
|
* createProductComponents<MyProductType>();
|
|
68
69
|
* ```
|
|
70
|
+
* @publicDocs
|
|
69
71
|
*/
|
|
70
72
|
function createProductComponents() {
|
|
71
73
|
const Context = createContext(null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"product.mjs","names":[],"sources":["../../src/react/product.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n createElement,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n type FormHTMLAttributes,\n type ReactNode,\n type SubmitEvent,\n} from \"react\";\n\nimport type { CartLine } from \"../core/cart/state\";\nimport { getLogger } from \"../core/logging\";\nimport {\n createProductFormRegister,\n createProductFormStore,\n type ProductFormErrors,\n type ProductFormOptions,\n type ProductFormRegister,\n type ProductFormStore,\n type ProductInput,\n type ProductVariantFrom,\n type ValidProductSelectionResult,\n type VariantSelectionResult,\n} from \"../core/product\";\nimport { getCartEndpoint, useCartStore } from \"./cart\";\n\nconst log = getLogger(\"product\");\n\n// ---------------------------------------------------------------------------\n// Shared types\n// ---------------------------------------------------------------------------\n\nexport type { ValidProductSelectionResult } from \"../core/product\";\n\n/** Options for {@link useProductForm}. */\nexport interface UseProductFormOptions<TProduct extends ProductInput> {\n onSelect?: (result: ValidProductSelectionResult<TProduct>) => void;\n}\n\n/** Return value of {@link useProductForm}. */\nexport interface UseProductFormResult<TProduct extends ProductInput> {\n options: ProductFormOptions<TProduct>;\n selectedVariant: ProductVariantFrom<TProduct> | null;\n register: ProductFormRegister;\n formProps: (opts?: {\n beforeSubmit?: (e: SubmitEvent<HTMLFormElement>) => void;\n afterSubmit?: (e: SubmitEvent<HTMLFormElement>) => void;\n }) => FormHTMLAttributes<HTMLFormElement>;\n errors: ProductFormErrors;\n matchedLineItem: CartLine | null;\n /** Whether a form submission is currently in flight. */\n pending: boolean;\n selectOption: (\n name: string,\n value: string,\n ) => VariantSelectionResult<ProductVariantFrom<TProduct>>;\n}\n\n/** Props for the `ProductProvider` returned by {@link createProductComponents}. */\nexport interface ProductProviderProps<TProduct extends ProductInput> {\n product: TProduct;\n onSelect?: (result: ValidProductSelectionResult<TProduct>) => void;\n children?: ReactNode;\n}\n\n/** Return value of the `useProduct` hook from {@link createProductComponents}. */\nexport interface UseProductResult<TProduct extends ProductInput> {\n options: ProductFormOptions<TProduct>;\n selectedVariant: ProductVariantFrom<TProduct> | null;\n selectOption: (\n name: string,\n value: string,\n ) => VariantSelectionResult<ProductVariantFrom<TProduct>>;\n errors: ProductFormErrors;\n matchedLineItem: CartLine | null;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Standalone hook\n// ---------------------------------------------------------------------------\n\n/**\n * Subscribes to a {@link ProductFormStore} and returns form-ready state.\n *\n * This is a pure subscription hook — it does **not** manage store lifecycle.\n * Create the store with `createProductFormStore` and manage its lifecycle\n * (hydration, destruction) yourself, or use `createProductComponents` for a\n * provider-based approach.\n */\nexport function useProductForm<TProduct extends ProductInput>(\n store: ProductFormStore<TProduct>,\n options?: UseProductFormOptions<TProduct>,\n): UseProductFormResult<TProduct> {\n const onSelectRef = useRef(options?.onSelect);\n onSelectRef.current = options?.onSelect;\n\n const state = useSyncExternalStore(store.subscribe, store.getState, store.getState);\n const [pending, setPending] = useState(false);\n\n const selectOption = useCallback(\n (name: string, value: string) => {\n const result = store.selectOption(name, value);\n if (result.status !== \"invalid\") {\n onSelectRef.current?.(result);\n }\n return result;\n },\n [store],\n );\n\n // oxlint-disable-next-line react-hooks/exhaustive-deps -- state is the full reactive snapshot; selectOption is stable\n const register = useMemo(\n () => createProductFormRegister(state.selectedVariant, selectOption),\n [state, selectOption],\n );\n\n const formProps = useCallback(\n (opts?: {\n beforeSubmit?: (e: SubmitEvent<HTMLFormElement>) => void;\n afterSubmit?: (e: SubmitEvent<HTMLFormElement>) => void;\n }): FormHTMLAttributes<HTMLFormElement> => ({\n onSubmit: (e: SubmitEvent<HTMLFormElement>) => {\n opts?.beforeSubmit?.(e);\n if (e.defaultPrevented) return;\n e.preventDefault();\n setPending(true);\n store\n .handleFormSubmit(e.nativeEvent)\n .then(\n () => opts?.afterSubmit?.(e),\n (error: unknown) => {\n // Cart user/network errors are surfaced via the reactive `errors`\n // state. Thrown exceptions (missing Shopify script, invalid form\n // target) are programming errors — log them for dev visibility.\n log.error(\"form submission error\", { error });\n },\n )\n .finally(() => setPending(false));\n },\n method: \"post\",\n action: getCartEndpoint(),\n }),\n [store],\n );\n\n return {\n options: state.options,\n selectedVariant: state.selectedVariant,\n register,\n formProps,\n errors: state.errors,\n matchedLineItem: state.matchedLineItem,\n pending,\n selectOption,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\ninterface ProductContextValue<TProduct extends ProductInput> {\n store: ProductFormStore<TProduct>;\n onSelectRef: React.RefObject<\n ((result: ValidProductSelectionResult<TProduct>) => void) | undefined\n >;\n}\n\n/**\n * Creates a typed set of product components bound to a specific product type.\n *\n * Returns `{ ProductProvider, useProduct, useProductForm }` where:\n * - `ProductProvider` manages store lifecycle (creation, hydration, cleanup)\n * - `useProduct` provides read-only state and variant selection\n * - `useProductForm` provides form-binding utilities (register, formProps, pending)\n *\n * Requires a `<CartProvider>` ancestor.\n *\n * @example\n * ```ts\n * const { ProductProvider, useProduct, useProductForm } =\n * createProductComponents<MyProductType>();\n * ```\n */\nexport function createProductComponents<TProduct extends ProductInput>(): {\n ProductProvider: (props: ProductProviderProps<TProduct>) => ReactNode;\n useProduct: () => UseProductResult<TProduct>;\n useProductForm: () => UseProductFormResult<TProduct>;\n} {\n const Context = createContext<ProductContextValue<TProduct> | null>(null);\n\n function useProductContext(hookName: string): ProductContextValue<TProduct> {\n const ctx = useContext(Context);\n if (!ctx) {\n throw new Error(\n `${hookName} must be used inside a <ProductProvider>. ` +\n \"Wrap your component tree with the ProductProvider from createProductComponents(), \" +\n \"or use the standalone useProductForm(store) hook instead.\",\n );\n }\n return ctx;\n }\n\n function ProductProvider({ product, onSelect, children }: ProductProviderProps<TProduct>) {\n const cartStore = useCartStore(\"ProductProvider\");\n\n // oxlint-disable-next-line react-hooks/exhaustive-deps -- store is intentionally created once\n const store = useMemo(() => createProductFormStore<TProduct>(product, cartStore), []);\n\n useEffect(() => {\n store.connect();\n return () => store.destroy();\n }, [store]);\n\n const productKey = `${product.id}:${product.selectedOrFirstAvailableVariant?.id ?? \"\"}`;\n const mountedRef = useRef(false);\n useEffect(() => {\n if (!mountedRef.current) {\n mountedRef.current = true;\n return;\n }\n store.hydrate(product);\n // oxlint-disable-next-line react-hooks/exhaustive-deps -- productKey is the semantic dep\n }, [productKey]);\n\n const onSelectRef = useRef(onSelect);\n onSelectRef.current = onSelect;\n\n // oxlint-disable-next-line react-hooks/exhaustive-deps -- onSelectRef is a stable ref object\n const value = useMemo<ProductContextValue<TProduct>>(() => ({ store, onSelectRef }), [store]);\n\n return createElement(Context.Provider, { value }, children);\n }\n\n function useProductHook(): UseProductResult<TProduct> {\n const { store, onSelectRef } = useProductContext(\"useProduct\");\n const state = useSyncExternalStore(store.subscribe, store.getState, store.getState);\n\n const selectOption = useCallback(\n (name: string, value: string) => {\n const result = store.selectOption(name, value);\n if (result.status !== \"invalid\") {\n onSelectRef.current?.(result);\n }\n return result;\n },\n [store, onSelectRef],\n );\n\n return {\n options: state.options,\n selectedVariant: state.selectedVariant,\n selectOption,\n errors: state.errors,\n matchedLineItem: state.matchedLineItem,\n };\n }\n\n function useProductFormHook(): UseProductFormResult<TProduct> {\n const { store, onSelectRef } = useProductContext(\"useProductForm\");\n return useProductForm<TProduct>(store, { onSelect: onSelectRef.current });\n }\n\n return {\n ProductProvider,\n useProduct: useProductHook,\n useProductForm: useProductFormHook,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"product.mjs","names":[],"sources":["../../src/react/product.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n createElement,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n type FormHTMLAttributes,\n type ReactNode,\n type SubmitEvent,\n} from \"react\";\n\nimport type { CartLine } from \"../core/cart/state\";\nimport { getLogger } from \"../core/logging\";\nimport {\n createProductFormRegister,\n createProductFormStore,\n type ProductFormErrors,\n type ProductFormOptions,\n type ProductFormRegister,\n type ProductFormStore,\n type ProductInput,\n type ProductVariantFrom,\n type ValidProductSelectionResult,\n type VariantSelectionResult,\n} from \"../core/product\";\nimport { getCartEndpoint, useCartStore } from \"./cart\";\n\nconst log = getLogger(\"product\");\n\n// ---------------------------------------------------------------------------\n// Shared types\n// ---------------------------------------------------------------------------\n\nexport type { ValidProductSelectionResult } from \"../core/product\";\n\n/** Options for the `useProductForm` hook. */\nexport interface UseProductFormOptions<TProduct extends ProductInput> {\n /** Runs after each resolved or unresolved selection. Invalid selections skip the callback. */\n onSelect?: (result: ValidProductSelectionResult<TProduct>) => void;\n}\n\n/** Product form state and bindings that the product form hooks return. */\nexport interface UseProductFormResult<TProduct extends ProductInput> {\n /** Each product option and its values, with the selection, existence, and availability of each value. */\n options: ProductFormOptions<TProduct>;\n /** Variant that matches the current selection, or `null` when the selection is partial or your query didn't load that variant. */\n selectedVariant: ProductVariantFrom<TProduct> | null;\n /** Returns the props for a product form field. Spread the props on the matching input or button. */\n register: ProductFormRegister;\n /**\n * Returns props for the product form element. Submitting the form adds the selected variant to the cart, and the form posts to the cart endpoint before JavaScript loads.\n * Call `preventDefault()` in `beforeSubmit` to cancel the submission. The `afterSubmit` callback runs after the cart store handles the submission. Cart errors appear in the errors field. When the cart store throws an error, the hook logs the error and skips `afterSubmit`.\n */\n formProps: (opts?: {\n beforeSubmit?: (e: SubmitEvent<HTMLFormElement>) => void;\n afterSubmit?: (e: SubmitEvent<HTMLFormElement>) => void;\n }) => FormHTMLAttributes<HTMLFormElement>;\n /** Cart errors and warnings to show near the add-to-cart button. */\n errors: ProductFormErrors;\n /** First cart line that holds the selected variant, or `null` when no cart line holds the variant. */\n matchedLineItem: CartLine | null;\n /**\n * Whether the cart is handling a form submission. Disable the add-to-cart button while the value is `true`.\n */\n pending: boolean;\n /** Selects an option value and returns the result. Resolved and unresolved selections run the `onSelect` callback. */\n selectOption: (\n name: string,\n value: string,\n ) => VariantSelectionResult<ProductVariantFrom<TProduct>>;\n}\n\n/** Props for the product provider from `createProductComponents`. */\nexport interface ProductProviderProps<TProduct extends ProductInput> {\n /**\n * The product from your Storefront API query.\n * The provider reloads the product into the store when the product ID or the selected or first available variant ID changes. The customer's selection persists through re-renders that keep both IDs.\n */\n product: TProduct;\n /**\n * Runs after each resolved or unresolved selection. Invalid selections skip the callback. Navigate to the new product URL in this callback, and keep navigation out of option controls.\n * Make the callback safe to run twice. A click on a hydrated option link runs the callback and also follows the link.\n */\n onSelect?: (result: ValidProductSelectionResult<TProduct>) => void;\n /** Content that reads product state through the product hooks. */\n children?: ReactNode;\n}\n\n/** Product state that the `useProduct` hook from `createProductComponents` returns. */\nexport interface UseProductResult<TProduct extends ProductInput> {\n /** Each product option and its values, with the selection, existence, and availability of each value. */\n options: ProductFormOptions<TProduct>;\n /** Variant that matches the current selection, or `null` when the selection is partial or your query didn't load that variant. */\n selectedVariant: ProductVariantFrom<TProduct> | null;\n /** Selects an option value and returns the result. Resolved and unresolved selections run the `onSelect` callback. */\n selectOption: (\n name: string,\n value: string,\n ) => VariantSelectionResult<ProductVariantFrom<TProduct>>;\n /** Cart errors and warnings to show near the add-to-cart button. */\n errors: ProductFormErrors;\n /** First cart line that holds the selected variant, or `null` when no cart line holds the variant. */\n matchedLineItem: CartLine | null;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Standalone hook\n// ---------------------------------------------------------------------------\n\n/**\n * Returns product form state and bindings from a product form store that you manage, and re-renders when the state changes.\n *\n * Create the store with `createProductFormStore`. Call `connect()` on mount, `hydrate()` when the product data changes, and `destroy()` on unmount. To skip the store setup, use the product provider from `createProductComponents`.\n *\n * @param store The store that `createProductFormStore` returns.\n * @param options An `onSelect` callback that runs after each resolved or unresolved selection.\n * @returns The option state, the selected variant, cart errors, the matching cart line, the pending state, the register function, and the form props.\n * @publicDocs\n */\nexport function useProductForm<TProduct extends ProductInput>(\n store: ProductFormStore<TProduct>,\n options?: UseProductFormOptions<TProduct>,\n): UseProductFormResult<TProduct> {\n const onSelectRef = useRef(options?.onSelect);\n onSelectRef.current = options?.onSelect;\n\n const state = useSyncExternalStore(store.subscribe, store.getState, store.getState);\n const [pending, setPending] = useState(false);\n\n const selectOption = useCallback(\n (name: string, value: string) => {\n const result = store.selectOption(name, value);\n if (result.status !== \"invalid\") {\n onSelectRef.current?.(result);\n }\n return result;\n },\n [store],\n );\n\n // oxlint-disable-next-line react-hooks/exhaustive-deps -- state is the full reactive snapshot; selectOption is stable\n const register = useMemo(\n () => createProductFormRegister(state.selectedVariant, selectOption),\n [state, selectOption],\n );\n\n const formProps = useCallback(\n (opts?: {\n beforeSubmit?: (e: SubmitEvent<HTMLFormElement>) => void;\n afterSubmit?: (e: SubmitEvent<HTMLFormElement>) => void;\n }): FormHTMLAttributes<HTMLFormElement> => ({\n onSubmit: (e: SubmitEvent<HTMLFormElement>) => {\n opts?.beforeSubmit?.(e);\n if (e.defaultPrevented) return;\n e.preventDefault();\n setPending(true);\n store\n .handleFormSubmit(e.nativeEvent)\n .then(\n () => opts?.afterSubmit?.(e),\n (error: unknown) => {\n // Cart user/network errors are surfaced via the reactive `errors`\n // state. Thrown exceptions (missing Shopify script, invalid form\n // target) are programming errors — log them for dev visibility.\n log.error(\"form submission error\", { error });\n },\n )\n .finally(() => setPending(false));\n },\n method: \"post\",\n action: getCartEndpoint(),\n }),\n [store],\n );\n\n return {\n options: state.options,\n selectedVariant: state.selectedVariant,\n register,\n formProps,\n errors: state.errors,\n matchedLineItem: state.matchedLineItem,\n pending,\n selectOption,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\ninterface ProductContextValue<TProduct extends ProductInput> {\n store: ProductFormStore<TProduct>;\n onSelectRef: React.RefObject<\n ((result: ValidProductSelectionResult<TProduct>) => void) | undefined\n >;\n}\n\n/**\n * Creates a product provider and product hooks typed to your product query.\n *\n * The provider creates the product form store, reloads the product into the store when the product changes, and destroys the store on unmount. The `useProduct` hook returns the option state and variant selection. The `useProductForm` hook adds field props, form props, and the pending state.\n *\n * Pass your Storefront API product query type as the type argument to type the selected variant and option values. Render the provider inside a cart provider. The provider throws an error without a cart provider ancestor, and each hook throws an error outside the product provider.\n *\n * @returns The typed `ProductProvider` component and the `useProduct` and `useProductForm` hooks.\n *\n * @example\n * ```ts\n * const { ProductProvider, useProduct, useProductForm } =\n * createProductComponents<MyProductType>();\n * ```\n * @publicDocs\n */\nexport function createProductComponents<TProduct extends ProductInput>(): {\n ProductProvider: (props: ProductProviderProps<TProduct>) => ReactNode;\n useProduct: () => UseProductResult<TProduct>;\n useProductForm: () => UseProductFormResult<TProduct>;\n} {\n const Context = createContext<ProductContextValue<TProduct> | null>(null);\n\n function useProductContext(hookName: string): ProductContextValue<TProduct> {\n const ctx = useContext(Context);\n if (!ctx) {\n throw new Error(\n `${hookName} must be used inside a <ProductProvider>. ` +\n \"Wrap your component tree with the ProductProvider from createProductComponents(), \" +\n \"or use the standalone useProductForm(store) hook instead.\",\n );\n }\n return ctx;\n }\n\n function ProductProvider({ product, onSelect, children }: ProductProviderProps<TProduct>) {\n const cartStore = useCartStore(\"ProductProvider\");\n\n // oxlint-disable-next-line react-hooks/exhaustive-deps -- store is intentionally created once\n const store = useMemo(() => createProductFormStore<TProduct>(product, cartStore), []);\n\n useEffect(() => {\n store.connect();\n return () => store.destroy();\n }, [store]);\n\n const productKey = `${product.id}:${product.selectedOrFirstAvailableVariant?.id ?? \"\"}`;\n const mountedRef = useRef(false);\n useEffect(() => {\n if (!mountedRef.current) {\n mountedRef.current = true;\n return;\n }\n store.hydrate(product);\n // oxlint-disable-next-line react-hooks/exhaustive-deps -- productKey is the semantic dep\n }, [productKey]);\n\n const onSelectRef = useRef(onSelect);\n onSelectRef.current = onSelect;\n\n // oxlint-disable-next-line react-hooks/exhaustive-deps -- onSelectRef is a stable ref object\n const value = useMemo<ProductContextValue<TProduct>>(() => ({ store, onSelectRef }), [store]);\n\n return createElement(Context.Provider, { value }, children);\n }\n\n function useProductHook(): UseProductResult<TProduct> {\n const { store, onSelectRef } = useProductContext(\"useProduct\");\n const state = useSyncExternalStore(store.subscribe, store.getState, store.getState);\n\n const selectOption = useCallback(\n (name: string, value: string) => {\n const result = store.selectOption(name, value);\n if (result.status !== \"invalid\") {\n onSelectRef.current?.(result);\n }\n return result;\n },\n [store, onSelectRef],\n );\n\n return {\n options: state.options,\n selectedVariant: state.selectedVariant,\n selectOption,\n errors: state.errors,\n matchedLineItem: state.matchedLineItem,\n };\n }\n\n function useProductFormHook(): UseProductFormResult<TProduct> {\n const { store, onSelectRef } = useProductContext(\"useProductForm\");\n return useProductForm<TProduct>(store, { onSelect: onSelectRef.current });\n }\n\n return {\n ProductProvider,\n useProduct: useProductHook,\n useProductForm: useProductFormHook,\n };\n}\n\n/**\n * Returns product form state and bindings from a product form store that you manage, and re-renders when the state changes.\n *\n * Create the store with `createProductFormStore`. Call `connect()` on mount, `hydrate()` when the product data changes, and `destroy()` on unmount. To skip the store setup, use the product provider from `createProductComponents`.\n *\n * @publicDocs\n */\nexport type UseProductFormForDocs =\n /**\n * @param store - The store that `createProductFormStore` returns.\n * @param options - An `onSelect` callback that runs after each resolved or unresolved selection.\n * @returns The option state, the selected variant, cart errors, the matching cart line, the pending state, the register function, and the form props.\n */\n (\n store: ProductFormStore,\n options?: UseProductFormOptions<ProductInput>,\n ) => UseProductFormResult<ProductInput>;\n"],"mappings":"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"}
|
|
@@ -2,7 +2,22 @@ import { ShopPayButtonOptions } from "../core/shop-pay/shop-pay.mjs";
|
|
|
2
2
|
import { ReactElement } from "react";
|
|
3
3
|
|
|
4
4
|
//#region src/react/shop-pay.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Props for the `ShopPayButton` component. The props match the options of `createShopPayButton`.
|
|
7
|
+
* The component doesn't accept `className` or `style` props.
|
|
8
|
+
*
|
|
9
|
+
* @publicDocs
|
|
10
|
+
*/
|
|
5
11
|
type ShopPayButtonProps = ShopPayButtonOptions;
|
|
12
|
+
/**
|
|
13
|
+
* Renders a Shop Pay button that takes the customer to checkout with Shop Pay. The button renders on the server and shows before scripts load.
|
|
14
|
+
*
|
|
15
|
+
* Set the size and shape with the `width` and `borderRadius` props. The component doesn't accept `className` or `style` props.
|
|
16
|
+
*
|
|
17
|
+
* @param options - The checkout target, variants, attribution, and display settings for the button.
|
|
18
|
+
* @returns The Shop Pay button element.
|
|
19
|
+
* @publicDocs
|
|
20
|
+
*/
|
|
6
21
|
declare function ShopPayButton(options: ShopPayButtonProps): ReactElement;
|
|
7
22
|
//#endregion
|
|
8
23
|
export { ShopPayButton, ShopPayButtonProps };
|
package/dist/react/shop-pay.mjs
CHANGED
|
@@ -4,6 +4,15 @@ import { createElement } from "react";
|
|
|
4
4
|
//#region src/react/shop-pay.tsx
|
|
5
5
|
const canUseDom = typeof document !== "undefined";
|
|
6
6
|
defineShopPayButton();
|
|
7
|
+
/**
|
|
8
|
+
* Renders a Shop Pay button that takes the customer to checkout with Shop Pay. The button renders on the server and shows before scripts load.
|
|
9
|
+
*
|
|
10
|
+
* Set the size and shape with the `width` and `borderRadius` props. The component doesn't accept `className` or `style` props.
|
|
11
|
+
*
|
|
12
|
+
* @param options - The checkout target, variants, attribution, and display settings for the button.
|
|
13
|
+
* @returns The Shop Pay button element.
|
|
14
|
+
* @publicDocs
|
|
15
|
+
*/
|
|
7
16
|
function ShopPayButton(options) {
|
|
8
17
|
return createElement(SHOP_PAY_BUTTON_TAG_NAME, {
|
|
9
18
|
...getShopPayButtonElementAttributes(options),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shop-pay.mjs","names":[],"sources":["../../src/react/shop-pay.tsx"],"sourcesContent":["import { createElement, type ReactElement } from \"react\";\n\nimport {\n defineShopPayButton,\n getShopPayButtonDeclarativeShadowDomHtml,\n getShopPayButtonElementAttributes,\n SHOP_PAY_BUTTON_TAG_NAME,\n type ShopPayButtonOptions,\n} from \"../core/shop-pay/shop-pay\";\n\nexport type ShopPayButtonProps = ShopPayButtonOptions;\n\nconst canUseDom = typeof document !== \"undefined\";\ndefineShopPayButton();\n\nexport function ShopPayButton(options: ShopPayButtonProps): ReactElement {\n return createElement(SHOP_PAY_BUTTON_TAG_NAME, {\n ...getShopPayButtonElementAttributes(options),\n ...(!canUseDom\n ? {\n dangerouslySetInnerHTML: {\n __html: getShopPayButtonDeclarativeShadowDomHtml(options),\n },\n }\n : {}),\n });\n}\n"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"shop-pay.mjs","names":[],"sources":["../../src/react/shop-pay.tsx"],"sourcesContent":["import { createElement, type ReactElement } from \"react\";\n\nimport {\n defineShopPayButton,\n getShopPayButtonDeclarativeShadowDomHtml,\n getShopPayButtonElementAttributes,\n SHOP_PAY_BUTTON_TAG_NAME,\n type ShopPayButtonOptions,\n} from \"../core/shop-pay/shop-pay\";\n\n/**\n * Props for the `ShopPayButton` component. The props match the options of `createShopPayButton`.\n * The component doesn't accept `className` or `style` props.\n *\n * @publicDocs\n */\nexport type ShopPayButtonProps = ShopPayButtonOptions;\n\nconst canUseDom = typeof document !== \"undefined\";\ndefineShopPayButton();\n\n/**\n * Renders a Shop Pay button that takes the customer to checkout with Shop Pay. The button renders on the server and shows before scripts load.\n *\n * Set the size and shape with the `width` and `borderRadius` props. The component doesn't accept `className` or `style` props.\n *\n * @param options - The checkout target, variants, attribution, and display settings for the button.\n * @returns The Shop Pay button element.\n * @publicDocs\n */\nexport function ShopPayButton(options: ShopPayButtonProps): ReactElement {\n return createElement(SHOP_PAY_BUTTON_TAG_NAME, {\n ...getShopPayButtonElementAttributes(options),\n ...(!canUseDom\n ? {\n dangerouslySetInnerHTML: {\n __html: getShopPayButtonDeclarativeShadowDomHtml(options),\n },\n }\n : {}),\n });\n}\n"],"mappings":";;;;AAkBA,MAAM,YAAY,OAAO,aAAa;AACtC,qBAAqB;;;;;;;;;;AAWrB,SAAgB,cAAc,SAA2C;AACvE,QAAO,cAAc,0BAA0B;EAC7C,GAAG,kCAAkC,QAAQ;EAC7C,GAAI,CAAC,YACD,EACE,yBAAyB,EACvB,QAAQ,yCAAyC,QAAQ,EAC1D,EACF,GACD,EAAE;EACP,CAAC"}
|