Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: include information about text range changes in onChange #45248

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,14 @@ export type NativeProps = $ReadOnly<{|
* TODO: differentiate between onChange and onChangeText
*/
onChange?: ?BubblingEventHandler<
$ReadOnly<{|target: Int32, eventCount: Int32, text: string|}>,
$ReadOnly<{|
target: Int32,
eventCount: Int32,
text: string,
start: Int32,
count: Int32,
before: Int32,
|}>,
>,

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,9 @@ export interface TextInputKeyPressEventData {
export interface TextInputChangeEventData extends TargetedEvent {
eventCount: number;
text: string;
before: number;
start: number;
count: number;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ export type ChangeEvent = SyntheticEvent<
eventCount: number,
target: number,
text: string,
start: number,
count: number,
before: number,
|}>,
>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ export type ChangeEvent = SyntheticEvent<
eventCount: number,
target: number,
text: string,
start: number,
count: number,
before: number,
|}>,
>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2270,7 +2270,14 @@ export type NativeProps = $ReadOnly<{|
onBlur?: ?BubblingEventHandler<$ReadOnly<{| target: Int32 |}>>,
onFocus?: ?BubblingEventHandler<$ReadOnly<{| target: Int32 |}>>,
onChange?: ?BubblingEventHandler<
$ReadOnly<{| target: Int32, eventCount: Int32, text: string |}>,
$ReadOnly<{|
target: Int32,
eventCount: Int32,
text: string,
start: Int32,
count: Int32,
before: Int32,
|}>,
>,
onChangeText?: ?BubblingEventHandler<
$ReadOnly<{| target: Int32, eventCount: Int32, text: string |}>,
Expand Down Expand Up @@ -2423,6 +2430,9 @@ export type ChangeEvent = SyntheticEvent<
eventCount: number,
target: number,
text: string,
start: number,
count: number,
before: number,
|}>,
>;
export type TextInputEvent = SyntheticEvent<
Expand Down Expand Up @@ -2768,6 +2778,9 @@ export type ChangeEvent = SyntheticEvent<
eventCount: number,
target: number,
text: string,
start: number,
count: number,
before: number,
|}>,
>;
export type TextInputEvent = SyntheticEvent<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ @implementation RCTTextInputComponentView {
*/
BOOL _comingFromJS;
BOOL _didMoveToWindow;

/**
* Keep track of the range of the text that is being changed.
*/
NSInteger _changeStart;
NSInteger _changeBefore;
NSInteger _changeCount;
}

#pragma mark - UIView overrides
Expand Down Expand Up @@ -328,6 +335,10 @@ - (NSString *)textInputShouldChangeText:(NSString *)text inRange:(NSRange)range
{
const auto &props = static_cast<const TextInputProps &>(*_props);

_changeStart = range.location;
_changeBefore = range.length;
_changeCount = text.length;

if (!_backedTextInputView.textWasPasted) {
if (_eventEmitter) {
const auto &textInputEventEmitter = static_cast<const TextInputEventEmitter &>(*_eventEmitter);
Expand Down Expand Up @@ -576,6 +587,9 @@ - (void)handleInputAccessoryDoneButton
.text = RCTStringFromNSString(_backedTextInputView.attributedText.string),
.selectionRange = [self _selectionRange],
.eventCount = static_cast<int>(_mostRecentEventCount),
.start = static_cast<int>(_changeStart),
.count = static_cast<int>(_changeCount),
.before = static_cast<int>(_changeBefore),
.contentOffset = RCTPointFromCGPoint(_backedTextInputView.contentOffset),
.contentInset = RCTEdgeInsetsFromUIEdgeInsets(_backedTextInputView.contentInset),
.contentSize = RCTSizeFromCGSize(_backedTextInputView.contentSize),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,23 @@ public class ReactTextChangedEvent extends Event<ReactTextChangedEvent> {

private String mText;
private int mEventCount;
// See https://developer.android.com/reference/android/text/TextWatcher#onTextChanged(java.lang.CharSequence,%20int,%20int,%20int)
private int mStart;
private int mCount;
private int mBefore;

@Deprecated
public ReactTextChangedEvent(int viewId, String text, int eventCount) {
this(ViewUtil.NO_SURFACE_ID, viewId, text, eventCount);
public ReactTextChangedEvent(int viewId, String text, int eventCount, int start, int count, int before) {
this(ViewUtil.NO_SURFACE_ID, viewId, text, eventCount, start, count, before);
}

public ReactTextChangedEvent(int surfaceId, int viewId, String text, int eventCount) {
public ReactTextChangedEvent(int surfaceId, int viewId, String text, int eventCount, int start, int count, int before) {
super(surfaceId, viewId);
mText = text;
mEventCount = eventCount;
mStart = start;
mCount = count;
mBefore = before;
}

@Override
Expand All @@ -47,6 +54,9 @@ protected WritableMap getEventData() {
eventData.putString("text", mText);
eventData.putInt("eventCount", mEventCount);
eventData.putInt("target", getViewTag());
eventData.putInt("start", mStart);
eventData.putInt("count", mCount);
eventData.putInt("before", mBefore);
return eventData;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1118,7 +1118,12 @@ public void onTextChanged(CharSequence s, int start, int before, int count) {
mSurfaceId,
mEditText.getId(),
s.toString(),
mEditText.incrementAndGetEventCounter()));
mEditText.incrementAndGetEventCounter(),
start,
count,
before
)
);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ static jsi::Value textInputMetricsPayload(
textInputMetrics.selectionRange.location +
textInputMetrics.selectionRange.length);
payload.setProperty(runtime, "selection", selection);
payload.setProperty(runtime, "start", textInputMetrics.start);
payload.setProperty(runtime, "count", textInputMetrics.count);
payload.setProperty(runtime, "before", textInputMetrics.before);
}

return payload;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ class TextInputEventEmitter : public ViewEventEmitter {
struct Metrics {
std::string text;
AttributedString::Range selectionRange;
int count;
int start;
int before;
// ScrollView-like metrics
Size contentSize;
Point contentOffset;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
'use strict';

import type {RNTesterModuleExample} from '../../types/RNTesterTypes';
import type {ChangeEvent} from 'react-native/Libraries/Components/TextInput/TextInput';
import type {TextStyle} from 'react-native/Libraries/StyleSheet/StyleSheet';

import RNTesterButton from '../../components/RNTesterButton';
Expand Down Expand Up @@ -813,6 +814,35 @@ function MultilineStyledTextInput({
);
}

function PartialUpdatesTextInput() {
const [value, setValue] = useState('');

const onChange = ({nativeEvent}: ChangeEvent) => {
console.log('onChange', nativeEvent);
setValue(previousValue => {
const {count, start, before, text: fullNewText} = nativeEvent;
// This method is called to notify you that, within fullNewText, the "count" characters beginning at "start" have just
// replaced old text that had length "before".
const newText = fullNewText.substring(start, start + count);
// Replace newText in the original text:
const updatedText =
previousValue.substring(0, start) +
newText +
previousValue.substring(start + before);

return updatedText;
});
};

return (
<ExampleTextInput
placeholder="Enter some text"
onChange={onChange}
value={value}
/>
);
}

module.exports = ([
{
title: 'Auto-focus',
Expand Down Expand Up @@ -1121,4 +1151,11 @@ module.exports = ([
);
},
},
{
title: 'Text input with partial updates in onChange',
name: 'partialUpdates',
render: function (): React.Node {
return <PartialUpdatesTextInput />;
},
},
]: Array<RNTesterModuleExample>);
Loading