UNPKG

5.78 kBJavaScriptView Raw
1/**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @format
8 * @flow strict-local
9 */
10
11import type {EventSubscription} from '../../vendor/emitter/EventEmitter';
12
13import NativeEventEmitter from '../../EventEmitter/NativeEventEmitter';
14import LayoutAnimation from '../../LayoutAnimation/LayoutAnimation';
15import dismissKeyboard from '../../Utilities/dismissKeyboard';
16import Platform from '../../Utilities/Platform';
17import NativeKeyboardObserver from './NativeKeyboardObserver';
18
19export type KeyboardEventName = $Keys<KeyboardEventDefinitions>;
20
21export type KeyboardEventEasing =
22 | 'easeIn'
23 | 'easeInEaseOut'
24 | 'easeOut'
25 | 'linear'
26 | 'keyboard';
27
28export type KeyboardMetrics = $ReadOnly<{|
29 screenX: number,
30 screenY: number,
31 width: number,
32 height: number,
33|}>;
34
35export type KeyboardEvent = AndroidKeyboardEvent | IOSKeyboardEvent;
36
37type BaseKeyboardEvent = {|
38 duration: number,
39 easing: KeyboardEventEasing,
40 endCoordinates: KeyboardMetrics,
41|};
42
43export type AndroidKeyboardEvent = $ReadOnly<{|
44 ...BaseKeyboardEvent,
45 duration: 0,
46 easing: 'keyboard',
47|}>;
48
49export type IOSKeyboardEvent = $ReadOnly<{|
50 ...BaseKeyboardEvent,
51 startCoordinates: KeyboardMetrics,
52 isEventFromThisApp: boolean,
53|}>;
54
55type KeyboardEventDefinitions = {
56 keyboardWillShow: [KeyboardEvent],
57 keyboardDidShow: [KeyboardEvent],
58 keyboardWillHide: [KeyboardEvent],
59 keyboardDidHide: [KeyboardEvent],
60 keyboardWillChangeFrame: [KeyboardEvent],
61 keyboardDidChangeFrame: [KeyboardEvent],
62};
63
64/**
65 * `Keyboard` module to control keyboard events.
66 *
67 * ### Usage
68 *
69 * The Keyboard module allows you to listen for native events and react to them, as
70 * well as make changes to the keyboard, like dismissing it.
71 *
72 *```
73 * import React, { Component } from 'react';
74 * import { Keyboard, TextInput } from 'react-native';
75 *
76 * class Example extends Component {
77 * componentWillMount () {
78 * this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow);
79 * this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
80 * }
81 *
82 * componentWillUnmount () {
83 * this.keyboardDidShowListener.remove();
84 * this.keyboardDidHideListener.remove();
85 * }
86 *
87 * _keyboardDidShow () {
88 * alert('Keyboard Shown');
89 * }
90 *
91 * _keyboardDidHide () {
92 * alert('Keyboard Hidden');
93 * }
94 *
95 * render() {
96 * return (
97 * <TextInput
98 * onSubmitEditing={Keyboard.dismiss}
99 * />
100 * );
101 * }
102 * }
103 *```
104 */
105
106class Keyboard {
107 _currentlyShowing: ?KeyboardEvent;
108
109 _emitter: NativeEventEmitter<KeyboardEventDefinitions> =
110 new NativeEventEmitter(
111 // T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
112 // If you want to use the native module on other platforms, please remove this condition and test its behavior
113 Platform.OS !== 'ios' ? null : NativeKeyboardObserver,
114 );
115
116 constructor() {
117 this.addListener('keyboardDidShow', ev => {
118 this._currentlyShowing = ev;
119 });
120 this.addListener('keyboardDidHide', _ev => {
121 this._currentlyShowing = null;
122 });
123 }
124
125 /**
126 * The `addListener` function connects a JavaScript function to an identified native
127 * keyboard notification event.
128 *
129 * This function then returns the reference to the listener.
130 *
131 * @param {string} eventName The `nativeEvent` is the string that identifies the event you're listening for. This
132 *can be any of the following:
133 *
134 * - `keyboardWillShow`
135 * - `keyboardDidShow`
136 * - `keyboardWillHide`
137 * - `keyboardDidHide`
138 * - `keyboardWillChangeFrame`
139 * - `keyboardDidChangeFrame`
140 *
141 * Android versions prior to API 30 rely on observing layout changes when
142 * `android:windowSoftInputMode` is set to `adjustResize` or `adjustPan`.
143 *
144 * `keyboardWillShow` as well as `keyboardWillHide` are not available on Android since there is
145 * no native corresponding event.
146 *
147 * @param {function} callback function to be called when the event fires.
148 */
149 addListener<K: $Keys<KeyboardEventDefinitions>>(
150 eventType: K,
151 listener: (...$ElementType<KeyboardEventDefinitions, K>) => mixed,
152 context?: mixed,
153 ): EventSubscription {
154 return this._emitter.addListener(eventType, listener);
155 }
156
157 /**
158 * Removes all listeners for a specific event type.
159 *
160 * @param {string} eventType The native event string listeners are watching which will be removed.
161 */
162 removeAllListeners<K: $Keys<KeyboardEventDefinitions>>(eventType: ?K): void {
163 this._emitter.removeAllListeners(eventType);
164 }
165
166 /**
167 * Dismisses the active keyboard and removes focus.
168 */
169 dismiss(): void {
170 dismissKeyboard();
171 }
172
173 /**
174 * Whether the keyboard is last known to be visible.
175 */
176 isVisible(): boolean {
177 return !!this._currentlyShowing;
178 }
179
180 /**
181 * Return the metrics of the soft-keyboard if visible.
182 */
183 metrics(): ?KeyboardMetrics {
184 return this._currentlyShowing?.endCoordinates;
185 }
186
187 /**
188 * Useful for syncing TextInput (or other keyboard accessory view) size of
189 * position changes with keyboard movements.
190 */
191 scheduleLayoutAnimation(event: KeyboardEvent): void {
192 const {duration, easing} = event;
193 if (duration != null && duration !== 0) {
194 LayoutAnimation.configureNext({
195 duration: duration,
196 update: {
197 duration: duration,
198 type: (easing != null && LayoutAnimation.Types[easing]) || 'keyboard',
199 },
200 });
201 }
202 }
203}
204
205module.exports = (new Keyboard(): Keyboard);