⭐️ Types - Added Types For ContextMenuButton
This commit is contained in:
parent
0a68d6dd00
commit
e0bc64f027
@ -1,214 +0,0 @@
|
||||
import React from 'react';
|
||||
import { StyleSheet, View, TouchableOpacity } from 'react-native';
|
||||
import Proptypes from 'prop-types';
|
||||
|
||||
import { RNIContextMenuButton } from '../native_components/RNIContextMenuButton';
|
||||
|
||||
import { ActionSheetFallback } from '../functions/ActionSheetFallback';
|
||||
import { ContextMenuView } from './ContextMenuView';
|
||||
|
||||
import { LIB_ENV } from '../constants/LibEnv';
|
||||
|
||||
const NATIVE_PROP_KEYS = {
|
||||
// props: values --------
|
||||
menuConfig: 'menuConfig',
|
||||
// props: flags ---------------------------
|
||||
enableContextMenu : 'enableContextMenu' ,
|
||||
isMenuPrimaryAction: 'isMenuPrimaryAction',
|
||||
// props: events --------------------
|
||||
onMenuWillShow : 'onMenuWillShow' ,
|
||||
onMenuWillHide : 'onMenuWillHide' ,
|
||||
onMenuWillCancel: 'onMenuWillCancel',
|
||||
onMenuDidShow : 'onMenuDidShow' ,
|
||||
onMenuDidHide : 'onMenuDidHide' ,
|
||||
onMenuDidCancel : 'onMenuDidCancel' ,
|
||||
// props: onPress events ----------
|
||||
onPressMenuItem: 'onPressMenuItem',
|
||||
};
|
||||
|
||||
|
||||
export class ContextMenuButton extends React.PureComponent {
|
||||
static proptypes = {
|
||||
menuConfig: Proptypes.object,
|
||||
// flags ------------------------------
|
||||
enableContextMenu : Proptypes.bool,
|
||||
isMenuPrimaryAction : Proptypes.bool,
|
||||
useActionSheetFallback: Proptypes.bool,
|
||||
wrapNativeComponent : Proptypes.bool,
|
||||
// events -----------------------
|
||||
onMenuWillShow : Proptypes.func,
|
||||
onMenuWillHide : Proptypes.func,
|
||||
onMenuWillCancel: Proptypes.func,
|
||||
onMenuDidShow : Proptypes.func,
|
||||
onMenuDidHide : Proptypes.func,
|
||||
onMenuDidCancel : Proptypes.func,
|
||||
// onPress events --------------
|
||||
onPressMenuItem: Proptypes.func,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
enableContextMenu : true,
|
||||
wrapNativeComponent : true,
|
||||
useActionSheetFallback: !LIB_ENV.isContextMenuViewSupported,
|
||||
};
|
||||
|
||||
constructor(props){
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
menuVisible: false,
|
||||
};
|
||||
};
|
||||
|
||||
getProps(){
|
||||
const otherProps = { ...this.props };
|
||||
const nativeProps = {};
|
||||
|
||||
const nativeKeys = Object.keys(NATIVE_PROP_KEYS);
|
||||
|
||||
for (const key of nativeKeys) {
|
||||
nativeProps[key] = otherProps[key];
|
||||
delete otherProps[key];
|
||||
};
|
||||
|
||||
return { nativeProps, ...otherProps };
|
||||
};
|
||||
|
||||
//#region - Event Handlers
|
||||
_handleOnLongPress = async () => {
|
||||
const { menuConfig, ...props } = this.props;
|
||||
const item = await ActionSheetFallback.show(menuConfig);
|
||||
|
||||
if(item == null){
|
||||
// cancelled pressed
|
||||
props.onMenuDidCancel?.();
|
||||
|
||||
} else {
|
||||
props.onPressMenuItem?.({nativeEvent: {...item}});
|
||||
};
|
||||
};
|
||||
|
||||
_handleOnMenuWillShow = (event) => {
|
||||
const { onMenuWillShow } = this.props;
|
||||
onMenuWillShow?.(event);
|
||||
|
||||
this.setState({menuVisible: true});
|
||||
};
|
||||
|
||||
_handleOnMenuWillHide = (event) => {
|
||||
const { onMenuWillHide } = this.props;
|
||||
onMenuWillHide?.(event);
|
||||
|
||||
this.setState({menuVisible: false});
|
||||
};
|
||||
|
||||
_handleOnMenuWillCancel = (event) => {
|
||||
const { onMenuWillCancel } = this.props;
|
||||
onMenuWillCancel?.(event);
|
||||
};
|
||||
|
||||
_handleOnMenuDidShow = (event) => {
|
||||
const { onMenuDidShow } = this.props;
|
||||
onMenuDidShow?.(event);
|
||||
};
|
||||
|
||||
_handleOnMenuDidHide = (event) => {
|
||||
const { onMenuDidHide } = this.props;
|
||||
onMenuDidHide?.(event);
|
||||
};
|
||||
|
||||
_handleOnMenuDidCancel = (event) => {
|
||||
const { onMenuDidCancel } = this.props;
|
||||
onMenuDidCancel?.(event);
|
||||
};
|
||||
|
||||
_handleOnPressMenuItem = (event) => {
|
||||
this.props.onPressMenuItem?.(event);
|
||||
};
|
||||
//#endregion
|
||||
|
||||
_renderContextMenuView(){
|
||||
const { nativeProps, style, children, ...props } = this.getProps();
|
||||
const { menuVisible } = this.state;
|
||||
|
||||
const nativeCompProps = {
|
||||
...nativeProps,
|
||||
// Native Props: Events ------------------------------------------
|
||||
[NATIVE_PROP_KEYS.onMenuWillShow ]: this._handleOnMenuWillShow ,
|
||||
[NATIVE_PROP_KEYS.onMenuWillHide ]: this._handleOnMenuWillHide ,
|
||||
[NATIVE_PROP_KEYS.onMenuWillCancel]: this._handleOnMenuWillCancel,
|
||||
[NATIVE_PROP_KEYS.onMenuDidShow ]: this._handleOnMenuDidShow ,
|
||||
[NATIVE_PROP_KEYS.onMenuDidHide ]: this._handleOnMenuDidHide ,
|
||||
[NATIVE_PROP_KEYS.onMenuDidCancel ]: this._handleOnMenuDidCancel ,
|
||||
// Native Props: OnPress Events --------------------------------
|
||||
[NATIVE_PROP_KEYS.onPressMenuItem]: this._handleOnPressMenuItem,
|
||||
};
|
||||
|
||||
const childItems = React.Children.map(children, child =>
|
||||
React.cloneElement(child, {menuVisible})
|
||||
);
|
||||
|
||||
const nativeComp = (LIB_ENV.isContextMenuButtonSupported? (
|
||||
<RNIContextMenuButton
|
||||
{...nativeCompProps}
|
||||
style={(props.wrapNativeComponent
|
||||
? styles.wrappedMenuButton
|
||||
: [styles.menuButton, style]
|
||||
)}
|
||||
>
|
||||
{childItems}
|
||||
</RNIContextMenuButton>
|
||||
):(
|
||||
<ContextMenuView
|
||||
{...nativeCompProps}
|
||||
>
|
||||
{childItems}
|
||||
</ContextMenuView>
|
||||
));
|
||||
|
||||
if(props.wrapNativeComponent){
|
||||
return(
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.8}
|
||||
{...{style, ...props}}
|
||||
>
|
||||
{nativeComp}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
} else {
|
||||
return nativeComp;
|
||||
};
|
||||
};
|
||||
|
||||
render(){
|
||||
const { useActionSheetFallback } = this.props;
|
||||
const useContextMenu =
|
||||
(LIB_ENV.isContextMenuViewSupported && !useActionSheetFallback);
|
||||
|
||||
return(
|
||||
useContextMenu? this._renderContextMenuView() :
|
||||
useActionSheetFallback? (
|
||||
<TouchableOpacity
|
||||
onLongPress={this._handleOnLongPress}
|
||||
activeOpacity={0.8}
|
||||
{...this.props}
|
||||
>
|
||||
{this.props.children}
|
||||
</TouchableOpacity>
|
||||
):(
|
||||
<View {...this.props}>
|
||||
{this.props.children}
|
||||
</View>
|
||||
)
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
menuButton: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
wrappedMenuButton: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
@ -1,11 +0,0 @@
|
||||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
|
||||
|
||||
export function ContextMenuButton(props){
|
||||
return(
|
||||
<View {...props}>
|
||||
{props.children}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
261
src/components/ContextMenuButton.tsx
Normal file
261
src/components/ContextMenuButton.tsx
Normal file
@ -0,0 +1,261 @@
|
||||
import React from 'react';
|
||||
import { StyleSheet, View, TouchableOpacity, ViewProps } from 'react-native';
|
||||
|
||||
import { RNIContextMenuButton, RNIContextMenuButtonBaseProps } from '../native_components/RNIContextMenuButton';
|
||||
|
||||
import type { OnMenuWillShowEvent, OnMenuWillHideEvent, OnMenuDidShowEvent, OnMenuDidHideEvent, OnMenuWillCancelEvent, OnMenuDidCancelEvent, OnPressMenuItemEvent } from '../types/MenuEvents';
|
||||
|
||||
// @ts-ignore - TODO
|
||||
import { ActionSheetFallback } from '../functions/ActionSheetFallback';
|
||||
import { ContextMenuView } from './ContextMenuView';
|
||||
|
||||
import { LIB_ENV, IS_PLATFORM_IOS } from '../constants/LibEnv';
|
||||
|
||||
|
||||
export type ContextMenuButtonBaseProps = Pick<RNIContextMenuButtonBaseProps,
|
||||
| 'enableContextMenu'
|
||||
| 'isMenuPrimaryAction'
|
||||
| 'menuConfig'
|
||||
// Lifecycle Events
|
||||
| 'onMenuWillShow'
|
||||
| 'onMenuWillHide'
|
||||
| 'onMenuWillCancel'
|
||||
| 'onMenuDidShow'
|
||||
| 'onMenuDidHide'
|
||||
| 'onMenuDidCancel'
|
||||
// `OnPress` Events
|
||||
| 'onPressMenuItem'
|
||||
> & {
|
||||
useActionSheetFallback?: boolean;
|
||||
wrapNativeComponent?: boolean;
|
||||
};
|
||||
|
||||
export type ContextMenuButtonProps =
|
||||
ViewProps & ContextMenuButtonBaseProps;
|
||||
|
||||
export type ContextMenuButtonState = {
|
||||
menuVisible: boolean;
|
||||
};
|
||||
|
||||
export class ContextMenuButton extends React.PureComponent<ContextMenuButtonProps, ContextMenuButtonState> {
|
||||
|
||||
constructor(props: ContextMenuButtonProps){
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
menuVisible: false,
|
||||
};
|
||||
};
|
||||
|
||||
getProps = () => {
|
||||
const {
|
||||
menuConfig,
|
||||
enableContextMenu,
|
||||
isMenuPrimaryAction,
|
||||
useActionSheetFallback,
|
||||
wrapNativeComponent,
|
||||
onMenuWillShow,
|
||||
onMenuWillHide,
|
||||
onMenuWillCancel,
|
||||
onMenuDidShow,
|
||||
onMenuDidHide,
|
||||
onMenuDidCancel,
|
||||
onPressMenuItem,
|
||||
...viewProps
|
||||
} = this.props;
|
||||
|
||||
return {
|
||||
// A. Provide default values to props...
|
||||
enableContextMenu: (
|
||||
enableContextMenu ?? true
|
||||
),
|
||||
wrapNativeComponent: (
|
||||
wrapNativeComponent ?? true
|
||||
),
|
||||
useActionSheetFallback: (
|
||||
useActionSheetFallback ?? !LIB_ENV.isContextMenuViewSupported
|
||||
),
|
||||
|
||||
// B. Pass down props...
|
||||
menuConfig,
|
||||
isMenuPrimaryAction,
|
||||
onMenuWillShow,
|
||||
onMenuWillHide,
|
||||
onMenuWillCancel,
|
||||
onMenuDidShow,
|
||||
onMenuDidHide,
|
||||
onMenuDidCancel,
|
||||
onPressMenuItem,
|
||||
// C. Move all the default view-related
|
||||
// props here...
|
||||
viewProps
|
||||
};
|
||||
};
|
||||
|
||||
//#region - Event Handlers
|
||||
_handleOnLongPress = async () => {
|
||||
const props = this.props;
|
||||
|
||||
const selectedItem =
|
||||
await ActionSheetFallback.show(props.menuConfig);
|
||||
|
||||
if(selectedItem == null){
|
||||
// A. cancelled pressed
|
||||
props.onMenuDidCancel?.({
|
||||
isUsingActionSheetFallback: true
|
||||
});
|
||||
|
||||
} else {
|
||||
// B. an item was selected
|
||||
props.onPressMenuItem?.({
|
||||
isUsingActionSheetFallback: true,
|
||||
nativeEvent: {
|
||||
...selectedItem,
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
_handleOnMenuWillShow: OnMenuWillShowEvent = (event) => {
|
||||
this.props.onMenuWillShow?.(event);
|
||||
event.stopPropagation();
|
||||
|
||||
this.setState({menuVisible: true});
|
||||
};
|
||||
|
||||
_handleOnMenuWillHide: OnMenuWillHideEvent = (event) => {
|
||||
this.props.onMenuWillHide?.(event);
|
||||
event.stopPropagation();
|
||||
|
||||
this.setState({menuVisible: false});
|
||||
};
|
||||
|
||||
_handleOnMenuWillCancel: OnMenuWillCancelEvent = (event) => {
|
||||
this.props.onMenuWillCancel?.(event);
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
_handleOnMenuDidShow: OnMenuDidShowEvent = (event) => {
|
||||
this.props.onMenuDidShow?.(event);
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
_handleOnMenuDidHide: OnMenuDidHideEvent = (event) => {
|
||||
this.props.onMenuDidHide?.(event);
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
_handleOnMenuDidCancel: OnMenuDidCancelEvent = (event) => {
|
||||
this.props.onMenuDidCancel?.(event);
|
||||
|
||||
// guard: event is a native event
|
||||
if(event.isUsingActionSheetFallback) return;
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
_handleOnPressMenuItem: OnPressMenuItemEvent = (event) => {
|
||||
this.props.onPressMenuItem?.(event);
|
||||
|
||||
// guard: event is a native event
|
||||
if(event.isUsingActionSheetFallback) return;
|
||||
event.stopPropagation();
|
||||
};
|
||||
//#endregion
|
||||
|
||||
render(){
|
||||
const props = this.getProps();
|
||||
const { menuVisible } = this.state;
|
||||
|
||||
const isNativeComponentSupported = (
|
||||
LIB_ENV.isContextMenuButtonSupported &&
|
||||
!props.useActionSheetFallback
|
||||
);
|
||||
|
||||
const shouldUseActionSheetFallback = (
|
||||
IS_PLATFORM_IOS && props.useActionSheetFallback
|
||||
);
|
||||
|
||||
const nativeComponentProps: RNIContextMenuButtonBaseProps = {
|
||||
menuConfig: props.menuConfig,
|
||||
enableContextMenu: props.enableContextMenu,
|
||||
isMenuPrimaryAction: props.isMenuPrimaryAction,
|
||||
|
||||
// event handlers
|
||||
onMenuWillShow : this._handleOnMenuWillShow ,
|
||||
onMenuWillHide : this._handleOnMenuWillHide ,
|
||||
onMenuDidShow : this._handleOnMenuDidShow ,
|
||||
onMenuDidHide : this._handleOnMenuDidHide ,
|
||||
onMenuDidCancel : this._handleOnMenuDidCancel ,
|
||||
onMenuWillCancel: this._handleOnMenuWillCancel,
|
||||
onPressMenuItem : this._handleOnPressMenuItem ,
|
||||
};
|
||||
|
||||
if(isNativeComponentSupported){
|
||||
const childItems = React.Children.map(this.props.children, child =>
|
||||
//@ts-ignore
|
||||
React.cloneElement(child, {menuVisible})
|
||||
);
|
||||
|
||||
const nativeComponent = (LIB_ENV.isContextMenuButtonSupported? (
|
||||
<RNIContextMenuButton
|
||||
{...(props.wrapNativeComponent && props.viewProps)}
|
||||
{...nativeComponentProps}
|
||||
// override style prop
|
||||
style={(props.wrapNativeComponent
|
||||
? styles.wrappedMenuButton
|
||||
: [styles.menuButton, props.viewProps.style]
|
||||
)}
|
||||
>
|
||||
{childItems}
|
||||
</RNIContextMenuButton>
|
||||
):(
|
||||
<ContextMenuView
|
||||
{...props.viewProps}
|
||||
{...nativeComponentProps}
|
||||
>
|
||||
{childItems}
|
||||
</ContextMenuView>
|
||||
));
|
||||
|
||||
if(props.wrapNativeComponent){
|
||||
return(
|
||||
<TouchableOpacity
|
||||
{...props.viewProps}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
{nativeComponent}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
} else {
|
||||
return nativeComponent;
|
||||
};
|
||||
|
||||
} else if(shouldUseActionSheetFallback){
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onLongPress={this._handleOnLongPress}
|
||||
activeOpacity={0.8}
|
||||
{...props.viewProps}
|
||||
>
|
||||
{this.props.children}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
||||
} else {
|
||||
return (
|
||||
<View {...props.viewProps}>
|
||||
{this.props.children}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
menuButton: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
wrappedMenuButton: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user