@propeller-commerce/propeller-v2-react-ui 0.4.15 → 0.4.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/dist/index.cjs +59 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +160 -63
- package/dist/index.d.ts +160 -63
- package/dist/index.js +59 -20
- package/dist/index.js.map +1 -1
- package/dist/pure.cjs.map +1 -1
- package/dist/pure.d.cts +2 -2
- package/dist/pure.d.ts +2 -2
- package/dist/pure.js.map +1 -1
- package/dist/shared.d.cts +1 -1
- package/dist/shared.d.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,43 @@ once it reaches 1.0. Until then (the `0.x` line) the public API may change
|
|
|
8
8
|
between minor versions; breaking changes are called out below and in
|
|
9
9
|
[MIGRATION.md](./MIGRATION.md).
|
|
10
10
|
|
|
11
|
+
## [0.4.17] - 2026-07-09
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `ProductCard` / `ProductGrid`: new `belowName?: (product: Product) => ReactNode`
|
|
16
|
+
render prop. Renders arbitrary host-supplied content directly below the
|
|
17
|
+
product name (and above the short description / price) in both the grid and
|
|
18
|
+
row layouts, without forking the card — e.g. package descriptions or custom
|
|
19
|
+
badges. On `ProductGrid` it cascades to every card via `ProductGridConfig`;
|
|
20
|
+
on `ProductCard` it can also be set per-card (explicit prop wins over grid
|
|
21
|
+
context). Also exposed as the `<ProductCard.BelowName>` compound subcomponent.
|
|
22
|
+
|
|
23
|
+
## [0.4.16] - 2026-07-09
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- `ProductGrid`, `SearchBar`, `ProductInfo`: new `orderlistIds?: number[]` and
|
|
28
|
+
`applyOrderlists?: boolean` props to scope the product/search fetch to
|
|
29
|
+
specific orderlists (e.g. a chosen B2B contract). When `orderlistIds` is
|
|
30
|
+
non-empty, orderlists are applied (unless `applyOrderlists` is `false`);
|
|
31
|
+
otherwise `applyOrderlists: false` is sent so an authenticated user without a
|
|
32
|
+
contract still sees the full catalogue. Threaded through `useProductSearch`
|
|
33
|
+
and `useProductInfo` (explicit ids override the composable's default
|
|
34
|
+
all-company-orderlists resolution).
|
|
35
|
+
- `Menu`: new `getUrl?: (category: Category) => string` prop — a custom URL
|
|
36
|
+
builder that overrides `menuLinkFormat` / `configuration.urls.getCategoryUrl`,
|
|
37
|
+
letting hosts inject dynamic query strings (e.g. `?contract=…`). Mirrors the
|
|
38
|
+
existing `Breadcrumbs.getUrl`.
|
|
39
|
+
- `GridToolbar`: new `hidePriceSort?: boolean` prop to hide the price sort
|
|
40
|
+
option entirely (closed B2B portals where prices are "by quotation").
|
|
41
|
+
- `ProductTabs` / `ProductSpecifications`: new
|
|
42
|
+
`specificationsPackageDescription?` (ProductTabs) → `packageDescription?`
|
|
43
|
+
(ProductSpecifications) passthrough, rendering an extra package-description
|
|
44
|
+
string above the specifications table.
|
|
45
|
+
|
|
46
|
+
All additions are additive and backward-compatible.
|
|
47
|
+
|
|
11
48
|
## [0.4.15] - 2026-07-08
|
|
12
49
|
|
|
13
50
|
### Changed
|
package/dist/index.cjs
CHANGED
|
@@ -2045,7 +2045,13 @@ function useProductInfo(options) {
|
|
|
2045
2045
|
setError(null);
|
|
2046
2046
|
try {
|
|
2047
2047
|
let orderlistIds = [];
|
|
2048
|
-
|
|
2048
|
+
let applyOrderlists = true;
|
|
2049
|
+
if (options.orderlistIds && options.orderlistIds.length > 0) {
|
|
2050
|
+
orderlistIds = options.orderlistIds;
|
|
2051
|
+
applyOrderlists = options.applyOrderlists !== false;
|
|
2052
|
+
} else if (options.applyOrderlists === false) {
|
|
2053
|
+
applyOrderlists = false;
|
|
2054
|
+
} else if (options.user && options.companyId) {
|
|
2049
2055
|
const orderlistService = propellerV2CoreUi.createServices(graphqlClient).orderlist;
|
|
2050
2056
|
const searchInput = { companyIds: [options.companyId] };
|
|
2051
2057
|
const orderlists = await orderlistService.getOrderlists(searchInput);
|
|
@@ -2056,7 +2062,7 @@ function useProductInfo(options) {
|
|
|
2056
2062
|
const variables = {
|
|
2057
2063
|
productId,
|
|
2058
2064
|
language,
|
|
2059
|
-
applyOrderlists
|
|
2065
|
+
applyOrderlists,
|
|
2060
2066
|
orderlistIds,
|
|
2061
2067
|
imageSearchFilters: imageSearchFilters ?? configuration.imageSearchFiltersGrid,
|
|
2062
2068
|
imageVariantFilters: imageVariantFilters ?? configuration.imageVariantFiltersLarge ?? configuration.imageVariantFiltersSmall,
|
|
@@ -2240,6 +2246,10 @@ function useProductSearch(options) {
|
|
|
2240
2246
|
const userId = options.user && "contactId" in options.user ? options.user.contactId : options.user && "customerId" in options.user ? options.user.customerId : void 0;
|
|
2241
2247
|
const contactId = options.user && "contactId" in options.user ? options.user.contactId : void 0;
|
|
2242
2248
|
const customerId = options.user && "customerId" in options.user ? options.user.customerId : void 0;
|
|
2249
|
+
const orderlistScope = options.orderlistIds && options.orderlistIds.length > 0 ? {
|
|
2250
|
+
applyOrderlists: options.applyOrderlists !== false,
|
|
2251
|
+
orderlistIds: options.orderlistIds
|
|
2252
|
+
} : { applyOrderlists: false };
|
|
2243
2253
|
const categoryProductSearchInput = {
|
|
2244
2254
|
language: lang,
|
|
2245
2255
|
page: pagination.currentPage,
|
|
@@ -2257,7 +2267,8 @@ function useProductSearch(options) {
|
|
|
2257
2267
|
...priceFilter && { price: priceFilter },
|
|
2258
2268
|
...sortInputs.length && { sortInputs },
|
|
2259
2269
|
...options.companyId && { companyId: options.companyId },
|
|
2260
|
-
...userId !== void 0 && { userId }
|
|
2270
|
+
...userId !== void 0 && { userId },
|
|
2271
|
+
...orderlistScope
|
|
2261
2272
|
};
|
|
2262
2273
|
const priceCalculateProductInput = {
|
|
2263
2274
|
taxZone,
|
|
@@ -7430,7 +7441,7 @@ function CategoryDescription(props) {
|
|
|
7430
7441
|
const plain = html.replace(/<[^>]*>/g, "");
|
|
7431
7442
|
if (plain.length <= maxLen) return html;
|
|
7432
7443
|
const truncated = plain.substring(0, maxLen);
|
|
7433
|
-
return truncated.substring(0, truncated.lastIndexOf(" ")) + "\
|
|
7444
|
+
return truncated.substring(0, truncated.lastIndexOf(" ")) + "\u2026";
|
|
7434
7445
|
}
|
|
7435
7446
|
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: !!html ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7436
7447
|
"div",
|
|
@@ -10697,7 +10708,8 @@ function GridToolbar(rawProps) {
|
|
|
10697
10708
|
}
|
|
10698
10709
|
function getSortOptions() {
|
|
10699
10710
|
const opts = props.sortOptions || [];
|
|
10700
|
-
|
|
10711
|
+
const base = opts.length > 0 ? opts : ALL_SORT_FIELDS;
|
|
10712
|
+
return props.hidePriceSort ? base.filter((f) => f !== propellerSdkV2.ProductSortField.PRICE) : base;
|
|
10701
10713
|
}
|
|
10702
10714
|
function getOffsetOptions() {
|
|
10703
10715
|
const opts = props.offset || [];
|
|
@@ -11120,10 +11132,12 @@ function Menu(rawProps) {
|
|
|
11120
11132
|
}
|
|
11121
11133
|
function getCategoryUrl(cat) {
|
|
11122
11134
|
const lang = props.language || "NL";
|
|
11123
|
-
|
|
11124
|
-
|
|
11125
|
-
lang
|
|
11126
|
-
|
|
11135
|
+
const category = {
|
|
11136
|
+
categoryId: cat.categoryId,
|
|
11137
|
+
slug: [{ value: cat.slug, language: lang }]
|
|
11138
|
+
};
|
|
11139
|
+
if (props.getUrl) return props.getUrl(category);
|
|
11140
|
+
return props.configuration.urls.getCategoryUrl(category, lang);
|
|
11127
11141
|
}
|
|
11128
11142
|
function getSubCategories(cat) {
|
|
11129
11143
|
return (cat.children || []).filter((sub) => sub.name && sub.slug);
|
|
@@ -13308,7 +13322,8 @@ var RESOLVE_SPEC2 = {
|
|
|
13308
13322
|
addToCartComponent: { grid: "addToCartComponent" },
|
|
13309
13323
|
imageComponent: { grid: "imageComponent" },
|
|
13310
13324
|
badgesComponent: { grid: "badgesComponent" },
|
|
13311
|
-
favoriteComponent: { grid: "favoriteComponent" }
|
|
13325
|
+
favoriteComponent: { grid: "favoriteComponent" },
|
|
13326
|
+
belowName: { grid: "belowName" }
|
|
13312
13327
|
};
|
|
13313
13328
|
var ProductCardContext = React6.createContext(null);
|
|
13314
13329
|
function useProductCard() {
|
|
@@ -13389,6 +13404,7 @@ function ProductCardRoot(rawProps) {
|
|
|
13389
13404
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-product-card__body flex flex-1 flex-row items-center gap-4 px-4 py-2 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5 flex-1 min-w-0", children: [
|
|
13390
13405
|
props.showSku !== false && sku ? /* @__PURE__ */ jsxRuntime.jsx(ProductCardSku, {}) : null,
|
|
13391
13406
|
props.showName !== false ? /* @__PURE__ */ jsxRuntime.jsx(ProductCardName, { linkable: true }) : null,
|
|
13407
|
+
/* @__PURE__ */ jsxRuntime.jsx(ProductCardBelowName, {}),
|
|
13392
13408
|
textLabelValues.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(ProductCardTextLabels, {}) : null,
|
|
13393
13409
|
props.showManufacturer && manufacturer ? /* @__PURE__ */ jsxRuntime.jsx(ProductCardManufacturer, {}) : null,
|
|
13394
13410
|
props.showShortDescription && shortDescription ? /* @__PURE__ */ jsxRuntime.jsx(ProductCardShortDescription, {}) : null
|
|
@@ -13404,6 +13420,7 @@ function ProductCardRoot(rawProps) {
|
|
|
13404
13420
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-product-card__body flex flex-1 flex-col gap-1.5 p-3 sm:gap-2 sm:p-4", children: [
|
|
13405
13421
|
props.showSku !== false && sku ? /* @__PURE__ */ jsxRuntime.jsx(ProductCardSku, {}) : null,
|
|
13406
13422
|
props.showName !== false ? /* @__PURE__ */ jsxRuntime.jsx(ProductCardName, { linkable: true }) : null,
|
|
13423
|
+
/* @__PURE__ */ jsxRuntime.jsx(ProductCardBelowName, {}),
|
|
13407
13424
|
textLabelValues.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(ProductCardTextLabels, {}) : null,
|
|
13408
13425
|
props.showStock && !!product.inventory ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden md:block", children: /* @__PURE__ */ jsxRuntime.jsx(ProductCardStock, {}) }) : null,
|
|
13409
13426
|
props.showManufacturer && manufacturer ? /* @__PURE__ */ jsxRuntime.jsx(ProductCardManufacturer, {}) : null,
|
|
@@ -13586,6 +13603,13 @@ function ProductCardStock(props) {
|
|
|
13586
13603
|
}
|
|
13587
13604
|
);
|
|
13588
13605
|
}
|
|
13606
|
+
function ProductCardBelowName() {
|
|
13607
|
+
const { product, resolved } = useProductCard();
|
|
13608
|
+
if (!resolved.belowName) return null;
|
|
13609
|
+
const content = resolved.belowName(product);
|
|
13610
|
+
if (content == null || content === false) return null;
|
|
13611
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-product-card__below-name", children: content });
|
|
13612
|
+
}
|
|
13589
13613
|
function ProductCardPrice(props) {
|
|
13590
13614
|
const { product, derived, resolved } = useProductCard();
|
|
13591
13615
|
if (!product?.price) return null;
|
|
@@ -13664,6 +13688,7 @@ ProductCard.Badges = ProductCardBadges;
|
|
|
13664
13688
|
ProductCard.Favorite = ProductCardFavorite;
|
|
13665
13689
|
ProductCard.Name = ProductCardName;
|
|
13666
13690
|
ProductCard.Sku = ProductCardSku;
|
|
13691
|
+
ProductCard.BelowName = ProductCardBelowName;
|
|
13667
13692
|
ProductCard.ShortDescription = ProductCardShortDescription;
|
|
13668
13693
|
ProductCard.Manufacturer = ProductCardManufacturer;
|
|
13669
13694
|
ProductCard.TextLabels = ProductCardTextLabels;
|
|
@@ -13938,7 +13963,7 @@ function useProductGrid() {
|
|
|
13938
13963
|
const ctx = React6.useContext(ProductGridSearchContext);
|
|
13939
13964
|
if (!ctx) {
|
|
13940
13965
|
throw new Error(
|
|
13941
|
-
"<ProductGrid.X> must be rendered inside <ProductGrid>. Got null context \
|
|
13966
|
+
"<ProductGrid.X> must be rendered inside <ProductGrid>. Got null context \u2014 wrap the subcomponents in <ProductGrid categoryId={...}>...</ProductGrid>."
|
|
13942
13967
|
);
|
|
13943
13968
|
}
|
|
13944
13969
|
return ctx;
|
|
@@ -13959,6 +13984,8 @@ function ProductGrid(rawProps) {
|
|
|
13959
13984
|
categoryId: props.categoryId,
|
|
13960
13985
|
term: props.term,
|
|
13961
13986
|
brand: props.brand,
|
|
13987
|
+
orderlistIds: props.orderlistIds,
|
|
13988
|
+
applyOrderlists: props.applyOrderlists,
|
|
13962
13989
|
language: props.language,
|
|
13963
13990
|
taxZone: props.taxZone,
|
|
13964
13991
|
user: props.user,
|
|
@@ -14044,7 +14071,8 @@ function ProductGrid(rawProps) {
|
|
|
14044
14071
|
badgesComponent: props.badgesComponent,
|
|
14045
14072
|
favoriteComponent: props.favoriteComponent,
|
|
14046
14073
|
productCardComponent: props.productCardComponent,
|
|
14047
|
-
clusterCardComponent: props.clusterCardComponent
|
|
14074
|
+
clusterCardComponent: props.clusterCardComponent,
|
|
14075
|
+
belowName: props.belowName
|
|
14048
14076
|
};
|
|
14049
14077
|
if (rawProps.children !== void 0) {
|
|
14050
14078
|
const compoundCtx = {
|
|
@@ -14361,9 +14389,15 @@ function useProductInfoContext() {
|
|
|
14361
14389
|
}
|
|
14362
14390
|
function ProductInfo(rawProps) {
|
|
14363
14391
|
const props = useInfraProps(rawProps);
|
|
14364
|
-
const { product: fetchedProduct, loading, fetchProduct } = useProductInfo(
|
|
14365
|
-
|
|
14366
|
-
|
|
14392
|
+
const { product: fetchedProduct, loading, fetchProduct } = useProductInfo({
|
|
14393
|
+
graphqlClient: props.graphqlClient ?? {},
|
|
14394
|
+
language: props.language,
|
|
14395
|
+
configuration: props.configuration,
|
|
14396
|
+
user: props.user,
|
|
14397
|
+
companyId: props.companyId,
|
|
14398
|
+
orderlistIds: props.orderlistIds,
|
|
14399
|
+
applyOrderlists: props.applyOrderlists
|
|
14400
|
+
});
|
|
14367
14401
|
const activeProduct = props.product || fetchedProduct;
|
|
14368
14402
|
function getProductName5() {
|
|
14369
14403
|
if (!activeProduct) return "";
|
|
@@ -15081,13 +15115,14 @@ function ProductSpecifications(rawProps) {
|
|
|
15081
15115
|
if (!props.productId || !props.graphqlClient) return;
|
|
15082
15116
|
fetchSpecs(props.productId);
|
|
15083
15117
|
}, [props.productId]);
|
|
15084
|
-
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: !loading && hasPublicAttributes() ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
15118
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: !loading && (hasPublicAttributes() || props.packageDescription) ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
15085
15119
|
"div",
|
|
15086
15120
|
{
|
|
15087
15121
|
className: `propeller-product-specifications ${props.className || ""}`,
|
|
15088
15122
|
"data-layout": props.layout === "list" ? "list" : "table",
|
|
15089
15123
|
"data-grouped": props.grouping ? "true" : "false",
|
|
15090
15124
|
children: [
|
|
15125
|
+
props.packageDescription ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "propeller-product-specifications__package-description mb-4 text-sm text-muted-foreground", children: props.packageDescription }) : null,
|
|
15091
15126
|
!props.grouping ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
15092
15127
|
props.layout !== "list" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-hidden rounded-lg border border-border", children: /* @__PURE__ */ jsxRuntime.jsx("table", { className: "w-full text-sm", children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-border", children: getAttributes()?.map((attr, i) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "propeller-product-specifications__row odd:bg-card even:bg-muted/20", children: [
|
|
15093
15128
|
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-2 font-medium text-foreground w-1/2", children: getAttributeLabel(attr) }),
|
|
@@ -15316,7 +15351,8 @@ function ProductTabs(rawProps) {
|
|
|
15316
15351
|
attributes: getSpecsAttributes(),
|
|
15317
15352
|
language: props.language,
|
|
15318
15353
|
layout: props.specificationsLayout,
|
|
15319
|
-
grouping: props.specificationsGrouping
|
|
15354
|
+
grouping: props.specificationsGrouping,
|
|
15355
|
+
packageDescription: props.specificationsPackageDescription
|
|
15320
15356
|
}
|
|
15321
15357
|
) }) : null,
|
|
15322
15358
|
isActive("downloads") && isTabVisible("downloads") ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -15419,7 +15455,8 @@ function ProductTabs(rawProps) {
|
|
|
15419
15455
|
attributes: getSpecsAttributes(),
|
|
15420
15456
|
language: props.language,
|
|
15421
15457
|
layout: props.specificationsLayout,
|
|
15422
|
-
grouping: props.specificationsGrouping
|
|
15458
|
+
grouping: props.specificationsGrouping,
|
|
15459
|
+
packageDescription: props.specificationsPackageDescription
|
|
15423
15460
|
}
|
|
15424
15461
|
) }) : null
|
|
15425
15462
|
] }) : null,
|
|
@@ -16206,7 +16243,7 @@ function RegisterForm(rawProps) {
|
|
|
16206
16243
|
const contactLabel = props.labels?.contactLabel || "Company";
|
|
16207
16244
|
const customerLabel = props.labels?.customerLabel || "Consumer";
|
|
16208
16245
|
const emailPlaceholder = props.labels?.emailPlaceholder || "name@example.com";
|
|
16209
|
-
const passwordPlaceholder = props.labels?.passwordPlaceholder || "\
|
|
16246
|
+
const passwordPlaceholder = props.labels?.passwordPlaceholder || "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
|
|
16210
16247
|
const passwordMismatchText = props.labels?.passwordMismatch || "Passwords do not match";
|
|
16211
16248
|
const loginText = props.labels?.loginText || "Already have an account?";
|
|
16212
16249
|
const loginLinkText = props.labels?.loginLink || "Log in";
|
|
@@ -17110,7 +17147,9 @@ function SearchBar(rawProps) {
|
|
|
17110
17147
|
graphqlClient: props.graphqlClient,
|
|
17111
17148
|
language: props.language,
|
|
17112
17149
|
configuration: props.configuration || {},
|
|
17113
|
-
companyId: props.companyId
|
|
17150
|
+
companyId: props.companyId,
|
|
17151
|
+
orderlistIds: props.orderlistIds,
|
|
17152
|
+
applyOrderlists: props.applyOrderlists
|
|
17114
17153
|
});
|
|
17115
17154
|
const results = searchResults.slice(0, maxResults).map((item) => mapToSearchBarResult(item));
|
|
17116
17155
|
const itemsFound = searchItemsFound;
|