@patientos/website-kit 0.2.6 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +54 -2
  2. package/dist/booking-block-D-UOAPx3.d.ts +74 -0
  3. package/dist/cart-block-B-ytc9us.d.ts +20 -0
  4. package/dist/cart-button-BCLFittB.d.ts +24 -0
  5. package/dist/cart-storage.d.ts +110 -0
  6. package/dist/cart-storage.js +39 -0
  7. package/dist/checkout-block-BeXwpqYe.d.ts +24 -0
  8. package/dist/chunk-2CZFJOKK.js +79 -0
  9. package/dist/{chunk-ZYX4TXBN.js → chunk-3T7UWKAH.js} +4 -310
  10. package/dist/{chunk-7ZOQ6UKG.js → chunk-4GKSSM5N.js} +578 -3163
  11. package/dist/chunk-4NF7SSIX.js +367 -0
  12. package/dist/chunk-7BUDUYXN.js +80 -0
  13. package/dist/chunk-FZ4WKWIE.js +310 -0
  14. package/dist/{chunk-ZZFBTLR4.js → chunk-HTVKKLWI.js} +20 -5
  15. package/dist/chunk-IOC6QLB7.js +159 -0
  16. package/dist/chunk-S3ZQQOQT.js +2057 -0
  17. package/dist/chunk-UTAMB2SK.js +214 -0
  18. package/dist/chunk-WQSJ46TP.js +19 -0
  19. package/dist/chunk-ZNH6TSYP.js +167 -0
  20. package/dist/index.d.ts +12 -32
  21. package/dist/index.js +88 -26
  22. package/dist/islands-impl/cart-button.d.ts +19 -0
  23. package/dist/islands-impl/cart-button.js +13 -0
  24. package/dist/islands-impl/cart.d.ts +31 -0
  25. package/dist/islands-impl/cart.js +11 -0
  26. package/dist/islands-impl/checkout.d.ts +6 -0
  27. package/dist/islands-impl/checkout.js +13 -0
  28. package/dist/islands-impl/store.d.ts +30 -0
  29. package/dist/islands-impl/store.js +16 -0
  30. package/dist/islands-impl.d.ts +14 -10
  31. package/dist/islands-impl.js +22 -7
  32. package/dist/islands-registry.js +13 -4
  33. package/dist/{portal-account-8uQc_Ncx.d.ts → portal-account-DRJyr3nz.d.ts} +1 -82
  34. package/dist/{portal-account.client-BBe_q9yC.d.ts → portal-account.client-Bke4NsgW.d.ts} +2 -1
  35. package/dist/portal-booking-client.d.ts +3 -2
  36. package/dist/portal-booking-client.js +2 -1
  37. package/dist/portal-client-my3q5GME.d.ts +83 -0
  38. package/dist/store-block-CFxnsfKU.d.ts +37 -0
  39. package/dist/store-catalog-p7TFOpfk.d.ts +78 -0
  40. package/dist/store-catalog.d.ts +3 -0
  41. package/dist/store-catalog.js +11 -0
  42. package/dist/website-kit.css +193 -0
  43. package/package.json +31 -1
  44. package/dist/checkout-block-BdpPIuQ5.d.ts +0 -136
