@fountain-ui/core 2.0.0-beta.15 → 2.0.0-beta.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.
Files changed (111) hide show
  1. package/build/commonjs/Tabs/IndexAwareTab.js +13 -7
  2. package/build/commonjs/Tabs/IndexAwareTab.js.map +1 -1
  3. package/build/commonjs/Tabs/InternalContext.js +17 -0
  4. package/build/commonjs/Tabs/InternalContext.js.map +1 -0
  5. package/build/commonjs/Tabs/ScrollableTabsView.js +34 -0
  6. package/build/commonjs/Tabs/ScrollableTabsView.js.map +1 -0
  7. package/build/commonjs/Tabs/TabIndicator.js +46 -19
  8. package/build/commonjs/Tabs/TabIndicator.js.map +1 -1
  9. package/build/commonjs/Tabs/TabIndicatorProps.js.map +1 -1
  10. package/build/commonjs/Tabs/Tabs.js +23 -21
  11. package/build/commonjs/Tabs/Tabs.js.map +1 -1
  12. package/build/commonjs/Tabs/TabsProps.js.map +1 -1
  13. package/build/commonjs/Tabs/index.js.map +1 -1
  14. package/build/commonjs/Tabs/types.js +2 -0
  15. package/build/commonjs/Tabs/types.js.map +1 -0
  16. package/build/commonjs/Tabs/useIndexStore.js +35 -0
  17. package/build/commonjs/Tabs/useIndexStore.js.map +1 -0
  18. package/build/commonjs/Tabs/useScrollViewReaction.js +23 -27
  19. package/build/commonjs/Tabs/useScrollViewReaction.js.map +1 -1
  20. package/build/commonjs/hooks/index.js +8 -0
  21. package/build/commonjs/hooks/index.js.map +1 -1
  22. package/build/commonjs/hooks/useCollapsibleAppBar.js +1 -1
  23. package/build/commonjs/hooks/useCollapsibleAppBar.js.map +1 -1
  24. package/build/commonjs/hooks/useSyncAnimatedValue.js +37 -0
  25. package/build/commonjs/hooks/useSyncAnimatedValue.js.map +1 -0
  26. package/build/commonjs/index.js +14 -0
  27. package/build/commonjs/index.js.map +1 -1
  28. package/build/commonjs/store/MockStore.js +23 -0
  29. package/build/commonjs/store/MockStore.js.map +1 -0
  30. package/build/commonjs/store/SimpleStore.js +45 -0
  31. package/build/commonjs/store/SimpleStore.js.map +1 -0
  32. package/build/commonjs/store/index.js +24 -0
  33. package/build/commonjs/store/index.js.map +1 -0
  34. package/build/commonjs/store/types.js +2 -0
  35. package/build/commonjs/store/types.js.map +1 -0
  36. package/build/commonjs/types/index.js.map +1 -1
  37. package/build/module/Tabs/IndexAwareTab.js +12 -8
  38. package/build/module/Tabs/IndexAwareTab.js.map +1 -1
  39. package/build/module/Tabs/InternalContext.js +7 -0
  40. package/build/module/Tabs/InternalContext.js.map +1 -0
  41. package/build/module/Tabs/ScrollableTabsView.js +21 -0
  42. package/build/module/Tabs/ScrollableTabsView.js.map +1 -0
  43. package/build/module/Tabs/TabIndicator.js +44 -17
  44. package/build/module/Tabs/TabIndicator.js.map +1 -1
  45. package/build/module/Tabs/TabIndicatorProps.js.map +1 -1
  46. package/build/module/Tabs/Tabs.js +21 -21
  47. package/build/module/Tabs/Tabs.js.map +1 -1
  48. package/build/module/Tabs/TabsProps.js.map +1 -1
  49. package/build/module/Tabs/index.js.map +1 -1
  50. package/build/module/Tabs/types.js +2 -0
  51. package/build/module/Tabs/types.js.map +1 -0
  52. package/build/module/Tabs/useIndexStore.js +25 -0
  53. package/build/module/Tabs/useIndexStore.js.map +1 -0
  54. package/build/module/Tabs/useScrollViewReaction.js +23 -28
  55. package/build/module/Tabs/useScrollViewReaction.js.map +1 -1
  56. package/build/module/hooks/index.js +1 -0
  57. package/build/module/hooks/index.js.map +1 -1
  58. package/build/module/hooks/useCollapsibleAppBar.js +1 -1
  59. package/build/module/hooks/useCollapsibleAppBar.js.map +1 -1
  60. package/build/module/hooks/useSyncAnimatedValue.js +27 -0
  61. package/build/module/hooks/useSyncAnimatedValue.js.map +1 -0
  62. package/build/module/index.js +1 -0
  63. package/build/module/index.js.map +1 -1
  64. package/build/module/store/MockStore.js +14 -0
  65. package/build/module/store/MockStore.js.map +1 -0
  66. package/build/module/store/SimpleStore.js +36 -0
  67. package/build/module/store/SimpleStore.js.map +1 -0
  68. package/build/module/store/index.js +3 -0
  69. package/build/module/store/index.js.map +1 -0
  70. package/build/module/store/types.js +2 -0
  71. package/build/module/store/types.js.map +1 -0
  72. package/build/module/types/index.js.map +1 -1
  73. package/build/typescript/Tabs/IndexAwareTab.d.ts +0 -2
  74. package/build/typescript/Tabs/InternalContext.d.ts +7 -0
  75. package/build/typescript/Tabs/ScrollableTabsView.d.ts +6 -0
  76. package/build/typescript/Tabs/TabIndicatorProps.d.ts +4 -5
  77. package/build/typescript/Tabs/Tabs.d.ts +1 -1
  78. package/build/typescript/Tabs/TabsProps.d.ts +3 -16
  79. package/build/typescript/Tabs/index.d.ts +2 -1
  80. package/build/typescript/Tabs/types.d.ts +9 -0
  81. package/build/typescript/Tabs/useIndexStore.d.ts +3 -0
  82. package/build/typescript/Tabs/useScrollViewReaction.d.ts +1 -2
  83. package/build/typescript/hooks/index.d.ts +1 -0
  84. package/build/typescript/hooks/useSyncAnimatedValue.d.ts +6 -0
  85. package/build/typescript/index.d.ts +1 -0
  86. package/build/typescript/store/MockStore.d.ts +6 -0
  87. package/build/typescript/store/SimpleStore.d.ts +9 -0
  88. package/build/typescript/store/index.d.ts +3 -0
  89. package/build/typescript/store/types.d.ts +8 -0
  90. package/build/typescript/types/index.d.ts +6 -0
  91. package/package.json +4 -4
  92. package/src/Tabs/IndexAwareTab.tsx +10 -13
  93. package/src/Tabs/InternalContext.ts +13 -0
  94. package/src/Tabs/ScrollableTabsView.tsx +26 -0
  95. package/src/Tabs/TabIndicator.tsx +37 -21
  96. package/src/Tabs/TabIndicatorProps.ts +4 -5
  97. package/src/Tabs/Tabs.tsx +43 -44
  98. package/src/Tabs/TabsProps.ts +4 -28
  99. package/src/Tabs/index.ts +2 -1
  100. package/src/Tabs/types.ts +18 -0
  101. package/src/Tabs/useIndexStore.ts +31 -0
  102. package/src/Tabs/useScrollViewReaction.ts +15 -26
  103. package/src/hooks/index.ts +1 -0
  104. package/src/hooks/useCollapsibleAppBar.ts +1 -1
  105. package/src/hooks/useSyncAnimatedValue.ts +37 -0
  106. package/src/index.ts +2 -0
  107. package/src/store/MockStore.ts +17 -0
  108. package/src/store/SimpleStore.ts +36 -0
  109. package/src/store/index.ts +3 -0
  110. package/src/store/types.ts +9 -0
  111. package/src/types/index.ts +7 -0
