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.
- package/README.md +65 -528
- package/lib/module/components/Bridge.js +16 -0
- package/lib/module/components/Bridge.js.map +1 -0
- package/lib/module/components/FlatList.js +5 -54
- package/lib/module/components/FlatList.js.map +1 -1
- package/lib/module/components/Header.js +71 -13
- package/lib/module/components/Header.js.map +1 -1
- package/lib/module/components/HeaderDynamic.js +34 -0
- package/lib/module/components/HeaderDynamic.js.map +1 -0
- package/lib/module/components/HeaderMotion.js +14 -20
- package/lib/module/components/HeaderMotion.js.map +1 -1
- package/lib/module/components/HeaderPanBoundary.js +54 -0
- package/lib/module/components/HeaderPanBoundary.js.map +1 -0
- package/lib/module/components/NavigationBridge.js +20 -0
- package/lib/module/components/NavigationBridge.js.map +1 -0
- package/lib/module/components/ScrollManager.js +19 -7
- package/lib/module/components/ScrollManager.js.map +1 -1
- package/lib/module/components/ScrollView.js +6 -39
- package/lib/module/components/ScrollView.js.map +1 -1
- package/lib/module/components/createHeaderMotionScrollable.js +136 -0
- package/lib/module/components/createHeaderMotionScrollable.js.map +1 -0
- package/lib/module/components/index.js +3 -1
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/context.js +8 -1
- package/lib/module/context.js.map +1 -1
- package/lib/module/hooks/index.js +1 -0
- package/lib/module/hooks/index.js.map +1 -1
- package/lib/module/hooks/useActiveScrollId.js +7 -6
- package/lib/module/hooks/useActiveScrollId.js.map +1 -1
- package/lib/module/hooks/useConsumerScrollHandlers.js +86 -0
- package/lib/module/hooks/useConsumerScrollHandlers.js.map +1 -0
- package/lib/module/hooks/useHeaderMotionBridge.js +14 -0
- package/lib/module/hooks/useHeaderMotionBridge.js.map +1 -0
- package/lib/module/hooks/useMotionProgress.js +12 -42
- package/lib/module/hooks/useMotionProgress.js.map +1 -1
- package/lib/module/hooks/useMotionProgress.test.js +56 -0
- package/lib/module/hooks/useMotionProgress.test.js.map +1 -0
- package/lib/module/hooks/useScrollManager.js +168 -87
- package/lib/module/hooks/useScrollManager.js.map +1 -1
- package/lib/module/index.js +21 -18
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/defaults.js +2 -1
- package/lib/module/utils/defaults.js.map +1 -1
- package/lib/module/utils/header.js +24 -0
- package/lib/module/utils/header.js.map +1 -0
- package/lib/module/utils/headerOffsetStyle.js +31 -0
- package/lib/module/utils/headerOffsetStyle.js.map +1 -0
- package/lib/module/utils/index.js +2 -0
- package/lib/module/utils/index.js.map +1 -1
- package/lib/typescript/docs/docusaurus.config.d.ts +4 -0
- package/lib/typescript/docs/docusaurus.config.d.ts.map +1 -0
- package/lib/typescript/docs/sidebars.d.ts +4 -0
- package/lib/typescript/docs/sidebars.d.ts.map +1 -0
- package/lib/typescript/docs/src/pages/index.d.ts +2 -0
- package/lib/typescript/docs/src/pages/index.d.ts.map +1 -0
- package/lib/typescript/src/components/Bridge.d.ts +19 -0
- package/lib/typescript/src/components/Bridge.d.ts.map +1 -0
- package/lib/typescript/src/components/FlatList.d.ts +7 -15
- package/lib/typescript/src/components/FlatList.d.ts.map +1 -1
- package/lib/typescript/src/components/Header.d.ts +73 -12
- package/lib/typescript/src/components/Header.d.ts.map +1 -1
- package/lib/typescript/src/components/HeaderDynamic.d.ts +11 -0
- package/lib/typescript/src/components/HeaderDynamic.d.ts.map +1 -0
- package/lib/typescript/src/components/HeaderMotion.d.ts +38 -23
- package/lib/typescript/src/components/HeaderMotion.d.ts.map +1 -1
- package/lib/typescript/src/components/HeaderPanBoundary.d.ts +11 -0
- package/lib/typescript/src/components/HeaderPanBoundary.d.ts.map +1 -0
- package/lib/typescript/src/components/NavigationBridge.d.ts +19 -0
- package/lib/typescript/src/components/NavigationBridge.d.ts.map +1 -0
- package/lib/typescript/src/components/ScrollManager.d.ts +13 -9
- package/lib/typescript/src/components/ScrollManager.d.ts.map +1 -1
- package/lib/typescript/src/components/ScrollView.d.ts +7 -14
- package/lib/typescript/src/components/ScrollView.d.ts.map +1 -1
- package/lib/typescript/src/components/createHeaderMotionScrollable.d.ts +86 -0
- package/lib/typescript/src/components/createHeaderMotionScrollable.d.ts.map +1 -0
- package/lib/typescript/src/components/index.d.ts +3 -1
- package/lib/typescript/src/components/index.d.ts.map +1 -1
- package/lib/typescript/src/context.d.ts +3 -17
- package/lib/typescript/src/context.d.ts.map +1 -1
- package/lib/typescript/src/hooks/index.d.ts +1 -0
- package/lib/typescript/src/hooks/index.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useActiveScrollId.d.ts +7 -6
- package/lib/typescript/src/hooks/useActiveScrollId.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useConsumerScrollHandlers.d.ts +64 -0
- package/lib/typescript/src/hooks/useConsumerScrollHandlers.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useHeaderMotionBridge.d.ts +10 -0
- package/lib/typescript/src/hooks/useHeaderMotionBridge.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useMotionProgress.d.ts +8 -25
- package/lib/typescript/src/hooks/useMotionProgress.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useMotionProgress.test.d.ts +2 -0
- package/lib/typescript/src/hooks/useMotionProgress.test.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useScrollManager.d.ts +61 -29
- package/lib/typescript/src/hooks/useScrollManager.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +56 -26
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +54 -17
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/defaults.d.ts +3 -2
- package/lib/typescript/src/utils/defaults.d.ts.map +1 -1
- package/lib/typescript/src/utils/header.d.ts +10 -0
- package/lib/typescript/src/utils/header.d.ts.map +1 -0
- package/lib/typescript/src/utils/headerOffsetStyle.d.ts +19 -0
- package/lib/typescript/src/utils/headerOffsetStyle.d.ts.map +1 -0
- package/lib/typescript/src/utils/index.d.ts +2 -0
- package/lib/typescript/src/utils/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/refreshControl.d.ts +12 -12
- package/package.json +12 -5
- package/src/components/Bridge.tsx +29 -0
- package/src/components/FlatList.tsx +18 -76
- package/src/components/Header.tsx +159 -23
- package/src/components/HeaderDynamic.tsx +45 -0
- package/src/components/HeaderMotion.tsx +47 -50
- package/src/components/HeaderPanBoundary.tsx +92 -0
- package/src/components/NavigationBridge.tsx +30 -0
- package/src/components/ScrollManager.tsx +23 -11
- package/src/components/ScrollView.tsx +16 -60
- package/src/components/createHeaderMotionScrollable.tsx +438 -0
- package/src/components/index.ts +3 -1
- package/src/context.ts +11 -24
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useActiveScrollId.ts +7 -6
- package/src/hooks/useConsumerScrollHandlers.ts +148 -0
- package/src/hooks/useHeaderMotionBridge.ts +15 -0
- package/src/hooks/useMotionProgress.test.ts +67 -0
- package/src/hooks/useMotionProgress.ts +12 -45
- package/src/hooks/useScrollManager.ts +251 -114
- package/src/index.ts +82 -36
- package/src/types.ts +81 -29
- package/src/utils/defaults.ts +7 -1
- package/src/utils/header.tsx +52 -0
- package/src/utils/headerOffsetStyle.ts +40 -0
- package/src/utils/index.ts +2 -0
- package/lib/module/components/HeaderBase.js +0 -107
- package/lib/module/components/HeaderBase.js.map +0 -1
- package/lib/typescript/src/components/HeaderBase.d.ts +0 -41
- package/lib/typescript/src/components/HeaderBase.d.ts.map +0 -1
- package/src/components/HeaderBase.tsx +0 -140
package/src/index.ts
CHANGED
|
@@ -1,76 +1,122 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
createHeaderMotionScrollable,
|
|
3
|
+
Bridge,
|
|
4
4
|
HeaderMotionContextProvider,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
|
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 {
|
|
20
|
+
import type { HeaderDynamicProps } from './types';
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
*
|
|
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.
|
|
44
|
-
* {(
|
|
75
|
+
* <HeaderMotion.Bridge>
|
|
76
|
+
* {(value) => (
|
|
45
77
|
* <Stack.Screen
|
|
46
78
|
* options={{
|
|
47
79
|
* header: () => (
|
|
48
|
-
* <
|
|
80
|
+
* <HeaderMotion.NavigationBridge value={value}>
|
|
81
|
+
* <MyAnimatedHeader />
|
|
82
|
+
* </HeaderMotion.NavigationBridge>
|
|
49
83
|
* ),
|
|
50
84
|
* }}
|
|
51
85
|
* />
|
|
52
86
|
* )}
|
|
53
|
-
* </HeaderMotion.
|
|
87
|
+
* </HeaderMotion.Bridge>
|
|
54
88
|
* <HeaderMotion.ScrollView>
|
|
55
89
|
* <MyScrollableContent />
|
|
56
90
|
* </HeaderMotion.ScrollView>
|
|
57
91
|
* </HeaderMotion>
|
|
58
92
|
* ```
|
|
59
93
|
*/
|
|
60
|
-
const HeaderMotion =
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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 {
|
|
109
|
+
export { createHeaderMotionScrollable };
|
|
110
|
+
export { Bridge, Header, NavigationBridge };
|
|
70
111
|
export type {
|
|
112
|
+
CreateHeaderMotionScrollableOptions,
|
|
113
|
+
HeaderDynamicProps,
|
|
71
114
|
HeaderMotionFlatListProps,
|
|
72
|
-
|
|
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 {
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
107
|
+
originalHeaderHeight: number;
|
|
54
108
|
}
|
|
55
109
|
|
|
56
110
|
export interface ScrollManagerHeaderMotionContext {
|
|
57
|
-
originalHeaderHeight:
|
|
58
|
-
|
|
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:
|
|
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;
|
package/src/utils/defaults.ts
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import type {
|
|
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
|
+
}
|
package/src/utils/index.ts
CHANGED
|
@@ -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"}
|