@@ -0,0 +1,214 @@
1
+ import {
2
+ normalizeCatalogResponse,
3
+ storeCatalogPath
4
+ } from "./chunk-WQSJ46TP.js";
5
+ import {
6
+ createStoreClient,
7
+ formatMoney
8
+ } from "./chunk-7BUDUYXN.js";
9
+ import {
10
+ addToCart,
11
+ cartItemCount,
12
+ readCart,
13
+ subscribeToCart
14
+ } from "./chunk-4NF7SSIX.js";
15
+
16
+ // src/store-block.client.tsx
17
+ import * as React from "react";
18
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
19
+ var SKELETON_CARDS = 6;
20
+ var ADD_BUTTON_ATTR = "data-sk-store-add";
21
+ var STORE_PREHYDRATION_SCRIPT = `(function(){
22
+ if(window.__skStoreArmed)return;window.__skStoreArmed=1;
23
+ document.addEventListener('click',function(e){
24
+ var t=e.target;if(!t||!t.closest)return;
25
+ var b=t.closest('button[${ADD_BUTTON_ATTR}]');if(!b||b.disabled)return;
26
+ var c=b.closest('.sk-store__card');var s=c?c.querySelector('select'):null;
27
+ var v=(s&&s.value)||b.getAttribute('data-sk-store-variant');if(!v)return;
28
+ (b.__skStoreQueue||(b.__skStoreQueue=[])).push({variantId:v,event:e});
29
+ },true);
30
+ })();`;
31
+ function takeQueuedClicks(button) {
32
+ const queue = button?.__skStoreQueue;
33
+ if (!queue || queue.length === 0) return [];
34
+ button.__skStoreQueue = [];
35
+ return queue;
36
+ }
37
+ function dropQueuedClick(button, nativeEvent) {
38
+ const queue = button?.__skStoreQueue;
39
+ if (!queue) return;
40
+ const at = queue.findIndex((entry) => entry.event === nativeEvent);
41
+ if (at >= 0) queue.splice(at, 1);
42
+ }
43
+ function StoreClient({
44
+ categoryHandle,
45
+ productHandle,
46
+ storeApiOrigin,
47
+ title,
48
+ columns,
49
+ hideCartLink,
50
+ initialCatalog
51
+ }) {
52
+ const client = React.useMemo(
53
+ () => createStoreClient({ apiOrigin: storeApiOrigin }),
54
+ [storeApiOrigin]
55
+ );
56
+ const [catalog, setCatalog] = React.useState(
57
+ initialCatalog ?? null
58
+ );
59
+ const [failed, setFailed] = React.useState(false);
60
+ const [count, setCount] = React.useState(0);
61
+ React.useEffect(() => {
62
+ setCount(cartItemCount(readCart()));
63
+ return subscribeToCart((cart) => setCount(cartItemCount(cart)));
64
+ }, []);
65
+ const hasSeed = initialCatalog != null;
66
+ React.useEffect(() => {
67
+ let alive = true;
68
+ void client.get(storeCatalogPath(productHandle)).then((result) => {
69
+ if (!alive) return;
70
+ if (!result.ok) {
71
+ setFailed((previous) => hasSeed ? previous : true);
72
+ return;
73
+ }
74
+ setCatalog(normalizeCatalogResponse(result.data));
75
+ setFailed(false);
76
+ });
77
+ return () => {
78
+ alive = false;
79
+ };
80
+ }, [client, productHandle, hasSeed]);
81
+ const products = React.useMemo(() => {
82
+ if (!catalog) return [];
83
+ if (productHandle || !categoryHandle) return catalog.products;
84
+ return catalog.products.filter(
85
+ (product) => product.categories.some((category) => category.handle === categoryHandle)
86
+ );
87
+ }, [catalog, categoryHandle, productHandle]);
88
+ if (failed && !catalog) {
89
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
90
+ /* @__PURE__ */ jsx("h2", { className: "sk-store__heading", children: title ?? "Shop" }),
91
+ /* @__PURE__ */ jsx("p", { className: "sk-store__placeholder", role: "note", children: "Our shop is unavailable right now. Please contact the clinic to order." })
92
+ ] });
93
+ }
94
+ const gridStyle = columns ? { "--sk-store-columns": columns } : void 0;
95
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
96
+ hasSeed ? /* @__PURE__ */ jsx(
97
+ "script",
98
+ {
99
+ className: "sk-store__prehydration",
100
+ dangerouslySetInnerHTML: { __html: STORE_PREHYDRATION_SCRIPT }
101
+ }
102
+ ) : null,
103
+ /* @__PURE__ */ jsxs("div", { className: "sk-store__header", children: [
104
+ /* @__PURE__ */ jsx("h2", { className: "sk-store__heading", children: title ?? "Shop" }),
105
+ hideCartLink ? null : /* @__PURE__ */ jsxs("a", { className: "sk-store__cart-link", href: "/cart", children: [
106
+ "Cart",
107
+ count > 0 ? ` (${count})` : ""
108
+ ] })
109
+ ] }),
110
+ !catalog ? (
111
+ // A fixed-height skeleton grid, not a line of text: the text-then-grid swap is
112
+ // what made the shop jump on every load.
113
+ /* @__PURE__ */ jsxs(Fragment, { children: [
114
+ /* @__PURE__ */ jsx("p", { className: "sk-store__placeholder sk-store__placeholder--sr", role: "status", children: "Loading the shop\u2026" }),
115
+ /* @__PURE__ */ jsx("ul", { className: "sk-store__grid", style: gridStyle, "aria-hidden": true, children: Array.from({ length: SKELETON_CARDS }, (_, index) => /* @__PURE__ */ jsxs("li", { className: "sk-store__card sk-store__card--skeleton", children: [
116
+ /* @__PURE__ */ jsx("span", { className: "sk-store__image sk-store__image--empty" }),
117
+ /* @__PURE__ */ jsx("span", { className: "sk-store__skeleton-line" }),
118
+ /* @__PURE__ */ jsx("span", { className: "sk-store__skeleton-line sk-store__skeleton-line--short" }),
119
+ /* @__PURE__ */ jsx("span", { className: "sk-store__skeleton-button" })
120
+ ] }, index)) })
121
+ ] })
122
+ ) : products.length === 0 ? /* @__PURE__ */ jsx("p", { className: "sk-store__placeholder", role: "status", children: "Nothing is available to order online just now." }) : /* @__PURE__ */ jsx("ul", { className: "sk-store__grid", style: gridStyle, children: products.map((product) => /* @__PURE__ */ jsx(StoreCard, { product }, product.id)) })
123
+ ] });
124
+ }
125
+ function StoreCard({ product }) {
126
+ const [variantId, setVariantId] = React.useState(
127
+ () => (product.variants.find((v) => v.inStock) ?? product.variants[0]).id
128
+ );
129
+ const [addFeedback, setAddFeedback] = React.useState(null);
130
+ const variant = product.variants.find((v) => v.id === variantId) ?? product.variants[0];
131
+ const image = product.thumbnailUrl ?? product.images[0]?.url ?? null;
132
+ const addRef = React.useRef(null);
133
+ const add = React.useCallback(async (id) => {
134
+ const result = await addToCart(id, 1);
135
+ setAddFeedback(result.status);
136
+ window.setTimeout(() => setAddFeedback(null), 1600);
137
+ }, []);
138
+ const variants = product.variants;
139
+ React.useEffect(() => {
140
+ const queued = takeQueuedClicks(addRef.current);
141
+ if (queued.length === 0) return;
142
+ void (async () => {
143
+ for (const click of queued) {
144
+ const known = variants.some((v) => v.id === click.variantId);
145
+ await add(known ? click.variantId : variantId);
146
+ }
147
+ })();
148
+ }, []);
149
+ function onAdd(nativeEvent) {
150
+ dropQueuedClick(addRef.current, nativeEvent);
151
+ void add(variant.id);
152
+ }
153
+ return /* @__PURE__ */ jsxs("li", { className: "sk-store__card", children: [
154
+ /* @__PURE__ */ jsxs("a", { className: "sk-store__card-link", href: `/store/${product.handle}`, children: [
155
+ image ? (
156
+ // Square by contract (CSS `aspect-ratio` AND the intrinsic ratio in the
157
+ // markup), lazy, and decoded off the main thread: the card must hold its
158
+ // height before the bytes arrive or the whole grid reflows around it.
159
+ /* @__PURE__ */ jsx(
160
+ "img",
161
+ {
162
+ className: "sk-store__image",
163
+ src: image,
164
+ alt: product.images[0]?.alt ?? "",
165
+ width: 600,
166
+ height: 600,
167
+ loading: "lazy",
168
+ decoding: "async"
169
+ }
170
+ )
171
+ ) : /* @__PURE__ */ jsx(
172
+ "span",
173
+ {
174
+ className: "sk-store__image sk-store__image--empty",
175
+ "aria-hidden": true
176
+ }
177
+ ),
178
+ /* @__PURE__ */ jsx("span", { className: "sk-store__title", children: product.title })
179
+ ] }),
180
+ /* @__PURE__ */ jsxs("span", { className: "sk-store__price", children: [
181
+ formatMoney(variant.price),
182
+ product.gstApplicable ? /* @__PURE__ */ jsx("span", { className: "sk-store__gst", children: " incl. GST" }) : null
183
+ ] }),
184
+ product.variants.length > 1 ? /* @__PURE__ */ jsxs("label", { className: "sk-store__variant", children: [
185
+ /* @__PURE__ */ jsx("span", { className: "sk-store__variant-label", children: "Option" }),
186
+ /* @__PURE__ */ jsx(
187
+ "select",
188
+ {
189
+ value: variantId,
190
+ onChange: (e) => setVariantId(e.target.value),
191
+ children: product.variants.map((v) => /* @__PURE__ */ jsx("option", { value: v.id, disabled: !v.inStock, children: (v.options.length ? v.options.join(" / ") : v.title) + (v.inStock ? "" : " \u2014 sold out") }, v.id))
192
+ }
193
+ )
194
+ ] }) : null,
195
+ /* @__PURE__ */ jsx(
196
+ "button",
197
+ {
198
+ type: "button",
199
+ className: "sk-store__add",
200
+ ref: addRef,
201
+ "data-sk-store-add": "",
202
+ "data-sk-store-variant": variant.id,
203
+ onClick: (event) => onAdd(event.nativeEvent),
204
+ disabled: !variant.inStock,
205
+ children: !variant.inStock ? "Sold out" : addFeedback === "added" ? "Added \u2713" : addFeedback === "line_limit" ? "Cart full \u2014 50 products max" : addFeedback === "quantity_limit" ? "Maximum 99 in cart" : addFeedback === "cart_storage_unavailable" ? "This browser can\u2019t safely update the cart" : "Add to cart"
206
+ }
207
+ )
208
+ ] });
209
+ }
210
+
211
+ export {
212
+ STORE_PREHYDRATION_SCRIPT,
213
+ StoreClient
214
+ };
@@ -0,0 +1,19 @@
1
+ // src/store-catalog.ts
2
+ function storeCatalogPath(productHandle) {
3
+ return productHandle ? `/api/store/catalog/${encodeURIComponent(productHandle)}` : "/api/store/catalog";
4
+ }
5
+ function normalizeCatalogResponse(data) {
6
+ return "product" in data ? { products: [data.product], currency: data.currency } : data;
7
+ }
8
+ async function fetchStoreCatalog(client, options = {}) {
9
+ const result = await client.get(
10
+ storeCatalogPath(options.productHandle)
11
+ );
12
+ return result.ok ? normalizeCatalogResponse(result.data) : null;
13
+ }
14
+
15
+ export {
16
+ storeCatalogPath,
17
+ normalizeCatalogResponse,
18
+ fetchStoreCatalog
19
+ };
@@ -0,0 +1,167 @@
1
+ import {
2
+ CartLineList,
3
+ CartProblemList,
4
+ CartTotals,
5
+ UnsupportedCartLineList,
6
+ quoteSourceQuantities,
7
+ useCartQuote
8
+ } from "./chunk-IOC6QLB7.js";
9
+ import {
10
+ createStoreClient
11
+ } from "./chunk-7BUDUYXN.js";
12
+ import {
13
+ cartItemCount,
14
+ readCart,
15
+ subscribeToCart
16
+ } from "./chunk-4NF7SSIX.js";
17
+
18
+ // src/cart-button.client.tsx
19
+ import * as React from "react";
20
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
21
+ var EMPTY_CART = { v: 1, lines: [] };
22
+ function CartButtonClient({
23
+ storeApiOrigin,
24
+ label,
25
+ cartHref,
26
+ checkoutHref
27
+ }) {
28
+ const [count, setCount] = React.useState(0);
29
+ const [open, setOpen] = React.useState(false);
30
+ React.useEffect(() => {
31
+ setCount(cartItemCount(readCart()));
32
+ return subscribeToCart((cart) => setCount(cartItemCount(cart)));
33
+ }, []);
34
+ const text = label ?? "Cart";
35
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
36
+ /* @__PURE__ */ jsxs(
37
+ "button",
38
+ {
39
+ type: "button",
40
+ className: "sk-cart-button__trigger",
41
+ "aria-expanded": open,
42
+ "aria-haspopup": "dialog",
43
+ "aria-label": count > 0 ? `${text} \u2014 ${count} item${count === 1 ? "" : "s"}` : text,
44
+ onClick: () => setOpen((wasOpen) => !wasOpen),
45
+ children: [
46
+ /* @__PURE__ */ jsx("span", { className: "sk-cart-button__label", children: text }),
47
+ count > 0 ? /* @__PURE__ */ jsx("span", { className: "sk-cart-button__badge", "aria-hidden": true, children: count > 99 ? "99+" : count }) : null
48
+ ]
49
+ }
50
+ ),
51
+ /* @__PURE__ */ jsx(
52
+ CartDrawer,
53
+ {
54
+ open,
55
+ onClose: () => setOpen(false),
56
+ storeApiOrigin,
57
+ cartHref,
58
+ checkoutHref
59
+ }
60
+ )
61
+ ] });
62
+ }
63
+ function CartDrawer({
64
+ open,
65
+ onClose,
66
+ storeApiOrigin,
67
+ cartHref,
68
+ checkoutHref
69
+ }) {
70
+ const client = React.useMemo(
71
+ () => createStoreClient({ apiOrigin: storeApiOrigin }),
72
+ [storeApiOrigin]
73
+ );
74
+ const [cart, setCart] = React.useState(EMPTY_CART);
75
+ const [mutationUnavailable, setMutationUnavailable] = React.useState(false);
76
+ const closeRef = React.useRef(null);
77
+ React.useEffect(() => {
78
+ setCart(readCart());
79
+ return subscribeToCart(setCart);
80
+ }, []);
81
+ React.useEffect(() => {
82
+ if (!open) return;
83
+ closeRef.current?.focus();
84
+ const onKeyDown = (event) => {
85
+ if (event.key === "Escape") onClose();
86
+ };
87
+ window.addEventListener("keydown", onKeyDown);
88
+ return () => window.removeEventListener("keydown", onKeyDown);
89
+ }, [open, onClose]);
90
+ const { quote, quoteSourceCart, failed } = useCartQuote(client, cart, open);
91
+ async function runMutation(mutation) {
92
+ const result = await mutation;
93
+ setMutationUnavailable(result.status === "cart_storage_unavailable");
94
+ }
95
+ if (!open) return null;
96
+ const quotedVariantIds = new Set(quote?.lines.map((l) => l.variantId) ?? []);
97
+ const unsupportedLines = cart.lines.filter(
98
+ (line) => line.kind !== "retail" || quote !== null && !quotedVariantIds.has(line.variantId)
99
+ );
100
+ return /* @__PURE__ */ jsxs("div", { className: "sk-cart-drawer", children: [
101
+ /* @__PURE__ */ jsx(
102
+ "div",
103
+ {
104
+ className: "sk-cart-drawer__scrim",
105
+ onClick: onClose,
106
+ "aria-hidden": true
107
+ }
108
+ ),
109
+ /* @__PURE__ */ jsxs(
110
+ "aside",
111
+ {
112
+ className: "sk-cart-drawer__panel",
113
+ role: "dialog",
114
+ "aria-modal": "true",
115
+ "aria-label": "Your cart",
116
+ children: [
117
+ /* @__PURE__ */ jsxs("div", { className: "sk-cart-drawer__head", children: [
118
+ /* @__PURE__ */ jsx("h2", { className: "sk-cart__heading", children: "Your cart" }),
119
+ /* @__PURE__ */ jsx(
120
+ "button",
121
+ {
122
+ type: "button",
123
+ ref: closeRef,
124
+ className: "sk-cart-drawer__close",
125
+ onClick: onClose,
126
+ children: "Close"
127
+ }
128
+ )
129
+ ] }),
130
+ /* @__PURE__ */ jsx("div", { className: "sk-cart-drawer__body", children: cart.lines.length === 0 ? /* @__PURE__ */ jsx("p", { className: "sk-cart__placeholder", role: "status", children: "Your cart is empty." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
131
+ quote ? /* @__PURE__ */ jsx(CartProblemList, { problems: quote.problems }) : null,
132
+ failed ? /* @__PURE__ */ jsx("p", { className: "sk-cart__placeholder", role: "alert", children: "We couldn't price your cart just now. Please try again in a moment." }) : null,
133
+ mutationUnavailable ? /* @__PURE__ */ jsx("p", { className: "sk-cart__placeholder", role: "alert", children: "This browser can\u2019t safely update the cart. Try a supported browser with site storage enabled." }) : null,
134
+ /* @__PURE__ */ jsx(
135
+ UnsupportedCartLineList,
136
+ {
137
+ lines: unsupportedLines,
138
+ onMutate: (m) => void runMutation(m)
139
+ }
140
+ ),
141
+ quote ? /* @__PURE__ */ jsxs(Fragment, { children: [
142
+ /* @__PURE__ */ jsx(
143
+ CartLineList,
144
+ {
145
+ lines: quote.lines,
146
+ sourceQuantities: quoteSourceQuantities(quoteSourceCart),
147
+ onMutate: (m) => void runMutation(m)
148
+ }
149
+ ),
150
+ /* @__PURE__ */ jsx(CartTotals, { quote }),
151
+ quote.requiresShipping ? /* @__PURE__ */ jsx("p", { className: "sk-cart__note", children: "Delivery details are confirmed at checkout." }) : null
152
+ ] }) : failed ? null : /* @__PURE__ */ jsx("p", { className: "sk-cart__placeholder", role: "status", children: "Pricing your cart\u2026" })
153
+ ] }) }),
154
+ /* @__PURE__ */ jsxs("div", { className: "sk-cart-drawer__foot", children: [
155
+ /* @__PURE__ */ jsx("a", { className: "sk-cart__continue", href: cartHref ?? "/cart", children: "View cart" }),
156
+ quote && quote.lines.length > 0 && unsupportedLines.length === 0 ? /* @__PURE__ */ jsx("a", { className: "sk-cart__checkout", href: checkoutHref ?? "/checkout", children: "Checkout" }) : null
157
+ ] })
158
+ ]
159
+ }
160
+ )
161
+ ] });
162
+ }
163
+
164
+ export {
165
+ CartButtonClient,
166
+ CartDrawer
167
+ };
package/dist/index.d.ts CHANGED
@@ -2,9 +2,16 @@ export { BPOINT_SCRIPT_ORIGIN, BPOINT_SCRIPT_URL, CONSULT_CHUNK_PATH, THIRD_PART
2
2
  import * as React from 'react';
3
3
  export { ISLAND_ATTR, ISLAND_PROPS_ATTR, Island, IslandProps, IslandRegistry, mountIslandsWith, readIslandProps } from './islands.js';
4
4
  export { ISLANDS, IslandName, mountIslands } from './islands-registry.js';
5
- import { P as PortalClient, W as WhoAmI, a as PortalPrefill, b as PublicClaim, F as FetchLike } from './portal-account-8uQc_Ncx.js';
6
- export { c as PortalAccount, d as PortalAccountMarkerProps, e as PortalAccountProps, f as PortalAccountSurface, g as PortalClientConfig, h as PortalPanel, i as PortalPanelMarkerProps, j as PortalPanelProps, k as PortalResult, l as PortalSurface, m as createPortalClient } from './portal-account-8uQc_Ncx.js';
7
- export { B as BookingBlock, a as BookingBlockMarkerProps, b as BookingBlockProps, c as BookingTypeOption, C as Cart, d as CartMarkerProps, e as CartProps, f as CertificateFunnel, g as CertificateFunnelMarkerProps, h as CertificateFunnelProps, i as Checkout, j as CheckoutMarkerProps, k as CheckoutProps, F as FunnelPublicApi, l as FunnelServiceOption, S as Store, m as StoreMarkerProps, n as StoreProps } from './checkout-block-BdpPIuQ5.js';
5
+ import { P as PortalClient, W as WhoAmI, a as PortalPrefill, b as PublicClaim } from './portal-client-my3q5GME.js';
6
+ export { F as FetchLike, c as PortalClientConfig, d as PortalResult, e as createPortalClient } from './portal-client-my3q5GME.js';
7
+ export { S as StoreCatalog, a as StoreCatalogResponse, b as StoreClient, c as StoreClientConfig, d as StoreProduct, e as StoreResult, f as StoreVariant, g as createStoreClient, h as fetchStoreCatalog, n as normalizeCatalogResponse, s as storeCatalogPath } from './store-catalog-p7TFOpfk.js';
8
+ export { CartLine, CartLineKind, CartMutationResult, Cart as StoredCart, addToCart, adjustCartQuantity, cartItemCount, cartToWireLines, clearCart, mutateCart, readCart, removeFromCart, setCartQuantity, subscribeToCart } from './cart-storage.js';
9
+ export { S as Store, a as StoreMarkerProps, b as StoreProps, f as formatMoney } from './store-block-CFxnsfKU.js';
10
+ export { B as BookingBlock, a as BookingBlockMarkerProps, b as BookingBlockProps, c as BookingTypeOption, C as CertificateFunnel, d as CertificateFunnelMarkerProps, e as CertificateFunnelProps, F as FunnelPublicApi, f as FunnelServiceOption } from './booking-block-D-UOAPx3.js';
11
+ export { P as PortalAccount, a as PortalAccountMarkerProps, b as PortalAccountProps, c as PortalAccountSurface, d as PortalPanel, e as PortalPanelMarkerProps, f as PortalPanelProps, g as PortalSurface } from './portal-account-DRJyr3nz.js';
12
+ export { C as Cart, a as CartMarkerProps, b as CartProps } from './cart-block-B-ytc9us.js';
13
+ export { C as CartButton, a as CartButtonMarkerProps, b as CartButtonProps } from './cart-button-BCLFittB.js';
14
+ export { C as Checkout, a as CheckoutMarkerProps, b as CheckoutProps } from './checkout-block-BeXwpqYe.js';
8
15
  export { PatientSurfaceTheme, patientSurfaceTheme } from './patient-surface-theme.js';
9
16
  export { Rgb, contrastRatio, deriveForeground, parseHexColor, relativeLuminance } from './contrast.js';
10
17
  export { AnswerValue, Answers, ShowWhenCondition, evaluateShowWhen, visibleQuestions } from './show-when.js';
@@ -210,33 +217,6 @@ interface PortalSession {
210
217
  */
211
218
  declare function usePortalSession(): PortalSession;
212
219
 
213
- interface StoreClientConfig {
214
- /** The validated PatientOS patient/API origin. Empty means same-origin. */
215
- apiOrigin?: string | null;
216
- /** Injectable browser transport for tests. */
217
- fetchImpl?: FetchLike;
218
- }
219
- type StoreResult<T> = {
220
- ok: true;
221
- data: T;
222
- } | {
223
- ok: false;
224
- status: number | null;
225
- error: string | null;
226
- message: string | null;
227
- };
228
- interface StoreClient {
229
- readonly apiOrigin: string;
230
- url(path: string): string;
231
- get<T>(path: string): Promise<StoreResult<T>>;
232
- post<T>(path: string, body: unknown, options?: {
233
- signal?: AbortSignal;
234
- }): Promise<StoreResult<T>>;
235
- /** PatientOS magic-link sign-in that returns to this site's checkout. */
236
- signInHref(returnPath?: string): string;
237
- }
238
- declare function createStoreClient(config?: StoreClientConfig): StoreClient;
239
-
240
- declare const WEBSITE_KIT_VERSION = "0.1.0";
220
+ declare const WEBSITE_KIT_VERSION = "0.2.8";
241
221
 
242
- export { Button, type ButtonProps, type ButtonVariant, Container, type ContainerProps, FAQ, type FAQProps, type FaqItem, FetchLike, Hero, type HeroProps, Hours, type HoursProps, type HoursRow, MapEmbed, type MapEmbedProps, PortalClient, PortalPrefill, PortalProvider, type PortalProviderProps, type PortalSession, type PortalSessionStatus, PublicClaim, Row, type RowProps, Section, type SectionProps, type Service, ServiceGrid, type ServiceGridProps, Stack, type StackProps, type StoreClient, type StoreClientConfig, type StoreResult, TeamGrid, type TeamGridProps, type TeamMember, ThemeProvider, type ThemeProviderProps, WEBSITE_KIT_VERSION, WebsiteShell, type WebsiteShellProps, type WebsiteTheme, WhoAmI, createStoreClient, defaultTheme, resolveTheme, themeToCssVars, usePortalClient, usePortalSession };
222
+ export { Button, type ButtonProps, type ButtonVariant, Container, type ContainerProps, FAQ, type FAQProps, type FaqItem, Hero, type HeroProps, Hours, type HoursProps, type HoursRow, MapEmbed, type MapEmbedProps, PortalClient, PortalPrefill, PortalProvider, type PortalProviderProps, type PortalSession, type PortalSessionStatus, PublicClaim, Row, type RowProps, Section, type SectionProps, type Service, ServiceGrid, type ServiceGridProps, Stack, type StackProps, TeamGrid, type TeamGridProps, type TeamMember, ThemeProvider, type ThemeProviderProps, WEBSITE_KIT_VERSION, WebsiteShell, type WebsiteShellProps, type WebsiteTheme, WhoAmI, defaultTheme, resolveTheme, themeToCssVars, usePortalClient, usePortalSession };
package/dist/index.js CHANGED
@@ -1,12 +1,21 @@
1
+ import {
2
+ contrastRatio,
3
+ deriveForeground,
4
+ derivePrimaryPair,
5
+ parseHexColor,
6
+ relativeLuminance
7
+ } from "./chunk-LCHW6XF4.js";
8
+ import {
9
+ patientSurfaceTheme
10
+ } from "./chunk-JTZ6GYA7.js";
1
11
  import {
2
12
  ISLANDS,
3
13
  mountIslands
4
- } from "./chunk-ZZFBTLR4.js";
14
+ } from "./chunk-HTVKKLWI.js";
5
15
  import {
6
16
  PortalAccount,
7
17
  PortalPanel,
8
18
  PortalProvider,
9
- createStoreClient,
10
19
  getAppointmentTypes,
11
20
  getClinic,
12
21
  getPublicApi,
@@ -14,15 +23,8 @@ import {
14
23
  getThemeTokens,
15
24
  usePortalClient,
16
25
  usePortalSession
17
- } from "./chunk-7ZOQ6UKG.js";
18
- import {
19
- BPOINT_SCRIPT_ORIGIN,
20
- BPOINT_SCRIPT_URL,
21
- CONSULT_CHUNK_PATH,
22
- THIRD_PARTY_SCRIPT_ORIGINS,
23
- TURNSTILE_SCRIPT_ORIGIN,
24
- TURNSTILE_SCRIPT_URL
25
- } from "./chunk-ZOF22TJA.js";
26
+ } from "./chunk-4GKSSM5N.js";
27
+ import "./chunk-3T7UWKAH.js";
26
28
  import {
27
29
  evaluateShowWhen,
28
30
  visibleQuestions
@@ -34,19 +36,43 @@ import {
34
36
  mountIslandsWith,
35
37
  readIslandProps
36
38
  } from "./chunk-THMT43MV.js";
39
+ import "./chunk-2CZFJOKK.js";
40
+ import "./chunk-ZNH6TSYP.js";
41
+ import "./chunk-IOC6QLB7.js";
42
+ import "./chunk-S3ZQQOQT.js";
37
43
  import {
38
- createPortalClient
39
- } from "./chunk-ZYX4TXBN.js";
44
+ BPOINT_SCRIPT_ORIGIN,
45
+ BPOINT_SCRIPT_URL,
46
+ CONSULT_CHUNK_PATH,
47
+ THIRD_PARTY_SCRIPT_ORIGINS,
48
+ TURNSTILE_SCRIPT_ORIGIN,
49
+ TURNSTILE_SCRIPT_URL
50
+ } from "./chunk-ZOF22TJA.js";
51
+ import "./chunk-UTAMB2SK.js";
40
52
  import {
41
- contrastRatio,
42
- deriveForeground,
43
- derivePrimaryPair,
44
- parseHexColor,
45
- relativeLuminance
46
- } from "./chunk-LCHW6XF4.js";
53
+ fetchStoreCatalog,
54
+ normalizeCatalogResponse,
55
+ storeCatalogPath
56
+ } from "./chunk-WQSJ46TP.js";
47
57
  import {
48
- patientSurfaceTheme
49
- } from "./chunk-JTZ6GYA7.js";
58
+ createStoreClient,
59
+ formatMoney
60
+ } from "./chunk-7BUDUYXN.js";
61
+ import {
62
+ addToCart,
63
+ adjustCartQuantity,
64
+ cartItemCount,
65
+ cartToWireLines,
66
+ clearCart,
67
+ mutateCart,
68
+ readCart,
69
+ removeFromCart,
70
+ setCartQuantity,
71
+ subscribeToCart
72
+ } from "./chunk-4NF7SSIX.js";
73
+ import {
74
+ createPortalClient
75
+ } from "./chunk-FZ4WKWIE.js";
50
76
  import "./chunk-MLKGABMK.js";