@@ -0,0 +1,6 @@
1
+ import type { ScrollViewProps } from 'react-native';
2
+ import TabCoordinate from './TabCoordinate';
3
+ export interface ScrollableTabsViewProps extends ScrollViewProps {
4
+ coordinates: TabCoordinate[];
5
+ }
6
+ export default function ScrollableTabsView(props: ScrollableTabsViewProps): JSX.Element;
@@ -1,5 +1,4 @@
1
1
  import type { ViewProps } from 'react-native';
2
- import type { SharedValue } from 'react-native-reanimated';
3
2
  import type { OverridableComponentProps } from '../types';
4
3
  import type TabCoordinate from './TabCoordinate';
5
4
  export default interface TabIndicatorProps extends OverridableComponentProps<ViewProps, {
@@ -12,12 +11,12 @@ export default interface TabIndicatorProps extends OverridableComponentProps<Vie
12
11
  */
13
12
  disabled: boolean;
14
13
  /**
15
- * If `true`, the tab will be able to scroll.
14
+ * Initial index.
16
15
  */
17
- scrollable: boolean;
16
+ initialIndex: number;
18
17
  /**
19
- * Shared index value for using animated interpolation.
18
+ * If `true`, the tab will be able to scroll.
20
19
  */
21
- sharedIndex: SharedValue<number>;
20
+ scrollable: boolean;
22
21
  }> {
23
22
  }
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
2
  import type TabsProps from './TabsProps';
