@kotao/storefront 0.2.5 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +57 -2
  2. package/dist/bridge-CfNBWu0H.d.ts +260 -0
  3. package/dist/cart-documents-BLavRDiu.d.ts +89 -0
  4. package/dist/cart-documents.d.ts +1 -2
  5. package/dist/cart-documents.js +1 -1
  6. package/dist/{chunk-M7BPQ54D.js → chunk-2MRB6SMP.js} +4 -3
  7. package/dist/chunk-2MRB6SMP.js.map +1 -0
  8. package/dist/chunk-CKGI3Y7L.js +19 -0
  9. package/dist/chunk-CKGI3Y7L.js.map +1 -0
  10. package/dist/{chunk-GGOZELAA.js → chunk-DTKLAOKR.js} +231 -165
  11. package/dist/chunk-DTKLAOKR.js.map +1 -0
  12. package/dist/{chunk-MCIFKJQK.js → chunk-R4N565GX.js} +5 -2
  13. package/dist/chunk-R4N565GX.js.map +1 -0
  14. package/dist/chunk-RFKLO6TD.js +1594 -0
  15. package/dist/chunk-RFKLO6TD.js.map +1 -0
  16. package/dist/{chunk-XMTWFJCZ.js → chunk-RFM27BKZ.js} +31 -7
  17. package/dist/chunk-RFM27BKZ.js.map +1 -0
  18. package/dist/{chunk-MYDKIJRH.js → chunk-SVMYFYIA.js} +2 -2
  19. package/dist/{chunk-MYDKIJRH.js.map → chunk-SVMYFYIA.js.map} +1 -1
  20. package/dist/{chunk-H3D7BAOL.js → chunk-VOTBNOWH.js} +21 -3
  21. package/dist/chunk-VOTBNOWH.js.map +1 -0
  22. package/dist/{chunk-PXVDVUFX.js → chunk-W74FGUZH.js} +10 -3
  23. package/dist/chunk-W74FGUZH.js.map +1 -0
  24. package/dist/{chunk-2IWNUDSZ.js → chunk-WNXI3MPM.js} +2 -2
  25. package/dist/chunk-WNXI3MPM.js.map +1 -0
  26. package/dist/chunk-XXNCFWXT.js +2118 -0
  27. package/dist/chunk-XXNCFWXT.js.map +1 -0
  28. package/dist/{client-bsiNPHBi.d.ts → client-CwOPPpFF.d.ts} +11 -1
  29. package/dist/{createCartHandler-C4RrPwSG.d.ts → createCartHandler-7DdKB-GF.d.ts} +1 -1
  30. package/dist/custom-css-Bl7Ur3-H.d.ts +6 -0
  31. package/dist/editor.d.ts +57 -104
  32. package/dist/editor.js +270 -1
  33. package/dist/editor.js.map +1 -1
  34. package/dist/events-react.d.ts +4 -2
  35. package/dist/events-react.js +6 -4
  36. package/dist/events-react.js.map +1 -1
  37. package/dist/events.d.ts +3 -3
  38. package/dist/events.js +1 -1
  39. package/dist/{fetch-M3Y-fQrW.d.ts → fetch-Bq_cc8_G.d.ts} +5 -0
  40. package/dist/hydrogen-CqDMW3YC.d.ts +259 -0
  41. package/dist/index.d.ts +75 -51
  42. package/dist/index.js +19 -15
  43. package/dist/index.js.map +1 -1
  44. package/dist/react-router.d.ts +40 -9
  45. package/dist/react-router.js +16 -7
  46. package/dist/react-router.js.map +1 -1
  47. package/dist/react.d.ts +79 -11
  48. package/dist/react.js +279 -10
  49. package/dist/react.js.map +1 -1
  50. package/dist/{routing-sEonP93J.d.ts → routing-Bz3Cg0BW.d.ts} +36 -6
  51. package/dist/seo.d.ts +7 -4
  52. package/dist/seo.js +2 -2
  53. package/dist/settings.d.ts +14 -4
  54. package/dist/settings.js +2 -2
  55. package/dist/{shop-BJSvtmnV.d.ts → shop-CnrnSAJw.d.ts} +33 -6
  56. package/dist/{store-CtCWHI8H.d.ts → store-ZzoBbWir.d.ts} +1 -1
  57. package/dist/storefront-api-types.d.ts +12 -0
  58. package/dist/{types-BDhHWZsP.d.ts → types-BkY0yET-.d.ts} +3 -3
  59. package/dist/{types-bcbQzx87.d.ts → types-DZX8ScsJ.d.ts} +2 -0
  60. package/package.json +4 -4
  61. package/dist/cart-documents-CwwRTvm8.d.ts +0 -21
  62. package/dist/chunk-2IWNUDSZ.js.map +0 -1
  63. package/dist/chunk-GGOZELAA.js.map +0 -1
  64. package/dist/chunk-H3D7BAOL.js.map +0 -1
  65. package/dist/chunk-M7BPQ54D.js.map +0 -1
  66. package/dist/chunk-MCIFKJQK.js.map +0 -1
  67. package/dist/chunk-PXVDVUFX.js.map +0 -1
  68. package/dist/chunk-PXZ737N3.js +0 -439
  69. package/dist/chunk-PXZ737N3.js.map +0 -1
  70. package/dist/chunk-XMTWFJCZ.js.map +0 -1
