react-native-header-motion 1.0.0-alpha.0 → 1.0.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 (137) hide show
  1. package/README.md +65 -528
  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 -54
  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 +14 -20
  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 +19 -7
  17. package/lib/module/components/ScrollManager.js.map +1 -1
  18. package/lib/module/components/ScrollView.js +6 -39
  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/useConsumerScrollHandlers.js +86 -0
  31. package/lib/module/hooks/useConsumerScrollHandlers.js.map +1 -0
  32. package/lib/module/hooks/useHeaderMotionBridge.js +14 -0
  33. package/lib/module/hooks/useHeaderMotionBridge.js.map +1 -0
  34. package/lib/module/hooks/useMotionProgress.js +12 -42
  35. package/lib/module/hooks/useMotionProgress.js.map +1 -1
  36. package/lib/module/hooks/useMotionProgress.test.js +56 -0
  37. package/lib/module/hooks/useMotionProgress.test.js.map +1 -0
  38. package/lib/module/hooks/useScrollManager.js +168 -87
  39. package/lib/module/hooks/useScrollManager.js.map +1 -1
  40. package/lib/module/index.js +21 -18
  41. package/lib/module/index.js.map +1 -1
  42. package/lib/module/utils/defaults.js +2 -1
  43. package/lib/module/utils/defaults.js.map +1 -1
  44. package/lib/module/utils/header.js +24 -0
  45. package/lib/module/utils/header.js.map +1 -0
  46. package/lib/module/utils/headerOffsetStyle.js +31 -0
  47. package/lib/module/utils/headerOffsetStyle.js.map +1 -0
  48. package/lib/module/utils/index.js +2 -0
  49. package/lib/module/utils/index.js.map +1 -1
  50. package/lib/typescript/docs/docusaurus.config.d.ts +4 -0
  51. package/lib/typescript/docs/docusaurus.config.d.ts.map +1 -0
  52. package/lib/typescript/docs/sidebars.d.ts +4 -0
  53. package/lib/typescript/docs/sidebars.d.ts.map +1 -0
  54. package/lib/typescript/docs/src/pages/index.d.ts +2 -0
  55. package/lib/typescript/docs/src/pages/index.d.ts.map +1 -0
  56. package/lib/typescript/src/components/Bridge.d.ts +19 -0
  57. package/lib/typescript/src/components/Bridge.d.ts.map +1 -0
  58. package/lib/typescript/src/components/FlatList.d.ts +7 -15
  59. package/lib/typescript/src/components/FlatList.d.ts.map +1 -1
  60. package/lib/typescript/src/components/Header.d.ts +73 -12
  61. package/lib/typescript/src/components/Header.d.ts.map +1 -1
  62. package/lib/typescript/src/components/HeaderDynamic.d.ts +11 -0
  63. package/lib/typescript/src/components/HeaderDynamic.d.ts.map +1 -0
  64. package/lib/typescript/src/components/HeaderMotion.d.ts +38 -23
  65. package/lib/typescript/src/components/HeaderMotion.d.ts.map +1 -1
  66. package/lib/typescript/src/components/HeaderPanBoundary.d.ts +11 -0
  67. package/lib/typescript/src/components/HeaderPanBoundary.d.ts.map +1 -0
  68. package/lib/typescript/src/components/NavigationBridge.d.ts +19 -0
  69. package/lib/typescript/src/components/NavigationBridge.d.ts.map +1 -0
  70. package/lib/typescript/src/components/ScrollManager.d.ts +13 -9
  71. package/lib/typescript/src/components/ScrollManager.d.ts.map +1 -1
  72. package/lib/typescript/src/components/ScrollView.d.ts +7 -14
  73. package/lib/typescript/src/components/ScrollView.d.ts.map +1 -1
  74. package/lib/typescript/src/components/createHeaderMotionScrollable.d.ts +86 -0
  75. package/lib/typescript/src/components/createHeaderMotionScrollable.d.ts.map +1 -0
  76. package/lib/typescript/src/components/index.d.ts +3 -1
  77. package/lib/typescript/src/components/index.d.ts.map +1 -1
  78. package/lib/typescript/src/context.d.ts +3 -17
  79. package/lib/typescript/src/context.d.ts.map +1 -1
  80. package/lib/typescript/src/hooks/index.d.ts +1 -0
  81. package/lib/typescript/src/hooks/index.d.ts.map +1 -1
  82. package/lib/typescript/src/hooks/useActiveScrollId.d.ts +7 -6
  83. package/lib/typescript/src/hooks/useActiveScrollId.d.ts.map +1 -1
  84. package/lib/typescript/src/hooks/useConsumerScrollHandlers.d.ts +64 -0
  85. package/lib/typescript/src/hooks/useConsumerScrollHandlers.d.ts.map +1 -0
  86. package/lib/typescript/src/hooks/useHeaderMotionBridge.d.ts +10 -0
  87. package/lib/typescript/src/hooks/useHeaderMotionBridge.d.ts.map +1 -0
  88. package/lib/typescript/src/hooks/useMotionProgress.d.ts +8 -25
  89. package/lib/typescript/src/hooks/useMotionProgress.d.ts.map +1 -1
  90. package/lib/typescript/src/hooks/useMotionProgress.test.d.ts +2 -0
  91. package/lib/typescript/src/hooks/useMotionProgress.test.d.ts.map +1 -0
  92. package/lib/typescript/src/hooks/useScrollManager.d.ts +61 -29
  93. package/lib/typescript/src/hooks/useScrollManager.d.ts.map +1 -1
  94. package/lib/typescript/src/index.d.ts +56 -26
  95. package/lib/typescript/src/index.d.ts.map +1 -1
  96. package/lib/typescript/src/types.d.ts +54 -17
  97. package/lib/typescript/src/types.d.ts.map +1 -1
  98. package/lib/typescript/src/utils/defaults.d.ts +3 -2
  99. package/lib/typescript/src/utils/defaults.d.ts.map +1 -1
  100. package/lib/typescript/src/utils/header.d.ts +10 -0
  101. package/lib/typescript/src/utils/header.d.ts.map +1 -0
  102. package/lib/typescript/src/utils/headerOffsetStyle.d.ts +19 -0
  103. package/lib/typescript/src/utils/headerOffsetStyle.d.ts.map +1 -0
  104. package/lib/typescript/src/utils/index.d.ts +2 -0
  105. package/lib/typescript/src/utils/index.d.ts.map +1 -1
  106. package/lib/typescript/src/utils/refreshControl.d.ts +12 -12
  107. package/package.json +12 -5
  108. package/src/components/Bridge.tsx +29 -0
  109. package/src/components/FlatList.tsx +18 -76
  110. package/src/components/Header.tsx +159 -23
  111. package/src/components/HeaderDynamic.tsx +45 -0
  112. package/src/components/HeaderMotion.tsx +47 -50
  113. package/src/components/HeaderPanBoundary.tsx +92 -0
  114. package/src/components/NavigationBridge.tsx +30 -0
  115. package/src/components/ScrollManager.tsx +23 -11
  116. package/src/components/ScrollView.tsx +16 -60
  117. package/src/components/createHeaderMotionScrollable.tsx +438 -0
  118. package/src/components/index.ts +3 -1
  119. package/src/context.ts +11 -24
  120. package/src/hooks/index.ts +1 -0
  121. package/src/hooks/useActiveScrollId.ts +7 -6
  122. package/src/hooks/useConsumerScrollHandlers.ts +148 -0
  123. package/src/hooks/useHeaderMotionBridge.ts +15 -0
  124. package/src/hooks/useMotionProgress.test.ts +67 -0
  125. package/src/hooks/useMotionProgress.ts +12 -45
  126. package/src/hooks/useScrollManager.ts +251 -114
  127. package/src/index.ts +82 -36
  128. package/src/types.ts +81 -29
  129. package/src/utils/defaults.ts +7 -1
  130. package/src/utils/header.tsx +52 -0
  131. package/src/utils/headerOffsetStyle.ts +40 -0
  132. package/src/utils/index.ts +2 -0
  133. package/lib/module/components/HeaderBase.js +0 -107
  134. package/lib/module/components/HeaderBase.js.map +0 -1
  135. package/lib/typescript/src/components/HeaderBase.d.ts +0 -41
  136. package/lib/typescript/src/components/HeaderBase.d.ts.map +0 -1
  137. package/src/components/HeaderBase.tsx +0 -140
