@momo-kits/tab-view 0.0.55-alpha.31 → 0.74.2-react-native.2

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.
@@ -1,160 +0,0 @@
1
- import { Colors } from '@momo-kits/core-v2';
2
- import * as React from 'react';
3
- import {
4
- Animated,
5
- Easing,
6
- StyleSheet,
7
- I18nManager,
8
- StyleProp,
9
- ViewStyle,
10
- Platform,
11
- } from 'react-native';
12
-
13
- import type { Route, SceneRendererProps, NavigationState } from './types';
14
-
15
- export type GetTabWidth = (index: number) => number;
16
-
17
- export type Props<T extends Route> = SceneRendererProps & {
18
- navigationState: NavigationState<T>;
19
- width: string | number;
20
- style?: StyleProp<ViewStyle>;
21
- getTabWidth: GetTabWidth;
22
- gap?: number;
23
- };
24
-
25
- export default class TabBarIndicator<T extends Route> extends React.Component<
26
- Props<T>
27
- > {
28
- componentDidMount() {
29
- this.fadeInIndicator();
30
- }
31
-
32
- componentDidUpdate() {
33
- this.fadeInIndicator();
34
- }
35
-
36
- private fadeInIndicator = () => {
37
- const { navigationState, layout, width, getTabWidth } = this.props;
38
-
39
- if (
40
- !this.isIndicatorShown &&
41
- width === 'auto' &&
42
- layout.width &&
43
- // We should fade-in the indicator when we have widths for all the tab items
44
- navigationState.routes.every((_, i) => getTabWidth(i))
45
- ) {
46
- this.isIndicatorShown = true;
47
-
48
- Animated.timing(this.opacity, {
49
- toValue: 1,
50
- duration: 150,
51
- easing: Easing.in(Easing.linear),
52
- useNativeDriver: true,
53
- }).start();
54
- }
55
- };
56
-
57
- private isIndicatorShown = false;
58
-
59
- private opacity = new Animated.Value(this.props.width === 'auto' ? 0 : 1);
60
-
61
- private getTranslateX = (
62
- position: Animated.AnimatedInterpolation,
63
- routes: Route[],
64
- getTabWidth: GetTabWidth,
65
- gap?: number,
66
- ) => {
67
- const inputRange = routes.map((_, i) => i);
68
-
69
- // every index contains widths at all previous indices
70
- const outputRange = routes.reduce<number[]>((acc, _, i) => {
71
- if (i === 0) return [0];
72
- return [...acc, acc[i - 1] + getTabWidth(i - 1) + (gap ?? 0)];
73
- }, []);
74
-
75
- const translateX = position.interpolate({
76
- inputRange,
77
- outputRange,
78
- extrapolate: 'clamp',
79
- });
80
-
81
- return Animated.multiply(translateX, I18nManager.isRTL ? -1 : 1);
82
- };
83
-
84
- render() {
85
- const {
86
- position,
87
- navigationState,
88
- getTabWidth,
89
- width,
90
- style,
91
- layout,
92
- gap,
93
- } = this.props;
94
- const { routes } = navigationState;
95
-
96
- const transform = [];
97
-
98
- if (layout.width) {
99
- const translateX =
100
- routes.length > 1
101
- ? this.getTranslateX(position, routes, getTabWidth, gap)
102
- : 0;
103
-
104
- transform.push({ translateX });
105
- }
106
-
107
- if (width === 'auto') {
108
- const inputRange = routes.map((_, i) => i);
109
- const outputRange = inputRange.map(getTabWidth);
110
-
111
- transform.push(
112
- {
113
- scaleX:
114
- routes.length > 1
115
- ? position.interpolate({
116
- inputRange,
117
- outputRange,
118
- extrapolate: 'clamp',
119
- })
120
- : outputRange[0],
121
- },
122
- { translateX: 0.5 },
123
- );
124
- }
125
-
126
- return (
127
- <Animated.View
128
- style={[
129
- styles.indicator,
130
- { width: width === 'auto' ? 1 : width },
131
- // If layout is not available, use `left` property for positioning the indicator
132
- // This avoids rendering delay until we are able to calculate translateX
133
- // If platform is macos use `left` property as `transform` is broken at the moment.
134
- // See: https://github.com/microsoft/react-native-macos/issues/280
135
- layout.width && Platform.OS !== 'macos'
136
- ? { left: 0 }
137
- : {
138
- left: `${
139
- (100 / routes.length) * navigationState.index
140
- }%`,
141
- },
142
- { transform },
143
- width === 'auto' ? { opacity: this.opacity } : null,
144
- style,
145
- ]}
146
- />
147
- );
148
- }
149
- }
150
-
151
- const styles = StyleSheet.create({
152
- indicator: {
153
- backgroundColor: Colors.pink_03,
154
- position: 'absolute',
155
- left: 0,
156
- bottom: 0,
157
- right: 0,
158
- height: 2,
159
- },
160
- });
package/TabBarItem.tsx DELETED
@@ -1,296 +0,0 @@
1
- import * as React from 'react';
2
- import {
3
- Animated,
4
- StyleSheet,
5
- View,
6
- StyleProp,
7
- LayoutChangeEvent,
8
- TextStyle,
9
- ViewStyle,
10
- } from 'react-native';
11
- import PlatformPressable from './PlatformPressable';
12
- import type { Scene, Route, NavigationState } from './types';
13
- import { Text } from '@momo-kits/core-v2';
14
-
15
- export type Props<T extends Route> = {
16
- position: Animated.AnimatedInterpolation;
17
- route: T;
18
- navigationState: NavigationState<T>;
19
- activeColor?: string;
20
- inactiveColor?: string;
21
- pressColor?: string;
22
- pressOpacity?: number;
23
- getLabelText: (scene: Scene<T>) => string | undefined;
24
- getAccessible: (scene: Scene<T>) => boolean | undefined;
25
- getAccessibilityLabel: (scene: Scene<T>) => string | undefined;
26
- getTestID: (scene: Scene<T>) => string | undefined;
27
- renderLabel?: (scene: {
28
- route: T;
29
- focused: boolean;
30
- color: string;
31
- }) => React.ReactNode;
32
- renderIcon?: (scene: {
33
- route: T;
34
- focused: boolean;
35
- color: string;
36
- }) => React.ReactNode;
37
- renderBadge?: (scene: Scene<T>) => React.ReactNode;
38
- onLayout?: (event: LayoutChangeEvent) => void;
39
- onPress: () => void;
40
- onLongPress: () => void;
41
- labelStyle?: StyleProp<TextStyle>;
42
- style: StyleProp<ViewStyle>;
43
- };
44
-
45
- const DEFAULT_ACTIVE_COLOR = 'rgba(255, 255, 255, 1)';
46
- const DEFAULT_INACTIVE_COLOR = 'rgba(255, 255, 255, 0.7)';
47
-
48
- export default class TabBarItem<T extends Route> extends React.Component<
49
- Props<T>
50
- > {
51
- private getActiveOpacity = (
52
- position: Animated.AnimatedInterpolation,
53
- routes: Route[],
54
- tabIndex: number,
55
- ) => {
56
- if (routes.length > 1) {
57
- const inputRange = routes.map((_, i) => i);
58
-
59
- return position.interpolate({
60
- inputRange,
61
- outputRange: inputRange.map((i) => (i === tabIndex ? 1 : 0)),
62
- });
63
- } else {
64
- return 1;
65
- }
66
- };
67
-
68
- private getInactiveOpacity = (
69
- position: Animated.AnimatedInterpolation,
70
- routes: Route[],
71
- tabIndex: number,
72
- ) => {
73
- if (routes.length > 1) {
74
- const inputRange = routes.map((_: Route, i: number) => i);
75
-
76
- return position.interpolate({
77
- inputRange,
78
- outputRange: inputRange.map((i: number) =>
79
- i === tabIndex ? 0 : 1,
80
- ),
81
- });
82
- } else {
83
- return 0;
84
- }
85
- };
86
-
87
- render() {
88
- const {
89
- route,
90
- position,
91
- navigationState,
92
- renderLabel: renderLabelCustom,
93
- renderIcon,
94
- renderBadge,
95
- getLabelText,
96
- getTestID,
97
- getAccessibilityLabel,
98
- getAccessible,
99
- activeColor: activeColorCustom,
100
- inactiveColor: inactiveColorCustom,
101
- pressColor,
102
- pressOpacity,
103
- labelStyle,
104
- style,
105
- onLayout,
106
- onPress,
107
- onLongPress,
108
- } = this.props;
109
-
110
- const tabIndex = navigationState.routes.indexOf(route);
111
- const isFocused = navigationState.index === tabIndex;
112
-
113
- const labelColorFromStyle = StyleSheet.flatten(labelStyle || {}).color;
114
-
115
- const activeColor =
116
- activeColorCustom !== undefined
117
- ? activeColorCustom
118
- : typeof labelColorFromStyle === 'string'
119
- ? labelColorFromStyle
120
- : DEFAULT_ACTIVE_COLOR;
121
- const inactiveColor =
122
- inactiveColorCustom !== undefined
123
- ? inactiveColorCustom
124
- : typeof labelColorFromStyle === 'string'
125
- ? labelColorFromStyle
126
- : DEFAULT_INACTIVE_COLOR;
127
-
128
- const activeOpacity = this.getActiveOpacity(
129
- position,
130
- navigationState.routes,
131
- tabIndex,
132
- );
133
- const inactiveOpacity = this.getInactiveOpacity(
134
- position,
135
- navigationState.routes,
136
- tabIndex,
137
- );
138
-
139
- let icon: React.ReactNode | null = null;
140
- let label: React.ReactNode | null = null;
141
-
142
- if (renderIcon) {
143
- const activeIcon = renderIcon({
144
- route,
145
- focused: true,
146
- color: activeColor,
147
- });
148
- const inactiveIcon = renderIcon({
149
- route,
150
- focused: false,
151
- color: inactiveColor,
152
- });
153
-
154
- if (inactiveIcon != null && activeIcon != null) {
155
- icon = (
156
- <View style={styles.icon}>
157
- <Animated.View style={{ opacity: inactiveOpacity }}>
158
- {inactiveIcon}
159
- </Animated.View>
160
- <Animated.View
161
- style={[
162
- StyleSheet.absoluteFill,
163
- { opacity: activeOpacity },
164
- ]}>
165
- {activeIcon}
166
- </Animated.View>
167
- </View>
168
- );
169
- }
170
- }
171
-
172
- const renderLabel =
173
- renderLabelCustom !== undefined
174
- ? renderLabelCustom
175
- : ({ route, color }: { route: T; color: string }) => {
176
- const labelText = getLabelText({ route });
177
-
178
- if (typeof labelText === 'string') {
179
- return (
180
- <Animated.Text
181
- style={[
182
- styles.label,
183
- icon ? { marginTop: 0 } : null,
184
- labelStyle,
185
- { color },
186
- ]}>
187
- {labelText}
188
- </Animated.Text>
189
- );
190
- }
191
-
192
- return labelText;
193
- };
194
-
195
- if (renderLabel) {
196
- const activeLabel = renderLabel({
197
- route,
198
- focused: true,
199
- color: activeColor,
200
- });
201
- const inactiveLabel = renderLabel({
202
- route,
203
- focused: false,
204
- color: inactiveColor,
205
- });
206
-
207
- label = (
208
- <View>
209
- <Animated.View style={{ opacity: inactiveOpacity }}>
210
- {inactiveLabel}
211
- </Animated.View>
212
- <Animated.View
213
- style={[
214
- StyleSheet.absoluteFill,
215
- { opacity: activeOpacity },
216
- ]}>
217
- {activeLabel}
218
- </Animated.View>
219
- </View>
220
- );
221
- }
222
-
223
- const tabStyle = StyleSheet.flatten(style);
224
- const isWidthSet = tabStyle?.width !== undefined;
225
- const tabContainerStyle: ViewStyle | null = isWidthSet
226
- ? null
227
- : { flex: 1 };
228
-
229
- const scene = { route };
230
-
231
- let accessibilityLabel = getAccessibilityLabel(scene);
232
-
233
- accessibilityLabel =
234
- typeof accessibilityLabel !== 'undefined'
235
- ? accessibilityLabel
236
- : getLabelText(scene);
237
-
238
- const badge = renderBadge ? renderBadge(scene) : null;
239
-
240
- return (
241
- <PlatformPressable
242
- android_ripple={{ borderless: true }}
243
- testID={getTestID(scene)}
244
- accessible={getAccessible(scene)}
245
- accessibilityLabel={accessibilityLabel}
246
- accessibilityRole="tab"
247
- accessibilityState={{ selected: isFocused }}
248
- // @ts-ignore: this is to support older React Native versions
249
- accessibilityStates={isFocused ? ['selected'] : []}
250
- pressColor={pressColor}
251
- pressOpacity={pressOpacity}
252
- delayPressIn={0}
253
- onLayout={onLayout}
254
- onPress={onPress}
255
- onLongPress={onLongPress}
256
- style={[styles.pressable, tabContainerStyle]}>
257
- <View pointerEvents="none" style={[styles.item, tabStyle]}>
258
- {icon}
259
- {label}
260
- {badge != null ? (
261
- <View style={styles.badge}>{badge}</View>
262
- ) : null}
263
- </View>
264
- </PlatformPressable>
265
- );
266
- }
267
- }
268
-
269
- const styles = StyleSheet.create({
270
- label: {
271
- margin: 4,
272
- fontSize: 14,
273
- fontWeight: 'bold',
274
- backgroundColor: 'transparent',
275
- },
276
- icon: {
277
- margin: 2,
278
- },
279
- item: {
280
- flex: 1,
281
- alignItems: 'center',
282
- justifyContent: 'center',
283
- padding: 10,
284
- minHeight: 48,
285
- },
286
- badge: {
287
- position: 'absolute',
288
- top: 0,
289
- right: 0,
290
- },
291
- pressable: {
292
- // The label is not pressable on Windows
293
- // Adding backgroundColor: 'transparent' seems to fix it
294
- backgroundColor: 'transparent',
295
- },
296
- });
package/TabView.tsx DELETED
@@ -1,156 +0,0 @@
1
- import * as React from 'react';
2
- import {
3
- StyleSheet,
4
- View,
5
- StyleProp,
6
- ViewStyle,
7
- LayoutChangeEvent,
8
- } from 'react-native';
9
- import TabBar from './TabBar';
10
- import SceneView from './SceneView';
11
- import Pager from './Pager';
12
- import type {
13
- Layout,
14
- NavigationState,
15
- Route,
16
- SceneRendererProps,
17
- PagerProps,
18
- } from './types';
19
-
20
- export type Props<T extends Route> = PagerProps & {
21
- onIndexChange: (index: number) => void;
22
- navigationState: NavigationState<T>;
23
- renderScene: (props: SceneRendererProps & { route: T }) => React.ReactNode;
24
- renderLazyPlaceholder?: (props: { route: T }) => React.ReactNode;
25
- renderTabBar?: (
26
- props: SceneRendererProps & { navigationState: NavigationState<T> },
27
- ) => React.ReactNode;
28
- tabBarPosition?: 'top' | 'bottom';
29
- initialLayout?: Partial<Layout>;
30
- lazy?: ((props: { route: T }) => boolean) | boolean;
31
- lazyPreloadDistance?: number;
32
- sceneContainerStyle?: StyleProp<ViewStyle>;
33
- pagerStyle?: StyleProp<ViewStyle>;
34
- style?: StyleProp<ViewStyle>;
35
- };
36
-
37
- export default function TabView<T extends Route>({
38
- onIndexChange,
39
- navigationState,
40
- renderScene,
41
- initialLayout,
42
- keyboardDismissMode = 'auto',
43
- lazy = false,
44
- lazyPreloadDistance = 0,
45
- onSwipeStart,
46
- onSwipeEnd,
47
- renderLazyPlaceholder = () => null,
48
- renderTabBar = (props) => <TabBar {...props} />,
49
- sceneContainerStyle,
50
- pagerStyle,
51
- style,
52
- swipeEnabled = true,
53
- tabBarPosition = 'top',
54
- }: Props<T>) {
55
- const [layout, setLayout] = React.useState({
56
- width: 0,
57
- height: 0,
58
- ...initialLayout,
59
- });
60
-
61
- const jumpToIndex = (index: number) => {
62
- if (index !== navigationState.index) {
63
- onIndexChange(index);
64
- }
65
- };
66
-
67
- const handleLayout = (e: LayoutChangeEvent) => {
68
- const { height, width } = e.nativeEvent.layout;
69
-
70
- setLayout((prevLayout) => {
71
- if (prevLayout.width === width && prevLayout.height === height) {
72
- return prevLayout;
73
- }
74
-
75
- return { height, width };
76
- });
77
- };
78
-
79
- return (
80
- <View onLayout={handleLayout} style={[styles.pager, style]}>
81
- <Pager
82
- layout={layout}
83
- navigationState={navigationState}
84
- keyboardDismissMode={keyboardDismissMode}
85
- swipeEnabled={swipeEnabled}
86
- onSwipeStart={onSwipeStart}
87
- onSwipeEnd={onSwipeEnd}
88
- onIndexChange={jumpToIndex}
89
- style={pagerStyle}>
90
- {({ position, render, addEnterListener, jumpTo }) => {
91
- // All of the props here must not change between re-renders
92
- // This is crucial to optimizing the routes with PureComponent
93
- const sceneRendererProps = {
94
- position,
95
- layout,
96
- jumpTo,
97
- };
98
-
99
- return (
100
- <React.Fragment>
101
- {tabBarPosition === 'top' &&
102
- renderTabBar({
103
- ...sceneRendererProps,
104
- navigationState,
105
- })}
106
- {render(
107
- navigationState.routes.map((route, i) => {
108
- return (
109
- <SceneView
110
- {...sceneRendererProps}
111
- addEnterListener={addEnterListener}
112
- key={route.key}
113
- index={i}
114
- lazy={
115
- typeof lazy === 'function'
116
- ? lazy({ route })
117
- : lazy
118
- }
119
- lazyPreloadDistance={
120
- lazyPreloadDistance
121
- }
122
- navigationState={navigationState}
123
- style={sceneContainerStyle}>
124
- {({ loading }) =>
125
- loading
126
- ? renderLazyPlaceholder({
127
- route,
128
- })
129
- : renderScene({
130
- ...sceneRendererProps,
131
- route,
132
- })
133
- }
134
- </SceneView>
135
- );
136
- }),
137
- )}
138
- {tabBarPosition === 'bottom' &&
139
- renderTabBar({
140
- ...sceneRendererProps,
141
- navigationState,
142
- })}
143
- </React.Fragment>
144
- );
145
- }}
146
- </Pager>
147
- </View>
148
- );
149
- }
150
-
151
- const styles = StyleSheet.create({
152
- pager: {
153
- flex: 1,
154
- overflow: 'hidden',
155
- },
156
- });
package/types.tsx DELETED
@@ -1,58 +0,0 @@
1
- import type { Animated } from 'react-native';
2
- import type { PagerViewProps } from 'react-native-pager-view';
3
-
4
- export type Route = {
5
- key: string;
6
- icon?: string;
7
- title?: string;
8
- accessible?: boolean;
9
- accessibilityLabel?: string;
10
- testID?: string;
11
- };
12
-
13
- export type Event = {
14
- defaultPrevented: boolean;
15
- preventDefault(): void;
16
- };
17
-
18
- export type Scene<T extends Route> = {
19
- route: T;
20
- };
21
-
22
- export type NavigationState<T extends Route> = {
23
- index: number;
24
- routes: T[];
25
- };
26
-
27
- export type Layout = {
28
- width: number;
29
- height: number;
30
- };
31
-
32
- export type Listener = (value: number) => void;
33
-
34
- export type SceneRendererProps = {
35
- layout: Layout;
36
- position: Animated.AnimatedInterpolation;
37
- jumpTo: (key: string) => void;
38
- };
39
-
40
- export type EventEmitterProps = {
41
- addEnterListener: (listener: Listener) => () => void;
42
- };
43
-
44
- export type PagerProps = Omit<
45
- PagerViewProps,
46
- | 'initialPage'
47
- | 'scrollEnabled'
48
- | 'onPageScroll'
49
- | 'onPageSelected'
50
- | 'onPageScrollStateChanged'
51
- | 'keyboardDismissMode'
52
- | 'children'
53
- > & {
54
- keyboardDismissMode?: 'none' | 'on-drag' | 'auto';
55
- swipeEnabled?: boolean;
56
- onSwipeStart?: () => void;
57
- onSwipeEnd?: () => void;
58
- };
@@ -1,12 +0,0 @@
1
- import * as React from 'react';
2
- import { Animated } from 'react-native';
3
-
4
- export default function useAnimatedValue(initialValue: number) {
5
- const lazyRef = React.useRef<Animated.Value>();
6
-
7
- if (lazyRef.current === undefined) {
8
- lazyRef.current = new Animated.Value(initialValue);
9
- }
10
-
11
- return lazyRef.current as Animated.Value;
12
- }