pixelize-design-library 2.4.2-beta.56 → 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 +60 -0
- package/COMPONENT-GOTCHAS.md +48 -20
- package/DESIGN-GUIDE.md +18 -1
- 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/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/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/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/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/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/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/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/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,24 @@
|
|
|
1
|
+
/** sm 32px, md 40px, lg 48px high, matching the button scale; the hit area is padded to 44px. */
|
|
2
|
+
export type QuantityStepperSize = "sm" | "md" | "lg";
|
|
3
|
+
/** soft is a tonal fill; outline is a 1px control border on any surface. */
|
|
4
|
+
export type QuantityStepperVariant = "soft" | "outline";
|
|
5
|
+
/** Compact − qty + control. Display only: the number is never typed, which keeps it fast on mobile. */
|
|
6
|
+
export interface QuantityStepperProps {
|
|
7
|
+
value?: number;
|
|
8
|
+
defaultValue?: number;
|
|
9
|
+
onValueChange?: (value: number) => void;
|
|
10
|
+
/** Lowest value; defaults to 1. At the floor the minus button is aria-disabled. */
|
|
11
|
+
min?: number;
|
|
12
|
+
/** Highest value; omit for no ceiling. At the ceiling the plus button is aria-disabled. */
|
|
13
|
+
max?: number;
|
|
14
|
+
step?: number;
|
|
15
|
+
size?: QuantityStepperSize;
|
|
16
|
+
variant?: QuantityStepperVariant;
|
|
17
|
+
/** At `min` the minus becomes a trash button that calls onRemove, for cart lines. */
|
|
18
|
+
removable?: boolean;
|
|
19
|
+
onRemove?: () => void;
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
/** Product name, so the group reads "Quantity of Slim-Fit Denim Jacket". */
|
|
22
|
+
itemLabel?: string;
|
|
23
|
+
"data-testid"?: string;
|
|
24
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RatingProps } from "./RatingProps";
|
|
2
|
+
/** Star rating with fractional fill; becomes a radio group for "write a review" when `onValueChange` is set. */
|
|
3
|
+
declare const Rating: ({ value, defaultValue, max, size, showValue, count, compactCount, locale, onValueChange, isDisabled, "aria-label": ariaLabel, "data-testid": testId, }: RatingProps) => import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
export default Rating;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const react_2 = require("@chakra-ui/react");
|
|
6
|
+
const lucide_react_1 = require("lucide-react");
|
|
7
|
+
const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
8
|
+
const motion_1 = require("../../Theme/tokens/primitives/motion");
|
|
9
|
+
const motion_2 = require("../../Theme/chakra/motion");
|
|
10
|
+
const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
|
|
11
|
+
const price_1 = require("../../Utils/price");
|
|
12
|
+
const STAR_PX = { xs: 12, sm: 14, md: 18, lg: 24 };
|
|
13
|
+
const HIT_PX = { xs: 24, sm: 28, md: 44, lg: 44 };
|
|
14
|
+
const TEXT_SIZE = { xs: "xs", sm: "xs", md: "sm", lg: "md" };
|
|
15
|
+
const GAP_PX = { xs: 1, sm: 1, md: 2, lg: 2 };
|
|
16
|
+
const clamp = (n, lo, hi) => Math.min(hi, Math.max(lo, n));
|
|
17
|
+
const tenth = (n) => Math.round(n * 10) / 10;
|
|
18
|
+
const StarGlyph = ({ px, fillPercent, filled, empty }) => ((0, jsx_runtime_1.jsxs)(react_2.Box, { as: "span", "aria-hidden": true, position: "relative", display: "inline-flex", boxSize: `${px}px`, flexShrink: 0, color: empty, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Star, { size: px, fill: "currentColor", strokeWidth: 1.5 }), fillPercent > 0 && ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", "data-fill": fillPercent, position: "absolute", inset: 0, color: filled, sx: {
|
|
19
|
+
clipPath: `inset(0 ${100 - fillPercent}% 0 0)`,
|
|
20
|
+
"[dir=rtl] &": { clipPath: `inset(0 0 0 ${100 - fillPercent}%)` },
|
|
21
|
+
}, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Star, { size: px, fill: "currentColor", strokeWidth: 1.5 }) }))] }));
|
|
22
|
+
const fillOf = (index, value) => clamp(value - index, 0, 1) * 100;
|
|
23
|
+
/** Star rating with fractional fill; becomes a radio group for "write a review" when `onValueChange` is set. */
|
|
24
|
+
const Rating = ({ value, defaultValue = 0, max = 5, size = "sm", showValue = true, count, compactCount = false, locale = price_1.DEFAULT_LOCALE, onValueChange, isDisabled = false, "aria-label": ariaLabel, "data-testid": testId, }) => {
|
|
25
|
+
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
26
|
+
const [inner, setInner] = (0, react_1.useState)(defaultValue);
|
|
27
|
+
const [hover, setHover] = (0, react_1.useState)(null);
|
|
28
|
+
const reduced = (0, motion_2.usePrefersReducedMotion)();
|
|
29
|
+
const refs = (0, react_1.useRef)([]);
|
|
30
|
+
const interactive = !!onValueChange;
|
|
31
|
+
const current = tenth(clamp(value !== null && value !== void 0 ? value : (interactive ? inner : 0), 0, max));
|
|
32
|
+
const px = STAR_PX[size];
|
|
33
|
+
const nf = new Intl.NumberFormat(locale);
|
|
34
|
+
const valueText = new Intl.NumberFormat(locale, { minimumFractionDigits: 1, maximumFractionDigits: 1 }).format(current);
|
|
35
|
+
const countText = count === undefined
|
|
36
|
+
? null
|
|
37
|
+
: compactCount
|
|
38
|
+
? new Intl.NumberFormat(locale, { notation: "compact", maximumFractionDigits: 1 }).format(count)
|
|
39
|
+
: nf.format(count);
|
|
40
|
+
const summary = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [showValue && current > 0 && ((0, jsx_runtime_1.jsx)(react_2.Text, { as: "span", fontSize: TEXT_SIZE[size], fontWeight: 600, color: colors.header[900], sx: { fontVariantNumeric: "tabular-nums" }, children: valueText })), countText !== null && ((0, jsx_runtime_1.jsxs)(react_2.Text, { as: "span", fontSize: TEXT_SIZE[size], color: colors.textMuted, sx: { fontVariantNumeric: "tabular-nums" }, children: ["(", countText, ")"] }))] }));
|
|
41
|
+
if (!interactive) {
|
|
42
|
+
const label = ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : (current === 0 && !count
|
|
43
|
+
? "Not rated yet"
|
|
44
|
+
: `Rated ${valueText} out of ${nf.format(max)}${count ? ` from ${nf.format(count)} ${count === 1 ? "rating" : "ratings"}` : ""}`);
|
|
45
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.Flex, { as: "span", role: "img", "aria-label": label, "data-testid": testId, display: "inline-flex", align: "center", gap: 1.5, verticalAlign: "middle", children: [(0, jsx_runtime_1.jsx)(react_2.Flex, { as: "span", "aria-hidden": true, gap: `${GAP_PX[size]}px`, children: Array.from({ length: max }, (_, i) => ((0, jsx_runtime_1.jsx)(StarGlyph, { px: px, fillPercent: fillOf(i, current), filled: colors.star, empty: colors.boxborder[300] }, i))) }), summary] }));
|
|
46
|
+
}
|
|
47
|
+
const select = (next) => {
|
|
48
|
+
if (isDisabled)
|
|
49
|
+
return;
|
|
50
|
+
if (value === undefined)
|
|
51
|
+
setInner(next);
|
|
52
|
+
onValueChange(next);
|
|
53
|
+
};
|
|
54
|
+
const chosen = Math.round(current);
|
|
55
|
+
const focusStop = chosen > 0 ? chosen : 1;
|
|
56
|
+
const shown = hover !== null && hover !== void 0 ? hover : chosen;
|
|
57
|
+
const onKeyDown = (e) => {
|
|
58
|
+
var _a;
|
|
59
|
+
const move = {
|
|
60
|
+
ArrowRight: chosen + 1,
|
|
61
|
+
ArrowUp: chosen + 1,
|
|
62
|
+
ArrowLeft: chosen - 1,
|
|
63
|
+
ArrowDown: chosen - 1,
|
|
64
|
+
Home: 1,
|
|
65
|
+
End: max,
|
|
66
|
+
};
|
|
67
|
+
if (!(e.key in move))
|
|
68
|
+
return;
|
|
69
|
+
e.preventDefault();
|
|
70
|
+
const next = clamp(move[e.key], 1, max);
|
|
71
|
+
select(next);
|
|
72
|
+
(_a = refs.current[next - 1]) === null || _a === void 0 ? void 0 : _a.focus();
|
|
73
|
+
};
|
|
74
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.Flex, { as: "span", display: "inline-flex", align: "center", gap: 1.5, "data-testid": testId, children: [(0, jsx_runtime_1.jsx)(react_2.Flex, { as: "span", role: "radiogroup", "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : "Rating", "aria-disabled": isDisabled || undefined, gap: `${GAP_PX[size]}px`, onMouseLeave: () => setHover(null), children: Array.from({ length: max }, (_, i) => {
|
|
75
|
+
const n = i + 1;
|
|
76
|
+
return ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "button", type: "button", role: "radio", ref: (el) => {
|
|
77
|
+
refs.current[i] = el;
|
|
78
|
+
}, "aria-checked": n === chosen, "aria-label": `${n} ${n === 1 ? "star" : "stars"}`, tabIndex: n === focusStop && !isDisabled ? 0 : -1, disabled: isDisabled, onClick: () => select(n), onKeyDown: onKeyDown, onMouseEnter: () => !isDisabled && setHover(n), display: "inline-flex", alignItems: "center", justifyContent: "center", boxSize: `${HIT_PX[size]}px`, mx: `-${(HIT_PX[size] - px) / 2 - 1}px`, borderRadius: "full", cursor: isDisabled ? "not-allowed" : "pointer", opacity: isDisabled ? 0.5 : 1, transition: (0, motion_1.transitionFor)(["transform"]), _active: isDisabled || reduced ? undefined : { transform: "scale(0.88)" }, sx: { ...(0, focusRing_styles_1.focusVisibleStyles)(colors), ...(0, motion_2.reducedMotionSx)() }, children: (0, jsx_runtime_1.jsx)(StarGlyph, { px: px, fillPercent: fillOf(i, shown), filled: colors.star, empty: colors.boxborder[300] }) }, n));
|
|
79
|
+
}) }), summary] }));
|
|
80
|
+
};
|
|
81
|
+
exports.default = Rating;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Star sizes: 12, 14, 18 and 24 px glyphs. */
|
|
2
|
+
export type RatingSize = "xs" | "sm" | "md" | "lg";
|
|
3
|
+
/** Star rating: read-only display by default, a keyboard-operable radio group when `onValueChange` is set. */
|
|
4
|
+
export interface RatingProps {
|
|
5
|
+
/** Rating from 0 to `max`, rounded to one decimal; fractions fill a star partially and 5.0 fills all five. Controls the selection in interactive mode. */
|
|
6
|
+
value?: number;
|
|
7
|
+
/** Initial selection for an interactive rating that is not controlled with `value`. */
|
|
8
|
+
defaultValue?: number;
|
|
9
|
+
/** Number of stars. */
|
|
10
|
+
max?: number;
|
|
11
|
+
size?: RatingSize;
|
|
12
|
+
/** Shows the numeric value ("4.6") after the stars. */
|
|
13
|
+
showValue?: boolean;
|
|
14
|
+
/** Number of ratings, shown in brackets after the value and read out in the accessible name. */
|
|
15
|
+
count?: number;
|
|
16
|
+
/** Abbreviates the visible count ("1.3K"); the accessible name always carries the full figure. */
|
|
17
|
+
compactCount?: boolean;
|
|
18
|
+
/** Locale for number formatting; defaults to en-IN. */
|
|
19
|
+
locale?: string;
|
|
20
|
+
/** Makes the rating interactive (radio group, arrow keys) and fires with the chosen whole-star value. */
|
|
21
|
+
onValueChange?: (value: number) => void;
|
|
22
|
+
/** Interactive mode only: stops selection and removes the stars from the tab order. */
|
|
23
|
+
isDisabled?: boolean;
|
|
24
|
+
/** Overrides the accessible name; the group name in interactive mode, the "Rated 4.6 out of 5" text otherwise. */
|
|
25
|
+
"aria-label"?: string;
|
|
26
|
+
"data-testid"?: string;
|
|
27
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ReviewListProps } from "./ReviewListProps";
|
|
2
|
+
/** Customer reviews: stars, clamped body, verified marker, helpful vote and load-more paging, with a slot for sorting. */
|
|
3
|
+
declare const ReviewList: ({ reviews, onHelpful, clampChars, sortSlot, hasMore, onLoadMore, isLoading, isLoadingMore, emptyState, locale, labels, "data-testid": testId, }: ReviewListProps) => import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
export default ReviewList;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
+
const react_1 = require("react");
|
|
8
|
+
const react_2 = require("@chakra-ui/react");
|
|
9
|
+
const lucide_react_1 = require("lucide-react");
|
|
10
|
+
const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
11
|
+
const motion_1 = require("../../Theme/tokens/primitives/motion");
|
|
12
|
+
const motion_2 = require("../../Theme/chakra/motion");
|
|
13
|
+
const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
|
|
14
|
+
const price_1 = require("../../Utils/price");
|
|
15
|
+
const Button_1 = __importDefault(require("../Button/Button"));
|
|
16
|
+
const Rating_1 = __importDefault(require("../Rating/Rating"));
|
|
17
|
+
const Skeletons_1 = __importDefault(require("../Skeletons/Skeletons"));
|
|
18
|
+
const DEFAULT_LABELS = {
|
|
19
|
+
verified: "Verified purchase",
|
|
20
|
+
helpful: (count) => `Helpful (${count})`,
|
|
21
|
+
readMore: "Read more",
|
|
22
|
+
readLess: "Read less",
|
|
23
|
+
loadMore: "Load more reviews",
|
|
24
|
+
loadingMore: "Loading more reviews…",
|
|
25
|
+
emptyTitle: "No reviews yet",
|
|
26
|
+
emptyBody: "Reviews from verified buyers will show up here.",
|
|
27
|
+
purchasedSize: (size) => `Size ${size}`,
|
|
28
|
+
loadingReviews: "Loading reviews",
|
|
29
|
+
moreLoaded: (count) => `${count} more ${count === 1 ? "review" : "reviews"} loaded`,
|
|
30
|
+
photoAlt: (author) => `Photo from ${author}'s review`,
|
|
31
|
+
};
|
|
32
|
+
const DATE_ONLY = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
33
|
+
const tabular = { fontVariantNumeric: "tabular-nums" };
|
|
34
|
+
const SKELETON_ROWS = [0, 1, 2];
|
|
35
|
+
const makeFormatter = (locale) => {
|
|
36
|
+
const options = { day: "numeric", month: "short", year: "numeric" };
|
|
37
|
+
try {
|
|
38
|
+
return new Intl.DateTimeFormat(locale, options);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return new Intl.DateTimeFormat(price_1.DEFAULT_LOCALE, options);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const formatDate = (raw, fmt) => {
|
|
45
|
+
const dateOnly = DATE_ONLY.exec(raw);
|
|
46
|
+
const parsed = dateOnly ? new Date(Number(dateOnly[1]), Number(dateOnly[2]) - 1, Number(dateOnly[3])) : new Date(raw);
|
|
47
|
+
if (Number.isNaN(parsed.getTime()))
|
|
48
|
+
return { text: raw, valid: false };
|
|
49
|
+
return { text: fmt.format(parsed), valid: true };
|
|
50
|
+
};
|
|
51
|
+
const Dot = () => ((0, jsx_runtime_1.jsx)(react_2.Text, { as: "span", "aria-hidden": true, children: "\u00B7" }));
|
|
52
|
+
const ReviewRow = ({ review, index, clampChars, fmt, labels, pressed, expanded, onToggleHelpful, onToggleExpanded }) => {
|
|
53
|
+
var _a;
|
|
54
|
+
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
55
|
+
const bodyId = (0, react_1.useId)();
|
|
56
|
+
const isLong = review.body.length > clampChars;
|
|
57
|
+
const date = formatDate(review.date, fmt);
|
|
58
|
+
const count = ((_a = review.helpful) !== null && _a !== void 0 ? _a : 0) + (pressed ? 1 : 0);
|
|
59
|
+
const showHelpful = review.helpful !== undefined || pressed;
|
|
60
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.Box, { as: "li", listStyleType: "none", "data-testid": `review-${review.id}`, children: [index > 0 && (0, jsx_runtime_1.jsx)(react_2.Box, { "aria-hidden": true, h: "1px", bg: colors.background[100] }), (0, jsx_runtime_1.jsxs)(react_2.Flex, { direction: "column", gap: 2.5, py: 6, children: [(0, jsx_runtime_1.jsxs)(react_2.Flex, { align: "center", gap: 2, wrap: "wrap", children: [(0, jsx_runtime_1.jsx)(Rating_1.default, { value: review.rating, size: "sm", showValue: false }), review.title && ((0, jsx_runtime_1.jsx)(react_2.Text, { as: "h3", fontSize: "md", fontWeight: 600, lineHeight: 1.4, color: colors.header[900], children: review.title }))] }), (0, jsx_runtime_1.jsxs)(react_2.Box, { children: [(0, jsx_runtime_1.jsx)(react_2.Text, { id: bodyId, fontSize: "sm", lineHeight: 1.6, color: colors.text[700], noOfLines: isLong && !expanded ? 4 : undefined, children: review.body }), isLong && ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "button", type: "button", mt: 1, fontSize: "sm", fontWeight: 600, color: colors.accentText, "aria-expanded": expanded, "aria-controls": bodyId, onClick: () => onToggleExpanded(review.id), borderRadius: "sm", transition: (0, motion_1.transitionFor)(["opacity"]), _hover: { opacity: 0.8 }, sx: { ...(0, focusRing_styles_1.focusVisibleStyles)(colors), ...(0, motion_2.reducedMotionSx)() }, children: expanded ? labels.readLess : labels.readMore }))] }), review.media && review.media.length > 0 && ((0, jsx_runtime_1.jsx)(react_2.Flex, { gap: 2, wrap: "wrap", children: review.media.map((m) => {
|
|
61
|
+
var _a;
|
|
62
|
+
return ((0, jsx_runtime_1.jsx)(react_2.Box, { boxSize: "72px", borderRadius: "12px", overflow: "hidden", bg: colors.background[100], flexShrink: 0, children: (0, jsx_runtime_1.jsx)("img", { src: m.src, alt: (_a = m.alt) !== null && _a !== void 0 ? _a : labels.photoAlt(review.author), loading: "lazy", style: { width: "100%", height: "100%", objectFit: "cover" } }) }, m.id));
|
|
63
|
+
}) })), (0, jsx_runtime_1.jsxs)(react_2.Flex, { align: "center", gap: 2, wrap: "wrap", fontSize: "xs", color: colors.textMuted, sx: tabular, children: [(0, jsx_runtime_1.jsx)(react_2.Text, { as: "span", fontWeight: 600, color: colors.header[900], children: review.author }), review.city && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(Dot, {}), (0, jsx_runtime_1.jsx)(react_2.Text, { as: "span", children: review.city })] })), (0, jsx_runtime_1.jsx)(Dot, {}), date.valid ? ((0, jsx_runtime_1.jsx)("time", { dateTime: review.date, children: date.text })) : ((0, jsx_runtime_1.jsx)(react_2.Text, { as: "span", children: date.text })), review.purchasedSize && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(Dot, {}), (0, jsx_runtime_1.jsx)(react_2.Text, { as: "span", children: labels.purchasedSize(review.purchasedSize) })] })), review.verified && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(Dot, {}), (0, jsx_runtime_1.jsxs)(react_2.Flex, { as: "span", align: "center", gap: 1, fontWeight: 600, color: colors.semanticText.success, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.BadgeCheck, { size: 14, "aria-hidden": true, focusable: "false" }), labels.verified] })] }))] }), showHelpful && ((0, jsx_runtime_1.jsx)(react_2.Box, { children: (0, jsx_runtime_1.jsx)(Button_1.default, { variant: "ghost", size: "sm", "aria-pressed": pressed, onClick: () => onToggleHelpful(review), leftIcon: (0, jsx_runtime_1.jsx)(lucide_react_1.ThumbsUp, { size: 14, "aria-hidden": true, fill: pressed ? "currentColor" : "none" }), sx: { color: pressed ? colors.header[900] : colors.textMuted, fontVariantNumeric: "tabular-nums" }, children: labels.helpful(count) }) }))] })] }));
|
|
64
|
+
};
|
|
65
|
+
const SkeletonRow = () => ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "li", listStyleType: "none", "aria-hidden": true, py: 6, children: (0, jsx_runtime_1.jsxs)(react_2.Flex, { direction: "column", gap: 3, children: [(0, jsx_runtime_1.jsx)(Skeletons_1.default, { type: "skeleton", width: "80px", height: "14px", borderRadius: "sm" }), (0, jsx_runtime_1.jsxs)(react_2.Flex, { direction: "column", gap: 2, children: [(0, jsx_runtime_1.jsx)(Skeletons_1.default, { type: "skeleton", width: "100%", height: "12px", borderRadius: "sm" }), (0, jsx_runtime_1.jsx)(Skeletons_1.default, { type: "skeleton", width: "94%", height: "12px", borderRadius: "sm" }), (0, jsx_runtime_1.jsx)(Skeletons_1.default, { type: "skeleton", width: "60%", height: "12px", borderRadius: "sm" })] }), (0, jsx_runtime_1.jsx)(Skeletons_1.default, { type: "skeleton", width: "180px", height: "12px", borderRadius: "sm" })] }) }));
|
|
66
|
+
/** Customer reviews: stars, clamped body, verified marker, helpful vote and load-more paging, with a slot for sorting. */
|
|
67
|
+
const ReviewList = ({ reviews, onHelpful, clampChars = 220, sortSlot, hasMore = false, onLoadMore, isLoading = false, isLoadingMore = false, emptyState, locale = price_1.DEFAULT_LOCALE, labels, "data-testid": testId, }) => {
|
|
68
|
+
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
69
|
+
const copy = { ...DEFAULT_LABELS, ...labels };
|
|
70
|
+
const fmt = (0, react_1.useMemo)(() => makeFormatter(locale), [locale]);
|
|
71
|
+
const [pressed, setPressed] = (0, react_1.useState)({});
|
|
72
|
+
const [expanded, setExpanded] = (0, react_1.useState)({});
|
|
73
|
+
const [announcement, setAnnouncement] = (0, react_1.useState)("");
|
|
74
|
+
const previousCount = (0, react_1.useRef)(reviews.length);
|
|
75
|
+
const moreLoaded = copy.moreLoaded;
|
|
76
|
+
(0, react_1.useEffect)(() => {
|
|
77
|
+
const added = reviews.length - previousCount.current;
|
|
78
|
+
const hadReviews = previousCount.current > 0;
|
|
79
|
+
previousCount.current = reviews.length;
|
|
80
|
+
setAnnouncement(!isLoading && hadReviews && added > 0 ? moreLoaded(added) : "");
|
|
81
|
+
}, [reviews.length, isLoading, moreLoaded]);
|
|
82
|
+
const toggleHelpful = (review) => {
|
|
83
|
+
const next = !pressed[review.id];
|
|
84
|
+
setPressed((prev) => ({ ...prev, [review.id]: next }));
|
|
85
|
+
if (next)
|
|
86
|
+
onHelpful === null || onHelpful === void 0 ? void 0 : onHelpful(review.id);
|
|
87
|
+
};
|
|
88
|
+
const toggleExpanded = (id) => setExpanded((prev) => ({ ...prev, [id]: !prev[id] }));
|
|
89
|
+
const isEmpty = !isLoading && reviews.length === 0;
|
|
90
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.Box, { as: "section", "data-testid": testId, children: [sortSlot && ((0, jsx_runtime_1.jsx)(react_2.Flex, { justify: "flex-end", mb: 4, children: sortSlot })), isEmpty ? ((emptyState !== null && emptyState !== void 0 ? emptyState : ((0, jsx_runtime_1.jsxs)(react_2.Flex, { direction: "column", align: "center", gap: 1, py: 12, textAlign: "center", children: [(0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: "md", fontWeight: 600, color: colors.header[900], children: copy.emptyTitle }), (0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: "sm", color: colors.textMuted, children: copy.emptyBody })] })))) : ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "ul", m: 0, p: 0, "aria-busy": isLoading || undefined, children: isLoading
|
|
91
|
+
? SKELETON_ROWS.map((n) => (0, jsx_runtime_1.jsx)(SkeletonRow, {}, n))
|
|
92
|
+
: reviews.map((review, index) => ((0, jsx_runtime_1.jsx)(ReviewRow, { review: review, index: index, clampChars: clampChars, fmt: fmt, labels: copy, pressed: !!pressed[review.id], expanded: !!expanded[review.id], onToggleHelpful: toggleHelpful, onToggleExpanded: toggleExpanded }, review.id))) })), hasMore && !isLoading && !isEmpty && ((0, jsx_runtime_1.jsx)(react_2.Flex, { justify: "center", mt: 4, children: (0, jsx_runtime_1.jsx)(Button_1.default, { variant: "outline", size: "md", onClick: onLoadMore, isDisabled: isLoadingMore, "aria-busy": isLoadingMore || undefined, children: isLoadingMore ? copy.loadingMore : copy.loadMore }) })), (0, jsx_runtime_1.jsx)(react_2.VisuallyHidden, { role: "status", "aria-live": "polite", children: isLoading ? copy.loadingReviews : announcement })] }));
|
|
93
|
+
};
|
|
94
|
+
exports.default = ReviewList;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
/** A photo attached to a review. */
|
|
3
|
+
export interface ReviewMedia {
|
|
4
|
+
id: string;
|
|
5
|
+
src: string;
|
|
6
|
+
alt?: string;
|
|
7
|
+
}
|
|
8
|
+
/** One customer review. */
|
|
9
|
+
export interface ReviewItem {
|
|
10
|
+
id: string;
|
|
11
|
+
author: string;
|
|
12
|
+
city?: string;
|
|
13
|
+
/** Whole stars, 1 to 5. */
|
|
14
|
+
rating: number;
|
|
15
|
+
/** ISO date such as "2026-09-21"; formatted per `locale`, and shown as given when it is not a valid date. */
|
|
16
|
+
date: string;
|
|
17
|
+
title?: string;
|
|
18
|
+
body: string;
|
|
19
|
+
/** Adds the "Verified purchase" marker. */
|
|
20
|
+
verified?: boolean;
|
|
21
|
+
/** Helpful votes so far. */
|
|
22
|
+
helpful?: number;
|
|
23
|
+
/** Variant the reviewer bought, e.g. "M". */
|
|
24
|
+
purchasedSize?: string;
|
|
25
|
+
media?: ReviewMedia[];
|
|
26
|
+
}
|
|
27
|
+
/** Every string the list renders; override all of them for another language. */
|
|
28
|
+
export interface ReviewListLabels {
|
|
29
|
+
verified: string;
|
|
30
|
+
/** Button name, given the vote count. */
|
|
31
|
+
helpful: (count: number) => string;
|
|
32
|
+
readMore: string;
|
|
33
|
+
readLess: string;
|
|
34
|
+
loadMore: string;
|
|
35
|
+
loadingMore: string;
|
|
36
|
+
emptyTitle: string;
|
|
37
|
+
emptyBody: string;
|
|
38
|
+
/** Detail line, given the size the reviewer bought. */
|
|
39
|
+
purchasedSize: (size: string) => string;
|
|
40
|
+
/** Screen-reader text while skeleton rows show. */
|
|
41
|
+
loadingReviews?: string;
|
|
42
|
+
/** Polite announcement when a page is appended, given how many reviews arrived. */
|
|
43
|
+
moreLoaded?: (count: number) => string;
|
|
44
|
+
/** Fallback alt text for a photo without one, given the reviewer's name. */
|
|
45
|
+
photoAlt?: (author: string) => string;
|
|
46
|
+
}
|
|
47
|
+
/** Customer reviews with verified marker, helpful votes, clamped long bodies, a sort slot and load-more paging. */
|
|
48
|
+
export interface ReviewListProps {
|
|
49
|
+
reviews: ReviewItem[];
|
|
50
|
+
/** Fires with the review id when "Helpful" is pressed; the button then shows as pressed and the count rises by one. */
|
|
51
|
+
onHelpful?: (reviewId: string) => void;
|
|
52
|
+
/** Bodies longer than this many characters are clamped to four lines behind a "Read more" toggle. */
|
|
53
|
+
clampChars?: number;
|
|
54
|
+
/** Rendered above the list, typically a `Select` for sorting. */
|
|
55
|
+
sortSlot?: ReactNode;
|
|
56
|
+
/** Shows the "Load more" button. */
|
|
57
|
+
hasMore?: boolean;
|
|
58
|
+
onLoadMore?: () => void;
|
|
59
|
+
/** Shows skeleton rows in place of the reviews. */
|
|
60
|
+
isLoading?: boolean;
|
|
61
|
+
/** Disables "Load more" and shows `labels.loadingMore` while the next page loads. */
|
|
62
|
+
isLoadingMore?: boolean;
|
|
63
|
+
/** Replaces the default empty state. */
|
|
64
|
+
emptyState?: ReactNode;
|
|
65
|
+
locale?: string;
|
|
66
|
+
labels?: Partial<ReviewListLabels>;
|
|
67
|
+
"data-testid"?: string;
|
|
68
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ReviewSummaryProps } from "./ReviewSummaryProps";
|
|
2
|
+
/** Average rating, count and a five-row distribution; scales down to a thin summary or an invitation when there is little or no evidence. */
|
|
3
|
+
declare const ReviewSummary: ({ rating, count, breakdown, minRatingsForBars, onWriteReview, id, locale, labels, "data-testid": testId, }: ReviewSummaryProps) => import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
export default ReviewSummary;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
+
const react_1 = require("@chakra-ui/react");
|
|
8
|
+
const lucide_react_1 = require("lucide-react");
|
|
9
|
+
const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
10
|
+
const price_1 = require("../../Utils/price");
|
|
11
|
+
const Rating_1 = __importDefault(require("../Rating/Rating"));
|
|
12
|
+
const Button_1 = __importDefault(require("../Button/Button"));
|
|
13
|
+
const STARS = [5, 4, 3, 2, 1];
|
|
14
|
+
const DEFAULT_MIN_RATINGS_FOR_BARS = 10;
|
|
15
|
+
const tabular = { fontVariantNumeric: "tabular-nums" };
|
|
16
|
+
const defaultLabels = (locale) => ({
|
|
17
|
+
heading: "Ratings & reviews",
|
|
18
|
+
writeReview: "Write a review",
|
|
19
|
+
ratings: (count) => `${new Intl.NumberFormat(locale).format(count)} ${count === 1 ? "rating" : "ratings"}`,
|
|
20
|
+
emptyTitle: "No reviews yet",
|
|
21
|
+
emptyBody: "Bought this? Be the first to say how it fits.",
|
|
22
|
+
bar: (stars, percent) => `${stars} ${stars === 1 ? "star" : "stars"}, ${percent}%`,
|
|
23
|
+
});
|
|
24
|
+
const percentages = (breakdown) => {
|
|
25
|
+
const total = STARS.reduce((sum, stars) => sum + breakdown[stars], 0);
|
|
26
|
+
return (stars) => (total > 0 ? Math.round((breakdown[stars] / total) * 100) : 0);
|
|
27
|
+
};
|
|
28
|
+
/** Average rating, count and a five-row distribution; scales down to a thin summary or an invitation when there is little or no evidence. */
|
|
29
|
+
const ReviewSummary = ({ rating, count, breakdown, minRatingsForBars = DEFAULT_MIN_RATINGS_FOR_BARS, onWriteReview, id, locale = price_1.DEFAULT_LOCALE, labels, "data-testid": testId, }) => {
|
|
30
|
+
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
31
|
+
const copy = { ...defaultLabels(locale), ...labels };
|
|
32
|
+
const empty = count <= 0 || rating <= 0;
|
|
33
|
+
const showBars = !empty && !!breakdown && count >= minRatingsForBars;
|
|
34
|
+
const average = Math.min(5, Math.max(0, rating));
|
|
35
|
+
const averageText = new Intl.NumberFormat(locale, { minimumFractionDigits: 1, maximumFractionDigits: 1 }).format(average);
|
|
36
|
+
const nf = new Intl.NumberFormat(locale);
|
|
37
|
+
const writeButton = onWriteReview ? ((0, jsx_runtime_1.jsx)(Button_1.default, { variant: "outline", onClick: onWriteReview, children: copy.writeReview })) : null;
|
|
38
|
+
const distribution = (counts) => {
|
|
39
|
+
const percentOf = percentages(counts);
|
|
40
|
+
return ((0, jsx_runtime_1.jsx)(react_1.Flex, { direction: "column", gap: 2, flex: "1", minW: 0, maxW: "26rem", w: "100%", children: STARS.map((stars) => {
|
|
41
|
+
const percent = percentOf(stars);
|
|
42
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.Box, { display: "grid", gridTemplateColumns: "2.25rem minmax(0, 1fr) 3.5rem", alignItems: "center", columnGap: 3, children: [(0, jsx_runtime_1.jsxs)(react_1.Flex, { "aria-hidden": true, align: "center", gap: 1, fontSize: "sm", color: colors.text[800], sx: tabular, children: [(0, jsx_runtime_1.jsx)("span", { children: stars }), (0, jsx_runtime_1.jsx)(lucide_react_1.Star, { size: 12, fill: "currentColor", strokeWidth: 1.5 })] }), (0, jsx_runtime_1.jsx)(react_1.Box, { role: "meter", "aria-label": copy.bar(stars, percent), "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": percent, h: "0.5rem", borderRadius: "full", bg: colors.background[200], overflow: "hidden", children: (0, jsx_runtime_1.jsx)(react_1.Box, { h: "100%", w: `${percent}%`, borderRadius: "full", bg: colors.star }) }), (0, jsx_runtime_1.jsx)(react_1.Text, { "aria-hidden": true, fontSize: "sm", textAlign: "right", color: colors.textMuted, sx: tabular, children: nf.format(counts[stars]) })] }, stars));
|
|
43
|
+
}) }));
|
|
44
|
+
};
|
|
45
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.Box, { as: "section", "aria-labelledby": id, "data-testid": testId, children: [(0, jsx_runtime_1.jsx)(react_1.Text, { as: "h2", id: id, fontSize: "lg", fontWeight: 700, lineHeight: 1.3, color: colors.header[900], children: copy.heading }), empty ? ((0, jsx_runtime_1.jsxs)(react_1.Flex, { direction: "column", align: "flex-start", gap: 1, mt: 4, children: [(0, jsx_runtime_1.jsx)(react_1.Text, { fontSize: "md", fontWeight: 600, color: colors.header[900], children: copy.emptyTitle }), (0, jsx_runtime_1.jsx)(react_1.Text, { fontSize: "sm", color: colors.textMuted, children: copy.emptyBody }), writeButton ? (0, jsx_runtime_1.jsx)(react_1.Box, { mt: 3, children: writeButton }) : null] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(react_1.Flex, { direction: { base: "column", sm: "row" }, align: { base: "flex-start", sm: "center" }, gap: { base: 5, sm: 10 }, mt: 4, children: [(0, jsx_runtime_1.jsxs)(react_1.Flex, { direction: "column", align: "flex-start", gap: 1.5, flexShrink: 0, children: [(0, jsx_runtime_1.jsx)(react_1.Text, { fontSize: "3rem", lineHeight: 1, fontWeight: 700, letterSpacing: "-0.02em", color: colors.header[900], sx: tabular, children: averageText }), (0, jsx_runtime_1.jsx)(Rating_1.default, { value: average, size: "md", showValue: false }), (0, jsx_runtime_1.jsx)(react_1.Text, { fontSize: "sm", color: colors.textMuted, sx: tabular, children: copy.ratings(count) })] }), showBars && breakdown ? distribution(breakdown) : null] }), writeButton ? (0, jsx_runtime_1.jsx)(react_1.Box, { mt: 5, children: writeButton }) : null] }))] }));
|
|
46
|
+
};
|
|
47
|
+
exports.default = ReviewSummary;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Ratings per star count, 1 to 5. */
|
|
2
|
+
export type RatingBreakdown = Record<1 | 2 | 3 | 4 | 5, number>;
|
|
3
|
+
/** Every string the summary renders; override all of them for another language. */
|
|
4
|
+
export interface ReviewSummaryLabels {
|
|
5
|
+
heading: string;
|
|
6
|
+
writeReview: string;
|
|
7
|
+
/** Count line, given the total number of ratings. */
|
|
8
|
+
ratings: (count: number) => string;
|
|
9
|
+
emptyTitle: string;
|
|
10
|
+
emptyBody: string;
|
|
11
|
+
/** Accessible name of one bar, given the star level and the share as a whole percentage. */
|
|
12
|
+
bar: (stars: number, percent: number) => string;
|
|
13
|
+
}
|
|
14
|
+
/** Average rating, count and a five-row distribution; scales down to an invitation when there are no ratings. */
|
|
15
|
+
export interface ReviewSummaryProps {
|
|
16
|
+
/** Average rating from 0 to 5. */
|
|
17
|
+
rating: number;
|
|
18
|
+
/** Total number of ratings. */
|
|
19
|
+
count: number;
|
|
20
|
+
/** Counts per star; the bars show each row's share of their sum. */
|
|
21
|
+
breakdown?: RatingBreakdown;
|
|
22
|
+
/** The distribution only appears at or above this many ratings, because bars on a handful of ratings mislead. */
|
|
23
|
+
minRatingsForBars?: number;
|
|
24
|
+
/** Shows a "Write a review" button when provided. */
|
|
25
|
+
onWriteReview?: () => void;
|
|
26
|
+
/** Heading element id, so a "4.6 (412)" link elsewhere can scroll here and a region can be labelled by it. */
|
|
27
|
+
id?: string;
|
|
28
|
+
locale?: string;
|
|
29
|
+
labels?: Partial<ReviewSummaryLabels>;
|
|
30
|
+
"data-testid"?: string;
|
|
31
|
+
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { SegmentedControlProps } from "./SegmentedControlProps";
|
|
2
2
|
/** Renders role="radiogroup" of role="radio" buttons with roving tabindex, not a native form radio group; shares variant with Toggle/TableToggle (see MODULE.md). */
|
|
3
|
-
export default function SegmentedControl<T extends string = string>({ options, value, onChange, size, isFullWidth, variant, surface, "aria-label": ariaLabel, }: SegmentedControlProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
export default function SegmentedControl<T extends string = string>({ options, value, onChange, size, isFullWidth, variant, orientation, surface, "aria-label": ariaLabel, }: SegmentedControlProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -25,16 +25,20 @@ const MAX_COUNT = 99;
|
|
|
25
25
|
const COUNT_TAG_SIZE = { sm: "xs", md: "sm", lg: "md" };
|
|
26
26
|
const formatCount = (count) => (count > MAX_COUNT ? `${MAX_COUNT}+` : `${count}`);
|
|
27
27
|
/** Renders role="radiogroup" of role="radio" buttons with roving tabindex, not a native form radio group; shares variant with Toggle/TableToggle (see MODULE.md). */
|
|
28
|
-
function SegmentedControl({ options, value, onChange, size = "sm", isFullWidth = false, variant = "default", surface, "aria-label": ariaLabel, }) {
|
|
28
|
+
function SegmentedControl({ options, value, onChange, size = "sm", isFullWidth = false, variant = "default", orientation = "horizontal", surface, "aria-label": ariaLabel, }) {
|
|
29
|
+
var _a;
|
|
29
30
|
const { colors, radii } = (0, useCustomTheme_1.useCustomTheme)();
|
|
30
31
|
const control = controlSizes_1.BUTTON_SIZES[size];
|
|
31
32
|
const isBrandSlide = variant === "brandSlide";
|
|
32
33
|
const isUnderlineSlide = variant === "underlineSlide";
|
|
33
34
|
const hasIndicator = isBrandSlide || isUnderlineSlide;
|
|
35
|
+
const isVertical = orientation === "vertical";
|
|
36
|
+
// Below "md" a shortLabel (when the option has one) replaces the full label; the full label stays the accessible name.
|
|
37
|
+
const isCompact = (_a = (0, react_2.useBreakpointValue)({ base: true, md: false }, { ssr: false })) !== null && _a !== void 0 ? _a : false;
|
|
34
38
|
const brandFill = (0, filledSurface_1.resolveFilledSurface)(colors.primary, colors);
|
|
35
39
|
const trackSurface = (0, segmentedInk_1.segmentedTrackSurface)(variant, colors, surface);
|
|
36
40
|
const restInk = (0, segmentedInk_1.segmentedRestInk)(colors, trackSurface);
|
|
37
|
-
const selectedInk = (0, outlineRung_1.liftToContrast)(colors.accentText, trackSurface, accentText_1.AA_NORMAL_TEXT);
|
|
41
|
+
const selectedInk = (0, outlineRung_1.liftToContrast)(colors.accentText, (0, segmentedInk_1.segmentedSelectedSurface)(variant, colors, trackSurface), accentText_1.AA_NORMAL_TEXT);
|
|
38
42
|
const hoverInk = (0, outlineRung_1.liftToContrast)(colors.text[900], trackSurface, accentText_1.AA_NORMAL_TEXT);
|
|
39
43
|
const segmentH = `calc(${control.height} - ${isUnderlineSlide ? UNDERLINE_WIDTH : `2 * ${TRACK_PADDING}`})`;
|
|
40
44
|
// Concentric: an inset child keeping the parent's radius bulges at the corners.
|
|
@@ -60,6 +64,8 @@ function SegmentedControl({ options, value, onChange, size = "sm", isFullWidth =
|
|
|
60
64
|
options,
|
|
61
65
|
isFullWidth,
|
|
62
66
|
size,
|
|
67
|
+
orientation,
|
|
68
|
+
isCompact,
|
|
63
69
|
]);
|
|
64
70
|
const selectAt = (index) => {
|
|
65
71
|
var _a;
|
|
@@ -90,11 +96,17 @@ function SegmentedControl({ options, value, onChange, size = "sm", isFullWidth =
|
|
|
90
96
|
selectAt(enabled[enabled.length - 1]);
|
|
91
97
|
}
|
|
92
98
|
};
|
|
93
|
-
return ((0, jsx_runtime_1.jsxs)(react_2.Flex, { ref: trackRef, role: "radiogroup", "aria-label": ariaLabel, onKeyDown: handleKeyDown, onFocus: (event) => setShowFocus(event.target.matches(":focus-visible")), onBlur: () => setShowFocus(false), boxShadow: showFocus && !isUnderlineSlide ? focusShadow : plateEdge, position: "relative", display: "inline-flex", align: "center", gap: isUnderlineSlide ? "0" : TRACK_PADDING, p: isUnderlineSlide ? "0" : TRACK_PADDING, h: control.height, boxSizing: "border-box", borderRadius: isUnderlineSlide ? "none" : "lg", borderBottom: isUnderlineSlide ? `${UNDERLINE_WIDTH} solid ${colors.boxborder[200]}` : undefined, bg: isUnderlineSlide ? "transparent" : trackSurface, w: isFullWidth ? "100%" : "fit-content", minW: "max-content", children: [isBrandSlide && pill && ((0, jsx_runtime_1.jsx)(react_2.Box, { "aria-hidden": "true", position: "absolute", top: TRACK_PADDING, left: 0, h: segmentH, w: `${pill.width}px`, borderRadius: innerRadius, bg: brandFill.fill, boxShadow: `0 0.125rem 0.5rem ${colors.boxShadow.primary}`, transform: `translateX(${pill.left}px)`, transition: SLIDE, sx: (0, motion_2.reducedMotionSx)() })), isUnderlineSlide && pill && ((0, jsx_runtime_1.jsx)(react_2.Box, { "aria-hidden": "true", position: "absolute", bottom: "-0.0625rem", left: 0, h: "0.125rem", w: `${pill.width}px`, borderRadius: "full", bg: colors.primary[500], transform: `translateX(${pill.left}px)`, transition: SLIDE, sx: (0, motion_2.reducedMotionSx)() })), options.map((option, index) => {
|
|
99
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.Flex, { ref: trackRef, role: "radiogroup", "aria-label": ariaLabel, "aria-orientation": isVertical ? "vertical" : undefined, onKeyDown: handleKeyDown, onFocus: (event) => setShowFocus(event.target.matches(":focus-visible")), onBlur: () => setShowFocus(false), boxShadow: showFocus && !isUnderlineSlide ? focusShadow : plateEdge, position: "relative", display: "inline-flex", flexDirection: isVertical ? "column" : "row", align: isVertical ? "stretch" : "center", gap: isUnderlineSlide ? "0" : TRACK_PADDING, p: isUnderlineSlide ? "0" : TRACK_PADDING, h: isVertical ? "fit-content" : control.height, boxSizing: "border-box", borderRadius: isUnderlineSlide ? "none" : "lg", borderBottom: isUnderlineSlide && !isVertical ? `${UNDERLINE_WIDTH} solid ${colors.boxborder[200]}` : undefined, borderLeft: isUnderlineSlide && isVertical ? `${UNDERLINE_WIDTH} solid ${colors.boxborder[200]}` : undefined, bg: isUnderlineSlide ? "transparent" : trackSurface, w: isFullWidth ? "100%" : "fit-content", minW: "max-content", children: [isBrandSlide && pill && ((0, jsx_runtime_1.jsx)(react_2.Box, { "aria-hidden": "true", position: "absolute", top: isVertical ? 0 : TRACK_PADDING, left: isVertical ? TRACK_PADDING : 0, right: isVertical ? TRACK_PADDING : undefined, h: segmentH, w: isVertical ? undefined : `${pill.width}px`, borderRadius: innerRadius, bg: brandFill.fill, boxShadow: `0 0.125rem 0.5rem ${colors.boxShadow.primary}`, transform: isVertical ? `translateY(${pill.top}px)` : `translateX(${pill.left}px)`, transition: SLIDE, sx: (0, motion_2.reducedMotionSx)() })), isUnderlineSlide && pill && ((0, jsx_runtime_1.jsx)(react_2.Box, { "aria-hidden": "true", position: "absolute", bottom: isVertical ? undefined : "-0.0625rem", left: isVertical ? "-0.0625rem" : 0, top: isVertical ? 0 : undefined, h: isVertical ? segmentH : "0.125rem", w: isVertical ? "0.125rem" : `${pill.width}px`, borderRadius: "full", bg: colors.primary[500], transform: isVertical ? `translateY(${pill.top}px)` : `translateX(${pill.left}px)`, transition: SLIDE, sx: (0, motion_2.reducedMotionSx)() })), options.map((option, index) => {
|
|
94
100
|
const isSelected = option.value === value;
|
|
101
|
+
const showShortLabel = isCompact && !!option.shortLabel;
|
|
102
|
+
const displayLabel = showShortLabel ? option.shortLabel : option.label;
|
|
103
|
+
// Swapping to shortLabel must not shrink the accessible name — restate the full label (+ count) explicitly.
|
|
104
|
+
const compactAriaLabel = showShortLabel
|
|
105
|
+
? `${option.label}${option.count != null ? ` ${formatCount(option.count)}` : ""}`
|
|
106
|
+
: undefined;
|
|
95
107
|
return ((0, jsx_runtime_1.jsxs)(react_2.Box, { as: "button", type: "button", ref: (node) => {
|
|
96
108
|
refs.current[index] = node;
|
|
97
|
-
}, role: "radio", "aria-checked": isSelected, disabled: option.isDisabled, tabIndex: index === tabIndex ? 0 : -1, onClick: () => onChange(option.value), position: "relative", zIndex: 1, display: "inline-flex", alignItems: "center", justifyContent: "center", gap: "0.375rem", flex: isFullWidth ? "1" : undefined, h: segmentH, px: control.paddingX, borderRadius: innerRadius, borderWidth: "1px", borderStyle: "solid", borderColor: hasIndicator
|
|
109
|
+
}, role: "radio", "aria-checked": isSelected, "aria-label": compactAriaLabel, disabled: option.isDisabled, tabIndex: index === tabIndex ? 0 : -1, onClick: () => onChange(option.value), position: "relative", zIndex: 1, display: "inline-flex", alignItems: "center", justifyContent: isVertical ? "flex-start" : "center", gap: "0.375rem", flex: isFullWidth ? "1" : undefined, h: segmentH, px: control.paddingX, borderRadius: innerRadius, borderWidth: "1px", borderStyle: "solid", borderColor: hasIndicator
|
|
98
110
|
? "transparent"
|
|
99
111
|
: isSelected
|
|
100
112
|
? colors.primary.opacity[32]
|
|
@@ -108,6 +120,6 @@ function SegmentedControl({ options, value, onChange, size = "sm", isFullWidth =
|
|
|
108
120
|
_focusVisible: segmentFocus,
|
|
109
121
|
[focusRing_styles_1.FOCUS_VISIBLE_OVER_STATE]: segmentFocus,
|
|
110
122
|
...(0, motion_2.reducedMotionSx)(),
|
|
111
|
-
}, children: [option.icon,
|
|
123
|
+
}, children: [option.icon, displayLabel, option.count != null && ((0, jsx_runtime_1.jsx)(Tag_1.default, { label: formatCount(option.count), size: COUNT_TAG_SIZE[size], variant: "subtle", colorScheme: "gray" }))] }, option.value));
|
|
112
124
|
})] }));
|
|
113
125
|
}
|
|
@@ -3,10 +3,14 @@ import type { ControlSize } from "../../Theme/tokens/primitives/controlSizes";
|
|
|
3
3
|
export type SegmentedControlSize = Exclude<ControlSize, "xs">;
|
|
4
4
|
/** brandSlide = primary view switcher; underlineSlide = dense toolbar; default = plain buttons. */
|
|
5
5
|
export type SegmentedControlVariant = "default" | "brandSlide" | "underlineSlide";
|
|
6
|
+
/** Stacking axis of the track; vertical keeps each segment's own height and lets the track's width follow its widest option. Defaults to "horizontal". */
|
|
7
|
+
export type SegmentedControlOrientation = "horizontal" | "vertical";
|
|
6
8
|
/** One selectable segment; disabled options are skipped by keyboard nav (arrows/Home/End). */
|
|
7
9
|
export type SegmentedControlOption<T extends string = string> = {
|
|
8
10
|
value: T;
|
|
9
11
|
label: string;
|
|
12
|
+
/** Shown instead of label below the "md" breakpoint, when set; the full label stays the segment's accessible name. */
|
|
13
|
+
shortLabel?: string;
|
|
10
14
|
/** Rendered before the label. */
|
|
11
15
|
icon?: JSX.Element;
|
|
12
16
|
isDisabled?: boolean;
|
|
@@ -27,6 +31,8 @@ export type SegmentedControlProps<T extends string = string> = {
|
|
|
27
31
|
isFullWidth?: boolean;
|
|
28
32
|
/** brandSlide = primary view switcher; underlineSlide = dense toolbars/headers. */
|
|
29
33
|
variant?: SegmentedControlVariant;
|
|
34
|
+
/** Stacks segments top-to-bottom instead of side-by-side. Defaults to "horizontal"; fully opt-in. */
|
|
35
|
+
orientation?: SegmentedControlOrientation;
|
|
30
36
|
/** Background this sits on. Optional: the track is opaque on any backdrop; pass it only to aim the plate's hairline edge at the real backdrop. */
|
|
31
37
|
surface?: string;
|
|
32
38
|
"aria-label": string;
|
|
@@ -2,6 +2,8 @@ import type { PaletteProps } from "../../Theme/tokens/types";
|
|
|
2
2
|
import type { SegmentedControlVariant } from "./SegmentedControlProps";
|
|
3
3
|
/** The surface a segment's label really sits on. */
|
|
4
4
|
export declare const segmentedTrackSurface: (variant: SegmentedControlVariant, colors: PaletteProps, surface?: string) => string;
|
|
5
|
+
/** The surface the selected label sits on: the default variant lifts it onto its own plate. */
|
|
6
|
+
export declare const segmentedSelectedSurface: (variant: SegmentedControlVariant, colors: PaletteProps, trackSurface: string) => string;
|
|
5
7
|
/** Unselected label ink, lifted to AA against the track it is drawn on. */
|
|
6
8
|
export declare const segmentedRestInk: (colors: PaletteProps, trackSurface: string) => string;
|
|
7
9
|
/** A radiogroup is one tab stop, so the group wears the ring: a brand hairline plus the field bloom. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.segmentedPlateEdge = exports.segmentedFocusShadow = exports.segmentedRestInk = exports.segmentedTrackSurface = void 0;
|
|
3
|
+
exports.segmentedPlateEdge = exports.segmentedFocusShadow = exports.segmentedRestInk = exports.segmentedSelectedSurface = exports.segmentedTrackSurface = void 0;
|
|
4
4
|
const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
|
|
5
5
|
const outlineRung_1 = require("../../Theme/tokens/builders/outlineRung");
|
|
6
6
|
const accentText_1 = require("../../Theme/tokens/builders/accentText");
|
|
@@ -15,6 +15,9 @@ const segmentedTrackSurface = (variant, colors, surface) => {
|
|
|
15
15
|
return colors.background[600];
|
|
16
16
|
};
|
|
17
17
|
exports.segmentedTrackSurface = segmentedTrackSurface;
|
|
18
|
+
/** The surface the selected label sits on: the default variant lifts it onto its own plate. */
|
|
19
|
+
const segmentedSelectedSurface = (variant, colors, trackSurface) => (variant === "default" ? colors.backgroundColor.light : trackSurface);
|
|
20
|
+
exports.segmentedSelectedSurface = segmentedSelectedSurface;
|
|
18
21
|
/** Unselected label ink, lifted to AA against the track it is drawn on. */
|
|
19
22
|
const segmentedRestInk = (colors, trackSurface) => (0, outlineRung_1.liftToContrast)(colors.text[600], trackSurface, accentText_1.AA_NORMAL_TEXT);
|
|
20
23
|
exports.segmentedRestInk = segmentedRestInk;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { type DependencyList } from "react";
|
|
2
2
|
export type SlidingPillRect = {
|
|
3
3
|
left: number;
|
|
4
|
+
top: number;
|
|
4
5
|
width: number;
|
|
6
|
+
height: number;
|
|
5
7
|
} | null;
|
|
6
8
|
/** Measures the selected item's DOM rect for a sliding pill/underline; shared by SegmentedControl and TableToggle. */
|
|
7
9
|
export declare function useSlidingPill(enabled: boolean, selectedIndex: number, extraDeps: DependencyList): {
|
|
@@ -18,7 +18,12 @@ function useSlidingPill(enabled, selectedIndex, extraDeps) {
|
|
|
18
18
|
}
|
|
19
19
|
const trackRect = track.getBoundingClientRect();
|
|
20
20
|
const nodeRect = node.getBoundingClientRect();
|
|
21
|
-
setPill({
|
|
21
|
+
setPill({
|
|
22
|
+
left: nodeRect.left - trackRect.left,
|
|
23
|
+
top: nodeRect.top - trackRect.top,
|
|
24
|
+
width: nodeRect.width,
|
|
25
|
+
height: nodeRect.height,
|
|
26
|
+
});
|
|
22
27
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
23
28
|
}, [enabled, selectedIndex, ...extraDeps]);
|
|
24
29
|
return { trackRef, itemRefs, pill };
|