react-native-header-motion 0.4.0 → 1.0.0-beta.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 (140) hide show
  1. package/README.md +400 -335
  2. package/lib/module/components/Bridge.js +16 -0
  3. package/lib/module/components/Bridge.js.map +1 -0
  4. package/lib/module/components/FlatList.js +5 -62
  5. package/lib/module/components/FlatList.js.map +1 -1
  6. package/lib/module/components/Header.js +71 -13
  7. package/lib/module/components/Header.js.map +1 -1
  8. package/lib/module/components/HeaderDynamic.js +34 -0
  9. package/lib/module/components/HeaderDynamic.js.map +1 -0
  10. package/lib/module/components/HeaderMotion.js +59 -23
  11. package/lib/module/components/HeaderMotion.js.map +1 -1
  12. package/lib/module/components/HeaderPanBoundary.js +54 -0
  13. package/lib/module/components/HeaderPanBoundary.js.map +1 -0
  14. package/lib/module/components/NavigationBridge.js +20 -0
  15. package/lib/module/components/NavigationBridge.js.map +1 -0
  16. package/lib/module/components/ScrollManager.js +7 -5
  17. package/lib/module/components/ScrollManager.js.map +1 -1
  18. package/lib/module/components/ScrollView.js +6 -47
  19. package/lib/module/components/ScrollView.js.map +1 -1
  20. package/lib/module/components/createHeaderMotionScrollable.js +136 -0
  21. package/lib/module/components/createHeaderMotionScrollable.js.map +1 -0
  22. package/lib/module/components/index.js +3 -1
  23. package/lib/module/components/index.js.map +1 -1
  24. package/lib/module/context.js +8 -1
  25. package/lib/module/context.js.map +1 -1
  26. package/lib/module/hooks/index.js +1 -0
  27. package/lib/module/hooks/index.js.map +1 -1
  28. package/lib/module/hooks/useActiveScrollId.js +7 -6
  29. package/lib/module/hooks/useActiveScrollId.js.map +1 -1
  30. package/lib/module/hooks/useHeaderMotionBridge.js +14 -0
  31. package/lib/module/hooks/useHeaderMotionBridge.js.map +1 -0
  32. package/lib/module/hooks/useMotionProgress.js +10 -36
  33. package/lib/module/hooks/useMotionProgress.js.map +1 -1
  34. package/lib/module/hooks/useMotionProgress.test.js +56 -0
  35. package/lib/module/hooks/useMotionProgress.test.js.map +1 -0
  36. package/lib/module/hooks/useScrollManager.js +219 -109
  37. package/lib/module/hooks/useScrollManager.js.map +1 -1
  38. package/lib/module/index.js +21 -18
  39. package/lib/module/index.js.map +1 -1
  40. package/lib/module/utils/defaults.js +2 -1
  41. package/lib/module/utils/defaults.js.map +1 -1
  42. package/lib/module/utils/header.js +24 -0
  43. package/lib/module/utils/header.js.map +1 -0
  44. package/lib/module/utils/headerOffsetStyle.js +31 -0
  45. package/lib/module/utils/headerOffsetStyle.js.map +1 -0
  46. package/lib/module/utils/index.js +3 -0
  47. package/lib/module/utils/index.js.map +1 -1
  48. package/lib/module/utils/refreshControl.js +93 -0
  49. package/lib/module/utils/refreshControl.js.map +1 -0
  50. package/lib/module/utils/values.js +36 -0
  51. package/lib/module/utils/values.js.map +1 -1
  52. package/lib/typescript/src/components/Bridge.d.ts +19 -0
  53. package/lib/typescript/src/components/Bridge.d.ts.map +1 -0
  54. package/lib/typescript/src/components/FlatList.d.ts +7 -15
  55. package/lib/typescript/src/components/FlatList.d.ts.map +1 -1
  56. package/lib/typescript/src/components/Header.d.ts +73 -12
  57. package/lib/typescript/src/components/Header.d.ts.map +1 -1
  58. package/lib/typescript/src/components/HeaderDynamic.d.ts +11 -0
  59. package/lib/typescript/src/components/HeaderDynamic.d.ts.map +1 -0
  60. package/lib/typescript/src/components/HeaderMotion.d.ts +37 -18
  61. package/lib/typescript/src/components/HeaderMotion.d.ts.map +1 -1
  62. package/lib/typescript/src/components/HeaderPanBoundary.d.ts +11 -0
  63. package/lib/typescript/src/components/HeaderPanBoundary.d.ts.map +1 -0
  64. package/lib/typescript/src/components/NavigationBridge.d.ts +19 -0
  65. package/lib/typescript/src/components/NavigationBridge.d.ts.map +1 -0
  66. package/lib/typescript/src/components/ScrollManager.d.ts +18 -25
  67. package/lib/typescript/src/components/ScrollManager.d.ts.map +1 -1
  68. package/lib/typescript/src/components/ScrollView.d.ts +7 -14
  69. package/lib/typescript/src/components/ScrollView.d.ts.map +1 -1
  70. package/lib/typescript/src/components/createHeaderMotionScrollable.d.ts +86 -0
  71. package/lib/typescript/src/components/createHeaderMotionScrollable.d.ts.map +1 -0
  72. package/lib/typescript/src/components/index.d.ts +3 -1
  73. package/lib/typescript/src/components/index.d.ts.map +1 -1
  74. package/lib/typescript/src/context.d.ts +3 -13
  75. package/lib/typescript/src/context.d.ts.map +1 -1
  76. package/lib/typescript/src/hooks/index.d.ts +1 -0
  77. package/lib/typescript/src/hooks/index.d.ts.map +1 -1
  78. package/lib/typescript/src/hooks/useActiveScrollId.d.ts +7 -6
  79. package/lib/typescript/src/hooks/useActiveScrollId.d.ts.map +1 -1
  80. package/lib/typescript/src/hooks/useHeaderMotionBridge.d.ts +10 -0
  81. package/lib/typescript/src/hooks/useHeaderMotionBridge.d.ts.map +1 -0
  82. package/lib/typescript/src/hooks/useMotionProgress.d.ts +8 -25
  83. package/lib/typescript/src/hooks/useMotionProgress.d.ts.map +1 -1
  84. package/lib/typescript/src/hooks/useMotionProgress.test.d.ts +2 -0
  85. package/lib/typescript/src/hooks/useMotionProgress.test.d.ts.map +1 -0
  86. package/lib/typescript/src/hooks/useScrollManager.d.ts +63 -31
  87. package/lib/typescript/src/hooks/useScrollManager.d.ts.map +1 -1
  88. package/lib/typescript/src/index.d.ts +56 -26
  89. package/lib/typescript/src/index.d.ts.map +1 -1
  90. package/lib/typescript/src/types.d.ts +63 -15
  91. package/lib/typescript/src/types.d.ts.map +1 -1
  92. package/lib/typescript/src/utils/defaults.d.ts +3 -2
  93. package/lib/typescript/src/utils/defaults.d.ts.map +1 -1
  94. package/lib/typescript/src/utils/header.d.ts +10 -0
  95. package/lib/typescript/src/utils/header.d.ts.map +1 -0
  96. package/lib/typescript/src/utils/headerOffsetStyle.d.ts +19 -0
  97. package/lib/typescript/src/utils/headerOffsetStyle.d.ts.map +1 -0
  98. package/lib/typescript/src/utils/index.d.ts +3 -0
  99. package/lib/typescript/src/utils/index.d.ts.map +1 -1
  100. package/lib/typescript/src/utils/refreshControl.d.ts +150 -0
  101. package/lib/typescript/src/utils/refreshControl.d.ts.map +1 -0
  102. package/lib/typescript/src/utils/values.d.ts +4 -1
  103. package/lib/typescript/src/utils/values.d.ts.map +1 -1
  104. package/package.json +13 -5
  105. package/src/components/Bridge.tsx +29 -0
  106. package/src/components/FlatList.tsx +18 -84
  107. package/src/components/Header.tsx +159 -23
  108. package/src/components/HeaderDynamic.tsx +45 -0
  109. package/src/components/HeaderMotion.tsx +114 -41
  110. package/src/components/HeaderPanBoundary.tsx +92 -0
  111. package/src/components/NavigationBridge.tsx +30 -0
  112. package/src/components/ScrollManager.tsx +38 -43
  113. package/src/components/ScrollView.tsx +16 -68
  114. package/src/components/createHeaderMotionScrollable.tsx +438 -0
  115. package/src/components/index.ts +3 -1
  116. package/src/context.ts +12 -18
  117. package/src/hooks/index.ts +1 -0
  118. package/src/hooks/useActiveScrollId.ts +7 -6
  119. package/src/hooks/useHeaderMotionBridge.ts +15 -0
  120. package/src/hooks/useMotionProgress.test.ts +67 -0
  121. package/src/hooks/useMotionProgress.ts +12 -37
  122. package/src/hooks/useScrollManager.ts +310 -129
  123. package/src/index.ts +82 -36
  124. package/src/types.ts +85 -25
  125. package/src/utils/defaults.ts +7 -1
  126. package/src/utils/header.tsx +52 -0
  127. package/src/utils/headerOffsetStyle.ts +40 -0
  128. package/src/utils/index.ts +3 -0
  129. package/src/utils/refreshControl.tsx +118 -0
  130. package/src/utils/values.ts +57 -1
  131. package/lib/module/components/HeaderBase.js +0 -59
  132. package/lib/module/components/HeaderBase.js.map +0 -1
  133. package/lib/module/hooks/refreshControl.js +0 -31
  134. package/lib/module/hooks/refreshControl.js.map +0 -1
  135. package/lib/typescript/src/components/HeaderBase.d.ts +0 -34
  136. package/lib/typescript/src/components/HeaderBase.d.ts.map +0 -1
  137. package/lib/typescript/src/hooks/refreshControl.d.ts +0 -13
  138. package/lib/typescript/src/hooks/refreshControl.d.ts.map +0 -1
  139. package/src/components/HeaderBase.tsx +0 -51
  140. package/src/hooks/refreshControl.ts +0 -55