package/dist/react.d.ts CHANGED
@@ -1,16 +1,19 @@
1
- import react__default, { ReactNode, ReactElement, ImgHTMLAttributes, VideoHTMLAttributes, ElementType, AnchorHTMLAttributes, CSSProperties, ComponentPropsWithoutRef } from 'react';
1
+ import react__default, { ReactNode, ReactElement, ImgHTMLAttributes, VideoHTMLAttributes, ElementType, AnchorHTMLAttributes, CSSProperties, ComponentPropsWithoutRef, ComponentType } from 'react';
2
2
  import { FetcherWithComponents } from 'react-router';
3
- import { j as CartFormAction, a as CartLineInput } from './types-bcbQzx87.js';
4
- import { g as getCartFormInput, S as ShopAnalytics, A as AnalyticsContext, C as ConsentState, a as AnalyticsEmitter, b as AnalyticsBus } from './shop-BJSvtmnV.js';
5
- export { u as useNonce } from './shop-BJSvtmnV.js';
3
+ import { j as CartFormAction, a as CartLineInput } from './types-DZX8ScsJ.js';
4
+ import { g as getCartFormInput, S as ShopAnalytics, A as AnalyticsContext, C as ConsentState, a as AnalyticsEmitter, b as AnalyticsBus } from './shop-CnrnSAJw.js';
5
+ export { u as useNonce } from './shop-CnrnSAJw.js';
6
6
  import { Cart } from './storefront-api-types.js';
7
7
  import { ImageAttrsOptions, ImageData, ImageCrop, ImageFormat } from './image.js';
8
8
  import { C as Connection, P as PaginationInfo } from './pagination-DCrQk02Q.js';
9
- import { S as SeoConfig } from './types-BDhHWZsP.js';
10
- import { T as ThemeSettingsStore, R as ResolvedSettings, a as ThemeSettingsSnapshot } from './store-CtCWHI8H.js';
11
- import { SmartCartMountConfig, CartAdapter } from '@kotao/smart-cart';
12
- export { R as REACT_SEAM_CART_DOCUMENTS, c as createStorefrontApiCartAdapter } from './cart-documents-CwwRTvm8.js';
9
+ import { S as SeoConfig } from './types-BkY0yET-.js';
10
+ import { T as ThemeSettingsStore, R as ResolvedSettings, c as ThemeSettingsSnapshot } from './store-ZzoBbWir.js';
11
+ import { S as SmartCartMountConfig, C as CartAdapter } from './cart-documents-BLavRDiu.js';
12
+ export { R as REACT_SEAM_CART_DOCUMENTS, a as SmartCartBranding, b as SmartCartLine, c as SmartCartLineDiscount, d as SmartCartLineOption, e as SmartCartMoney, f as SmartCartState, g as createStorefrontApiCartAdapter } from './cart-documents-BLavRDiu.js';
13
13
  import { P as PublicForm, a as FormAnswers, b as FormFieldErrors } from './types-CSNXbGFW.js';
14
+ import { a as HydrogenEditorPreviewRuntime, x as HydrogenEditorReconciliationReason, l as HydrogenEditorInstanceSnapshot } from './hydrogen-CqDMW3YC.js';
15
+ import '@kotao/storefront-contracts';
16
+ import './bridge-CfNBWu0H.js';
14
17
 
15
18
  type CartFormInputValue = unknown;
16
19
  type CartFormFetcher = FetcherWithComponents<unknown>;
