@patientos/website-kit 0.2.7 → 0.2.9

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.
@@ -75,7 +75,7 @@ function CartLineList({
75
75
  children: "\u2212"
76
76
  }
77
77
  ),
78
- /* @__PURE__ */ jsx("span", { "aria-live": "polite", children: line.quantity }),
78
+ /* @__PURE__ */ jsx("span", { className: "sk-cart__qty-value", "aria-live": "polite", children: line.quantity }),
79
79
  /* @__PURE__ */ jsx(
80
80
  "button",
81
81
  {
@@ -9,16 +9,16 @@ import {
9
9
  } from "./chunk-THMT43MV.js";
10
10
  import {
11
11
  CartClient
12
- } from "./chunk-2CZFJOKK.js";
12
+ } from "./chunk-U6FJ6XYX.js";
13
13
  import {
14
14
  CartButtonClient
15
- } from "./chunk-ZNH6TSYP.js";
15
+ } from "./chunk-ORGTP5GB.js";
16
16
  import {
17
17
  CheckoutClient
18
18
  } from "./chunk-S3ZQQOQT.js";
19
19
  import {
20
20
  StoreClient
21
- } from "./chunk-673YEXAI.js";
21
+ } from "./chunk-UTAMB2SK.js";
22
22
 
23
23
  // src/islands-registry.tsx
24
24
  var ISLANDS = {
@@ -5,7 +5,7 @@ import {
5
5
  UnsupportedCartLineList,
6
6
  quoteSourceQuantities,
7
7
  useCartQuote
8
- } from "./chunk-IOC6QLB7.js";
8
+ } from "./chunk-BHF7S6UQ.js";
9
9
  import {
10
10
  createStoreClient
11
11
  } from "./chunk-7BUDUYXN.js";
@@ -5,7 +5,7 @@ import {
5
5
  UnsupportedCartLineList,
6
6
  quoteSourceQuantities,
7
7
  useCartQuote
8
- } from "./chunk-IOC6QLB7.js";
8
+ } from "./chunk-BHF7S6UQ.js";
9
9
  import {
10
10
  createStoreClient
11
11
  } from "./chunk-7BUDUYXN.js";