@@ -3,17 +3,18 @@ import { useSharedValue } from 'react-native-reanimated';
3
3
  import type { ActiveScrollIdValues, SetActiveScrollId } from '../types';
4
4
 
5
5
  /**
6
- * Hook to manage active scroll ID for multi-scroll scenarios (e.g. tabs with different scroll views).
7
- * Returns both a state value and a shared value, along with a setter function.
6
+ * Keeps a React state value and a shared value in sync for the currently active
7
+ * scrollable.
8
8
  *
9
- * Use this when you have multiple scroll views (like in a tabbed interface) and need to
10
- * track which one is currently active. Pass the shared value to `HeaderMotion`'s `activeScrollId` prop.
9
+ * Use this when one header is shared across multiple scroll views, for example
10
+ * pager pages or tabs. Pass `values.sv` to `HeaderMotion` and use the setter
11
+ * whenever the active page changes.
11
12
  *
12
13
  * @template T - The type of the scroll ID string
13
14
  * @param initialActiveScrollId - The initial active scroll ID
14
15
  * @returns A tuple containing:
15
- * - `[0]`: Object with `state` (React state) and `sv` (shared value) for the active scroll ID
16
- * - `[1]`: Function to set the active scroll ID
16
+ * - an object with both the React `state` and shared-value `sv`
17
+ * - a setter that updates both in lockstep
17
18
  *
18
19
  * @example
19
20
  * ```tsx
@@ -0,0 +1,15 @@
1
+ import { useHeaderMotionContextOrThrow } from '../context';
2
+ import type { HeaderMotionBridgeValue } from '../types';
3
+
4
+ /**
5
+ * Returns the full internal HeaderMotion context value.
6
+ *
7
+ * Most app code should use `useMotionProgress()` instead. Reach for this hook
8
+ * only when you need to carry HeaderMotion context across a tree boundary and
9
+ * re-provide it somewhere else.
10
+ */
11
+ export function useHeaderMotionBridge(): HeaderMotionBridgeValue {
12
+ return useHeaderMotionContextOrThrow(
13
+ 'useHeaderMotionBridge must be used within <HeaderMotion />. Use it only when bridging context into a separate subtree with <HeaderMotion.Bridge /> and <HeaderMotion.NavigationBridge />.'
14
+ );
15
+ }
@@ -0,0 +1,67 @@
1
+ const mockUseHeaderMotionContextOrThrow = jest.fn();
2
+
3
+ jest.mock('../context', () => ({
4
+ __esModule: true,
5
+ useHeaderMotionContextOrThrow: (...args: any[]) =>
6
+ mockUseHeaderMotionContextOrThrow(...args),
7
+ }));
8
+
9
+ import { useHeaderMotionBridge } from './useHeaderMotionBridge';
10
+ import { useMotionProgress } from './useMotionProgress';
11
+
12
+ function createSharedValue<T>(value: T) {
13
+ return {
14
+ get: jest.fn(() => value),
15
+ set: jest.fn(),
16
+ value,
17
+ };
18
+ }
19
+
20
+ const bridgeValue = {
21
+ progress: createSharedValue(0),
22
+ progressThreshold: createSharedValue(120),
23
+ measureTotalHeight: jest.fn(),
24
+ measureDynamic: jest.fn(),
25
+ headerPanMomentumOffset: createSharedValue<number | null>(null),
26
+ scrollValues: createSharedValue({}),
27
+ activeScrollId: undefined,
28
+ scrollToRef: { current: null },
29
+ originalHeaderHeight: 0,
30
+ };
31
+
32
+ describe('motion hooks', () => {
33
+ beforeEach(() => {
34
+ mockUseHeaderMotionContextOrThrow.mockReset();
35
+ });
36
+
37
+ it('useMotionProgress returns only progress and progressThreshold', () => {
38
+ mockUseHeaderMotionContextOrThrow.mockReturnValue(bridgeValue);
39
+
40
+ expect(useMotionProgress()).toEqual({
41
+ progress: bridgeValue.progress,
42
+ progressThreshold: bridgeValue.progressThreshold,
43
+ });
44
+ expect(mockUseHeaderMotionContextOrThrow).toHaveBeenCalledWith(
45
+ 'useMotionProgress must be used within <HeaderMotion /> or <HeaderMotion.NavigationBridge />. If you are rendering inside a navigation header, bridge the context with <HeaderMotion.Bridge /> and <HeaderMotion.NavigationBridge />.'
46
+ );
47
+ });
48
+
49
+ it('useHeaderMotionBridge returns the full bridge value', () => {
50
+ mockUseHeaderMotionContextOrThrow.mockReturnValue(bridgeValue);
51
+
52
+ expect(useHeaderMotionBridge()).toBe(bridgeValue);
53
+ expect(mockUseHeaderMotionContextOrThrow).toHaveBeenCalledWith(
54
+ 'useHeaderMotionBridge must be used within <HeaderMotion />. Use it only when bridging context into a separate subtree with <HeaderMotion.Bridge /> and <HeaderMotion.NavigationBridge />.'
55
+ );
56
+ });
57
+
58
+ it('rethrows missing-context errors from the helper hook', () => {
59
+ const error = new Error('missing context');
60
+ mockUseHeaderMotionContextOrThrow.mockImplementation(() => {
61
+ throw error;
62
+ });
63
+
64
+ expect(() => useMotionProgress()).toThrow(error);
65
+ expect(() => useHeaderMotionBridge()).toThrow(error);
66
+ });
67
+ });
@@ -1,56 +1,31 @@
1
- import { useContext } from 'react';
2
- import { HeaderMotionContext } from '../context';
1
+ import { useHeaderMotionContextOrThrow } from '../context';
3
2
  import type { MotionProgress } from '../types';
