@uiw/react-native
Version:
UIW for React Native
183 lines (182 loc) • 5.79 kB
JavaScript
import React, { Fragment, useEffect, useState } from 'react';
import { Animated, StyleSheet, TouchableOpacity, Dimensions, } from 'react-native';
import { debounce } from 'lodash';
import { useTheme } from '@shopify/restyle';
const DEVICE_WIDTH = Dimensions.get('window').width;
const DEVICE_HEIGHT = Dimensions.get('window').height;
export default function Drawer(props) {
const theme = useTheme();
const getInitPosition = () => {
const { drawerWidth, placement, drawerHeight } = props;
const xy = { x: 0, y: 0 };
if (placement === 'left') {
xy.x = -(drawerWidth || 0);
}
if (placement === 'right') {
xy.x = DEVICE_WIDTH || 0;
}
if (placement === 'top') {
xy.y = -(drawerHeight || 0);
}
if (placement === 'bottom') {
xy.y = DEVICE_HEIGHT || 0;
}
return xy;
};
const [state, setState] = useState({
zIndexValue: 0,
overlayValue: new Animated.Value(0),
drawerValue: new Animated.ValueXY({ ...getInitPosition() }),
isOpen: !!props.isOpen,
});
const openDrawer = () => {
setState({ ...state, zIndexValue: 3002, isOpen: true });
Animated.parallel([
Animated.spring(state.drawerValue, {
toValue: { x: 0, y: 0 },
overshootClamping: true,
useNativeDriver: true,
}),
Animated.spring(state.overlayValue, {
toValue: 0.7,
overshootClamping: true,
useNativeDriver: true,
}),
]).start(() => {
props.openDrawer(true);
props.onChange(true);
});
};
const closeDrawer = () => {
const { drawerValue, overlayValue } = state;
Animated.parallel([
Animated.spring(drawerValue, {
toValue: { ...getInitPosition() },
overshootClamping: true,
useNativeDriver: true,
}),
Animated.spring(overlayValue, {
toValue: 0,
overshootClamping: true,
useNativeDriver: true,
}),
]).start(() => {
props.closeDrawer(false);
props.onChange(false);
setState({ ...state, zIndexValue: 0, isOpen: false });
});
};
const handleDrawer = debounce((isOpen) => {
isOpen ? openDrawer() : closeDrawer();
}, 100);
useEffect(() => {
handleDrawer(state.isOpen);
}, [state.isOpen]);
useEffect(() => {
handleDrawer(!!props.isOpen);
}, [props.isOpen]);
useEffect(() => {
if (props.isOpen) {
openDrawer();
}
}, []);
const onOverlayClick = (e) => {
const { maskClosable } = props;
if (!maskClosable) {
return false;
}
e.stopPropagation();
closeDrawer();
};
const { style, drawerWidth, drawerBackgroundColor, maskProps, placement, drawerHeight } = props;
const { isOpen, drawerValue, overlayValue, zIndexValue } = state;
const isTopOrBottom = placement === 'top' || placement === 'bottom';
const changeStyle = isTopOrBottom ? 'height' : 'width';
const dynamicDrawerStyles = {
backgroundColor: drawerBackgroundColor || theme.colors.mask,
};
if (isTopOrBottom) {
dynamicDrawerStyles.top = placement === 'top' ? 0 : null;
dynamicDrawerStyles.bottom = placement === 'bottom' ? 0 : null;
dynamicDrawerStyles.height = drawerWidth;
dynamicDrawerStyles.width = '100%';
}
else {
dynamicDrawerStyles.left = placement === 'left' ? 0 : null;
dynamicDrawerStyles.right = placement === 'right' ? 0 : null;
dynamicDrawerStyles.width = drawerWidth;
}
const overlayOpacity = overlayValue.interpolate({
inputRange: [0, 1],
outputRange: [0, 0.3],
extrapolate: 'clamp',
});
return (<Fragment>
<Animated.View pointerEvents={isOpen ? 'auto' : 'none'} style={[
styles.overlay,
styles.positionFull,
maskProps,
{
// opacity: overlayValue,
opacity: overlayOpacity,
zIndex: zIndexValue,
},
]}>
<TouchableOpacity style={[
styles.positionFull,
// eslint-disable-next-line
{
zIndex: zIndexValue,
position: 'absolute',
},
]} onPress={onOverlayClick}/>
</Animated.View>
<Animated.View style={[
styles.drawer,
dynamicDrawerStyles,
style,
// eslint-disable-next-line
{
zIndex: zIndexValue + 1,
[changeStyle]: isOpen ? (isTopOrBottom ? drawerHeight : drawerWidth) : 0,
transform: [
{ translateX: drawerValue.x }, // x轴移动
{ translateY: drawerValue.y }, // y轴移动
],
},
]}>
{props.children}
</Animated.View>
</Fragment>);
}
Drawer.defaultProps = {
placement: 'left',
drawerBackgroundColor: undefined,
drawerWidth: 300,
drawerHeight: 500,
maskClosable: true,
isOpen: false,
onChange: () => null,
openDrawer: () => null,
closeDrawer: () => null,
};
const styles = StyleSheet.create({
drawer: {
position: 'absolute',
top: 0,
bottom: 0,
flex: 1,
zIndex: 3004,
},
positionFull: {
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
},
overlay: {
backgroundColor: '#000',
zIndex: 3002,
},
});