@@ -12,11 +12,27 @@ import applyLayout from '../../modules/applyLayout';
12
12
import applyNativeMethods from '../../modules/applyNativeMethods' ;
13
13
import { bool } from 'prop-types' ;
14
14
import { Component } from 'react' ;
15
+ import nullthrows from 'nullthrows' ;
15
16
import createElement from '../createElement' ;
16
17
import StyleSheet from '../StyleSheet' ;
17
18
import TextPropTypes from './TextPropTypes' ;
19
+ import Touchable from '../Touchable' ;
18
20
19
- class Text extends Component < * > {
21
+ type ResponseHandlers = { |
22
+ onStartShouldSetResponder : ( ) => boolean ,
23
+ onResponderGrant : ( event : * , dispatchID : string ) => void ,
24
+ onResponderMove : ( event : * ) => void ,
25
+ onResponderRelease : ( event : * ) => void ,
26
+ onResponderTerminate : ( event : * ) => void ,
27
+ onResponderTerminationRequest : ( ) = > boolean
28
+ | } ;
29
+
30
+ const isTouchable = ( props : * ) : boolean =>
31
+ props . onPress != null || props . onLongPress != null || props . onStartShouldSetResponder != null ;
32
+
33
+ const PRESS_RECT_OFFSET = { top : 20 , left : 20 , right : 20 , bottom : 30 } ;
34
+
35
+ class Text extends Component < * , * > {
20
36
static displayName = 'Text' ;
21
37
22
38
static propTypes = TextPropTypes ;
@@ -29,6 +45,40 @@ class Text extends Component<*> {
29
45
isInAParentText : bool
30
46
} ;
31
47
48
+ static getDerivedStateFromProps ( nextProps : * , prevState : * ) : * | null {
49
+ return prevState . responseHandlers == null && isTouchable ( nextProps )
50
+ ? {
51
+ responseHandlers : prevState . createResponderHandlers ( )
52
+ }
53
+ : null ;
54
+ }
55
+
56
+ constructor ( props ) {
57
+ super ( props ) ;
58
+ if ( isTouchable ( props ) ) this . _attachTouchHandlers ( ) ;
59
+ }
60
+
61
+ state = {
62
+ ...Touchable . Mixin . touchableGetInitialState ( ) ,
63
+ isHighlighted : false ,
64
+ createResponderHandlers : this . _createResponseHandlers . bind ( this ) ,
65
+ responseHandlers : null
66
+ } ;
67
+
68
+ // Flow definitions for touchable events
69
+ touchableGetPressRectOffset : ?( ) = > * ;
70
+ touchableHandleActivePressIn : ?( ) = > void ;
71
+ touchableHandleActivePressOut : ?( ) = > void ;
72
+ touchableHandleKeyEvent : ?( event : * ) => void ;
73
+ touchableHandleLongPress : ?( event : * ) => void ;
74
+ touchableHandlePress : ?( event : * ) => void ;
75
+ touchableHandleResponderGrant : ?( event : * , dispatchID : string ) = > void ;
76
+ touchableHandleResponderMove : ?( event : * ) => void ;
77
+ touchableHandleResponderRelease : ?( event : * ) => void ;
78
+ touchableHandleResponderTerminate : ?( event : * ) => void ;
79
+ touchableHandleResponderTerminationRequest : ?( ) = > boolean ;
80
+ touchableHandleStartShouldSetResponder : ?( ) = > boolean ;
81
+
32
82
getChildContext ( ) {
33
83
return { isInAParentText : true } ;
34
84
}
@@ -37,7 +87,6 @@ class Text extends Component<*> {
37
87
const {
38
88
dir,
39
89
numberOfLines,
40
- onPress ,
41
90
selectable,
42
91
style,
43
92
/* eslint-disable */
@@ -48,6 +97,7 @@ class Text extends Component<*> {
48
97
minimumFontScale,
49
98
onLayout,
50
99
onLongPress,
100
+ onPress,
51
101
pressRetentionOffset,
52
102
selectionColor,
53
103
suppressHighlighting,
@@ -59,10 +109,16 @@ class Text extends Component<*> {
59
109
60
110
const { isInAParentText } = this . context ;
61
111
62
- if ( onPress ) {
112
+ if ( isTouchable ( this . props ) ) {
63
113
otherProps . accessible = true ;
64
- otherProps . onClick = this . _createPressHandler ( onPress ) ;
65
- otherProps . onKeyDown = this . _createEnterHandler ( onPress ) ;
114
+ otherProps . onKeyDown = this . touchableHandleKeyEvent ;
115
+ otherProps . onKeyUp = this . touchableHandleKeyEvent ;
116
+ otherProps . onResponderGrant = this . touchableHandleResponderGrant ;
117
+ otherProps . onResponderMove = this . touchableHandleResponderMove ;
118
+ otherProps . onResponderRelease = this . touchableHandleResponderRelease ;
119
+ otherProps . onResponderTerminate = this . touchableHandleResponderTerminate ;
120
+ otherProps . onResponderTerminationRequest = this . touchableHandleResponderTerminationRequest ;
121
+ otherProps . onStartShouldSetResponder = this . touchableHandleStartShouldSetResponder ;
66
122
}
67
123
68
124
// allow browsers to automatically infer the language writing direction
@@ -73,27 +129,89 @@ class Text extends Component<*> {
73
129
style ,
74
130
selectable === false && styles . notSelectable ,
75
131
numberOfLines === 1 && styles . singleLineStyle ,
76
- onPress && styles . pressable
132
+ isTouchable ( this . props ) && styles . pressable
77
133
] ;
78
134
79
135
const component = isInAParentText ? 'span ' : 'div ';
80
-
81
136
return createElement ( component , otherProps ) ;
82
137
}
83
138
84
- _createEnterHandler ( fn ) {
85
- return e => {
86
- if ( e . keyCode === 13 ) {
87
- fn && fn ( e ) ;
139
+ _createResponseHandlers ( ) : ResponseHandlers {
140
+ return {
141
+ onStartShouldSetResponder : ( ) : boolean => {
142
+ return isTouchable ( this . props ) ;
143
+ } ,
144
+ onResponderGrant : ( event : * , dispatchID : string ) : void => {
145
+ nullthrows ( this . touchableHandleResponderGrant ) ( event , dispatchID ) ;
146
+ if ( this . props . onResponderGrant != null ) {
147
+ this . props . onResponderGrant . call ( this , event , dispatchID ) ;
148
+ }
149
+ } ,
150
+ onResponderMove : ( event : * ) : void => {
151
+ nullthrows ( this . touchableHandleResponderMove ) ( event ) ;
152
+ if ( this . props . onResponderMove != null ) {
153
+ this . props . onResponderMove . call ( this , event ) ;
154
+ }
155
+ } ,
156
+ onResponderRelease : ( event : * ) : void => {
157
+ nullthrows ( this . touchableHandleResponderRelease ) ( event ) ;
158
+ if ( this . props . onResponderRelease != null ) {
159
+ this . props . onResponderRelease . call ( this , event ) ;
160
+ }
161
+ } ,
162
+ onResponderTerminate : ( event : * ) : void => {
163
+ nullthrows ( this . touchableHandleResponderTerminate ) ( event ) ;
164
+ if ( this . props . onResponderTerminate != null ) {
165
+ this . props . onResponderTerminate . call ( this , event ) ;
166
+ }
167
+ } ,
168
+ onResponderTerminationRequest : ( ) : boolean => {
169
+ const { onResponderTerminationRequest } = this . props ;
170
+ if ( ! nullthrows ( this . touchableHandleResponderTerminationRequest ) ( ) ) {
171
+ return false ;
172
+ }
173
+ if ( onResponderTerminationRequest == null ) {
174
+ return true ;
175
+ }
176
+ return onResponderTerminationRequest ( ) ;
88
177
}
89
178
} ;
90
179
}
91
180
92
- _createPressHandler ( fn ) {
93
- return e => {
94
- e . stopPropagation ( ) ;
95
- fn && fn ( e ) ;
181
+ /**
182
+ * Lazily attaches Touchable.Mixin handlers.
183
+ */
184
+ _attachTouchHandlers ( ) : void {
185
+ if ( this . touchableGetPressRectOffset != null ) {
186
+ return ;
187
+ }
188
+ for ( const key in Touchable . Mixin ) {
189
+ if ( typeof Touchable . Mixin [ key ] === 'function' ) {
190
+ ( this : any ) [ key ] = Touchable . Mixin [ key ] . bind ( this ) ;
191
+ }
192
+ }
193
+ this . touchableHandleActivePressIn = ( ) : void => {
194
+ if ( ! this . props . suppressHighlighting && isTouchable ( this . props ) ) {
195
+ this . setState ( { isHighlighted : true } ) ;
196
+ }
197
+ } ;
198
+ this . touchableHandleActivePressOut = ( ) : void => {
199
+ if ( ! this . props . suppressHighlighting && isTouchable ( this . props ) ) {
200
+ this . setState ( { isHighlighted : false } ) ;
201
+ }
202
+ } ;
203
+ this . touchableHandlePress = ( event : * ) : void => {
204
+ if ( this . props . onPress != null ) {
205
+ this . props . onPress ( event ) ;
206
+ }
207
+ } ;
208
+ this . touchableHandleLongPress = ( event : * ) : void => {
209
+ if ( this . props . onLongPress != null ) {
210
+ this . props . onLongPress ( event ) ;
211
+ }
96
212
} ;
213
+ this . touchableGetPressRectOffset = ( ) : * =>
214
+ this . props . pressRetentionOffset == null ? PRESS_RECT_OFFSET : this . props . pressRetentionOffset ;
97
215
}
98
216
}
99
217
0 commit comments