react-native-keyboard-controller 1.6.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.md +1 -0
  2. package/android/src/main/java/com/reactnativekeyboardcontroller/KeyboardAnimationCallback.kt +9 -1
  3. package/android/src/main/java/com/reactnativekeyboardcontroller/events/KeyboardTransitionEvent.kt +9 -9
  4. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ViewGroup.kt +10 -0
  5. package/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt +13 -7
  6. package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt +58 -40
  7. package/lib/commonjs/components/KeyboardAvoidingView/hooks.js +35 -0
  8. package/lib/commonjs/components/KeyboardAvoidingView/hooks.js.map +1 -0
  9. package/lib/commonjs/components/KeyboardAvoidingView/index.js +97 -0
  10. package/lib/commonjs/components/KeyboardAvoidingView/index.js.map +1 -0
  11. package/lib/commonjs/components/index.js +14 -0
  12. package/lib/commonjs/components/index.js.map +1 -0
  13. package/lib/commonjs/index.js +17 -0
  14. package/lib/commonjs/index.js.map +1 -1
  15. package/lib/module/components/KeyboardAvoidingView/hooks.js +28 -0
  16. package/lib/module/components/KeyboardAvoidingView/hooks.js.map +1 -0
  17. package/lib/module/components/KeyboardAvoidingView/index.js +88 -0
  18. package/lib/module/components/KeyboardAvoidingView/index.js.map +1 -0
  19. package/lib/module/components/index.js +2 -0
  20. package/lib/module/components/index.js.map +1 -0
  21. package/lib/module/index.js +1 -0
  22. package/lib/module/index.js.map +1 -1
  23. package/lib/typescript/components/KeyboardAvoidingView/hooks.d.ts +5 -0
  24. package/lib/typescript/components/KeyboardAvoidingView/index.d.ts +29 -0
  25. package/lib/typescript/components/index.d.ts +1 -0
  26. package/lib/typescript/index.d.ts +1 -0
  27. package/package.json +1 -1
  28. package/src/components/KeyboardAvoidingView/hooks.ts +29 -0
  29. package/src/components/KeyboardAvoidingView/index.tsx +147 -0
  30. package/src/components/index.ts +1 -0
  31. package/src/index.ts +2 -0
package/README.md CHANGED
@@ -13,6 +13,7 @@ Keyboard manager which works in identical way on both iOS and Android.
13
13
  - module for changing soft input mode on Android 🤔
14
14
  - reanimated support 🚀
15
15
  - interactive keyboard dismissing 👆📱
16
+ - works with any navigation library 🧭
16
17
  - and more is coming... Stay tuned! 😊
17
18
 
18
19
  ## Installation
