react-native#TextInputKeyPressEventData TypeScript Examples
The following examples show how to use
react-native#TextInputKeyPressEventData.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: MentionInput.tsx From lowkey-react-native-mentions-input with MIT License | 4 votes |
MentionsInput = React.forwardRef(
(
{
suggestedUsersComponent,
textInputStyle,
onFocusStateChange = () => {},
onTextChange = () => {},
onMarkdownChange = () => {},
placeholder = 'Write a message...',
placeholderTextColor,
multiline,
textInputTextStyle,
leftComponent = <></>,
rightComponent = <></>,
innerComponent = <></>,
users,
...props
}: Props,
ref
) => {
const [isOpen, SetIsOpen] = useState(false);
const [suggestedUsers, SetSuggesedUsers] = useState<SuggestedUsers[]>([]);
const [matches, SetMatches] = useState<any[]>([]);
const [mentions, SetMentions] = useState<any[]>([]);
const [currentCursorPosition, SetCurrentCursorPosition] = useState(0);
useEffect(() => {
if (props.value === '' && (mentions.length > 0 || matches.length > 0)) {
SetMatches([]);
SetMentions([]);
SetCurrentCursorPosition(1);
}
}, [matches, mentions, props.value]);
const transformTag = useCallback((value: string) => {
return value
.replace(/\s+/g, '')
.toLowerCase()
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '');
}, []);
const handleSuggestionsOpen = useCallback(
(values: RegExpMatchArray[], currentCursorPosition: number) => {
let shouldPresentSuggestions = false;
let newSuggestedUsers: Array<SuggestedUsers> = [];
users.map(
(user, index) =>
(newSuggestedUsers[index] = {
...user,
startPosition: 0,
})
);
values.map((match) => {
if (match === null) {
return;
}
const matchStartPosition = match.index;
if (typeof matchStartPosition === 'undefined') {
return;
}
const matchEndPosition = matchStartPosition + match[0].length;
if (
currentCursorPosition > matchStartPosition &&
currentCursorPosition <= matchEndPosition
) {
shouldPresentSuggestions = true;
newSuggestedUsers = newSuggestedUsers
.filter((user) =>
user.name
.toLowerCase()
.includes(match[0].substring(1).toLowerCase())
)
.map((user) => {
user.startPosition = matchStartPosition;
return user;
});
}
});
const isSameSuggestedUser =
suggestedUsers.length === newSuggestedUsers.length &&
suggestedUsers.every(
(value, index) =>
value.id === newSuggestedUsers[index].id &&
value.startPosition == newSuggestedUsers[index].startPosition
);
SetIsOpen(shouldPresentSuggestions);
if (!isSameSuggestedUser) {
SetSuggesedUsers(newSuggestedUsers);
}
},
[users, suggestedUsers]
);
const formatMarkdown = useCallback(
(markdown: string) => {
let parseHeadIndex = 0;
let markdownArray = [];
if (mentions.length === 0) {
markdownArray.push({
type: 'text',
data: markdown,
});
}
mentions.map((mention, index) => {
let match = matches.find((m) => {
return (
m.index === mention.user.startPosition &&
m[0] === `@${mention.user.name}`
);
});
if (typeof match === 'undefined') {
return;
}
markdownArray.push({
type: 'text',
data: markdown.substring(
parseHeadIndex,
mention.user.startPosition
),
});
markdownArray.push({
type: 'mention',
data: `<@${mention.user.name}::${mention.user.id}>`,
});
parseHeadIndex =
mention.user.startPosition + mention.user.name.length + 1;
if (index === mentions.length - 1) {
markdownArray.push({
type: 'text',
data: markdown.substring(parseHeadIndex, markdown.length),
});
}
});
markdown = '';
markdownArray.map((m) => {
if (m.type === 'text') {
markdown = markdown + encodeURIComponent(m.data);
} else if (m.type === 'mention') {
markdown = markdown + m.data;
}
});
onMarkdownChange(markdown);
},
[onMarkdownChange, mentions, matches]
);
const handleDelete = useCallback(
({ nativeEvent }: NativeSyntheticEvent<TextInputKeyPressEventData>) => {
if (nativeEvent.key === 'Backspace') {
mentions.map((mention, index) => {
const matchStartPosition = mention.user.startPosition;
const matchEndPosition =
matchStartPosition + mention.user.name.length + 1;
if (
currentCursorPosition > matchStartPosition &&
currentCursorPosition <= matchEndPosition
) {
const newMentions = mentions;
newMentions.splice(index, 1);
SetMentions(newMentions);
}
});
}
},
[mentions, currentCursorPosition]
);
const onSelectionChange = useCallback(
({
nativeEvent,
}: NativeSyntheticEvent<TextInputSelectionChangeEventData>) => {
if (nativeEvent.selection.start === nativeEvent.selection.end) {
SetCurrentCursorPosition(nativeEvent.selection.start);
}
},
[]
);
const handleMentions = useCallback(
(newText: string, currentCursorPosition: number) => {
const pattern = PATTERNS.USERNAME_MENTION;
let newMatches = [...matchAll(newText, pattern)];
let newMentions = newText.length > 0 ? mentions : [];
newMentions.map((mention) => {
const matchStartPosition = mention.user.startPosition;
if (decodeURI(newText).length - decodeURI(props.value).length > 0) {
if (
matchStartPosition + (newText.length - props.value.length) >
currentCursorPosition &&
currentCursorPosition !== props.value.length
) {
mention.user.startPosition =
mention.user.startPosition +
(newText.length - props.value.length);
}
} else {
if (matchStartPosition >= currentCursorPosition) {
mention.user.startPosition =
mention.user.startPosition +
(newText.length - props.value.length);
}
}
return mention;
});
onTextChange(newText);
formatMarkdown(newText);
const isSameMatch =
matches.length === newMatches.length &&
matches.every((value, index) => value === newMatches[index]);
SetMentions(newMentions);
if (!isSameMatch) {
SetMatches(newMatches);
}
},
[mentions, onTextChange, formatMarkdown, props.value, matches]
);
const onChangeText = useCallback(
(newText: string) => {
handleMentions(newText, currentCursorPosition);
},
[handleMentions, currentCursorPosition]
);
const handleAddMentions = useCallback(
(user: {
id: number;
name: string;
avatar: string;
startPosition: number;
}) => {
const startPosition = user.startPosition;
const mention = mentions.find(
(m) => m.user.startPosition === startPosition
);
if (mention) {
return;
}
const match = matches.find((m) => m.index === startPosition);
let newMentions = mentions;
const userName = transformTag(user.name);
const newText =
props.value.substring(0, match.index) +
`@${userName} ` +
props.value.substring(
match.index + match[0].length,
props.value.length
);
newMentions.push({
user: {
...user,
name: userName,
startPosition: startPosition,
test: 1000,
},
});
newMentions.sort((a, b) =>
a.user.startPosition > b.user.startPosition
? 1
: b.user.startPosition > a.user.startPosition
? -1
: 0
);
SetMentions(newMentions);
SetIsOpen(false);
const newCursor = match.index + user.name.length + 1;
SetCurrentCursorPosition(newCursor);
setTimeout(() => {
handleMentions(newText, newCursor);
}, 100);
},
[mentions, matches, transformTag, props.value, handleMentions]
);
const onFocus = useCallback(() => {
onFocusStateChange(true);
}, [onFocusStateChange]);
const onBlur = useCallback(() => {
onFocusStateChange(false);
}, [onFocusStateChange]);
useEffect(() => {
formatMarkdown(props.value);
}, [props.value, formatMarkdown]);
useEffect(() => {
let timeout = setTimeout(() => {
handleSuggestionsOpen(matches, currentCursorPosition);
}, 100);
return () => clearTimeout(timeout);
}, [handleSuggestionsOpen, matches, currentCursorPosition]);
return (
<View>
<View>
{isOpen && suggestedUsersComponent(suggestedUsers, handleAddMentions)}
<View style={styles.inputContainerRow}>
<View>{leftComponent}</View>
<View style={[textInputStyle, styles.row]}>
<TextInput
{...props}
onFocus={onFocus}
onBlur={onBlur}
placeholder={placeholder}
placeholderTextColor={placeholderTextColor}
multiline={multiline}
value={decodeURI(props.value.replace(/%/g, encodeURI('%')))}
onChangeText={onChangeText}
onKeyPress={handleDelete}
style={[
textInputTextStyle,
styles.flex,
{ paddingBottom: multiline ? 5 : 0 },
]}
onSelectionChange={onSelectionChange}
//@ts-ignore
ref={ref}
/>
<View style={styles.innerContainer}>{innerComponent}</View>
</View>
{rightComponent}
</View>
</View>
</View>
);
}
)