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 |
|
11 | import type {EventSubscription} from '../../vendor/emitter/EventEmitter';
|
12 |
|
13 | import NativeEventEmitter from '../../EventEmitter/NativeEventEmitter';
|
14 | import LayoutAnimation from '../../LayoutAnimation/LayoutAnimation';
|
15 | import dismissKeyboard from '../../Utilities/dismissKeyboard';
|
16 | import Platform from '../../Utilities/Platform';
|
17 | import NativeKeyboardObserver from './NativeKeyboardObserver';
|
18 |
|
19 | export type KeyboardEventName = $Keys<KeyboardEventDefinitions>;
|
20 |
|
21 | export type KeyboardEventEasing =
|
22 | | 'easeIn'
|
23 | | 'easeInEaseOut'
|
24 | | 'easeOut'
|
25 | | 'linear'
|
26 | | 'keyboard';
|
27 |
|
28 | export type KeyboardMetrics = $ReadOnly<{|
|
29 | screenX: number,
|
30 | screenY: number,
|
31 | width: number,
|
32 | height: number,
|
33 | |}>;
|
34 |
|
35 | export type KeyboardEvent = AndroidKeyboardEvent | IOSKeyboardEvent;
|
36 |
|
37 | type BaseKeyboardEvent = {|
|
38 | duration: number,
|
39 | easing: KeyboardEventEasing,
|
40 | endCoordinates: KeyboardMetrics,
|
41 | |};
|
42 |
|
43 | export type AndroidKeyboardEvent = $ReadOnly<{|
|
44 | ...BaseKeyboardEvent,
|
45 | duration: 0,
|
46 | easing: 'keyboard',
|
47 | |}>;
|
48 |
|
49 | export type IOSKeyboardEvent = $ReadOnly<{|
|
50 | ...BaseKeyboardEvent,
|
51 | startCoordinates: KeyboardMetrics,
|
52 | isEventFromThisApp: boolean,
|
53 | |}>;
|
54 |
|
55 | type 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 |
|
106 | class 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 |
|
205 | module.exports = (new Keyboard(): Keyboard);
|