From ea04202e4f2efc82588d264917af24afbff77e69 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 15 Mar 2018 10:31:52 -0500 Subject: [PATCH 1/2] Implements Scroll Into View --- README.md | 1 + lib/KeyboardAwareHOC.js | 73 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 81da024..225e3f5 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,7 @@ Use `innerRef` to get the component reference and use `this.scrollRef.props` to | `getScrollResponder` | `void` | Get `ScrollResponder` | | `scrollToPosition` | `x: number, y: number, animated: bool = true` | Scroll to specific position with or without animation. | | `scrollToEnd` | `animated?: bool = true` | Scroll to end with or without animation. | +| `scrollIntoView` | `element: React.Element<*>, options: { getScrollPosition: ?(parentLayout, childLayout, contentOffset) => { x: number, y: number, animated: boolean } }` | Scrolls an element inside a KeyboardAwareScrollView into view. | ### Using high order component Enabling any component to be keyboard-aware is very easy. Take a look at the code of `KeyboardAwareListView`: diff --git a/lib/KeyboardAwareHOC.js b/lib/KeyboardAwareHOC.js index 6bbe295..aa3b5be 100644 --- a/lib/KeyboardAwareHOC.js +++ b/lib/KeyboardAwareHOC.js @@ -6,7 +6,8 @@ import ReactNative, { Keyboard, Platform, UIManager, - TextInput + TextInput, + findNodeHandle } from 'react-native' import { isIphoneX } from 'react-native-iphone-x-helper' @@ -36,6 +37,32 @@ export type KeyboardAwareHOCState = { keyboardSpace: number } +export type ElementLayout = { + x: number, + y: number, + width: number, + height: number +} + +export type ContentOffset = { + x: number, + y: number +} + +export type ScrollPosition = { + x: number, + y: number, + animated: boolean +} + +export type ScrollIntoViewOptions = ?{ + getScrollPosition?: ( + parentLayout: ElementLayout, + childLayout: ElementLayout, + contentOffset: ContentOffset + ) => ScrollPosition +} + function listenToKeyboardEvents(ScrollableComponent: React$Component) { return class extends React.Component< KeyboardAwareHOCProps, @@ -44,7 +71,7 @@ function listenToKeyboardEvents(ScrollableComponent: React$Component) { _rnkasv_keyboardView: any keyboardWillShowEvent: ?Function keyboardWillHideEvent: ?Function - position: { x: number, y: number } + position: ContentOffset defaultResetScrollToCoords: ?{ x: number, y: number } resetCoords: ?{ x: number, y: number } mountedComponent: boolean @@ -180,6 +207,48 @@ function listenToKeyboardEvents(ScrollableComponent: React$Component) { }, keyboardOpeningTime) } + scrollIntoView = async ( + element: React.Element<*>, + options: ScrollIntoViewOptions = {} + ) => { + if (!this._rnkasv_keyboardView || !view) { + return + } + + const [ + parentLayout, + childLayout + ] = await Promise.all([ + this._measureElement(this._rnkasv_keyboardView), + this._measureElement(element) + ]) + + const getScrollPosition = options.getScrollPosition || this._defaultGetScrollPosition + const { x, y, animated } = getScrollPosition(parentLayout, childLayout, this.position) + this._container.scrollToPosition(x, y, animated) + } + + _defaultGetScrollPosition = ( + parentLayout: ElementLayout, + childLayout: ElementLayout, + contentOffset: ContentOffset + ): ScrollPosition => { + return { + x: 0, + y: Math.max(0, childLayout.y - parentLayout.y + contentOffset.y), + animated: true, + } + } + + _measureElement = (element: React.Element<*>): Promise => { + const node = findNodeHandle(element) + return new Promise((resolve) => { + UIManager.measureInWindow(node, (x, y, width, height) => { + resolve({ x, y, width, height }) + }) + }) + } + // Keyboard actions _updateKeyboardSpace = (frames: Object) => { // Automatically scroll to focused TextInput From 3d258940529f750adf2ca1e47435b2b2c27cc534 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 15 Mar 2018 17:19:14 -0500 Subject: [PATCH 2/2] fix linting issues and incorrect implementation --- lib/KeyboardAwareHOC.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/KeyboardAwareHOC.js b/lib/KeyboardAwareHOC.js index aa3b5be..4108adc 100644 --- a/lib/KeyboardAwareHOC.js +++ b/lib/KeyboardAwareHOC.js @@ -211,7 +211,7 @@ function listenToKeyboardEvents(ScrollableComponent: React$Component) { element: React.Element<*>, options: ScrollIntoViewOptions = {} ) => { - if (!this._rnkasv_keyboardView || !view) { + if (!this._rnkasv_keyboardView || !element) { return } @@ -225,7 +225,7 @@ function listenToKeyboardEvents(ScrollableComponent: React$Component) { const getScrollPosition = options.getScrollPosition || this._defaultGetScrollPosition const { x, y, animated } = getScrollPosition(parentLayout, childLayout, this.position) - this._container.scrollToPosition(x, y, animated) + this.scrollToPosition(x, y, animated) } _defaultGetScrollPosition = ( @@ -242,8 +242,8 @@ function listenToKeyboardEvents(ScrollableComponent: React$Component) { _measureElement = (element: React.Element<*>): Promise => { const node = findNodeHandle(element) - return new Promise((resolve) => { - UIManager.measureInWindow(node, (x, y, width, height) => { + return new Promise((resolve: (ElementLayout) => void) => { + UIManager.measureInWindow(node, (x: number, y: number, width: number, height: number) => { resolve({ x, y, width, height }) }) })