package/src/index.ts CHANGED
@@ -1,76 +1,122 @@
1
1
  import {
2
- AnimatedHeaderBase,
3
- HeaderBase,
2
+ createHeaderMotionScrollable,
3
+ Bridge,
4
4
  HeaderMotionContextProvider,
5
- HeaderMotionFlatList,
6
- HeaderMotionHeader,
7
- HeaderMotionScrollManager,
8
- HeaderMotionScrollView,
5
+ FlatList,
6
+ Header,
7
+ NavigationBridge,
8
+ ScrollManager,
9
+ ScrollView,
10
+ type CreateHeaderMotionScrollableOptions,
11
+ type HeaderProps,
12
+ type HeaderMotionBridgeProps,
9
13
  type HeaderMotionFlatListProps,
10
- type HeaderMotionHeaderProps,
14
+ type HeaderMotionNavigationBridgeProps,
11
15
  type HeaderMotionProps,
12
16
  type HeaderMotionScrollManagerProps,
17
+ type HeaderMotionScrollableOwnProps,
13
18
  type HeaderMotionScrollViewProps,
14
19
  } from './components';
15
- import type { ReactElement } from 'react';
20
+ import type { HeaderDynamicProps } from './types';
16
21
 
17
- /**
18
- * Compound component type for HeaderMotion.
19
- * Provides the main context provider and sub-components for building collapsible headers.
20
- */
21
- type HeaderMotionComponent = {
22
- /** Main context provider component */
23
- <T extends string>(props: HeaderMotionProps<T>): ReactElement;
24
- /** Component for providing motion progress properties to animated headers.
25
- * Use to pass props to the header components in React Navigation / Expo Router, which cannot access HeaderMotion's context and `useMotionProgress` otherwise.
22
+ type HeaderMotionCompound = typeof HeaderMotionContextProvider & {
23
+ /**
24
+ * Header container that measures the total header height and can optionally
25
+ * make the header surface pannable.
26
+ *
27
+ * Use `HeaderMotion.Header.Dynamic` inside it to mark the part of the header
28
+ * that should define the collapse distance.
29
+ */
30
+ Header: typeof Header;
31
+ /**
32
+ * Captures the current HeaderMotion context and exposes it through a render
33
+ * function so it can be forwarded across a React tree boundary.
34
+ *
35
+ * This is primarily useful for navigation-rendered headers.
36
+ */
37
+ Bridge: typeof Bridge;
38
+ /**
39
+ * Re-provides a previously captured HeaderMotion context value in another
40
+ * subtree.
41
+ *
42
+ * This is primarily useful for navigation libraries that render headers
43
+ * outside the screen subtree where `HeaderMotion` lives.
44
+ */
45
+ NavigationBridge: typeof NavigationBridge;
46
+ /**
47
+ * Render-prop wrapper for managing a custom scrollable.
48
+ *
49
+ * Prefer `createHeaderMotionScrollable()` for most custom integrations. Use
50
+ * `ScrollManager` only when the factory approach is not flexible enough.
26
51
  */
27
- Header: typeof HeaderMotionHeader;
28
- /** Component for custom scroll implementations */
29
- ScrollManager: typeof HeaderMotionScrollManager;
30
- /** Animated ScrollView component with header motion integration */
31
- ScrollView: typeof HeaderMotionScrollView;
32
- /** Animated FlatList component with header motion integration */
33
- FlatList: typeof HeaderMotionFlatList;
52
+ ScrollManager: typeof ScrollManager;
53
+ /**
54
+ * Pre-wired `Animated.ScrollView` that participates in HeaderMotion's scroll
55
+ * tracking and header offsetting.
56
+ */
57
+ ScrollView: typeof ScrollView;
58
+ /**
59
+ * Pre-wired `Animated.FlatList` that participates in HeaderMotion's scroll
60
+ * tracking and header offsetting.
61
+ */
62
+ FlatList: typeof FlatList;
34
63
  };
