Skip to content

Commit 9cc911d

Browse files
duom青源duom青源
duom青源
authored and
duom青源
committed
feat: bugfix
1 parent 9339695 commit 9cc911d

File tree

7 files changed

+241
-99
lines changed

7 files changed

+241
-99
lines changed

example/src/App.tsx

+6-17
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,13 @@ import {
1212
IATTextViewBase,
1313
IInserTextAttachmentItem,
1414
ITextType,
15-
IVTTextInputData,
15+
IonMentionData,
1616
VariableTextInputView,
1717
} from 'react-native-variable-text-input';
1818
export const App = () => {
1919
const inPutRef = React.createRef<IATTextViewBase>();
2020
const onChangeText = (text: string) => {
21-
console.log('输入框数据====>', text);
22-
const triggerRegEx = /({([^{^}]*)}\[([^[]*)]\(([^(^)]*)\))/gi;
23-
const singleGroupTriggerRegEx = /({[^{^}]*}\[[^[]*]\([^(^)]*\))/gi;
24-
const matchStr = text.match(triggerRegEx);
25-
if (matchStr !== null) {
26-
const subStrArr = text.split(triggerRegEx);
27-
subStrArr.forEach((item) => {
28-
const arr = item.match(singleGroupTriggerRegEx);
29-
console.log('==处理后的数据==>', arr);
30-
});
31-
}
21+
console.log('====>', text);
3222
};
3323
const insertEmoji = () => {
3424
const data: ImageResolvedAssetSource = Image.resolveAssetSource(
@@ -92,9 +82,9 @@ export const App = () => {
9282
const sub = (e: any) => {
9383
console.log('rrrrr===>', e);
9484
};
95-
const onTextInput = (event: IVTTextInputData) => {
85+
const onMention = (data: IonMentionData) => {
9686
//todo
97-
console.log('=====>', event);
87+
console.log('onMentions===>', data);
9888
};
9989
return (
10090
<ScrollView contentContainerStyle={styles.container}>
@@ -107,8 +97,9 @@ export const App = () => {
10797
underlineColorAndroid={'rgba(0,0,0,0)'}
10898
blurOnSubmit={true}
10999
onSubmitEditing={sub}
100+
mentions={['@', '#']}
101+
onMention={onMention}
110102
keyboardAppearance={'dark'}
111-
onTextInput={onTextInput}
112103
/>
113104
<View style={{ flexDirection: 'row', marginTop: 40 }}>
114105
<TouchableOpacity onPress={blur} style={{ marginLeft: 20 }}>
@@ -155,7 +146,5 @@ const styles = StyleSheet.create({
155146
color: '#fff',
156147
fontSize: 14,
157148
width: '100%',
158-
minHeight: 100,
159-
borderRadius: 5,
160149
},
161150
});

ios/VariableTextInput.m

-1
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,6 @@ -(void)paste:(id)sender{
485485
NSAttributedString *newAttriString = [self getEmojiText:defaultPasteboard.string];
486486
[self insertAttriStringToTextview:newAttriString];
487487
}
488-
return;
489488
}
490489
[super paste:sender];
491490
}

ios/VariableTextInputViewManager.m

+1
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ -(void)insertTagText:(NSDictionary *)mention{
239239
[self setTextAttachment:image tag:emojiTag size:textSize copyStr:copyStr];
240240

241241
}
242+
242243
-(void)setAttributedText:(NSArray *)arr{
243244
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc]init];
244245
UIFont *textFont = [_textInput.defultTypingAttributes objectForKey:@"NSFont"];

src/Util.ts

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import type { IEmojiData, IInserTextAttachmentItem } from './exTypes';
2+
3+
/**
4+
* {"@"}[userName](id)
5+
*/
6+
const regex = /({[^{}[\]()]*}\[[^[\]()]*]\([^(){}]*\))/g;
7+
const singleRegex = /{([^\]}]*)}\[([^\]]*)]\(([^)]*)\)/;
8+
const emojiPattern = /\[[^\]]*\]|(\w+)/g;
9+
10+
/**
11+
* Delete keyboard when inserting @ and #
12+
*/
13+
const deletKeyBord = (str: string, target: string) => {
14+
const regex1 = new RegExp(`${target}(?=[^${target}]*$)`);
15+
const result = str.replace(regex1, '');
16+
return result;
17+
};
18+
/**
19+
* handleText
20+
* example
21+
* any{"@"}[userName](id)any to ["any","{"@"}[userName](id)","any"]
22+
*/
23+
const handleText = (str: string) => {
24+
const result = str.split(regex).filter(Boolean);
25+
return result;
26+
};
27+
/**
28+
* singleArr
29+
* example
30+
* {"@"}[userName](id) to ["@","userName","id"]
31+
*/
32+
const singleArr = (str: string) => {
33+
const result = str.match(singleRegex)?.slice(1);
34+
return result;
35+
};
36+
/**
37+
* getAttArr
38+
*/
39+
const getAttributedTextArr = (str: string, emojiData?: IEmojiData[]) => {
40+
const arr = handleText(str);
41+
const newAtt: IInserTextAttachmentItem[] = [];
42+
arr.forEach((item) => {
43+
const matchResult = singleArr(item);
44+
if (!!matchResult) {
45+
const attItem: IInserTextAttachmentItem = {
46+
type: 2,
47+
tag: '@',
48+
name: matchResult[1],
49+
id: matchResult[2],
50+
color: '#CEDA39',
51+
};
52+
newAtt.push(attItem);
53+
} else {
54+
if (!!emojiData && emojiData.length > 1) {
55+
const emojiStrAr = getEmojiStrArr(item);
56+
const emojiTagArr = emojiData.map((emoji) => emoji.emojiTag);
57+
emojiStrAr?.forEach((jItem) => {
58+
if (emojiTagArr.includes(jItem)) {
59+
const mateArr = emojiData.filter((fi) => fi.emojiTag === jItem);
60+
const singleEmojiData = mateArr[0];
61+
const emojiItem: IInserTextAttachmentItem = {
62+
type: 1,
63+
emojiTag: singleEmojiData?.emojiTag,
64+
img: singleEmojiData?.img,
65+
emojiUri: singleEmojiData?.img?.uri,
66+
};
67+
newAtt.push(emojiItem);
68+
} else {
69+
const normalItem: IInserTextAttachmentItem = {
70+
type: 0,
71+
text: jItem,
72+
};
73+
newAtt.push(normalItem);
74+
}
75+
});
76+
}
77+
}
78+
});
79+
return newAtt;
80+
};
81+
const getEmojiStrArr = (str: string) => {
82+
const arr = str.match(emojiPattern)?.filter(Boolean);
83+
return arr;
84+
};
85+
export { getAttributedTextArr, deletKeyBord, regex, singleRegex, emojiPattern };