@@ -32,6 +32,7 @@ class KeyboardAnimationCallback(
32
32
  dispatchMode: Int = DISPATCH_MODE_STOP,
33
33
  val context: ThemedReactContext?,
34
34
  ) : WindowInsetsAnimationCompat.Callback(dispatchMode), OnApplyWindowInsetsListener {
35
+ private val surfaceId = UIManagerHelper.getSurfaceId(view)
35
36
  private var persistentKeyboardHeight = 0.0
36
37
  private var isKeyboardVisible = false
37
38
  private var isTransitioning = false
@@ -57,6 +58,7 @@ class KeyboardAnimationCallback(
57
58
  // can bring breaking changes
58
59
  this.sendEventToJS(
59
60
  KeyboardTransitionEvent(
61
+ surfaceId,
60
62
  view.id,
61
63
  "topKeyboardMoveStart",
62
64
  this.persistentKeyboardHeight,
@@ -67,6 +69,7 @@ class KeyboardAnimationCallback(
67
69
  )
68
70
  this.sendEventToJS(
69
71
  KeyboardTransitionEvent(
72
+ surfaceId,
70
73
  view.id,
71
74
  "topKeyboardMoveEnd",
72
75
  this.persistentKeyboardHeight,
@@ -115,6 +118,7 @@ class KeyboardAnimationCallback(
115
118
  this.emitEvent("KeyboardController::keyboardWillShow", getEventParams(keyboardHeight))
116
119
  this.sendEventToJS(
117
120
  KeyboardTransitionEvent(
121
+ surfaceId,
118
122
  view.id,
119
123
  "topKeyboardMoveStart",
120
124
  keyboardHeight,
@@ -129,6 +133,7 @@ class KeyboardAnimationCallback(
129
133
  val toValue = animator.animatedValue as Float
130
134
  this.sendEventToJS(
131
135
  KeyboardTransitionEvent(
136
+ surfaceId,
132
137
  view.id,
133
138
  "topKeyboardMove",
134
139
  toValue.toDouble(),
@@ -142,6 +147,7 @@ class KeyboardAnimationCallback(
142
147
  this.emitEvent("KeyboardController::keyboardDidShow", getEventParams(keyboardHeight))
143
148
  this.sendEventToJS(
144
149
  KeyboardTransitionEvent(
150
+ surfaceId,
145
151
  view.id,
146
152
  "topKeyboardMoveEnd",
147
153
  keyboardHeight,
@@ -182,6 +188,7 @@ class KeyboardAnimationCallback(
182
188
  Log.i(TAG, "HEIGHT:: $keyboardHeight TAG:: $viewTagFocused")
183
189
  this.sendEventToJS(
184
190
  KeyboardTransitionEvent(
191
+ surfaceId,
185
192
  view.id,
186
193
  "topKeyboardMoveStart",
187
194
  keyboardHeight,
@@ -223,7 +230,7 @@ class KeyboardAnimationCallback(
223
230
  Log.i(TAG, "DiffY: $diffY $height $progress ${InteractiveKeyboardProvider.isInteractive} $viewTagFocused")
224
231
 
225
232
  val event = if (InteractiveKeyboardProvider.isInteractive) "topKeyboardMoveInteractive" else "topKeyboardMove"
226
- this.sendEventToJS(KeyboardTransitionEvent(view.id, event, height, progress, duration, viewTagFocused))
233
+ this.sendEventToJS(KeyboardTransitionEvent(surfaceId, view.id, event, height, progress, duration, viewTagFocused))
227
234
 
228
235
  return insets
229
236
  }
@@ -254,6 +261,7 @@ class KeyboardAnimationCallback(
254
261
  )
255
262
  this.sendEventToJS(
256
263
  KeyboardTransitionEvent(
264
+ surfaceId,
257
265
  view.id,
258
266
  "topKeyboardMoveEnd",
259
267
  keyboardHeight,
@@ -1,28 +1,28 @@
1
1
  package com.reactnativekeyboardcontroller.events
2
2
 
3
3
  import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.WritableMap
4
5
  import com.facebook.react.uimanager.events.Event
5
- import com.facebook.react.uimanager.events.RCTEventEmitter
6
6
 
7
+ @Suppress("detekt:LongParameterList")
7
8
  class KeyboardTransitionEvent(
9
+ surfaceId: Int,
8
10
  viewId: Int,
9
11
  private val event: String,
10
12
  private val height: Double,
11
13
  private val progress: Double,
12
14
  private val duration: Int,
13
15
  private val target: Int,
14
- ) : Event<KeyboardTransitionEvent>(viewId) {
16
+ ) : Event<KeyboardTransitionEvent>(surfaceId, viewId) {
15
17
  override fun getEventName() = event
16
18
 
17
19
  // All events for a given view can be coalesced?
18
20
  override fun getCoalescingKey(): Short = 0
19
21
 
20
- override fun dispatch(rctEventEmitter: RCTEventEmitter) {
21
- val map = Arguments.createMap()
22
- map.putDouble("progress", progress)
23
- map.putDouble("height", height)
24
- map.putInt("duration", duration)
25
- map.putInt("target", target)
26
- rctEventEmitter.receiveEvent(viewTag, eventName, map)
22
+ override fun getEventData(): WritableMap? = Arguments.createMap().apply {
23
+ putDouble("progress", progress)
24
+ putDouble("height", height)
25
+ putInt("duration", duration)
26
+ putInt("target", target)
27
27
  }
28
28
  }
@@ -0,0 +1,10 @@
1
+ package com.reactnativekeyboardcontroller.extensions
2
+
3
+ import android.view.ViewGroup
4
+
5
+ fun ViewGroup?.removeSelf() {
6
+ this ?: return
7
+ val parent = parent as? ViewGroup ?: return
8
+
9
+ parent.removeView(this)
10
+ }
@@ -5,12 +5,7 @@ import com.facebook.react.bridge.ReactApplicationContext
5
5
  import com.facebook.react.bridge.UiThreadUtil
6
6
 
7
7
  class KeyboardControllerModuleImpl(private val mReactContext: ReactApplicationContext) {
8
- private val mDefaultMode: Int = mReactContext
9
- .currentActivity
10
- ?.window
11
- ?.attributes
12
- ?.softInputMode
13
- ?: WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED
8
+ private val mDefaultMode: Int = getCurrentMode()
14
9
 
15
10
  fun setInputMode(mode: Int) {
16
11
  setSoftInputMode(mode)
@@ -22,10 +17,21 @@ class KeyboardControllerModuleImpl(private val mReactContext: ReactApplicationCo
22
17
 
23
18
  private fun setSoftInputMode(mode: Int) {
24
19
  UiThreadUtil.runOnUiThread {
25
- mReactContext.currentActivity?.window?.setSoftInputMode(mode)
20
+ if (getCurrentMode() != mode) {
21
+ mReactContext.currentActivity?.window?.setSoftInputMode(mode)
22
+ }
26
23
  }
27
24
  }
28
25
 
26
+ private fun getCurrentMode(): Int {
27
+ return mReactContext
28
+ .currentActivity
29
+ ?.window
30
+ ?.attributes
31
+ ?.softInputMode
32
+ ?: WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED
33
+ }
34
+
29
35
  companion object {
30
36
  const val NAME = "KeyboardController"
31
37
  }
@@ -13,6 +13,7 @@ import androidx.core.view.WindowInsetsCompat
13
13
  import com.facebook.react.uimanager.ThemedReactContext
14
14
  import com.facebook.react.views.view.ReactViewGroup
15
15
  import com.reactnativekeyboardcontroller.KeyboardAnimationCallback
16
+ import com.reactnativekeyboardcontroller.extensions.removeSelf
16
17
  import com.reactnativekeyboardcontroller.extensions.requestApplyInsetsWhenAttached
17
18
  import com.reactnativekeyboardcontroller.extensions.rootView
18
19
 
@@ -22,36 +23,72 @@ private val TAG = EdgeToEdgeReactViewGroup::class.qualifiedName
22
23
  class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : ReactViewGroup(reactContext) {
23
24
  private var isStatusBarTranslucent = false
24
25
  private var isNavigationBarTranslucent = false
26
+ private var eventView: ReactViewGroup? = null
25
27
 
26
- init {
27
- val activity = reactContext.currentActivity
28
+ // region View lifecycles
29
+ override fun onAttachedToWindow() {
30
+ super.onAttachedToWindow()
28
31
 
29
- if (activity != null) {
30
- val callback = KeyboardAnimationCallback(
31
- view = this,
32
- persistentInsetTypes = WindowInsetsCompat.Type.systemBars(),
33
- deferredInsetTypes = WindowInsetsCompat.Type.ime(),
34
- // We explicitly allow dispatch to continue down to binding.messageHolder's
35
- // child views, so that step 2.5 below receives the call
36
- dispatchMode = WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE,
37
- context = reactContext,
38
- )
39
- ViewCompat.setWindowInsetsAnimationCallback(this, callback)
40
- ViewCompat.setOnApplyWindowInsetsListener(this, callback)
41
- this.requestApplyInsetsWhenAttached()
42
- } else {
32
+ val activity = reactContext.currentActivity
33
+ if (activity == null) {
43
34
  Log.w(TAG, "Can not setup keyboard animation listener, since `currentActivity` is null")
35
+ return
36
+ }
37
+
38
+ Handler(Looper.getMainLooper()).post(this::setupWindowInsets)
39
+ WindowCompat.setDecorFitsSystemWindows(
40
+ activity.window,
41
+ false,
42
+ )
43
+
44
+ eventView = ReactViewGroup(context)
45
+ val root = this.getContentView()
46
+ root?.addView(eventView)
47
+
48
+ val callback = KeyboardAnimationCallback(
49
+ view = this,
50
+ persistentInsetTypes = WindowInsetsCompat.Type.systemBars(),
51
+ deferredInsetTypes = WindowInsetsCompat.Type.ime(),
52
+ dispatchMode = WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE,
53
+ context = reactContext,
54
+ )
55
+
56
+ eventView?.let {
57
+ ViewCompat.setWindowInsetsAnimationCallback(it, callback)
58
+ ViewCompat.setOnApplyWindowInsetsListener(it, callback)
59
+ it.requestApplyInsetsWhenAttached()
44
60
  }
45
61
  }
46
62
 
63
+ override fun onDetachedFromWindow() {
64
+ super.onDetachedFromWindow()
65
+
66
+ eventView.removeSelf()
67
+ }
68
+ // endregion
69
+
70
+ // region Props setters
71
+ fun setStatusBarTranslucent(isStatusBarTranslucent: Boolean) {
72
+ this.isStatusBarTranslucent = isStatusBarTranslucent
73
+ }
74
+
75
+ fun setNavigationBarTranslucent(isNavigationBarTranslucent: Boolean) {
76
+ this.isNavigationBarTranslucent = isNavigationBarTranslucent
77
+ }
78
+ // endregion
79
+
80
+ // region Private functions/class helpers
81
+ private fun getContentView(): FitWindowsLinearLayout? {
82
+ return reactContext.currentActivity?.window?.decorView?.rootView?.findViewById(
83
+ androidx.appcompat.R.id.action_bar_root,
84
+ )
85
+ }
86
+
47
87
  private fun setupWindowInsets() {
48
88
  val rootView = reactContext.rootView
49
89
  if (rootView != null) {
50
90
  ViewCompat.setOnApplyWindowInsetsListener(rootView) { v, insets ->
51
- val content =
52
- reactContext.rootView?.findViewById<FitWindowsLinearLayout>(
53
- androidx.appcompat.R.id.action_bar_root,
54
- )
91
+ val content = getContentView()
55
92
  val params = FrameLayout.LayoutParams(
56
93
  FrameLayout.LayoutParams.MATCH_PARENT,
57
94
  FrameLayout.LayoutParams.MATCH_PARENT,
@@ -80,24 +117,5 @@ class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : R
80
117
  }
81
118
  }
82
119
  }
83
-
84
- override fun onAttachedToWindow() {
85
- super.onAttachedToWindow()
86
-
87
- Handler(Looper.getMainLooper()).post(this::setupWindowInsets)
88
- reactContext.currentActivity?.let {
89
- WindowCompat.setDecorFitsSystemWindows(
90
- it.window,
91
- false,
92
- )
93
- }
94
- }
95
-
96
- fun setStatusBarTranslucent(isStatusBarTranslucent: Boolean) {
97
- this.isStatusBarTranslucent = isStatusBarTranslucent
98
- }
99
-
100
- fun setNavigationBarTranslucent(isNavigationBarTranslucent: Boolean) {
101
- this.isNavigationBarTranslucent = isNavigationBarTranslucent
102
- }
120
+ // endregion
103
121
  }
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useKeyboardAnimation = void 0;
7
+ var _reactNativeReanimated = require("react-native-reanimated");
8
+ var _hooks = require("../../hooks");
9
+ const useKeyboardAnimation = () => {
10
+ const heightWhenOpened = (0, _reactNativeReanimated.useSharedValue)(0);
11
+ const height = (0, _reactNativeReanimated.useSharedValue)(0);
12
+ const progress = (0, _reactNativeReanimated.useSharedValue)(0);
13
+ (0, _hooks.useKeyboardHandler)({
14
+ onStart: e => {
15
+ 'worklet';
16
+
17
+ if (e.height > 0) {
18
+ heightWhenOpened.value = e.height;
19
+ }
20
+ },
21
+ onMove: e => {
22
+ 'worklet';
23
+
24
+ progress.value = e.progress;
25
+ height.value = e.height;
26
+ }
27
+ }, []);
28
+ return {
29
+ height,
30
+ progress,
31
+ heightWhenOpened
32
+ };
33
+ };
34
+ exports.useKeyboardAnimation = useKeyboardAnimation;
35
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["useKeyboardAnimation","heightWhenOpened","useSharedValue","height","progress","useKeyboardHandler","onStart","e","value","onMove"],"sources":["hooks.ts"],"sourcesContent":["import { useSharedValue } from 'react-native-reanimated';\nimport { useKeyboardHandler } from '../../hooks';\n\nexport const useKeyboardAnimation = () => {\n const heightWhenOpened = useSharedValue(0);\n const height = useSharedValue(0);\n const progress = useSharedValue(0);\n\n useKeyboardHandler(\n {\n onStart: (e) => {\n 'worklet';\n\n if (e.height > 0) {\n heightWhenOpened.value = e.height;\n }\n },\n onMove: (e) => {\n 'worklet';\n\n progress.value = e.progress;\n height.value = e.height;\n },\n },\n []\n );\n\n return { height, progress, heightWhenOpened };\n};\n"],"mappings":";;;;;;AAAA;AACA;AAEO,MAAMA,oBAAoB,GAAG,MAAM;EACxC,MAAMC,gBAAgB,GAAG,IAAAC,qCAAc,EAAC,CAAC,CAAC;EAC1C,MAAMC,MAAM,GAAG,IAAAD,qCAAc,EAAC,CAAC,CAAC;EAChC,MAAME,QAAQ,GAAG,IAAAF,qCAAc,EAAC,CAAC,CAAC;EAElC,IAAAG,yBAAkB,EAChB;IACEC,OAAO,EAAGC,CAAC,IAAK;MACd,SAAS;;MAET,IAAIA,CAAC,CAACJ,MAAM,GAAG,CAAC,EAAE;QAChBF,gBAAgB,CAACO,KAAK,GAAGD,CAAC,CAACJ,MAAM;MACnC;IACF,CAAC;IACDM,MAAM,EAAGF,CAAC,IAAK;MACb,SAAS;;MAETH,QAAQ,CAACI,KAAK,GAAGD,CAAC,CAACH,QAAQ;MAC3BD,MAAM,CAACK,KAAK,GAAGD,CAAC,CAACJ,MAAM;IACzB;EACF,CAAC,EACD,EAAE,CACH;EAED,OAAO;IAAEA,MAAM;IAAEC,QAAQ;IAAEH;EAAiB,CAAC;AAC/C,CAAC;AAAC"}
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
10
+ var _hooks = require("./hooks");
11
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
12
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
13
+ function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
14
+ const defaultLayout = {
15
+ x: 0,
16
+ y: 0,
17
+ width: 0,
18
+ height: 0
19
+ };
20
+
21
+ /**
22
+ * View that moves out of the way when the keyboard appears by automatically
23
+ * adjusting its height, position, or bottom padding.
24
+ */
25
+ const KeyboardAvoidingView = /*#__PURE__*/(0, _react.forwardRef)((_ref, ref) => {
26
+ let {
27
+ behavior,
28
+ children,
29
+ contentContainerStyle,
30
+ enabled = true,
31
+ keyboardVerticalOffset = 0,
32
+ style,
33
+ onLayout: onLayoutProps,
34
+ ...props
35
+ } = _ref;
36
+ const initialFrame = (0, _reactNativeReanimated.useSharedValue)(null);
37
+ const frame = (0, _reactNativeReanimated.useDerivedValue)(() => initialFrame.value || defaultLayout);
38
+ const keyboard = (0, _hooks.useKeyboardAnimation)();
39
+ const {
40
+ height: screenHeight
41
+ } = (0, _reactNative.useWindowDimensions)();
42
+ const relativeKeyboardHeight = (0, _reactNativeReanimated.useWorkletCallback)(() => {
43
+ const keyboardY = screenHeight - keyboard.heightWhenOpened.value - keyboardVerticalOffset;
44
+ return Math.max(frame.value.y + frame.value.height - keyboardY, 0);
45
+ }, [screenHeight, keyboardVerticalOffset]);
46
+ const onLayout = (0, _react.useCallback)(e => {
47
+ if (initialFrame.value === null) {
48
+ initialFrame.value = e.nativeEvent.layout;
49
+ }
50
+ onLayoutProps === null || onLayoutProps === void 0 ? void 0 : onLayoutProps(e);
51
+ }, [onLayoutProps]);
52
+ const animatedStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
53
+ const bottom = (0, _reactNativeReanimated.interpolate)(keyboard.progress.value, [0, 1], [0, relativeKeyboardHeight()]);
54
+ const bottomHeight = enabled ? bottom : 0;
55
+ switch (behavior) {
56
+ case 'height':
57
+ if (bottomHeight > 0) {
58
+ return {
59
+ height: frame.value.height - bottomHeight,
60
+ flex: 0
61
+ };
62
+ }
63
+ return {};
64
+ case 'position':
65
+ return {
66
+ bottom: bottomHeight
67
+ };
68
+ case 'padding':
69
+ return {
70
+ paddingBottom: bottomHeight
71
+ };
72
+ default:
73
+ return {};
74
+ }
75
+ }, [behavior, enabled, relativeKeyboardHeight]);
76
+ const isPositionBehavior = behavior === 'position';
77
+ const containerStyle = isPositionBehavior ? contentContainerStyle : style;
78
+ const combinedStyles = (0, _react.useMemo)(() => [containerStyle, animatedStyle], [containerStyle, animatedStyle]);
79
+ if (isPositionBehavior) {
80
+ return /*#__PURE__*/_react.default.createElement(_reactNative.View, _extends({
81
+ ref: ref,
82
+ style: style,
83
+ onLayout: onLayout
84
+ }, props), /*#__PURE__*/_react.default.createElement(_reactNativeReanimated.default.View, {
85
+ style: combinedStyles
86
+ }, children));
87
+ }
88
+ return /*#__PURE__*/_react.default.createElement(_reactNativeReanimated.default.View, _extends({
89
+ // @ts-expect-error because `ref` from reanimated is not compatible with react-native
90
+ ref: ref,
91
+ onLayout: onLayout,
92
+ style: combinedStyles
93
+ }, props), children);
94
+ });
95
+ var _default = KeyboardAvoidingView;
96
+ exports.default = _default;
97
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["defaultLayout","x","y","width","height","KeyboardAvoidingView","forwardRef","ref","behavior","children","contentContainerStyle","enabled","keyboardVerticalOffset","style","onLayout","onLayoutProps","props","initialFrame","useSharedValue","frame","useDerivedValue","value","keyboard","useKeyboardAnimation","screenHeight","useWindowDimensions","relativeKeyboardHeight","useWorkletCallback","keyboardY","heightWhenOpened","Math","max","useCallback","e","nativeEvent","layout","animatedStyle","useAnimatedStyle","bottom","interpolate","progress","bottomHeight","flex","paddingBottom","isPositionBehavior","containerStyle","combinedStyles","useMemo"],"sources":["index.tsx"],"sourcesContent":["import React, { forwardRef, useCallback, useMemo } from 'react';\nimport {\n LayoutRectangle,\n useWindowDimensions,\n View,\n ViewProps,\n} from 'react-native';\nimport Reanimated, {\n useAnimatedStyle,\n useWorkletCallback,\n useSharedValue,\n useDerivedValue,\n interpolate,\n} from 'react-native-reanimated';\nimport { useKeyboardAnimation } from './hooks';\n\ntype Props = {\n /**\n * Specify how to react to the presence of the keyboard.\n */\n behavior?: 'height' | 'position' | 'padding';\n\n /**\n * Style of the content container when `behavior` is 'position'.\n */\n contentContainerStyle?: ViewProps['style'];\n\n /**\n * Controls whether this `KeyboardAvoidingView` instance should take effect.\n * This is useful when more than one is on the screen. Defaults to true.\n */\n enabled?: boolean;\n\n /**\n * Distance between the top of the user screen and the React Native view. This\n * may be non-zero in some cases. Defaults to 0.\n */\n keyboardVerticalOffset?: number;\n} & ViewProps;\n\nconst defaultLayout: LayoutRectangle = {\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n};\n\n/**\n * View that moves out of the way when the keyboard appears by automatically\n * adjusting its height, position, or bottom padding.\n */\nconst KeyboardAvoidingView = forwardRef<View, React.PropsWithChildren<Props>>(\n (\n {\n behavior,\n children,\n contentContainerStyle,\n enabled = true,\n keyboardVerticalOffset = 0,\n style,\n onLayout: onLayoutProps,\n ...props\n },\n ref\n ) => {\n const initialFrame = useSharedValue<LayoutRectangle | null>(null);\n const frame = useDerivedValue(() => initialFrame.value || defaultLayout);\n\n const keyboard = useKeyboardAnimation();\n const { height: screenHeight } = useWindowDimensions();\n\n const relativeKeyboardHeight = useWorkletCallback(() => {\n const keyboardY =\n screenHeight - keyboard.heightWhenOpened.value - keyboardVerticalOffset;\n\n return Math.max(frame.value.y + frame.value.height - keyboardY, 0);\n }, [screenHeight, keyboardVerticalOffset]);\n\n const onLayout = useCallback<NonNullable<ViewProps['onLayout']>>(\n (e) => {\n if (initialFrame.value === null) {\n initialFrame.value = e.nativeEvent.layout;\n }\n onLayoutProps?.(e);\n },\n [onLayoutProps]\n );\n\n const animatedStyle = useAnimatedStyle(() => {\n const bottom = interpolate(\n keyboard.progress.value,\n [0, 1],\n [0, relativeKeyboardHeight()]\n );\n const bottomHeight = enabled ? bottom : 0;\n\n switch (behavior) {\n case 'height':\n if (bottomHeight > 0) {\n return {\n height: frame.value.height - bottomHeight,\n flex: 0,\n };\n }\n\n return {};\n\n case 'position':\n return { bottom: bottomHeight };\n\n case 'padding':\n return { paddingBottom: bottomHeight };\n\n default:\n return {};\n }\n }, [behavior, enabled, relativeKeyboardHeight]);\n const isPositionBehavior = behavior === 'position';\n const containerStyle = isPositionBehavior ? contentContainerStyle : style;\n const combinedStyles = useMemo(\n () => [containerStyle, animatedStyle],\n [containerStyle, animatedStyle]\n );\n\n if (isPositionBehavior) {\n return (\n <View ref={ref} style={style} onLayout={onLayout} {...props}>\n <Reanimated.View style={combinedStyles}>{children}</Reanimated.View>\n </View>\n );\n }\n\n return (\n <Reanimated.View\n // @ts-expect-error because `ref` from reanimated is not compatible with react-native\n ref={ref}\n onLayout={onLayout}\n style={combinedStyles}\n {...props}\n >\n {children}\n </Reanimated.View>\n );\n }\n);\n\nexport default KeyboardAvoidingView;\n"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "KeyboardAvoidingView", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _KeyboardAvoidingView.default;
10
+ }
11
+ });
12
+ var _KeyboardAvoidingView = _interopRequireDefault(require("./KeyboardAvoidingView"));
13
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["index.ts"],"sourcesContent":["export { default as KeyboardAvoidingView } from './KeyboardAvoidingView';\n"],"mappings":";;;;;;;;;;;AAAA;AAAyE"}
@@ -3,10 +3,20 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ var _exportNames = {
7
+ KeyboardAvoidingView: true
8
+ };
9
+ Object.defineProperty(exports, "KeyboardAvoidingView", {
10
+ enumerable: true,
11
+ get: function () {
12
+ return _components.KeyboardAvoidingView;
13
+ }
14
+ });
6
15
  require("./monkey-patch");
7
16
  var _bindings = require("./bindings");
8
17
  Object.keys(_bindings).forEach(function (key) {
9
18
  if (key === "default" || key === "__esModule") return;
19
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
10
20
  if (key in exports && exports[key] === _bindings[key]) return;
11
21
  Object.defineProperty(exports, key, {
12
22
  enumerable: true,
@@ -18,6 +28,7 @@ Object.keys(_bindings).forEach(function (key) {
18
28
  var _animated = require("./animated");
19
29
  Object.keys(_animated).forEach(function (key) {
20
30
  if (key === "default" || key === "__esModule") return;
31
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
21
32
  if (key in exports && exports[key] === _animated[key]) return;
22
33
  Object.defineProperty(exports, key, {
23
34
  enumerable: true,
@@ -29,6 +40,7 @@ Object.keys(_animated).forEach(function (key) {
29
40
  var _replicas = require("./replicas");
30
41
  Object.keys(_replicas).forEach(function (key) {
31
42
  if (key === "default" || key === "__esModule") return;
43
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
32
44
  if (key in exports && exports[key] === _replicas[key]) return;
33
45
  Object.defineProperty(exports, key, {
34
46
  enumerable: true,
@@ -40,6 +52,7 @@ Object.keys(_replicas).forEach(function (key) {
40
52
  var _context = require("./context");
41
53
  Object.keys(_context).forEach(function (key) {
42
54
  if (key === "default" || key === "__esModule") return;
55
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
43
56
  if (key in exports && exports[key] === _context[key]) return;
44
57
  Object.defineProperty(exports, key, {
45
58
  enumerable: true,
@@ -51,6 +64,7 @@ Object.keys(_context).forEach(function (key) {
51
64
  var _hooks = require("./hooks");
52
65
  Object.keys(_hooks).forEach(function (key) {
53
66
  if (key === "default" || key === "__esModule") return;
67
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
54
68
  if (key in exports && exports[key] === _hooks[key]) return;
55
69
  Object.defineProperty(exports, key, {
56
70
  enumerable: true,
@@ -62,6 +76,7 @@ Object.keys(_hooks).forEach(function (key) {
62
76
  var _constants = require("./constants");
63
77
  Object.keys(_constants).forEach(function (key) {
64
78
  if (key === "default" || key === "__esModule") return;
79
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
65
80
  if (key in exports && exports[key] === _constants[key]) return;
66
81
  Object.defineProperty(exports, key, {
67
82
  enumerable: true,
@@ -73,6 +88,7 @@ Object.keys(_constants).forEach(function (key) {
73
88
  var _types = require("./types");
74
89
  Object.keys(_types).forEach(function (key) {
75
90
  if (key === "default" || key === "__esModule") return;
91
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
76
92
  if (key in exports && exports[key] === _types[key]) return;
77
93
  Object.defineProperty(exports, key, {
78
94
  enumerable: true,
@@ -81,4 +97,5 @@ Object.keys(_types).forEach(function (key) {
81
97
  }
82
98
  });
83
99
  });
100
+ var _components = require("./components");
84
101
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["index.ts"],"sourcesContent":["import './monkey-patch';\n\nexport * from './bindings';\nexport * from './animated';\nexport * from './replicas';\nexport * from './context';\nexport * from './hooks';\nexport * from './constants';\nexport * from './types';\n"],"mappings":";;;;;AAAA;AAEA;AAAA;EAAA;EAAA;EAAA;IAAA;IAAA;MAAA;IAAA;EAAA;AAAA;AACA;AAAA;EAAA;EAAA;EAAA;IAAA;IAAA;MAAA;IAAA;EAAA;AAAA;AACA;AAAA;EAAA;EAAA;EAAA;IAAA;IAAA;MAAA;IAAA;EAAA;AAAA;AACA;AAAA;EAAA;EAAA;EAAA;IAAA;IAAA;MAAA;IAAA;EAAA;AAAA;AACA;AAAA;EAAA;EAAA;EAAA;IAAA;IAAA;MAAA;IAAA;EAAA;AAAA;AACA;AAAA;EAAA;EAAA;EAAA;IAAA;IAAA;MAAA;IAAA;EAAA;AAAA;AACA;AAAA;EAAA;EAAA;EAAA;IAAA;IAAA;MAAA;IAAA;EAAA;AAAA"}
1
+ {"version":3,"names":[],"sources":["index.ts"],"sourcesContent":["import './monkey-patch';\n\nexport * from './bindings';\nexport * from './animated';\nexport * from './replicas';\nexport * from './context';\nexport * from './hooks';\nexport * from './constants';\nexport * from './types';\n\nexport { KeyboardAvoidingView } from './components';\n"],"mappings":";;;;;;;;;;;;;;AAAA;AAEA;AAAA;EAAA;EAAA;EAAA;EAAA;IAAA;IAAA;MAAA;IAAA;EAAA;AAAA;AACA;AAAA;EAAA;EAAA;EAAA;EAAA;IAAA;IAAA;MAAA;IAAA;EAAA;AAAA;AACA;AAAA;EAAA;EAAA;EAAA;EAAA;IAAA;IAAA;MAAA;IAAA;EAAA;AAAA;AACA;AAAA;EAAA;EAAA;EAAA;EAAA;IAAA;IAAA;MAAA;IAAA;EAAA;AAAA;AACA;AAAA;EAAA;EAAA;EAAA;EAAA;IAAA;IAAA;MAAA;IAAA;EAAA;AAAA;AACA;AAAA;EAAA;EAAA;EAAA;EAAA;IAAA;IAAA;MAAA;IAAA;EAAA;AAAA;AACA;AAAA;EAAA;EAAA;EAAA;EAAA;IAAA;IAAA;MAAA;IAAA;EAAA;AAAA;AAEA"}
@@ -0,0 +1,28 @@
1
+ import { useSharedValue } from 'react-native-reanimated';
2
+ import { useKeyboardHandler } from '../../hooks';
3
+ export const useKeyboardAnimation = () => {
4
+ const heightWhenOpened = useSharedValue(0);
5
+ const height = useSharedValue(0);
6
+ const progress = useSharedValue(0);
7
+ useKeyboardHandler({
8
+ onStart: e => {
9
+ 'worklet';
10
+
11
+ if (e.height > 0) {
12
+ heightWhenOpened.value = e.height;
13
+ }
14
+ },
15
+ onMove: e => {
16
+ 'worklet';
17
+
18
+ progress.value = e.progress;
19
+ height.value = e.height;
20
+ }
21
+ }, []);
22
+ return {
23
+ height,
24
+ progress,
25
+ heightWhenOpened
26
+ };
27
+ };
28
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["useSharedValue","useKeyboardHandler","useKeyboardAnimation","heightWhenOpened","height","progress","onStart","e","value","onMove"],"sources":["hooks.ts"],"sourcesContent":["import { useSharedValue } from 'react-native-reanimated';\nimport { useKeyboardHandler } from '../../hooks';\n\nexport const useKeyboardAnimation = () => {\n const heightWhenOpened = useSharedValue(0);\n const height = useSharedValue(0);\n const progress = useSharedValue(0);\n\n useKeyboardHandler(\n {\n onStart: (e) => {\n 'worklet';\n\n if (e.height > 0) {\n heightWhenOpened.value = e.height;\n }\n },\n onMove: (e) => {\n 'worklet';\n\n progress.value = e.progress;\n height.value = e.height;\n },\n },\n []\n );\n\n return { height, progress, heightWhenOpened };\n};\n"],"mappings":"AAAA,SAASA,cAAc,QAAQ,yBAAyB;AACxD,SAASC,kBAAkB,QAAQ,aAAa;AAEhD,OAAO,MAAMC,oBAAoB,GAAG,MAAM;EACxC,MAAMC,gBAAgB,GAAGH,cAAc,CAAC,CAAC,CAAC;EAC1C,MAAMI,MAAM,GAAGJ,cAAc,CAAC,CAAC,CAAC;EAChC,MAAMK,QAAQ,GAAGL,cAAc,CAAC,CAAC,CAAC;EAElCC,kBAAkB,CAChB;IACEK,OAAO,EAAGC,CAAC,IAAK;MACd,SAAS;;MAET,IAAIA,CAAC,CAACH,MAAM,GAAG,CAAC,EAAE;QAChBD,gBAAgB,CAACK,KAAK,GAAGD,CAAC,CAACH,MAAM;MACnC;IACF,CAAC;IACDK,MAAM,EAAGF,CAAC,IAAK;MACb,SAAS;;MAETF,QAAQ,CAACG,KAAK,GAAGD,CAAC,CAACF,QAAQ;MAC3BD,MAAM,CAACI,KAAK,GAAGD,CAAC,CAACH,MAAM;IACzB;EACF,CAAC,EACD,EAAE,CACH;EAED,OAAO;IAAEA,MAAM;IAAEC,QAAQ;IAAEF;EAAiB,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,88 @@
1
+ function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
+ import React, { forwardRef, useCallback, useMemo } from 'react';
3
+ import { useWindowDimensions, View } from 'react-native';
4
+ import Reanimated, { useAnimatedStyle, useWorkletCallback, useSharedValue, useDerivedValue, interpolate } from 'react-native-reanimated';
5
+ import { useKeyboardAnimation } from './hooks';
6
+ const defaultLayout = {
7
+ x: 0,
8
+ y: 0,
9
+ width: 0,
10
+ height: 0
11
+ };
12
+
13
+ /**
14
+ * View that moves out of the way when the keyboard appears by automatically
15
+ * adjusting its height, position, or bottom padding.
16
+ */
17
+ const KeyboardAvoidingView = /*#__PURE__*/forwardRef((_ref, ref) => {
18
+ let {
19
+ behavior,
20
+ children,
21
+ contentContainerStyle,
22
+ enabled = true,
23
+ keyboardVerticalOffset = 0,
24
+ style,
25
+ onLayout: onLayoutProps,
26
+ ...props
27
+ } = _ref;
28
+ const initialFrame = useSharedValue(null);
29
+ const frame = useDerivedValue(() => initialFrame.value || defaultLayout);
30
+ const keyboard = useKeyboardAnimation();
31
+ const {
32
+ height: screenHeight
33
+ } = useWindowDimensions();
34
+ const relativeKeyboardHeight = useWorkletCallback(() => {
35
+ const keyboardY = screenHeight - keyboard.heightWhenOpened.value - keyboardVerticalOffset;
36
+ return Math.max(frame.value.y + frame.value.height - keyboardY, 0);
37
+ }, [screenHeight, keyboardVerticalOffset]);
38
+ const onLayout = useCallback(e => {
39
+ if (initialFrame.value === null) {
40
+ initialFrame.value = e.nativeEvent.layout;
41
+ }
42
+ onLayoutProps === null || onLayoutProps === void 0 ? void 0 : onLayoutProps(e);
43
+ }, [onLayoutProps]);
44
+ const animatedStyle = useAnimatedStyle(() => {
45
+ const bottom = interpolate(keyboard.progress.value, [0, 1], [0, relativeKeyboardHeight()]);
46
+ const bottomHeight = enabled ? bottom : 0;
47
+ switch (behavior) {
48
+ case 'height':
49
+ if (bottomHeight > 0) {
50
+ return {
51
+ height: frame.value.height - bottomHeight,
52
+ flex: 0
53
+ };
54
+ }
55
+ return {};
56
+ case 'position':
57
+ return {
58
+ bottom: bottomHeight
59
+ };
60
+ case 'padding':
61
+ return {
62
+ paddingBottom: bottomHeight
63
+ };
64
+ default:
65
+ return {};
66
+ }
67
+ }, [behavior, enabled, relativeKeyboardHeight]);
68
+ const isPositionBehavior = behavior === 'position';
69
+ const containerStyle = isPositionBehavior ? contentContainerStyle : style;
70
+ const combinedStyles = useMemo(() => [containerStyle, animatedStyle], [containerStyle, animatedStyle]);
71
+ if (isPositionBehavior) {
72
+ return /*#__PURE__*/React.createElement(View, _extends({
73
+ ref: ref,
74
+ style: style,
75
+ onLayout: onLayout
76
+ }, props), /*#__PURE__*/React.createElement(Reanimated.View, {
77
+ style: combinedStyles
78
+ }, children));
79
+ }
80
+ return /*#__PURE__*/React.createElement(Reanimated.View, _extends({
81
+ // @ts-expect-error because `ref` from reanimated is not compatible with react-native
82
+ ref: ref,
83
+ onLayout: onLayout,
84
+ style: combinedStyles
85
+ }, props), children);
86
+ });
87
+ export default KeyboardAvoidingView;
88
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["React","forwardRef","useCallback","useMemo","useWindowDimensions","View","Reanimated","useAnimatedStyle","useWorkletCallback","useSharedValue","useDerivedValue","interpolate","useKeyboardAnimation","defaultLayout","x","y","width","height","KeyboardAvoidingView","ref","behavior","children","contentContainerStyle","enabled","keyboardVerticalOffset","style","onLayout","onLayoutProps","props","initialFrame","frame","value","keyboard","screenHeight","relativeKeyboardHeight","keyboardY","heightWhenOpened","Math","max","e","nativeEvent","layout","animatedStyle","bottom","progress","bottomHeight","flex","paddingBottom","isPositionBehavior","containerStyle","combinedStyles"],"sources":["index.tsx"],"sourcesContent":["import React, { forwardRef, useCallback, useMemo } from 'react';\nimport {\n LayoutRectangle,\n useWindowDimensions,\n View,\n ViewProps,\n} from 'react-native';\nimport Reanimated, {\n useAnimatedStyle,\n useWorkletCallback,\n useSharedValue,\n useDerivedValue,\n interpolate,\n} from 'react-native-reanimated';\nimport { useKeyboardAnimation } from './hooks';\n\ntype Props = {\n /**\n * Specify how to react to the presence of the keyboard.\n */\n behavior?: 'height' | 'position' | 'padding';\n\n /**\n * Style of the content container when `behavior` is 'position'.\n */\n contentContainerStyle?: ViewProps['style'];\n\n /**\n * Controls whether this `KeyboardAvoidingView` instance should take effect.\n * This is useful when more than one is on the screen. Defaults to true.\n */\n enabled?: boolean;\n\n /**\n * Distance between the top of the user screen and the React Native view. This\n * may be non-zero in some cases. Defaults to 0.\n */\n keyboardVerticalOffset?: number;\n} & ViewProps;\n\nconst defaultLayout: LayoutRectangle = {\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n};\n\n/**\n * View that moves out of the way when the keyboard appears by automatically\n * adjusting its height, position, or bottom padding.\n */\nconst KeyboardAvoidingView = forwardRef<View, React.PropsWithChildren<Props>>(\n (\n {\n behavior,\n children,\n contentContainerStyle,\n enabled = true,\n keyboardVerticalOffset = 0,\n style,\n onLayout: onLayoutProps,\n ...props\n },\n ref\n ) => {\n const initialFrame = useSharedValue<LayoutRectangle | null>(null);\n const frame = useDerivedValue(() => initialFrame.value || defaultLayout);\n\n const keyboard = useKeyboardAnimation();\n const { height: screenHeight } = useWindowDimensions();\n\n const relativeKeyboardHeight = useWorkletCallback(() => {\n const keyboardY =\n screenHeight - keyboard.heightWhenOpened.value - keyboardVerticalOffset;\n\n return Math.max(frame.value.y + frame.value.height - keyboardY, 0);\n }, [screenHeight, keyboardVerticalOffset]);\n\n const onLayout = useCallback<NonNullable<ViewProps['onLayout']>>(\n (e) => {\n if (initialFrame.value === null) {\n initialFrame.value = e.nativeEvent.layout;\n }\n onLayoutProps?.(e);\n },\n [onLayoutProps]\n );\n\n const animatedStyle = useAnimatedStyle(() => {\n const bottom = interpolate(\n keyboard.progress.value,\n [0, 1],\n [0, relativeKeyboardHeight()]\n );\n const bottomHeight = enabled ? bottom : 0;\n\n switch (behavior) {\n case 'height':\n if (bottomHeight > 0) {\n return {\n height: frame.value.height - bottomHeight,\n flex: 0,\n };\n }\n\n return {};\n\n case 'position':\n return { bottom: bottomHeight };\n\n case 'padding':\n return { paddingBottom: bottomHeight };\n\n default:\n return {};\n }\n }, [behavior, enabled, relativeKeyboardHeight]);\n const isPositionBehavior = behavior === 'position';\n const containerStyle = isPositionBehavior ? contentContainerStyle : style;\n const combinedStyles = useMemo(\n () => [containerStyle, animatedStyle],\n [containerStyle, animatedStyle]\n );\n\n if (isPositionBehavior) {\n return (\n <View ref={ref} style={style} onLayout={onLayout} {...props}>\n <Reanimated.View style={combinedStyles}>{children}</Reanimated.View>\n </View>\n );\n }\n\n return (\n <Reanimated.View\n // @ts-expect-error because `ref` from reanimated is not compatible with react-native\n ref={ref}\n onLayout={onLayout}\n style={combinedStyles}\n {...props}\n >\n {children}\n </Reanimated.View>\n );\n }\n);\n\nexport default KeyboardAvoidingView;\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { default as KeyboardAvoidingView } from './KeyboardAvoidingView';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["default","KeyboardAvoidingView"],"sources":["index.ts"],"sourcesContent":["export { default as KeyboardAvoidingView } from './KeyboardAvoidingView';\n"],"mappings":"AAAA,SAASA,OAAO,IAAIC,oBAAoB,QAAQ,wBAAwB"}
@@ -6,4 +6,5 @@ export * from './context';
6
6
  export * from './hooks';
7
7
  export * from './constants';
8
8
  export * from './types';
9
+ export { KeyboardAvoidingView } from './components';
9
10
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["index.ts"],"sourcesContent":["import './monkey-patch';\n\nexport * from './bindings';\nexport * from './animated';\nexport * from './replicas';\nexport * from './context';\nexport * from './hooks';\nexport * from './constants';\nexport * from './types';\n"],"mappings":"AAAA,OAAO,gBAAgB;AAEvB,cAAc,YAAY;AAC1B,cAAc,YAAY;AAC1B,cAAc,YAAY;AAC1B,cAAc,WAAW;AACzB,cAAc,SAAS;AACvB,cAAc,aAAa;AAC3B,cAAc,SAAS"}
1
+ {"version":3,"names":["KeyboardAvoidingView"],"sources":["index.ts"],"sourcesContent":["import './monkey-patch';\n\nexport * from './bindings';\nexport * from './animated';\nexport * from './replicas';\nexport * from './context';\nexport * from './hooks';\nexport * from './constants';\nexport * from './types';\n\nexport { KeyboardAvoidingView } from './components';\n"],"mappings":"AAAA,OAAO,gBAAgB;AAEvB,cAAc,YAAY;AAC1B,cAAc,YAAY;AAC1B,cAAc,YAAY;AAC1B,cAAc,WAAW;AACzB,cAAc,SAAS;AACvB,cAAc,aAAa;AAC3B,cAAc,SAAS;AAEvB,SAASA,oBAAoB,QAAQ,cAAc"}
@@ -0,0 +1,5 @@
1
+ export declare const useKeyboardAnimation: () => {
2
+ height: import("react-native-reanimated").SharedValue<number>;
3
+ progress: import("react-native-reanimated").SharedValue<number>;
4
+ heightWhenOpened: import("react-native-reanimated").SharedValue<number>;
5
+ };
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+ import { View, ViewProps } from 'react-native';
3
+ /**
4
+ * View that moves out of the way when the keyboard appears by automatically
5
+ * adjusting its height, position, or bottom padding.
6
+ */
7
+ declare const KeyboardAvoidingView: React.ForwardRefExoticComponent<{
8
+ /**
9
+ * Specify how to react to the presence of the keyboard.
10
+ */
11
+ behavior?: "height" | "padding" | "position" | undefined;
12
+ /**
13
+ * Style of the content container when `behavior` is 'position'.
14
+ */
15
+ contentContainerStyle?: ViewProps['style'];
16
+ /**
17
+ * Controls whether this `KeyboardAvoidingView` instance should take effect.
18
+ * This is useful when more than one is on the screen. Defaults to true.
19
+ */
20
+ enabled?: boolean | undefined;
21
+ /**
22
+ * Distance between the top of the user screen and the React Native view. This
23
+ * may be non-zero in some cases. Defaults to 0.
24
+ */
25
+ keyboardVerticalOffset?: number | undefined;
26
+ } & ViewProps & {
27
+ children?: React.ReactNode;
28
+ } & React.RefAttributes<View>>;
29
+ export default KeyboardAvoidingView;
@@ -0,0 +1 @@
1
+ export { default as KeyboardAvoidingView } from './KeyboardAvoidingView';
@@ -6,3 +6,4 @@ export * from './context';
6
6
  export * from './hooks';
7
7
  export * from './constants';
8
8
  export * from './types';
9
+ export { KeyboardAvoidingView } from './components';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-keyboard-controller",
3
- "version": "1.6.1",
3
+ "version": "1.7.0",
4
4
  "description": "Keyboard manager which works in identical way on both iOS and Android",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -0,0 +1,29 @@
1
+ import { useSharedValue } from 'react-native-reanimated';
2
+ import { useKeyboardHandler } from '../../hooks';
3
+
4
+ export const useKeyboardAnimation = () => {
5
+ const heightWhenOpened = useSharedValue(0);
6
+ const height = useSharedValue(0);
7
+ const progress = useSharedValue(0);
8
+
9
+ useKeyboardHandler(
10
+ {
11
+ onStart: (e) => {
12
+ 'worklet';
13
+
14
+ if (e.height > 0) {
15
+ heightWhenOpened.value = e.height;
16
+ }
17
+ },
18
+ onMove: (e) => {
19
+ 'worklet';
20
+
21
+ progress.value = e.progress;
22
+ height.value = e.height;
23
+ },
24
+ },
25
+ []
26
+ );
27
+
28
+ return { height, progress, heightWhenOpened };
29
+ };
@@ -0,0 +1,147 @@
1
+ import React, { forwardRef, useCallback, useMemo } from 'react';
2
+ import {
3
+ LayoutRectangle,
4
+ useWindowDimensions,
5
+ View,
6
+ ViewProps,
7
+ } from 'react-native';
8
+ import Reanimated, {
9
+ useAnimatedStyle,
10
+ useWorkletCallback,
11
+ useSharedValue,
12
+ useDerivedValue,
13
+ interpolate,
14
+ } from 'react-native-reanimated';
15
+ import { useKeyboardAnimation } from './hooks';
16
+
17
+ type Props = {
18
+ /**
19
+ * Specify how to react to the presence of the keyboard.
20
+ */
21
+ behavior?: 'height' | 'position' | 'padding';
22
+
23
+ /**
24
+ * Style of the content container when `behavior` is 'position'.
25
+ */
26
+ contentContainerStyle?: ViewProps['style'];
27
+
28
+ /**
29
+ * Controls whether this `KeyboardAvoidingView` instance should take effect.
30
+ * This is useful when more than one is on the screen. Defaults to true.
31
+ */
32
+ enabled?: boolean;
33
+
34
+ /**
35
+ * Distance between the top of the user screen and the React Native view. This
36
+ * may be non-zero in some cases. Defaults to 0.
37
+ */
38
+ keyboardVerticalOffset?: number;
39
+ } & ViewProps;
40
+
41
+ const defaultLayout: LayoutRectangle = {
42
+ x: 0,
43
+ y: 0,
44
+ width: 0,
45
+ height: 0,
46
+ };
47
+
48
+ /**
49
+ * View that moves out of the way when the keyboard appears by automatically
50
+ * adjusting its height, position, or bottom padding.
51
+ */
52
+ const KeyboardAvoidingView = forwardRef<View, React.PropsWithChildren<Props>>(
53
+ (
54
+ {
55
+ behavior,
56
+ children,
57
+ contentContainerStyle,
58
+ enabled = true,
59
+ keyboardVerticalOffset = 0,
60
+ style,
61
+ onLayout: onLayoutProps,
62
+ ...props
63
+ },
64
+ ref
65
+ ) => {
66
+ const initialFrame = useSharedValue<LayoutRectangle | null>(null);
67
+ const frame = useDerivedValue(() => initialFrame.value || defaultLayout);
68
+
69
+ const keyboard = useKeyboardAnimation();
70
+ const { height: screenHeight } = useWindowDimensions();
71
+
72
+ const relativeKeyboardHeight = useWorkletCallback(() => {
73
+ const keyboardY =
74
+ screenHeight - keyboard.heightWhenOpened.value - keyboardVerticalOffset;
75
+
76
+ return Math.max(frame.value.y + frame.value.height - keyboardY, 0);
77
+ }, [screenHeight, keyboardVerticalOffset]);
78
+
79
+ const onLayout = useCallback<NonNullable<ViewProps['onLayout']>>(
80
+ (e) => {
81
+ if (initialFrame.value === null) {
82
+ initialFrame.value = e.nativeEvent.layout;
83
+ }
84
+ onLayoutProps?.(e);
85
+ },
86
+ [onLayoutProps]
87
+ );
88
+
89
+ const animatedStyle = useAnimatedStyle(() => {
90
+ const bottom = interpolate(
91
+ keyboard.progress.value,
92
+ [0, 1],
93
+ [0, relativeKeyboardHeight()]
94
+ );
95
+ const bottomHeight = enabled ? bottom : 0;
96
+
97
+ switch (behavior) {
98
+ case 'height':
99
+ if (bottomHeight > 0) {
100
+ return {
101
+ height: frame.value.height - bottomHeight,
102
+ flex: 0,
103
+ };
104
+ }
105
+
106
+ return {};
107
+
108
+ case 'position':
109
+ return { bottom: bottomHeight };
110
+
111
+ case 'padding':
112
+ return { paddingBottom: bottomHeight };
113
+
114
+ default:
115
+ return {};
116
+ }
117
+ }, [behavior, enabled, relativeKeyboardHeight]);
118
+ const isPositionBehavior = behavior === 'position';
119
+ const containerStyle = isPositionBehavior ? contentContainerStyle : style;
120
+ const combinedStyles = useMemo(
121
+ () => [containerStyle, animatedStyle],
122
+ [containerStyle, animatedStyle]
123
+ );
124
+
125
+ if (isPositionBehavior) {
126
+ return (
127
+ <View ref={ref} style={style} onLayout={onLayout} {...props}>
128
+ <Reanimated.View style={combinedStyles}>{children}</Reanimated.View>
129
+ </View>
130
+ );
131
+ }
132
+
133
+ return (
134
+ <Reanimated.View
135
+ // @ts-expect-error because `ref` from reanimated is not compatible with react-native
136
+ ref={ref}
137
+ onLayout={onLayout}
138
+ style={combinedStyles}
139
+ {...props}
140
+ >
141
+ {children}
142
+ </Reanimated.View>
143
+ );
144
+ }
145
+ );
146
+
147
+ export default KeyboardAvoidingView;
@@ -0,0 +1 @@
1
+ export { default as KeyboardAvoidingView } from './KeyboardAvoidingView';
package/src/index.ts CHANGED
@@ -7,3 +7,5 @@ export * from './context';
7
7
  export * from './hooks';
8
8
  export * from './constants';
9
9
  export * from './types';
10
+
11
+ export { KeyboardAvoidingView } from './components';