@propeller-commerce/propeller-v2-react-ui 0.26.1 → 0.28.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.cjs CHANGED
@@ -41,6 +41,9 @@ function PropellerProvider({ value, children }) {
41
41
  function usePropellerDeps() {
42
42
  return React6.useContext(PropellerDepsContext);
43
43
  }
44
+ function useImgComponent() {
45
+ return React6.useContext(PropellerDepsContext)?.imgComponent;
46
+ }
44
47
  function useRequiredPropellerDeps() {
45
48
  const deps = React6.useContext(PropellerDepsContext);
46
49
  if (!deps) {
@@ -2634,6 +2637,7 @@ function toMatch(item, language) {
2634
2637
  const netPrice = displayItem?.price?.net ?? 0;
2635
2638
  const grossPrice = displayItem?.price?.gross ?? 0;
2636
2639
  const minQuantity = Math.max(1, displayItem?.minimumQuantity ?? 1);
2640
+ const step = Math.max(1, displayItem?.unit ?? 1);
2637
2641
  const imageUrl = isCluster ? propellerV2CoreUi.getClusterImageUrl(item) : propellerV2CoreUi.getProductImageUrl(item);
2638
2642
  return {
2639
2643
  productId,
@@ -2643,6 +2647,7 @@ function toMatch(item, language) {
2643
2647
  netPrice,
2644
2648
  grossPrice,
2645
2649
  minQuantity,
2650
+ step,
2646
2651
  imageUrl,
2647
2652
  priceOnRequest: isPriceOnRequest(item)
2648
2653
  };
@@ -4694,8 +4699,25 @@ function ActionCode(rawProps) {
4694
4699
  ] });
4695
4700
  }
4696
4701
  var ActionCode_default = ActionCode;
4702
+ function PropellerImg({ as: Injected, ...props }) {
4703
+ if (Injected) return /* @__PURE__ */ jsxRuntime.jsx(Injected, { ...props });
4704
+ const { src, alt, className, width, height, loading, onClick } = props;
4705
+ return /* @__PURE__ */ jsxRuntime.jsx(
4706
+ "img",
4707
+ {
4708
+ src,
4709
+ alt,
4710
+ className,
4711
+ width,
4712
+ height,
4713
+ loading,
4714
+ onClick
4715
+ }
4716
+ );
4717
+ }
4697
4718
  function CartBonusItems(rawProps) {
4698
4719
  const props = useInfraProps(rawProps);
4720
+ const Img = useImgComponent();
4699
4721
  const items = props.bonusItems ?? props.cart?.bonusItems ?? [];
4700
4722
  if (items.length === 0) return null;
4701
4723
  const currency = props.currency ?? "\u20AC";
@@ -4713,20 +4735,21 @@ function CartBonusItems(rawProps) {
4713
4735
  {
4714
4736
  className: "propeller-cart-bonus-item flex items-center gap-4 bg-card p-4 rounded-container shadow-sm border border-border",
4715
4737
  children: [
4716
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-cart-bonus-item__media w-20 h-20 flex-shrink-0 bg-surface-hover rounded-control overflow-hidden flex items-center justify-center", children: img ? /* @__PURE__ */ jsxRuntime.jsx("img", { className: "propeller-cart-bonus-item__image w-full h-full object-contain p-1", src: img, alt: name }) : /* @__PURE__ */ jsxRuntime.jsx(
4738
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-cart-bonus-item__media w-20 h-20 flex-shrink-0 bg-surface-hover rounded-control overflow-hidden flex items-center justify-center", children: img ? /* @__PURE__ */ jsxRuntime.jsx(PropellerImg, { as: Img, className: "propeller-cart-bonus-item__image w-full h-full object-contain p-1", src: img, alt: name, loading: "lazy" }) : /* @__PURE__ */ jsxRuntime.jsx(
4717
4739
  "svg",
4718
4740
  {
4741
+ "aria-hidden": "true",
4719
4742
  fill: "none",
4720
4743
  viewBox: "0 0 24 24",
4721
4744
  stroke: "currentColor",
4722
- className: "propeller-cart-bonus-item__image-placeholder w-8 h-8 text-foreground-subtle",
4723
- strokeWidth: 1.5,
4745
+ className: "propeller-cart-bonus-item__image-placeholder w-10 h-10 text-foreground-subtle",
4746
+ strokeWidth: 1,
4724
4747
  children: /* @__PURE__ */ jsxRuntime.jsx(
4725
4748
  "path",
4726
4749
  {
4727
4750
  strokeLinecap: "round",
4728
4751
  strokeLinejoin: "round",
4729
- d: "M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909M3.75 21h16.5A2.25 2.25 0 0022.5 18.75V5.25A2.25 2.25 0 0020.25 3H3.75A2.25 2.25 0 001.5 5.25v13.5A2.25 2.25 0 003.75 21z"
4752
+ d: "M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
4730
4753
  }
4731
4754
  )
4732
4755
  }
@@ -4771,6 +4794,7 @@ function mergeGrouped(p) {
4771
4794
  }
4772
4795
  function AddToCart(rawProps) {
4773
4796
  const props = mergeGrouped(useInfraProps(rawProps));
4797
+ const Img = useImgComponent();
4774
4798
  const { cart, loading, checkoutAllowed, addItem: addItem2, getMinQuantity, getStep } = useCart({
4775
4799
  graphqlClient: props.graphqlClient,
4776
4800
  user: props.user ?? null,
@@ -4779,6 +4803,7 @@ function AddToCart(rawProps) {
4779
4803
  onCartCreated: props.onCartCreated
4780
4804
  });
4781
4805
  const [quantity, setQuantity] = React6.useState(() => getMinQuantity(props.product));
4806
+ const [quantityDraft, setQuantityDraft] = React6.useState(null);
4782
4807
  const [toastMessage, setToastMessage] = React6.useState(() => "");
4783
4808
  const [toastType, setToastType] = React6.useState(() => "");
4784
4809
  const [toastVisible, setToastVisible] = React6.useState(() => false);
@@ -4801,16 +4826,42 @@ function AddToCart(rawProps) {
4801
4826
  function dismissToast() {
4802
4827
  setToastVisible(false);
4803
4828
  }
4829
+ function snapQuantity(value) {
4830
+ const min = getMinQuantity(props.product);
4831
+ const step = getStep(props.product);
4832
+ if (value <= min) return min;
4833
+ return Math.round((value - min) / step) * step + min;
4834
+ }
4804
4835
  function increment() {
4805
- setQuantity(quantity + getStep(props.product));
4836
+ setQuantityDraft(null);
4837
+ const snapped = snapQuantity(quantity);
4838
+ setQuantity(snapped === quantity ? quantity + getStep(props.product) : snapped);
4806
4839
  }
4807
4840
  function decrement() {
4808
4841
  const min = getMinQuantity(props.product);
4809
4842
  const step = getStep(props.product);
4843
+ setQuantityDraft(null);
4844
+ const snapped = snapQuantity(quantity);
4845
+ if (snapped !== quantity) {
4846
+ setQuantity(snapped);
4847
+ return;
4848
+ }
4810
4849
  if (quantity - step >= min) {
4811
4850
  setQuantity(quantity - step);
4812
4851
  }
4813
4852
  }
4853
+ function handleQuantityInput(raw) {
4854
+ setQuantityDraft(raw);
4855
+ const val = parseInt(raw, 10);
4856
+ if (!isNaN(val) && val >= getMinQuantity(props.product)) {
4857
+ setQuantity(snapQuantity(val));
4858
+ }
4859
+ }
4860
+ function handleQuantityBlur() {
4861
+ const val = parseInt(quantityDraft ?? "", 10);
4862
+ setQuantity(isNaN(val) ? getMinQuantity(props.product) : snapQuantity(val));
4863
+ setQuantityDraft(null);
4864
+ }
4814
4865
  function getModalImageUrl() {
4815
4866
  if (addedCartItem) {
4816
4867
  const img = addedCartItem.product?.media?.images?.items?.[0]?.imageVariants?.[0]?.url;
@@ -4940,15 +4991,9 @@ function AddToCart(rawProps) {
4940
4991
  className: "propeller-add-to-cart__quantity flex-1 md:flex-none md:w-12 text-center text-sm bg-transparent border-none focus:ring-0 focus:outline-none h-full [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none",
4941
4992
  min: getMinQuantity(props.product),
4942
4993
  step: getStep(props.product),
4943
- value: quantity,
4944
- onChange: (e) => {
4945
- const val = parseInt(e.target.value, 10);
4946
- const min = getMinQuantity(props.product);
4947
- const step = getStep(props.product);
4948
- if (!isNaN(val) && val >= min) {
4949
- setQuantity(Math.round((val - min) / step) * step + min);
4950
- }
4951
- }
4994
+ value: quantityDraft ?? quantity,
4995
+ onChange: (e) => handleQuantityInput(e.target.value),
4996
+ onBlur: handleQuantityBlur
4952
4997
  }
4953
4998
  ),
4954
4999
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -4973,15 +5018,9 @@ function AddToCart(rawProps) {
4973
5018
  className: "propeller-add-to-cart__quantity w-full md:w-16 h-10 text-center text-sm border border-input rounded-control focus:ring-2 focus:ring-secondary focus:border-transparent [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none",
4974
5019
  min: getMinQuantity(props.product),
4975
5020
  step: getStep(props.product),
4976
- value: quantity,
4977
- onChange: (e) => {
4978
- const val = parseInt(e.target.value, 10);
4979
- const min = getMinQuantity(props.product);
4980
- const step = getStep(props.product);
4981
- if (!isNaN(val) && val >= min) {
4982
- setQuantity(Math.round((val - min) / step) * step + min);
4983
- }
4984
- }
5021
+ value: quantityDraft ?? quantity,
5022
+ onChange: (e) => handleQuantityInput(e.target.value),
5023
+ onBlur: handleQuantityBlur
4985
5024
  }
4986
5025
  ) : null,
4987
5026
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -5112,27 +5151,30 @@ function AddToCart(rawProps) {
5112
5151
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-add-to-cart__modal-body px-6 py-5", children: [
5113
5152
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-add-to-cart__modal-product flex items-start gap-4", children: [
5114
5153
  !!getModalImageUrl() ? /* @__PURE__ */ jsxRuntime.jsx(
5115
- "img",
5154
+ PropellerImg,
5116
5155
  {
5156
+ as: Img,
5117
5157
  className: "propeller-add-to-cart__modal-image w-16 h-16 object-contain rounded border border-border-subtle flex-shrink-0",
5118
5158
  src: getModalImageUrl(),
5119
- alt: getModalName()
5159
+ alt: getModalName(),
5160
+ loading: "lazy"
5120
5161
  }
5121
5162
  ) : null,
5122
5163
  !getModalImageUrl() ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-add-to-cart__modal-image-placeholder w-16 h-16 flex items-center justify-center rounded border border-border-subtle flex-shrink-0 bg-surface-hover", children: /* @__PURE__ */ jsxRuntime.jsx(
5123
5164
  "svg",
5124
5165
  {
5166
+ "aria-hidden": "true",
5125
5167
  fill: "none",
5126
5168
  viewBox: "0 0 24 24",
5127
5169
  stroke: "currentColor",
5128
5170
  className: "w-8 h-8 text-foreground-subtle",
5129
- strokeWidth: 1.5,
5171
+ strokeWidth: 1,
5130
5172
  children: /* @__PURE__ */ jsxRuntime.jsx(
5131
5173
  "path",
5132
5174
  {
5133
5175
  strokeLinecap: "round",
5134
5176
  strokeLinejoin: "round",
5135
- d: "M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909M3.75 21h16.5A2.25 2.25 0 0022.5 18.75V5.25A2.25 2.25 0 0020.25 3H3.75A2.25 2.25 0 001.5 5.25v13.5A2.25 2.25 0 003.75 21z"
5177
+ d: "M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
5136
5178
  }
5137
5179
  )
5138
5180
  }
@@ -7192,29 +7234,33 @@ function CartItem(rawProps) {
7192
7234
  }
7193
7235
  function CartItemImage(props = {}) {
7194
7236
  const { derived } = useCartItemContext();
7237
+ const Img = useImgComponent();
7195
7238
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: props.className ?? "propeller-cart-item__media w-20 h-20 md:w-24 md:h-24 flex-shrink-0 bg-surface-hover flex items-center justify-center overflow-hidden relative rounded-control", children: [
7196
7239
  !!derived.imageUrl ? /* @__PURE__ */ jsxRuntime.jsx(
7197
- "img",
7240
+ PropellerImg,
7198
7241
  {
7242
+ as: Img,
7199
7243
  className: "propeller-cart-item__image w-full h-full object-contain p-1",
7200
7244
  src: derived.imageUrl,
7201
- alt: derived.name
7245
+ alt: derived.name,
7246
+ loading: "lazy"
7202
7247
  }
7203
7248
  ) : null,
7204
7249
  !derived.imageUrl ? /* @__PURE__ */ jsxRuntime.jsx(
7205
7250
  "svg",
7206
7251
  {
7252
+ "aria-hidden": "true",
7207
7253
  fill: "none",
7208
7254
  viewBox: "0 0 24 24",
7209
7255
  stroke: "currentColor",
7210
- className: "propeller-cart-item__image-placeholder w-8 h-8 text-foreground-subtle",
7211
- strokeWidth: 1.5,
7256
+ className: "propeller-cart-item__image-placeholder w-10 h-10 md:w-12 md:h-12 text-foreground-subtle",
7257
+ strokeWidth: 1,
7212
7258
  children: /* @__PURE__ */ jsxRuntime.jsx(
7213
7259
  "path",
7214
7260
  {
7215
7261
  strokeLinecap: "round",
7216
7262
  strokeLinejoin: "round",
7217
- d: "M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909M3.75 21h16.5A2.25 2.25 0 0022.5 18.75V5.25A2.25 2.25 0 0020.25 3H3.75A2.25 2.25 0 001.5 5.25v13.5A2.25 2.25 0 003.75 21z"
7263
+ d: "M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
7218
7264
  }
7219
7265
  )
7220
7266
  }
@@ -7335,6 +7381,7 @@ function CartItemNotes(props = {}) {
7335
7381
  }
7336
7382
  function CartItemCrossupsells(props = {}) {
7337
7383
  const { resolved, state, helpers, handlers } = useCartItemContext();
7384
+ const Img = useImgComponent();
7338
7385
  if (state.visibleCrossupsells.length === 0) return null;
7339
7386
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: props.className ?? "propeller-cart-item__crossupsells mt-3 pt-3 border-t border-border", children: [
7340
7387
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "propeller-cart-item__crossupsells-label text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2", children: propellerV2CoreUi.getLabel(resolved.labels, "crossupsellTitle", "You might also like") }),
@@ -7351,11 +7398,13 @@ function CartItemCrossupsells(props = {}) {
7351
7398
  onClick: (e) => handlers.onCrossupsellClick(item, e),
7352
7399
  children: [
7353
7400
  !!helpers.getCrossupsellImageUrl(item) ? /* @__PURE__ */ jsxRuntime.jsx(
7354
- "img",
7401
+ PropellerImg,
7355
7402
  {
7403
+ as: Img,
7356
7404
  className: "propeller-cart-item__crossupsell-image w-10 h-10 object-contain rounded flex-shrink-0",
7357
7405
  src: helpers.getCrossupsellImageUrl(item),
7358
- alt: helpers.getCrossupsellName(item)
7406
+ alt: helpers.getCrossupsellName(item),
7407
+ loading: "lazy"
7359
7408
  }
7360
7409
  ) : null,
7361
7410
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
@@ -7784,7 +7833,7 @@ function CartIconAndSidebar(rawProps) {
7784
7833
  includeTax: useTax,
7785
7834
  className: "propeller-cart-icon__item",
7786
7835
  children: [
7787
- /* @__PURE__ */ jsxRuntime.jsx(CartItem_default.Image, { className: "w-20 h-20 flex-shrink-0 bg-surface-hover rounded-control overflow-hidden border border-border-subtle" }),
7836
+ /* @__PURE__ */ jsxRuntime.jsx(CartItem_default.Image, { className: "w-20 h-20 flex-shrink-0 bg-surface-hover rounded-control overflow-hidden border border-border-subtle flex items-center justify-center relative" }),
7788
7837
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0 flex flex-col justify-between py-1", children: [
7789
7838
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
7790
7839
  /* @__PURE__ */ jsxRuntime.jsx(CartItem_default.Title, { className: "propeller-cart-icon__item-name text-sm font-medium hover:text-primary line-clamp-2" }),
@@ -8551,6 +8600,7 @@ function ClusterCardImage(props) {
8551
8600
  const showFavorite = props.showFavorite ?? !!resolved.enableAddFavorite;
8552
8601
  const isRow = derived.isRow;
8553
8602
  const InjectedImage = resolved.imageComponent;
8603
+ const Img = useImgComponent();
8554
8604
  if (InjectedImage) {
8555
8605
  return /* @__PURE__ */ jsxRuntime.jsx(
8556
8606
  InjectedImage,
@@ -8569,13 +8619,15 @@ function ClusterCardImage(props) {
8569
8619
  className: props.className ?? `propeller-cluster-card__media relative overflow-hidden bg-surface-hover ${isRow ? "w-20 h-20 flex-shrink-0 p-2" : "aspect-[4/3] sm:aspect-square p-2 sm:p-4"}`,
8570
8620
  children: [
8571
8621
  /* @__PURE__ */ jsxRuntime.jsx("a", { className: "block h-full w-full", href: derived.clusterUrl, onClick: handleClusterClick, children: derived.imageUrl ? /* @__PURE__ */ jsxRuntime.jsx(
8572
- "img",
8622
+ PropellerImg,
8573
8623
  {
8624
+ as: Img,
8574
8625
  className: "propeller-cluster-card__image h-full w-full object-contain transition-transform duration-300 group-hover:scale-105",
8575
8626
  src: derived.imageUrl,
8576
- alt: derived.name
8627
+ alt: derived.name,
8628
+ loading: "lazy"
8577
8629
  }
8578
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-cluster-card__image-placeholder flex h-full w-full items-center justify-center text-foreground-subtle", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", className: "h-16 w-16", children: /* @__PURE__ */ jsxRuntime.jsx(
8630
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-cluster-card__image-placeholder flex h-full w-full items-center justify-center text-foreground-subtle", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", className: "h-16 w-16", children: /* @__PURE__ */ jsxRuntime.jsx(
8579
8631
  "path",
8580
8632
  {
8581
8633
  strokeLinecap: "round",
@@ -8793,6 +8845,7 @@ function getMachineImageUrl(machine) {
8793
8845
  }
8794
8846
  function MachineCard(props) {
8795
8847
  const { machine, href, language } = props;
8848
+ const Img = useImgComponent();
8796
8849
  const Wrapper = href ? "a" : "div";
8797
8850
  const showImage = props.showImage ?? true;
8798
8851
  const showDescription = props.showDescription ?? false;
@@ -8811,8 +8864,9 @@ function MachineCard(props) {
8811
8864
  className: "flex h-full flex-col focus:outline-none focus-visible:ring-2 focus-visible:ring-primary",
8812
8865
  children: [
8813
8866
  showImage && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-machine-card__image aspect-square w-full overflow-hidden bg-background-subtle", children: imageUrl ? /* @__PURE__ */ jsxRuntime.jsx(
8814
- "img",
8867
+ PropellerImg,
8815
8868
  {
8869
+ as: Img,
8816
8870
  src: imageUrl,
8817
8871
  alt: name,
8818
8872
  loading: "lazy",
@@ -8829,6 +8883,7 @@ function MachineCard(props) {
8829
8883
  children: /* @__PURE__ */ jsxRuntime.jsx(
8830
8884
  "svg",
8831
8885
  {
8886
+ "aria-hidden": "true",
8832
8887
  className: "h-12 w-12",
8833
8888
  fill: "none",
8834
8889
  stroke: "currentColor",
@@ -9133,6 +9188,7 @@ function ProductCardImage(props) {
9133
9188
  const showFavorite = props.showFavorite ?? !!resolved.enableAddFavorite;
9134
9189
  const isRow = derived.isRow;
9135
9190
  const InjectedImage = resolved.imageComponent;
9191
+ const Img = useImgComponent();
9136
9192
  if (InjectedImage) {
9137
9193
  return /* @__PURE__ */ jsxRuntime.jsx(
9138
9194
  InjectedImage,
@@ -9151,13 +9207,15 @@ function ProductCardImage(props) {
9151
9207
  className: props.className ?? `propeller-product-card__media relative overflow-hidden bg-surface-hover ${isRow ? "w-20 h-20 flex-shrink-0 p-2" : "aspect-[4/3] sm:aspect-square p-2 sm:p-4"}`,
9152
9208
  children: [
9153
9209
  /* @__PURE__ */ jsxRuntime.jsx("a", { className: "block h-full w-full", href: derived.productUrl, onClick: handleProductClick, children: derived.imageUrl ? /* @__PURE__ */ jsxRuntime.jsx(
9154
- "img",
9210
+ PropellerImg,
9155
9211
  {
9212
+ as: Img,
9156
9213
  className: "propeller-product-card__image h-full w-full object-contain transition-transform duration-300 group-hover:scale-105",
9157
9214
  src: derived.imageUrl,
9158
- alt: derived.name
9215
+ alt: derived.name,
9216
+ loading: "lazy"
9159
9217
  }
9160
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-product-card__image-placeholder flex h-full w-full items-center justify-center text-foreground-subtle", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", className: "h-16 w-16", children: /* @__PURE__ */ jsxRuntime.jsx(
9218
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-product-card__image-placeholder flex h-full w-full items-center justify-center text-foreground-subtle", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", className: "h-16 w-16", children: /* @__PURE__ */ jsxRuntime.jsx(
9161
9219
  "path",
9162
9220
  {
9163
9221
  strokeLinecap: "round",
@@ -11012,6 +11070,7 @@ function MachinePartsSearch(props) {
11012
11070
  ] });
11013
11071
  }
11014
11072
  function ClusterConfigurator(props) {
11073
+ const Img = useImgComponent();
11015
11074
  const { settingsWithValues, handleAttributeSelect, initFromProduct } = useClusterConfigurator({
11016
11075
  products: props.products,
11017
11076
  config: props.config,
@@ -11102,7 +11161,7 @@ function ClusterConfigurator(props) {
11102
11161
  "data-selected": setting.selectedValue === val ? "true" : "false",
11103
11162
  className: `propeller-cluster-configurator__image-swatch relative w-16 h-16 rounded-control border-2 overflow-hidden transition-all ${setting.disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"} ${setting.selectedValue === val ? "border-secondary ring-2 ring-secondary/30 ring-offset-1" : "border-border hover:border-secondary/30"}`,
11104
11163
  children: [
11105
- /* @__PURE__ */ jsxRuntime.jsx("img", { className: "propeller-cluster-configurator__image w-full h-full object-cover", src: val, alt: val }),
11164
+ /* @__PURE__ */ jsxRuntime.jsx(PropellerImg, { as: Img, className: "propeller-cluster-configurator__image w-full h-full object-cover", src: val, alt: val, loading: "lazy" }),
11106
11165
  setting.selectedValue === val ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-cluster-configurator__image-check absolute inset-0 bg-secondary bg-opacity-20 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
11107
11166
  "svg",
11108
11167
  {
@@ -11202,6 +11261,7 @@ function getProductImageUrl4(product) {
11202
11261
  }
11203
11262
  function ClusterOptions(rawProps) {
11204
11263
  const props = useInfraProps(rawProps);
11264
+ const Img = useImgComponent();
11205
11265
  const [selectedProductIds, setSelectedProductIds] = React6.useState({});
11206
11266
  function getOptionsForRender() {
11207
11267
  const language = props.language;
@@ -11288,20 +11348,23 @@ function ClusterOptions(rawProps) {
11288
11348
  option.hasError ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "propeller-cluster-options__error mt-1 text-xs text-destructive", children: propellerV2CoreUi.getLabel(props.labels, "requiredError", "This option is required") }) : null,
11289
11349
  option.hasSelection ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-cluster-options__preview mt-3 flex items-center gap-3 rounded-container border border-border-subtle bg-surface-hover p-3", children: [
11290
11350
  !!option.previewImageUrl ? /* @__PURE__ */ jsxRuntime.jsx(
11291
- "img",
11351
+ PropellerImg,
11292
11352
  {
11353
+ as: Img,
11293
11354
  className: "propeller-cluster-options__preview-image h-12 w-12 flex-shrink-0 rounded border border-border-subtle bg-card object-contain",
11294
11355
  src: option.previewImageUrl,
11295
- alt: option.previewName
11356
+ alt: option.previewName,
11357
+ loading: "lazy"
11296
11358
  }
11297
11359
  ) : null,
11298
11360
  !option.previewImageUrl ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-cluster-options__preview-image-placeholder flex h-12 w-12 flex-shrink-0 items-center justify-center rounded border border-border bg-surface-hover", children: /* @__PURE__ */ jsxRuntime.jsx(
11299
11361
  "svg",
11300
11362
  {
11363
+ "aria-hidden": "true",
11301
11364
  fill: "none",
11302
11365
  stroke: "currentColor",
11303
11366
  viewBox: "0 0 24 24",
11304
- className: "h-5 w-5 text-foreground-subtle",
11367
+ className: "h-6 w-6 text-foreground-subtle",
11305
11368
  children: /* @__PURE__ */ jsxRuntime.jsx(
11306
11369
  "path",
11307
11370
  {
@@ -11785,19 +11848,22 @@ function FavoriteListItem(rawProps) {
11785
11848
  }
11786
11849
  function FavoriteListItemImage(props) {
11787
11850
  const { derived, handleItemClick, resolved } = useFavoriteListItemContext();
11851
+ const Img = useImgComponent();
11788
11852
  const { imageUrl, name, productUrl } = derived;
11789
11853
  const linkable = resolved.titleLinkable !== false;
11790
11854
  const wrapperClass = props.className ?? "propeller-favorite-list-item__media relative w-16 h-16 flex-shrink-0 overflow-hidden rounded-control bg-surface-hover p-1";
11791
11855
  const inner = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
11792
11856
  !!imageUrl ? /* @__PURE__ */ jsxRuntime.jsx(
11793
- "img",
11857
+ PropellerImg,
11794
11858
  {
11859
+ as: Img,
11795
11860
  className: "propeller-favorite-list-item__image h-full w-full object-contain",
11796
11861
  src: imageUrl,
11797
- alt: name
11862
+ alt: name,
11863
+ loading: "lazy"
11798
11864
  }
11799
11865
  ) : null,
11800
- !imageUrl ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-favorite-list-item__image-placeholder flex h-full w-full items-center justify-center text-foreground-subtle", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", className: "h-8 w-8", children: /* @__PURE__ */ jsxRuntime.jsx(
11866
+ !imageUrl ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-favorite-list-item__image-placeholder flex h-full w-full items-center justify-center text-foreground-subtle", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", className: "h-10 w-10", children: /* @__PURE__ */ jsxRuntime.jsx(
11801
11867
  "path",
11802
11868
  {
11803
11869
  strokeLinecap: "round",
@@ -11995,6 +12061,7 @@ FavoriteListItemExport.Actions = FavoriteListItemActions;
11995
12061
  var FavoriteListItem_default = FavoriteListItemExport;
11996
12062
  function FavoriteListDetails(rawProps) {
11997
12063
  const props = useInfraProps(rawProps);
12064
+ const Img = useImgComponent();
11998
12065
  const FavoriteListItemImpl = props.favoriteListItemComponent ?? FavoriteListItem_default;
11999
12066
  const GridPaginationImpl = props.gridPaginationComponent ?? GridPagination_default;
12000
12067
  const { addToList } = useFavorites({
@@ -12576,11 +12643,13 @@ function FavoriteListDetails(rawProps) {
12576
12643
  "data-adding": isAdding ? "true" : "false",
12577
12644
  children: [
12578
12645
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-favorite-list-details__search-item-media h-14 w-14 flex-shrink-0 rounded-control bg-surface-hover overflow-hidden flex items-center justify-center", children: getSearchItemImage(item) ? /* @__PURE__ */ jsxRuntime.jsx(
12579
- "img",
12646
+ PropellerImg,
12580
12647
  {
12648
+ as: Img,
12581
12649
  src: getSearchItemImage(item),
12582
12650
  alt: getSearchItemName(item),
12583
- className: "h-full w-full object-contain"
12651
+ className: "h-full w-full object-contain",
12652
+ loading: "lazy"
12584
12653
  }
12585
12654
  ) : /* @__PURE__ */ jsxRuntime.jsxs(
12586
12655
  "svg",
@@ -13288,6 +13357,7 @@ function getBundleNonLeaders(item) {
13288
13357
  }
13289
13358
  function ItemsOverview(rawProps) {
13290
13359
  const props = useInfraProps(rawProps);
13360
+ const Img = useImgComponent();
13291
13361
  const language = props.language;
13292
13362
  const useTax = !!props.includeTax;
13293
13363
  const containerClass = props.itemsOverviewContainerClass || "cart-items-overview";
@@ -13346,26 +13416,29 @@ function ItemsOverview(rawProps) {
13346
13416
  "data-bundle": isBundle ? "true" : "false",
13347
13417
  children: [
13348
13418
  showImage ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-items-overview__item-media w-16 h-16 flex-shrink-0 bg-surface-hover rounded-control overflow-hidden border border-border-subtle flex items-center justify-center", children: getItemImageUrl(item) ? /* @__PURE__ */ jsxRuntime.jsx(
13349
- "img",
13419
+ PropellerImg,
13350
13420
  {
13421
+ as: Img,
13351
13422
  className: "propeller-items-overview__item-image w-full h-full object-contain p-1.5",
13352
13423
  src: getItemImageUrl(item),
13353
- alt: getItemName(item, language)
13424
+ alt: getItemName(item, language),
13425
+ loading: "lazy"
13354
13426
  }
13355
13427
  ) : /* @__PURE__ */ jsxRuntime.jsx(
13356
13428
  "svg",
13357
13429
  {
13430
+ "aria-hidden": "true",
13358
13431
  fill: "none",
13359
13432
  viewBox: "0 0 24 24",
13360
13433
  stroke: "currentColor",
13361
- strokeWidth: "1.5",
13362
- className: "propeller-items-overview__item-image-placeholder w-6 h-6 text-foreground-subtle",
13434
+ strokeWidth: "1",
13435
+ className: "propeller-items-overview__item-image-placeholder w-8 h-8 text-foreground-subtle",
13363
13436
  children: /* @__PURE__ */ jsxRuntime.jsx(
13364
13437
  "path",
13365
13438
  {
13366
13439
  strokeLinecap: "round",
13367
13440
  strokeLinejoin: "round",
13368
- d: "M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909M3.75 21h16.5A2.25 2.25 0 0022.5 18.75V5.25A2.25 2.25 0 0020.25 3H3.75A2.25 2.25 0 001.5 5.25v13.5A2.25 2.25 0 003.75 21z"
13441
+ d: "M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
13369
13442
  }
13370
13443
  )
13371
13444
  }
@@ -13903,6 +13976,28 @@ function OrderActions(rawProps) {
13903
13976
  );
13904
13977
  }
13905
13978
  var OrderActions_default = OrderActions;
13979
+ var warnedOnce = /* @__PURE__ */ new Set();
13980
+ function warnOnce(key, message) {
13981
+ if (warnedOnce.has(key)) return;
13982
+ warnedOnce.add(key);
13983
+ console.warn(message);
13984
+ }
13985
+ function warnMissingProps(props) {
13986
+ if (process.env.NODE_ENV === "production") return;
13987
+ if (!props.language) {
13988
+ warnOnce(
13989
+ "language",
13990
+ "[OrderItemCard] No `language` prop: product names resolve in the default catalogue language, whatever page they are on. This component reads no context (it ships from the RSC-safe /pure entry), so the host must pass it."
13991
+ );
13992
+ }
13993
+ const linkable = props.titleLinkable !== void 0 ? props.titleLinkable : true;
13994
+ if (linkable && !props.configuration?.urls) {
13995
+ warnOnce(
13996
+ "urls",
13997
+ "[OrderItemCard] Title is linkable but no `configuration.urls` was passed: links fall back to a literal /product/:id/:slug and lose the locale prefix. Pass the url builders from the host, or set titleLinkable={false}."
13998
+ );
13999
+ }
14000
+ }
13906
14001
  function getProductName3(orderItem, language) {
13907
14002
  return propellerV2CoreUi.getLocalizedValue(orderItem?.product?.names, language, "") || orderItem?.name || "Unknown Product";
13908
14003
  }
@@ -13926,6 +14021,7 @@ function getProductUrl(orderItem, language, urls) {
13926
14021
  function OrderItemCard(props) {
13927
14022
  const item = props.orderItem;
13928
14023
  const isChildItem = props.isChildItem || false;
14024
+ warnMissingProps(props);
13929
14025
  const titleLinkable = props.titleLinkable !== void 0 ? props.titleLinkable : true;
13930
14026
  const showImage = isChildItem ? false : props.showImage !== void 0 ? props.showImage : true;
13931
14027
  const showSku = isChildItem ? false : props.showSku !== void 0 ? props.showSku : true;
@@ -13966,11 +14062,13 @@ function OrderItemCard(props) {
13966
14062
  className: `propeller-order-item-card__cell propeller-order-item-card__cell--product ${isChildItem ? "px-6 py-2 pl-28" : "px-6 py-4"}`,
13967
14063
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
13968
14064
  showImage ? productImage ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-order-item-card__media relative w-16 h-16 flex-shrink-0 rounded overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
13969
- "img",
14065
+ PropellerImg,
13970
14066
  {
14067
+ as: props.imgComponent,
13971
14068
  className: "propeller-order-item-card__image object-cover w-full h-full",
13972
14069
  src: productImage,
13973
- alt: productName
14070
+ alt: productName,
14071
+ loading: "lazy"
13974
14072
  }
13975
14073
  ) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-order-item-card__image-placeholder w-16 h-16 bg-surface-hover rounded flex items-center justify-center text-foreground-subtle text-xs", children: propellerV2CoreUi.getLabel(props.labels, "noImage", "No Img") }) : null,
13976
14074
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
@@ -14697,6 +14795,10 @@ function RequestPriceButton(props) {
14697
14795
  }
14698
14796
  var RequestPriceButton_default = RequestPriceButton;
14699
14797
  var ROW_SEQ = 0;
14798
+ function snapToStep(value, min, step) {
14799
+ if (!Number.isFinite(value) || value <= min) return min;
14800
+ return Math.round((value - min) / (step || 1)) * (step || 1) + min;
14801
+ }
14700
14802
  function blankRow() {
14701
14803
  ROW_SEQ += 1;
14702
14804
  return {
@@ -14708,6 +14810,7 @@ function blankRow() {
14708
14810
  grossPrice: 0,
14709
14811
  quantity: 1,
14710
14812
  minQuantity: 1,
14813
+ step: 1,
14711
14814
  matches: [],
14712
14815
  searching: false,
14713
14816
  searched: false
@@ -14732,6 +14835,7 @@ function QuickOrderInner(props) {
14732
14835
  className,
14733
14836
  labels
14734
14837
  } = props;
14838
+ const Img = useImgComponent();
14735
14839
  const user = props.user ?? null;
14736
14840
  const graphqlClient = props.graphqlClient;
14737
14841
  const displayPrice = (n) => props.formatPrice ? props.formatPrice(n) : propellerV2CoreUi.formatPrice(n, { symbol: currency, locale: propellerV2CoreUi.localeForLanguage(props.language) });
@@ -14801,6 +14905,7 @@ function QuickOrderInner(props) {
14801
14905
  grossPrice: match.grossPrice,
14802
14906
  quantity: match.minQuantity,
14803
14907
  minQuantity: match.minQuantity,
14908
+ step: match.step,
14804
14909
  matches: [],
14805
14910
  searching: false,
14806
14911
  searched: false
@@ -14813,7 +14918,7 @@ function QuickOrderInner(props) {
14813
14918
  const n = parseInt(raw, 10);
14814
14919
  setRows(
14815
14920
  (prev) => prev.map(
14816
- (r) => r.key === key ? { ...r, quantity: Number.isFinite(n) && n > 0 ? Math.max(r.minQuantity, n) : r.minQuantity } : r
14921
+ (r) => r.key === key ? { ...r, quantity: snapToStep(n, r.minQuantity, r.step) } : r
14817
14922
  )
14818
14923
  );
14819
14924
  },
@@ -14866,7 +14971,8 @@ function QuickOrderInner(props) {
14866
14971
  netPrice: exact.netPrice,
14867
14972
  grossPrice: exact.grossPrice,
14868
14973
  quantity: Math.max(exact.minQuantity, line.quantity),
14869
- minQuantity: exact.minQuantity
14974
+ minQuantity: exact.minQuantity,
14975
+ step: exact.step
14870
14976
  });
14871
14977
  }
14872
14978
  if (resolved.length) {
@@ -14961,7 +15067,7 @@ function QuickOrderInner(props) {
14961
15067
  children: [
14962
15068
  m.imageUrl ? (
14963
15069
  // eslint-disable-next-line @next/next/no-img-element
14964
- /* @__PURE__ */ jsxRuntime.jsx("img", { src: m.imageUrl, alt: "", width: 32, height: 32, className: "rounded object-cover" })
15070
+ /* @__PURE__ */ jsxRuntime.jsx(PropellerImg, { as: Img, src: m.imageUrl, alt: "", width: 32, height: 32, className: "rounded object-cover", loading: "lazy" })
14965
15071
  ) : null,
14966
15072
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex-1", children: [
14967
15073
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block text-foreground", children: m.name }),
@@ -14997,7 +15103,7 @@ function QuickOrderInner(props) {
14997
15103
  {
14998
15104
  type: "number",
14999
15105
  min: r.minQuantity,
15000
- step: 1,
15106
+ step: r.step || 1,
15001
15107
  value: r.productId ? r.quantity : "",
15002
15108
  disabled: !r.productId,
15003
15109
  onChange: (e) => setQuantity(r.key, e.target.value),
@@ -15626,6 +15732,7 @@ function getProductName4(product, language) {
15626
15732
  }
15627
15733
  function ProductBundles(rawProps) {
15628
15734
  const props = useInfraProps(rawProps);
15735
+ const Img = useImgComponent();
15629
15736
  const language = props.language;
15630
15737
  const currencySymbol = props.currency ?? "\u20AC";
15631
15738
  const money = (value) => propellerV2CoreUi.formatPrice(value, { symbol: currencySymbol, locale: propellerV2CoreUi.localeForLanguage(props.language) });
@@ -15761,11 +15868,13 @@ function ProductBundles(rawProps) {
15761
15868
  ) }) : null,
15762
15869
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center text-center w-40", children: [
15763
15870
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-product-bundles__item-media w-32 h-32 bg-surface-hover rounded-container overflow-hidden flex-shrink-0 mb-2", children: getProductImage(item.product) ? /* @__PURE__ */ jsxRuntime.jsx(
15764
- "img",
15871
+ PropellerImg,
15765
15872
  {
15873
+ as: Img,
15766
15874
  className: "propeller-product-bundles__item-image w-full h-full object-contain p-2",
15767
15875
  src: getProductImage(item.product),
15768
- alt: getProductName4(item.product, language)
15876
+ alt: getProductName4(item.product, language),
15877
+ loading: "lazy"
15769
15878
  }
15770
15879
  ) : null }),
15771
15880
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-product-bundles__item-name text-sm font-medium text-muted-foreground leading-tight mb-1", children: getProductName4(item.product, language) || "Product " + item.productId }),
@@ -15930,26 +16039,29 @@ function ProductBundles(rawProps) {
15930
16039
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-product-bundles__modal-body px-6 py-5", children: [
15931
16040
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-product-bundles__modal-product flex items-start gap-4", children: [
15932
16041
  lastAddedBundle && lastAddedBundle.items && lastAddedBundle.items.length > 0 && getProductImage(lastAddedBundle.items[0].product) ? /* @__PURE__ */ jsxRuntime.jsx(
15933
- "img",
16042
+ PropellerImg,
15934
16043
  {
16044
+ as: Img,
15935
16045
  className: "propeller-product-bundles__modal-image w-16 h-16 object-contain rounded border border-border-subtle flex-shrink-0",
15936
16046
  src: lastAddedBundle?.items?.[0] ? getProductImage(lastAddedBundle.items[0].product) : "",
15937
- alt: lastAddedBundle?.name || "Bundle"
16047
+ alt: lastAddedBundle?.name || "Bundle",
16048
+ loading: "lazy"
15938
16049
  }
15939
16050
  ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-product-bundles__modal-image-placeholder w-16 h-16 flex items-center justify-center rounded border border-border-subtle flex-shrink-0 bg-surface-hover", children: /* @__PURE__ */ jsxRuntime.jsx(
15940
16051
  "svg",
15941
16052
  {
16053
+ "aria-hidden": "true",
15942
16054
  fill: "none",
15943
16055
  viewBox: "0 0 24 24",
15944
16056
  stroke: "currentColor",
15945
16057
  className: "w-8 h-8 text-foreground-subtle",
15946
- strokeWidth: 1.5,
16058
+ strokeWidth: 1,
15947
16059
  children: /* @__PURE__ */ jsxRuntime.jsx(
15948
16060
  "path",
15949
16061
  {
15950
16062
  strokeLinecap: "round",
15951
16063
  strokeLinejoin: "round",
15952
- d: "M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909M3.75 21h16.5A2.25 2.25 0 0022.5 18.75V5.25A2.25 2.25 0 0020.25 3H3.75A2.25 2.25 0 001.5 5.25v13.5A2.25 2.25 0 003.75 21z"
16064
+ d: "M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
15953
16065
  }
15954
16066
  )
15955
16067
  }
@@ -16114,6 +16226,7 @@ function ProductDownloads(props) {
16114
16226
  }
16115
16227
  var ProductDownloads_default = ProductDownloads;
16116
16228
  function ProductGallery(props) {
16229
+ const Img = useImgComponent();
16117
16230
  const [selectedIndex, setSelectedIndex] = React6.useState(0);
16118
16231
  const [lightboxOpen, setLightboxOpen] = React6.useState(false);
16119
16232
  const images = props.images || [];
@@ -16151,11 +16264,13 @@ function ProductGallery(props) {
16151
16264
  }
16152
16265
  ) }) : null,
16153
16266
  images.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
16154
- "img",
16267
+ PropellerImg,
16155
16268
  {
16269
+ as: Img,
16156
16270
  alt: propellerV2CoreUi.getLabel(props.labels, "productImageAlt", "Product image"),
16157
16271
  src: mainImage,
16158
16272
  onClick: openLightbox,
16273
+ loading: "eager",
16159
16274
  className: `h-full w-full object-contain p-8 transition-transform duration-200 ${props.enableZoom !== false ? "cursor-zoom-in hover:scale-105" : ""}`
16160
16275
  }
16161
16276
  ) : null
@@ -16167,11 +16282,13 @@ function ProductGallery(props) {
16167
16282
  onClick: () => setSelectedIndex(index),
16168
16283
  className: `propeller-product-gallery__thumbnail relative flex-shrink-0 w-20 h-20 rounded-container border-2 overflow-hidden transition-all bg-card ${selectedIndex === index ? "border-primary ring-2 ring-primary/20" : "border-transparent hover:border-border"}`,
16169
16284
  children: /* @__PURE__ */ jsxRuntime.jsx(
16170
- "img",
16285
+ PropellerImg,
16171
16286
  {
16287
+ as: Img,
16172
16288
  className: "w-full h-full object-contain p-1",
16173
16289
  src: img,
16174
- alt: `${propellerV2CoreUi.getLabel(props.labels, "productImageAlt", "Product image")} ${index + 1}`
16290
+ alt: `${propellerV2CoreUi.getLabel(props.labels, "productImageAlt", "Product image")} ${index + 1}`,
16291
+ loading: "lazy"
16175
16292
  }
16176
16293
  )
16177
16294
  },
@@ -16226,12 +16343,13 @@ function ProductGallery(props) {
16226
16343
  }
16227
16344
  ) : null,
16228
16345
  /* @__PURE__ */ jsxRuntime.jsx(
16229
- "img",
16346
+ PropellerImg,
16230
16347
  {
16348
+ as: Img,
16231
16349
  alt: propellerV2CoreUi.getLabel(props.labels, "productImageFullscreenAlt", "Product image fullscreen"),
16232
16350
  className: "max-h-full max-w-full object-contain rounded-lg",
16233
16351
  src: mainImage,
16234
- onClick: (e) => e.stopPropagation()
16352
+ onClick: (e) => e.stopPropagation?.()
16235
16353
  }
16236
16354
  ),
16237
16355
  hasThumbnails ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -16285,6 +16403,7 @@ function pickImageUrl(mediaItems, target) {
16285
16403
  }
16286
16404
  function DefaultProductImage(props) {
16287
16405
  const { product, cluster, className } = props;
16406
+ const Img = useImgComponent();
16288
16407
  const targetLanguage = props.language ?? "NL";
16289
16408
  const mediaItems = (() => {
16290
16409
  const target = product ?? cluster?.defaultProduct ?? cluster;
@@ -16319,8 +16438,9 @@ function DefaultProductImage(props) {
16319
16438
  );
16320
16439
  }
16321
16440
  return /* @__PURE__ */ jsxRuntime.jsx(
16322
- "img",
16441
+ PropellerImg,
16323
16442
  {
16443
+ as: Img,
16324
16444
  src: url,
16325
16445
  alt: altText,
16326
16446
  className: className ?? "h-full w-full object-contain",
@@ -19229,6 +19349,7 @@ function mapToSearchBarResult(item, language, urls) {
19229
19349
  }
19230
19350
  function SearchBar(rawProps) {
19231
19351
  const props = useInfraProps(rawProps);
19352
+ const Img = useImgComponent();
19232
19353
  const [showDropdown, setShowDropdown] = React6.useState(false);
19233
19354
  const [localTerm, setLocalTerm] = React6.useState("");
19234
19355
  const containerRef = React6.useRef(null);
@@ -19394,11 +19515,13 @@ function SearchBar(rawProps) {
19394
19515
  ...resultProps,
19395
19516
  children: [
19396
19517
  result.imageUrl || noImageUrl() ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-search-bar__result-media relative w-16 h-16 flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
19397
- "img",
19518
+ PropellerImg,
19398
19519
  {
19520
+ as: Img,
19399
19521
  className: "propeller-search-bar__result-image w-full h-full object-contain",
19400
19522
  src: result.imageUrl || noImageUrl(),
19401
- alt: result.name
19523
+ alt: result.name,
19524
+ loading: "lazy"
19402
19525
  }
19403
19526
  ) }) : null,
19404
19527
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
@@ -19635,6 +19758,7 @@ exports.ProductSpecifications = ProductSpecifications_default;
19635
19758
  exports.ProductTabs = ProductTabs_default;
19636
19759
  exports.ProductVideos = ProductVideos_default;
19637
19760
  exports.PropellerDepsProvider = PropellerDepsProvider;
19761
+ exports.PropellerImg = PropellerImg;
19638
19762
  exports.PropellerProvider = PropellerProvider;
19639
19763
  exports.PurchaseAuthorizationConfigurator = PurchaseAuthorizationConfigurator_default;
19640
19764
  exports.PurchaseAuthorizationRequests = PurchaseAuthorizationRequests_default;
@@ -19660,6 +19784,7 @@ exports.useCheckout = useCheckout;
19660
19784
  exports.useClusterConfigurator = useClusterConfigurator;
19661
19785
  exports.useCompany = useCompany;
19662
19786
  exports.useFavorites = useFavorites;
19787
+ exports.useImgComponent = useImgComponent;
19663
19788
  exports.useInfraProps = useInfraProps;
19664
19789
  exports.useMachines = useMachines;
19665
19790
  exports.useMenu = useMenu;