[IMPROVE] migrate the ActionSheet component
This commit is contained in:
parent
99b6c3d073
commit
802c7b2b28
|
@ -7,17 +7,11 @@ import React, {
|
||||||
useCallback,
|
useCallback,
|
||||||
isValidElement
|
isValidElement
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Keyboard, Text } from 'react-native';
|
import { Keyboard, Text } from 'react-native';
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
import { TapGestureHandler, State } from 'react-native-gesture-handler';
|
import { TapGestureHandler, State } from 'react-native-gesture-handler';
|
||||||
import ScrollBottomSheet from 'react-native-scroll-bottom-sheet';
|
import ScrollBottomSheet from 'react-native-scroll-bottom-sheet';
|
||||||
import Animated, {
|
import Animated, { Extrapolate, interpolate, Value, Easing} from 'react-native-reanimated';
|
||||||
Extrapolate,
|
|
||||||
interpolate,
|
|
||||||
Value,
|
|
||||||
Easing
|
|
||||||
} from 'react-native-reanimated';
|
|
||||||
import * as Haptics from 'expo-haptics';
|
import * as Haptics from 'expo-haptics';
|
||||||
import { useBackHandler } from '@react-native-community/hooks';
|
import { useBackHandler } from '@react-native-community/hooks';
|
||||||
|
|
||||||
|
@ -29,9 +23,16 @@ import styles, { ITEM_HEIGHT } from './styles';
|
||||||
import { isTablet, isIOS } from '../../utils/deviceInfo';
|
import { isTablet, isIOS } from '../../utils/deviceInfo';
|
||||||
import * as List from '../List';
|
import * as List from '../List';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { useOrientation, useDimensions } from '../../dimensions';
|
import { useOrientation, useDimensions, IDimensionsContextProps } from '../../dimensions';
|
||||||
|
|
||||||
const getItemLayout = (data, index) => ({ length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index });
|
type TActionSheetData = {
|
||||||
|
options: any;
|
||||||
|
headerHeight?: number;
|
||||||
|
hasCancel?: boolean;
|
||||||
|
customHeader: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getItemLayout = (data: any, index: number) => ({ length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index });
|
||||||
|
|
||||||
const HANDLE_HEIGHT = isIOS ? 40 : 56;
|
const HANDLE_HEIGHT = isIOS ? 40 : 56;
|
||||||
const MAX_SNAP_HEIGHT = 16;
|
const MAX_SNAP_HEIGHT = 16;
|
||||||
|
@ -45,17 +46,17 @@ const ANIMATION_CONFIG = {
|
||||||
easing: Easing.bezier(0.645, 0.045, 0.355, 1.0)
|
easing: Easing.bezier(0.645, 0.045, 0.355, 1.0)
|
||||||
};
|
};
|
||||||
|
|
||||||
const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => {
|
const ActionSheet = React.memo(forwardRef(({ children, theme }: {children: JSX.Element; theme: string}, ref) => {
|
||||||
const bottomSheetRef = useRef();
|
const bottomSheetRef: any = useRef();
|
||||||
const [data, setData] = useState({});
|
const [data, setData] = useState<TActionSheetData>({} as TActionSheetData);
|
||||||
const [isVisible, setVisible] = useState(false);
|
const [isVisible, setVisible] = useState(false);
|
||||||
const { height } = useDimensions();
|
const { height }: Partial<IDimensionsContextProps> = useDimensions();
|
||||||
const { isLandscape } = useOrientation();
|
const { isLandscape } = useOrientation();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
|
|
||||||
const maxSnap = Math.max(
|
const maxSnap = Math.max(
|
||||||
(
|
(
|
||||||
height
|
height!
|
||||||
// Items height
|
// Items height
|
||||||
- (ITEM_HEIGHT * (data?.options?.length || 0))
|
- (ITEM_HEIGHT * (data?.options?.length || 0))
|
||||||
// Handle height
|
// Handle height
|
||||||
|
@ -77,7 +78,7 @@ const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => {
|
||||||
* we'll provide more one snap
|
* we'll provide more one snap
|
||||||
* that point 50% of the whole screen
|
* that point 50% of the whole screen
|
||||||
*/
|
*/
|
||||||
const snaps = (height - maxSnap > height * 0.6) && !isLandscape ? [maxSnap, height * 0.5, height] : [maxSnap, height];
|
const snaps: any = (height! - maxSnap > height! * 0.6) && !isLandscape ? [maxSnap, height! * 0.5, height] : [maxSnap, height];
|
||||||
const openedSnapIndex = snaps.length > 2 ? 1 : 0;
|
const openedSnapIndex = snaps.length > 2 ? 1 : 0;
|
||||||
const closedSnapIndex = snaps.length - 1;
|
const closedSnapIndex = snaps.length - 1;
|
||||||
|
|
||||||
|
@ -87,12 +88,12 @@ const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => {
|
||||||
bottomSheetRef.current?.snapTo(closedSnapIndex);
|
bottomSheetRef.current?.snapTo(closedSnapIndex);
|
||||||
};
|
};
|
||||||
|
|
||||||
const show = (options) => {
|
const show = (options: any) => {
|
||||||
setData(options);
|
setData(options);
|
||||||
toggleVisible();
|
toggleVisible();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onBackdropPressed = ({ nativeEvent }) => {
|
const onBackdropPressed = ({ nativeEvent }: any) => {
|
||||||
if (nativeEvent.oldState === State.ACTIVE) {
|
if (nativeEvent.oldState === State.ACTIVE) {
|
||||||
hide();
|
hide();
|
||||||
}
|
}
|
||||||
|
@ -128,7 +129,7 @@ const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => {
|
||||||
<Handle theme={theme} />
|
<Handle theme={theme} />
|
||||||
{isValidElement(data?.customHeader) ? data.customHeader : null}
|
{isValidElement(data?.customHeader) ? data.customHeader : null}
|
||||||
</>
|
</>
|
||||||
));
|
), [theme, data]);
|
||||||
|
|
||||||
const renderFooter = useCallback(() => (data?.hasCancel ? (
|
const renderFooter = useCallback(() => (data?.hasCancel ? (
|
||||||
<Button
|
<Button
|
||||||
|
@ -140,9 +141,9 @@ const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => {
|
||||||
{I18n.t('Cancel')}
|
{I18n.t('Cancel')}
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
) : null));
|
) : null), [theme, data, hide()]);
|
||||||
|
|
||||||
const renderItem = useCallback(({ item }) => <Item item={item} hide={hide} theme={theme} />);
|
const renderItem = useCallback(({ item }) => <Item item={item} hide={hide} theme={theme} />, []);
|
||||||
|
|
||||||
const animatedPosition = React.useRef(new Value(0));
|
const animatedPosition = React.useRef(new Value(0));
|
||||||
const opacity = interpolate(animatedPosition.current, {
|
const opacity = interpolate(animatedPosition.current, {
|
||||||
|
@ -168,7 +169,7 @@ const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => {
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</TapGestureHandler>
|
</TapGestureHandler>
|
||||||
<ScrollBottomSheet
|
<ScrollBottomSheet<any>
|
||||||
testID='action-sheet'
|
testID='action-sheet'
|
||||||
ref={bottomSheetRef}
|
ref={bottomSheetRef}
|
||||||
componentType='FlatList'
|
componentType='FlatList'
|
||||||
|
@ -200,9 +201,5 @@ const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => {
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}));
|
}));
|
||||||
ActionSheet.propTypes = {
|
|
||||||
children: PropTypes.node,
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ActionSheet;
|
export default ActionSheet;
|
|
@ -1,15 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
|
|
||||||
export const Handle = React.memo(({ theme }) => (
|
export const Handle = React.memo(({ theme }: {theme: string}) => (
|
||||||
<View style={[styles.handle, { backgroundColor: themes[theme].focusedBackground }]} testID='action-sheet-handle'>
|
<View style={[styles.handle, { backgroundColor: themes[theme].focusedBackground }]} testID='action-sheet-handle'>
|
||||||
<View style={[styles.handleIndicator, { backgroundColor: themes[theme].auxiliaryText }]} />
|
<View style={[styles.handleIndicator, { backgroundColor: themes[theme].auxiliaryText }]} />
|
||||||
</View>
|
</View>
|
||||||
));
|
));
|
||||||
Handle.propTypes = {
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
|
@ -1,13 +1,25 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Text, View } from 'react-native';
|
import { Text, View } from 'react-native';
|
||||||
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import styles from './styles';
|
|
||||||
import { Button } from './Button';
|
import { Button } from './Button';
|
||||||
|
import styles from './styles';
|
||||||
|
|
||||||
export const Item = React.memo(({ item, hide, theme }) => {
|
interface IActionSheetItem {
|
||||||
|
item: {
|
||||||
|
title: string;
|
||||||
|
icon: string;
|
||||||
|
danger: boolean;
|
||||||
|
testID: string;
|
||||||
|
onPress(): void;
|
||||||
|
right: Function;
|
||||||
|
};
|
||||||
|
theme: string
|
||||||
|
hide(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Item = React.memo(({ item, hide, theme }: IActionSheetItem) => {
|
||||||
const onPress = () => {
|
const onPress = () => {
|
||||||
hide();
|
hide();
|
||||||
item?.onPress();
|
item?.onPress();
|
||||||
|
@ -37,15 +49,3 @@ export const Item = React.memo(({ item, hide, theme }) => {
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
Item.propTypes = {
|
|
||||||
item: PropTypes.shape({
|
|
||||||
title: PropTypes.string,
|
|
||||||
icon: PropTypes.string,
|
|
||||||
danger: PropTypes.bool,
|
|
||||||
onPress: PropTypes.func,
|
|
||||||
right: PropTypes.func,
|
|
||||||
testID: PropTypes.string
|
|
||||||
}),
|
|
||||||
hide: PropTypes.func,
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
|
@ -13,18 +13,18 @@ export const useActionSheet = () => useContext(context);
|
||||||
|
|
||||||
const { Provider, Consumer } = context;
|
const { Provider, Consumer } = context;
|
||||||
|
|
||||||
export const withActionSheet = Component => forwardRef((props, ref) => (
|
export const withActionSheet = (Component: any) => forwardRef((props, ref) => (
|
||||||
<Consumer>
|
<Consumer>
|
||||||
{contexts => <Component {...props} {...contexts} ref={ref} />}
|
{contexts => <Component {...props} {...contexts} ref={ref} />}
|
||||||
</Consumer>
|
</Consumer>
|
||||||
));
|
));
|
||||||
|
|
||||||
export const ActionSheetProvider = React.memo(({ children }) => {
|
export const ActionSheetProvider = React.memo(({ children }) => {
|
||||||
const ref = useRef();
|
const ref: any = useRef();
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
|
|
||||||
const getContext = () => ({
|
const getContext = () => ({
|
||||||
showActionSheet: (options) => {
|
showActionSheet: (options: any) => {
|
||||||
ref.current?.showActionSheet(options);
|
ref.current?.showActionSheet(options);
|
||||||
},
|
},
|
||||||
hideActionSheet: () => {
|
hideActionSheet: () => {
|
|
@ -2,9 +2,9 @@ import React from 'react';
|
||||||
import { Dimensions } from 'react-native';
|
import { Dimensions } from 'react-native';
|
||||||
import hoistNonReactStatics from 'hoist-non-react-statics';
|
import hoistNonReactStatics from 'hoist-non-react-statics';
|
||||||
|
|
||||||
interface IDimensionsContextProps {
|
export interface IDimensionsContextProps {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height?: number;
|
||||||
scale: number;
|
scale: number;
|
||||||
fontScale: number;
|
fontScale: number;
|
||||||
setDimensions: ({ width, height, scale, fontScale }: { width: number; height: number; scale: number; fontScale: number; }) => void;
|
setDimensions: ({ width, height, scale, fontScale }: { width: number; height: number; scale: number; fontScale: number; }) => void;
|
||||||
|
|
Loading…
Reference in New Issue