3
- import type { TabsInstance } from './TabsProps';
3
+ import type { TabsInstance } from './types';
4
4
  declare const Tabs: React.ForwardRefExoticComponent<Pick<TabsProps, "testID" | "style" | "onLayout" | "keyboardDismissMode" | "children" | "pointerEvents" | "onStartShouldSetResponder" | "onMoveShouldSetResponder" | "onResponderEnd" | "onResponderGrant" | "onResponderReject" | "onResponderMove" | "onResponderRelease" | "onResponderStart" | "onResponderTerminationRequest" | "onResponderTerminate" | "onStartShouldSetResponderCapture" | "onMoveShouldSetResponderCapture" | "hitSlop" | "removeClippedSubviews" | "nativeID" | "collapsable" | "needsOffscreenAlphaCompositing" | "renderToHardwareTextureAndroid" | "focusable" | "shouldRasterizeIOS" | "isTVSelectable" | "hasTVPreferredFocus" | "tvParallaxProperties" | "tvParallaxShiftDistanceX" | "tvParallaxShiftDistanceY" | "tvParallaxTiltAngle" | "tvParallaxMagnification" | "onTouchStart" | "onTouchMove" | "onTouchEnd" | "onTouchCancel" | "onTouchEndCapture" | "accessible" | "accessibilityActions" | "accessibilityLabel" | "accessibilityRole" | "accessibilityState" | "accessibilityHint" | "accessibilityValue" | "onAccessibilityAction" | "accessibilityLiveRegion" | "importantForAccessibility" | "accessibilityElementsHidden" | "accessibilityViewIsModal" | "onAccessibilityEscape" | "onAccessibilityTap" | "onMagicTap" | "accessibilityIgnoresInvertColors" | "variant" | "keyboardShouldPersistTaps" | "onChange" | "disableIndicator" | "initialIndex" | "scrollable" | "UNSTABLE_sharedIndex"> & React.RefAttributes<TabsInstance>>;
5
5
  export default Tabs;
@@ -1,21 +1,8 @@
1
1
  import type { ReactNode, Ref } from 'react';
2
2
  import type { ViewProps } from 'react-native';
3
- import type { SharedValue } from 'react-native-reanimated';
4
3
  import type { TabVariant } from '../Tab';
5
- import type { OverridableComponentProps } from '../types';
6
- export declare type KeyboardDismissMode = 'none' | 'on-drag' | 'interactive';
7
- export declare type KeyboardShouldPersistTaps = 'never' | boolean | 'always' | 'handled';
8
- export interface TabsInstance {
9
- /**
10
- * Get current tab index.
11
- */
12
- getCurrentIndex: () => number;
13
- /**
14
- * Function to scroll to a specific tab. Invalid index is ignored.
15
- * @param index
16
- */
17
- setTab: (index: number) => void;
18
- }
4
+ import type { OverridableComponentProps, SyncAnimatedValue } from '../types';
5
+ import type { KeyboardDismissMode, KeyboardShouldPersistTaps, TabsInstance } from './types';
19
6
  export default interface TabsProps extends OverridableComponentProps<ViewProps, {
20
7
  ref?: Ref<TabsInstance>;
21
8
  /**
@@ -54,7 +41,7 @@ export default interface TabsProps extends OverridableComponentProps<ViewProps,
54
41
  /**
55
42
  * Unstable API.
56
43
  */
57
- UNSTABLE_sharedIndex?: SharedValue<number>;
44
+ UNSTABLE_sharedIndex?: SyncAnimatedValue;
58
45
  /**
59
46
  * The variant to use.
60
47
  * @default 'primary'
@@ -1,2 +1,3 @@
1
1
  export { default } from './Tabs';
2
- export type { default as TabsProps, TabsInstance } from './TabsProps';
2
+ export type { default as TabsProps } from './TabsProps';
3
+ export type { TabsInstance } from './types';
@@ -0,0 +1,9 @@
1
+ export declare type KeyboardDismissMode = 'none' | 'on-drag' | 'interactive';
2
+ export declare type KeyboardShouldPersistTaps = 'never' | boolean | 'always' | 'handled';
3
+ export interface TabsInstance {
4
+ /**
5
+ * Function to scroll to a specific tab. Invalid index is ignored.
6
+ * @param index
7
+ */
8
+ setTab: (index: number) => void;
9
+ }
@@ -0,0 +1,3 @@
1
+ import type { MonoStore } from '../store';
2
+ import type { SyncAnimatedValue } from '../types';
3
+ export default function useIndexStore(value: SyncAnimatedValue): MonoStore<number>;
@@ -1,9 +1,8 @@
1
1
  import type { MutableRefObject } from 'react';