51
77
 
52
78
  // src/tokens.tsx
@@ -408,13 +434,14 @@ function Store({
408
434
  storeApiOrigin,
409
435
  title,
410
436
  columns,
437
+ hideCartLink,
411
438
  className
412
439
  }) {
413
440
  return /* @__PURE__ */ jsxs5(
414
441
  Island,
415
442
  {
416
443
  name: "store",
417
- props: { categoryHandle, productHandle, storeApiOrigin, title, columns },
444
+ props: { categoryHandle, productHandle, storeApiOrigin, title, columns, hideCartLink },
418
445
  className: ["sk-store", className].filter(Boolean).join(" "),
419
446
  children: [
420
447
  /* @__PURE__ */ jsx6("h2", { className: "sk-store__heading", children: title ?? "Shop" }),
@@ -441,8 +468,28 @@ function Cart({ storeApiOrigin, className }) {
441
468
  );
442
469
  }
443
470
 
471
+ // src/cart-button.tsx
472
+ import { jsx as jsx8 } from "react/jsx-runtime";
473
+ function CartButton({
474
+ storeApiOrigin,
475
+ label,
476
+ cartHref,
477
+ checkoutHref,
478
+ className
479
+ }) {
480
+ return /* @__PURE__ */ jsx8(
481
+ Island,
482
+ {
483
+ name: "cart-button",
484
+ props: { storeApiOrigin, label, cartHref, checkoutHref },
485
+ className: ["sk-cart-button", className].filter(Boolean).join(" "),
486
+ children: /* @__PURE__ */ jsx8("a", { className: "sk-cart-button__trigger", href: cartHref ?? "/cart", children: label ?? "Cart" })
487
+ }
488
+ );
489
+ }
490
+
444
491
  // src/checkout-block.tsx
445
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
492
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
446
493
  function Checkout({
447
494
  storeApiOrigin,
448
495
  ordersHref,
@@ -456,15 +503,15 @@ function Checkout({
456
503
  props: { storeApiOrigin, ordersHref, completionHref },
457
504
  className: ["sk-checkout", className].filter(Boolean).join(" "),
458
505
  children: [
459
- /* @__PURE__ */ jsx8("h2", { className: "sk-checkout__heading", children: "Checkout" }),
460
- /* @__PURE__ */ jsx8("p", { className: "sk-checkout__placeholder", children: "Checkout needs JavaScript to load." })
506
+ /* @__PURE__ */ jsx9("h2", { className: "sk-checkout__heading", children: "Checkout" }),
507
+ /* @__PURE__ */ jsx9("p", { className: "sk-checkout__placeholder", children: "Checkout needs JavaScript to load." })
461
508
  ]
462
509
  }
463
510
  );
464
511
  }
465
512
 
466
513
  // src/index.ts
467
- var WEBSITE_KIT_VERSION = "0.1.0";
514
+ var WEBSITE_KIT_VERSION = "0.2.8";
468
515
  export {
469
516
  BPOINT_SCRIPT_ORIGIN,
470
517
  BPOINT_SCRIPT_URL,
@@ -472,6 +519,7 @@ export {
472
519
  Button,
473
520
  CONSULT_CHUNK_PATH,
474
521
  Cart,
522
+ CartButton,
475
523
  CertificateFunnel,
476
524
  Checkout,
477
525
  Container,
@@ -498,19 +546,33 @@ export {
498
546
  ThemeProvider,
499
547
  WEBSITE_KIT_VERSION,
500
548
  WebsiteShell,
549
+ addToCart,
550
+ adjustCartQuantity,
551
+ cartItemCount,
552
+ cartToWireLines,
553
+ clearCart,
501
554
  contrastRatio,
502
555
  createPortalClient,
503
556
  createStoreClient,
504
557
  defaultTheme,
505
558
  deriveForeground,
506
559
  evaluateShowWhen,
560
+ fetchStoreCatalog,
561
+ formatMoney,
507
562
  mountIslands,
508
563
  mountIslandsWith,
564
+ mutateCart,
565
+ normalizeCatalogResponse,
509
566
  parseHexColor,
510
567
  patientSurfaceTheme,
568
+ readCart,
511
569
  readIslandProps,
512
570
  relativeLuminance,
571
+ removeFromCart,
513
572
  resolveTheme,
573
+ setCartQuantity,
574
+ storeCatalogPath,
575
+ subscribeToCart,
514
576
  themeToCssVars,
515
577
  usePortalClient,
516
578
  usePortalSession,
@@ -0,0 +1,19 @@
1
+ import * as React from 'react';
2
+ import { b as CartButtonProps } from '../cart-button-BCLFittB.js';
3
+
4
+ declare function CartButtonClient({ storeApiOrigin, label, cartHref, checkoutHref, }: CartButtonProps): React.ReactElement;
5
+ type CartDrawerProps = {
6
+ open: boolean;
7
+ onClose: () => void;
8
+ /** PatientOS patient/API origin. Omitted keeps the same-origin contract. */
9
+ storeApiOrigin?: string;
10
+ cartHref?: string;
11
+ checkoutHref?: string;
12
+ };
13
+ /**
14
+ * The slide-over cart. Mounted always, INERT until `open` — that is what keeps the
15
+ * quote request lazy while still letting the hooks run unconditionally.
16
+ */
17
+ declare function CartDrawer({ open, onClose, storeApiOrigin, cartHref, checkoutHref, }: CartDrawerProps): React.ReactElement | null;
18
+
19
+ export { CartButtonClient, CartDrawer, type CartDrawerProps };