-
Notifications
You must be signed in to change notification settings - Fork 2.8k
/
MentionSuggestions.tsx
159 lines (142 loc) · 6.18 KB
/
MentionSuggestions.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import React, {useCallback} from 'react';
import type {MeasureInWindowOnSuccessCallback} from 'react-native';
import {View} from 'react-native';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import getStyledTextArray from '@libs/GetStyledTextArray';
import CONST from '@src/CONST';
import type {Icon} from '@src/types/onyx/OnyxCommon';
import AutoCompleteSuggestions from './AutoCompleteSuggestions';
import Avatar from './Avatar';
import Text from './Text';
type Mention = {
/**
* Main display text of the mention
* always visible right after icon (if present)
*/
text: string;
/**
* Additional text for the mention
* visible if it's value is different than Mention.text value
* rendered after Mention.text
*/
alternateText: string;
/**
* Handle of the mention
* used as a value for the mention (e.g. in for the filtering or putting the mention in the message)
*/
handle?: string;
/** Array of icons of the mention. If present, we use the first element of this array. For room suggestions, the icons are not used */
icons?: Icon[];
};
type MentionSuggestionsProps = {
/** The index of the highlighted mention */
highlightedMentionIndex?: number;
/** Array of suggested mentions */
mentions: Mention[];
/** Fired when the user selects a mention */
onSelect: (highlightedMentionIndex: number) => void;
/** Mention prefix that follows the @ sign */
prefix: string;
/** Show that we can use large mention picker.
* Depending on available space and whether the input is expanded, we can have a small or large mention suggester.
* When this value is false, the suggester will have a height of 2.5 items. When this value is true, the height can be up to 5 items. */
isMentionPickerLarge: boolean;
/** Measures the parent container's position and dimensions. */
measureParentContainer: (callback: MeasureInWindowOnSuccessCallback) => void;
};
/**
* Create unique keys for each mention item
*/
const keyExtractor = (item: Mention) => item.alternateText;
function MentionSuggestions({prefix, mentions, highlightedMentionIndex = 0, onSelect, isMentionPickerLarge, measureParentContainer = () => {}}: MentionSuggestionsProps) {
const theme = useTheme();
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
/**
* Render a suggestion menu item component.
*/
const renderSuggestionMenuItem = useCallback(
(item: Mention) => {
const isIcon = item.text === CONST.AUTO_COMPLETE_SUGGESTER.HERE_TEXT;
const styledDisplayName = getStyledTextArray(item.text, prefix);
const styledHandle = item.text === item.alternateText ? undefined : getStyledTextArray(item.alternateText, prefix);
return (
<View style={[styles.autoCompleteSuggestionContainer, styles.ph2]}>
{item.icons && !!item.icons.length && (
<View style={styles.mentionSuggestionsAvatarContainer}>
<Avatar
source={item.icons[0].source}
size={isIcon ? CONST.AVATAR_SIZE.MENTION_ICON : CONST.AVATAR_SIZE.SMALLER}
name={item.icons[0].name}
type={item.icons[0].type}
fill={isIcon ? theme.success : undefined}
fallbackIcon={item.icons[0].fallbackIcon}
/>
</View>
)}
<Text
style={[styles.mentionSuggestionsText, styles.flexShrink1]}
numberOfLines={1}
>
{styledDisplayName?.map(({text, isColored}, i) => (
<Text
// eslint-disable-next-line react/no-array-index-key
key={`${text}${i}`}
style={[StyleUtils.getColoredBackgroundStyle(isColored), styles.mentionSuggestionsDisplayName]}
>
{text}
</Text>
))}
</Text>
<Text
style={[styles.mentionSuggestionsText, styles.flex1]}
numberOfLines={1}
>
{styledHandle?.map(
({text, isColored}, i) =>
Boolean(text) && (
<Text
// eslint-disable-next-line react/no-array-index-key
key={`${text}${i}`}
style={[StyleUtils.getColoredBackgroundStyle(isColored), styles.textSupporting]}
>
{text}
</Text>
),
)}
</Text>
</View>
);
},
[
prefix,
styles.autoCompleteSuggestionContainer,
styles.ph2,
styles.mentionSuggestionsAvatarContainer,
styles.mentionSuggestionsText,
styles.flexShrink1,
styles.flex1,
styles.mentionSuggestionsDisplayName,
styles.textSupporting,
theme.success,
StyleUtils,
],
);
return (
<AutoCompleteSuggestions
suggestions={mentions}
renderSuggestionMenuItem={renderSuggestionMenuItem}
keyExtractor={keyExtractor}
highlightedSuggestionIndex={highlightedMentionIndex}
onSelect={onSelect}
isSuggestionPickerLarge={isMentionPickerLarge}
accessibilityLabelExtractor={keyExtractor}
measureParentContainer={measureParentContainer}
/>
);
}
MentionSuggestions.displayName = 'MentionSuggestions';
export default MentionSuggestions;
export type {Mention};