@evolve.ui/core-components 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/evolve-ui.entries.json +556 -0
- package/fileUpload/testUtils/mockFilesAndImages.d.cts +3 -3
- package/fileUpload/testUtils/mockFilesAndImages.d.cts.map +1 -1
- package/fileUpload/testUtils/mockFilesAndImages.d.mts +3 -3
- package/fileUpload/testUtils/mockFilesAndImages.d.mts.map +1 -1
- package/fileUpload/testUtils/testUtils.d.cts +1 -1
- package/fileUpload/testUtils/testUtils.d.cts.map +1 -1
- package/fileUpload/testUtils/testUtils.d.mts +1 -1
- package/fileUpload/testUtils/testUtils.d.mts.map +1 -1
- package/package.json +2 -2
- package/src/Accordion/Accordion.tsx +233 -0
- package/src/AddressDisplay/AddressDisplay.tsx +43 -0
- package/src/AddressDisplay/components/addressComponents.tsx +111 -0
- package/src/AddressDisplay/components/addressField.tsx +25 -0
- package/src/AddressDisplay/components/addressSection.tsx +8 -0
- package/src/AddressDisplay/components/getCountryValue.tsx +31 -0
- package/src/AddressDisplay/types.ts +26 -0
- package/src/AutomatedCarousel/AutomatedCarousel.tsx +163 -0
- package/src/AutomatedCarousel/automatedCarousel.types.ts +7 -0
- package/src/AutomatedCarousel/components/CarouselProgressBar/CarouselProgressBar.tsx +139 -0
- package/src/AutomatedCarousel/components/CarouselProgressBar/components/CarouselProgressBarUnit/CarouselProgressBarUnit.tsx +165 -0
- package/src/Badge/Badge.tsx +104 -0
- package/src/Badge/Badge.types.ts +58 -0
- package/src/Badge/ItemBadge.tsx +97 -0
- package/src/Badge/LiveStatusBadge.tsx +93 -0
- package/src/BannerNotification/BannerNotification.tsx +157 -0
- package/src/BannerNotification/components/BannerNotificationSkeleton.tsx +39 -0
- package/src/BannerNotification/components/CalloutIcon/BannerIcon.tsx +48 -0
- package/src/BannerNotification/utils/getRandomTooltipId.ts +3 -0
- package/src/BottomSheet/BottomSheet.tsx +476 -0
- package/src/BottomSheet/components/BottomSheetBackdrop/BottomSheetBackdrop.tsx +47 -0
- package/src/BottomSheet/components/BottomSheetContentArea/BottomSheetContentArea.tsx +92 -0
- package/src/BottomSheet/components/BottomSheetOverlay/BottomSheetOverlayTranslate3d.tsx +196 -0
- package/src/BottomSheet/components/SheetDescription/SheetDescription.tsx +22 -0
- package/src/BottomSheet/components/SheetTitle/SheetTitle.tsx +21 -0
- package/src/BottomSheet/components/index.tsx +5 -0
- package/src/BottomSheet/hooks/index.tsx +13 -0
- package/src/BottomSheet/hooks/useAriaHider.tsx +91 -0
- package/src/BottomSheet/hooks/useBottomSheetServices.tsx +224 -0
- package/src/BottomSheet/hooks/useDragHandler.tsx +282 -0
- package/src/BottomSheet/hooks/useFocusTrap.tsx +113 -0
- package/src/BottomSheet/hooks/useLayoutEffect.tsx +4 -0
- package/src/BottomSheet/hooks/useOptimizedResizeObserver.tsx +94 -0
- package/src/BottomSheet/hooks/useOptimizedScrollState.tsx +87 -0
- package/src/BottomSheet/hooks/useReady.tsx +34 -0
- package/src/BottomSheet/hooks/useReducedMotion.tsx +22 -0
- package/src/BottomSheet/hooks/useScrollLock.tsx +143 -0
- package/src/BottomSheet/hooks/useSnapPoints.tsx +355 -0
- package/src/BottomSheet/hooks/useSpring.tsx +47 -0
- package/src/BottomSheet/hooks/useSpringInterpolationsTranslate3d.tsx +101 -0
- package/src/BottomSheet/index.tsx +237 -0
- package/src/BottomSheet/state/atoms.ts +147 -0
- package/src/BottomSheet/state/useBottomSheetStateMachine.ts +246 -0
- package/src/BottomSheet/types.ts +172 -0
- package/src/BottomSheet/utils.ts +65 -0
- package/src/Breadcrumbs/Breadcrumbs.tsx +126 -0
- package/src/Button/Button.tsx +164 -0
- package/src/Button/Button.types.ts +80 -0
- package/src/Button/buttonColorClasses.ts +53 -0
- package/src/Callout/Callout.tsx +136 -0
- package/src/Callout/Callout.types.ts +29 -0
- package/src/Callout/components/CalloutIcon/CalloutIcon.tsx +56 -0
- package/src/Card/CardGrid.tsx +263 -0
- package/src/Card/SingleCard.tsx +22 -0
- package/src/Card/cardFactory.tsx +176 -0
- package/src/Card/components/createBadge.tsx +34 -0
- package/src/Card/components/createFavoriteButton.tsx +40 -0
- package/src/Card/components/createImage.tsx +64 -0
- package/src/Card/components/createInfo.tsx +84 -0
- package/src/Card/components/createName.tsx +63 -0
- package/src/Card/components/createPriceStatus.tsx +100 -0
- package/src/Card/components/types.ts +4 -0
- package/src/Card/hooks/useCardVisibilityTracking.ts +82 -0
- package/src/Card/sharedProps.ts +12 -0
- package/src/Card/types.ts +62 -0
- package/src/Carousel/Carousel.tsx +513 -0
- package/src/Carousel/Carousel.types.ts +43 -0
- package/src/Carousel/Carousel.utils.ts +63 -0
- package/src/Carousel/components/DotButton/DotButton.tsx +42 -0
- package/src/Carousel/components/NavigationButtons/NavigationButtons.tsx +77 -0
- package/src/Carousel/components/TitleSection/TitleSection.tsx +61 -0
- package/src/Carousel/components/TitleSection/components/SubHeaderLeft/SubHeaderLeft.tsx +53 -0
- package/src/Carousel/components/TitleSection/components/Title/Title.tsx +46 -0
- package/src/Carousel/index.ts +2 -0
- package/src/Carousel/testUtils.ts +10 -0
- package/src/Chips/Chip.tsx +153 -0
- package/src/Chips/ChipGroup.tsx +173 -0
- package/src/Chips/SuggestionChip.tsx +15 -0
- package/src/Chips/chipTypes.ts +113 -0
- package/src/Chips/index.ts +4 -0
- package/src/ChoiceListItem/Checkbox.tsx +69 -0
- package/src/ChoiceListItem/Radio.tsx +61 -0
- package/src/ChoiceListItem/choiceListItem.tsx +99 -0
- package/src/ChoiceListItem/common.tsx +148 -0
- package/src/ChoiceListItem/index.ts +3 -0
- package/src/Collapsible/Collapsible.tsx +58 -0
- package/src/Confetti/Confetti.tsx +270 -0
- package/src/Confetti/index.ts +2 -0
- package/src/ContentPager/ContentPager.tsx +189 -0
- package/src/ContentPager/components/Page.tsx +24 -0
- package/src/CookieConsent/components/CookieCategoryRow/CookieCategoryRow.tsx +30 -0
- package/src/CookieConsent/components/CookieConsent/CookieConsent.tsx +74 -0
- package/src/CookieConsent/components/CookieConsentBanner/CookieConsentBanner.tsx +51 -0
- package/src/CookieConsent/components/CookieConsentModal/CookieConsentModal.tsx +64 -0
- package/src/CookieConsent/hooks/useCookieConsent.ts +170 -0
- package/src/CookieConsent/hooks/useCookieConsentState.ts +142 -0
- package/src/CookieConsent/index.ts +16 -0
- package/src/CookieConsent/types/cookieConsent.types.ts +79 -0
- package/src/CookieConsent/types/index.ts +7 -0
- package/src/Countdown/Countdown.tsx +161 -0
- package/src/Countdown/Countdown.types.ts +4 -0
- package/src/Countdown/hooks/useGetCountdown.ts +103 -0
- package/src/DateTimeInput/dateInput/DateInput.tsx +199 -0
- package/src/DateTimeInput/datePicker/DatePicker.tsx +138 -0
- package/src/DateTimeInput/datePicker/components/calendar/Calendar.tsx +72 -0
- package/src/DateTimeInput/datePicker/index.ts +2 -0
- package/src/DateTimeInput/timeInput/TimeInput.tsx +128 -0
- package/src/DateTimeInput/timePicker/TimePicker.tsx +118 -0
- package/src/DateTimeInput/timePicker/components/TimePopover/AmPmSwitch.tsx +103 -0
- package/src/DateTimeInput/timePicker/components/TimePopover/TimeFieldInput.tsx +26 -0
- package/src/DateTimeInput/timePicker/components/TimePopover/TimePopover.tsx +84 -0
- package/src/DateTimeInput/timePicker/components/TimePopover/TimePopover.utils.ts +67 -0
- package/src/DateTimeInput/timePicker/components/TimePopover/TimePopoverActions.tsx +26 -0
- package/src/DateTimeInput/timePicker/index.ts +2 -0
- package/src/DateTimeInput/utils/getDisplayDate.ts +15 -0
- package/src/DateTimeInput/utils/getDisplayTime.ts +15 -0
- package/src/DateTimeInput/utils/index.ts +2 -0
- package/src/DateTimeInput/utils/parseTimeValue.ts +54 -0
- package/src/Drawer/Drawer.tsx +446 -0
- package/src/Drawer/atoms.ts +4 -0
- package/src/Drawer/components/DialogActions/DialogActions.tsx +33 -0
- package/src/Drawer/components/DialogContent/DialogContent.tsx +80 -0
- package/src/Drawer/components/DialogHeader/DialogHeader.tsx +68 -0
- package/src/EducationalPopover/EducationalPopover.tsx +174 -0
- package/src/FavoriteButton/FavoriteButton.tsx +64 -0
- package/src/FavoriteButton/components/IconOnlyFavoriteButton/IconOnlyFavoriteButton.tsx +80 -0
- package/src/FavoriteButton/components/LabeledAnimatedFavoriteButton/LabeledAnimatedFavoriteButton.tsx +179 -0
- package/src/FavoriteButton/components/LabeledFavoriteButton/LabeledFavoriteButton.tsx +71 -0
- package/src/FavoriteButton/components/SmallHeartStraightIcon/SmallHeartStraightIcon.tsx +14 -0
- package/src/FavoriteButton/hooks/useFavoriteButtonState.ts +30 -0
- package/src/FavoriteButton/index.ts +2 -0
- package/src/FavoriteButton/types/favoriteButton.ts +87 -0
- package/src/FeatureFlags/devTools/FeatureFlagDevToolsUI.tsx +226 -0
- package/src/FeatureFlags/devTools/getFeatureFlagCookieOverrides.ts +30 -0
- package/src/FeatureFlags/devTools/utils/cookies.ts +169 -0
- package/src/FeatureFlags/hooks/index.ts +2 -0
- package/src/FeatureFlags/hooks/useFeatureFlag.tsx +19 -0
- package/src/FeatureFlags/hooks/useFeatureFlags.tsx +15 -0
- package/src/FileUpload/components/FileCard/FileCard.tsx +50 -0
- package/src/FileUpload/components/FileDropzone/FileDropzone.tsx +221 -0
- package/src/FileUpload/components/FileDropzone/components/DropzoneContent/DropzoneContent.tsx +28 -0
- package/src/FileUpload/components/FileDropzone/components/DropzoneContent/DropzoneText.tsx +81 -0
- package/src/FileUpload/components/FileDropzone/components/FileDropzoneDebugger/FileDropzoneDebugger.tsx +40 -0
- package/src/FileUpload/components/FileDropzone/components/FileDropzoneDebugger/FileRejectionsDisplay.tsx +110 -0
- package/src/FileUpload/components/FileDropzoneOverlay/FileDropzoneOverlay.tsx +183 -0
- package/src/FileUpload/components/FileDropzoneOverlay/components/OverlayDropzoneContent.tsx +146 -0
- package/src/FileUpload/components/FileDropzoneOverlay/components/OverlayDropzoneDragText.tsx +42 -0
- package/src/FileUpload/components/FileDropzoneOverlay/styles.ts +79 -0
- package/src/FileUpload/components/FilePreviewList/FilePreviewList.tsx +251 -0
- package/src/FileUpload/components/FilePreviewList/virtualizerComponents/FilePreviewVirtualizedList/FilePreviewVirtualizedList.tsx +88 -0
- package/src/FileUpload/components/FileUpload/FileUpload.tsx +63 -0
- package/src/FileUpload/components/FileUploadCanvas/FileUploadCanvas.tsx +125 -0
- package/src/FileUpload/components/GridFileCard/GridFileCard.tsx +364 -0
- package/src/FileUpload/components/GridFileCard/utils/sortable.ts +48 -0
- package/src/FileUpload/components/ListFileCard/ListFileCard.tsx +259 -0
- package/src/FileUpload/components/types.ts +78 -0
- package/src/FileUpload/hooks/filePreview/useImageFilePreviewUrl.ts +62 -0
- package/src/FileUpload/hooks/index.ts +11 -0
- package/src/FileUpload/hooks/upload/useHandleDragEnter/useHandleDragEnter.ts +154 -0
- package/src/FileUpload/hooks/upload/useHandleDrop/createFileRejectionHelpers.ts +273 -0
- package/src/FileUpload/hooks/upload/useHandleDrop/useHandleDrop.ts +153 -0
- package/src/FileUpload/hooks/upload/useUploadAvailability/useUploadAvailability.ts +44 -0
- package/src/FileUpload/hooks/validation/createDropzoneValidator.ts +41 -0
- package/src/FileUpload/hooks/validation/useFileValidation.ts +25 -0
- package/src/FileUpload/hooks/validation/useFileValidationLogic.ts +107 -0
- package/src/FileUpload/hooks/validation/useValidationMessages.ts +92 -0
- package/src/FileUpload/hooks/validation/useValidationState.ts +171 -0
- package/src/FileUpload/index.ts +57 -0
- package/src/FileUpload/providers/FileUploadProvider.tsx +219 -0
- package/src/FileUpload/providers/UploadValidationProvider.tsx +159 -0
- package/src/FileUpload/testUtils/mockFilesAndImages.ts +64 -0
- package/src/FileUpload/testUtils/testUtils.ts +111 -0
- package/src/FileUpload/types/commonTypes.ts +267 -0
- package/src/FileUpload/types/index.ts +31 -0
- package/src/FileUpload/types/sharedTypes.ts +159 -0
- package/src/FileUpload/utils/constants.ts +79 -0
- package/src/FileUpload/utils/createMessageContext.ts +180 -0
- package/src/FileUpload/utils/getFileExtensionsFromAcceptedTypes.ts +73 -0
- package/src/FileUpload/utils/getFilePluralizationText.ts +68 -0
- package/src/FileUpload/utils/getMimeTypes.ts +26 -0
- package/src/FileUpload/utils/messageUtils.ts +41 -0
- package/src/FileUpload/utils/styles.ts +68 -0
- package/src/FormLabel/FormLabel.tsx +212 -0
- package/src/IconButton/IconButton.tsx +154 -0
- package/src/Icons/CreditCardIcon/CreditCardIcon.tsx +73 -0
- package/src/Icons/CreditCardIcon/cardIcons.tsx +270 -0
- package/src/Icons/IconBase.tsx +133 -0
- package/src/Icons/anyIcon.ts +3 -0
- package/src/Icons/generatedIcons/Archive.tsx +48 -0
- package/src/Icons/generatedIcons/ArrowClockwise.tsx +48 -0
- package/src/Icons/generatedIcons/ArrowDown.tsx +48 -0
- package/src/Icons/generatedIcons/ArrowLeft.tsx +48 -0
- package/src/Icons/generatedIcons/ArrowRight.tsx +48 -0
- package/src/Icons/generatedIcons/ArrowSquareOut.tsx +51 -0
- package/src/Icons/generatedIcons/ArrowSquareRight.tsx +51 -0
- package/src/Icons/generatedIcons/ArrowUp.tsx +48 -0
- package/src/Icons/generatedIcons/ArrowsCounterClockwise.tsx +48 -0
- package/src/Icons/generatedIcons/ArrowsLeftRight.tsx +48 -0
- package/src/Icons/generatedIcons/ArrowsOutCardinal.tsx +48 -0
- package/src/Icons/generatedIcons/Bank.tsx +48 -0
- package/src/Icons/generatedIcons/Bell.tsx +51 -0
- package/src/Icons/generatedIcons/BookBookmark.tsx +51 -0
- package/src/Icons/generatedIcons/BookmarksSimple.tsx +51 -0
- package/src/Icons/generatedIcons/Broadcast.tsx +48 -0
- package/src/Icons/generatedIcons/CalendarBlank.tsx +48 -0
- package/src/Icons/generatedIcons/CalendarStar.tsx +51 -0
- package/src/Icons/generatedIcons/Camera.tsx +51 -0
- package/src/Icons/generatedIcons/CaretDown.tsx +48 -0
- package/src/Icons/generatedIcons/CaretLeft.tsx +48 -0
- package/src/Icons/generatedIcons/CaretRight.tsx +48 -0
- package/src/Icons/generatedIcons/CaretUp.tsx +48 -0
- package/src/Icons/generatedIcons/Certificate.tsx +51 -0
- package/src/Icons/generatedIcons/ChartBar.tsx +48 -0
- package/src/Icons/generatedIcons/Check.tsx +51 -0
- package/src/Icons/generatedIcons/CheckCircle.tsx +48 -0
- package/src/Icons/generatedIcons/CirclesFour.tsx +51 -0
- package/src/Icons/generatedIcons/Clock.tsx +48 -0
- package/src/Icons/generatedIcons/ClockCountdown.tsx +48 -0
- package/src/Icons/generatedIcons/Close.tsx +51 -0
- package/src/Icons/generatedIcons/CloudArrowUp.tsx +51 -0
- package/src/Icons/generatedIcons/CloudWarning.tsx +51 -0
- package/src/Icons/generatedIcons/Code.tsx +48 -0
- package/src/Icons/generatedIcons/Compass.tsx +51 -0
- package/src/Icons/generatedIcons/Confetti.tsx +51 -0
- package/src/Icons/generatedIcons/Copy.tsx +48 -0
- package/src/Icons/generatedIcons/CornersOut.tsx +48 -0
- package/src/Icons/generatedIcons/CreditCard.tsx +48 -0
- package/src/Icons/generatedIcons/Crown.tsx +51 -0
- package/src/Icons/generatedIcons/CrownSimple.tsx +51 -0
- package/src/Icons/generatedIcons/CurrencyCircleDollar.tsx +48 -0
- package/src/Icons/generatedIcons/CurrencyDollarSimple.tsx +51 -0
- package/src/Icons/generatedIcons/Desktop.tsx +51 -0
- package/src/Icons/generatedIcons/Detective.tsx +51 -0
- package/src/Icons/generatedIcons/DotOutline.tsx +46 -0
- package/src/Icons/generatedIcons/DotsSixVertical.tsx +51 -0
- package/src/Icons/generatedIcons/DotsThreeOutline.tsx +51 -0
- package/src/Icons/generatedIcons/DotsThreeOutlineVertical.tsx +51 -0
- package/src/Icons/generatedIcons/DownloadSimple.tsx +51 -0
- package/src/Icons/generatedIcons/EnvelopeSimple.tsx +48 -0
- package/src/Icons/generatedIcons/Eraser.tsx +51 -0
- package/src/Icons/generatedIcons/Eye.tsx +51 -0
- package/src/Icons/generatedIcons/EyeSlash.tsx +51 -0
- package/src/Icons/generatedIcons/FacebookLogo.tsx +48 -0
- package/src/Icons/generatedIcons/FileArchive.tsx +48 -0
- package/src/Icons/generatedIcons/FileArrowDown.tsx +48 -0
- package/src/Icons/generatedIcons/FileCsv.tsx +48 -0
- package/src/Icons/generatedIcons/FilePdf.tsx +48 -0
- package/src/Icons/generatedIcons/FileText.tsx +48 -0
- package/src/Icons/generatedIcons/Files.tsx +51 -0
- package/src/Icons/generatedIcons/Fire.tsx +51 -0
- package/src/Icons/generatedIcons/Flag.tsx +51 -0
- package/src/Icons/generatedIcons/FlagBannerFold.tsx +48 -0
- package/src/Icons/generatedIcons/FloppyDisk.tsx +51 -0
- package/src/Icons/generatedIcons/Gauge.tsx +51 -0
- package/src/Icons/generatedIcons/Gavel.tsx +51 -0
- package/src/Icons/generatedIcons/Gear.tsx +51 -0
- package/src/Icons/generatedIcons/GlobeHemisphereWest.tsx +51 -0
- package/src/Icons/generatedIcons/GoogleLogo.tsx +48 -0
- package/src/Icons/generatedIcons/GridNine.tsx +51 -0
- package/src/Icons/generatedIcons/HandCoins.tsx +51 -0
- package/src/Icons/generatedIcons/HandbagSimple.tsx +51 -0
- package/src/Icons/generatedIcons/Headset.tsx +51 -0
- package/src/Icons/generatedIcons/HeartStraight.tsx +51 -0
- package/src/Icons/generatedIcons/HourglassLow.tsx +48 -0
- package/src/Icons/generatedIcons/House.tsx +51 -0
- package/src/Icons/generatedIcons/Image.tsx +51 -0
- package/src/Icons/generatedIcons/ImageSquare.tsx +51 -0
- package/src/Icons/generatedIcons/ImagesSquare.tsx +51 -0
- package/src/Icons/generatedIcons/Info.tsx +48 -0
- package/src/Icons/generatedIcons/InstagramLogo.tsx +51 -0
- package/src/Icons/generatedIcons/Lightning.tsx +48 -0
- package/src/Icons/generatedIcons/Link.tsx +51 -0
- package/src/Icons/generatedIcons/LinkedinLogo.tsx +51 -0
- package/src/Icons/generatedIcons/List.tsx +48 -0
- package/src/Icons/generatedIcons/Lock.tsx +51 -0
- package/src/Icons/generatedIcons/LockOpen.tsx +51 -0
- package/src/Icons/generatedIcons/MagnifyingGlass.tsx +48 -0
- package/src/Icons/generatedIcons/MapPin.tsx +51 -0
- package/src/Icons/generatedIcons/MapPinSimpleArea.tsx +48 -0
- package/src/Icons/generatedIcons/Medal.tsx +48 -0
- package/src/Icons/generatedIcons/Megaphone.tsx +48 -0
- package/src/Icons/generatedIcons/Minus.tsx +45 -0
- package/src/Icons/generatedIcons/Money.tsx +51 -0
- package/src/Icons/generatedIcons/Notches.tsx +48 -0
- package/src/Icons/generatedIcons/NotePencil.tsx +48 -0
- package/src/Icons/generatedIcons/Package.tsx +51 -0
- package/src/Icons/generatedIcons/PaintBrush.tsx +51 -0
- package/src/Icons/generatedIcons/PaperPlaneTilt.tsx +51 -0
- package/src/Icons/generatedIcons/Paperclip.tsx +51 -0
- package/src/Icons/generatedIcons/Pause.tsx +51 -0
- package/src/Icons/generatedIcons/PencilLine.tsx +51 -0
- package/src/Icons/generatedIcons/PencilRuler.tsx +51 -0
- package/src/Icons/generatedIcons/PencilSimple.tsx +51 -0
- package/src/Icons/generatedIcons/Phone.tsx +51 -0
- package/src/Icons/generatedIcons/PinterestLogo.tsx +51 -0
- package/src/Icons/generatedIcons/Play.tsx +51 -0
- package/src/Icons/generatedIcons/Plus.tsx +51 -0
- package/src/Icons/generatedIcons/PlusCircle.tsx +48 -0
- package/src/Icons/generatedIcons/Printer.tsx +51 -0
- package/src/Icons/generatedIcons/Question.tsx +48 -0
- package/src/Icons/generatedIcons/Receipt.tsx +51 -0
- package/src/Icons/generatedIcons/Rows.tsx +51 -0
- package/src/Icons/generatedIcons/SealCheck.tsx +51 -0
- package/src/Icons/generatedIcons/SelectionSlash.tsx +48 -0
- package/src/Icons/generatedIcons/ShareNetwork.tsx +51 -0
- package/src/Icons/generatedIcons/ShieldCheck.tsx +51 -0
- package/src/Icons/generatedIcons/ShieldStar.tsx +51 -0
- package/src/Icons/generatedIcons/ShoppingCart.tsx +48 -0
- package/src/Icons/generatedIcons/ShuffleAngular.tsx +48 -0
- package/src/Icons/generatedIcons/SidebarSimple.tsx +48 -0
- package/src/Icons/generatedIcons/SlidersHorizontal.tsx +51 -0
- package/src/Icons/generatedIcons/Smiley.tsx +48 -0
- package/src/Icons/generatedIcons/Sparkle.tsx +51 -0
- package/src/Icons/generatedIcons/SpinnerGap.tsx +48 -0
- package/src/Icons/generatedIcons/StackPlus.tsx +48 -0
- package/src/Icons/generatedIcons/Star.tsx +51 -0
- package/src/Icons/generatedIcons/Storefront.tsx +51 -0
- package/src/Icons/generatedIcons/Swap.tsx +51 -0
- package/src/Icons/generatedIcons/Tag.tsx +51 -0
- package/src/Icons/generatedIcons/ThumbsDown.tsx +48 -0
- package/src/Icons/generatedIcons/ThumbsUp.tsx +48 -0
- package/src/Icons/generatedIcons/Toolbox.tsx +48 -0
- package/src/Icons/generatedIcons/Trash.tsx +48 -0
- package/src/Icons/generatedIcons/TrendUp.tsx +48 -0
- package/src/Icons/generatedIcons/Trophy.tsx +51 -0
- package/src/Icons/generatedIcons/Truck.tsx +51 -0
- package/src/Icons/generatedIcons/UploadSimple.tsx +51 -0
- package/src/Icons/generatedIcons/User.tsx +48 -0
- package/src/Icons/generatedIcons/Users.tsx +48 -0
- package/src/Icons/generatedIcons/UsersThree.tsx +51 -0
- package/src/Icons/generatedIcons/VideoCamera.tsx +51 -0
- package/src/Icons/generatedIcons/Warning.tsx +51 -0
- package/src/Icons/generatedIcons/WarningCircle.tsx +48 -0
- package/src/Icons/generatedIcons/WifiHigh.tsx +51 -0
- package/src/Icons/generatedIcons/XCircle.tsx +48 -0
- package/src/Icons/generatedIcons/XLogo.tsx +48 -0
- package/src/Icons/generatedIcons/YoutubeLogo.tsx +51 -0
- package/src/Icons/importedIcons.ts +152 -0
- package/src/Icons/index.ts +149 -0
- package/src/Icons/socialIcons/appleSocialIcon.tsx +50 -0
- package/src/Icons/socialIcons/facebookSocialIcon.tsx +41 -0
- package/src/Icons/socialIcons/googleSocialIcon.tsx +134 -0
- package/src/ImageCluster/ImageCluster.tsx +125 -0
- package/src/ImageSlider/ImageSlider.tsx +582 -0
- package/src/ImageSlider/ImageSlider.types.ts +90 -0
- package/src/ImageSlider/components/IndexIndicator/IndexIndicator.tsx +21 -0
- package/src/ImageSlider/components/NavigationButtons/NavigationButtons.tsx +113 -0
- package/src/ImageSlider/components/ResponsiveImage/ResponsiveImage.tsx +114 -0
- package/src/ImageSlider/components/ResponsiveImage/index.ts +1 -0
- package/src/ImageSlider/components/Thumbnails/Thumbnails.tsx +184 -0
- package/src/ImageSlider/constants.ts +59 -0
- package/src/ImageSlider/hooks/index.ts +3 -0
- package/src/ImageSlider/hooks/useImageSlider.ts +57 -0
- package/src/ImageSlider/hooks/useImageSliderNavigation.ts +133 -0
- package/src/ImageSlider/hooks/useImageSliderScrollHandler.ts +171 -0
- package/src/ImageSlider/index.ts +3 -0
- package/src/ImageSlider/utils/imageOptimization.ts +97 -0
- package/src/ImageSlider/utils/index.ts +1 -0
- package/src/InlineNotification/InlineNotification.tsx +95 -0
- package/src/JumpMenu/JumpMenu.tsx +395 -0
- package/src/JumpMenu/components/JumpMenuItem/JumpMenuItem.tsx +62 -0
- package/src/JumpMenu/hooks/useContainerTracking.ts +171 -0
- package/src/JumpMenu/hooks/useIntersectionObserver.ts +77 -0
- package/src/JumpMenu/hooks/useKeyboardNavigation.ts +102 -0
- package/src/JumpMenu/hooks/useResizeObserver.ts +53 -0
- package/src/JumpMenu/hooks/useSectionObserver.ts +102 -0
- package/src/JumpMenu/index.ts +2 -0
- package/src/JumpMenu/types/index.ts +19 -0
- package/src/Loading/Loading.tsx +39 -0
- package/src/Loading/components/LoadingDot/LoadingDot.tsx +16 -0
- package/src/LoadingSpinner/LoadingSpinner.tsx +20 -0
- package/src/Menu/Menu.tsx +372 -0
- package/src/Menu/components/MenuDivider/MenuDivider.tsx +5 -0
- package/src/Menu/components/MenuItem/MenuItem.tsx +298 -0
- package/src/Menu/context/menuContext.tsx +20 -0
- package/src/Menu/hooks/useMenu.tsx +18 -0
- package/src/Menu/index.ts +3 -0
- package/src/Modal/ComposableModal.tsx +313 -0
- package/src/Modal/Modal.tsx +231 -0
- package/src/Modal/components/BackButton/BackButton.tsx +43 -0
- package/src/Modal/components/Backdrop/Backdrop.tsx +62 -0
- package/src/Modal/components/CloseButton/CloseButton.tsx +55 -0
- package/src/Modal/components/ModalActions/ModalActions.tsx +135 -0
- package/src/Modal/components/ModalContent/ModalContent.tsx +163 -0
- package/src/Modal/components/ModalHeader/ModalHeader.tsx +84 -0
- package/src/Modal/components/ModalHeader/hooks/useHeaderAnimation.tsx +49 -0
- package/src/Modal/components/ModalProgressSteps/ModalProgressSteps.tsx +26 -0
- package/src/Modal/components/ModalSubTitle/ModalSubTitle.tsx +29 -0
- package/src/Modal/components/ModalTitle/ModalTitle.tsx +22 -0
- package/src/Modal/components/PreventModalScroll/PreventModalScroll.tsx +106 -0
- package/src/Modal/components/index.ts +12 -0
- package/src/Modal/context.ts +33 -0
- package/src/Modal/hooks/useCloseModal.ts +133 -0
- package/src/NotificationBadge/NotificationBadge.tsx +34 -0
- package/src/Pagination/AdvancedPagination.tsx +48 -0
- package/src/Pagination/Pagination.tsx +216 -0
- package/src/Pagination/components/AdvancedPaginator/AdvancedPaginator.tsx +52 -0
- package/src/Pagination/components/AdvancedPaginator/components/PageSelect/PageSelect.tsx +43 -0
- package/src/Pagination/components/ItemCountDisplay/ItemCountDisplay.tsx +32 -0
- package/src/Pagination/components/ItemCountSelect/ItemCountSelect.tsx +33 -0
- package/src/Pagination/index.tsx +3 -0
- package/src/Pagination/paginationProvider.tsx +234 -0
- package/src/Pagination/utils.tsx +36 -0
- package/src/PhoneInput/PhoneInput.tsx +509 -0
- package/src/PhoneInput/useDropdownLogic.tsx +163 -0
- package/src/ProgressCircle/ProgressCircle.tsx +246 -0
- package/src/ProgressCircle/hooks/useProgressStatusTransitions.ts +49 -0
- package/src/ProgressCircle/utils/progressCircle.constants.ts +27 -0
- package/src/ProgressSteps/ProgressSteps.tsx +160 -0
- package/src/SaveConfetti/SaveConfetti.tsx +277 -0
- package/src/SaveConfetti/index.ts +2 -0
- package/src/SearchInput/SearchInput.tsx +236 -0
- package/src/SegmentedControl/SegmentedControl.tsx +111 -0
- package/src/SegmentedControl/SegmentedControlGroup.tsx +94 -0
- package/src/SegmentedControl/index.ts +4 -0
- package/src/Select/Select.tsx +154 -0
- package/src/Select/SelectAsync.tsx +13 -0
- package/src/Select/SelectAsyncCreatable.tsx +14 -0
- package/src/Select/SelectCreatable.tsx +12 -0
- package/src/Select/reexportedComponents.tsx +5 -0
- package/src/Select/selectComponents.tsx +306 -0
- package/src/Skeleton/Skeleton.tsx +13 -0
- package/src/SplitButton/SplitButton.tsx +298 -0
- package/src/SplitButton/SplitButtonTrigger.tsx +55 -0
- package/src/StarRating/StarRating.tsx +165 -0
- package/src/StarRating/components/StarComponent/StarComponent.tsx +44 -0
- package/src/Table/Table.tsx +474 -0
- package/src/Table/components/TableHeader/TableHeader.tsx +166 -0
- package/src/Table/components/TablePagination/TablePagination.tsx +18 -0
- package/src/Table/components/TablePagination/components/ItemCountDisplay/ItemCountDisplay.tsx +17 -0
- package/src/Table/components/TablePagination/components/ItemCountSelect/ItemCountSelect.tsx +31 -0
- package/src/Table/components/TablePagination/components/TablePaginator/TablePaginator.tsx +37 -0
- package/src/Table/components/TablePagination/components/TablePaginator/components/PageSelect/PageSelect.tsx +32 -0
- package/src/Table/components/TableRow/TableRows.tsx +147 -0
- package/src/Table/components/TableRow/components/TableRowCell/TableRowCell.tsx +49 -0
- package/src/Table/components/TableShadow/TableShadow.tsx +46 -0
- package/src/Table/index.ts +2 -0
- package/src/Table/sharedState.ts +30 -0
- package/src/Table/sharedStyles.ts +1 -0
- package/src/Table/types.ts +55 -0
- package/src/Tabs/Tabs.tsx +358 -0
- package/src/TextArea/TextArea.tsx +157 -0
- package/src/TextInput/TextInput.tsx +173 -0
- package/src/Toast/Toast.tsx +64 -0
- package/src/Toast/components/ToastContainer/ToastContainer.tsx +48 -0
- package/src/Toast/index.ts +2 -0
- package/src/Toast/types/role.ts +1 -0
- package/src/Toast/utils/getIcon.tsx +41 -0
- package/src/ToggleSwitch/ToggleSwitch.tsx +188 -0
- package/src/Tooltip/InfoIconTooltip.tsx +47 -0
- package/src/Tooltip/Tooltip.tsx +41 -0
- package/src/Typography/InlineButton/InlineButton.tsx +166 -0
- package/src/Typography/Links/Links.tsx +113 -0
- package/src/Typography/Text/Text.tsx +239 -0
- package/src/ViewMore/ViewMore.tsx +226 -0
- package/src/WebRTCPlayer/WebRTCPlayer.tsx +325 -0
- package/src/WebRTCPlayer/adaptor/NativeWebRTCAdaptor.ts +588 -0
- package/src/WebRTCPlayer/hooks/useWebRTCStream.ts +571 -0
- package/src/WebRTCPlayer/index.ts +22 -0
- package/src/WebRTCPlayer/types.ts +95 -0
- package/src/breakpoints/index.tsx +1 -0
- package/src/breakpoints/utils.tsx +50 -0
- package/src/declarations.d.ts +7 -0
- package/src/grid/gridHighlighter.tsx +83 -0
- package/src/grid/index.ts +3 -0
- package/src/grid/pageGrid.tsx +37 -0
- package/src/grid/useSubGridCSS.tsx +214 -0
- package/src/hooks/useHorizontalScrollFade.ts +44 -0
- package/src/hooks/usePrevious.ts +14 -0
- package/src/index.ts +351 -0
- package/src/intl/index.ts +4 -0
- package/src/intl/utils.ts +67 -0
- package/src/mixins/horizontalScrollFadeMixin.ts +56 -0
- package/src/mixins/index.ts +18 -0
- package/src/mixins/propMixins.ts +389 -0
- package/src/providers/createContextProvider.ts +22 -0
- package/src/providers/designSystemProvider.tsx +218 -0
- package/src/providers/errorBoundaryProvider.tsx +179 -0
- package/src/providers/featureFlagProvider.tsx +18 -0
- package/src/providers/gridSizeProvider.tsx +23 -0
- package/src/providers/index.ts +12 -0
- package/src/providers/linkProvider.tsx +32 -0
- package/src/providers/normalize.tsx +93 -0
- package/src/providers/targets/DatePickerTarget.tsx +10 -0
- package/src/providers/targets/ModalTarget.tsx +14 -0
- package/src/providers/targets/TimePickerTarget.tsx +10 -0
- package/src/providers/themeContext.tsx +24 -0
- package/src/providers/windowSizeProvider.tsx +125 -0
- package/src/spacing/index.ts +1 -0
- package/src/spacing/spacing.ts +11 -0
- package/src/utils/countriesWithISOCodes.ts +1419 -0
- package/src/utils/dateTimeFormat.ts +257 -0
- package/src/utils/effectEventListener.ts +11 -0
- package/src/utils/formatFileSize.ts +18 -0
- package/src/utils/isClientSide.ts +1 -0
- package/src/utils/keepTransition.ts +15 -0
- package/src/utils/nodeEnv.ts +9 -0
- package/src/utils/smoothScrollTo.ts +47 -0
- package/src/utils/utilityTypes.ts +13 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { createPortal } from 'react-dom';
|
|
3
|
+
|
|
4
|
+
import type { Props, RefHandles, SpringEvent } from './types';
|
|
5
|
+
|
|
6
|
+
/* eslint-disable react/jsx-pascal-case */
|
|
7
|
+
import { BottomSheet as _BottomSheet } from './BottomSheet';
|
|
8
|
+
import { useLayoutEffect } from './hooks';
|
|
9
|
+
|
|
10
|
+
export type { RefHandles as BottomSheetRef, Props as BottomSheetProps } from './types';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* BottomSheet component with SSR support and proper animation lifecycle management
|
|
14
|
+
*/
|
|
15
|
+
export const BottomSheet = forwardRef<RefHandles, Props>(function BottomSheet(
|
|
16
|
+
{ onCloseEnd, onDismiss, onSpringEnd, onSpringStart, ...props },
|
|
17
|
+
ref
|
|
18
|
+
) {
|
|
19
|
+
// === Component State ===
|
|
20
|
+
// Mounted state helps with SSR and ensures accessibility (no tabbing/screen reader access when closed)
|
|
21
|
+
const [mounted, setMounted] = useState(false);
|
|
22
|
+
const [modalTarget, setModalTarget] = useState<HTMLElement | null>(null);
|
|
23
|
+
|
|
24
|
+
// === Animation State ===
|
|
25
|
+
// Internal state to manage animation lifecycle separate from the open prop
|
|
26
|
+
const [isAnimating, setIsAnimating] = useState(false);
|
|
27
|
+
// Track if we're in the middle of an opening animation
|
|
28
|
+
const [isOpening, setIsOpening] = useState(false);
|
|
29
|
+
// Internal state to handle initial delay for proper transitions
|
|
30
|
+
const [shouldShow, setShouldShow] = useState(false);
|
|
31
|
+
|
|
32
|
+
// === Refs for State Management ===
|
|
33
|
+
const timerRef = useRef<ReturnType<typeof requestAnimationFrame>>(0);
|
|
34
|
+
// The last snap point for remembering user-defined height
|
|
35
|
+
const lastSnapRef = useRef(0);
|
|
36
|
+
// Track if we should call onDismiss after close animation completes
|
|
37
|
+
const shouldCallOnDismiss = useRef(false);
|
|
38
|
+
// Track last action time to prevent rapid successive calls
|
|
39
|
+
const lastActionTime = useRef(0);
|
|
40
|
+
// Track if we're in a force-closed state to ignore all spring events
|
|
41
|
+
const isForceClosedRef = useRef(false);
|
|
42
|
+
|
|
43
|
+
// Initial state determines animation behavior on mount
|
|
44
|
+
const initialStateRef = useRef<'OPEN' | 'CLOSED'>('CLOSED');
|
|
45
|
+
|
|
46
|
+
// Find the modal target element
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
const target = document.querySelector('.hui-modal-target');
|
|
49
|
+
|
|
50
|
+
if (!target) {
|
|
51
|
+
throw new Error('Modal target not found, add one in the DOM tree.');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
setModalTarget(target as HTMLElement);
|
|
55
|
+
}, []);
|
|
56
|
+
|
|
57
|
+
// === Helper Functions ===
|
|
58
|
+
// Start the close transition (similar to Drawer pattern)
|
|
59
|
+
const startCloseTransition = useCallback((callOnDismiss: boolean = false) => {
|
|
60
|
+
setIsAnimating(true);
|
|
61
|
+
shouldCallOnDismiss.current = callOnDismiss;
|
|
62
|
+
}, []);
|
|
63
|
+
|
|
64
|
+
// Handle internal onDismiss that calls the original onDismiss immediately
|
|
65
|
+
// Memoized to prevent unnecessary re-renders
|
|
66
|
+
const handleInternalDismiss = useCallback(() => {
|
|
67
|
+
const now = Date.now();
|
|
68
|
+
const DEBOUNCE_MS = 150; // Increased debounce time for stress testing
|
|
69
|
+
|
|
70
|
+
// Prevent dismiss during opening animations or rapid successive calls
|
|
71
|
+
// Allow dismiss during closing animations (drag-to-close scenario)
|
|
72
|
+
if (isOpening || isAnimating || now - lastActionTime.current < DEBOUNCE_MS) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
lastActionTime.current = now;
|
|
77
|
+
isForceClosedRef.current = true;
|
|
78
|
+
|
|
79
|
+
onDismiss?.();
|
|
80
|
+
}, [onDismiss, isOpening, isAnimating]);
|
|
81
|
+
|
|
82
|
+
// === Effects ===
|
|
83
|
+
// Close the sheet when the open prop changes to false
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
if (mounted && !props.open && !isAnimating) {
|
|
86
|
+
startCloseTransition(false);
|
|
87
|
+
}
|
|
88
|
+
}, [mounted, props.open, isAnimating, startCloseTransition]);
|
|
89
|
+
|
|
90
|
+
// Handle mounting and opening state - supports cases where sheet appears already open
|
|
91
|
+
useLayoutEffect(() => {
|
|
92
|
+
if (props.open) {
|
|
93
|
+
// Prevent rapid re-mounting during stress testing
|
|
94
|
+
const now = Date.now();
|
|
95
|
+
if (now - lastActionTime.current < 50) {
|
|
96
|
+
return undefined; // Skip if too rapid
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
cancelAnimationFrame(timerRef.current);
|
|
100
|
+
setMounted(true);
|
|
101
|
+
// Reset all state when opening
|
|
102
|
+
setIsAnimating(false);
|
|
103
|
+
setIsOpening(true);
|
|
104
|
+
shouldCallOnDismiss.current = false;
|
|
105
|
+
lastActionTime.current = now;
|
|
106
|
+
isForceClosedRef.current = false;
|
|
107
|
+
|
|
108
|
+
// Add initial delay for proper transition
|
|
109
|
+
// Use requestAnimationFrame to ensure DOM is ready for transition
|
|
110
|
+
const delayId = requestAnimationFrame(() => {
|
|
111
|
+
// Double-check we're still supposed to be open
|
|
112
|
+
if (props.open) {
|
|
113
|
+
setShouldShow(true);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Cleanup function will clear the delay
|
|
118
|
+
return () => {
|
|
119
|
+
cancelAnimationFrame(delayId);
|
|
120
|
+
initialStateRef.current = 'CLOSED';
|
|
121
|
+
setShouldShow(false);
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Reset shouldShow when not open
|
|
126
|
+
setShouldShow(false);
|
|
127
|
+
return undefined;
|
|
128
|
+
}, [props.open]);
|
|
129
|
+
|
|
130
|
+
const handleSpringStart = useCallback(
|
|
131
|
+
async function handleSpringStart(event: SpringEvent) {
|
|
132
|
+
// Only process events for open component or CLOSE events
|
|
133
|
+
if (!props.open && event.type !== 'CLOSE') {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
await onSpringStart?.(event);
|
|
138
|
+
|
|
139
|
+
if (event.type === 'OPEN') {
|
|
140
|
+
// Cancel any pending unmount when opening
|
|
141
|
+
cancelAnimationFrame(timerRef.current);
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
[onSpringStart, props.open]
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const handleSpringEnd = useCallback(
|
|
148
|
+
async function handleSpringEnd(event: SpringEvent) {
|
|
149
|
+
// Only process CLOSE events when force-closed, or any event when open
|
|
150
|
+
const shouldIgnore = (isForceClosedRef.current || !props.open) && event.type !== 'CLOSE';
|
|
151
|
+
if (shouldIgnore) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
await onSpringEnd?.(event);
|
|
156
|
+
|
|
157
|
+
if (event.type === 'OPEN') {
|
|
158
|
+
setIsOpening(false);
|
|
159
|
+
} else if (event.type === 'CLOSE') {
|
|
160
|
+
// Reset all animation state and unmount
|
|
161
|
+
setIsAnimating(false);
|
|
162
|
+
setIsOpening(false);
|
|
163
|
+
shouldCallOnDismiss.current = false;
|
|
164
|
+
timerRef.current = requestAnimationFrame(() => setMounted(false));
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
[onSpringEnd, props.open]
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
// Fallback unmounting - ensures component unmounts even if spring callback fails
|
|
171
|
+
useEffect(() => {
|
|
172
|
+
if (!props.open && mounted) {
|
|
173
|
+
const fallbackTimeoutId = setTimeout(() => {
|
|
174
|
+
// Only unmount if we're still in the closed state after timeout
|
|
175
|
+
if (!props.open) {
|
|
176
|
+
cancelAnimationFrame(timerRef.current);
|
|
177
|
+
setMounted(false);
|
|
178
|
+
setIsAnimating(false);
|
|
179
|
+
shouldCallOnDismiss.current = false;
|
|
180
|
+
isForceClosedRef.current = true;
|
|
181
|
+
}
|
|
182
|
+
}, 500);
|
|
183
|
+
|
|
184
|
+
return () => clearTimeout(fallbackTimeoutId);
|
|
185
|
+
}
|
|
186
|
+
return undefined;
|
|
187
|
+
}, [props.open, mounted]);
|
|
188
|
+
|
|
189
|
+
// Cleanup effect - let React handle DOM cleanup naturally
|
|
190
|
+
useEffect(() => {
|
|
191
|
+
return () => {
|
|
192
|
+
// Minimal cleanup - only clear empty modal targets to prevent memory leaks
|
|
193
|
+
// Let React handle the component unmounting naturally
|
|
194
|
+
setTimeout(() => {
|
|
195
|
+
try {
|
|
196
|
+
const modalTarget = document.querySelector('.hui-modal-target');
|
|
197
|
+
// Only clean if modal target is completely empty and we're sure React is done
|
|
198
|
+
if (modalTarget && modalTarget.children.length === 0) {
|
|
199
|
+
modalTarget.innerHTML = '';
|
|
200
|
+
}
|
|
201
|
+
} catch {
|
|
202
|
+
// Ignore cleanup errors during rapid state changes
|
|
203
|
+
}
|
|
204
|
+
}, 200); // Longer delay to avoid race conditions with React
|
|
205
|
+
};
|
|
206
|
+
}, []); // Only run on component unmount
|
|
207
|
+
|
|
208
|
+
// === Early Return for SSR and Unmounted State ===
|
|
209
|
+
if (!mounted || !modalTarget) {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// === Render ===
|
|
214
|
+
// Extract onDismiss from props to avoid conflicts with internal handler
|
|
215
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
216
|
+
const { onDismiss: _, ...bottomSheetProps } = props as Props & { onDismiss?: () => void };
|
|
217
|
+
|
|
218
|
+
return createPortal(
|
|
219
|
+
<div
|
|
220
|
+
aria-hidden={!props.open}
|
|
221
|
+
role="presentation"
|
|
222
|
+
>
|
|
223
|
+
<_BottomSheet
|
|
224
|
+
{...bottomSheetProps}
|
|
225
|
+
initialState={initialStateRef.current}
|
|
226
|
+
lastSnapRef={lastSnapRef}
|
|
227
|
+
onCloseEnd={onCloseEnd}
|
|
228
|
+
onDismiss={handleInternalDismiss}
|
|
229
|
+
onSpringEnd={handleSpringEnd}
|
|
230
|
+
onSpringStart={handleSpringStart}
|
|
231
|
+
open={shouldShow}
|
|
232
|
+
ref={ref}
|
|
233
|
+
/>
|
|
234
|
+
</div>,
|
|
235
|
+
modalTarget
|
|
236
|
+
);
|
|
237
|
+
});
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { atom } from 'jotai';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Bottom sheet state types
|
|
5
|
+
*/
|
|
6
|
+
export type BottomSheetState = 'closed' | 'opening' | 'open' | 'closing' | 'dragging' | 'snapping' | 'resizing';
|
|
7
|
+
|
|
8
|
+
export type BottomSheetEvent =
|
|
9
|
+
| { type: 'OPEN' }
|
|
10
|
+
| { type: 'CLOSE' }
|
|
11
|
+
| { type: 'DRAG' }
|
|
12
|
+
| { type: 'RESIZE' }
|
|
13
|
+
| {
|
|
14
|
+
payload: {
|
|
15
|
+
source: 'dragging' | 'custom' | string;
|
|
16
|
+
velocity: number;
|
|
17
|
+
y: number;
|
|
18
|
+
};
|
|
19
|
+
type: 'SNAP';
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** The payload shape carried by SNAP events. */
|
|
23
|
+
export type SnapPayload = Extract<BottomSheetEvent, { type: 'SNAP' }>['payload'];
|
|
24
|
+
|
|
25
|
+
export type BottomSheetContext = {
|
|
26
|
+
initialState: 'OPEN' | 'CLOSED';
|
|
27
|
+
snapSource?: 'dragging' | 'custom' | string;
|
|
28
|
+
velocity?: number;
|
|
29
|
+
y?: number;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Core state atoms
|
|
34
|
+
*/
|
|
35
|
+
export const bottomSheetStateAtom = atom<BottomSheetState>('closed');
|
|
36
|
+
export const bottomSheetContextAtom = atom<BottomSheetContext>({ initialState: 'CLOSED' });
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Derived state atoms
|
|
40
|
+
*/
|
|
41
|
+
export const isOpenAtom = atom((get) => {
|
|
42
|
+
const state = get(bottomSheetStateAtom);
|
|
43
|
+
return (
|
|
44
|
+
state === 'open' || state === 'opening' || state === 'dragging' || state === 'snapping' || state === 'resizing'
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export const isClosedAtom = atom((get) => {
|
|
49
|
+
const state = get(bottomSheetStateAtom);
|
|
50
|
+
return state === 'closed';
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export const isTransitioningAtom = atom((get) => {
|
|
54
|
+
const state = get(bottomSheetStateAtom);
|
|
55
|
+
return state === 'opening' || state === 'closing' || state === 'snapping' || state === 'resizing';
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export const isDraggingAtom = atom((get) => {
|
|
59
|
+
const state = get(bottomSheetStateAtom);
|
|
60
|
+
return state === 'dragging';
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Action atoms for state transitions
|
|
65
|
+
*/
|
|
66
|
+
export const sendEventAtom = atom(null, (get, set, event: BottomSheetEvent) => {
|
|
67
|
+
const currentState = get(bottomSheetStateAtom);
|
|
68
|
+
const context = get(bottomSheetContextAtom);
|
|
69
|
+
|
|
70
|
+
// Add guards to prevent rapid state changes
|
|
71
|
+
switch (event.type) {
|
|
72
|
+
case 'OPEN':
|
|
73
|
+
if (currentState === 'closed' || currentState === 'closing') {
|
|
74
|
+
set(bottomSheetStateAtom, 'opening');
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
|
|
78
|
+
case 'CLOSE':
|
|
79
|
+
if (currentState !== 'closed' && currentState !== 'closing') {
|
|
80
|
+
set(bottomSheetStateAtom, 'closing');
|
|
81
|
+
}
|
|
82
|
+
break;
|
|
83
|
+
|
|
84
|
+
case 'DRAG':
|
|
85
|
+
// Only allow drag if not already in a transitional state
|
|
86
|
+
if (
|
|
87
|
+
currentState === 'open' ||
|
|
88
|
+
(currentState === 'opening' && context.initialState === 'OPEN') ||
|
|
89
|
+
currentState === 'snapping' ||
|
|
90
|
+
currentState === 'resizing'
|
|
91
|
+
) {
|
|
92
|
+
set(bottomSheetStateAtom, 'dragging');
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
|
|
96
|
+
case 'SNAP':
|
|
97
|
+
// Only allow snap from dragging or stable open state
|
|
98
|
+
if (currentState === 'dragging' || currentState === 'open') {
|
|
99
|
+
// Batch the context and state updates to prevent multiple renders
|
|
100
|
+
const newContext = {
|
|
101
|
+
...context,
|
|
102
|
+
snapSource: event.payload.source,
|
|
103
|
+
velocity: event.payload.velocity,
|
|
104
|
+
y: event.payload.y,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
set(bottomSheetContextAtom, newContext);
|
|
108
|
+
set(bottomSheetStateAtom, 'snapping');
|
|
109
|
+
}
|
|
110
|
+
break;
|
|
111
|
+
|
|
112
|
+
case 'RESIZE':
|
|
113
|
+
// Only allow resize from stable states
|
|
114
|
+
if (currentState === 'open') {
|
|
115
|
+
set(bottomSheetStateAtom, 'resizing');
|
|
116
|
+
}
|
|
117
|
+
break;
|
|
118
|
+
|
|
119
|
+
default:
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Transition completion atoms
|
|
126
|
+
*/
|
|
127
|
+
export const completeTransitionAtom = atom(null, (get, set, targetState: BottomSheetState) => {
|
|
128
|
+
const currentState = get(bottomSheetStateAtom);
|
|
129
|
+
|
|
130
|
+
// Only complete transitions if we're currently in a transitioning state
|
|
131
|
+
if (get(isTransitioningAtom) || currentState === 'dragging') {
|
|
132
|
+
set(bottomSheetStateAtom, targetState);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Reset atom for cleanup
|
|
138
|
+
*/
|
|
139
|
+
export const resetStateAtom = atom(null, (get, set, initialState: 'OPEN' | 'CLOSED' = 'CLOSED') => {
|
|
140
|
+
set(bottomSheetStateAtom, initialState === 'OPEN' ? 'open' : 'closed');
|
|
141
|
+
set(bottomSheetContextAtom, {
|
|
142
|
+
initialState,
|
|
143
|
+
snapSource: undefined,
|
|
144
|
+
velocity: undefined,
|
|
145
|
+
y: undefined,
|
|
146
|
+
});
|
|
147
|
+
});
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
|
|
2
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
type BottomSheetContext,
|
|
6
|
+
bottomSheetContextAtom,
|
|
7
|
+
type BottomSheetEvent,
|
|
8
|
+
type BottomSheetState,
|
|
9
|
+
bottomSheetStateAtom,
|
|
10
|
+
completeTransitionAtom,
|
|
11
|
+
isClosedAtom,
|
|
12
|
+
isDraggingAtom,
|
|
13
|
+
isOpenAtom,
|
|
14
|
+
isTransitioningAtom,
|
|
15
|
+
resetStateAtom,
|
|
16
|
+
sendEventAtom,
|
|
17
|
+
type SnapPayload,
|
|
18
|
+
} from './atoms';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Services interface matching the XState pattern
|
|
22
|
+
*/
|
|
23
|
+
export interface BottomSheetServices {
|
|
24
|
+
activate: () => Promise<void>;
|
|
25
|
+
closeSmoothly: () => Promise<void>;
|
|
26
|
+
deactivate: () => Promise<void>;
|
|
27
|
+
onCloseEnd: () => Promise<void>;
|
|
28
|
+
onCloseStart: () => Promise<void>;
|
|
29
|
+
onOpenEnd: () => Promise<void>;
|
|
30
|
+
onOpenStart: () => Promise<void>;
|
|
31
|
+
onResizeEnd: () => Promise<void>;
|
|
32
|
+
onResizeStart: () => Promise<void>;
|
|
33
|
+
onSnapEnd: (context: BottomSheetContext) => Promise<void>;
|
|
34
|
+
onSnapStart: (context: BottomSheetContext) => Promise<void>;
|
|
35
|
+
openImmediately: () => Promise<void>;
|
|
36
|
+
openSmoothly: () => Promise<void>;
|
|
37
|
+
renderVisuallyHidden: () => Promise<void>;
|
|
38
|
+
resizeSmoothly: () => Promise<void>;
|
|
39
|
+
snapSmoothly: (context: BottomSheetContext) => Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Actions interface matching the XState pattern
|
|
44
|
+
*/
|
|
45
|
+
export interface BottomSheetActions {
|
|
46
|
+
onCloseCancel: () => void;
|
|
47
|
+
onCloseEnd: () => void;
|
|
48
|
+
onOpenCancel: () => void;
|
|
49
|
+
onOpenEnd: () => void;
|
|
50
|
+
onResizeCancel: () => void;
|
|
51
|
+
onResizeEnd: () => void;
|
|
52
|
+
onSnapCancel: (context: BottomSheetContext) => void;
|
|
53
|
+
onSnapEnd: (context: BottomSheetContext) => void;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Hook that provides bottom sheet state machine functionality using Jotai
|
|
58
|
+
*/
|
|
59
|
+
export const useBottomSheetStateMachine = (
|
|
60
|
+
services: BottomSheetServices,
|
|
61
|
+
actions: BottomSheetActions,
|
|
62
|
+
initialContext: BottomSheetContext
|
|
63
|
+
) => {
|
|
64
|
+
const [state] = useAtom(bottomSheetStateAtom);
|
|
65
|
+
const [context, setContext] = useAtom(bottomSheetContextAtom);
|
|
66
|
+
const sendEvent = useSetAtom(sendEventAtom);
|
|
67
|
+
const completeTransition = useSetAtom(completeTransitionAtom);
|
|
68
|
+
const resetState = useSetAtom(resetStateAtom);
|
|
69
|
+
|
|
70
|
+
const isOpen = useAtomValue(isOpenAtom);
|
|
71
|
+
const isClosed = useAtomValue(isClosedAtom);
|
|
72
|
+
const isTransitioning = useAtomValue(isTransitioningAtom);
|
|
73
|
+
const isDragging = useAtomValue(isDraggingAtom);
|
|
74
|
+
|
|
75
|
+
// Track previous state for transition logic
|
|
76
|
+
const prevStateRef = useRef<BottomSheetState>(state);
|
|
77
|
+
const servicesRef = useRef(services);
|
|
78
|
+
const actionsRef = useRef(actions);
|
|
79
|
+
|
|
80
|
+
// Update refs when services/actions change
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
servicesRef.current = services;
|
|
83
|
+
actionsRef.current = actions;
|
|
84
|
+
}, [services, actions]);
|
|
85
|
+
|
|
86
|
+
// Initialize context only once on mount
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
setContext(initialContext);
|
|
89
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
90
|
+
}, [setContext]); // Only depend on setContext, not initialContext to avoid infinite loops
|
|
91
|
+
|
|
92
|
+
// Handle state transitions and side effects
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
const currentServices = servicesRef.current;
|
|
95
|
+
const currentActions = actionsRef.current;
|
|
96
|
+
const prevState = prevStateRef.current;
|
|
97
|
+
const currentContext = context; // Capture context at the time of effect
|
|
98
|
+
|
|
99
|
+
const handleStateTransition = async () => {
|
|
100
|
+
try {
|
|
101
|
+
switch (state) {
|
|
102
|
+
case 'opening':
|
|
103
|
+
if (prevState === 'closed') {
|
|
104
|
+
await currentServices.onOpenStart();
|
|
105
|
+
|
|
106
|
+
if (currentContext.initialState === 'OPEN') {
|
|
107
|
+
await currentServices.renderVisuallyHidden();
|
|
108
|
+
await currentServices.activate();
|
|
109
|
+
await currentServices.openImmediately();
|
|
110
|
+
} else {
|
|
111
|
+
await currentServices.renderVisuallyHidden();
|
|
112
|
+
await currentServices.activate();
|
|
113
|
+
await currentServices.openSmoothly();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
await currentServices.onOpenEnd();
|
|
117
|
+
completeTransition('open');
|
|
118
|
+
} else if (prevState === 'closing') {
|
|
119
|
+
// Cancelled close, resume opening
|
|
120
|
+
currentActions.onCloseCancel();
|
|
121
|
+
await currentServices.onOpenStart();
|
|
122
|
+
await currentServices.renderVisuallyHidden();
|
|
123
|
+
await currentServices.activate();
|
|
124
|
+
await currentServices.openSmoothly();
|
|
125
|
+
await currentServices.onOpenEnd();
|
|
126
|
+
completeTransition('open');
|
|
127
|
+
}
|
|
128
|
+
break;
|
|
129
|
+
|
|
130
|
+
case 'closing':
|
|
131
|
+
if (prevState !== 'closed') {
|
|
132
|
+
if (prevState === 'opening') {
|
|
133
|
+
currentActions.onOpenCancel();
|
|
134
|
+
} else if (prevState === 'resizing') {
|
|
135
|
+
currentActions.onResizeCancel();
|
|
136
|
+
} else if (prevState === 'snapping') {
|
|
137
|
+
currentActions.onSnapCancel(currentContext);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
await currentServices.onCloseStart();
|
|
141
|
+
await currentServices.deactivate();
|
|
142
|
+
await currentServices.closeSmoothly();
|
|
143
|
+
await currentServices.onCloseEnd();
|
|
144
|
+
currentActions.onCloseEnd();
|
|
145
|
+
completeTransition('closed');
|
|
146
|
+
}
|
|
147
|
+
break;
|
|
148
|
+
|
|
149
|
+
case 'resizing':
|
|
150
|
+
if (prevState === 'open' || prevState === 'opening') {
|
|
151
|
+
await currentServices.onResizeStart();
|
|
152
|
+
await currentServices.resizeSmoothly();
|
|
153
|
+
await currentServices.onResizeEnd();
|
|
154
|
+
completeTransition('open');
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
157
|
+
|
|
158
|
+
case 'snapping':
|
|
159
|
+
// 'dragging' → snap on release; 'open' → the imperative snapTo API.
|
|
160
|
+
// (Upstream only serviced the dragging path, so snapTo from a resting
|
|
161
|
+
// sheet entered 'snapping' and stalled without ever animating.)
|
|
162
|
+
if (prevState === 'dragging' || prevState === 'open') {
|
|
163
|
+
await currentServices.onSnapStart(currentContext);
|
|
164
|
+
await currentServices.snapSmoothly(currentContext);
|
|
165
|
+
await currentServices.onSnapEnd(currentContext);
|
|
166
|
+
completeTransition('open');
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
169
|
+
|
|
170
|
+
case 'dragging':
|
|
171
|
+
// Dragging is handled by gesture handlers, no async operations needed
|
|
172
|
+
if (prevState === 'opening') {
|
|
173
|
+
currentActions.onOpenEnd();
|
|
174
|
+
} else if (prevState === 'closing') {
|
|
175
|
+
currentActions.onCloseCancel();
|
|
176
|
+
} else if (prevState === 'resizing') {
|
|
177
|
+
currentActions.onResizeCancel();
|
|
178
|
+
} else if (prevState === 'snapping') {
|
|
179
|
+
currentActions.onSnapCancel(currentContext);
|
|
180
|
+
}
|
|
181
|
+
break;
|
|
182
|
+
case 'closed':
|
|
183
|
+
case 'open':
|
|
184
|
+
default:
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
} catch (error) {
|
|
188
|
+
// eslint-disable-next-line no-console
|
|
189
|
+
console.error('Bottom sheet state transition error:', error);
|
|
190
|
+
// On error, try to recover to a stable state
|
|
191
|
+
if (state === 'opening' || state === 'closing' || state === 'resizing' || state === 'snapping') {
|
|
192
|
+
completeTransition(state === 'closing' ? 'closed' : 'open');
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
if (prevState !== state) {
|
|
198
|
+
handleStateTransition();
|
|
199
|
+
prevStateRef.current = state;
|
|
200
|
+
}
|
|
201
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
202
|
+
}, [state, completeTransition]); // Removed context from dependencies
|
|
203
|
+
|
|
204
|
+
// Send event function with proper typing
|
|
205
|
+
const send = useCallback(
|
|
206
|
+
(type: BottomSheetEvent['type'], payload?: SnapPayload) => {
|
|
207
|
+
if (type === 'SNAP' && payload) {
|
|
208
|
+
sendEvent({ payload, type: 'SNAP' });
|
|
209
|
+
} else {
|
|
210
|
+
sendEvent({ type } as BottomSheetEvent);
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
[sendEvent]
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
// Reset function for cleanup
|
|
217
|
+
const reset = useCallback(
|
|
218
|
+
(initialState: 'OPEN' | 'CLOSED' = 'CLOSED') => {
|
|
219
|
+
resetState(initialState);
|
|
220
|
+
prevStateRef.current = initialState === 'OPEN' ? 'open' : 'closed';
|
|
221
|
+
},
|
|
222
|
+
[resetState]
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
// Current state
|
|
227
|
+
current: {
|
|
228
|
+
context,
|
|
229
|
+
matches: (stateName: string) => state === stateName,
|
|
230
|
+
value: state,
|
|
231
|
+
},
|
|
232
|
+
isClosed,
|
|
233
|
+
isDragging,
|
|
234
|
+
|
|
235
|
+
// Derived state
|
|
236
|
+
isOpen,
|
|
237
|
+
isTransitioning,
|
|
238
|
+
reset,
|
|
239
|
+
|
|
240
|
+
// Actions
|
|
241
|
+
send,
|
|
242
|
+
|
|
243
|
+
// State value for easy access
|
|
244
|
+
state,
|
|
245
|
+
};
|
|
246
|
+
};
|