Skip to content

Commit

Permalink
feat: remove react-native-paper
Browse files Browse the repository at this point in the history
  • Loading branch information
mthuong committed May 12, 2021
1 parent b8891a5 commit 8c0b989
Show file tree
Hide file tree
Showing 15 changed files with 386 additions and 69 deletions.
19 changes: 8 additions & 11 deletions template/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import React, { Fragment } from 'react'
import { enableScreens } from 'react-native-screens'
import { Provider as StoreProvider } from 'react-redux'
import { Provider as PaperProvider, DefaultTheme } from 'react-native-paper'
import store from './src/stores/store'
import Navigator from './src/navigator/Navigator'
import { GlobalSnackBar } from './src/components/SnackBar/GlobalSnackBar'
Expand All @@ -26,16 +25,14 @@ enableScreens()
const App = () => {
return (
<StoreProvider store={store}>
<PaperProvider theme={DefaultTheme}>
<ThemeProvider>
<LocalizationProvider>
<Fragment>
<Navigator />
<GlobalSnackBar />
</Fragment>
</LocalizationProvider>
</ThemeProvider>
</PaperProvider>
<ThemeProvider>
<LocalizationProvider>
<Fragment>
<Navigator />
<GlobalSnackBar />
</Fragment>
</LocalizationProvider>
</ThemeProvider>
</StoreProvider>
)
}
Expand Down
5 changes: 0 additions & 5 deletions template/babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,4 @@ module.exports = {
],
'react-native-reanimated/plugin',
],
env: {
production: {
plugins: ['react-native-paper/babel'],
},
},
}
1 change: 0 additions & 1 deletion template/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
"react-native-gifted-chat": "^0.16.3",
"react-native-keyboard-aware-scroll-view": "^0.9.3",
"react-native-localization": "^2.1.6",
"react-native-paper": "^4.7.2",
"react-native-reanimated": "^2.0.0",
"react-native-safe-area-context": "^3.2.0",
"react-native-screens": "^2.18.1",
Expand Down
3 changes: 2 additions & 1 deletion template/src/components/Header/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Icon, IconTypes } from 'components/Icon'
import { Icon } from 'components/Icon'
import { RootNavigation } from 'navigator'
import React from 'react'
import { StyleSheet, View } from 'react-native'
import { TouchableOpacity } from 'react-native-gesture-handler'
import { useTheme } from 'theme'
import { Text } from 'components/text'
import { IconTypes } from 'components/Icon/types'

type HeaderProps = {
leftIcon?: IconTypes
Expand Down
11 changes: 9 additions & 2 deletions template/src/components/SnackBar/GlobalSnackBar.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import * as React from 'react'
import { Snackbar } from 'react-native-paper'
import Snackbar from './index'
import { snackbarSlice } from '../../stores'
import { useAppDispatch, useAppSelector } from '../../stores/hook'

export function GlobalSnackBar() {
const { visible, message } = useAppSelector((state) => state.snackbar)
const dispatch = useAppDispatch()

return (
<Snackbar
duration={4000}
visible={visible}
onDismiss={() => dispatch(snackbarSlice.actions.hide())}>
onDismiss={() => dispatch(snackbarSlice.actions.hide())}
action={{
label: 'X',
onPress: () => {
// Do something
},
}}>
{message}
</Snackbar>
)
Expand Down
281 changes: 281 additions & 0 deletions template/src/components/SnackBar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
import * as React from 'react'
import {
Animated,
SafeAreaView,
StyleProp,
StyleSheet,
ViewStyle,
View,
Text,
TouchableOpacity as Button,
} from 'react-native'

export type SnackbarProps = React.ComponentPropsWithRef<typeof View> & {
/**
* Whether the Snackbar is currently visible.
*/
visible: boolean
/**
* Label and press callback for the action button. It should contain the following properties:
* - `label` - Label of the action button
* - `onPress` - Callback that is called when action button is pressed.
*/
action?: Omit<React.ComponentProps<typeof Button>, 'children'> & {
label: string
}
/**
* The duration for which the Snackbar is shown.
*/
duration?: number
/**
* Callback called when Snackbar is dismissed. The `visible` prop needs to be updated when this is called.
*/
onDismiss: () => void
/**
* Text content of the Snackbar.
*/
children: React.ReactNode
/**
* Style for the wrapper of the snackbar
*/
wrapperStyle?: StyleProp<ViewStyle>
style?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>
ref?: React.RefObject<View>
}

const DURATION_SHORT = 4000
const DURATION_MEDIUM = 7000
const DURATION_LONG = 10000

const theme = {
roundness: 4,
colors: {
// primary: '#6200ee',
accent: '#53B175',
// background: '#f6f6f6',
surface: '#ffffff',
// error: '#B00020',
// text: '#000000',
onSurface: '#000000dd',
},
animation: {
scale: 1.0,
},
}

/**
* Snackbar provide brief feedback about an operation through a message at the bottom of the screen.
*
* ## Usage
* ```js
* import * as React from 'react';
* import { Button, View, StyleSheet } from 'react-native';
* import { Snackbar } from './Snackbar';
*
* const MyComponent = () => {
* const [visible, setVisible] = React.useState(false);
*
* const onToggleSnackBar = () => setVisible(!visible);
*
* const onDismissSnackBar = () => setVisible(false);
*
* return (
* <View style={styles.container}>
* <Button onPress={onToggleSnackBar}>{visible ? 'Hide' : 'Show'}</Button>
* <Snackbar
* visible={visible}
* onDismiss={onDismissSnackBar}
* action={{
* label: 'Undo',
* onPress: () => {
* // Do something
* },
* }}>
* Hey there! I'm a Snackbar.
* </Snackbar>
* </View>
* );
* };
*
* const styles = StyleSheet.create({
* container: {
* flex: 1,
* justifyContent: 'space-between',
* },
* });
*
* export default MyComponent;
* ```
*/
const Snackbar = ({
visible,
action,
duration = DURATION_MEDIUM,
onDismiss,
children,
wrapperStyle,
style,
...rest
}: SnackbarProps) => {
const { current: opacity } = React.useRef<Animated.Value>(
new Animated.Value(0.0)
)
const [hidden, setHidden] = React.useState<boolean>(!visible)

const hideTimeout = React.useRef<NodeJS.Timeout | undefined>(undefined)

const { scale } = theme.animation

React.useEffect(() => {
return () => {
if (hideTimeout.current) clearTimeout(hideTimeout.current)
}
}, [])

React.useLayoutEffect(() => {
if (visible) {
// show
if (hideTimeout.current) clearTimeout(hideTimeout.current)
setHidden(false)
Animated.timing(opacity, {
toValue: 1,
duration: 200 * scale,
useNativeDriver: true,
}).start(({ finished }) => {
if (finished) {
const isInfinity =
duration === Number.POSITIVE_INFINITY ||
duration === Number.NEGATIVE_INFINITY

if (finished && !isInfinity) {
hideTimeout.current = (setTimeout(
onDismiss,
duration
) as unknown) as NodeJS.Timeout
}
}
})
} else {
// hide
if (hideTimeout.current) clearTimeout(hideTimeout.current)

Animated.timing(opacity, {
toValue: 0,
duration: 100 * scale,
useNativeDriver: true,
}).start(({ finished }) => {
if (finished) setHidden(true)
})
}
}, [visible, duration, opacity, scale, onDismiss])

const { colors, roundness } = theme

if (hidden) return null

const {
style: actionStyle,
label: actionLabel,
onPress: onPressAction,
...actionProps
} = action || {}

return (
<SafeAreaView
pointerEvents='box-none'
style={[styles.wrapper, wrapperStyle]}>
<Animated.View
pointerEvents='box-none'
accessibilityLiveRegion='polite'
style={
[
styles.container,
{
borderRadius: roundness,
opacity: opacity,
transform: [
{
scale: visible
? opacity.interpolate({
inputRange: [0, 1],
outputRange: [0.9, 1],
})
: 1,
},
],
},
{ backgroundColor: colors.onSurface },
style,
] as StyleProp<ViewStyle>
}
{...rest}>
<Text
style={[
styles.content,
{ marginRight: action ? 0 : 16, color: colors.surface },
]}>
{children}
</Text>
{action ? (
<Button
onPress={(event) => {
console.tron.log('onPress')
onPressAction?.(event)
onDismiss()
}}
style={[styles.button, actionStyle]}
{...actionProps}>
<Text style={[styles.buttonText]}>{actionLabel}</Text>
</Button>
) : null}
</Animated.View>
</SafeAreaView>
)
}

/**
* Show the Snackbar for a short duration.
*/
Snackbar.DURATION_SHORT = DURATION_SHORT

/**
* Show the Snackbar for a medium duration.
*/
Snackbar.DURATION_MEDIUM = DURATION_MEDIUM

/**
* Show the Snackbar for a long duration.
*/
Snackbar.DURATION_LONG = DURATION_LONG

const styles = StyleSheet.create({
wrapper: {
position: 'absolute',
bottom: 0,
width: '100%',
},
container: {
elevation: 6,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
margin: 8,
borderRadius: 4,
},
content: {
marginLeft: 16,
marginVertical: 14,
flexWrap: 'wrap',
flex: 1,
},
button: {
marginHorizontal: 8,
marginVertical: 6,
},
buttonText: {
color: theme.colors.accent,
fontSize: 16,
},
})

export default Snackbar
Loading

0 comments on commit 8c0b989

Please sign in to comment.