@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,395 @@
|
|
|
1
|
+
import { cn } from '@evolve.ui/theme-system';
|
|
2
|
+
import { HTMLProps, MouseEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import { useHorizontalScrollFadeMixin } from '../mixins/horizontalScrollFadeMixin';
|
|
5
|
+
import { useWindowIsMobile } from '../providers/windowSizeProvider';
|
|
6
|
+
import { Caption } from '../Typography/Text/Text';
|
|
7
|
+
import { isClientSide } from '../utils/isClientSide';
|
|
8
|
+
import { JumpMenuItem } from './components/JumpMenuItem/JumpMenuItem';
|
|
9
|
+
import { useContainerTracking } from './hooks/useContainerTracking';
|
|
10
|
+
import { useKeyboardNavigation } from './hooks/useKeyboardNavigation';
|
|
11
|
+
import { useSectionObserver } from './hooks/useSectionObserver';
|
|
12
|
+
import { JumpMenuItem as JumpMenuItemType } from './types';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Props for the JumpMenu component
|
|
16
|
+
*/
|
|
17
|
+
type Props = HTMLProps<HTMLElement> & {
|
|
18
|
+
/** Custom aria-label for the navigation (defaults to 'Jump to section') */
|
|
19
|
+
ariaLabel?: string;
|
|
20
|
+
/** Additional CSS classes for the menu container */
|
|
21
|
+
className?: string;
|
|
22
|
+
/** CSS selector for the container element that defines the scope of fixed positioning behavior */
|
|
23
|
+
containerSelector?: string;
|
|
24
|
+
/** Array of menu items to display in the jump navigation */
|
|
25
|
+
items: JumpMenuItemType[];
|
|
26
|
+
/** Callback when active section changes via scroll or user interaction */
|
|
27
|
+
onActiveChange?: (activeId: string | null) => void;
|
|
28
|
+
/** Additional CSS classes for the outer container of the menu */
|
|
29
|
+
outerContainerClassName?: string;
|
|
30
|
+
/** Scroll offset in pixels to account for sticky headers when navigating to sections (defaults to 0) */
|
|
31
|
+
scrollOffset?: number;
|
|
32
|
+
/** Scroll offset in pixels for mobile viewports (overrides scrollOffset on mobile, defaults to scrollOffset value) */
|
|
33
|
+
scrollOffsetMobile?: number;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* JumpMenu component for horizontal scrollable navigation between page sections
|
|
38
|
+
*
|
|
39
|
+
* Features:
|
|
40
|
+
* - Horizontal scrolling with fade indicators on overflow
|
|
41
|
+
* - Automatic section highlighting based on scroll position
|
|
42
|
+
* - Keyboard navigation (Arrow keys, Home, End)
|
|
43
|
+
* - Smooth scrolling to target sections
|
|
44
|
+
* - Accessibility compliant with ARIA labels and roles
|
|
45
|
+
* - Fixed positioning with container visibility tracking
|
|
46
|
+
*
|
|
47
|
+
* @param ariaLabel - Custom aria-label for the navigation (defaults to 'Jump to section')
|
|
48
|
+
* @param className - Additional CSS classes for the menu container
|
|
49
|
+
* @param containerSelector - CSS selector for the container element that defines the scope of fixed positioning behavior
|
|
50
|
+
* @param items - Array of menu items to display - each item should have an id and either target or href
|
|
51
|
+
* @param onActiveChange - Callback when active section changes via scroll or user interaction
|
|
52
|
+
* @param outerContainerClassName - Additional CSS classes for the outer container of the menu
|
|
53
|
+
* @param scrollOffset - Scroll offset in pixels to account for sticky headers when navigating to sections (defaults to 0)
|
|
54
|
+
* @param scrollOffsetMobile - Scroll offset in pixels for mobile viewports (overrides scrollOffset on mobile, defaults to scrollOffset value)
|
|
55
|
+
*
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```tsx
|
|
59
|
+
* const menuItems = [
|
|
60
|
+
* { id: 'overview', label: 'Overview', href: '#overview-section' },
|
|
61
|
+
* { id: 'details', label: 'Details', href: '#details-section' },
|
|
62
|
+
* ];
|
|
63
|
+
*
|
|
64
|
+
* <div id="container">
|
|
65
|
+
* <JumpMenu
|
|
66
|
+
* items={menuItems}
|
|
67
|
+
* containerSelector="#container"
|
|
68
|
+
* onActiveChange={(activeId) => console.log('Active section:', activeId)}
|
|
69
|
+
* scrollOffset={80} // Optional: Account for 80px sticky header
|
|
70
|
+
* scrollOffsetMobile={120} // Optional: Account for larger mobile header
|
|
71
|
+
* />
|
|
72
|
+
* <section id="overview-section">Overview content</section>
|
|
73
|
+
* <section id="details-section">Details content</section>
|
|
74
|
+
* </div>
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export const JumpMenu = ({
|
|
78
|
+
ariaLabel = 'Jump to section',
|
|
79
|
+
className,
|
|
80
|
+
containerSelector,
|
|
81
|
+
items,
|
|
82
|
+
onActiveChange,
|
|
83
|
+
outerContainerClassName = '',
|
|
84
|
+
scrollOffset = 0,
|
|
85
|
+
scrollOffsetMobile,
|
|
86
|
+
...htmlProps
|
|
87
|
+
}: Props) => {
|
|
88
|
+
// Track user-initiated actions with timestamp to ignore subsequent intersection observer events
|
|
89
|
+
const userActionIgnoreDuration = 1000;
|
|
90
|
+
const lastUserActionRef = useRef<number>(0);
|
|
91
|
+
|
|
92
|
+
const [activeItemId, setActiveItemId] = useState<string | null>(
|
|
93
|
+
items.find((item) => item.isActive)?.id || items[0]?.id || null
|
|
94
|
+
);
|
|
95
|
+
const menuRef = useRef<HTMLDivElement>(null);
|
|
96
|
+
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
|
97
|
+
const horizontalScrollFadeProps = useHorizontalScrollFadeMixin(0);
|
|
98
|
+
const [navBackgroundColor, setNavBackgroundColor] = useState<string>('rgb(255, 255, 255)');
|
|
99
|
+
|
|
100
|
+
// Use the container tracking hook for visibility and width management
|
|
101
|
+
const { isContainerVisible, menuHeight, menuWidth } = useContainerTracking({
|
|
102
|
+
containerSelector,
|
|
103
|
+
menuRef,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Determine if container top has passed viewport top (need to check position separately from visibility)
|
|
107
|
+
const [containerTopPassed, setContainerTopPassed] = useState(false);
|
|
108
|
+
|
|
109
|
+
// Track pending scroll when clicking item before menu becomes fixed
|
|
110
|
+
const pendingScrollItemRef = useRef<string | null>(null);
|
|
111
|
+
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
if (!containerSelector || !isClientSide) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const containerElement = document.querySelector(containerSelector);
|
|
118
|
+
if (!containerElement) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const checkPosition = () => {
|
|
123
|
+
const rect = containerElement.getBoundingClientRect();
|
|
124
|
+
setContainerTopPassed(rect.top <= 0);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
checkPosition();
|
|
128
|
+
window.addEventListener('scroll', checkPosition, { passive: true });
|
|
129
|
+
return () => window.removeEventListener('scroll', checkPosition);
|
|
130
|
+
}, [containerSelector]);
|
|
131
|
+
|
|
132
|
+
const isMobile = useWindowIsMobile();
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Gets the appropriate scroll offset based on viewport size
|
|
136
|
+
* @returns The scroll offset to use for the current viewport
|
|
137
|
+
*/
|
|
138
|
+
const getScrollOffset = useCallback(() => {
|
|
139
|
+
return isMobile && scrollOffsetMobile !== undefined ? scrollOffsetMobile : scrollOffset;
|
|
140
|
+
}, [scrollOffset, scrollOffsetMobile, isMobile]);
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Scrolls the specified menu item into view within the horizontal scroll container
|
|
144
|
+
* Only scrolls when menu is in fixed position to prevent viewport shifts
|
|
145
|
+
* @param itemId - The ID of the menu item to scroll into view
|
|
146
|
+
* @param inline - Scroll alignment: 'center' shows neighbors, 'nearest' minimal scroll
|
|
147
|
+
*/
|
|
148
|
+
const scrollActiveItemIntoView = useCallback((itemId: string, inline: 'center' | 'nearest' = 'nearest') => {
|
|
149
|
+
const activeElement = scrollContainerRef.current?.querySelector(
|
|
150
|
+
`[data-item-id="${itemId}"]`
|
|
151
|
+
) as HTMLElement | null;
|
|
152
|
+
|
|
153
|
+
if (activeElement) {
|
|
154
|
+
activeElement.scrollIntoView({
|
|
155
|
+
behavior: 'smooth',
|
|
156
|
+
block: 'nearest',
|
|
157
|
+
inline,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}, []);
|
|
161
|
+
|
|
162
|
+
// When menu becomes fixed and we have a pending scroll, execute it
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
if (containerTopPassed && pendingScrollItemRef.current) {
|
|
165
|
+
scrollActiveItemIntoView(pendingScrollItemRef.current, 'center');
|
|
166
|
+
pendingScrollItemRef.current = null;
|
|
167
|
+
}
|
|
168
|
+
}, [containerTopPassed, scrollActiveItemIntoView]);
|
|
169
|
+
|
|
170
|
+
// Update active item when external state changes
|
|
171
|
+
useEffect(() => {
|
|
172
|
+
const externalActiveItem = items.find((item) => item.isActive);
|
|
173
|
+
if (externalActiveItem && externalActiveItem.id !== activeItemId) {
|
|
174
|
+
setActiveItemId(externalActiveItem.id);
|
|
175
|
+
}
|
|
176
|
+
}, [items, activeItemId]);
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Handles active section changes from the intersection observer
|
|
180
|
+
* Ignores updates that occur shortly after user interactions to prevent conflicts
|
|
181
|
+
* @param sectionId - The ID of the section that became active
|
|
182
|
+
*/
|
|
183
|
+
const handleActiveChange = useCallback(
|
|
184
|
+
(sectionId: string) => {
|
|
185
|
+
// Skip if this is a scroll-based update and we recently had a user action
|
|
186
|
+
const timeSinceLastUserAction = Date.now() - lastUserActionRef.current;
|
|
187
|
+
if (timeSinceLastUserAction < userActionIgnoreDuration) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (sectionId !== activeItemId) {
|
|
192
|
+
setActiveItemId(sectionId);
|
|
193
|
+
onActiveChange?.(sectionId);
|
|
194
|
+
// Auto-scroll with 'center' to show neighboring items (only when fixed)
|
|
195
|
+
if (containerTopPassed) {
|
|
196
|
+
scrollActiveItemIntoView(sectionId, 'center');
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
[activeItemId, onActiveChange, userActionIgnoreDuration, scrollActiveItemIntoView, containerTopPassed]
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
// Prepare sections for the observer
|
|
204
|
+
const sections = useMemo(
|
|
205
|
+
() =>
|
|
206
|
+
items
|
|
207
|
+
.filter((item) => item.target || item.href)
|
|
208
|
+
.map((item) => ({
|
|
209
|
+
id: item.id,
|
|
210
|
+
selector: item.target ? `#${item.target}` : item.href!,
|
|
211
|
+
})),
|
|
212
|
+
[items]
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
// Use modern IntersectionObserver for section detection
|
|
216
|
+
useSectionObserver(sections, handleActiveChange);
|
|
217
|
+
|
|
218
|
+
// Handle keyboard navigation with the dedicated hook
|
|
219
|
+
const { handleKeyDown } = useKeyboardNavigation({
|
|
220
|
+
activeItemId,
|
|
221
|
+
containerRef: menuRef,
|
|
222
|
+
getItemId: (item: JumpMenuItemType) => item.id,
|
|
223
|
+
items,
|
|
224
|
+
lastUserActionRef,
|
|
225
|
+
onActiveChange: (itemId: string) => {
|
|
226
|
+
setActiveItemId(itemId);
|
|
227
|
+
onActiveChange?.(itemId);
|
|
228
|
+
},
|
|
229
|
+
scrollItemIntoView: scrollActiveItemIntoView,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Handles menu item click events
|
|
234
|
+
* Updates active state, calls custom onClick handler, and navigates to target section
|
|
235
|
+
* @param item - The clicked menu item
|
|
236
|
+
* @param event - The mouse event
|
|
237
|
+
*/
|
|
238
|
+
const handleItemClick = useCallback(
|
|
239
|
+
(item: JumpMenuItemType, event: MouseEvent) => {
|
|
240
|
+
// Mark this as a user-initiated action
|
|
241
|
+
lastUserActionRef.current = Date.now();
|
|
242
|
+
|
|
243
|
+
// Scroll the clicked menu item into view FIRST, using 'center' to show neighbors
|
|
244
|
+
// Only scroll horizontally when menu is in fixed position to prevent viewport shifts
|
|
245
|
+
if (containerTopPassed) {
|
|
246
|
+
scrollActiveItemIntoView(item.id, 'center');
|
|
247
|
+
} else {
|
|
248
|
+
// Store pending scroll to execute once menu becomes fixed
|
|
249
|
+
pendingScrollItemRef.current = item.id;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
setActiveItemId(item.id);
|
|
253
|
+
onActiveChange?.(item.id);
|
|
254
|
+
item.onClick?.(item.id);
|
|
255
|
+
|
|
256
|
+
// Navigate to section if target or href is provided
|
|
257
|
+
const targetSelector = item.target ? `#${item.target}` : item.href;
|
|
258
|
+
|
|
259
|
+
if (targetSelector) {
|
|
260
|
+
// Always prevent default since we're using buttons now
|
|
261
|
+
event.preventDefault();
|
|
262
|
+
|
|
263
|
+
// Handle navigation manually
|
|
264
|
+
const targetElement = document.querySelector(targetSelector);
|
|
265
|
+
if (targetElement) {
|
|
266
|
+
const offset = getScrollOffset();
|
|
267
|
+
|
|
268
|
+
if (offset > 0) {
|
|
269
|
+
// Calculate position with offset
|
|
270
|
+
const elementPosition = targetElement.getBoundingClientRect().top + window.pageYOffset;
|
|
271
|
+
const offsetPosition = elementPosition - offset;
|
|
272
|
+
|
|
273
|
+
window.scrollTo({
|
|
274
|
+
behavior: 'smooth',
|
|
275
|
+
top: offsetPosition,
|
|
276
|
+
});
|
|
277
|
+
} else {
|
|
278
|
+
// Use default scroll behavior when no offset
|
|
279
|
+
targetElement.scrollIntoView({
|
|
280
|
+
behavior: 'smooth',
|
|
281
|
+
block: 'start',
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
[onActiveChange, getScrollOffset, scrollActiveItemIntoView, containerTopPassed]
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
// Simple positioning logic - no complex state management needed
|
|
291
|
+
// Fixed when we've scrolled past container top AND container hasn't scrolled past bottom
|
|
292
|
+
const shouldUseFixedPosition = containerSelector && containerTopPassed && isContainerVisible;
|
|
293
|
+
|
|
294
|
+
// Extract background color from nav element for dynamic gradient overlays
|
|
295
|
+
useEffect(() => {
|
|
296
|
+
if (menuRef.current && isClientSide) {
|
|
297
|
+
const computedStyle = window.getComputedStyle(menuRef.current);
|
|
298
|
+
const backgroundColor = computedStyle.backgroundColor;
|
|
299
|
+
if (backgroundColor && backgroundColor !== 'rgba(0, 0, 0, 0)' && backgroundColor !== 'transparent') {
|
|
300
|
+
setNavBackgroundColor(backgroundColor);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}, []);
|
|
304
|
+
|
|
305
|
+
return (
|
|
306
|
+
<div className={cn(outerContainerClassName)}>
|
|
307
|
+
{/* Simple placeholder to maintain layout when menu becomes fixed or is fading out */}
|
|
308
|
+
{(shouldUseFixedPosition || !isContainerVisible) && (
|
|
309
|
+
<div
|
|
310
|
+
aria-hidden="true"
|
|
311
|
+
style={{
|
|
312
|
+
height: menuHeight,
|
|
313
|
+
}}
|
|
314
|
+
/>
|
|
315
|
+
)}
|
|
316
|
+
|
|
317
|
+
<nav
|
|
318
|
+
aria-label={ariaLabel}
|
|
319
|
+
className={cn(
|
|
320
|
+
'z-10 w-full bg-background-primary transition-opacity duration-300',
|
|
321
|
+
{
|
|
322
|
+
'fixed top-0': shouldUseFixedPosition || !isContainerVisible, // Keep fixed during fade-out
|
|
323
|
+
'pointer-events-none': !isContainerVisible, // Disable interaction when hidden
|
|
324
|
+
relative: !shouldUseFixedPosition && isContainerVisible,
|
|
325
|
+
},
|
|
326
|
+
className
|
|
327
|
+
)}
|
|
328
|
+
ref={menuRef}
|
|
329
|
+
style={{
|
|
330
|
+
opacity: isContainerVisible ? 1 : 0,
|
|
331
|
+
width: shouldUseFixedPosition || !isContainerVisible ? `${menuWidth}px` : undefined,
|
|
332
|
+
...htmlProps.style,
|
|
333
|
+
}}
|
|
334
|
+
{...htmlProps}
|
|
335
|
+
>
|
|
336
|
+
<div className="flex w-full flex-col gap-xs py-xs">
|
|
337
|
+
<Caption
|
|
338
|
+
// z-[11] is to prevent the caption from being hidden by fade
|
|
339
|
+
className="z-[11] w-full px-xs"
|
|
340
|
+
color="secondary"
|
|
341
|
+
>
|
|
342
|
+
{ariaLabel}
|
|
343
|
+
</Caption>
|
|
344
|
+
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions -- Keyboard events bubble from focusable menu items */}
|
|
345
|
+
<div
|
|
346
|
+
className="scrollbar-hide flex w-full overflow-x-auto px-xs py-3xs"
|
|
347
|
+
onKeyDown={handleKeyDown}
|
|
348
|
+
ref={(element) => {
|
|
349
|
+
horizontalScrollFadeProps.ref(element);
|
|
350
|
+
scrollContainerRef.current = element;
|
|
351
|
+
menuRef.current = element;
|
|
352
|
+
}}
|
|
353
|
+
>
|
|
354
|
+
{/* Left fade overlay */}
|
|
355
|
+
<div
|
|
356
|
+
className={cn(
|
|
357
|
+
'pointer-events-none absolute bottom-0 left-0 top-0 z-10 w-[48px] transition-opacity duration-300 ease-in-out',
|
|
358
|
+
{
|
|
359
|
+
'opacity-0': !horizontalScrollFadeProps.shouldShowLeftFade,
|
|
360
|
+
'opacity-100': horizontalScrollFadeProps.shouldShowLeftFade,
|
|
361
|
+
}
|
|
362
|
+
)}
|
|
363
|
+
style={{
|
|
364
|
+
background: `linear-gradient(to right, ${navBackgroundColor}, transparent)`,
|
|
365
|
+
}}
|
|
366
|
+
/>
|
|
367
|
+
|
|
368
|
+
{/* Right fade overlay */}
|
|
369
|
+
<div
|
|
370
|
+
className={cn(
|
|
371
|
+
'pointer-events-none absolute bottom-0 right-0 top-0 z-10 w-[48px] transition-opacity duration-300 ease-in-out',
|
|
372
|
+
{
|
|
373
|
+
'opacity-0': !horizontalScrollFadeProps.shouldShowRightFade,
|
|
374
|
+
'opacity-100': horizontalScrollFadeProps.shouldShowRightFade,
|
|
375
|
+
}
|
|
376
|
+
)}
|
|
377
|
+
style={{
|
|
378
|
+
background: `linear-gradient(to left, ${navBackgroundColor}, transparent)`,
|
|
379
|
+
}}
|
|
380
|
+
/>
|
|
381
|
+
|
|
382
|
+
{items.map((item) => (
|
|
383
|
+
<JumpMenuItem
|
|
384
|
+
{...item}
|
|
385
|
+
activeItemId={activeItemId}
|
|
386
|
+
handleItemClick={handleItemClick}
|
|
387
|
+
key={item.id}
|
|
388
|
+
/>
|
|
389
|
+
))}
|
|
390
|
+
</div>
|
|
391
|
+
</div>
|
|
392
|
+
</nav>
|
|
393
|
+
</div>
|
|
394
|
+
);
|
|
395
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { cn } from '@evolve.ui/theme-system';
|
|
2
|
+
|
|
3
|
+
import { JumpMenuItem as JumpMenuItemData } from '../../types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Single jump menu item configuration with click handler
|
|
7
|
+
*/
|
|
8
|
+
export type Props<T extends JumpMenuItemData = JumpMenuItemData> = T & {
|
|
9
|
+
/** Unique identifier for the menu item */
|
|
10
|
+
activeItemId: string | null;
|
|
11
|
+
/** Additional CSS classes for the individual item */
|
|
12
|
+
className?: string;
|
|
13
|
+
/** Handle item click function */
|
|
14
|
+
handleItemClick: (item: T, event: React.MouseEvent) => void;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const JumpMenuItem = ({
|
|
18
|
+
activeItemId,
|
|
19
|
+
className,
|
|
20
|
+
handleItemClick,
|
|
21
|
+
href,
|
|
22
|
+
id,
|
|
23
|
+
isActive: isActiveProp,
|
|
24
|
+
label,
|
|
25
|
+
target,
|
|
26
|
+
...restProps
|
|
27
|
+
}: Props) => {
|
|
28
|
+
const isActive = isActiveProp || id === activeItemId;
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<button
|
|
32
|
+
aria-current={isActive ? 'page' : undefined}
|
|
33
|
+
className={cn(
|
|
34
|
+
// Base styles - use consistent typography class for both states
|
|
35
|
+
'hui-text-body-secondary relative whitespace-nowrap rounded-md px-xs py-2xs last:mr-sm [&:not(:last-child)]:mr-xs',
|
|
36
|
+
// Default state (Selected=Off, Hover=Off, Focused=Off)
|
|
37
|
+
'text-text-secondary',
|
|
38
|
+
// Focus styles - inset focus ring to stay within button dimensions
|
|
39
|
+
'focus-visible:shadow-[inset_0_0_0_4px_rgb(var(--color-focus-primary)/0.50)] focus-visible:outline-none',
|
|
40
|
+
// Interactive states
|
|
41
|
+
'cursor-pointer',
|
|
42
|
+
// Hover states - completely remove hover on touch devices, only show on pointer devices
|
|
43
|
+
'[&:hover]:[@media(pointer:fine)]:bg-background-hover',
|
|
44
|
+
!isActive && '[&:hover]:[@media(pointer:fine)]:text-text-primary',
|
|
45
|
+
// Touch/Active states for mobile - brief visual feedback on tap
|
|
46
|
+
!isActive && 'active:bg-background-hover active:text-text-primary active:duration-75',
|
|
47
|
+
// Selected states (Selected=On) - using ring for border without affecting dimensions
|
|
48
|
+
isActive && 'bg-background-primary font-bold text-text-primary ring-1 ring-inset ring-border-selected',
|
|
49
|
+
className
|
|
50
|
+
)}
|
|
51
|
+
data-item-id={id}
|
|
52
|
+
data-target={target || href?.replace('#', '')}
|
|
53
|
+
key={id}
|
|
54
|
+
onClick={(event) => {
|
|
55
|
+
handleItemClick({ href, id, isActive: isActiveProp, label, target, ...restProps }, event);
|
|
56
|
+
}}
|
|
57
|
+
type="button"
|
|
58
|
+
>
|
|
59
|
+
{label}
|
|
60
|
+
</button>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { isClientSide } from '../../utils/isClientSide';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Configuration for container tracking
|
|
7
|
+
*/
|
|
8
|
+
type ContainerTrackingConfig = {
|
|
9
|
+
/** CSS selector for the container element */
|
|
10
|
+
containerSelector?: string;
|
|
11
|
+
/** Reference to the menu element for height tracking */
|
|
12
|
+
menuRef?: React.RefObject<HTMLElement | null>;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Return type for the container tracking hook
|
|
17
|
+
*/
|
|
18
|
+
type UseContainerTrackingReturn = {
|
|
19
|
+
/** Whether the container is currently visible */
|
|
20
|
+
isContainerVisible: boolean;
|
|
21
|
+
/** Current height of the menu element in pixels */
|
|
22
|
+
menuHeight: number;
|
|
23
|
+
/** Current width of the container in pixels */
|
|
24
|
+
menuWidth: number;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Hook that tracks container visibility and dimensions for fixed positioning behavior
|
|
29
|
+
*
|
|
30
|
+
* This hook provides:
|
|
31
|
+
* - Simple container visibility tracking via scroll detection
|
|
32
|
+
* - Container width tracking for maintaining consistent sizing when position changes
|
|
33
|
+
* - Menu height tracking for placeholder div
|
|
34
|
+
* - Automatic event listener management with cleanup
|
|
35
|
+
* - SSR safety by checking client-side rendering
|
|
36
|
+
*
|
|
37
|
+
* @param config - Configuration object for container tracking
|
|
38
|
+
* @returns Object containing visibility state and dimensions
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```tsx
|
|
42
|
+
* const { isContainerVisible, menuWidth, menuHeight } = useContainerTracking({
|
|
43
|
+
* containerSelector: '#main-container',
|
|
44
|
+
* menuRef
|
|
45
|
+
* });
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export const useContainerTracking = ({
|
|
49
|
+
containerSelector,
|
|
50
|
+
menuRef,
|
|
51
|
+
}: ContainerTrackingConfig): UseContainerTrackingReturn => {
|
|
52
|
+
const [isContainerVisible, setIsContainerVisible] = useState<boolean>(true);
|
|
53
|
+
const [menuWidth, setMenuWidth] = useState<number>(0);
|
|
54
|
+
const [menuHeight, setMenuHeight] = useState<number>(0);
|
|
55
|
+
const resizeObserverRef = useRef<ResizeObserver | null>(null);
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Sets up simple scroll-based visibility tracking for the container
|
|
59
|
+
* Menu becomes fixed when container top reaches viewport top and stays fixed until container scrolls past
|
|
60
|
+
*/
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
// We don't want to run this in SSR since we are using the window object
|
|
63
|
+
if (!isClientSide) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!containerSelector) {
|
|
68
|
+
setIsContainerVisible(true);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const containerElement = document.querySelector(containerSelector);
|
|
73
|
+
if (!containerElement) {
|
|
74
|
+
setIsContainerVisible(true);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const onScroll = () => {
|
|
79
|
+
const rect = containerElement.getBoundingClientRect();
|
|
80
|
+
|
|
81
|
+
// Menu should be visible when:
|
|
82
|
+
// 1. Before we scroll to container (rect.top > 0) - show in relative position
|
|
83
|
+
// 2. While scrolling through container (rect.top <= 0 && rect.bottom > 0) - show in fixed position
|
|
84
|
+
// 3. After scrolling past container (rect.bottom <= 0) - hide completely
|
|
85
|
+
const shouldBeVisible = rect.bottom > 0; // Visible as long as container hasn't scrolled completely past
|
|
86
|
+
|
|
87
|
+
setIsContainerVisible(shouldBeVisible);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// Initial check
|
|
91
|
+
onScroll();
|
|
92
|
+
|
|
93
|
+
// Listen to scroll events with passive flag for performance
|
|
94
|
+
window.addEventListener('scroll', onScroll, { passive: true });
|
|
95
|
+
|
|
96
|
+
return () => {
|
|
97
|
+
window.removeEventListener('scroll', onScroll);
|
|
98
|
+
};
|
|
99
|
+
}, [containerSelector]);
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Updates menu width and height for fixed positioning
|
|
103
|
+
* This ensures the fixed menu maintains the same dimensions as its static position
|
|
104
|
+
* Uses ResizeObserver for precise, real-time dimension tracking
|
|
105
|
+
*/
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
// We don't want to run this in SSR since we are using the window object
|
|
108
|
+
if (!isClientSide) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const updateContainerWidth = () => {
|
|
113
|
+
if (containerSelector) {
|
|
114
|
+
const containerElement = document.querySelector(containerSelector);
|
|
115
|
+
if (containerElement) {
|
|
116
|
+
const containerRect = containerElement.getBoundingClientRect();
|
|
117
|
+
setMenuWidth(containerRect.width);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const updateMenuHeight = () => {
|
|
123
|
+
if (menuRef?.current) {
|
|
124
|
+
// Use offsetHeight for more accurate measurement including borders and padding
|
|
125
|
+
const height = menuRef.current.offsetHeight;
|
|
126
|
+
if (height > 0) {
|
|
127
|
+
setMenuHeight(height);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// Set up ResizeObserver for menu element height tracking
|
|
133
|
+
if (menuRef?.current && 'ResizeObserver' in window) {
|
|
134
|
+
resizeObserverRef.current = new ResizeObserver((entries) => {
|
|
135
|
+
for (const entry of entries) {
|
|
136
|
+
// Use contentRect height for reliable measurement
|
|
137
|
+
const height = entry.contentRect.height;
|
|
138
|
+
if (height > 0) {
|
|
139
|
+
setMenuHeight(height);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
resizeObserverRef.current.observe(menuRef.current);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Initial measurements
|
|
148
|
+
updateContainerWidth();
|
|
149
|
+
updateMenuHeight();
|
|
150
|
+
|
|
151
|
+
// Listen for window resize for container width updates
|
|
152
|
+
window.addEventListener('resize', updateContainerWidth);
|
|
153
|
+
|
|
154
|
+
return () => {
|
|
155
|
+
window.removeEventListener('resize', updateContainerWidth);
|
|
156
|
+
if (resizeObserverRef.current) {
|
|
157
|
+
resizeObserverRef.current.disconnect();
|
|
158
|
+
resizeObserverRef.current = null;
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}, [containerSelector, menuRef]);
|
|
162
|
+
|
|
163
|
+
return useMemo(
|
|
164
|
+
() => ({
|
|
165
|
+
isContainerVisible,
|
|
166
|
+
menuHeight,
|
|
167
|
+
menuWidth,
|
|
168
|
+
}),
|
|
169
|
+
[isContainerVisible, menuHeight, menuWidth]
|
|
170
|
+
);
|
|
171
|
+
};
|