35
64
 
36
65
  /**
37
66
  * Main HeaderMotion component.
38
- * A compound component that provides context for collapsible header animations.
67
+ * Root provider and compound entrypoint for the library.
68
+ *
69
+ * It tracks header measurements, derives the shared `progress` value, and
70
+ * exposes the pre-wired subcomponents used to connect headers and scrollables.
39
71
  *
40
72
  * @example
41
73
  * ```tsx
42
74
  * <HeaderMotion>
43
- * <HeaderMotion.Header>
44
- * {(headerProps) => (
75
+ * <HeaderMotion.Bridge>
76
+ * {(value) => (
45
77
  * <Stack.Screen
46
78
  * options={{
47
79
  * header: () => (
48
- * <MyAnimatedHeader {...headerProps} />
80
+ * <HeaderMotion.NavigationBridge value={value}>
81
+ * <MyAnimatedHeader />
82
+ * </HeaderMotion.NavigationBridge>
49
83
  * ),
50
84
  * }}
51
85
  * />
52
86
  * )}
53
- * </HeaderMotion.Header>
87
+ * </HeaderMotion.Bridge>
54
88
  * <HeaderMotion.ScrollView>
55
89
  * <MyScrollableContent />
56
90
  * </HeaderMotion.ScrollView>
57
91
  * </HeaderMotion>
58
92
  * ```
59
93
  */