src/VariableTextInputView.tsx

+86-81
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@ import {
66
NativeSyntheticEvent,
77
TextInputChangeEventData,
88
NativeModules,
9-
ImageResolvedAssetSource,
109
StyleSheet,
1110
processColor,
12-
ProcessedColorValue,
1311
TextInputContentSizeChangeEventData,
1412
Platform,
1513
KeyboardTypeOptions,
@@ -22,44 +20,18 @@ import React, {
2220
} from 'react';
2321
import { UIManager } from 'react-native';
2422
import { findNodeHandle } from 'react-native';
23+
import type {
24+
IATTextViewBase,
25+
IEmojiData,
26+
IInserTextAttachmentItem,
27+
IVTTextInputData,
28+
IonMentionData,
29+
MentionData,
30+
PrivateItemData,
31+
} from './exTypes';
32+
import { deletKeyBord, getAttributedTextArr } from './Util';
2533
const VariableTextInputViewManager = NativeModules.VariableTextInputViewManager;
26-
export interface IVTTextInputData {
27-
nativeEvent: {
28-
text: string;
29-
};
30-
}
31-
export enum ITextType {
32-
emoji = 1,
33-
normal = 0,
34-
tagText = 2,
35-
}
36-
interface PrivateItemData {
37-
type: ITextType;
38-
text?: string;
39-
color?: ProcessedColorValue | null | undefined;
40-
tag?: '@' | '#';
41-
name?: string;
42-
id?: string;
43-
img?: ImageResolvedAssetSource; //emoji图片
44-
emojiTag?: string; //[微笑] //emojitag
45-
}
46-
export interface IInserTextAttachmentItem {
47-
type: ITextType;
48-
text?: string;
49-
color?: ColorValue;
50-
tag?: '@' | '#';
51-
name?: string;
52-
id?: string;
53-
img?: ImageResolvedAssetSource; //emoji图片
54-
emojiTag?: string; //[微笑] //emojitag
55-
emojiUri?: string;
56-
}
57-
export interface IOnTagsType {
58-
tag: string;
59-
keyWord: string;
60-
}
61-
interface IProps {
62-
onMention?: () => void;
34+
interface INativeProps {
6335
style?: StyleProp<TextStyle> | undefined;
6436
placeholder?: string;
6537
placeholderTextColor?: ColorValue;
@@ -85,14 +57,28 @@ interface IProps {
8557
onAndroidSubmitEditing?: (text: string) => void;
8658
submitBehavior?: 'submit';
8759
}
88-
export type IATTextViewBase = {
89-
focus: () => void;
90-
blur: () => void;
91-
insertEmoji: (img: IInserTextAttachmentItem) => void;
92-
insertMentions: (data: IInserTextAttachmentItem) => void;
93-
changeAttributedText: (data: IInserTextAttachmentItem[]) => void;
94-
dismissTag: () => void;
95-
};
60+
interface IProps {
61+
style?: StyleProp<TextStyle> | undefined;
62+
placeholder?: string;
63+
placeholderTextColor?: ColorValue;
64+
keyboardAppearance?: 'default' | 'light' | 'dark'; //ios only
65+
onChange?: (e: NativeSyntheticEvent<TextInputChangeEventData>) => void;
66+
onChangeText?: (text: string) => void;
67+
maxTextLength?: number;
68+
text?: string;
69+
blurOnSubmit?: boolean;
70+
onTextInput?: (event: IVTTextInputData) => void;
71+
onContentSizeChange?: (
72+
e: NativeSyntheticEvent<TextInputContentSizeChangeEventData>
73+
) => void;
74+
underlineColorAndroid?: ColorValue;
75+
keyboardType?: KeyboardTypeOptions | undefined;
76+
onSubmitEditing?: (text: string) => void;
77+
submitBehavior?: 'submit';
78+
emojiData?: IEmojiData[];
79+
mentions?: string[]; //'@','#'
80+
onMention?: (data: IonMentionData) => void;
81+
}
9682
export type IATTextViewRef = React.ForwardedRef<IATTextViewBase>;
9783

9884
const VariableTextInputView = forwardRef(
@@ -101,9 +87,45 @@ const VariableTextInputView = forwardRef(
10187
undefined
10288
);
10389
const nativeRef = useRef(null);
90+
const [mention, setMention] = useState<string>('');
91+
const [keyWord, setKeyWord] = useState<string>('');
92+
const [textValue, setTextValue] = useState<string>('');
10493
const _onChange = (e: NativeSyntheticEvent<TextInputChangeEventData>) => {
105-
props.onChangeText && props.onChangeText(e.nativeEvent.text);
106-
props.onChange && props.onChange(e);
94+
const text = e.nativeEvent.text;
95+
setTextValue(text);
96+
if (!!props.mentions && props.mentions.length > 0 && text.length > 0) {
97+
const lastStr = text.slice(-1);
98+
if (props.mentions.includes(lastStr)) {
99+
setMention(lastStr);
100+
props.onMention && props.onMention({ mention: lastStr, keyWord: '' });
101+
}
102+
if (!!mention) {
103+
const result = text.split(mention).pop();
104+
const mentionData: IonMentionData = {
105+
mention,
106+
keyWord: result || '',
107+
};
108+
setKeyWord(result || '');
109+
props.onMention && props.onMention(mentionData);
110+
}
111+
props.onChangeText && props.onChangeText(text);
112+
props.onChange && props.onChange(e);
113+
}
114+
};
115+
// useEffect(() => {
116+
// if (!!props.text) {
117+
// const attStrArr: IInserTextAttachmentItem[] = getAttributedTextArr(
118+
// props.text,
119+
// props.emojiData
120+
// );
121+
// changeAttributedText(attStrArr);
122+
// }
123+
// }, [props.text]);
124+
const clearMention = () => {
125+
if (!!mention) {
126+
setMention('');
127+
setKeyWord('');
128+
}
107129
};
108130
const focus = () => {
109131
if (Platform.OS === 'android') {
@@ -118,6 +140,7 @@ const VariableTextInputView = forwardRef(
118140
} else {
119141
VariableTextInputViewManager.blur();
120142
}
143+
clearMention();
121144
};
122145
const callNativeMethod = (methodName: string, data?: any) => {
123146
const reactTag = findNodeHandle(nativeRef.current);
@@ -186,28 +209,16 @@ const VariableTextInputView = forwardRef(
186209
setCurrentHeight(event.nativeEvent.contentSize.height);
187210
}
188211
};
189-
const onAndroidContentSizeChange = (event: any) => {
190-
const { style } = props;
191-
const styles = StyleSheet.flatten(style);
192-
if (styles.height === undefined) {
193-
const contentSizeHeight = event.nativeEvent.contentSize.height;
194-
if (!!styles.maxHeight && contentSizeHeight > styles.maxHeight) {
195-
setCurrentHeight(parseFloat(`${styles.maxHeight}`));
196-
return;
197-
}
198-
if (!!styles.minHeight && contentSizeHeight < styles.minHeight) {
199-
setCurrentHeight(parseFloat(`${styles.minHeight}`));
200-
return;
201-
}
202-
setCurrentHeight(event.nativeEvent.contentSize.height);
203-
}
204-
};
205-
const dismissTag = () => {
206-
if (Platform.OS === 'android') {
207-
callNativeMethod('dismissTag');
208-
} else {
209-
VariableTextInputViewManager.dismissTag();
210-
}
212+
const insertMentionAndDelateKeyword = (data: MentionData) => {
213+
const item: IInserTextAttachmentItem = {
214+
type: 2,
215+
...data,
216+
};
217+
const str = deletKeyBord(textValue, `${mention}${keyWord}`);
218+
const arr = getAttributedTextArr(str, props.emojiData);
219+
const newAttArr = [...arr, item];
220+
changeAttributedText(newAttArr);
221+
clearMention();
211222
};
212223
useImperativeHandle(ref, () => {
213224
return {
@@ -216,15 +227,9 @@ const VariableTextInputView = forwardRef(
216227
insertEmoji: insertEmoji,
217228
insertMentions: insertMentions,
218229
changeAttributedText: changeAttributedText,
219-
dismissTag: dismissTag,
230+
insertMentionAndDelateKeyword: insertMentionAndDelateKeyword,
220231
};
221232
});
222-
const onAndroidChange = (
223-
e: NativeSyntheticEvent<TextInputChangeEventData>
224-
) => {
225-
props.onChangeText && props.onChangeText(e.nativeEvent.text);
226-
props.onChange && props.onChange(e);
227-
};
228233
const onAndroidSubmitEditing = () => {};
229234
const onAndroidTextInput = (e: IVTTextInputData) => {
230235
props.onTextInput && props.onTextInput(e);
@@ -235,8 +240,8 @@ const VariableTextInputView = forwardRef(
235240
ref={nativeRef}
236241
onChange={_onChange}
237242
onContentSizeChange={onContentSizeChange}
238-
onAndroidChange={onAndroidChange}
239-
onAndroidContentSizeChange={onAndroidContentSizeChange}
243+
onAndroidChange={_onChange}
244+
onAndroidContentSizeChange={onContentSizeChange}
240245
{...props}
241246
onAndroidSubmitEditing={onAndroidSubmitEditing}
242247
onAndroidTextInput={onAndroidTextInput}
@@ -245,7 +250,7 @@ const VariableTextInputView = forwardRef(
245250
);
246251
}
247252
);
248-
const RNTVariableTextInputView = requireNativeComponent<IProps>(
253+
const RNTVariableTextInputView = requireNativeComponent<INativeProps>(
249254
'VariableTextInputView'
250255
);
251256
export { VariableTextInputView };

0 commit comments

Comments
 (0)