5
5
* LICENSE file in the root directory of this source tree.
6
6
*
7
7
* @format
8
+ * @flow
8
9
*/
9
10
10
11
'use strict' ;
11
12
12
- const DeprecatedColorPropType = require ( 'DeprecatedColorPropType' ) ;
13
- const DeprecatedViewPropTypes = require ( 'DeprecatedViewPropTypes' ) ;
14
- const Image = require ( 'Image' ) ;
15
- const NativeMethodsMixin = require ( 'NativeMethodsMixin' ) ;
16
- const PropTypes = require ( 'prop-types' ) ;
17
13
const React = require ( 'React' ) ;
18
14
const UIManager = require ( 'UIManager' ) ;
19
15
20
- const createReactClass = require ( 'create-react-class' ) ;
21
16
const requireNativeComponent = require ( 'requireNativeComponent' ) ;
22
17
const resolveAssetSource = require ( 'resolveAssetSource' ) ;
23
18
24
- const optionalImageSource = PropTypes . oneOfType ( [
25
- Image . propTypes . source ,
26
- // Image.propTypes.source is required but we want it to be optional, so we OR
27
- // it with a nullable propType.
28
- PropTypes . oneOf ( [ ] ) ,
29
- ] ) ;
19
+ import type { SyntheticEvent } from 'CoreEventTypes' ;
20
+ import type { ImageSource } from 'ImageSource' ;
21
+ import type { ColorValue } from 'StyleSheetTypes' ;
22
+ import type { ViewProps } from 'ViewPropTypes' ;
23
+ import type { NativeComponent } from 'ReactNative' ;
30
24
31
25
/**
32
26
* React component that wraps the Android-only [`Toolbar` widget][0]. A Toolbar can display a logo,
@@ -63,124 +57,157 @@ const optionalImageSource = PropTypes.oneOfType([
63
57
*
64
58
* [0]: https://developer.android.com/reference/android/support/v7/widget/Toolbar.html
65
59
*/
66
- const ToolbarAndroid = createReactClass ( {
67
- displayName : 'ToolbarAndroid' ,
68
- mixins : [ NativeMethodsMixin ] ,
69
-
70
- propTypes : {
71
- ...DeprecatedViewPropTypes ,
72
- /**
73
- * Sets possible actions on the toolbar as part of the action menu. These are displayed as icons
74
- * or text on the right side of the widget. If they don't fit they are placed in an 'overflow'
75
- * menu.
76
- *
77
- * This property takes an array of objects, where each object has the following keys:
78
- *
79
- * * `title`: **required**, the title of this action
80
- * * `icon`: the icon for this action, e.g. `require('./some_icon.png')`
81
- * * `show`: when to show this action as an icon or hide it in the overflow menu: `always`,
82
- * `ifRoom` or `never`
83
- * * `showWithText`: boolean, whether to show text alongside the icon or not
84
- */
85
- actions : PropTypes . arrayOf (
86
- PropTypes . shape ( {
87
- title : PropTypes . string . isRequired ,
88
- icon : optionalImageSource ,
89
- show : PropTypes . oneOf ( [ 'always' , 'ifRoom' , 'never' ] ) ,
90
- showWithText : PropTypes . bool ,
91
- } ) ,
92
- ) ,
93
- /**
94
- * Sets the toolbar logo.
95
- */
96
- logo : optionalImageSource ,
97
- /**
98
- * Sets the navigation icon.
99
- */
100
- navIcon : optionalImageSource ,
101
- /**
102
- * Callback that is called when an action is selected. The only argument that is passed to the
103
- * callback is the position of the action in the actions array.
104
- */
105
- onActionSelected : PropTypes . func ,
106
- /**
107
- * Callback called when the icon is selected.
108
- */
109
- onIconClicked : PropTypes . func ,
110
- /**
111
- * Sets the overflow icon.
112
- */
113
- overflowIcon : optionalImageSource ,
114
- /**
115
- * Sets the toolbar subtitle.
116
- */
117
- subtitle : PropTypes . string ,
118
- /**
119
- * Sets the toolbar subtitle color.
120
- */
121
- subtitleColor : DeprecatedColorPropType ,
122
- /**
123
- * Sets the toolbar title.
124
- */
125
- title : PropTypes . string ,
126
- /**
127
- * Sets the toolbar title color.
128
- */
129
- titleColor : DeprecatedColorPropType ,
130
- /**
131
- * Sets the content inset for the toolbar starting edge.
132
- *
133
- * The content inset affects the valid area for Toolbar content other than
134
- * the navigation button and menu. Insets define the minimum margin for
135
- * these components and can be used to effectively align Toolbar content
136
- * along well-known gridlines.
137
- */
138
- contentInsetStart : PropTypes . number ,
139
- /**
140
- * Sets the content inset for the toolbar ending edge.
141
- *
142
- * The content inset affects the valid area for Toolbar content other than
143
- * the navigation button and menu. Insets define the minimum margin for
144
- * these components and can be used to effectively align Toolbar content
145
- * along well-known gridlines.
146
- */
147
- contentInsetEnd : PropTypes . number ,
148
- /**
149
- * Used to set the toolbar direction to RTL.
150
- * In addition to this property you need to add
151
- *
152
- * android:supportsRtl="true"
153
- *
154
- * to your application AndroidManifest.xml and then call
155
- * `setLayoutDirection(LayoutDirection.RTL)` in your MainActivity
156
- * `onCreate` method.
157
- */
158
- rtl : PropTypes . bool ,
159
- /**
160
- * Used to locate this view in end-to-end tests.
161
- */
162
- testID : PropTypes . string ,
163
- } ,
164
60
165
- render : function ( ) {
166
- const nativeProps = {
167
- ...this . props ,
61
+ const NativeToolbar = requireNativeComponent ( 'ToolbarAndroid' ) ;
62
+
63
+ type Action = $ReadOnly < { |
64
+ title : string ,
65
+ icon ?: ?ImageSource ,
66
+ show ?: 'always' | 'ifRoom' | 'never' ,
67
+ showWithText ?: boolean ,
68
+ | } > ;
69
+
70
+ type ToolbarAndroidChangeEvent = SyntheticEvent <
71
+ $ReadOnly < { |
72
+ position : number ,
73
+ | } > ,
74
+ > ;
75
+
76
+ type ToolbarAndroidProps = $ReadOnly < { |
77
+ ...ViewProps ,
78
+ /**
79
+ * or text on the right side of the widget. If they don't fit they are placed in an 'overflow'
80
+ * Sets possible actions on the toolbar as part of the action menu. These are displayed as icons
81
+ * menu.
82
+ *
83
+ * This property takes an array of objects, where each object has the following keys:
84
+ *
85
+ * * `title`: **required**, the title of this action
86
+ * * `icon`: the icon for this action, e.g. `require('./some_icon.png')`
87
+ * * `show`: when to show this action as an icon or hide it in the overflow menu: `always`,
88
+ * `ifRoom` or `never`
89
+ * * `showWithText`: boolean, whether to show text alongside the icon or not
90
+ */
91
+ actions ?: ?Array < Action > ,
92
+ /**
93
+ * Sets the toolbar logo.
94
+ */
95
+ logo ?: ?ImageSource ,
96
+ /**
97
+ * Sets the navigation icon.
98
+ */
99
+ navIcon ?: ?ImageSource ,
100
+ /**
101
+ * Callback that is called when an action is selected. The only argument that is passed to the
102
+ * callback is the position of the action in the actions array.
103
+ */
104
+ onActionSelected ?: ?( position : number ) => void ,
105
+ /**
106
+ * Callback called when the icon is selected.
107
+ */
108
+ onIconClicked ?: ?( ) => void ,
109
+ /**
110
+ * Sets the overflow icon.
111
+ */
112
+ overflowIcon ?: ?ImageSource ,
113
+ /**
114
+ * Sets the toolbar subtitle.
115
+ */
116
+ subtitle ?: ?string ,
117
+ /**
118
+ * Sets the toolbar subtitle color.
119
+ */
120
+ subtitleColor ?: ?ColorValue ,
121
+ /**
122
+ * Sets the toolbar title.
123
+ */
124
+ title ?: ?Stringish ,
125
+ /**
126
+ * Sets the toolbar title color.
127
+ */
128
+ titleColor ?: ?ColorValue ,
129
+ /**
130
+ * Sets the content inset for the toolbar starting edge.
131
+ *
132
+ * The content inset affects the valid area for Toolbar content other than
133
+ * the navigation button and menu. Insets define the minimum margin for
134
+ * these components and can be used to effectively align Toolbar content
135
+ * along well-known gridlines.
136
+ */
137
+ contentInsetStart ?: ?number ,
138
+ /**
139
+ * Sets the content inset for the toolbar ending edge.
140
+ *
141
+ * The content inset affects the valid area for Toolbar content other than
142
+ * the navigation button and menu. Insets define the minimum margin for
143
+ * these components and can be used to effectively align Toolbar content
144
+ * along well-known gridlines.
145
+ */
146
+ contentInsetEnd ?: ?number ,
147
+ /**
148
+ * Used to set the toolbar direction to RTL.
149
+ * In addition to this property you need to add
150
+ *
151
+ * android:supportsRtl="true"
152
+ *
153
+ * to your application AndroidManifest.xml and then call
154
+ * `setLayoutDirection(LayoutDirection.RTL)` in your MainActivity
155
+ * `onCreate` method.
156
+ */
157
+ rtl ?: ?boolean ,
158
+ /**
159
+ * Used to locate this view in end-to-end tests.
160
+ */
161
+ testID ?: ?string ,
162
+ | } > ;
163
+
164
+ type Props = $ReadOnly < { |
165
+ ...ToolbarAndroidProps ,
166
+ forwardedRef : ?React . Ref < typeof NativeToolbar > ,
167
+ | } > ;
168
+
169
+ class ToolbarAndroid extends React . Component < Props > {
170
+ _onSelect = ( event : ToolbarAndroidChangeEvent ) => {
171
+ const position = event . nativeEvent . position ;
172
+ if ( position === - 1 ) {
173
+ this . props . onIconClicked && this . props . onIconClicked ( ) ;
174
+ } else {
175
+ this . props . onActionSelected && this . props . onActionSelected ( position ) ;
176
+ }
177
+ } ;
178
+
179
+ render ( ) {
180
+ const {
181
+ onIconClicked ,
182
+ onActionSelected,
183
+ forwardedRef,
184
+ ...otherProps
185
+ } = this . props ;
186
+
187
+ const nativeProps : { ...typeof otherProps , nativeActions ?: Array < Action > } = {
188
+ ...otherProps ,
168
189
} ;
190
+
169
191
if ( this . props . logo ) {
170
192
nativeProps . logo = resolveAssetSource ( this . props . logo ) ;
171
193
}
194
+
172
195
if ( this . props . navIcon ) {
173
196
nativeProps . navIcon = resolveAssetSource ( this . props . navIcon ) ;
174
197
}
198
+
175
199
if ( this . props . overflowIcon ) {
176
200
nativeProps . overflowIcon = resolveAssetSource ( this . props . overflowIcon ) ;
177
201
}
202
+
178
203
if ( this . props . actions ) {
179
204
const nativeActions = [ ] ;
180
205
for ( let i = 0 ; i < this . props . actions . length ; i ++ ) {
181
206
const action = {
182
- ...this . props . actions [ i ] ,
207
+ icon : this . props . actions [ i ] . icon ,
208
+ show : this . props . actions [ i ] . show ,
183
209
} ;
210
+
184
211
if ( action . icon ) {
185
212
action . icon = resolveAssetSource ( action . icon ) ;
186
213
}
@@ -189,24 +216,36 @@ const ToolbarAndroid = createReactClass({
189
216
'ToolbarAndroid' ,
190
217
) . Constants . ShowAsAction [ action . show ] ;
191
218
}
192
- nativeActions . push ( action ) ;
219
+
220
+ nativeActions . push ( {
221
+ ...this . props . actions [ i ] ,
222
+ ...action ,
223
+ } ) ;
193
224
}
225
+
194
226
nativeProps . nativeActions = nativeActions ;
195
227
}
196
228
197
- return < NativeToolbar onSelect = { this . _onSelect } { ...nativeProps } /> ;
198
- } ,
229
+ return (
230
+ < NativeToolbar
231
+ onSelect = { this . _onSelect }
232
+ { ...nativeProps }
233
+ ref = { forwardedRef }
234
+ />
235
+ ) ;
236
+ }
237
+ }
199
238
200
- _onSelect : function ( event ) {
201
- const position = event . nativeEvent . position ;
202
- if ( position === - 1 ) {
203
- this . props . onIconClicked && this . props . onIconClicked ( ) ;
204
- } else {
205
- this . props . onActionSelected && this . props . onActionSelected ( position ) ;
206
- }
239
+ // $FlowFixMe - TODO T29156721 `React.forwardRef` is not defined in Flow, yet.
240
+ const ToolbarAndroidToExport = React . forwardRef (
241
+ (
242
+ props : ToolbarAndroidProps ,
243
+ forwardedRef : ? React . Ref < typeof NativeToolbar > ,
244
+ ) => {
245
+ return < ToolbarAndroid { ... props } forwardedRef = { forwardedRef } / > ;
207
246
} ,
208
- } ) ;
209
-
210
- const NativeToolbar = requireNativeComponent ( 'ToolbarAndroid' ) ;
247
+ ) ;
211
248
212
- module . exports = ToolbarAndroid ;
249
+ module . exports = ( ToolbarAndroidToExport : Class <
250
+ NativeComponent < ToolbarAndroidProps > ,
251
+ > ) ;
0 commit comments