@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.
- package/build/commonjs/Tabs/IndexAwareTab.js +13 -7
- package/build/commonjs/Tabs/IndexAwareTab.js.map +1 -1
- package/build/commonjs/Tabs/InternalContext.js +17 -0
- package/build/commonjs/Tabs/InternalContext.js.map +1 -0
- package/build/commonjs/Tabs/ScrollableTabsView.js +34 -0
- package/build/commonjs/Tabs/ScrollableTabsView.js.map +1 -0
- package/build/commonjs/Tabs/TabIndicator.js +46 -19
- package/build/commonjs/Tabs/TabIndicator.js.map +1 -1
- package/build/commonjs/Tabs/TabIndicatorProps.js.map +1 -1
- package/build/commonjs/Tabs/Tabs.js +23 -21
- package/build/commonjs/Tabs/Tabs.js.map +1 -1
- package/build/commonjs/Tabs/TabsProps.js.map +1 -1
- package/build/commonjs/Tabs/index.js.map +1 -1
- package/build/commonjs/Tabs/types.js +2 -0
- package/build/commonjs/Tabs/types.js.map +1 -0
- package/build/commonjs/Tabs/useIndexStore.js +35 -0
- package/build/commonjs/Tabs/useIndexStore.js.map +1 -0
- package/build/commonjs/Tabs/useScrollViewReaction.js +23 -27
- package/build/commonjs/Tabs/useScrollViewReaction.js.map +1 -1
- package/build/commonjs/hooks/index.js +8 -0
- package/build/commonjs/hooks/index.js.map +1 -1
- package/build/commonjs/hooks/useCollapsibleAppBar.js +1 -1
- package/build/commonjs/hooks/useCollapsibleAppBar.js.map +1 -1
- package/build/commonjs/hooks/useSyncAnimatedValue.js +37 -0
- package/build/commonjs/hooks/useSyncAnimatedValue.js.map +1 -0
- package/build/commonjs/index.js +14 -0
- package/build/commonjs/index.js.map +1 -1
- package/build/commonjs/store/MockStore.js +23 -0
- package/build/commonjs/store/MockStore.js.map +1 -0
- package/build/commonjs/store/SimpleStore.js +45 -0
- package/build/commonjs/store/SimpleStore.js.map +1 -0
- package/build/commonjs/store/index.js +24 -0
- package/build/commonjs/store/index.js.map +1 -0
- package/build/commonjs/store/types.js +2 -0
- package/build/commonjs/store/types.js.map +1 -0
- package/build/commonjs/types/index.js.map +1 -1
- package/build/module/Tabs/IndexAwareTab.js +12 -8
- package/build/module/Tabs/IndexAwareTab.js.map +1 -1
- package/build/module/Tabs/InternalContext.js +7 -0
- package/build/module/Tabs/InternalContext.js.map +1 -0
- package/build/module/Tabs/ScrollableTabsView.js +21 -0
- package/build/module/Tabs/ScrollableTabsView.js.map +1 -0
- package/build/module/Tabs/TabIndicator.js +44 -17
- package/build/module/Tabs/TabIndicator.js.map +1 -1
- package/build/module/Tabs/TabIndicatorProps.js.map +1 -1
- package/build/module/Tabs/Tabs.js +21 -21
- package/build/module/Tabs/Tabs.js.map +1 -1
- package/build/module/Tabs/TabsProps.js.map +1 -1
- package/build/module/Tabs/index.js.map +1 -1
- package/build/module/Tabs/types.js +2 -0
- package/build/module/Tabs/types.js.map +1 -0
- package/build/module/Tabs/useIndexStore.js +25 -0
- package/build/module/Tabs/useIndexStore.js.map +1 -0
- package/build/module/Tabs/useScrollViewReaction.js +23 -28
- package/build/module/Tabs/useScrollViewReaction.js.map +1 -1
- package/build/module/hooks/index.js +1 -0
- package/build/module/hooks/index.js.map +1 -1
- package/build/module/hooks/useCollapsibleAppBar.js +1 -1
- package/build/module/hooks/useCollapsibleAppBar.js.map +1 -1
- package/build/module/hooks/useSyncAnimatedValue.js +27 -0
- package/build/module/hooks/useSyncAnimatedValue.js.map +1 -0
- package/build/module/index.js +1 -0
- package/build/module/index.js.map +1 -1
- package/build/module/store/MockStore.js +14 -0
- package/build/module/store/MockStore.js.map +1 -0
- package/build/module/store/SimpleStore.js +36 -0
- package/build/module/store/SimpleStore.js.map +1 -0
- package/build/module/store/index.js +3 -0
- package/build/module/store/index.js.map +1 -0
- package/build/module/store/types.js +2 -0
- package/build/module/store/types.js.map +1 -0
- package/build/module/types/index.js.map +1 -1
- package/build/typescript/Tabs/IndexAwareTab.d.ts +0 -2
- package/build/typescript/Tabs/InternalContext.d.ts +7 -0
- package/build/typescript/Tabs/ScrollableTabsView.d.ts +6 -0
- package/build/typescript/Tabs/TabIndicatorProps.d.ts +4 -5
- package/build/typescript/Tabs/Tabs.d.ts +1 -1
- package/build/typescript/Tabs/TabsProps.d.ts +3 -16
- package/build/typescript/Tabs/index.d.ts +2 -1
- package/build/typescript/Tabs/types.d.ts +9 -0
- package/build/typescript/Tabs/useIndexStore.d.ts +3 -0
- package/build/typescript/Tabs/useScrollViewReaction.d.ts +1 -2
- package/build/typescript/hooks/index.d.ts +1 -0
- package/build/typescript/hooks/useSyncAnimatedValue.d.ts +6 -0
- package/build/typescript/index.d.ts +1 -0
- package/build/typescript/store/MockStore.d.ts +6 -0
- package/build/typescript/store/SimpleStore.d.ts +9 -0
- package/build/typescript/store/index.d.ts +3 -0
- package/build/typescript/store/types.d.ts +8 -0
- package/build/typescript/types/index.d.ts +6 -0
- package/package.json +4 -4
- package/src/Tabs/IndexAwareTab.tsx +10 -13
- package/src/Tabs/InternalContext.ts +13 -0
- package/src/Tabs/ScrollableTabsView.tsx +26 -0
- package/src/Tabs/TabIndicator.tsx +37 -21
- package/src/Tabs/TabIndicatorProps.ts +4 -5
- package/src/Tabs/Tabs.tsx +43 -44
- package/src/Tabs/TabsProps.ts +4 -28
- package/src/Tabs/index.ts +2 -1
- package/src/Tabs/types.ts +18 -0
- package/src/Tabs/useIndexStore.ts +31 -0
- package/src/Tabs/useScrollViewReaction.ts +15 -26
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useCollapsibleAppBar.ts +1 -1
- package/src/hooks/useSyncAnimatedValue.ts +37 -0
- package/src/index.ts +2 -0
- package/src/store/MockStore.ts +17 -0
- package/src/store/SimpleStore.ts +36 -0
- package/src/store/index.ts +3 -0
- package/src/store/types.ts +9 -0
- 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
|
-
*
|
|
14
|
+
* Initial index.
|
|
16
15
|
*/
|
|
17
|
-
|
|
16
|
+
initialIndex: number;
|
|
18
17
|
/**
|
|
19
|
-
*
|
|
18
|
+
* If `true`, the tab will be able to scroll.
|
|
20
19
|
*/
|
|
21
|
-
|
|
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 './
|
|
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
|
-
|
|
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?:
|
|
44
|
+
UNSTABLE_sharedIndex?: SyncAnimatedValue;
|
|
58
45
|
/**
|
|
59
46
|
* The variant to use.
|
|
60
47
|
* @default 'primary'
|
|
@@ -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
|
+
}
|
|
@@ -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(
|
|
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,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
|
+
}
|
|
@@ -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.
|
|
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.
|
|
18
|
-
"@fountain-ui/utils": "^2.0.0-beta.
|
|
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": "
|
|
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
|
|
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(
|
|
16
|
+
const [selected, setSelected] = useState(false);
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
|
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
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
52
|
-
const
|
|
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
|
|
60
|
+
const nextTranslateX = x1 + (tabWidth - INDICATOR_WIDTH) / 2;
|
|
59
61
|
|
|
60
62
|
const inset = scrollable ? SCROLLABLE_TABS_INSET : 0;
|
|
61
|
-
const
|
|
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
|
-
|
|
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
|
-
*
|
|
17
|
+
* Initial index.
|
|
19
18
|
*/
|
|
20
|
-
|
|
19
|
+
initialIndex: number;
|
|
21
20
|
|
|
22
21
|
/**
|
|
23
|
-
*
|
|
22
|
+
* If `true`, the tab will be able to scroll.
|
|
24
23
|
*/
|
|
25
|
-
|
|
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 {
|
|
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 './
|
|
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
|
|
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 =
|
|
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.
|
|
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
|
|
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
|
-
<
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
|
package/src/Tabs/TabsProps.ts
CHANGED
|
@@ -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?:
|
|
53
|
+
UNSTABLE_sharedIndex?: SyncAnimatedValue;
|
|
78
54
|
|
|
79
55
|
/**
|
|
80
56
|
* The variant to use.
|
package/src/Tabs/index.ts
CHANGED
|
@@ -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
|
+
}
|