From edbe6a2b248fa552da6ab4a9d4e274766a10ad04 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Tue, 16 Feb 2016 03:04:16 -0800 Subject: [PATCH] Add imperative API to StatusBar Reviewed By: svcscm Differential Revision: D2938743 fb-gh-sync-id: 30af304efd5b089854d9a8defc1b77fd8e817d13 shipit-source-id: 30af304efd5b089854d9a8defc1b77fd8e817d13 --- Examples/UIExplorer/StatusBarExample.js | 131 ++++++++++++++++-- Libraries/Components/StatusBar/StatusBar.js | 88 ++++++++++-- .../Components/StatusBar/StatusBarIOS.ios.js | 32 ++--- 3 files changed, 212 insertions(+), 39 deletions(-) diff --git a/Examples/UIExplorer/StatusBarExample.js b/Examples/UIExplorer/StatusBarExample.js index 094148c4d..e33dbd71a 100644 --- a/Examples/UIExplorer/StatusBarExample.js +++ b/Examples/UIExplorer/StatusBarExample.js @@ -57,12 +57,16 @@ const showHideTransitions = [ 'slide', ]; +function getValue(values: Array, index: number): any { + return values[index % values.length]; +} + const StatusBarExample = React.createClass({ getInitialState(): State { return { animated: true, - backgroundColor: this._getValue(colors, 0), - showHideTransition: this._getValue(showHideTransitions, 0), + backgroundColor: getValue(colors, 0), + showHideTransition: getValue(showHideTransitions, 0), }; }, @@ -70,10 +74,6 @@ const StatusBarExample = React.createClass({ _barStyleIndex: 0, _showHideTransitionIndex: 0, - _getValue(values: Array, index: number): any { - return values[index % values.length]; - }, - render() { return ( @@ -110,10 +110,10 @@ const StatusBarExample = React.createClass({ style={styles.wrapper} onPress={() => { this._barStyleIndex++; - this.setState({barStyle: this._getValue(barStyles, this._barStyleIndex)}); + this.setState({barStyle: getValue(barStyles, this._barStyleIndex)}); }}> - style: '{this._getValue(barStyles, this._barStyleIndex)}' + style: '{getValue(barStyles, this._barStyleIndex)}' @@ -138,13 +138,13 @@ const StatusBarExample = React.createClass({ this._showHideTransitionIndex++; this.setState({ showHideTransition: - this._getValue(showHideTransitions, this._showHideTransitionIndex), + getValue(showHideTransitions, this._showHideTransitionIndex), }); }}> showHideTransition: - '{this._getValue(showHideTransitions, this._showHideTransitionIndex)}' + '{getValue(showHideTransitions, this._showHideTransitionIndex)}' @@ -155,10 +155,10 @@ const StatusBarExample = React.createClass({ style={styles.wrapper} onPress={() => { this._colorIndex++; - this.setState({backgroundColor: this._getValue(colors, this._colorIndex)}); + this.setState({backgroundColor: getValue(colors, this._colorIndex)}); }}> - backgroundColor: '{this._getValue(colors, this._colorIndex)}' + backgroundColor: '{getValue(colors, this._colorIndex)}' @@ -181,11 +181,116 @@ const StatusBarExample = React.createClass({ }, }); +const StatusBarStaticExample = React.createClass({ + _colorIndex: 0, + _barStyleIndex: 0, + _showHideTransitionIndex: 0, + + getInitialState() { + return { + backgroundColor: getValue(colors, 0), + barStyle: getValue(barStyles, 0), + hidden: false, + networkActivityIndicatorVisible: false, + translucent: false, + }; + }, + + render() { + return ( + + + { + const hidden = !this.state.hidden; + StatusBar.setHidden(hidden, 'slide'); + this.setState({hidden}); + }}> + + hidden: {this.state.hidden ? 'true' : 'false'} + + + + iOS + + { + this._barStyleIndex++; + const barStyle = getValue(barStyles, this._barStyleIndex); + StatusBar.setBarStyle(barStyle, true); + this.setState({barStyle}); + }}> + + style: '{getValue(barStyles, this._barStyleIndex)}' + + + + + { + const networkActivityIndicatorVisible = !this.state.networkActivityIndicatorVisible; + StatusBar.setNetworkActivityIndicatorVisible(networkActivityIndicatorVisible); + this.setState({networkActivityIndicatorVisible}); + }}> + + + networkActivityIndicatorVisible: + {this.state.networkActivityIndicatorVisible ? 'true' : 'false'} + + + + + Android + + { + this._colorIndex++; + const backgroundColor = getValue(colors, this._colorIndex); + StatusBar.setBackgroundColor(backgroundColor, true); + this.setState({backgroundColor}); + }}> + + backgroundColor: '{getValue(colors, this._colorIndex)}' + + + + + { + const translucent = !this.state.translucent; + const backgroundColor = !this.state.translucent ? 'rgba(0, 0, 0, 0.4)' : 'black'; + StatusBar.setTranslucent(translucent); + StatusBar.setBackgroundColor(backgroundColor, true); + this.setState({ + translucent, + backgroundColor, + }); + }}> + + translucent: {this.state.translucent ? 'true' : 'false'} + + + + + ); + }, +}); + exports.examples = [{ - title: 'Status Bar', + title: 'StatusBar', render() { return ; }, +}, { + title: 'StatusBar static API', + render() { + return ; + }, }]; var styles = StyleSheet.create({ diff --git a/Libraries/Components/StatusBar/StatusBar.js b/Libraries/Components/StatusBar/StatusBar.js index b6571146a..159577b58 100644 --- a/Libraries/Components/StatusBar/StatusBar.js +++ b/Libraries/Components/StatusBar/StatusBar.js @@ -19,6 +19,17 @@ const processColor = require('processColor'); const StatusBarManager = require('NativeModules').StatusBarManager; +export type StatusBarStyle = $Enum<{ + 'default': string, + 'light-content': string, +}>; + +export type StatusBarAnimation = $Enum<{ + 'none': string, + 'fade': string, + 'slide': string, +}>; + type DefaultProps = { animated: boolean; }; @@ -26,16 +37,10 @@ type DefaultProps = { /** * Merges the prop stack with the default values. */ -function mergePropsStack(propsStack: Array): Object { +function mergePropsStack(propsStack: Array, defaultValues: Object): Object { return propsStack.reduce((prev, cur) => { return Object.assign(prev, cur); - }, { - backgroundColor: 'black', - barStyle: 'default', - translucent: false, - hidden: false, - networkActivityIndicatorVisible: false, - }); + }, defaultValues); } /** @@ -64,10 +69,75 @@ function mergePropsStack(propsStack: Array): Object { * /> * * ``` + * + * ### Imperative API + * + * For cases where using a component is not ideal, there is also an imperative + * API exposed as static functions on the component. It is however not recommended + * to use the static API and the compoment for the same prop because any value + * set by the static API will get overriden by the one set by the component in + * the next render. */ const StatusBar = React.createClass({ statics: { _propsStack: [], + _defaultProps: { + backgroundColor: 'black', + barStyle: 'default', + translucent: false, + hidden: false, + networkActivityIndicatorVisible: false, + }, + + // Provide an imperative API as static functions of the component. + // See the corresponding prop for more detail. + setHidden(hidden: boolean, animation?: StatusBarAnimation) { + animation = animation || 'none'; + StatusBar._defaultProps.hidden = hidden; + if (Platform.OS === 'ios') { + StatusBarManager.setHidden(hidden, animation); + } else if (Platform.OS === 'android') { + StatusBarManager.setHidden(hidden); + } + }, + + setBarStyle(style: StatusBarStyle, animated?: boolean) { + if (Platform.OS !== 'ios') { + console.warn('`setBarStyle` is only available on iOS'); + return; + } + animated = animated || false; + StatusBar._defaultProps.barStyle = style; + StatusBarManager.setStyle(style, animated); + }, + + setNetworkActivityIndicatorVisible(visible: boolean) { + if (Platform.OS !== 'ios') { + console.warn('`setNetworkActivityIndicatorVisible` is only available on iOS'); + return; + } + StatusBar._defaultProps.networkActivityIndicatorVisible = visible; + StatusBarManager.setNetworkActivityIndicatorVisible(visible); + }, + + setBackgroundColor(color, animated?: boolean) { + if (Platform.OS !== 'android') { + console.warn('`setBackgroundColor` is only available on Android'); + return; + } + animated = animated || false; + StatusBar._defaultProps.backgroundColor = color; + StatusBarManager.setColor(processColor(color), animated); + }, + + setTranslucent(translucent: boolean) { + if (Platform.OS !== 'android') { + console.warn('`setTranslucent` is only available on Android'); + return; + } + StatusBar._defaultProps.translucent = translucent; + StatusBarManager.setTranslucent(translucent); + }, }, propTypes: { @@ -156,7 +226,7 @@ const StatusBar = React.createClass({ * Updates the native status bar with the props from the stack. */ _updatePropsStack() { - const mergedProps = mergePropsStack(StatusBar._propsStack); + const mergedProps = mergePropsStack(StatusBar._propsStack, StatusBar._defaultProps); if (Platform.OS === 'ios') { if (mergedProps.barStyle !== undefined) { diff --git a/Libraries/Components/StatusBar/StatusBarIOS.ios.js b/Libraries/Components/StatusBar/StatusBarIOS.ios.js index 59369ef14..a4c0357b0 100644 --- a/Libraries/Components/StatusBar/StatusBarIOS.ios.js +++ b/Libraries/Components/StatusBar/StatusBarIOS.ios.js @@ -11,33 +11,31 @@ */ 'use strict'; -var RCTStatusBarManager = require('NativeModules').StatusBarManager; +const StatusBar = require('StatusBar'); -type StatusBarStyle = $Enum<{ - 'default': string, - 'light-content': string, -}>; +import type {StatusBarStyle, StatusBarAnimation} from 'StatusBar'; -type StatusBarAnimation = $Enum<{ - 'none': string, - 'fade': string, - 'slide': string, -}>; - -var StatusBarIOS = { +/** + * Deprecated. Use `StatusBar` instead. + */ +const StatusBarIOS = { setStyle(style: StatusBarStyle, animated?: boolean) { - animated = animated || false; - RCTStatusBarManager.setStyle(style, animated); + console.warn('`StatusBarIOS.setStyle` is deprecated. Use `StatusBar.setBarStyle` instead.'); + StatusBar.setBarStyle(style, animated); }, setHidden(hidden: boolean, animation?: StatusBarAnimation) { - animation = animation || 'none'; - RCTStatusBarManager.setHidden(hidden, animation); + console.warn('`StatusBarIOS.setHidden` is deprecated. Use `StatusBar.setHidden` instead.'); + StatusBar.setHidden(hidden, animation); }, setNetworkActivityIndicatorVisible(visible: boolean) { - RCTStatusBarManager.setNetworkActivityIndicatorVisible(visible); + console.warn( + '`StatusBarIOS.setNetworkActivityIndicatorVisible` is deprecated. ' + + 'Use `StatusBar.setNetworkActivityIndicatorVisible` instead.' + ); + StatusBar.setNetworkActivityIndicatorVisible(visible); }, };