@react-native-oh/react-native-harmony 0.72.27 → 0.72.28-1
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.
- package/Libraries/Components/Keyboard/KeyboardAvoidingView.harmony.js +255 -0
- package/Libraries/Components/ScrollView/ScrollView.harmony.js +28 -3
- package/Libraries/Components/StatusBar/StatusBar.harmony.js +3 -3
- package/index.js +1 -1
- package/package.json +2 -2
- package/react_native_openharmony.har +0 -0
|
@@ -0,0 +1,255 @@
|
|
|
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 {ViewStyleProp} from 'react-native/Libraries/Components/Keyboard/../../StyleSheet/StyleSheet';
|
|
12
|
+
import type {
|
|
13
|
+
ViewLayout,
|
|
14
|
+
ViewLayoutEvent,
|
|
15
|
+
ViewProps,
|
|
16
|
+
} from 'react-native/Libraries/Components/Keyboard/../View/ViewPropTypes';
|
|
17
|
+
import type {KeyboardEvent, KeyboardMetrics} from 'react-native/Libraries/Components/Keyboard/Keyboard';
|
|
18
|
+
|
|
19
|
+
import LayoutAnimation from 'react-native/Libraries/Components/Keyboard/../../LayoutAnimation/LayoutAnimation';
|
|
20
|
+
import StyleSheet from 'react-native/Libraries/Components/Keyboard/../../StyleSheet/StyleSheet';
|
|
21
|
+
import Platform from '../../Utilities/Platform';
|
|
22
|
+
import {type EventSubscription} from 'react-native/Libraries/Components/Keyboard/../../vendor/emitter/EventEmitter';
|
|
23
|
+
import AccessibilityInfo from 'react-native/Libraries/Components/Keyboard/../AccessibilityInfo/AccessibilityInfo';
|
|
24
|
+
import View from 'react-native/Libraries/Components/Keyboard/../View/View';
|
|
25
|
+
import Keyboard from 'react-native/Libraries/Components/Keyboard/Keyboard';
|
|
26
|
+
import * as React from 'react';
|
|
27
|
+
|
|
28
|
+
type Props = $ReadOnly<{|
|
|
29
|
+
...ViewProps,
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Specify how to react to the presence of the keyboard.
|
|
33
|
+
*/
|
|
34
|
+
behavior?: ?('height' | 'position' | 'padding'),
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Style of the content container when `behavior` is 'position'.
|
|
38
|
+
*/
|
|
39
|
+
contentContainerStyle?: ?ViewStyleProp,
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Controls whether this `KeyboardAvoidingView` instance should take effect.
|
|
43
|
+
* This is useful when more than one is on the screen. Defaults to true.
|
|
44
|
+
*/
|
|
45
|
+
enabled?: ?boolean,
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Distance between the top of the user screen and the React Native view. This
|
|
49
|
+
* may be non-zero in some cases. Defaults to 0.
|
|
50
|
+
*/
|
|
51
|
+
keyboardVerticalOffset?: number,
|
|
52
|
+
|}>;
|
|
53
|
+
|
|
54
|
+
type State = {|
|
|
55
|
+
bottom: number,
|
|
56
|
+
|};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* View that moves out of the way when the keyboard appears by automatically
|
|
60
|
+
* adjusting its height, position, or bottom padding.
|
|
61
|
+
*/
|
|
62
|
+
class KeyboardAvoidingView extends React.Component<Props, State> {
|
|
63
|
+
_frame: ?ViewLayout = null;
|
|
64
|
+
_keyboardEvent: ?KeyboardEvent = null;
|
|
65
|
+
_subscriptions: Array<EventSubscription> = [];
|
|
66
|
+
viewRef: {current: React.ElementRef<typeof View> | null, ...};
|
|
67
|
+
_initialFrameHeight: number = 0;
|
|
68
|
+
|
|
69
|
+
constructor(props: Props) {
|
|
70
|
+
super(props);
|
|
71
|
+
this.state = {bottom: 0};
|
|
72
|
+
this.viewRef = React.createRef();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async _relativeKeyboardHeight(
|
|
76
|
+
keyboardFrame: KeyboardMetrics,
|
|
77
|
+
): Promise<number> {
|
|
78
|
+
const frame = this._frame;
|
|
79
|
+
if (!frame || !keyboardFrame) {
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// On iOS when Prefer Cross-Fade Transitions is enabled, the keyboard position
|
|
84
|
+
// & height is reported differently (0 instead of Y position value matching height of frame)
|
|
85
|
+
if (
|
|
86
|
+
Platform.OS === 'ios' &&
|
|
87
|
+
keyboardFrame.screenY === 0 &&
|
|
88
|
+
(await AccessibilityInfo.prefersCrossFadeTransitions())
|
|
89
|
+
) {
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const keyboardY =
|
|
94
|
+
keyboardFrame.screenY - (this.props.keyboardVerticalOffset ?? 0);
|
|
95
|
+
|
|
96
|
+
if (this.props.behavior === 'height') {
|
|
97
|
+
return Math.max(
|
|
98
|
+
this.state.bottom + frame.y + frame.height - keyboardY,
|
|
99
|
+
0,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Calculate the displacement needed for the view such that it
|
|
104
|
+
// no longer overlaps with the keyboard
|
|
105
|
+
return Math.max(frame.y + frame.height - keyboardY, 0);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
_onKeyboardChange = (event: ?KeyboardEvent) => {
|
|
109
|
+
this._keyboardEvent = event;
|
|
110
|
+
// $FlowFixMe[unused-promise]
|
|
111
|
+
this._updateBottomIfNecessary();
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
_onLayout = async (event: ViewLayoutEvent) => {
|
|
115
|
+
const wasFrameNull = this._frame == null;
|
|
116
|
+
this._frame = event.nativeEvent.layout;
|
|
117
|
+
if (!this._initialFrameHeight) {
|
|
118
|
+
// save the initial frame height, before the keyboard is visible
|
|
119
|
+
this._initialFrameHeight = this._frame.height;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (wasFrameNull) {
|
|
123
|
+
await this._updateBottomIfNecessary();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (this.props.onLayout) {
|
|
127
|
+
this.props.onLayout(event);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
_updateBottomIfNecessary = async () => {
|
|
132
|
+
if (this._keyboardEvent == null) {
|
|
133
|
+
this.setState({bottom: 0});
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const {duration, easing, endCoordinates} = this._keyboardEvent;
|
|
138
|
+
const height = await this._relativeKeyboardHeight(endCoordinates);
|
|
139
|
+
|
|
140
|
+
if (this.state.bottom === height) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (duration && easing) {
|
|
145
|
+
LayoutAnimation.configureNext({
|
|
146
|
+
// We have to pass the duration equal to minimal accepted duration defined here: RCTLayoutAnimation.m
|
|
147
|
+
duration: duration > 10 ? duration : 10,
|
|
148
|
+
update: {
|
|
149
|
+
duration: duration > 10 ? duration : 10,
|
|
150
|
+
type: LayoutAnimation.Types[easing] || 'keyboard',
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
this.setState({bottom: height});
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
componentDidMount(): void {
|
|
158
|
+
if (Platform.OS === 'ios') {
|
|
159
|
+
this._subscriptions = [
|
|
160
|
+
Keyboard.addListener('keyboardWillChangeFrame', this._onKeyboardChange),
|
|
161
|
+
];
|
|
162
|
+
} else {
|
|
163
|
+
this._subscriptions = [
|
|
164
|
+
Keyboard.addListener('keyboardDidHide', this._onKeyboardChange),
|
|
165
|
+
Keyboard.addListener('keyboardDidShow', this._onKeyboardChange),
|
|
166
|
+
];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
componentWillUnmount(): void {
|
|
171
|
+
this._subscriptions.forEach(subscription => {
|
|
172
|
+
subscription.remove();
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
render(): React.Node {
|
|
177
|
+
const {
|
|
178
|
+
behavior,
|
|
179
|
+
children,
|
|
180
|
+
contentContainerStyle,
|
|
181
|
+
enabled = true,
|
|
182
|
+
// eslint-disable-next-line no-unused-vars
|
|
183
|
+
keyboardVerticalOffset = 0,
|
|
184
|
+
style,
|
|
185
|
+
onLayout,
|
|
186
|
+
...props
|
|
187
|
+
} = this.props;
|
|
188
|
+
const bottomHeight = enabled === true ? this.state.bottom : 0;
|
|
189
|
+
switch (behavior) {
|
|
190
|
+
case 'height':
|
|
191
|
+
let heightStyle;
|
|
192
|
+
if (this._frame != null && this.state.bottom > 0) {
|
|
193
|
+
// Note that we only apply a height change when there is keyboard present,
|
|
194
|
+
// i.e. this.state.bottom is greater than 0. If we remove that condition,
|
|
195
|
+
// this.frame.height will never go back to its original value.
|
|
196
|
+
// When height changes, we need to disable flex.
|
|
197
|
+
heightStyle = {
|
|
198
|
+
height: this._initialFrameHeight - bottomHeight,
|
|
199
|
+
flex: 0,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
/* RNOH: patch */
|
|
203
|
+
return (
|
|
204
|
+
<View
|
|
205
|
+
ref={this.viewRef}
|
|
206
|
+
style={StyleSheet.compose(style, heightStyle)}
|
|
207
|
+
onLayout={this._onLayout}
|
|
208
|
+
{...props}>
|
|
209
|
+
{React.Children.map(children, child => React.cloneElement(child, {__keyboardAvoidingViewBottomHeight: bottomHeight}))}
|
|
210
|
+
</View>
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
case 'position':
|
|
214
|
+
return (
|
|
215
|
+
<View
|
|
216
|
+
ref={this.viewRef}
|
|
217
|
+
style={style}
|
|
218
|
+
onLayout={this._onLayout}
|
|
219
|
+
{...props}>
|
|
220
|
+
<View
|
|
221
|
+
style={StyleSheet.compose(contentContainerStyle, {
|
|
222
|
+
bottom: bottomHeight,
|
|
223
|
+
})}>
|
|
224
|
+
{children}
|
|
225
|
+
</View>
|
|
226
|
+
</View>
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
case 'padding':
|
|
230
|
+
/* RNOH: patch */
|
|
231
|
+
return (
|
|
232
|
+
<View
|
|
233
|
+
ref={this.viewRef}
|
|
234
|
+
style={StyleSheet.compose(style, {paddingBottom: bottomHeight})}
|
|
235
|
+
onLayout={this._onLayout}
|
|
236
|
+
{...props}>
|
|
237
|
+
{React.Children.map(children, child => React.cloneElement(child, {__keyboardAvoidingViewBottomHeight: bottomHeight}))}
|
|
238
|
+
</View>
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
default:
|
|
242
|
+
return (
|
|
243
|
+
<View
|
|
244
|
+
ref={this.viewRef}
|
|
245
|
+
onLayout={this._onLayout}
|
|
246
|
+
style={style}
|
|
247
|
+
{...props}>
|
|
248
|
+
{children}
|
|
249
|
+
</View>
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export default KeyboardAvoidingView;
|
|
@@ -684,6 +684,8 @@ type State = {|
|
|
|
684
684
|
// RNOH patch - resolving flashScrollIndicators command on the JS side
|
|
685
685
|
// as it is currently not feasible on the native side
|
|
686
686
|
showScrollIndicator: boolean,
|
|
687
|
+
contentOffset: ?number,
|
|
688
|
+
contentHeight: ?number,
|
|
687
689
|
|};
|
|
688
690
|
|
|
689
691
|
const IS_ANIMATING_TOUCH_START_THRESHOLD_MS = 16;
|
|
@@ -772,6 +774,8 @@ class ScrollView extends React.Component<Props, State> {
|
|
|
772
774
|
// RNOH patch - resolving flashScrollIndicators command on the JS side
|
|
773
775
|
// as it is currently not feasible on the native side
|
|
774
776
|
showScrollIndicator: false,
|
|
777
|
+
contentOffset: null,
|
|
778
|
+
contentHeight: null,
|
|
775
779
|
};
|
|
776
780
|
|
|
777
781
|
componentDidMount() {
|
|
@@ -1188,14 +1192,15 @@ class ScrollView extends React.Component<Props, State> {
|
|
|
1188
1192
|
);
|
|
1189
1193
|
}
|
|
1190
1194
|
}
|
|
1195
|
+
this.setState({
|
|
1196
|
+
contentOffset: e.nativeEvent.contentOffset.y
|
|
1197
|
+
});
|
|
1191
1198
|
this._observedScrollSinceBecomingResponder = true;
|
|
1192
1199
|
this.props.onScroll && this.props.onScroll(e);
|
|
1193
1200
|
};
|
|
1194
1201
|
|
|
1195
1202
|
_handleLayout = (e: LayoutEvent) => {
|
|
1196
|
-
|
|
1197
|
-
this.setState({ layoutHeight: e.nativeEvent.layout.height });
|
|
1198
|
-
}
|
|
1203
|
+
this.setState({ layoutHeight: e.nativeEvent.layout.height });
|
|
1199
1204
|
if (this.props.onLayout) {
|
|
1200
1205
|
this.props.onLayout(e);
|
|
1201
1206
|
}
|
|
@@ -1203,6 +1208,26 @@ class ScrollView extends React.Component<Props, State> {
|
|
|
1203
1208
|
|
|
1204
1209
|
_handleContentOnLayout = (e: LayoutEvent) => {
|
|
1205
1210
|
const { width, height } = e.nativeEvent.layout;
|
|
1211
|
+
/**
|
|
1212
|
+
* RNOH patch - Fixed the issue where sticky headers are in disordered position after foldable phone is expanded/folded.
|
|
1213
|
+
* When a foldable phone is expanded/folded, the height of the list items often changes,
|
|
1214
|
+
* causing the content height of the ScrollView to change.
|
|
1215
|
+
* But since the scroll event is not triggered, the position of the sticky headers is
|
|
1216
|
+
* still offset according to the original content height.
|
|
1217
|
+
* So we should recalculate the contentOffset based on the new contentHeight and trigger
|
|
1218
|
+
* the scroll event to fix the position of sticky headers
|
|
1219
|
+
*/
|
|
1220
|
+
let { contentOffset, contentHeight, layoutHeight } = this.state;
|
|
1221
|
+
if (contentOffset && contentHeight) {
|
|
1222
|
+
contentOffset *= (height - layoutHeight) / (contentHeight - layoutHeight);
|
|
1223
|
+
(contentOffset < 0) && (contentOffset = 0);
|
|
1224
|
+
this.scrollTo({ y: contentOffset, animated: false });
|
|
1225
|
+
this._scrollAnimatedValue.setValue(contentOffset);
|
|
1226
|
+
}
|
|
1227
|
+
this.setState({
|
|
1228
|
+
contentOffset,
|
|
1229
|
+
contentHeight: height
|
|
1230
|
+
});
|
|
1206
1231
|
this.props.onContentSizeChange &&
|
|
1207
1232
|
this.props.onContentSizeChange(width, height);
|
|
1208
1233
|
};
|
|
@@ -406,6 +406,9 @@ class StatusBar extends React.Component<Props> {
|
|
|
406
406
|
StatusBar._defaultProps
|
|
407
407
|
);
|
|
408
408
|
//RNOH: patch - delete code specific to IOS and Android
|
|
409
|
+
if (!oldProps || oldProps.hidden.value !== mergedProps.hidden.value) {
|
|
410
|
+
NativeStatusBarManagerHarmony.setHidden(mergedProps.hidden.value);
|
|
411
|
+
}
|
|
409
412
|
|
|
410
413
|
// Update the props that have changed using the merged values from the props stack.
|
|
411
414
|
if (!oldProps || oldProps.barStyle.value !== mergedProps.barStyle.value) {
|
|
@@ -431,9 +434,6 @@ class StatusBar extends React.Component<Props> {
|
|
|
431
434
|
NativeStatusBarManagerHarmony.setColor(processedColor, mergedProps.backgroundColor.animated);
|
|
432
435
|
}
|
|
433
436
|
}
|
|
434
|
-
if (!oldProps || oldProps.hidden.value !== mergedProps.hidden.value) {
|
|
435
|
-
NativeStatusBarManagerHarmony.setHidden(mergedProps.hidden.value);
|
|
436
|
-
}
|
|
437
437
|
// Update the current prop values.
|
|
438
438
|
StatusBar._currentValues = mergedProps;
|
|
439
439
|
});
|
package/index.js
CHANGED
|
@@ -76,7 +76,7 @@ module.exports = {
|
|
|
76
76
|
return require('react-native/Libraries/Components/Keyboard/Keyboard');
|
|
77
77
|
},
|
|
78
78
|
get KeyboardAvoidingView() {
|
|
79
|
-
return require('
|
|
79
|
+
return require('./Libraries/Components/Keyboard/KeyboardAvoidingView')
|
|
80
80
|
.default;
|
|
81
81
|
},
|
|
82
82
|
get NativeEventEmitter() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-native-oh/react-native-harmony",
|
|
3
|
-
"version": "0.72.
|
|
3
|
+
"version": "0.72.28-1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"./*.har"
|
|
50
50
|
],
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@react-native-oh/react-native-harmony-cli": "^0.0.26",
|
|
52
|
+
"@react-native-oh/react-native-harmony-cli": "^0.0.26-12",
|
|
53
53
|
"colors": "^1.4.0",
|
|
54
54
|
"fs-extra": "^11.1.1",
|
|
55
55
|
"metro": "^0.76.3",
|
|
Binary file
|