@propeller-commerce/propeller-v2-react-ui 0.26.0 → 0.27.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 +59 -6
- package/dist/index.cjs +89 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +90 -30
- package/dist/index.js.map +1 -1
- package/dist/pure.cjs +23 -0
- package/dist/pure.cjs.map +1 -1
- package/dist/pure.js +23 -0
- package/dist/pure.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1548,6 +1548,8 @@ interface QuickOrderMatch {
|
|
|
1548
1548
|
grossPrice: number;
|
|
1549
1549
|
/** Minimum order quantity (defaults to 1). */
|
|
1550
1550
|
minQuantity: number;
|
|
1551
|
+
/** Order step: quantities run min, min+step, min+2*step, … (defaults to 1). */
|
|
1552
|
+
step: number;
|
|
1551
1553
|
/** Thumbnail URL, if any. */
|
|
1552
1554
|
imageUrl: string;
|
|
1553
1555
|
/** Price is quoted on request — not orderable through a bulk flow. */
|
package/dist/index.d.ts
CHANGED
|
@@ -1548,6 +1548,8 @@ interface QuickOrderMatch {
|
|
|
1548
1548
|
grossPrice: number;
|
|
1549
1549
|
/** Minimum order quantity (defaults to 1). */
|
|
1550
1550
|
minQuantity: number;
|
|
1551
|
+
/** Order step: quantities run min, min+step, min+2*step, … (defaults to 1). */
|
|
1552
|
+
step: number;
|
|
1551
1553
|
/** Thumbnail URL, if any. */
|
|
1552
1554
|
imageUrl: string;
|
|
1553
1555
|
/** Price is quoted on request — not orderable through a bulk flow. */
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ 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';
|
|
9
9
|
import { twMerge } from 'tailwind-merge';
|
|
10
|
-
import { User, Trash2,
|
|
10
|
+
import { User, Trash2, Tag } from 'lucide-react';
|
|
11
11
|
|
|
12
12
|
// src/index.ts
|
|
13
13
|
var PropellerDepsContext = createContext(null);
|
|
@@ -2614,6 +2614,7 @@ function toMatch(item, language) {
|
|
|
2614
2614
|
const netPrice = displayItem?.price?.net ?? 0;
|
|
2615
2615
|
const grossPrice = displayItem?.price?.gross ?? 0;
|
|
2616
2616
|
const minQuantity = Math.max(1, displayItem?.minimumQuantity ?? 1);
|
|
2617
|
+
const step = Math.max(1, displayItem?.unit ?? 1);
|
|
2617
2618
|
const imageUrl = isCluster ? getClusterImageUrl(item) : getProductImageUrl(item);
|
|
2618
2619
|
return {
|
|
2619
2620
|
productId,
|
|
@@ -2623,6 +2624,7 @@ function toMatch(item, language) {
|
|
|
2623
2624
|
netPrice,
|
|
2624
2625
|
grossPrice,
|
|
2625
2626
|
minQuantity,
|
|
2627
|
+
step,
|
|
2626
2628
|
imageUrl,
|
|
2627
2629
|
priceOnRequest: isPriceOnRequest(item)
|
|
2628
2630
|
};
|
|
@@ -4759,6 +4761,7 @@ function AddToCart(rawProps) {
|
|
|
4759
4761
|
onCartCreated: props.onCartCreated
|
|
4760
4762
|
});
|
|
4761
4763
|
const [quantity, setQuantity] = useState(() => getMinQuantity(props.product));
|
|
4764
|
+
const [quantityDraft, setQuantityDraft] = useState(null);
|
|
4762
4765
|
const [toastMessage, setToastMessage] = useState(() => "");
|
|
4763
4766
|
const [toastType, setToastType] = useState(() => "");
|
|
4764
4767
|
const [toastVisible, setToastVisible] = useState(() => false);
|
|
@@ -4781,16 +4784,42 @@ function AddToCart(rawProps) {
|
|
|
4781
4784
|
function dismissToast() {
|
|
4782
4785
|
setToastVisible(false);
|
|
4783
4786
|
}
|
|
4787
|
+
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;
|
|
4792
|
+
}
|
|
4784
4793
|
function increment() {
|
|
4785
|
-
|
|
4794
|
+
setQuantityDraft(null);
|
|
4795
|
+
const snapped = snapQuantity(quantity);
|
|
4796
|
+
setQuantity(snapped === quantity ? quantity + getStep(props.product) : snapped);
|
|
4786
4797
|
}
|
|
4787
4798
|
function decrement() {
|
|
4788
4799
|
const min = getMinQuantity(props.product);
|
|
4789
4800
|
const step = getStep(props.product);
|
|
4801
|
+
setQuantityDraft(null);
|
|
4802
|
+
const snapped = snapQuantity(quantity);
|
|
4803
|
+
if (snapped !== quantity) {
|
|
4804
|
+
setQuantity(snapped);
|
|
4805
|
+
return;
|
|
4806
|
+
}
|
|
4790
4807
|
if (quantity - step >= min) {
|
|
4791
4808
|
setQuantity(quantity - step);
|
|
4792
4809
|
}
|
|
4793
4810
|
}
|
|
4811
|
+
function handleQuantityInput(raw) {
|
|
4812
|
+
setQuantityDraft(raw);
|
|
4813
|
+
const val = parseInt(raw, 10);
|
|
4814
|
+
if (!isNaN(val) && val >= getMinQuantity(props.product)) {
|
|
4815
|
+
setQuantity(snapQuantity(val));
|
|
4816
|
+
}
|
|
4817
|
+
}
|
|
4818
|
+
function handleQuantityBlur() {
|
|
4819
|
+
const val = parseInt(quantityDraft ?? "", 10);
|
|
4820
|
+
setQuantity(isNaN(val) ? getMinQuantity(props.product) : snapQuantity(val));
|
|
4821
|
+
setQuantityDraft(null);
|
|
4822
|
+
}
|
|
4794
4823
|
function getModalImageUrl() {
|
|
4795
4824
|
if (addedCartItem) {
|
|
4796
4825
|
const img = addedCartItem.product?.media?.images?.items?.[0]?.imageVariants?.[0]?.url;
|
|
@@ -4920,15 +4949,9 @@ function AddToCart(rawProps) {
|
|
|
4920
4949
|
className: "propeller-add-to-cart__quantity flex-1 md:flex-none md:w-12 text-center text-sm bg-transparent border-none focus:ring-0 focus:outline-none h-full [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none",
|
|
4921
4950
|
min: getMinQuantity(props.product),
|
|
4922
4951
|
step: getStep(props.product),
|
|
4923
|
-
value: quantity,
|
|
4924
|
-
onChange: (e) =>
|
|
4925
|
-
|
|
4926
|
-
const min = getMinQuantity(props.product);
|
|
4927
|
-
const step = getStep(props.product);
|
|
4928
|
-
if (!isNaN(val) && val >= min) {
|
|
4929
|
-
setQuantity(Math.round((val - min) / step) * step + min);
|
|
4930
|
-
}
|
|
4931
|
-
}
|
|
4952
|
+
value: quantityDraft ?? quantity,
|
|
4953
|
+
onChange: (e) => handleQuantityInput(e.target.value),
|
|
4954
|
+
onBlur: handleQuantityBlur
|
|
4932
4955
|
}
|
|
4933
4956
|
),
|
|
4934
4957
|
/* @__PURE__ */ jsxs(
|
|
@@ -4953,15 +4976,9 @@ function AddToCart(rawProps) {
|
|
|
4953
4976
|
className: "propeller-add-to-cart__quantity w-full md:w-16 h-10 text-center text-sm border border-input rounded-control focus:ring-2 focus:ring-secondary focus:border-transparent [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none",
|
|
4954
4977
|
min: getMinQuantity(props.product),
|
|
4955
4978
|
step: getStep(props.product),
|
|
4956
|
-
value: quantity,
|
|
4957
|
-
onChange: (e) =>
|
|
4958
|
-
|
|
4959
|
-
const min = getMinQuantity(props.product);
|
|
4960
|
-
const step = getStep(props.product);
|
|
4961
|
-
if (!isNaN(val) && val >= min) {
|
|
4962
|
-
setQuantity(Math.round((val - min) / step) * step + min);
|
|
4963
|
-
}
|
|
4964
|
-
}
|
|
4979
|
+
value: quantityDraft ?? quantity,
|
|
4980
|
+
onChange: (e) => handleQuantityInput(e.target.value),
|
|
4981
|
+
onBlur: handleQuantityBlur
|
|
4965
4982
|
}
|
|
4966
4983
|
) : null,
|
|
4967
4984
|
/* @__PURE__ */ jsxs(
|
|
@@ -7638,7 +7655,7 @@ function CartIconAndSidebar(rawProps) {
|
|
|
7638
7655
|
]
|
|
7639
7656
|
}
|
|
7640
7657
|
),
|
|
7641
|
-
props.showTotals && isHovered && totalItems > 0 ? /* @__PURE__ */ jsxs("div", { className: "propeller-cart-icon__popover absolute top-full right-0 mt-1 z-40 bg-popover border border-border rounded-container shadow-lg px-3 py-2 min-w-[140px] text-sm whitespace-nowrap", children: [
|
|
7658
|
+
props.showTotals && isHovered && totalItems > 0 ? /* @__PURE__ */ jsxs("div", { className: "propeller-cart-icon__popover absolute top-full right-0 mt-1 z-40 bg-popover text-foreground border border-border rounded-container shadow-lg px-3 py-2 min-w-[140px] text-sm whitespace-nowrap", children: [
|
|
7642
7659
|
/* @__PURE__ */ jsxs("div", { className: "flex justify-between gap-4", children: [
|
|
7643
7660
|
/* @__PURE__ */ jsx("span", { className: "propeller-cart-icon__popover-label text-muted-foreground", children: totalLabel("totalLabel") }),
|
|
7644
7661
|
/* @__PURE__ */ jsx("span", { className: "propeller-cart-icon__popover-total font-semibold text-foreground", children: totalPrice })
|
|
@@ -7666,7 +7683,7 @@ function CartIconAndSidebar(rawProps) {
|
|
|
7666
7683
|
"aria-modal": "true",
|
|
7667
7684
|
"aria-label": sidebarTitle,
|
|
7668
7685
|
"data-open": sidebarOpen ? "true" : "false",
|
|
7669
|
-
className: cn(`propeller-cart-icon__sidebar fixed inset-y-0 right-0 z-[70] w-full max-w-md bg-card shadow-2xl transform transition-transform duration-300 ease-in-out border-l border-border${sidebarOpen ? " translate-x-0" : " translate-x-full"}${props.sidebarClassName ? " " + props.sidebarClassName : ""}`),
|
|
7686
|
+
className: cn(`propeller-cart-icon__sidebar fixed inset-y-0 right-0 z-[70] w-full max-w-md bg-card text-foreground shadow-2xl transform transition-transform duration-300 ease-in-out border-l border-border${sidebarOpen ? " translate-x-0" : " translate-x-full"}${props.sidebarClassName ? " " + props.sidebarClassName : ""}`),
|
|
7670
7687
|
children: /* @__PURE__ */ jsx("div", { className: "flex flex-col h-full", children: isMounted ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7671
7688
|
/* @__PURE__ */ jsxs("div", { className: "propeller-cart-icon__sidebar-header flex items-center justify-between px-5 py-4 border-b border-border", children: [
|
|
7672
7689
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
@@ -9327,7 +9344,7 @@ function ProductCardPrice(props) {
|
|
|
9327
9344
|
) });
|
|
9328
9345
|
}
|
|
9329
9346
|
function ProductCardAddToCart(props) {
|
|
9330
|
-
const { product, resolved } = useProductCard();
|
|
9347
|
+
const { product, derived, resolved, handleProductClick } = useProductCard();
|
|
9331
9348
|
if (isContentHidden(resolved.portalMode, resolved.user, resolved.isAuthenticated)) {
|
|
9332
9349
|
return /* @__PURE__ */ jsx(
|
|
9333
9350
|
LoginToOrderButton_default,
|
|
@@ -9338,6 +9355,19 @@ function ProductCardAddToCart(props) {
|
|
|
9338
9355
|
}
|
|
9339
9356
|
);
|
|
9340
9357
|
}
|
|
9358
|
+
if (isPriceOnRequest(product)) {
|
|
9359
|
+
return /* @__PURE__ */ jsx(
|
|
9360
|
+
"a",
|
|
9361
|
+
{
|
|
9362
|
+
href: derived.productUrl,
|
|
9363
|
+
onClick: handleProductClick,
|
|
9364
|
+
className: cn(
|
|
9365
|
+
`propeller-product-card__view-product inline-flex h-10 w-full items-center justify-center rounded-control bg-primary px-4 text-sm font-medium text-primary-foreground transition-opacity hover:opacity-90 ${props.className || ""}`
|
|
9366
|
+
),
|
|
9367
|
+
children: getLabel(resolved.priceLabels, "requestPrice", "Request a price")
|
|
9368
|
+
}
|
|
9369
|
+
);
|
|
9370
|
+
}
|
|
9341
9371
|
const InjectedAddToCart = resolved.addToCartComponent;
|
|
9342
9372
|
if (InjectedAddToCart) {
|
|
9343
9373
|
return /* @__PURE__ */ jsx(
|
|
@@ -11893,7 +11923,7 @@ function FavoriteListItemActions(props) {
|
|
|
11893
11923
|
className: props.className ?? "propeller-favorite-list-item__actions flex items-center gap-2 flex-shrink-0",
|
|
11894
11924
|
onClick: (e) => e.stopPropagation(),
|
|
11895
11925
|
children: [
|
|
11896
|
-
resolved.allowAddToCart !== false && isProduct && !!resolved.graphqlClient ? /* @__PURE__ */ jsx(
|
|
11926
|
+
resolved.allowAddToCart !== false && isProduct && !!resolved.graphqlClient && !isPriceOnRequest(product) ? /* @__PURE__ */ jsx(
|
|
11897
11927
|
AddToCart_default,
|
|
11898
11928
|
{
|
|
11899
11929
|
className: "flex items-center gap-2",
|
|
@@ -13870,6 +13900,28 @@ function OrderActions(rawProps) {
|
|
|
13870
13900
|
);
|
|
13871
13901
|
}
|
|
13872
13902
|
var OrderActions_default = OrderActions;
|
|
13903
|
+
var warnedOnce = /* @__PURE__ */ new Set();
|
|
13904
|
+
function warnOnce(key, message) {
|
|
13905
|
+
if (warnedOnce.has(key)) return;
|
|
13906
|
+
warnedOnce.add(key);
|
|
13907
|
+
console.warn(message);
|
|
13908
|
+
}
|
|
13909
|
+
function warnMissingProps(props) {
|
|
13910
|
+
if (process.env.NODE_ENV === "production") return;
|
|
13911
|
+
if (!props.language) {
|
|
13912
|
+
warnOnce(
|
|
13913
|
+
"language",
|
|
13914
|
+
"[OrderItemCard] No `language` prop: product names resolve in the default catalogue language, whatever page they are on. This component reads no context (it ships from the RSC-safe /pure entry), so the host must pass it."
|
|
13915
|
+
);
|
|
13916
|
+
}
|
|
13917
|
+
const linkable = props.titleLinkable !== void 0 ? props.titleLinkable : true;
|
|
13918
|
+
if (linkable && !props.configuration?.urls) {
|
|
13919
|
+
warnOnce(
|
|
13920
|
+
"urls",
|
|
13921
|
+
"[OrderItemCard] Title is linkable but no `configuration.urls` was passed: links fall back to a literal /product/:id/:slug and lose the locale prefix. Pass the url builders from the host, or set titleLinkable={false}."
|
|
13922
|
+
);
|
|
13923
|
+
}
|
|
13924
|
+
}
|
|
13873
13925
|
function getProductName3(orderItem, language) {
|
|
13874
13926
|
return getLocalizedValue(orderItem?.product?.names, language, "") || orderItem?.name || "Unknown Product";
|
|
13875
13927
|
}
|
|
@@ -13893,6 +13945,7 @@ function getProductUrl(orderItem, language, urls) {
|
|
|
13893
13945
|
function OrderItemCard(props) {
|
|
13894
13946
|
const item = props.orderItem;
|
|
13895
13947
|
const isChildItem = props.isChildItem || false;
|
|
13948
|
+
warnMissingProps(props);
|
|
13896
13949
|
const titleLinkable = props.titleLinkable !== void 0 ? props.titleLinkable : true;
|
|
13897
13950
|
const showImage = isChildItem ? false : props.showImage !== void 0 ? props.showImage : true;
|
|
13898
13951
|
const showSku = isChildItem ? false : props.showSku !== void 0 ? props.showSku : true;
|
|
@@ -14656,7 +14709,7 @@ function RequestPriceButton(props) {
|
|
|
14656
14709
|
if (props.onRequestPrice) props.onRequestPrice();
|
|
14657
14710
|
},
|
|
14658
14711
|
children: [
|
|
14659
|
-
/* @__PURE__ */ jsx(
|
|
14712
|
+
/* @__PURE__ */ jsx(Tag, { className: "propeller-request-price__icon w-4 h-4", "aria-hidden": "true" }),
|
|
14660
14713
|
label
|
|
14661
14714
|
]
|
|
14662
14715
|
}
|
|
@@ -14664,6 +14717,10 @@ function RequestPriceButton(props) {
|
|
|
14664
14717
|
}
|
|
14665
14718
|
var RequestPriceButton_default = RequestPriceButton;
|
|
14666
14719
|
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
|
+
}
|
|
14667
14724
|
function blankRow() {
|
|
14668
14725
|
ROW_SEQ += 1;
|
|
14669
14726
|
return {
|
|
@@ -14675,6 +14732,7 @@ function blankRow() {
|
|
|
14675
14732
|
grossPrice: 0,
|
|
14676
14733
|
quantity: 1,
|
|
14677
14734
|
minQuantity: 1,
|
|
14735
|
+
step: 1,
|
|
14678
14736
|
matches: [],
|
|
14679
14737
|
searching: false,
|
|
14680
14738
|
searched: false
|
|
@@ -14768,6 +14826,7 @@ function QuickOrderInner(props) {
|
|
|
14768
14826
|
grossPrice: match.grossPrice,
|
|
14769
14827
|
quantity: match.minQuantity,
|
|
14770
14828
|
minQuantity: match.minQuantity,
|
|
14829
|
+
step: match.step,
|
|
14771
14830
|
matches: [],
|
|
14772
14831
|
searching: false,
|
|
14773
14832
|
searched: false
|
|
@@ -14780,7 +14839,7 @@ function QuickOrderInner(props) {
|
|
|
14780
14839
|
const n = parseInt(raw, 10);
|
|
14781
14840
|
setRows(
|
|
14782
14841
|
(prev) => prev.map(
|
|
14783
|
-
(r) => r.key === key ? { ...r, quantity:
|
|
14842
|
+
(r) => r.key === key ? { ...r, quantity: snapToStep(n, r.minQuantity, r.step) } : r
|
|
14784
14843
|
)
|
|
14785
14844
|
);
|
|
14786
14845
|
},
|
|
@@ -14833,7 +14892,8 @@ function QuickOrderInner(props) {
|
|
|
14833
14892
|
netPrice: exact.netPrice,
|
|
14834
14893
|
grossPrice: exact.grossPrice,
|
|
14835
14894
|
quantity: Math.max(exact.minQuantity, line.quantity),
|
|
14836
|
-
minQuantity: exact.minQuantity
|
|
14895
|
+
minQuantity: exact.minQuantity,
|
|
14896
|
+
step: exact.step
|
|
14837
14897
|
});
|
|
14838
14898
|
}
|
|
14839
14899
|
if (resolved.length) {
|
|
@@ -14964,7 +15024,7 @@ function QuickOrderInner(props) {
|
|
|
14964
15024
|
{
|
|
14965
15025
|
type: "number",
|
|
14966
15026
|
min: r.minQuantity,
|
|
14967
|
-
step: 1,
|
|
15027
|
+
step: r.step || 1,
|
|
14968
15028
|
value: r.productId ? r.quantity : "",
|
|
14969
15029
|
disabled: !r.productId,
|
|
14970
15030
|
onChange: (e) => setQuantity(r.key, e.target.value),
|
|
@@ -19332,7 +19392,7 @@ function SearchBar(rawProps) {
|
|
|
19332
19392
|
// boilerplate's dark header band happened to make work: on a
|
|
19333
19393
|
// light header `bg-white/95 border-white/20` rendered as an
|
|
19334
19394
|
// invisible input.
|
|
19335
|
-
"propeller-search-bar__input w-full pl-10 pr-10 py-2 bg-card border border-input rounded-container focus:outline-none focus:ring-2 focus:ring-secondary placeholder:text-muted-foreground",
|
|
19395
|
+
"propeller-search-bar__input w-full pl-10 pr-10 py-2 bg-card text-foreground border border-input rounded-container focus:outline-none focus:ring-2 focus:ring-secondary placeholder:text-muted-foreground",
|
|
19336
19396
|
props.inputClassName
|
|
19337
19397
|
),
|
|
19338
19398
|
placeholder: props.placeholder || "Search products...",
|
|
@@ -19342,7 +19402,7 @@ function SearchBar(rawProps) {
|
|
|
19342
19402
|
),
|
|
19343
19403
|
searchLoading ? /* @__PURE__ */ jsx("div", { className: "propeller-search-bar__spinner-wrapper absolute right-3 top-1/2 transform -translate-y-1/2", children: /* @__PURE__ */ jsx("div", { className: "propeller-search-bar__spinner animate-spin rounded-full h-5 w-5 border-b-2 border-primary" }) }) : null
|
|
19344
19404
|
] }) }),
|
|
19345
|
-
showDropdown ? /* @__PURE__ */ jsxs("div", { className: "propeller-search-bar__dropdown absolute top-full left-0 right-0 mt-2 bg-card rounded-container shadow-xl border border-border z-50 flex flex-col max-h-96", children: [
|
|
19405
|
+
showDropdown ? /* @__PURE__ */ jsxs("div", { className: "propeller-search-bar__dropdown absolute top-full left-0 right-0 mt-2 bg-card text-foreground rounded-container shadow-xl border border-border z-50 flex flex-col max-h-96", children: [
|
|
19346
19406
|
results.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
19347
19407
|
/* @__PURE__ */ jsx("div", { className: "propeller-search-bar__results flex-1 overflow-y-auto", children: results?.map((result, index) => {
|
|
19348
19408
|
const ResultTag = props.getResultHref ? "a" : "div";
|