@unif/react-native-chat 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/bubble/Bubble.js +74 -72
- package/lib/commonjs/bubble/Bubble.js.map +1 -1
- package/lib/commonjs/card-wrapper/CardWrapper.js +25 -24
- package/lib/commonjs/card-wrapper/CardWrapper.js.map +1 -1
- package/lib/commonjs/conversations/Conversations.js +135 -84
- package/lib/commonjs/conversations/Conversations.js.map +1 -1
- package/lib/commonjs/index.js +0 -6
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/prompts/Prompts.js +38 -37
- package/lib/commonjs/prompts/Prompts.js.map +1 -1
- package/lib/commonjs/sender/Sender.js +120 -122
- package/lib/commonjs/sender/Sender.js.map +1 -1
- package/lib/commonjs/theme/tokens.js +2 -56
- package/lib/commonjs/theme/tokens.js.map +1 -1
- package/lib/commonjs/welcome/Welcome.js +89 -90
- package/lib/commonjs/welcome/Welcome.js.map +1 -1
- package/lib/module/bubble/Bubble.js +73 -71
- package/lib/module/bubble/Bubble.js.map +1 -1
- package/lib/module/card-wrapper/CardWrapper.js +24 -23
- package/lib/module/card-wrapper/CardWrapper.js.map +1 -1
- package/lib/module/conversations/Conversations.js +137 -86
- package/lib/module/conversations/Conversations.js.map +1 -1
- package/lib/module/index.js +3 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/prompts/Prompts.js +37 -36
- package/lib/module/prompts/Prompts.js.map +1 -1
- package/lib/module/sender/Sender.js +121 -123
- package/lib/module/sender/Sender.js.map +1 -1
- package/lib/module/theme/tokens.js +2 -55
- package/lib/module/theme/tokens.js.map +1 -1
- package/lib/module/welcome/Welcome.js +88 -89
- package/lib/module/welcome/Welcome.js.map +1 -1
- package/lib/typescript/commonjs/bubble/Bubble.d.ts.map +1 -1
- package/lib/typescript/commonjs/card-wrapper/CardWrapper.d.ts.map +1 -1
- package/lib/typescript/commonjs/conversations/Conversations.d.ts +4 -2
- package/lib/typescript/commonjs/conversations/Conversations.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +2 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/prompts/Prompts.d.ts.map +1 -1
- package/lib/typescript/commonjs/sender/Sender.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/tokens.d.ts +1 -45
- package/lib/typescript/commonjs/theme/tokens.d.ts.map +1 -1
- package/lib/typescript/commonjs/welcome/Welcome.d.ts.map +1 -1
- package/lib/typescript/module/bubble/Bubble.d.ts.map +1 -1
- package/lib/typescript/module/card-wrapper/CardWrapper.d.ts.map +1 -1
- package/lib/typescript/module/conversations/Conversations.d.ts +4 -2
- package/lib/typescript/module/conversations/Conversations.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +2 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/prompts/Prompts.d.ts.map +1 -1
- package/lib/typescript/module/sender/Sender.d.ts.map +1 -1
- package/lib/typescript/module/theme/tokens.d.ts +1 -45
- package/lib/typescript/module/theme/tokens.d.ts.map +1 -1
- package/lib/typescript/module/welcome/Welcome.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/bubble/Bubble.tsx +85 -80
- package/src/card-wrapper/CardWrapper.tsx +31 -29
- package/src/conversations/Conversations.tsx +170 -100
- package/src/index.tsx +4 -1
- package/src/prompts/Prompts.tsx +51 -41
- package/src/sender/Sender.tsx +125 -138
- package/src/theme/tokens.ts +1 -56
- package/src/welcome/Welcome.tsx +109 -108
package/src/prompts/Prompts.tsx
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* 水平 ScrollView + Chip 列表
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import React from 'react';
|
|
6
|
+
import React, { useMemo } from 'react';
|
|
7
7
|
import {
|
|
8
8
|
View,
|
|
9
9
|
Text,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
type ViewStyle,
|
|
14
14
|
type TextStyle,
|
|
15
15
|
} from 'react-native';
|
|
16
|
-
import {
|
|
16
|
+
import { useTokens } from '@unif/react-native-ui';
|
|
17
17
|
|
|
18
18
|
export interface PromptItem {
|
|
19
19
|
id: string;
|
|
@@ -45,24 +45,62 @@ const Prompts: React.FC<PromptsProps> = ({
|
|
|
45
45
|
styles: semanticStyles,
|
|
46
46
|
testID = 'prompts',
|
|
47
47
|
}) => {
|
|
48
|
+
const t = useTokens();
|
|
49
|
+
|
|
50
|
+
const ds = useMemo(
|
|
51
|
+
() =>
|
|
52
|
+
StyleSheet.create({
|
|
53
|
+
container: {
|
|
54
|
+
paddingVertical: 8,
|
|
55
|
+
backgroundColor: 'transparent',
|
|
56
|
+
},
|
|
57
|
+
scrollContent: {
|
|
58
|
+
paddingHorizontal: 12,
|
|
59
|
+
gap: 8,
|
|
60
|
+
},
|
|
61
|
+
wrapContainer: {
|
|
62
|
+
flexDirection: 'row',
|
|
63
|
+
flexWrap: 'wrap',
|
|
64
|
+
paddingHorizontal: 12,
|
|
65
|
+
gap: 8,
|
|
66
|
+
},
|
|
67
|
+
chip: {
|
|
68
|
+
backgroundColor: '#F3F4F6',
|
|
69
|
+
borderRadius: 16,
|
|
70
|
+
paddingHorizontal: 14,
|
|
71
|
+
paddingVertical: 8,
|
|
72
|
+
},
|
|
73
|
+
chipText: {
|
|
74
|
+
fontSize: 13,
|
|
75
|
+
color: t.colorText,
|
|
76
|
+
},
|
|
77
|
+
chipTextDisabled: {
|
|
78
|
+
color: t.colorTextPlaceholder,
|
|
79
|
+
},
|
|
80
|
+
}),
|
|
81
|
+
[t]
|
|
82
|
+
);
|
|
83
|
+
|
|
48
84
|
if (items.length === 0) return null;
|
|
49
85
|
|
|
50
86
|
const renderChips = () =>
|
|
51
87
|
items.map((item) => (
|
|
52
88
|
<TouchableOpacity
|
|
53
89
|
key={item.id}
|
|
54
|
-
style={[
|
|
90
|
+
style={[ds.chip, semanticStyles?.chip]}
|
|
55
91
|
onPress={() => !disabled && onSelect(item)}
|
|
56
92
|
activeOpacity={disabled ? 1 : 0.7}
|
|
57
93
|
disabled={disabled}
|
|
58
|
-
testID={`${testID}-${item.id}`}
|
|
94
|
+
testID={`${testID}-${item.id}`}
|
|
95
|
+
>
|
|
59
96
|
<Text
|
|
60
97
|
style={[
|
|
61
|
-
|
|
98
|
+
ds.chipText,
|
|
62
99
|
semanticStyles?.chipText,
|
|
63
|
-
disabled &&
|
|
100
|
+
disabled && ds.chipTextDisabled,
|
|
64
101
|
]}
|
|
65
|
-
numberOfLines={1}
|
|
102
|
+
numberOfLines={1}
|
|
103
|
+
>
|
|
66
104
|
{item.label}
|
|
67
105
|
</Text>
|
|
68
106
|
</TouchableOpacity>
|
|
@@ -71,53 +109,25 @@ const Prompts: React.FC<PromptsProps> = ({
|
|
|
71
109
|
if (wrap) {
|
|
72
110
|
return (
|
|
73
111
|
<View
|
|
74
|
-
style={[
|
|
75
|
-
testID={testID}
|
|
112
|
+
style={[ds.container, ds.wrapContainer, semanticStyles?.root, style]}
|
|
113
|
+
testID={testID}
|
|
114
|
+
>
|
|
76
115
|
{renderChips()}
|
|
77
116
|
</View>
|
|
78
117
|
);
|
|
79
118
|
}
|
|
80
119
|
|
|
81
120
|
return (
|
|
82
|
-
<View style={[
|
|
121
|
+
<View style={[ds.container, semanticStyles?.root, style]} testID={testID}>
|
|
83
122
|
<ScrollView
|
|
84
123
|
horizontal
|
|
85
124
|
showsHorizontalScrollIndicator={false}
|
|
86
|
-
contentContainerStyle={
|
|
125
|
+
contentContainerStyle={ds.scrollContent}
|
|
126
|
+
>
|
|
87
127
|
{renderChips()}
|
|
88
128
|
</ScrollView>
|
|
89
129
|
</View>
|
|
90
130
|
);
|
|
91
131
|
};
|
|
92
132
|
|
|
93
|
-
const defaultStyles = StyleSheet.create({
|
|
94
|
-
container: {
|
|
95
|
-
paddingVertical: 8,
|
|
96
|
-
backgroundColor: 'transparent',
|
|
97
|
-
},
|
|
98
|
-
scrollContent: {
|
|
99
|
-
paddingHorizontal: 12,
|
|
100
|
-
gap: 8,
|
|
101
|
-
},
|
|
102
|
-
wrapContainer: {
|
|
103
|
-
flexDirection: 'row',
|
|
104
|
-
flexWrap: 'wrap',
|
|
105
|
-
paddingHorizontal: 12,
|
|
106
|
-
gap: 8,
|
|
107
|
-
},
|
|
108
|
-
chip: {
|
|
109
|
-
backgroundColor: '#F3F4F6',
|
|
110
|
-
borderRadius: 16,
|
|
111
|
-
paddingHorizontal: 14,
|
|
112
|
-
paddingVertical: 8,
|
|
113
|
-
},
|
|
114
|
-
chipText: {
|
|
115
|
-
fontSize: 13,
|
|
116
|
-
color: chatTokens.colorText,
|
|
117
|
-
},
|
|
118
|
-
chipTextDisabled: {
|
|
119
|
-
color: chatTokens.colorTextPlaceholder,
|
|
120
|
-
},
|
|
121
|
-
});
|
|
122
|
-
|
|
123
133
|
export default React.memo(Prompts);
|
package/src/sender/Sender.tsx
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* 基于 UI 库 Input + ActionSheet + WaveAnimation + Button 构建
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import React, { useState, useCallback, useRef } from 'react';
|
|
8
|
+
import React, { useState, useCallback, useRef, useMemo } from 'react';
|
|
9
9
|
import {
|
|
10
10
|
View,
|
|
11
11
|
Text,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
PanResponder,
|
|
16
16
|
type ViewStyle,
|
|
17
17
|
} from 'react-native';
|
|
18
|
-
import {
|
|
18
|
+
import { useTokens } from '@unif/react-native-ui';
|
|
19
19
|
import Icon from 'react-native-vector-icons/Ionicons';
|
|
20
20
|
|
|
21
21
|
export interface ActionSheetOption {
|
|
@@ -47,7 +47,6 @@ export interface SenderProps {
|
|
|
47
47
|
style?: ViewStyle;
|
|
48
48
|
styles?: Partial<SenderSemanticStyles>;
|
|
49
49
|
testID?: string;
|
|
50
|
-
// 渲染注入点(允许使用方注入 UI 库组件)
|
|
51
50
|
renderInput?: (props: {
|
|
52
51
|
value: string;
|
|
53
52
|
onChangeText: (text: string) => void;
|
|
@@ -89,6 +88,7 @@ const Sender: React.FC<SenderProps> = ({
|
|
|
89
88
|
renderActionSheet,
|
|
90
89
|
renderWaveAnimation,
|
|
91
90
|
}) => {
|
|
91
|
+
const t = useTokens();
|
|
92
92
|
const [mode, setMode] = useState<'text' | 'voice'>('text');
|
|
93
93
|
const [expanded, setExpanded] = useState(false);
|
|
94
94
|
const [text, setText] = useState('');
|
|
@@ -155,25 +155,117 @@ const Sender: React.FC<SenderProps> = ({
|
|
|
155
155
|
|
|
156
156
|
const hasText = text.trim().length > 0;
|
|
157
157
|
|
|
158
|
+
const ds = useMemo(
|
|
159
|
+
() =>
|
|
160
|
+
StyleSheet.create({
|
|
161
|
+
container: {
|
|
162
|
+
paddingHorizontal: t.space,
|
|
163
|
+
paddingTop: t.spaceSm,
|
|
164
|
+
paddingBottom: Platform.OS === 'ios' ? t.spaceSm : t.space,
|
|
165
|
+
},
|
|
166
|
+
collapsedBar: {
|
|
167
|
+
flexDirection: 'row',
|
|
168
|
+
alignItems: 'center',
|
|
169
|
+
backgroundColor: t.colorBgElevated,
|
|
170
|
+
borderRadius: t.radiusXl,
|
|
171
|
+
height: 46,
|
|
172
|
+
paddingHorizontal: 12,
|
|
173
|
+
...t.shadow,
|
|
174
|
+
},
|
|
175
|
+
modeButton: {
|
|
176
|
+
padding: 4,
|
|
177
|
+
},
|
|
178
|
+
placeholderArea: {
|
|
179
|
+
flex: 1,
|
|
180
|
+
justifyContent: 'center',
|
|
181
|
+
paddingLeft: 8,
|
|
182
|
+
},
|
|
183
|
+
placeholderText: {
|
|
184
|
+
fontSize: t.fontSize,
|
|
185
|
+
color: t.colorTextPlaceholder,
|
|
186
|
+
},
|
|
187
|
+
voiceArea: {
|
|
188
|
+
flex: 1,
|
|
189
|
+
justifyContent: 'center',
|
|
190
|
+
alignItems: 'center',
|
|
191
|
+
},
|
|
192
|
+
voiceText: {
|
|
193
|
+
fontSize: t.fontSize,
|
|
194
|
+
fontWeight: '500',
|
|
195
|
+
color: t.colorText,
|
|
196
|
+
},
|
|
197
|
+
plusButton: {
|
|
198
|
+
padding: 4,
|
|
199
|
+
},
|
|
200
|
+
expandedFallback: {
|
|
201
|
+
backgroundColor: t.colorBgElevated,
|
|
202
|
+
borderRadius: 12,
|
|
203
|
+
padding: 12,
|
|
204
|
+
...t.shadow,
|
|
205
|
+
},
|
|
206
|
+
expandedInputArea: {
|
|
207
|
+
minHeight: 40,
|
|
208
|
+
maxHeight: 100,
|
|
209
|
+
},
|
|
210
|
+
expandedText: {
|
|
211
|
+
fontSize: t.fontSize,
|
|
212
|
+
color: t.colorText,
|
|
213
|
+
},
|
|
214
|
+
toolbar: {
|
|
215
|
+
flexDirection: 'row',
|
|
216
|
+
alignItems: 'center',
|
|
217
|
+
},
|
|
218
|
+
toolButton: {
|
|
219
|
+
padding: 4,
|
|
220
|
+
},
|
|
221
|
+
toolbarSpacer: {
|
|
222
|
+
flex: 1,
|
|
223
|
+
},
|
|
224
|
+
sendButton: {
|
|
225
|
+
marginLeft: 8,
|
|
226
|
+
},
|
|
227
|
+
sendCircle: {
|
|
228
|
+
width: 32,
|
|
229
|
+
height: 32,
|
|
230
|
+
borderRadius: 16,
|
|
231
|
+
backgroundColor: t.colorPrimary,
|
|
232
|
+
justifyContent: 'center',
|
|
233
|
+
alignItems: 'center',
|
|
234
|
+
},
|
|
235
|
+
recordingBar: {
|
|
236
|
+
flexDirection: 'row',
|
|
237
|
+
alignItems: 'center',
|
|
238
|
+
justifyContent: 'space-between',
|
|
239
|
+
height: 46,
|
|
240
|
+
borderRadius: t.radiusXl,
|
|
241
|
+
paddingHorizontal: 16,
|
|
242
|
+
},
|
|
243
|
+
recordingHint: {
|
|
244
|
+
fontSize: 14,
|
|
245
|
+
color: '#FFFFFF',
|
|
246
|
+
fontWeight: '500',
|
|
247
|
+
},
|
|
248
|
+
}),
|
|
249
|
+
[t]
|
|
250
|
+
);
|
|
251
|
+
|
|
158
252
|
// === 录音态 ===
|
|
159
253
|
if (isRecording && voiceEnabled) {
|
|
160
|
-
const bgColor = recordingCancelled
|
|
161
|
-
? chatTokens.colorError
|
|
162
|
-
: chatTokens.colorPrimary;
|
|
254
|
+
const bgColor = recordingCancelled ? t.colorError : t.colorPrimary;
|
|
163
255
|
const hintText = recordingCancelled ? '松手取消' : '松开发送,上划取消';
|
|
164
256
|
|
|
165
257
|
return (
|
|
166
|
-
<View style={[
|
|
258
|
+
<View style={[ds.container, semanticStyles?.root, style]}>
|
|
167
259
|
<View
|
|
168
260
|
style={[
|
|
169
|
-
|
|
261
|
+
ds.recordingBar,
|
|
170
262
|
{ backgroundColor: bgColor },
|
|
171
263
|
semanticStyles?.recording,
|
|
172
264
|
]}
|
|
173
265
|
{...panResponder.panHandlers}
|
|
174
266
|
testID={`${testID}-recording`}
|
|
175
267
|
>
|
|
176
|
-
<Text style={
|
|
268
|
+
<Text style={ds.recordingHint}>{hintText}</Text>
|
|
177
269
|
{renderWaveAnimation?.({ active: true, color: '#FFFFFF' })}
|
|
178
270
|
</View>
|
|
179
271
|
</View>
|
|
@@ -183,57 +275,52 @@ const Sender: React.FC<SenderProps> = ({
|
|
|
183
275
|
// === 展开态 ===
|
|
184
276
|
if (expanded && mode === 'text') {
|
|
185
277
|
const toolbar = (
|
|
186
|
-
<View style={[
|
|
278
|
+
<View style={[ds.toolbar, semanticStyles?.toolbar]}>
|
|
187
279
|
{voiceEnabled && (
|
|
188
280
|
<TouchableOpacity
|
|
189
281
|
onPress={handleToggleMode}
|
|
190
|
-
style={
|
|
282
|
+
style={ds.toolButton}
|
|
191
283
|
testID={`${testID}-toggle-mode`}
|
|
192
284
|
>
|
|
193
285
|
<Icon
|
|
194
286
|
name="search-outline"
|
|
195
287
|
size={22}
|
|
196
|
-
color={
|
|
288
|
+
color={t.colorTextSecondary}
|
|
197
289
|
/>
|
|
198
290
|
</TouchableOpacity>
|
|
199
291
|
)}
|
|
200
292
|
|
|
201
|
-
<View style={
|
|
293
|
+
<View style={ds.toolbarSpacer} />
|
|
202
294
|
|
|
203
295
|
{actionsEnabled && (
|
|
204
296
|
<TouchableOpacity
|
|
205
297
|
onPress={() => setShowActions(true)}
|
|
206
|
-
style={
|
|
298
|
+
style={ds.toolButton}
|
|
207
299
|
testID={`${testID}-actions`}
|
|
208
300
|
>
|
|
209
|
-
<Icon name="add" size={22} color={
|
|
301
|
+
<Icon name="add" size={22} color={t.colorTextSecondary} />
|
|
210
302
|
</TouchableOpacity>
|
|
211
303
|
)}
|
|
212
304
|
|
|
213
305
|
{isRequesting ? (
|
|
214
306
|
<TouchableOpacity
|
|
215
307
|
testID={`${testID}-stop`}
|
|
216
|
-
style={
|
|
308
|
+
style={ds.sendButton}
|
|
217
309
|
onPress={onStop}
|
|
218
310
|
activeOpacity={0.7}
|
|
219
311
|
>
|
|
220
|
-
<View
|
|
221
|
-
style={[
|
|
222
|
-
defaultStyles.sendCircle,
|
|
223
|
-
{ backgroundColor: chatTokens.colorError },
|
|
224
|
-
]}
|
|
225
|
-
>
|
|
312
|
+
<View style={[ds.sendCircle, { backgroundColor: t.colorError }]}>
|
|
226
313
|
<Icon name="stop" size={14} color="#FFFFFF" />
|
|
227
314
|
</View>
|
|
228
315
|
</TouchableOpacity>
|
|
229
316
|
) : hasText ? (
|
|
230
317
|
<TouchableOpacity
|
|
231
318
|
testID={`${testID}-send`}
|
|
232
|
-
style={
|
|
319
|
+
style={ds.sendButton}
|
|
233
320
|
onPress={handleSend}
|
|
234
321
|
activeOpacity={0.7}
|
|
235
322
|
>
|
|
236
|
-
<View style={
|
|
323
|
+
<View style={ds.sendCircle}>
|
|
237
324
|
<Icon name="arrow-up" size={18} color="#FFFFFF" />
|
|
238
325
|
</View>
|
|
239
326
|
</TouchableOpacity>
|
|
@@ -242,7 +329,7 @@ const Sender: React.FC<SenderProps> = ({
|
|
|
242
329
|
);
|
|
243
330
|
|
|
244
331
|
return (
|
|
245
|
-
<View style={[
|
|
332
|
+
<View style={[ds.container, semanticStyles?.root, style]}>
|
|
246
333
|
{renderInput ? (
|
|
247
334
|
renderInput({
|
|
248
335
|
value: text,
|
|
@@ -254,12 +341,9 @@ const Sender: React.FC<SenderProps> = ({
|
|
|
254
341
|
maxLength,
|
|
255
342
|
})
|
|
256
343
|
) : (
|
|
257
|
-
<View style={
|
|
258
|
-
{
|
|
259
|
-
|
|
260
|
-
<Text style={defaultStyles.expandedText}>
|
|
261
|
-
{text || placeholder}
|
|
262
|
-
</Text>
|
|
344
|
+
<View style={ds.expandedFallback}>
|
|
345
|
+
<View style={ds.expandedInputArea}>
|
|
346
|
+
<Text style={ds.expandedText}>{text || placeholder}</Text>
|
|
263
347
|
</View>
|
|
264
348
|
{toolbar}
|
|
265
349
|
</View>
|
|
@@ -278,25 +362,25 @@ const Sender: React.FC<SenderProps> = ({
|
|
|
278
362
|
const isVoice = mode === 'voice';
|
|
279
363
|
|
|
280
364
|
return (
|
|
281
|
-
<View style={[
|
|
282
|
-
<View style={[
|
|
365
|
+
<View style={[ds.container, semanticStyles?.root, style]}>
|
|
366
|
+
<View style={[ds.collapsedBar, semanticStyles?.collapsed]}>
|
|
283
367
|
{voiceEnabled && (
|
|
284
368
|
<TouchableOpacity
|
|
285
369
|
onPress={handleToggleMode}
|
|
286
|
-
style={
|
|
370
|
+
style={ds.modeButton}
|
|
287
371
|
testID={`${testID}-toggle-mode`}
|
|
288
372
|
>
|
|
289
373
|
<Icon
|
|
290
374
|
name={isVoice ? 'keypad-outline' : 'search-outline'}
|
|
291
375
|
size={22}
|
|
292
|
-
color={
|
|
376
|
+
color={t.colorTextSecondary}
|
|
293
377
|
/>
|
|
294
378
|
</TouchableOpacity>
|
|
295
379
|
)}
|
|
296
380
|
|
|
297
381
|
{isVoice ? (
|
|
298
382
|
<TouchableOpacity
|
|
299
|
-
style={
|
|
383
|
+
style={ds.voiceArea}
|
|
300
384
|
onPressIn={() => {
|
|
301
385
|
startY.current = 0;
|
|
302
386
|
setIsRecording(true);
|
|
@@ -309,26 +393,26 @@ const Sender: React.FC<SenderProps> = ({
|
|
|
309
393
|
activeOpacity={0.7}
|
|
310
394
|
testID={`${testID}-voice-hold`}
|
|
311
395
|
>
|
|
312
|
-
<Text style={
|
|
396
|
+
<Text style={ds.voiceText}>按住说话</Text>
|
|
313
397
|
</TouchableOpacity>
|
|
314
398
|
) : (
|
|
315
399
|
<TouchableOpacity
|
|
316
|
-
style={
|
|
400
|
+
style={ds.placeholderArea}
|
|
317
401
|
onPress={handleExpand}
|
|
318
402
|
activeOpacity={0.7}
|
|
319
403
|
testID={`${testID}-expand`}
|
|
320
404
|
>
|
|
321
|
-
<Text style={
|
|
405
|
+
<Text style={ds.placeholderText}>{placeholder}</Text>
|
|
322
406
|
</TouchableOpacity>
|
|
323
407
|
)}
|
|
324
408
|
|
|
325
409
|
{actionsEnabled && (
|
|
326
410
|
<TouchableOpacity
|
|
327
411
|
onPress={() => setShowActions(true)}
|
|
328
|
-
style={
|
|
412
|
+
style={ds.plusButton}
|
|
329
413
|
testID={`${testID}-plus`}
|
|
330
414
|
>
|
|
331
|
-
<Icon name="add" size={22} color={
|
|
415
|
+
<Icon name="add" size={22} color={t.colorTextSecondary} />
|
|
332
416
|
</TouchableOpacity>
|
|
333
417
|
)}
|
|
334
418
|
</View>
|
|
@@ -343,101 +427,4 @@ const Sender: React.FC<SenderProps> = ({
|
|
|
343
427
|
);
|
|
344
428
|
};
|
|
345
429
|
|
|
346
|
-
const defaultStyles = StyleSheet.create({
|
|
347
|
-
container: {
|
|
348
|
-
paddingHorizontal: chatTokens.space,
|
|
349
|
-
paddingTop: chatTokens.spaceSm,
|
|
350
|
-
paddingBottom:
|
|
351
|
-
Platform.OS === 'ios' ? chatTokens.spaceSm : chatTokens.space,
|
|
352
|
-
},
|
|
353
|
-
|
|
354
|
-
// 收起态
|
|
355
|
-
collapsedBar: {
|
|
356
|
-
flexDirection: 'row',
|
|
357
|
-
alignItems: 'center',
|
|
358
|
-
backgroundColor: chatTokens.colorBgElevated,
|
|
359
|
-
borderRadius: chatTokens.radiusXl,
|
|
360
|
-
height: 46,
|
|
361
|
-
paddingHorizontal: 12,
|
|
362
|
-
...chatTokens.shadow,
|
|
363
|
-
},
|
|
364
|
-
modeButton: {
|
|
365
|
-
padding: 4,
|
|
366
|
-
},
|
|
367
|
-
placeholderArea: {
|
|
368
|
-
flex: 1,
|
|
369
|
-
justifyContent: 'center',
|
|
370
|
-
paddingLeft: 8,
|
|
371
|
-
},
|
|
372
|
-
placeholderText: {
|
|
373
|
-
fontSize: chatTokens.fontSize,
|
|
374
|
-
color: chatTokens.colorTextPlaceholder,
|
|
375
|
-
},
|
|
376
|
-
voiceArea: {
|
|
377
|
-
flex: 1,
|
|
378
|
-
justifyContent: 'center',
|
|
379
|
-
alignItems: 'center',
|
|
380
|
-
},
|
|
381
|
-
voiceText: {
|
|
382
|
-
fontSize: chatTokens.fontSize,
|
|
383
|
-
fontWeight: '500',
|
|
384
|
-
color: chatTokens.colorText,
|
|
385
|
-
},
|
|
386
|
-
plusButton: {
|
|
387
|
-
padding: 4,
|
|
388
|
-
},
|
|
389
|
-
|
|
390
|
-
// 展开态
|
|
391
|
-
expandedFallback: {
|
|
392
|
-
backgroundColor: chatTokens.colorBgElevated,
|
|
393
|
-
borderRadius: chatTokens.radiusMd,
|
|
394
|
-
padding: 12,
|
|
395
|
-
...chatTokens.shadow,
|
|
396
|
-
},
|
|
397
|
-
expandedInputArea: {
|
|
398
|
-
minHeight: 40,
|
|
399
|
-
maxHeight: 100,
|
|
400
|
-
},
|
|
401
|
-
expandedText: {
|
|
402
|
-
fontSize: chatTokens.fontSize,
|
|
403
|
-
color: chatTokens.colorText,
|
|
404
|
-
},
|
|
405
|
-
toolbar: {
|
|
406
|
-
flexDirection: 'row',
|
|
407
|
-
alignItems: 'center',
|
|
408
|
-
},
|
|
409
|
-
toolButton: {
|
|
410
|
-
padding: 4,
|
|
411
|
-
},
|
|
412
|
-
toolbarSpacer: {
|
|
413
|
-
flex: 1,
|
|
414
|
-
},
|
|
415
|
-
sendButton: {
|
|
416
|
-
marginLeft: 8,
|
|
417
|
-
},
|
|
418
|
-
sendCircle: {
|
|
419
|
-
width: 32,
|
|
420
|
-
height: 32,
|
|
421
|
-
borderRadius: 16,
|
|
422
|
-
backgroundColor: chatTokens.colorPrimary,
|
|
423
|
-
justifyContent: 'center',
|
|
424
|
-
alignItems: 'center',
|
|
425
|
-
},
|
|
426
|
-
|
|
427
|
-
// 录音态
|
|
428
|
-
recordingBar: {
|
|
429
|
-
flexDirection: 'row',
|
|
430
|
-
alignItems: 'center',
|
|
431
|
-
justifyContent: 'space-between',
|
|
432
|
-
height: 46,
|
|
433
|
-
borderRadius: chatTokens.radiusXl,
|
|
434
|
-
paddingHorizontal: 16,
|
|
435
|
-
},
|
|
436
|
-
recordingHint: {
|
|
437
|
-
fontSize: 14,
|
|
438
|
-
color: '#FFFFFF',
|
|
439
|
-
fontWeight: '500',
|
|
440
|
-
},
|
|
441
|
-
});
|
|
442
|
-
|
|
443
430
|
export default React.memo(Sender);
|
package/src/theme/tokens.ts
CHANGED
|
@@ -1,18 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Chat 专属 Design Tokens
|
|
3
|
-
* 仅定义 Chat 特有的 token,通用 token 从 @unif/react-native-ui
|
|
3
|
+
* 仅定义 Chat 特有的 token,通用 token 从 @unif/react-native-ui 的 useTokens() 获取
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
interface ChatThemeConfig {
|
|
7
|
-
primaryColor?: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
let _chatConfig: ChatThemeConfig = {};
|
|
11
|
-
|
|
12
|
-
export function configure(options: ChatThemeConfig): void {
|
|
13
|
-
_chatConfig = { ..._chatConfig, ...options };
|
|
14
|
-
}
|
|
15
|
-
|
|
16
6
|
export const chatTokens = {
|
|
17
7
|
// 气泡
|
|
18
8
|
colorBgUserMsg: '#E8F0FE',
|
|
@@ -25,49 +15,4 @@ export const chatTokens = {
|
|
|
25
15
|
|
|
26
16
|
// 录音
|
|
27
17
|
colorRecording: '#FF3B30',
|
|
28
|
-
|
|
29
|
-
// 通用(colorPrimary 支持运行时注入)
|
|
30
|
-
get colorPrimary() {
|
|
31
|
-
return _chatConfig.primaryColor || '#1677FF';
|
|
32
|
-
},
|
|
33
|
-
colorError: '#FF3B30',
|
|
34
|
-
colorText: '#1F2937',
|
|
35
|
-
colorTextSecondary: '#6B7280',
|
|
36
|
-
colorTextPlaceholder: '#9CA3AF',
|
|
37
|
-
colorBgElevated: '#FFFFFF',
|
|
38
|
-
colorBorder: '#E5E7EB',
|
|
39
|
-
colorLink: '#1677FF',
|
|
40
|
-
|
|
41
|
-
// 间距
|
|
42
|
-
spaceXs: 4,
|
|
43
|
-
spaceSm: 8,
|
|
44
|
-
space: 12,
|
|
45
|
-
spaceMd: 16,
|
|
46
|
-
|
|
47
|
-
// 字体
|
|
48
|
-
fontSize: 15,
|
|
49
|
-
fontSizeSm: 13,
|
|
50
|
-
lineHeight: 22,
|
|
51
|
-
|
|
52
|
-
// 圆角
|
|
53
|
-
radiusSm: 6,
|
|
54
|
-
radiusMd: 12,
|
|
55
|
-
radiusXl: 23,
|
|
56
|
-
radiusFull: 999,
|
|
57
|
-
|
|
58
|
-
// 阴影
|
|
59
|
-
shadow: {
|
|
60
|
-
shadowColor: '#000',
|
|
61
|
-
shadowOffset: { width: 0, height: 1 },
|
|
62
|
-
shadowOpacity: 0.05,
|
|
63
|
-
shadowRadius: 3,
|
|
64
|
-
elevation: 2,
|
|
65
|
-
},
|
|
66
|
-
shadowSm: {
|
|
67
|
-
shadowColor: '#000',
|
|
68
|
-
shadowOffset: { width: 0, height: 1 },
|
|
69
|
-
shadowOpacity: 0.03,
|
|
70
|
-
shadowRadius: 2,
|
|
71
|
-
elevation: 1,
|
|
72
|
-
},
|
|
73
18
|
};
|