Last active
March 29, 2019 22:31
-
-
Save vovkasm/f10e1f1c24e355ab242aabc98ba07fe0 to your computer and use it in GitHub Desktop.
How to hide keyboard by click outside text field withotu ScrollView
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// HACK | |
import { | |
blurTextInput, | |
currentlyFocusedField, | |
focusTextInput, | |
isTextInput, | |
} from 'react-native/Libraries/Components/TextInput/TextInputState' | |
export { blurTextInput, currentlyFocusedField, focusTextInput, isTextInput } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// tslint:disable:object-literal-sort-keys | |
import React from 'react' | |
import { GestureResponderEvent, LayoutRectangle, NativeTouchEvent, UIManager } from 'react-native' | |
// HACK | |
import { blurTextInput, currentlyFocusedField, isTextInput } from 'App/lib/extendedTextState' | |
/** | |
* Touchable states. | |
*/ | |
enum States { | |
NOT_RESPONDER, // Not the responder | |
PRESS_IN, // Responder, active, in the `PressRect` | |
PRESS_OUT, // Responder, active, out of `PressRect` | |
} | |
const PRESS_EXPAND_PX = 20 | |
interface IProps { | |
disabled?: boolean | |
} | |
export class KeyboardDissmisser extends React.Component<IProps> { | |
private touchState: States = States.NOT_RESPONDER | |
private responderID: number | undefined = undefined | |
private viewLayout: LayoutRectangle | undefined = undefined | |
render() { | |
if (!this.props.children) return null | |
// TODO(vovkasm): fix this shit | |
const child = React.Children.only(this.props.children) as any | |
return React.cloneElement(child, { | |
accessible: false, | |
children: child.props.children, | |
onResponderGrant: this.onResponderGrant, | |
onResponderMove: this.onResponderMove, | |
onResponderRelease: this.onResponderRelease, | |
onResponderTerminate: this.onResponderTerminate, | |
onResponderTerminationRequest: this.onResponderTerminationRequest, | |
onStartShouldSetResponder: this.onStartShouldSetResponder, | |
}) | |
} | |
private onResponderTerminationRequest = () => { | |
return true | |
} | |
/** | |
* Must return true to start the process of `Touchable`. | |
*/ | |
private onStartShouldSetResponder = (e: GestureResponderEvent) => { | |
if (this.props.disabled) return false | |
const currentlyFocusedTextInput = currentlyFocusedField() | |
return currentlyFocusedTextInput != null && e.target !== currentlyFocusedTextInput | |
} | |
/** | |
* Place as callback for a DOM element's `onResponderGrant` event. | |
* @param {SyntheticEvent} e Synthetic event from event system. | |
* | |
*/ | |
private onResponderGrant = (e: GestureResponderEvent) => { | |
this.responderID = e.currentTarget | |
this.touchState = States.PRESS_IN | |
this.measureView() | |
} | |
/** | |
* Place as callback for a DOM element's `onResponderRelease` event. | |
*/ | |
private onResponderRelease = (e: GestureResponderEvent) => { | |
if (this.touchState === States.PRESS_IN) this.handlePress(e) | |
this.touchState = States.NOT_RESPONDER | |
} | |
/** | |
* Place as callback for a DOM element's `onResponderTerminate` event. | |
*/ | |
private onResponderTerminate = (e: GestureResponderEvent) => { | |
this.touchState = States.NOT_RESPONDER | |
} | |
/** | |
* Place as callback for a DOM element's `onResponderMove` event. | |
*/ | |
private onResponderMove = (e: GestureResponderEvent) => { | |
if (this.touchState === States.NOT_RESPONDER) return | |
// Measurement may not have returned yet. | |
if (!this.viewLayout) return | |
const viewLayout = this.viewLayout | |
const viewX = viewLayout.x | |
const viewY = viewLayout.y | |
const viewW = viewLayout.width | |
const viewH = viewLayout.height | |
const touch = extractSingleTouch(e.nativeEvent) | |
const x = touch.pageX | |
const y = touch.pageY | |
const isTouchWithinActive = | |
x > viewX - PRESS_EXPAND_PX && | |
y > viewY - PRESS_EXPAND_PX && | |
x < viewX + viewW + PRESS_EXPAND_PX && | |
y < viewY + viewH + PRESS_EXPAND_PX | |
this.touchState = isTouchWithinActive ? States.PRESS_IN : States.PRESS_OUT | |
} | |
private handlePress(e: GestureResponderEvent) { | |
const current = currentlyFocusedField() | |
if (current && !isTextInput(e.target)) { | |
blurTextInput(current) | |
} | |
} | |
/** | |
* Measures the `HitRect` node on activation. | |
*/ | |
private measureView() { | |
if (!this.responderID) return | |
UIManager.measure(this.responderID, this.handleMeasure) | |
} | |
private handleMeasure = (x: number, y: number, width: number, height: number, pageX: number, pageY: number) => { | |
// UIManager can't measure | |
if (!width && !height) return | |
if (this.viewLayout) { | |
const viewLayout = this.viewLayout | |
viewLayout.x = pageX | |
viewLayout.y = pageY | |
viewLayout.width = width | |
viewLayout.height = height | |
} else { | |
this.viewLayout = { x: pageX, y: pageY, width, height } | |
} | |
} | |
} | |
// TODO(vovkasm): Is this needed for RN? | |
/** | |
* Utility function for common case of extracting out the primary touch from a | |
* touch event. | |
* - `touchEnd` events usually do not have the `touches` property. | |
* http://stackoverflow.com/questions/3666929/ | |
* mobile-sarai-touchend-event-not-firing-when-last-touch-is-removed | |
* | |
* @param nativeEvent Native event that may or may not be a touch. | |
* @return an object with pageX and pageY or null. | |
*/ | |
function extractSingleTouch(nativeEvent: NativeTouchEvent): NativeTouchEvent { | |
const touches = nativeEvent.touches | |
const changedTouches = nativeEvent.changedTouches | |
const hasTouches = touches && touches.length > 0 | |
const hasChangedTouches = changedTouches && changedTouches.length > 0 | |
return !hasTouches && hasChangedTouches ? changedTouches[0] : hasTouches ? touches[0] : nativeEvent | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment