7
7
* @flow
8
8
* @format
9
9
*/
10
+
10
11
'use strict' ;
11
12
12
13
const React = require ( 'React' ) ;
@@ -18,17 +19,31 @@ const Touchable = require('Touchable');
18
19
const UIManager = require ( 'UIManager' ) ;
19
20
20
21
const createReactNativeComponentClass = require ( 'createReactNativeComponentClass' ) ;
22
+ const nullthrows = require ( 'fbjs/lib/nullthrows' ) ;
21
23
const processColor = require ( 'processColor' ) ;
22
24
23
25
import type { PressEvent } from 'CoreEventTypes' ;
24
26
import type { PressRetentionOffset , TextProps } from 'TextProps' ;
25
27
28
+ type ResponseHandlers = $ReadOnly < { |
29
+ onStartShouldSetResponder : ( ) => boolean ,
30
+ onResponderGrant : ( event : SyntheticEvent < > , dispatchID : string ) => void ,
31
+ onResponderMove : ( event : SyntheticEvent < > ) => void ,
32
+ onResponderRelease : ( event : SyntheticEvent < > ) => void ,
33
+ onResponderTerminate : ( event : SyntheticEvent < > ) => void ,
34
+ onResponderTerminationRequest : ( ) = > boolean ,
35
+ | } > ;
36
+
37
+ type Props = TextProps ;
38
+
26
39
type State = { |
27
40
touchable : { |
28
41
touchState : ?string ,
29
42
responderID : ?number ,
30
43
| } ,
31
44
isHighlighted : boolean ,
45
+ createResponderHandlers : ( ) = > ResponseHandlers ,
46
+ responseHandlers : ?ResponseHandlers ,
32
47
| } ;
33
48
34
49
const PRESS_RECT_OFFSET = { top : 20 , left : 20 , right : 20 , bottom : 30 } ;
@@ -55,7 +70,7 @@ const viewConfig = {
55
70
*
56
71
* See https://facebook.github.io/react-native/docs/text.html
57
72
*/
58
- class Text extends ReactNative . NativeComponent < TextProps , State > {
73
+ class Text extends ReactNative . NativeComponent < Props , State > {
59
74
static propTypes = TextPropTypes ;
60
75
61
76
static defaultProps = {
@@ -64,176 +79,185 @@ class Text extends ReactNative.NativeComponent<TextProps, State> {
64
79
ellipsizeMode : 'tail' ,
65
80
} ;
66
81
82
+ touchableGetPressRectOffset : ?( ) = > PressRetentionOffset ;
83
+ touchableHandleActivePressIn : ?( ) = > void ;
84
+ touchableHandleActivePressOut : ?( ) = > void ;
85
+ touchableHandleLongPress : ?( event : PressEvent ) = > void ;
86
+ touchableHandlePress : ?( event : PressEvent ) = > void ;
87
+ touchableHandleResponderGrant : ?(
88
+ event : SyntheticEvent < > ,
89
+ dispatchID : string ,
90
+ ) = > void ;
91
+ touchableHandleResponderMove : ?( event : SyntheticEvent < > ) => void ;
92
+ touchableHandleResponderRelease : ?( event : SyntheticEvent < > ) => void ;
93
+ touchableHandleResponderTerminate : ?( event : SyntheticEvent < > ) => void ;
94
+ touchableHandleResponderTerminationRequest : ?( ) = > boolean ;
95
+
67
96
state = {
68
97
...Touchable . Mixin . touchableGetInitialState ( ) ,
69
98
isHighlighted : false ,
99
+ createResponderHandlers : this . _createResponseHandlers . bind ( this ) ,
100
+ responseHandlers : null ,
70
101
} ;
71
102
72
- viewConfig = viewConfig ;
103
+ static getDerivedStateFromProps ( nextProps : Props , prevState : State ) : ?State {
104
+ return prevState . responseHandlers = = null && isTouchable ( nextProps )
105
+ ? {
106
+ ...prevState ,
107
+ responseHandlers : prevState . createResponderHandlers ( ) ,
108
+ }
109
+ : null ;
110
+ }
73
111
74
- _handlers : ? Object ;
112
+ static viewConfig = viewConfig ;
75
113
76
- _hasPressHandler ( ) : boolean {
77
- return ! ! this . props . onPress || ! ! this . props . onLongPress ;
78
- }
79
- /**
80
- * These are assigned lazily the first time the responder is set to make plain
81
- * text nodes as cheap as possible.
82
- */
83
- touchableHandleActivePressIn : ?Function ;
84
- touchableHandleActivePressOut : ?Function ;
85
- touchableHandlePress : ?Function ;
86
- touchableHandleLongPress : ?Function ;
87
- touchableHandleResponderGrant : ?Function ;
88
- touchableHandleResponderMove : ?Function ;
89
- touchableHandleResponderRelease : ?Function ;
90
- touchableHandleResponderTerminate : ?Function ;
91
- touchableHandleResponderTerminationRequest : ?Function ;
92
- touchableGetPressRectOffset : ?Function ;
93
-
94
- render ( ) : React . Element < any > {
95
- let newProps = this . props ;
96
- if ( this . props . onStartShouldSetResponder || this . _hasPressHandler ( ) ) {
97
- if ( ! this . _handlers ) {
98
- this . _handlers = {
99
- onStartShouldSetResponder : ( ) : boolean => {
100
- const shouldSetFromProps =
101
- this . props . onStartShouldSetResponder &&
102
- this . props . onStartShouldSetResponder ( ) ;
103
- const setResponder = shouldSetFromProps || this . _hasPressHandler ( ) ;
104
- if ( setResponder && ! this . touchableHandleActivePressIn ) {
105
- // Attach and bind all the other handlers only the first time a touch
106
- // actually happens.
107
- for ( const key in Touchable . Mixin ) {
108
- if ( typeof Touchable . Mixin [ key ] === 'function' ) {
109
- ( this : any ) [ key ] = Touchable . Mixin [ key ] . bind ( this ) ;
110
- }
111
- }
112
- this . touchableHandleActivePressIn = ( ) => {
113
- if (
114
- this . props . suppressHighlighting ||
115
- ! this . _hasPressHandler ( )
116
- ) {
117
- return ;
118
- }
119
- this . setState ( {
120
- isHighlighted : true ,
121
- } ) ;
122
- } ;
123
-
124
- this . touchableHandleActivePressOut = ( ) => {
125
- if (
126
- this . props . suppressHighlighting ||
127
- ! this . _hasPressHandler ( )
128
- ) {
129
- return ;
130
- }
131
- this . setState ( {
132
- isHighlighted : false ,
133
- } ) ;
134
- } ;
135
-
136
- this . touchableHandlePress = ( e : PressEvent ) => {
137
- this . props . onPress && this . props . onPress ( e ) ;
138
- } ;
139
-
140
- this . touchableHandleLongPress = ( e : PressEvent ) => {
141
- this . props . onLongPress && this . props . onLongPress ( e ) ;
142
- } ;
143
-
144
- this . touchableGetPressRectOffset = function ( ) : PressRetentionOffset {
145
- return this . props . pressRetentionOffset || PRESS_RECT_OFFSET ;
146
- } ;
147
- }
148
- return setResponder ;
149
- } ,
150
- onResponderGrant : function ( e : SyntheticEvent < > , dispatchID : string ) {
151
- // $FlowFixMe TouchableMixin handlers couldn't actually be null
152
- this . touchableHandleResponderGrant ( e , dispatchID ) ;
153
- this . props . onResponderGrant &&
154
- this . props . onResponderGrant . apply ( this , arguments ) ;
155
- } . bind ( this ) ,
156
- onResponderMove : function ( e : SyntheticEvent < > ) {
157
- // $FlowFixMe TouchableMixin handlers couldn't actually be null
158
- this. touchableHandleResponderMove ( e ) ;
159
- this . props . onResponderMove &&
160
- this . props . onResponderMove . apply ( this , arguments ) ;
161
- } . bind ( this ) ,
162
- onResponderRelease : function ( e : SyntheticEvent < > ) {
163
- // $FlowFixMe TouchableMixin handlers couldn't actually be null
164
- this. touchableHandleResponderRelease ( e ) ;
165
- this . props . onResponderRelease &&
166
- this . props . onResponderRelease . apply ( this , arguments ) ;
167
- } . bind ( this ) ,
168
- onResponderTerminate : function ( e : SyntheticEvent < > ) {
169
- // $FlowFixMe TouchableMixin handlers couldn't actually be null
170
- this. touchableHandleResponderTerminate ( e ) ;
171
- this . props . onResponderTerminate &&
172
- this . props . onResponderTerminate . apply ( this , arguments ) ;
173
- } . bind ( this ) ,
174
- onResponderTerminationRequest : function ( ) : boolean {
175
- // Allow touchable or props.onResponderTerminationRequest to deny
176
- // the request
177
- // $FlowFixMe TouchableMixin handlers couldn't actually be null
178
- var allowTermination = this . touchableHandleResponderTerminationRequest ( ) ;
179
- if ( allowTermination && this . props . onResponderTerminationRequest ) {
180
- allowTermination = this . props . onResponderTerminationRequest . apply (
181
- this ,
182
- arguments ,
183
- ) ;
184
- }
185
- return allowTermination ;
186
- } . bind ( this ) ,
187
- } ;
188
- }
189
- newProps = {
190
- ...this . props ,
191
- ...this . _handlers ,
114
+ render ( ) : React . Node {
115
+ let props = this . props ;
116
+ if ( isTouchable ( props ) ) {
117
+ props = {
118
+ ...props ,
119
+ ...this . state . responseHandlers ,
192
120
isHighlighted : this . state . isHighlighted ,
193
121
} ;
194
122
}
195
- if ( newProps . selectionColor != null ) {
196
- newProps = {
197
- ...newProps ,
198
- selectionColor : processColor ( newProps . selectionColor ) ,
123
+ if ( props . selectionColor != null ) {
124
+ props = {
125
+ ...props ,
126
+ selectionColor : processColor ( props . selectionColor ) ,
199
127
} ;
200
128
}
201
- if ( Touchable . TOUCH_TARGET_DEBUG && newProps . onPress ) {
202
- newProps = {
203
- ...newProps ,
204
- style : [ this . props . style , { color : 'magenta' } ] ,
205
- } ;
129
+ if ( __DEV__ ) {
130
+ if ( Touchable . TOUCH_TARGET_DEBUG && props . onPress != null ) {
131
+ props = {
132
+ ...props ,
133
+ style : [ props . style , { color : 'magenta' } ] ,
134
+ } ;
135
+ }
206
136
}
207
137
return (
208
138
< TextAncestor . Consumer >
209
139
{ hasTextAncestor =>
210
140
hasTextAncestor ? (
211
- < RCTVirtualText { ...newProps } />
141
+ < RCTVirtualText { ...props } />
212
142
) : (
213
143
< TextAncestor . Provider value = { true } >
214
- < RCTText { ...newProps } />
144
+ < RCTText { ...props } />
215
145
</ TextAncestor . Provider >
216
146
)
217
147
}
218
148
</ TextAncestor . Consumer >
219
149
) ;
220
150
}
151
+
152
+ _createResponseHandlers ( ) : ResponseHandlers {
153
+ return {
154
+ onStartShouldSetResponder : ( ) : boolean => {
155
+ const { onStartShouldSetResponder } = this . props ;
156
+ const shouldSetResponder =
157
+ ( onStartShouldSetResponder == null
158
+ ? false
159
+ : onStartShouldSetResponder ( ) ) || isTouchable ( this . props ) ;
160
+
161
+ if ( shouldSetResponder ) {
162
+ this . _attachTouchHandlers ( ) ;
163
+ }
164
+ return shouldSetResponder ;
165
+ } ,
166
+ onResponderGrant : ( event : SyntheticEvent < > , dispatchID : string ) : void => {
167
+ nullthrows ( this . touchableHandleResponderGrant ) ( event , dispatchID ) ;
168
+ if ( this . props . onResponderGrant != null ) {
169
+ this . props . onResponderGrant . apply ( this , arguments ) ;
170
+ }
171
+ } ,
172
+ onResponderMove : ( event : SyntheticEvent < > ) : void => {
173
+ nullthrows ( this . touchableHandleResponderMove ) ( event ) ;
174
+ if ( this . props . onResponderMove != null ) {
175
+ this . props . onResponderMove . apply ( this , arguments ) ;
176
+ }
177
+ } ,
178
+ onResponderRelease : ( event : SyntheticEvent < > ) : void => {
179
+ nullthrows ( this . touchableHandleResponderRelease ) ( event ) ;
180
+ if ( this . props . onResponderRelease != null ) {
181
+ this . props . onResponderRelease . apply ( this , arguments ) ;
182
+ }
183
+ } ,
184
+ onResponderTerminate : ( event : SyntheticEvent < > ) : void => {
185
+ nullthrows ( this . touchableHandleResponderTerminate ) ( event ) ;
186
+ if ( this . props . onResponderTerminate != null ) {
187
+ this . props . onResponderTerminate . apply ( this , arguments ) ;
188
+ }
189
+ } ,
190
+ onResponderTerminationRequest : ( ) : boolean => {
191
+ const { onResponderTerminationRequest} = this . props ;
192
+ if ( ! nullthrows ( this . touchableHandleResponderTerminationRequest ) ( ) ) {
193
+ return false ;
194
+ }
195
+ if ( onResponderTerminationRequest == null ) {
196
+ return true ;
197
+ }
198
+ return onResponderTerminationRequest ( ) ;
199
+ } ,
200
+ } ;
201
+ }
202
+
203
+ /**
204
+ * Lazily attaches Touchable.Mixin handlers.
205
+ */
206
+ _attachTouchHandlers ( ) : void {
207
+ if ( this . touchableGetPressRectOffset != null ) {
208
+ return ;
209
+ }
210
+ for ( const key in Touchable . Mixin ) {
211
+ if ( typeof Touchable . Mixin [ key ] === 'function' ) {
212
+ ( this : any ) [ key ] = Touchable . Mixin [ key ] . bind ( this ) ;
213
+ }
214
+ }
215
+ this . touchableHandleActivePressIn = ( ) : void => {
216
+ if ( ! this . props . suppressHighlighting && isTouchable ( this . props ) ) {
217
+ this . setState ( { isHighlighted : true } ) ;
218
+ }
219
+ } ;
220
+ this . touchableHandleActivePressOut = ( ) : void => {
221
+ if ( ! this . props . suppressHighlighting && isTouchable ( this . props ) ) {
222
+ this . setState ( { isHighlighted : false } ) ;
223
+ }
224
+ } ;
225
+ this . touchableHandlePress = ( event : PressEvent ) : void => {
226
+ if ( this . props . onPress != null ) {
227
+ this . props . onPress ( event ) ;
228
+ }
229
+ } ;
230
+ this . touchableHandleLongPress = ( event : PressEvent ) : void => {
231
+ if ( this . props . onLongPress != null ) {
232
+ this . props . onLongPress ( event ) ;
233
+ }
234
+ } ;
235
+ this . touchableGetPressRectOffset = ( ) : PressRetentionOffset =>
236
+ this . props . pressRetentionOffset == null
237
+ ? PRESS_RECT_OFFSET
238
+ : this . props . pressRetentionOffset ;
239
+ }
221
240
}
222
241
223
- var RCTText = createReactNativeComponentClass (
242
+ const isTouchable = ( props : Props ) : boolean =>
243
+ props . onPress != null ||
244
+ props . onLongPress != null ||
245
+ props . onStartShouldSetResponder != null ;
246
+
247
+ const RCTText = createReactNativeComponentClass (
224
248
viewConfig . uiViewClassName ,
225
249
( ) => viewConfig ,
226
250
) ;
227
- var RCTVirtualText = RCTText ;
228
-
229
- if ( UIManager . RCTVirtualText ) {
230
- RCTVirtualText = createReactNativeComponentClass ( 'RCTVirtualText' , ( ) => ( {
231
- validAttributes : {
232
- ... ReactNativeViewAttributes . UIView ,
233
- isHighlighted : true ,
234
- } ,
235
- uiViewClassName : 'RCTVirtualText' ,
236
- } ) ) ;
237
- }
251
+
252
+ const RCTVirtualText =
253
+ UIManager . RCTVirtualText == null
254
+ ? RCTText
255
+ : createReactNativeComponentClass ( 'RCTVirtualText' , ( ) => ( {
256
+ validAttributes : {
257
+ ... ReactNativeViewAttributes . UIView ,
258
+ isHighlighted : true ,
259
+ } ,
260
+ uiViewClassName : 'RCTVirtualText' ,
261
+ } ) ) ;
238
262
239
263
module . exports = Text ;
0 commit comments