@@ -71,7 +74,7 @@ interface AnalyticsProviderProps {
71
74
  readonly shopId?: string;
72
75
  readonly shop?: ShopAnalytics;
73
76
  readonly geo?: AnalyticsContext['geo'];
74
- /** Current page URL; changing it auto-emits `page_viewed` (themes pass it from RR7 navigation). */
77
+ /** Current page URL; changing it auto-emits `page_viewed` (themes pass it from RR8 navigation). */
75
78
  readonly url?: string;
76
79
  /** Same-origin pixel endpoint (default '/api/storefront/pixel'). */
77
80
  readonly pixelEndpoint?: string;
@@ -111,9 +114,11 @@ interface AnalyticsProductViewProps {
111
114
  readonly products?: readonly {
112
115
  readonly handle?: string | null;
113
116
  readonly id?: string | null;
114
- readonly price?: string | null;
117
+ readonly price?: number | string | null;
115
118
  readonly productId?: string | null;
116
119
  readonly quantity?: number | null;
120
+ readonly sku?: string | null;
121
+ readonly currency?: string | null;
117
122
  readonly title?: string | null;
118
123
  readonly variantId?: string | null;
119
124
  readonly variantTitle?: string | null;
@@ -319,4 +324,67 @@ interface StorefrontFormFieldsProps {
319
324
  declare function StorefrontFormFields({ form, locale, fallbackLocale, answers: initialAnswers, errors, onAnswersChange, idPrefix, }: StorefrontFormFieldsProps): react__default.JSX.Element;
320
325
  declare function StorefrontForm({ form, locale, fallbackLocale, answers, errors, submitted, submitLabel, onAnswersChange, action, method, ...props }: StorefrontFormProps): react__default.JSX.Element;
321
326
 
322
- export { Analytics, AnalyticsCartView, type AnalyticsCartViewProps, AnalyticsCollectionView, type AnalyticsCollectionViewProps, AnalyticsProductView, type AnalyticsProductViewProps, AnalyticsProvider, type AnalyticsProviderProps, AnalyticsSearchView, type AnalyticsSearchViewProps, CartDrawerOverlay, type CartDrawerOverlayProps, CartForm, type CartFormFetcher, type CartFormProps, type CartQueryDataReturn, type CartReturn, ConsentBanner, type ConsentBannerProps, type ConsentBannerRenderProps, Image, type ImageProps, MediaFile, type MediaFileImageOptions, type MediaFileProps, type MediaFileVideoOptions, Money, type MoneyData, type MoneyProps, type OptimisticCartLineInput, OptimisticInput, type OptimisticInputData, type OptimisticInputProps, Pagination, type PaginationProps, Seo, type SeoProps, ShopPayButton, type ShopPayButtonProps, StorefrontForm, StorefrontFormFields, type StorefrontFormFieldsProps, type StorefrontFormProps, ThemeSettingsProvider, type ThemeSettingsProviderProps, type UseMoneyValue, openSmartCart, useAnalytics, useGlobalThemeTokens, useMoney, useOptimisticData, useSectionSettings, useThemeSettings };
327
+ interface HydrogenEditorRegisteredComponentProps {
328
+ readonly instanceId: string;
329
+ readonly props: Readonly<Record<string, unknown>>;
330
+ readonly styles: Readonly<Record<string, string | number>>;
331
+ readonly slots: Readonly<Record<string, readonly string[]>>;
332
+ readonly variantId?: string;
333
+ }
334
+ type HydrogenEditorRegisteredComponent = ComponentType<HydrogenEditorRegisteredComponentProps>;
335
+ interface HydrogenEditorProviderProps {
336
+ readonly preview: HydrogenEditorPreviewRuntime;
337
+ readonly documentId: string;
338
+ readonly route?: string;
339
+ readonly reconciliationReason?: HydrogenEditorReconciliationReason;
340
+ /**
341
+ * Changes when same-route loader data or an HMR generation reconciles. It is deliberately not
342
+ * interpreted as authority and is never sent across the bridge.
343
+ */
344
+ readonly reconciliationKey?: string | number;
345
+ readonly components?: Readonly<Record<string, HydrogenEditorRegisteredComponent>>;
346
+ readonly viewportWidth?: number;
347
+ readonly children: ReactNode;
348
+ }
349
+ /**
350
+ * Activates the editor runtime for a route. Route/loader/HMR changes reconcile the existing
351
+ * preview store instead of replacing it, so source-backed draft operations and selection remain
352
+ * visible. The provider is editor-only and must not be mounted in ordinary public responses.
353
+ */
354
+ declare function HydrogenEditorProvider({ children, components, documentId, preview, reconciliationKey, reconciliationReason, route, viewportWidth, }: HydrogenEditorProviderProps): ReactElement;
355
+ interface HydrogenEditorReactInstance {
356
+ readonly snapshot: HydrogenEditorInstanceSnapshot;
357
+ readonly props: Readonly<Record<string, unknown>>;
358
+ readonly styles: Readonly<Record<string, string | number>>;
359
+ }
360
+ /**
361
+ * Subscribes to exactly one component instance. A prop/style operation invalidates only this
362
+ * subscription; structural operations additionally invalidate the affected parent/slot.
363
+ */
364
+ declare function useHydrogenEditorInstance(instanceId: string, sourceProps?: Readonly<Record<string, unknown>>): HydrogenEditorReactInstance | null;
365
+ declare function useHydrogenEditorSlot(parentInstanceId: string, slotId: string): readonly string[];
366
+ interface HydrogenEditorBoundaryProps {
367
+ readonly instanceId: string;
368
+ readonly children: ReactElement;
369
+ readonly applyPreviewProps?: boolean;
370
+ readonly viewportWidth?: number;
371
+ }
372
+ /**
373
+ * A reconciliation and interaction boundary for one extracted JSX instance. It uses a
374
+ * `display: contents` marker only in editor mode, preserving the component's layout while clicks
375
+ * and pointer transitions remain attributable to the stable instance ID.
376
+ */
377
+ declare function HydrogenEditorBoundary({ applyPreviewProps, children, instanceId, viewportWidth, }: HydrogenEditorBoundaryProps): ReactElement | null;
378
+ interface HydrogenEditorSlotProps {
379
+ readonly parentInstanceId: string;
380
+ readonly slotId: string;
381
+ /**
382
+ * Source-rendered children keyed by stable instance ID. Newly inserted instances fall through to
383
+ * the registered component library without replacing or regenerating existing route source.
384
+ */
385
+ readonly existingChildren?: Readonly<Record<string, ReactElement>>;
386
+ readonly empty?: ReactNode;
387
+ }
388
+ declare function HydrogenEditorSlot({ empty, existingChildren, parentInstanceId, slotId, }: HydrogenEditorSlotProps): ReactElement;
389
+
390
+ export { Analytics, AnalyticsCartView, type AnalyticsCartViewProps, AnalyticsCollectionView, type AnalyticsCollectionViewProps, AnalyticsProductView, type AnalyticsProductViewProps, AnalyticsProvider, type AnalyticsProviderProps, AnalyticsSearchView, type AnalyticsSearchViewProps, CartAdapter, CartDrawerOverlay, type CartDrawerOverlayProps, CartForm, type CartFormFetcher, type CartFormProps, type CartQueryDataReturn, type CartReturn, ConsentBanner, type ConsentBannerProps, type ConsentBannerRenderProps, HydrogenEditorBoundary, type HydrogenEditorBoundaryProps, HydrogenEditorProvider, type HydrogenEditorProviderProps, type HydrogenEditorReactInstance, type HydrogenEditorRegisteredComponent, type HydrogenEditorRegisteredComponentProps, HydrogenEditorSlot, type HydrogenEditorSlotProps, Image, type ImageProps, MediaFile, type MediaFileImageOptions, type MediaFileProps, type MediaFileVideoOptions, Money, type MoneyData, type MoneyProps, type OptimisticCartLineInput, OptimisticInput, type OptimisticInputData, type OptimisticInputProps, Pagination, type PaginationProps, Seo, type SeoProps, ShopPayButton, type ShopPayButtonProps, SmartCartMountConfig, StorefrontForm, StorefrontFormFields, type StorefrontFormFieldsProps, type StorefrontFormProps, ThemeSettingsProvider, type ThemeSettingsProviderProps, type UseMoneyValue, openSmartCart, useAnalytics, useGlobalThemeTokens, useHydrogenEditorInstance, useHydrogenEditorSlot, useMoney, useOptimisticData, useSectionSettings, useThemeSettings };
package/dist/react.js CHANGED
@@ -1,13 +1,14 @@
1
- import { getCartFormInput, createAnalyticsBus, createPixelTransport } from './chunk-XMTWFJCZ.js';
2
- export { useNonce } from './chunk-XMTWFJCZ.js';
1
+ import { getCartFormInput, createAnalyticsBus, collectAnalyticsAttribution, createPixelTransport } from './chunk-RFM27BKZ.js';
2
+ export { useNonce } from './chunk-RFM27BKZ.js';
3
3
  import { getPaginationInfo } from './chunk-5QOFKGKD.js';
4
4
  import { CART_FORM_ACTIONS, GRANTED_CONSENT, CONSENT_COOKIE, readConsentCookie, mergeConsent, buildConsentCookie } from './chunk-OGWYTYYS.js';
5
- import { getSeoMeta, serializeJsonLd } from './chunk-MYDKIJRH.js';
6
- export { REACT_SEAM_CART_DOCUMENTS, createStorefrontApiCartAdapter } from './chunk-2IWNUDSZ.js';
5
+ import { getSeoMeta, serializeJsonLd } from './chunk-SVMYFYIA.js';
6
+ export { REACT_SEAM_CART_DOCUMENTS, createStorefrontApiCartAdapter } from './chunk-WNXI3MPM.js';
7
+ import { resolveHydrogenEditorStyles } from './chunk-XXNCFWXT.js';
7
8
  export { StorefrontForm, StorefrontFormFields } from './chunk-VV653S52.js';
8
9
  import './chunk-IIOHENCG.js';
9
10
  import { buildImageAttrs } from './chunk-3YQXJNR7.js';
10
- import { createContext, useState, useRef, useMemo, useEffect, useCallback, useContext, useSyncExternalStore } from 'react';
11
+ import { createContext, useState, useRef, useMemo, useEffect, useCallback, useContext, useSyncExternalStore, Children, isValidElement, cloneElement, createElement } from 'react';
11
12
  import { useFetcher } from 'react-router';
12
13
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
13
14
 
@@ -37,6 +38,22 @@ function OptimisticInput({ id, data }) {
37
38
  function useOptimisticData(_id) {
38
39
  return void 0;
39
40
  }
41
+
42
+ // src/analytics/anonymous-context.ts
43
+ function anonymousPageContext(candidateUrl, currentUrl) {
44
+ if (!candidateUrl || !currentUrl) return {};
45
+ try {
46
+ const current = new URL(currentUrl);
47
+ const candidate = new URL(candidateUrl, current);
48
+ if (!["http:", "https:"].includes(current.protocol)) return {};
49
+ if (!["http:", "https:"].includes(candidate.protocol)) return {};
50
+ if (candidate.origin !== current.origin) return {};
51
+ const pathname = /^\/checkout(?:\/|$)/.test(candidate.pathname) ? "/checkout" : candidate.pathname || "/";
52
+ return { url: `${candidate.origin}${pathname}` };
53
+ } catch {
54
+ return {};
55
+ }
56
+ }
40
57
  var beaconSend = (endpoint, body) => {
41
58
  if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
42
59
  if (navigator.sendBeacon(endpoint, new Blob([body], { type: "application/json" }))) return;
@@ -57,12 +74,21 @@ function AnalyticsProvider(props) {
57
74
  const [mounted, setMounted] = useState(false);
58
75
  const consentRef = useRef(consent);
59
76
  consentRef.current = consent;
77
+ const pageUrlRef = useRef(props.url);
78
+ pageUrlRef.current = props.url;
60
79
  const contextRef = useRef({});
61
80
  const shopId = props.shopId ?? props.shop?.shopId ?? props.shop?.storefrontId;
81
+ const browserUrl = typeof window === "undefined" ? void 0 : (() => {
82
+ try {
83
+ return new URL(props.url ?? window.location.href, window.location.href).href;
84
+ } catch {
85
+ return props.url;
86
+ }
87
+ })();
62
88
  contextRef.current = {
63
89
  ...shopId !== void 0 ? { shopId } : {},
64
90
  ...props.geo !== void 0 ? { geo: props.geo } : {},
65
- ...props.url !== void 0 ? { url: props.url } : {}
91
+ ...browserUrl !== void 0 ? { url: browserUrl } : props.url !== void 0 ? { url: props.url } : {}
66
92
  };
67
93
  const endpointRef = useRef(props.pixelEndpoint);
68
94
  const bus = useMemo(
@@ -72,7 +98,25 @@ function AnalyticsProvider(props) {
72
98
  ...endpointRef.current !== void 0 ? { endpoint: endpointRef.current } : {}
73
99
  }),
74
100
  getConsent: () => consentRef.current,
75
- getContext: () => contextRef.current
101
+ getAnonymousContext: () => {
102
+ if (typeof window === "undefined") return {};
103
+ return anonymousPageContext(
104
+ pageUrlRef.current ?? window.location.href,
105
+ window.location.href
106
+ );
107
+ },
108
+ // createAnalyticsBus calls this only after an explicit analytics grant. Keep every browser
109
+ // attribution/device read inside this lazy seam so denied visits never evaluate it.
110
+ getContext: () => ({
111
+ ...collectAnalyticsAttribution(
112
+ typeof window === "undefined" || typeof document === "undefined" ? null : {
113
+ referrer: document.referrer,
114
+ search: window.location.search,
115
+ ...typeof window.matchMedia === "function" ? { matchMedia: (query) => window.matchMedia(query) } : {}
116
+ }
117
+ ),
118
+ ...contextRef.current
119
+ })
76
120
  }),
77
121
  []
78
122
  );
@@ -144,13 +188,22 @@ function AnalyticsProductView({ data }) {
144
188
  const product = data?.products?.[0];
145
189
  const productId = product?.productId ?? product?.id ?? null;
146
190
  const handle = product?.handle ?? void 0;
191
+ const price = product?.price == null ? void 0 : Number(product.price);
147
192
  const payload = useMemo(() => {
148
193
  if (productId == null) return null;
149
194
  return {
150
195
  productId,
151
- ...handle != null ? { handle } : {}
196
+ ...handle != null ? { handle } : {},
197
+ ...product?.variantId != null ? { variantId: product.variantId } : {},
198
+ ...product?.title != null ? { title: product.title } : {},
199
+ ...product?.variantTitle != null ? { variantTitle: product.variantTitle } : {},
200
+ ...product?.vendor != null ? { vendor: product.vendor } : {},
201
+ ...product?.sku != null ? { sku: product.sku } : {},
202
+ ...price !== void 0 && Number.isFinite(price) ? { price } : {},
203
+ ...product?.currency != null ? { currency: product.currency } : {},
204
+ ...product?.quantity != null ? { quantity: product.quantity } : {}
152
205
  };
153
- }, [handle, productId]);
206
+ }, [handle, price, product, productId]);
154
207
  usePublishAnalytics("product_viewed", payload);
155
208
  return null;
156
209
  }
@@ -1948,7 +2001,223 @@ function CartDrawerOverlay({ config, adapter }) {
1948
2001
  }, [config, adapter]);
1949
2002
  return null;
1950
2003
  }