@@ -17,6 +17,29 @@ import {
17
17
  import * as React from "react";
18
18
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
19
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
+ }
20
43
  function StoreClient({
21
44
  categoryHandle,
22
45
  productHandle,
@@ -70,6 +93,13 @@ function StoreClient({
70
93
  }
71
94
  const gridStyle = columns ? { "--sk-store-columns": columns } : void 0;
72
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,
73
103
  /* @__PURE__ */ jsxs("div", { className: "sk-store__header", children: [
74
104
  /* @__PURE__ */ jsx("h2", { className: "sk-store__heading", children: title ?? "Shop" }),
75
105
  hideCartLink ? null : /* @__PURE__ */ jsxs("a", { className: "sk-store__cart-link", href: "/cart", children: [
@@ -99,10 +129,26 @@ function StoreCard({ product }) {
99
129
  const [addFeedback, setAddFeedback] = React.useState(null);
100
130
  const variant = product.variants.find((v) => v.id === variantId) ?? product.variants[0];
101
131
  const image = product.thumbnailUrl ?? product.images[0]?.url ?? null;
102
- async function onAdd() {
103
- const result = await addToCart(variant.id, 1);
132
+ const addRef = React.useRef(null);
133
+ const add = React.useCallback(async (id) => {
134
+ const result = await addToCart(id, 1);
104
135
  setAddFeedback(result.status);
105
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);
106
152
  }
107
153
  return /* @__PURE__ */ jsxs("li", { className: "sk-store__card", children: [
108
154
  /* @__PURE__ */ jsxs("a", { className: "sk-store__card-link", href: `/store/${product.handle}`, children: [
@@ -151,7 +197,10 @@ function StoreCard({ product }) {
151
197
  {
152
198
  type: "button",
153
199
  className: "sk-store__add",
154
- onClick: () => void onAdd(),
200
+ ref: addRef,
201
+ "data-sk-store-add": "",
202
+ "data-sk-store-variant": variant.id,
203
+ onClick: (event) => onAdd(event.nativeEvent),
155
204
  disabled: !variant.inStock,
156
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"
157
206
  }
@@ -160,5 +209,6 @@ function StoreCard({ product }) {
160
209
  }
161
210
 
162
211
  export {
212
+ STORE_PREHYDRATION_SCRIPT,
163
213
  StoreClient
164
214
  };
package/dist/index.d.ts CHANGED
@@ -217,6 +217,6 @@ interface PortalSession {
217
217
  */
218
218
  declare function usePortalSession(): PortalSession;
219
219
 
220
- declare const WEBSITE_KIT_VERSION = "0.2.7";
220
+ declare const WEBSITE_KIT_VERSION = "0.2.9";
221
221
 
222
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
@@ -11,7 +11,7 @@ import {
11
11
  import {
12
12
  ISLANDS,
13
13
  mountIslands
14
- } from "./chunk-HYXTDUP2.js";
14
+ } from "./chunk-DP2HJMVB.js";
15
15
  import {
16
16
  PortalAccount,
17
17
  PortalPanel,
@@ -36,9 +36,9 @@ import {
36
36
  mountIslandsWith,
37
37
  readIslandProps
38
38
  } from "./chunk-THMT43MV.js";
39
- import "./chunk-2CZFJOKK.js";
40
- import "./chunk-ZNH6TSYP.js";
41
- import "./chunk-IOC6QLB7.js";
39
+ import "./chunk-U6FJ6XYX.js";
40
+ import "./chunk-ORGTP5GB.js";
41
+ import "./chunk-BHF7S6UQ.js";
42
42
  import "./chunk-S3ZQQOQT.js";
43
43
  import {
44
44
  BPOINT_SCRIPT_ORIGIN,
@@ -48,7 +48,7 @@ import {
48
48
  TURNSTILE_SCRIPT_ORIGIN,
49
49
  TURNSTILE_SCRIPT_URL
50
50
  } from "./chunk-ZOF22TJA.js";
51
- import "./chunk-673YEXAI.js";
51
+ import "./chunk-UTAMB2SK.js";
52
52
  import {
53
53
  fetchStoreCatalog,
54
54
  normalizeCatalogResponse,
@@ -511,7 +511,7 @@ function Checkout({
511
511
  }
512
512
 
513
513
  // src/index.ts
514
- var WEBSITE_KIT_VERSION = "0.2.7";
514
+ var WEBSITE_KIT_VERSION = "0.2.9";
515
515
  export {
516
516
  BPOINT_SCRIPT_ORIGIN,
517
517
  BPOINT_SCRIPT_URL,
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  CartButtonClient,
3
3
  CartDrawer
4
- } from "../chunk-ZNH6TSYP.js";
5
- import "../chunk-IOC6QLB7.js";
4
+ } from "../chunk-ORGTP5GB.js";
5
+ import "../chunk-BHF7S6UQ.js";
6
6
  import "../chunk-7BUDUYXN.js";
7
7
  import "../chunk-4NF7SSIX.js";
8
8
  import "../chunk-FZ4WKWIE.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  CartClient
3
- } from "../chunk-2CZFJOKK.js";
4
- import "../chunk-IOC6QLB7.js";
3
+ } from "../chunk-U6FJ6XYX.js";
4
+ import "../chunk-BHF7S6UQ.js";
5
5
  import "../chunk-7BUDUYXN.js";
6
6
  import "../chunk-4NF7SSIX.js";
7
7
  import "../chunk-FZ4WKWIE.js";
@@ -2,7 +2,7 @@ import {
2
2
  CheckoutClient
3
3
  } from "../chunk-S3ZQQOQT.js";
4
4
  import "../chunk-ZOF22TJA.js";
5
- import "../chunk-673YEXAI.js";
5
+ import "../chunk-UTAMB2SK.js";
6
6
  import "../chunk-WQSJ46TP.js";
7
7
  import "../chunk-7BUDUYXN.js";
8
8
  import "../chunk-4NF7SSIX.js";
@@ -17,6 +17,14 @@ type StoreClientProps = StoreProps & {
17
17
  */
18
18
  initialCatalog?: StoreCatalog | null;
19
19
  };
20
+ /**
21
+ * The inline script rendered into SERVER HTML beside a seeded grid.
22
+ *
23
+ * Deliberately tiny, dependency-free and idempotent (several store islands can share
24
+ * one page). It only records — it never writes the cart, because the cart writer is
25
+ * async, lock-serialised, and lives in the bundle that has not arrived yet.
26
+ */
27
+ declare const STORE_PREHYDRATION_SCRIPT = "(function(){\nif(window.__skStoreArmed)return;window.__skStoreArmed=1;\ndocument.addEventListener('click',function(e){\nvar t=e.target;if(!t||!t.closest)return;\nvar b=t.closest('button[data-sk-store-add]');if(!b||b.disabled)return;\nvar c=b.closest('.sk-store__card');var s=c?c.querySelector('select'):null;\nvar v=(s&&s.value)||b.getAttribute('data-sk-store-variant');if(!v)return;\n(b.__skStoreQueue||(b.__skStoreQueue=[])).push({variantId:v,event:e});\n},true);\n})();";
20
28
  declare function StoreClient({ categoryHandle, productHandle, storeApiOrigin, title, columns, hideCartLink, initialCatalog, }: StoreClientProps): React.ReactElement;
21
29
 
22
- export { StoreClient, type StoreClientProps };
30
+ export { STORE_PREHYDRATION_SCRIPT, StoreClient, type StoreClientProps };
@@ -1,6 +1,7 @@
1
1
  import {
2
+ STORE_PREHYDRATION_SCRIPT,
2
3
  StoreClient
3
- } from "../chunk-673YEXAI.js";
4
+ } from "../chunk-UTAMB2SK.js";
4
5
  import "../chunk-WQSJ46TP.js";
5
6
  import {
6
7
  formatMoney
@@ -9,6 +10,7 @@ import "../chunk-4NF7SSIX.js";
9
10
  import "../chunk-FZ4WKWIE.js";
10
11
  import "../chunk-MLKGABMK.js";
11
12
  export {
13
+ STORE_PREHYDRATION_SCRIPT,
12
14
  StoreClient,
13
15
  formatMoney
14
16
  };
@@ -9,18 +9,18 @@ import "./chunk-QDO3SJWR.js";
9
9
  import "./chunk-THMT43MV.js";
10
10
  import {
11
11
  CartClient
12
- } from "./chunk-2CZFJOKK.js";
12
+ } from "./chunk-U6FJ6XYX.js";
13
13
  import {
14
14
  CartButtonClient
15
- } from "./chunk-ZNH6TSYP.js";
16
- import "./chunk-IOC6QLB7.js";
15
+ } from "./chunk-ORGTP5GB.js";
16
+ import "./chunk-BHF7S6UQ.js";
17
17
  import {
18
18
  CheckoutClient
19
19
  } from "./chunk-S3ZQQOQT.js";
20
20
  import "./chunk-ZOF22TJA.js";
21
21
  import {
22
22
  StoreClient
23
- } from "./chunk-673YEXAI.js";
23
+ } from "./chunk-UTAMB2SK.js";
24
24
  import "./chunk-WQSJ46TP.js";
25
25
  import "./chunk-7BUDUYXN.js";
26
26
  import "./chunk-4NF7SSIX.js";
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  ISLANDS,
3
3
  mountIslands
4
- } from "./chunk-HYXTDUP2.js";
4
+ } from "./chunk-DP2HJMVB.js";
5
5
  import "./chunk-4GKSSM5N.js";
6
6
  import "./chunk-3T7UWKAH.js";
7
7
  import "./chunk-QDO3SJWR.js";
8
8
  import "./chunk-THMT43MV.js";
9
- import "./chunk-2CZFJOKK.js";
10
- import "./chunk-ZNH6TSYP.js";
11
- import "./chunk-IOC6QLB7.js";
9
+ import "./chunk-U6FJ6XYX.js";
10
+ import "./chunk-ORGTP5GB.js";
11
+ import "./chunk-BHF7S6UQ.js";
12
12
  import "./chunk-S3ZQQOQT.js";
13
13
  import "./chunk-ZOF22TJA.js";
14
- import "./chunk-673YEXAI.js";
14
+ import "./chunk-UTAMB2SK.js";
15
15
  import "./chunk-WQSJ46TP.js";
16
16
  import "./chunk-7BUDUYXN.js";
17
17
  import "./chunk-4NF7SSIX.js";
@@ -2891,19 +2891,59 @@ select.sk-identity__input[data-empty] {
2891
2891
  list-style: none;
2892
2892
  margin: 0;
2893
2893
  padding: 0;
2894
+ /* The drawer is ~380px inside a wide viewport, so a VIEWPORT media query can never
2895
+ tell us the row is cramped (PAT-948). Size the row off its own container instead. */
2896
+ container-type: inline-size;
2897
+ container-name: sk-cart-lines;
2894
2898
  }
2895
2899
  .sk-cart__line {
2896
2900
  display: grid;
2897
- grid-template-columns: 1fr auto auto auto;
2901
+ /* Five children, so name five tracks. They used to run in a FOUR-column grid, which
2902
+ wrapped Remove onto its own row where it stretched the full 1fr title column and
2903
+ centred its own label — the reason it floated mid-row (PAT-948). */
2904
+ grid-template-columns: minmax(0, 1fr) auto auto auto auto;
2905
+ grid-template-areas: 'title unit qty total remove';
2898
2906
  align-items: center;
2899
2907
  gap: calc(var(--sk-space, 0.5rem) * 2);
2900
2908
  padding: calc(var(--sk-space, 0.5rem) * 2) 0;
2901
2909
  border-bottom: 1px solid color-mix(in srgb, var(--sk-muted, #5b6b7a) 30%, transparent);
2902
2910
  }
2911
+ .sk-cart__line-title {
2912
+ grid-area: title;
2913
+ }
2914
+ .sk-cart__line-unit {
2915
+ grid-area: unit;
2916
+ }
2917
+ .sk-cart__qty {
2918
+ grid-area: qty;
2919
+ }
2920
+ .sk-cart__line-total {
2921
+ grid-area: total;
2922
+ }
2923
+ .sk-cart__remove {
2924
+ grid-area: remove;
2925
+ }
2926
+ /* Narrow CONTAINER (the drawer, and a phone): two columns, three rows. */
2927
+ @container sk-cart-lines (max-width: 34rem) {
2928
+ .sk-cart__line {
2929
+ grid-template-columns: minmax(0, 1fr) auto;
2930
+ grid-template-areas:
2931
+ 'title title'
2932
+ 'unit total'
2933
+ 'qty remove';
2934
+ row-gap: calc(var(--sk-space, 0.5rem) * 1);
2935
+ }
2936
+ }
2903
2937
  .sk-cart__line-title {
2904
2938
  color: inherit;
2905
2939
  font-weight: 600;
2906
2940
  text-decoration: none;
2941
+ /* One line, clipped — a product name is not worth reflowing the row it sits in.
2942
+ min-width:0 is what lets the 1fr track actually shrink inside a grid. */
2943
+ min-width: 0;
2944
+ overflow: hidden;
2945
+ text-overflow: ellipsis;
2946
+ white-space: nowrap;
2907
2947
  }
2908
2948
  .sk-cart__line-unit {
2909
2949
  font-size: 0.875rem;
@@ -2913,6 +2953,14 @@ select.sk-identity__input[data-empty] {
2913
2953
  display: inline-flex;
2914
2954
  align-items: center;
2915
2955
  gap: calc(var(--sk-space, 0.5rem) * 1);
2956
+ justify-self: start;
2957
+ }
2958
+ /* The quantity reserves room for two digits and uses tabular figures, so stepping
2959
+ 1 -> 2 -> 10 never shifts the − and + buttons under the shopper's cursor. */
2960
+ .sk-cart__qty-value {
2961
+ min-width: 2ch;
2962
+ text-align: center;
2963
+ font-variant-numeric: tabular-nums;
2916
2964
  }
2917
2965
  .sk-cart__qty button {
2918
2966
  min-width: 2rem;
@@ -2928,6 +2976,10 @@ select.sk-identity__input[data-empty] {
2928
2976
  .sk-cart__line-total {
2929
2977
  font-variant-numeric: tabular-nums;
2930
2978
  font-weight: 600;
2979
+ /* Reserve the widest realistic total so $70.00 and $599.00 hold the same column
2980
+ and the stepper beside them does not slide between lines. */
2981
+ min-width: 6ch;
2982
+ text-align: right;
2931
2983
  }
2932
2984
  .sk-cart__remove {
2933
2985
  border: 0;
@@ -2938,6 +2990,11 @@ select.sk-identity__input[data-empty] {
2938
2990
  opacity: 0.75;
2939
2991
  cursor: pointer;
2940
2992
  text-decoration: underline;
2993
+ /* A grid item stretches by default, so this button used to fill its track and
2994
+ centre its own label. Shrink it to its text and pin it to the row's end. */
2995
+ justify-self: end;
2996
+ padding: 0;
2997
+ width: max-content;
2941
2998
  }
2942
2999
  .sk-cart__totals {
2943
3000
  margin: calc(var(--sk-space, 0.5rem) * 3) 0 0;
@@ -2993,11 +3050,8 @@ select.sk-identity__input[data-empty] {
2993
3050
  font-weight: 600;
2994
3051
  text-decoration: none;
2995
3052
  }
2996
- @media (max-width: 600px) {
2997
- .sk-cart__line {
2998
- grid-template-columns: 1fr auto;
2999
- }
3000
- }
3053
+ /* The old viewport `max-width: 600px` rule lived here. It could not see the drawer,
3054
+ which is ~380px inside a wide window; the @container rule above replaces it. */
3001
3055
 
3002
3056
 
3003
3057
  /* ── checkout (PAT-766) ──────────────────────────────────────────────────── */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patientos/website-kit",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "description": "PatientOS clinic website components and patient-facing interactive islands.",
5
5
  "type": "module",
6
6
  "sideEffects": [