@propeller-commerce/propeller-v2-react-ui 0.15.10 → 0.15.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,27 @@ once it reaches 1.0. Until then (the `0.x` line) the public API may change
8
8
  between minor versions; breaking changes are called out below and in
9
9
  [MIGRATION.md](./MIGRATION.md).
10
10
 
11
+ ## [0.15.11] - 2026-08-11
12
+
13
+ ### Fixed
14
+
15
+ - **Checkout step 3 opened with nothing selected on a fresh cart (PWP-934).**
16
+ `CartPaymethods` and `CartCarriers` only ever adopted a value the cart already
17
+ stored, so a cart that had never reached step 3 — every first order, and every
18
+ order after the cart is recreated — rendered both grids blank and refused to
19
+ continue until the user clicked. They now preselect the cart's stored option
20
+ when there is one and otherwise the first option offered, so Continue works
21
+ straight away. A stored value the backend no longer offers also falls back
22
+ instead of leaving an invisible selection.
23
+
24
+ ### Changed
25
+
26
+ - The active option is now derived from the cart rather than mirrored into state
27
+ by an effect, so it is present in the first render (including SSR) instead of
28
+ appearing after hydration. `onPaymethodSelect` / `onCarrierSelect` still fire
29
+ once for the preselection, so hosts that persist the choice — and price the
30
+ transaction costs off it — keep working unchanged.
31
+
11
32
  ## [0.15.10] - 2026-08-11
12
33
 
13
34
  ### Fixed
package/dist/index.cjs CHANGED
@@ -6427,6 +6427,12 @@ function CartBonusItems(rawProps) {
6427
6427
  ] });
6428
6428
  }
6429
6429
  var CartBonusItems_default = CartBonusItems;
6430
+
6431
+ // src/composables/shared/utils/preselect.ts
6432
+ function pickPreselected(items, stored, keyOf) {
6433
+ const list = items || [];
6434
+ return (stored ? list.find((item) => keyOf(item) === stored) : void 0) || list[0];
6435
+ }
6430
6436
  function CartCarriers(props) {
6431
6437
  const [selectedName, setSelectedName] = React6.useState("");
6432
6438
  const containerClass = props.carriersContainerClass || "cart-carriers";
@@ -6440,16 +6446,16 @@ function CartCarriers(props) {
6440
6446
  setSelectedName(carrier.name);
6441
6447
  if (props.onCarrierSelect) props.onCarrierSelect(carrier);
6442
6448
  }
6449
+ const preselected = pickPreselected(
6450
+ carriers,
6451
+ props.cart?.postageData?.carrier,
6452
+ (c) => c.name
6453
+ );
6454
+ const activeName = selectedName || preselected?.name || "";
6443
6455
  React6.useEffect(() => {
6444
- if (!selectedName && props.cart?.postageData?.carrier) {
6445
- const name = props.cart.postageData.carrier;
6446
- setSelectedName(name);
6447
- if (props.onCarrierSelect) {
6448
- const match = carriers.find((c) => c.name === name);
6449
- if (match) props.onCarrierSelect(match);
6450
- }
6451
- }
6452
- }, [props.cart, selectedName, props.onCarrierSelect, carriers, props]);
6456
+ if (selectedName || !preselected) return;
6457
+ if (props.onCarrierSelect) props.onCarrierSelect(preselected);
6458
+ }, [preselected?.name]);
6453
6459
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `propeller-cart-carriers ${containerClass}`, children: [
6454
6460
  carriers.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-cart-carriers__grid grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3", children: carriers.map((carrier, index) => {
6455
6461
  const logoUrl = showLogo ? carrier.logo || "" : "";
@@ -6457,8 +6463,8 @@ function CartCarriers(props) {
6457
6463
  "div",
6458
6464
  {
6459
6465
  onClick: () => handleSelect(carrier),
6460
- "data-selected": selectedName === carrier.name ? "true" : "false",
6461
- className: `propeller-cart-carriers__carrier relative cursor-pointer border rounded-container p-3 flex flex-col items-center justify-center gap-2 text-center aspect-square transition-all ${selectedName === carrier.name ? "border-secondary bg-secondary/5 shadow-sm" : "border-border hover:border-secondary/30"}`,
6466
+ "data-selected": activeName === carrier.name ? "true" : "false",
6467
+ className: `propeller-cart-carriers__carrier relative cursor-pointer border rounded-container p-3 flex flex-col items-center justify-center gap-2 text-center aspect-square transition-all ${activeName === carrier.name ? "border-secondary bg-secondary/5 shadow-sm" : "border-border hover:border-secondary/30"}`,
6462
6468
  children: [
6463
6469
  props.showPrice !== false ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "propeller-cart-carriers__carrier-price absolute top-2 right-2 text-xs bg-surface-hover text-muted-foreground px-2 py-0.5 rounded-full", children: formatCarrierPrice(carrier.price) }) : null,
6464
6470
  logoUrl ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "propeller-cart-carriers__carrier-logo-wrap flex items-center justify-center h-10 w-full", children: /* @__PURE__ */ jsxRuntime.jsx("img", { className: "propeller-cart-carriers__carrier-logo max-h-10 max-w-[80%] w-auto object-contain", src: logoUrl, alt: carrier.name }) }) : null,
