@propeller-commerce/propeller-v2-react-ui 0.12.0 → 0.13.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/CHANGELOG.md +41 -0
- package/dist/index.cjs +83 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -1
- package/dist/index.d.ts +39 -1
- package/dist/index.js +84 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,47 @@ 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.13.0] - 2026-08-05
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **Availability filter on `<GridFilters>`** — an opt-in section below the
|
|
16
|
+
price block with two checkboxes, In stock and Out of stock, wired to the
|
|
17
|
+
server-side stock filter the SDK exposes on `CategoryProductSearchInput`.
|
|
18
|
+
New props: `showAvailabilityFilter` (defaults `false`),
|
|
19
|
+
`activeAvailability` and `onAvailabilityChange`. Nothing selected sends no
|
|
20
|
+
filter, so the default listing is unchanged.
|
|
21
|
+
|
|
22
|
+
Selecting both buckets means "in stock or out of stock", which is every
|
|
23
|
+
product. The filter has no OR operator and the union is the unfiltered set
|
|
24
|
+
anyway, so both-selected sends no filter — the same as selecting neither.
|
|
25
|
+
The checkboxes stay ticked; only the request collapses.
|
|
26
|
+
|
|
27
|
+
The mapping lives in `propeller-v2-core-ui`'s `buildInventoryFilter`, so
|
|
28
|
+
this package never restates the operator semantics. Requires
|
|
29
|
+
`propeller-v2-core-ui` >= 0.5.0 and `propeller-sdk-v2` >= 0.15.0, where the
|
|
30
|
+
inventory filter was added.
|
|
31
|
+
|
|
32
|
+
- **`availability` and `onAvailabilityFilterRemove` on `<GridToolbar>`** —
|
|
33
|
+
the active-filter row now renders one removable chip per selected bucket,
|
|
34
|
+
so a stock-filtered listing shows why its results are narrowed and can be
|
|
35
|
+
undone from the results area. One chip per bucket, matching the attribute
|
|
36
|
+
chips rather than the price chip which covers a whole range. The row's
|
|
37
|
+
active check accounts for availability, so it appears when availability is
|
|
38
|
+
the only active filter.
|
|
39
|
+
|
|
40
|
+
- **`availability` on `<ProductGrid>`** — forwards the selection to
|
|
41
|
+
`useProductSearch`, which applies it to both the grid fetch and the
|
|
42
|
+
typeahead preview. Filtering only the grid would let the preview offer
|
|
43
|
+
products the grid then hides.
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- The availability section is hidden for anonymous visitors in a semi-closed
|
|
48
|
+
portal, the same rule the price filter already follows. Hosts should pass
|
|
49
|
+
their own "show stock" setting through `showAvailabilityFilter`: filtering
|
|
50
|
+
by stock reads as broken when the cards display no stock to filter on.
|
|
51
|
+
|
|
11
52
|
## [0.12.0] - 2026-07-31
|
|
12
53
|
|
|
13
54
|
### Added
|
package/dist/index.cjs
CHANGED
|
@@ -2261,7 +2261,8 @@ function useProductSearch(options) {
|
|
|
2261
2261
|
userKey,
|
|
2262
2262
|
JSON.stringify(options.textFilters ?? []),
|
|
2263
2263
|
options.priceFilterMin ?? "",
|
|
2264
|
-
options.priceFilterMax ?? ""
|
|
2264
|
+
options.priceFilterMax ?? "",
|
|
2265
|
+
JSON.stringify(options.availability ?? [])
|
|
2265
2266
|
].join("|");
|
|
2266
2267
|
if (inflightFetches2.has(inflightKey)) {
|
|
2267
2268
|
fetchIdRef.current--;
|
|
@@ -2303,6 +2304,7 @@ function useProductSearch(options) {
|
|
|
2303
2304
|
}
|
|
2304
2305
|
] : [];
|
|
2305
2306
|
const priceFilter = options.priceFilterMin !== void 0 || options.priceFilterMax !== void 0 ? { from: options.priceFilterMin ?? 0, to: options.priceFilterMax ?? 999999 } : void 0;
|
|
2307
|
+
const inventoryFilter = propellerV2CoreUi.buildInventoryFilter(options.availability);
|
|
2306
2308
|
const userId = options.user && "contactId" in options.user ? options.user.contactId : options.user && "customerId" in options.user ? options.user.customerId : void 0;
|
|
2307
2309
|
const contactId = options.user && "contactId" in options.user ? options.user.contactId : void 0;
|
|
2308
2310
|
const customerId = options.user && "customerId" in options.user ? options.user.customerId : void 0;
|
|
@@ -2325,6 +2327,7 @@ function useProductSearch(options) {
|
|
|
2325
2327
|
...options.brand && { manufacturers: [options.brand] },
|
|
2326
2328
|
...options.textFilters?.length && { textFilters: options.textFilters },
|
|
2327
2329
|
...priceFilter && { price: priceFilter },
|
|
2330
|
+
...inventoryFilter && { inventory: inventoryFilter },
|
|
2328
2331
|
...sortInputs.length && { sortInputs },
|
|
2329
2332
|
...options.companyId && { companyId: options.companyId },
|
|
2330
2333
|
...userId !== void 0 && { userId },
|
|
@@ -2401,6 +2404,7 @@ function useProductSearch(options) {
|
|
|
2401
2404
|
options.textFilters,
|
|
2402
2405
|
options.priceFilterMin,
|
|
2403
2406
|
options.priceFilterMax,
|
|
2407
|
+
options.availability,
|
|
2404
2408
|
options.sortField,
|
|
2405
2409
|
options.sortOrder,
|
|
2406
2410
|
options.companyId,
|
|
@@ -2440,6 +2444,7 @@ function useProductSearch(options) {
|
|
|
2440
2444
|
const userId = options.user && "contactId" in options.user ? options.user.contactId : options.user && "customerId" in options.user ? options.user.customerId : void 0;
|
|
2441
2445
|
const contactId = options.user && "contactId" in options.user ? options.user.contactId : void 0;
|
|
2442
2446
|
const customerId = options.user && "customerId" in options.user ? options.user.customerId : void 0;
|
|
2447
|
+
const inventoryFilter = propellerV2CoreUi.buildInventoryFilter(options.availability);
|
|
2443
2448
|
const categoryProductSearchInput = {
|
|
2444
2449
|
language,
|
|
2445
2450
|
page: 1,
|
|
@@ -2451,6 +2456,7 @@ function useProductSearch(options) {
|
|
|
2451
2456
|
propellerSdkV2.ProductStatus.S
|
|
2452
2457
|
],
|
|
2453
2458
|
hidden: false,
|
|
2459
|
+
...inventoryFilter && { inventory: inventoryFilter },
|
|
2454
2460
|
term,
|
|
2455
2461
|
searchFields: [
|
|
2456
2462
|
{
|
|
@@ -2530,6 +2536,7 @@ function useProductSearch(options) {
|
|
|
2530
2536
|
JSON.stringify(options.textFilters),
|
|
2531
2537
|
options.priceFilterMin,
|
|
2532
2538
|
options.priceFilterMax,
|
|
2539
|
+
options.availability,
|
|
2533
2540
|
options.sortField,
|
|
2534
2541
|
options.sortOrder,
|
|
2535
2542
|
pageSize,
|
|
@@ -9052,6 +9059,7 @@ function ProductGrid(rawProps) {
|
|
|
9052
9059
|
textFilters: props.textFilters,
|
|
9053
9060
|
priceFilterMin: props.priceFilterMin,
|
|
9054
9061
|
priceFilterMax: props.priceFilterMax,
|
|
9062
|
+
availability: props.availability,
|
|
9055
9063
|
sortField: props.sortField,
|
|
9056
9064
|
sortOrder: props.sortOrder,
|
|
9057
9065
|
pageSize: props.pageSize,
|
|
@@ -9354,11 +9362,18 @@ function GridFilters(rawProps) {
|
|
|
9354
9362
|
const [isPending, setIsPending] = React6.useState(() => false);
|
|
9355
9363
|
const [appliedMin, setAppliedMin] = React6.useState(() => void 0);
|
|
9356
9364
|
const [appliedMax, setAppliedMax] = React6.useState(() => void 0);
|
|
9365
|
+
const [availability, setAvailability] = React6.useState(
|
|
9366
|
+
() => [...props.activeAvailability || []]
|
|
9367
|
+
);
|
|
9357
9368
|
function showPriceFilter() {
|
|
9358
9369
|
const mode = props.portalMode || "open";
|
|
9359
9370
|
if (mode === "open") return true;
|
|
9360
9371
|
return !!props.user;
|
|
9361
9372
|
}
|
|
9373
|
+
function showAvailability() {
|
|
9374
|
+
if (!props.showAvailabilityFilter) return false;
|
|
9375
|
+
return !propellerV2CoreUi.isContentHidden(props.portalMode, props.user);
|
|
9376
|
+
}
|
|
9362
9377
|
function getFilterName(filter) {
|
|
9363
9378
|
return filter?.attributeDescription?.name || "";
|
|
9364
9379
|
}
|
|
@@ -9403,6 +9418,13 @@ function GridFilters(rawProps) {
|
|
|
9403
9418
|
props.onFilterChange(filter, value);
|
|
9404
9419
|
if (props.getSelectedFilters) props.getSelectedFilters();
|
|
9405
9420
|
}
|
|
9421
|
+
function handleAvailabilityToggle(value, checked) {
|
|
9422
|
+
const next = checked ? [...availability, value] : availability.filter((v) => v !== value);
|
|
9423
|
+
setAvailability(next);
|
|
9424
|
+
setIsPending(true);
|
|
9425
|
+
if (props.onAvailabilityChange) props.onAvailabilityChange(next);
|
|
9426
|
+
if (props.getSelectedFilters) props.getSelectedFilters();
|
|
9427
|
+
}
|
|
9406
9428
|
function handleMinChange(value) {
|
|
9407
9429
|
const n = value > currentMax ? currentMax : value;
|
|
9408
9430
|
setCurrentMin(n);
|
|
@@ -9476,11 +9498,16 @@ function GridFilters(rawProps) {
|
|
|
9476
9498
|
setCurrentMin(props.priceMin || 0);
|
|
9477
9499
|
setCurrentMax(props.priceMax || 9999);
|
|
9478
9500
|
setExpandedFilters({});
|
|
9501
|
+
setAvailability([]);
|
|
9479
9502
|
}, [props.clearSignal, props.priceMin, props.priceMax]);
|
|
9480
9503
|
React6.useEffect(() => {
|
|
9481
9504
|
if (!props.activeTextFilters) return;
|
|
9482
9505
|
setSelectedFilters(props.activeTextFilters);
|
|
9483
9506
|
}, [props.activeTextFilters]);
|
|
9507
|
+
React6.useEffect(() => {
|
|
9508
|
+
if (!props.activeAvailability) return;
|
|
9509
|
+
setAvailability([...props.activeAvailability]);
|
|
9510
|
+
}, [props.activeAvailability]);
|
|
9484
9511
|
React6.useEffect(() => {
|
|
9485
9512
|
if (props.activePriceMin === void 0 && props.activePriceMax === void 0) {
|
|
9486
9513
|
setCurrentMin(props.priceMin || 0);
|
|
@@ -9580,6 +9607,40 @@ function GridFilters(rawProps) {
|
|
|
9580
9607
|
" ",
|
|
9581
9608
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-grid-filters__divider h-px bg-border-subtle" })
|
|
9582
9609
|
] }) : null,
|
|
9610
|
+
showAvailability() ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
9611
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-grid-filters__availability space-y-3", children: [
|
|
9612
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "propeller-grid-filters__availability-title text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: propellerV2CoreUi.getLabel(props.labels, "availability", "Availability") }),
|
|
9613
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-grid-filters__availability-options space-y-1.5", children: [
|
|
9614
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "propeller-grid-filters__option flex items-center gap-2 cursor-pointer group", children: [
|
|
9615
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9616
|
+
"input",
|
|
9617
|
+
{
|
|
9618
|
+
type: "checkbox",
|
|
9619
|
+
className: "propeller-grid-filters__checkbox h-4 w-4 rounded border-input text-secondary focus:ring-secondary cursor-pointer flex-shrink-0",
|
|
9620
|
+
checked: availability.includes("in-stock"),
|
|
9621
|
+
disabled: !!props.isLoading,
|
|
9622
|
+
onChange: (e) => handleAvailabilityToggle("in-stock", e.target.checked)
|
|
9623
|
+
}
|
|
9624
|
+
),
|
|
9625
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "propeller-grid-filters__option-label flex-1 text-sm text-muted-foreground leading-none select-none group-hover:text-foreground", children: propellerV2CoreUi.getLabel(props.labels, "inStock", "In stock") })
|
|
9626
|
+
] }),
|
|
9627
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "propeller-grid-filters__option flex items-center gap-2 cursor-pointer group", children: [
|
|
9628
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9629
|
+
"input",
|
|
9630
|
+
{
|
|
9631
|
+
type: "checkbox",
|
|
9632
|
+
className: "propeller-grid-filters__checkbox h-4 w-4 rounded border-input text-secondary focus:ring-secondary cursor-pointer flex-shrink-0",
|
|
9633
|
+
checked: availability.includes("out-of-stock"),
|
|
9634
|
+
disabled: !!props.isLoading,
|
|
9635
|
+
onChange: (e) => handleAvailabilityToggle("out-of-stock", e.target.checked)
|
|
9636
|
+
}
|
|
9637
|
+
),
|
|
9638
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "propeller-grid-filters__option-label flex-1 text-sm text-muted-foreground leading-none select-none group-hover:text-foreground", children: propellerV2CoreUi.getLabel(props.labels, "outOfStock", "Out of stock") })
|
|
9639
|
+
] })
|
|
9640
|
+
] })
|
|
9641
|
+
] }),
|
|
9642
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-grid-filters__divider h-px bg-border-subtle" })
|
|
9643
|
+
] }) : null,
|
|
9583
9644
|
props.filters.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "propeller-grid-filters__empty text-sm text-foreground-subtle italic", children: propellerV2CoreUi.getLabel(props.labels, "noFiltersAvailable", "No filters available") }) : null,
|
|
9584
9645
|
getFilteredFilters()?.map((filter) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-grid-filters__group border-b border-border-subtle pb-3 last:border-b-0", "data-expanded": isExpanded(getFilterName(filter)) ? "true" : "false", children: [
|
|
9585
9646
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -9755,6 +9816,8 @@ var DEFAULT_LABELS = {
|
|
|
9755
9816
|
results: "results",
|
|
9756
9817
|
perPage: " per page",
|
|
9757
9818
|
price: "Price",
|
|
9819
|
+
inStock: "In stock",
|
|
9820
|
+
outOfStock: "Out of stock",
|
|
9758
9821
|
switchToList: "Switch to list view",
|
|
9759
9822
|
switchToGrid: "Switch to grid view"
|
|
9760
9823
|
};
|
|
@@ -9783,7 +9846,8 @@ function GridToolbar(rawProps) {
|
|
|
9783
9846
|
const text = props.activeTextFilters || {};
|
|
9784
9847
|
const hasText = Object.keys(text).some((k) => (text[k] || []).length > 0);
|
|
9785
9848
|
const hasPrice = props.priceFilterMin !== void 0 || props.priceFilterMax !== void 0;
|
|
9786
|
-
|
|
9849
|
+
const hasAvailability = (props.availability || []).length > 0;
|
|
9850
|
+
return hasText || hasPrice || hasAvailability;
|
|
9787
9851
|
}
|
|
9788
9852
|
function getActiveFilterBadges() {
|
|
9789
9853
|
const text = props.activeTextFilters || {};
|
|
@@ -9801,6 +9865,9 @@ function GridToolbar(rawProps) {
|
|
|
9801
9865
|
function isPriceSortDisabled() {
|
|
9802
9866
|
return propellerV2CoreUi.isContentHidden(props.portalMode, props.user);
|
|
9803
9867
|
}
|
|
9868
|
+
function getAvailabilityLabel(value) {
|
|
9869
|
+
return value === "in-stock" ? getLabel56("inStock") : getLabel56("outOfStock");
|
|
9870
|
+
}
|
|
9804
9871
|
function handleSortFieldChange(field) {
|
|
9805
9872
|
setCurrentSortField(field);
|
|
9806
9873
|
if (props.onSortChange) props.onSortChange(field, currentSortOrder);
|
|
@@ -9974,6 +10041,20 @@ function GridToolbar(rawProps) {
|
|
|
9974
10041
|
]
|
|
9975
10042
|
}
|
|
9976
10043
|
) : null,
|
|
10044
|
+
(props.availability || []).map((value) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10045
|
+
"span",
|
|
10046
|
+
{
|
|
10047
|
+
className: "propeller-grid-toolbar__filter-badge propeller-grid-toolbar__filter-badge--availability inline-flex items-center gap-1 cursor-pointer px-2.5 py-0.5 rounded-full text-xs font-semibold border border-input bg-background hover:bg-primary hover:text-primary-foreground hover:border-primary transition-colors",
|
|
10048
|
+
onClick: (event) => {
|
|
10049
|
+
if (props.onAvailabilityFilterRemove) props.onAvailabilityFilterRemove(value);
|
|
10050
|
+
},
|
|
10051
|
+
children: [
|
|
10052
|
+
getAvailabilityLabel(value),
|
|
10053
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "propeller-grid-toolbar__filter-badge-remove", children: "\xD7" })
|
|
10054
|
+
]
|
|
10055
|
+
},
|
|
10056
|
+
value
|
|
10057
|
+
)),
|
|
9977
10058
|
getActiveFilterBadges()?.map((badge) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9978
10059
|
"span",
|
|
9979
10060
|
{
|