@lime-bundles/react 7.0.0 → 7.2.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.
package/dist/index.d.cts CHANGED
@@ -51,6 +51,14 @@ interface BundleComponentProps {
51
51
  * `marketIds` doesn't include this market.
52
52
  */
53
53
  marketId?: string;
54
+ /**
55
+ * The product page this component is rendered on — full GID
56
+ * (`gid://shopify/Product/123`) or bare numeric id. Multi-product
57
+ * renderers (`<VolumeBundle>`) price and sell this product; without it
58
+ * they fall back to the bundle's first product, so any bundle spanning
59
+ * more than one product should pass it.
60
+ */
61
+ productId?: string;
54
62
  }
55
63
 
56
64
  declare function FixedBundle(props: BundleComponentProps): react_jsx_runtime.JSX.Element | null;
package/dist/index.d.ts CHANGED
@@ -51,6 +51,14 @@ interface BundleComponentProps {
51
51
  * `marketIds` doesn't include this market.
52
52
  */
53
53
  marketId?: string;
54
+ /**
55
+ * The product page this component is rendered on — full GID
56
+ * (`gid://shopify/Product/123`) or bare numeric id. Multi-product
57
+ * renderers (`<VolumeBundle>`) price and sell this product; without it
58
+ * they fall back to the bundle's first product, so any bundle spanning
59
+ * more than one product should pass it.
60
+ */
61
+ productId?: string;
54
62
  }
55
63
 
56
64
  declare function FixedBundle(props: BundleComponentProps): react_jsx_runtime.JSX.Element | null;
package/dist/index.js CHANGED
@@ -961,7 +961,9 @@ function FixedProductRow({ bundle, product, currency, isOos, onVariantChange })
961
961
  src: thumbImage.url,
962
962
  alt: thumbImage.altText ?? product.title,
963
963
  className: "lb-bundle__product-image",
964
- loading: "lazy"
964
+ loading: "lazy",
965
+ width: thumbImage.width ?? void 0,
966
+ height: thumbImage.height ?? void 0
965
967
  }
966
968
  ),
