@wix/headless-stores 0.0.74 → 0.0.76
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/cjs/dist/react/Product.d.ts +189 -0
- package/cjs/dist/react/Product.js +193 -0
- package/cjs/dist/react/ProductList.d.ts +1 -1
- package/cjs/dist/react/ProductList.js +1 -1
- package/cjs/dist/react/core/ProductVariantSelector.d.ts +2 -0
- package/cjs/dist/react/core/ProductVariantSelector.js +4 -0
- package/dist/react/Product.d.ts +189 -0
- package/dist/react/Product.js +193 -0
- package/dist/react/ProductList.d.ts +1 -1
- package/dist/react/ProductList.js +1 -1
- package/dist/react/core/ProductVariantSelector.d.ts +2 -0
- package/dist/react/core/ProductVariantSelector.js +4 -0
- package/package.json +3 -3
|
@@ -641,6 +641,135 @@ export declare const ProductMediaGallery: React.ForwardRefExoticComponent<Produc
|
|
|
641
641
|
* Alias for ProductMediaGallery to match the documented API
|
|
642
642
|
*/
|
|
643
643
|
export { ProductMediaGallery as MediaGallery };
|
|
644
|
+
/**
|
|
645
|
+
* Props for Product Quantity component
|
|
646
|
+
*/
|
|
647
|
+
export interface ProductQuantityProps {
|
|
648
|
+
/** Whether to render as a child component */
|
|
649
|
+
asChild?: boolean;
|
|
650
|
+
/** Custom render function when using asChild */
|
|
651
|
+
children?: AsChildChildren<{
|
|
652
|
+
selectedQuantity: number;
|
|
653
|
+
availableQuantity: number | null;
|
|
654
|
+
inStock: boolean;
|
|
655
|
+
isPreOrderEnabled: boolean;
|
|
656
|
+
setSelectedQuantity: (quantity: number) => void;
|
|
657
|
+
}>;
|
|
658
|
+
/** CSS classes to apply to the default element */
|
|
659
|
+
className?: string;
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Props for Product Quantity sub-components
|
|
663
|
+
*/
|
|
664
|
+
export interface ProductQuantitySubComponentProps {
|
|
665
|
+
/** CSS classes to apply to the element */
|
|
666
|
+
className?: string;
|
|
667
|
+
/** Whether to render as a child component */
|
|
668
|
+
asChild?: boolean;
|
|
669
|
+
/** Custom render function when using asChild */
|
|
670
|
+
children?: AsChildChildren<{
|
|
671
|
+
disabled: boolean;
|
|
672
|
+
}>;
|
|
673
|
+
/** Whether the component is disabled */
|
|
674
|
+
disabled?: boolean;
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Product quantity selector component that integrates with the selected variant.
|
|
678
|
+
* Provides quantity controls with stock validation and pre-order support.
|
|
679
|
+
* Uses a compound component pattern with Root, Decrement, Input, Increment, and Raw sub-components.
|
|
680
|
+
*
|
|
681
|
+
* @component
|
|
682
|
+
* @example
|
|
683
|
+
* ```tsx
|
|
684
|
+
* // Compound component usage (recommended)
|
|
685
|
+
* <Product.Quantity.Root className="flex items-center gap-3">
|
|
686
|
+
* <div className="flex items-center border border-brand-light rounded-lg">
|
|
687
|
+
* <Product.Quantity.Decrement className="px-3 py-1 hover:bg-surface-primary transition-colors" />
|
|
688
|
+
* <Product.Quantity.Input className="w-16 text-center py-1 border-x border-brand-light focus:outline-none focus:ring-2 focus:ring-brand-primary" />
|
|
689
|
+
* <Product.Quantity.Increment className="px-3 py-1 hover:bg-surface-primary transition-colors" />
|
|
690
|
+
* </div>
|
|
691
|
+
* <Product.Quantity.Raw asChild>
|
|
692
|
+
* {({ availableQuantity, inStock, isPreOrderEnabled }) => (
|
|
693
|
+
* <div>
|
|
694
|
+
* {!inStock && isPreOrderEnabled && availableQuantity && (
|
|
695
|
+
* <span className="text-content-muted text-sm">
|
|
696
|
+
* Max: {availableQuantity} Pre Order
|
|
697
|
+
* </span>
|
|
698
|
+
* )}
|
|
699
|
+
* {inStock && availableQuantity && availableQuantity < 10 && (
|
|
700
|
+
* <span className="text-content-muted text-sm">
|
|
701
|
+
* Only {availableQuantity} left in stock
|
|
702
|
+
* </span>
|
|
703
|
+
* )}
|
|
704
|
+
* </div>
|
|
705
|
+
* )}
|
|
706
|
+
* </Product.Quantity.Raw>
|
|
707
|
+
* </Product.Quantity.Root>
|
|
708
|
+
*
|
|
709
|
+
* // Legacy asChild usage (still supported)
|
|
710
|
+
* <Product.Quantity asChild>
|
|
711
|
+
* {({ selectedQuantity, availableQuantity, inStock, setSelectedQuantity }) => (
|
|
712
|
+
* <div className="flex items-center gap-3">
|
|
713
|
+
* <div className="flex items-center border border-brand-light rounded-lg">
|
|
714
|
+
* <button
|
|
715
|
+
* onClick={() => setSelectedQuantity(selectedQuantity - 1)}
|
|
716
|
+
* disabled={selectedQuantity <= 1 || (!inStock && !isPreOrderEnabled)}
|
|
717
|
+
* className="px-3 py-2 hover:bg-surface-primary disabled:opacity-50"
|
|
718
|
+
* >
|
|
719
|
+
* -
|
|
720
|
+
* </button>
|
|
721
|
+
* <span className="px-4 py-2 border-x border-brand-light min-w-[3rem] text-center">
|
|
722
|
+
* {selectedQuantity}
|
|
723
|
+
* </span>
|
|
724
|
+
* <button
|
|
725
|
+
* onClick={() => setSelectedQuantity(selectedQuantity + 1)}
|
|
726
|
+
* disabled={availableQuantity && selectedQuantity >= availableQuantity}
|
|
727
|
+
* className="px-3 py-2 hover:bg-surface-primary disabled:opacity-50"
|
|
728
|
+
* >
|
|
729
|
+
* +
|
|
730
|
+
* </button>
|
|
731
|
+
* </div>
|
|
732
|
+
* </div>
|
|
733
|
+
* )}
|
|
734
|
+
* </Product.Quantity>
|
|
735
|
+
* ```
|
|
736
|
+
*/
|
|
737
|
+
export declare const ProductQuantity: React.ForwardRefExoticComponent<ProductQuantityProps & React.RefAttributes<HTMLDivElement>>;
|
|
738
|
+
/**
|
|
739
|
+
* Product Quantity Decrement component.
|
|
740
|
+
* Automatically handles disabled state based on stock and pre-order settings.
|
|
741
|
+
* Must be used within Product.Quantity.Root.
|
|
742
|
+
*
|
|
743
|
+
* @component
|
|
744
|
+
* @example
|
|
745
|
+
* ```tsx
|
|
746
|
+
* <Product.Quantity.Decrement className="px-3 py-1 hover:bg-surface-primary transition-colors" />
|
|
747
|
+
* ```
|
|
748
|
+
*/
|
|
749
|
+
export declare const ProductQuantityDecrement: React.ForwardRefExoticComponent<ProductQuantitySubComponentProps & React.RefAttributes<HTMLButtonElement>>;
|
|
750
|
+
/**
|
|
751
|
+
* Product Quantity Input component.
|
|
752
|
+
* Displays the current quantity value. Must be used within Product.Quantity.Root.
|
|
753
|
+
*
|
|
754
|
+
* @component
|
|
755
|
+
* @example
|
|
756
|
+
* ```tsx
|
|
757
|
+
* <Product.Quantity.Input className="w-16 text-center py-1 border-x border-brand-light focus:outline-none focus:ring-2 focus:ring-brand-primary" />
|
|
758
|
+
* ```
|
|
759
|
+
*/
|
|
760
|
+
export declare const ProductQuantityInput: React.ForwardRefExoticComponent<ProductQuantitySubComponentProps & React.RefAttributes<HTMLInputElement>>;
|
|
761
|
+
/**
|
|
762
|
+
* Product Quantity Increment component.
|
|
763
|
+
* Automatically handles disabled state based on stock availability.
|
|
764
|
+
* Must be used within Product.Quantity.Root.
|
|
765
|
+
*
|
|
766
|
+
* @component
|
|
767
|
+
* @example
|
|
768
|
+
* ```tsx
|
|
769
|
+
* <Product.Quantity.Increment className="px-3 py-1 hover:bg-surface-primary transition-colors" />
|
|
770
|
+
* ```
|
|
771
|
+
*/
|
|
772
|
+
export declare const ProductQuantityIncrement: React.ForwardRefExoticComponent<ProductQuantitySubComponentProps & React.RefAttributes<HTMLButtonElement>>;
|
|
644
773
|
/**
|
|
645
774
|
* Props for Product Action components following the documented API
|
|
646
775
|
*/
|
|
@@ -660,6 +789,50 @@ export interface ProductActionProps {
|
|
|
660
789
|
/** Content to display when loading */
|
|
661
790
|
loadingState?: string | React.ReactNode;
|
|
662
791
|
}
|
|
792
|
+
/**
|
|
793
|
+
* Props for Product Quantity Raw component
|
|
794
|
+
*/
|
|
795
|
+
export interface ProductQuantityRawSubComponentProps {
|
|
796
|
+
/** CSS classes to apply to the element */
|
|
797
|
+
className?: string;
|
|
798
|
+
/** Whether to render as a child component */
|
|
799
|
+
asChild?: boolean;
|
|
800
|
+
/** Custom render function when using asChild */
|
|
801
|
+
children?: AsChildChildren<{
|
|
802
|
+
selectedQuantity: number;
|
|
803
|
+
availableQuantity: number;
|
|
804
|
+
inStock: boolean;
|
|
805
|
+
isPreOrderEnabled: boolean;
|
|
806
|
+
setSelectedQuantity: (quantity: number) => void;
|
|
807
|
+
}>;
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Product Quantity Raw component.
|
|
811
|
+
* Provides access to raw quantity data for custom stock messages and advanced use cases.
|
|
812
|
+
* Must be used within Product.Quantity.Root.
|
|
813
|
+
*
|
|
814
|
+
* @component
|
|
815
|
+
* @example
|
|
816
|
+
* ```tsx
|
|
817
|
+
* <Product.Quantity.Raw asChild>
|
|
818
|
+
* {({ availableQuantity, inStock, isPreOrderEnabled }) => (
|
|
819
|
+
* <div>
|
|
820
|
+
* {!inStock && isPreOrderEnabled && availableQuantity && (
|
|
821
|
+
* <span className="text-content-muted text-sm">
|
|
822
|
+
* Max: {availableQuantity} Pre Order
|
|
823
|
+
* </span>
|
|
824
|
+
* )}
|
|
825
|
+
* {inStock && availableQuantity && availableQuantity < 10 && (
|
|
826
|
+
* <span className="text-content-muted text-sm">
|
|
827
|
+
* Only {availableQuantity} left in stock
|
|
828
|
+
* </span>
|
|
829
|
+
* )}
|
|
830
|
+
* </div>
|
|
831
|
+
* )}
|
|
832
|
+
* </Product.Quantity.Raw>
|
|
833
|
+
* ```
|
|
834
|
+
*/
|
|
835
|
+
export declare const ProductQuantityRaw: React.ForwardRefExoticComponent<ProductQuantityRawSubComponentProps & React.RefAttributes<HTMLElement>>;
|
|
663
836
|
/**
|
|
664
837
|
* Add to cart action button component following the documented API.
|
|
665
838
|
* Automatically integrates with the selected variant and handles loading states.
|
|
@@ -687,3 +860,19 @@ export declare const Action: {
|
|
|
687
860
|
/** Pre-order action button */
|
|
688
861
|
readonly PreOrder: React.ForwardRefExoticComponent<ProductActionProps & React.RefAttributes<HTMLButtonElement>>;
|
|
689
862
|
};
|
|
863
|
+
/**
|
|
864
|
+
* Quantity namespace containing all product quantity components
|
|
865
|
+
* following the compound component pattern: Product.Quantity.Root, Product.Quantity.Decrement, etc.
|
|
866
|
+
*/
|
|
867
|
+
export declare const Quantity: {
|
|
868
|
+
/** Product quantity selector component */
|
|
869
|
+
readonly Root: React.ForwardRefExoticComponent<ProductQuantityProps & React.RefAttributes<HTMLDivElement>>;
|
|
870
|
+
/** Product quantity decrement component */
|
|
871
|
+
readonly Decrement: React.ForwardRefExoticComponent<ProductQuantitySubComponentProps & React.RefAttributes<HTMLButtonElement>>;
|
|
872
|
+
/** Product quantity input component */
|
|
873
|
+
readonly Input: React.ForwardRefExoticComponent<ProductQuantitySubComponentProps & React.RefAttributes<HTMLInputElement>>;
|
|
874
|
+
/** Product quantity increment component */
|
|
875
|
+
readonly Increment: React.ForwardRefExoticComponent<ProductQuantitySubComponentProps & React.RefAttributes<HTMLButtonElement>>;
|
|
876
|
+
/** Product quantity raw component */
|
|
877
|
+
readonly Raw: React.ForwardRefExoticComponent<ProductQuantityRawSubComponentProps & React.RefAttributes<HTMLElement>>;
|
|
878
|
+
};
|
|
@@ -4,6 +4,7 @@ import React from 'react';
|
|
|
4
4
|
import { Commerce } from '@wix/headless-ecom/react';
|
|
5
5
|
import { AsChildSlot } from '@wix/headless-utils/react';
|
|
6
6
|
import { MediaGallery } from '@wix/headless-media/react';
|
|
7
|
+
import { Quantity as QuantityComponent } from '@wix/headless-components/react';
|
|
7
8
|
import * as CoreProduct from './core/Product.js';
|
|
8
9
|
import * as ProductVariantSelector from './core/ProductVariantSelector.js';
|
|
9
10
|
import * as ProductModifiers from './core/ProductModifiers.js';
|
|
@@ -54,6 +55,11 @@ var TestIds;
|
|
|
54
55
|
TestIds["productActionAddToCart"] = "product-action-add-to-cart";
|
|
55
56
|
TestIds["productActionBuyNow"] = "product-action-buy-now";
|
|
56
57
|
TestIds["productActionPreOrder"] = "product-action-pre-order";
|
|
58
|
+
TestIds["productQuantity"] = "product-quantity";
|
|
59
|
+
TestIds["productQuantityDecrement"] = "product-quantity-decrement";
|
|
60
|
+
TestIds["productQuantityInput"] = "product-quantity-input";
|
|
61
|
+
TestIds["productQuantityIncrement"] = "product-quantity-increment";
|
|
62
|
+
TestIds["productQuantityRaw"] = "product-quantity-raw";
|
|
57
63
|
})(TestIds || (TestIds = {}));
|
|
58
64
|
/**
|
|
59
65
|
* Root component that provides all necessary service contexts for a complete product experience.
|
|
@@ -679,6 +685,177 @@ export const ProductMediaGallery = React.forwardRef((props, ref) => {
|
|
|
679
685
|
* Alias for ProductMediaGallery to match the documented API
|
|
680
686
|
*/
|
|
681
687
|
export { ProductMediaGallery as MediaGallery };
|
|
688
|
+
/**
|
|
689
|
+
* Product quantity selector component that integrates with the selected variant.
|
|
690
|
+
* Provides quantity controls with stock validation and pre-order support.
|
|
691
|
+
* Uses a compound component pattern with Root, Decrement, Input, Increment, and Raw sub-components.
|
|
692
|
+
*
|
|
693
|
+
* @component
|
|
694
|
+
* @example
|
|
695
|
+
* ```tsx
|
|
696
|
+
* // Compound component usage (recommended)
|
|
697
|
+
* <Product.Quantity.Root className="flex items-center gap-3">
|
|
698
|
+
* <div className="flex items-center border border-brand-light rounded-lg">
|
|
699
|
+
* <Product.Quantity.Decrement className="px-3 py-1 hover:bg-surface-primary transition-colors" />
|
|
700
|
+
* <Product.Quantity.Input className="w-16 text-center py-1 border-x border-brand-light focus:outline-none focus:ring-2 focus:ring-brand-primary" />
|
|
701
|
+
* <Product.Quantity.Increment className="px-3 py-1 hover:bg-surface-primary transition-colors" />
|
|
702
|
+
* </div>
|
|
703
|
+
* <Product.Quantity.Raw asChild>
|
|
704
|
+
* {({ availableQuantity, inStock, isPreOrderEnabled }) => (
|
|
705
|
+
* <div>
|
|
706
|
+
* {!inStock && isPreOrderEnabled && availableQuantity && (
|
|
707
|
+
* <span className="text-content-muted text-sm">
|
|
708
|
+
* Max: {availableQuantity} Pre Order
|
|
709
|
+
* </span>
|
|
710
|
+
* )}
|
|
711
|
+
* {inStock && availableQuantity && availableQuantity < 10 && (
|
|
712
|
+
* <span className="text-content-muted text-sm">
|
|
713
|
+
* Only {availableQuantity} left in stock
|
|
714
|
+
* </span>
|
|
715
|
+
* )}
|
|
716
|
+
* </div>
|
|
717
|
+
* )}
|
|
718
|
+
* </Product.Quantity.Raw>
|
|
719
|
+
* </Product.Quantity.Root>
|
|
720
|
+
*
|
|
721
|
+
* // Legacy asChild usage (still supported)
|
|
722
|
+
* <Product.Quantity asChild>
|
|
723
|
+
* {({ selectedQuantity, availableQuantity, inStock, setSelectedQuantity }) => (
|
|
724
|
+
* <div className="flex items-center gap-3">
|
|
725
|
+
* <div className="flex items-center border border-brand-light rounded-lg">
|
|
726
|
+
* <button
|
|
727
|
+
* onClick={() => setSelectedQuantity(selectedQuantity - 1)}
|
|
728
|
+
* disabled={selectedQuantity <= 1 || (!inStock && !isPreOrderEnabled)}
|
|
729
|
+
* className="px-3 py-2 hover:bg-surface-primary disabled:opacity-50"
|
|
730
|
+
* >
|
|
731
|
+
* -
|
|
732
|
+
* </button>
|
|
733
|
+
* <span className="px-4 py-2 border-x border-brand-light min-w-[3rem] text-center">
|
|
734
|
+
* {selectedQuantity}
|
|
735
|
+
* </span>
|
|
736
|
+
* <button
|
|
737
|
+
* onClick={() => setSelectedQuantity(selectedQuantity + 1)}
|
|
738
|
+
* disabled={availableQuantity && selectedQuantity >= availableQuantity}
|
|
739
|
+
* className="px-3 py-2 hover:bg-surface-primary disabled:opacity-50"
|
|
740
|
+
* >
|
|
741
|
+
* +
|
|
742
|
+
* </button>
|
|
743
|
+
* </div>
|
|
744
|
+
* </div>
|
|
745
|
+
* )}
|
|
746
|
+
* </Product.Quantity>
|
|
747
|
+
* ```
|
|
748
|
+
*/
|
|
749
|
+
export const ProductQuantity = React.forwardRef((props, ref) => {
|
|
750
|
+
const { asChild, children, className } = props;
|
|
751
|
+
return (_jsx(ProductVariantSelector.Stock, { children: ({ inStock, isPreOrderEnabled, availableQuantity, selectedQuantity, setSelectedQuantity, }) => {
|
|
752
|
+
const renderProps = {
|
|
753
|
+
selectedQuantity,
|
|
754
|
+
availableQuantity,
|
|
755
|
+
inStock,
|
|
756
|
+
isPreOrderEnabled,
|
|
757
|
+
setSelectedQuantity,
|
|
758
|
+
};
|
|
759
|
+
if (asChild && children) {
|
|
760
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productQuantity, customElement: children, customElementProps: renderProps }));
|
|
761
|
+
}
|
|
762
|
+
return (_jsx(QuantityComponent.Root, { initialValue: selectedQuantity, onValueChange: setSelectedQuantity, className: className, ref: ref, "data-testid": TestIds.productQuantity, children: React.isValidElement(children) ? children : null }));
|
|
763
|
+
} }));
|
|
764
|
+
});
|
|
765
|
+
/**
|
|
766
|
+
* Product Quantity Decrement component.
|
|
767
|
+
* Automatically handles disabled state based on stock and pre-order settings.
|
|
768
|
+
* Must be used within Product.Quantity.Root.
|
|
769
|
+
*
|
|
770
|
+
* @component
|
|
771
|
+
* @example
|
|
772
|
+
* ```tsx
|
|
773
|
+
* <Product.Quantity.Decrement className="px-3 py-1 hover:bg-surface-primary transition-colors" />
|
|
774
|
+
* ```
|
|
775
|
+
*/
|
|
776
|
+
export const ProductQuantityDecrement = React.forwardRef((props, ref) => {
|
|
777
|
+
const { asChild, children, className } = props;
|
|
778
|
+
return (_jsx(ProductVariantSelector.Stock, { children: ({ selectedQuantity, inStock, isPreOrderEnabled }) => {
|
|
779
|
+
const disabled = selectedQuantity <= 1 || (!inStock && !isPreOrderEnabled);
|
|
780
|
+
if (asChild && children) {
|
|
781
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productQuantityDecrement, customElement: children, customElementProps: { disabled } }));
|
|
782
|
+
}
|
|
783
|
+
return (_jsx(QuantityComponent.Decrement, { className: className, ref: ref, "data-testid": TestIds.productQuantityDecrement, disabled: disabled }));
|
|
784
|
+
} }));
|
|
785
|
+
});
|
|
786
|
+
/**
|
|
787
|
+
* Product Quantity Input component.
|
|
788
|
+
* Displays the current quantity value. Must be used within Product.Quantity.Root.
|
|
789
|
+
*
|
|
790
|
+
* @component
|
|
791
|
+
* @example
|
|
792
|
+
* ```tsx
|
|
793
|
+
* <Product.Quantity.Input className="w-16 text-center py-1 border-x border-brand-light focus:outline-none focus:ring-2 focus:ring-brand-primary" />
|
|
794
|
+
* ```
|
|
795
|
+
*/
|
|
796
|
+
export const ProductQuantityInput = React.forwardRef((props, ref) => {
|
|
797
|
+
const { asChild, children, className, disabled = true } = props;
|
|
798
|
+
return (_jsx(ProductVariantSelector.Stock, { children: () => {
|
|
799
|
+
if (asChild && children) {
|
|
800
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, disabled: disabled, "data-testid": TestIds.productQuantityInput, customElement: children, customElementProps: {} }));
|
|
801
|
+
}
|
|
802
|
+
return (_jsx(QuantityComponent.Input, { className: className, disabled: disabled, ref: ref, "data-testid": TestIds.productQuantityInput }));
|
|
803
|
+
} }));
|
|
804
|
+
});
|
|
805
|
+
/**
|
|
806
|
+
* Product Quantity Increment component.
|
|
807
|
+
* Automatically handles disabled state based on stock availability.
|
|
808
|
+
* Must be used within Product.Quantity.Root.
|
|
809
|
+
*
|
|
810
|
+
* @component
|
|
811
|
+
* @example
|
|
812
|
+
* ```tsx
|
|
813
|
+
* <Product.Quantity.Increment className="px-3 py-1 hover:bg-surface-primary transition-colors" />
|
|
814
|
+
* ```
|
|
815
|
+
*/
|
|
816
|
+
export const ProductQuantityIncrement = React.forwardRef((props, ref) => {
|
|
817
|
+
const { asChild, children, className } = props;
|
|
818
|
+
return (_jsx(ProductVariantSelector.Stock, { children: ({ selectedQuantity, availableQuantity, inStock, isPreOrderEnabled, }) => {
|
|
819
|
+
const disabled = (!!availableQuantity && selectedQuantity >= availableQuantity) ||
|
|
820
|
+
(!inStock && !isPreOrderEnabled);
|
|
821
|
+
if (asChild && children) {
|
|
822
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productQuantityIncrement, customElement: children, customElementProps: { disabled } }));
|
|
823
|
+
}
|
|
824
|
+
return (_jsx(QuantityComponent.Increment, { className: className, ref: ref, "data-testid": TestIds.productQuantityIncrement, disabled: disabled }));
|
|
825
|
+
} }));
|
|
826
|
+
});
|
|
827
|
+
/**
|
|
828
|
+
* Product Quantity Raw component.
|
|
829
|
+
* Provides access to raw quantity data for custom stock messages and advanced use cases.
|
|
830
|
+
* Must be used within Product.Quantity.Root.
|
|
831
|
+
*
|
|
832
|
+
* @component
|
|
833
|
+
* @example
|
|
834
|
+
* ```tsx
|
|
835
|
+
* <Product.Quantity.Raw asChild>
|
|
836
|
+
* {({ availableQuantity, inStock, isPreOrderEnabled }) => (
|
|
837
|
+
* <div>
|
|
838
|
+
* {!inStock && isPreOrderEnabled && availableQuantity && (
|
|
839
|
+
* <span className="text-content-muted text-sm">
|
|
840
|
+
* Max: {availableQuantity} Pre Order
|
|
841
|
+
* </span>
|
|
842
|
+
* )}
|
|
843
|
+
* {inStock && availableQuantity && availableQuantity < 10 && (
|
|
844
|
+
* <span className="text-content-muted text-sm">
|
|
845
|
+
* Only {availableQuantity} left in stock
|
|
846
|
+
* </span>
|
|
847
|
+
* )}
|
|
848
|
+
* </div>
|
|
849
|
+
* )}
|
|
850
|
+
* </Product.Quantity.Raw>
|
|
851
|
+
* ```
|
|
852
|
+
*/
|
|
853
|
+
export const ProductQuantityRaw = React.forwardRef((props, ref) => {
|
|
854
|
+
const { asChild, children, className } = props;
|
|
855
|
+
return (_jsx(ProductVariantSelector.Stock, { children: (renderProps) => {
|
|
856
|
+
return (_jsx(AsChildSlot, { ref: ref, customElement: children, asChild: asChild, className: className, "data-testid": TestIds.productQuantityRaw, customElementProps: renderProps }));
|
|
857
|
+
} }));
|
|
858
|
+
});
|
|
682
859
|
/**
|
|
683
860
|
* Add to cart action button component following the documented API.
|
|
684
861
|
* Automatically integrates with the selected variant and handles loading states.
|
|
@@ -758,3 +935,19 @@ export const Action = {
|
|
|
758
935
|
/** Pre-order action button */
|
|
759
936
|
PreOrder: ProductActionPreOrder,
|
|
760
937
|
};
|
|
938
|
+
/**
|
|
939
|
+
* Quantity namespace containing all product quantity components
|
|
940
|
+
* following the compound component pattern: Product.Quantity.Root, Product.Quantity.Decrement, etc.
|
|
941
|
+
*/
|
|
942
|
+
export const Quantity = {
|
|
943
|
+
/** Product quantity selector component */
|
|
944
|
+
Root: ProductQuantity,
|
|
945
|
+
/** Product quantity decrement component */
|
|
946
|
+
Decrement: ProductQuantityDecrement,
|
|
947
|
+
/** Product quantity input component */
|
|
948
|
+
Input: ProductQuantityInput,
|
|
949
|
+
/** Product quantity increment component */
|
|
950
|
+
Increment: ProductQuantityIncrement,
|
|
951
|
+
/** Product quantity raw component */
|
|
952
|
+
Raw: ProductQuantityRaw,
|
|
953
|
+
};
|
|
@@ -4,7 +4,7 @@ import React from 'react';
|
|
|
4
4
|
import type { ProductsListServiceConfig } from '../services/products-list-service.js';
|
|
5
5
|
import { productsV3 } from '@wix/stores';
|
|
6
6
|
import { AsChildChildren } from '@wix/headless-utils/react';
|
|
7
|
-
export { Filter, CategoryFilter } from './core/ProductListFilters.js';
|
|
7
|
+
export { Filter, CategoryFilter, ResetTrigger as FilterResetTrigger, } from './core/ProductListFilters.js';
|
|
8
8
|
/**
|
|
9
9
|
* Props for the ProductList root component following the documented API
|
|
10
10
|
*/
|
|
@@ -8,7 +8,7 @@ import * as CoreProductListPagination from './core/ProductListPagination.js';
|
|
|
8
8
|
import { ProductListSort as ProductListSortPrimitive } from './core/ProductListSort.js';
|
|
9
9
|
import * as Product from './Product.js';
|
|
10
10
|
import { AsChildSlot } from '@wix/headless-utils/react';
|
|
11
|
-
export { Filter, CategoryFilter } from './core/ProductListFilters.js';
|
|
11
|
+
export { Filter, CategoryFilter, ResetTrigger as FilterResetTrigger, } from './core/ProductListFilters.js';
|
|
12
12
|
var TestIds;
|
|
13
13
|
(function (TestIds) {
|
|
14
14
|
TestIds["productListRoot"] = "product-list-root";
|
|
@@ -236,6 +236,8 @@ export interface StockRenderProps {
|
|
|
236
236
|
incrementQuantity: () => void;
|
|
237
237
|
/** Function to decrement quantity */
|
|
238
238
|
decrementQuantity: () => void;
|
|
239
|
+
/** Function to set selected quantity */
|
|
240
|
+
setSelectedQuantity: (quantity: number) => void;
|
|
239
241
|
}
|
|
240
242
|
/**
|
|
241
243
|
* Headless component for product stock status
|
|
@@ -238,6 +238,9 @@ export function Stock(props) {
|
|
|
238
238
|
const decrementQuantity = () => {
|
|
239
239
|
variantService.decrementQuantity();
|
|
240
240
|
};
|
|
241
|
+
const setSelectedQuantity = (quantity) => {
|
|
242
|
+
variantService.setSelectedQuantity(quantity);
|
|
243
|
+
};
|
|
241
244
|
return props.children({
|
|
242
245
|
inStock,
|
|
243
246
|
availableQuantity,
|
|
@@ -247,6 +250,7 @@ export function Stock(props) {
|
|
|
247
250
|
trackInventory,
|
|
248
251
|
selectedQuantity,
|
|
249
252
|
incrementQuantity,
|
|
253
|
+
setSelectedQuantity,
|
|
250
254
|
decrementQuantity,
|
|
251
255
|
});
|
|
252
256
|
}
|
package/dist/react/Product.d.ts
CHANGED
|
@@ -641,6 +641,135 @@ export declare const ProductMediaGallery: React.ForwardRefExoticComponent<Produc
|
|
|
641
641
|
* Alias for ProductMediaGallery to match the documented API
|
|
642
642
|
*/
|
|
643
643
|
export { ProductMediaGallery as MediaGallery };
|
|
644
|
+
/**
|
|
645
|
+
* Props for Product Quantity component
|
|
646
|
+
*/
|
|
647
|
+
export interface ProductQuantityProps {
|
|
648
|
+
/** Whether to render as a child component */
|
|
649
|
+
asChild?: boolean;
|
|
650
|
+
/** Custom render function when using asChild */
|
|
651
|
+
children?: AsChildChildren<{
|
|
652
|
+
selectedQuantity: number;
|
|
653
|
+
availableQuantity: number | null;
|
|
654
|
+
inStock: boolean;
|
|
655
|
+
isPreOrderEnabled: boolean;
|
|
656
|
+
setSelectedQuantity: (quantity: number) => void;
|
|
657
|
+
}>;
|
|
658
|
+
/** CSS classes to apply to the default element */
|
|
659
|
+
className?: string;
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Props for Product Quantity sub-components
|
|
663
|
+
*/
|
|
664
|
+
export interface ProductQuantitySubComponentProps {
|
|
665
|
+
/** CSS classes to apply to the element */
|
|
666
|
+
className?: string;
|
|
667
|
+
/** Whether to render as a child component */
|
|
668
|
+
asChild?: boolean;
|
|
669
|
+
/** Custom render function when using asChild */
|
|
670
|
+
children?: AsChildChildren<{
|
|
671
|
+
disabled: boolean;
|
|
672
|
+
}>;
|
|
673
|
+
/** Whether the component is disabled */
|
|
674
|
+
disabled?: boolean;
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Product quantity selector component that integrates with the selected variant.
|
|
678
|
+
* Provides quantity controls with stock validation and pre-order support.
|
|
679
|
+
* Uses a compound component pattern with Root, Decrement, Input, Increment, and Raw sub-components.
|
|
680
|
+
*
|
|
681
|
+
* @component
|
|
682
|
+
* @example
|
|
683
|
+
* ```tsx
|
|
684
|
+
* // Compound component usage (recommended)
|
|
685
|
+
* <Product.Quantity.Root className="flex items-center gap-3">
|
|
686
|
+
* <div className="flex items-center border border-brand-light rounded-lg">
|
|
687
|
+
* <Product.Quantity.Decrement className="px-3 py-1 hover:bg-surface-primary transition-colors" />
|
|
688
|
+
* <Product.Quantity.Input className="w-16 text-center py-1 border-x border-brand-light focus:outline-none focus:ring-2 focus:ring-brand-primary" />
|
|
689
|
+
* <Product.Quantity.Increment className="px-3 py-1 hover:bg-surface-primary transition-colors" />
|
|
690
|
+
* </div>
|
|
691
|
+
* <Product.Quantity.Raw asChild>
|
|
692
|
+
* {({ availableQuantity, inStock, isPreOrderEnabled }) => (
|
|
693
|
+
* <div>
|
|
694
|
+
* {!inStock && isPreOrderEnabled && availableQuantity && (
|
|
695
|
+
* <span className="text-content-muted text-sm">
|
|
696
|
+
* Max: {availableQuantity} Pre Order
|
|
697
|
+
* </span>
|
|
698
|
+
* )}
|
|
699
|
+
* {inStock && availableQuantity && availableQuantity < 10 && (
|
|
700
|
+
* <span className="text-content-muted text-sm">
|
|
701
|
+
* Only {availableQuantity} left in stock
|
|
702
|
+
* </span>
|
|
703
|
+
* )}
|
|
704
|
+
* </div>
|
|
705
|
+
* )}
|
|
706
|
+
* </Product.Quantity.Raw>
|
|
707
|
+
* </Product.Quantity.Root>
|
|
708
|
+
*
|
|
709
|
+
* // Legacy asChild usage (still supported)
|
|
710
|
+
* <Product.Quantity asChild>
|
|
711
|
+
* {({ selectedQuantity, availableQuantity, inStock, setSelectedQuantity }) => (
|
|
712
|
+
* <div className="flex items-center gap-3">
|
|
713
|
+
* <div className="flex items-center border border-brand-light rounded-lg">
|
|
714
|
+
* <button
|
|
715
|
+
* onClick={() => setSelectedQuantity(selectedQuantity - 1)}
|
|
716
|
+
* disabled={selectedQuantity <= 1 || (!inStock && !isPreOrderEnabled)}
|
|
717
|
+
* className="px-3 py-2 hover:bg-surface-primary disabled:opacity-50"
|
|
718
|
+
* >
|
|
719
|
+
* -
|
|
720
|
+
* </button>
|
|
721
|
+
* <span className="px-4 py-2 border-x border-brand-light min-w-[3rem] text-center">
|
|
722
|
+
* {selectedQuantity}
|
|
723
|
+
* </span>
|
|
724
|
+
* <button
|
|
725
|
+
* onClick={() => setSelectedQuantity(selectedQuantity + 1)}
|
|
726
|
+
* disabled={availableQuantity && selectedQuantity >= availableQuantity}
|
|
727
|
+
* className="px-3 py-2 hover:bg-surface-primary disabled:opacity-50"
|
|
728
|
+
* >
|
|
729
|
+
* +
|
|
730
|
+
* </button>
|
|
731
|
+
* </div>
|
|
732
|
+
* </div>
|
|
733
|
+
* )}
|
|
734
|
+
* </Product.Quantity>
|
|
735
|
+
* ```
|
|
736
|
+
*/
|
|
737
|
+
export declare const ProductQuantity: React.ForwardRefExoticComponent<ProductQuantityProps & React.RefAttributes<HTMLDivElement>>;
|
|
738
|
+
/**
|
|
739
|
+
* Product Quantity Decrement component.
|
|
740
|
+
* Automatically handles disabled state based on stock and pre-order settings.
|
|
741
|
+
* Must be used within Product.Quantity.Root.
|
|
742
|
+
*
|
|
743
|
+
* @component
|
|
744
|
+
* @example
|
|
745
|
+
* ```tsx
|
|
746
|
+
* <Product.Quantity.Decrement className="px-3 py-1 hover:bg-surface-primary transition-colors" />
|
|
747
|
+
* ```
|
|
748
|
+
*/
|
|
749
|
+
export declare const ProductQuantityDecrement: React.ForwardRefExoticComponent<ProductQuantitySubComponentProps & React.RefAttributes<HTMLButtonElement>>;
|
|
750
|
+
/**
|
|
751
|
+
* Product Quantity Input component.
|
|
752
|
+
* Displays the current quantity value. Must be used within Product.Quantity.Root.
|
|
753
|
+
*
|
|
754
|
+
* @component
|
|
755
|
+
* @example
|
|
756
|
+
* ```tsx
|
|
757
|
+
* <Product.Quantity.Input className="w-16 text-center py-1 border-x border-brand-light focus:outline-none focus:ring-2 focus:ring-brand-primary" />
|
|
758
|
+
* ```
|
|
759
|
+
*/
|
|
760
|
+
export declare const ProductQuantityInput: React.ForwardRefExoticComponent<ProductQuantitySubComponentProps & React.RefAttributes<HTMLInputElement>>;
|
|
761
|
+
/**
|
|
762
|
+
* Product Quantity Increment component.
|
|
763
|
+
* Automatically handles disabled state based on stock availability.
|
|
764
|
+
* Must be used within Product.Quantity.Root.
|
|
765
|
+
*
|
|
766
|
+
* @component
|
|
767
|
+
* @example
|
|
768
|
+
* ```tsx
|
|
769
|
+
* <Product.Quantity.Increment className="px-3 py-1 hover:bg-surface-primary transition-colors" />
|
|
770
|
+
* ```
|
|
771
|
+
*/
|
|
772
|
+
export declare const ProductQuantityIncrement: React.ForwardRefExoticComponent<ProductQuantitySubComponentProps & React.RefAttributes<HTMLButtonElement>>;
|
|
644
773
|
/**
|
|
645
774
|
* Props for Product Action components following the documented API
|
|
646
775
|
*/
|
|
@@ -660,6 +789,50 @@ export interface ProductActionProps {
|
|
|
660
789
|
/** Content to display when loading */
|
|
661
790
|
loadingState?: string | React.ReactNode;
|
|
662
791
|
}
|
|
792
|
+
/**
|
|
793
|
+
* Props for Product Quantity Raw component
|
|
794
|
+
*/
|
|
795
|
+
export interface ProductQuantityRawSubComponentProps {
|
|
796
|
+
/** CSS classes to apply to the element */
|
|
797
|
+
className?: string;
|
|
798
|
+
/** Whether to render as a child component */
|
|
799
|
+
asChild?: boolean;
|
|
800
|
+
/** Custom render function when using asChild */
|
|
801
|
+
children?: AsChildChildren<{
|
|
802
|
+
selectedQuantity: number;
|
|
803
|
+
availableQuantity: number;
|
|
804
|
+
inStock: boolean;
|
|
805
|
+
isPreOrderEnabled: boolean;
|
|
806
|
+
setSelectedQuantity: (quantity: number) => void;
|
|
807
|
+
}>;
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Product Quantity Raw component.
|
|
811
|
+
* Provides access to raw quantity data for custom stock messages and advanced use cases.
|
|
812
|
+
* Must be used within Product.Quantity.Root.
|
|
813
|
+
*
|
|
814
|
+
* @component
|
|
815
|
+
* @example
|
|
816
|
+
* ```tsx
|
|
817
|
+
* <Product.Quantity.Raw asChild>
|
|
818
|
+
* {({ availableQuantity, inStock, isPreOrderEnabled }) => (
|
|
819
|
+
* <div>
|
|
820
|
+
* {!inStock && isPreOrderEnabled && availableQuantity && (
|
|
821
|
+
* <span className="text-content-muted text-sm">
|
|
822
|
+
* Max: {availableQuantity} Pre Order
|
|
823
|
+
* </span>
|
|
824
|
+
* )}
|
|
825
|
+
* {inStock && availableQuantity && availableQuantity < 10 && (
|
|
826
|
+
* <span className="text-content-muted text-sm">
|
|
827
|
+
* Only {availableQuantity} left in stock
|
|
828
|
+
* </span>
|
|
829
|
+
* )}
|
|
830
|
+
* </div>
|
|
831
|
+
* )}
|
|
832
|
+
* </Product.Quantity.Raw>
|
|
833
|
+
* ```
|
|
834
|
+
*/
|
|
835
|
+
export declare const ProductQuantityRaw: React.ForwardRefExoticComponent<ProductQuantityRawSubComponentProps & React.RefAttributes<HTMLElement>>;
|
|
663
836
|
/**
|
|
664
837
|
* Add to cart action button component following the documented API.
|
|
665
838
|
* Automatically integrates with the selected variant and handles loading states.
|
|
@@ -687,3 +860,19 @@ export declare const Action: {
|
|
|
687
860
|
/** Pre-order action button */
|
|
688
861
|
readonly PreOrder: React.ForwardRefExoticComponent<ProductActionProps & React.RefAttributes<HTMLButtonElement>>;
|
|
689
862
|
};
|
|
863
|
+
/**
|
|
864
|
+
* Quantity namespace containing all product quantity components
|
|
865
|
+
* following the compound component pattern: Product.Quantity.Root, Product.Quantity.Decrement, etc.
|
|
866
|
+
*/
|
|
867
|
+
export declare const Quantity: {
|
|
868
|
+
/** Product quantity selector component */
|
|
869
|
+
readonly Root: React.ForwardRefExoticComponent<ProductQuantityProps & React.RefAttributes<HTMLDivElement>>;
|
|
870
|
+
/** Product quantity decrement component */
|
|
871
|
+
readonly Decrement: React.ForwardRefExoticComponent<ProductQuantitySubComponentProps & React.RefAttributes<HTMLButtonElement>>;
|
|
872
|
+
/** Product quantity input component */
|
|
873
|
+
readonly Input: React.ForwardRefExoticComponent<ProductQuantitySubComponentProps & React.RefAttributes<HTMLInputElement>>;
|
|
874
|
+
/** Product quantity increment component */
|
|
875
|
+
readonly Increment: React.ForwardRefExoticComponent<ProductQuantitySubComponentProps & React.RefAttributes<HTMLButtonElement>>;
|
|
876
|
+
/** Product quantity raw component */
|
|
877
|
+
readonly Raw: React.ForwardRefExoticComponent<ProductQuantityRawSubComponentProps & React.RefAttributes<HTMLElement>>;
|
|
878
|
+
};
|
package/dist/react/Product.js
CHANGED
|
@@ -4,6 +4,7 @@ import React from 'react';
|
|
|
4
4
|
import { Commerce } from '@wix/headless-ecom/react';
|
|
5
5
|
import { AsChildSlot } from '@wix/headless-utils/react';
|
|
6
6
|
import { MediaGallery } from '@wix/headless-media/react';
|
|
7
|
+
import { Quantity as QuantityComponent } from '@wix/headless-components/react';
|
|
7
8
|
import * as CoreProduct from './core/Product.js';
|
|
8
9
|
import * as ProductVariantSelector from './core/ProductVariantSelector.js';
|
|
9
10
|
import * as ProductModifiers from './core/ProductModifiers.js';
|
|
@@ -54,6 +55,11 @@ var TestIds;
|
|
|
54
55
|
TestIds["productActionAddToCart"] = "product-action-add-to-cart";
|
|
55
56
|
TestIds["productActionBuyNow"] = "product-action-buy-now";
|
|
56
57
|
TestIds["productActionPreOrder"] = "product-action-pre-order";
|
|
58
|
+
TestIds["productQuantity"] = "product-quantity";
|
|
59
|
+
TestIds["productQuantityDecrement"] = "product-quantity-decrement";
|
|
60
|
+
TestIds["productQuantityInput"] = "product-quantity-input";
|
|
61
|
+
TestIds["productQuantityIncrement"] = "product-quantity-increment";
|
|
62
|
+
TestIds["productQuantityRaw"] = "product-quantity-raw";
|
|
57
63
|
})(TestIds || (TestIds = {}));
|
|
58
64
|
/**
|
|
59
65
|
* Root component that provides all necessary service contexts for a complete product experience.
|
|
@@ -679,6 +685,177 @@ export const ProductMediaGallery = React.forwardRef((props, ref) => {
|
|
|
679
685
|
* Alias for ProductMediaGallery to match the documented API
|
|
680
686
|
*/
|
|
681
687
|
export { ProductMediaGallery as MediaGallery };
|
|
688
|
+
/**
|
|
689
|
+
* Product quantity selector component that integrates with the selected variant.
|
|
690
|
+
* Provides quantity controls with stock validation and pre-order support.
|
|
691
|
+
* Uses a compound component pattern with Root, Decrement, Input, Increment, and Raw sub-components.
|
|
692
|
+
*
|
|
693
|
+
* @component
|
|
694
|
+
* @example
|
|
695
|
+
* ```tsx
|
|
696
|
+
* // Compound component usage (recommended)
|
|
697
|
+
* <Product.Quantity.Root className="flex items-center gap-3">
|
|
698
|
+
* <div className="flex items-center border border-brand-light rounded-lg">
|
|
699
|
+
* <Product.Quantity.Decrement className="px-3 py-1 hover:bg-surface-primary transition-colors" />
|
|
700
|
+
* <Product.Quantity.Input className="w-16 text-center py-1 border-x border-brand-light focus:outline-none focus:ring-2 focus:ring-brand-primary" />
|
|
701
|
+
* <Product.Quantity.Increment className="px-3 py-1 hover:bg-surface-primary transition-colors" />
|
|
702
|
+
* </div>
|
|
703
|
+
* <Product.Quantity.Raw asChild>
|
|
704
|
+
* {({ availableQuantity, inStock, isPreOrderEnabled }) => (
|
|
705
|
+
* <div>
|
|
706
|
+
* {!inStock && isPreOrderEnabled && availableQuantity && (
|
|
707
|
+
* <span className="text-content-muted text-sm">
|
|
708
|
+
* Max: {availableQuantity} Pre Order
|
|
709
|
+
* </span>
|
|
710
|
+
* )}
|
|
711
|
+
* {inStock && availableQuantity && availableQuantity < 10 && (
|
|
712
|
+
* <span className="text-content-muted text-sm">
|
|
713
|
+
* Only {availableQuantity} left in stock
|
|
714
|
+
* </span>
|
|
715
|
+
* )}
|
|
716
|
+
* </div>
|
|
717
|
+
* )}
|
|
718
|
+
* </Product.Quantity.Raw>
|
|
719
|
+
* </Product.Quantity.Root>
|
|
720
|
+
*
|
|
721
|
+
* // Legacy asChild usage (still supported)
|
|
722
|
+
* <Product.Quantity asChild>
|
|
723
|
+
* {({ selectedQuantity, availableQuantity, inStock, setSelectedQuantity }) => (
|
|
724
|
+
* <div className="flex items-center gap-3">
|
|
725
|
+
* <div className="flex items-center border border-brand-light rounded-lg">
|
|
726
|
+
* <button
|
|
727
|
+
* onClick={() => setSelectedQuantity(selectedQuantity - 1)}
|
|
728
|
+
* disabled={selectedQuantity <= 1 || (!inStock && !isPreOrderEnabled)}
|
|
729
|
+
* className="px-3 py-2 hover:bg-surface-primary disabled:opacity-50"
|
|
730
|
+
* >
|
|
731
|
+
* -
|
|
732
|
+
* </button>
|
|
733
|
+
* <span className="px-4 py-2 border-x border-brand-light min-w-[3rem] text-center">
|
|
734
|
+
* {selectedQuantity}
|
|
735
|
+
* </span>
|
|
736
|
+
* <button
|
|
737
|
+
* onClick={() => setSelectedQuantity(selectedQuantity + 1)}
|
|
738
|
+
* disabled={availableQuantity && selectedQuantity >= availableQuantity}
|
|
739
|
+
* className="px-3 py-2 hover:bg-surface-primary disabled:opacity-50"
|
|
740
|
+
* >
|
|
741
|
+
* +
|
|
742
|
+
* </button>
|
|
743
|
+
* </div>
|
|
744
|
+
* </div>
|
|
745
|
+
* )}
|
|
746
|
+
* </Product.Quantity>
|
|
747
|
+
* ```
|
|
748
|
+
*/
|
|
749
|
+
export const ProductQuantity = React.forwardRef((props, ref) => {
|
|
750
|
+
const { asChild, children, className } = props;
|
|
751
|
+
return (_jsx(ProductVariantSelector.Stock, { children: ({ inStock, isPreOrderEnabled, availableQuantity, selectedQuantity, setSelectedQuantity, }) => {
|
|
752
|
+
const renderProps = {
|
|
753
|
+
selectedQuantity,
|
|
754
|
+
availableQuantity,
|
|
755
|
+
inStock,
|
|
756
|
+
isPreOrderEnabled,
|
|
757
|
+
setSelectedQuantity,
|
|
758
|
+
};
|
|
759
|
+
if (asChild && children) {
|
|
760
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productQuantity, customElement: children, customElementProps: renderProps }));
|
|
761
|
+
}
|
|
762
|
+
return (_jsx(QuantityComponent.Root, { initialValue: selectedQuantity, onValueChange: setSelectedQuantity, className: className, ref: ref, "data-testid": TestIds.productQuantity, children: React.isValidElement(children) ? children : null }));
|
|
763
|
+
} }));
|
|
764
|
+
});
|
|
765
|
+
/**
|
|
766
|
+
* Product Quantity Decrement component.
|
|
767
|
+
* Automatically handles disabled state based on stock and pre-order settings.
|
|
768
|
+
* Must be used within Product.Quantity.Root.
|
|
769
|
+
*
|
|
770
|
+
* @component
|
|
771
|
+
* @example
|
|
772
|
+
* ```tsx
|
|
773
|
+
* <Product.Quantity.Decrement className="px-3 py-1 hover:bg-surface-primary transition-colors" />
|
|
774
|
+
* ```
|
|
775
|
+
*/
|
|
776
|
+
export const ProductQuantityDecrement = React.forwardRef((props, ref) => {
|
|
777
|
+
const { asChild, children, className } = props;
|
|
778
|
+
return (_jsx(ProductVariantSelector.Stock, { children: ({ selectedQuantity, inStock, isPreOrderEnabled }) => {
|
|
779
|
+
const disabled = selectedQuantity <= 1 || (!inStock && !isPreOrderEnabled);
|
|
780
|
+
if (asChild && children) {
|
|
781
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productQuantityDecrement, customElement: children, customElementProps: { disabled } }));
|
|
782
|
+
}
|
|
783
|
+
return (_jsx(QuantityComponent.Decrement, { className: className, ref: ref, "data-testid": TestIds.productQuantityDecrement, disabled: disabled }));
|
|
784
|
+
} }));
|
|
785
|
+
});
|
|
786
|
+
/**
|
|
787
|
+
* Product Quantity Input component.
|
|
788
|
+
* Displays the current quantity value. Must be used within Product.Quantity.Root.
|
|
789
|
+
*
|
|
790
|
+
* @component
|
|
791
|
+
* @example
|
|
792
|
+
* ```tsx
|
|
793
|
+
* <Product.Quantity.Input className="w-16 text-center py-1 border-x border-brand-light focus:outline-none focus:ring-2 focus:ring-brand-primary" />
|
|
794
|
+
* ```
|
|
795
|
+
*/
|
|
796
|
+
export const ProductQuantityInput = React.forwardRef((props, ref) => {
|
|
797
|
+
const { asChild, children, className, disabled = true } = props;
|
|
798
|
+
return (_jsx(ProductVariantSelector.Stock, { children: () => {
|
|
799
|
+
if (asChild && children) {
|
|
800
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, disabled: disabled, "data-testid": TestIds.productQuantityInput, customElement: children, customElementProps: {} }));
|
|
801
|
+
}
|
|
802
|
+
return (_jsx(QuantityComponent.Input, { className: className, disabled: disabled, ref: ref, "data-testid": TestIds.productQuantityInput }));
|
|
803
|
+
} }));
|
|
804
|
+
});
|
|
805
|
+
/**
|
|
806
|
+
* Product Quantity Increment component.
|
|
807
|
+
* Automatically handles disabled state based on stock availability.
|
|
808
|
+
* Must be used within Product.Quantity.Root.
|
|
809
|
+
*
|
|
810
|
+
* @component
|
|
811
|
+
* @example
|
|
812
|
+
* ```tsx
|
|
813
|
+
* <Product.Quantity.Increment className="px-3 py-1 hover:bg-surface-primary transition-colors" />
|
|
814
|
+
* ```
|
|
815
|
+
*/
|
|
816
|
+
export const ProductQuantityIncrement = React.forwardRef((props, ref) => {
|
|
817
|
+
const { asChild, children, className } = props;
|
|
818
|
+
return (_jsx(ProductVariantSelector.Stock, { children: ({ selectedQuantity, availableQuantity, inStock, isPreOrderEnabled, }) => {
|
|
819
|
+
const disabled = (!!availableQuantity && selectedQuantity >= availableQuantity) ||
|
|
820
|
+
(!inStock && !isPreOrderEnabled);
|
|
821
|
+
if (asChild && children) {
|
|
822
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productQuantityIncrement, customElement: children, customElementProps: { disabled } }));
|
|
823
|
+
}
|
|
824
|
+
return (_jsx(QuantityComponent.Increment, { className: className, ref: ref, "data-testid": TestIds.productQuantityIncrement, disabled: disabled }));
|
|
825
|
+
} }));
|
|
826
|
+
});
|
|
827
|
+
/**
|
|
828
|
+
* Product Quantity Raw component.
|
|
829
|
+
* Provides access to raw quantity data for custom stock messages and advanced use cases.
|
|
830
|
+
* Must be used within Product.Quantity.Root.
|
|
831
|
+
*
|
|
832
|
+
* @component
|
|
833
|
+
* @example
|
|
834
|
+
* ```tsx
|
|
835
|
+
* <Product.Quantity.Raw asChild>
|
|
836
|
+
* {({ availableQuantity, inStock, isPreOrderEnabled }) => (
|
|
837
|
+
* <div>
|
|
838
|
+
* {!inStock && isPreOrderEnabled && availableQuantity && (
|
|
839
|
+
* <span className="text-content-muted text-sm">
|
|
840
|
+
* Max: {availableQuantity} Pre Order
|
|
841
|
+
* </span>
|
|
842
|
+
* )}
|
|
843
|
+
* {inStock && availableQuantity && availableQuantity < 10 && (
|
|
844
|
+
* <span className="text-content-muted text-sm">
|
|
845
|
+
* Only {availableQuantity} left in stock
|
|
846
|
+
* </span>
|
|
847
|
+
* )}
|
|
848
|
+
* </div>
|
|
849
|
+
* )}
|
|
850
|
+
* </Product.Quantity.Raw>
|
|
851
|
+
* ```
|
|
852
|
+
*/
|
|
853
|
+
export const ProductQuantityRaw = React.forwardRef((props, ref) => {
|
|
854
|
+
const { asChild, children, className } = props;
|
|
855
|
+
return (_jsx(ProductVariantSelector.Stock, { children: (renderProps) => {
|
|
856
|
+
return (_jsx(AsChildSlot, { ref: ref, customElement: children, asChild: asChild, className: className, "data-testid": TestIds.productQuantityRaw, customElementProps: renderProps }));
|
|
857
|
+
} }));
|
|
858
|
+
});
|
|
682
859
|
/**
|
|
683
860
|
* Add to cart action button component following the documented API.
|
|
684
861
|
* Automatically integrates with the selected variant and handles loading states.
|
|
@@ -758,3 +935,19 @@ export const Action = {
|
|
|
758
935
|
/** Pre-order action button */
|
|
759
936
|
PreOrder: ProductActionPreOrder,
|
|
760
937
|
};
|
|
938
|
+
/**
|
|
939
|
+
* Quantity namespace containing all product quantity components
|
|
940
|
+
* following the compound component pattern: Product.Quantity.Root, Product.Quantity.Decrement, etc.
|
|
941
|
+
*/
|
|
942
|
+
export const Quantity = {
|
|
943
|
+
/** Product quantity selector component */
|
|
944
|
+
Root: ProductQuantity,
|
|
945
|
+
/** Product quantity decrement component */
|
|
946
|
+
Decrement: ProductQuantityDecrement,
|
|
947
|
+
/** Product quantity input component */
|
|
948
|
+
Input: ProductQuantityInput,
|
|
949
|
+
/** Product quantity increment component */
|
|
950
|
+
Increment: ProductQuantityIncrement,
|
|
951
|
+
/** Product quantity raw component */
|
|
952
|
+
Raw: ProductQuantityRaw,
|
|
953
|
+
};
|
|
@@ -4,7 +4,7 @@ import React from 'react';
|
|
|
4
4
|
import type { ProductsListServiceConfig } from '../services/products-list-service.js';
|
|
5
5
|
import { productsV3 } from '@wix/stores';
|
|
6
6
|
import { AsChildChildren } from '@wix/headless-utils/react';
|
|
7
|
-
export { Filter, CategoryFilter } from './core/ProductListFilters.js';
|
|
7
|
+
export { Filter, CategoryFilter, ResetTrigger as FilterResetTrigger, } from './core/ProductListFilters.js';
|
|
8
8
|
/**
|
|
9
9
|
* Props for the ProductList root component following the documented API
|
|
10
10
|
*/
|
|
@@ -8,7 +8,7 @@ import * as CoreProductListPagination from './core/ProductListPagination.js';
|
|
|
8
8
|
import { ProductListSort as ProductListSortPrimitive } from './core/ProductListSort.js';
|
|
9
9
|
import * as Product from './Product.js';
|
|
10
10
|
import { AsChildSlot } from '@wix/headless-utils/react';
|
|
11
|
-
export { Filter, CategoryFilter } from './core/ProductListFilters.js';
|
|
11
|
+
export { Filter, CategoryFilter, ResetTrigger as FilterResetTrigger, } from './core/ProductListFilters.js';
|
|
12
12
|
var TestIds;
|
|
13
13
|
(function (TestIds) {
|
|
14
14
|
TestIds["productListRoot"] = "product-list-root";
|
|
@@ -236,6 +236,8 @@ export interface StockRenderProps {
|
|
|
236
236
|
incrementQuantity: () => void;
|
|
237
237
|
/** Function to decrement quantity */
|
|
238
238
|
decrementQuantity: () => void;
|
|
239
|
+
/** Function to set selected quantity */
|
|
240
|
+
setSelectedQuantity: (quantity: number) => void;
|
|
239
241
|
}
|
|
240
242
|
/**
|
|
241
243
|
* Headless component for product stock status
|
|
@@ -238,6 +238,9 @@ export function Stock(props) {
|
|
|
238
238
|
const decrementQuantity = () => {
|
|
239
239
|
variantService.decrementQuantity();
|
|
240
240
|
};
|
|
241
|
+
const setSelectedQuantity = (quantity) => {
|
|
242
|
+
variantService.setSelectedQuantity(quantity);
|
|
243
|
+
};
|
|
241
244
|
return props.children({
|
|
242
245
|
inStock,
|
|
243
246
|
availableQuantity,
|
|
@@ -247,6 +250,7 @@ export function Stock(props) {
|
|
|
247
250
|
trackInventory,
|
|
248
251
|
selectedQuantity,
|
|
249
252
|
incrementQuantity,
|
|
253
|
+
setSelectedQuantity,
|
|
250
254
|
decrementQuantity,
|
|
251
255
|
});
|
|
252
256
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/headless-stores",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.76",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"prebuild": "cd ../media && yarn build && cd ../ecom && yarn build",
|
|
@@ -62,8 +62,8 @@
|
|
|
62
62
|
"@wix/auto_sdk_stores_read-only-variants-v-3": "^1.0.23",
|
|
63
63
|
"@wix/ecom": "^1.0.1278",
|
|
64
64
|
"@wix/essentials": "^0.1.24",
|
|
65
|
-
"@wix/headless-components": "0.0.
|
|
66
|
-
"@wix/headless-ecom": "0.0.
|
|
65
|
+
"@wix/headless-components": "0.0.12",
|
|
66
|
+
"@wix/headless-ecom": "0.0.24",
|
|
67
67
|
"@wix/headless-media": "0.0.11",
|
|
68
68
|
"@wix/headless-utils": "0.0.3",
|
|
69
69
|
"@wix/redirects": "^1.0.83",
|