2004
+ var HydrogenEditorReactContext = createContext(null);
2005
+ var HYDROGEN_CANVAS_INTENT_EVENT = "kotao:hydrogen-canvas-intent";
2006
+ var HYDROGEN_CANVAS_RESULT_EVENT = "kotao:hydrogen-canvas-result";
2007
+ var HYDROGEN_CANVAS_STATE_EVENT = "kotao:hydrogen-canvas-state";
2008
+ var HYDROGEN_CANVAS_STATE_REQUEST_EVENT = "kotao:hydrogen-canvas-state-request";
2009
+ function useHydrogenEditorCanvasEvents(preview) {
2010
+ const snapshot = useSyncExternalStore(
2011
+ (listener) => preview.store.subscribe(listener),
2012
+ () => preview.store.getSnapshot(),
2013
+ () => preview.store.getSnapshot()
2014
+ );
2015
+ useEffect(() => {
2016
+ if (typeof document === "undefined" || typeof CustomEvent === "undefined") return;
2017
+ const dispatchState = () => {
2018
+ document.dispatchEvent(
2019
+ new CustomEvent(HYDROGEN_CANVAS_STATE_EVENT, {
2020
+ detail: {
2021
+ selectedInstanceId: snapshot.selectedInstanceId,
2022
+ hoveredInstanceId: snapshot.hoveredInstanceId,
2023
+ selectionStatus: snapshot.selectionStatus,
2024
+ revision: snapshot.revision
2025
+ }
2026
+ })
2027
+ );
2028
+ };
2029
+ dispatchState();
2030
+ document.addEventListener(HYDROGEN_CANVAS_STATE_REQUEST_EVENT, dispatchState);
2031
+ return () => document.removeEventListener(HYDROGEN_CANVAS_STATE_REQUEST_EVENT, dispatchState);
2032
+ }, [
2033
+ snapshot.hoveredInstanceId,
2034
+ snapshot.revision,
2035
+ snapshot.selectedInstanceId,
2036
+ snapshot.selectionStatus
2037
+ ]);
2038
+ useEffect(() => {
2039
+ if (typeof document === "undefined" || typeof CustomEvent === "undefined") return;
2040
+ const onIntent = (event) => {
2041
+ if (!(event instanceof CustomEvent)) return;
2042
+ const result = preview.dispatchCanvasIntent(event.detail);
2043
+ document.dispatchEvent(new CustomEvent(HYDROGEN_CANVAS_RESULT_EVENT, { detail: result }));
2044
+ };
2045
+ document.addEventListener(HYDROGEN_CANVAS_INTENT_EVENT, onIntent);
2046
+ return () => document.removeEventListener(HYDROGEN_CANVAS_INTENT_EVENT, onIntent);
2047
+ }, [preview]);
2048
+ }
2049
+ function HydrogenEditorProvider({
2050
+ children,
2051
+ components = {},
2052
+ documentId,
2053
+ preview,
2054
+ reconciliationKey,
2055
+ reconciliationReason = "route",
2056
+ route,
2057
+ viewportWidth
2058
+ }) {
2059
+ useHydrogenEditorCanvasEvents(preview);
2060
+ const value = useMemo(
2061
+ () => ({
2062
+ preview,
2063
+ components,
2064
+ ...viewportWidth !== void 0 ? { viewportWidth } : {}
2065
+ }),
2066
+ [components, preview, viewportWidth]
2067
+ );
2068
+ useEffect(() => {
2069
+ preview.reconcile(documentId, reconciliationReason, route);
2070
+ preview.ready();
2071
+ }, [documentId, preview, reconciliationKey, reconciliationReason, route]);
2072
+ return /* @__PURE__ */ jsx(HydrogenEditorReactContext.Provider, { value, children });
2073
+ }
2074
+ function useHydrogenEditorContext() {
2075
+ const context = useContext(HydrogenEditorReactContext);
2076
+ if (context === null) {
2077
+ throw new Error("Hydrogen editor hooks must be rendered inside HydrogenEditorProvider");
2078
+ }
2079
+ return context;
2080
+ }
2081
+ function useInstanceSnapshot(instanceId) {
2082
+ const { preview } = useHydrogenEditorContext();
2083
+ return useSyncExternalStore(
2084
+ (listener) => preview.store.subscribeInstance(instanceId, listener),
2085
+ () => preview.store.getInstanceSnapshot(instanceId),
2086
+ () => preview.store.getInstanceSnapshot(instanceId)
2087
+ );
2088
+ }
2089
+ function useHydrogenEditorInstance(instanceId, sourceProps = {}) {
2090
+ const { preview, viewportWidth } = useHydrogenEditorContext();
2091
+ const snapshot = useInstanceSnapshot(instanceId);
2092
+ return useMemo(() => {
2093
+ if (snapshot === null) return null;
2094
+ return {
2095
+ snapshot,
2096
+ props: { ...sourceProps, ...snapshot.resolvedProps },
2097
+ styles: resolveHydrogenEditorStyles(preview.manifest, snapshot.instance, viewportWidth)
2098
+ };
2099
+ }, [preview.manifest, snapshot, sourceProps, viewportWidth]);
2100
+ }
2101
+ function useHydrogenEditorSlot(parentInstanceId, slotId) {
2102
+ const snapshot = useInstanceSnapshot(parentInstanceId);
2103
+ return snapshot?.childInstanceIds[slotId] ?? [];
2104
+ }
2105
+ function HydrogenEditorBoundary({
2106
+ applyPreviewProps = true,
2107
+ children,
2108
+ instanceId,
2109
+ viewportWidth
2110
+ }) {
2111
+ const { preview, viewportWidth: contextViewportWidth } = useHydrogenEditorContext();
2112
+ const snapshot = useInstanceSnapshot(instanceId);
2113
+ useEffect(() => preview.registerInstance(instanceId), [instanceId, preview]);
2114
+ if (snapshot === null) return null;
2115
+ const child = Children.only(children);
2116
+ if (!isValidElement(child)) return null;
2117
+ const effectiveViewportWidth = viewportWidth ?? contextViewportWidth;
2118
+ const previewProps = applyPreviewProps ? snapshot.resolvedProps : {};
2119
+ const childStyle = isRecordStyle(child.props.style) ? child.props.style : {};
2120
+ const previewStyle = applyPreviewProps ? resolveHydrogenEditorStyles(preview.manifest, snapshot.instance, effectiveViewportWidth) : {};
2121
+ const renderedChild = cloneElement(child, {
2122
+ ...previewProps,
2123
+ ...Object.keys(previewStyle).length > 0 ? { style: { ...childStyle, ...previewStyle } } : {}
2124
+ });
2125
+ const blockingReason = snapshot.capabilityReasons.find((reason) => reason.severity === "blocking");
2126
+ const inlineProps = snapshot.component.props.filter(
2127
+ (control) => control.allowedBindings.includes("inline") && (snapshot.instance.props[control.propName] ?? control.defaultBinding)?.kind !== "external" && (control.control === "text" || control.control === "textarea" || control.control === "richtext")
2128
+ ).map((control) => ({ propName: control.propName, label: control.label }));
2129
+ const layoutControls = snapshot.component.styles.layout.filter((control) => ["gap", "height", "margin", "padding", "width"].includes(control.control)).map((control) => ({
2130
+ controlId: control.controlId,
2131
+ control: control.control,
2132
+ label: control.label,
2133
+ allowedValues: control.allowedValues
2134
+ }));
2135
+ const onPointerEnter = (_event) => {
2136
+ preview.hoverInstance(instanceId);
2137
+ };
2138
+ const onPointerLeave = (_event) => {
2139
+ preview.hoverInstance(null);
2140
+ };
2141
+ const onClick = (event) => {
2142
+ event.preventDefault();
2143
+ event.stopPropagation();
2144
+ preview.selectInstance(instanceId);
2145
+ };
2146
+ return /* @__PURE__ */ jsx(
2147
+ "span",
2148
+ {
2149
+ "data-kotao-hydrogen-capability": snapshot.editable ? "editable" : "read-only",
2150
+ "data-kotao-hydrogen-capabilities": snapshot.component.capabilities.join(" "),
2151
+ "data-kotao-hydrogen-component": snapshot.component.componentId,
2152
+ "data-kotao-hydrogen-instance": instanceId,
2153
+ "data-kotao-hydrogen-inline-props": JSON.stringify(inlineProps),
2154
+ "data-kotao-hydrogen-label": snapshot.component.name,
2155
+ "data-kotao-hydrogen-layout-controls": JSON.stringify(layoutControls),
2156
+ "data-kotao-hydrogen-reason": blockingReason?.code,
2157
+ onClick,
2158
+ onPointerEnter,
2159
+ onPointerLeave,
2160
+ style: { display: "contents" },
2161
+ children: renderedChild
2162
+ }
2163
+ );
2164
+ }
2165
+ function isRecordStyle(value) {
2166
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2167
+ }
2168
+ function HydrogenEditorRegisteredInstance({
2169
+ instanceId
2170
+ }) {
2171
+ const { components } = useHydrogenEditorContext();
2172
+ const instance = useHydrogenEditorInstance(instanceId);
2173
+ if (instance === null) return null;
2174
+ const Component = components[instance.snapshot.component.componentId];
2175
+ if (Component === void 0) {
2176
+ return /* @__PURE__ */ jsx(
2177
+ "span",
2178
+ {
2179
+ "data-kotao-hydrogen-instance": instanceId,
2180
+ "data-kotao-hydrogen-reason": "COMPONENT_RENDERER_UNAVAILABLE",
2181
+ style: { display: "contents" }
2182
+ }
2183
+ );
2184
+ }
2185
+ return /* @__PURE__ */ jsx(HydrogenEditorBoundary, { applyPreviewProps: false, instanceId, children: createElement(Component, {
2186
+ instanceId,
2187
+ props: instance.props,
2188
+ styles: instance.styles,
2189
+ slots: instance.snapshot.childInstanceIds,
2190
+ ...instance.snapshot.instance.variantId !== void 0 ? { variantId: instance.snapshot.instance.variantId } : {}
2191
+ }) });
2192
+ }
2193
+ function HydrogenEditorSlot({
2194
+ empty = null,
2195
+ existingChildren = {},
2196
+ parentInstanceId,
2197
+ slotId
2198
+ }) {
2199
+ const parent = useInstanceSnapshot(parentInstanceId);
2200
+ const instanceIds = useHydrogenEditorSlot(parentInstanceId, slotId);
2201
+ const slot = parent?.component.slots.find((candidate) => candidate.slotId === slotId);
2202
+ return /* @__PURE__ */ jsx(
2203
+ "span",
2204
+ {
2205
+ "data-kotao-hydrogen-slot": slotId,
2206
+ "data-kotao-hydrogen-slot-accepts": slot?.accepts.mode === "any" ? "*" : slot?.accepts.componentIds.join(" ") ?? "",
2207
+ "data-kotao-hydrogen-slot-count": instanceIds.length,
2208
+ "data-kotao-hydrogen-slot-insertion": slot?.insertion ?? "fixed",
2209
+ "data-kotao-hydrogen-slot-label": slot?.label ?? slotId,
2210
+ "data-kotao-hydrogen-slot-max": slot?.maxInstances ?? 0,
2211
+ "data-kotao-hydrogen-slot-parent": parentInstanceId,
2212
+ style: { display: "contents" },
2213
+ children: instanceIds.length === 0 ? empty : instanceIds.map((instanceId) => {
2214
+ const existing = existingChildren[instanceId];
2215
+ return existing === void 0 ? /* @__PURE__ */ jsx(HydrogenEditorRegisteredInstance, { instanceId }, instanceId) : /* @__PURE__ */ jsx(HydrogenEditorBoundary, { instanceId, children: existing }, instanceId);
2216
+ })
2217
+ }
2218
+ );
2219
+ }
1951
2220
 
1952
- export { Analytics, AnalyticsCartView, AnalyticsCollectionView, AnalyticsProductView, AnalyticsProvider, AnalyticsSearchView, CartDrawerOverlay, CartForm, ConsentBanner, Image, MediaFile, Money, OptimisticInput, Pagination, Seo, ShopPayButton, ThemeSettingsProvider, openSmartCart, useAnalytics, useGlobalThemeTokens, useMoney, useOptimisticData, useSectionSettings, useThemeSettings };
2221
+ export { Analytics, AnalyticsCartView, AnalyticsCollectionView, AnalyticsProductView, AnalyticsProvider, AnalyticsSearchView, CartDrawerOverlay, CartForm, ConsentBanner, HydrogenEditorBoundary, HydrogenEditorProvider, HydrogenEditorSlot, Image, MediaFile, Money, OptimisticInput, Pagination, Seo, ShopPayButton, ThemeSettingsProvider, openSmartCart, useAnalytics, useGlobalThemeTokens, useHydrogenEditorInstance, useHydrogenEditorSlot, useMoney, useOptimisticData, useSectionSettings, useThemeSettings };
1953
2222
  //# sourceMappingURL=react.js.map
1954
2223
  //# sourceMappingURL=react.js.map