@propeller-commerce/propeller-v2-react-ui 0.15.5 → 0.15.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -3536,6 +3536,8 @@ interface CategoryDescriptionProps {
3536
3536
  maxLength?: number;
3537
3537
  /** Extra CSS class applied to the root element. */
3538
3538
  className?: string;
3539
+ /** Translated labels: `readMore`, `readLess`. */
3540
+ labels?: Record<string, string>;
3539
3541
  }
3540
3542
  /**
3541
3543
  * Renders a category's localised HTML description, optionally truncated with a
@@ -5217,11 +5219,12 @@ interface OrderBonusItemsProps {
5217
5219
  * Renders an order's bonus items — free items added through incentives — as a
5218
5220
  * read-only "Bonus items" section (heading + table of OrderItemCard rows).
5219
5221
  *
5220
- * Bonus items are order items of `class === 'product'` (the free goods) with
5221
- * `isBonus === 'Y'` — whether standalone or grouped by the backend under an
5222
- * `incentive` item via `parentOrderItemId`. Any backend-side grouping is
5223
- * purely presentational; in the flat SDK item list every bonus good is
5224
- * already `product`/`isBonus === Y`, so filtering on that yields the same set.
5222
+ * Bonus items are order items of `class === 'product'` with `isBonus === 'Y'`.
5223
+ * The API models the discount as a separate sibling `incentive` item that
5224
+ * carries the negative delta and points back via `parentOrderItemId`, so the
5225
+ * product line alone still shows the undiscounted price. Each bonus line is
5226
+ * therefore netted against its incentive siblings before display — a fully
5227
+ * discounted item reaches 0, a partially discounted one keeps the remainder.
5225
5228
  *
5226
5229
  * Renders nothing when there are no bonus items, so it's safe to drop into any
5227
5230
  * order surface (thank-you, order details, quote details) unconditionally.
@@ -6083,6 +6086,8 @@ interface ProductDescriptionProps {
6083
6086
  maxLength?: number;
6084
6087
  /** Extra CSS class applied to the root element. */
6085
6088
  className?: string;
6089
+ /** Translated labels: `readMore`, `readLess`. */
6090
+ labels?: Record<string, string>;
6086
6091
  }
6087
6092
  /**
6088
6093
  * Renders a product's localised HTML description, optionally collapsed to a
package/dist/index.d.ts CHANGED
@@ -3536,6 +3536,8 @@ interface CategoryDescriptionProps {
3536
3536
  maxLength?: number;
3537
3537
  /** Extra CSS class applied to the root element. */
3538
3538
  className?: string;
3539
+ /** Translated labels: `readMore`, `readLess`. */
3540
+ labels?: Record<string, string>;
3539
3541
  }
3540
3542
  /**
3541
3543
  * Renders a category's localised HTML description, optionally truncated with a
@@ -5217,11 +5219,12 @@ interface OrderBonusItemsProps {
5217
5219
  * Renders an order's bonus items — free items added through incentives — as a
5218
5220
  * read-only "Bonus items" section (heading + table of OrderItemCard rows).
5219
5221
  *
5220
- * Bonus items are order items of `class === 'product'` (the free goods) with
5221
- * `isBonus === 'Y'` — whether standalone or grouped by the backend under an
5222
- * `incentive` item via `parentOrderItemId`. Any backend-side grouping is
5223
- * purely presentational; in the flat SDK item list every bonus good is
5224
- * already `product`/`isBonus === Y`, so filtering on that yields the same set.
5222
+ * Bonus items are order items of `class === 'product'` with `isBonus === 'Y'`.
5223
+ * The API models the discount as a separate sibling `incentive` item that
5224
+ * carries the negative delta and points back via `parentOrderItemId`, so the
5225
+ * product line alone still shows the undiscounted price. Each bonus line is
5226
+ * therefore netted against its incentive siblings before display — a fully
5227
+ * discounted item reaches 0, a partially discounted one keeps the remainder.
5225
5228
  *
5226
5229
  * Renders nothing when there are no bonus items, so it's safe to drop into any
5227
5230
  * order surface (thank-you, order details, quote details) unconditionally.
@@ -6083,6 +6086,8 @@ interface ProductDescriptionProps {
6083
6086
  maxLength?: number;
6084
6087
  /** Extra CSS class applied to the root element. */
