@momo-kits/foundation 0.165.1-beta.1 → 0.165.1-beta.3

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.
@@ -24,7 +24,12 @@ import { Icon } from '../../Icon';
24
24
  import { exportFontFamily, Text } from '../../Text';
25
25
  import StackScreen from '../StackScreen';
26
26
  import { BottomTabProps } from '../types';
27
- import { getOptions, getStackOptions, useAppState } from '../utils';
27
+ import {
28
+ getOptions,
29
+ getStackOptions,
30
+ useAppState,
31
+ useFloatingArrowBack,
32
+ } from '../utils';
28
33
  import BottomTabBar from './BottomTabBar';
29
34
  import { ApplicationContext, MiniAppContext } from '../../Context';
30
35
 
@@ -153,7 +158,7 @@ const TabScreen: React.FC<any> = ({ route, navigation }) => {
153
158
  initialParams,
154
159
  bottomTab: 'nested',
155
160
  }}
156
- options={{ ...getStackOptions({ swipeBack: false }) }}
161
+ options={{ ...getStackOptions() }}
157
162
  />
158
163
  </Stack.Navigator>
159
164
  </Animated.View>
@@ -188,7 +193,7 @@ const TabScreen: React.FC<any> = ({ route, navigation }) => {
188
193
  initialParams,
189
194
  bottomTab: 'default',
190
195
  }}
191
- options={{ ...getStackOptions({ swipeBack: false }), ...options }}
196
+ options={{ ...getStackOptions(), ...options }}
192
197
  />
193
198
  </Stack.Navigator>
194
199
  </NavigationContainer>
