From e0bc64f027ee8e88476cdbbc56df19cf8929469e Mon Sep 17 00:00:00 2001 From: Dominic Go <18517029+dominicstop@users.noreply.github.com> Date: Sun, 14 Nov 2021 15:14:58 +0800 Subject: [PATCH] =?UTF-8?q?=E2=AD=90=EF=B8=8F=20Types=20-=20Added=20Types?= =?UTF-8?q?=20For=20`ContextMenuButton`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ContextMenuButton.ios.js | 214 ------------------- src/components/ContextMenuButton.js | 11 - src/components/ContextMenuButton.tsx | 261 ++++++++++++++++++++++++ 3 files changed, 261 insertions(+), 225 deletions(-) delete mode 100644 src/components/ContextMenuButton.ios.js delete mode 100644 src/components/ContextMenuButton.js create mode 100644 src/components/ContextMenuButton.tsx diff --git a/src/components/ContextMenuButton.ios.js b/src/components/ContextMenuButton.ios.js deleted file mode 100644 index cbce4ed..0000000 --- a/src/components/ContextMenuButton.ios.js +++ /dev/null @@ -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? ( - - {childItems} - - ):( - - {childItems} - - )); - - if(props.wrapNativeComponent){ - return( - - {nativeComp} - - ); - } else { - return nativeComp; - }; - }; - - render(){ - const { useActionSheetFallback } = this.props; - const useContextMenu = - (LIB_ENV.isContextMenuViewSupported && !useActionSheetFallback); - - return( - useContextMenu? this._renderContextMenuView() : - useActionSheetFallback? ( - - {this.props.children} - - ):( - - {this.props.children} - - ) - ); - }; -}; - -const styles = StyleSheet.create({ - menuButton: { - backgroundColor: 'transparent', - }, - wrappedMenuButton: { - flex: 1, - }, -}); \ No newline at end of file diff --git a/src/components/ContextMenuButton.js b/src/components/ContextMenuButton.js deleted file mode 100644 index 5cc1631..0000000 --- a/src/components/ContextMenuButton.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import { View } from 'react-native'; - - -export function ContextMenuButton(props){ - return( - - {props.children} - - ); -}; \ No newline at end of file diff --git a/src/components/ContextMenuButton.tsx b/src/components/ContextMenuButton.tsx new file mode 100644 index 0000000..ad9e5e3 --- /dev/null +++ b/src/components/ContextMenuButton.tsx @@ -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 & { + useActionSheetFallback?: boolean; + wrapNativeComponent?: boolean; +}; + +export type ContextMenuButtonProps = + ViewProps & ContextMenuButtonBaseProps; + +export type ContextMenuButtonState = { + menuVisible: boolean; +}; + +export class ContextMenuButton extends React.PureComponent { + + 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? ( + + {childItems} + + ):( + + {childItems} + + )); + + if(props.wrapNativeComponent){ + return( + + {nativeComponent} + + ); + } else { + return nativeComponent; + }; + + } else if(shouldUseActionSheetFallback){ + return ( + + {this.props.children} + + ); + + } else { + return ( + + {this.props.children} + + ); + }; + }; +}; + +const styles = StyleSheet.create({ + menuButton: { + backgroundColor: 'transparent', + }, + wrappedMenuButton: { + flex: 1, + }, +}); \ No newline at end of file