60
- const HeaderMotion = HeaderMotionContextProvider as HeaderMotionComponent;
61
- HeaderMotion.Header = HeaderMotionHeader;
62
- HeaderMotion.ScrollManager = HeaderMotionScrollManager;
63
- HeaderMotion.ScrollView = HeaderMotionScrollView;
64
- HeaderMotion.FlatList = HeaderMotionFlatList;
94
+ const HeaderMotion: HeaderMotionCompound = Object.assign(
95
+ HeaderMotionContextProvider,
96
+ {
97
+ Header,
98
+ Bridge,
99
+ NavigationBridge,
100
+ ScrollManager,
101
+ ScrollView,
102
+ FlatList,
103
+ }
104
+ );
65
105
 
66
106
  export default HeaderMotion;
67
107
  export * from './hooks';
68
108
  export type * from './types';
69
- export { AnimatedHeaderBase, HeaderBase };
109
+ export { createHeaderMotionScrollable };
110
+ export { Bridge, Header, NavigationBridge };
70
111
  export type {
112
+ CreateHeaderMotionScrollableOptions,
113
+ HeaderDynamicProps,
71
114
  HeaderMotionFlatListProps,
72
- HeaderMotionHeaderProps,
115
+ HeaderMotionBridgeProps,
116
+ HeaderMotionNavigationBridgeProps,
73
117
  HeaderMotionProps,
74
118
  HeaderMotionScrollManagerProps,
119
+ HeaderMotionScrollableOwnProps,
75
120
  HeaderMotionScrollViewProps,
121
+ HeaderProps,
76
122
  };
package/src/types.ts CHANGED
@@ -1,10 +1,58 @@
1
1
  import type { ReactElement } from 'react';
2
- import type { LayoutChangeEvent, ScrollViewProps } from 'react-native';
3
- import type { AnimatedRef, SharedValue } from 'react-native-reanimated';
2
+ import type {
3
+ LayoutChangeEvent,
4
+ ScrollViewProps,
5
+ ViewProps,
6
+ } from 'react-native';
7
+ import type {
8
+ AnimatedProps,
9
+ AnimatedRef,
10
+ SharedValue,
11
+ } from 'react-native-reanimated';
4
12
  import { DEFAULT_SCROLL_ID } from './utils/defaults';
5
13
  import type { InstanceOrElement } from 'react-native-reanimated/lib/typescript/commonTypes';
14
+ import type {
15
+ GestureStateChangeEvent,
16
+ PanGestureHandlerEventPayload,
17
+ } from 'react-native-gesture-handler';
18
+ import type { WithDecayConfig } from 'react-native-reanimated';
6
19
 
7
20
  export type Progress = SharedValue<number>;
21
+ export type HeaderMotionOffsetStrategy =
22
+ | 'padding'
23
+ | 'margin'
24
+ | 'top'
25
+ | 'translate'
26
+ | 'none';
27
+
28
+ export interface HeaderMotionOffsetProps {
29
+ /**
30
+ * How the scrollable content should be pushed below the measured header.
31
+ *
32
+ * `padding` is the safest default for most screens. `margin`, `top`, and
33
+ * `translate` can be useful when the scrollable or its children need a
34
+ * different layout behavior.
35
+ *
36
+ * `top` and `translate` add bottom compensation so the end of the content
37
+ * remains reachable.
38
+ *
39
+ * @default 'padding'
40
+ */
41
+ headerOffsetStrategy?: HeaderMotionOffsetStrategy;
42
+ /**
43
+ * Adds a minimum content height so scrollables with short content can still collapse the
44
+ * header completely.
45
+ *
46
+ * **Experimental: this relies on extra layout measurement and may still be
47
+ * refined.**
48
+ *
49
+ * Enable this when some screens do not have enough content to naturally
50
+ * scroll through the full collapse distance.
51
+ *
52
+ * @default false
53
+ */
54
+ ensureScrollableContentMinHeight?: boolean;
55
+ }
8
56
 
9
57
  export type ProgressThreshold =
10
58
  | number
@@ -26,46 +74,46 @@ export type ScrollValues = Record<string, ScrollValue> & {
26
74
  [key in typeof DEFAULT_SCROLL_ID]?: ScrollValue;
27
75
  };
28
76
 
29
- export type WithCollapsibleHeaderProps<
30
- T extends Record<string, unknown> = Record<string, unknown>
31
- > = T & MotionProgress;
32
-
33
- export type WithCollapsiblePagedHeaderProps<
34
- Tab extends string = string,
35
- T extends Record<string, unknown> = Record<string, unknown>
36
- > = WithCollapsibleHeaderProps<T> & {
37
- onTabChange: (newTab: Tab) => void;
38
- activeTab: Tab;
39
- };
40
-
41
77
  export interface MotionProgress {
42
78
  progress: Progress;
43
79
  progressThreshold: SharedValue<number>;
80
+ }
81
+
82
+ export type HeaderPanDecayEvent =
83
+ GestureStateChangeEvent<PanGestureHandlerEventPayload>;
84
+
85
+ export type HeaderPanDecayConfig =
86
+ | WithDecayConfig
87
+ | ((event: HeaderPanDecayEvent) => WithDecayConfig);
88
+
89
+ export type HeaderAsChildProps = {
90
+ asChild: true;
91
+ children: ReactElement;
92
+ };
93
+
94
+ export type HeaderDefaultProps = AnimatedProps<ViewProps> & {
95
+ asChild?: false;
96
+ };
97
+
98
+ export type HeaderDynamicProps = HeaderDefaultProps | HeaderAsChildProps;
99
+
100
+ export interface HeaderMotionBridgeValue extends MotionProgress {
44
101
  measureTotalHeight: MeasureAnimatedHeaderAndSet;
45
102
  measureDynamic: MeasureAnimatedHeaderAndSet;
46
- animatedHeaderBaseProps: AnimatedHeaderBaseMotionProps;
103
+ headerPanMomentumOffset: SharedValue<number | null>;
104
+ scrollValues: SharedValue<ScrollValues>;
47
105
  activeScrollId: SharedValue<string> | undefined;
48
- }
49
-
50
- export interface AnimatedHeaderBaseMotionProps {
51
- enableHeaderPan: boolean;
52
106
  scrollToRef: React.RefObject<ScrollTo | null>;
53
- headerPanMomentumOffset: SharedValue<number | null>;
107
+ originalHeaderHeight: number;
54
108
  }
55
109
 
56
110
  export interface ScrollManagerHeaderMotionContext {
57
- originalHeaderHeight: SharedValue<number>;
58
- minHeightContentContainerStyle:
59
- | {}
60
- | {
61
- minHeight: number;
62
- };
111
+ originalHeaderHeight: number;
112
+ contentContainerMinHeight?: number;
63
113
  }
64
114
 
