@propeller-commerce/propeller-v2-react-ui 0.27.0 → 0.29.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.js CHANGED
@@ -1,8 +1,8 @@
1
1
  "use client";
2
- import { getLabel, getLocalizedValue, formatPrice, localeForLanguage, getClusterSku, getClusterImageUrl, isContentHidden, getProductSku, getProductImageUrl, createServices, isCheckoutAllowed, err, ok, formatSurcharge, deriveUserMode, isContact, isCustomer, getAddresses, attributeNameMatches, getAttributeDisplayName, filterProductsBySelections, extractAttributeValues, buildInventoryFilter, findPurchaserPac, isOverAuthorizationLimit, MIN_STOCK_THRESHOLD, getNettedBonusItems, getLanguageString, collectAttributeValues, getCountryName, resolveLanguageEntry, getLanguageUri } from '@propeller-commerce/propeller-v2-core-ui';
2
+ import { getLabel, getLocalizedValue, formatPrice, localeForLanguage, getClusterSku, getClusterImageUrl, isContentHidden, getProductSku, getProductImageUrl, createServices, isCheckoutAllowed, err, ok, formatSurcharge, deriveUserMode, isContact, isCustomer, getAddresses, resolveOrderableQuantity, attributeNameMatches, getAttributeDisplayName, filterProductsBySelections, extractAttributeValues, buildInventoryFilter, findPurchaserPac, isOverAuthorizationLimit, MIN_STOCK_THRESHOLD, getNettedBonusItems, getLanguageString, collectAttributeValues, getCountryName, resolveLanguageEntry, getLanguageUri } from '@propeller-commerce/propeller-v2-core-ui';
3
3
  export * from '@propeller-commerce/propeller-v2-core-ui';
4
4
  import * as React6 from 'react';
5
- import { createContext, useState, useRef, useCallback, useEffect, useMemo, useContext, useSyncExternalStore } from 'react';
5
+ import { createContext, useState, useContext, useRef, useCallback, useEffect, useMemo, useSyncExternalStore } from 'react';
6
6
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
7
7
  import { ProductStatus, ProductSortField, SortOrder, CrossupsellType, PriceDisplay, CartStatus, CartAddressType, Gender, YesNo, AddressType, MagicTokenService, PaymentStatuses, OrderSearchFields, OrderItemClass, ProductSearchableField, machineService, PurchaseRole, AttributeType, OrderSortField, OrderType, BundleCondition, OrderDiscountType } from '@propeller-commerce/propeller-sdk-v2';
8
8
  import { clsx } from 'clsx';