967
969
  /* @__PURE__ */ jsxs3("div", { className: "lb-bundle__product-info", children: [
@@ -1584,7 +1586,9 @@ function MixMatchPickerRow({
1584
1586
  {
1585
1587
  src: thumbImage.url,
1586
1588
  alt: thumbImage.altText ?? product.title,
1587
- loading: "lazy"
1589
+ loading: "lazy",
1590
+ width: thumbImage.width ?? void 0,
1591
+ height: thumbImage.height ?? void 0
1588
1592
  }
1589
1593
  ),
1590
1594
  variantInBundle && /* @__PURE__ */ jsx4("span", { className: "lb-mix-match__modal-added-badge", children: "Added" })
@@ -2309,6 +2313,7 @@ import {
2309
2313
  resolveDefaultTierIndex,
2310
2314
  resolvePopularTierIndex,
2311
2315
  isVariantFulfillable as isVariantFulfillable4,
2316
+ resolveContextProduct,
2312
2317
  shouldShowLowStockBadge as shouldShowLowStockBadge2
2313
2318
  } from "@lime-bundles/core";
2314
2319
  import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
@@ -2321,7 +2326,8 @@ function VolumeBundle(props) {
2321
2326
  analyticsEnabled,
2322
2327
  onAddToCart,
2323
2328
  onError,
2324
- className
2329
+ className,
2330
+ productId
2325
2331
  } = props;
2326
2332
  const result = useBundleData({
2327
2333
  shopDomain,
@@ -2371,7 +2377,7 @@ function VolumeBundle(props) {
2371
2377
  );
2372
2378
  }
2373
2379
  }, [result, onError]);
2374
- const product = bundle?.products[0];
2380
+ const product = bundle ? resolveContextProduct(bundle.products, { productId }) : void 0;
2375
2381
  const variants = useMemo7(() => product?.variants.nodes ?? [], [product]);
2376
2382
  const optionNames = useMemo7(() => {
2377
2383
  const first = variants[0];
@@ -2504,7 +2510,9 @@ function VolumeBundle(props) {
2504
2510
  src: thumbImage.url,
2505
2511
  alt: thumbImage.altText ?? product.title,
2506
2512
  className: "lb-bundle__product-image",
2507
- loading: "lazy"
2513
+ loading: "lazy",
2514
+ width: thumbImage.width ?? void 0,
2515
+ height: thumbImage.height ?? void 0
2508
2516
  }
2509
2517
  ),
2510
2518
  /* @__PURE__ */ jsxs6("div", { className: "lb-bundle__product-info", children: [
@@ -2952,7 +2960,9 @@ function BogoProductRow({ side, product, quantity, percent, badge, currency, onV
2952
2960
  src: thumbImage.url,
2953
2961
  alt: thumbImage.altText ?? product.title,
2954
2962
  className: "lb-bundle__product-image",
2955
- loading: "lazy"
2963
+ loading: "lazy",
2964
+ width: thumbImage.width ?? void 0,
2965
+ height: thumbImage.height ?? void 0
2956
2966
  }
2957
2967
  ),
2958
2968
  /* @__PURE__ */ jsxs7("div", { className: "lb-bundle__product-info", children: [
@@ -3004,14 +3014,13 @@ function BogoProductRow({ side, product, quantity, percent, badge, currency, onV
3004
3014
  }
3005
3015
 
3006
3016
  // src/components/MultiStepBundle.tsx
3007
- import { useCallback as useCallback9, useEffect as useEffect15, useMemo as useMemo10, useState as useState12 } from "react";
3017
+ import { Fragment, useCallback as useCallback9, useEffect as useEffect15, useMemo as useMemo10, useState as useState12 } from "react";
3008
3018
  import {
3009
3019
  formatMoney as formatMoney7,
3010
3020
  formatUnitPrice as formatUnitPrice7,
3011
3021
  calculateDiscount as calculateDiscount3,
3012
3022
  isVariantFulfillable as isVariantFulfillable7,
3013
3023
  productsForStep as productsForStep2,
3014
- stepHeadroom as stepHeadroom2,
3015
3024
  DEFAULT_PRODUCT_RULE as DEFAULT_PRODUCT_RULE4
3016
3025
  } from "@lime-bundles/core";
3017
3026
 
@@ -3525,7 +3534,9 @@ function MultiStepPickerRow({
3525
3534
  {
3526
3535
  src: thumbImage.url,
3527
3536
  alt: thumbImage.altText ?? product.title,
3528
- loading: "lazy"
3537
+ loading: "lazy",
3538
+ width: thumbImage.width ?? void 0,
3539
+ height: thumbImage.height ?? void 0
3529
3540
  }
3530
3541
  ),
3531
3542
  variantInStep && /* @__PURE__ */ jsx8("span", { className: "lb-mix-match__modal-added-badge", children: "Added" })
@@ -3819,6 +3830,9 @@ function MultiStepBundle(props) {
3819
3830
  const allSatisfied = steps.length > 0 && steps.every((_, i) => stepSatisfied(i));
3820
3831
  const valid = !!bundle && allSatisfied;
3821
3832
  const completedSteps = steps.filter((_, i) => stepSatisfied(i)).length;
3833
+ const firstUnsatisfied = steps.findIndex((_, i) => !stepSatisfied(i));
3834
+ const remainingSteps = steps.length - completedSteps;
3835
+ const anyPicks = selections.some((s) => s.length > 0);
3822
3836
  const summary = useMemo10(() => {
3823
3837
  let comparePrice = 0;
3824
3838
  let salePrice = 0;
@@ -4061,115 +4075,156 @@ function MultiStepBundle(props) {
4061
4075
  )
4062
4076
  ] })
4063
4077
  ] }),
4064
- /* @__PURE__ */ jsx9("div", { className: "lb-multi-step__groups", "data-step-groups": true, children: steps.map((step, i) => /* @__PURE__ */ jsxs9(
4065
- "section",
4066
- {
4067
- className: "lb-multi-step__group",
4068
- "data-step-group": i,
4069
- children: [
4070
- /* @__PURE__ */ jsxs9("div", { className: "lb-multi-step__group-heading", children: [
4071
- /* @__PURE__ */ jsx9("span", { className: "lb-multi-step__group-name", children: step.name }),
4078
+ /* @__PURE__ */ jsx9("div", { className: "lb-multi-step__groups", "data-step-groups": true, children: steps.map((step, i) => /* @__PURE__ */ jsxs9(Fragment, { children: [
4079
+ i === firstUnsatisfied && /* @__PURE__ */ jsxs9(
4080
+ "button",
4081
+ {
4082
+ type: "button",
4083
+ className: "lb-mix-match__add-product lb-multi-step__door",
4084
+ "data-step-door": firstUnsatisfied,
4085
+ "aria-label": `${anyPicks ? "Continue building" : "Choose your products"}: ${step.name}`,
4086
+ onClick: () => setPickerOpenAt(firstUnsatisfied),
4087
+ children: [
4072
4088
  /* @__PURE__ */ jsx9(
4073
4089
  "span",
4074
4090
  {
4075
- className: `lb-multi-step__group-count${stepSatisfied(i) ? " lb-multi-step__group-count--met" : ""}`,
4076
- "data-step-count": i,
4077
- children: `${Math.min(unitsByStep[i] ?? 0, step.minQuantity)} of ${step.minQuantity} added`
4091
+ className: "lb-mix-match__add-product-icon",
4092
+ "aria-hidden": "true",
4093
+ children: /* @__PURE__ */ jsx9(PlusIcon2, {})
4078
4094
  }
4079
- )
4080
- ] }),
4081
- /* @__PURE__ */ jsx9(
4082
- "div",
4083
- {
4084
- className: "lb-mix-match__slots lb-edge-fade lb-multi-step__group-slots",
4085
- "data-step-slots": i,
4086
- children: (selections[i] ?? []).map((item, index) => (
4087
- /* Whole card reopens the wizard AT this step. The inner
4088
- title button provides the keyboard/AT path (its
4089
- activation bubbles here); × stops propagation. */
4090
- // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions -- keyboard path is the inner "Edit selection" title button whose activation bubbles to this handler
4091
- /* @__PURE__ */ jsxs9(
4092
- "div",
4093
- {
4094
- className: "lb-mix-match__slot lb-mix-match__slot--filled",
4095
- onClick: () => setPickerOpenAt(i),
4096
- children: [
4097
- item.featuredImage && /* @__PURE__ */ jsx9("div", { className: "lb-bundle-thumbnail", children: /* @__PURE__ */ jsx9(
4098
- "img",
4099
- {
4100
- src: item.featuredImage,
4101
- alt: item.title,
4102
- loading: "lazy"
4103
- }
4104
- ) }),
4105
- /* @__PURE__ */ jsxs9("div", { className: "lb-mix-match__filled-info", children: [
4106
- /* @__PURE__ */ jsx9(
4107
- "button",
4108
- {
4109
- type: "button",
4110
- className: "lb-mix-match__filled-title",
4111
- "aria-label": `Edit selection: ${item.title}`,
4112
- children: item.title
4113
- }
4114
- ),
4115
- item.variantTitle && item.variantTitle !== "Default Title" && /* @__PURE__ */ jsx9("span", { className: "lb-mix-match__filled-variant", children: item.variantTitle }),
4116
- /* @__PURE__ */ jsxs9("span", { className: "lb-mix-match__filled-price", children: [
4117
- item.compareAtPrice !== null && item.compareAtPrice > item.price && /* @__PURE__ */ jsx9("s", { className: "lb-mix-match__filled-compare", children: formatMoney7(item.compareAtPrice, currency) }),
4118
- /* @__PURE__ */ jsx9("span", { className: "lb-bundle-product-price", children: formatMoney7(item.price, currency) }),
4119
- /* @__PURE__ */ jsx9("span", { className: "lb-bundle-qty-inline", children: `\xD7${item.quantity}` })
4120
- ] }),
4121
- item.unitPrice && /* @__PURE__ */ jsx9("span", { className: "lb-bundle-product-unit-price", children: item.unitPrice })
4122
- ] }),
4123
- canRemoveItem2(bundle, selections, item) ? /* @__PURE__ */ jsx9(
4124
- "button",
4125
- {
4126
- type: "button",
4127
- className: "lb-mix-match__slot-remove",
4128
- "aria-label": `Remove ${item.title}`,
4129
- onClick: (e) => {
4130
- e.stopPropagation();
4131
- removeSlot(i, index);
4132
- },
4133
- children: /* @__PURE__ */ jsx9(CloseIcon4, {})
4134
- }
4135
- ) : (
4136
- /* Required pick at its floor — quiet state chip instead
4137
- of the ×; removal returns once another slot covers
4138
- the product's minimum (variant swap flow). */
4139
- /* @__PURE__ */ jsx9("span", { className: "lb-mix-match__slot-required", children: "Required" })
4140
- )
4141
- ]
4142
- },
4143
- `${item.productId}:${item.variantId}:${index}`
4144
- )
4145
- ))
4146
- }
4147
- ),
4148
- stepHeadroom2(step.maxQuantity, unitsByStep[i] ?? 0) !== 0 && /* @__PURE__ */ jsxs9(
4095
+ ),
4096
+ /* @__PURE__ */ jsxs9("span", { className: "lb-multi-step__door-copy", children: [
4097
+ /* @__PURE__ */ jsx9("span", { className: "lb-mix-match__add-product-label", children: anyPicks ? "Continue building" : "Choose your products" }),
4098
+ /* @__PURE__ */ jsx9("span", { className: "lb-multi-step__door-sub", children: anyPicks ? `${remainingSteps} ${remainingSteps === 1 ? "step" : "steps"} to go` : `${steps.length} quick ${steps.length === 1 ? "step" : "steps"}` })
4099
+ ] })
4100
+ ]
4101
+ }
4102
+ ),
4103
+ (selections[i] ?? []).length === 0 ? /* @__PURE__ */ jsx9(
4104
+ "section",
4105
+ {
4106
+ className: "lb-multi-step__group",
4107
+ "data-step-group": i,
4108
+ "data-step-mode": "row",
4109
+ children: /* @__PURE__ */ jsxs9(
4149
4110
  "button",
4150
4111
  {
4151
4112
  type: "button",
4152
- className: "lb-mix-match__add-product lb-multi-step__step-trigger",
4153
- "data-step-trigger": i,
4113
+ className: "lb-multi-step__step-row",
4114
+ "data-step-row": i,
4154
4115
  "aria-label": `${step.minQuantity === 1 && step.maxQuantity === 1 ? "Choose a product" : "Choose products"}: ${step.name}`,
4155
4116
  onClick: () => setPickerOpenAt(i),
4156
4117
  children: [
4157
4118
  /* @__PURE__ */ jsx9(
4158
4119
  "span",
4159
4120
  {
4160
- className: "lb-mix-match__add-product-icon",
4121
+ className: "lb-multi-step__step-ix",
4161
4122
  "aria-hidden": "true",
4162
- children: /* @__PURE__ */ jsx9(PlusIcon2, {})
4123
+ children: i + 1
4163
4124
  }
4164
4125
  ),
4165
- /* @__PURE__ */ jsx9("span", { className: "lb-mix-match__add-product-label", children: step.minQuantity === 1 && step.maxQuantity === 1 ? "Choose a product" : "Choose products" })
4126
+ /* @__PURE__ */ jsx9("span", { className: "lb-multi-step__group-name", children: step.name }),
4127
+ /* @__PURE__ */ jsx9(
4128
+ "span",
4129
+ {
4130
+ className: `lb-multi-step__group-count${stepSatisfied(i) ? " lb-multi-step__group-count--met" : ""}`,
4131
+ "data-step-count": i,
4132
+ children: `${Math.min(unitsByStep[i] ?? 0, step.minQuantity)} of ${step.minQuantity} added`
4133
+ }
4134
+ )
4166
4135
  ]
4167
4136
  }
4168
4137
  )
4169
- ]
4170
- },
4171
- i
4172
- )) }),
4138
+ }
4139
+ ) : /* @__PURE__ */ jsxs9(
4140
+ "section",
4141
+ {
4142
+ className: "lb-multi-step__group",
4143
+ "data-step-group": i,
4144
+ "data-step-mode": "open",
4145
+ children: [
4146
+ /* @__PURE__ */ jsxs9("div", { className: "lb-multi-step__group-heading", children: [
4147
+ /* @__PURE__ */ jsx9("span", { className: "lb-multi-step__group-name", children: step.name }),
4148
+ /* @__PURE__ */ jsx9(
4149
+ "span",
4150
+ {
4151
+ className: `lb-multi-step__group-count${stepSatisfied(i) ? " lb-multi-step__group-count--met" : ""}`,
4152
+ "data-step-count": i,
4153
+ children: `${Math.min(unitsByStep[i] ?? 0, step.minQuantity)} of ${step.minQuantity} added`
4154
+ }
4155
+ )
4156
+ ] }),
4157
+ /* @__PURE__ */ jsx9(
4158
+ "div",
4159
+ {
4160
+ className: "lb-mix-match__slots lb-edge-fade lb-multi-step__group-slots",
4161
+ "data-step-slots": i,
4162
+ children: (selections[i] ?? []).map((item, index) => (
4163
+ /* Whole card reopens the wizard AT this step. The inner
4164
+ title button provides the keyboard/AT path (its
4165
+ activation bubbles here); × stops propagation. */
4166
+ // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions -- keyboard path is the inner "Edit selection" title button whose activation bubbles to this handler
4167
+ /* @__PURE__ */ jsxs9(
4168
+ "div",
4169
+ {
4170
+ className: "lb-mix-match__slot lb-mix-match__slot--filled",
4171
+ onClick: () => setPickerOpenAt(i),
4172
+ children: [
4173
+ item.featuredImage && /* @__PURE__ */ jsx9("div", { className: "lb-bundle-thumbnail", children: /* @__PURE__ */ jsx9(
4174
+ "img",
4175
+ {
4176
+ src: item.featuredImage,
4177
+ alt: item.title,
4178
+ loading: "lazy"
4179
+ }
4180
+ ) }),
4181
+ /* @__PURE__ */ jsxs9("div", { className: "lb-mix-match__filled-info", children: [
4182
+ /* @__PURE__ */ jsx9(
4183
+ "button",
4184
+ {
4185
+ type: "button",
4186
+ className: "lb-mix-match__filled-title",
4187
+ "aria-label": `Edit selection: ${item.title}`,
4188
+ children: item.title
4189
+ }
4190
+ ),
4191
+ item.variantTitle && item.variantTitle !== "Default Title" && /* @__PURE__ */ jsx9("span", { className: "lb-mix-match__filled-variant", children: item.variantTitle }),
4192
+ /* @__PURE__ */ jsxs9("span", { className: "lb-mix-match__filled-price", children: [
4193
+ item.compareAtPrice !== null && item.compareAtPrice > item.price && /* @__PURE__ */ jsx9("s", { className: "lb-mix-match__filled-compare", children: formatMoney7(item.compareAtPrice, currency) }),
4194
+ /* @__PURE__ */ jsx9("span", { className: "lb-bundle-product-price", children: formatMoney7(item.price, currency) }),
4195
+ /* @__PURE__ */ jsx9("span", { className: "lb-bundle-qty-inline", children: `\xD7${item.quantity}` })
4196
+ ] }),
4197
+ item.unitPrice && /* @__PURE__ */ jsx9("span", { className: "lb-bundle-product-unit-price", children: item.unitPrice })
4198
+ ] }),
4199
+ canRemoveItem2(bundle, selections, item) ? /* @__PURE__ */ jsx9(
4200
+ "button",
4201
+ {
4202
+ type: "button",
4203
+ className: "lb-mix-match__slot-remove",
4204
+ "aria-label": `Remove ${item.title}`,
4205
+ onClick: (e) => {
4206
+ e.stopPropagation();
4207
+ removeSlot(i, index);
4208
+ },
4209
+ children: /* @__PURE__ */ jsx9(CloseIcon4, {})
4210
+ }
4211
+ ) : (
4212
+ /* Required pick at its floor — quiet state chip instead
4213
+ of the ×; removal returns once another slot covers
4214
+ the product's minimum (variant swap flow). */
4215
+ /* @__PURE__ */ jsx9("span", { className: "lb-mix-match__slot-required", children: "Required" })
4216
+ )
4217
+ ]
4218
+ },
4219
+ `${item.productId}:${item.variantId}:${index}`
4220
+ )
4221
+ ))
4222
+ }
4223
+ )
4224
+ ]
4225
+ }
4226
+ )
4227
+ ] }, i)) }),
4173
4228
  /* @__PURE__ */ jsx9("div", { className: "lb-bundle-divider" }),
4174
4229
  allSatisfied && /* @__PURE__ */ jsxs9("div", { className: "lb-bundle-summary", "data-pricing-section": true, children: [
4175
4230
  /* @__PURE__ */ jsxs9("div", { className: "lb-bundle-summary__text", children: [
@@ -4348,7 +4403,7 @@ import {
4348
4403
  calculateTierSavings as calculateTierSavings2,
4349
4404
  getActiveTier as getActiveTier2,
4350
4405
  productsForStep as productsForStep3,
4351
- stepHeadroom as stepHeadroom3,
4406
+ stepHeadroom as stepHeadroom2,
4352
4407
  validateQuantity,
4353
4408
  transformImageUrl,
4354
4409
  formatCountdown,
@@ -4409,7 +4464,7 @@ export {
4409
4464
  reportAddToCart2 as reportAddToCart,
4410
4465
  reportImpression2 as reportImpression,
4411
4466
  sanitizeCustomCss,
4412
- stepHeadroom3 as stepHeadroom,
4467
+ stepHeadroom2 as stepHeadroom,
4413
4468
  thumbnailRatioVars,
4414
4469
  transformImageUrl,
4415
4470
  useAnalytics,