mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-05-29 17:12:37 +00:00
Fix TextInput onSelectionChange logic
This commit is contained in:
@@ -209,6 +209,12 @@ called with `{ nativeEvent: { selection: { start, end } } }`.
|
|||||||
</Story>
|
</Story>
|
||||||
</Preview>
|
</Preview>
|
||||||
|
|
||||||
|
<Preview withSource='none'>
|
||||||
|
<Story name="onSelectionChangeControlled">
|
||||||
|
<Stories.onSelectionChangeControlled />
|
||||||
|
</Story>
|
||||||
|
</Preview>
|
||||||
|
|
||||||
### onSubmitEditing
|
### onSubmitEditing
|
||||||
|
|
||||||
Callback that is called when the keyboard's submit button is pressed. When
|
Callback that is called when the keyboard's submit button is pressed. When
|
||||||
|
|||||||
@@ -2,106 +2,27 @@
|
|||||||
* @noflow
|
* @noflow
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
|
import { View, Text, TextInput } from 'react-native';
|
||||||
import { styles } from '../helpers';
|
import { styles } from '../helpers';
|
||||||
import { Text, TextInput, View } from 'react-native';
|
|
||||||
|
|
||||||
type SelectionExampleState = {
|
function OnSelectionChange() {
|
||||||
selection: {
|
const [text, setText] = useState('');
|
||||||
start: number,
|
const [selection, setSelection] = useState({ start: 0, end: 0 });
|
||||||
end?: number
|
|
||||||
},
|
|
||||||
value: string
|
|
||||||
};
|
|
||||||
|
|
||||||
class OnSelectionChangeExample extends React.Component {
|
return (
|
||||||
state: SelectionExampleState;
|
<View>
|
||||||
|
<TextInput
|
||||||
_textInput: any;
|
onChangeText={setText}
|
||||||
|
onSelectionChange={event => {
|
||||||
constructor(props) {
|
setSelection(event.nativeEvent.selection);
|
||||||
super(props);
|
}}
|
||||||
this.state = {
|
style={styles.textinput}
|
||||||
selection: { start: 0, end: 0 },
|
value={text}
|
||||||
value: props.value
|
/>
|
||||||
};
|
<Text>{JSON.stringify(selection)}</Text>
|
||||||
}
|
</View>
|
||||||
|
);
|
||||||
onSelectionChange = ({ nativeEvent: { selection } }) => {
|
|
||||||
this.setState({ selection });
|
|
||||||
};
|
|
||||||
|
|
||||||
onChangeText = value => {
|
|
||||||
this.setState({ value });
|
|
||||||
};
|
|
||||||
|
|
||||||
getRandomPosition() {
|
|
||||||
const length = this.state.value.length;
|
|
||||||
return Math.round(Math.random() * length);
|
|
||||||
}
|
|
||||||
|
|
||||||
select = (start, end) => () => {
|
|
||||||
this._textInput.focus();
|
|
||||||
this.setState({ selection: { start, end } });
|
|
||||||
};
|
|
||||||
|
|
||||||
selectRandom = () => {
|
|
||||||
const positions = [this.getRandomPosition(), this.getRandomPosition()].sort();
|
|
||||||
this.select(...positions)();
|
|
||||||
};
|
|
||||||
|
|
||||||
placeAt = position => () => {
|
|
||||||
this.select(position, position)();
|
|
||||||
};
|
|
||||||
|
|
||||||
placeAtRandom = () => {
|
|
||||||
this.placeAt(this.getRandomPosition())();
|
|
||||||
};
|
|
||||||
|
|
||||||
setRef = textInput => {
|
|
||||||
this._textInput = textInput;
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const length = this.state.value.length;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View>
|
|
||||||
<TextInput
|
|
||||||
multiline={this.props.multiline}
|
|
||||||
onChangeText={this.onChangeText}
|
|
||||||
onSelectionChange={this.onSelectionChange}
|
|
||||||
ref={this.setRef}
|
|
||||||
selection={this.state.selection}
|
|
||||||
style={this.props.style}
|
|
||||||
value={this.state.value}
|
|
||||||
/>
|
|
||||||
<View>
|
|
||||||
<Text>selection = {JSON.stringify(this.state.selection)}</Text>
|
|
||||||
<Text onPress={this.placeAt(0)}>Place at Start (0, 0)</Text>
|
|
||||||
<Text onPress={this.placeAt(length)}>
|
|
||||||
Place at End ({length}, {length})
|
|
||||||
</Text>
|
|
||||||
<Text onPress={this.placeAtRandom}>Place at Random</Text>
|
|
||||||
<Text onPress={this.select(0, length)}>Select All</Text>
|
|
||||||
<Text onPress={this.selectRandom}>Select Random</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const TextInputOnSelectionChangeExample = () => (
|
export default OnSelectionChange;
|
||||||
<View>
|
|
||||||
<OnSelectionChangeExample style={styles.textinput} value="text selection can be changed" />
|
|
||||||
<OnSelectionChangeExample
|
|
||||||
multiline
|
|
||||||
style={styles.multiline}
|
|
||||||
value={'multiline text selection\ncan also be changed'}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default function OnSelectionChange() {
|
|
||||||
return <TextInputOnSelectionChangeExample />;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
/**
|
||||||
|
* @noflow
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { styles } from '../helpers';
|
||||||
|
import { Text, TextInput, View } from 'react-native';
|
||||||
|
|
||||||
|
type SelectionExampleState = {
|
||||||
|
selection: {
|
||||||
|
start: number,
|
||||||
|
end?: number
|
||||||
|
},
|
||||||
|
value: string
|
||||||
|
};
|
||||||
|
|
||||||
|
class OnSelectionChangeExample extends React.Component {
|
||||||
|
state: SelectionExampleState;
|
||||||
|
|
||||||
|
_textInput: any;
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
selection: { start: 0, end: 0 },
|
||||||
|
value: props.value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelectionChange = ({ nativeEvent: { selection } }) => {
|
||||||
|
this.setState({ selection });
|
||||||
|
};
|
||||||
|
|
||||||
|
onChangeText = value => {
|
||||||
|
this.setState({ value });
|
||||||
|
};
|
||||||
|
|
||||||
|
getRandomPosition() {
|
||||||
|
const length = this.state.value.length;
|
||||||
|
return Math.round(Math.random() * length);
|
||||||
|
}
|
||||||
|
|
||||||
|
select = (start, end) => () => {
|
||||||
|
this._textInput.focus();
|
||||||
|
this.setState({ selection: { start, end } });
|
||||||
|
};
|
||||||
|
|
||||||
|
selectRandom = () => {
|
||||||
|
const positions = [this.getRandomPosition(), this.getRandomPosition()].sort();
|
||||||
|
this.select(...positions)();
|
||||||
|
};
|
||||||
|
|
||||||
|
placeAt = position => () => {
|
||||||
|
this.select(position, position)();
|
||||||
|
};
|
||||||
|
|
||||||
|
placeAtRandom = () => {
|
||||||
|
this.placeAt(this.getRandomPosition())();
|
||||||
|
};
|
||||||
|
|
||||||
|
setRef = textInput => {
|
||||||
|
this._textInput = textInput;
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const length = this.state.value.length;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<TextInput
|
||||||
|
multiline={this.props.multiline}
|
||||||
|
onChangeText={this.onChangeText}
|
||||||
|
onSelectionChange={this.onSelectionChange}
|
||||||
|
ref={this.setRef}
|
||||||
|
selection={this.state.selection}
|
||||||
|
style={this.props.style}
|
||||||
|
value={this.state.value}
|
||||||
|
/>
|
||||||
|
<View>
|
||||||
|
<Text>selection = {JSON.stringify(this.state.selection)}</Text>
|
||||||
|
<Text onPress={this.placeAt(0)}>Place at Start (0, 0)</Text>
|
||||||
|
<Text onPress={this.placeAt(length)}>
|
||||||
|
Place at End ({length}, {length})
|
||||||
|
</Text>
|
||||||
|
<Text onPress={this.placeAtRandom}>Place at Random</Text>
|
||||||
|
<Text onPress={this.select(0, length)}>Select All</Text>
|
||||||
|
<Text onPress={this.selectRandom}>Select Random</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const TextInputOnSelectionChangeExample = () => (
|
||||||
|
<View>
|
||||||
|
<OnSelectionChangeExample style={styles.textinput} value="text selection can be changed" />
|
||||||
|
<OnSelectionChangeExample
|
||||||
|
multiline
|
||||||
|
style={styles.multiline}
|
||||||
|
value={'multiline text selection\ncan also be changed'}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default function OnSelectionChange() {
|
||||||
|
return <TextInputOnSelectionChangeExample />;
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ export { default as maxLength } from './MaxLength';
|
|||||||
export { default as multiline } from './Multiline';
|
export { default as multiline } from './Multiline';
|
||||||
export { default as numberOfLines } from './NumberOfLines';
|
export { default as numberOfLines } from './NumberOfLines';
|
||||||
export { default as onSelectionChange } from './OnSelectionChange';
|
export { default as onSelectionChange } from './OnSelectionChange';
|
||||||
|
export { default as onSelectionChangeControlled } from './OnSelectionChangeControlled';
|
||||||
export { default as placeholder } from './Placeholder';
|
export { default as placeholder } from './Placeholder';
|
||||||
export { default as placeholderTextColor } from './PlaceholderTextColor';
|
export { default as placeholderTextColor } from './PlaceholderTextColor';
|
||||||
export { default as secureTextEntry } from './SecureTextEntry';
|
export { default as secureTextEntry } from './SecureTextEntry';
|
||||||
|
|||||||
+15
-21
@@ -22,19 +22,14 @@ import useResponderEvents from '../../hooks/useResponderEvents';
|
|||||||
import StyleSheet from '../StyleSheet';
|
import StyleSheet from '../StyleSheet';
|
||||||
import TextInputState from '../../modules/TextInputState';
|
import TextInputState from '../../modules/TextInputState';
|
||||||
|
|
||||||
const emptyObject = {};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether a 'selection' prop differs from a node's existing
|
* Determines whether a 'selection' prop differs from a node's existing
|
||||||
* selection state.
|
* selection state.
|
||||||
*/
|
*/
|
||||||
const isSelectionStale = (node, selection) => {
|
const isSelectionStale = (node, selection) => {
|
||||||
if (node != null && selection != null && selection.start != null) {
|
const { selectionEnd, selectionStart } = node;
|
||||||
const { selectionEnd, selectionStart } = node;
|
const { start, end } = selection;
|
||||||
const { start, end } = selection;
|
return start !== selectionStart || end !== selectionEnd;
|
||||||
return start !== selectionStart || end !== selectionEnd;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,7 +37,7 @@ const isSelectionStale = (node, selection) => {
|
|||||||
* error.
|
* error.
|
||||||
*/
|
*/
|
||||||
const setSelection = (node, selection) => {
|
const setSelection = (node, selection) => {
|
||||||
if (node != null && selection != null && isSelectionStale(node, selection)) {
|
if (isSelectionStale(node, selection)) {
|
||||||
const { start, end } = selection;
|
const { start, end } = selection;
|
||||||
try {
|
try {
|
||||||
node.setSelectionRange(start, end || start);
|
node.setSelectionRange(start, end || start);
|
||||||
@@ -155,7 +150,7 @@ const TextInput = forwardRef<TextInputProps, *>((props, forwardedRef) => {
|
|||||||
placeholderTextColor,
|
placeholderTextColor,
|
||||||
returnKeyType,
|
returnKeyType,
|
||||||
secureTextEntry = false,
|
secureTextEntry = false,
|
||||||
selection = emptyObject,
|
selection,
|
||||||
selectTextOnFocus,
|
selectTextOnFocus,
|
||||||
spellCheck
|
spellCheck
|
||||||
} = props;
|
} = props;
|
||||||
@@ -251,7 +246,6 @@ const TextInput = forwardRef<TextInputProps, *>((props, forwardedRef) => {
|
|||||||
if (onChangeText) {
|
if (onChangeText) {
|
||||||
onChangeText(text);
|
onChangeText(text);
|
||||||
}
|
}
|
||||||
handleSelectionChange(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFocus(e) {
|
function handleFocus(e) {
|
||||||
@@ -312,22 +306,22 @@ const TextInput = forwardRef<TextInputProps, *>((props, forwardedRef) => {
|
|||||||
if (onSelectionChange) {
|
if (onSelectionChange) {
|
||||||
try {
|
try {
|
||||||
const node = e.target;
|
const node = e.target;
|
||||||
if (isSelectionStale(node, selection)) {
|
const { selectionStart, selectionEnd } = node;
|
||||||
const { selectionStart, selectionEnd } = node;
|
e.nativeEvent.selection = {
|
||||||
e.nativeEvent.selection = {
|
start: selectionStart,
|
||||||
start: selectionStart,
|
end: selectionEnd
|
||||||
end: selectionEnd
|
};
|
||||||
};
|
e.nativeEvent.text = e.target.value;
|
||||||
e.nativeEvent.text = e.target.value;
|
onSelectionChange(e);
|
||||||
onSelectionChange(e);
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
const node = hostRef.current;
|
const node = hostRef.current;
|
||||||
setSelection(node, selection);
|
if (node != null && selection != null) {
|
||||||
|
setSelection(node, selection);
|
||||||
|
}
|
||||||
if (document.activeElement === node) {
|
if (document.activeElement === node) {
|
||||||
TextInputState._currentlyFocusedNode = node;
|
TextInputState._currentlyFocusedNode = node;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user