DrawerLayoutAndroid.android.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @flow
  8. * @format
  9. */
  10. 'use strict';
  11. const Platform = require('../../Utilities/Platform');
  12. const React = require('react');
  13. const StatusBar = require('../StatusBar/StatusBar');
  14. const StyleSheet = require('../../StyleSheet/StyleSheet');
  15. const View = require('../View/View');
  16. const dismissKeyboard = require('../../Utilities/dismissKeyboard');
  17. const nullthrows = require('nullthrows');
  18. import AndroidDrawerLayoutNativeComponent, {
  19. Commands,
  20. } from './AndroidDrawerLayoutNativeComponent';
  21. const DRAWER_STATES = ['Idle', 'Dragging', 'Settling'];
  22. import type {ViewStyleProp} from '../../StyleSheet/StyleSheet';
  23. import type {ColorValue} from '../../StyleSheet/StyleSheetTypes';
  24. import type {DirectEventHandler} from '../../Types/CodegenTypes';
  25. import type {
  26. MeasureOnSuccessCallback,
  27. MeasureInWindowOnSuccessCallback,
  28. MeasureLayoutOnSuccessCallback,
  29. } from '../../Renderer/shims/ReactNativeTypes';
  30. type DrawerStates = 'Idle' | 'Dragging' | 'Settling';
  31. type DrawerSlideEvent = $ReadOnly<{|
  32. offset: number,
  33. |}>;
  34. type Props = $ReadOnly<{|
  35. /**
  36. * Determines whether the keyboard gets dismissed in response to a drag.
  37. * - 'none' (the default), drags do not dismiss the keyboard.
  38. * - 'on-drag', the keyboard is dismissed when a drag begins.
  39. */
  40. keyboardDismissMode?: ?('none' | 'on-drag'),
  41. /**
  42. * Specifies the background color of the drawer. The default value is white.
  43. * If you want to set the opacity of the drawer, use rgba. Example:
  44. *
  45. * ```
  46. * return (
  47. * <DrawerLayoutAndroid drawerBackgroundColor="rgba(0,0,0,0.5)">
  48. * </DrawerLayoutAndroid>
  49. * );
  50. * ```
  51. */
  52. drawerBackgroundColor: ColorValue,
  53. /**
  54. * Specifies the side of the screen from which the drawer will slide in.
  55. */
  56. drawerPosition: ?('left' | 'right'),
  57. /**
  58. * Specifies the width of the drawer, more precisely the width of the view that be pulled in
  59. * from the edge of the window.
  60. */
  61. drawerWidth?: ?number,
  62. /**
  63. * Specifies the lock mode of the drawer. The drawer can be locked in 3 states:
  64. * - unlocked (default), meaning that the drawer will respond (open/close) to touch gestures.
  65. * - locked-closed, meaning that the drawer will stay closed and not respond to gestures.
  66. * - locked-open, meaning that the drawer will stay opened and not respond to gestures.
  67. * The drawer may still be opened and closed programmatically (`openDrawer`/`closeDrawer`).
  68. */
  69. drawerLockMode?: ?('unlocked' | 'locked-closed' | 'locked-open'),
  70. /**
  71. * Function called whenever there is an interaction with the navigation view.
  72. */
  73. onDrawerSlide?: ?DirectEventHandler<DrawerSlideEvent>,
  74. /**
  75. * Function called when the drawer state has changed. The drawer can be in 3 states:
  76. * - Idle, meaning there is no interaction with the navigation view happening at the time
  77. * - Dragging, meaning there is currently an interaction with the navigation view
  78. * - Settling, meaning that there was an interaction with the navigation view, and the
  79. * navigation view is now finishing its closing or opening animation
  80. */
  81. onDrawerStateChanged?: ?(state: DrawerStates) => mixed,
  82. /**
  83. * Function called whenever the navigation view has been opened.
  84. */
  85. onDrawerOpen?: ?() => mixed,
  86. /**
  87. * Function called whenever the navigation view has been closed.
  88. */
  89. onDrawerClose?: ?() => mixed,
  90. /**
  91. * The navigation view that will be rendered to the side of the screen and can be pulled in.
  92. */
  93. renderNavigationView: () => React.Element<any>,
  94. /**
  95. * Make the drawer take the entire screen and draw the background of the
  96. * status bar to allow it to open over the status bar. It will only have an
  97. * effect on API 21+.
  98. */
  99. statusBarBackgroundColor?: ?ColorValue,
  100. children?: React.Node,
  101. style?: ?ViewStyleProp,
  102. |}>;
  103. type State = {|
  104. statusBarBackgroundColor: ColorValue,
  105. |};
  106. /**
  107. * React component that wraps the platform `DrawerLayout` (Android only). The
  108. * Drawer (typically used for navigation) is rendered with `renderNavigationView`
  109. * and direct children are the main view (where your content goes). The navigation
  110. * view is initially not visible on the screen, but can be pulled in from the
  111. * side of the window specified by the `drawerPosition` prop and its width can
  112. * be set by the `drawerWidth` prop.
  113. *
  114. * Example:
  115. *
  116. * ```
  117. * render: function() {
  118. * var navigationView = (
  119. * <View style={{flex: 1, backgroundColor: '#fff'}}>
  120. * <Text style={{margin: 10, fontSize: 15, textAlign: 'left'}}>I'm in the Drawer!</Text>
  121. * </View>
  122. * );
  123. * return (
  124. * <DrawerLayoutAndroid
  125. * drawerWidth={300}
  126. * drawerPosition="left"
  127. * renderNavigationView={() => navigationView}>
  128. * <View style={{flex: 1, alignItems: 'center'}}>
  129. * <Text style={{margin: 10, fontSize: 15, textAlign: 'right'}}>Hello</Text>
  130. * <Text style={{margin: 10, fontSize: 15, textAlign: 'right'}}>World!</Text>
  131. * </View>
  132. * </DrawerLayoutAndroid>
  133. * );
  134. * },
  135. * ```
  136. */
  137. class DrawerLayoutAndroid extends React.Component<Props, State> {
  138. static get positions(): mixed {
  139. console.warn(
  140. 'Setting DrawerLayoutAndroid drawerPosition using `DrawerLayoutAndroid.positions` is deprecated. Instead pass the string value "left" or "right"',
  141. );
  142. return {Left: 'left', Right: 'right'};
  143. }
  144. static defaultProps: {|
  145. drawerBackgroundColor: 'white',
  146. |} = {
  147. drawerBackgroundColor: 'white',
  148. };
  149. _nativeRef = React.createRef<
  150. React.ElementRef<typeof AndroidDrawerLayoutNativeComponent>,
  151. >();
  152. state: State = {statusBarBackgroundColor: null};
  153. render(): React.Node {
  154. const {
  155. onDrawerStateChanged,
  156. renderNavigationView,
  157. onDrawerOpen,
  158. onDrawerClose,
  159. ...props
  160. } = this.props;
  161. const drawStatusBar =
  162. Platform.Version >= 21 && this.props.statusBarBackgroundColor != null;
  163. const drawerViewWrapper = (
  164. <View
  165. style={[
  166. styles.drawerSubview,
  167. {
  168. width: this.props.drawerWidth,
  169. backgroundColor: this.props.drawerBackgroundColor,
  170. },
  171. ]}
  172. collapsable={false}>
  173. {renderNavigationView()}
  174. {drawStatusBar && <View style={styles.drawerStatusBar} />}
  175. </View>
  176. );
  177. const childrenWrapper = (
  178. <View style={styles.mainSubview} collapsable={false}>
  179. {drawStatusBar && (
  180. <StatusBar
  181. translucent
  182. backgroundColor={this.props.statusBarBackgroundColor}
  183. />
  184. )}
  185. {drawStatusBar && (
  186. <View
  187. style={[
  188. styles.statusBar,
  189. {backgroundColor: this.props.statusBarBackgroundColor},
  190. ]}
  191. />
  192. )}
  193. {this.props.children}
  194. </View>
  195. );
  196. return (
  197. <AndroidDrawerLayoutNativeComponent
  198. {...props}
  199. ref={this._nativeRef}
  200. drawerWidth={this.props.drawerWidth}
  201. drawerPosition={this.props.drawerPosition}
  202. drawerLockMode={this.props.drawerLockMode}
  203. style={[styles.base, this.props.style]}
  204. onDrawerSlide={this._onDrawerSlide}
  205. onDrawerOpen={this._onDrawerOpen}
  206. onDrawerClose={this._onDrawerClose}
  207. onDrawerStateChanged={this._onDrawerStateChanged}>
  208. {childrenWrapper}
  209. {drawerViewWrapper}
  210. </AndroidDrawerLayoutNativeComponent>
  211. );
  212. }
  213. _onDrawerSlide = event => {
  214. if (this.props.onDrawerSlide) {
  215. this.props.onDrawerSlide(event);
  216. }
  217. if (this.props.keyboardDismissMode === 'on-drag') {
  218. dismissKeyboard();
  219. }
  220. };
  221. _onDrawerOpen = () => {
  222. if (this.props.onDrawerOpen) {
  223. this.props.onDrawerOpen();
  224. }
  225. };
  226. _onDrawerClose = () => {
  227. if (this.props.onDrawerClose) {
  228. this.props.onDrawerClose();
  229. }
  230. };
  231. _onDrawerStateChanged = event => {
  232. if (this.props.onDrawerStateChanged) {
  233. this.props.onDrawerStateChanged(
  234. DRAWER_STATES[event.nativeEvent.drawerState],
  235. );
  236. }
  237. };
  238. /**
  239. * Opens the drawer.
  240. */
  241. openDrawer() {
  242. Commands.openDrawer(nullthrows(this._nativeRef.current));
  243. }
  244. /**
  245. * Closes the drawer.
  246. */
  247. closeDrawer() {
  248. Commands.closeDrawer(nullthrows(this._nativeRef.current));
  249. }
  250. /**
  251. * Closing and opening example
  252. * Note: To access the drawer you have to give it a ref
  253. *
  254. * Class component:
  255. *
  256. * render () {
  257. * this.openDrawer = () => {
  258. * this.refs.DRAWER.openDrawer()
  259. * }
  260. * this.closeDrawer = () => {
  261. * this.refs.DRAWER.closeDrawer()
  262. * }
  263. * return (
  264. * <DrawerLayoutAndroid ref={'DRAWER'}>
  265. * {children}
  266. * </DrawerLayoutAndroid>
  267. * )
  268. * }
  269. *
  270. * Function component:
  271. *
  272. * const drawerRef = useRef()
  273. * const openDrawer = () => {
  274. * drawerRef.current.openDrawer()
  275. * }
  276. * const closeDrawer = () => {
  277. * drawerRef.current.closeDrawer()
  278. * }
  279. * return (
  280. * <DrawerLayoutAndroid ref={drawerRef}>
  281. * {children}
  282. * </DrawerLayoutAndroid>
  283. * )
  284. */
  285. /**
  286. * Native methods
  287. */
  288. blur() {
  289. nullthrows(this._nativeRef.current).blur();
  290. }
  291. focus() {
  292. nullthrows(this._nativeRef.current).focus();
  293. }
  294. measure(callback: MeasureOnSuccessCallback) {
  295. nullthrows(this._nativeRef.current).measure(callback);
  296. }
  297. measureInWindow(callback: MeasureInWindowOnSuccessCallback) {
  298. nullthrows(this._nativeRef.current).measureInWindow(callback);
  299. }
  300. measureLayout(
  301. relativeToNativeNode: number,
  302. onSuccess: MeasureLayoutOnSuccessCallback,
  303. onFail?: () => void,
  304. ) {
  305. nullthrows(this._nativeRef.current).measureLayout(
  306. relativeToNativeNode,
  307. onSuccess,
  308. onFail,
  309. );
  310. }
  311. setNativeProps(nativeProps: Object) {
  312. nullthrows(this._nativeRef.current).setNativeProps(nativeProps);
  313. }
  314. }
  315. const styles = StyleSheet.create({
  316. base: {
  317. flex: 1,
  318. elevation: 16,
  319. },
  320. mainSubview: {
  321. position: 'absolute',
  322. top: 0,
  323. left: 0,
  324. right: 0,
  325. bottom: 0,
  326. },
  327. drawerSubview: {
  328. position: 'absolute',
  329. top: 0,
  330. bottom: 0,
  331. },
  332. statusBar: {
  333. height: StatusBar.currentHeight,
  334. },
  335. drawerStatusBar: {
  336. position: 'absolute',
  337. top: 0,
  338. left: 0,
  339. right: 0,
  340. height: StatusBar.currentHeight,
  341. backgroundColor: 'rgba(0, 0, 0, 0.251)',
  342. },
  343. });
  344. module.exports = DrawerLayoutAndroid;