mirror of https://github.com/jitsi/jitsi-meet
Merge pull request #2932 from saghul/refactor-bottomsheet
[RN] Refactor SimpleBottomSheetpull/2936/head jitsi-meet_3028
commit
ab7e572162
@ -0,0 +1,84 @@ |
||||
// @flow
|
||||
|
||||
import React, { Component, type Node } from 'react'; |
||||
import { Modal, TouchableWithoutFeedback, View } from 'react-native'; |
||||
|
||||
import { bottomSheetStyles as styles } from './styles'; |
||||
|
||||
/** |
||||
* The type of {@code BottomSheet}'s React {@code Component} prop types. |
||||
*/ |
||||
type Props = { |
||||
|
||||
/** |
||||
* The children to be displayed within this component. |
||||
*/ |
||||
children: Node, |
||||
|
||||
/** |
||||
* Handler for the cancel event, which happens when the user dismisses |
||||
* the sheet. |
||||
*/ |
||||
onCancel: ?Function |
||||
}; |
||||
|
||||
/** |
||||
* A component emulating Android's BottomSheet. For all intents and purposes, |
||||
* this component has been designed to work and behave as a {@code Dialog}. |
||||
*/ |
||||
export default class BottomSheet extends Component<Props> { |
||||
/** |
||||
* Initializes a new {@code BottomSheet} instance. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
constructor(props: Props) { |
||||
super(props); |
||||
|
||||
this._onCancel = this._onCancel.bind(this); |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
return [ |
||||
<View |
||||
key = 'overlay' |
||||
style = { styles.overlay } />, |
||||
<Modal |
||||
animationType = { 'slide' } |
||||
key = 'modal' |
||||
onRequestClose = { this._onCancel } |
||||
transparent = { true } |
||||
visible = { true }> |
||||
<View style = { styles.container }> |
||||
<TouchableWithoutFeedback |
||||
onPress = { this._onCancel } > |
||||
<View style = { styles.backdrop } /> |
||||
</TouchableWithoutFeedback> |
||||
<View style = { styles.sheet }> |
||||
{ this.props.children } |
||||
</View> |
||||
</View> |
||||
</Modal> |
||||
]; |
||||
} |
||||
|
||||
_onCancel: () => void; |
||||
|
||||
/** |
||||
* Cancels the dialog by calling the onCancel prop callback. |
||||
* |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_onCancel() { |
||||
const { onCancel } = this.props; |
||||
|
||||
onCancel && onCancel(); |
||||
} |
||||
} |
@ -1,206 +0,0 @@ |
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react'; |
||||
import { |
||||
Modal, |
||||
Text, |
||||
TouchableHighlight, |
||||
TouchableWithoutFeedback, |
||||
View |
||||
} from 'react-native'; |
||||
import { connect } from 'react-redux'; |
||||
|
||||
import { Icon } from '../../font-icons'; |
||||
|
||||
import { simpleBottomSheet as styles } from './styles'; |
||||
|
||||
/** |
||||
* Underlay color for the buttons on the sheet. |
||||
* |
||||
* @type {string} |
||||
*/ |
||||
const BUTTON_UNDERLAY_COLOR = '#eee'; |
||||
|
||||
type Option = { |
||||
|
||||
/** |
||||
* Name of the icon which will be rendered on the right. |
||||
*/ |
||||
iconName: string, |
||||
|
||||
/** |
||||
* True if the element is selected (will be highlighted in blue), |
||||
* false otherwise. |
||||
*/ |
||||
selected: boolean, |
||||
|
||||
/** |
||||
* Text which will be rendered in the row. |
||||
*/ |
||||
text: string |
||||
}; |
||||
|
||||
|
||||
/** |
||||
* The type of {@code SimpleBottomSheet}'s React {@code Component} prop types. |
||||
*/ |
||||
type Props = { |
||||
|
||||
/** |
||||
* Handler for the cancel event, which happens when the user dismisses |
||||
* the sheet. |
||||
*/ |
||||
onCancel: Function, |
||||
|
||||
/** |
||||
* Handler for the event when an option has been selected in the sheet. |
||||
*/ |
||||
onSubmit: Function, |
||||
|
||||
/** |
||||
* Array of options which will be rendered as rows. |
||||
*/ |
||||
options: Array<Option> |
||||
}; |
||||
|
||||
/** |
||||
* A component emulating Android's BottomSheet, in a simplified form. |
||||
* It supports text options with an icon, which the user can tap. The style has |
||||
* been implemented following the Material Design guidelines for bottom |
||||
* sheets: https://material.io/guidelines/components/bottom-sheets.html
|
||||
* |
||||
* For all intents and purposes, this component has been designed to work and |
||||
* behave as a {@code Dialog}. |
||||
*/ |
||||
class SimpleBottomSheet extends Component<Props> { |
||||
/** |
||||
* Initializes a new {@code SimpleBottomSheet} instance. |
||||
* |
||||
* @param {Object} props - The read-only React {@code Component} props with |
||||
* which the new instance is to be initialized. |
||||
*/ |
||||
constructor(props) { |
||||
super(props); |
||||
|
||||
this._onButtonPress = this._onButtonPress.bind(this); |
||||
this._onCancel = this._onCancel.bind(this); |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
return ( |
||||
<Modal |
||||
animationType = { 'slide' } |
||||
onRequestClose = { this._onCancel } |
||||
transparent = { true } |
||||
visible = { true }> |
||||
<View style = { styles.container }> |
||||
<TouchableWithoutFeedback |
||||
onPress = { this._onCancel } > |
||||
<View style = { styles.overlay } /> |
||||
</TouchableWithoutFeedback> |
||||
<View style = { styles.sheet }> |
||||
<View style = { styles.rowsWrapper }> |
||||
{ this._renderOptions() } |
||||
</View> |
||||
</View> |
||||
</View> |
||||
</Modal> |
||||
); |
||||
} |
||||
|
||||
_onButtonPress: (?Object) => void; |
||||
|
||||
/** |
||||
* Handle pressing of one of the options. The sheet will be hidden and the |
||||
* onSubmit prop will be called with the selected option. |
||||
* |
||||
* @param {Object} option - The option which the user selected. |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_onButtonPress(option) { |
||||
const { onSubmit } = this.props; |
||||
|
||||
onSubmit && onSubmit(option); |
||||
} |
||||
|
||||
_onCancel: () => void; |
||||
|
||||
/** |
||||
* Cancels the dialog by calling the onCancel prop callback. |
||||
* |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_onCancel() { |
||||
const { onCancel } = this.props; |
||||
|
||||
onCancel && onCancel(); |
||||
} |
||||
|
||||
/** |
||||
* Renders sheet rows based on the options prop. |
||||
* |
||||
* @private |
||||
* @returns {Array} - Array of rows to be rendered in the sheet. |
||||
*/ |
||||
_renderOptions() { |
||||
return this.props.options.map( |
||||
(option, index) => this._renderRow(option, index)); |
||||
} |
||||
|
||||
/** |
||||
* Renders a single row of the sheet. |
||||
* |
||||
* @param {Object} option - Single option which needs to be rendered. |
||||
* @param {int} index - Option index, used as a key for React. |
||||
* @private |
||||
* @returns {ReactElement} - A row element with an icon and text. |
||||
*/ |
||||
_renderRow(option, index) { |
||||
const { iconName, selected, text } = option; |
||||
const selectedStyle = selected ? styles.rowSelectedText : {}; |
||||
|
||||
return ( |
||||
<TouchableHighlight |
||||
key = { index } |
||||
|
||||
// TODO The following disables an eslint error alerting about a
|
||||
// known potential/theoretical performance pernalty:
|
||||
//
|
||||
// A bind call or arrow function in a JSX prop will create a
|
||||
// brand new function on every single render. This is bad for
|
||||
// performance, as it will result in the garbage collector being
|
||||
// invoked way more than is necessary. It may also cause
|
||||
// unnecessary re-renders if a brand new function is passed as a
|
||||
// prop to a component that uses reference equality check on the
|
||||
// prop to determine if it should update.
|
||||
//
|
||||
// I'm not addressing the potential/theoretical performance
|
||||
// penalty at the time of this writing because it doesn't seem
|
||||
// to me that it's a practical performance penalty in the case.
|
||||
//
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onPress = { this._onButtonPress.bind(this, option) } |
||||
underlayColor = { BUTTON_UNDERLAY_COLOR } > |
||||
<View style = { styles.row } > |
||||
<Icon |
||||
name = { iconName } |
||||
style = { [ styles.rowIcon, selectedStyle ] } /> |
||||
<View style = { styles.rowPadding } /> |
||||
<Text style = { [ styles.rowText, selectedStyle ] } > |
||||
{ text } |
||||
</Text> |
||||
</View> |
||||
</TouchableHighlight> |
||||
); |
||||
} |
||||
} |
||||
|
||||
export default connect()(SimpleBottomSheet); |
@ -1,4 +1,4 @@ |
||||
export { default as BottomSheet } from './BottomSheet'; |
||||
export { default as DialogContainer } from './DialogContainer'; |
||||
export { default as Dialog } from './Dialog'; |
||||
export { default as SimpleBottomSheet } from './SimpleBottomSheet'; |
||||
export { default as StatelessDialog } from './StatelessDialog'; |
||||
|
@ -0,0 +1,50 @@ |
||||
// @flow
|
||||
|
||||
import { ColorPalette, createStyleSheet } from '../../../base/styles'; |
||||
|
||||
/** |
||||
* Underlay color for the buttons on the sheet. |
||||
* |
||||
* @type {string} |
||||
*/ |
||||
export const UNDERLAY_COLOR = '#eee'; |
||||
|
||||
/** |
||||
* The React {@code Component} styles of {@code AudioRoutePickerDialog}. |
||||
* |
||||
* It uses a {@code BottomSheet} and these have been implemented as per the |
||||
* Material Design guidelines: |
||||
* {@link https://material.io/guidelines/components/bottom-sheets.html}.
|
||||
*/ |
||||
export default createStyleSheet({ |
||||
/** |
||||
* Base style for each row. |
||||
*/ |
||||
deviceRow: { |
||||
alignItems: 'center', |
||||
flexDirection: 'row', |
||||
height: 48 |
||||
}, |
||||
|
||||
/** |
||||
* Style for the {@code Icon} element in a row. |
||||
*/ |
||||
deviceIcon: { |
||||
fontSize: 24 |
||||
}, |
||||
|
||||
/** |
||||
* Style for the {@code Text} element in a row. |
||||
*/ |
||||
deviceText: { |
||||
fontSize: 16, |
||||
marginLeft: 32 |
||||
}, |
||||
|
||||
/** |
||||
* Style for a row which is marked as selected. |
||||
*/ |
||||
selectedText: { |
||||
color: ColorPalette.blue |
||||
} |
||||
}); |
Loading…
Reference in new issue