2
2
  import type { ScrollView, ViewProps } from 'react-native';
3
- import { SharedValue } from 'react-native-reanimated';
4
3
  import type TabCoordinate from './TabCoordinate';
5
4
  export interface UseScrollViewReaction {
6
5
  scrollViewRef: MutableRefObject<ScrollView | null>;
7
6
  onLayout: ViewProps['onLayout'];
8
7
  }
9
- export default function useScrollViewReaction(sharedIndex: SharedValue<number>, coordinates: TabCoordinate[]): UseScrollViewReaction;
8
+ export default function useScrollViewReaction(coordinates: TabCoordinate[]): UseScrollViewReaction;
@@ -3,5 +3,6 @@ export { default as useCollapsibleAppBar } from './useCollapsibleAppBar';
3
3
  export { default as useContentContainerStyle } from './useContentContainerStyle';
4
4
  export { default as useElevationStyle } from './useElevationStyle';
5
5
  export { default as useFadeInAppBar } from './useFadeInAppBar';
6
+ export { default as useSyncAnimatedValue } from './useSyncAnimatedValue';
6
7
  export { default as useThrottle } from './useThrottle';
7
8
  export { default as useValidWindowDimensions } from './useValidWindowDimensions';
@@ -0,0 +1,6 @@
1
+ import type { SyncAnimatedValue } from '../types';
2
+ export interface SyncAnimatedValueConfig {
3
+ initialValue: number;
4
+ shouldSyncAlways?: boolean;
5
+ }
6
+ export default function useSyncAnimatedValue(config: SyncAnimatedValueConfig): SyncAnimatedValue;
@@ -2,6 +2,7 @@ export * from './styles';
2
2
  export * from './types';
3
3
  export * from './hooks';
4
4
  export * from './utils';
5
+ export * from './store';
5
6
  export { default as Accordion } from './Accordion';
6
7
  export * from './Accordion';
7
8
  export { default as AppBar } from './AppBar';
@@ -0,0 +1,6 @@
1
+ import { MonoStore, StoreSubscription } from './types';
2
+ export default class MockStore<T> implements MonoStore<T> {
3
+ dispatch(data: T): void;
4
+ removeAllListeners(): void;
5
+ subscribe(listener: (data: T) => void): StoreSubscription;
6
+ }
@@ -0,0 +1,9 @@
1
+ import { MonoStore, StoreSubscription } from './types';
2
+ export default class SimpleStore<T> implements MonoStore<T> {
3
+ private data;
4
+ private listeners;
5
+ constructor(initialData: T);
6
+ subscribe(listener: (data: T) => void): StoreSubscription;
7
+ dispatch(data: T): void;
8
+ removeAllListeners(): void;
9
+ }
@@ -0,0 +1,3 @@
1
+ export { default as SimpleStore } from './SimpleStore';
2
+ export { default as MockStore } from './MockStore';
3
+ export type { MonoStore } from './types';
@@ -0,0 +1,8 @@
1
+ export interface StoreSubscription {
2
+ (): void;
3
+ }
4
+ export interface MonoStore<T> {
5
+ dispatch: (data: T) => void;
6
+ subscribe: (listener: (data: T) => void) => StoreSubscription;
7
+ removeAllListeners: () => void;
8
+ }
@@ -1,3 +1,4 @@
1
+ import type { Animated } from 'react-native';
1
2
  import type { FountainUiStyle } from '@fountain-ui/styles';
2
3
  export declare type ExtendedStyle<StyleType extends FountainUiStyle = FountainUiStyle> = StyleType;
3
4
  export declare type ComponentProps<P = {}> = BaseProps & Readonly<P>;
@@ -8,3 +9,8 @@ export interface BaseProps extends Readonly<{
8
9
  }
9
10
  export type { Theme } from '@fountain-ui/styles';
10
11
  export declare type CommonComponentColor = 'primary' | 'secondary' | 'tertiary' | 'warning' | 'accent';
