@oxyhq/bloom 0.50.2 → 0.51.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/NOTICE +89 -0
- package/README.md +12 -7
- package/lib/commonjs/avatar-group/AvatarGroup.web.js +2 -1
- package/lib/commonjs/avatar-group/AvatarGroup.web.js.map +1 -1
- package/lib/commonjs/bottom-sheet/index.web.js +2 -1
- package/lib/commonjs/bottom-sheet/index.web.js.map +1 -1
- package/lib/commonjs/context-menu/index.web.js +3 -2
- package/lib/commonjs/context-menu/index.web.js.map +1 -1
- package/lib/commonjs/dialog/Dialog.web.js +4 -3
- package/lib/commonjs/dialog/Dialog.web.js.map +1 -1
- package/lib/commonjs/index.js +90 -83
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +116 -109
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/menu/index.web.js +2 -1
- package/lib/commonjs/menu/index.web.js.map +1 -1
- package/lib/commonjs/popover/index.web.js +3 -2
- package/lib/commonjs/popover/index.web.js.map +1 -1
- package/lib/commonjs/portal/index.js +4 -1
- package/lib/commonjs/portal/index.js.map +1 -1
- package/lib/commonjs/progressive-blur/index.js +79 -0
- package/lib/commonjs/progressive-blur/index.js.map +1 -0
- package/lib/commonjs/progressive-blur/index.web.js +67 -0
- package/lib/commonjs/progressive-blur/index.web.js.map +1 -0
- package/lib/commonjs/progressive-blur/shared.js +66 -0
- package/lib/commonjs/progressive-blur/shared.js.map +1 -0
- package/lib/commonjs/progressive-blur/types.js +6 -0
- package/lib/commonjs/progressive-blur/types.js.map +1 -0
- package/lib/commonjs/select/index.web.js +2 -1
- package/lib/commonjs/select/index.web.js.map +1 -1
- package/lib/commonjs/styles/web-view-style.js +26 -0
- package/lib/commonjs/styles/web-view-style.js.map +1 -0
- package/lib/commonjs/tab-bar/TabBarBase.js +415 -0
- package/lib/commonjs/tab-bar/TabBarBase.js.map +1 -0
- package/lib/commonjs/tab-bar/expo-router/RouterTabBar.js +83 -0
- package/lib/commonjs/tab-bar/expo-router/RouterTabBar.js.map +1 -0
- package/lib/commonjs/tab-bar/expo-router/fading-tab-screen.js +119 -0
- package/lib/commonjs/tab-bar/expo-router/fading-tab-screen.js.map +1 -0
- package/lib/commonjs/tab-bar/expo-router/index.js +26 -0
- package/lib/commonjs/tab-bar/expo-router/index.js.map +1 -0
- package/lib/commonjs/tab-bar/glyph.js +43 -0
- package/lib/commonjs/tab-bar/glyph.js.map +1 -0
- package/lib/commonjs/tab-bar/glyph.native.js +55 -0
- package/lib/commonjs/tab-bar/glyph.native.js.map +1 -0
- package/lib/commonjs/tab-bar/index.js +65 -0
- package/lib/commonjs/tab-bar/index.js.map +1 -0
- package/lib/commonjs/tab-bar/index.web.js +67 -0
- package/lib/commonjs/tab-bar/index.web.js.map +1 -0
- package/lib/commonjs/tab-bar/minimize-context.js +127 -0
- package/lib/commonjs/tab-bar/minimize-context.js.map +1 -0
- package/lib/commonjs/tab-bar/shared.js +139 -0
- package/lib/commonjs/tab-bar/shared.js.map +1 -0
- package/lib/commonjs/tab-bar/surface.js +50 -0
- package/lib/commonjs/tab-bar/surface.js.map +1 -0
- package/lib/commonjs/tab-bar/surface.native.js +56 -0
- package/lib/commonjs/tab-bar/surface.native.js.map +1 -0
- package/lib/commonjs/tab-bar/surface.web.js +59 -0
- package/lib/commonjs/tab-bar/surface.web.js.map +1 -0
- package/lib/commonjs/tab-bar/types.js +6 -0
- package/lib/commonjs/tab-bar/types.js.map +1 -0
- package/lib/commonjs/toast/Positioner.js +117 -0
- package/lib/commonjs/toast/Positioner.js.map +1 -0
- package/lib/commonjs/toast/ToastContent.js +301 -0
- package/lib/commonjs/toast/ToastContent.js.map +1 -0
- package/lib/commonjs/toast/ToastHost.js +69 -0
- package/lib/commonjs/toast/ToastHost.js.map +1 -0
- package/lib/commonjs/toast/ToastHost.native.js +44 -0
- package/lib/commonjs/toast/ToastHost.native.js.map +1 -0
- package/lib/commonjs/toast/ToastIcon.js +57 -0
- package/lib/commonjs/toast/ToastIcon.js.map +1 -0
- package/lib/commonjs/toast/ToastRow.js +411 -0
- package/lib/commonjs/toast/ToastRow.js.map +1 -0
- package/lib/commonjs/toast/ToastSwipeHandler.js +189 -0
- package/lib/commonjs/toast/ToastSwipeHandler.js.map +1 -0
- package/lib/commonjs/toast/Toaster.js +205 -0
- package/lib/commonjs/toast/Toaster.js.map +1 -0
- package/lib/commonjs/toast/animations.js +274 -0
- package/lib/commonjs/toast/animations.js.map +1 -0
- package/lib/commonjs/toast/constants.js +56 -0
- package/lib/commonjs/toast/constants.js.map +1 -0
- package/lib/commonjs/toast/context.js +42 -0
- package/lib/commonjs/toast/context.js.map +1 -0
- package/lib/commonjs/toast/index.js +32 -126
- package/lib/commonjs/toast/index.js.map +1 -1
- package/lib/commonjs/toast/position-utils.js +115 -0
- package/lib/commonjs/toast/position-utils.js.map +1 -0
- package/lib/commonjs/toast/positioner-utils.js +141 -0
- package/lib/commonjs/toast/positioner-utils.js.map +1 -0
- package/lib/commonjs/toast/toast-comparator.js +28 -0
- package/lib/commonjs/toast/toast-comparator.js.map +1 -0
- package/lib/commonjs/toast/toast-fns.js +90 -0
- package/lib/commonjs/toast/toast-fns.js.map +1 -0
- package/lib/commonjs/toast/toast-store.js +352 -0
- package/lib/commonjs/toast/toast-store.js.map +1 -0
- package/lib/commonjs/toast/toast-timers.js +110 -0
- package/lib/commonjs/toast/toast-timers.js.map +1 -0
- package/lib/commonjs/toast/types.js +42 -0
- package/lib/commonjs/toast/types.js.map +1 -1
- package/lib/commonjs/toast/use-animated-target.js +57 -0
- package/lib/commonjs/toast/use-animated-target.js.map +1 -0
- package/lib/commonjs/toast/use-app-state.js +76 -0
- package/lib/commonjs/toast/use-app-state.js.map +1 -0
- package/lib/commonjs/toast/use-toast-colors.js +114 -0
- package/lib/commonjs/toast/use-toast-colors.js.map +1 -0
- package/lib/commonjs/toast/use-toast-position.js +40 -0
- package/lib/commonjs/toast/use-toast-position.js.map +1 -0
- package/lib/commonjs/tooltip/index.web.js +2 -1
- package/lib/commonjs/tooltip/index.web.js.map +1 -1
- package/lib/commonjs/zoomable-image-gallery/ZoomableImageGallery.js +20 -8
- package/lib/commonjs/zoomable-image-gallery/ZoomableImageGallery.js.map +1 -1
- package/lib/module/avatar-group/AvatarGroup.web.js +2 -1
- package/lib/module/avatar-group/AvatarGroup.web.js.map +1 -1
- package/lib/module/bottom-sheet/index.web.js +2 -1
- package/lib/module/bottom-sheet/index.web.js.map +1 -1
- package/lib/module/context-menu/index.web.js +3 -2
- package/lib/module/context-menu/index.web.js.map +1 -1
- package/lib/module/dialog/Dialog.web.js +4 -3
- package/lib/module/dialog/Dialog.web.js.map +1 -1
- package/lib/module/index.js +3 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +3 -2
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/menu/index.web.js +2 -1
- package/lib/module/menu/index.web.js.map +1 -1
- package/lib/module/popover/index.web.js +3 -2
- package/lib/module/popover/index.web.js.map +1 -1
- package/lib/module/portal/index.js +4 -1
- package/lib/module/portal/index.js.map +1 -1
- package/lib/module/progressive-blur/index.js +74 -0
- package/lib/module/progressive-blur/index.js.map +1 -0
- package/lib/module/progressive-blur/index.web.js +62 -0
- package/lib/module/progressive-blur/index.web.js.map +1 -0
- package/lib/module/progressive-blur/shared.js +60 -0
- package/lib/module/progressive-blur/shared.js.map +1 -0
- package/lib/module/progressive-blur/types.js +4 -0
- package/lib/module/progressive-blur/types.js.map +1 -0
- package/lib/module/select/index.web.js +2 -1
- package/lib/module/select/index.web.js.map +1 -1
- package/lib/module/styles/web-view-style.js +22 -0
- package/lib/module/styles/web-view-style.js.map +1 -0
- package/lib/module/tab-bar/TabBarBase.js +408 -0
- package/lib/module/tab-bar/TabBarBase.js.map +1 -0
- package/lib/module/tab-bar/expo-router/RouterTabBar.js +78 -0
- package/lib/module/tab-bar/expo-router/RouterTabBar.js.map +1 -0
- package/lib/module/tab-bar/expo-router/fading-tab-screen.js +113 -0
- package/lib/module/tab-bar/expo-router/fading-tab-screen.js.map +1 -0
- package/lib/module/tab-bar/expo-router/index.js +15 -0
- package/lib/module/tab-bar/expo-router/index.js.map +1 -0
- package/lib/module/tab-bar/glyph.js +38 -0
- package/lib/module/tab-bar/glyph.js.map +1 -0
- package/lib/module/tab-bar/glyph.native.js +50 -0
- package/lib/module/tab-bar/glyph.native.js.map +1 -0
- package/lib/module/tab-bar/index.js +24 -0
- package/lib/module/tab-bar/index.js.map +1 -0
- package/lib/module/tab-bar/index.web.js +26 -0
- package/lib/module/tab-bar/index.web.js.map +1 -0
- package/lib/module/tab-bar/minimize-context.js +118 -0
- package/lib/module/tab-bar/minimize-context.js.map +1 -0
- package/lib/module/tab-bar/shared.js +132 -0
- package/lib/module/tab-bar/shared.js.map +1 -0
- package/lib/module/tab-bar/surface.js +44 -0
- package/lib/module/tab-bar/surface.js.map +1 -0
- package/lib/module/tab-bar/surface.native.js +50 -0
- package/lib/module/tab-bar/surface.native.js.map +1 -0
- package/lib/module/tab-bar/surface.web.js +53 -0
- package/lib/module/tab-bar/surface.web.js.map +1 -0
- package/lib/module/tab-bar/types.js +4 -0
- package/lib/module/tab-bar/types.js.map +1 -0
- package/lib/module/toast/Positioner.js +110 -0
- package/lib/module/toast/Positioner.js.map +1 -0
- package/lib/module/toast/ToastContent.js +295 -0
- package/lib/module/toast/ToastContent.js.map +1 -0
- package/lib/module/toast/ToastHost.js +63 -0
- package/lib/module/toast/ToastHost.js.map +1 -0
- package/lib/module/toast/ToastHost.native.js +38 -0
- package/lib/module/toast/ToastHost.native.js.map +1 -0
- package/lib/module/toast/ToastIcon.js +52 -0
- package/lib/module/toast/ToastIcon.js.map +1 -0
- package/lib/module/toast/ToastRow.js +407 -0
- package/lib/module/toast/ToastRow.js.map +1 -0
- package/lib/module/toast/ToastSwipeHandler.js +182 -0
- package/lib/module/toast/ToastSwipeHandler.js.map +1 -0
- package/lib/module/toast/Toaster.js +198 -0
- package/lib/module/toast/Toaster.js.map +1 -0
- package/lib/module/toast/animations.js +261 -0
- package/lib/module/toast/animations.js.map +1 -0
- package/lib/module/toast/constants.js +52 -0
- package/lib/module/toast/constants.js.map +1 -0
- package/lib/module/toast/context.js +34 -0
- package/lib/module/toast/context.js.map +1 -0
- package/lib/module/toast/index.js +27 -103
- package/lib/module/toast/index.js.map +1 -1
- package/lib/module/toast/position-utils.js +107 -0
- package/lib/module/toast/position-utils.js.map +1 -0
- package/lib/module/toast/positioner-utils.js +134 -0
- package/lib/module/toast/positioner-utils.js.map +1 -0
- package/lib/module/toast/toast-comparator.js +22 -0
- package/lib/module/toast/toast-comparator.js.map +1 -0
- package/lib/module/toast/toast-fns.js +85 -0
- package/lib/module/toast/toast-fns.js.map +1 -0
- package/lib/module/toast/toast-store.js +346 -0
- package/lib/module/toast/toast-store.js.map +1 -0
- package/lib/module/toast/toast-timers.js +104 -0
- package/lib/module/toast/toast-timers.js.map +1 -0
- package/lib/module/toast/types.js +38 -0
- package/lib/module/toast/types.js.map +1 -1
- package/lib/module/toast/use-animated-target.js +52 -0
- package/lib/module/toast/use-animated-target.js.map +1 -0
- package/lib/module/toast/use-app-state.js +70 -0
- package/lib/module/toast/use-app-state.js.map +1 -0
- package/lib/module/toast/use-toast-colors.js +109 -0
- package/lib/module/toast/use-toast-colors.js.map +1 -0
- package/lib/module/toast/use-toast-position.js +35 -0
- package/lib/module/toast/use-toast-position.js.map +1 -0
- package/lib/module/tooltip/index.web.js +2 -1
- package/lib/module/tooltip/index.web.js.map +1 -1
- package/lib/module/zoomable-image-gallery/ZoomableImageGallery.js +20 -8
- package/lib/module/zoomable-image-gallery/ZoomableImageGallery.js.map +1 -1
- package/lib/typescript/commonjs/avatar-group/AvatarGroup.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/bottom-sheet/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/context-menu/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/Dialog.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +2 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.web.d.ts +2 -1
- package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/menu/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/popover/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/portal/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/progressive-blur/index.d.ts +23 -0
- package/lib/typescript/commonjs/progressive-blur/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/progressive-blur/index.web.d.ts +30 -0
- package/lib/typescript/commonjs/progressive-blur/index.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/progressive-blur/shared.d.ts +35 -0
- package/lib/typescript/commonjs/progressive-blur/shared.d.ts.map +1 -0
- package/lib/typescript/commonjs/progressive-blur/types.d.ts +21 -0
- package/lib/typescript/commonjs/progressive-blur/types.d.ts.map +1 -0
- package/lib/typescript/commonjs/select/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/styles/web-view-style.d.ts +21 -0
- package/lib/typescript/commonjs/styles/web-view-style.d.ts.map +1 -0
- package/lib/typescript/commonjs/tab-bar/TabBarBase.d.ts +36 -0
- package/lib/typescript/commonjs/tab-bar/TabBarBase.d.ts.map +1 -0
- package/lib/typescript/commonjs/tab-bar/expo-router/RouterTabBar.d.ts +59 -0
- package/lib/typescript/commonjs/tab-bar/expo-router/RouterTabBar.d.ts.map +1 -0
- package/lib/typescript/commonjs/tab-bar/expo-router/fading-tab-screen.d.ts +11 -0
- package/lib/typescript/commonjs/tab-bar/expo-router/fading-tab-screen.d.ts.map +1 -0
- package/lib/typescript/commonjs/tab-bar/expo-router/index.d.ts +14 -0
- package/lib/typescript/commonjs/tab-bar/expo-router/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/tab-bar/glyph.d.ts +6 -0
- package/lib/typescript/commonjs/tab-bar/glyph.d.ts.map +1 -0
- package/lib/typescript/commonjs/tab-bar/glyph.native.d.ts +6 -0
- package/lib/typescript/commonjs/tab-bar/glyph.native.d.ts.map +1 -0
- package/lib/typescript/commonjs/tab-bar/index.d.ts +12 -0
- package/lib/typescript/commonjs/tab-bar/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/tab-bar/index.web.d.ts +12 -0
- package/lib/typescript/commonjs/tab-bar/index.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/tab-bar/minimize-context.d.ts +51 -0
- package/lib/typescript/commonjs/tab-bar/minimize-context.d.ts.map +1 -0
- package/lib/typescript/commonjs/tab-bar/shared.d.ts +93 -0
- package/lib/typescript/commonjs/tab-bar/shared.d.ts.map +1 -0
- package/lib/typescript/commonjs/tab-bar/surface.d.ts +6 -0
- package/lib/typescript/commonjs/tab-bar/surface.d.ts.map +1 -0
- package/lib/typescript/commonjs/tab-bar/surface.native.d.ts +6 -0
- package/lib/typescript/commonjs/tab-bar/surface.native.d.ts.map +1 -0
- package/lib/typescript/commonjs/tab-bar/surface.web.d.ts +6 -0
- package/lib/typescript/commonjs/tab-bar/surface.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/tab-bar/types.d.ts +85 -0
- package/lib/typescript/commonjs/tab-bar/types.d.ts.map +1 -0
- package/lib/typescript/commonjs/toast/Positioner.d.ts +21 -0
- package/lib/typescript/commonjs/toast/Positioner.d.ts.map +1 -0
- package/lib/typescript/commonjs/toast/ToastContent.d.ts +14 -0
- package/lib/typescript/commonjs/toast/ToastContent.d.ts.map +1 -0
- package/lib/typescript/commonjs/toast/ToastHost.d.ts +6 -0
- package/lib/typescript/commonjs/toast/ToastHost.d.ts.map +1 -0
- package/lib/typescript/commonjs/toast/ToastHost.native.d.ts +6 -0
- package/lib/typescript/commonjs/toast/ToastHost.native.d.ts.map +1 -0
- package/lib/typescript/commonjs/toast/ToastIcon.d.ts +9 -0
- package/lib/typescript/commonjs/toast/ToastIcon.d.ts.map +1 -0
- package/lib/typescript/commonjs/toast/ToastRow.d.ts +80 -0
- package/lib/typescript/commonjs/toast/ToastRow.d.ts.map +1 -0
- package/lib/typescript/commonjs/toast/ToastSwipeHandler.d.ts +30 -0
- package/lib/typescript/commonjs/toast/ToastSwipeHandler.d.ts.map +1 -0
- package/lib/typescript/commonjs/toast/Toaster.d.ts +19 -0
- package/lib/typescript/commonjs/toast/Toaster.d.ts.map +1 -0
- package/lib/typescript/commonjs/toast/animations.d.ts +62 -0
- package/lib/typescript/commonjs/toast/animations.d.ts.map +1 -0
- package/lib/typescript/commonjs/toast/constants.d.ts +39 -0
- package/lib/typescript/commonjs/toast/constants.d.ts.map +1 -0
- package/lib/typescript/commonjs/toast/context.d.ts +19 -0
- package/lib/typescript/commonjs/toast/context.d.ts.map +1 -0
- package/lib/typescript/commonjs/toast/index.d.ts +23 -76
- package/lib/typescript/commonjs/toast/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/toast/position-utils.d.ts +30 -0
- package/lib/typescript/commonjs/toast/position-utils.d.ts.map +1 -0
- package/lib/typescript/commonjs/toast/positioner-utils.d.ts +72 -0
- package/lib/typescript/commonjs/toast/positioner-utils.d.ts.map +1 -0
- package/lib/typescript/commonjs/toast/toast-comparator.d.ts +12 -0
- package/lib/typescript/commonjs/toast/toast-comparator.d.ts.map +1 -0
- package/lib/typescript/commonjs/toast/toast-fns.d.ts +3 -0
- package/lib/typescript/commonjs/toast/toast-fns.d.ts.map +1 -0
- package/lib/typescript/commonjs/toast/toast-store.d.ts +52 -0
- package/lib/typescript/commonjs/toast/toast-store.d.ts.map +1 -0
- package/lib/typescript/commonjs/toast/toast-timers.d.ts +24 -0
- package/lib/typescript/commonjs/toast/toast-timers.d.ts.map +1 -0
- package/lib/typescript/commonjs/toast/types.d.ts +222 -5
- package/lib/typescript/commonjs/toast/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/toast/use-animated-target.d.ts +7 -0
- package/lib/typescript/commonjs/toast/use-animated-target.d.ts.map +1 -0
- package/lib/typescript/commonjs/toast/use-app-state.d.ts +18 -0
- package/lib/typescript/commonjs/toast/use-app-state.d.ts.map +1 -0
- package/lib/typescript/commonjs/toast/use-toast-colors.d.ts +22 -0
- package/lib/typescript/commonjs/toast/use-toast-colors.d.ts.map +1 -0
- package/lib/typescript/commonjs/toast/use-toast-position.d.ts +37 -0
- package/lib/typescript/commonjs/toast/use-toast-position.d.ts.map +1 -0
- package/lib/typescript/commonjs/tooltip/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/zoomable-image-gallery/ZoomableImageGallery.d.ts.map +1 -1
- package/lib/typescript/module/avatar-group/AvatarGroup.web.d.ts.map +1 -1
- package/lib/typescript/module/bottom-sheet/index.web.d.ts.map +1 -1
- package/lib/typescript/module/context-menu/index.web.d.ts.map +1 -1
- package/lib/typescript/module/dialog/Dialog.web.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +2 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/index.web.d.ts +2 -1
- package/lib/typescript/module/index.web.d.ts.map +1 -1
- package/lib/typescript/module/menu/index.web.d.ts.map +1 -1
- package/lib/typescript/module/popover/index.web.d.ts.map +1 -1
- package/lib/typescript/module/portal/index.d.ts.map +1 -1
- package/lib/typescript/module/progressive-blur/index.d.ts +23 -0
- package/lib/typescript/module/progressive-blur/index.d.ts.map +1 -0
- package/lib/typescript/module/progressive-blur/index.web.d.ts +30 -0
- package/lib/typescript/module/progressive-blur/index.web.d.ts.map +1 -0
- package/lib/typescript/module/progressive-blur/shared.d.ts +35 -0
- package/lib/typescript/module/progressive-blur/shared.d.ts.map +1 -0
- package/lib/typescript/module/progressive-blur/types.d.ts +21 -0
- package/lib/typescript/module/progressive-blur/types.d.ts.map +1 -0
- package/lib/typescript/module/select/index.web.d.ts.map +1 -1
- package/lib/typescript/module/styles/web-view-style.d.ts +21 -0
- package/lib/typescript/module/styles/web-view-style.d.ts.map +1 -0
- package/lib/typescript/module/tab-bar/TabBarBase.d.ts +36 -0
- package/lib/typescript/module/tab-bar/TabBarBase.d.ts.map +1 -0
- package/lib/typescript/module/tab-bar/expo-router/RouterTabBar.d.ts +59 -0
- package/lib/typescript/module/tab-bar/expo-router/RouterTabBar.d.ts.map +1 -0
- package/lib/typescript/module/tab-bar/expo-router/fading-tab-screen.d.ts +11 -0
- package/lib/typescript/module/tab-bar/expo-router/fading-tab-screen.d.ts.map +1 -0
- package/lib/typescript/module/tab-bar/expo-router/index.d.ts +14 -0
- package/lib/typescript/module/tab-bar/expo-router/index.d.ts.map +1 -0
- package/lib/typescript/module/tab-bar/glyph.d.ts +6 -0
- package/lib/typescript/module/tab-bar/glyph.d.ts.map +1 -0
- package/lib/typescript/module/tab-bar/glyph.native.d.ts +6 -0
- package/lib/typescript/module/tab-bar/glyph.native.d.ts.map +1 -0
- package/lib/typescript/module/tab-bar/index.d.ts +12 -0
- package/lib/typescript/module/tab-bar/index.d.ts.map +1 -0
- package/lib/typescript/module/tab-bar/index.web.d.ts +12 -0
- package/lib/typescript/module/tab-bar/index.web.d.ts.map +1 -0
- package/lib/typescript/module/tab-bar/minimize-context.d.ts +51 -0
- package/lib/typescript/module/tab-bar/minimize-context.d.ts.map +1 -0
- package/lib/typescript/module/tab-bar/shared.d.ts +93 -0
- package/lib/typescript/module/tab-bar/shared.d.ts.map +1 -0
- package/lib/typescript/module/tab-bar/surface.d.ts +6 -0
- package/lib/typescript/module/tab-bar/surface.d.ts.map +1 -0
- package/lib/typescript/module/tab-bar/surface.native.d.ts +6 -0
- package/lib/typescript/module/tab-bar/surface.native.d.ts.map +1 -0
- package/lib/typescript/module/tab-bar/surface.web.d.ts +6 -0
- package/lib/typescript/module/tab-bar/surface.web.d.ts.map +1 -0
- package/lib/typescript/module/tab-bar/types.d.ts +85 -0
- package/lib/typescript/module/tab-bar/types.d.ts.map +1 -0
- package/lib/typescript/module/toast/Positioner.d.ts +21 -0
- package/lib/typescript/module/toast/Positioner.d.ts.map +1 -0
- package/lib/typescript/module/toast/ToastContent.d.ts +14 -0
- package/lib/typescript/module/toast/ToastContent.d.ts.map +1 -0
- package/lib/typescript/module/toast/ToastHost.d.ts +6 -0
- package/lib/typescript/module/toast/ToastHost.d.ts.map +1 -0
- package/lib/typescript/module/toast/ToastHost.native.d.ts +6 -0
- package/lib/typescript/module/toast/ToastHost.native.d.ts.map +1 -0
- package/lib/typescript/module/toast/ToastIcon.d.ts +9 -0
- package/lib/typescript/module/toast/ToastIcon.d.ts.map +1 -0
- package/lib/typescript/module/toast/ToastRow.d.ts +80 -0
- package/lib/typescript/module/toast/ToastRow.d.ts.map +1 -0
- package/lib/typescript/module/toast/ToastSwipeHandler.d.ts +30 -0
- package/lib/typescript/module/toast/ToastSwipeHandler.d.ts.map +1 -0
- package/lib/typescript/module/toast/Toaster.d.ts +19 -0
- package/lib/typescript/module/toast/Toaster.d.ts.map +1 -0
- package/lib/typescript/module/toast/animations.d.ts +62 -0
- package/lib/typescript/module/toast/animations.d.ts.map +1 -0
- package/lib/typescript/module/toast/constants.d.ts +39 -0
- package/lib/typescript/module/toast/constants.d.ts.map +1 -0
- package/lib/typescript/module/toast/context.d.ts +19 -0
- package/lib/typescript/module/toast/context.d.ts.map +1 -0
- package/lib/typescript/module/toast/index.d.ts +23 -76
- package/lib/typescript/module/toast/index.d.ts.map +1 -1
- package/lib/typescript/module/toast/position-utils.d.ts +30 -0
- package/lib/typescript/module/toast/position-utils.d.ts.map +1 -0
- package/lib/typescript/module/toast/positioner-utils.d.ts +72 -0
- package/lib/typescript/module/toast/positioner-utils.d.ts.map +1 -0
- package/lib/typescript/module/toast/toast-comparator.d.ts +12 -0
- package/lib/typescript/module/toast/toast-comparator.d.ts.map +1 -0
- package/lib/typescript/module/toast/toast-fns.d.ts +3 -0
- package/lib/typescript/module/toast/toast-fns.d.ts.map +1 -0
- package/lib/typescript/module/toast/toast-store.d.ts +52 -0
- package/lib/typescript/module/toast/toast-store.d.ts.map +1 -0
- package/lib/typescript/module/toast/toast-timers.d.ts +24 -0
- package/lib/typescript/module/toast/toast-timers.d.ts.map +1 -0
- package/lib/typescript/module/toast/types.d.ts +222 -5
- package/lib/typescript/module/toast/types.d.ts.map +1 -1
- package/lib/typescript/module/toast/use-animated-target.d.ts +7 -0
- package/lib/typescript/module/toast/use-animated-target.d.ts.map +1 -0
- package/lib/typescript/module/toast/use-app-state.d.ts +18 -0
- package/lib/typescript/module/toast/use-app-state.d.ts.map +1 -0
- package/lib/typescript/module/toast/use-toast-colors.d.ts +22 -0
- package/lib/typescript/module/toast/use-toast-colors.d.ts.map +1 -0
- package/lib/typescript/module/toast/use-toast-position.d.ts +37 -0
- package/lib/typescript/module/toast/use-toast-position.d.ts.map +1 -0
- package/lib/typescript/module/tooltip/index.web.d.ts.map +1 -1
- package/lib/typescript/module/zoomable-image-gallery/ZoomableImageGallery.d.ts.map +1 -1
- package/package.json +63 -25
- package/src/__tests__/TabBar.test.tsx +342 -0
- package/src/__tests__/TabBarReanimatedDeps.test.ts +130 -0
- package/src/__tests__/TabBarWebFork.test.ts +197 -0
- package/src/__tests__/ToastContent.test.tsx +192 -0
- package/src/__tests__/ToastHostWebFork.test.ts +155 -0
- package/src/__tests__/Toaster.test.tsx +527 -0
- package/src/__tests__/animated-style-deps.test.ts +156 -0
- package/src/__tests__/toast-animations.test.ts +98 -0
- package/src/__tests__/toast-comparator.test.ts +131 -0
- package/src/__tests__/toast-enter-driver.test.ts +147 -0
- package/src/__tests__/toast-fns.test.ts +240 -0
- package/src/__tests__/toast-position-utils.test.ts +259 -0
- package/src/__tests__/toast-positioner-utils.test.ts +214 -0
- package/src/__tests__/toast-store.test.ts +622 -0
- package/src/__tests__/toast-timers.test.ts +163 -0
- package/src/__tests__/use-animated-target.test.tsx +60 -0
- package/src/__tests__/use-app-state.test.tsx +127 -0
- package/src/__tests__/use-toast-colors.test.tsx +189 -0
- package/src/__tests__/web-position-fixed.test.ts +70 -0
- package/src/avatar-group/AvatarGroup.web.tsx +2 -1
- package/src/bottom-sheet/index.web.tsx +3 -2
- package/src/context-menu/index.web.tsx +3 -2
- package/src/dialog/Dialog.web.tsx +4 -3
- package/src/index.ts +4 -1
- package/src/index.web.ts +4 -1
- package/src/menu/index.web.tsx +2 -1
- package/src/popover/index.web.tsx +3 -2
- package/src/portal/index.tsx +4 -1
- package/src/progressive-blur/index.tsx +78 -0
- package/src/progressive-blur/index.web.tsx +65 -0
- package/src/progressive-blur/shared.ts +81 -0
- package/src/progressive-blur/types.ts +21 -0
- package/src/select/index.web.tsx +2 -1
- package/src/styles/web-view-style.ts +21 -0
- package/src/tab-bar/TabBar.stories.tsx +217 -0
- package/src/tab-bar/TabBarBase.tsx +517 -0
- package/src/tab-bar/expo-router/RouterTabBar.tsx +80 -0
- package/src/tab-bar/expo-router/fading-tab-screen.tsx +109 -0
- package/src/tab-bar/expo-router/index.ts +13 -0
- package/src/tab-bar/glyph.native.tsx +46 -0
- package/src/tab-bar/glyph.tsx +31 -0
- package/src/tab-bar/index.ts +32 -0
- package/src/tab-bar/index.web.tsx +34 -0
- package/src/tab-bar/minimize-context.tsx +117 -0
- package/src/tab-bar/shared.ts +159 -0
- package/src/tab-bar/surface.native.tsx +60 -0
- package/src/tab-bar/surface.tsx +47 -0
- package/src/tab-bar/surface.web.tsx +56 -0
- package/src/tab-bar/types.ts +88 -0
- package/src/toast/Positioner.tsx +121 -0
- package/src/toast/Toast.stories.tsx +292 -65
- package/src/toast/ToastContent.tsx +405 -0
- package/src/toast/ToastHost.native.tsx +31 -0
- package/src/toast/ToastHost.tsx +67 -0
- package/src/toast/ToastIcon.tsx +55 -0
- package/src/toast/ToastRow.tsx +485 -0
- package/src/toast/ToastSwipeHandler.tsx +222 -0
- package/src/toast/Toaster.tsx +259 -0
- package/src/toast/animations.ts +313 -0
- package/src/toast/constants.ts +76 -0
- package/src/toast/context.tsx +44 -0
- package/src/toast/index.tsx +47 -123
- package/src/toast/position-utils.ts +149 -0
- package/src/toast/positioner-utils.ts +142 -0
- package/src/toast/toast-comparator.ts +41 -0
- package/src/toast/toast-fns.ts +104 -0
- package/src/toast/toast-store.ts +432 -0
- package/src/toast/toast-timers.ts +124 -0
- package/src/toast/types.ts +301 -5
- package/src/toast/use-animated-target.ts +61 -0
- package/src/toast/use-app-state.ts +77 -0
- package/src/toast/use-toast-colors.ts +133 -0
- package/src/toast/use-toast-position.ts +46 -0
- package/src/tooltip/index.web.tsx +2 -1
- package/src/zoomable-image-gallery/ZoomableImageGallery.stories.tsx +134 -0
- package/src/zoomable-image-gallery/ZoomableImageGallery.tsx +50 -27
- package/lib/commonjs/toast/Toast.js +0 -267
- package/lib/commonjs/toast/Toast.js.map +0 -1
- package/lib/commonjs/toast/const.js +0 -8
- package/lib/commonjs/toast/const.js.map +0 -1
- package/lib/commonjs/toast/index.web.js +0 -114
- package/lib/commonjs/toast/index.web.js.map +0 -1
- package/lib/commonjs/toast/sonner/index.js +0 -9
- package/lib/commonjs/toast/sonner/index.js.map +0 -1
- package/lib/commonjs/toast/sonner/index.web.js +0 -9
- package/lib/commonjs/toast/sonner/index.web.js.map +0 -1
- package/lib/module/toast/Toast.js +0 -258
- package/lib/module/toast/Toast.js.map +0 -1
- package/lib/module/toast/const.js +0 -4
- package/lib/module/toast/const.js.map +0 -1
- package/lib/module/toast/index.web.js +0 -74
- package/lib/module/toast/index.web.js.map +0 -1
- package/lib/module/toast/sonner/index.js +0 -5
- package/lib/module/toast/sonner/index.js.map +0 -1
- package/lib/module/toast/sonner/index.web.js +0 -5
- package/lib/module/toast/sonner/index.web.js.map +0 -1
- package/lib/typescript/commonjs/toast/Toast.d.ts +0 -23
- package/lib/typescript/commonjs/toast/Toast.d.ts.map +0 -1
- package/lib/typescript/commonjs/toast/const.d.ts +0 -2
- package/lib/typescript/commonjs/toast/const.d.ts.map +0 -1
- package/lib/typescript/commonjs/toast/index.web.d.ts +0 -29
- package/lib/typescript/commonjs/toast/index.web.d.ts.map +0 -1
- package/lib/typescript/commonjs/toast/sonner/index.d.ts +0 -2
- package/lib/typescript/commonjs/toast/sonner/index.d.ts.map +0 -1
- package/lib/typescript/commonjs/toast/sonner/index.web.d.ts +0 -2
- package/lib/typescript/commonjs/toast/sonner/index.web.d.ts.map +0 -1
- package/lib/typescript/module/toast/Toast.d.ts +0 -23
- package/lib/typescript/module/toast/Toast.d.ts.map +0 -1
- package/lib/typescript/module/toast/const.d.ts +0 -2
- package/lib/typescript/module/toast/const.d.ts.map +0 -1
- package/lib/typescript/module/toast/index.web.d.ts +0 -29
- package/lib/typescript/module/toast/index.web.d.ts.map +0 -1
- package/lib/typescript/module/toast/sonner/index.d.ts +0 -2
- package/lib/typescript/module/toast/sonner/index.d.ts.map +0 -1
- package/lib/typescript/module/toast/sonner/index.web.d.ts +0 -2
- package/lib/typescript/module/toast/sonner/index.web.d.ts.map +0 -1
- package/src/toast/Toast.tsx +0 -269
- package/src/toast/const.ts +0 -1
- package/src/toast/index.web.tsx +0 -101
- package/src/toast/sonner/index.ts +0 -3
- package/src/toast/sonner/index.web.ts +0 -3
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ported from expo-glass-tabs v0.1.1 — src/glass-tab-bar.tsx
|
|
3
|
+
* (MIT © 2026 David Mokos). See the top-level NOTICE.
|
|
4
|
+
*
|
|
5
|
+
* The platform-agnostic body of the floating tab bar: gestures, springs, every
|
|
6
|
+
* animated style, the highlight geometry and the button. The only pieces that
|
|
7
|
+
* differ per platform are injected as component props — the capsule `Surface`
|
|
8
|
+
* (liquid glass on iOS 26, backdrop-filter on web, a solid fill everywhere
|
|
9
|
+
* else), the `Blur` behind the bar, and the `Glyph` (SF Symbol on iOS, the
|
|
10
|
+
* item's own icon node elsewhere). Same shape as `BottomSheetBase`'s `Shell`.
|
|
11
|
+
*
|
|
12
|
+
* Nothing here imports expo-glass-effect, expo-symbols, expo-router or
|
|
13
|
+
* react-native-screens, so this file is safe in a web bundle and in a
|
|
14
|
+
* consumer's type-check.
|
|
15
|
+
*/
|
|
16
|
+
import {
|
|
17
|
+
Children,
|
|
18
|
+
createContext,
|
|
19
|
+
useCallback,
|
|
20
|
+
useContext,
|
|
21
|
+
useEffect,
|
|
22
|
+
useMemo,
|
|
23
|
+
type ComponentType,
|
|
24
|
+
} from 'react';
|
|
25
|
+
import { Pressable, StyleSheet, useWindowDimensions, View } from 'react-native';
|
|
26
|
+
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
27
|
+
import Animated, {
|
|
28
|
+
Extrapolation,
|
|
29
|
+
interpolate,
|
|
30
|
+
interpolateColor,
|
|
31
|
+
runOnJS,
|
|
32
|
+
useAnimatedStyle,
|
|
33
|
+
useSharedValue,
|
|
34
|
+
withSpring,
|
|
35
|
+
type SharedValue,
|
|
36
|
+
} from 'react-native-reanimated';
|
|
37
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
38
|
+
|
|
39
|
+
import { useHaptics } from '../hooks/useHaptics';
|
|
40
|
+
import type { ProgressiveBlurProps } from '../progressive-blur/types';
|
|
41
|
+
import { setMinimized, useMinimizeState } from './minimize-context';
|
|
42
|
+
import {
|
|
43
|
+
BAR_MARGIN,
|
|
44
|
+
BLUR_BLEED,
|
|
45
|
+
EXPANDED_HEIGHT,
|
|
46
|
+
HIGHLIGHT_EXPANDED,
|
|
47
|
+
HIGHLIGHT_MINIMIZED,
|
|
48
|
+
ICON_SIZE,
|
|
49
|
+
ITEM_GAP,
|
|
50
|
+
ITEM_PAD_V,
|
|
51
|
+
LABEL_FONT_SIZE,
|
|
52
|
+
MINIMIZED_HEIGHT,
|
|
53
|
+
MINIMIZED_INSET,
|
|
54
|
+
ROW_PAD_H,
|
|
55
|
+
SLIDE_SPRING,
|
|
56
|
+
useTabBarTheme,
|
|
57
|
+
type TabBarGlyphProps,
|
|
58
|
+
type TabBarSurfaceProps,
|
|
59
|
+
} from './shared';
|
|
60
|
+
import type { TabBarButtonProps, TabBarProps, TabBarTheme } from './types';
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* What a `TabBarButton` needs from the bar it sits in. `null` when a button is
|
|
64
|
+
* rendered standalone, which keeps a lone button usable (it just falls back to
|
|
65
|
+
* its own `isFocused` for tinting and has no highlight to drive).
|
|
66
|
+
*/
|
|
67
|
+
type BarContextValue = {
|
|
68
|
+
/** Live highlight position, in tab units. Fractional while scrubbing. */
|
|
69
|
+
slideIndex: SharedValue<number>;
|
|
70
|
+
/** True while a scrub is in progress — the finger owns the highlight. */
|
|
71
|
+
isDragging: SharedValue<boolean>;
|
|
72
|
+
theme: TabBarTheme;
|
|
73
|
+
/** Set only on the CONTROLLED path (see `TabBarProps.activeIndex`). */
|
|
74
|
+
activeIndex: number | undefined;
|
|
75
|
+
/** Report a selection made by a button press. */
|
|
76
|
+
selectIndex: (index: number) => void;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const BarContext = createContext<BarContextValue | null>(null);
|
|
80
|
+
|
|
81
|
+
interface TabBarBodyProps extends TabBarProps {
|
|
82
|
+
Surface: ComponentType<TabBarSurfaceProps>;
|
|
83
|
+
Blur: ComponentType<ProgressiveBlurProps>;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Floating tab bar with Revolut-style minimize-on-scroll, a sliding highlight
|
|
88
|
+
* and finger scrubbing.
|
|
89
|
+
*/
|
|
90
|
+
function TabBarBody({
|
|
91
|
+
Surface,
|
|
92
|
+
Blur,
|
|
93
|
+
children,
|
|
94
|
+
activeIndex,
|
|
95
|
+
onIndexChange,
|
|
96
|
+
theme: themeOverrides,
|
|
97
|
+
haptics = true,
|
|
98
|
+
style,
|
|
99
|
+
...viewProps
|
|
100
|
+
}: TabBarBodyProps) {
|
|
101
|
+
const insets = useSafeAreaInsets();
|
|
102
|
+
const { width: windowWidth } = useWindowDimensions();
|
|
103
|
+
const minimized = useMinimizeState();
|
|
104
|
+
const progress = minimized.progress;
|
|
105
|
+
const slideIndex = useSharedValue(0);
|
|
106
|
+
const isDragging = useSharedValue(false);
|
|
107
|
+
const lastTicked = useSharedValue(-1);
|
|
108
|
+
const tabCount = Math.max(Children.count(children), 1);
|
|
109
|
+
const theme = useTabBarTheme(themeOverrides);
|
|
110
|
+
const impact = useHaptics();
|
|
111
|
+
|
|
112
|
+
// Picker-style tick while the highlight crosses tab boundaries mid-drag.
|
|
113
|
+
// `useHaptics` already no-ops on web, when the optional `expo-haptics` peer
|
|
114
|
+
// is absent, and when a `BloomHapticsProvider` has haptics turned off — so
|
|
115
|
+
// the only decision left here is the caller's own `haptics` prop.
|
|
116
|
+
const tick = useCallback(() => {
|
|
117
|
+
if (haptics) impact('light');
|
|
118
|
+
}, [haptics, impact]);
|
|
119
|
+
|
|
120
|
+
// Navigation happens only on release — switching screens live while
|
|
121
|
+
// scrubbing makes the content jump under the finger.
|
|
122
|
+
const selectIndex = useCallback((index: number) => onIndexChange?.(index), [onIndexChange]);
|
|
123
|
+
|
|
124
|
+
// CONTROLLED path: the bar owns the highlight when `activeIndex` is provided,
|
|
125
|
+
// so it tracks the prop even when a consumer renders custom children instead
|
|
126
|
+
// of `TabBarButton`s. Syncing an imperative animation to a controlled prop is
|
|
127
|
+
// a legitimate effect (external-system sync), the same shape `Tabs` uses for
|
|
128
|
+
// its underline. The focus-driven path is handled per-button instead — see
|
|
129
|
+
// `TabBarButtonBody`.
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
if (activeIndex === undefined) return;
|
|
132
|
+
// While scrubbing the finger owns the highlight; never fight it.
|
|
133
|
+
if (isDragging.value) return;
|
|
134
|
+
slideIndex.value = withSpring(activeIndex, SLIDE_SPRING);
|
|
135
|
+
}, [activeIndex, slideIndex, isDragging]);
|
|
136
|
+
|
|
137
|
+
// Scrubbing: the highlight tracks the finger 1:1 while dragging (no spring —
|
|
138
|
+
// it must feel attached), haptic ticks fire on boundary crossings, and
|
|
139
|
+
// navigation happens only on release. Taps are handled by a Tap gesture
|
|
140
|
+
// racing the pan — the detector consumes the bar's touches, so the inner
|
|
141
|
+
// Pressables never receive them.
|
|
142
|
+
const gesture = useMemo(() => {
|
|
143
|
+
const indexAtX = (x: number, minimizedValue: number) => {
|
|
144
|
+
'worklet';
|
|
145
|
+
const sideInset = interpolate(
|
|
146
|
+
minimizedValue,
|
|
147
|
+
[0, 1],
|
|
148
|
+
[0, MINIMIZED_INSET],
|
|
149
|
+
Extrapolation.CLAMP,
|
|
150
|
+
);
|
|
151
|
+
const barWidth = windowWidth - BAR_MARGIN * 2 - sideInset * 2;
|
|
152
|
+
const itemWidth = (barWidth - ROW_PAD_H * 2) / tabCount;
|
|
153
|
+
const raw = (x - ROW_PAD_H) / itemWidth - 0.5;
|
|
154
|
+
return Math.min(Math.max(raw, 0), tabCount - 1);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const pan = Gesture.Pan()
|
|
158
|
+
.activeOffsetX([-6, 6])
|
|
159
|
+
.failOffsetY([-14, 14])
|
|
160
|
+
.onStart(() => {
|
|
161
|
+
isDragging.value = true;
|
|
162
|
+
lastTicked.value = Math.round(slideIndex.value);
|
|
163
|
+
// Scrubbing is a deliberate bar interaction — surface the labels.
|
|
164
|
+
setMinimized(minimized, 0);
|
|
165
|
+
})
|
|
166
|
+
.onUpdate((event) => {
|
|
167
|
+
const index = indexAtX(event.x, progress.value);
|
|
168
|
+
slideIndex.value = index;
|
|
169
|
+
|
|
170
|
+
const rounded = Math.round(index);
|
|
171
|
+
if (rounded !== lastTicked.value) {
|
|
172
|
+
lastTicked.value = rounded;
|
|
173
|
+
runOnJS(tick)();
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
.onFinalize(() => {
|
|
177
|
+
// Fires on failure too (e.g. the touch was a tap) — only act when the
|
|
178
|
+
// pan actually activated, or we'd stomp the tap's navigation.
|
|
179
|
+
if (!isDragging.value) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const rounded = Math.round(slideIndex.value);
|
|
183
|
+
slideIndex.value = withSpring(rounded, SLIDE_SPRING);
|
|
184
|
+
runOnJS(selectIndex)(rounded);
|
|
185
|
+
isDragging.value = false;
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const tap = Gesture.Tap()
|
|
189
|
+
// Real fingers drift a few points — the default tolerance (~2pt) makes
|
|
190
|
+
// ordinary taps fail. Past 6pt horizontal the pan takes over.
|
|
191
|
+
.maxDistance(16)
|
|
192
|
+
.maxDuration(400)
|
|
193
|
+
.onEnd((event, success) => {
|
|
194
|
+
if (!success) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const index = Math.round(indexAtX(event.x, progress.value));
|
|
198
|
+
slideIndex.value = withSpring(index, SLIDE_SPRING);
|
|
199
|
+
setMinimized(minimized, 0);
|
|
200
|
+
runOnJS(selectIndex)(index);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
return Gesture.Race(pan, tap);
|
|
204
|
+
}, [
|
|
205
|
+
windowWidth,
|
|
206
|
+
tabCount,
|
|
207
|
+
selectIndex,
|
|
208
|
+
tick,
|
|
209
|
+
isDragging,
|
|
210
|
+
lastTicked,
|
|
211
|
+
slideIndex,
|
|
212
|
+
minimized,
|
|
213
|
+
progress,
|
|
214
|
+
]);
|
|
215
|
+
|
|
216
|
+
// CRITICAL — every shared value and every scalar a mapper READS must appear
|
|
217
|
+
// in its dependency array. On web WITHOUT the react-native-worklets babel
|
|
218
|
+
// plugin (what every Oxy RN-Web app ships) reanimated cannot auto-detect a
|
|
219
|
+
// worklet's reads, so it drives the mapper off the deps array instead: with a
|
|
220
|
+
// missing or incomplete array the mapper runs ONCE and freezes at the first
|
|
221
|
+
// frame while the shared values keep animating underneath — the bar would
|
|
222
|
+
// never minimize and the highlight would never move, with no error anywhere.
|
|
223
|
+
// Native (plugin present) auto-tracks and ignores the extra deps, so listing
|
|
224
|
+
// them is correct on both platforms. Same rule as `BottomSheetBase` and
|
|
225
|
+
// `ZoomableImageGallery`. Do NOT strip these.
|
|
226
|
+
const barStyle = useAnimatedStyle(() => {
|
|
227
|
+
const height = interpolate(
|
|
228
|
+
progress.value,
|
|
229
|
+
[0, 1],
|
|
230
|
+
[EXPANDED_HEIGHT, MINIMIZED_HEIGHT],
|
|
231
|
+
Extrapolation.CLAMP,
|
|
232
|
+
);
|
|
233
|
+
return {
|
|
234
|
+
height,
|
|
235
|
+
// Revolut-style: the pill shrinks in both dimensions.
|
|
236
|
+
marginHorizontal: interpolate(
|
|
237
|
+
progress.value,
|
|
238
|
+
[0, 1],
|
|
239
|
+
[0, MINIMIZED_INSET],
|
|
240
|
+
Extrapolation.CLAMP,
|
|
241
|
+
),
|
|
242
|
+
};
|
|
243
|
+
}, [progress]);
|
|
244
|
+
|
|
245
|
+
// The capsule shape lives on the surface itself: iOS 26 glass renders its own
|
|
246
|
+
// native corner configuration (true squircle + rim lighting). Clipping a
|
|
247
|
+
// rectangular glass with an RN mask breaks that.
|
|
248
|
+
const shapeStyle = useAnimatedStyle(() => {
|
|
249
|
+
const height = interpolate(
|
|
250
|
+
progress.value,
|
|
251
|
+
[0, 1],
|
|
252
|
+
[EXPANDED_HEIGHT, MINIMIZED_HEIGHT],
|
|
253
|
+
Extrapolation.CLAMP,
|
|
254
|
+
);
|
|
255
|
+
return { borderRadius: height / 2 };
|
|
256
|
+
}, [progress]);
|
|
257
|
+
|
|
258
|
+
// One shared highlight that slides between tabs (transform-only → GPU). All
|
|
259
|
+
// geometry derives from shared values, never from layout callbacks.
|
|
260
|
+
const highlightStyle = useAnimatedStyle(() => {
|
|
261
|
+
const barHeight = interpolate(
|
|
262
|
+
progress.value,
|
|
263
|
+
[0, 1],
|
|
264
|
+
[EXPANDED_HEIGHT, MINIMIZED_HEIGHT],
|
|
265
|
+
Extrapolation.CLAMP,
|
|
266
|
+
);
|
|
267
|
+
const height = interpolate(
|
|
268
|
+
progress.value,
|
|
269
|
+
[0, 1],
|
|
270
|
+
[HIGHLIGHT_EXPANDED, HIGHLIGHT_MINIMIZED],
|
|
271
|
+
Extrapolation.CLAMP,
|
|
272
|
+
);
|
|
273
|
+
const sideInset = interpolate(
|
|
274
|
+
progress.value,
|
|
275
|
+
[0, 1],
|
|
276
|
+
[0, MINIMIZED_INSET],
|
|
277
|
+
Extrapolation.CLAMP,
|
|
278
|
+
);
|
|
279
|
+
const barWidth = windowWidth - BAR_MARGIN * 2 - sideInset * 2;
|
|
280
|
+
const itemWidth = (barWidth - ROW_PAD_H * 2) / tabCount;
|
|
281
|
+
return {
|
|
282
|
+
height,
|
|
283
|
+
width: itemWidth,
|
|
284
|
+
borderRadius: height / 2,
|
|
285
|
+
top: (barHeight - height) / 2,
|
|
286
|
+
transform: [{ translateX: ROW_PAD_H + itemWidth * slideIndex.value }],
|
|
287
|
+
};
|
|
288
|
+
}, [progress, slideIndex, windowWidth, tabCount]);
|
|
289
|
+
|
|
290
|
+
const bottomOffset = Math.max(insets.bottom - 16, 12);
|
|
291
|
+
const barContext = useMemo(
|
|
292
|
+
() => ({ slideIndex, isDragging, theme, activeIndex, selectIndex }),
|
|
293
|
+
[slideIndex, isDragging, theme, activeIndex, selectIndex],
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
return (
|
|
297
|
+
<View {...viewProps} style={[styles.root, style]}>
|
|
298
|
+
{/* Progressive blur rising from the screen's bottom edge behind the pill. */}
|
|
299
|
+
<Blur
|
|
300
|
+
direction="bottom"
|
|
301
|
+
style={{
|
|
302
|
+
position: 'absolute',
|
|
303
|
+
left: 0,
|
|
304
|
+
right: 0,
|
|
305
|
+
bottom: 0,
|
|
306
|
+
height: bottomOffset + EXPANDED_HEIGHT + BLUR_BLEED,
|
|
307
|
+
}}
|
|
308
|
+
/>
|
|
309
|
+
<View style={[styles.barWrap, { marginBottom: bottomOffset }]}>
|
|
310
|
+
<GestureDetector gesture={gesture}>
|
|
311
|
+
<Animated.View style={barStyle}>
|
|
312
|
+
<Surface theme={theme} style={shapeStyle} />
|
|
313
|
+
<Animated.View
|
|
314
|
+
style={[styles.highlight, { backgroundColor: theme.highlight }, highlightStyle]}
|
|
315
|
+
/>
|
|
316
|
+
<View accessibilityRole="tablist" style={styles.itemRow}>
|
|
317
|
+
<BarContext.Provider value={barContext}>{children}</BarContext.Provider>
|
|
318
|
+
</View>
|
|
319
|
+
</Animated.View>
|
|
320
|
+
</GestureDetector>
|
|
321
|
+
</View>
|
|
322
|
+
</View>
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
interface TabBarButtonBodyProps extends TabBarButtonProps {
|
|
327
|
+
Glyph: ComponentType<TabBarGlyphProps>;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** One tab trigger: icon + label that fades out when the bar minimizes. */
|
|
331
|
+
function TabBarButtonBody({
|
|
332
|
+
Glyph,
|
|
333
|
+
item,
|
|
334
|
+
index,
|
|
335
|
+
isFocused,
|
|
336
|
+
onPress,
|
|
337
|
+
style,
|
|
338
|
+
...pressableProps
|
|
339
|
+
}: TabBarButtonBodyProps) {
|
|
340
|
+
const minimized = useMinimizeState();
|
|
341
|
+
const progress = minimized.progress;
|
|
342
|
+
const bar = useContext(BarContext);
|
|
343
|
+
// Resolved outside the bar too, so a standalone button still themes itself.
|
|
344
|
+
const standaloneTheme = useTabBarTheme();
|
|
345
|
+
const theme = bar?.theme ?? standaloneTheme;
|
|
346
|
+
const slideIndex = bar?.slideIndex;
|
|
347
|
+
// The two paths meet here: an explicit `isFocused` (router adapter) wins;
|
|
348
|
+
// otherwise focus comes from the bar's controlled `activeIndex`.
|
|
349
|
+
const focused = isFocused ?? (bar?.activeIndex === index);
|
|
350
|
+
|
|
351
|
+
// FOCUS-DRIVEN path only. Covers programmatic navigation too (deep links,
|
|
352
|
+
// back gestures) — the trigger re-renders focused and the highlight follows.
|
|
353
|
+
// While scrubbing, the finger owns the highlight; never fight it with a
|
|
354
|
+
// spring. On the controlled path the BAR drives the highlight instead, so
|
|
355
|
+
// this stays out of the way to avoid two writers on one shared value.
|
|
356
|
+
useEffect(() => {
|
|
357
|
+
if (isFocused === undefined || !isFocused || !bar) return;
|
|
358
|
+
if (bar.isDragging.value) return;
|
|
359
|
+
bar.slideIndex.value = withSpring(index, SLIDE_SPRING);
|
|
360
|
+
}, [isFocused, index, bar]);
|
|
361
|
+
|
|
362
|
+
// Tint follows the sliding highlight, not navigation focus: whatever the pill
|
|
363
|
+
// is over lights up — live while scrubbing, traveling on taps. Without a bar
|
|
364
|
+
// there is nothing to follow, so it falls back to plain focus.
|
|
365
|
+
// (Deps: see the CRITICAL note in `TabBarBody`.)
|
|
366
|
+
const activeGlyphStyle = useAnimatedStyle(
|
|
367
|
+
() => ({
|
|
368
|
+
opacity: slideIndex ? 1 - Math.min(Math.abs(slideIndex.value - index), 1) : focused ? 1 : 0,
|
|
369
|
+
}),
|
|
370
|
+
[slideIndex, index, focused],
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
const labelStyle = useAnimatedStyle(
|
|
374
|
+
() => ({
|
|
375
|
+
opacity: interpolate(progress.value, [0, 0.4], [1, 0], Extrapolation.CLAMP),
|
|
376
|
+
color: slideIndex
|
|
377
|
+
? interpolateColor(
|
|
378
|
+
Math.min(Math.abs(slideIndex.value - index), 1),
|
|
379
|
+
[0, 1],
|
|
380
|
+
[theme.activeTint, theme.inactiveTint],
|
|
381
|
+
)
|
|
382
|
+
: focused
|
|
383
|
+
? theme.activeTint
|
|
384
|
+
: theme.inactiveTint,
|
|
385
|
+
}),
|
|
386
|
+
[progress, slideIndex, index, focused, theme],
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
// Height is animated EXPLICITLY (not derived from children) so the icon stays
|
|
390
|
+
// perfectly centered every frame — layout-driven sizing lags a frame behind
|
|
391
|
+
// UI-thread animation.
|
|
392
|
+
const boxStyle = useAnimatedStyle(
|
|
393
|
+
() => ({
|
|
394
|
+
height: interpolate(
|
|
395
|
+
progress.value,
|
|
396
|
+
[0, 1],
|
|
397
|
+
[HIGHLIGHT_EXPANDED, HIGHLIGHT_MINIMIZED],
|
|
398
|
+
Extrapolation.CLAMP,
|
|
399
|
+
),
|
|
400
|
+
}),
|
|
401
|
+
[progress],
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
return (
|
|
405
|
+
<Pressable
|
|
406
|
+
// Accessibility defaults come BEFORE the spread so an explicit prop — or
|
|
407
|
+
// one injected by a router trigger — still wins.
|
|
408
|
+
accessibilityRole="tab"
|
|
409
|
+
accessibilityState={{ selected: focused }}
|
|
410
|
+
accessibilityLabel={item.label}
|
|
411
|
+
{...pressableProps}
|
|
412
|
+
onPress={(event) => {
|
|
413
|
+
// The bar's GestureDetector normally consumes touches; this still fires
|
|
414
|
+
// for assistive-technology activation (VoiceOver) and keyboard focus.
|
|
415
|
+
if (bar) bar.slideIndex.value = withSpring(index, SLIDE_SPRING);
|
|
416
|
+
setMinimized(minimized, 0);
|
|
417
|
+
// Controlled path only. On the focus-driven path the trigger's own
|
|
418
|
+
// `onPress` below performs the navigation, so reporting the selection
|
|
419
|
+
// here as well would navigate twice.
|
|
420
|
+
if (isFocused === undefined) bar?.selectIndex(index);
|
|
421
|
+
onPress?.(event);
|
|
422
|
+
}}
|
|
423
|
+
// `Pressable`'s `style` also accepts a function of the press state; both
|
|
424
|
+
// forms are composed on top of the flex-share base so a caller can tint
|
|
425
|
+
// or scale a tab on press without losing the layout.
|
|
426
|
+
style={
|
|
427
|
+
typeof style === 'function'
|
|
428
|
+
? (state) => [styles.button, style(state)]
|
|
429
|
+
: [styles.button, style]
|
|
430
|
+
}
|
|
431
|
+
>
|
|
432
|
+
<Animated.View style={[styles.itemBox, boxStyle]}>
|
|
433
|
+
{/* Inactive glyph underneath, active glyph crossfading on top. */}
|
|
434
|
+
<View>
|
|
435
|
+
<Glyph item={item} tint={theme.inactiveTint} size={ICON_SIZE} />
|
|
436
|
+
<Animated.View style={[StyleSheet.absoluteFill, styles.glyphLayer, activeGlyphStyle]}>
|
|
437
|
+
<Glyph item={item} tint={theme.activeTint} size={ICON_SIZE} />
|
|
438
|
+
</Animated.View>
|
|
439
|
+
</View>
|
|
440
|
+
{/* Fades out and is clipped by the shrinking box — no layout anim. */}
|
|
441
|
+
<Animated.Text numberOfLines={1} style={[styles.label, labelStyle]}>
|
|
442
|
+
{item.label}
|
|
443
|
+
</Animated.Text>
|
|
444
|
+
</Animated.View>
|
|
445
|
+
</Pressable>
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Bind the platform `Surface` + `Blur` to the shared body, once per entry
|
|
451
|
+
* module. The returned component is created at module scope, so its identity is
|
|
452
|
+
* stable across renders. `index.ts` binds the native/neutral pieces and
|
|
453
|
+
* `index.web.tsx` binds the web pieces explicitly — the same split
|
|
454
|
+
* `BottomSheet` makes with its `Shell`.
|
|
455
|
+
*/
|
|
456
|
+
export function createTabBar(
|
|
457
|
+
Surface: ComponentType<TabBarSurfaceProps>,
|
|
458
|
+
Blur: ComponentType<ProgressiveBlurProps>,
|
|
459
|
+
) {
|
|
460
|
+
const TabBar = (props: TabBarProps) => <TabBarBody {...props} Surface={Surface} Blur={Blur} />;
|
|
461
|
+
TabBar.displayName = 'TabBar';
|
|
462
|
+
return TabBar;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/** Bind the platform `Glyph` to the shared button body. See `createTabBar`. */
|
|
466
|
+
export function createTabBarButton(Glyph: ComponentType<TabBarGlyphProps>) {
|
|
467
|
+
const TabBarButton = (props: TabBarButtonProps) => <TabBarButtonBody {...props} Glyph={Glyph} />;
|
|
468
|
+
TabBarButton.displayName = 'TabBarButton';
|
|
469
|
+
return TabBarButton;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const styles = StyleSheet.create({
|
|
473
|
+
root: {
|
|
474
|
+
position: 'absolute',
|
|
475
|
+
left: 0,
|
|
476
|
+
right: 0,
|
|
477
|
+
bottom: 0,
|
|
478
|
+
// In the style object, not as a prop: RN-Web deprecated the `pointerEvents`
|
|
479
|
+
// prop and warns on every render when it is used.
|
|
480
|
+
pointerEvents: 'box-none',
|
|
481
|
+
},
|
|
482
|
+
barWrap: {
|
|
483
|
+
marginHorizontal: BAR_MARGIN,
|
|
484
|
+
pointerEvents: 'box-none',
|
|
485
|
+
},
|
|
486
|
+
highlight: {
|
|
487
|
+
position: 'absolute',
|
|
488
|
+
left: 0,
|
|
489
|
+
borderCurve: 'continuous',
|
|
490
|
+
},
|
|
491
|
+
itemRow: {
|
|
492
|
+
flex: 1,
|
|
493
|
+
flexDirection: 'row',
|
|
494
|
+
alignItems: 'center',
|
|
495
|
+
paddingHorizontal: ROW_PAD_H,
|
|
496
|
+
},
|
|
497
|
+
button: {
|
|
498
|
+
flex: 1,
|
|
499
|
+
alignItems: 'center',
|
|
500
|
+
justifyContent: 'center',
|
|
501
|
+
},
|
|
502
|
+
itemBox: {
|
|
503
|
+
alignSelf: 'stretch',
|
|
504
|
+
alignItems: 'center',
|
|
505
|
+
paddingTop: ITEM_PAD_V,
|
|
506
|
+
overflow: 'hidden',
|
|
507
|
+
},
|
|
508
|
+
glyphLayer: {
|
|
509
|
+
alignItems: 'center',
|
|
510
|
+
justifyContent: 'center',
|
|
511
|
+
},
|
|
512
|
+
label: {
|
|
513
|
+
fontSize: LABEL_FONT_SIZE,
|
|
514
|
+
fontWeight: '600',
|
|
515
|
+
marginTop: ITEM_GAP,
|
|
516
|
+
},
|
|
517
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ported from expo-glass-tabs v0.1.1 — src/glass-tab-bar.tsx
|
|
3
|
+
* (MIT © 2026 David Mokos). See the top-level NOTICE.
|
|
4
|
+
*
|
|
5
|
+
* expo-router bindings for the tab bar. This subpath
|
|
6
|
+
* (`@oxyhq/bloom/tab-bar/expo-router`) is the ONLY place in Bloom that may
|
|
7
|
+
* import `expo-router` or `react-native-screens`, which is why it is a separate
|
|
8
|
+
* entry: the core `@oxyhq/bloom/tab-bar` stays usable in an app with no router
|
|
9
|
+
* at all, and neither optional peer is pulled into a bundle that never asks for
|
|
10
|
+
* it.
|
|
11
|
+
*/
|
|
12
|
+
import type { TabListProps, TabTriggerSlotProps } from 'expo-router/ui';
|
|
13
|
+
|
|
14
|
+
import { TabBar, TabBarButton } from '../index';
|
|
15
|
+
import type { TabBarItem, TabBarProps } from '../types';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* `activeIndex` is omitted deliberately: with a router the highlight is driven
|
|
19
|
+
* by each trigger's own focus (see `RouterTabBarButton`), which is what keeps it
|
|
20
|
+
* correct through deep links, back gestures and any other programmatic
|
|
21
|
+
* navigation. Setting both would put two writers on one shared value.
|
|
22
|
+
*/
|
|
23
|
+
export type RouterTabBarProps = TabListProps & Omit<TabBarProps, 'activeIndex'>;
|
|
24
|
+
|
|
25
|
+
export type RouterTabBarButtonProps = TabTriggerSlotProps & {
|
|
26
|
+
item: TabBarItem;
|
|
27
|
+
/** Position in the bar — must match the trigger's order in the `TabList`. */
|
|
28
|
+
index: number;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The bar, as expo-router's `<TabList asChild>` child.
|
|
33
|
+
*
|
|
34
|
+
* Navigation happens through `onIndexChange`, not through the triggers: the
|
|
35
|
+
* bar's gesture detector consumes the touches (that is what makes scrubbing
|
|
36
|
+
* possible), so a tap never reaches the trigger's own `Pressable`.
|
|
37
|
+
*
|
|
38
|
+
* ```tsx
|
|
39
|
+
* <TabList asChild>
|
|
40
|
+
* <RouterTabBar onIndexChange={(i) => router.navigate(TABS[i].href)}>
|
|
41
|
+
* {TABS.map(({ href, ...item }, index) => (
|
|
42
|
+
* <TabTrigger key={item.name} name={item.name} href={href} asChild>
|
|
43
|
+
* <RouterTabBarButton item={item} index={index} />
|
|
44
|
+
* </TabTrigger>
|
|
45
|
+
* ))}
|
|
46
|
+
* </RouterTabBar>
|
|
47
|
+
* </TabList>
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export function RouterTabBar({
|
|
51
|
+
// `asChild` is `TabList`'s own instruction to forward its props to this
|
|
52
|
+
// component instead of wrapping them in a `<View>`. It is consumed there and
|
|
53
|
+
// is meaningless below, so it is dropped rather than passed to a real view.
|
|
54
|
+
asChild: _asChild,
|
|
55
|
+
...props
|
|
56
|
+
}: RouterTabBarProps) {
|
|
57
|
+
return <TabBar {...props} />;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
RouterTabBar.displayName = 'RouterTabBar';
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* One trigger, as expo-router's `<TabTrigger asChild>` child. The trigger
|
|
64
|
+
* supplies `isFocused` and `onPress`; the button uses the first to drive the
|
|
65
|
+
* highlight and the second to navigate on keyboard / assistive-technology
|
|
66
|
+
* activation.
|
|
67
|
+
*/
|
|
68
|
+
export function RouterTabBarButton({
|
|
69
|
+
item,
|
|
70
|
+
index,
|
|
71
|
+
// The trigger's resolved path, injected for react-native-web's anchor
|
|
72
|
+
// rendering. `Pressable` has no such prop, and navigation already runs
|
|
73
|
+
// through the bar's `onIndexChange`, so it stops here.
|
|
74
|
+
href: _href,
|
|
75
|
+
...props
|
|
76
|
+
}: RouterTabBarButtonProps) {
|
|
77
|
+
return <TabBarButton {...props} item={item} index={index} />;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
RouterTabBarButton.displayName = 'RouterTabBarButton';
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ported from expo-glass-tabs v0.1.1 — src/fading-tab-slot.tsx
|
|
3
|
+
* (MIT © 2026 David Mokos). See the top-level NOTICE.
|
|
4
|
+
*/
|
|
5
|
+
import { useEffect, useRef, type PropsWithChildren } from 'react';
|
|
6
|
+
import { StyleSheet } from 'react-native';
|
|
7
|
+
import Animated, {
|
|
8
|
+
Easing,
|
|
9
|
+
interpolate,
|
|
10
|
+
useAnimatedStyle,
|
|
11
|
+
useSharedValue,
|
|
12
|
+
withTiming,
|
|
13
|
+
} from 'react-native-reanimated';
|
|
14
|
+
import { Screen } from 'react-native-screens';
|
|
15
|
+
import type { TabsDescriptor, TabsSlotRenderOptions } from 'expo-router/ui';
|
|
16
|
+
|
|
17
|
+
/** Strong ease-out — entering content should feel instant, then settle. */
|
|
18
|
+
const EASE_OUT = Easing.bezier(0.23, 1, 0.32, 1);
|
|
19
|
+
const ENTER_DURATION = 220;
|
|
20
|
+
/** Never enter from nothing: a whisper of depth, no zoom. */
|
|
21
|
+
const ENTER_SCALE = 0.985;
|
|
22
|
+
|
|
23
|
+
function FadeIn({ focused, children }: PropsWithChildren<{ focused: boolean }>) {
|
|
24
|
+
const progress = useSharedValue(1);
|
|
25
|
+
const isFirstRender = useRef(true);
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
// Don't animate the very first screen on app launch.
|
|
29
|
+
if (isFirstRender.current) {
|
|
30
|
+
isFirstRender.current = false;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (focused) {
|
|
34
|
+
progress.value = 0;
|
|
35
|
+
progress.value = withTiming(1, { duration: ENTER_DURATION, easing: EASE_OUT });
|
|
36
|
+
} else {
|
|
37
|
+
// Outgoing screen hides instantly (display: none) — only entries animate.
|
|
38
|
+
progress.value = 0;
|
|
39
|
+
}
|
|
40
|
+
}, [focused, progress]);
|
|
41
|
+
|
|
42
|
+
// `progress` must stay in the deps array — see the CRITICAL note in
|
|
43
|
+
// `TabBarBase`: without the worklets babel plugin (web) an omitted array
|
|
44
|
+
// freezes the mapper at the first frame, and every tab would enter invisible.
|
|
45
|
+
const style = useAnimatedStyle(
|
|
46
|
+
() => ({
|
|
47
|
+
opacity: progress.value,
|
|
48
|
+
transform: [{ scale: interpolate(progress.value, [0, 1], [ENTER_SCALE, 1]) }],
|
|
49
|
+
}),
|
|
50
|
+
[progress],
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
return <Animated.View style={[styles.fade, style]}>{children}</Animated.View>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Drop-in for `TabSlot`'s `renderFn`: identical to expo-router's default render,
|
|
58
|
+
* plus a subtle fade + micro-scale on the screen becoming focused.
|
|
59
|
+
*
|
|
60
|
+
* ```tsx
|
|
61
|
+
* <TabSlot style={{ height: '100%' }} renderFn={renderFadingTabScreen} />
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export function renderFadingTabScreen(
|
|
65
|
+
descriptor: TabsDescriptor,
|
|
66
|
+
{ isFocused, loaded, detachInactiveScreens }: TabsSlotRenderOptions,
|
|
67
|
+
) {
|
|
68
|
+
const { lazy = true, unmountOnBlur, freezeOnBlur } = descriptor.options;
|
|
69
|
+
if (unmountOnBlur && !isFocused) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
if (lazy && !loaded && !isFocused) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
return (
|
|
76
|
+
<Screen
|
|
77
|
+
key={descriptor.route.key}
|
|
78
|
+
enabled={detachInactiveScreens}
|
|
79
|
+
activityState={isFocused ? 2 : 0}
|
|
80
|
+
freezeOnBlur={freezeOnBlur}
|
|
81
|
+
style={[styles.screen, isFocused ? styles.focused : styles.unfocused]}
|
|
82
|
+
>
|
|
83
|
+
<FadeIn focused={isFocused}>{descriptor.render()}</FadeIn>
|
|
84
|
+
</Screen>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const styles = StyleSheet.create({
|
|
89
|
+
fade: {
|
|
90
|
+
flex: 1,
|
|
91
|
+
},
|
|
92
|
+
screen: {
|
|
93
|
+
flex: 1,
|
|
94
|
+
position: 'relative',
|
|
95
|
+
height: '100%',
|
|
96
|
+
},
|
|
97
|
+
focused: {
|
|
98
|
+
zIndex: 1,
|
|
99
|
+
display: 'flex',
|
|
100
|
+
flexShrink: 0,
|
|
101
|
+
flexGrow: 1,
|
|
102
|
+
},
|
|
103
|
+
unfocused: {
|
|
104
|
+
zIndex: -1,
|
|
105
|
+
display: 'none',
|
|
106
|
+
flexShrink: 1,
|
|
107
|
+
flexGrow: 0,
|
|
108
|
+
},
|
|
109
|
+
});
|