@douyinfe/semi-foundation 2.62.0 → 2.63.0-beta.0
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/chat/chat.scss +598 -0
- package/chat/chatBoxActionFoundation.ts +64 -0
- package/chat/constants.ts +68 -0
- package/chat/foundation.ts +306 -0
- package/chat/inputboxFoundation.ts +98 -0
- package/chat/rtl.scss +22 -0
- package/chat/variables.scss +125 -0
- package/input/textareaFoundation.ts +5 -0
- package/lib/cjs/chat/chat.css +484 -0
- package/lib/cjs/chat/chat.scss +598 -0
- package/lib/cjs/chat/chatBoxActionFoundation.d.ts +24 -0
- package/lib/cjs/chat/chatBoxActionFoundation.js +49 -0
- package/lib/cjs/chat/constants.d.ts +41 -0
- package/lib/cjs/chat/constants.js +56 -0
- package/lib/cjs/chat/foundation.d.ts +76 -0
- package/lib/cjs/chat/foundation.js +275 -0
- package/lib/cjs/chat/inputboxFoundation.d.ts +20 -0
- package/lib/cjs/chat/inputboxFoundation.js +118 -0
- package/lib/cjs/chat/rtl.scss +22 -0
- package/lib/cjs/chat/variables.scss +125 -0
- package/lib/cjs/input/textareaFoundation.js +7 -0
- package/lib/cjs/treeSelect/foundation.d.ts +3 -3
- package/lib/cjs/treeSelect/foundation.js +2 -6
- package/lib/cjs/upload/foundation.d.ts +1 -0
- package/lib/cjs/upload/foundation.js +3 -1
- package/lib/cjs/upload/upload.css +4 -0
- package/lib/cjs/upload/upload.scss +9 -0
- package/lib/es/chat/chat.css +484 -0
- package/lib/es/chat/chat.scss +598 -0
- package/lib/es/chat/chatBoxActionFoundation.d.ts +24 -0
- package/lib/es/chat/chatBoxActionFoundation.js +41 -0
- package/lib/es/chat/constants.d.ts +41 -0
- package/lib/es/chat/constants.js +51 -0
- package/lib/es/chat/foundation.d.ts +76 -0
- package/lib/es/chat/foundation.js +267 -0
- package/lib/es/chat/inputboxFoundation.d.ts +20 -0
- package/lib/es/chat/inputboxFoundation.js +110 -0
- package/lib/es/chat/rtl.scss +22 -0
- package/lib/es/chat/variables.scss +125 -0
- package/lib/es/input/textareaFoundation.js +7 -0
- package/lib/es/treeSelect/foundation.d.ts +3 -3
- package/lib/es/treeSelect/foundation.js +2 -6
- package/lib/es/upload/foundation.d.ts +1 -0
- package/lib/es/upload/foundation.js +3 -1
- package/lib/es/upload/upload.css +4 -0
- package/lib/es/upload/upload.scss +9 -0
- package/package.json +3 -3
- package/treeSelect/foundation.ts +3 -9
- package/upload/foundation.ts +4 -2
- package/upload/upload.scss +9 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BASE_CLASS_PREFIX
|
|
3
|
+
} from '../base/constants';
|
|
4
|
+
|
|
5
|
+
const cssClasses = {
|
|
6
|
+
PREFIX: `${BASE_CLASS_PREFIX}-chat`,
|
|
7
|
+
PREFIX_DIVIDER: `${BASE_CLASS_PREFIX}-chat-divider`,
|
|
8
|
+
PREFIX_CHAT_BOX: `${BASE_CLASS_PREFIX}-chat-chatBox`,
|
|
9
|
+
PREFIX_CHAT_BOX_ACTION: `${BASE_CLASS_PREFIX}-chat-chatBox-action`,
|
|
10
|
+
PREFIX_INPUT_BOX: `${BASE_CLASS_PREFIX}-chat-inputBox`,
|
|
11
|
+
PREFIX_ATTACHMENT: `${BASE_CLASS_PREFIX}-chat-attachment`,
|
|
12
|
+
PREFIX_HINT: `${BASE_CLASS_PREFIX}-chat-hint`,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const ROLE = {
|
|
16
|
+
USER: 'user',
|
|
17
|
+
ASSISTANT: 'assistant',
|
|
18
|
+
SYSTEM: 'system',
|
|
19
|
+
DIVIDER: 'divider',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const CHAT_ALIGN = {
|
|
23
|
+
LEFT_RIGHT: 'leftRight',
|
|
24
|
+
LEFT_ALIGN: 'leftAlign',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const MESSAGE_STATUS = {
|
|
28
|
+
LOADING: 'loading',
|
|
29
|
+
INCOMPLETE: 'incomplete',
|
|
30
|
+
COMPLETE: 'complete',
|
|
31
|
+
ERROR: 'error'
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const PIC_SUFFIX_ARRAY = ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp'];
|
|
35
|
+
|
|
36
|
+
const PIC_PREFIX = 'image/';
|
|
37
|
+
|
|
38
|
+
const SCROLL_ANIMATION_TIME = 300;
|
|
39
|
+
const SHOW_SCROLL_GAP = 100;
|
|
40
|
+
|
|
41
|
+
const MODE = {
|
|
42
|
+
BUBBLE: 'bubble',
|
|
43
|
+
NO_BUBBLE: 'noBubble',
|
|
44
|
+
USER_BUBBLE: 'userBubble'
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const SEND_HOT_KEY = {
|
|
48
|
+
ENTER: 'enter',
|
|
49
|
+
SHIFT_PLUS_ENTER: 'shift+enter'
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const strings = {
|
|
53
|
+
ROLE,
|
|
54
|
+
CHAT_ALIGN,
|
|
55
|
+
MESSAGE_STATUS,
|
|
56
|
+
PIC_SUFFIX_ARRAY,
|
|
57
|
+
PIC_PREFIX,
|
|
58
|
+
SCROLL_ANIMATION_TIME,
|
|
59
|
+
SHOW_SCROLL_GAP,
|
|
60
|
+
MODE,
|
|
61
|
+
SEND_HOT_KEY,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
export {
|
|
66
|
+
cssClasses,
|
|
67
|
+
strings,
|
|
68
|
+
};
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import BaseFoundation, { DefaultAdapter } from "../base/foundation";
|
|
2
|
+
import { strings } from "./constants";
|
|
3
|
+
import { Animation } from '@douyinfe/semi-animation';
|
|
4
|
+
import { debounce } from "lodash";
|
|
5
|
+
import { getUuidv4 } from "../utils/uuid";
|
|
6
|
+
import { handlePrevent } from "../utils/a11y";
|
|
7
|
+
|
|
8
|
+
const { PIC_PREFIX, PIC_SUFFIX_ARRAY, ROLE,
|
|
9
|
+
SCROLL_ANIMATION_TIME, SHOW_SCROLL_GAP
|
|
10
|
+
} = strings;
|
|
11
|
+
|
|
12
|
+
export interface Content {
|
|
13
|
+
type: 'text' | 'image_url' | 'file_url';
|
|
14
|
+
text?: string;
|
|
15
|
+
image_url?: {
|
|
16
|
+
url: string;
|
|
17
|
+
[x: string]: any
|
|
18
|
+
};
|
|
19
|
+
file_url?: {
|
|
20
|
+
url: string;
|
|
21
|
+
name: string;
|
|
22
|
+
size: string;
|
|
23
|
+
type: string;
|
|
24
|
+
[x: string]: any
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface Message {
|
|
29
|
+
role?: string;
|
|
30
|
+
name?: string;
|
|
31
|
+
id?: string;
|
|
32
|
+
content?: string | Content[];
|
|
33
|
+
parentId?: string;
|
|
34
|
+
createAt?: number;
|
|
35
|
+
status?: 'loading' | 'incomplete' | 'complete' | 'error';
|
|
36
|
+
[x: string]: any
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ChatAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
|
|
40
|
+
getContainerRef: () => React.RefObject<HTMLDivElement>;
|
|
41
|
+
setWheelScroll: (flag: boolean) => void;
|
|
42
|
+
notifyChatsChange: (chats: Message[]) => void;
|
|
43
|
+
notifyLikeMessage: (message: Message) => void;
|
|
44
|
+
notifyDislikeMessage: (message: Message) => void;
|
|
45
|
+
notifyCopyMessage: (message: Message) => void;
|
|
46
|
+
notifyClearContext: () => void;
|
|
47
|
+
notifyMessageSend: (content: string, attachment: any[]) => void;
|
|
48
|
+
notifyInputChange: (props: { inputValue: string; attachment: any[]}) => void;
|
|
49
|
+
setBackBottomVisible: (visible: boolean) => void;
|
|
50
|
+
registerWheelEvent: () => void;
|
|
51
|
+
unRegisterWheelEvent: () => void;
|
|
52
|
+
notifyStopGenerate: (e: any) => void;
|
|
53
|
+
notifyHintClick: (hint: string) => void;
|
|
54
|
+
setUploadAreaVisible: (visible: boolean) => void;
|
|
55
|
+
manualUpload: (e: any) => void;
|
|
56
|
+
getDropAreaElement: () => HTMLDivElement
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
export default class ChatFoundation <P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<ChatAdapter<P, S>, P, S> {
|
|
61
|
+
|
|
62
|
+
animation: any;
|
|
63
|
+
|
|
64
|
+
constructor(adapter: ChatAdapter<P, S>) {
|
|
65
|
+
super({ ...adapter });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
init = () => {
|
|
69
|
+
this.scrollToBottomImmediately();
|
|
70
|
+
this._adapter.registerWheelEvent();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
destroy = () => {
|
|
74
|
+
this.animation && this.animation.destroy();
|
|
75
|
+
this._adapter.unRegisterWheelEvent();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
stopGenerate = (e: any) => {
|
|
79
|
+
this._adapter.notifyStopGenerate(e);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
scrollToBottomImmediately = () => {
|
|
83
|
+
const containerRef = this._adapter.getContainerRef();
|
|
84
|
+
const element = containerRef?.current;
|
|
85
|
+
if (element) {
|
|
86
|
+
element.scrollTop = element.scrollHeight;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
scrollToBottomWithAnimation = () => {
|
|
91
|
+
const duration = SCROLL_ANIMATION_TIME;
|
|
92
|
+
const containerRef = this._adapter.getContainerRef();
|
|
93
|
+
const element = containerRef?.current;
|
|
94
|
+
if (!element) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const from = element.scrollTop;
|
|
98
|
+
const to = element.scrollHeight;
|
|
99
|
+
this.animation = new Animation(
|
|
100
|
+
{
|
|
101
|
+
from: { scrollTop: from },
|
|
102
|
+
to: { scrollTop: to },
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
duration,
|
|
106
|
+
easing: 'easeInOutCubic'
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
this.animation.on('frame', ({ scrollTop }: { scrollTop: number }) => {
|
|
111
|
+
element.scrollTop = scrollTop;
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
this.animation.start();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
containerScroll = (e: any) => {
|
|
118
|
+
if (e.target !== e.currentTarget) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
e.persist();
|
|
122
|
+
const update = () => {
|
|
123
|
+
this.getScroll(e.target);
|
|
124
|
+
};
|
|
125
|
+
requestAnimationFrame(update);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
getScroll = debounce((target: any) => {
|
|
129
|
+
const scrollHeight = target.scrollHeight;
|
|
130
|
+
const clientHeight = target.clientHeight;
|
|
131
|
+
const scrollTop = target.scrollTop;
|
|
132
|
+
const { backBottomVisible } = this.getStates();
|
|
133
|
+
if (scrollHeight - scrollTop - clientHeight <= SHOW_SCROLL_GAP) {
|
|
134
|
+
if (backBottomVisible) {
|
|
135
|
+
this._adapter.setBackBottomVisible(false);
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
if (!backBottomVisible) {
|
|
139
|
+
this._adapter.setBackBottomVisible(true);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return scroll;
|
|
143
|
+
}, 100)
|
|
144
|
+
|
|
145
|
+
clearContext = (e: any) => {
|
|
146
|
+
const { chats } = this.getStates();
|
|
147
|
+
if (chats[chats.length - 1].role === ROLE.DIVIDER) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const dividerMessage = {
|
|
151
|
+
role: ROLE.DIVIDER,
|
|
152
|
+
id: getUuidv4(),
|
|
153
|
+
createAt: Date.now(),
|
|
154
|
+
};
|
|
155
|
+
const newChats = [...chats, dividerMessage];
|
|
156
|
+
this._adapter.notifyChatsChange(newChats);
|
|
157
|
+
this._adapter.notifyClearContext();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
onMessageSend = (input: string, attachment: any[]) => {
|
|
161
|
+
let content;
|
|
162
|
+
if (Boolean(attachment) && attachment.length === 0) {
|
|
163
|
+
content = input;
|
|
164
|
+
} else {
|
|
165
|
+
content = [];
|
|
166
|
+
input && content.push({ type: 'text', text: input });
|
|
167
|
+
(attachment ?? []).map(item => {
|
|
168
|
+
const { fileInstance, name = '', url, size } = item;
|
|
169
|
+
const suffix = name.split('.').pop();
|
|
170
|
+
const isImg = fileInstance?.type?.startsWith(PIC_PREFIX) || PIC_SUFFIX_ARRAY.includes(suffix);
|
|
171
|
+
if (isImg) {
|
|
172
|
+
content.push({
|
|
173
|
+
type: 'image_url',
|
|
174
|
+
image_url: { url: url }
|
|
175
|
+
});
|
|
176
|
+
} else {
|
|
177
|
+
content.push({
|
|
178
|
+
type: 'file_url',
|
|
179
|
+
file_url: {
|
|
180
|
+
url: url,
|
|
181
|
+
name: name,
|
|
182
|
+
size: size,
|
|
183
|
+
type: fileInstance?.type
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
if (content) {
|
|
190
|
+
const newMessage = {
|
|
191
|
+
role: ROLE.USER,
|
|
192
|
+
id: getUuidv4(),
|
|
193
|
+
createAt: Date.now(),
|
|
194
|
+
content,
|
|
195
|
+
};
|
|
196
|
+
this._adapter.notifyChatsChange([...this.getStates().chats, newMessage]);
|
|
197
|
+
}
|
|
198
|
+
this._adapter.setWheelScroll(false);
|
|
199
|
+
this._adapter.registerWheelEvent();
|
|
200
|
+
this._adapter.notifyMessageSend(input, attachment);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
onHintClick = (hint: string) => {
|
|
204
|
+
const { chats } = this.getStates();
|
|
205
|
+
const newMessage = {
|
|
206
|
+
role: ROLE.USER,
|
|
207
|
+
id: getUuidv4(),
|
|
208
|
+
createAt: Date.now(),
|
|
209
|
+
content: hint,
|
|
210
|
+
};
|
|
211
|
+
const newChats = [...chats, newMessage];
|
|
212
|
+
this._adapter.notifyChatsChange(newChats);
|
|
213
|
+
this._adapter.notifyHintClick(hint);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
onInputChange = (props: { inputValue: string; attachment: any[]}) => {
|
|
217
|
+
this._adapter.notifyInputChange(props as any);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
deleteMessage = (message: Message) => {
|
|
221
|
+
const { onMessageDelete, onChatsChange } = this.getProps();
|
|
222
|
+
const { chats } = this.getStates();
|
|
223
|
+
onMessageDelete?.(message);
|
|
224
|
+
const newChats = chats.filter(item => item.id !== message.id);
|
|
225
|
+
onChatsChange?.(newChats);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
likeMessage = (message: Message) => {
|
|
229
|
+
const { chats } = this.getStates();
|
|
230
|
+
this._adapter.notifyLikeMessage(message);
|
|
231
|
+
const index = chats.findIndex(item => item.id === message.id);
|
|
232
|
+
const newChat = {
|
|
233
|
+
...chats[index],
|
|
234
|
+
like: !chats[index].like,
|
|
235
|
+
dislike: false,
|
|
236
|
+
};
|
|
237
|
+
const newChats = [...chats];
|
|
238
|
+
newChats.splice(index, 1, newChat);
|
|
239
|
+
this._adapter.notifyChatsChange(newChats);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
dislikeMessage = (message: Message) => {
|
|
243
|
+
const { chats } = this.getStates();
|
|
244
|
+
this._adapter.notifyDislikeMessage(message);
|
|
245
|
+
const index = chats.findIndex(item => item.id === message.id);
|
|
246
|
+
const newChat = {
|
|
247
|
+
...chats[index],
|
|
248
|
+
like: false,
|
|
249
|
+
dislike: !chats[index].dislike,
|
|
250
|
+
};
|
|
251
|
+
const newChats = [...chats];
|
|
252
|
+
newChats.splice(index, 1, newChat);
|
|
253
|
+
this._adapter.notifyChatsChange(newChats);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
resetMessage = (message: Message) => {
|
|
257
|
+
const { chats } = this.getStates();
|
|
258
|
+
const lastMessage = chats[chats.length - 1];
|
|
259
|
+
const newLastChat = {
|
|
260
|
+
...lastMessage,
|
|
261
|
+
status: 'loading',
|
|
262
|
+
content: '',
|
|
263
|
+
id: getUuidv4(),
|
|
264
|
+
createAt: Date.now(),
|
|
265
|
+
};
|
|
266
|
+
const newChats = chats.slice(0, -1).concat(newLastChat);
|
|
267
|
+
this._adapter.notifyChatsChange(newChats);
|
|
268
|
+
const { onMessageReset } = this.getProps();
|
|
269
|
+
onMessageReset?.(message);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
handleDragOver = (e: any) => {
|
|
273
|
+
this._adapter.setUploadAreaVisible(true);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
handleContainerDragOver = (e: any) => {
|
|
277
|
+
handlePrevent(e);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
handleContainerDrop = (e) => {
|
|
281
|
+
this._adapter.setUploadAreaVisible(false);
|
|
282
|
+
this._adapter.manualUpload(e?.dataTransfer?.files);
|
|
283
|
+
// 禁用默认实现,防止文件被打开
|
|
284
|
+
//Disable the default implementation, preventing files from being opened
|
|
285
|
+
handlePrevent(e);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
handleContainerDragLeave = (e: any) => {
|
|
289
|
+
handlePrevent(e);
|
|
290
|
+
// 鼠标移动至 container 的子元素,则不做任何操作
|
|
291
|
+
// If the mouse moves to the child element of container, no operation will be performed.
|
|
292
|
+
const dropAreaElement = this._adapter.getDropAreaElement();
|
|
293
|
+
if (dropAreaElement !== e.target && dropAreaElement.contains(e.target)) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* 延迟隐藏 container ,防止父元素的 mouseOver 被触发,导致 container 无法隐藏
|
|
298
|
+
* Delay hiding of the container to prevent the parent element's mouseOver from being triggered,
|
|
299
|
+
* causing the container to be unable to be hidden.
|
|
300
|
+
*/
|
|
301
|
+
setTimeout(() => {
|
|
302
|
+
this._adapter.setUploadAreaVisible(false);
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { handlePrevent } from "../utils/a11y";
|
|
2
|
+
import BaseFoundation, { DefaultAdapter } from "../base/foundation";
|
|
3
|
+
import { strings } from './constants';
|
|
4
|
+
|
|
5
|
+
const { SEND_HOT_KEY } = strings;
|
|
6
|
+
|
|
7
|
+
export interface InputBoxAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
|
|
8
|
+
notifyInputChange: (props: { inputValue: string; attachment: any[]}) => void;
|
|
9
|
+
setInputValue: (value: string) => void;
|
|
10
|
+
setAttachment: (attachment: any[]) => void;
|
|
11
|
+
notifySend: (content: string, attachment: any[]) => void
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default class InputBoxFoundation <P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<InputBoxAdapter<P, S>, P, S> {
|
|
15
|
+
constructor(adapter: InputBoxAdapter<P, S>) {
|
|
16
|
+
super({ ...adapter });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
onInputAreaChange = (value: string) => {
|
|
20
|
+
const attachment = this.getState('attachment');
|
|
21
|
+
this._adapter.setInputValue(value);
|
|
22
|
+
this._adapter.notifyInputChange({ inputValue: value, attachment });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
onAttachmentAdd = (props: any) => {
|
|
26
|
+
const { fileList } = props;
|
|
27
|
+
const { uploadProps } = this.getProps();
|
|
28
|
+
const { onChange } = uploadProps;
|
|
29
|
+
if (onChange) {
|
|
30
|
+
onChange(props);
|
|
31
|
+
}
|
|
32
|
+
const { content } = this.getStates();
|
|
33
|
+
let newFileList = [...fileList];
|
|
34
|
+
this._adapter.setAttachment(newFileList);
|
|
35
|
+
this._adapter.notifyInputChange({
|
|
36
|
+
inputValue: content,
|
|
37
|
+
attachment: newFileList
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
onAttachmentDelete = (props: any) => {
|
|
42
|
+
const { content, attachment } = this.getStates();
|
|
43
|
+
const newAttachMent = attachment.filter(item => item.uid !== props.uid);
|
|
44
|
+
this._adapter.setAttachment(newAttachMent);
|
|
45
|
+
this._adapter.notifyInputChange({
|
|
46
|
+
inputValue: content,
|
|
47
|
+
attachment: newAttachMent
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
onSend = (e: any) => {
|
|
52
|
+
if (this.getDisableSend()) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const { content, attachment } = this.getStates();
|
|
56
|
+
this._adapter.setInputValue('');
|
|
57
|
+
this._adapter.setAttachment([]);
|
|
58
|
+
this._adapter.notifySend(content, attachment);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
getDisableSend = () => {
|
|
62
|
+
const { content, attachment } = this.getStates();
|
|
63
|
+
const { disableSend: disableSendInProps } = this.getProps();
|
|
64
|
+
const disabledSend = disableSendInProps || (content.length === 0 && attachment.length === 0);
|
|
65
|
+
return disabledSend;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
onEnterPress = (e: any) => {
|
|
69
|
+
const { sendHotKey } = this.getProps();
|
|
70
|
+
if (sendHotKey === SEND_HOT_KEY.SHIFT_PLUS_ENTER && e.shiftKey === false) {
|
|
71
|
+
return ;
|
|
72
|
+
} else if (sendHotKey === SEND_HOT_KEY.ENTER && e.shiftKey === true) {
|
|
73
|
+
return ;
|
|
74
|
+
}
|
|
75
|
+
handlePrevent(e);
|
|
76
|
+
this.onSend(e);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
onPaste = (e: any) => {
|
|
80
|
+
const items = e.clipboardData?.items;
|
|
81
|
+
const { manualUpload } = this.getProps();
|
|
82
|
+
let files = [];
|
|
83
|
+
if (items) {
|
|
84
|
+
for (const it of items) {
|
|
85
|
+
const file = it.getAsFile();
|
|
86
|
+
file && files.push(it.getAsFile());
|
|
87
|
+
}
|
|
88
|
+
if (files.length) {
|
|
89
|
+
// 文件上传,则需要阻止默认粘贴行为
|
|
90
|
+
// File upload, you need to prevent the default paste behavior
|
|
91
|
+
manualUpload(files);
|
|
92
|
+
e.preventDefault();
|
|
93
|
+
e.stopPropagation();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
}
|
package/chat/rtl.scss
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
$module: #{$prefix}-chat;
|
|
3
|
+
|
|
4
|
+
.#{$prefix}-rtl,
|
|
5
|
+
.#{$prefix}-portal-rtl {
|
|
6
|
+
.#{$module} {
|
|
7
|
+
direction: rtl;
|
|
8
|
+
|
|
9
|
+
&-hint-icon {
|
|
10
|
+
transform: scaleX(-1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
&-inputBox-sendButton-icon {
|
|
14
|
+
transform: rotate(225deg);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
&-chatBox-action-icon-redo {
|
|
18
|
+
transform: scaleX(-1);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// radius
|
|
2
|
+
$radius-chat_chatBox_content: var(--semi-border-radius-large); // 聊天框内容圆角
|
|
3
|
+
$radius-chat_inputBox_container: 16px; // 输入框容器圆角
|
|
4
|
+
$radius-chat_attachment_img: var(--semi-border-radius-medium); // 附件图片圆角
|
|
5
|
+
$radius-chat_attachment_file: var(--semi-border-radius-medium); // 附件文件圆角
|
|
6
|
+
$radius-chat_hint_item: var(--semi-border-radius-large); // 提示条圆角
|
|
7
|
+
$radius-chat_chatBox_content_code: var(--semi-border-radius-large); // 代码块圆角
|
|
8
|
+
$radius-chat_chatBox_content_code_topSlot_copy: var(--semi-border-radius-large); // 代码块顶部复制按钮圆角
|
|
9
|
+
$radius-chat_dropArea: 16px; // 拖拽上传区域圆角
|
|
10
|
+
|
|
11
|
+
//color
|
|
12
|
+
$color-chat_action_content-bg: var(--semi-color-bg-0); // 返回按钮/停止生成内容按钮背景颜色
|
|
13
|
+
$color-chat_action_content-border: var(--semi-color-border); // 返回按钮/停止生成按钮描边颜色
|
|
14
|
+
$color-chat_divider: var(--semi-color-text-2); // 分割线颜色
|
|
15
|
+
$color-chat_chatBox_title: var(--semi-color-text-0); //聊天框标题颜色
|
|
16
|
+
$color-chat_chatBox_action_icon: var(--semi-color-text-2); // 聊天框操作区域按钮图标颜色
|
|
17
|
+
$color-chat_chatBox_action_icon-hover: var(--semi-color-text-0); // 聊天框操作区域按钮图标hover颜色
|
|
18
|
+
$color-chat_chatBox_action-bg-hover: transparent; // 聊天框操作区域按钮hover背景颜色
|
|
19
|
+
$color-chat_chatBox_content_text: var(--semi-color-text-0); // 聊天框内容文字颜色
|
|
20
|
+
$color-chat_chatBox_content_bg: var(--semi-color-fill-0); // 聊天框内容背景颜色
|
|
21
|
+
$color-chat_chatBox_content_user-bg: var(--semi-color-primary); // 聊天框内容用户背景颜色
|
|
22
|
+
$color-chat_chatBox_content_user-text: var(--semi-color-white); // 聊天框内容用户文字颜色
|
|
23
|
+
$color-chat_chatBox_content_error-bg: var(--semi-color-danger-hover); // 聊天框内容错误背景颜色
|
|
24
|
+
$color-chat_chatBox_content_error-text: var(--semi-color-white); // 聊天框内容错误文字颜色
|
|
25
|
+
$color-chat_inputBottom_clearButton_icon: var(--semi-color-text-2); //清空按钮图标颜色
|
|
26
|
+
$color-chat_inputBottom_uploadButton_icon: var(--semi-color-text-0); // 上传按钮图标颜色
|
|
27
|
+
$color-chat_inputBottom_sendButton_icon-disable: var(--semi-color-primary-disabled); // 发送按钮禁用态图标颜色
|
|
28
|
+
$color-chat_inputBox_container-border: var(--semi-color-border); // 输入框容器边框颜色
|
|
29
|
+
$color-chat_attachment_clear_icon: var(--semi-color-text-2); // 附件清除图标颜色
|
|
30
|
+
$color-chat_attachment_file-bg: var(--semi-color-fill-0); // 附件文件背景颜色
|
|
31
|
+
$color-chat_chatBox_user_attachment_file-bg: var(--semi-color-bg-0); // 用户聊天框附件文件背景颜色
|
|
32
|
+
$color-chat_chatBox_other_attachment_file-bg: var(--semi-color-fill-2); // 聊天框附件文件背景颜色
|
|
33
|
+
$color-chat_attachment_file_icon: var(--semi-color-text-2); // 附件文件图标颜色
|
|
34
|
+
$color-chat_attachment_file_title: var(--semi-color-text-0); // 附件文件标题颜色
|
|
35
|
+
$color-chat_attachment_file_metadata_text: var(--semi-color-text-2); // 附件文件元数据文字颜色
|
|
36
|
+
$color-chat_hint_item-border: var(--semi-color-border); // 提示条边框颜色
|
|
37
|
+
$color-chat_hint_item-bg: transparent; // 提示条背景颜色
|
|
38
|
+
$color-chat_hint_item-bg-hover: var(--semi-color-fill-0); // 提示条hover背景颜色
|
|
39
|
+
$color-chat_hint_content_text: var(--semi-color-text-1); // 提示条文字颜色
|
|
40
|
+
$color-chat_hint_icon: var(--semi-color-text-2); // 提示条图标颜色
|
|
41
|
+
$color-chat_chatBox_loading-bg: var(--semi-color-text-0); // 聊天内容加载图标圆圈颜色
|
|
42
|
+
$color-chat_chatBox_code_topSlot: rgba(var(--semi-white), 1); // 代码块顶部字体颜色
|
|
43
|
+
$color-chat_chatBox_code_topSlot-bg: rgba(var(--semi-grey-4), 1); //代码块顶部背景色
|
|
44
|
+
$color-chat_chatBox_code_topSlot_toCopy-bg-hover: rgba(var(--semi-grey-5), 1); // 代码块顶部复制按钮hover背景色
|
|
45
|
+
$color-chat_chatBox_code_content: var(--semi-color-bg-0); // 代码块内容背景色
|
|
46
|
+
$color-chat_action_content-bg-hover: var(--semi-color-tertiary-light-hover); // 返回按钮/停止生成按钮hover背景颜色
|
|
47
|
+
$color-chat_dropArea-bg: rgba(var(--semi-grey-2), 0.9); // 拖拽区域文字颜色
|
|
48
|
+
$color-chat_dropArea-border: var(--semi-color-border); // 拖拽区域边框颜色
|
|
49
|
+
|
|
50
|
+
// spacing
|
|
51
|
+
$spacing-chat_paddingY: 12px; // chat组件上下内边距
|
|
52
|
+
$spacing-chat_container-paddingX: 16px; // 消息框水平内边距
|
|
53
|
+
$spacing-chat_action_content-bottom: 0; // 返回按钮/停止生成按钮底部边距
|
|
54
|
+
$spacing-chat_chatBox-marginY: 8px; // 聊天框上下外边距
|
|
55
|
+
$spacing-chat_chatBox-columnGap: 12px; // 聊天框内容列间距
|
|
56
|
+
$spacing-chat_chatBox_action-columnGap: 10px; // 聊天框操作区域按钮列间距
|
|
57
|
+
$spacing-chat_chatBox_action-marginX: 10px; // 聊天框操作区域左右外边距
|
|
58
|
+
$spacing-chat_chatBox_action_btn-padding: 0; // 聊天框操作区域按钮内边距
|
|
59
|
+
$spacing-chat_chatBox_content-paddingY: 8px; // 聊天框内容上下内边距
|
|
60
|
+
$spacing-chat_chatBox_content-paddingX: 12px; // 聊天框内容左右内边距
|
|
61
|
+
$spacing-chat_inputBox-paddingTop: 8px; // 输入框顶部内边距
|
|
62
|
+
$spacing-chat_inputBox-paddingBottom: 8px; // 输入框底部内边距
|
|
63
|
+
$spacing-chat_inputBox-paddingX: 16px; // 输入框左右内边距
|
|
64
|
+
$spacing-chat_inputBox_container-padding: 11px; // 输入框容器内边距
|
|
65
|
+
$spacing-chat_inputBox_inner-columnGap: 4px; // 输入框容器列间距
|
|
66
|
+
// $spacing-chat_inputBox_textarea-marginX: 5px; // 输入框textArea左右内边距
|
|
67
|
+
$spacing-chat_inputBox-marginY: 4px;
|
|
68
|
+
$spacing-chat_attachment-columnGap: 10px; // 附件列间距
|
|
69
|
+
$spacing-chat_attachment-RowGap: 5px; // 附件行间距
|
|
70
|
+
$spacing-chat_attachment_clear-top: 8px; // 附件清除图标顶部间距
|
|
71
|
+
$spacing-chat_attachment_clear-right: 8px; // 附件清除图标右内边距
|
|
72
|
+
$spacing-chat_attachment_file-columnGap: 5px; // 文件附件列间距
|
|
73
|
+
$spacing-chat_attachment_file-padding: 5px; // 文件附件内边距
|
|
74
|
+
$spacing-chat_chatBox_loading_item-gap: 15px; // 聊天内容加载图标间距
|
|
75
|
+
$spacing-chat_divider-marginY: 12px; // 分割线上下外边距
|
|
76
|
+
$spacing-chat_chatBox_content_attachment-marginY: 4px; // 聊天框内容文件/图片上下外间距
|
|
77
|
+
$spacing-chat_chatBox_content_code_topSlot-paddingX: 5px; // 聊天框代码块顶部上下内边距
|
|
78
|
+
$spacing-chat_chatBox_content_code_topSlot-paddingY: 8px; // 聊天框代码块顶部左右内边距
|
|
79
|
+
$spacing-chat_chatBox_content_code_topSlot_copy-columnGap: 5px; // 聊天框代码块顶部复制按钮列间距:
|
|
80
|
+
$spacing-chat_chatBox_content_code_topSlot_copy-padding: 5px; // 聊天框代码块顶部复制按钮列间距:
|
|
81
|
+
$spacing-chat_chatBox_wrap: 8px; // 聊天框外层间距
|
|
82
|
+
$spacing-chat_hint-rowGap: 10px; // 提示条行间距
|
|
83
|
+
$spacing-chat_hint-marginY: 12px; // 提示条容器上下外边距
|
|
84
|
+
$spacing-chat_hint-marginLeft: 34px; // 提示条容器左外边距
|
|
85
|
+
$spacing-chat_hint_item-marginY: 8px; // 提示条上下外边距
|
|
86
|
+
$spacing-chat_hint_item-marginX: 12px; // 提示条左右外边距
|
|
87
|
+
$spacing-chat_hint_item-columnGap: 20px; // 提示条内容列间距
|
|
88
|
+
$spacing-chat_chatBox_loading-item-marginX: 18px; // 聊天内容加载图标中心圆圈左右外边距
|
|
89
|
+
$spacing-chat_chatBox_loading-item-marginY: 6px; // 聊天内容加载图标中心圆圈上下外边距
|
|
90
|
+
|
|
91
|
+
// width
|
|
92
|
+
$width-chat_backBottom_wrapper: 42px; // 返回按钮宽度
|
|
93
|
+
$width-chat_action_content-border: 1px; // 返回按钮/停止生成按钮描边宽度
|
|
94
|
+
$width-chat_inputBottom_clearButton: 48px; // 清空按钮宽度
|
|
95
|
+
$width-chat_inputBottom_uploadButton: 32px; // 上传按钮宽度
|
|
96
|
+
$width-chat_inputBottom_sendButton: 32px; // 发送按钮宽度
|
|
97
|
+
$width-chat_inputBox_container-border: 1px; // 输入框容器边框宽度
|
|
98
|
+
$width-chat_attachment_file: 50px; // 附件文件宽度
|
|
99
|
+
$width-chat_hint_item-border: 1px; // 提示条边框宽度
|
|
100
|
+
$width-chat_chatBox_loading: 8px; // 加载中单个圆圈图标宽度
|
|
101
|
+
$width-chat_attachment_file_title: 90px; // 附件文件标题最大宽度
|
|
102
|
+
$width-chat_max: 800px; // chat组件最大宽度
|
|
103
|
+
$width-chat_dropArea-border: 5px; // 拖拽上传边框宽度
|
|
104
|
+
$width-chat_chatBox_content_code_topSlot_copy: 150px; // 聊天框代码块顶部复制按钮最小宽度
|
|
105
|
+
// height
|
|
106
|
+
$height-chat_action_stop: 42px; //停止生成按钮高度
|
|
107
|
+
|
|
108
|
+
//font
|
|
109
|
+
$font-chat_divider-fontWeight: $font-weight-regular; // 分割线字重
|
|
110
|
+
$font-chat_divider-fontSize: $font-size-small; // 分割线字体大小
|
|
111
|
+
$font-chat_chatBox_title-lineHeight: 20px; //聊天框标题行高
|
|
112
|
+
$font-chat_chatBox_title-fontSize: $font-size-header-6; // 聊天框标题字体大小
|
|
113
|
+
$font-chat_chatBox_title-fontWeight: $font-weight-regular; // 聊天框标题字重
|
|
114
|
+
$font-chat_inputBottom_clearButton_icon-fontSize: 30px; // 输入区清空上下文按钮图标大小
|
|
115
|
+
$font-chat_attachment_file_title-fontSize: $font-size-header-6; // 附件文件标题字体大小
|
|
116
|
+
$font-chat_attachment_file_metadata-fontSize: $font-size-regular; // 附件文件元数据字体大小
|
|
117
|
+
$font-chat_hint_content-fontSize: $font-size-regular; // 提示条文字大小
|
|
118
|
+
// $font-chat_hint_icon: 20px; // 提示条图标大小
|
|
119
|
+
$font-chat_chatBox_code_topSlot: 12px; // 代码块顶部字体大小
|
|
120
|
+
$font-chat_chatBox_code_topSlot-lineHeight: 16px; //代码块顶部区域字体行高
|
|
121
|
+
$font-chat_dropArea_text: 48px; // 拖拽上传区域文字大小
|
|
122
|
+
|
|
123
|
+
//z-index
|
|
124
|
+
$z-chat_dropArea: 10; // 拖拽上传区域z-index
|
|
125
|
+
$z-chat_action: 1; // 返回按钮/停止生成按钮z-index
|
|
@@ -171,6 +171,11 @@ export default class TextAreaFoundation extends BaseFoundation<TextAreaAdapter>
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
handleKeyDown(e: any) {
|
|
174
|
+
const { disabledEnterStartNewLine } = this.getProps();
|
|
175
|
+
if (disabledEnterStartNewLine && e.key === 'Enter' && !e.shiftKey) {
|
|
176
|
+
// Prevent default line wrapping behavior
|
|
177
|
+
e.preventDefault();
|
|
178
|
+
}
|
|
174
179
|
this._adapter.notifyKeyDown(e);
|
|
175
180
|
if (e.keyCode === 13) {
|
|
176
181
|
this._adapter.notifyPressEnter(e);
|