@oxyhq/bloom 0.52.0 → 0.53.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.
Files changed (205) hide show
  1. package/lib/commonjs/bottom-sheet/BottomSheetBase.js +7 -4
  2. package/lib/commonjs/bottom-sheet/BottomSheetBase.js.map +1 -1
  3. package/lib/commonjs/connection-dots/ConnectionDots.web.js.map +1 -1
  4. package/lib/commonjs/design-tokens/shadows.js +4 -0
  5. package/lib/commonjs/design-tokens/shadows.js.map +1 -1
  6. package/lib/commonjs/dialog/Dialog.js.map +1 -1
  7. package/lib/commonjs/dialog/Dialog.web.js +26 -10
  8. package/lib/commonjs/dialog/Dialog.web.js.map +1 -1
  9. package/lib/commonjs/index.js +66 -66
  10. package/lib/commonjs/index.js.map +1 -1
  11. package/lib/commonjs/index.web.js +90 -90
  12. package/lib/commonjs/index.web.js.map +1 -1
  13. package/lib/commonjs/loading/Loading.web.js.map +1 -1
  14. package/lib/commonjs/loading/SpinnerIcon.web.js +10 -6
  15. package/lib/commonjs/loading/SpinnerIcon.web.js.map +1 -1
  16. package/lib/commonjs/motion/index.js +1 -1
  17. package/lib/commonjs/motion/index.web.js +32 -0
  18. package/lib/commonjs/motion/index.web.js.map +1 -0
  19. package/lib/commonjs/motion/motion.js +10 -2
  20. package/lib/commonjs/motion/motion.js.map +1 -1
  21. package/lib/commonjs/motion/motion.web.js +137 -0
  22. package/lib/commonjs/motion/motion.web.js.map +1 -0
  23. package/lib/commonjs/progressive-blur/index.web.js.map +1 -1
  24. package/lib/commonjs/styles/web-view-style.js +33 -0
  25. package/lib/commonjs/styles/web-view-style.js.map +1 -1
  26. package/lib/commonjs/tab-bar/TabBarBase.js +18 -4
  27. package/lib/commonjs/tab-bar/TabBarBase.js.map +1 -1
  28. package/lib/commonjs/tab-bar/index.js +5 -5
  29. package/lib/commonjs/tab-bar/index.js.map +1 -1
  30. package/lib/commonjs/tab-bar/index.web.js +5 -5
  31. package/lib/commonjs/tab-bar/index.web.js.map +1 -1
  32. package/lib/commonjs/tab-bar/shared.js +3 -3
  33. package/lib/commonjs/tab-bar/surface.web.js.map +1 -1
  34. package/lib/commonjs/tab-bar/use-footprint.js +56 -0
  35. package/lib/commonjs/tab-bar/use-footprint.js.map +1 -0
  36. package/lib/commonjs/toast/ToastRow.js +7 -3
  37. package/lib/commonjs/toast/ToastRow.js.map +1 -1
  38. package/lib/commonjs/toast/ToastSwipeHandler.js +29 -13
  39. package/lib/commonjs/toast/ToastSwipeHandler.js.map +1 -1
  40. package/lib/commonjs/toast/constants.js +29 -1
  41. package/lib/commonjs/toast/constants.js.map +1 -1
  42. package/lib/commonjs/toast/position-utils.js +9 -4
  43. package/lib/commonjs/toast/position-utils.js.map +1 -1
  44. package/lib/commonjs/zoomable-image-gallery/ZoomableImageGallery.js +12 -9
  45. package/lib/commonjs/zoomable-image-gallery/ZoomableImageGallery.js.map +1 -1
  46. package/lib/module/bottom-sheet/BottomSheetBase.js +7 -4
  47. package/lib/module/bottom-sheet/BottomSheetBase.js.map +1 -1
  48. package/lib/module/connection-dots/ConnectionDots.web.js.map +1 -1
  49. package/lib/module/design-tokens/shadows.js +4 -0
  50. package/lib/module/design-tokens/shadows.js.map +1 -1
  51. package/lib/module/dialog/Dialog.js.map +1 -1
  52. package/lib/module/dialog/Dialog.web.js +26 -10
  53. package/lib/module/dialog/Dialog.web.js.map +1 -1
  54. package/lib/module/index.js +1 -1
  55. package/lib/module/index.js.map +1 -1
  56. package/lib/module/index.web.js +1 -1
  57. package/lib/module/index.web.js.map +1 -1
  58. package/lib/module/loading/Loading.web.js.map +1 -1
  59. package/lib/module/loading/SpinnerIcon.web.js +10 -6
  60. package/lib/module/loading/SpinnerIcon.web.js.map +1 -1
  61. package/lib/module/motion/index.js +1 -1
  62. package/lib/module/motion/index.js.map +1 -1
  63. package/lib/module/motion/index.web.js +10 -0
  64. package/lib/module/motion/index.web.js.map +1 -0
  65. package/lib/module/motion/motion.js +10 -2
  66. package/lib/module/motion/motion.js.map +1 -1
  67. package/lib/module/motion/motion.web.js +133 -0
  68. package/lib/module/motion/motion.web.js.map +1 -0
  69. package/lib/module/progressive-blur/index.web.js.map +1 -1
  70. package/lib/module/styles/web-view-style.js +33 -0
  71. package/lib/module/styles/web-view-style.js.map +1 -1
  72. package/lib/module/tab-bar/TabBarBase.js +18 -4
  73. package/lib/module/tab-bar/TabBarBase.js.map +1 -1
  74. package/lib/module/tab-bar/index.js +1 -1
  75. package/lib/module/tab-bar/index.js.map +1 -1
  76. package/lib/module/tab-bar/index.web.js +1 -1
  77. package/lib/module/tab-bar/index.web.js.map +1 -1
  78. package/lib/module/tab-bar/shared.js +3 -3
  79. package/lib/module/tab-bar/surface.web.js.map +1 -1
  80. package/lib/module/tab-bar/use-footprint.js +52 -0
  81. package/lib/module/tab-bar/use-footprint.js.map +1 -0
  82. package/lib/module/toast/ToastRow.js +8 -4
  83. package/lib/module/toast/ToastRow.js.map +1 -1
  84. package/lib/module/toast/ToastSwipeHandler.js +29 -13
  85. package/lib/module/toast/ToastSwipeHandler.js.map +1 -1
  86. package/lib/module/toast/constants.js +28 -1
  87. package/lib/module/toast/constants.js.map +1 -1
  88. package/lib/module/toast/position-utils.js +9 -4
  89. package/lib/module/toast/position-utils.js.map +1 -1
  90. package/lib/module/zoomable-image-gallery/ZoomableImageGallery.js +12 -9
  91. package/lib/module/zoomable-image-gallery/ZoomableImageGallery.js.map +1 -1
  92. package/lib/typescript/commonjs/bottom-sheet/BottomSheetBase.d.ts.map +1 -1
  93. package/lib/typescript/commonjs/connection-dots/ConnectionDots.web.d.ts.map +1 -1
  94. package/lib/typescript/commonjs/design-tokens/shadows.d.ts +4 -0
  95. package/lib/typescript/commonjs/design-tokens/shadows.d.ts.map +1 -1
  96. package/lib/typescript/commonjs/dialog/Dialog.web.d.ts.map +1 -1
  97. package/lib/typescript/commonjs/index.web.d.ts +2 -2
  98. package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
  99. package/lib/typescript/commonjs/loading/Loading.web.d.ts.map +1 -1
  100. package/lib/typescript/commonjs/loading/SpinnerIcon.web.d.ts.map +1 -1
  101. package/lib/typescript/commonjs/motion/index.web.d.ts +9 -0
  102. package/lib/typescript/commonjs/motion/index.web.d.ts.map +1 -0
  103. package/lib/typescript/commonjs/motion/motion.d.ts +14 -6
  104. package/lib/typescript/commonjs/motion/motion.d.ts.map +1 -1
  105. package/lib/typescript/commonjs/motion/motion.web.d.ts +79 -0
  106. package/lib/typescript/commonjs/motion/motion.web.d.ts.map +1 -0
  107. package/lib/typescript/commonjs/progressive-blur/index.web.d.ts.map +1 -1
  108. package/lib/typescript/commonjs/styles/web-view-style.d.ts +49 -0
  109. package/lib/typescript/commonjs/styles/web-view-style.d.ts.map +1 -1
  110. package/lib/typescript/commonjs/tab-bar/TabBarBase.d.ts.map +1 -1
  111. package/lib/typescript/commonjs/tab-bar/index.d.ts +1 -1
  112. package/lib/typescript/commonjs/tab-bar/index.d.ts.map +1 -1
  113. package/lib/typescript/commonjs/tab-bar/index.web.d.ts +1 -1
  114. package/lib/typescript/commonjs/tab-bar/index.web.d.ts.map +1 -1
  115. package/lib/typescript/commonjs/tab-bar/shared.d.ts +3 -3
  116. package/lib/typescript/commonjs/tab-bar/surface.web.d.ts.map +1 -1
  117. package/lib/typescript/commonjs/tab-bar/use-footprint.d.ts +31 -0
  118. package/lib/typescript/commonjs/tab-bar/use-footprint.d.ts.map +1 -0
  119. package/lib/typescript/commonjs/toast/ToastRow.d.ts.map +1 -1
  120. package/lib/typescript/commonjs/toast/ToastSwipeHandler.d.ts +12 -6
  121. package/lib/typescript/commonjs/toast/ToastSwipeHandler.d.ts.map +1 -1
  122. package/lib/typescript/commonjs/toast/constants.d.ts +26 -8
  123. package/lib/typescript/commonjs/toast/constants.d.ts.map +1 -1
  124. package/lib/typescript/commonjs/toast/position-utils.d.ts +8 -3
  125. package/lib/typescript/commonjs/toast/position-utils.d.ts.map +1 -1
  126. package/lib/typescript/commonjs/zoomable-image-gallery/ZoomableImageGallery.d.ts.map +1 -1
  127. package/lib/typescript/module/bottom-sheet/BottomSheetBase.d.ts.map +1 -1
  128. package/lib/typescript/module/connection-dots/ConnectionDots.web.d.ts.map +1 -1
  129. package/lib/typescript/module/design-tokens/shadows.d.ts +4 -0
  130. package/lib/typescript/module/design-tokens/shadows.d.ts.map +1 -1
  131. package/lib/typescript/module/dialog/Dialog.web.d.ts.map +1 -1
  132. package/lib/typescript/module/index.web.d.ts +2 -2
  133. package/lib/typescript/module/index.web.d.ts.map +1 -1
  134. package/lib/typescript/module/loading/Loading.web.d.ts.map +1 -1
  135. package/lib/typescript/module/loading/SpinnerIcon.web.d.ts.map +1 -1
  136. package/lib/typescript/module/motion/index.web.d.ts +9 -0
  137. package/lib/typescript/module/motion/index.web.d.ts.map +1 -0
  138. package/lib/typescript/module/motion/motion.d.ts +14 -6
  139. package/lib/typescript/module/motion/motion.d.ts.map +1 -1
  140. package/lib/typescript/module/motion/motion.web.d.ts +79 -0
  141. package/lib/typescript/module/motion/motion.web.d.ts.map +1 -0
  142. package/lib/typescript/module/progressive-blur/index.web.d.ts.map +1 -1
  143. package/lib/typescript/module/styles/web-view-style.d.ts +49 -0
  144. package/lib/typescript/module/styles/web-view-style.d.ts.map +1 -1
  145. package/lib/typescript/module/tab-bar/TabBarBase.d.ts.map +1 -1
  146. package/lib/typescript/module/tab-bar/index.d.ts +1 -1
  147. package/lib/typescript/module/tab-bar/index.d.ts.map +1 -1
  148. package/lib/typescript/module/tab-bar/index.web.d.ts +1 -1
  149. package/lib/typescript/module/tab-bar/index.web.d.ts.map +1 -1
  150. package/lib/typescript/module/tab-bar/shared.d.ts +3 -3
  151. package/lib/typescript/module/tab-bar/surface.web.d.ts.map +1 -1
  152. package/lib/typescript/module/tab-bar/use-footprint.d.ts +31 -0
  153. package/lib/typescript/module/tab-bar/use-footprint.d.ts.map +1 -0
  154. package/lib/typescript/module/toast/ToastRow.d.ts.map +1 -1
  155. package/lib/typescript/module/toast/ToastSwipeHandler.d.ts +12 -6
  156. package/lib/typescript/module/toast/ToastSwipeHandler.d.ts.map +1 -1
  157. package/lib/typescript/module/toast/constants.d.ts +26 -8
  158. package/lib/typescript/module/toast/constants.d.ts.map +1 -1
  159. package/lib/typescript/module/toast/position-utils.d.ts +8 -3
  160. package/lib/typescript/module/toast/position-utils.d.ts.map +1 -1
  161. package/lib/typescript/module/zoomable-image-gallery/ZoomableImageGallery.d.ts.map +1 -1
  162. package/package.json +6 -1
  163. package/src/__tests__/Motion.test.tsx +31 -3
  164. package/src/__tests__/MotionWebFork.test.ts +140 -0
  165. package/src/__tests__/TabBarAnimatedShorthand.test.ts +135 -0
  166. package/src/__tests__/TabBarFootprint.test.tsx +172 -0
  167. package/src/__tests__/TabBarWebFork.test.ts +1 -1
  168. package/src/__tests__/ToastHostWebFork.test.ts +6 -2
  169. package/src/__tests__/Toaster.test.tsx +62 -1
  170. package/src/__tests__/toast-position-utils.test.ts +23 -7
  171. package/src/__tests__/web-css-style.test.ts +139 -0
  172. package/src/bottom-sheet/BottomSheetBase.tsx +11 -7
  173. package/src/connection-dots/ConnectionDots.web.tsx +4 -3
  174. package/src/design-tokens/shadows.ts +5 -1
  175. package/src/dialog/Dialog.tsx +1 -1
  176. package/src/dialog/Dialog.web.tsx +41 -26
  177. package/src/index.web.ts +2 -2
  178. package/src/loading/Loading.web.tsx +7 -5
  179. package/src/loading/SpinnerIcon.web.tsx +12 -9
  180. package/src/motion/index.web.ts +8 -0
  181. package/src/motion/motion.ts +14 -6
  182. package/src/motion/motion.web.ts +99 -0
  183. package/src/progressive-blur/index.web.tsx +4 -3
  184. package/src/styles/web-view-style.ts +50 -0
  185. package/src/tab-bar/TabBarBase.tsx +17 -12
  186. package/src/tab-bar/index.ts +1 -1
  187. package/src/tab-bar/index.web.tsx +1 -1
  188. package/src/tab-bar/shared.ts +3 -3
  189. package/src/tab-bar/surface.web.tsx +4 -3
  190. package/src/tab-bar/use-footprint.ts +50 -0
  191. package/src/toast/ToastRow.tsx +8 -3
  192. package/src/toast/ToastSwipeHandler.tsx +30 -12
  193. package/src/toast/constants.ts +28 -0
  194. package/src/toast/position-utils.ts +10 -5
  195. package/src/zoomable-image-gallery/ZoomableImageGallery.tsx +9 -8
  196. package/lib/commonjs/tab-bar/use-reserved-space.js +0 -50
  197. package/lib/commonjs/tab-bar/use-reserved-space.js.map +0 -1
  198. package/lib/module/tab-bar/use-reserved-space.js +0 -46
  199. package/lib/module/tab-bar/use-reserved-space.js.map +0 -1
  200. package/lib/typescript/commonjs/tab-bar/use-reserved-space.d.ts +0 -25
  201. package/lib/typescript/commonjs/tab-bar/use-reserved-space.d.ts.map +0 -1
  202. package/lib/typescript/module/tab-bar/use-reserved-space.d.ts +0 -25
  203. package/lib/typescript/module/tab-bar/use-reserved-space.d.ts.map +0 -1
  204. package/src/__tests__/TabBarReservedSpace.test.tsx +0 -150
  205. package/src/tab-bar/use-reserved-space.ts +0 -44
