@legendapp/list 2.0.17 → 2.0.18

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/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## 2.0.18
2
+ - Improvement: KeyboardAvoidingLegendList now supports KeyboardGestureArea with improved interactive behavior
3
+
1
4
  ## 2.0.17
2
5
  - Feat: Add stickyHeaderOffset property to control sticky header positioning
3
6
  - Feat: Add sticky header backdrop component support
package/index.js CHANGED
@@ -2441,7 +2441,6 @@ function onScroll(ctx, state, event) {
2441
2441
  }
2442
2442
  let newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
2443
2443
  state.scrollPending = newScroll;
2444
- console.log("newScroll", newScroll);
2445
2444
  const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
2446
2445
  if (state.initialScroll && newScroll > maxOffset) {
2447
2446
  newScroll = maxOffset;
package/index.mjs CHANGED
@@ -2420,7 +2420,6 @@ function onScroll(ctx, state, event) {
2420
2420
  }
2421
2421
  let newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
2422
2422
  state.scrollPending = newScroll;
2423
- console.log("newScroll", newScroll);
2424
2423
  const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
2425
2424
  if (state.initialScroll && newScroll > maxOffset) {
2426
2425
  newScroll = maxOffset;
package/keyboard.d.mts CHANGED
@@ -6,7 +6,7 @@ import { AnimatedLegendListProps } from '@legendapp/list/reanimated';
6
6
 
7
7
  declare const KeyboardAvoidingLegendList: <ItemT>(props: Omit<AnimatedLegendListProps<ItemT>, "contentInset" | "onScroll"> & {
8
8
  onScroll?: (event: ReanimatedScrollEvent) => void;
9
- contentInset?: Insets;
9
+ contentInset?: Insets | undefined;
10
10
  safeAreaInsetBottom?: number;
11
11
  } & React.RefAttributes<LegendListRef>) => React.ReactNode;
12
12
 
package/keyboard.d.ts CHANGED
@@ -6,7 +6,7 @@ import { AnimatedLegendListProps } from '@legendapp/list/reanimated';
6
6
 
7
7
  declare const KeyboardAvoidingLegendList: <ItemT>(props: Omit<AnimatedLegendListProps<ItemT>, "contentInset" | "onScroll"> & {
8
8
  onScroll?: (event: ReanimatedScrollEvent) => void;
9
- contentInset?: Insets;
9
+ contentInset?: Insets | undefined;
10
10
  safeAreaInsetBottom?: number;
11
11
  } & React.RefAttributes<LegendListRef>) => React.ReactNode;
12
12
 
package/keyboard.js CHANGED
@@ -57,20 +57,27 @@ var KeyboardAvoidingLegendList = React.forwardRef(function KeyboardAvoidingLegen
57
57
  horizontal,
58
58
  onScroll: onScrollProp,
59
59
  safeAreaInsetBottom = 0,
60
+ style: styleProp,
60
61
  ...rest
61
62
  } = props;
63
+ const styleFlattened = reactNative.StyleSheet.flatten(styleProp);
62
64
  const refLegendList = React.useRef(null);
63
65
  const combinedRef = useCombinedRef(forwardedRef, refLegendList);
66
+ const isIos = reactNative.Platform.OS === "ios";
67
+ const isAndroid = reactNative.Platform.OS === "android";
64
68
  const scrollViewRef = reactNativeReanimated.useAnimatedRef();
65
69
  const scrollOffsetY = reactNativeReanimated.useSharedValue(0);
66
70
  const animatedOffsetY = reactNativeReanimated.useSharedValue(null);
67
71
  const scrollOffsetAtKeyboardStart = reactNativeReanimated.useSharedValue(0);
72
+ const mode = reactNativeReanimated.useSharedValue("idle");
68
73
  const keyboardInset = reactNativeReanimated.useSharedValue(0);
69
74
  const keyboardHeight = reactNativeReanimated.useSharedValue(0);
70
75
  const isOpening = reactNativeReanimated.useSharedValue(false);
76
+ const didInteractive = reactNativeReanimated.useSharedValue(false);
77
+ const isKeyboardOpen = reactNativeReanimated.useSharedValue(false);
71
78
  const scrollHandler = reactNativeReanimated.useAnimatedScrollHandler(
72
79
  (event) => {
73
- scrollOffsetY.value = event.contentOffset[horizontal ? "x" : "y"];
80
+ scrollOffsetY.set(event.contentOffset[horizontal ? "x" : "y"]);
74
81
  if (onScrollProp) {
75
82
  reactNativeReanimated.runOnJS(onScrollProp)(event);
76
83
  }
@@ -89,37 +96,79 @@ var KeyboardAvoidingLegendList = React.forwardRef(function KeyboardAvoidingLegen
89
96
  {
90
97
  onStart: (event) => {
91
98
  "worklet";
92
- if (event.height > 0) {
93
- keyboardHeight.set(event.height - safeAreaInsetBottom);
99
+ mode.set("running");
100
+ if (isKeyboardOpen.get() && event.progress === 1 && event.height > 0) {
101
+ return;
102
+ }
103
+ if (!didInteractive.get()) {
104
+ if (event.height > 0) {
105
+ keyboardHeight.set(event.height - safeAreaInsetBottom);
106
+ }
107
+ isOpening.set(event.progress > 0);
108
+ scrollOffsetAtKeyboardStart.set(scrollOffsetY.get());
109
+ animatedOffsetY.set(scrollOffsetY.get());
110
+ reactNativeReanimated.runOnJS(setScrollProcessingEnabled)(false);
111
+ }
112
+ },
113
+ onInteractive: (event) => {
114
+ "worklet";
115
+ if (mode.get() !== "running") {
116
+ reactNativeReanimated.runOnJS(setScrollProcessingEnabled)(false);
117
+ }
118
+ mode.set("running");
119
+ if (!didInteractive.get()) {
120
+ didInteractive.set(true);
121
+ }
122
+ if (isAndroid && !horizontal) {
123
+ keyboardInset.set(Math.max(0, event.height - safeAreaInsetBottom));
94
124
  }
95
- isOpening.set(event.progress > 0);
96
- scrollOffsetAtKeyboardStart.value = scrollOffsetY.value;
97
- animatedOffsetY.set(scrollOffsetY.value);
98
- reactNativeReanimated.runOnJS(setScrollProcessingEnabled)(false);
99
125
  },
100
126
  onMove: (event) => {
101
127
  "worklet";
102
- const vIsOpening = isOpening.get();
103
- const vKeyboardHeight = keyboardHeight.get();
104
- const vProgress = vIsOpening ? event.progress : 1 - event.progress;
105
- const targetOffset = scrollOffsetAtKeyboardStart.value + (vIsOpening ? vKeyboardHeight : -vKeyboardHeight) * vProgress;
106
- scrollOffsetY.value = targetOffset;
107
- animatedOffsetY.set(targetOffset);
108
- if (!horizontal) {
109
- keyboardInset.value = Math.max(0, event.height - safeAreaInsetBottom);
128
+ if (!didInteractive.get()) {
129
+ const vIsOpening = isOpening.get();
130
+ const vKeyboardHeight = keyboardHeight.get();
131
+ const vProgress = vIsOpening ? event.progress : 1 - event.progress;
132
+ const targetOffset = Math.max(
133
+ 0,
134
+ scrollOffsetAtKeyboardStart.get() + (vIsOpening ? vKeyboardHeight : -vKeyboardHeight) * vProgress
135
+ );
136
+ scrollOffsetY.set(targetOffset);
137
+ animatedOffsetY.set(targetOffset);
138
+ if (!horizontal) {
139
+ keyboardInset.set(Math.max(0, event.height - safeAreaInsetBottom));
140
+ }
110
141
  }
111
142
  },
112
143
  onEnd: (event) => {
113
144
  "worklet";
114
- const vIsOpening = isOpening.get();
115
- const vKeyboardHeight = keyboardHeight.get();
116
- const targetOffset = scrollOffsetAtKeyboardStart.value + (vIsOpening ? vKeyboardHeight : -vKeyboardHeight) * (vIsOpening ? event.progress : 1 - event.progress);
117
- scrollOffsetY.value = targetOffset;
118
- animatedOffsetY.set(targetOffset);
119
- if (!horizontal) {
120
- keyboardInset.value = Math.max(0, event.height - safeAreaInsetBottom);
145
+ const wasInteractive = didInteractive.get();
146
+ const vMode = mode.get();
147
+ mode.set("idle");
148
+ if (vMode === "running") {
149
+ if (!wasInteractive) {
150
+ const vIsOpening = isOpening.get();
151
+ const vKeyboardHeight = keyboardHeight.get();
152
+ const targetOffset = Math.max(
153
+ 0,
154
+ scrollOffsetAtKeyboardStart.get() + (vIsOpening ? vKeyboardHeight : -vKeyboardHeight) * (vIsOpening ? event.progress : 1 - event.progress)
155
+ );
156
+ scrollOffsetY.set(targetOffset);
157
+ animatedOffsetY.set(targetOffset);
158
+ }
159
+ reactNativeReanimated.runOnJS(setScrollProcessingEnabled)(true);
160
+ didInteractive.set(false);
161
+ isKeyboardOpen.set(event.height > 0);
162
+ if (!horizontal) {
163
+ const newInset = Math.max(0, event.height - safeAreaInsetBottom);
164
+ if (newInset > 0) {
165
+ keyboardInset.set(newInset);
166
+ } else {
167
+ keyboardInset.set(newInset);
168
+ animatedOffsetY.set(scrollOffsetY.get());
169
+ }
170
+ }
121
171
  }
122
- reactNativeReanimated.runOnJS(setScrollProcessingEnabled)(true);
123
172
  }
124
173
  },
125
174
  [scrollViewRef, safeAreaInsetBottom]
@@ -127,24 +176,32 @@ var KeyboardAvoidingLegendList = React.forwardRef(function KeyboardAvoidingLegen
127
176
  const animatedProps = reactNativeReanimated.useAnimatedProps(() => {
128
177
  "worklet";
129
178
  var _a, _b, _c, _d;
179
+ const vAnimatedOffsetY = animatedOffsetY.get();
130
180
  const baseProps = {
131
- contentOffset: animatedOffsetY.value === null ? void 0 : {
181
+ contentOffset: vAnimatedOffsetY === null ? void 0 : {
132
182
  x: 0,
133
- y: animatedOffsetY.value
183
+ y: vAnimatedOffsetY
134
184
  }
135
185
  };
136
- return reactNative.Platform.OS === "ios" ? Object.assign(baseProps, {
186
+ return isIos ? Object.assign(baseProps, {
137
187
  contentInset: {
138
- bottom: ((_a = contentInsetProp == null ? void 0 : contentInsetProp.bottom) != null ? _a : 0) + (horizontal ? 0 : keyboardInset.value),
188
+ bottom: ((_a = contentInsetProp == null ? void 0 : contentInsetProp.bottom) != null ? _a : 0) + (horizontal ? 0 : keyboardInset.get()),
139
189
  left: (_b = contentInsetProp == null ? void 0 : contentInsetProp.left) != null ? _b : 0,
140
190
  right: (_c = contentInsetProp == null ? void 0 : contentInsetProp.right) != null ? _c : 0,
141
191
  top: (_d = contentInsetProp == null ? void 0 : contentInsetProp.top) != null ? _d : 0
142
192
  }
143
193
  }) : baseProps;
144
194
  });
145
- const style = reactNative.Platform.OS !== "ios" ? reactNativeReanimated.useAnimatedStyle(() => ({
146
- marginBottom: keyboardInset.value
147
- })) : void 0;
195
+ const style = isAndroid ? reactNativeReanimated.useAnimatedStyle(
196
+ () => {
197
+ var _a;
198
+ return {
199
+ ...styleFlattened || {},
200
+ marginBottom: (_a = keyboardInset.get()) != null ? _a : 0
201
+ };
202
+ },
203
+ [styleProp, keyboardInset]
204
+ ) : void 0;
148
205
  return /* @__PURE__ */ React__namespace.createElement(
149
206
  reanimated.AnimatedLegendList,
150
207
  {
package/keyboard.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { forwardRef, useRef, useCallback } from 'react';
3
- import { Platform } from 'react-native';
3
+ import { StyleSheet, Platform } from 'react-native';
4
4
  import { useKeyboardHandler } from 'react-native-keyboard-controller';
5
5
  import { useAnimatedRef, useSharedValue, useAnimatedScrollHandler, runOnJS, useAnimatedProps, useAnimatedStyle } from 'react-native-reanimated';
6
6
  import { AnimatedLegendList } from '@legendapp/list/reanimated';
@@ -36,20 +36,27 @@ var KeyboardAvoidingLegendList = forwardRef(function KeyboardAvoidingLegendList2
36
36
  horizontal,
37
37
  onScroll: onScrollProp,
38
38
  safeAreaInsetBottom = 0,
39
+ style: styleProp,
39
40
  ...rest
40
41
  } = props;
42
+ const styleFlattened = StyleSheet.flatten(styleProp);
41
43
  const refLegendList = useRef(null);
42
44
  const combinedRef = useCombinedRef(forwardedRef, refLegendList);
45
+ const isIos = Platform.OS === "ios";
46
+ const isAndroid = Platform.OS === "android";
43
47
  const scrollViewRef = useAnimatedRef();
44
48
  const scrollOffsetY = useSharedValue(0);
45
49
  const animatedOffsetY = useSharedValue(null);
46
50
  const scrollOffsetAtKeyboardStart = useSharedValue(0);
51
+ const mode = useSharedValue("idle");
47
52
  const keyboardInset = useSharedValue(0);
48
53
  const keyboardHeight = useSharedValue(0);
49
54
  const isOpening = useSharedValue(false);
55
+ const didInteractive = useSharedValue(false);
56
+ const isKeyboardOpen = useSharedValue(false);
50
57
  const scrollHandler = useAnimatedScrollHandler(
51
58
  (event) => {
52
- scrollOffsetY.value = event.contentOffset[horizontal ? "x" : "y"];
59
+ scrollOffsetY.set(event.contentOffset[horizontal ? "x" : "y"]);
53
60
  if (onScrollProp) {
54
61
  runOnJS(onScrollProp)(event);
55
62
  }
@@ -68,37 +75,79 @@ var KeyboardAvoidingLegendList = forwardRef(function KeyboardAvoidingLegendList2
68
75
  {
69
76
  onStart: (event) => {
70
77
  "worklet";
71
- if (event.height > 0) {
72
- keyboardHeight.set(event.height - safeAreaInsetBottom);
78
+ mode.set("running");
79
+ if (isKeyboardOpen.get() && event.progress === 1 && event.height > 0) {
80
+ return;
81
+ }
82
+ if (!didInteractive.get()) {
83
+ if (event.height > 0) {
84
+ keyboardHeight.set(event.height - safeAreaInsetBottom);
85
+ }
86
+ isOpening.set(event.progress > 0);
87
+ scrollOffsetAtKeyboardStart.set(scrollOffsetY.get());
88
+ animatedOffsetY.set(scrollOffsetY.get());
89
+ runOnJS(setScrollProcessingEnabled)(false);
90
+ }
91
+ },
92
+ onInteractive: (event) => {
93
+ "worklet";
94
+ if (mode.get() !== "running") {
95
+ runOnJS(setScrollProcessingEnabled)(false);
96
+ }
97
+ mode.set("running");
98
+ if (!didInteractive.get()) {
99
+ didInteractive.set(true);
100
+ }
101
+ if (isAndroid && !horizontal) {
102
+ keyboardInset.set(Math.max(0, event.height - safeAreaInsetBottom));
73
103
  }
74
- isOpening.set(event.progress > 0);
75
- scrollOffsetAtKeyboardStart.value = scrollOffsetY.value;
76
- animatedOffsetY.set(scrollOffsetY.value);
77
- runOnJS(setScrollProcessingEnabled)(false);
78
104
  },
79
105
  onMove: (event) => {
80
106
  "worklet";
81
- const vIsOpening = isOpening.get();
82
- const vKeyboardHeight = keyboardHeight.get();
83
- const vProgress = vIsOpening ? event.progress : 1 - event.progress;
84
- const targetOffset = scrollOffsetAtKeyboardStart.value + (vIsOpening ? vKeyboardHeight : -vKeyboardHeight) * vProgress;
85
- scrollOffsetY.value = targetOffset;
86
- animatedOffsetY.set(targetOffset);
87
- if (!horizontal) {
88
- keyboardInset.value = Math.max(0, event.height - safeAreaInsetBottom);
107
+ if (!didInteractive.get()) {
108
+ const vIsOpening = isOpening.get();
109
+ const vKeyboardHeight = keyboardHeight.get();
110
+ const vProgress = vIsOpening ? event.progress : 1 - event.progress;
111
+ const targetOffset = Math.max(
112
+ 0,
113
+ scrollOffsetAtKeyboardStart.get() + (vIsOpening ? vKeyboardHeight : -vKeyboardHeight) * vProgress
114
+ );
115
+ scrollOffsetY.set(targetOffset);
116
+ animatedOffsetY.set(targetOffset);
117
+ if (!horizontal) {
118
+ keyboardInset.set(Math.max(0, event.height - safeAreaInsetBottom));
119
+ }
89
120
  }
90
121
  },
91
122
  onEnd: (event) => {
92
123
  "worklet";
93
- const vIsOpening = isOpening.get();
94
- const vKeyboardHeight = keyboardHeight.get();
95
- const targetOffset = scrollOffsetAtKeyboardStart.value + (vIsOpening ? vKeyboardHeight : -vKeyboardHeight) * (vIsOpening ? event.progress : 1 - event.progress);
96
- scrollOffsetY.value = targetOffset;
97
- animatedOffsetY.set(targetOffset);
98
- if (!horizontal) {
99
- keyboardInset.value = Math.max(0, event.height - safeAreaInsetBottom);
124
+ const wasInteractive = didInteractive.get();
125
+ const vMode = mode.get();
126
+ mode.set("idle");
127
+ if (vMode === "running") {
128
+ if (!wasInteractive) {
129
+ const vIsOpening = isOpening.get();
130
+ const vKeyboardHeight = keyboardHeight.get();
131
+ const targetOffset = Math.max(
132
+ 0,
133
+ scrollOffsetAtKeyboardStart.get() + (vIsOpening ? vKeyboardHeight : -vKeyboardHeight) * (vIsOpening ? event.progress : 1 - event.progress)
134
+ );
135
+ scrollOffsetY.set(targetOffset);
136
+ animatedOffsetY.set(targetOffset);
137
+ }
138
+ runOnJS(setScrollProcessingEnabled)(true);
139
+ didInteractive.set(false);
140
+ isKeyboardOpen.set(event.height > 0);
141
+ if (!horizontal) {
142
+ const newInset = Math.max(0, event.height - safeAreaInsetBottom);
143
+ if (newInset > 0) {
144
+ keyboardInset.set(newInset);
145
+ } else {
146
+ keyboardInset.set(newInset);
147
+ animatedOffsetY.set(scrollOffsetY.get());
148
+ }
149
+ }
100
150
  }
101
- runOnJS(setScrollProcessingEnabled)(true);
102
151
  }
103
152
  },
104
153
  [scrollViewRef, safeAreaInsetBottom]
@@ -106,24 +155,32 @@ var KeyboardAvoidingLegendList = forwardRef(function KeyboardAvoidingLegendList2
106
155
  const animatedProps = useAnimatedProps(() => {
107
156
  "worklet";
108
157
  var _a, _b, _c, _d;
158
+ const vAnimatedOffsetY = animatedOffsetY.get();
109
159
  const baseProps = {
110
- contentOffset: animatedOffsetY.value === null ? void 0 : {
160
+ contentOffset: vAnimatedOffsetY === null ? void 0 : {
111
161
  x: 0,
112
- y: animatedOffsetY.value
162
+ y: vAnimatedOffsetY
113
163
  }
114
164
  };
115
- return Platform.OS === "ios" ? Object.assign(baseProps, {
165
+ return isIos ? Object.assign(baseProps, {
116
166
  contentInset: {
117
- bottom: ((_a = contentInsetProp == null ? void 0 : contentInsetProp.bottom) != null ? _a : 0) + (horizontal ? 0 : keyboardInset.value),
167
+ bottom: ((_a = contentInsetProp == null ? void 0 : contentInsetProp.bottom) != null ? _a : 0) + (horizontal ? 0 : keyboardInset.get()),
118
168
  left: (_b = contentInsetProp == null ? void 0 : contentInsetProp.left) != null ? _b : 0,
119
169
  right: (_c = contentInsetProp == null ? void 0 : contentInsetProp.right) != null ? _c : 0,
120
170
  top: (_d = contentInsetProp == null ? void 0 : contentInsetProp.top) != null ? _d : 0
121
171
  }
122
172
  }) : baseProps;
123
173
  });
124
- const style = Platform.OS !== "ios" ? useAnimatedStyle(() => ({
125
- marginBottom: keyboardInset.value
126
- })) : void 0;
174
+ const style = isAndroid ? useAnimatedStyle(
175
+ () => {
176
+ var _a;
177
+ return {
178
+ ...styleFlattened || {},
179
+ marginBottom: (_a = keyboardInset.get()) != null ? _a : 0
180
+ };
181
+ },
182
+ [styleProp, keyboardInset]
183
+ ) : void 0;
127
184
  return /* @__PURE__ */ React.createElement(
128
185
  AnimatedLegendList,
129
186
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/list",
3
- "version": "2.0.17",
3
+ "version": "2.0.18",
4
4
  "description": "Legend List is a drop-in replacement for FlatList with much better performance and supporting dynamically sized items.",
5
5
  "sideEffects": false,
6
6
  "private": false,