65
115
  export interface ScrollManagerConfig<TRef extends InstanceOrElement = any> {
66
- scrollableProps: Required<
67
- Pick<ScrollViewProps, 'onScroll' | 'scrollEventThrottle'>
68
- > & {
116
+ scrollableProps: Pick<ScrollViewProps, 'onScroll' | 'onLayout'> & {
69
117
  refreshControl?: ReactElement;
70
118
  ref: AnimatedRef<TRef>;
71
119
  };
@@ -74,6 +122,10 @@ export interface ScrollManagerConfig<TRef extends InstanceOrElement = any> {
74
122
 
75
123
  export type ScrollTo = (y: number, options?: ScrollToOptions) => void;
76
124
 
125
+ export type ScrollHandlerContext = {
126
+ lastOffset: number | undefined;
127
+ };
128
+
77
129
  interface ScrollToOptions {
78
130
  isValueDelta?: boolean;
79
131
  animated?: boolean;
@@ -1,15 +1,21 @@
1
- import type { MeasureAnimatedHeader, ProgressThreshold } from '../types';
1
+ import type {
2
+ HeaderMotionOffsetStrategy,
3
+ MeasureAnimatedHeader,
4
+ ProgressThreshold,
5
+ } from '../types';
2
6
 
3
7
  const DEFAULT_PROGRESS_THRESHOLD: ProgressThreshold = (measuredHeaderValue) =>
4
8
  measuredHeaderValue;
5
9
  const DEFAULT_MEASURE_DYNAMIC: MeasureAnimatedHeader = (e) =>
6
10
  e.nativeEvent.layout.height;
11
+ const DEFAULT_HEADER_OFFSET_STRATEGY: HeaderMotionOffsetStrategy = 'padding';
7
12
 
8
13
  // Symbol doesn't work?
9
14
  // const DEFAULT_SCROLL_ID = Symbol("HEADER_MOTION_DEFAULT_SCROLL_ID");
10
15
  const DEFAULT_SCROLL_ID = '__HEADER_MOTION_DEFAULT_SCROLL_ID__';
11
16
 
12
17
  export {
18
+ DEFAULT_HEADER_OFFSET_STRATEGY,
13
19
  DEFAULT_MEASURE_DYNAMIC,
14
20
  DEFAULT_PROGRESS_THRESHOLD,
15
21
  DEFAULT_SCROLL_ID,
@@ -0,0 +1,52 @@
1
+ import {
2
+ Fragment,
3
+ cloneElement,
4
+ isValidElement,
5
+ type ReactElement,
6
+ } from 'react';
7
+ import type { ViewProps } from 'react-native';
8
+
9
+ export type SlottableElementProps = {
10
+ onLayout?: ViewProps['onLayout'];
11
+ };
12
+
13
+ export type SlottableElement = ReactElement<SlottableElementProps>;
14
+
15
+ export function composeOnLayoutHandlers(
16
+ userHandler: ViewProps['onLayout'],
17
+ internalHandler: ViewProps['onLayout']
18
+ ) {
19
+ return (e: Parameters<NonNullable<ViewProps['onLayout']>>[0]) => {
20
+ internalHandler?.(e);
21
+ userHandler?.(e);
22
+ };
23
+ }
24
+
25
+ export function resolveSlottableChild(
26
+ componentName: string,
27
+ child: ReactElement
28
+ ) {
29
+ if (!isValidElement(child) || child.type === Fragment) {
30
+ throw new Error(
31
+ `${componentName} with \`asChild\` expects a single valid React element child that accepts \`onLayout\`.`
32
+ );
33
+ }
34
+
35
+ return child as SlottableElement;
36
+ }
37
+
38
+ export function cloneWithOnLayout(
39
+ child: SlottableElement,
40
+ onLayout: ViewProps['onLayout'],
41
+ componentName: string
42
+ ) {
43
+ if (!isValidElement(child)) {
44
+ throw new Error(
45
+ `${componentName} with \`asChild\` expects a valid React element child.`
46
+ );
47
+ }
48
+
49
+ return cloneElement(child, {
50
+ onLayout: composeOnLayoutHandlers(child.props.onLayout, onLayout),
51
+ });
52
+ }
@@ -0,0 +1,40 @@
1
+ import type { SharedValue } from 'react-native-reanimated';
2
+ import type { HeaderMotionOffsetStrategy } from '../types';
3
+ import { DEFAULT_HEADER_OFFSET_STRATEGY } from './defaults';
4
+
5
+ type HeaderOffsetValue = number | SharedValue<number>;
6
+
7
+ type HeaderOffsetStyle =
8
+ | undefined
9
+ | { paddingTop: HeaderOffsetValue }
10
+ | { marginTop: HeaderOffsetValue }
11
+ | { top: HeaderOffsetValue; paddingBottom: HeaderOffsetValue }
12
+ | {
13
+ transform: [{ translateY: HeaderOffsetValue }];
14
+ paddingBottom: HeaderOffsetValue;
15
+ };
16
+
17
+ export function resolveHeaderOffsetStyle(
18
+ originalHeaderHeight: HeaderOffsetValue,
19
+ headerOffsetStrategy: HeaderMotionOffsetStrategy = DEFAULT_HEADER_OFFSET_STRATEGY
20
+ ): HeaderOffsetStyle {
21
+ switch (headerOffsetStrategy) {
22
+ case 'none':
23
+ return undefined;
24
+ case 'margin':
25
+ return { marginTop: originalHeaderHeight };
26
+ case 'top':
27
+ return {
28
+ top: originalHeaderHeight,
29
+ paddingBottom: originalHeaderHeight,
30
+ };
31
+ case 'translate':
32
+ return {
33
+ transform: [{ translateY: originalHeaderHeight }],
34
+ paddingBottom: originalHeaderHeight,
35
+ };
36
+ case 'padding':
37
+ default:
38
+ return { paddingTop: originalHeaderHeight };
39
+ }
40
+ }
@@ -1,3 +1,5 @@
1
1
  export * from './defaults';
2
+ export * from './header';
3
+ export * from './headerOffsetStyle';
2
4
  export * from './values';
3
5
  export * from './refreshControl';
@@ -1,107 +0,0 @@
1
- "use strict";
2
-
3
- import { useMemo } from 'react';
4
- import { Platform, StyleSheet, View } from 'react-native';
5
- import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
6
- import Animated, { useAnimatedReaction, withDecay } from 'react-native-reanimated';
7
- import { jsx as _jsx } from "react/jsx-runtime";
8
- const PLATFORM_PANNING_ENABLED = Platform.select({
9
- default: true,
10
- android: false
11
- });
12
- /**
13
- * Base header component with absolute positioning.
14
- * Provides a foundation for building headers that need to be positioned absolutely.
15
- *
16
- * @example
17
- * ```tsx
18
- * <HeaderBase
19
- * onLayout={measureTotalHeight}
20
- * >
21
- * ...
22
- * </HeaderBase>
23
- * ```
24
- */
25
- export function HeaderBase({
26
- style,
27
- ...rest
28
- }) {
29
- return /*#__PURE__*/_jsx(View, {
30
- style: [style, styles.container],
31
- ...rest
32
- });
33
- }
34
-
35
- /**
36
- * Animated version of HeaderBase using Reanimated's Animated.View.
37
- * Use this when you need to animate the header based on scroll progress.
38
- *
39
- * @example
40
- * ```tsx
41
- * <AnimatedHeaderBase
42
- * onLayout={measureTotalHeight}
43
- * style={[{ paddingTop: insets.top }, animatedStyle]}
44
- * >
45
- * ...
46
- * </AnimatedHeaderBase>
47
- * ```
48
- */
49
-
50
- // TODO: Thinking about DX, perhaps creating another context in AnimatedHeaderBase or somewhere else could make sense
51
- // Note: Depending on feedback, there might be a need to intercept ongoing scroll when starting to pan (perhaps even on the tap itself but to be checked what feels better when using)
52
- // Note: Depending on feedback, there might be a need to block momentum by forcing scrollTo
53
- export function AnimatedHeaderBase({
54
- style,
55
- animatedHeaderBaseProps,
56
- withGestureHandlerRootView = false,
57
- ...rest
58
- }) {
59
- if (!animatedHeaderBaseProps) {
60
- throw new Error('AnimatedHeaderBase requires `animatedHeaderBaseProps`. Pass the value from HeaderMotion.Header or useMotionProgress.');
61
- }
62
- const {
63
- enableHeaderPan,
64
- scrollToRef,
65
- headerPanMomentumOffset: momentumScrollOffset
66
- } = animatedHeaderBaseProps;
67
- useAnimatedReaction(() => momentumScrollOffset.get(), (offset, prevOffset) => {
68
- if (offset !== null) {
69
- const dy = offset - (prevOffset ?? 0);
70
- scrollToRef.current?.(dy);
71
- }
72
- });
73
- const isPanEnabled = PLATFORM_PANNING_ENABLED && enableHeaderPan;
74
- const pan = useMemo(() => Gesture.Pan().enabled(isPanEnabled).onChange(e => {
75
- const dy = e.changeY;
76
- scrollToRef.current?.(dy);
77
- }).onEnd(e => {
78
- momentumScrollOffset.set(withDecay({
79
- velocity: e.velocityY
80
- }, () => momentumScrollOffset.set(null)));
81
- }).shouldCancelWhenOutside(false),
82
- // Note: In first testing Android works without gesture handler whatsoever. If this functionality is needed, we can further control it with a prop in the future
83
- [isPanEnabled, scrollToRef, momentumScrollOffset]);
84
- const content = /*#__PURE__*/_jsx(GestureDetector, {
85
- gesture: pan,
86
- children: /*#__PURE__*/_jsx(Animated.View, {
87
- style: [style, styles.container],
88
- ...rest
89
- })
90
- });
91
- if (!withGestureHandlerRootView) {
92
- return content;
93
- }
94
- return /*#__PURE__*/_jsx(GestureHandlerRootView, {
95
- children: content
96
- });
97
- }
98
- const styles = StyleSheet.create({
99
- container: {
100
- position: 'absolute',
101
- left: 0,
102
- right: 0
103
- }
104
- });
105
-
106
- // TODO: Lib refactor: context repetition, make people use less boilerplate by just wrapping the header with <HeaderBaseOrSomethingElse ctx={headerProps} /> that does everything under the hood (measuring total for example). That will then allow for people to use context inside
107
- //# sourceMappingURL=HeaderBase.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["useMemo","Platform","StyleSheet","View","Gesture","GestureDetector","GestureHandlerRootView","Animated","useAnimatedReaction","withDecay","jsx","_jsx","PLATFORM_PANNING_ENABLED","select","default","android","HeaderBase","style","rest","styles","container","AnimatedHeaderBase","animatedHeaderBaseProps","withGestureHandlerRootView","Error","enableHeaderPan","scrollToRef","headerPanMomentumOffset","momentumScrollOffset","get","offset","prevOffset","dy","current","isPanEnabled","pan","Pan","enabled","onChange","e","changeY","onEnd","set","velocity","velocityY","shouldCancelWhenOutside","content","gesture","children","create","position","left","right"],"sourceRoot":"../../../src","sources":["components/HeaderBase.tsx"],"mappings":"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","ignoreList":[]}
@@ -1,41 +0,0 @@
1
- import { type ViewProps } from 'react-native';
2
- import { type AnimatedProps } from 'react-native-reanimated';
3
- import type { MotionProgress } from '../types';
4
- export type HeaderBaseProps = ViewProps;
5
- export type AnimatedHeaderBaseProps = AnimatedProps<ViewProps> & Pick<MotionProgress, 'animatedHeaderBaseProps'> & {
6
- /**
7
- * Wraps the header with GestureHandlerRootView.
8
- * Keep this disabled when your app already has a root-level GestureHandlerRootView.
9
- */
10
- withGestureHandlerRootView?: boolean;
11
- };
12
- /**
13
- * Base header component with absolute positioning.
14
- * Provides a foundation for building headers that need to be positioned absolutely.
15
- *
16
- * @example
17
- * ```tsx
18
- * <HeaderBase
19
- * onLayout={measureTotalHeight}
20
- * >
21
- * ...
22
- * </HeaderBase>
23
- * ```
24
- */
25
- export declare function HeaderBase({ style, ...rest }: HeaderBaseProps): import("react/jsx-runtime").JSX.Element;
26
- /**
27
- * Animated version of HeaderBase using Reanimated's Animated.View.
28
- * Use this when you need to animate the header based on scroll progress.
29
- *
30
- * @example
31
- * ```tsx
32
- * <AnimatedHeaderBase
33
- * onLayout={measureTotalHeight}
34
- * style={[{ paddingTop: insets.top }, animatedStyle]}
35
- * >
36
- * ...
37
- * </AnimatedHeaderBase>
38
- * ```
39
- */
40
- export declare function AnimatedHeaderBase({ style, animatedHeaderBaseProps, withGestureHandlerRootView, ...rest }: AnimatedHeaderBaseProps): import("react/jsx-runtime").JSX.Element;
41
- //# sourceMappingURL=HeaderBase.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"HeaderBase.d.ts","sourceRoot":"","sources":["../../../../src/components/HeaderBase.tsx"],"names":[],"mappings":"AACA,OAAO,EAA8B,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAM1E,OAAiB,EAGf,KAAK,aAAa,EACnB,MAAM,yBAAyB,CAAC;AAOjC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C,MAAM,MAAM,eAAe,GAAG,SAAS,CAAC;AACxC,MAAM,MAAM,uBAAuB,GAAG,aAAa,CAAC,SAAS,CAAC,GAC5D,IAAI,CAAC,cAAc,EAAE,yBAAyB,CAAC,GAAG;IAChD;;;OAGG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;CACtC,CAAC;AAEJ;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,EAAE,eAAe,2CAE7D;AAED;;;;;;;;;;;;;GAaG;AAKH,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,uBAAuB,EACvB,0BAAkC,EAClC,GAAG,IAAI,EACR,EAAE,uBAAuB,2CA2DzB"}