@@ -21,6 +21,9 @@ function PropellerProvider({ value, children }) {
21
21
  function usePropellerDeps() {
22
22
  return useContext(PropellerDepsContext);
23
23
  }
24
+ function useImgComponent() {
25
+ return useContext(PropellerDepsContext)?.imgComponent;
26
+ }
24
27
  function useRequiredPropellerDeps() {
25
28
  const deps = useContext(PropellerDepsContext);
26
29
  if (!deps) {
@@ -597,6 +600,14 @@ function useAuth(options) {
597
600
  }, [graphqlClient]);
598
601
  return { loading, error, login, magicLogin, createMagicToken, registerContact, registerCustomer, forgotPassword };
599
602
  }
603
+ var CART_NOT_FOUND = "CART_NOT_FOUND_ERROR";
604
+ function isCartNotFound(error) {
605
+ const entries = error?.errors;
606
+ if (!Array.isArray(entries)) return false;
607
+ return entries.some(
608
+ (e) => e?.extensions?.code === CART_NOT_FOUND || /cart:?\s*\S+\s+not found/i.test(e?.message ?? "")
609
+ );
610
+ }
600
611
  async function initCart(config) {
601
612
  const {
602
613
  services,
@@ -736,6 +747,11 @@ function useCart(options) {
736
747
  setCreatedCartId(c.cartId);
737
748
  cartIdRef.current = c.cartId;
738
749
  }, []);
750
+ const forgetCart = useCallback(() => {
751
+ setCart(null);
752
+ setCreatedCartId("");
753
+ cartIdRef.current = "";
754
+ }, []);
739
755
  const imageSearchFilters = useCallback(
740
756
  () => configuration?.imageSearchFiltersGrid ?? DEFAULT_IMAGE_SEARCH_FILTERS,
741
757
  [configuration]
@@ -819,13 +835,28 @@ function useCart(options) {
819
835
  } else return err("No cart ID provided");
820
836
  }
821
837
  const service = createServices(graphqlClient).cart;
822
- const resultCart = await service.addItemToCart({
823
- id: resolvedCartId,
824
- input: { productId: opts.product.productId, quantity: opts.quantity, ...opts.cluster?.clusterId !== void 0 && { clusterId: opts.cluster.clusterId }, ...childItemInputs && { childItems: childItemInputs }, ...opts.notes && { notes: opts.notes }, ...opts.price !== void 0 && { price: opts.price } },
825
- language,
826
- imageSearchFilters: imageSearchFilters(),
827
- imageVariantFilters: imageVariantFilters()
828
- });
838
+ const input = { productId: opts.product.productId, quantity: opts.quantity, ...opts.cluster?.clusterId !== void 0 && { clusterId: opts.cluster.clusterId }, ...childItemInputs && { childItems: childItemInputs }, ...opts.notes && { notes: opts.notes }, ...opts.price !== void 0 && { price: opts.price } };
839
+ let resultCart;
840
+ try {
841
+ resultCart = await service.addItemToCart({
842
+ id: resolvedCartId,
843
+ input,
844
+ language,
845
+ imageSearchFilters: imageSearchFilters(),
846
+ imageVariantFilters: imageVariantFilters()
847
+ });
848
+ } catch (e) {
849
+ if (!isCartNotFound(e) || !opts.createCart) throw e;
850
+ forgetCart();
851
+ const fresh = await resolveCart();
852
+ resultCart = await service.addItemToCart({
853
+ id: fresh.cartId,
854
+ input,
855
+ language,
856
+ imageSearchFilters: imageSearchFilters(),
857
+ imageVariantFilters: imageVariantFilters()
858
+ });
859
+ }
829
860
  rememberCart(resultCart);
830
861
  const addedItem = resultCart.items?.find((i) => i.productId === opts.product.productId) ?? null;
831
862
  opts.afterAddToCart?.(resultCart, addedItem);
@@ -837,7 +868,7 @@ function useCart(options) {
837
868
  } finally {
838
869
  setLoading(false);
839
870
  }
840
- }, [graphqlClient, cartId, language, configuration, resolveCart, rememberCart, imageSearchFilters, imageVariantFilters]);
871
+ }, [graphqlClient, cartId, language, configuration, resolveCart, rememberCart, forgetCart, imageSearchFilters, imageVariantFilters]);
841
872
  const addItems = useCallback(async (items) => {
842
873
  const added = [];
843
874
  let lastCart = null;
@@ -2123,19 +2154,8 @@ function useProductInfo(options) {
2123
2154
  setLoading(true);
2124
2155
  setError(null);
2125
2156
  try {
2126
- let orderlistIds = [];
2127
- let applyOrderlists = true;
2128
- if (options.orderlistIds && options.orderlistIds.length > 0) {
2129
- orderlistIds = options.orderlistIds;
2130
- applyOrderlists = options.applyOrderlists !== false;
2131
- } else if (options.applyOrderlists === false) {
2132
- applyOrderlists = false;
2133
- } else if (options.user && options.companyId) {
2134
- const orderlistService = createServices(graphqlClient).orderlist;
2135
- const searchInput = { companyIds: [options.companyId] };
2136
- const orderlists = await orderlistService.getOrderlists(searchInput);
2137
- orderlistIds = (orderlists?.items ?? []).map((ol) => ol.id);
2138
- }
2157
+ const orderlistIds = options.orderlistIds ?? [];
2158
+ const applyOrderlists = options.applyOrderlists !== false;
2139
2159
  const service = createServices(graphqlClient).product;
2140
2160
  const attributeInput = buildAttributeInput2();
2141
2161
  const variables = {
@@ -2346,7 +2366,7 @@ function useProductSearch(options) {
2346
2366
  const orderlistScope = options.orderlistIds && options.orderlistIds.length > 0 ? {
2347
2367
  applyOrderlists: options.applyOrderlists !== false,
2348
2368
  orderlistIds: options.orderlistIds
2349
- } : { applyOrderlists: false };
2369
+ } : options.applyOrderlists === false ? { applyOrderlists: false } : {};
2350
2370
  const categoryProductSearchInput = {
2351
2371
  language: lang,
2352
2372
  page: pagination.currentPage,
@@ -2476,7 +2496,7 @@ function useProductSearch(options) {
2476
2496
  const orderlistScope = options.orderlistIds && options.orderlistIds.length > 0 ? {
2477
2497
  applyOrderlists: options.applyOrderlists !== false,
2478
2498
  orderlistIds: options.orderlistIds
2479
- } : { applyOrderlists: false };
2499
+ } : options.applyOrderlists === false ? { applyOrderlists: false } : {};
2480
2500
  const userId = resolveListingUserId(options.user, configuration);
2481
2501
  const contactId = options.user && "contactId" in options.user ? options.user.contactId : void 0;
2482
2502
  const customerId = options.user && "customerId" in options.user ? options.user.customerId : void 0;
@@ -2652,7 +2672,7 @@ function useQuickOrder(options) {
2652
2672
  if (!catId) return [];
2653
2673
  try {
2654
2674
  const service = createServices(graphqlClient).category;
2655
- const orderlistScope = orderlistIds && orderlistIds.length > 0 ? { applyOrderlists: applyOrderlists !== false, orderlistIds } : { applyOrderlists: false };
2675
+ const orderlistScope = orderlistIds && orderlistIds.length > 0 ? { applyOrderlists: applyOrderlists !== false, orderlistIds } : applyOrderlists === false ? { applyOrderlists: false } : {};
2656
2676
  const userId = resolveListingUserId(user, configuration);
2657
2677
  const contactId = user && "contactId" in user ? user.contactId : void 0;
2658
2678
  const customerId = user && "customerId" in user ? user.customerId : void 0;
@@ -3261,28 +3281,33 @@ function useProductSlider(options) {
3261
3281
  }
3262
3282
  }
3263
3283
  if (!productIds.length && !clusterIds.length) return items;
3284
+ const catId = configuration.baseCategoryId;
3285
+ if (!catId) return items;
3264
3286
  const userId = resolveUserId();
3265
3287
  const companyId = resolveCompanyId();
3266
- const searchInput = {
3288
+ const categoryProductSearchInput = {
3267
3289
  ...productIds.length && { productIds },
3268
3290
  ...clusterIds.length && { clusterIds },
3269
3291
  language,
3270
3292
  page: 1,
3271
3293
  offset: 50,
3272
3294
  statuses: [ProductStatus.A, ProductStatus.P, ProductStatus.T, ProductStatus.S],
3295
+ hidden: false,
3273
3296
  ...userId !== void 0 && { userId },
3274
3297
  ...companyId !== void 0 && { companyId }
3275
3298
  };
3276
3299
  const filterAvailableAttributeInput = { isSearchable: true };
3277
3300
  const variables = {
3278
- input: searchInput,
3301
+ categoryId: catId,
3302
+ language,
3303
+ categoryProductSearchInput,
3304
+ filterAvailableAttributeInput,
3279
3305
  imageSearchFilters: configuration.imageSearchFiltersGrid,
3280
- imageVariantFilters: configuration.imageVariantFiltersMedium,
3281
- filterAvailableAttributeInput
3306
+ imageVariantFilters: configuration.imageVariantFiltersMedium
3282
3307
  };
3283
3308
  try {
3284
- const response = await createServices(graphqlClient).product.getProducts(variables);
3285
- const resolved = response?.items ?? [];
3309
+ const response = await createServices(graphqlClient).category.getCategory(variables);
3310
+ const resolved = response?.products?.items ?? [];
3286
3311
  const allowed = /* @__PURE__ */ new Set();
3287
3312
  for (const r of resolved) {
3288
3313
  const id = r.productId ?? r.clusterId;
@@ -3338,10 +3363,17 @@ function useProductSlider(options) {
3338
3363
  setLoading(true);
3339
3364
  setError(null);
3340
3365
  try {
3341
- const service = createServices(graphqlClient).product;
3342
- const searchInput = {
3343
- productIds,
3344
- clusterIds,
3366
+ const catId = configuration.baseCategoryId;
3367
+ if (!catId) {
3368
+ setProducts([]);
3369
+ return;
3370
+ }
3371
+ const service = createServices(graphqlClient).category;
3372
+ const userId = resolveUserId();
3373
+ const companyId = resolveCompanyId();
3374
+ const categoryProductSearchInput = {
3375
+ ...productIds.length && { productIds },
3376
+ ...clusterIds.length && { clusterIds },
3345
3377
  language,
3346
3378
  page: 1,
3347
3379
  offset: 50,
@@ -3350,17 +3382,24 @@ function useProductSlider(options) {
3350
3382
  ProductStatus.P,
3351
3383
  ProductStatus.T,
3352
3384
  ProductStatus.S
3353
- ]
3385
+ ],
3386
+ hidden: false,
3387
+ ...userId !== void 0 && { userId },
3388
+ ...companyId !== void 0 && { companyId }
3354
3389
  };
3355
3390
  const filterAvailableAttributeInput = { isSearchable: true };
3356
3391
  const variables = {
3357
- input: searchInput,
3392
+ categoryId: catId,
3393
+ language,
3394
+ categoryProductSearchInput,
3395
+ priceCalculateProductInput: buildPriceInput(),
3396
+ filterAvailableAttributeInput,
3358
3397
  imageSearchFilters: configuration.imageSearchFiltersGrid,
3359
- imageVariantFilters: configuration.imageVariantFiltersMedium,
3360
- filterAvailableAttributeInput
3398
+ imageVariantFilters: configuration.imageVariantFiltersMedium
3361
3399
  };
3362
- const response = await service.getProducts(variables);
3363
- setProducts(response?.items ?? []);
3400
+ const response = await service.getCategory(variables);
3401
+ const productsResponse = response?.products;
3402
+ setProducts(productsResponse?.items ?? []);
3364
3403
  } catch (e) {
3365
3404
  setError(e instanceof Error ? e.message : "Failed to fetch products");
3366
3405
  setProducts([]);
@@ -4676,8 +4715,25 @@ function ActionCode(rawProps) {
4676
4715
  ] });
4677
4716
  }
4678
4717
  var ActionCode_default = ActionCode;
4718
+ function PropellerImg({ as: Injected, ...props }) {
4719
+ if (Injected) return /* @__PURE__ */ jsx(Injected, { ...props });
4720
+ const { src, alt, className, width, height, loading, onClick } = props;
4721
+ return /* @__PURE__ */ jsx(
4722
+ "img",
4723
+ {
4724
+ src,
4725
+ alt,
4726
+ className,
4727
+ width,
4728
+ height,
4729
+ loading,
4730
+ onClick
4731
+ }
4732
+ );
4733
+ }
4679
4734
  function CartBonusItems(rawProps) {
4680
4735
  const props = useInfraProps(rawProps);
4736
+ const Img = useImgComponent();
4681
4737
  const items = props.bonusItems ?? props.cart?.bonusItems ?? [];
4682
4738
  if (items.length === 0) return null;
4683
4739
  const currency = props.currency ?? "\u20AC";
@@ -4695,20 +4751,21 @@ function CartBonusItems(rawProps) {
4695
4751
  {
4696
4752
  className: "propeller-cart-bonus-item flex items-center gap-4 bg-card p-4 rounded-container shadow-sm border border-border",
4697
4753
  children: [
4698
- /* @__PURE__ */ 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__ */ jsx("img", { className: "propeller-cart-bonus-item__image w-full h-full object-contain p-1", src: img, alt: name }) : /* @__PURE__ */ jsx(
4754
+ /* @__PURE__ */ 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__ */ 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__ */ jsx(
4699
4755
  "svg",
4700
4756
  {
4757
+ "aria-hidden": "true",
4701
4758
  fill: "none",
4702
4759
  viewBox: "0 0 24 24",
4703
4760
  stroke: "currentColor",
4704
- className: "propeller-cart-bonus-item__image-placeholder w-8 h-8 text-foreground-subtle",
4705
- strokeWidth: 1.5,
4761
+ className: "propeller-cart-bonus-item__image-placeholder w-10 h-10 text-foreground-subtle",
4762
+ strokeWidth: 1,
4706
4763
  children: /* @__PURE__ */ jsx(
4707
4764
  "path",
4708
4765
  {
4709
4766
  strokeLinecap: "round",
4710
4767
  strokeLinejoin: "round",
4711
- 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"
4768
+ 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"
4712
4769
  }
4713
4770
  )
4714
4771
  }
@@ -4753,6 +4810,7 @@ function mergeGrouped(p) {
4753
4810
  }
4754
4811
  function AddToCart(rawProps) {
4755
4812
  const props = mergeGrouped(useInfraProps(rawProps));
4813
+ const Img = useImgComponent();
4756
4814
  const { cart, loading, checkoutAllowed, addItem: addItem2, getMinQuantity, getStep } = useCart({
4757
4815
  graphqlClient: props.graphqlClient,
4758
4816
  user: props.user ?? null,
@@ -4785,10 +4843,7 @@ function AddToCart(rawProps) {
4785
4843
  setToastVisible(false);
4786
4844
  }
4787
4845
  function snapQuantity(value) {
4788
- const min = getMinQuantity(props.product);
4789
- const step = getStep(props.product);
4790
- if (value <= min) return min;
4791
- return Math.round((value - min) / step) * step + min;
4846
+ return resolveOrderableQuantity(value, getMinQuantity(props.product), getStep(props.product));
4792
4847
  }
4793
4848
  function increment() {
4794
4849
  setQuantityDraft(null);
@@ -5109,27 +5164,30 @@ function AddToCart(rawProps) {
5109
5164
  /* @__PURE__ */ jsxs("div", { className: "propeller-add-to-cart__modal-body px-6 py-5", children: [
5110
5165
  /* @__PURE__ */ jsxs("div", { className: "propeller-add-to-cart__modal-product flex items-start gap-4", children: [
5111
5166
  !!getModalImageUrl() ? /* @__PURE__ */ jsx(
5112
- "img",
5167
+ PropellerImg,
5113
5168
  {
5169
+ as: Img,
5114
5170
  className: "propeller-add-to-cart__modal-image w-16 h-16 object-contain rounded border border-border-subtle flex-shrink-0",
5115
5171
  src: getModalImageUrl(),
5116
- alt: getModalName()
5172
+ alt: getModalName(),
5173
+ loading: "lazy"
5117
5174
  }
5118
5175
  ) : null,
5119
5176
  !getModalImageUrl() ? /* @__PURE__ */ 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__ */ jsx(
5120
5177
  "svg",
5121
5178
  {
5179
+ "aria-hidden": "true",
5122
5180
  fill: "none",
5123
5181
  viewBox: "0 0 24 24",
5124
5182
  stroke: "currentColor",
5125
5183
  className: "w-8 h-8 text-foreground-subtle",
5126
- strokeWidth: 1.5,
5184
+ strokeWidth: 1,
5127
5185
  children: /* @__PURE__ */ jsx(
5128
5186
  "path",
5129
5187
  {
5130
5188
  strokeLinecap: "round",
5131
5189
  strokeLinejoin: "round",
5132
- 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"
5190
+ 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"
5133
5191
  }
5134
5192
  )
5135
5193
  }
@@ -6894,6 +6952,7 @@ function CartItem(rawProps) {
6894
6952
  companyId: props.companyId
6895
6953
  });
6896
6954
  const [quantity, setQuantity] = useState(() => props.cartItem.quantity || 1);
6955
+ const [quantityDraft, setQuantityDraft] = useState(() => null);
6897
6956
  const [notes, setNotes] = useState(() => props.cartItem.notes || "");
6898
6957
  const [loading, setLoading] = useState(() => false);
6899
6958
  const [deleting, setDeleting] = useState(() => false);
@@ -7017,6 +7076,34 @@ function CartItem(rawProps) {
7017
7076
  setLoading(false);
7018
7077
  }
7019
7078
  }
7079
+ function handleQuantityDecrement() {
7080
+ setQuantityDraft(null);
7081
+ const orderable = resolveOrderableQuantity(quantity, minQuantity, step);
7082
+ if (orderable !== quantity) {
7083
+ handleQuantityChange(orderable);
7084
+ return;
7085
+ }
7086
+ if (quantity - step >= minQuantity) handleQuantityChange(quantity - step);
7087
+ }
7088
+ function handleQuantityIncrement() {
7089
+ setQuantityDraft(null);
7090
+ const orderable = resolveOrderableQuantity(quantity, minQuantity, step);
7091
+ handleQuantityChange(orderable === quantity ? quantity + step : orderable);
7092
+ }
7093
+ function handleQuantityInput(raw) {
7094
+ setQuantityDraft(raw);
7095
+ const val = parseInt(raw, 10);
7096
+ if (!isNaN(val) && val >= minQuantity) {
7097
+ handleQuantityChange(resolveOrderableQuantity(val, minQuantity, step));
7098
+ }
7099
+ }
7100
+ function handleQuantityBlur() {
7101
+ if (quantityDraft === null) return;
7102
+ const val = parseInt(quantityDraft, 10);
7103
+ setQuantityDraft(null);
7104
+ const orderable = resolveOrderableQuantity(val, minQuantity, step);
7105
+ if (orderable !== quantity) handleQuantityChange(orderable);
7106
+ }
7020
7107
  function handleNoteChange(note) {
7021
7108
  setNotes(note);
7022
7109
  if (props.onNoteChange) {
@@ -7119,7 +7206,8 @@ function CartItem(rawProps) {
7119
7206
  deleting,
7120
7207
  crossupsells,
7121
7208
  visibleCrossupsells: getVisibleCrossupsells(),
7122
- addingCrossupsellId
7209
+ addingCrossupsellId,
7210
+ quantityDraft
7123
7211
  },
7124
7212
  helpers: {
7125
7213
  getBundleItemName,
@@ -7132,6 +7220,10 @@ function CartItem(rawProps) {
7132
7220
  },
7133
7221
  handlers: {
7134
7222
  onQuantityChange: handleQuantityChange,
7223
+ onQuantityDecrement: handleQuantityDecrement,
7224
+ onQuantityIncrement: handleQuantityIncrement,
7225
+ onQuantityInput: handleQuantityInput,
7226
+ onQuantityBlur: handleQuantityBlur,
7135
7227
  onNoteChange: handleNoteChange,
7136
7228
  onDelete: handleDelete,
7137
7229
  onTitleClick: (e) => props.onTitleClick?.(e, props.cartItem),
@@ -7189,29 +7281,33 @@ function CartItem(rawProps) {
7189
7281
  }
7190
7282
  function CartItemImage(props = {}) {
7191
7283
  const { derived } = useCartItemContext();
7284
+ const Img = useImgComponent();
7192
7285
  return /* @__PURE__ */ 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: [
7193
7286
  !!derived.imageUrl ? /* @__PURE__ */ jsx(
7194
- "img",
7287
+ PropellerImg,
7195
7288
  {
7289
+ as: Img,
7196
7290
  className: "propeller-cart-item__image w-full h-full object-contain p-1",
7197
7291
  src: derived.imageUrl,
7198
- alt: derived.name
7292
+ alt: derived.name,
7293
+ loading: "lazy"
7199
7294
  }
7200
7295
  ) : null,
7201
7296
  !derived.imageUrl ? /* @__PURE__ */ jsx(
7202
7297
  "svg",
7203
7298
  {
7299
+ "aria-hidden": "true",
7204
7300
  fill: "none",
7205
7301
  viewBox: "0 0 24 24",
7206
7302
  stroke: "currentColor",
7207
- className: "propeller-cart-item__image-placeholder w-8 h-8 text-foreground-subtle",
7208
- strokeWidth: 1.5,
7303
+ className: "propeller-cart-item__image-placeholder w-10 h-10 md:w-12 md:h-12 text-foreground-subtle",
7304
+ strokeWidth: 1,
7209
7305
  children: /* @__PURE__ */ jsx(
7210
7306
  "path",
7211
7307
  {
7212
7308
  strokeLinecap: "round",
7213
7309
  strokeLinejoin: "round",
7214
- 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"
7310
+ 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"
7215
7311
  }
7216
7312
  )
7217
7313
  }
@@ -7332,6 +7428,7 @@ function CartItemNotes(props = {}) {
7332
7428
  }
7333
7429
  function CartItemCrossupsells(props = {}) {
7334
7430
  const { resolved, state, helpers, handlers } = useCartItemContext();
7431
+ const Img = useImgComponent();
7335
7432
  if (state.visibleCrossupsells.length === 0) return null;
7336
7433
  return /* @__PURE__ */ jsxs("div", { className: props.className ?? "propeller-cart-item__crossupsells mt-3 pt-3 border-t border-border", children: [
7337
7434
  /* @__PURE__ */ jsx("p", { className: "propeller-cart-item__crossupsells-label text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2", children: getLabel(resolved.labels, "crossupsellTitle", "You might also like") }),
@@ -7348,11 +7445,13 @@ function CartItemCrossupsells(props = {}) {
7348
7445
  onClick: (e) => handlers.onCrossupsellClick(item, e),
7349
7446
  children: [
7350
7447
  !!helpers.getCrossupsellImageUrl(item) ? /* @__PURE__ */ jsx(
7351
- "img",
7448
+ PropellerImg,
7352
7449
  {
7450
+ as: Img,
7353
7451
  className: "propeller-cart-item__crossupsell-image w-10 h-10 object-contain rounded flex-shrink-0",
7354
7452
  src: helpers.getCrossupsellImageUrl(item),
7355
- alt: helpers.getCrossupsellName(item)
7453
+ alt: helpers.getCrossupsellName(item),
7454
+ loading: "lazy"
7356
7455
  }
7357
7456
  ) : null,
7358
7457
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
@@ -7439,7 +7538,7 @@ function CartItemQuantity(props = {}) {
7439
7538
  {
7440
7539
  type: "button",
7441
7540
  className: "propeller-cart-item__decrement px-2.5 h-full text-muted-foreground hover:bg-surface-hover disabled:opacity-40 disabled:cursor-not-allowed transition-colors rounded-l-control select-none",
7442
- onClick: () => handlers.onQuantityChange(derived.quantity - derived.step),
7541
+ onClick: handlers.onQuantityDecrement,
7443
7542
  disabled: derived.quantity <= derived.minQuantity || state.loading,
7444
7543
  children: "-"
7445
7544
  }
@@ -7451,13 +7550,9 @@ function CartItemQuantity(props = {}) {
7451
7550
  className: "propeller-cart-item__quantity w-10 text-center text-sm bg-transparent border-x border-input h-full focus:ring-0 focus:outline-none [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none",
7452
7551
  min: derived.minQuantity,
7453
7552
  step: derived.step,
7454
- value: derived.quantity,
7455
- onChange: (e) => {
7456
- const val = parseInt(e.target.value, 10);
7457
- if (!isNaN(val) && val >= derived.minQuantity) {
7458
- handlers.onQuantityChange(Math.round((val - derived.minQuantity) / derived.step) * derived.step + derived.minQuantity);
7459
- }
7460
- }
7553
+ value: state.quantityDraft ?? derived.quantity,
7554
+ onChange: (e) => handlers.onQuantityInput(e.target.value),
7555
+ onBlur: handlers.onQuantityBlur
7461
7556
  }
7462
7557
  ),
7463
7558
  /* @__PURE__ */ jsx(
@@ -7465,7 +7560,7 @@ function CartItemQuantity(props = {}) {
7465
7560
  {
7466
7561
  type: "button",
7467
7562
  className: "propeller-cart-item__increment px-2.5 h-full text-muted-foreground hover:bg-surface-hover disabled:opacity-40 disabled:cursor-not-allowed transition-colors rounded-r-control select-none",
7468
- onClick: () => handlers.onQuantityChange(derived.quantity + derived.step),
7563
+ onClick: handlers.onQuantityIncrement,
7469
7564
  disabled: state.loading,
7470
7565
  children: "+"
7471
7566
  }
@@ -7479,13 +7574,9 @@ function CartItemQuantity(props = {}) {
7479
7574
  className: props.className ?? "propeller-cart-item__quantity w-14 h-9 text-center text-sm border border-input rounded-control focus:ring-2 focus:ring-primary focus:border-transparent [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none",
7480
7575
  min: derived.minQuantity,
7481
7576
  step: derived.step,
7482
- value: derived.quantity,
7483
- onChange: (e) => {
7484
- const val = parseInt(e.target.value, 10);
7485
- if (!isNaN(val) && val >= derived.minQuantity) {
7486
- handlers.onQuantityChange(Math.round((val - derived.minQuantity) / derived.step) * derived.step + derived.minQuantity);
7487
- }
7488
- }
7577
+ value: state.quantityDraft ?? derived.quantity,
7578
+ onChange: (e) => handlers.onQuantityInput(e.target.value),
7579
+ onBlur: handlers.onQuantityBlur
7489
7580
  }
7490
7581
  );
7491
7582
  }
@@ -7781,7 +7872,7 @@ function CartIconAndSidebar(rawProps) {
7781
7872
  includeTax: useTax,
7782
7873
  className: "propeller-cart-icon__item",
7783
7874
  children: [
7784
- /* @__PURE__ */ jsx(CartItem_default.Image, { className: "w-20 h-20 flex-shrink-0 bg-surface-hover rounded-control overflow-hidden border border-border-subtle" }),
7875
+ /* @__PURE__ */ 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" }),
7785
7876
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 flex flex-col justify-between py-1", children: [
7786
7877
  /* @__PURE__ */ jsxs("div", { children: [
7787
7878
  /* @__PURE__ */ jsx(CartItem_default.Title, { className: "propeller-cart-icon__item-name text-sm font-medium hover:text-primary line-clamp-2" }),
@@ -8548,6 +8639,7 @@ function ClusterCardImage(props) {
8548
8639
  const showFavorite = props.showFavorite ?? !!resolved.enableAddFavorite;
8549
8640
  const isRow = derived.isRow;
8550
8641
  const InjectedImage = resolved.imageComponent;
8642
+ const Img = useImgComponent();
8551
8643
  if (InjectedImage) {
8552
8644
  return /* @__PURE__ */ jsx(
8553
8645
  InjectedImage,
@@ -8566,13 +8658,15 @@ function ClusterCardImage(props) {
8566
8658
  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"}`,
8567
8659
  children: [
8568
8660
  /* @__PURE__ */ jsx("a", { className: "block h-full w-full", href: derived.clusterUrl, onClick: handleClusterClick, children: derived.imageUrl ? /* @__PURE__ */ jsx(
8569
- "img",
8661
+ PropellerImg,
8570
8662
  {
8663
+ as: Img,
8571
8664
  className: "propeller-cluster-card__image h-full w-full object-contain transition-transform duration-300 group-hover:scale-105",
8572
8665
  src: derived.imageUrl,
8573
- alt: derived.name
8666
+ alt: derived.name,
8667
+ loading: "lazy"
8574
8668
  }
8575
- ) : /* @__PURE__ */ jsx("div", { className: "propeller-cluster-card__image-placeholder flex h-full w-full items-center justify-center text-foreground-subtle", children: /* @__PURE__ */ jsx("svg", { fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", className: "h-16 w-16", children: /* @__PURE__ */ jsx(
8669
+ ) : /* @__PURE__ */ jsx("div", { className: "propeller-cluster-card__image-placeholder flex h-full w-full items-center justify-center text-foreground-subtle", children: /* @__PURE__ */ jsx("svg", { "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", className: "h-16 w-16", children: /* @__PURE__ */ jsx(
8576
8670
  "path",
8577
8671
  {
8578
8672
  strokeLinecap: "round",
@@ -8790,6 +8884,7 @@ function getMachineImageUrl(machine) {
8790
8884
  }
8791
8885
  function MachineCard(props) {
8792
8886
  const { machine, href, language } = props;
8887
+ const Img = useImgComponent();
8793
8888
  const Wrapper = href ? "a" : "div";
8794
8889
  const showImage = props.showImage ?? true;
8795
8890
  const showDescription = props.showDescription ?? false;
@@ -8808,8 +8903,9 @@ function MachineCard(props) {
8808
8903
  className: "flex h-full flex-col focus:outline-none focus-visible:ring-2 focus-visible:ring-primary",
8809
8904
  children: [
8810
8905
  showImage && /* @__PURE__ */ jsx("div", { className: "propeller-machine-card__image aspect-square w-full overflow-hidden bg-background-subtle", children: imageUrl ? /* @__PURE__ */ jsx(
8811
- "img",
8906
+ PropellerImg,
8812
8907
  {
8908
+ as: Img,
8813
8909
  src: imageUrl,
8814
8910
  alt: name,
8815
8911
  loading: "lazy",
@@ -8826,6 +8922,7 @@ function MachineCard(props) {
8826
8922
  children: /* @__PURE__ */ jsx(
8827
8923
  "svg",
8828
8924
  {
8925
+ "aria-hidden": "true",
8829
8926
  className: "h-12 w-12",
8830
8927
  fill: "none",
8831
8928
  stroke: "currentColor",
@@ -9130,6 +9227,7 @@ function ProductCardImage(props) {
9130
9227
  const showFavorite = props.showFavorite ?? !!resolved.enableAddFavorite;
9131
9228
  const isRow = derived.isRow;
9132
9229
  const InjectedImage = resolved.imageComponent;
9230
+ const Img = useImgComponent();
9133
9231
  if (InjectedImage) {
9134
9232
  return /* @__PURE__ */ jsx(
9135
9233
  InjectedImage,
@@ -9148,13 +9246,15 @@ function ProductCardImage(props) {
9148
9246
  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"}`,
9149
9247
  children: [
9150
9248
  /* @__PURE__ */ jsx("a", { className: "block h-full w-full", href: derived.productUrl, onClick: handleProductClick, children: derived.imageUrl ? /* @__PURE__ */ jsx(
9151
- "img",
9249
+ PropellerImg,
9152
9250
  {
9251
+ as: Img,
9153
9252
  className: "propeller-product-card__image h-full w-full object-contain transition-transform duration-300 group-hover:scale-105",
9154
9253
  src: derived.imageUrl,
9155
- alt: derived.name
9254
+ alt: derived.name,
9255
+ loading: "lazy"
9156
9256
  }
9157
- ) : /* @__PURE__ */ jsx("div", { className: "propeller-product-card__image-placeholder flex h-full w-full items-center justify-center text-foreground-subtle", children: /* @__PURE__ */ jsx("svg", { fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", className: "h-16 w-16", children: /* @__PURE__ */ jsx(
9257
+ ) : /* @__PURE__ */ jsx("div", { className: "propeller-product-card__image-placeholder flex h-full w-full items-center justify-center text-foreground-subtle", children: /* @__PURE__ */ jsx("svg", { "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", className: "h-16 w-16", children: /* @__PURE__ */ jsx(
9158
9258
  "path",
9159
9259
  {
9160
9260
  strokeLinecap: "round",
@@ -11009,6 +11109,7 @@ function MachinePartsSearch(props) {
11009
11109
  ] });
11010
11110
  }
11011
11111
  function ClusterConfigurator(props) {
11112
+ const Img = useImgComponent();
11012
11113
  const { settingsWithValues, handleAttributeSelect, initFromProduct } = useClusterConfigurator({
11013
11114
  products: props.products,
11014
11115
  config: props.config,
@@ -11099,7 +11200,7 @@ function ClusterConfigurator(props) {
11099
11200
  "data-selected": setting.selectedValue === val ? "true" : "false",
11100
11201
  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"}`,
11101
11202
  children: [
11102
- /* @__PURE__ */ jsx("img", { className: "propeller-cluster-configurator__image w-full h-full object-cover", src: val, alt: val }),
11203
+ /* @__PURE__ */ jsx(PropellerImg, { as: Img, className: "propeller-cluster-configurator__image w-full h-full object-cover", src: val, alt: val, loading: "lazy" }),
11103
11204
  setting.selectedValue === val ? /* @__PURE__ */ jsx("div", { className: "propeller-cluster-configurator__image-check absolute inset-0 bg-secondary bg-opacity-20 flex items-center justify-center", children: /* @__PURE__ */ jsx(
11104
11205
  "svg",
11105
11206
  {
@@ -11199,6 +11300,7 @@ function getProductImageUrl4(product) {
11199
11300
  }
11200
11301
  function ClusterOptions(rawProps) {
11201
11302
  const props = useInfraProps(rawProps);
11303
+ const Img = useImgComponent();
11202
11304
  const [selectedProductIds, setSelectedProductIds] = useState({});
11203
11305
  function getOptionsForRender() {
11204
11306
  const language = props.language;
@@ -11285,20 +11387,23 @@ function ClusterOptions(rawProps) {
11285
11387
  option.hasError ? /* @__PURE__ */ jsx("p", { className: "propeller-cluster-options__error mt-1 text-xs text-destructive", children: getLabel(props.labels, "requiredError", "This option is required") }) : null,
11286
11388
  option.hasSelection ? /* @__PURE__ */ 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: [
11287
11389
  !!option.previewImageUrl ? /* @__PURE__ */ jsx(
11288
- "img",
11390
+ PropellerImg,
11289
11391
  {
11392
+ as: Img,
11290
11393
  className: "propeller-cluster-options__preview-image h-12 w-12 flex-shrink-0 rounded border border-border-subtle bg-card object-contain",
11291
11394
  src: option.previewImageUrl,
11292
- alt: option.previewName
11395
+ alt: option.previewName,
11396
+ loading: "lazy"
11293
11397
  }
11294
11398
  ) : null,
11295
11399
  !option.previewImageUrl ? /* @__PURE__ */ 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__ */ jsx(
11296
11400
  "svg",
11297
11401
  {
11402
+ "aria-hidden": "true",
11298
11403
  fill: "none",
11299
11404
  stroke: "currentColor",
11300
11405
  viewBox: "0 0 24 24",
11301
- className: "h-5 w-5 text-foreground-subtle",
11406
+ className: "h-6 w-6 text-foreground-subtle",
11302
11407
  children: /* @__PURE__ */ jsx(
11303
11408
  "path",
11304
11409
  {
@@ -11782,19 +11887,22 @@ function FavoriteListItem(rawProps) {
11782
11887
  }
11783
11888
  function FavoriteListItemImage(props) {
11784
11889
  const { derived, handleItemClick, resolved } = useFavoriteListItemContext();
11890
+ const Img = useImgComponent();
11785
11891
  const { imageUrl, name, productUrl } = derived;
11786
11892
  const linkable = resolved.titleLinkable !== false;
11787
11893
  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";
11788
11894
  const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
11789
11895
  !!imageUrl ? /* @__PURE__ */ jsx(
11790
- "img",
11896
+ PropellerImg,
11791
11897
  {
11898
+ as: Img,
11792
11899
  className: "propeller-favorite-list-item__image h-full w-full object-contain",
11793
11900
  src: imageUrl,
11794
- alt: name
11901
+ alt: name,
11902
+ loading: "lazy"
11795
11903
  }
11796
11904
  ) : null,
11797
- !imageUrl ? /* @__PURE__ */ jsx("div", { className: "propeller-favorite-list-item__image-placeholder flex h-full w-full items-center justify-center text-foreground-subtle", children: /* @__PURE__ */ jsx("svg", { fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", className: "h-8 w-8", children: /* @__PURE__ */ jsx(
11905
+ !imageUrl ? /* @__PURE__ */ jsx("div", { className: "propeller-favorite-list-item__image-placeholder flex h-full w-full items-center justify-center text-foreground-subtle", children: /* @__PURE__ */ jsx("svg", { "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", className: "h-10 w-10", children: /* @__PURE__ */ jsx(
11798
11906
  "path",
11799
11907
  {
11800
11908
  strokeLinecap: "round",
@@ -11992,6 +12100,7 @@ FavoriteListItemExport.Actions = FavoriteListItemActions;
11992
12100
  var FavoriteListItem_default = FavoriteListItemExport;
11993
12101
  function FavoriteListDetails(rawProps) {
11994
12102
  const props = useInfraProps(rawProps);
12103
+ const Img = useImgComponent();
11995
12104
  const FavoriteListItemImpl = props.favoriteListItemComponent ?? FavoriteListItem_default;
11996
12105
  const GridPaginationImpl = props.gridPaginationComponent ?? GridPagination_default;
11997
12106
  const { addToList } = useFavorites({
@@ -12573,11 +12682,13 @@ function FavoriteListDetails(rawProps) {
12573
12682
  "data-adding": isAdding ? "true" : "false",
12574
12683
  children: [
12575
12684
  /* @__PURE__ */ 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__ */ jsx(
12576
- "img",
12685
+ PropellerImg,
12577
12686
  {
12687
+ as: Img,
12578
12688
  src: getSearchItemImage(item),
12579
12689
  alt: getSearchItemName(item),
12580
- className: "h-full w-full object-contain"
12690
+ className: "h-full w-full object-contain",
12691
+ loading: "lazy"
12581
12692
  }
12582
12693
  ) : /* @__PURE__ */ jsxs(
12583
12694
  "svg",
@@ -13285,6 +13396,7 @@ function getBundleNonLeaders(item) {
13285
13396
  }
13286
13397
  function ItemsOverview(rawProps) {
13287
13398
  const props = useInfraProps(rawProps);
13399
+ const Img = useImgComponent();
13288
13400
  const language = props.language;
13289
13401
  const useTax = !!props.includeTax;
13290
13402
  const containerClass = props.itemsOverviewContainerClass || "cart-items-overview";
@@ -13343,26 +13455,29 @@ function ItemsOverview(rawProps) {
13343
13455
  "data-bundle": isBundle ? "true" : "false",
13344
13456
  children: [
13345
13457
  showImage ? /* @__PURE__ */ 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__ */ jsx(
13346
- "img",
13458
+ PropellerImg,
13347
13459
  {
13460
+ as: Img,
13348
13461
  className: "propeller-items-overview__item-image w-full h-full object-contain p-1.5",
13349
13462
  src: getItemImageUrl(item),
13350
- alt: getItemName(item, language)
13463
+ alt: getItemName(item, language),
13464
+ loading: "lazy"
13351
13465
  }
13352
13466
  ) : /* @__PURE__ */ jsx(
13353
13467
  "svg",
13354
13468
  {
13469
+ "aria-hidden": "true",
13355
13470
  fill: "none",
13356
13471
  viewBox: "0 0 24 24",
13357
13472
  stroke: "currentColor",
13358
- strokeWidth: "1.5",
13359
- className: "propeller-items-overview__item-image-placeholder w-6 h-6 text-foreground-subtle",
13473
+ strokeWidth: "1",
13474
+ className: "propeller-items-overview__item-image-placeholder w-8 h-8 text-foreground-subtle",
13360
13475
  children: /* @__PURE__ */ jsx(
13361
13476
  "path",
13362
13477
  {
13363
13478
  strokeLinecap: "round",
13364
13479
  strokeLinejoin: "round",
13365
- 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"
13480
+ 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"
13366
13481
  }
13367
13482
  )
13368
13483
  }
@@ -13986,11 +14101,13 @@ function OrderItemCard(props) {
13986
14101
  className: `propeller-order-item-card__cell propeller-order-item-card__cell--product ${isChildItem ? "px-6 py-2 pl-28" : "px-6 py-4"}`,
13987
14102
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
13988
14103
  showImage ? productImage ? /* @__PURE__ */ jsx("div", { className: "propeller-order-item-card__media relative w-16 h-16 flex-shrink-0 rounded overflow-hidden", children: /* @__PURE__ */ jsx(
13989
- "img",
14104
+ PropellerImg,
13990
14105
  {
14106
+ as: props.imgComponent,
13991
14107
  className: "propeller-order-item-card__image object-cover w-full h-full",
13992
14108
  src: productImage,
13993
- alt: productName
14109
+ alt: productName,
14110
+ loading: "lazy"
13994
14111
  }
13995
14112
  ) }) : /* @__PURE__ */ 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: getLabel(props.labels, "noImage", "No Img") }) : null,
13996
14113
  /* @__PURE__ */ jsxs("div", { children: [
@@ -14717,10 +14834,6 @@ function RequestPriceButton(props) {
14717
14834
  }
14718
14835
  var RequestPriceButton_default = RequestPriceButton;
14719
14836
  var ROW_SEQ = 0;
14720
- function snapToStep(value, min, step) {
14721
- if (!Number.isFinite(value) || value <= min) return min;
14722
- return Math.round((value - min) / (step || 1)) * (step || 1) + min;
14723
- }
14724
14837
  function blankRow() {
14725
14838
  ROW_SEQ += 1;
14726
14839
  return {
@@ -14757,6 +14870,7 @@ function QuickOrderInner(props) {
14757
14870
  className,
14758
14871
  labels
14759
14872
  } = props;
14873
+ const Img = useImgComponent();
14760
14874
  const user = props.user ?? null;
14761
14875
  const graphqlClient = props.graphqlClient;
14762
14876
  const displayPrice = (n) => props.formatPrice ? props.formatPrice(n) : formatPrice(n, { symbol: currency, locale: localeForLanguage(props.language) });
@@ -14839,7 +14953,7 @@ function QuickOrderInner(props) {
14839
14953
  const n = parseInt(raw, 10);
14840
14954
  setRows(
14841
14955
  (prev) => prev.map(
14842
- (r) => r.key === key ? { ...r, quantity: snapToStep(n, r.minQuantity, r.step) } : r
14956
+ (r) => r.key === key ? { ...r, quantity: resolveOrderableQuantity(n, r.minQuantity, r.step) } : r
14843
14957
  )
14844
14958
  );
14845
14959
  },
@@ -14988,7 +15102,7 @@ function QuickOrderInner(props) {
14988
15102
  children: [
14989
15103
  m.imageUrl ? (
14990
15104
  // eslint-disable-next-line @next/next/no-img-element
14991
- /* @__PURE__ */ jsx("img", { src: m.imageUrl, alt: "", width: 32, height: 32, className: "rounded object-cover" })
15105
+ /* @__PURE__ */ jsx(PropellerImg, { as: Img, src: m.imageUrl, alt: "", width: 32, height: 32, className: "rounded object-cover", loading: "lazy" })
14992
15106
  ) : null,
14993
15107
  /* @__PURE__ */ jsxs("span", { className: "flex-1", children: [
14994
15108
  /* @__PURE__ */ jsx("span", { className: "block text-foreground", children: m.name }),
@@ -15653,6 +15767,7 @@ function getProductName4(product, language) {
15653
15767
  }
15654
15768
  function ProductBundles(rawProps) {
15655
15769
  const props = useInfraProps(rawProps);
15770
+ const Img = useImgComponent();
15656
15771
  const language = props.language;
15657
15772
  const currencySymbol = props.currency ?? "\u20AC";
15658
15773
  const money = (value) => formatPrice(value, { symbol: currencySymbol, locale: localeForLanguage(props.language) });
@@ -15788,11 +15903,13 @@ function ProductBundles(rawProps) {
15788
15903
  ) }) : null,
15789
15904
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center text-center w-40", children: [
15790
15905
  /* @__PURE__ */ 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__ */ jsx(
15791
- "img",
15906
+ PropellerImg,
15792
15907
  {
15908
+ as: Img,
15793
15909
  className: "propeller-product-bundles__item-image w-full h-full object-contain p-2",
15794
15910
  src: getProductImage(item.product),
15795
- alt: getProductName4(item.product, language)
15911
+ alt: getProductName4(item.product, language),
15912
+ loading: "lazy"
15796
15913
  }
15797
15914
  ) : null }),
15798
15915
  /* @__PURE__ */ 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 }),
@@ -15957,26 +16074,29 @@ function ProductBundles(rawProps) {
15957
16074
  /* @__PURE__ */ jsxs("div", { className: "propeller-product-bundles__modal-body px-6 py-5", children: [
15958
16075
  /* @__PURE__ */ jsxs("div", { className: "propeller-product-bundles__modal-product flex items-start gap-4", children: [
15959
16076
  lastAddedBundle && lastAddedBundle.items && lastAddedBundle.items.length > 0 && getProductImage(lastAddedBundle.items[0].product) ? /* @__PURE__ */ jsx(
15960
- "img",
16077
+ PropellerImg,
15961
16078
  {
16079
+ as: Img,
15962
16080
  className: "propeller-product-bundles__modal-image w-16 h-16 object-contain rounded border border-border-subtle flex-shrink-0",
15963
16081
  src: lastAddedBundle?.items?.[0] ? getProductImage(lastAddedBundle.items[0].product) : "",
15964
- alt: lastAddedBundle?.name || "Bundle"
16082
+ alt: lastAddedBundle?.name || "Bundle",
16083
+ loading: "lazy"
15965
16084
  }
15966
16085
  ) : /* @__PURE__ */ 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__ */ jsx(
15967
16086
  "svg",
15968
16087
  {
16088
+ "aria-hidden": "true",
15969
16089
  fill: "none",
15970
16090
  viewBox: "0 0 24 24",
15971
16091
  stroke: "currentColor",
15972
16092
  className: "w-8 h-8 text-foreground-subtle",
15973
- strokeWidth: 1.5,
16093
+ strokeWidth: 1,
15974
16094
  children: /* @__PURE__ */ jsx(
15975
16095
  "path",
15976
16096
  {
15977
16097
  strokeLinecap: "round",
15978
16098
  strokeLinejoin: "round",
15979
- 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"
16099
+ 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"
15980
16100
  }
15981
16101
  )
15982
16102
  }
@@ -16141,6 +16261,7 @@ function ProductDownloads(props) {
16141
16261
  }
16142
16262
  var ProductDownloads_default = ProductDownloads;
16143
16263
  function ProductGallery(props) {
16264
+ const Img = useImgComponent();
16144
16265
  const [selectedIndex, setSelectedIndex] = useState(0);
16145
16266
  const [lightboxOpen, setLightboxOpen] = useState(false);
16146
16267
  const images = props.images || [];
@@ -16178,11 +16299,13 @@ function ProductGallery(props) {
16178
16299
  }
16179
16300
  ) }) : null,
16180
16301
  images.length > 0 ? /* @__PURE__ */ jsx(
16181
- "img",
16302
+ PropellerImg,
16182
16303
  {
16304
+ as: Img,
16183
16305
  alt: getLabel(props.labels, "productImageAlt", "Product image"),
16184
16306
  src: mainImage,
16185
16307
  onClick: openLightbox,
16308
+ loading: "eager",
16186
16309
  className: `h-full w-full object-contain p-8 transition-transform duration-200 ${props.enableZoom !== false ? "cursor-zoom-in hover:scale-105" : ""}`
16187
16310
  }
16188
16311
  ) : null
@@ -16194,11 +16317,13 @@ function ProductGallery(props) {
16194
16317
  onClick: () => setSelectedIndex(index),
16195
16318
  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"}`,
16196
16319
  children: /* @__PURE__ */ jsx(
16197
- "img",
16320
+ PropellerImg,
16198
16321
  {
16322
+ as: Img,
16199
16323
  className: "w-full h-full object-contain p-1",
16200
16324
  src: img,
16201
- alt: `${getLabel(props.labels, "productImageAlt", "Product image")} ${index + 1}`
16325
+ alt: `${getLabel(props.labels, "productImageAlt", "Product image")} ${index + 1}`,
16326
+ loading: "lazy"
16202
16327
  }
16203
16328
  )
16204
16329
  },
@@ -16253,12 +16378,13 @@ function ProductGallery(props) {
16253
16378
  }
16254
16379
  ) : null,
16255
16380
  /* @__PURE__ */ jsx(
16256
- "img",
16381
+ PropellerImg,
16257
16382
  {
16383
+ as: Img,
16258
16384
  alt: getLabel(props.labels, "productImageFullscreenAlt", "Product image fullscreen"),
16259
16385
  className: "max-h-full max-w-full object-contain rounded-lg",
16260
16386
  src: mainImage,
16261
- onClick: (e) => e.stopPropagation()
16387
+ onClick: (e) => e.stopPropagation?.()
16262
16388
  }
16263
16389
  ),
16264
16390
  hasThumbnails ? /* @__PURE__ */ jsx(
@@ -16312,6 +16438,7 @@ function pickImageUrl(mediaItems, target) {
16312
16438
  }
16313
16439
  function DefaultProductImage(props) {
16314
16440
  const { product, cluster, className } = props;
16441
+ const Img = useImgComponent();
16315
16442
  const targetLanguage = props.language ?? "NL";
16316
16443
  const mediaItems = (() => {
16317
16444
  const target = product ?? cluster?.defaultProduct ?? cluster;
@@ -16346,8 +16473,9 @@ function DefaultProductImage(props) {
16346
16473
  );
16347
16474
  }
16348
16475
  return /* @__PURE__ */ jsx(
16349
- "img",
16476
+ PropellerImg,
16350
16477
  {
16478
+ as: Img,
16351
16479
  src: url,
16352
16480
  alt: altText,
16353
16481
  className: className ?? "h-full w-full object-contain",
@@ -19256,6 +19384,7 @@ function mapToSearchBarResult(item, language, urls) {
19256
19384
  }
19257
19385
  function SearchBar(rawProps) {
19258
19386
  const props = useInfraProps(rawProps);
19387
+ const Img = useImgComponent();
19259
19388
  const [showDropdown, setShowDropdown] = useState(false);
19260
19389
  const [localTerm, setLocalTerm] = useState("");
19261
19390
  const containerRef = useRef(null);
@@ -19421,11 +19550,13 @@ function SearchBar(rawProps) {
19421
19550
  ...resultProps,
19422
19551
  children: [
19423
19552
  result.imageUrl || noImageUrl() ? /* @__PURE__ */ jsx("div", { className: "propeller-search-bar__result-media relative w-16 h-16 flex-shrink-0", children: /* @__PURE__ */ jsx(
19424
- "img",
19553
+ PropellerImg,
19425
19554
  {
19555
+ as: Img,
19426
19556
  className: "propeller-search-bar__result-image w-full h-full object-contain",
19427
19557
  src: result.imageUrl || noImageUrl(),
19428
- alt: result.name
19558
+ alt: result.name,
19559
+ loading: "lazy"
19429
19560
  }
19430
19561
  ) }) : null,
19431
19562
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
@@ -19589,6 +19720,6 @@ function UserDetails(rawProps) {
19589
19720
  }
19590
19721
  var UserDetails_default = UserDetails;
19591
19722
 
19592
- export { AccountIconAndMenu_default as AccountIconAndMenu, ActionCode_default as ActionCode, AddToCart_default as AddToCart, AddToFavorite_default as AddToFavorite, AddressCard_default as AddressCard, AddressSelector_default as AddressSelector, BreadcrumbsWithProvider_default as Breadcrumbs, CartBonusItems_default as CartBonusItems, CartCarriers_default as CartCarriers, CartIconAndSidebar_default as CartIconAndSidebar, CartItem_default as CartItem, CartOverview_default as CartOverview, CartPaymethods_default as CartPaymethods, CartSummary_default as CartSummary, CategoryDescription_default as CategoryDescription, CategoryShortDescriptionWithProvider_default as CategoryShortDescription, ClusterCard_default as ClusterCard, ClusterConfigurator_default as ClusterConfigurator, ClusterInfo_default as ClusterInfo, ClusterOptions_default as ClusterOptions, CompanySwitcher_default as CompanySwitcher, AddToCart_default as DefaultAddToCart, AddToFavorite_default as DefaultAddToFavorite, ItemStock_default as DefaultItemStock, DefaultProductBadges, ProductBulkPrices_default as DefaultProductBulkPrices, ProductBundles_default as DefaultProductBundles, DefaultProductImage, ProductPrice_default as DefaultProductPrice, DefaultProductSurcharges, DeliveryDate_default as DeliveryDate, FavoriteListDetails_default as FavoriteListDetails, FavoriteListItem_default as FavoriteListItem, FavoriteLists_default as FavoriteLists, ForgotPassword_default as ForgotPassword, GridFilters_default as GridFilters, GridFiltersPanel, GridPagination_default as GridPagination, GridTitleWithProvider_default as GridTitle, GridToolbar_default as GridToolbar, ItemStock_default as ItemStock, ItemsOverview_default as ItemsOverview, LoginForm_default as LoginForm, LoginToOrderButton_default as LoginToOrderButton, MachineCard, MachineGrid, Menu_default as Menu, OrderActions_default as OrderActions, OrderBonusItems_default as OrderBonusItems, OrderItemCard_default as OrderItemCard, OrderList_default as OrderList, OrderShipments_default as OrderShipments, OrderSummaryWithProvider_default as OrderSummary, OrderTotalsWithProvider_default as OrderTotals, PRICE_REQUEST_STORAGE_KEY, PriceRequestList_default as PriceRequestList, PriceToggle_default as PriceToggle, ProductBulkPricesWithProvider_default as ProductBulkPrices, ProductBundles_default as ProductBundles, ProductCard_default as ProductCard, ProductDescription_default as ProductDescription, ProductDownloads_default as ProductDownloads, ProductGallery_default as ProductGallery, ProductGrid_default as ProductGrid, ProductGridConfigProvider, ProductInfo_default as ProductInfo, ProductPriceWithProvider_default as ProductPrice, ProductShortDescriptionWithProvider_default as ProductShortDescription, ProductSlider_default as ProductSlider, ProductSpecifications_default as ProductSpecifications, ProductTabs_default as ProductTabs, ProductVideos_default as ProductVideos, PropellerDepsProvider, PropellerProvider, PurchaseAuthorizationConfigurator_default as PurchaseAuthorizationConfigurator, PurchaseAuthorizationRequests_default as PurchaseAuthorizationRequests, QuickOrder, QuoteActions_default as QuoteActions, RegisterForm_default as RegisterForm, RequestPriceButton_default as RequestPriceButton, SearchBar_default as SearchBar, UserDetails_default as UserDetails, addItem as addPriceRequestItem, buildRootMachinesQuery, fetchActiveCart, initCart, isPriceOnRequest, mergeAnonymousCart, containsItem as priceRequestContains, removeItem as removePriceRequestItem, setItemQuantity as setPriceRequestItemQuantity, useAddress, useAuth, useCart, useCheckout, useClusterConfigurator, useCompany, useFavorites, useInfraProps, useMachines, useMenu, useOrders, usePriceRequest, useProductBundles, useProductGridConfig, useProductInfo, useProductSearch, useProductSlider, useProductSpecs, usePropellerContext, usePropellerDeps, usePropellerScope, usePurchaseAuthorizationConfigurator, usePurchaseAuthorizationRequests, useQuickOrder, useRequiredPropellerDeps, useResolvedProps, useServices, useSpareParts, useUserMode };
19723
+ export { AccountIconAndMenu_default as AccountIconAndMenu, ActionCode_default as ActionCode, AddToCart_default as AddToCart, AddToFavorite_default as AddToFavorite, AddressCard_default as AddressCard, AddressSelector_default as AddressSelector, BreadcrumbsWithProvider_default as Breadcrumbs, CartBonusItems_default as CartBonusItems, CartCarriers_default as CartCarriers, CartIconAndSidebar_default as CartIconAndSidebar, CartItem_default as CartItem, CartOverview_default as CartOverview, CartPaymethods_default as CartPaymethods, CartSummary_default as CartSummary, CategoryDescription_default as CategoryDescription, CategoryShortDescriptionWithProvider_default as CategoryShortDescription, ClusterCard_default as ClusterCard, ClusterConfigurator_default as ClusterConfigurator, ClusterInfo_default as ClusterInfo, ClusterOptions_default as ClusterOptions, CompanySwitcher_default as CompanySwitcher, AddToCart_default as DefaultAddToCart, AddToFavorite_default as DefaultAddToFavorite, ItemStock_default as DefaultItemStock, DefaultProductBadges, ProductBulkPrices_default as DefaultProductBulkPrices, ProductBundles_default as DefaultProductBundles, DefaultProductImage, ProductPrice_default as DefaultProductPrice, DefaultProductSurcharges, DeliveryDate_default as DeliveryDate, FavoriteListDetails_default as FavoriteListDetails, FavoriteListItem_default as FavoriteListItem, FavoriteLists_default as FavoriteLists, ForgotPassword_default as ForgotPassword, GridFilters_default as GridFilters, GridFiltersPanel, GridPagination_default as GridPagination, GridTitleWithProvider_default as GridTitle, GridToolbar_default as GridToolbar, ItemStock_default as ItemStock, ItemsOverview_default as ItemsOverview, LoginForm_default as LoginForm, LoginToOrderButton_default as LoginToOrderButton, MachineCard, MachineGrid, Menu_default as Menu, OrderActions_default as OrderActions, OrderBonusItems_default as OrderBonusItems, OrderItemCard_default as OrderItemCard, OrderList_default as OrderList, OrderShipments_default as OrderShipments, OrderSummaryWithProvider_default as OrderSummary, OrderTotalsWithProvider_default as OrderTotals, PRICE_REQUEST_STORAGE_KEY, PriceRequestList_default as PriceRequestList, PriceToggle_default as PriceToggle, ProductBulkPricesWithProvider_default as ProductBulkPrices, ProductBundles_default as ProductBundles, ProductCard_default as ProductCard, ProductDescription_default as ProductDescription, ProductDownloads_default as ProductDownloads, ProductGallery_default as ProductGallery, ProductGrid_default as ProductGrid, ProductGridConfigProvider, ProductInfo_default as ProductInfo, ProductPriceWithProvider_default as ProductPrice, ProductShortDescriptionWithProvider_default as ProductShortDescription, ProductSlider_default as ProductSlider, ProductSpecifications_default as ProductSpecifications, ProductTabs_default as ProductTabs, ProductVideos_default as ProductVideos, PropellerDepsProvider, PropellerImg, PropellerProvider, PurchaseAuthorizationConfigurator_default as PurchaseAuthorizationConfigurator, PurchaseAuthorizationRequests_default as PurchaseAuthorizationRequests, QuickOrder, QuoteActions_default as QuoteActions, RegisterForm_default as RegisterForm, RequestPriceButton_default as RequestPriceButton, SearchBar_default as SearchBar, UserDetails_default as UserDetails, addItem as addPriceRequestItem, buildRootMachinesQuery, fetchActiveCart, initCart, isPriceOnRequest, mergeAnonymousCart, containsItem as priceRequestContains, removeItem as removePriceRequestItem, setItemQuantity as setPriceRequestItemQuantity, useAddress, useAuth, useCart, useCheckout, useClusterConfigurator, useCompany, useFavorites, useImgComponent, useInfraProps, useMachines, useMenu, useOrders, usePriceRequest, useProductBundles, useProductGridConfig, useProductInfo, useProductSearch, useProductSlider, useProductSpecs, usePropellerContext, usePropellerDeps, usePropellerScope, usePurchaseAuthorizationConfigurator, usePurchaseAuthorizationRequests, useQuickOrder, useRequiredPropellerDeps, useResolvedProps, useServices, useSpareParts, useUserMode };
19593
19724
  //# sourceMappingURL=index.js.map
19594
19725
  //# sourceMappingURL=index.js.map