@@ -212,6 +217,7 @@ const BottomTab: React.FC<BottomTabProps> = ({
212
217
  const indicatorAnimated = useSharedValue(0);
213
218
  const activeIndex = React.useRef(initialIndex > 0 ? initialIndex : 0);
214
219
  const itemWidth = dimensions.width / tabs.length;
220
+ const reportFloatingArrowBack = useFloatingArrowBack();
215
221
 
216
222
  const indicatorStyle = useAnimatedStyle(() => ({
217
223
  width: itemWidth,
@@ -232,11 +238,41 @@ const BottomTab: React.FC<BottomTabProps> = ({
232
238
  });
233
239
  }, [itemWidth, indicatorAnimated]);
234
240
 
241
+ // Tied to focus, not mount: a screen pushed from a tab leaves BottomTab mounted but blurred,
242
+ // and the flag has to be off there. Also covers `initialRouteName` starting on a later tab.
243
+ useFocusEffect(
244
+ React.useCallback(() => {
245
+ reportFloatingArrowBack(activeIndex.current !== 0);
246
+ return () => reportFloatingArrowBack(false);
247
+ }, [reportFloatingArrowBack]),
248
+ );
249
+
250
+ /**
251
+ * Two halves of one decision, so they are set together.
252
+ *
253
+ * On the first tab the stack card owns the swipe and runs the usual transition. On any other
254
+ * tab it must not: every tab lives inside that single card, and the gesture pops it with
255
+ * `StackActions.pop()` aimed at the stack rather than a back the tab router could claim — so
256
+ * the swipe would take all the tabs with it instead of returning to the first one. Turning the
257
+ * card gesture off there would leave the swipe doing nothing, so hand it to the host's
258
+ * floating-arrow interaction instead.
259
+ */
260
+ const syncBackOwnership = (index: number) => {
261
+ const isFirstTab = index === 0;
262
+
263
+ navigation?.setOptions({
264
+ gestureEnabled: Platform.OS === 'ios' && isFirstTab,
265
+ });
266
+ reportFloatingArrowBack(!isFirstTab);
267
+ };
268
+
235
269
  const onFocus = (e: any) => {
236
270
  activeIndex.current = tabs.findIndex(i => e.target.includes(i.name));
237
271
  indicatorAnimated.value = withTiming(itemWidth * activeIndex.current, {
238
272
  duration: 200,
239
273
  });
274
+
275
+ syncBackOwnership(activeIndex.current);
240
276
  };
241
277
 
242
278
  const handler: {
@@ -275,9 +311,7 @@ const BottomTab: React.FC<BottomTabProps> = ({
275
311
  },
276
312
  ]}
277
313
  >
278
- <Animated.View
279
- style={[styles.indicatorContainer, indicatorStyle]}
280
- >
314
+ <Animated.View style={[styles.indicatorContainer, indicatorStyle]}>
281
315
  <View
282
316
  style={[
283
317
  styles.indicator,
@@ -296,7 +330,11 @@ const BottomTab: React.FC<BottomTabProps> = ({
296
330
  activeTintColor={theme.colors.primary}
297
331
  style={[
298
332
  {
299
- height: 64 + (Platform.OS === 'ios' ? Math.min(insets.bottom, 21) : insets.bottom),
333
+ height:
334
+ 64 +
335
+ (Platform.OS === 'ios'
336
+ ? Math.min(insets.bottom, 21)
337
+ : insets.bottom),
300
338
  },
301
339
  ]}
302
340
  />
@@ -59,10 +59,8 @@ const forStackTransitionWithOverlay = (
59
59
  /**
60
60
  * default options for stack screen
61
61
  */
62
- const getStackOptions = ({
63
- swipeBack = true,
64
- }: { swipeBack?: boolean } = {}): StackNavigationOptions => {
65
- const base: StackNavigationOptions = {
62
+ const getStackOptions = (): StackNavigationOptions => {
63
+ return {
66
64
  headerTitleAlign: 'left',
67
65
  headerTitle: props => <HeaderTitle {...props} />,
68
66
  headerTitleContainerStyle: {
@@ -78,25 +76,6 @@ const getStackOptions = ({
78
76
  headerLeft: (props: any) => <HeaderLeft {...props} />,
79
77
  headerRight: (props: any) => <HeaderRight {...props} />,
80
78
  headerTintColor: Colors.black_17,
81
- };
82
-
83
- /**
84
- * A bottom tab stack holds a single route, so the card transition never runs and
85
- * `CardContainer` pins `gestureEnabled` to false at index 0 regardless. What does bite is
86
- * `cardOverlayEnabled`: `current.progress` rests at 1, so the overlay would sit at its full
87
- * 0.6 forever instead of only during a push, tinting any tab screen that paints no
88
- * background of its own. This branch is the option set from before swipe back, unchanged.
89
- */
90
- if (!swipeBack) {
91
- return {
92
- ...base,
93
- gestureEnabled: false,
94
- ...TransitionPresets.SlideFromRightIOS,
95
- };
96
- }
97
-
98
- return {
99
- ...base,
100
79
  gestureEnabled: Platform.OS === 'ios',
101
80
  ...TransitionPresets.SlideFromRightIOS,
102
81
  cardStyleInterpolator: forStackTransitionWithOverlay,
@@ -355,6 +334,45 @@ const useNativeCanGoBack = () => {
355
334
  );
356
335
  };
357
336
 
337
+ /**
338
+ * Hands the swipe back to the host's floating-arrow interaction while a bottom tab other than
339
+ * the first one is showing.
340
+ *
341
+ * The whole tab navigator lives inside a single stack card, and the card gesture pops that card
342
+ * through `StackActions.pop()` aimed at the stack — it never dispatches a back the tab router
343
+ * could claim. So on tab 2 a swipe would take all four tabs with it instead of returning to tab
344
+ * 1. The host already owns an interaction for exactly this, so report the state and let it run.
345
+ *
346
+ * Reports `false` on unmount: the flag lives on the host and would otherwise outlive the tabs
347
+ * that asked for it.
348
+ */
349
+ const useFloatingArrowBack = () => {
350
+ const lastValue = useRef<boolean | undefined>(undefined);
351
+
352
+ const report = useCallback((shouldUse: boolean) => {
353
+ if (lastValue.current === shouldUse) {
354
+ return;
355
+ }
356
+
357
+ const setShouldUseFloatingArrowBack =
358
+ NativeModules?.MiniAppModule?.setShouldUseFloatingArrowBack;
359
+ if (typeof setShouldUseFloatingArrowBack !== 'function') {
360
+ return;
361
+ }
362
+
363
+ lastValue.current = shouldUse;
364
+ try {
365
+ setShouldUseFloatingArrowBack(shouldUse);
366
+ } catch (error) {
367
+ console.warn('[BottomTab] setShouldUseFloatingArrowBack failed:', error);
368
+ }
369
+ }, []);
370
+
371
+ useEffect(() => () => report(false), [report]);
372
+
373
+ return report;
374
+ };
375
+
358
376
  /**
359
377
  * Union of the two animation runtimes an internal foundation component might
360
378
  * receive. Public Screen props still accept React Native Animated.Value only;
@@ -506,6 +524,7 @@ export {
506
524
  useComponentId,
507
525
  useAppState,
508
526
  useNativeCanGoBack,
527
+ useFloatingArrowBack,
509
528
  useNormalizedSharedValue,
510
529
  SwipeBackHaptic,
511
530
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.165.1-beta.1",
3
+ "version": "0.165.1-beta.3",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {},