This commit is contained in:
Yamozha
2021-04-02 02:24:13 +03:00
parent c23950b545
commit 7256d79e2c
31493 changed files with 3036630 additions and 0 deletions

View File

@ -0,0 +1,195 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow strict-local
*/
'use strict';
const LayoutAnimation = require('../../LayoutAnimation/LayoutAnimation');
const NativeEventEmitter = require('../../EventEmitter/NativeEventEmitter');
const dismissKeyboard = require('../../Utilities/dismissKeyboard');
const invariant = require('invariant');
import NativeKeyboardObserver from './NativeKeyboardObserver';
const KeyboardEventEmitter: NativeEventEmitter = new NativeEventEmitter(
NativeKeyboardObserver,
);
export type KeyboardEventName =
| 'keyboardWillShow'
| 'keyboardDidShow'
| 'keyboardWillHide'
| 'keyboardDidHide'
| 'keyboardWillChangeFrame'
| 'keyboardDidChangeFrame';
export type KeyboardEventEasing =
| 'easeIn'
| 'easeInEaseOut'
| 'easeOut'
| 'linear'
| 'keyboard';
export type KeyboardEventCoordinates = $ReadOnly<{|
screenX: number,
screenY: number,
width: number,
height: number,
|}>;
export type KeyboardEvent = AndroidKeyboardEvent | IOSKeyboardEvent;
type BaseKeyboardEvent = {|
duration: number,
easing: KeyboardEventEasing,
endCoordinates: KeyboardEventCoordinates,
|};
export type AndroidKeyboardEvent = $ReadOnly<{|
...BaseKeyboardEvent,
duration: 0,
easing: 'keyboard',
|}>;
export type IOSKeyboardEvent = $ReadOnly<{|
...BaseKeyboardEvent,
startCoordinates: KeyboardEventCoordinates,
isEventFromThisApp: boolean,
|}>;
type KeyboardEventListener = (e: KeyboardEvent) => void;
// The following object exists for documentation purposes
// Actual work happens in
// https://github.com/facebook/react-native/blob/master/Libraries/EventEmitter/NativeEventEmitter.js
/**
* `Keyboard` module to control keyboard events.
*
* ### Usage
*
* The Keyboard module allows you to listen for native events and react to them, as
* well as make changes to the keyboard, like dismissing it.
*
*```
* import React, { Component } from 'react';
* import { Keyboard, TextInput } from 'react-native';
*
* class Example extends Component {
* componentWillMount () {
* this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow);
* this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
* }
*
* componentWillUnmount () {
* this.keyboardDidShowListener.remove();
* this.keyboardDidHideListener.remove();
* }
*
* _keyboardDidShow () {
* alert('Keyboard Shown');
* }
*
* _keyboardDidHide () {
* alert('Keyboard Hidden');
* }
*
* render() {
* return (
* <TextInput
* onSubmitEditing={Keyboard.dismiss}
* />
* );
* }
* }
*```
*/
const Keyboard = {
/**
* The `addListener` function connects a JavaScript function to an identified native
* keyboard notification event.
*
* This function then returns the reference to the listener.
*
* @param {string} eventName The `nativeEvent` is the string that identifies the event you're listening for. This
*can be any of the following:
*
* - `keyboardWillShow`
* - `keyboardDidShow`
* - `keyboardWillHide`
* - `keyboardDidHide`
* - `keyboardWillChangeFrame`
* - `keyboardDidChangeFrame`
*
* Note that if you set `android:windowSoftInputMode` to `adjustResize` or `adjustNothing`,
* only `keyboardDidShow` and `keyboardDidHide` events will be available on Android.
* `keyboardWillShow` as well as `keyboardWillHide` are generally not available on Android
* since there is no native corresponding event.
*
* @param {function} callback function to be called when the event fires.
*/
addListener(eventName: KeyboardEventName, callback: KeyboardEventListener) {
invariant(false, 'Dummy method used for documentation');
},
/**
* Removes a specific listener.
*
* @param {string} eventName The `nativeEvent` is the string that identifies the event you're listening for.
* @param {function} callback function to be called when the event fires.
*/
removeListener(
eventName: KeyboardEventName,
callback: KeyboardEventListener,
) {
invariant(false, 'Dummy method used for documentation');
},
/**
* Removes all listeners for a specific event type.
*
* @param {string} eventType The native event string listeners are watching which will be removed.
*/
removeAllListeners(eventName: KeyboardEventName) {
invariant(false, 'Dummy method used for documentation');
},
/**
* Dismisses the active keyboard and removes focus.
*/
dismiss() {
invariant(false, 'Dummy method used for documentation');
},
/**
* Useful for syncing TextInput (or other keyboard accessory view) size of
* position changes with keyboard movements.
*/
scheduleLayoutAnimation(event: KeyboardEvent) {
invariant(false, 'Dummy method used for documentation');
},
};
// Throw away the dummy object and reassign it to original module
KeyboardEventEmitter.dismiss = dismissKeyboard;
KeyboardEventEmitter.scheduleLayoutAnimation = function(event: KeyboardEvent) {
const {duration, easing} = event;
if (duration != null && duration !== 0) {
LayoutAnimation.configureNext({
duration: duration,
update: {
duration: duration,
type: (easing != null && LayoutAnimation.Types[easing]) || 'keyboard',
},
});
}
};
module.exports = KeyboardEventEmitter;

View File

@ -0,0 +1,230 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/
'use strict';
const Keyboard = require('./Keyboard');
const LayoutAnimation = require('../../LayoutAnimation/LayoutAnimation');
const Platform = require('../../Utilities/Platform');
const React = require('react');
const StyleSheet = require('../../StyleSheet/StyleSheet');
const View = require('../View/View');
import type {ViewStyleProp} from '../../StyleSheet/StyleSheet';
import type EmitterSubscription from '../../vendor/emitter/EmitterSubscription';
import type {
ViewProps,
ViewLayout,
ViewLayoutEvent,
} from '../View/ViewPropTypes';
import type {KeyboardEvent} from './Keyboard';
type Props = $ReadOnly<{|
...ViewProps,
/**
* Specify how to react to the presence of the keyboard.
*/
behavior?: ?('height' | 'position' | 'padding'),
/**
* Style of the content container when `behavior` is 'position'.
*/
contentContainerStyle?: ?ViewStyleProp,
/**
* Controls whether this `KeyboardAvoidingView` instance should take effect.
* This is useful when more than one is on the screen. Defaults to true.
*/
enabled: ?boolean,
/**
* Distance between the top of the user screen and the React Native view. This
* may be non-zero in some cases. Defaults to 0.
*/
keyboardVerticalOffset: number,
|}>;
type State = {|
bottom: number,
|};
/**
* View that moves out of the way when the keyboard appears by automatically
* adjusting its height, position, or bottom padding.
*/
class KeyboardAvoidingView extends React.Component<Props, State> {
static defaultProps: {|enabled: boolean, keyboardVerticalOffset: number|} = {
enabled: true,
keyboardVerticalOffset: 0,
};
_frame: ?ViewLayout = null;
_subscriptions: Array<EmitterSubscription> = [];
viewRef: {current: React.ElementRef<any> | null, ...};
_initialFrameHeight: number = 0;
constructor(props: Props) {
super(props);
this.state = {bottom: 0};
this.viewRef = React.createRef();
}
_relativeKeyboardHeight(keyboardFrame): number {
const frame = this._frame;
if (!frame || !keyboardFrame) {
return 0;
}
const keyboardY = keyboardFrame.screenY - this.props.keyboardVerticalOffset;
// Calculate the displacement needed for the view such that it
// no longer overlaps with the keyboard
return Math.max(frame.y + frame.height - keyboardY, 0);
}
_onKeyboardChange = (event: ?KeyboardEvent) => {
if (event == null) {
this.setState({bottom: 0});
return;
}
const {duration, easing, endCoordinates} = event;
const height = this._relativeKeyboardHeight(endCoordinates);
if (this.state.bottom === height) {
return;
}
if (duration && easing) {
LayoutAnimation.configureNext({
// We have to pass the duration equal to minimal accepted duration defined here: RCTLayoutAnimation.m
duration: duration > 10 ? duration : 10,
update: {
duration: duration > 10 ? duration : 10,
type: LayoutAnimation.Types[easing] || 'keyboard',
},
});
}
this.setState({bottom: height});
};
_onLayout = (event: ViewLayoutEvent) => {
this._frame = event.nativeEvent.layout;
if (!this._initialFrameHeight) {
// save the initial frame height, before the keyboard is visible
this._initialFrameHeight = this._frame.height;
}
};
componentDidMount(): void {
if (Platform.OS === 'ios') {
this._subscriptions = [
Keyboard.addListener('keyboardWillChangeFrame', this._onKeyboardChange),
];
} else {
this._subscriptions = [
Keyboard.addListener('keyboardDidHide', this._onKeyboardChange),
Keyboard.addListener('keyboardDidShow', this._onKeyboardChange),
];
}
}
componentWillUnmount(): void {
this._subscriptions.forEach(subscription => {
subscription.remove();
});
}
render(): React.Node {
const {
behavior,
children,
contentContainerStyle,
enabled,
keyboardVerticalOffset,
style,
...props
} = this.props;
const bottomHeight = enabled ? this.state.bottom : 0;
switch (behavior) {
case 'height':
let heightStyle;
if (this._frame != null && this.state.bottom > 0) {
// Note that we only apply a height change when there is keyboard present,
// i.e. this.state.bottom is greater than 0. If we remove that condition,
// this.frame.height will never go back to its original value.
// When height changes, we need to disable flex.
heightStyle = {
height: this._initialFrameHeight - bottomHeight,
flex: 0,
};
}
return (
<View
ref={this.viewRef}
style={StyleSheet.compose(
style,
heightStyle,
)}
onLayout={this._onLayout}
{...props}>
{children}
</View>
);
case 'position':
return (
<View
ref={this.viewRef}
style={style}
onLayout={this._onLayout}
{...props}>
<View
style={StyleSheet.compose(
contentContainerStyle,
{
bottom: bottomHeight,
},
)}>
{children}
</View>
</View>
);
case 'padding':
return (
<View
ref={this.viewRef}
style={StyleSheet.compose(
style,
{paddingBottom: bottomHeight},
)}
onLayout={this._onLayout}
{...props}>
{children}
</View>
);
default:
return (
<View
ref={this.viewRef}
onLayout={this._onLayout}
style={style}
{...props}>
{children}
</View>
);
}
}
}
module.exports = KeyboardAvoidingView;

View File

@ -0,0 +1,21 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
'use strict';
import type {TurboModule} from '../../TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+addListener: (eventName: string) => void;
+removeListeners: (count: number) => void;
}
export default (TurboModuleRegistry.get<Spec>('KeyboardObserver'): ?Spec);