@riverbankcms/sdk 0.74.0 → 0.76.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.
Files changed (145) hide show
  1. package/README.md +2 -0
  2. package/dist/{PreviewEditorSidebar-5XL7M3OM.mjs → PreviewEditorSidebar-X2HWXFOL.mjs} +172 -89
  3. package/dist/{PreviewEditorUI-ZDFRDJID.mjs → PreviewEditorUI-AQXIXPOH.mjs} +3 -3
  4. package/dist/SdkPreviewModeRuntime-ZQNDCASH.mjs +8 -0
  5. package/dist/_dts/api/src/bookingOperations.d.ts +12 -0
  6. package/dist/_dts/api/src/cache-tags.d.ts +22 -0
  7. package/dist/_dts/api/src/contentRuntime.d.ts +1 -0
  8. package/dist/_dts/api/src/endpoints.d.ts +24 -24
  9. package/dist/_dts/api/src/index.d.ts +1 -1
  10. package/dist/_dts/api/src/publicExperienceEndpoints.d.ts +13 -4
  11. package/dist/_dts/block-form/src/blockDraftSaveability.d.ts +4 -2
  12. package/dist/_dts/block-form/src/client/index.d.ts +1 -1
  13. package/dist/_dts/block-form/src/manifest/utils.d.ts +24 -0
  14. package/dist/_dts/blocks/src/client/booking-form.d.ts +2 -0
  15. package/dist/_dts/blocks/src/client/carousel.d.ts +1 -0
  16. package/dist/_dts/blocks/src/client/course-registration.d.ts +2 -0
  17. package/dist/_dts/blocks/src/client/events.d.ts +6 -0
  18. package/dist/_dts/blocks/src/client/form.d.ts +2 -0
  19. package/dist/_dts/blocks/src/client/header.d.ts +1 -0
  20. package/dist/_dts/blocks/src/client/location-map.d.ts +1 -0
  21. package/dist/_dts/blocks/src/client/newsletter-form.d.ts +2 -0
  22. package/dist/_dts/blocks/src/client/shop.d.ts +6 -0
  23. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +4 -4
  24. package/dist/_dts/blocks/src/lib/video.d.ts +2 -0
  25. package/dist/_dts/blocks/src/system/manifest/fieldValidation/index.d.ts +21 -1
  26. package/dist/_dts/blocks/src/system/runtime/components/registry.client.d.ts +7 -6
  27. package/dist/_dts/blocks/src/system/runtime/components/runtime-backed-blocks.d.ts +3 -3
  28. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.headless.state.d.ts +3 -2
  29. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.interactive.d.ts +3 -27
  30. package/dist/_dts/blocks/src/system/runtime/nodes/booking-runtime-sources.d.ts +163 -0
  31. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration.interactive.d.ts +5 -5
  32. package/dist/_dts/blocks/src/system/runtime/nodes/events/index.d.ts +3 -0
  33. package/dist/_dts/blocks/src/system/runtime/nodes/header-section.client.d.ts +1 -0
  34. package/dist/_dts/blocks/src/system/runtime/nodes/header-section.server.d.ts +1 -0
  35. package/dist/_dts/blocks/src/system/runtime/shared/components/BookingOptionCompactRow.d.ts +29 -0
  36. package/dist/_dts/blocks/src/system/runtime/shared/components/BookingOptionList.d.ts +13 -0
  37. package/dist/_dts/blocks/src/system/runtime/shared/components/index.d.ts +4 -0
  38. package/dist/_dts/blocks/src/system/runtime/shared/index.d.ts +4 -3
  39. package/dist/_dts/blocks/src/system/runtime/shared/utils/bookingRoute.d.ts +20 -0
  40. package/dist/_dts/blocks/src/system/runtime/shared/utils/index.d.ts +2 -1
  41. package/dist/_dts/blocks/src/system/types/link.d.ts +12 -0
  42. package/dist/_dts/content-editor/src/hooks/validateBlocksForPublish.d.ts +5 -0
  43. package/dist/_dts/editor-blocks/src/BlockEditor.d.ts +2 -0
  44. package/dist/_dts/editor-blocks/src/block-editor/BlockCard.d.ts +3 -1
  45. package/dist/_dts/editor-blocks/src/block-editor/BlockCardHeader.d.ts +3 -1
  46. package/dist/_dts/editor-blocks/src/block-editor/BlockSettingsModal.d.ts +5 -1
  47. package/dist/_dts/editor-blocks/src/block-editor/SortableBlockItem.d.ts +2 -0
  48. package/dist/_dts/editor-blocks/src/block-editor/actionControlMode.d.ts +2 -0
  49. package/dist/_dts/editor-blocks/src/index.d.ts +1 -1
  50. package/dist/_dts/preview-next/src/client/preview/SiteChromeEditableSurface.d.ts +12 -2
  51. package/dist/_dts/preview-next/src/client/preview/themeOverlay.d.ts +21 -3
  52. package/dist/_dts/sdk/src/components.d.ts +2 -2
  53. package/dist/_dts/sdk/src/public-api/contracts.d.ts +1 -0
  54. package/dist/_dts/sdk/src/rendering/index.d.ts +2 -2
  55. package/dist/_dts/sdk/src/rendering/islands/deferredVideoEmbeds.d.ts +2 -0
  56. package/dist/_dts/sdk/src/rendering/overrideResolution.d.ts +6 -0
  57. package/dist/_dts/sdk/src/rendering/overrides.d.ts +21 -1
  58. package/dist/_dts/sdk/src/rendering/server.d.ts +4 -0
  59. package/dist/_dts/sdk/src/version.d.ts +1 -1
  60. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  61. package/dist/{chunk-2MIWNFJM.mjs → chunk-LYI7TEMH.mjs} +494 -171
  62. package/dist/{chunk-YY7K3QD2.mjs → chunk-Q4OLG66B.mjs} +9 -9
  63. package/dist/{chunk-CEQBBLMJ.mjs → chunk-TW2Y4RR5.mjs} +69 -14
  64. package/dist/{chunk-CBD47MTM.mjs → chunk-V5VORNEJ.mjs} +2 -2
  65. package/dist/{chunk-7RX72SK2.mjs → chunk-VX7B5VAC.mjs} +36 -25
  66. package/dist/{chunk-YRJKZEQ3.mjs → chunk-Z7JLNTJR.mjs} +1 -1
  67. package/dist/cli/index.mjs +3395 -3753
  68. package/dist/client/{accordion-ZZVXWV3N.mjs → accordion-2YHIBOZH.mjs} +1 -1
  69. package/dist/client/bookingForm-35IBDWAS.mjs +3012 -0
  70. package/dist/client/bookings.mjs +30 -21
  71. package/dist/client/{carousel-25WK2H7G.mjs → carousel-K6XG7NZQ.mjs} +3 -2
  72. package/dist/client/cart-HCFYUTTU.mjs +18 -0
  73. package/dist/client/checkout-J3U4XR7K.mjs +18 -0
  74. package/dist/client/chunk-3DCJ45Z2.mjs +36 -0
  75. package/dist/client/chunk-3PEDD5KJ.mjs +146 -0
  76. package/dist/client/{chunk-6VGXRE34.mjs → chunk-7UOSCYY4.mjs} +76 -29
  77. package/dist/client/chunk-BOIHIU7E.mjs +106 -0
  78. package/dist/client/chunk-F7LHOKEH.mjs +146 -0
  79. package/dist/client/chunk-FGUIVT2B.mjs +204 -0
  80. package/dist/client/chunk-I2MMZO53.mjs +6 -0
  81. package/dist/client/chunk-II7O4XN3.mjs +2206 -0
  82. package/dist/client/chunk-J227A3CQ.mjs +245 -0
  83. package/dist/client/chunk-JEZECJ3W.mjs +383 -0
  84. package/dist/client/chunk-M3L3KYYR.mjs +1181 -0
  85. package/dist/client/chunk-OJPZEZU2.mjs +31 -0
  86. package/dist/client/chunk-PFBX3IQL.mjs +1378 -0
  87. package/dist/client/chunk-RTZ3JKKX.mjs +178 -0
  88. package/dist/client/chunk-SNSWVZEP.mjs +5592 -0
  89. package/dist/client/chunk-TQ5MSPDX.mjs +74 -0
  90. package/dist/client/chunk-YLMKMCSJ.mjs +86 -0
  91. package/dist/client/client.mjs +54052 -54518
  92. package/dist/client/courseRegistration-ZBXS7VGI.mjs +1737 -0
  93. package/dist/client/{eventCalendar-X6GIPYFM.mjs → eventCalendar-EQQZHJBA.mjs} +13 -2
  94. package/dist/client/{eventCombined-EZUOPM3G.mjs → eventCombined-JCFTGBFQ.mjs} +13 -2
  95. package/dist/client/eventDetails-HLUTQSYY.mjs +25 -0
  96. package/dist/client/{eventListing-HEFGAETC.mjs → eventListing-FG4MICVB.mjs} +13 -2
  97. package/dist/client/{eventRegistration-3LCP2V3P.mjs → eventRegistration-GUEB7LBT.mjs} +13 -2
  98. package/dist/client/form-YQEW4YIT.mjs +300 -0
  99. package/dist/client/headerSection-GTR3TTRM.mjs +729 -0
  100. package/dist/client/hooks.mjs +49 -33
  101. package/dist/client/locationMap-SPC7VGUH.mjs +227 -0
  102. package/dist/client/newsletterForm-4IW5BLID.mjs +208 -0
  103. package/dist/client/productDetail-KP5H2WJO.mjs +18 -0
  104. package/dist/client/productList-3M3M72XZ.mjs +18 -0
  105. package/dist/client/rendering/client.mjs +15580 -16277
  106. package/dist/client/rendering/islands.mjs +98 -21
  107. package/dist/client/rendering.mjs +48730 -49191
  108. package/dist/client/shop-ACQKVGOM.mjs +20 -0
  109. package/dist/preview-next/before-render.mjs +37 -25
  110. package/dist/preview-next/chrome.css +1 -1
  111. package/dist/preview-next/client/runtime.mjs +5 -5
  112. package/dist/preview-next/editor-base.css +2 -148
  113. package/dist/preview-next/editor-chrome.css +179 -0
  114. package/dist/preview-next/middleware.mjs +37 -25
  115. package/dist/{sdk-runtime-OHNONZ5U.mjs → sdk-runtime-K7HNB4U2.mjs} +6 -6
  116. package/dist/server/components.mjs +1294 -1103
  117. package/dist/server/config-validation.mjs +49 -33
  118. package/dist/server/config.mjs +49 -33
  119. package/dist/server/data.mjs +49 -33
  120. package/dist/server/index.mjs +45 -33
  121. package/dist/server/next.mjs +1240 -1050
  122. package/dist/server/page-converter.mjs +12 -8
  123. package/dist/server/prebuild.mjs +1 -1
  124. package/dist/server/rendering/server.mjs +1389 -1104
  125. package/dist/server/rendering.mjs +1300 -1109
  126. package/dist/server/routing.mjs +49 -33
  127. package/dist/server/server.mjs +50 -34
  128. package/dist/server/theme-bridge.mjs +5 -0
  129. package/dist/styles/index.css +5 -0
  130. package/package.json +2 -1
  131. package/dist/SdkPreviewModeRuntime-CRYNDSPL.mjs +0 -8
  132. package/dist/client/bookingForm-4X77HKDS.mjs +0 -34
  133. package/dist/client/cart-JWD2VRFU.mjs +0 -11
  134. package/dist/client/checkout-V7KYGWL7.mjs +0 -11
  135. package/dist/client/chunk-5IYGMTUE.mjs +0 -18614
  136. package/dist/client/chunk-RKCFVIMS.mjs +0 -26
  137. package/dist/client/courseRegistration-O6ZPK4YT.mjs +0 -29
  138. package/dist/client/eventDetails-3WODIXET.mjs +0 -14
  139. package/dist/client/form-AKXQMY6Z.mjs +0 -85
  140. package/dist/client/headerSection-5KHCMU6B.mjs +0 -12
  141. package/dist/client/locationMap-7HIADW76.mjs +0 -14
  142. package/dist/client/newsletterForm-5N4YTMU3.mjs +0 -60
  143. package/dist/client/productDetail-IZUA7ZP3.mjs +0 -11
  144. package/dist/client/productList-KWIIJE6I.mjs +0 -11
  145. package/dist/client/shop-XZQFRQWZ.mjs +0 -13