4
3
 
5
4
  /**
6
- * Hook to access motion progress values and measuring functions for header animations.
7
- * Returns the progress value (0-1), threshold, and measurement functions.
5
+ * Returns the two shared values most header animations actually need:
6
+ * `progress` and `progressThreshold`.
8
7
  *
9
- * Must be used within a {@link HeaderMotion} component.
8
+ * Use this inside your animated header components to derive transforms,
9
+ * opacity, scale, parallax, or any other visual response to scroll.
10
10
  *
11
- * @returns Motion progress values and measuring functions:
12
- * - `progress`: Shared value from 0 to 1
13
- * - `progressThreshold`: The threshold at which animation completes
14
- * - `measureTotalHeight`: Function to measure total header height. Should be passed to the `onLayout` prop of the base of a header, to let scrollables account for the total header height
15
- * - `measureDynamic`: Function to measure a dimension of choice of the animated element of the header - should be passed to the `onLayout` prop of such. If used, can be used for dynamic calculation of the {@link progressThreshold}.
16
- *
17
- * @throws Error if used outside of a {@link HeaderMotion} component
11
+ * `progress` usually lives in the `0..1` range, where `0` is the expanded
12
+ * state and `1` is the fully collapsed state. `progressThreshold` is the pixel
13
+ * distance that corresponds to that transition.
18
14
  *
19
15
  * @example
20
16
  * ```tsx
21
17
  * function MyHeader() {
22
- * const { progress, progressThreshold, measureTotalHeight, measureDynamic } = useMotionProgress();
23
- * const dynamicStyle = useAnimatedStyle(() => {
24
- * const translateY = interpolate(
25
- * progress.value,
26
- * [0, 1],
27
- * [0, -progressThreshold],
28
- * Extrapolation.CLAMP,
29
- * )
30
- * return { transform: [{ translateY }] }
31
- * })
32
- * return (
33
- * <AnimatedHeaderBase onLayout={measureTotalHeight}>
34
- * <Animated.View onLayout={measureDynamic} style={dynamicStyle} />
35
- * </AnimatedHeaderBase>
36
- * )
18
+ * const { progress, progressThreshold } = useMotionProgress();
37
19
  * }
38
20
  * ```
39
21
  */
40
22
  export function useMotionProgress(): MotionProgress {
41
- const ctxValue = useContext(HeaderMotionContext);
42
- if (!ctxValue) {
43
- throw new Error(
44
- 'useMotionProgress must be used within a <HeaderMotion /> component. If using inside a navigation header, consider using <HeaderMotion.Header /> instead to ensure context access.'
45
- );
46
- }
47
- const { progress, measureTotalHeight, measureDynamic, progressThreshold } =
48
- ctxValue;
23
+ const { progress, progressThreshold } = useHeaderMotionContextOrThrow(
24
+ 'useMotionProgress must be used within <HeaderMotion /> or <HeaderMotion.NavigationBridge />. If you are rendering inside a navigation header, bridge the context with <HeaderMotion.Bridge /> and <HeaderMotion.NavigationBridge />.'
25
+ );
49
26
 
50
27
  return {
51
28
  progress,
52
- measureTotalHeight,
53
- measureDynamic,
54
29
  progressThreshold,
55
30
  };
56
31
  }