@@ -0,0 +1,79 @@
1
+ /**
2
+ * WEB FORK of `motion.ts`. Same three names, same contract ("a value you hand to
3
+ * an `Animated.View`'s `entering` / `exiting`"), different mechanism — because on
4
+ * web the native mechanism does nothing at all and the obvious replacement is
5
+ * worse than nothing.
6
+ *
7
+ * WHY THE DEFAULT FILE CANNOT SERVE WEB. `motion.ts` exports custom
8
+ * `EntryExitAnimationFunction` worklet builders. Reanimated's web layout-animation
9
+ * manager resolves an animation by NAME: `tryGetAnimationConfig` reads
10
+ * `config.presetName` off the function, a custom builder has none, and
11
+ * `checkUndefinedAnimationFail` then bails to `makeElementVisible(element, 0)`.
12
+ * Measured in Chrome 5.x/reanimated 4.5.0: the card mounts at `transform: none`,
13
+ * `opacity: 1` and stays there for every sampled frame of both directions, with
14
+ * one `[Reanimated] Couldn't load entering/exiting animation` warning per element.
15
+ * Native is unaffected — it runs the builder directly, with no name lookup.
16
+ *
17
+ * WHY THE ENTER AND THE EXITS USE DIFFERENT MECHANISMS. Rebuilding all three as
18
+ * `Keyframe`s is the obvious fix and is only HALF right, because reanimated's two
19
+ * web directions differ in one decisive argument:
20
+ *
21
+ * - `exiting` -> `handleExitingAnimation` calls `setElementAnimation` on a
22
+ * throwaway clone with `shouldSavePosition` FALSE. The custom keyframe's
23
+ * `duration x 5` cleanup only tears that clone down, which is what it is for.
24
+ * A `Keyframe` here is safe AND exact: it compiles to a real CSS `@keyframes`
25
+ * rule, so the full scale + opacity shape survives.
26
+ * - `entering` -> `chooseAction` calls `setElementAnimation` on the REAL element
27
+ * with `shouldSavePosition` TRUE, and since a custom keyframe's generated
28
+ * `REA-ENTERING-n` name is absent from reanimated's built-in `Animations`
29
+ * map, `scheduleAnimationCleanup` fires
30
+ * `setElementPosition` at `duration x 5` — `position: absolute`, a frozen
31
+ * `top`/`left`/`width`/`height` snapshot, `transform` cleared. The animation
32
+ * plays and the element is then pinned, silently, one and a half seconds
33
+ * later. That is failure mode (C) in `~/Oxy/AGENTS.md` and it is strictly
34
+ * worse than the inert animation it would replace.
35
+ *
36
+ * So the enter uses a PREDEFINED builder, whose `presetName` IS in `Animations`
37
+ * and therefore skips `scheduleAnimationCleanup` entirely. Predefined builders
38
+ * cannot combine a fade with a scale, so the web enter keeps the fade and drops
39
+ * the 0.7 -> 1 growth. `ScreenTransition` in this same folder already made that
40
+ * trade for the same reason ("on web the slide is intentionally dropped").
41
+ *
42
+ * The imperative shared-value drive (failure mode (A)'s remedy, and what
43
+ * `toast/ToastRow.tsx` uses for its own enter) is the third option and is not
44
+ * available here: these are exported VALUES, not components, so there is no
45
+ * render to hang a `useSharedValue` off — and it could not express an exit at
46
+ * all, since keeping an unmounting element alive is exactly what `exiting` is for.
47
+ *
48
+ * WEB EASING IS LINEAR. `Keyframe` has no `.easing()` modifier, and a per-stop
49
+ * easing only survives `convertAnimationObjectToKeyframes` when it resolves to one
50
+ * of reanimated's seven `WebEasings` names (all of them ease-IN curves), while
51
+ * `.easing(Easing.bezier(...))` needs the `__closure` the worklets babel plugin
52
+ * adds — absent in every Oxy web build — and degrades to linear WITH a warning.
53
+ * `ShrinkAndPop` therefore carries an extra midpoint stop so its piecewise-linear
54
+ * web curve tracks the native `withTiming` segments closely rather than cutting
55
+ * the corner.
56
+ */
57
+ import { type EntryOrExitLayoutType } from 'react-native-reanimated';
58
+ /**
59
+ * Entering: fade in.
60
+ *
61
+ * The scale is dropped on web — see the header. `FadeIn`'s own default duration
62
+ * is already 300ms, matching the native builder, and the bare class is exported
63
+ * rather than a `.duration()` instance so nothing shares a mutable builder.
64
+ */
65
+ export declare const ScaleAndFadeIn: EntryOrExitLayoutType;
66
+ /** Exiting counterpart to {@link ScaleAndFadeIn}: fade out while shrinking to 70%. */
67
+ export declare const ScaleAndFadeOut: EntryOrExitLayoutType;
68
+ /**
69
+ * Exiting with a brief "pop": the scale dips to 70%, overshoots to 110%, and the
70
+ * element fades out.
71
+ *
72
+ * Stops are the native timeline sampled at its own breakpoints, so every property
73
+ * is declared at every stop and nothing depends on CSS's cross-stop interpolation:
74
+ * scale reaches 0.7 at 75ms (30%) and 1.1 at 225ms (90%); opacity holds until
75
+ * 125ms (50%) and then falls to 0 by 250ms. The 50% stop's 0.8333 scale and the
76
+ * 90% stop's 0.2 opacity are the native values at those instants.
77
+ */
78
+ export declare const ShrinkAndPop: EntryOrExitLayoutType;
79
+ //# sourceMappingURL=motion.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"motion.web.d.ts","sourceRoot":"","sources":["../../../../src/motion/motion.web.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,yBAAyB,CAAC;AAOjC;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,EAAE,qBAA8B,CAAC;AAE5D,sFAAsF;AACtF,eAAO,MAAM,eAAe,EAAE,qBAGD,CAAC;AAE9B;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,EAAE,qBAMF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/progressive-blur/index.web.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAEpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,SAAa,EACb,SAAiB,EACjB,GAAG,IAAI,EACR,EAAE,oBAAoB,2CAkBtB;yBAvBe,eAAe"}
1
+ {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/progressive-blur/index.web.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAEpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,SAAa,EACb,SAAiB,EACjB,GAAG,IAAI,EACR,EAAE,oBAAoB,2CAkBtB;yBAvBe,eAAe"}
@@ -18,4 +18,53 @@ import type { ViewStyle } from 'react-native';
18
18
  * where Yoga silently ignores it.
19
19
  */
20
20
  export declare const WEB_POSITION_FIXED: ViewStyle["position"];
21
+ /**
22
+ * A `ViewStyle` that may ALSO carry CSS react-native-web forwards straight to the
23
+ * DOM node but React Native does not model at all.
24
+ *
25
+ * This is the OTHER shape of the same RN/RNW type gap `WEB_POSITION_FIXED`
26
+ * covers, and it needs a different escape hatch because the two are structurally
27
+ * different: `position` is a key RN DOES model, just with a narrower value union,
28
+ * so a value-level cast is enough and it can still be written inside an ordinary
29
+ * style object. The properties below have no key on `ViewStyle` at all, so the
30
+ * whole OBJECT has to cross the boundary.
31
+ *
32
+ * ANNOTATE, DO NOT CAST. Every `.web` fork used to spell this `{ … } as ViewStyle`
33
+ * inline. That is not a widening, it is SILENCING: `as` only requires the two
34
+ * types to be COMPARABLE, so `{ animatoin: '…' } as ViewStyle` compiles happily
35
+ * and the typo ships as a style key react-native-web drops on the floor.
36
+ * Annotating with this interface restores excess-property checking — a key that
37
+ * is neither a real RN style nor a listed web-only property is a compile error —
38
+ * and, unlike a widening helper function, it erases completely, so the emitted
39
+ * JavaScript is byte-identical to the cast it replaces.
40
+ *
41
+ * ```ts
42
+ * const glass: WebCssStyle = { backdropFilter: 'blur(24px)' };
43
+ * // …then pass it anywhere a `ViewStyle` is expected.
44
+ * ```
45
+ *
46
+ * Only add a key here once react-native-web is confirmed to pass it through and
47
+ * RN has no equivalent. Several near-misses do NOT belong: RN 0.83 models
48
+ * `cursor`, `boxShadow`, `filter` and `pointerEvents` on `ViewStyle` directly, so
49
+ * those need no widening at all (`userSelect` is the trap — RN declares it on
50
+ * `TextStyle` only). Prefixed spellings sit alongside the standard ones because
51
+ * Safari still needs them for `backdrop-filter` and `mask-image`.
52
+ */
53
+ export interface WebCssStyle extends ViewStyle {
54
+ /** CSS `animation` shorthand. Bloom's web forks self-inject the `@keyframes`. */
55
+ animation?: string;
56
+ animationDelay?: string;
57
+ transitionProperty?: string;
58
+ transitionDuration?: string;
59
+ transitionTimingFunction?: string;
60
+ backdropFilter?: string;
61
+ WebkitBackdropFilter?: string;
62
+ maskImage?: string;
63
+ WebkitMaskImage?: string;
64
+ backgroundImage?: string;
65
+ scrollbarWidth?: 'auto' | 'thin' | 'none';
66
+ scrollbarColor?: string;
67
+ /** RN declares this on `TextStyle`, not `ViewStyle`. */
68
+ userSelect?: 'auto' | 'none' | 'text' | 'contain' | 'all';
69
+ }
21
70
  //# sourceMappingURL=web-view-style.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"web-view-style.d.ts","sourceRoot":"","sources":["../../../../src/styles/web-view-style.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,kBAAkB,EAAc,SAAS,CAAC,UAAU,CAAC,CAAC"}
1
+ {"version":3,"file":"web-view-style.d.ts","sourceRoot":"","sources":["../../../../src/styles/web-view-style.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,kBAAkB,EAAc,SAAS,CAAC,UAAU,CAAC,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAC1C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,KAAK,CAAC;CAC3D"}
@@ -1 +1 @@
1
- {"version":3,"file":"TabBarBase.d.ts","sourceRoot":"","sources":["../../../../src/tab-bar/TabBarBase.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAOL,KAAK,aAAa,EACnB,MAAM,OAAO,CAAC;AAgBf,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEtE,OAAO,EAiBL,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACxB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAe,MAAM,SAAS,CAAC;AAqa3E;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,aAAa,CAAC,kBAAkB,CAAC,EAC1C,IAAI,EAAE,aAAa,CAAC,oBAAoB,CAAC;YAElB,WAAW;;EAGnC;AAED,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,CAAC,gBAAgB,CAAC;YAC1C,iBAAiB;;EAG/C"}
1
+ {"version":3,"file":"TabBarBase.d.ts","sourceRoot":"","sources":["../../../../src/tab-bar/TabBarBase.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAOL,KAAK,aAAa,EACnB,MAAM,OAAO,CAAC;AAgBf,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEtE,OAAO,EAiBL,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACxB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAe,MAAM,SAAS,CAAC;AA0a3E;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,aAAa,CAAC,kBAAkB,CAAC,EAC1C,IAAI,EAAE,aAAa,CAAC,oBAAoB,CAAC;YAElB,WAAW;;EAGnC;AAED,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,CAAC,gBAAgB,CAAC;YAC1C,iBAAiB;;EAG/C"}
@@ -8,6 +8,6 @@ export declare const TabBarButton: {
8
8
  };
9
9
  export { MINIMIZE_SPRING, setMinimized, TabBarMinimizeProvider, useMinimizeOnScroll, useMinimizeState, useTabBarMinimized, } from './minimize-context';
10
10
  export type { MinimizeState } from './minimize-context';
11
- export { useTabBarReservedSpace } from './use-reserved-space';
11
+ export { useTabBarFootprint } from './use-footprint';
12
12
  export type { TabBarButtonProps, TabBarItem, TabBarProps, TabBarTheme } from './types';
13
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/tab-bar/index.ts"],"names":[],"mappings":"AAmBA,eAAO,MAAM,MAAM;;;CAA+C,CAAC;AACnE,eAAO,MAAM,YAAY;;;CAAkC,CAAC;AAE5D,OAAO,EACL,eAAe,EACf,YAAY,EACZ,sBAAsB,EACtB,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/tab-bar/index.ts"],"names":[],"mappings":"AAmBA,eAAO,MAAM,MAAM;;;CAA+C,CAAC;AACnE,eAAO,MAAM,YAAY;;;CAAkC,CAAC;AAE5D,OAAO,EACL,eAAe,EACf,YAAY,EACZ,sBAAsB,EACtB,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC"}
@@ -8,6 +8,6 @@ export declare const TabBarButton: {
8
8
  };
9
9
  export { MINIMIZE_SPRING, setMinimized, TabBarMinimizeProvider, useMinimizeOnScroll, useMinimizeState, useTabBarMinimized, } from './minimize-context';
10
10
  export type { MinimizeState } from './minimize-context';
11
- export { useTabBarReservedSpace } from './use-reserved-space';
11
+ export { useTabBarFootprint } from './use-footprint';
12
12
  export type { TabBarButtonProps, TabBarItem, TabBarProps, TabBarTheme } from './types';
13
13
  //# sourceMappingURL=index.web.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/tab-bar/index.web.tsx"],"names":[],"mappings":"AAqBA,eAAO,MAAM,MAAM;;;CAA+C,CAAC;AACnE,eAAO,MAAM,YAAY;;;CAAkC,CAAC;AAE5D,OAAO,EACL,eAAe,EACf,YAAY,EACZ,sBAAsB,EACtB,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/tab-bar/index.web.tsx"],"names":[],"mappings":"AAqBA,eAAO,MAAM,MAAM;;;CAA+C,CAAC;AACnE,eAAO,MAAM,YAAY;;;CAAkC,CAAC;AAE5D,OAAO,EACL,eAAe,EACf,YAAY,EACZ,sBAAsB,EACtB,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC"}
@@ -29,9 +29,9 @@ export declare const LABEL_FONT_SIZE = 9.5;
29
29
  /**
30
30
  * Vertical gap between the bottom of the pill and the bottom of the window.
31
31
  *
32
- * The safe-area inset is folded in HERE, which is why anything reserving layout
33
- * space for the bar must go through `useTabBarReservedSpace` (which derives its
34
- * number from this same function) rather than adding `insets.bottom` to a
32
+ * The safe-area inset is folded in HERE, which is why anything accounting for
33
+ * the bar in its own layout must go through `useTabBarFootprint` (which derives
34
+ * its number from this same function) rather than adding `insets.bottom` to a
35
35
  * hardcoded height — that double-counts the inset.
36
36
  */
37
37
  export declare function tabBarBottomGap(bottomInset: number): number;
@@ -1 +1 @@
1
- {"version":3,"file":"surface.web.d.ts","sourceRoot":"","sources":["../../../../src/tab-bar/surface.web.tsx"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAkBnD,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,kBAAkB,2CAWjE;yBAXe,aAAa"}
1
+ {"version":3,"file":"surface.web.d.ts","sourceRoot":"","sources":["../../../../src/tab-bar/surface.web.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAkBnD,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,kBAAkB,2CAWjE;yBAXe,aAAa"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The vertical space the tab bar OCCUPIES above the bottom of the window, in
3
+ * px: its own height plus its own bottom offset, and nothing else.
4
+ *
5
+ * It is deliberately the raw footprint, with no clearance folded in. Clearance
6
+ * is a consumer decision and it differs per call site — a scroll view wants the
7
+ * footprint plus a breathing gap so the last row clears the pill, while a
8
+ * floating action button anchored by `bottom` already supplies its own gap and
9
+ * wants the footprint bare. Baking a margin in here would place that FAB too
10
+ * high and make one word mean two things inside a single file. Add your own:
11
+ *
12
+ * ```tsx
13
+ * const footprint = useTabBarFootprint();
14
+ *
15
+ * <Animated.ScrollView contentContainerStyle={{ paddingBottom: footprint + 12 }} />
16
+ * <Fab style={{ position: 'absolute', right: 16, bottom: footprint }} />
17
+ * ```
18
+ *
19
+ * The value ALREADY INCLUDES the bottom safe-area inset — the bar folds the
20
+ * inset into its own gap, so this is measured from the true window edge, not
21
+ * from the safe area. Never write `footprint + insets.bottom`: that counts the
22
+ * home indicator twice and strands a visible band of dead space under every
23
+ * list (114px accounted for a 76px bar on an iOS device or PWA). Whatever you
24
+ * add on top is your own clearance — never the inset a second time.
25
+ *
26
+ * It is the EXPANDED height, deliberately. The bar minimizes while scrolling
27
+ * down and re-expands on the way up, so measuring the minimized height would
28
+ * hide the end of a list the moment the user scrolled back to it.
29
+ */
30
+ export declare function useTabBarFootprint(): number;
31
+ //# sourceMappingURL=use-footprint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-footprint.d.ts","sourceRoot":"","sources":["../../../../src/tab-bar/use-footprint.ts"],"names":[],"mappings":"AAiBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAG3C"}
@@ -1 +1 @@
1
- {"version":3,"file":"ToastRow.d.ts","sourceRoot":"","sources":["../../../../src/toast/ToastRow.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AA2B/B,OAAO,KAAK,EAEV,aAAa,EAEb,QAAQ,EACR,WAAW,EACZ,MAAM,SAAS,CAAC;AAwDjB,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAHR,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI;kCA6UzC,CAAC"}
1
+ {"version":3,"file":"ToastRow.d.ts","sourceRoot":"","sources":["../../../../src/toast/ToastRow.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AA4B/B,OAAO,KAAK,EAEV,aAAa,EAEb,QAAQ,EACR,WAAW,EACZ,MAAM,SAAS,CAAC;AAwDjB,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAHR,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI;kCAiVzC,CAAC"}
@@ -2,12 +2,18 @@
2
2
  * Derived from sonner-native v0.26.4 — src/gestures.tsx
3
3
  * (MIT © Gunnar Torfi Steinarsson). See the top-level NOTICE.
4
4
  *
5
- * W2 — the animated style reads `translate`, `direction`, `position`,
6
- * `windowWidth` and `isAndroid`; ALL of them are in the dependency array. On web
7
- * without the worklets babel plugin reanimated re-runs the mapper on its deps
8
- * rather than on auto-tracked reads, so a missing dep freezes the swipe transform
9
- * at its first frame (upstream omits `position` and `isAndroid`, so a toast that
10
- * changes position mid-life swipes the wrong way).
5
+ * W2 — the animated style reads `translate`, `direction`, `position`, `rowWidth`
6
+ * and `isAndroid`; ALL of them are in the dependency array. On web without the
7
+ * worklets babel plugin reanimated re-runs the mapper on its deps rather than on
8
+ * auto-tracked reads, so a missing dep freezes the swipe transform at its first
9
+ * frame (upstream omits `position` and `isAndroid`, so a toast that changes
10
+ * position mid-life swipes the wrong way).
11
+ *
12
+ * W12 — this wrapper is THE row box, so it is where `TOAST_MAX_ROW_WIDTH` caps
13
+ * and centres the toast. Capping here rather than on `ToastContent`'s card
14
+ * covers `toast.custom` JSX and `unstyled` rows too, which never reach that
15
+ * card. Every distance the gesture measures is a fraction of the capped
16
+ * `rowWidth`, not of the window — see the constant.
11
17
  */
12
18
  import * as React from 'react';
13
19
  import { type ViewStyle } from 'react-native';
@@ -1 +1 @@
1
- {"version":3,"file":"ToastSwipeHandler.d.ts","sourceRoot":"","sources":["../../../../src/toast/ToastSwipeHandler.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAIL,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AActB,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAWzD,KAAK,sBAAsB,GAAG,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,GAAG;IAC5D,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,GAAG,CAAC,SAAS,GAAG,SAAS,CAAC,EAAE,CAAC;IAC9C,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,OAAO,EAAE,CAAC,IAAI,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACnD,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CACtC,KAAK,CAAC,iBAAiB,CAAC,sBAAsB,CAAC,CAqJhD,CAAC"}
1
+ {"version":3,"file":"ToastSwipeHandler.d.ts","sourceRoot":"","sources":["../../../../src/toast/ToastSwipeHandler.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAIL,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAetB,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAWzD,KAAK,sBAAsB,GAAG,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,GAAG;IAC5D,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,GAAG,CAAC,SAAS,GAAG,SAAS,CAAC,EAAE,CAAC;IAC9C,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,OAAO,EAAE,CAAC,IAAI,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACnD,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CACtC,KAAK,CAAC,iBAAiB,CAAC,sBAAsB,CAAC,CAuJhD,CAAC"}
@@ -1,11 +1,3 @@
1
- /**
2
- * Derived from sonner-native v0.26.4 — src/constants.ts and src/animations.ts
3
- * (MIT © Gunnar Torfi Steinarsson). See the top-level NOTICE.
4
- *
5
- * The two animation durations live HERE rather than in `animations.ts` so
6
- * `toast-store.ts` never has to import a Reanimated-dependent module: the store
7
- * stays pure JS and its suite needs zero React Native mocks.
8
- */
9
1
  import type { AutoWiggle, ToastPosition, ToastSwipeDirection, ToastTheme, ToastVariant } from './types';
10
2
  /** Fallback row height until a toast has been measured. */
11
3
  export declare const ESTIMATED_TOAST_HEIGHT = 70;
@@ -13,6 +5,32 @@ export declare const CLOSE_BUTTON_HIT_AREA = 60;
13
5
  export declare const OUTSIDE_PRESS_PADDING = 20;
14
6
  /** However deep a stack gets, a buried row never narrows past this scale. */
15
7
  export declare const MIN_STACK_SCALE_X = 0.8;
8
+ /**
9
+ * W12 — HOW WIDE A TOAST ROW IS ALLOWED TO GET.
10
+ *
11
+ * sonner-native has no cap: the row is `width: '100%'` and the card inside it is
12
+ * inset by `marginHorizontal: space.lg`, so the card is always
13
+ * screen-width-minus-32. That is correct for the platform it was written for and
14
+ * wrong everywhere else — on a 1280px desktop viewport it produces a ~1248px
15
+ * card, which reads as a page banner rather than a toast. sonner (web) instead
16
+ * fixes the card at `--width: 356px`.
17
+ *
18
+ * The cap therefore sizes the ROW BOX, which carries the card's two `space.lg`
19
+ * gutters, so the visible card lands on sonner's 356px on any viewport wide
20
+ * enough to reach it. `maxWidth` needs no platform fork because it degrades by
21
+ * itself: below a 388px viewport the row is still screen-width and the card is
22
+ * still screen-width-minus-32, exactly as before, so phone portrait — the case
23
+ * the mobile sizing was right for — is untouched. Above it (desktop, tablet,
24
+ * phone landscape) the row caps and centres.
25
+ *
26
+ * `ToastSwipeHandler`'s dismiss threshold and `calculateStackScaleX`'s depth cue
27
+ * are fractions of this row width, NOT of the window: at 1280px a
28
+ * window-relative threshold would demand a 320px drag from a 388px card, and a
29
+ * window-relative squeeze would shrink a buried row by 4px instead of 16px.
30
+ * A consumer who overrides the cap through `toastOptions.toastContainerStyle`
31
+ * moves the card only; both of those keep measuring against this default.
32
+ */
33
+ export declare const TOAST_MAX_ROW_WIDTH: number;
16
34
  /** Enter animation length; also the auto-close timer's head start and the overlay teardown delay. */
17
35
  export declare const ENTERING_ANIMATION_DURATION = 300;
18
36
  export declare const STACKING_ANIMATION_DURATION = 600;
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/toast/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EACV,UAAU,EACV,aAAa,EACb,mBAAmB,EACnB,UAAU,EACV,YAAY,EACb,MAAM,SAAS,CAAC;AAEjB,2DAA2D;AAC3D,eAAO,MAAM,sBAAsB,KAAK,CAAC;AACzC,eAAO,MAAM,qBAAqB,KAAK,CAAC;AACxC,eAAO,MAAM,qBAAqB,KAAK,CAAC;AACxC,6EAA6E;AAC7E,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC,qGAAqG;AACrG,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAC/C,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C,eAAO,MAAM,aAAa,EAAE;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,aAAa,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,uBAAuB,EAAE,mBAAmB,CAAC;IAC7C,OAAO,EAAE,YAAY,GAAG,SAAS,CAAC;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,qBAAqB,EAAE,OAAO,CAAC;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,UAAU,CAAC;IAClB,kBAAkB,EAAE,UAAU,CAAC;IAC/B,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,OAAO,CAAC;CA8B3B,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/toast/constants.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,UAAU,EACV,aAAa,EACb,mBAAmB,EACnB,UAAU,EACV,YAAY,EACb,MAAM,SAAS,CAAC;AAEjB,2DAA2D;AAC3D,eAAO,MAAM,sBAAsB,KAAK,CAAC;AACzC,eAAO,MAAM,qBAAqB,KAAK,CAAC;AACxC,eAAO,MAAM,qBAAqB,KAAK,CAAC;AACxC,6EAA6E;AAC7E,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,mBAAmB,QAAqB,CAAC;AAEtD,qGAAqG;AACrG,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAC/C,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C,eAAO,MAAM,aAAa,EAAE;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,aAAa,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,uBAAuB,EAAE,mBAAmB,CAAC;IAC7C,OAAO,EAAE,YAAY,GAAG,SAAS,CAAC;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,qBAAqB,EAAE,OAAO,CAAC;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,UAAU,CAAC;IAClB,kBAAkB,EAAE,UAAU,CAAC;IAC/B,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,OAAO,CAAC;CA8B3B,CAAC"}
@@ -5,16 +5,21 @@ export declare const getOrderedToastIds: (toasts: ToastProps[], position: ToastP
5
5
  *
6
6
  * Scale rather than width: changing the real width would rewrap the text. Returns
7
7
  * 1 (no squeeze) for a single row, an expanded stack, stacking turned off, or
8
- * before the window width is known.
8
+ * before the row width is known.
9
+ *
10
+ * `rowWidth` is the row's OWN width, not the window's. Upstream can use the
11
+ * screen width because its row is always full-bleed; Bloom's is capped at
12
+ * `TOAST_MAX_ROW_WIDTH`, and dividing a fixed `stackGap` inset by a 1280px window
13
+ * would shrink a buried row by 4 visible pixels instead of the intended 16.
9
14
  */
10
- export declare const calculateStackScaleX: ({ index, numberOfToasts, enableStacking, position, isExpanded, stackGap, screenWidth, }: {
15
+ export declare const calculateStackScaleX: ({ index, numberOfToasts, enableStacking, position, isExpanded, stackGap, rowWidth, }: {
11
16
  index: number;
12
17
  numberOfToasts: number;
13
18
  enableStacking: boolean;
14
19
  position: ToastPosition;
15
20
  isExpanded: boolean;
16
21
  stackGap: number;
17
- screenWidth: number;
22
+ rowWidth: number;
18
23
  }) => number;
19
24
  export declare const calculateToastPosition: ({ index, numberOfToasts, enableStacking, position, allToastHeights, gap, orderedToastIds, isExpanded, stackGap, }: {
20
25
  index: number;
@@ -1 +1 @@
1
- {"version":3,"file":"position-utils.d.ts","sourceRoot":"","sources":["../../../../src/toast/position-utils.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAEzD,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,UAAU,EAAE,EACpB,UAAU,aAAa,EACvB,gBAAgB,OAAO,KACtB,KAAK,CAAC,MAAM,GAAG,MAAM,CAQvB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAAI,yFAQlC;IACD,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,EAAE,aAAa,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB,KAAG,MASH,CAAC;AAaF,eAAO,MAAM,sBAAsB,GAAI,mHAUpC;IACD,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,EAAE,aAAa,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,MAAM,CAAC,CAAC;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IACxC,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB,KAAG,MAoDH,CAAC"}
1
+ {"version":3,"file":"position-utils.d.ts","sourceRoot":"","sources":["../../../../src/toast/position-utils.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAEzD,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,UAAU,EAAE,EACpB,UAAU,aAAa,EACvB,gBAAgB,OAAO,KACtB,KAAK,CAAC,MAAM,GAAG,MAAM,CAQvB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,GAAI,sFAQlC;IACD,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,EAAE,aAAa,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,KAAG,MASH,CAAC;AAaF,eAAO,MAAM,sBAAsB,GAAI,mHAUpC;IACD,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,EAAE,aAAa,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,MAAM,CAAC,CAAC;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IACxC,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB,KAAG,MAoDH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ZoomableImageGallery.d.ts","sourceRoot":"","sources":["../../../../src/zoomable-image-gallery/ZoomableImageGallery.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAkEjF,OAAO,KAAK,EAGV,0BAA0B,EAC1B,yBAAyB,EAC1B,MAAM,SAAS,CAAC;AA44BjB,eAAO,MAAM,oBAAoB,8GAA4B,CAAC;AAyK9D,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"ZoomableImageGallery.d.ts","sourceRoot":"","sources":["../../../../src/zoomable-image-gallery/ZoomableImageGallery.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAkEjF,OAAO,KAAK,EAGV,0BAA0B,EAC1B,yBAAyB,EAC1B,MAAM,SAAS,CAAC;AA64BjB,eAAO,MAAM,oBAAoB,8GAA4B,CAAC;AAyK9D,eAAe,oBAAoB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/bloom",
3
- "version": "0.52.0",
3
+ "version": "0.53.0",
4
4
  "description": "Bloom UI — Oxy ecosystem component library for React Native + Expo + Web",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -477,6 +477,11 @@
477
477
  },
478
478
  "./motion": {
479
479
  "react-native": "./src/motion/index.ts",
480
+ "browser": {
481
+ "types": "./lib/typescript/module/motion/index.web.d.ts",
482
+ "import": "./lib/module/motion/index.web.js",
483
+ "require": "./lib/commonjs/motion/index.web.js"
484
+ },
480
485
  "import": {
481
486
  "types": "./lib/typescript/module/motion/index.d.ts",
482
487
  "default": "./lib/module/motion/index.js"
@@ -1,6 +1,10 @@
1
1
  import React from 'react';
2
2
  import { Text } from 'react-native';
3
3
  import { render } from '@testing-library/react-native';
4
+ import type {
5
+ EntryOrExitLayoutType,
6
+ LayoutAnimation,
7
+ } from 'react-native-reanimated';
4
8
 
5
9
  import {
6
10
  ScaleAndFadeIn,
@@ -12,21 +16,45 @@ import {
12
16
  // The reanimated mock resolves `withTiming(v)` -> v, `withDelay(d, v)` -> v and
13
17
  // `withSequence(...v)` -> last, so the builder output is deterministic.
14
18
 
19
+ /**
20
+ * The presets are typed as reanimated's `entering`/`exiting` union so the two
21
+ * platform files present ONE contract and nothing can depend on the mechanism.
22
+ * These tests are about the native mechanism specifically, so they narrow back
23
+ * down through a runtime check rather than a cast: a predefined builder is a
24
+ * class (it carries the `createInstance` static), a custom worklet builder is a
25
+ * plain function.
26
+ */
27
+ const isWorkletBuilder = (
28
+ preset: EntryOrExitLayoutType,
29
+ ): preset is () => LayoutAnimation =>
30
+ // A predefined builder is a class and carries the `createInstance` static; a
31
+ // custom worklet builder is a plain nullary function.
32
+ typeof preset === 'function' &&
33
+ !('createInstance' in preset) &&
34
+ preset.length === 0;
35
+
36
+ const runNativeBuilder = (preset: EntryOrExitLayoutType): LayoutAnimation => {
37
+ if (!isWorkletBuilder(preset)) {
38
+ throw new Error('expected the native preset to be a custom worklet builder');
39
+ }
40
+ return preset();
41
+ };
42
+
15
43
  describe('motion presets', () => {
16
44
  it('ScaleAndFadeIn enters from 70% scale and transparent', () => {
17
- const { initialValues, animations } = ScaleAndFadeIn();
45
+ const { initialValues, animations } = runNativeBuilder(ScaleAndFadeIn);
18
46
  expect(initialValues).toEqual({ opacity: 0, transform: [{ scale: 0.7 }] });
19
47
  expect(animations).toEqual({ opacity: 1, transform: [{ scale: 1 }] });
20
48
  });
21
49
 
22
50
  it('ScaleAndFadeOut exits to 70% scale and transparent', () => {
23
- const { initialValues, animations } = ScaleAndFadeOut();
51
+ const { initialValues, animations } = runNativeBuilder(ScaleAndFadeOut);
24
52
  expect(initialValues).toEqual({ opacity: 1, transform: [{ scale: 1 }] });
25
53
  expect(animations).toEqual({ opacity: 0, transform: [{ scale: 0.7 }] });
26
54
  });
27
55
 
28
56
  it('ShrinkAndPop overshoots to 110% before leaving', () => {
29
- const { initialValues, animations } = ShrinkAndPop();
57
+ const { initialValues, animations } = runNativeBuilder(ShrinkAndPop);
30
58
  expect(initialValues).toEqual({ opacity: 1, transform: [{ scale: 1 }] });
31
59
  // withSequence resolves to its final keyframe (the 1.1 overshoot) in the mock.
32
60
  expect(animations.transform).toEqual([{ scale: 1.1 }]);
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Regression guard for the `motion` web fork.
3
+ *
4
+ * The bug: `motion.ts`'s custom worklet builders are INERT on web. Reanimated's
5
+ * web layout-animation manager resolves an animation by preset name, a custom
6
+ * builder has none, and `startWebLayoutAnimation` falls through to
7
+ * `makeElementVisible` — the element appears at its final frame with one
8
+ * `[Reanimated] Couldn't load entering/exiting animation` warning apiece.
9
+ *
10
+ * The trap: rebuilding all three as `Keyframe`s fixes the exits and makes the
11
+ * ENTER worse. Reanimated's `entering` path calls `setElementAnimation` with
12
+ * `shouldSavePosition` TRUE, and any animation name absent from its built-in
13
+ * `Animations` map — which every custom `Keyframe` is, since they mint a fresh
14
+ * `REA-ENTERING-n` rule — also gets a `scheduleAnimationCleanup` that pins the
15
+ * element with `position: absolute` and a frozen box at `duration x 5`. That is
16
+ * failure mode (C) in `~/Oxy/AGENTS.md`.
17
+ *
18
+ * So the invariant is asymmetric, and BOTH halves are load-bearing:
19
+ * entering -> a PREDEFINED builder (its `presetName` is in `Animations`, so no
20
+ * cleanup is ever scheduled), never a `Keyframe`.
21
+ * exiting -> a `Keyframe` is safe (the cleanup only reaps the throwaway clone)
22
+ * and is the only mechanism that can express the full shape.
23
+ *
24
+ * The actual motion is verified in a real browser — jest cannot run reanimated's
25
+ * web layout-animation manager.
26
+ */
27
+ import { readFileSync } from 'node:fs';
28
+ import { join } from 'node:path';
29
+
30
+ import { Keyframe } from 'react-native-reanimated';
31
+
32
+ import {
33
+ ScaleAndFadeIn,
34
+ ScaleAndFadeOut,
35
+ ShrinkAndPop,
36
+ } from '../motion/motion.web';
37
+
38
+ const SRC = join(__dirname, '..');
39
+ const read = (rel: string) => readFileSync(join(SRC, rel), 'utf8');
40
+
41
+ /**
42
+ * Upstream's `ReanimatedKeyframe` type hides the instance state that the web
43
+ * layout manager actually reads (`createCustomKeyFrameAnimation` takes
44
+ * `config.definitions` raw, `getDurationFromConfig` takes `config.durationV`), so
45
+ * the test narrows to it with a runtime-checked predicate rather than a cast.
46
+ */
47
+ type KeyframeState = {
48
+ definitions: Record<string, Record<string, unknown>>;
49
+ durationV: number | undefined;
50
+ };
51
+
52
+ const isKeyframeState = (value: unknown): value is KeyframeState =>
53
+ value instanceof Keyframe && 'definitions' in value;
54
+
55
+ const stateOf = (preset: unknown): KeyframeState => {
56
+ if (!isKeyframeState(preset)) {
57
+ throw new Error('expected a Keyframe');
58
+ }
59
+ return preset;
60
+ };
61
+
62
+ describe('motion web fork', () => {
63
+ it('never hands a Keyframe to `entering`', () => {
64
+ // The whole reason the enter and the exits use different mechanisms.
65
+ expect(ScaleAndFadeIn instanceof Keyframe).toBe(false);
66
+ expect(typeof ScaleAndFadeIn).toBe('object');
67
+ });
68
+
69
+ it('uses a predefined reanimated builder for the enter', () => {
70
+ const web = read('motion/motion.web.ts');
71
+ expect(web).toMatch(/export const ScaleAndFadeIn[^=]*=\s*FadeIn;/);
72
+ expect(web).toMatch(/import \{[\s\S]*\bFadeIn\b[\s\S]*\} from 'react-native-reanimated'/);
73
+ });
74
+
75
+ it('uses a Keyframe for both exits', () => {
76
+ expect(ScaleAndFadeOut instanceof Keyframe).toBe(true);
77
+ expect(ShrinkAndPop instanceof Keyframe).toBe(true);
78
+ });
79
+
80
+ it('keeps the full scale + opacity shape on ScaleAndFadeOut', () => {
81
+ expect(stateOf(ScaleAndFadeOut).definitions).toEqual({
82
+ from: { opacity: 1, transform: [{ scale: 1 }] },
83
+ to: { opacity: 0, transform: [{ scale: 0.7 }] },
84
+ });
85
+ });
86
+
87
+ /**
88
+ * Every stop declares every animated property, so the CSS the web parser emits
89
+ * does not depend on cross-stop interpolation, and the intermediate values are
90
+ * the native timeline sampled at those instants (250ms total: scale 1 -> 0.7 by
91
+ * 75ms, -> 1.1 by 225ms; opacity held to 125ms, then to 0 by 250ms).
92
+ */
93
+ it('samples the native ShrinkAndPop timeline at every stop', () => {
94
+ const stops = stateOf(ShrinkAndPop).definitions;
95
+ expect(Object.keys(stops)).toEqual(['0', '30', '50', '90', '100']);
96
+ for (const [percent, style] of Object.entries(stops)) {
97
+ expect({ percent, keys: Object.keys(style).sort() }).toEqual({
98
+ percent,
99
+ keys: ['opacity', 'transform'],
100
+ });
101
+ }
102
+ expect(stops['0']).toEqual({ opacity: 1, transform: [{ scale: 1 }] });
103
+ expect(stops['30']).toEqual({ opacity: 1, transform: [{ scale: 0.7 }] });
104
+ expect(stops['90']).toEqual({ opacity: 0.2, transform: [{ scale: 1.1 }] });
105
+ expect(stops['100']).toEqual({ opacity: 0, transform: [{ scale: 1.1 }] });
106
+ });
107
+
108
+ it('matches the native durations', () => {
109
+ // `withTiming`'s default for the pair the native file animates, and
110
+ // 75 + 150 for the pop's two sequenced scale legs.
111
+ expect(stateOf(ScaleAndFadeOut).durationV).toBe(300);
112
+ expect(stateOf(ShrinkAndPop).durationV).toBe(250);
113
+ });
114
+
115
+ it('leaves the native file on its custom worklet builders', () => {
116
+ const native = read('motion/motion.ts');
117
+ expect(native).toMatch(/'worklet';/);
118
+ expect(native).not.toMatch(/\bKeyframe\b/);
119
+ expect(native).not.toMatch(/\bFadeIn\b/);
120
+ });
121
+
122
+ it('is wired as a web-forked subpath', () => {
123
+ const script = readFileSync(
124
+ join(SRC, '..', 'scripts', 'generate-platform-exports.mjs'),
125
+ 'utf8',
126
+ );
127
+ // Between the WEB_FORKED_SUBPATHS opener and its closing bracket.
128
+ const forked = script.slice(
129
+ script.indexOf('const WEB_FORKED_SUBPATHS'),
130
+ script.indexOf(']);', script.indexOf('const WEB_FORKED_SUBPATHS')),
131
+ );
132
+ expect(forked).toContain("'./motion'");
133
+
134
+ const pkg: { exports: Record<string, { browser?: { import?: string } }> } =
135
+ JSON.parse(readFileSync(join(SRC, '..', 'package.json'), 'utf8'));
136
+ expect(pkg.exports['./motion']?.browser?.import).toBe(
137
+ './lib/module/motion/index.web.js',
138
+ );
139
+ });
140
+ });