@@ -0,0 +1,1378 @@
1
+ "use client";
2
+ import { addPassToCommerceCart, useCommerceCart } from './chunk-BOIHIU7E.mjs';
3
+ import { CarouselNodeClient } from './chunk-II7O4XN3.mjs';
4
+ import { isRecord } from './chunk-I2MMZO53.mjs';
5
+ import { createBodyThemePortalRoot, consumeSearchParams, scrollToBlock, TerminalPanel, StateIcon, CheckIcon, CrossIcon, ActionRow, consumeSearchParam, buildCheckoutReturnUrls } from './chunk-JEZECJ3W.mjs';
6
+ import { ModalShell, resolveDedicatedCheckoutPath } from './chunk-3PEDD5KJ.mjs';
7
+ import { useBlockApi, FeedbackMessage, BlockApiProvider } from './chunk-M3L3KYYR.mjs';
8
+ import { mountReactIsland } from './chunk-N7PBWBWQ.mjs';
9
+ import { themeButtonClassName, formatCommercePrice, formatInterval, getShopDisplayForItems, buildProductDetailDisplay, productItemKey, buildProductCartItem, buildProductCardDisplay, assertNever } from './chunk-7UOSCYY4.mjs';
10
+ import { getCurrentResolveImageUrlContext, resolveImageUrlWithContext, ImagePresets } from './chunk-PMAIXMAB.mjs';
11
+ import * as React3 from 'react';
12
+ import React3__default, { useState, useRef, useCallback, useEffect } from 'react';
13
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
14
+ import { z } from 'zod';
15
+
16
+ // ../blocks/src/primitives/content/utils/sanitizeHref.ts
17
+ var SAFE_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]);
18
+ var CONTROL_CHARS_PATTERN = /[\u0000-\u001F\u007F]+/g;
19
+ function normalizeInput(value) {
20
+ return value.replace(CONTROL_CHARS_PATTERN, "").trim();
21
+ }
22
+ function decodeForSchemeCheck(value) {
23
+ try {
24
+ return decodeURIComponent(value);
25
+ } catch {
26
+ return value;
27
+ }
28
+ }
29
+ function sanitizeHref(raw) {
30
+ if (typeof raw !== "string") return null;
31
+ const normalized = normalizeInput(raw);
32
+ if (!normalized) return null;
33
+ if (normalized.startsWith("//")) return null;
34
+ if (normalized.startsWith("/")) return normalized;
35
+ if (normalized.startsWith("#")) return normalized;
36
+ const decoded = normalizeInput(decodeForSchemeCheck(normalized));
37
+ const schemeMatch = decoded.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):/);
38
+ if (!schemeMatch) return null;
39
+ const rawScheme = schemeMatch[1];
40
+ if (!rawScheme) return null;
41
+ const protocol = `${rawScheme.toLowerCase()}:`;
42
+ if (!SAFE_PROTOCOLS.has(protocol)) return null;
43
+ try {
44
+ const parsed = new URL(normalized);
45
+ if (!SAFE_PROTOCOLS.has(parsed.protocol.toLowerCase())) {
46
+ return null;
47
+ }
48
+ } catch {
49
+ return null;
50
+ }
51
+ return normalized;
52
+ }
53
+ function parseDimension(value) {
54
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) {
55
+ return Math.round(value);
56
+ }
57
+ if (typeof value === "string" && value.trim().length > 0) {
58
+ const parsed = Number(value);
59
+ if (Number.isFinite(parsed) && parsed > 0) {
60
+ return Math.round(parsed);
61
+ }
62
+ }
63
+ return void 0;
64
+ }
65
+ function normalizeRel(target, rel) {
66
+ const normalizedRel = rel?.trim();
67
+ if (target !== "_blank") {
68
+ return normalizedRel && normalizedRel.length > 0 ? normalizedRel : void 0;
69
+ }
70
+ const tokens = new Set(
71
+ (normalizedRel ?? "").split(/\s+/).map((token) => token.trim().toLowerCase()).filter(Boolean)
72
+ );
73
+ tokens.add("noopener");
74
+ tokens.add("noreferrer");
75
+ return Array.from(tokens).join(" ");
76
+ }
77
+ function renderMarks(text, marks, key) {
78
+ if (!marks || marks.length === 0) return text;
79
+ return marks.reduce((acc, mark) => {
80
+ switch (mark.type) {
81
+ case "bold":
82
+ case "strong":
83
+ return /* @__PURE__ */ jsx("strong", { children: acc }, `${key}-b`);
84
+ case "italic":
85
+ case "em":
86
+ return /* @__PURE__ */ jsx("em", { children: acc }, `${key}-i`);
87
+ case "link": {
88
+ if (typeof acc === "string" && acc.length === 0) return acc;
89
+ const attrs = mark.attrs ?? {};
90
+ const href = sanitizeHref(attrs.href);
91
+ if (!href) return acc;
92
+ const target = typeof attrs.target === "string" && attrs.target.trim().length > 0 ? attrs.target.trim() : void 0;
93
+ const rel = typeof attrs.rel === "string" ? attrs.rel : void 0;
94
+ return /* @__PURE__ */ jsx("a", { href, target, rel: normalizeRel(target, rel), children: acc }, `${key}-l`);
95
+ }
96
+ default:
97
+ return acc;
98
+ }
99
+ }, text);
100
+ }
101
+ function renderDoc(node, key, imageResolveContext) {
102
+ if (!node || !node.type) return null;
103
+ const children = node.content?.map((child, index) => renderDoc(child, `${key}-${index}`, imageResolveContext));
104
+ switch (node.type) {
105
+ case "doc":
106
+ return /* @__PURE__ */ jsx(Fragment, { children });
107
+ case "paragraph":
108
+ if (!children && !node.text) return /* @__PURE__ */ jsx("p", {}, key);
109
+ return /* @__PURE__ */ jsx("p", { children: children ?? renderMarks(node.text ?? "", node.marks, key) }, key);
110
+ case "heading": {
111
+ const level = Math.min(Math.max(node.attrs?.level ?? 2, 1), 6);
112
+ const headingTag = `h${level}`;
113
+ return React3__default.createElement(headingTag, { key }, children);
114
+ }
115
+ case "bulletList":
116
+ return /* @__PURE__ */ jsx("ul", { children }, key);
117
+ case "orderedList":
118
+ return /* @__PURE__ */ jsx("ol", { children }, key);
119
+ case "listItem":
120
+ return /* @__PURE__ */ jsx("li", { children }, key);
121
+ case "blockquote":
122
+ return /* @__PURE__ */ jsx("blockquote", { children }, key);
123
+ case "horizontalRule":
124
+ return /* @__PURE__ */ jsx("hr", {}, key);
125
+ case "image": {
126
+ const attrs = node.attrs ?? {};
127
+ const src = typeof attrs.src === "string" ? attrs.src.trim() : "";
128
+ const identifier = typeof attrs.identifier === "string" ? attrs.identifier.trim() : "";
129
+ const alt = typeof attrs.alt === "string" ? attrs.alt : "";
130
+ const title = typeof attrs.title === "string" && attrs.title.trim().length > 0 ? attrs.title : void 0;
131
+ const width = parseDimension(attrs.width);
132
+ const height = parseDimension(attrs.height);
133
+ const resolvedSrc = src || (identifier ? resolveImageUrlWithContext(
134
+ { type: "image", identifier },
135
+ width ? { width, resize: "contain", quality: 85 } : height ? { height, resize: "contain", quality: 85 } : ImagePresets.full,
136
+ imageResolveContext ?? void 0
137
+ ) ?? "" : "");
138
+ if (!resolvedSrc) return null;
139
+ return /* @__PURE__ */ jsx(
140
+ "img",
141
+ {
142
+ src: resolvedSrc,
143
+ alt,
144
+ title,
145
+ width,
146
+ height,
147
+ loading: "lazy",
148
+ decoding: "async"
149
+ },
150
+ key
151
+ );
152
+ }
153
+ case "text":
154
+ return renderMarks(node.text ?? "", node.marks, key);
155
+ default:
156
+ return null;
157
+ }
158
+ }
159
+ var DEFAULT_RICH_TEXT_CLASS = "rb-prose rb-max-w-none";
160
+ function RichText({ as: Comp = "div", className, doc, path }) {
161
+ const imageResolveContext = getCurrentResolveImageUrlContext() ?? null;
162
+ const content = doc ? renderDoc(doc, "doc", imageResolveContext) : null;
163
+ const dataAttributes = path ? { "data-builder-richtext": path } : void 0;
164
+ const resolvedClassName = className?.trim() || DEFAULT_RICH_TEXT_CLASS;
165
+ return /* @__PURE__ */ jsx(Comp, { className: resolvedClassName, ...dataAttributes, children: content });
166
+ }
167
+ var TipTapMarkSchema = z.object({
168
+ type: z.string(),
169
+ attrs: z.record(z.string(), z.unknown()).optional()
170
+ });
171
+ var TipTapNodeSchema = z.lazy(
172
+ () => z.object({
173
+ type: z.string(),
174
+ content: z.array(TipTapNodeSchema).optional(),
175
+ text: z.string().optional(),
176
+ attrs: z.record(z.string(), z.unknown()).optional(),
177
+ marks: z.array(TipTapMarkSchema).optional()
178
+ })
179
+ );
180
+ z.union([
181
+ // Canonical: TipTap document directly
182
+ TipTapNodeSchema,
183
+ // Legacy (read-tolerance only): wrapped in { doc: {...} }
184
+ z.object({
185
+ doc: TipTapNodeSchema
186
+ })
187
+ ]);
188
+ z.object({
189
+ type: z.literal("doc"),
190
+ content: z.array(TipTapNodeSchema)
191
+ });
192
+ function unwrapRichTextRoot(value) {
193
+ if (!isRecord(value)) return value;
194
+ if (!("doc" in value)) return value;
195
+ const inner = value.doc;
196
+ if (isRecord(inner) && inner.type === "doc") return inner;
197
+ return value;
198
+ }
199
+
200
+ // ../blocks/src/system/runtime/nodes/richText-coerce.ts
201
+ function parseInlineMarkdown(text) {
202
+ const out = [];
203
+ const pushText = (value, marks) => {
204
+ if (!value) return;
205
+ out.push(
206
+ marks && marks.length > 0 ? { type: "text", text: value, marks } : { type: "text", text: value }
207
+ );
208
+ };
209
+ let rest = text;
210
+ while (rest.length > 0) {
211
+ const start = rest.indexOf("**");
212
+ if (start === -1) {
213
+ pushText(rest);
214
+ break;
215
+ }
216
+ const end = rest.indexOf("**", start + 2);
217
+ if (end === -1) {
218
+ pushText(rest);
219
+ break;
220
+ }
221
+ pushText(rest.slice(0, start));
222
+ pushText(rest.slice(start + 2, end), [{ type: "bold" }]);
223
+ rest = rest.slice(end + 2);
224
+ }
225
+ return out;
226
+ }
227
+ function markdownToTipTapDoc(markdown) {
228
+ const normalized = markdown.replace(/\r\n/g, "\n").trim();
229
+ if (!normalized) return { type: "doc", content: [] };
230
+ const paragraphs = normalized.split(/\n{2,}/).map((p) => p.trim()).filter(Boolean);
231
+ return {
232
+ type: "doc",
233
+ content: paragraphs.map((p) => {
234
+ const inline = parseInlineMarkdown(p);
235
+ return inline.length > 0 ? { type: "paragraph", content: inline } : { type: "paragraph" };
236
+ })
237
+ };
238
+ }
239
+ function coerceRichTextDoc(value) {
240
+ const raw = unwrapRichTextRoot(value);
241
+ if (typeof raw === "string") {
242
+ return markdownToTipTapDoc(raw);
243
+ }
244
+ if (isRecord(raw) && typeof raw.type === "string") {
245
+ return raw;
246
+ }
247
+ return null;
248
+ }
249
+ function ProductBadgesView({ badges }) {
250
+ if (badges.length === 0) return null;
251
+ return /* @__PURE__ */ jsx("div", { className: "rb-flex rb-flex-wrap rb-gap-2", children: badges.map((badge) => /* @__PURE__ */ jsx(
252
+ "span",
253
+ {
254
+ className: "rb-inline-flex rb-items-center rb-rounded-full rb-border rb-px-3 rb-py-1 rb-text-xs",
255
+ children: badge
256
+ },
257
+ badge
258
+ )) });
259
+ }
260
+ function VariantSelectView(props) {
261
+ const { options, selectedVariantId, disabled = false, onChange } = props;
262
+ if (options.length === 0) return null;
263
+ return /* @__PURE__ */ jsxs("label", { className: "rb-flex rb-flex-col rb-gap-1", children: [
264
+ /* @__PURE__ */ jsx("span", { className: "rb-label", children: "Variant" }),
265
+ /* @__PURE__ */ jsx(
266
+ "select",
267
+ {
268
+ className: "form-input",
269
+ value: selectedVariantId ?? "",
270
+ disabled,
271
+ onChange: onChange ? (event) => onChange(event.target.value || null) : void 0,
272
+ children: options.map((variant) => /* @__PURE__ */ jsxs("option", { value: variant.id, children: [
273
+ variant.label,
274
+ " ",
275
+ variant.soldOut ? "(Sold out)" : ""
276
+ ] }, variant.id))
277
+ }
278
+ )
279
+ ] });
280
+ }
281
+ function ProductCardView(props) {
282
+ const { display, layout, variantControl, actions, feedback } = props;
283
+ return /* @__PURE__ */ jsxs("article", { className: `rb-flex rb-gap-4 rb-rounded-2xl rb-border rb-p-4 ${layout === "list" ? "rb-flex-row" : "rb-flex-col"}`, children: [
284
+ display.image ? /* @__PURE__ */ jsx("a", { href: display.path ?? void 0, className: "rb-block rb-overflow-hidden rb-rounded-xl rb-border", children: /* @__PURE__ */ jsx("img", { src: display.image.url, alt: display.image.alt ?? "", className: "rb-h-48 rb-w-full rb-object-cover" }) }) : null,
285
+ /* @__PURE__ */ jsxs("div", { className: "rb-flex rb-flex-1 rb-flex-col rb-gap-3", children: [
286
+ /* @__PURE__ */ jsxs("div", { className: "rb-flex rb-flex-col rb-gap-2", children: [
287
+ /* @__PURE__ */ jsxs("div", { className: "rb-flex rb-items-start rb-justify-between rb-gap-3", children: [
288
+ /* @__PURE__ */ jsxs("div", { children: [
289
+ display.path ? /* @__PURE__ */ jsx("a", { href: display.path, children: /* @__PURE__ */ jsx("h3", { children: display.title }) }) : /* @__PURE__ */ jsx("h3", { children: display.title }),
290
+ display.summary ? /* @__PURE__ */ jsx("p", { className: "rb-caption status-muted", children: display.summary }) : null
291
+ ] }),
292
+ display.priceLabel ? /* @__PURE__ */ jsx("div", { className: "rb-text-right", children: /* @__PURE__ */ jsx("strong", { children: display.priceLabel }) }) : null
293
+ ] }),
294
+ /* @__PURE__ */ jsx(ProductBadgesView, { badges: display.badges })
295
+ ] }),
296
+ variantControl,
297
+ /* @__PURE__ */ jsx("div", { className: "rb-mt-auto rb-flex rb-items-center rb-gap-3", children: actions }),
298
+ feedback,
299
+ /* @__PURE__ */ jsx("span", { className: "rb-caption status-muted", children: display.selectionLabel })
300
+ ] })
301
+ ] });
302
+ }
303
+ function ProductListSectionView(props) {
304
+ const { display, children } = props;
305
+ return /* @__PURE__ */ jsxs("div", { className: `shop shop--product-list ${display.className ?? ""}`.trim(), "data-block": "shop", children: [
306
+ display.heading ? /* @__PURE__ */ jsx("h2", { children: display.heading }) : null,
307
+ display.state === "empty" ? /* @__PURE__ */ jsx("p", { className: "rb-caption status-muted", children: display.emptyMessage }) : /* @__PURE__ */ jsx(
308
+ "div",
309
+ {
310
+ className: `rb-grid rb-gap-4 ${display.layout === "list" ? "rb-grid-cols-1" : ""}`,
311
+ style: display.layout === "grid" ? { gridTemplateColumns: `repeat(${display.columns}, minmax(0, 1fr))` } : void 0,
312
+ children
313
+ }
314
+ )
315
+ ] });
316
+ }
317
+ function ProductDetailView(props) {
318
+ const { display, gallery, variantControl, actions, feedback, body } = props;
319
+ if (display.state === "empty") {
320
+ return /* @__PURE__ */ jsx("div", { className: `shop shop--product-detail ${display.className ?? ""}`.trim(), "data-block": "shop", children: /* @__PURE__ */ jsx("p", { className: "rb-caption status-muted", children: display.emptyMessage }) });
321
+ }
322
+ return /* @__PURE__ */ jsxs("div", { className: `shop shop--product-detail rb-grid rb-gap-6 rb-md-grid-cols-2 ${display.className ?? ""}`.trim(), "data-block": "shop", children: [
323
+ /* @__PURE__ */ jsx("div", { className: "rb-flex rb-flex-col rb-gap-4", children: gallery }),
324
+ /* @__PURE__ */ jsxs("div", { className: "rb-flex rb-flex-col rb-gap-4", children: [
325
+ display.heading ? /* @__PURE__ */ jsx("p", { className: "rb-label", children: display.heading }) : null,
326
+ /* @__PURE__ */ jsxs("div", { children: [
327
+ /* @__PURE__ */ jsx("h2", { children: display.title }),
328
+ /* @__PURE__ */ jsx("div", { className: "rb-mt-2", children: /* @__PURE__ */ jsx("strong", { children: display.priceLabel }) })
329
+ ] }),
330
+ /* @__PURE__ */ jsx(ProductBadgesView, { badges: display.badges }),
331
+ display.summary ? /* @__PURE__ */ jsx("p", { className: "rb-caption status-muted", children: display.summary }) : null,
332
+ variantControl,
333
+ /* @__PURE__ */ jsxs("div", { className: "rb-flex rb-items-center rb-gap-3", children: [
334
+ actions,
335
+ display.categoryLabel ? /* @__PURE__ */ jsx("span", { className: "rb-caption status-muted", children: display.categoryLabel }) : null
336
+ ] }),
337
+ feedback,
338
+ body
339
+ ] })
340
+ ] });
341
+ }
342
+ function ProductGalleryFrameView(props) {
343
+ const { gallery, renderCarousel } = props;
344
+ if (gallery.length === 0) return null;
345
+ const slides = gallery.map((image, index) => /* @__PURE__ */ jsx("div", { className: "rb-overflow-hidden rb-rounded-xl rb-border", children: /* @__PURE__ */ jsx(
346
+ "img",
347
+ {
348
+ src: image.url,
349
+ alt: image.alt ?? "",
350
+ className: "rb-h-full rb-w-full rb-object-cover"
351
+ }
352
+ ) }, `${image.url}:${index}`));
353
+ if (slides.length === 1) {
354
+ return slides[0] ?? null;
355
+ }
356
+ return /* @__PURE__ */ jsx(Fragment, { children: renderCarousel(slides) });
357
+ }
358
+ function PassCardView(props) {
359
+ return /* @__PURE__ */ jsxs("article", { className: "shop__card shop__card--pass", children: [
360
+ /* @__PURE__ */ jsxs("div", { className: "shop__card-header", children: [
361
+ /* @__PURE__ */ jsx("h4", { className: "shop__card-name", children: props.display.title }),
362
+ /* @__PURE__ */ jsx("div", { className: "shop__card-price", children: props.display.priceLabel })
363
+ ] }),
364
+ props.display.description ? /* @__PURE__ */ jsx("p", { className: "shop__card-description", children: props.display.description }) : null,
365
+ /* @__PURE__ */ jsxs("div", { className: "shop__card-details", children: [
366
+ /* @__PURE__ */ jsx("span", { className: "shop__card-credits", children: props.display.creditsLabel }),
367
+ props.display.perCreditLabel ? /* @__PURE__ */ jsxs("span", { className: "shop__card-per-credit", children: [
368
+ "(",
369
+ props.display.perCreditLabel,
370
+ ")"
371
+ ] }) : null,
372
+ props.display.validityLabel ? /* @__PURE__ */ jsx("span", { className: "shop__card-validity", children: props.display.validityLabel }) : null
373
+ ] }),
374
+ props.action,
375
+ props.feedback
376
+ ] });
377
+ }
378
+ function MembershipCardView(props) {
379
+ return /* @__PURE__ */ jsxs("article", { className: "shop__card shop__card--membership", children: [
380
+ /* @__PURE__ */ jsxs("div", { className: "shop__card-header", children: [
381
+ /* @__PURE__ */ jsx("h4", { className: "shop__card-name", children: props.display.title }),
382
+ /* @__PURE__ */ jsxs("div", { className: "shop__card-price", children: [
383
+ props.display.priceLabel,
384
+ /* @__PURE__ */ jsx("span", { className: "shop__card-interval", children: props.display.intervalLabel })
385
+ ] })
386
+ ] }),
387
+ props.display.description ? /* @__PURE__ */ jsx("p", { className: "shop__card-description", children: props.display.description }) : null,
388
+ /* @__PURE__ */ jsx("div", { className: "shop__card-details", children: /* @__PURE__ */ jsx("span", { className: "shop__card-unlimited", children: "Unlimited classes" }) }),
389
+ props.action
390
+ ] });
391
+ }
392
+ function PassesMembershipsSectionView(props) {
393
+ const { display, beforeContent, passCards, membershipCards } = props;
394
+ const productsClass = `shop__products shop__products--${display.layout}`;
395
+ const gridStyle = display.state === "ready" ? { "--shop-columns": display.columns } : void 0;
396
+ return /* @__PURE__ */ jsxs(
397
+ "div",
398
+ {
399
+ className: `shop ${display.state === "ready" ? `shop--${display.layout}` : "shop--empty"} ${display.className ?? ""}`.trim(),
400
+ id: "shop",
401
+ "data-block": "shop",
402
+ style: gridStyle,
403
+ children: [
404
+ beforeContent,
405
+ display.state === "empty" ? /* @__PURE__ */ jsx("p", { className: "shop__empty-text", children: display.emptyMessage }) : null,
406
+ display.passes.length > 0 ? /* @__PURE__ */ jsxs("section", { className: "shop__section shop__section--passes", children: [
407
+ /* @__PURE__ */ jsx("h3", { className: "shop__section-heading", children: display.passesHeading }),
408
+ /* @__PURE__ */ jsx("div", { className: productsClass, children: passCards })
409
+ ] }) : null,
410
+ display.memberships.length > 0 ? /* @__PURE__ */ jsxs("section", { className: "shop__section shop__section--memberships", children: [
411
+ /* @__PURE__ */ jsx("h3", { className: "shop__section-heading", children: display.membershipsHeading }),
412
+ /* @__PURE__ */ jsx("div", { className: productsClass, children: membershipCards })
413
+ ] }) : null
414
+ ]
415
+ }
416
+ );
417
+ }
418
+ function CartShellView(props) {
419
+ const { display, renderControls, footerActions } = props;
420
+ return /* @__PURE__ */ jsxs("div", { className: `shop shop--cart ${display.className ?? ""}`.trim(), "data-block": "shop", id: "shop-cart", children: [
421
+ display.heading ? /* @__PURE__ */ jsx("h2", { children: display.heading }) : null,
422
+ display.state === "empty" ? /* @__PURE__ */ jsxs("div", { className: "rb-flex rb-flex-col rb-gap-2", children: [
423
+ /* @__PURE__ */ jsx("p", { className: "rb-caption status-muted", children: display.emptyMessage }),
424
+ display.supportText ? /* @__PURE__ */ jsx("p", { className: "rb-caption status-muted", children: display.supportText }) : null
425
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "rb-flex rb-flex-col rb-gap-4", children: [
426
+ /* @__PURE__ */ jsx("div", { className: "rb-flex rb-flex-col rb-gap-3", children: display.items.map((item) => /* @__PURE__ */ jsxs("div", { className: "rb-flex rb-items-center rb-justify-between rb-gap-4 rb-rounded-xl rb-border rb-p-4", children: [
427
+ /* @__PURE__ */ jsxs("div", { className: "rb-flex rb-flex-col rb-gap-1", children: [
428
+ /* @__PURE__ */ jsx("strong", { children: item.title }),
429
+ /* @__PURE__ */ jsx("span", { className: "rb-caption status-muted", children: item.priceLabel })
430
+ ] }),
431
+ /* @__PURE__ */ jsx("div", { className: "rb-flex rb-items-center rb-gap-2", children: renderControls ? renderControls(item) : null })
432
+ ] }, item.key)) }),
433
+ /* @__PURE__ */ jsxs("div", { className: "rb-flex rb-items-center rb-justify-between rb-gap-4", children: [
434
+ /* @__PURE__ */ jsxs("div", { children: [
435
+ /* @__PURE__ */ jsx("strong", { children: "Subtotal" }),
436
+ /* @__PURE__ */ jsx("div", { children: display.subtotalLabel })
437
+ ] }),
438
+ /* @__PURE__ */ jsx("div", { className: "rb-flex rb-gap-2", children: footerActions })
439
+ ] })
440
+ ] })
441
+ ] });
442
+ }
443
+ function CheckoutShellView(props) {
444
+ return /* @__PURE__ */ jsxs("div", { className: `shop shop--checkout ${props.display.className ?? ""}`.trim(), "data-block": "shop", id: "shop-checkout", children: [
445
+ props.display.heading ? /* @__PURE__ */ jsx("h2", { children: props.display.heading }) : null,
446
+ props.notice,
447
+ props.display.state === "ready" ? props.body : /* @__PURE__ */ jsx("p", { className: "rb-caption status-muted", children: props.display.message })
448
+ ] });
449
+ }
450
+ var DEFAULT_TIMEOUT_MS = 2500;
451
+ function useTransientShopAddFeedback(timeoutMs = DEFAULT_TIMEOUT_MS) {
452
+ const [activeKey, setActiveKey] = React3.useState(null);
453
+ React3.useEffect(() => {
454
+ if (!activeKey) return;
455
+ const timeoutId = window.setTimeout(() => {
456
+ setActiveKey((current) => current === activeKey ? null : current);
457
+ }, timeoutMs);
458
+ return () => window.clearTimeout(timeoutId);
459
+ }, [activeKey, timeoutMs]);
460
+ return {
461
+ activeKey,
462
+ markAdded: setActiveKey,
463
+ clear: () => setActiveKey(null)
464
+ };
465
+ }
466
+ function ShopAddFeedback(props) {
467
+ if (!props.active) return null;
468
+ return /* @__PURE__ */ jsxs(
469
+ FeedbackMessage,
470
+ {
471
+ tone: "success",
472
+ className: "rb-caption status-muted",
473
+ role: "status",
474
+ live: "polite",
475
+ children: [
476
+ "Added ",
477
+ props.itemLabel,
478
+ " to cart.",
479
+ " ",
480
+ /* @__PURE__ */ jsx("a", { href: "#shop-cart", children: "View cart" }),
481
+ "."
482
+ ]
483
+ }
484
+ );
485
+ }
486
+
487
+ // ../blocks/src/system/runtime/nodes/shop-checkout.state.ts
488
+ var initialShopCheckoutState = {
489
+ form: {
490
+ name: "",
491
+ email: ""
492
+ },
493
+ submission: "idle",
494
+ feedback: { tag: "idle" },
495
+ notice: null
496
+ };
497
+ function shopCheckoutReducer(state, action) {
498
+ switch (action.type) {
499
+ case "setField":
500
+ return {
501
+ ...state,
502
+ form: {
503
+ ...state.form,
504
+ [action.field]: action.value
505
+ },
506
+ feedback: { tag: "idle" }
507
+ };
508
+ case "submitStarted":
509
+ return {
510
+ ...state,
511
+ submission: "submitting",
512
+ feedback: { tag: "idle" }
513
+ };
514
+ case "submitFailed":
515
+ return {
516
+ ...state,
517
+ submission: "idle",
518
+ feedback: { tag: "error", message: action.message }
519
+ };
520
+ case "submitCompleted":
521
+ return {
522
+ ...state,
523
+ submission: "idle"
524
+ };
525
+ case "setNotice":
526
+ return {
527
+ ...state,
528
+ notice: action.notice
529
+ };
530
+ default:
531
+ return assertNever(action);
532
+ }
533
+ }
534
+ var CHECKOUT_ANCHOR_ID = "shop-checkout";
535
+ function ProductListController(props) {
536
+ const api = useBlockApi();
537
+ const cart = useCommerceCart(api.config.siteId);
538
+ const addFeedback = useTransientShopAddFeedback();
539
+ const [variantSelection, setVariantSelection] = React3.useState(
540
+ () => Object.fromEntries(props.render.display.cards.map((card) => [card.productId, card.selectedVariantId]))
541
+ );
542
+ const cards = React3.useMemo(
543
+ () => props.render.hydration.products.map(
544
+ (product) => buildProductCardDisplay(
545
+ product,
546
+ {
547
+ showSummary: props.render.hydration.showSummary,
548
+ showBadges: props.render.hydration.showBadges,
549
+ showPrices: props.render.hydration.showPrices,
550
+ addButtonText: props.render.hydration.addButtonText
551
+ },
552
+ variantSelection[product.id] ?? null
553
+ )
554
+ ),
555
+ [props.render.hydration, variantSelection]
556
+ );
557
+ const display = React3.useMemo(
558
+ () => ({
559
+ ...props.render.display,
560
+ state: cards.length === 0 ? "empty" : "ready",
561
+ cards
562
+ }),
563
+ [cards, props.render.display]
564
+ );
565
+ return /* @__PURE__ */ jsx(ProductListSectionView, { display, children: display.cards.map((card) => /* @__PURE__ */ jsx(
566
+ ProductCardView,
567
+ {
568
+ display: card,
569
+ layout: display.layout,
570
+ variantControl: /* @__PURE__ */ jsx(
571
+ VariantSelectView,
572
+ {
573
+ options: card.variantOptions,
574
+ selectedVariantId: card.selectedVariantId,
575
+ onChange: (variantId) => setVariantSelection((current) => ({
576
+ ...current,
577
+ [card.productId]: variantId
578
+ }))
579
+ }
580
+ ),
581
+ actions: /* @__PURE__ */ jsxs(Fragment, { children: [
582
+ /* @__PURE__ */ jsx(
583
+ "button",
584
+ {
585
+ type: "button",
586
+ className: themeButtonClassName({ variant: "default", size: "md" }),
587
+ onClick: () => {
588
+ const product = props.render.hydration.products.find((candidate) => candidate.id === card.productId);
589
+ if (!product) return;
590
+ const cartItem = buildProductCartItem({
591
+ product,
592
+ variantId: card.selectedVariantId,
593
+ quantity: 1
594
+ });
595
+ cart.addProduct(cartItem);
596
+ addFeedback.markAdded(cartItem.key);
597
+ },
598
+ disabled: card.soldOut,
599
+ children: card.soldOut ? "Sold out" : card.actionLabel
600
+ }
601
+ ),
602
+ card.path ? /* @__PURE__ */ jsx(
603
+ "a",
604
+ {
605
+ href: card.path,
606
+ className: themeButtonClassName({ variant: "secondary", size: "sm" }),
607
+ children: "View details"
608
+ }
609
+ ) : null
610
+ ] }),
611
+ feedback: /* @__PURE__ */ jsx(
612
+ ShopAddFeedback,
613
+ {
614
+ active: addFeedback.activeKey === productItemKey(card.productId, card.selectedVariantId),
615
+ itemLabel: card.selectionLabel
616
+ }
617
+ )
618
+ },
619
+ card.productId
620
+ )) });
621
+ }
622
+ function ProductDetailController(props) {
623
+ const api = useBlockApi();
624
+ const cart = useCommerceCart(api.config.siteId);
625
+ const addFeedback = useTransientShopAddFeedback();
626
+ const selectedProduct = React3.useMemo(
627
+ () => props.render.hydration.products.find((product) => product.id === props.render.hydration.productId) ?? null,
628
+ [props.render.hydration.productId, props.render.hydration.products]
629
+ );
630
+ const [variantId, setVariantId] = React3.useState(
631
+ props.render.display.state === "ready" ? props.render.display.selectedVariantId : null
632
+ );
633
+ React3.useEffect(() => {
634
+ setVariantId(props.render.display.state === "ready" ? props.render.display.selectedVariantId : null);
635
+ }, [props.render.display]);
636
+ const display = React3.useMemo(
637
+ () => buildProductDetailDisplay(
638
+ selectedProduct,
639
+ {
640
+ heading: props.render.display.heading,
641
+ className: props.render.display.className,
642
+ emptyMessage: props.render.display.state === "empty" ? props.render.display.emptyMessage : void 0,
643
+ showSummary: props.render.hydration.showSummary,
644
+ showBadges: props.render.hydration.showBadges,
645
+ addButtonText: props.render.hydration.addButtonText
646
+ },
647
+ variantId
648
+ ),
649
+ [props.render.display, props.render.hydration, selectedProduct, variantId]
650
+ );
651
+ const bodyDoc = display.state === "ready" && props.render.hydration.showBody ? coerceRichTextDoc(display.body) : null;
652
+ return /* @__PURE__ */ jsx(
653
+ ProductDetailView,
654
+ {
655
+ display,
656
+ gallery: display.state === "ready" ? /* @__PURE__ */ jsx(
657
+ ProductGalleryFrameView,
658
+ {
659
+ gallery: display.gallery,
660
+ renderCarousel: (slides) => /* @__PURE__ */ jsx(CarouselNodeClient, { slidesToShow: 1, showDots: true, showControls: true, className: "rb-w-full", children: slides })
661
+ }
662
+ ) : null,
663
+ variantControl: display.state === "ready" ? /* @__PURE__ */ jsx(
664
+ VariantSelectView,
665
+ {
666
+ options: display.variantOptions,
667
+ selectedVariantId: display.selectedVariantId,
668
+ onChange: setVariantId
669
+ }
670
+ ) : null,
671
+ actions: display.state === "ready" ? /* @__PURE__ */ jsx(
672
+ "button",
673
+ {
674
+ type: "button",
675
+ className: themeButtonClassName({ variant: "default", size: "md" }),
676
+ disabled: display.soldOut,
677
+ onClick: () => {
678
+ if (!selectedProduct) return;
679
+ const cartItem = buildProductCartItem({
680
+ product: selectedProduct,
681
+ variantId: display.selectedVariantId,
682
+ quantity: 1
683
+ });
684
+ cart.addProduct(cartItem);
685
+ addFeedback.markAdded(cartItem.key);
686
+ },
687
+ children: display.soldOut ? "Sold out" : display.actionLabel
688
+ }
689
+ ) : null,
690
+ feedback: display.state === "ready" ? /* @__PURE__ */ jsx(
691
+ ShopAddFeedback,
692
+ {
693
+ active: addFeedback.activeKey === productItemKey(selectedProduct?.id ?? "", display.selectedVariantId),
694
+ itemLabel: display.selectionLabel
695
+ }
696
+ ) : null,
697
+ body: bodyDoc ? /* @__PURE__ */ jsx("div", { className: "rb-prose rb-max-w-none", children: /* @__PURE__ */ jsx(RichText, { doc: bodyDoc }) }) : null
698
+ }
699
+ );
700
+ }
701
+ function CartController(props) {
702
+ const api = useBlockApi();
703
+ const cart = useCommerceCart(api.config.siteId);
704
+ const display = React3.useMemo(
705
+ () => getShopDisplayForItems(props.render, cart.items),
706
+ [cart.items, props.render]
707
+ );
708
+ return /* @__PURE__ */ jsx(
709
+ CartShellView,
710
+ {
711
+ display,
712
+ renderControls: (item) => /* @__PURE__ */ jsxs(Fragment, { children: [
713
+ /* @__PURE__ */ jsx(
714
+ "input",
715
+ {
716
+ type: "number",
717
+ min: 1,
718
+ className: "form-input rb-w-20",
719
+ value: item.quantity,
720
+ readOnly: !item.quantityEditable,
721
+ "aria-label": `Quantity for ${item.title}`,
722
+ onChange: item.quantityEditable ? (event) => cart.updateQuantity(item.key, Number(event.target.value)) : void 0
723
+ }
724
+ ),
725
+ /* @__PURE__ */ jsx(
726
+ "button",
727
+ {
728
+ type: "button",
729
+ className: themeButtonClassName({ variant: "secondary", size: "sm" }),
730
+ onClick: () => cart.removeItem(item.key),
731
+ children: "Remove"
732
+ }
733
+ )
734
+ ] }),
735
+ footerActions: /* @__PURE__ */ jsxs(Fragment, { children: [
736
+ /* @__PURE__ */ jsx(
737
+ "button",
738
+ {
739
+ type: "button",
740
+ className: themeButtonClassName({ variant: "secondary", size: "sm" }),
741
+ onClick: () => cart.clear(),
742
+ children: display.clearButtonText
743
+ }
744
+ ),
745
+ /* @__PURE__ */ jsx(
746
+ "button",
747
+ {
748
+ type: "button",
749
+ className: themeButtonClassName({ variant: "default", size: "md" }),
750
+ onClick: () => {
751
+ window.location.href = resolveDedicatedCheckoutPath(window.location.pathname);
752
+ },
753
+ children: display.checkoutButtonText
754
+ }
755
+ )
756
+ ] })
757
+ }
758
+ );
759
+ }
760
+ function CheckoutController(props) {
761
+ const api = useBlockApi();
762
+ const cart = useCommerceCart(api.config.siteId);
763
+ const [state, dispatch] = React3.useReducer(
764
+ shopCheckoutReducer,
765
+ initialShopCheckoutState
766
+ );
767
+ React3.useEffect(() => {
768
+ const checkout = consumeSearchParam("checkout");
769
+ if (checkout === "success") {
770
+ cart.clear();
771
+ dispatch({ type: "setNotice", notice: "success" });
772
+ } else if (checkout === "cancelled") {
773
+ dispatch({ type: "setNotice", notice: "cancelled" });
774
+ }
775
+ }, [cart]);
776
+ const display = React3.useMemo(
777
+ () => getShopDisplayForItems(props.render, cart.items),
778
+ [cart.items, props.render]
779
+ );
780
+ const handleSubmit = async (event) => {
781
+ event.preventDefault();
782
+ if (display.state !== "ready" || state.submission === "submitting") return;
783
+ dispatch({ type: "submitStarted" });
784
+ try {
785
+ const { successUrl, cancelUrl } = buildCheckoutReturnUrls("checkout", CHECKOUT_ANCHOR_ID);
786
+ const checkoutItems = cart.items.map(
787
+ (item) => item.kind === "product" ? {
788
+ type: "product",
789
+ productId: item.productId,
790
+ ...item.variantId ? { variantId: item.variantId } : {},
791
+ quantity: item.quantity
792
+ } : {
793
+ type: "pass",
794
+ passId: item.passId
795
+ }
796
+ );
797
+ const result = await api.createUnifiedCheckout({
798
+ mode: "one_time",
799
+ items: checkoutItems,
800
+ customer: {
801
+ email: state.form.email.trim().toLowerCase(),
802
+ name: state.form.name.trim()
803
+ },
804
+ successUrl,
805
+ cancelUrl
806
+ });
807
+ if (result.type === "redirect") {
808
+ window.location.href = result.checkoutUrl;
809
+ return;
810
+ }
811
+ cart.clear();
812
+ window.location.href = successUrl;
813
+ } catch (submitError) {
814
+ dispatch({
815
+ type: "submitFailed",
816
+ message: submitError instanceof Error ? submitError.message : "Checkout failed"
817
+ });
818
+ return;
819
+ } finally {
820
+ dispatch({ type: "submitCompleted" });
821
+ }
822
+ };
823
+ return /* @__PURE__ */ jsx(
824
+ CheckoutShellView,
825
+ {
826
+ display,
827
+ notice: state.notice ? /* @__PURE__ */ jsx(
828
+ FeedbackMessage,
829
+ {
830
+ tone: state.notice === "success" ? "success" : "warning",
831
+ className: "rb-mb-4 rb-rounded-xl rb-border rb-p-4",
832
+ role: "status",
833
+ children: state.notice === "success" ? "Payment successful." : "Checkout cancelled."
834
+ }
835
+ ) : null,
836
+ body: /* @__PURE__ */ jsxs("form", { className: "rb-flex rb-flex-col rb-gap-4", onSubmit: handleSubmit, children: [
837
+ /* @__PURE__ */ jsxs("label", { className: "rb-flex rb-flex-col rb-gap-1", children: [
838
+ /* @__PURE__ */ jsx("span", { className: "rb-label", children: "Name" }),
839
+ /* @__PURE__ */ jsx(
840
+ "input",
841
+ {
842
+ className: "form-input",
843
+ value: state.form.name,
844
+ onChange: (event) => dispatch({ type: "setField", field: "name", value: event.target.value }),
845
+ required: true
846
+ }
847
+ )
848
+ ] }),
849
+ /* @__PURE__ */ jsxs("label", { className: "rb-flex rb-flex-col rb-gap-1", children: [
850
+ /* @__PURE__ */ jsx("span", { className: "rb-label", children: "Email" }),
851
+ /* @__PURE__ */ jsx(
852
+ "input",
853
+ {
854
+ className: "form-input",
855
+ type: "email",
856
+ value: state.form.email,
857
+ onChange: (event) => dispatch({ type: "setField", field: "email", value: event.target.value }),
858
+ required: true
859
+ }
860
+ )
861
+ ] }),
862
+ state.feedback.tag === "error" ? /* @__PURE__ */ jsx(
863
+ FeedbackMessage,
864
+ {
865
+ tone: "error",
866
+ className: "rb-caption status-muted",
867
+ role: "alert",
868
+ children: state.feedback.message
869
+ }
870
+ ) : null,
871
+ /* @__PURE__ */ jsx(
872
+ "button",
873
+ {
874
+ type: "submit",
875
+ className: themeButtonClassName({ variant: "default", size: "md" }),
876
+ disabled: state.submission === "submitting",
877
+ children: state.submission === "submitting" ? "Loading\u2026" : display.submitButtonText
878
+ }
879
+ )
880
+ ] })
881
+ }
882
+ );
883
+ }
884
+ function CartClient(props) {
885
+ return /* @__PURE__ */ jsx(CartController, { render: props.render });
886
+ }
887
+ function CheckoutClient(props) {
888
+ return /* @__PURE__ */ jsx(CheckoutController, { render: props.render });
889
+ }
890
+ function ProductDetailClient(props) {
891
+ return /* @__PURE__ */ jsx(ProductDetailController, { render: props.render });
892
+ }
893
+ function ProductListClient(props) {
894
+ return /* @__PURE__ */ jsx(ProductListController, { render: props.render });
895
+ }
896
+
897
+ // ../blocks/src/system/runtime/nodes/shop.state.ts
898
+ var EMPTY_FORM = {
899
+ email: "",
900
+ name: ""
901
+ };
902
+ var CLOSED_CHECKOUT = { tag: "closed" };
903
+ var initialShopPassesMembershipsState = {
904
+ returnNotice: null,
905
+ checkout: CLOSED_CHECKOUT
906
+ };
907
+ function createOpenCheckoutState(item) {
908
+ return {
909
+ tag: "open",
910
+ item,
911
+ form: EMPTY_FORM,
912
+ submitting: false,
913
+ formError: null,
914
+ duplicateWarning: null
915
+ };
916
+ }
917
+ function shopPassesMembershipsReducer(state, action) {
918
+ switch (action.type) {
919
+ case "openPassCheckout":
920
+ return {
921
+ ...state,
922
+ checkout: createOpenCheckoutState({
923
+ productType: "pass",
924
+ product: action.pass
925
+ })
926
+ };
927
+ case "openMembershipCheckout":
928
+ return {
929
+ ...state,
930
+ checkout: createOpenCheckoutState({
931
+ productType: "membership",
932
+ product: action.membership
933
+ })
934
+ };
935
+ case "closeCheckout":
936
+ return {
937
+ ...state,
938
+ checkout: CLOSED_CHECKOUT
939
+ };
940
+ case "setCheckoutField":
941
+ if (state.checkout.tag !== "open") return state;
942
+ return {
943
+ ...state,
944
+ checkout: {
945
+ ...state.checkout,
946
+ form: {
947
+ ...state.checkout.form,
948
+ [action.field]: action.value
949
+ },
950
+ formError: null,
951
+ duplicateWarning: null
952
+ }
953
+ };
954
+ case "submitStarted":
955
+ if (state.checkout.tag !== "open") return state;
956
+ return {
957
+ ...state,
958
+ checkout: {
959
+ ...state.checkout,
960
+ submitting: true,
961
+ formError: null,
962
+ duplicateWarning: null
963
+ }
964
+ };
965
+ case "submitFailed":
966
+ if (state.checkout.tag !== "open") return state;
967
+ return {
968
+ ...state,
969
+ checkout: {
970
+ ...state.checkout,
971
+ submitting: false,
972
+ formError: action.error
973
+ }
974
+ };
975
+ case "submitDuplicateWarning":
976
+ if (state.checkout.tag !== "open") return state;
977
+ return {
978
+ ...state,
979
+ checkout: {
980
+ ...state.checkout,
981
+ submitting: false,
982
+ duplicateWarning: action.warning
983
+ }
984
+ };
985
+ case "setReturnNotice":
986
+ return {
987
+ ...state,
988
+ returnNotice: action.notice
989
+ };
990
+ case "clearReturnNotice":
991
+ return {
992
+ ...state,
993
+ returnNotice: null
994
+ };
995
+ default:
996
+ return assertNever(action);
997
+ }
998
+ }
999
+ function getOpenCheckout(state) {
1000
+ return state.checkout.tag === "open" ? state.checkout : null;
1001
+ }
1002
+ var SHOP_ANCHOR_ID = "shop";
1003
+ function ShopClient(props) {
1004
+ return /* @__PURE__ */ jsx(PassesMembershipsController, { render: props.render });
1005
+ }
1006
+ function getCheckoutModalTitle(checkout) {
1007
+ if (!checkout) return "";
1008
+ if (checkout.item.productType === "membership") return "Subscribe to Membership";
1009
+ return checkout.item.product.productUseCase === "appointment_package" ? "Purchase Package" : "Purchase Pass";
1010
+ }
1011
+ function PassesMembershipsController({ render }) {
1012
+ const api = useBlockApi();
1013
+ const [state, dispatch] = React3__default.useReducer(
1014
+ shopPassesMembershipsReducer,
1015
+ initialShopPassesMembershipsState
1016
+ );
1017
+ const passAddFeedback = useTransientShopAddFeedback();
1018
+ const [modalPortalRoot, setModalPortalRoot] = useState(null);
1019
+ const shopRootRef = useRef(null);
1020
+ const emailInputRef = useRef(null);
1021
+ const focusedCheckoutKeyRef = useRef(null);
1022
+ const checkout = getOpenCheckout(state);
1023
+ const display = render.display;
1024
+ const hydration = render.hydration;
1025
+ const checkoutTitle = getCheckoutModalTitle(checkout);
1026
+ const handleBuyPass = useCallback((pass) => {
1027
+ if (hydration.passPurchaseMode === "add_to_cart") {
1028
+ addPassToCommerceCart(api.config.siteId, {
1029
+ passId: pass.id,
1030
+ title: pass.name,
1031
+ unitPriceCents: pass.price,
1032
+ currency: pass.currency
1033
+ });
1034
+ passAddFeedback.markAdded(pass.id);
1035
+ return;
1036
+ }
1037
+ dispatch({ type: "openPassCheckout", pass });
1038
+ }, [api.config.siteId, hydration.passPurchaseMode, passAddFeedback]);
1039
+ const handleSubscribe = useCallback((membership) => {
1040
+ dispatch({ type: "openMembershipCheckout", membership });
1041
+ }, []);
1042
+ const closeCheckout = useCallback(() => {
1043
+ dispatch({ type: "closeCheckout" });
1044
+ }, []);
1045
+ useEffect(() => {
1046
+ const checkoutKey = checkout ? `${checkout.item.productType}:${checkout.item.product.id}` : null;
1047
+ if (!checkoutKey) {
1048
+ focusedCheckoutKeyRef.current = null;
1049
+ return;
1050
+ }
1051
+ if (checkoutKey !== focusedCheckoutKeyRef.current && emailInputRef.current) {
1052
+ emailInputRef.current.focus();
1053
+ focusedCheckoutKeyRef.current = checkoutKey;
1054
+ }
1055
+ }, [checkout]);
1056
+ useEffect(() => {
1057
+ const sourceEl = shopRootRef.current;
1058
+ if (!sourceEl) return;
1059
+ const portal = createBodyThemePortalRoot(sourceEl);
1060
+ if (!portal) return;
1061
+ portal.root.style.setProperty("--rb-modal-z-index", "20000");
1062
+ portal.root.style.setProperty("--rb-modal-overlay-bg", "rgb(var(--tb-text, 0 0 0) / 0.6)");
1063
+ setModalPortalRoot(portal.root);
1064
+ return () => {
1065
+ portal.cleanup();
1066
+ };
1067
+ }, []);
1068
+ useEffect(() => {
1069
+ const { checkout: checkoutStatus } = consumeSearchParams(["checkout", "product", "session_id"]);
1070
+ if (!checkoutStatus) return;
1071
+ scrollToBlock(SHOP_ANCHOR_ID);
1072
+ if (checkoutStatus === "success") {
1073
+ dispatch({ type: "setReturnNotice", notice: { kind: "success" } });
1074
+ return;
1075
+ }
1076
+ if (checkoutStatus === "cancelled") {
1077
+ dispatch({ type: "setReturnNotice", notice: { kind: "cancelled" } });
1078
+ }
1079
+ }, []);
1080
+ const submitCheckout = useCallback(async (confirmDuplicate = false) => {
1081
+ if (!checkout) return;
1082
+ const email = checkout.form.email.trim().toLowerCase();
1083
+ const name = checkout.form.name.trim();
1084
+ if (!email || !name) {
1085
+ dispatch({ type: "submitFailed", error: "Please enter your email and name" });
1086
+ return;
1087
+ }
1088
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
1089
+ dispatch({ type: "submitFailed", error: "Please enter a valid email address" });
1090
+ return;
1091
+ }
1092
+ dispatch({ type: "submitStarted" });
1093
+ try {
1094
+ const currentUrl = window.location.href;
1095
+ const successUrl = new URL(currentUrl);
1096
+ successUrl.searchParams.set("checkout", "success");
1097
+ successUrl.searchParams.set("product", checkout.item.product.id);
1098
+ const cancelUrl = new URL(currentUrl);
1099
+ cancelUrl.searchParams.set("checkout", "cancelled");
1100
+ const result = await api.createShopCheckout({
1101
+ productType: checkout.item.productType,
1102
+ productId: checkout.item.product.id,
1103
+ customerEmail: email,
1104
+ customerName: name,
1105
+ successUrl: successUrl.toString(),
1106
+ cancelUrl: cancelUrl.toString(),
1107
+ confirmDuplicate
1108
+ });
1109
+ if ("warning" in result && result.warning === "duplicate_membership") {
1110
+ dispatch({
1111
+ type: "submitDuplicateWarning",
1112
+ warning: result
1113
+ });
1114
+ return;
1115
+ }
1116
+ if ("bypassed" in result && result.bypassed) {
1117
+ window.location.href = successUrl.toString();
1118
+ return;
1119
+ }
1120
+ if ("checkoutUrl" in result && result.checkoutUrl) {
1121
+ window.location.href = result.checkoutUrl;
1122
+ return;
1123
+ }
1124
+ throw new Error("No checkout URL returned");
1125
+ } catch (submitError) {
1126
+ dispatch({
1127
+ type: "submitFailed",
1128
+ error: submitError instanceof Error ? submitError.message : "An error occurred"
1129
+ });
1130
+ }
1131
+ }, [api, checkout]);
1132
+ const handleFormSubmit = useCallback((event) => {
1133
+ event.preventDefault();
1134
+ submitCheckout(false);
1135
+ }, [submitCheckout]);
1136
+ const handleConfirmDuplicate = useCallback(() => {
1137
+ submitCheckout(true);
1138
+ }, [submitCheckout]);
1139
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1140
+ /* @__PURE__ */ jsx("div", { ref: shopRootRef, children: /* @__PURE__ */ jsx(
1141
+ PassesMembershipsSectionView,
1142
+ {
1143
+ display,
1144
+ beforeContent: /* @__PURE__ */ jsx(Fragment, { children: state.returnNotice ? /* @__PURE__ */ jsx(
1145
+ TerminalPanel,
1146
+ {
1147
+ tone: state.returnNotice.kind === "success" ? "success" : "warning",
1148
+ className: "shop__return",
1149
+ titleClassName: "shop__return-title",
1150
+ bodyClassName: "shop__return-text",
1151
+ icon: /* @__PURE__ */ jsx(StateIcon, { variant: state.returnNotice.kind === "success" ? "success" : "warning", children: state.returnNotice.kind === "success" ? /* @__PURE__ */ jsx(CheckIcon, {}) : /* @__PURE__ */ jsx(CrossIcon, {}) }),
1152
+ title: state.returnNotice.kind === "success" ? "Payment successful" : "Payment cancelled",
1153
+ body: state.returnNotice.kind === "success" ? "Check your email for confirmation." : "You can try again whenever you\u2019re ready.",
1154
+ actions: /* @__PURE__ */ jsx(
1155
+ "button",
1156
+ {
1157
+ type: "button",
1158
+ className: themeButtonClassName({
1159
+ variant: "secondary",
1160
+ size: "sm",
1161
+ extraClassName: "shop__return-dismiss"
1162
+ }),
1163
+ onClick: () => dispatch({ type: "clearReturnNotice" }),
1164
+ children: "Dismiss"
1165
+ }
1166
+ )
1167
+ }
1168
+ ) : null }),
1169
+ passCards: display.passes.map((passDisplay) => {
1170
+ const pass = hydration.passes.find((candidate) => candidate.id === passDisplay.id);
1171
+ if (!pass) return null;
1172
+ return /* @__PURE__ */ jsx(
1173
+ PassCardView,
1174
+ {
1175
+ display: passDisplay,
1176
+ action: /* @__PURE__ */ jsx(
1177
+ "button",
1178
+ {
1179
+ type: "button",
1180
+ className: themeButtonClassName({
1181
+ variant: "default",
1182
+ size: "md",
1183
+ extraClassName: "shop__cta"
1184
+ }),
1185
+ onClick: () => handleBuyPass(pass),
1186
+ children: passDisplay.actionLabel
1187
+ }
1188
+ ),
1189
+ feedback: /* @__PURE__ */ jsx(
1190
+ ShopAddFeedback,
1191
+ {
1192
+ active: passAddFeedback.activeKey === pass.id,
1193
+ itemLabel: pass.name
1194
+ }
1195
+ )
1196
+ },
1197
+ pass.id
1198
+ );
1199
+ }),
1200
+ membershipCards: display.memberships.map((membershipDisplay) => {
1201
+ const membership = hydration.memberships.find((candidate) => candidate.id === membershipDisplay.id);
1202
+ if (!membership) return null;
1203
+ return /* @__PURE__ */ jsx(
1204
+ MembershipCardView,
1205
+ {
1206
+ display: membershipDisplay,
1207
+ action: /* @__PURE__ */ jsx(
1208
+ "button",
1209
+ {
1210
+ type: "button",
1211
+ className: themeButtonClassName({
1212
+ variant: "default",
1213
+ size: "md",
1214
+ extraClassName: "shop__cta"
1215
+ }),
1216
+ onClick: () => handleSubscribe(membership),
1217
+ children: membershipDisplay.actionLabel
1218
+ }
1219
+ )
1220
+ },
1221
+ membership.id
1222
+ );
1223
+ })
1224
+ }
1225
+ ) }),
1226
+ checkout ? /* @__PURE__ */ jsx(
1227
+ ModalShell,
1228
+ {
1229
+ title: checkoutTitle,
1230
+ titleId: "shop-modal-title",
1231
+ onClose: closeCheckout,
1232
+ portal: true,
1233
+ portalRoot: modalPortalRoot,
1234
+ overlayClassName: "rb-modal-overlay",
1235
+ modalClassName: "rb-modal",
1236
+ headerClassName: "rb-modal__header",
1237
+ titleClassName: "rb-modal__title",
1238
+ closeButtonClassName: "rb-modal__close",
1239
+ closeIcon: /* @__PURE__ */ jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }),
1240
+ children: /* @__PURE__ */ jsxs("div", { className: "rb-modal__body", children: [
1241
+ /* @__PURE__ */ jsxs("p", { className: "shop__modal-product", children: [
1242
+ checkout.item.product.name,
1243
+ " \u2014 ",
1244
+ formatCommercePrice(checkout.item.product.price, checkout.item.product.currency),
1245
+ checkout.item.productType === "membership" ? /* @__PURE__ */ jsx("span", { className: "shop__modal-interval", children: formatInterval(checkout.item.product.billingInterval) }) : null
1246
+ ] }),
1247
+ checkout.duplicateWarning ? /* @__PURE__ */ jsxs(
1248
+ FeedbackMessage,
1249
+ {
1250
+ tone: "warning",
1251
+ className: "shop__modal-warning",
1252
+ role: "alert",
1253
+ children: [
1254
+ /* @__PURE__ */ jsx("p", { className: "shop__modal-warning-title", children: "You already have an active membership" }),
1255
+ /* @__PURE__ */ jsxs("p", { className: "shop__modal-warning-text", children: [
1256
+ "Your current membership: ",
1257
+ /* @__PURE__ */ jsx("strong", { children: checkout.duplicateWarning.existingMembership.name }),
1258
+ checkout.duplicateWarning.existingMembership.expiresAt ? /* @__PURE__ */ jsxs(Fragment, { children: [
1259
+ " (expires ",
1260
+ new Date(checkout.duplicateWarning.existingMembership.expiresAt).toLocaleDateString(),
1261
+ ")"
1262
+ ] }) : null
1263
+ ] }),
1264
+ /* @__PURE__ */ jsxs(ActionRow, { className: "shop__modal-warning-actions", children: [
1265
+ /* @__PURE__ */ jsx(
1266
+ "button",
1267
+ {
1268
+ type: "button",
1269
+ className: themeButtonClassName({ variant: "secondary", size: "md" }),
1270
+ onClick: closeCheckout,
1271
+ children: "Cancel"
1272
+ }
1273
+ ),
1274
+ /* @__PURE__ */ jsx(
1275
+ "button",
1276
+ {
1277
+ type: "button",
1278
+ className: themeButtonClassName({ variant: "default", size: "md" }),
1279
+ onClick: handleConfirmDuplicate,
1280
+ disabled: checkout.submitting,
1281
+ children: checkout.submitting ? "Processing..." : "Subscribe Anyway"
1282
+ }
1283
+ )
1284
+ ] })
1285
+ ]
1286
+ }
1287
+ ) : /* @__PURE__ */ jsxs("form", { onSubmit: handleFormSubmit, className: "shop__modal-form", children: [
1288
+ /* @__PURE__ */ jsxs("div", { className: "shop__form-field", children: [
1289
+ /* @__PURE__ */ jsx("label", { htmlFor: "shop-email", className: "form-label", children: "Email address" }),
1290
+ /* @__PURE__ */ jsx(
1291
+ "input",
1292
+ {
1293
+ ref: emailInputRef,
1294
+ type: "email",
1295
+ id: "shop-email",
1296
+ className: "form-input",
1297
+ value: checkout.form.email,
1298
+ onChange: (event) => dispatch({ type: "setCheckoutField", field: "email", value: event.target.value }),
1299
+ placeholder: "you@example.com",
1300
+ required: true,
1301
+ disabled: checkout.submitting
1302
+ }
1303
+ )
1304
+ ] }),
1305
+ /* @__PURE__ */ jsxs("div", { className: "shop__form-field", children: [
1306
+ /* @__PURE__ */ jsx("label", { htmlFor: "shop-name", className: "form-label", children: "Full name" }),
1307
+ /* @__PURE__ */ jsx(
1308
+ "input",
1309
+ {
1310
+ type: "text",
1311
+ id: "shop-name",
1312
+ className: "form-input",
1313
+ value: checkout.form.name,
1314
+ onChange: (event) => dispatch({ type: "setCheckoutField", field: "name", value: event.target.value }),
1315
+ placeholder: "Jane Smith",
1316
+ required: true,
1317
+ disabled: checkout.submitting
1318
+ }
1319
+ )
1320
+ ] }),
1321
+ checkout.formError ? /* @__PURE__ */ jsx(
1322
+ FeedbackMessage,
1323
+ {
1324
+ tone: "error",
1325
+ className: "shop__modal-error",
1326
+ role: "alert",
1327
+ children: checkout.formError
1328
+ }
1329
+ ) : null,
1330
+ /* @__PURE__ */ jsxs(ActionRow, { className: "shop__modal-actions", children: [
1331
+ /* @__PURE__ */ jsx(
1332
+ "button",
1333
+ {
1334
+ type: "button",
1335
+ className: themeButtonClassName({ variant: "secondary", size: "md" }),
1336
+ onClick: closeCheckout,
1337
+ disabled: checkout.submitting,
1338
+ children: "Cancel"
1339
+ }
1340
+ ),
1341
+ /* @__PURE__ */ jsx(
1342
+ "button",
1343
+ {
1344
+ type: "submit",
1345
+ className: themeButtonClassName({ variant: "default", size: "md" }),
1346
+ disabled: checkout.submitting,
1347
+ children: checkout.submitting ? "Processing..." : "Continue to Payment"
1348
+ }
1349
+ )
1350
+ ] })
1351
+ ] })
1352
+ ] })
1353
+ }
1354
+ ) : null
1355
+ ] });
1356
+ }
1357
+ function mountShopBlockIsland(root, props, Component, label) {
1358
+ const { siteId, apiBaseUrl, stage } = props;
1359
+ if (!siteId) {
1360
+ throw new Error(`[sdk:islands] ${label} requires siteId`);
1361
+ }
1362
+ if (!apiBaseUrl) {
1363
+ throw new Error(`[sdk:islands] ${label} requires apiBaseUrl`);
1364
+ }
1365
+ const apiConfig = {
1366
+ baseUrl: apiBaseUrl,
1367
+ auth: { type: "none" },
1368
+ siteId,
1369
+ isPreview: stage === "preview"
1370
+ };
1371
+ return mountReactIsland(root, ({ root: reactRoot }) => {
1372
+ reactRoot.render(
1373
+ /* @__PURE__ */ jsx(BlockApiProvider, { config: apiConfig, children: /* @__PURE__ */ jsx(Component, { ...props }) })
1374
+ );
1375
+ });
1376
+ }
1377
+
1378
+ export { CartClient, CheckoutClient, ProductDetailClient, ProductListClient, ShopClient, mountShopBlockIsland };