12
+ export interface SyncAnimatedValue {
13
+ animatedValue: Animated.Value;
14
+ initialValue: number;
15
+ getCurrentValue: () => number;
16
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fountain-ui/core",
3
- "version": "2.0.0-beta.15",
3
+ "version": "2.0.0-beta.18",
4
4
  "author": "Fountain-UI Team",
5
5
  "description": "React components that implement Tappytoon's Fountain Design.",
6
6
  "license": "MIT",
@@ -14,8 +14,8 @@
14
14
  "prepare": "bob build"
15
15
  },
16
16
  "dependencies": {
17
- "@fountain-ui/styles": "^2.0.0-beta.7",
18
- "@fountain-ui/utils": "^2.0.0-beta.3"
17
+ "@fountain-ui/styles": "^2.0.0-beta.8",
18
+ "@fountain-ui/utils": "^2.0.0-beta.4"
19
19
  },
20
20
  "peerDependencies": {
21
21
  "@react-native-community/slider": "^4.1.6",
@@ -67,5 +67,5 @@
67
67
  "publishConfig": {
68
68
  "access": "public"
69
69
  },
70
- "gitHead": "efb9e6cc7cc65e52d1084a0c3664f419dc171240"
70
+ "gitHead": "9c4af6d69493666d86b7736892f778af775a3c80"
71
71
  }
@@ -1,30 +1,27 @@
1
1
  import type { ReactElement } from 'react';
2
- import React, { cloneElement, useState } from 'react';
3
- import type { SharedValue } from 'react-native-reanimated';
4
- import { runOnJS, useAnimatedReaction } from 'react-native-reanimated';
2
+ import React, { cloneElement, useContext, useEffect, useState } from 'react';
3
+ import InternalContext from './InternalContext';
5
4
 
6
5
  export interface IndexAwareTabProps {
7
6
  children: ReactElement;
8
7
  index: number;
9
- sharedIndex: SharedValue<number>;
10
8
  }
11
9
 
12
10
  export default function IndexAwareTab(props: IndexAwareTabProps) {
13
11
  const {
14
12
  children,
15
13
  index,
16
- sharedIndex,
17
14
  } = props;
18
15
 
19
- const [selected, setSelected] = useState(index === sharedIndex.value);
16
+ const [selected, setSelected] = useState(false);
20
17
 
21
- useAnimatedReaction(
22
- () => index === sharedIndex.value,
23
- (result) => {
24
- runOnJS(setSelected)(result);
25
- },
26
- [index],
27
- );
18
+ const { indexStore } = useContext(InternalContext);
19
+
20
+ useEffect(() => {
21
+ return indexStore.subscribe((currentIndex) => {
22
+ setSelected(currentIndex === index);
23
+ });
24
+ }, [indexStore, index]);
28
25
 
29
26
  return cloneElement(children, { selected });
30
27
  };
@@ -0,0 +1,13 @@
1
+ import { createContext } from 'react';
2
+ import type { MonoStore } from '../store';
3
+ import { MockStore } from '../store';
4
+
5
+ export interface InternalContextValue {
6
+ indexStore: MonoStore<number>;
7
+ }
8
+
9
+ const InternalContext = createContext<InternalContextValue>({
10
+ indexStore: new MockStore(),
11
+ });
12
+
13
+ export default InternalContext;
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+ import type { ScrollViewProps } from 'react-native';
3
+ import { ScrollView } from 'react-native';
4
+ import TabCoordinate from './TabCoordinate';
5
+ import useScrollViewReaction from './useScrollViewReaction';
6
+
7
+ export interface ScrollableTabsViewProps extends ScrollViewProps {
8
+ coordinates: TabCoordinate[];
9
+ }
10
+
11
+ export default function ScrollableTabsView(props: ScrollableTabsViewProps) {
12
+ const {
13
+ coordinates,
14
+ ...scrollViewProps
15
+ } = props;
16
+
17
+ const { scrollViewRef, onLayout } = useScrollViewReaction(coordinates);
18
+
19
+ return (
20
+ <ScrollView
21
+ ref={scrollViewRef}
22
+ onLayout={onLayout}
23
+ {...scrollViewProps}
24
+ />
25
+ );
26
+ };
@@ -1,10 +1,10 @@
1
- import React from 'react';
2
- import type { WithTimingConfig } from 'react-native-reanimated';
3
- import Animated, { Easing, useAnimatedStyle, withTiming } from 'react-native-reanimated';
1
+ import React, { useCallback, useContext, useEffect, useRef } from 'react';
2
+ import { Animated, Easing } from 'react-native';
4
3
  import { NamedStylesStringUnion, UseStyles } from '@fountain-ui/styles';