6085
6088
  className?: string;
6089
+ /** Translated labels: `readMore`, `readLess`. */
6090
+ labels?: Record<string, string>;
6086
6091
  }
6087
6092
  /**
6088
6093
  * Renders a product's localised HTML description, optionally collapsed to a
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { getLabel, getLocalizedValue, formatPrice, getClusterSku, getClusterImageUrl, isContentHidden, getProductSku, getProductImageUrl, createServices, err, ok, formatSurcharge, deriveUserMode, isContact, isCustomer, getAddresses, attributeNameMatches, getAttributeDisplayName, filterProductsBySelections, extractAttributeValues, buildInventoryFilter, MIN_STOCK_THRESHOLD, getLanguageString, collectAttributeValues, getCountryName } from '@propeller-commerce/propeller-v2-core-ui';
2
+ import { getLabel, getLocalizedValue, formatPrice, getClusterSku, getClusterImageUrl, isContentHidden, getProductSku, getProductImageUrl, createServices, err, ok, formatSurcharge, deriveUserMode, isContact, isCustomer, getAddresses, attributeNameMatches, getAttributeDisplayName, filterProductsBySelections, extractAttributeValues, buildInventoryFilter, MIN_STOCK_THRESHOLD, getNettedBonusItems, getLanguageString, collectAttributeValues, getCountryName } 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
5
  import { createContext, useState, useRef, useMemo, useCallback, useContext, useEffect } from 'react';
@@ -4161,10 +4161,7 @@ function AccountIconAndMenu(rawProps) {
4161
4161
  )
4162
4162
  }
4163
4163
  ),
4164
- props.user ? /* @__PURE__ */ jsxs("span", { className: "propeller-account-menu__greeting hidden md:block font-normal", children: [
4165
- "Hi, ",
4166
- getUserName()
4167
- ] }) : null,
4164
+ props.user ? /* @__PURE__ */ jsx("span", { className: "propeller-account-menu__greeting hidden md:block font-normal", children: getLabel(props.labels, "greeting", "Hi, {name}").replace("{name}", getUserName()) }) : null,
4168
4165
  !props.user ? /* @__PURE__ */ jsx("span", { className: "propeller-account-menu__greeting hidden md:block font-normal", children: getLabel(props.labels, "accountLabel", "Account") }) : null
4169
4166
  ]
4170
4167
  }
@@ -7975,7 +7972,7 @@ function CategoryDescription(props) {
7975
7972
  type: "button",
7976
7973
  className: "propeller-category-description__toggle mt-2 text-sm font-medium text-primary hover:underline",
7977
7974
  onClick: () => setExpanded((v) => !v),
7978
- children: expanded ? "Read less" : "Read more"
7975
+ children: expanded ? getLabel(props.labels, "readLess", "Read less") : getLabel(props.labels, "readMore", "Read more")
7979
7976
  }
7980
7977
  ) : null
7981
7978
  ]
@@ -9892,7 +9889,7 @@ function GridToolbar(rawProps) {
9892
9889
  const [currentSortOrder, setCurrentSortOrder] = useState(SortOrder.DESC);
9893
9890
  const [currentOffset, setCurrentOffset] = useState(12);
9894
9891
  const [currentViewMode, setCurrentViewMode] = useState("grid");
9895
- function getLabel56(key) {
9892
+ function getLabel59(key) {
9896
9893
  const labels = props.labels || {};
9897
9894
  return labels[key] !== void 0 ? labels[key] : DEFAULT_LABELS[key] || key;
9898
9895
  }
@@ -9930,8 +9927,8 @@ function GridToolbar(rawProps) {
9930
9927
  }
9931
9928
  function getAvailabilityLabel() {
9932
9929
  const qty = props.minStock;
9933
- if (qty !== void 0 && qty > MIN_STOCK_THRESHOLD) return `${getLabel56("inStock")}: ${qty}+`;
9934
- return getLabel56("inStock");
9930
+ if (qty !== void 0 && qty > MIN_STOCK_THRESHOLD) return `${getLabel59("inStock")}: ${qty}+`;
9931
+ return getLabel59("inStock");
9935
9932
  }
9936
9933
  function handleSortFieldChange(field) {
9937
9934
  setCurrentSortField(field);
@@ -9977,7 +9974,7 @@ function GridToolbar(rawProps) {
9977
9974
  /* @__PURE__ */ jsx("div", { className: "propeller-grid-toolbar__count text-sm text-muted-foreground font-medium", children: props.itemsFound > 0 ? /* @__PURE__ */ jsxs("span", { children: [
9978
9975
  props.itemsFound,
9979
9976
  "\xA0",
9980
- props.itemsFound === 1 ? getLabel56("productSingular") : getLabel56("productPlural")
9977
+ props.itemsFound === 1 ? getLabel59("productSingular") : getLabel59("productPlural")
9981
9978
  ] }) : null }),
9982
9979
  /* @__PURE__ */ jsxs("div", { className: "propeller-grid-toolbar__controls flex flex-wrap items-center gap-3", children: [
9983
9980
  /* @__PURE__ */ jsx(
@@ -9988,7 +9985,7 @@ function GridToolbar(rawProps) {
9988
9985
  onChange: (e) => handleOffsetChange(parseInt(e.target.value)),
9989
9986
  children: getOffsetOptions()?.map((n) => /* @__PURE__ */ jsxs("option", { value: n, children: [
9990
9987
  n,
9991
- getLabel56("perPage")
9988
+ getLabel59("perPage")
9992
9989
  ] }, n))
9993
9990
  }
9994
9991
  ),
@@ -10004,7 +10001,7 @@ function GridToolbar(rawProps) {
10004
10001
  {
10005
10002
  value: field,
10006
10003
  disabled: field === "PRICE" && isPriceSortDisabled(),
10007
- children: getLabel56(field)
10004
+ children: getLabel59(field)
10008
10005
  },
10009
10006
  field
10010
10007
  ))
@@ -10017,8 +10014,8 @@ function GridToolbar(rawProps) {
10017
10014
  value: currentSortOrder,
10018
10015
  onChange: (e) => handleSortOrderChange(e.target.value),
10019
10016
  children: [
10020
- /* @__PURE__ */ jsx("option", { value: SortOrder.ASC, children: getLabel56("ASC") }),
10021
- /* @__PURE__ */ jsx("option", { value: SortOrder.DESC, children: getLabel56("DESC") })
10017
+ /* @__PURE__ */ jsx("option", { value: SortOrder.ASC, children: getLabel59("ASC") }),
10018
+ /* @__PURE__ */ jsx("option", { value: SortOrder.DESC, children: getLabel59("DESC") })
10022
10019
  ]
10023
10020
  }
10024
10021
  ),
@@ -10028,7 +10025,7 @@ function GridToolbar(rawProps) {
10028
10025
  type: "button",
10029
10026
  className: "propeller-grid-toolbar__view-toggle h-9 w-9 flex items-center justify-center rounded-control border border-input bg-transparent hover:bg-accent hover:text-accent-foreground transition-colors",
10030
10027
  onClick: (event) => handleViewChange(),
10031
- title: currentViewMode === "grid" ? getLabel56("switchToList") : getLabel56("switchToGrid"),
10028
+ title: currentViewMode === "grid" ? getLabel59("switchToList") : getLabel59("switchToGrid"),
10032
10029
  children: [
10033
10030
  currentViewMode === "grid" ? /* @__PURE__ */ jsxs(
10034
10031
  "svg",
@@ -10086,7 +10083,7 @@ function GridToolbar(rawProps) {
10086
10083
  onClick: (event) => {
10087
10084
  if (props.onClearFilters) props.onClearFilters();
10088
10085
  },
10089
- children: getLabel56("clearAll")
10086
+ children: getLabel59("clearAll")
10090
10087
  }
10091
10088
  ),
10092
10089
  props.priceFilterMin !== void 0 || props.priceFilterMax !== void 0 ? /* @__PURE__ */ jsxs(
@@ -10097,7 +10094,7 @@ function GridToolbar(rawProps) {
10097
10094
  if (props.onPriceFilterRemove) props.onPriceFilterRemove();
10098
10095
  },
10099
10096
  children: [
10100
- getLabel56("price"),
10097
+ getLabel59("price"),
10101
10098
  ": \u20AC",
10102
10099
  props.priceFilterMin ?? 0,
10103
10100
  " \u2013 \u20AC",
@@ -10147,7 +10144,7 @@ var DEFAULT_LABELS2 = {
10147
10144
  of: "of"
10148
10145
  };
10149
10146
  function GridPagination(props) {
10150
- function getLabel56(key) {
10147
+ function getLabel59(key) {
10151
10148
  const labels = props.labels || {};
10152
10149
  return labels[key] !== void 0 ? labels[key] : DEFAULT_LABELS2[key] || key;
10153
10150
  }
@@ -10220,15 +10217,15 @@ function GridPagination(props) {
10220
10217
  className: "propeller-grid-pagination__btn propeller-grid-pagination__btn--prev inline-flex items-center rounded-control border border-input bg-card px-4 py-2 text-sm font-medium text-foreground shadow-sm hover:bg-surface-hover disabled:opacity-40 disabled:cursor-not-allowed",
10221
10218
  disabled: currentPage === 1,
10222
10219
  onClick: () => handlePageChange(currentPage - 1),
10223
- children: getLabel56("previous")
10220
+ children: getLabel59("previous")
10224
10221
  }
10225
10222
  ),
10226
10223
  /* @__PURE__ */ jsxs("span", { className: "propeller-grid-pagination__info px-2 text-sm font-medium text-muted-foreground", children: [
10227
- getLabel56("page"),
10224
+ getLabel59("page"),
10228
10225
  "\xA0",
10229
10226
  currentPage,
10230
10227
  "\xA0",
10231
- getLabel56("of"),
10228
+ getLabel59("of"),
10232
10229
  "\xA0",
10233
10230
  totalPages
10234
10231
  ] }),
@@ -10239,7 +10236,7 @@ function GridPagination(props) {
10239
10236
  className: "propeller-grid-pagination__btn propeller-grid-pagination__btn--next inline-flex items-center rounded-control border border-input bg-card px-4 py-2 text-sm font-medium text-foreground shadow-sm hover:bg-surface-hover disabled:opacity-40 disabled:cursor-not-allowed",
10240
10237
  disabled: currentPage === totalPages,
10241
10238
  onClick: () => handlePageChange(currentPage + 1),
10242
- children: getLabel56("next")
10239
+ children: getLabel59("next")
10243
10240
  }
10244
10241
  )
10245
10242
  ] }) : null,
@@ -10251,7 +10248,7 @@ function GridPagination(props) {
10251
10248
  className: "propeller-grid-pagination__btn propeller-grid-pagination__btn--prev inline-flex items-center rounded-control border border-input bg-card px-3 py-2 text-sm font-medium text-foreground shadow-sm hover:bg-surface-hover disabled:opacity-40 disabled:cursor-not-allowed",
10252
10249
  disabled: currentPage === 1,
10253
10250
  onClick: () => handlePageChange(currentPage - 1),
10254
- children: getLabel56("previous")
10251
+ children: getLabel59("previous")
10255
10252
  }
10256
10253
  ),
10257
10254
  getFullPages().map((item, idx) => /* @__PURE__ */ jsx(
@@ -10278,7 +10275,7 @@ function GridPagination(props) {
10278
10275
  className: "propeller-grid-pagination__btn propeller-grid-pagination__btn--next inline-flex items-center rounded-control border border-input bg-card px-3 py-2 text-sm font-medium text-foreground shadow-sm hover:bg-surface-hover disabled:opacity-40 disabled:cursor-not-allowed",
10279
10276
  disabled: currentPage === totalPages,
10280
10277
  onClick: () => handlePageChange(currentPage + 1),
10281
- children: getLabel56("next")
10278
+ children: getLabel59("next")
10282
10279
  }
10283
10280
  )
10284
10281
  ] }) : null
@@ -10417,7 +10414,7 @@ function MachineGrid(rawProps) {
10417
10414
  const cards = rootMachines.map((m) => ({ machine: m, href: childSlugHref(m) })).filter((c) => c.href !== null);
10418
10415
  return /* @__PURE__ */ jsxs("div", { className: rawProps.className, children: [
10419
10416
  /* @__PURE__ */ jsx("h1", { className: "propeller-grid-title mb-6 text-2xl font-semibold", children: rawProps.rootTitle ?? "Machines" }),
10420
- rootLoading ? /* @__PURE__ */ jsx("p", { className: "py-12 text-center text-foreground-subtle", children: "Loading\u2026" }) : cards.length === 0 ? /* @__PURE__ */ jsx("p", { className: "py-12 text-center text-foreground-subtle", children: "No machines found." }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4", children: cards.map(({ machine: m, href }) => /* @__PURE__ */ jsx(
10417
+ rootLoading ? /* @__PURE__ */ jsx("p", { className: "py-12 text-center text-foreground-subtle", children: getLabel(rawProps.machineCardLabels, "loading", "Loading\u2026") }) : cards.length === 0 ? /* @__PURE__ */ jsx("p", { className: "py-12 text-center text-foreground-subtle", children: getLabel(rawProps.machineCardLabels, "noMachines", "No machines found.") }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4", children: cards.map(({ machine: m, href }) => /* @__PURE__ */ jsx(
10421
10418
  MachineCard,
10422
10419
  {
10423
10420
  machine: m,
@@ -13617,9 +13614,7 @@ var OrderItemCard_default = OrderItemCard;
13617
13614
  function OrderBonusItems(rawProps) {
13618
13615
  const props = useInfraProps(rawProps);
13619
13616
  const allItems = props.items ?? props.order?.items ?? [];
13620
- const bonusItems = allItems.filter(
13621
- (item) => item.class === OrderItemClass.product && item.isBonus === YesNo.Y
13622
- );
13617
+ const bonusItems = getNettedBonusItems(allItems);
13623
13618
  if (bonusItems.length === 0) return null;
13624
13619
  const OrderItemCardImpl = props.orderItemCardComponent ?? OrderItemCard_default;
13625
13620
  return /* @__PURE__ */ jsxs("div", { className: `propeller-order-bonus-items ${props.className || "mb-8"}`, children: [
@@ -15416,7 +15411,7 @@ function ProductDescription(props) {
15416
15411
  type: "button",
15417
15412
  className: "propeller-product-description__toggle mt-2 text-sm font-medium text-primary hover:underline",
15418
15413
  onClick: () => setExpanded((v) => !v),
15419
- children: expanded ? "Read less" : "Read more"
15414
+ children: expanded ? getLabel(props.labels, "readLess", "Read less") : getLabel(props.labels, "readMore", "Read more")
15420
15415
  }
15421
15416
  ) : null
15422
15417
  ]
@@ -16731,7 +16726,8 @@ function ProductTabs(rawProps) {
16731
16726
  product: props.product,
16732
16727
  language: props.language,
16733
16728
  collapsed: props.descriptionCollapsed,
16734
- maxLength: props.descriptionMaxLength
16729
+ maxLength: props.descriptionMaxLength,
16730
+ labels: props.labels
16735
16731
  }
16736
16732
  ) : null,
16737
16733
  specsVisited && isTabVisible("specifications") ? /* @__PURE__ */ jsx("div", { className: isActive("specifications") ? "" : "hidden", children: /* @__PURE__ */ jsx(
@@ -16801,7 +16797,8 @@ function ProductTabs(rawProps) {
16801
16797
  product: props.product,
16802
16798
  language: props.language,
16803
16799
  collapsed: props.descriptionCollapsed,
16804
- maxLength: props.descriptionMaxLength
16800
+ maxLength: props.descriptionMaxLength,
16801
+ labels: props.labels
16805
16802
  }
16806
16803
  ) }) : null
16807
16804
  ] }) : null,
@@ -17129,8 +17126,9 @@ function PurchaseAuthorizationConfigurator(rawProps) {
17129
17126
  {
17130
17127
  type: "button",
17131
17128
  className: "text-muted-foreground hover:text-foreground transition",
17129
+ "aria-label": getLabel(props.labels, "closeLabel", "Close"),
17132
17130
  onClick: () => closeAddContactModal(),
17133
- children: "\u2715"
17131
+ children: /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2715" })
17134
17132
  }
17135
17133
  )
17136
17134
  ] }),
@@ -17228,7 +17226,7 @@ function PurchaseAuthorizationConfigurator(rawProps) {
17228
17226
  }
17229
17227
  )
17230
17228
  ] }),
17231
- addContactError ? /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: addContactError }) : null,
17229
+ addContactError ? /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: props.labels?.addContactFailed || addContactError }) : null,
17232
17230
  /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-3 pt-2", children: [
17233
17231
  /* @__PURE__ */ jsx(
17234
17232
  "button",
@@ -17381,8 +17379,9 @@ function PurchaseAuthorizationRequests(rawProps) {
17381
17379
  {
17382
17380
  type: "button",
17383
17381
  className: "propeller-purchase-authorization-requests__modal-close text-foreground-subtle hover:text-foreground focus:outline-none",
17382
+ "aria-label": getLabel(props.labels, "closeLabel", "Close"),
17384
17383
  onClick: () => closeModal(),
17385
- children: /* @__PURE__ */ jsx("svg", { fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", className: "h-5 w-5", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) })
17384
+ children: /* @__PURE__ */ jsx("svg", { "aria-hidden": "true", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", className: "h-5 w-5", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) })
17386
17385
  }
17387
17386
  )
17388
17387
  ] }),
@@ -17665,6 +17664,8 @@ function RegisterForm(rawProps) {
17665
17664
  const emailPlaceholder = props.labels?.emailPlaceholder || "name@example.com";
17666
17665
  const passwordPlaceholder = props.labels?.passwordPlaceholder || "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
17667
17666
  const passwordMismatchText = props.labels?.passwordMismatch || "Passwords do not match";
17667
+ const selectUserTypeText = props.labels?.selectUserType || "Please select an account type.";
17668
+ const registrationFailedText = props.labels?.registrationFailed;
17668
17669
  const loginText = props.labels?.loginText || "Already have an account?";
17669
17670
  const loginLinkText = props.labels?.loginLink || "Log in";
17670
17671
  function isFieldRequired(fieldName) {
@@ -17675,7 +17676,7 @@ function RegisterForm(rawProps) {
17675
17676
  async function handleSubmit(e) {
17676
17677
  e.preventDefault();
17677
17678
  if (!effectiveUserType) {
17678
- setError("Please select an account type.");
17679
+ setError(selectUserTypeText);
17679
17680
  return;
17680
17681
  }
17681
17682
  if (password !== confirmPassword) {
@@ -17751,7 +17752,7 @@ function RegisterForm(rawProps) {
17751
17752
  );
17752
17753
  }
17753
17754
  } else if (result.error) {
17754
- setError(result.error);
17755
+ setError(registrationFailedText || result.error);
17755
17756
  }
17756
17757
  }
17757
17758
  return /* @__PURE__ */ jsxs("div", { className: "propeller-register-form", "data-loading": loading ? "true" : "false", "data-user-type": selectedUserType, children: [