@@ -7762,16 +7768,16 @@ function CartPaymethods(rawProps) {
7762
7768
  setSelectedCode(method.code);
7763
7769
  if (props.onPaymethodSelect) props.onPaymethodSelect(method);
7764
7770
  }
7771
+ const preselected = pickPreselected(
7772
+ payMethods,
7773
+ props.cart?.paymentData?.method,
7774
+ (m) => m.code
7775
+ );
7776
+ const activeCode = selectedCode || preselected?.code || "";
7765
7777
  React6.useEffect(() => {
7766
- if (!selectedCode && props.cart?.paymentData?.method) {
7767
- const code = props.cart.paymentData.method;
7768
- setSelectedCode(code);
7769
- if (props.onPaymethodSelect) {
7770
- const match = payMethods.find((m) => m.code === code);
7771
- if (match) props.onPaymethodSelect(match);
7772
- }
7773
- }
7774
- }, [props.cart, selectedCode, props.onPaymethodSelect, payMethods, props]);
7778
+ if (selectedCode || !preselected) return;
7779
+ if (props.onPaymethodSelect) props.onPaymethodSelect(preselected);
7780
+ }, [preselected?.code]);
7775
7781
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `propeller-cart-paymethods ${containerClass}`, children: [
7776
7782
  payMethods.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-cart-paymethods__grid grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3", children: payMethods.map((method, index) => {
7777
7783
  const logoUrl = showLogo ? getLogoUrl(method) : "";
@@ -7779,8 +7785,8 @@ function CartPaymethods(rawProps) {
7779
7785
  "div",
7780
7786
  {
7781
7787
  onClick: () => handleSelect(method),
7782
- "data-selected": selectedCode === method.code ? "true" : "false",
7783
- className: `propeller-cart-paymethods__method relative cursor-pointer border rounded-container p-3 flex flex-col items-center justify-center gap-2 text-center aspect-square transition-all ${selectedCode === method.code ? "border-secondary bg-secondary/5 shadow-sm" : "border-border hover:border-secondary/30"}`,
7788
+ "data-selected": activeCode === method.code ? "true" : "false",
7789
+ className: `propeller-cart-paymethods__method relative cursor-pointer border rounded-container p-3 flex flex-col items-center justify-center gap-2 text-center aspect-square transition-all ${activeCode === method.code ? "border-secondary bg-secondary/5 shadow-sm" : "border-border hover:border-secondary/30"}`,
7784
7790
  children: [
7785
7791
  method.price > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "propeller-cart-paymethods__method-price absolute top-2 right-2 text-xs bg-surface-hover text-muted-foreground px-2 py-0.5 rounded-full", children: formatMethodPrice(method.price) }) : null,
7786
7792
  logoUrl ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "propeller-cart-paymethods__method-logo-wrap flex items-center justify-center h-10 w-full", children: /* @__PURE__ */ jsxRuntime.jsx(