pixelize-design-library 2.4.2-beta.55 → 2.4.2-beta.57
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 +64 -0
- package/COMPONENT-GOTCHAS.md +50 -22
- package/DESIGN-GUIDE.md +19 -2
- package/dist/Components/AddressCard/AddressCard.d.ts +4 -0
- package/dist/Components/AddressCard/AddressCard.js +25 -0
- package/dist/Components/AddressCard/AddressCardGroup.d.ts +4 -0
- package/dist/Components/AddressCard/AddressCardGroup.js +7 -0
- package/dist/Components/AddressCard/AddressCardProps.d.ts +43 -0
- package/dist/Components/AddressCard/AddressCardProps.js +2 -0
- package/dist/Components/BagBar/BagBar.d.ts +4 -0
- package/dist/Components/BagBar/BagBar.js +32 -0
- package/dist/Components/BagBar/BagBarProps.d.ts +30 -0
- package/dist/Components/BagBar/BagBarProps.js +2 -0
- package/dist/Components/CartItem/CartItem.d.ts +4 -0
- package/dist/Components/CartItem/CartItem.js +60 -0
- package/dist/Components/CartItem/CartItemProps.d.ts +64 -0
- package/dist/Components/CartItem/CartItemProps.js +2 -0
- package/dist/Components/CartItem/cartItemOptions.d.ts +3 -0
- package/dist/Components/CartItem/cartItemOptions.js +13 -0
- package/dist/Components/CheckoutKit/selectable.d.ts +58 -0
- package/dist/Components/CheckoutKit/selectable.js +120 -0
- package/dist/Components/Commerce/commerceTypes.d.ts +52 -0
- package/dist/Components/Commerce/commerceTypes.js +2 -0
- package/dist/Components/Commerce/commerceVariants.d.ts +13 -0
- package/dist/Components/Commerce/commerceVariants.js +51 -0
- package/dist/Components/Commerce/index.d.ts +2 -0
- package/dist/Components/Commerce/index.js +18 -0
- package/dist/Components/Commerce/useContainerWidth.d.ts +3 -0
- package/dist/Components/Commerce/useContainerWidth.js +25 -0
- package/dist/Components/CouponField/CouponField.d.ts +4 -0
- package/dist/Components/CouponField/CouponField.js +72 -0
- package/dist/Components/CouponField/CouponFieldProps.d.ts +49 -0
- package/dist/Components/CouponField/CouponFieldProps.js +2 -0
- package/dist/Components/DatePicker/DatePickerProps.d.ts +1 -1
- package/dist/Components/DatePicker/PickerPortal.d.ts +11 -0
- package/dist/Components/DatePicker/PickerPortal.js +26 -0
- package/dist/Components/DatePicker/RangeDatePicker.d.ts +1 -1
- package/dist/Components/DatePicker/RangeDatePicker.js +67 -32
- package/dist/Components/DatePicker/SingleDatePicker.js +49 -13
- package/dist/Components/DatePicker/TimeOnlyPicker.js +39 -4
- package/dist/Components/DetailSections/DetailSections.d.ts +4 -0
- package/dist/Components/DetailSections/DetailSections.js +112 -0
- package/dist/Components/DetailSections/DetailSectionsProps.d.ts +58 -0
- package/dist/Components/DetailSections/DetailSectionsProps.js +2 -0
- package/dist/Components/Input/PhoneNumberInputProps.d.ts +1 -1
- package/dist/Components/MediaGallery/MediaFrame.d.ts +31 -0
- package/dist/Components/MediaGallery/MediaFrame.js +55 -0
- package/dist/Components/MediaGallery/MediaGallery.d.ts +4 -0
- package/dist/Components/MediaGallery/MediaGallery.js +206 -0
- package/dist/Components/MediaGallery/MediaGalleryProps.d.ts +76 -0
- package/dist/Components/MediaGallery/MediaGalleryProps.js +20 -0
- package/dist/Components/MediaGallery/MediaLightbox.d.ts +4 -0
- package/dist/Components/MediaGallery/MediaLightbox.js +116 -0
- package/dist/Components/MediaGallery/MediaLightboxProps.d.ts +40 -0
- package/dist/Components/MediaGallery/MediaLightboxProps.js +15 -0
- package/dist/Components/MediaGallery/MediaThumb.d.ts +18 -0
- package/dist/Components/MediaGallery/MediaThumb.js +25 -0
- package/dist/Components/MediaGallery/glass.d.ts +31 -0
- package/dist/Components/MediaGallery/glass.js +47 -0
- package/dist/Components/MediaGallery/mediaGalleryModel.d.ts +22 -0
- package/dist/Components/MediaGallery/mediaGalleryModel.js +83 -0
- package/dist/Components/MediaGallery/motion.d.ts +5 -0
- package/dist/Components/MediaGallery/motion.js +12 -0
- package/dist/Components/MediaGallery/useMatchMedia.d.ts +2 -0
- package/dist/Components/MediaGallery/useMatchMedia.js +20 -0
- package/dist/Components/MultiSelect/MultiSelectProps.d.ts +1 -1
- package/dist/Components/OffersList/OffersList.d.ts +4 -0
- package/dist/Components/OffersList/OffersList.js +83 -0
- package/dist/Components/OffersList/OffersListProps.d.ts +34 -0
- package/dist/Components/OffersList/OffersListProps.js +2 -0
- package/dist/Components/OptionChip/OptionChip.d.ts +4 -0
- package/dist/Components/OptionChip/OptionChip.js +97 -0
- package/dist/Components/OptionChip/OptionChipProps.d.ts +33 -0
- package/dist/Components/OptionChip/OptionChipProps.js +2 -0
- package/dist/Components/OptionChip/optionInk.d.ts +12 -0
- package/dist/Components/OptionChip/optionInk.js +25 -0
- package/dist/Components/OrderStatusTimeline/OrderStatusTimeline.d.ts +4 -0
- package/dist/Components/OrderStatusTimeline/OrderStatusTimeline.js +57 -0
- package/dist/Components/OrderStatusTimeline/OrderStatusTimelineProps.d.ts +29 -0
- package/dist/Components/OrderStatusTimeline/OrderStatusTimelineProps.js +2 -0
- package/dist/Components/OrderSummary/OrderSummary.d.ts +4 -0
- package/dist/Components/OrderSummary/OrderSummary.js +63 -0
- package/dist/Components/OrderSummary/OrderSummaryProps.d.ts +53 -0
- package/dist/Components/OrderSummary/OrderSummaryProps.js +2 -0
- package/dist/Components/PaymentOption/PaymentOption.d.ts +4 -0
- package/dist/Components/PaymentOption/PaymentOption.js +24 -0
- package/dist/Components/PaymentOption/PaymentOptionGroup.d.ts +4 -0
- package/dist/Components/PaymentOption/PaymentOptionGroup.js +7 -0
- package/dist/Components/PaymentOption/PaymentOptionProps.d.ts +32 -0
- package/dist/Components/PaymentOption/PaymentOptionProps.js +2 -0
- package/dist/Components/PinInput/PinInputProps.d.ts +1 -1
- package/dist/Components/PincodeChecker/PincodeChecker.d.ts +4 -0
- package/dist/Components/PincodeChecker/PincodeChecker.js +175 -0
- package/dist/Components/PincodeChecker/PincodeCheckerProps.d.ts +57 -0
- package/dist/Components/PincodeChecker/PincodeCheckerProps.js +2 -0
- package/dist/Components/PriceBlock/PriceBlock.d.ts +4 -0
- package/dist/Components/PriceBlock/PriceBlock.js +41 -0
- package/dist/Components/PriceBlock/PriceBlockProps.d.ts +39 -0
- package/dist/Components/PriceBlock/PriceBlockProps.js +2 -0
- package/dist/Components/ProductBadges/ProductBadges.d.ts +4 -0
- package/dist/Components/ProductBadges/ProductBadges.js +61 -0
- package/dist/Components/ProductBadges/ProductBadgesProps.d.ts +18 -0
- package/dist/Components/ProductBadges/ProductBadgesProps.js +2 -0
- package/dist/Components/ProductCard/ProductCard.d.ts +3 -2
- package/dist/Components/ProductCard/ProductCard.js +103 -52
- package/dist/Components/ProductCard/ProductCardAction.d.ts +17 -0
- package/dist/Components/ProductCard/ProductCardAction.js +65 -0
- package/dist/Components/ProductCard/ProductCardGrid.d.ts +4 -0
- package/dist/Components/ProductCard/ProductCardGrid.js +20 -0
- package/dist/Components/ProductCard/ProductCardMedia.d.ts +18 -0
- package/dist/Components/ProductCard/ProductCardMedia.js +35 -0
- package/dist/Components/ProductCard/ProductCardProps.d.ts +138 -56
- package/dist/Components/ProductCard/ProductCardSkeleton.d.ts +4 -0
- package/dist/Components/ProductCard/ProductCardSkeleton.js +17 -0
- package/dist/Components/ProductCard/ProductCardSwatches.d.ts +15 -0
- package/dist/Components/ProductCard/ProductCardSwatches.js +37 -0
- package/dist/Components/ProductCard/QuickAddSheet.d.ts +4 -0
- package/dist/Components/ProductCard/QuickAddSheet.js +109 -0
- package/dist/Components/ProductCard/QuickAddSheetProps.d.ts +38 -0
- package/dist/Components/ProductCard/QuickAddSheetProps.js +2 -0
- package/dist/Components/ProductCard/cardLabels.d.ts +2 -0
- package/dist/Components/ProductCard/cardLabels.js +16 -0
- package/dist/Components/ProductCard/cardMetrics.d.ts +27 -0
- package/dist/Components/ProductCard/cardMetrics.js +55 -0
- package/dist/Components/ProductCard/productCardFixtures.d.ts +7 -0
- package/dist/Components/ProductCard/productCardFixtures.js +61 -0
- package/dist/Components/ProductCard/productCardModel.d.ts +22 -0
- package/dist/Components/ProductCard/productCardModel.js +79 -0
- package/dist/Components/ProductCard/useMinWidth.d.ts +2 -0
- package/dist/Components/ProductCard/useMinWidth.js +20 -0
- package/dist/Components/ProductDetails/ProductDetails.d.ts +3 -2
- package/dist/Components/ProductDetails/ProductDetails.js +209 -33
- package/dist/Components/ProductDetails/ProductDetailsProps.d.ts +143 -41
- package/dist/Components/ProductDetails/productDetailsModel.d.ts +14 -0
- package/dist/Components/ProductDetails/productDetailsModel.js +45 -0
- package/dist/Components/ProductDetails/useInViewport.d.ts +3 -0
- package/dist/Components/ProductDetails/useInViewport.js +18 -0
- package/dist/Components/QuantityStepper/QuantityStepper.d.ts +4 -0
- package/dist/Components/QuantityStepper/QuantityStepper.js +59 -0
- package/dist/Components/QuantityStepper/QuantityStepperProps.d.ts +24 -0
- package/dist/Components/QuantityStepper/QuantityStepperProps.js +2 -0
- package/dist/Components/Rating/Rating.d.ts +4 -0
- package/dist/Components/Rating/Rating.js +81 -0
- package/dist/Components/Rating/RatingProps.d.ts +27 -0
- package/dist/Components/Rating/RatingProps.js +2 -0
- package/dist/Components/ReviewList/ReviewList.d.ts +4 -0
- package/dist/Components/ReviewList/ReviewList.js +94 -0
- package/dist/Components/ReviewList/ReviewListProps.d.ts +68 -0
- package/dist/Components/ReviewList/ReviewListProps.js +2 -0
- package/dist/Components/ReviewSummary/ReviewSummary.d.ts +4 -0
- package/dist/Components/ReviewSummary/ReviewSummary.js +47 -0
- package/dist/Components/ReviewSummary/ReviewSummaryProps.d.ts +31 -0
- package/dist/Components/ReviewSummary/ReviewSummaryProps.js +2 -0
- package/dist/Components/Search/SearchProps.d.ts +1 -1
- package/dist/Components/SearchSelect/SearchSelectProps.d.ts +1 -1
- package/dist/Components/SegmentedControl/SegmentedControl.d.ts +1 -1
- package/dist/Components/SegmentedControl/SegmentedControl.js +17 -5
- package/dist/Components/SegmentedControl/SegmentedControlProps.d.ts +6 -0
- package/dist/Components/SegmentedControl/segmentedInk.d.ts +2 -0
- package/dist/Components/SegmentedControl/segmentedInk.js +4 -1
- package/dist/Components/SegmentedControl/useSlidingPill.d.ts +2 -0
- package/dist/Components/SegmentedControl/useSlidingPill.js +6 -1
- package/dist/Components/Select/SelectProps.d.ts +1 -1
- package/dist/Components/SelectSearch/SelectSearchProps.d.ts +1 -1
- package/dist/Components/SelectV2/SelectV2Props.d.ts +1 -1
- package/dist/Components/SideBar/SecondaryBar.js +13 -8
- package/dist/Components/SideBar/SideBarProps.d.ts +3 -1
- package/dist/Components/SideBar/components/MenuItemBox.js +47 -10
- package/dist/Components/SideBar/components/MenuPopoverContent.js +4 -3
- package/dist/Components/SpecTable/SpecTable.d.ts +4 -0
- package/dist/Components/SpecTable/SpecTable.js +39 -0
- package/dist/Components/SpecTable/SpecTableProps.d.ts +25 -0
- package/dist/Components/SpecTable/SpecTableProps.js +2 -0
- package/dist/Components/Spinner/Spinner.d.ts +1 -1
- package/dist/Components/Spinner/Spinner.js +2 -2
- package/dist/Components/Spinner/SpinnerProps.d.ts +2 -0
- package/dist/Components/StageProgress/StepperStage.js +2 -1
- package/dist/Components/StatCard/StatCard.d.ts +1 -1
- package/dist/Components/StatCard/StatCard.js +2 -2
- package/dist/Components/StatCard/StatCardProps.d.ts +4 -0
- package/dist/Components/StickyBuyBar/StickyBuyBar.d.ts +3 -0
- package/dist/Components/StickyBuyBar/StickyBuyBar.js +85 -0
- package/dist/Components/StickyBuyBar/StickyBuyBarProps.d.ts +54 -0
- package/dist/Components/StickyBuyBar/StickyBuyBarProps.js +2 -0
- package/dist/Components/StockStatus/StockStatus.d.ts +4 -0
- package/dist/Components/StockStatus/StockStatus.js +31 -0
- package/dist/Components/StockStatus/StockStatusProps.d.ts +14 -0
- package/dist/Components/StockStatus/StockStatusProps.js +2 -0
- package/dist/Components/Table/Table.js +2 -1
- package/dist/Components/Table/TableProps.d.ts +5 -2
- package/dist/Components/Table/columnLabel.d.ts +6 -0
- package/dist/Components/Table/columnLabel.js +27 -0
- package/dist/Components/Table/components/TableBody.js +5 -1
- package/dist/Components/Table/components/TableHeader.js +3 -2
- package/dist/Components/Table/filters/ActiveFilters.js +2 -1
- package/dist/Components/Table/filters/TableFilters.js +2 -1
- package/dist/Components/Table/hooks/useTable.js +3 -2
- package/dist/Components/Table/settings/ManageColumns.js +4 -3
- package/dist/Components/Table/settings/TableSettings.js +2 -1
- package/dist/Components/TrustBadges/TrustBadges.d.ts +4 -0
- package/dist/Components/TrustBadges/TrustBadges.js +24 -0
- package/dist/Components/TrustBadges/TrustBadgesProps.d.ts +20 -0
- package/dist/Components/TrustBadges/TrustBadgesProps.js +2 -0
- package/dist/Components/VariantSelector/VariantSelector.d.ts +4 -0
- package/dist/Components/VariantSelector/VariantSelector.js +123 -0
- package/dist/Components/VariantSelector/VariantSelectorProps.d.ts +27 -0
- package/dist/Components/VariantSelector/VariantSelectorProps.js +2 -0
- package/dist/Components/VariantSelector/variantSelection.d.ts +6 -0
- package/dist/Components/VariantSelector/variantSelection.js +25 -0
- package/dist/Components/WishlistToggle/WishlistToggle.d.ts +4 -0
- package/dist/Components/WishlistToggle/WishlistToggle.js +65 -0
- package/dist/Components/WishlistToggle/WishlistToggleProps.d.ts +19 -0
- package/dist/Components/WishlistToggle/WishlistToggleProps.js +2 -0
- package/dist/Theme/assertPalette.d.ts +1 -1
- package/dist/Theme/assertPalette.js +1 -1
- package/dist/Theme/tokens/brands/index.js +2 -0
- package/dist/Theme/tokens/brands/zinc/palette.dark.d.ts +1 -0
- package/dist/Theme/tokens/builders/buildDarkPalette.js +2 -0
- package/dist/Theme/tokens/builders/index.d.ts +1 -0
- package/dist/Theme/tokens/builders/index.js +1 -0
- package/dist/Theme/tokens/builders/rechartsTheme.d.ts +56 -0
- package/dist/Theme/tokens/builders/rechartsTheme.js +43 -0
- package/dist/Theme/tokens/builders/star.d.ts +3 -0
- package/dist/Theme/tokens/builders/star.js +8 -0
- package/dist/Theme/tokens/primitives/controlSizes.js +1 -1
- package/dist/Theme/tokens/types.d.ts +3 -1
- package/dist/Theme/useChartTheme.d.ts +4 -0
- package/dist/Theme/useChartTheme.js +12 -0
- package/dist/Utils/price.d.ts +16 -0
- package/dist/Utils/price.js +34 -0
- package/dist/esm/Components/AddressCard/AddressCard.js +20 -0
- package/dist/esm/Components/AddressCard/AddressCardGroup.js +5 -0
- package/dist/esm/Components/AddressCard/AddressCardProps.js +1 -0
- package/dist/esm/Components/BagBar/BagBar.js +27 -0
- package/dist/esm/Components/BagBar/BagBarProps.js +1 -0
- package/dist/esm/Components/CartItem/CartItem.js +55 -0
- package/dist/esm/Components/CartItem/CartItemProps.js +1 -0
- package/dist/esm/Components/CartItem/cartItemOptions.js +9 -0
- package/dist/esm/Components/CheckoutKit/selectable.js +112 -0
- package/dist/esm/Components/Commerce/commerceTypes.js +1 -0
- package/dist/esm/Components/Commerce/commerceVariants.js +41 -0
- package/dist/esm/Components/Commerce/index.js +2 -0
- package/dist/esm/Components/Commerce/useContainerWidth.js +21 -0
- package/dist/esm/Components/CouponField/CouponField.js +67 -0
- package/dist/esm/Components/CouponField/CouponFieldProps.js +1 -0
- package/dist/esm/Components/DatePicker/PickerPortal.js +21 -0
- package/dist/esm/Components/DatePicker/RangeDatePicker.js +34 -32
- package/dist/esm/Components/DatePicker/SingleDatePicker.js +16 -13
- package/dist/esm/Components/DatePicker/TimeOnlyPicker.js +6 -4
- package/dist/esm/Components/DetailSections/DetailSections.js +107 -0
- package/dist/esm/Components/DetailSections/DetailSectionsProps.js +1 -0
- package/dist/esm/Components/MediaGallery/MediaFrame.js +48 -0
- package/dist/esm/Components/MediaGallery/MediaGallery.js +201 -0
- package/dist/esm/Components/MediaGallery/MediaGalleryProps.js +17 -0
- package/dist/esm/Components/MediaGallery/MediaLightbox.js +78 -0
- package/dist/esm/Components/MediaGallery/MediaLightboxProps.js +12 -0
- package/dist/esm/Components/MediaGallery/MediaThumb.js +23 -0
- package/dist/esm/Components/MediaGallery/glass.js +42 -0
- package/dist/esm/Components/MediaGallery/mediaGalleryModel.js +71 -0
- package/dist/esm/Components/MediaGallery/motion.js +8 -0
- package/dist/esm/Components/MediaGallery/useMatchMedia.js +16 -0
- package/dist/esm/Components/OffersList/OffersList.js +81 -0
- package/dist/esm/Components/OffersList/OffersListProps.js +1 -0
- package/dist/esm/Components/OptionChip/OptionChip.js +95 -0
- package/dist/esm/Components/OptionChip/OptionChipProps.js +1 -0
- package/dist/esm/Components/OptionChip/optionInk.js +21 -0
- package/dist/esm/Components/OrderStatusTimeline/OrderStatusTimeline.js +55 -0
- package/dist/esm/Components/OrderStatusTimeline/OrderStatusTimelineProps.js +1 -0
- package/dist/esm/Components/OrderSummary/OrderSummary.js +58 -0
- package/dist/esm/Components/OrderSummary/OrderSummaryProps.js +1 -0
- package/dist/esm/Components/PaymentOption/PaymentOption.js +19 -0
- package/dist/esm/Components/PaymentOption/PaymentOptionGroup.js +5 -0
- package/dist/esm/Components/PaymentOption/PaymentOptionProps.js +1 -0
- package/dist/esm/Components/PincodeChecker/PincodeChecker.js +170 -0
- package/dist/esm/Components/PincodeChecker/PincodeCheckerProps.js +1 -0
- package/dist/esm/Components/PriceBlock/PriceBlock.js +39 -0
- package/dist/esm/Components/PriceBlock/PriceBlockProps.js +1 -0
- package/dist/esm/Components/ProductBadges/ProductBadges.js +59 -0
- package/dist/esm/Components/ProductBadges/ProductBadgesProps.js +1 -0
- package/dist/esm/Components/ProductCard/ProductCard.js +105 -54
- package/dist/esm/Components/ProductCard/ProductCardAction.js +60 -0
- package/dist/esm/Components/ProductCard/ProductCardGrid.js +18 -0
- package/dist/esm/Components/ProductCard/ProductCardMedia.js +33 -0
- package/dist/esm/Components/ProductCard/ProductCardSkeleton.js +12 -0
- package/dist/esm/Components/ProductCard/ProductCardSwatches.js +35 -0
- package/dist/esm/Components/ProductCard/QuickAddSheet.js +71 -0
- package/dist/esm/Components/ProductCard/QuickAddSheetProps.js +1 -0
- package/dist/esm/Components/ProductCard/cardLabels.js +13 -0
- package/dist/esm/Components/ProductCard/cardMetrics.js +52 -0
- package/dist/esm/Components/ProductCard/productCardFixtures.js +58 -0
- package/dist/esm/Components/ProductCard/productCardModel.js +65 -0
- package/dist/esm/Components/ProductCard/useMinWidth.js +16 -0
- package/dist/esm/Components/ProductDetails/ProductDetails.js +210 -34
- package/dist/esm/Components/ProductDetails/productDetailsModel.js +35 -0
- package/dist/esm/Components/ProductDetails/useInViewport.js +14 -0
- package/dist/esm/Components/QuantityStepper/QuantityStepper.js +57 -0
- package/dist/esm/Components/QuantityStepper/QuantityStepperProps.js +1 -0
- package/dist/esm/Components/Rating/Rating.js +79 -0
- package/dist/esm/Components/Rating/RatingProps.js +1 -0
- package/dist/esm/Components/ReviewList/ReviewList.js +89 -0
- package/dist/esm/Components/ReviewList/ReviewListProps.js +1 -0
- package/dist/esm/Components/ReviewSummary/ReviewSummary.js +42 -0
- package/dist/esm/Components/ReviewSummary/ReviewSummaryProps.js +1 -0
- package/dist/esm/Components/SegmentedControl/SegmentedControl.js +19 -7
- package/dist/esm/Components/SegmentedControl/segmentedInk.js +2 -0
- package/dist/esm/Components/SegmentedControl/useSlidingPill.js +6 -1
- package/dist/esm/Components/SideBar/SecondaryBar.js +14 -9
- package/dist/esm/Components/SideBar/components/MenuItemBox.js +14 -10
- package/dist/esm/Components/SideBar/components/MenuPopoverContent.js +4 -3
- package/dist/esm/Components/SpecTable/SpecTable.js +34 -0
- package/dist/esm/Components/SpecTable/SpecTableProps.js +1 -0
- package/dist/esm/Components/Spinner/Spinner.js +2 -2
- package/dist/esm/Components/StageProgress/StepperStage.js +2 -1
- package/dist/esm/Components/StatCard/StatCard.js +2 -2
- package/dist/esm/Components/StickyBuyBar/StickyBuyBar.js +80 -0
- package/dist/esm/Components/StickyBuyBar/StickyBuyBarProps.js +1 -0
- package/dist/esm/Components/StockStatus/StockStatus.js +29 -0
- package/dist/esm/Components/StockStatus/StockStatusProps.js +1 -0
- package/dist/esm/Components/Table/Table.js +2 -1
- package/dist/esm/Components/Table/columnLabel.js +21 -0
- package/dist/esm/Components/Table/components/TableBody.js +5 -1
- package/dist/esm/Components/Table/components/TableHeader.js +3 -2
- package/dist/esm/Components/Table/filters/ActiveFilters.js +2 -1
- package/dist/esm/Components/Table/filters/TableFilters.js +2 -1
- package/dist/esm/Components/Table/hooks/useTable.js +3 -2
- package/dist/esm/Components/Table/settings/ManageColumns.js +4 -3
- package/dist/esm/Components/Table/settings/TableSettings.js +2 -1
- package/dist/esm/Components/TrustBadges/TrustBadges.js +22 -0
- package/dist/esm/Components/TrustBadges/TrustBadgesProps.js +1 -0
- package/dist/esm/Components/VariantSelector/VariantSelector.js +118 -0
- package/dist/esm/Components/VariantSelector/VariantSelectorProps.js +1 -0
- package/dist/esm/Components/VariantSelector/variantSelection.js +19 -0
- package/dist/esm/Components/WishlistToggle/WishlistToggle.js +63 -0
- package/dist/esm/Components/WishlistToggle/WishlistToggleProps.js +1 -0
- package/dist/esm/Theme/assertPalette.js +1 -1
- package/dist/esm/Theme/tokens/brands/index.js +2 -0
- package/dist/esm/Theme/tokens/builders/buildDarkPalette.js +2 -0
- package/dist/esm/Theme/tokens/builders/index.js +1 -0
- package/dist/esm/Theme/tokens/builders/rechartsTheme.js +37 -0
- package/dist/esm/Theme/tokens/builders/star.js +4 -0
- package/dist/esm/Theme/tokens/primitives/controlSizes.js +1 -1
- package/dist/esm/Theme/useChartTheme.js +8 -0
- package/dist/esm/Utils/price.js +27 -0
- package/dist/esm/index.js +36 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +86 -4
- package/package.json +1 -1
- package/dist/Components/ProductCard/ProductLabel.d.ts +0 -7
- package/dist/Components/ProductCard/ProductLabel.js +0 -20
- package/dist/Components/ProductCard/ProductPrice.d.ts +0 -10
- package/dist/Components/ProductCard/ProductPrice.js +0 -126
- package/dist/Components/ProductCard/ProductReview.d.ts +0 -8
- package/dist/Components/ProductCard/ProductReview.js +0 -30
- package/dist/Components/ProductCard/ProductTags.d.ts +0 -8
- package/dist/Components/ProductCard/ProductTags.js +0 -53
- package/dist/Components/ProductDetails/ProductDtlPrice.d.ts +0 -7
- package/dist/Components/ProductDetails/ProductDtlPrice.js +0 -24
- package/dist/Components/ProductDetails/ProductImageSlider.d.ts +0 -13
- package/dist/Components/ProductDetails/ProductImageSlider.js +0 -240
- package/dist/esm/Components/ProductCard/ProductLabel.js +0 -18
- package/dist/esm/Components/ProductCard/ProductPrice.js +0 -121
- package/dist/esm/Components/ProductCard/ProductReview.js +0 -28
- package/dist/esm/Components/ProductCard/ProductTags.js +0 -48
- package/dist/esm/Components/ProductDetails/ProductDtlPrice.js +0 -22
- package/dist/esm/Components/ProductDetails/ProductImageSlider.js +0 -204
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useId, useRef, useState } from "react";
|
|
3
|
+
import { Box, Flex, Text, VisuallyHidden } from "@chakra-ui/react";
|
|
4
|
+
import { keyframes } from "@emotion/react";
|
|
5
|
+
import { Check, CircleAlert, Info } from "lucide-react";
|
|
6
|
+
import Button from "../Button/Button.js";
|
|
7
|
+
import { useCustomTheme } from "../../Theme/useCustomTheme.js";
|
|
8
|
+
import { fieldFocusVisible } from "../../Theme/chakra/focusRing.styles.js";
|
|
9
|
+
import { FIELD_RADIUS, FIELD_TRANSITION, fieldHoverBorder, fieldTextStyles } from "../Common/fieldStyles.js";
|
|
10
|
+
import { placeholderInk } from "../../Theme/tokens/builders/placeholderInk.js";
|
|
11
|
+
import { transitionFor } from "../../Theme/tokens/primitives/motion.js";
|
|
12
|
+
import { reducedMotionSx } from "../../Theme/chakra/motion.js";
|
|
13
|
+
const DEFAULT_LABELS = {
|
|
14
|
+
heading: "Check delivery",
|
|
15
|
+
inputLabel: "Pincode",
|
|
16
|
+
placeholder: "Enter pincode",
|
|
17
|
+
check: "Check",
|
|
18
|
+
change: "Change",
|
|
19
|
+
hint: "Delivery date, COD and returns for your area",
|
|
20
|
+
invalid: "Enter a 6-digit pincode",
|
|
21
|
+
unserviceable: "We don't deliver here yet",
|
|
22
|
+
checking: "Checking delivery",
|
|
23
|
+
deliveryBy: "Delivery by",
|
|
24
|
+
codAvailable: "Cash on Delivery available",
|
|
25
|
+
codUnavailable: "Cash on Delivery not available",
|
|
26
|
+
deliverTo: "Deliver to",
|
|
27
|
+
unserviceableHint: "Try another pincode",
|
|
28
|
+
};
|
|
29
|
+
const PIN_LENGTH = 6;
|
|
30
|
+
const FIELD_HEIGHT = "3rem";
|
|
31
|
+
const ERROR_RING = "1.5px";
|
|
32
|
+
const tabular = { fontVariantNumeric: "tabular-nums" };
|
|
33
|
+
const pulse = keyframes `0%, 100% { opacity: 1; } 50% { opacity: 0.5; }`;
|
|
34
|
+
const digitsOnly = (raw) => raw.replace(/\D/g, "").slice(0, PIN_LENGTH);
|
|
35
|
+
const isPin = (pin) => pin.length === PIN_LENGTH;
|
|
36
|
+
const isThenable = (value) => typeof value === "object" && value !== null && typeof value.then === "function";
|
|
37
|
+
const SkeletonBlock = ({ w, h }) => {
|
|
38
|
+
const { colors } = useCustomTheme();
|
|
39
|
+
return (_jsx(Box, { "aria-hidden": true, w: w, h: h, borderRadius: "md", bg: colors.background[200], animation: `${pulse} 1.2s ease-in-out infinite`, sx: reducedMotionSx() }));
|
|
40
|
+
};
|
|
41
|
+
/** Pincode field that answers with a delivery date, COD availability and a shipping note, held in place so the panel never jumps. */
|
|
42
|
+
const PincodeChecker = ({ value, defaultValue = "", onValueChange, status: statusProp, result: resultProp, onCheck, autoCheck = true, checkOnMount = false, labels, "data-testid": testId, }) => {
|
|
43
|
+
const { colors } = useCustomTheme();
|
|
44
|
+
const copy = { ...DEFAULT_LABELS, ...labels };
|
|
45
|
+
const baseId = useId();
|
|
46
|
+
const inputId = `${baseId}-pin`;
|
|
47
|
+
const hintId = `${baseId}-hint`;
|
|
48
|
+
const errorId = `${baseId}-error`;
|
|
49
|
+
const [innerPin, setInnerPin] = useState(digitsOnly(defaultValue));
|
|
50
|
+
const [innerStatus, setInnerStatus] = useState("idle");
|
|
51
|
+
const [innerResult, setInnerResult] = useState();
|
|
52
|
+
const [editing, setEditing] = useState(false);
|
|
53
|
+
const [localInvalid, setLocalInvalid] = useState(false);
|
|
54
|
+
const inputRef = useRef(null);
|
|
55
|
+
const changeRef = useRef(null);
|
|
56
|
+
const latest = useRef(0);
|
|
57
|
+
const mounted = useRef(true);
|
|
58
|
+
const focusInput = useRef(false);
|
|
59
|
+
const focusChange = useRef(false);
|
|
60
|
+
const pin = value === undefined ? innerPin : digitsOnly(value);
|
|
61
|
+
const status = statusProp !== null && statusProp !== void 0 ? statusProp : innerStatus;
|
|
62
|
+
const result = resultProp !== null && resultProp !== void 0 ? resultProp : innerResult;
|
|
63
|
+
const invalid = localInvalid || status === "invalid";
|
|
64
|
+
const outcome = status === "serviceable" || status === "unserviceable"
|
|
65
|
+
? status
|
|
66
|
+
: status === "idle" || status === "invalid"
|
|
67
|
+
? null
|
|
68
|
+
: result
|
|
69
|
+
? result.ok
|
|
70
|
+
? "serviceable"
|
|
71
|
+
: "unserviceable"
|
|
72
|
+
: null;
|
|
73
|
+
const checking = status === "checking";
|
|
74
|
+
const showField = editing || status === "idle" || invalid;
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
mounted.current = true;
|
|
77
|
+
return () => {
|
|
78
|
+
mounted.current = false;
|
|
79
|
+
};
|
|
80
|
+
}, []);
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (status === "serviceable" || status === "unserviceable")
|
|
83
|
+
setEditing(false);
|
|
84
|
+
}, [status]);
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
var _a, _b;
|
|
87
|
+
if (showField && focusInput.current) {
|
|
88
|
+
focusInput.current = false;
|
|
89
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
90
|
+
}
|
|
91
|
+
if (!showField && focusChange.current) {
|
|
92
|
+
focusChange.current = false;
|
|
93
|
+
(_b = changeRef.current) === null || _b === void 0 ? void 0 : _b.focus();
|
|
94
|
+
}
|
|
95
|
+
}, [showField]);
|
|
96
|
+
const settle = useCallback((answer) => {
|
|
97
|
+
if (document.activeElement === inputRef.current)
|
|
98
|
+
focusChange.current = true;
|
|
99
|
+
setInnerResult(answer);
|
|
100
|
+
setInnerStatus(answer.ok ? "serviceable" : "unserviceable");
|
|
101
|
+
setEditing(false);
|
|
102
|
+
}, []);
|
|
103
|
+
const run = useCallback((code) => {
|
|
104
|
+
if (!isPin(code)) {
|
|
105
|
+
setLocalInvalid(true);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
setLocalInvalid(false);
|
|
109
|
+
setEditing(showField);
|
|
110
|
+
const ticket = ++latest.current;
|
|
111
|
+
const answer = onCheck === null || onCheck === void 0 ? void 0 : onCheck(code);
|
|
112
|
+
if (answer === undefined)
|
|
113
|
+
return;
|
|
114
|
+
if (!isThenable(answer)) {
|
|
115
|
+
settle(answer);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
setInnerStatus("checking");
|
|
119
|
+
answer.then((resolved) => {
|
|
120
|
+
if (mounted.current && ticket === latest.current)
|
|
121
|
+
settle(resolved);
|
|
122
|
+
}, () => {
|
|
123
|
+
if (mounted.current && ticket === latest.current)
|
|
124
|
+
settle({ ok: false });
|
|
125
|
+
});
|
|
126
|
+
}, [onCheck, settle, showField]);
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (checkOnMount && isPin(pin))
|
|
129
|
+
run(pin);
|
|
130
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
131
|
+
}, []);
|
|
132
|
+
const onChange = (event) => {
|
|
133
|
+
const next = digitsOnly(event.target.value);
|
|
134
|
+
if (value === undefined)
|
|
135
|
+
setInnerPin(next);
|
|
136
|
+
onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(next);
|
|
137
|
+
setEditing(true);
|
|
138
|
+
setLocalInvalid(false);
|
|
139
|
+
if (autoCheck && isPin(next))
|
|
140
|
+
run(next);
|
|
141
|
+
};
|
|
142
|
+
const onSubmit = (event) => {
|
|
143
|
+
var _a;
|
|
144
|
+
event.preventDefault();
|
|
145
|
+
if (!isPin(pin))
|
|
146
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
147
|
+
run(pin);
|
|
148
|
+
};
|
|
149
|
+
const reopen = () => {
|
|
150
|
+
focusInput.current = true;
|
|
151
|
+
setEditing(true);
|
|
152
|
+
};
|
|
153
|
+
const fieldRing = invalid
|
|
154
|
+
? `inset 0 0 0 ${ERROR_RING} ${colors.semanticText.error}`
|
|
155
|
+
: "none";
|
|
156
|
+
const field = (_jsxs(Box, { as: "form", noValidate: true, onSubmit: onSubmit, children: [_jsxs(Flex, { gap: 2, align: "stretch", children: [_jsx(Box, { as: "input", ref: inputRef, id: inputId, type: "text", inputMode: "numeric", autoComplete: "postal-code", maxLength: PIN_LENGTH, value: pin, placeholder: copy.placeholder, onChange: onChange, onBlur: () => {
|
|
157
|
+
if (pin.length > 0 && !isPin(pin))
|
|
158
|
+
setLocalInvalid(true);
|
|
159
|
+
}, "aria-invalid": invalid || undefined, "aria-describedby": invalid ? errorId : hintId, flex: 1, minW: 0, h: FIELD_HEIGHT, px: 4, fontSize: "md", bg: colors.background[50], borderWidth: "1px", borderStyle: "solid", borderColor: "transparent", borderRadius: FIELD_RADIUS, boxShadow: fieldRing, transition: FIELD_TRANSITION, _placeholder: { color: placeholderInk(colors), opacity: 1 }, _hover: invalid ? undefined : { borderColor: fieldHoverBorder(colors) }, _focusVisible: {
|
|
160
|
+
borderColor: invalid ? "transparent" : colors.primary[500],
|
|
161
|
+
...fieldFocusVisible(colors),
|
|
162
|
+
boxShadow: invalid ? `${fieldRing}, ${fieldFocusVisible(colors).boxShadow}` : fieldFocusVisible(colors).boxShadow,
|
|
163
|
+
}, sx: { ...fieldTextStyles(colors), ...tabular, letterSpacing: "0.08em", ...reducedMotionSx() } }), _jsx(Button, { type: "submit", size: "lg", label: copy.check })] }), invalid ? (_jsxs(Flex, { id: errorId, mt: 2, gap: 1.5, align: "center", fontSize: "sm", fontWeight: 500, color: colors.semanticText.error, children: [_jsx(CircleAlert, { size: 16, "aria-hidden": true, style: { flexShrink: 0 } }), _jsx(Text, { as: "span", children: (status === "invalid" && (result === null || result === void 0 ? void 0 : result.message)) || copy.invalid })] })) : (_jsx(Text, { id: hintId, mt: 2, fontSize: "xs", color: colors.textMuted, children: copy.hint }))] }));
|
|
164
|
+
const changeButton = (_jsx(Button, { ref: changeRef, variant: "link", size: "sm", h: "2.75rem", flexShrink: 0, onClick: reopen, label: copy.change }));
|
|
165
|
+
const savedLine = !showField && outcome === "serviceable" ? (_jsxs(Flex, { align: "center", justify: "space-between", gap: 3, children: [_jsxs(Text, { fontSize: "sm", color: colors.textMuted, sx: tabular, children: [copy.deliverTo, " ", _jsxs(Text, { as: "span", fontWeight: 600, color: colors.header[900], children: [pin, (result === null || result === void 0 ? void 0 : result.city) ? ` · ${result.city}` : ""] })] }), changeButton] })) : null;
|
|
166
|
+
const details = outcome === "serviceable" && result ? (_jsxs(Flex, { direction: "column", gap: 2, children: [result.deliveryBy && (_jsxs(Text, { fontSize: "lg", lineHeight: 1.3, sx: tabular, children: [_jsxs(Text, { as: "span", fontSize: "sm", color: colors.textMuted, children: [copy.deliveryBy, " "] }), _jsx(Text, { as: "span", fontWeight: 700, color: colors.header[900], children: result.deliveryBy })] })), result.cod !== undefined && (_jsxs(Flex, { align: "center", gap: 2, fontSize: "sm", fontWeight: 500, color: colors.header[900], children: [result.cod ? (_jsx(Box, { as: "span", color: colors.semanticText.success, display: "inline-flex", children: _jsx(Check, { size: 16, "aria-hidden": true }) })) : (_jsx(Box, { as: "span", color: colors.semanticText.warning, display: "inline-flex", children: _jsx(Info, { size: 16, "aria-hidden": true }) })), _jsx(Text, { as: "span", children: result.cod ? copy.codAvailable : copy.codUnavailable })] })), result.shippingNote && (_jsx(Text, { fontSize: "xs", color: colors.textMuted, children: result.shippingNote }))] })) : outcome === "unserviceable" ? (_jsxs(Flex, { align: "flex-start", justify: "space-between", gap: 3, children: [_jsxs(Box, { children: [_jsx(Text, { fontSize: "md", fontWeight: 600, color: colors.header[900], children: (result === null || result === void 0 ? void 0 : result.message) || copy.unserviceable }), _jsx(Text, { fontSize: "sm", color: colors.textMuted, children: copy.unserviceableHint })] }), !showField && changeButton] })) : null;
|
|
167
|
+
const skeleton = checking && !outcome && (_jsxs(Flex, { direction: "column", gap: 3, "aria-hidden": true, children: [_jsx(SkeletonBlock, { w: "55%", h: "0.875rem" }), _jsx(SkeletonBlock, { w: "40%", h: "1.5rem" }), _jsx(SkeletonBlock, { w: "70%", h: "0.875rem" })] }));
|
|
168
|
+
return (_jsxs(Box, { "aria-busy": checking || undefined, bg: colors.background[100], borderRadius: "xl", p: 4, "data-testid": testId, children: [_jsx(Text, { as: "h2", fontSize: "md", fontWeight: 600, color: colors.header[900], mb: 3, children: copy.heading }), showField && (_jsxs(_Fragment, { children: [_jsx(VisuallyHidden, { as: "label", htmlFor: inputId, children: copy.inputLabel }), field] })), _jsxs(Flex, { direction: "column", gap: 3, mt: showField && (outcome || skeleton) ? 4 : 0, "aria-live": "polite", opacity: checking && outcome ? 0.6 : 1, transition: transitionFor(["opacity"]), sx: reducedMotionSx(), children: [checking && _jsx(VisuallyHidden, { children: copy.checking }), savedLine, details, skeleton] })] }));
|
|
169
|
+
};
|
|
170
|
+
export default PincodeChecker;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Flex, Text, VisuallyHidden } from "@chakra-ui/react";
|
|
3
|
+
import { useCustomTheme } from "../../Theme/useCustomTheme.js";
|
|
4
|
+
import { discountAmount, discountPercent, formatPrice } from "../../Utils/price.js";
|
|
5
|
+
const SCALE = {
|
|
6
|
+
sm: { price: "0.9375rem", priceLine: 1.33, secondary: "0.75rem", secondaryLine: 1.33, note: "0.75rem" },
|
|
7
|
+
md: { price: "1.125rem", priceLine: 1.22, secondary: "0.8125rem", secondaryLine: 1.38, note: "0.75rem" },
|
|
8
|
+
lg: { price: "1.75rem", priceLine: 1.21, secondary: "0.9375rem", secondaryLine: 1.47, note: "0.8125rem" },
|
|
9
|
+
};
|
|
10
|
+
const ALIGN = {
|
|
11
|
+
start: { items: "flex-start", justify: "flex-start", text: "left" },
|
|
12
|
+
center: { items: "center", justify: "center", text: "center" },
|
|
13
|
+
end: { items: "flex-end", justify: "flex-end", text: "right" },
|
|
14
|
+
};
|
|
15
|
+
const DEFAULT_LABELS = {
|
|
16
|
+
price: "Price",
|
|
17
|
+
originalPrice: "Original price",
|
|
18
|
+
off: (percent) => `${percent}% off`,
|
|
19
|
+
save: (amount) => `You save ${amount}`,
|
|
20
|
+
};
|
|
21
|
+
const unitText = (unit, fmt) => typeof unit === "string" ? unit : `${formatPrice(unit.value, fmt)} / ${unit.per === 1 ? "" : `${unit.per} `}${unit.unit}`;
|
|
22
|
+
const tabular = { fontVariantNumeric: "tabular-nums" };
|
|
23
|
+
/** Selling price, struck MRP, discount, savings, tax note and unit price in one tabular, screen-reader-friendly block. */
|
|
24
|
+
const PriceBlock = ({ price, mrp, priceRange, size = "md", align = "start", showSavings = false, taxNote, unitPrice, currency, locale, labels, "data-testid": testId, }) => {
|
|
25
|
+
const { colors } = useCustomTheme();
|
|
26
|
+
const fmt = { currency, locale };
|
|
27
|
+
const copy = { ...DEFAULT_LABELS, ...labels };
|
|
28
|
+
const scale = SCALE[size];
|
|
29
|
+
const a = ALIGN[align];
|
|
30
|
+
const isRange = !!priceRange && priceRange.min !== priceRange.max;
|
|
31
|
+
const selling = priceRange ? priceRange.min : price;
|
|
32
|
+
const priceText = isRange ? `${formatPrice(priceRange.min, fmt)} – ${formatPrice(priceRange.max, fmt)}` : selling === undefined ? null : formatPrice(selling, fmt);
|
|
33
|
+
const percent = !priceRange && selling !== undefined ? discountPercent(selling, mrp) : 0;
|
|
34
|
+
const saved = !priceRange && selling !== undefined ? discountAmount(selling, mrp) : 0;
|
|
35
|
+
const hasDiscount = percent > 0;
|
|
36
|
+
const muted = { fontSize: scale.note, lineHeight: 1.4, color: colors.textMuted, sx: tabular };
|
|
37
|
+
return (_jsxs(Flex, { direction: "column", align: a.items, gap: 0.5, textAlign: a.text, "data-testid": testId, children: [_jsxs(Flex, { align: "baseline", justify: a.justify, columnGap: 2, rowGap: 0, wrap: "wrap", children: [priceText !== null && (_jsxs(Text, { as: "span", fontSize: scale.price, lineHeight: scale.priceLine, fontWeight: 700, letterSpacing: "-0.02em", color: colors.header[900], sx: tabular, children: [_jsxs(VisuallyHidden, { children: [copy.price, " "] }), priceText] })), hasDiscount && mrp !== undefined && (_jsxs(_Fragment, { children: [_jsxs(Box, { as: "del", fontSize: scale.secondary, lineHeight: scale.secondaryLine, color: colors.textMuted, textDecoration: "line-through", sx: tabular, children: [_jsxs(VisuallyHidden, { children: [copy.originalPrice, " "] }), formatPrice(mrp, fmt)] }), _jsx(Text, { as: "span", fontSize: scale.secondary, lineHeight: scale.secondaryLine, fontWeight: 600, color: colors.semanticText.success, sx: tabular, children: copy.off(percent) })] }))] }), showSavings && hasDiscount && (_jsx(Text, { as: "span", fontSize: scale.note, lineHeight: 1.4, fontWeight: 600, color: colors.semanticText.success, sx: tabular, children: copy.save(formatPrice(saved, fmt)) })), taxNote && (_jsx(Text, { as: "span", ...muted, children: taxNote })), unitPrice != null && (_jsx(Text, { as: "span", ...muted, children: unitText(unitPrice, fmt) }))] }));
|
|
38
|
+
};
|
|
39
|
+
export default PriceBlock;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Flex, Text } from "@chakra-ui/react";
|
|
3
|
+
import { useCustomTheme } from "../../Theme/useCustomTheme.js";
|
|
4
|
+
import { isDarkCanvas } from "../../Theme/tokens/builders/accentText.js";
|
|
5
|
+
import { resolveFilledSurface } from "../../Theme/tokens/builders/filledSurface.js";
|
|
6
|
+
import { overlayScrim } from "../../Theme/tokens/builders/scrim.js";
|
|
7
|
+
import { resolveStatusTone } from "../../Theme/tokens/builders/statusTone.js";
|
|
8
|
+
const scaleFor = (colors, tone) => {
|
|
9
|
+
switch (tone) {
|
|
10
|
+
case "accent":
|
|
11
|
+
return colors.primary;
|
|
12
|
+
case "neutral":
|
|
13
|
+
return colors.gray;
|
|
14
|
+
case "success":
|
|
15
|
+
return colors.semantic.success;
|
|
16
|
+
case "warning":
|
|
17
|
+
return colors.semantic.warning;
|
|
18
|
+
case "danger":
|
|
19
|
+
return colors.semantic.error;
|
|
20
|
+
case "info":
|
|
21
|
+
return colors.semantic.info;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const SIZE = {
|
|
25
|
+
sm: { h: "1.25rem", font: "0.6875rem", px: 1.5 },
|
|
26
|
+
md: { h: "1.5rem", font: "0.75rem", px: 2 },
|
|
27
|
+
};
|
|
28
|
+
const lookFor = (colors, tone, variant) => {
|
|
29
|
+
if (variant === "glass") {
|
|
30
|
+
const dot = tone === "inverse" || tone === "neutral" ? undefined : resolveFilledSurface(scaleFor(colors, tone), colors).fill;
|
|
31
|
+
return { bg: overlayScrim(colors), color: colors.white, dot };
|
|
32
|
+
}
|
|
33
|
+
if (tone === "inverse") {
|
|
34
|
+
return variant === "soft"
|
|
35
|
+
? { bg: colors.background[100], color: colors.header[900] }
|
|
36
|
+
: { bg: colors.header[900], color: colors.background[50] };
|
|
37
|
+
}
|
|
38
|
+
const scale = scaleFor(colors, tone);
|
|
39
|
+
if (variant === "soft") {
|
|
40
|
+
const soft = resolveStatusTone(scale, isDarkCanvas(colors.backgroundColor.main) ? "dark" : "light");
|
|
41
|
+
return { bg: soft.bg, color: soft.fg };
|
|
42
|
+
}
|
|
43
|
+
const filled = resolveFilledSurface(scale, colors);
|
|
44
|
+
return { bg: filled.fill, color: filled.color };
|
|
45
|
+
};
|
|
46
|
+
/** Stack of corner badges (sale, new, low stock…) built from `ProductBadge[]`; filled chips resolve ink per brand and mode. */
|
|
47
|
+
const ProductBadges = ({ badges, max = 2, size = "sm", variant = "solid", orientation = "vertical", "data-testid": testId, }) => {
|
|
48
|
+
const { colors } = useCustomTheme();
|
|
49
|
+
const shown = badges.slice(0, Math.max(0, max));
|
|
50
|
+
if (shown.length === 0)
|
|
51
|
+
return null;
|
|
52
|
+
const s = SIZE[size];
|
|
53
|
+
return (_jsx(Flex, { as: "ul", role: "list", listStyleType: "none", m: 0, p: 0, direction: orientation === "vertical" ? "column" : "row", align: "flex-start", wrap: orientation === "vertical" ? "nowrap" : "wrap", gap: 1.5, maxW: "100%", "data-testid": testId, children: shown.map((badge, i) => {
|
|
54
|
+
var _a, _b;
|
|
55
|
+
const look = lookFor(colors, (_a = badge.tone) !== null && _a !== void 0 ? _a : "neutral", variant);
|
|
56
|
+
return (_jsxs(Flex, { as: "li", "data-tone": (_b = badge.tone) !== null && _b !== void 0 ? _b : "neutral", "data-variant": variant, align: "center", gap: 1, h: s.h, px: s.px, maxW: "100%", borderRadius: "md", bg: look.bg, color: look.color, sx: variant === "glass" ? { backdropFilter: "blur(8px)", WebkitBackdropFilter: "blur(8px)" } : undefined, children: [look.dot && _jsx(Box, { as: "span", "aria-hidden": true, boxSize: "6px", flexShrink: 0, borderRadius: "full", bg: look.dot }), _jsx(Text, { as: "span", fontSize: s.font, fontWeight: 600, lineHeight: 1, letterSpacing: "0.01em", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", sx: { fontVariantNumeric: "tabular-nums" }, children: badge.label })] }, `${badge.label}-${i}`));
|
|
57
|
+
}) }));
|
|
58
|
+
};
|
|
59
|
+
export default ProductBadges;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,62 +1,113 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
3
|
-
import { Box,
|
|
4
|
-
import
|
|
5
|
-
import ProductPrice from "./ProductPrice.js";
|
|
6
|
-
import Button from "../Button/Button.js";
|
|
7
|
-
import ProductLabel from "./ProductLabel.js";
|
|
2
|
+
import { useId, useState } from "react";
|
|
3
|
+
import { Box, Flex, Text, VisuallyHidden } from "@chakra-ui/react";
|
|
4
|
+
import { Check } from "lucide-react";
|
|
8
5
|
import { useCustomTheme } from "../../Theme/useCustomTheme.js";
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
6
|
+
import { transitionFor } from "../../Theme/tokens/primitives/motion.js";
|
|
7
|
+
import { reducedMotionSx, usePrefersReducedMotion } from "../../Theme/chakra/motion.js";
|
|
8
|
+
import { focusVisibleRing } from "../../Theme/chakra/focusRing.styles.js";
|
|
9
|
+
import PriceBlock from "../PriceBlock/PriceBlock.js";
|
|
10
|
+
import ProductBadges from "../ProductBadges/ProductBadges.js";
|
|
11
|
+
import Rating from "../Rating/Rating.js";
|
|
12
|
+
import StockStatus from "../StockStatus/StockStatus.js";
|
|
13
|
+
import WishlistToggle from "../WishlistToggle/WishlistToggle.js";
|
|
14
|
+
import { optionInk } from "../OptionChip/optionInk.js";
|
|
15
|
+
import { DEFAULT_LABELS } from "./cardLabels.js";
|
|
16
|
+
import { DENSITY, FINE_POINTER, MEDIA_RADIUS, TITLE_LINES } from "./cardMetrics.js";
|
|
17
|
+
import { DEFAULT_SWATCH_LIMIT, deriveAction, framesFor, lowStockCount, rangeOf, soleVariantOf, swatchAxisOf, valueSoldOut, variantsFor, isSoldOut, } from "./productCardModel.js";
|
|
18
|
+
import ProductCardAction from "./ProductCardAction.js";
|
|
19
|
+
import ProductCardMedia from "./ProductCardMedia.js";
|
|
20
|
+
import ProductCardSkeleton from "./ProductCardSkeleton.js";
|
|
21
|
+
import ProductCardSwatches from "./ProductCardSwatches.js";
|
|
22
|
+
import QuickAddSheet from "./QuickAddSheet.js";
|
|
23
|
+
const NO_AXES = [];
|
|
24
|
+
const NO_VARIANTS = [];
|
|
25
|
+
/** Chromeless product card: tonal media well, brand, title, rating, price and one action; the whole card is one link target. */
|
|
26
|
+
const ProductCard = (props) => {
|
|
27
|
+
var _a, _b;
|
|
28
|
+
const { title, brand, media, alt, price, mrp, unitPrice, rating, ratingCount, badges, axes = NO_AXES, variants = NO_VARIANTS, mediaIdsByOption, swatchLimit = DEFAULT_SWATCH_LIMIT, stock, outOfStock, href, linkAs, linkProps, onPress, layout = "grid", density = "comfortable", aspectRatio = "4/5", action: actionOverride, quantity, onQuantityChange, onAdd, quickAddPresentation, quickAddDrawerProps, onRemove, onNotify, wishlisted, onWishlistChange, selected = false, isLoading = false, mediaOverlay, meta, actions, labels, currency, locale, "data-testid": testId, } = props;
|
|
29
|
+
const { colors } = useCustomTheme();
|
|
30
|
+
const reduced = usePrefersReducedMotion();
|
|
31
|
+
const titleId = useId();
|
|
32
|
+
const [sheetOpen, setSheetOpen] = useState(false);
|
|
33
|
+
const [picked, setPicked] = useState(null);
|
|
34
|
+
const [previewed, setPreviewed] = useState(null);
|
|
35
|
+
const [innerQuantity, setInnerQuantity] = useState(0);
|
|
36
|
+
const [pressed, setPressed] = useState(false);
|
|
37
|
+
if (isLoading)
|
|
38
|
+
return _jsx(ProductCardSkeleton, { layout: layout, density: density, aspectRatio: aspectRatio, "data-testid": testId });
|
|
39
|
+
const copy = { ...DEFAULT_LABELS, ...labels };
|
|
40
|
+
const m = DENSITY[density];
|
|
41
|
+
const horizontal = layout === "list";
|
|
42
|
+
const rail = layout === "rail";
|
|
43
|
+
const soldOut = isSoldOut(outOfStock, stock, variants);
|
|
44
|
+
const sole = soleVariantOf(axes, variants);
|
|
45
|
+
const action = deriveAction(actionOverride, soldOut, axes);
|
|
46
|
+
const left = soldOut ? undefined : lowStockCount(stock, sole);
|
|
47
|
+
const bagged = quantity !== null && quantity !== void 0 ? quantity : innerQuantity;
|
|
48
|
+
const maxQuantity = (() => {
|
|
49
|
+
const count = stock !== null && stock !== void 0 ? stock : sole === null || sole === void 0 ? void 0 : sole.stock;
|
|
50
|
+
return count !== undefined && count > 0 ? count : undefined;
|
|
51
|
+
})();
|
|
52
|
+
const swatchAxis = swatchAxisOf(axes);
|
|
53
|
+
const activeId = (_a = previewed !== null && previewed !== void 0 ? previewed : picked) !== null && _a !== void 0 ? _a : (_b = swatchAxis === null || swatchAxis === void 0 ? void 0 : swatchAxis.values[0]) === null || _b === void 0 ? void 0 : _b.id;
|
|
54
|
+
const frames = framesFor(media, mediaIdsByOption, swatchAxis === null || swatchAxis === void 0 ? void 0 : swatchAxis.id, activeId);
|
|
55
|
+
const priced = variantsFor(variants, swatchAxis === null || swatchAxis === void 0 ? void 0 : swatchAxis.id, picked !== null && picked !== void 0 ? picked : undefined);
|
|
56
|
+
const range = sole ? undefined : rangeOf(priced);
|
|
57
|
+
const soldOutValues = new Set(swatchAxis ? swatchAxis.values.filter((value) => valueSoldOut(variants, swatchAxis.id, value.id)).map((value) => value.id) : []);
|
|
58
|
+
const interactive = !!href || !!linkAs || !!onPress;
|
|
59
|
+
const LinkTag = href || linkAs ? (linkAs !== null && linkAs !== void 0 ? linkAs : "a") : "button";
|
|
60
|
+
const linkExtras = href || linkAs ? { href, ...linkProps } : { type: "button" };
|
|
61
|
+
const add = () => {
|
|
62
|
+
if (quantity === undefined)
|
|
63
|
+
setInnerQuantity(1);
|
|
64
|
+
onAdd === null || onAdd === void 0 ? void 0 : onAdd(sole === null || sole === void 0 ? void 0 : sole.id, 1);
|
|
20
65
|
};
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
66
|
+
const press = () => {
|
|
67
|
+
if (action === "notify")
|
|
68
|
+
onNotify === null || onNotify === void 0 ? void 0 : onNotify();
|
|
69
|
+
else if (action === "options")
|
|
70
|
+
setSheetOpen(true);
|
|
71
|
+
else
|
|
72
|
+
add();
|
|
26
73
|
};
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
74
|
+
const changeQuantity = (next) => {
|
|
75
|
+
if (quantity === undefined)
|
|
76
|
+
setInnerQuantity(next);
|
|
77
|
+
onQuantityChange === null || onQuantityChange === void 0 ? void 0 : onQuantityChange(next);
|
|
32
78
|
};
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
md: { cardWidth: "20rem", imgHeight: "15rem", padding: 5 },
|
|
37
|
-
lg: { cardWidth: "22rem", imgHeight: "17rem", padding: 6 },
|
|
79
|
+
const remove = () => {
|
|
80
|
+
changeQuantity(0);
|
|
81
|
+
onRemove === null || onRemove === void 0 ? void 0 : onRemove();
|
|
38
82
|
};
|
|
39
|
-
const {
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
83
|
+
const defaultControl = (_jsx(ProductCardAction, { kind: action, title: title, labels: copy, placement: horizontal ? "inline" : "media", inset: m.actionInset, hoverBar: density === "spacious" && !horizontal, quantity: action === "notify" ? 0 : bagged, maxQuantity: maxQuantity, onPress: press, onQuantityChange: changeQuantity, onRemove: remove }));
|
|
84
|
+
const slotted = horizontal ? (_jsx(Box, { "data-control": true, position: "relative", zIndex: 2, children: actions })) : (_jsx(Box, { "data-control": true, position: "absolute", right: m.actionInset, bottom: m.actionInset, zIndex: 2, children: actions }));
|
|
85
|
+
const control = actions ? slotted : defaultControl;
|
|
86
|
+
const wishlist = (_jsx(WishlistToggle, { variant: horizontal ? "soft" : "glass", size: "md", label: copy.save(title), selected: wishlisted, onSelectedChange: onWishlistChange }));
|
|
87
|
+
const hoverFrame = previewed === null ? frames[1] : undefined;
|
|
88
|
+
const pressable = interactive && !reduced;
|
|
89
|
+
const ink = optionInk(colors).selected;
|
|
90
|
+
const metaLine = meta !== null && meta !== void 0 ? meta : (left !== undefined ? _jsx(StockStatus, { status: "low-stock", count: left, size: "sm" }) : null);
|
|
91
|
+
return (_jsxs(Box, { as: "article", "aria-labelledby": titleId, "data-testid": testId, "data-layout": layout, "data-density": density, "data-soldout": soldOut || undefined, "data-selected": selected || undefined, "data-pressed": pressed || undefined, onPointerDown: (event) => setPressed(!event.target.closest("[data-control]")), onPointerUp: () => setPressed(false), onPointerLeave: () => setPressed(false), onPointerCancel: () => setPressed(false), position: "relative", display: "flex", flexDirection: horizontal ? "row" : "column", gap: horizontal ? 3 : 0, flexGrow: rail ? 0 : 1, flexShrink: rail ? 0 : 1, w: rail ? m.railWidth : "100%", minW: 0, cursor: interactive ? "pointer" : "default", transition: transitionFor(["transform"]), sx: {
|
|
92
|
+
...(rail ? { scrollSnapAlign: "start" } : {}),
|
|
93
|
+
...reducedMotionSx(),
|
|
94
|
+
...(pressable ? { "&[data-pressed]": { transform: "scale(0.98)" } } : {}),
|
|
95
|
+
[FINE_POINTER]: {
|
|
96
|
+
"&:hover [data-hover-frame], &:focus-within [data-hover-frame]": { opacity: 1 },
|
|
97
|
+
"&:hover [data-bar], &:focus-within [data-bar]": { opacity: 1, transform: "none" },
|
|
98
|
+
...(reduced ? {} : { "&:hover [data-primary]": { transform: "scale(1.03)" } }),
|
|
99
|
+
},
|
|
100
|
+
}, children: [_jsxs(ProductCardMedia, { primary: frames[0], hover: hoverFrame, title: title, alt: alt, aspectRatio: aspectRatio, dimmed: soldOut, selected: selected, fallbackText: copy.imageUnavailable, width: horizontal ? m.listMedia : undefined, children: [!soldOut && badges && badges.length > 0 ? (_jsx(Box, { position: "absolute", top: m.badgeInset, insetStart: m.badgeInset, zIndex: 2, maxW: "calc(100% - 3.5rem)", pointerEvents: "none", children: _jsx(ProductBadges, { badges: badges, max: 2, size: "sm", variant: "solid" }) })) : null, !horizontal ? (_jsx(Box, { "data-control": true, position: "absolute", top: m.wishlistInset, insetEnd: m.wishlistInset, zIndex: 2, children: wishlist })) : null, selected ? (_jsx(Box, { "aria-hidden": true, position: "absolute", bottom: m.actionInset, insetStart: m.actionInset, zIndex: 2, display: "inline-flex", alignItems: "center", justifyContent: "center", boxSize: "1.5rem", borderRadius: "full", bg: ink.fill, color: ink.ink, children: _jsx(Check, { size: 14, strokeWidth: 3 }) })) : null, mediaOverlay ? (_jsx(Box, { position: "absolute", inset: 0, zIndex: 2, pointerEvents: "none", sx: { "& > *": { pointerEvents: "auto" } }, children: mediaOverlay })) : null, !horizontal ? control : null] }), _jsxs(Flex, { direction: "column", flex: 1, minW: 0, gap: m.bodyGap, pt: horizontal ? 0 : m.bodyTop, children: [brand ? (_jsx(Text, { as: "span", fontSize: m.brand.size, lineHeight: `${m.brand.line}px`, fontWeight: 500, color: colors.textMuted, noOfLines: 1, children: brand })) : null, _jsx(Text, { as: "h3", id: titleId, m: 0, fontSize: m.title.size, lineHeight: `${m.title.line}px`, fontWeight: 500, color: colors.header[900], noOfLines: TITLE_LINES, minH: horizontal ? undefined : `${m.title.line * TITLE_LINES}px`, children: interactive ? (_jsx(Box, { as: LinkTag, ...linkExtras, "data-card-link": true, onClick: onPress, color: "inherit", textAlign: "start", textDecoration: "none", cursor: "pointer", sx: {
|
|
101
|
+
font: "inherit",
|
|
102
|
+
background: "none",
|
|
103
|
+
border: 0,
|
|
104
|
+
p: 0,
|
|
105
|
+
outline: "none",
|
|
106
|
+
"&::after": { content: '""', position: "absolute", inset: 0, zIndex: 1, borderRadius: MEDIA_RADIUS },
|
|
107
|
+
"&:focus-visible::after": focusVisibleRing(colors),
|
|
108
|
+
}, children: title })) : (title) }), selected ? _jsx(VisuallyHidden, { children: copy.selected }) : null, rating !== undefined && rating > 0 ? (_jsx(Rating, { value: rating, count: ratingCount, size: "xs", compactCount: true })) : horizontal ? null : (_jsx(Box, { minH: "1rem", "aria-hidden": true })), _jsx(PriceBlock, { size: m.price, price: price, mrp: soldOut ? undefined : mrp, priceRange: range, unitPrice: unitPrice, currency: currency, locale: locale }), soldOut ? _jsx(StockStatus, { status: "out-of-stock", message: copy.soldOut, size: "sm" }) : null, metaLine, swatchAxis && activeId ? (_jsx(ProductCardSwatches, { axis: swatchAxis, limit: swatchLimit, dot: m.dot, activeId: activeId, soldOutIds: soldOutValues, labels: copy, onPreview: setPreviewed, onPick: setPicked })) : null, horizontal ? (_jsxs(Flex, { "data-control": true, position: "relative", zIndex: 2, mt: "auto", pt: 2, align: "center", justify: "space-between", gap: 1, children: [wishlist, control] })) : null] }), action === "options" ? (_jsx(QuickAddSheet, { isOpen: sheetOpen, onClose: () => setSheetOpen(false), title: title, brand: brand, media: frames[0], axes: axes, variants: variants, defaultSelection: swatchAxis && picked ? { [swatchAxis.id]: picked } : undefined, currency: currency, locale: locale, presentation: quickAddPresentation, drawerContentProps: quickAddDrawerProps, onConfirm: (variant, count) => {
|
|
109
|
+
onAdd === null || onAdd === void 0 ? void 0 : onAdd(variant.id, count);
|
|
110
|
+
setSheetOpen(false);
|
|
111
|
+
} })) : null] }));
|
|
61
112
|
};
|
|
62
113
|
export default ProductCard;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "@chakra-ui/react";
|
|
3
|
+
import { keyframes } from "@emotion/react";
|
|
4
|
+
import { Bell, Plus } from "lucide-react";
|
|
5
|
+
import { useCustomTheme } from "../../Theme/useCustomTheme.js";
|
|
6
|
+
import { MOTION_EASE, transitionFor } from "../../Theme/tokens/primitives/motion.js";
|
|
7
|
+
import { overlayScrim } from "../../Theme/tokens/builders/scrim.js";
|
|
8
|
+
import { reducedMotionSx, usePrefersReducedMotion } from "../../Theme/chakra/motion.js";
|
|
9
|
+
import { focusVisibleStyles } from "../../Theme/chakra/focusRing.styles.js";
|
|
10
|
+
import QuantityStepper from "../QuantityStepper/QuantityStepper.js";
|
|
11
|
+
import { FINE_POINTER } from "./cardMetrics.js";
|
|
12
|
+
const morphIn = keyframes `
|
|
13
|
+
0% { opacity: 0; transform: scale(0.85); }
|
|
14
|
+
100% { opacity: 1; transform: scale(1); }
|
|
15
|
+
`;
|
|
16
|
+
const DISC = "2.25rem";
|
|
17
|
+
const BAR_HEIGHT = "2.5rem";
|
|
18
|
+
/** The card's one control: glass circle (or hover bar), notify pill, or the stepper it morphs into. */
|
|
19
|
+
const ProductCardAction = ({ kind, title, labels, placement, inset, hoverBar, quantity, maxQuantity, onPress, onQuantityChange, onRemove, }) => {
|
|
20
|
+
const { colors } = useCustomTheme();
|
|
21
|
+
const reduced = usePrefersReducedMotion();
|
|
22
|
+
const onMedia = placement === "media";
|
|
23
|
+
const place = onMedia
|
|
24
|
+
? { position: "absolute", right: inset, bottom: inset, zIndex: 2 }
|
|
25
|
+
: { position: "relative", zIndex: 2 };
|
|
26
|
+
if (quantity > 0) {
|
|
27
|
+
return (_jsx(Box, { "data-control": true, "data-stepper": true, ...place, display: "inline-flex", borderRadius: "full", bg: onMedia ? colors.surfaceRaised : colors.transparent, boxShadow: onMedia ? "sm" : "none", animation: reduced ? undefined : `${morphIn} 200ms ${MOTION_EASE}`, children: _jsx(QuantityStepper, { size: "sm", value: quantity, min: 1, max: maxQuantity, removable: true, itemLabel: title, onValueChange: onQuantityChange, onRemove: onRemove }) }));
|
|
28
|
+
}
|
|
29
|
+
const isNotify = kind === "notify";
|
|
30
|
+
const name = isNotify ? labels.notifyMeFor(title) : kind === "options" ? labels.chooseOptions(title) : labels.addToBag(title);
|
|
31
|
+
const barText = kind === "options" ? labels.chooseOptionsText : labels.addToBagText;
|
|
32
|
+
const bar = onMedia && hoverBar && !isNotify;
|
|
33
|
+
const look = onMedia
|
|
34
|
+
? { bg: overlayScrim(colors), hoverBg: overlayScrim(colors), color: colors.white }
|
|
35
|
+
: { bg: colors.background[100], hoverBg: colors.background[200], color: colors.header[900] };
|
|
36
|
+
return (_jsxs(Box, { as: "button", type: "button", "data-control": true, "data-bar": bar || undefined, "data-action": kind, "aria-label": name, onClick: onPress, ...place, display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 1.5, h: DISC, minW: DISC, px: isNotify ? 3 : 0, borderRadius: "full", bg: look.bg, color: look.color, cursor: "pointer", transition: transitionFor(["background-color", "opacity", "transform"], "base"), _hover: { bg: look.hoverBg }, _active: reduced ? undefined : { transform: "scale(0.9)" }, sx: {
|
|
37
|
+
outline: "none",
|
|
38
|
+
...(onMedia ? { backdropFilter: "blur(8px)", WebkitBackdropFilter: "blur(8px)" } : {}),
|
|
39
|
+
...focusVisibleStyles(colors),
|
|
40
|
+
...reducedMotionSx({ "&:active": { transform: "none" } }),
|
|
41
|
+
"&::before": { content: '""', position: "absolute", inset: "-0.25rem" },
|
|
42
|
+
"& [data-label]": { display: isNotify ? "inline" : "none" },
|
|
43
|
+
...(bar
|
|
44
|
+
? {
|
|
45
|
+
[FINE_POINTER]: {
|
|
46
|
+
left: inset,
|
|
47
|
+
right: "auto",
|
|
48
|
+
width: `calc(100% - 2 * ${inset})`,
|
|
49
|
+
height: BAR_HEIGHT,
|
|
50
|
+
borderRadius: "lg",
|
|
51
|
+
opacity: 0,
|
|
52
|
+
transform: reduced ? "none" : "translateY(0.5rem)",
|
|
53
|
+
"& [data-label]": { display: "inline" },
|
|
54
|
+
"&:focus-visible": { opacity: 1, transform: "none" },
|
|
55
|
+
},
|
|
56
|
+
}
|
|
57
|
+
: {}),
|
|
58
|
+
}, children: [isNotify ? _jsx(Bell, { size: 16, "aria-hidden": true }) : _jsx(Plus, { size: 18, strokeWidth: 2.25, "aria-hidden": true }), _jsx(Text, { as: "span", "data-label": true, fontSize: "0.8125rem", fontWeight: 600, lineHeight: 1, whiteSpace: "nowrap", children: isNotify ? labels.notifyMe : barText })] }));
|
|
59
|
+
};
|
|
60
|
+
export default ProductCardAction;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Children } from "react";
|
|
3
|
+
import { Box } from "@chakra-ui/react";
|
|
4
|
+
const DEFAULT_COLUMNS = { base: 2, sm: 2, md: 3, lg: 4, xl: 5 };
|
|
5
|
+
const template = (columns) => `repeat(${columns}, minmax(0, 1fr))`;
|
|
6
|
+
/** List of cards on one grid: 12 px columns and 24 px rows on a phone, 24 and 32 from lg (override with `gap`), rows level in height. */
|
|
7
|
+
const ProductCardGrid = ({ children, columns, gap, columnGap, rowGap, "aria-label": ariaLabel, "data-testid": testId }) => {
|
|
8
|
+
var _a, _b;
|
|
9
|
+
const cols = { ...DEFAULT_COLUMNS, ...columns };
|
|
10
|
+
return (_jsx(Box, { as: "ul", role: "list", "aria-label": ariaLabel, "data-testid": testId, listStyleType: "none", m: 0, p: 0, display: "grid", alignItems: "stretch", columnGap: (_a = columnGap !== null && columnGap !== void 0 ? columnGap : gap) !== null && _a !== void 0 ? _a : { base: "0.75rem", lg: "1.5rem" }, rowGap: (_b = rowGap !== null && rowGap !== void 0 ? rowGap : gap) !== null && _b !== void 0 ? _b : { base: "1.5rem", lg: "2rem" }, gridTemplateColumns: {
|
|
11
|
+
base: template(cols.base),
|
|
12
|
+
sm: template(cols.sm),
|
|
13
|
+
md: template(cols.md),
|
|
14
|
+
lg: template(cols.lg),
|
|
15
|
+
xl: template(cols.xl),
|
|
16
|
+
}, children: Children.toArray(children).map((child, index) => (_jsx(Box, { as: "li", display: "flex", minW: 0, children: _jsx(Box, { display: "flex", flexDirection: "column", flex: 1, minW: 0, children: child }) }, index))) }));
|
|
17
|
+
};
|
|
18
|
+
export default ProductCardGrid;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { Box, Text } from "@chakra-ui/react";
|
|
4
|
+
import { ImageOff } from "lucide-react";
|
|
5
|
+
import { useCustomTheme } from "../../Theme/useCustomTheme.js";
|
|
6
|
+
import { transitionFor } from "../../Theme/tokens/primitives/motion.js";
|
|
7
|
+
import { reducedMotionSx } from "../../Theme/chakra/motion.js";
|
|
8
|
+
import { CSS_RATIO, MEDIA_RADIUS } from "./cardMetrics.js";
|
|
9
|
+
const DIMMED_OPACITY = 0.6;
|
|
10
|
+
const Frame = ({ src, alt, fallbackText, quiet = false }) => {
|
|
11
|
+
const { colors } = useCustomTheme();
|
|
12
|
+
const ref = useRef(null);
|
|
13
|
+
const [loaded, setLoaded] = useState(false);
|
|
14
|
+
const [failed, setFailed] = useState(src === "");
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const el = ref.current;
|
|
17
|
+
if (el && el.complete && el.naturalWidth > 0)
|
|
18
|
+
setLoaded(true);
|
|
19
|
+
}, []);
|
|
20
|
+
if (failed) {
|
|
21
|
+
if (quiet)
|
|
22
|
+
return null;
|
|
23
|
+
return (_jsxs(Box, { role: "img", "aria-label": alt, position: "absolute", inset: 0, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 1.5, color: colors.textMuted, bg: colors.background[100], children: [_jsx(ImageOff, { size: 24, "aria-hidden": true }), fallbackText ? (_jsx(Text, { as: "span", fontSize: "xs", fontWeight: 500, children: fallbackText })) : null] }));
|
|
24
|
+
}
|
|
25
|
+
return (_jsx(Box, { as: "img", ref: ref, src: src, alt: alt, loading: "lazy", decoding: "async", draggable: false, onLoad: () => setLoaded(true), onError: () => setFailed(true), position: "absolute", inset: 0, w: "100%", h: "100%", objectFit: "cover", display: "block", opacity: loaded ? 1 : 0, transition: transitionFor(["opacity"]), sx: reducedMotionSx() }));
|
|
26
|
+
};
|
|
27
|
+
/** Tonal media well: cover-fitted primary frame, optional hover frame, broken-image tile; overlays are children. */
|
|
28
|
+
const ProductCardMedia = ({ primary, hover, title, alt, aspectRatio, dimmed, selected, fallbackText, width, children, }) => {
|
|
29
|
+
var _a, _b;
|
|
30
|
+
const { colors } = useCustomTheme();
|
|
31
|
+
return (_jsxs(Box, { "data-media": true, "data-ratio": aspectRatio, position: "relative", flexShrink: 0, w: width !== null && width !== void 0 ? width : "100%", sx: { aspectRatio: CSS_RATIO[aspectRatio] }, borderRadius: MEDIA_RADIUS, overflow: "hidden", bg: colors.background[100], boxShadow: selected ? `0 0 0 2px ${colors.header[900]}` : "none", transition: transitionFor(["box-shadow"]), children: [_jsx(Box, { "data-primary": true, position: "absolute", inset: 0, opacity: dimmed ? DIMMED_OPACITY : 1, transition: transitionFor(["transform", "opacity"], "base"), sx: reducedMotionSx(), children: primary ? (_jsx(Frame, { src: primary.src, alt: (_b = (_a = primary.alt) !== null && _a !== void 0 ? _a : alt) !== null && _b !== void 0 ? _b : title, fallbackText: fallbackText }, primary.src)) : (_jsx(Frame, { src: "", alt: alt !== null && alt !== void 0 ? alt : title, fallbackText: fallbackText }, "none")) }), hover ? (_jsx(Box, { "data-hover-frame": true, "aria-hidden": true, position: "absolute", inset: 0, opacity: 0, pointerEvents: "none", transition: transitionFor(["opacity"], "base"), sx: reducedMotionSx(), children: _jsx(Frame, { src: hover.src, alt: "", quiet: true }, hover.src) })) : null, children] }));
|
|
32
|
+
};
|
|
33
|
+
export default ProductCardMedia;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Flex, VisuallyHidden } from "@chakra-ui/react";
|
|
3
|
+
import Skeletons from "../Skeletons/Skeletons.js";
|
|
4
|
+
import { CSS_RATIO, DENSITY, MEDIA_RADIUS, TITLE_LINES } from "./cardMetrics.js";
|
|
5
|
+
/** Loading twin of ProductCard: same media ratio, title reservation and gaps, so the swap never moves the grid. */
|
|
6
|
+
const ProductCardSkeleton = ({ layout = "grid", density = "compact", aspectRatio = "4/5", "data-testid": testId, }) => {
|
|
7
|
+
const m = DENSITY[density];
|
|
8
|
+
const horizontal = layout === "list";
|
|
9
|
+
const rail = layout === "rail";
|
|
10
|
+
return (_jsxs(Flex, { "aria-busy": "true", "data-testid": testId, "data-layout": layout, direction: horizontal ? "row" : "column", gap: horizontal ? 3 : 0, w: rail ? m.railWidth : "100%", h: "100%", minW: 0, flexShrink: rail ? 0 : undefined, children: [_jsx(VisuallyHidden, { children: "Loading product" }), _jsx(Box, { flexShrink: 0, w: horizontal ? m.listMedia : "100%", borderRadius: MEDIA_RADIUS, overflow: "hidden", sx: { aspectRatio: CSS_RATIO[aspectRatio] }, children: _jsx(Skeletons, { type: "skeleton", height: "100%", width: "100%", borderRadius: MEDIA_RADIUS }) }), _jsxs(Flex, { direction: "column", flex: 1, minW: 0, gap: m.bodyGap, pt: horizontal ? 0 : m.bodyTop, "aria-hidden": true, children: [_jsx(Skeletons, { type: "skeleton", height: `${m.brand.line - 4}px`, width: "35%" }), _jsxs(Flex, { direction: "column", gap: "4px", minH: `${m.title.line * TITLE_LINES}px`, justify: "center", children: [_jsx(Skeletons, { type: "skeleton", height: `${m.title.line - 6}px`, width: "92%" }), _jsx(Skeletons, { type: "skeleton", height: `${m.title.line - 6}px`, width: "60%" })] }), _jsx(Skeletons, { type: "skeleton", height: "1rem", width: "45%" }), _jsx(Skeletons, { type: "skeleton", height: "1.25rem", width: "55%" })] })] }));
|
|
11
|
+
};
|
|
12
|
+
export default ProductCardSkeleton;
|