5
4
  import { useTheme } from '../styles';
6
5
  import type TabIndicatorProps from './TabIndicatorProps';
7
6
  import { defaultCoordinate } from './TabCoordinate';
7
+ import InternalContext from './InternalContext';
8
8
 
9
9
  type TabIndicatorStyles = NamedStylesStringUnion<'root' | 'disabled'>;
10
10
 
@@ -31,49 +31,65 @@ const useStyles: UseStyles<TabIndicatorStyles> = function (): TabIndicatorStyles
31
31
  };
32
32
  };
33
33
 
34
- const ANIMATION_CONFIG: Readonly<WithTimingConfig> = {
35
- duration: 300,
36
- easing: Easing.out(Easing.exp),
34
+ const startAnimation = (value: Animated.Value, toValue: number) => {
35
+ Animated.timing(value, {
36
+ toValue: toValue,
37
+ duration: 300,
38
+ easing: Easing.bezier(0.25, 1, 0.5, 1),
39
+ useNativeDriver: true,
40
+ }).start();
37
41
  };
38
42
 
39
43
  export default function TabIndicator(props: TabIndicatorProps) {
40
44
  const {
41
45
  coordinates,
42
46
  disabled,
47
+ initialIndex,
43
48
  scrollable,
44
- sharedIndex,
45
49
  style,
46
50
  ...otherProps
47
51
  } = props;
48
52
 
49
53
  const styles = useStyles();
50
54
 
51
- const animatedStyle = useAnimatedStyle(() => {
52
- const index = sharedIndex.value;
53
-
54
- const { x1, x2 } = coordinates[index] ?? defaultCoordinate;
55
+ const computeAnimationValues = useCallback((currentIndex: number) => {
56
+ const { x1, x2 } = coordinates[currentIndex] ?? defaultCoordinate;
55
57
 
56
58
  const tabWidth = x2 - x1;
57
59
 
58
- const translateX = x1 + (tabWidth - INDICATOR_WIDTH) / 2;
60
+ const nextTranslateX = x1 + (tabWidth - INDICATOR_WIDTH) / 2;
59
61
 
60
62
  const inset = scrollable ? SCROLLABLE_TABS_INSET : 0;
61
- const scaleX = (tabWidth - inset) / INDICATOR_WIDTH;
62
-
63
- return {
64
- transform: [
65
- { translateX: withTiming(translateX, ANIMATION_CONFIG) },
66
- { scaleX: withTiming(scaleX, ANIMATION_CONFIG) },
67
- ],
68
- };
63
+ const nextScaleX = (tabWidth - inset) / INDICATOR_WIDTH;
64
+
65
+ return { tx: nextTranslateX, sx: nextScaleX };
69
66
  }, [coordinates, scrollable]);
70
67
 
68
+ const {
69
+ tx: initialTx,
70
+ sx: initialSx,
71
+ } = computeAnimationValues(initialIndex);
72
+
73
+ const translateX = useRef(new Animated.Value(initialTx)).current;
74
+ const scaleX = useRef(new Animated.Value(initialSx)).current;
75
+
76
+ const { indexStore } = useContext(InternalContext);
77
+
78
+ useEffect(() => {
79
+ return indexStore.subscribe(currentIndex => {
80
+ const { tx, sx } = computeAnimationValues(currentIndex);
81
+
82
+ startAnimation(translateX, tx);
83
+ startAnimation(scaleX, sx);
84
+ });
85
+ }, [indexStore, computeAnimationValues]);
86
+
71
87
  return (
72
88
  <Animated.View
73
89
  style={[
74
90
  styles.root,
75
91
  disabled ? styles.disabled : undefined,
76
- animatedStyle,
92
+ { transform: [{ translateX }, { scaleX }] },
77
93
  style,
78
94
  ]}
79
95
  {...otherProps}
@@ -1,5 +1,4 @@
1
1
  import type { ViewProps } from 'react-native';
2
- import type { SharedValue } from 'react-native-reanimated';
3
2
  import type { OverridableComponentProps } from '../types';
4
3
  import type TabCoordinate from './TabCoordinate';
5
4
 
@@ -15,12 +14,12 @@ export default interface TabIndicatorProps extends OverridableComponentProps<Vie
15
14
  disabled: boolean;
16
15
 
17
16
  /**
18
- * If `true`, the tab will be able to scroll.
17
+ * Initial index.
19
18
  */
20
- scrollable: boolean;
19
+ initialIndex: number;
21
20
 
22
21
  /**
23
- * Shared index value for using animated interpolation.
22
+ * If `true`, the tab will be able to scroll.
24
23
  */
25
- sharedIndex: SharedValue<number>;
24
+ scrollable: boolean;
26
25
  }> {}
package/src/Tabs/Tabs.tsx CHANGED
@@ -1,15 +1,17 @@
1
1
  import React, { cloneElement, forwardRef, useImperativeHandle } from 'react';
2
2
  import type { GestureResponderEvent, LayoutChangeEvent } from 'react-native';
3
- import { ScrollView, View } from 'react-native';
4
- import { useSharedValue } from 'react-native-reanimated';
3
+ import { View } from 'react-native';
5
4
  import { NamedStylesStringUnion, UseStyles } from '@fountain-ui/styles';
6
5
  import { css, useTheme } from '../styles';
6
+ import { useSyncAnimatedValue } from '../hooks';
7
7
  import type TabsProps from './TabsProps';
8
- import type { TabsInstance } from './TabsProps';
8
+ import type { TabsInstance } from './types';
9
9
  import TabIndicator from './TabIndicator';
10
+ import ScrollableTabsView from './ScrollableTabsView';
10
11
  import IndexAwareTab from './IndexAwareTab';
11
12
  import useTabCoordinates from './useTabCoordinates';
12
- import useScrollViewReaction from './useScrollViewReaction';
13
+ import useIndexStore from './useIndexStore';
14
+ import InternalContext from './InternalContext';
13
15
 
14
16
  type TabsStyleKeys =
15
17
  | 'root'
@@ -50,20 +52,17 @@ const Tabs = forwardRef<TabsInstance, TabsProps>(function Tabs(props, ref) {
50
52
  UNSTABLE_sharedIndex,
51
53
  } = props;
52
54
 
53
- const fallbackSharedIndex = useSharedValue<number>(initialIndex);
55
+ const fallbackSharedIndex = useSyncAnimatedValue({ initialValue: initialIndex });
54
56
 
55
57
  const sharedIndex = UNSTABLE_sharedIndex ?? fallbackSharedIndex;
56
58
 
57
- const getCurrentIndex = (): number => sharedIndex.value;
58
-
59
59
  const setTab = (newIndex: number) => {
60
- sharedIndex.value = newIndex;
60
+ sharedIndex.animatedValue.setValue(newIndex);
61
61
  };
62
62
 
63
63
  useImperativeHandle(
64
64
  ref,
65
65
  () => ({
66
- getCurrentIndex,
67
66
  setTab,
68
67
  }),
69
68
  [sharedIndex],
@@ -73,7 +72,7 @@ const Tabs = forwardRef<TabsInstance, TabsProps>(function Tabs(props, ref) {
73
72
 
74
73
  const { coordinates, updateCoordinate } = useTabCoordinates(children);
75
74
 
76
- const { scrollViewRef, onLayout } = useScrollViewReaction(sharedIndex, coordinates);
75
+ const indexStore = useIndexStore(sharedIndex);
77
76
 
78
77
  const isReadyToRenderIndicator = coordinates.length > 0;
79
78
 
@@ -112,7 +111,6 @@ const Tabs = forwardRef<TabsInstance, TabsProps>(function Tabs(props, ref) {
112
111
  <IndexAwareTab
113
112
  children={tabElement}
114
113
  index={index}
115
- sharedIndex={sharedIndex}
116
114
  />
117
115
  );
118
116
  });
@@ -121,44 +119,45 @@ const Tabs = forwardRef<TabsInstance, TabsProps>(function Tabs(props, ref) {
121
119
  <TabIndicator
122
120
  coordinates={coordinates}
123
121
  disabled={disableIndicator}
122
+ initialIndex={sharedIndex.initialValue}
124
123
  scrollable={scrollable}
125
- sharedIndex={sharedIndex}
126
124
  />
127
125
  ) : null;
128
126
 
129
127
  return (
130
- <View
131
- onLayout={onLayout}
132
- style={css([
133
- styles.root,
134
- scrollable ? undefined : styles.fixedRoot,
135
- style,
136
- ])}
137
- >
138
- {scrollable ? (
139
- <ScrollView
140
- automaticallyAdjustContentInsets={false}
141
- bounces={false}
142
- contentContainerStyle={styles.scrollableContainer}
143
- directionalLockEnabled={true}
144
- horizontal={true}
145
- ref={scrollViewRef}
146
- scrollsToTop={false}
147
- showsHorizontalScrollIndicator={false}
148
- showsVerticalScrollIndicator={false}
149
- keyboardDismissMode={keyboardDismissMode}
150
- keyboardShouldPersistTaps={keyboardShouldPersistTaps}
151
- >
152
- {tabElements}
153
- {tabIndicator}
154
- </ScrollView>
155
- ) : (
156
- <React.Fragment>
157
- {tabElements}
158
- {tabIndicator}
159
- </React.Fragment>
160
- )}
161
- </View>
128
+ <InternalContext.Provider value={{ indexStore }}>
129
+ <View
130
+ style={css([
131
+ styles.root,
132
+ scrollable ? undefined : styles.fixedRoot,
133
+ style,
134
+ ])}
135
+ >
136
+ {scrollable ? (
137
+ <ScrollableTabsView
138
+ automaticallyAdjustContentInsets={false}
139
+ bounces={false}
140
+ contentContainerStyle={styles.scrollableContainer}
141
+ coordinates={coordinates}
142
+ directionalLockEnabled={true}
143
+ horizontal={true}
144
+ scrollsToTop={false}
145
+ showsHorizontalScrollIndicator={false}
146
+ showsVerticalScrollIndicator={false}
147
+ keyboardDismissMode={keyboardDismissMode}
148
+ keyboardShouldPersistTaps={keyboardShouldPersistTaps}
149
+ >
150
+ {tabElements}
151
+ {tabIndicator}
152
+ </ScrollableTabsView>
153
+ ) : (
154
+ <React.Fragment>
155
+ {tabElements}
156
+ {tabIndicator}
157
+ </React.Fragment>
158
+ )}
159
+ </View>
160
+ </InternalContext.Provider>
162
161
  );
163
162
  });
164
163
 
@@ -1,32 +1,8 @@
1
1
  import type { ReactNode, Ref } from 'react';
2
- import type { ViewProps } from 'react-native';
3
- import type { SharedValue } from 'react-native-reanimated';
2
+ import type { Animated, ViewProps } from 'react-native';
4
3
  import type { TabVariant } from '../Tab';
5
- import type { OverridableComponentProps } from '../types';
6
-
7
- export type KeyboardDismissMode =
8
- 'none'
9
- | 'on-drag'
10
- | 'interactive'; // ios only
11
-
12
- export type KeyboardShouldPersistTaps =
13
- 'never'
14
- | boolean
15
- | 'always'
16
- | 'handled'; // app only
17
-
18
- export interface TabsInstance {
19
- /**
20
- * Get current tab index.
21
- */
22
- getCurrentIndex: () => number;
23
-
24
- /**
25
- * Function to scroll to a specific tab. Invalid index is ignored.
26
- * @param index
27
- */
28
- setTab: (index: number) => void;
29
- }
4
+ import type { OverridableComponentProps, SyncAnimatedValue } from '../types';
5
+ import type { KeyboardDismissMode, KeyboardShouldPersistTaps, TabsInstance } from './types';
30
6
 
31
7
  export default interface TabsProps extends OverridableComponentProps<ViewProps, {
32
8
  ref?: Ref<TabsInstance>;
@@ -74,7 +50,7 @@ export default interface TabsProps extends OverridableComponentProps<ViewProps,
74
50
  /**
75
51
  * Unstable API.
76
52
  */
77
- UNSTABLE_sharedIndex?: SharedValue<number>;
53
+ UNSTABLE_sharedIndex?: SyncAnimatedValue;
78
54
 
79
55
  /**
80
56
  * The variant to use.
package/src/Tabs/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { default } from './Tabs';
2
- export type { default as TabsProps, TabsInstance } from './TabsProps';
2
+ export type { default as TabsProps } from './TabsProps';
3
+ export type { TabsInstance } from './types';
@@ -0,0 +1,18 @@
1
+ export type KeyboardDismissMode =
2
+ 'none'
3
+ | 'on-drag'
4
+ | 'interactive'; // ios only
5
+
6
+ export type KeyboardShouldPersistTaps =
7
+ 'never'
8
+ | boolean
9
+ | 'always'
10
+ | 'handled'; // app only
11
+
12
+ export interface TabsInstance {
13
+ /**
14
+ * Function to scroll to a specific tab. Invalid index is ignored.
15
+ * @param index
16
+ */
17
+ setTab: (index: number) => void;
18
+ }