Chore: Evaluate ActionSheet - TypeScript (#3927)
This commit is contained in:
parent
e8d23791d3
commit
2fb7d917a7
|
@ -1,30 +1,29 @@
|
|||
import { useBackHandler } from '@react-native-community/hooks';
|
||||
import * as Haptics from 'expo-haptics';
|
||||
import React, { forwardRef, isValidElement, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
||||
import { Keyboard, Text } from 'react-native';
|
||||
import { HandlerStateChangeEventPayload, State, TapGestureHandler } from 'react-native-gesture-handler';
|
||||
import Animated, { Easing, Extrapolate, interpolateNode, Value } from 'react-native-reanimated';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { State, TapGestureHandler } from 'react-native-gesture-handler';
|
||||
import ScrollBottomSheet from 'react-native-scroll-bottom-sheet';
|
||||
import Animated, { Easing, Extrapolate, Value, interpolateNode } from 'react-native-reanimated';
|
||||
import * as Haptics from 'expo-haptics';
|
||||
import { useBackHandler } from '@react-native-community/hooks';
|
||||
|
||||
import { Item } from './Item';
|
||||
import { Handle } from './Handle';
|
||||
import { Button } from './Button';
|
||||
import { themes } from '../../constants/colors';
|
||||
import styles, { ITEM_HEIGHT } from './styles';
|
||||
import { useDimensions, useOrientation } from '../../dimensions';
|
||||
import I18n from '../../i18n';
|
||||
import { useTheme } from '../../theme';
|
||||
import { isIOS, isTablet } from '../../utils/deviceInfo';
|
||||
import * as List from '../List';
|
||||
import I18n from '../../i18n';
|
||||
import { IDimensionsContextProps, useDimensions, useOrientation } from '../../dimensions';
|
||||
import { Button } from './Button';
|
||||
import { Handle } from './Handle';
|
||||
import { IActionSheetItem, Item } from './Item';
|
||||
import { TActionSheetOptions, TActionSheetOptionsItem } from './Provider';
|
||||
import styles, { ITEM_HEIGHT } from './styles';
|
||||
|
||||
interface IActionSheetData {
|
||||
options: any;
|
||||
headerHeight?: number;
|
||||
hasCancel?: boolean;
|
||||
customHeader: any;
|
||||
}
|
||||
|
||||
const getItemLayout = (data: any, index: number) => ({ length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index });
|
||||
const getItemLayout = (data: TActionSheetOptionsItem[] | null | undefined, index: number) => ({
|
||||
length: ITEM_HEIGHT,
|
||||
offset: ITEM_HEIGHT * index,
|
||||
index
|
||||
});
|
||||
|
||||
const HANDLE_HEIGHT = isIOS ? 40 : 56;
|
||||
const MAX_SNAP_HEIGHT = 16;
|
||||
|
@ -39,16 +38,17 @@ const ANIMATION_CONFIG = {
|
|||
};
|
||||
|
||||
const ActionSheet = React.memo(
|
||||
forwardRef(({ children, theme }: { children: JSX.Element; theme: string }, ref) => {
|
||||
const bottomSheetRef: any = useRef();
|
||||
const [data, setData] = useState<IActionSheetData>({} as IActionSheetData);
|
||||
forwardRef(({ children }: { children: React.ReactElement }, ref) => {
|
||||
const { theme } = useTheme();
|
||||
const bottomSheetRef = useRef<ScrollBottomSheet<TActionSheetOptionsItem>>(null);
|
||||
const [data, setData] = useState<TActionSheetOptions>({} as TActionSheetOptions);
|
||||
const [isVisible, setVisible] = useState(false);
|
||||
const { height }: Partial<IDimensionsContextProps> = useDimensions();
|
||||
const { height } = useDimensions();
|
||||
const { isLandscape } = useOrientation();
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
const maxSnap = Math.max(
|
||||
height! -
|
||||
height -
|
||||
// Items height
|
||||
ITEM_HEIGHT * (data?.options?.length || 0) -
|
||||
// Handle height
|
||||
|
@ -69,7 +69,7 @@ const ActionSheet = React.memo(
|
|||
* we'll provide more one snap
|
||||
* that point 50% of the whole screen
|
||||
*/
|
||||
const snaps: any = height! - maxSnap > height! * 0.6 && !isLandscape ? [maxSnap, height! * 0.5, height] : [maxSnap, height];
|
||||
const snaps = height - maxSnap > height * 0.6 && !isLandscape ? [maxSnap, height * 0.5, height] : [maxSnap, height];
|
||||
const openedSnapIndex = snaps.length > 2 ? 1 : 0;
|
||||
const closedSnapIndex = snaps.length - 1;
|
||||
|
||||
|
@ -79,12 +79,12 @@ const ActionSheet = React.memo(
|
|||
bottomSheetRef.current?.snapTo(closedSnapIndex);
|
||||
};
|
||||
|
||||
const show = (options: any) => {
|
||||
const show = (options: TActionSheetOptions) => {
|
||||
setData(options);
|
||||
toggleVisible();
|
||||
};
|
||||
|
||||
const onBackdropPressed = ({ nativeEvent }: any) => {
|
||||
const onBackdropPressed = ({ nativeEvent }: { nativeEvent: HandlerStateChangeEventPayload }) => {
|
||||
if (nativeEvent.oldState === State.ACTIVE) {
|
||||
hide();
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ const ActionSheet = React.memo(
|
|||
|
||||
const renderHandle = () => (
|
||||
<>
|
||||
<Handle theme={theme} />
|
||||
<Handle />
|
||||
{isValidElement(data?.customHeader) ? data.customHeader : null}
|
||||
</>
|
||||
);
|
||||
|
@ -127,21 +127,23 @@ const ActionSheet = React.memo(
|
|||
<Button
|
||||
onPress={hide}
|
||||
style={[styles.button, { backgroundColor: themes[theme].auxiliaryBackground }]}
|
||||
// TODO: Remove when migrate Touch
|
||||
theme={theme}
|
||||
accessibilityLabel={I18n.t('Cancel')}>
|
||||
<Text style={[styles.text, { color: themes[theme].bodyText }]}>{I18n.t('Cancel')}</Text>
|
||||
</Button>
|
||||
) : null;
|
||||
|
||||
const renderItem = ({ item }: any) => <Item item={item} hide={hide} theme={theme} />;
|
||||
const renderItem = ({ item }: { item: IActionSheetItem['item'] }) => <Item item={item} hide={hide} />;
|
||||
|
||||
const animatedPosition = React.useRef(new Value(0));
|
||||
// TODO: Similar to https://github.com/wcandillon/react-native-redash/issues/307#issuecomment-827442320
|
||||
const opacity = interpolateNode(animatedPosition.current, {
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, themes[theme].backdropOpacity],
|
||||
extrapolate: Extrapolate.CLAMP
|
||||
}) as any;
|
||||
}) as any; // The function's return differs from the expected type of opacity, however this problem is something related to lib, maybe when updating the types will be fixed.
|
||||
|
||||
const bottomSheet = isLandscape || isTablet ? styles.bottomSheet : {};
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -160,7 +162,7 @@ const ActionSheet = React.memo(
|
|||
]}
|
||||
/>
|
||||
</TapGestureHandler>
|
||||
<ScrollBottomSheet
|
||||
<ScrollBottomSheet<TActionSheetOptionsItem>
|
||||
testID='action-sheet'
|
||||
ref={bottomSheetRef}
|
||||
componentType='FlatList'
|
||||
|
@ -169,18 +171,11 @@ const ActionSheet = React.memo(
|
|||
renderHandle={renderHandle}
|
||||
onSettle={index => index === closedSnapIndex && toggleVisible()}
|
||||
animatedPosition={animatedPosition.current}
|
||||
containerStyle={
|
||||
[
|
||||
styles.container,
|
||||
{ backgroundColor: themes[theme].focusedBackground },
|
||||
(isLandscape || isTablet) && styles.bottomSheet
|
||||
] as any
|
||||
}
|
||||
containerStyle={{ ...styles.container, ...bottomSheet, backgroundColor: themes[theme].focusedBackground }}
|
||||
animationConfig={ANIMATION_CONFIG}
|
||||
// FlatList props
|
||||
data={data?.options}
|
||||
data={data.options}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={(item: any) => item.title}
|
||||
keyExtractor={item => item.title}
|
||||
style={{ backgroundColor: themes[theme].focusedBackground }}
|
||||
contentContainerStyle={styles.content}
|
||||
ItemSeparatorComponent={List.Separator}
|
||||
|
|
|
@ -3,9 +3,13 @@ import { View } from 'react-native';
|
|||
|
||||
import styles from './styles';
|
||||
import { themes } from '../../constants/colors';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
export const Handle = React.memo(({ theme }: { theme: string }) => (
|
||||
export const Handle = React.memo(() => {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<View style={[styles.handle, { backgroundColor: themes[theme].focusedBackground }]} testID='action-sheet-handle'>
|
||||
<View style={[styles.handleIndicator, { backgroundColor: themes[theme].auxiliaryText }]} />
|
||||
</View>
|
||||
));
|
||||
);
|
||||
});
|
||||
|
|
|
@ -3,23 +3,24 @@ import { Text, View } from 'react-native';
|
|||
|
||||
import { themes } from '../../constants/colors';
|
||||
import { CustomIcon } from '../../lib/Icons';
|
||||
import { useTheme } from '../../theme';
|
||||
import { Button } from './Button';
|
||||
import styles from './styles';
|
||||
|
||||
interface IActionSheetItem {
|
||||
export interface IActionSheetItem {
|
||||
item: {
|
||||
title: string;
|
||||
icon: string;
|
||||
danger: boolean;
|
||||
testID: string;
|
||||
onPress(): void;
|
||||
right: Function;
|
||||
danger?: boolean;
|
||||
testID?: string;
|
||||
onPress: () => void;
|
||||
right?: Function;
|
||||
};
|
||||
theme: string;
|
||||
hide(): void;
|
||||
}
|
||||
|
||||
export const Item = React.memo(({ item, hide, theme }: IActionSheetItem) => {
|
||||
export const Item = React.memo(({ item, hide }: IActionSheetItem) => {
|
||||
const { theme } = useTheme();
|
||||
const onPress = () => {
|
||||
hide();
|
||||
item?.onPress();
|
||||
|
|
|
@ -1,14 +1,21 @@
|
|||
import React, { ForwardedRef, forwardRef, useContext, useRef } from 'react';
|
||||
|
||||
import ActionSheet from './ActionSheet';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
export type TActionSheetOptionsItem = { title: string; icon: string; onPress: () => void };
|
||||
|
||||
export type TActionSheetOptions = {
|
||||
options: TActionSheetOptionsItem[];
|
||||
headerHeight: number;
|
||||
customHeader: React.ReactElement | null;
|
||||
hasCancel?: boolean;
|
||||
};
|
||||
interface IActionSheetProvider {
|
||||
Provider: any;
|
||||
Consumer: any;
|
||||
showActionSheet: (item: TActionSheetOptions) => void;
|
||||
hideActionSheet: () => void;
|
||||
}
|
||||
|
||||
const context: IActionSheetProvider = React.createContext({
|
||||
const context = React.createContext<IActionSheetProvider>({
|
||||
showActionSheet: () => {},
|
||||
hideActionSheet: () => {}
|
||||
});
|
||||
|
@ -17,17 +24,16 @@ export const useActionSheet = () => useContext(context);
|
|||
|
||||
const { Provider, Consumer } = context;
|
||||
|
||||
export const withActionSheet = (Component: any): any =>
|
||||
forwardRef((props: any, ref: ForwardedRef<any>) => (
|
||||
<Consumer>{(contexts: any) => <Component {...props} {...contexts} ref={ref} />}</Consumer>
|
||||
export const withActionSheet = (Component: React.ComponentType<any>): typeof Component =>
|
||||
forwardRef((props: typeof React.Component, ref: ForwardedRef<IActionSheetProvider>) => (
|
||||
<Consumer>{(contexts: IActionSheetProvider) => <Component {...props} {...contexts} ref={ref} />}</Consumer>
|
||||
));
|
||||
|
||||
export const ActionSheetProvider = React.memo(({ children }: { children: JSX.Element | JSX.Element[] }) => {
|
||||
const ref: ForwardedRef<any> = useRef();
|
||||
const { theme }: any = useTheme();
|
||||
export const ActionSheetProvider = React.memo(({ children }: { children: React.ReactElement | React.ReactElement[] }) => {
|
||||
const ref: ForwardedRef<IActionSheetProvider> = useRef(null);
|
||||
|
||||
const getContext = () => ({
|
||||
showActionSheet: (options: any) => {
|
||||
showActionSheet: (options: TActionSheetOptions) => {
|
||||
ref.current?.showActionSheet(options);
|
||||
},
|
||||
hideActionSheet: () => {
|
||||
|
@ -37,7 +43,7 @@ export const ActionSheetProvider = React.memo(({ children }: { children: JSX.Ele
|
|||
|
||||
return (
|
||||
<Provider value={getContext()}>
|
||||
<ActionSheet ref={ref} theme={theme}>
|
||||
<ActionSheet ref={ref}>
|
||||
<>{children}</>
|
||||
</ActionSheet>
|
||||
</Provider>
|
||||
|
|
|
@ -6,10 +6,10 @@ import { TNavigationOptions } from './definitions/navigationTypes';
|
|||
|
||||
export interface IDimensionsContextProps {
|
||||
width: number;
|
||||
height?: number;
|
||||
scale: number;
|
||||
fontScale: number;
|
||||
setDimensions: ({
|
||||
height: number;
|
||||
scale?: number;
|
||||
fontScale?: number;
|
||||
setDimensions?: ({
|
||||
width,
|
||||
height,
|
||||
scale,
|
||||
|
@ -22,7 +22,7 @@ export interface IDimensionsContextProps {
|
|||
}) => void;
|
||||
}
|
||||
|
||||
export const DimensionsContext = React.createContext<Partial<IDimensionsContextProps>>(Dimensions.get('window'));
|
||||
export const DimensionsContext = React.createContext<IDimensionsContextProps>(Dimensions.get('window'));
|
||||
|
||||
export function withDimensions<T extends object>(Component: React.ComponentType<T> & TNavigationOptions): typeof Component {
|
||||
const DimensionsComponent = (props: T) => (
|
||||
|
@ -37,7 +37,7 @@ export const useDimensions = () => React.useContext(DimensionsContext);
|
|||
|
||||
export const useOrientation = () => {
|
||||
const { width, height } = React.useContext(DimensionsContext);
|
||||
const isPortrait = height! > width!;
|
||||
const isPortrait = height > width;
|
||||
return {
|
||||
isPortrait,
|
||||
isLandscape: !isPortrait
|
||||
|
|
|
@ -666,4 +666,4 @@ const mapStateToProps = (state: IApplicationState) => ({
|
|||
viewAllTeamsPermission: state.permissions['view-all-teams']
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(withActionSheet(withTheme(RoomMembersView)));
|
||||
export default connect(mapStateToProps)(withTheme(withActionSheet(RoomMembersView)));
|
||||
|
|
Loading…
Reference in New Issue