mirror of https://github.com/jitsi/jitsi-meet
parent
2a5adfc601
commit
13212a5980
@ -1,178 +0,0 @@ |
||||
// @flow
|
||||
|
||||
import React, { PureComponent, type Node } from 'react'; |
||||
import { Animated, TouchableWithoutFeedback, View } from 'react-native'; |
||||
|
||||
import styles, { SIDEBAR_WIDTH } from './styles'; |
||||
|
||||
/** |
||||
* The type of the React {@code Component} props of {@link SideBar}. |
||||
*/ |
||||
type Props = { |
||||
|
||||
/** |
||||
* The children of {@code SideBar}. |
||||
*/ |
||||
children: Node, |
||||
|
||||
/** |
||||
* Callback to notify the containing {@code Component} that the sidebar is |
||||
* closing. |
||||
*/ |
||||
onHide: Function, |
||||
|
||||
/** |
||||
* Whether the menu (of the {@code SideBar}?) is displayed/rendered/shown. |
||||
*/ |
||||
show: boolean |
||||
}; |
||||
|
||||
/** |
||||
* The type of the React {@code Component} state of {@link SideBar}. |
||||
*/ |
||||
type State = { |
||||
|
||||
/** |
||||
* Whether the side overlay should be displayed/rendered/shown. |
||||
*/ |
||||
showOverlay: boolean, |
||||
|
||||
/** |
||||
* The native animation object. |
||||
*/ |
||||
sliderAnimation: Animated.Value |
||||
}; |
||||
|
||||
/** |
||||
* A generic animated side bar to be used for left-side, hamburger-style menus. |
||||
*/ |
||||
export default class SideBar extends PureComponent<Props, State> { |
||||
/** |
||||
* Implements React's {@link Component#getDerivedStateFromProps()}. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
static getDerivedStateFromProps(props: Props, prevState: State) { |
||||
return { |
||||
showOverlay: props.show || prevState.showOverlay |
||||
}; |
||||
} |
||||
|
||||
/** |
||||
* Initializes a new {@code SideBar} instance. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
constructor(props: Props) { |
||||
super(props); |
||||
|
||||
this.state = { |
||||
showOverlay: false, |
||||
sliderAnimation: new Animated.Value(0) |
||||
}; |
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
this._onHideMenu = this._onHideMenu.bind(this); |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#componentDidMount()}. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
componentDidMount() { |
||||
this._setShow(this.props.show); |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#componentDidUpdate()}. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
componentDidUpdate() { |
||||
this._setShow(this.props.show); |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
render() { |
||||
return ( |
||||
<View |
||||
pointerEvents = 'box-none' |
||||
style = { styles.sideMenuContainer } > |
||||
{ |
||||
this.state.showOverlay |
||||
&& <TouchableWithoutFeedback |
||||
onPress = { this._onHideMenu } > |
||||
<View style = { styles.sideMenuShadow } /> |
||||
</TouchableWithoutFeedback> |
||||
} |
||||
<Animated.View style = { this._getContentStyle() }> |
||||
{ this.props.children } |
||||
</Animated.View> |
||||
</View> |
||||
); |
||||
} |
||||
|
||||
_getContentStyle: () => Array<Object>; |
||||
|
||||
/** |
||||
* Assembles a style array for the sidebar content. |
||||
* |
||||
* @private |
||||
* @returns {Array<Object>} |
||||
*/ |
||||
_getContentStyle() { |
||||
return [ |
||||
styles.sideMenuContent, |
||||
{ transform: [ { translateX: this.state.sliderAnimation } ] } |
||||
]; |
||||
} |
||||
|
||||
_onHideMenu: () => void; |
||||
|
||||
/** |
||||
* Hides the side menu. |
||||
* |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_onHideMenu() { |
||||
this._setShow(false); |
||||
|
||||
const { onHide } = this.props; |
||||
|
||||
onHide && onHide(); |
||||
} |
||||
|
||||
_setShow: (boolean) => void; |
||||
|
||||
/** |
||||
* Shows/hides the side menu. |
||||
* |
||||
* @param {boolean} show - If the side menu is to be made visible, |
||||
* {@code true}; otherwise, {@code false}. |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_setShow(show) { |
||||
Animated |
||||
.timing( |
||||
/* value */ this.state.sliderAnimation, |
||||
/* config */ { |
||||
toValue: show ? SIDEBAR_WIDTH : 0, |
||||
useNativeDriver: true |
||||
}) |
||||
.start(({ finished }) => { |
||||
finished && !show && this.setState({ showOverlay: false }); |
||||
|
||||
// XXX Technically, the arrow function can further be simplified
|
||||
// by removing the {} and returning the boolean expression
|
||||
// above. Practically and unfortunately though, Flow freaks out
|
||||
// and states that Animated.timing doesn't exist!?
|
||||
}); |
||||
} |
||||
} |
@ -0,0 +1,265 @@ |
||||
// @flow
|
||||
|
||||
import React, { PureComponent, type Node } from 'react'; |
||||
import { |
||||
Animated, |
||||
Dimensions, |
||||
TouchableWithoutFeedback, |
||||
View |
||||
} from 'react-native'; |
||||
|
||||
import { type StyleType } from '../../../styles'; |
||||
|
||||
import styles from './slidingviewstyles'; |
||||
|
||||
/** |
||||
* The type of the React {@code Component} props of {@link SlidingView}. |
||||
*/ |
||||
type Props = { |
||||
|
||||
/** |
||||
* The children of {@code SlidingView}. |
||||
*/ |
||||
children: Node, |
||||
|
||||
/** |
||||
* Callback to notify the containing {@code Component} that the view is |
||||
* closing. |
||||
*/ |
||||
onHide: Function, |
||||
|
||||
/** |
||||
* Position of the SlidingView: 'left', 'right', 'top', 'bottom'. |
||||
* later). |
||||
*/ |
||||
position: string, |
||||
|
||||
/** |
||||
* Whether the {@code SlidingView} is to be displayed/rendered/shown or not. |
||||
*/ |
||||
show: boolean, |
||||
|
||||
/** |
||||
* Style of the animated view. |
||||
*/ |
||||
style: StyleType |
||||
}; |
||||
|
||||
/** |
||||
* The type of the React {@code Component} state of {@link SlidingView}. |
||||
*/ |
||||
type State = { |
||||
|
||||
/** |
||||
* Whether the sliding overlay should be displayed/rendered/shown. |
||||
*/ |
||||
showOverlay: boolean, |
||||
|
||||
/** |
||||
* The native animation object. |
||||
*/ |
||||
sliderAnimation: Animated.Value, |
||||
|
||||
/** |
||||
* Offset to move the view out of the screen. |
||||
*/ |
||||
positionOffset: number |
||||
}; |
||||
|
||||
/** |
||||
* A generic animated slider view to be used for animated menus. |
||||
*/ |
||||
export default class SlidingView extends PureComponent<Props, State> { |
||||
/** |
||||
* True if the component is mounted. |
||||
*/ |
||||
_mounted: boolean; |
||||
|
||||
/** |
||||
* Implements React's {@link Component#getDerivedStateFromProps()}. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
static getDerivedStateFromProps(props: Props, prevState: State) { |
||||
return { |
||||
showOverlay: props.show || prevState.showOverlay |
||||
}; |
||||
} |
||||
|
||||
/** |
||||
* Initializes a new {@code SlidingView} instance. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
constructor(props: Props) { |
||||
super(props); |
||||
|
||||
const { height, width } = Dimensions.get('window'); |
||||
const { position } = props; |
||||
|
||||
let positionOffset = height; |
||||
|
||||
if (position === 'left' || position === 'right') { |
||||
positionOffset = width; |
||||
} |
||||
|
||||
this.state = { |
||||
showOverlay: false, |
||||
sliderAnimation: new Animated.Value(0), |
||||
positionOffset |
||||
}; |
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
this._onHideMenu = this._onHideMenu.bind(this); |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#componentDidMount()}. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
componentDidMount() { |
||||
this._mounted = true; |
||||
this._setShow(this.props.show); |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#componentDidUpdate()}. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
componentDidUpdate() { |
||||
this._setShow(this.props.show); |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#componentWillUnmount()}. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
componentWillUnmount() { |
||||
this._mounted = false; |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
render() { |
||||
const { showOverlay } = this.state; |
||||
|
||||
if (!showOverlay) { |
||||
return null; |
||||
} |
||||
|
||||
return ( |
||||
<View |
||||
pointerEvents = 'box-none' |
||||
style = { styles.sliderViewContainer } > |
||||
<TouchableWithoutFeedback |
||||
onPress = { this._onHideMenu } > |
||||
<View style = { styles.sliderViewShadow } /> |
||||
</TouchableWithoutFeedback> |
||||
<Animated.View |
||||
style = { this._getContentStyle() }> |
||||
{ this.props.children } |
||||
</Animated.View> |
||||
</View> |
||||
); |
||||
} |
||||
|
||||
_getContentStyle: () => Array<Object>; |
||||
|
||||
/** |
||||
* Assembles a style array for the SlideView content. |
||||
* |
||||
* @private |
||||
* @returns {Array<Object>} |
||||
*/ |
||||
_getContentStyle() { |
||||
const style = { |
||||
...this.props.style, |
||||
...styles.sliderViewContent |
||||
}; |
||||
const { positionOffset } = this.state; |
||||
|
||||
switch (this.props.position) { |
||||
case 'bottom': |
||||
Object.assign(style, { |
||||
bottom: -positionOffset, |
||||
left: 0, |
||||
right: 0, |
||||
top: positionOffset |
||||
}, { |
||||
transform: [ { translateY: this.state.sliderAnimation } ] |
||||
}); |
||||
break; |
||||
case 'left': |
||||
Object.assign(style, { |
||||
bottom: 0, |
||||
left: -positionOffset, |
||||
right: positionOffset, |
||||
top: 0 |
||||
}, { |
||||
transform: [ { translateX: this.state.sliderAnimation } ] |
||||
}); |
||||
break; |
||||
} |
||||
|
||||
return style; |
||||
} |
||||
|
||||
_onHideMenu: () => void; |
||||
|
||||
/** |
||||
* Hides the slider. |
||||
* |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_onHideMenu() { |
||||
this._setShow(false); |
||||
|
||||
const { onHide } = this.props; |
||||
|
||||
onHide && onHide(); |
||||
} |
||||
|
||||
_setShow: (boolean) => void; |
||||
|
||||
/** |
||||
* Shows/hides the slider menu. |
||||
* |
||||
* @param {boolean} show - If the slider view is to be made visible, |
||||
* {@code true}; otherwise, {@code false}. |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_setShow(show) { |
||||
if (!this._mounted) { |
||||
return; |
||||
} |
||||
|
||||
const { positionOffset } = this.state; |
||||
const { position } = this.props; |
||||
let toValue = positionOffset; |
||||
|
||||
if (position === 'bottom' || position === 'right') { |
||||
toValue = -positionOffset; |
||||
} |
||||
|
||||
Animated |
||||
.timing( |
||||
/* value */ this.state.sliderAnimation, |
||||
/* config */ { |
||||
duration: 200, |
||||
toValue: show ? toValue : 0, |
||||
useNativeDriver: true |
||||
}) |
||||
.start(({ finished }) => { |
||||
finished && this._mounted && !show |
||||
&& this.setState({ showOverlay: false }); |
||||
}); |
||||
} |
||||
} |
@ -0,0 +1,31 @@ |
||||
// @flow
|
||||
|
||||
import { StyleSheet } from 'react-native'; |
||||
|
||||
import { OVERLAY_Z_INDEX } from '../../constants'; |
||||
|
||||
export default { |
||||
/** |
||||
* The topmost container of the side bar. |
||||
*/ |
||||
sliderViewContainer: { |
||||
...StyleSheet.absoluteFillObject, |
||||
zIndex: OVERLAY_Z_INDEX |
||||
}, |
||||
|
||||
/** |
||||
* The container of the actual content of the side menu. |
||||
*/ |
||||
sliderViewContent: { |
||||
position: 'absolute' |
||||
}, |
||||
|
||||
/** |
||||
* The opaque area that covers the rest of the screen, when the side bar is |
||||
* open. |
||||
*/ |
||||
sliderViewShadow: { |
||||
...StyleSheet.absoluteFillObject, |
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)' |
||||
} |
||||
}; |
@ -0,0 +1,7 @@ |
||||
// @flow
|
||||
|
||||
/** |
||||
* Z-index for components that are to be rendered like an overlay, to be over |
||||
* everything, such as modal-type of components, or dialogs. |
||||
*/ |
||||
export const OVERLAY_Z_INDEX = 1000; |
Loading…
Reference in new issue