funda-ui 4.6.111 → 4.6.222
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/Chatbox/index.css +95 -14
- package/Chatbox/index.d.ts +7 -0
- package/Chatbox/index.js +406 -279
- package/Date/index.js +53 -47
- package/Select/index.d.ts +4 -4
- package/TagInput/index.d.ts +6 -0
- package/TagInput/index.js +6 -3
- package/lib/cjs/Chatbox/index.d.ts +7 -0
- package/lib/cjs/Chatbox/index.js +406 -279
- package/lib/cjs/Date/index.js +53 -47
- package/lib/cjs/Select/index.d.ts +4 -4
- package/lib/cjs/TagInput/index.d.ts +6 -0
- package/lib/cjs/TagInput/index.js +6 -3
- package/lib/css/Chatbox/index.css +95 -14
- package/lib/esm/Chatbox/PureLoader.tsx +4 -2
- package/lib/esm/Chatbox/TypingEffect.tsx +12 -41
- package/lib/esm/Chatbox/index.scss +125 -25
- package/lib/esm/Chatbox/index.tsx +120 -30
- package/lib/esm/Chatbox/utils/func.ts +52 -1
- package/lib/esm/Date/index.tsx +60 -43
- package/lib/esm/Input/index.tsx +1 -0
- package/lib/esm/Select/index.tsx +4 -5
- package/lib/esm/TagInput/index.tsx +30 -10
- package/lib/esm/Textarea/index.tsx +0 -2
- package/package.json +1 -1
|
@@ -2,49 +2,69 @@
|
|
|
2
2
|
|
|
3
3
|
/*=================== Chatbox (Loading) =================*/
|
|
4
4
|
.custom-chatbox-loader-container {
|
|
5
|
+
--custom-chatbox-loader-color: #b9caf7;
|
|
6
|
+
--custom-chatbox-loader-color2: #0d6efd;
|
|
7
|
+
|
|
8
|
+
|
|
5
9
|
width: 130px;
|
|
6
10
|
text-align: start;
|
|
7
11
|
|
|
8
12
|
.custom-chatbox-loader {
|
|
9
13
|
height: 4px;
|
|
10
14
|
width: 100%;
|
|
11
|
-
--c: no-repeat linear-gradient(var(--
|
|
12
|
-
background: var(--c), var(--c),
|
|
15
|
+
--c: no-repeat linear-gradient(var(--custom-chatbox-loader-color2) 0 0);
|
|
16
|
+
background: var(--c), var(--c), var(--custom-chatbox-loader-color);
|
|
13
17
|
background-size: 60% 100%;
|
|
14
|
-
animation:
|
|
18
|
+
animation: loader-move 3s infinite;
|
|
15
19
|
}
|
|
16
|
-
}
|
|
17
20
|
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
background:
|
|
22
|
+
@keyframes loader-move {
|
|
23
|
+
0% {
|
|
24
|
+
background-position: -150% 0, -150% 0
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
66% {
|
|
28
|
+
background-position: 250% 0, -150% 0
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
100% {
|
|
32
|
+
background-position: 250% 0, 250% 0
|
|
26
33
|
}
|
|
27
34
|
}
|
|
28
|
-
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
|
|
32
|
-
@keyframes cssAnim--loadermove {
|
|
33
|
-
0% {
|
|
34
|
-
background-position: -150% 0, -150% 0
|
|
35
|
-
}
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
.custom-chatbox-mini-loader {
|
|
40
|
+
--custom-chatbox-miniloader-color: rgba(0,0,0,.5);
|
|
41
|
+
|
|
42
|
+
width: 15px;
|
|
43
|
+
height: 15px;
|
|
44
|
+
margin: .5rem;
|
|
45
|
+
margin-bottom: 0;
|
|
46
|
+
border: 3px dotted var(--custom-chatbox-miniloader-color);
|
|
47
|
+
border-radius: 50%;
|
|
48
|
+
display: inline-block;
|
|
49
|
+
position: relative;
|
|
50
|
+
box-sizing: border-box;
|
|
51
|
+
animation: mini-loader-spin 1s linear infinite;
|
|
52
|
+
|
|
53
|
+
@keyframes mini-loader-spin {
|
|
54
|
+
0% {
|
|
55
|
+
transform: rotate(0deg);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
100% {
|
|
59
|
+
transform: rotate(360deg);
|
|
60
|
+
}
|
|
40
61
|
|
|
41
|
-
100% {
|
|
42
|
-
background-position: 250% 0, 250% 0
|
|
43
62
|
}
|
|
44
63
|
}
|
|
45
64
|
|
|
46
65
|
|
|
47
66
|
|
|
67
|
+
|
|
48
68
|
/*=================== Chatbox (Core) =================*/
|
|
49
69
|
|
|
50
70
|
.custom-chatbox-circle {
|
|
@@ -142,8 +162,10 @@
|
|
|
142
162
|
--custom-chatbox-toolkit-opt-border-color: #e9ecef;
|
|
143
163
|
--custom-chatbox-toolkit-opt-active-color: #c2dfff;
|
|
144
164
|
--custom-chatbox-toolkit-btn-radius: 20px;
|
|
145
|
-
|
|
146
|
-
|
|
165
|
+
--custom-chatbox-questions-bg: #f5f5f5;
|
|
166
|
+
--custom-chatbox-questions-hover-bg: #e9e9e9;
|
|
167
|
+
--custom-chatbox-content-html-elem-border-color: #ddd;
|
|
168
|
+
--custom-chatbox-content-html-elem-bg: rgba(0,0,0,.05);
|
|
147
169
|
|
|
148
170
|
min-width: var(--custom-chatbox-w);
|
|
149
171
|
max-width: var(--custom-chatbox-w);
|
|
@@ -164,6 +186,8 @@
|
|
|
164
186
|
padding: 0;
|
|
165
187
|
font-size: 0.75rem;
|
|
166
188
|
margin-bottom: .5rem;
|
|
189
|
+
background: var(--custom-chatbox-msg-bg);
|
|
190
|
+
padding: .5rem;
|
|
167
191
|
}
|
|
168
192
|
|
|
169
193
|
summary {
|
|
@@ -200,7 +224,7 @@
|
|
|
200
224
|
|
|
201
225
|
/* message list */
|
|
202
226
|
.messages {
|
|
203
|
-
height: calc(100% -
|
|
227
|
+
height: calc(100% - 110px);
|
|
204
228
|
overflow-y: auto;
|
|
205
229
|
margin-bottom: 10px;
|
|
206
230
|
font-size: 13px;
|
|
@@ -253,6 +277,43 @@
|
|
|
253
277
|
margin-top: .3rem;
|
|
254
278
|
display: inline-block;
|
|
255
279
|
text-align: start;
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
/* Custom HTML Styles */
|
|
283
|
+
.table-container {
|
|
284
|
+
overflow-x: auto;
|
|
285
|
+
margin-bottom: .5rem;
|
|
286
|
+
|
|
287
|
+
&::-webkit-scrollbar {
|
|
288
|
+
height: 10px;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
&::-webkit-scrollbar-thumb {
|
|
292
|
+
background: rgba(0, 0, 0, 0.2);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
table {
|
|
296
|
+
width: 100%;
|
|
297
|
+
border-collapse: collapse;
|
|
298
|
+
border-radius: 0.35rem;
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
thead {
|
|
302
|
+
background: var(--custom-chatbox-content-html-elem-bg);
|
|
303
|
+
|
|
304
|
+
tr {
|
|
305
|
+
white-space: nowrap;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
th, td {
|
|
310
|
+
padding: .25rem;
|
|
311
|
+
text-align: left;
|
|
312
|
+
border: 1px solid var(--custom-chatbox-content-html-elem-border-color);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
256
317
|
}
|
|
257
318
|
|
|
258
319
|
|
|
@@ -274,6 +335,8 @@
|
|
|
274
335
|
|
|
275
336
|
.qa-content {
|
|
276
337
|
width: var(--custom-chatbox-content-w);
|
|
338
|
+
background: transparent;
|
|
339
|
+
padding-top: 0;
|
|
277
340
|
}
|
|
278
341
|
}
|
|
279
342
|
|
|
@@ -455,6 +518,11 @@
|
|
|
455
518
|
/* new chat button */
|
|
456
519
|
.newchat-btn {
|
|
457
520
|
text-align: center;
|
|
521
|
+
position: absolute;
|
|
522
|
+
bottom: 95px;
|
|
523
|
+
left: 50%;
|
|
524
|
+
transform: translateX(-50%);
|
|
525
|
+
z-index: 1;
|
|
458
526
|
|
|
459
527
|
> button {
|
|
460
528
|
padding: 3px 6px;
|
|
@@ -542,7 +610,7 @@
|
|
|
542
610
|
display: flex;
|
|
543
611
|
align-items: center;
|
|
544
612
|
justify-content: space-between;
|
|
545
|
-
border: 1px solid
|
|
613
|
+
border: 1px solid var(--custom-chatbox-gray-color);
|
|
546
614
|
border-radius: var(--custom-chatbox-toolkit-btn-radius);
|
|
547
615
|
cursor: pointer;
|
|
548
616
|
}
|
|
@@ -571,6 +639,7 @@
|
|
|
571
639
|
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
|
|
572
640
|
margin-bottom: 10px;
|
|
573
641
|
max-height: 300px;
|
|
642
|
+
min-width: 120px;
|
|
574
643
|
overflow-y: auto;
|
|
575
644
|
animation: dropupAnimation 0.2s ease;
|
|
576
645
|
|
|
@@ -605,6 +674,7 @@
|
|
|
605
674
|
color: var(--custom-chatbox-gray-color);
|
|
606
675
|
}
|
|
607
676
|
|
|
677
|
+
&.selected:not(.cancel),
|
|
608
678
|
&:hover {
|
|
609
679
|
background-color: var(--custom-chatbox-toolkit-opt-active-color);
|
|
610
680
|
}
|
|
@@ -615,5 +685,35 @@
|
|
|
615
685
|
|
|
616
686
|
|
|
617
687
|
|
|
688
|
+
/* default questions */
|
|
689
|
+
.default-questions-title {
|
|
690
|
+
margin-bottom: .5rem;
|
|
691
|
+
}
|
|
692
|
+
.default-questions {
|
|
693
|
+
padding: 15px;
|
|
694
|
+
width: 100%;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
.default-question-item {
|
|
698
|
+
padding: .3rem 1rem;
|
|
699
|
+
margin-bottom: .2rem;
|
|
700
|
+
background-color: var(--custom-chatbox-questions-bg);
|
|
701
|
+
border-radius: 0.35rem;
|
|
702
|
+
cursor: pointer;
|
|
703
|
+
transition: all 0.3s ease;
|
|
704
|
+
|
|
705
|
+
&:hover {
|
|
706
|
+
background-color: var(--custom-chatbox-questions-hover-bg);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
&:last-child {
|
|
711
|
+
margin-bottom: 0;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
|
|
717
|
+
|
|
618
718
|
|
|
619
719
|
}
|
|
@@ -12,7 +12,6 @@ import useClickOutside from 'funda-utils/dist/cjs/useClickOutside';
|
|
|
12
12
|
import { htmlEncode } from 'funda-utils/dist/cjs/sanitize';
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
|
|
16
15
|
// loader
|
|
17
16
|
import PureLoader from './PureLoader';
|
|
18
17
|
import TypingEffect from "./TypingEffect";
|
|
@@ -39,11 +38,19 @@ export type MessageDetail = {
|
|
|
39
38
|
tag: string; // such as '[reply]'
|
|
40
39
|
};
|
|
41
40
|
|
|
41
|
+
|
|
42
|
+
export type QuestionData = {
|
|
43
|
+
title: string;
|
|
44
|
+
list: Array<string>;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
|
|
42
48
|
export interface FloatingButton {
|
|
43
49
|
label: string; // HTML string
|
|
44
50
|
value: string;
|
|
45
51
|
onClick: string;
|
|
46
52
|
isSelect?: boolean; // Mark whether it is a drop-down selection button
|
|
53
|
+
dynamicOptions?: boolean; // Mark whether to use dynamic options
|
|
47
54
|
[key: string]: any; // Allows dynamic `onSelect__<number>` attributes, such as `onSelect__1`, `onSelect__2`, ...
|
|
48
55
|
}
|
|
49
56
|
|
|
@@ -106,10 +113,12 @@ export type ChatboxProps = {
|
|
|
106
113
|
toolkitButtons?: FloatingButton[];
|
|
107
114
|
newChatButton?: FloatingButton;
|
|
108
115
|
customMethods?: CustomMethod[]; // [{"name": "method1", "func": "() => { console.log('test'); }"}, ...]
|
|
116
|
+
defaultQuestions?: QuestionData;
|
|
109
117
|
customRequest?: CustomRequestFunction;
|
|
110
118
|
renderParser?: (input: string) => Promise<string>;
|
|
111
119
|
requestBodyFormatter?: (body: any, contextData: Record<string, any>, conversationHistory: MessageDetail[]) => Promise<Record<string, any>>;
|
|
112
120
|
nameFormatter?: (input: string) => string;
|
|
121
|
+
onQuestionClick?: (text: string, methods: Record<string, Function>) => void;
|
|
113
122
|
onInputChange?: (controlRef: React.RefObject<any>, val: string) => any;
|
|
114
123
|
onInputCallback?: (input: string) => Promise<string>;
|
|
115
124
|
onChunk?: (controlRef: React.RefObject<any>, lastContent: string, conversationHistory: MessageDetail[]) => any;
|
|
@@ -192,8 +201,10 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
192
201
|
setShow(false);
|
|
193
202
|
},
|
|
194
203
|
clearData: () => {
|
|
195
|
-
|
|
204
|
+
// Update both the conversation history and displayed messages
|
|
196
205
|
conversationHistory.current = [];
|
|
206
|
+
setMsgList([]);
|
|
207
|
+
|
|
197
208
|
},
|
|
198
209
|
sendMsg: () => {
|
|
199
210
|
handleClickSafe();
|
|
@@ -205,6 +216,11 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
205
216
|
conversationHistory.current = conversationHistory.current.slice(-maxLength);
|
|
206
217
|
}
|
|
207
218
|
},
|
|
219
|
+
setHistory: (messages: MessageDetail[]) => {
|
|
220
|
+
// Update both the conversation history and displayed messages
|
|
221
|
+
conversationHistory.current = [...messages];
|
|
222
|
+
setMsgList([...messages]);
|
|
223
|
+
},
|
|
208
224
|
setVal: (v: string) => {
|
|
209
225
|
if (inputContentRef.current) inputContentRef.current.set(v);
|
|
210
226
|
},
|
|
@@ -279,6 +295,7 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
279
295
|
newChatButton,
|
|
280
296
|
maxHistoryLength,
|
|
281
297
|
customRequest,
|
|
298
|
+
onQuestionClick,
|
|
282
299
|
renderParser,
|
|
283
300
|
requestBodyFormatter,
|
|
284
301
|
nameFormatter,
|
|
@@ -352,6 +369,7 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
352
369
|
toolkitButtons,
|
|
353
370
|
newChatButton,
|
|
354
371
|
customRequest,
|
|
372
|
+
onQuestionClick,
|
|
355
373
|
renderParser,
|
|
356
374
|
requestBodyFormatter,
|
|
357
375
|
nameFormatter,
|
|
@@ -361,6 +379,7 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
361
379
|
onComplete,
|
|
362
380
|
|
|
363
381
|
//
|
|
382
|
+
defaultQuestionsRes: questions,
|
|
364
383
|
latestContextData,
|
|
365
384
|
questionNameRes: _questionName,
|
|
366
385
|
answerNameRes: _answerName,
|
|
@@ -375,6 +394,28 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
375
394
|
}
|
|
376
395
|
|
|
377
396
|
|
|
397
|
+
|
|
398
|
+
//================================================================
|
|
399
|
+
// Custom Questions
|
|
400
|
+
//================================================================
|
|
401
|
+
const [questions, setQuestions] = useState<QuestionData | undefined>(props.defaultQuestions);
|
|
402
|
+
useEffect(() => {
|
|
403
|
+
if (props.defaultQuestions) {
|
|
404
|
+
setQuestions(props.defaultQuestions);
|
|
405
|
+
}
|
|
406
|
+
}, [props.defaultQuestions]);
|
|
407
|
+
const hasQuestion = () => {
|
|
408
|
+
return args().defaultQuestionsRes && (args().defaultQuestionsRes as QuestionData).list.length > 0;
|
|
409
|
+
};
|
|
410
|
+
const handleQuestionClick = (text: string) => {
|
|
411
|
+
if (inputContentRef.current) {
|
|
412
|
+
inputContentRef.current.set(text);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
args().onQuestionClick?.(text, exposedMethods());
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
|
|
378
419
|
//================================================================
|
|
379
420
|
// Custom buttons
|
|
380
421
|
//================================================================
|
|
@@ -393,16 +434,28 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
393
434
|
return newState;
|
|
394
435
|
});
|
|
395
436
|
};
|
|
396
|
-
const executeButtonAction = (actionStr: string, buttonId: string, buttonElement: HTMLButtonElement) => {
|
|
437
|
+
const executeButtonAction = async (actionStr: string, buttonId: string, buttonElement: HTMLButtonElement) => {
|
|
397
438
|
try {
|
|
398
|
-
// Create a new function to execute
|
|
399
439
|
const actionFn = new Function('method', 'isActive', 'button', actionStr);
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
440
|
+
|
|
441
|
+
// !!!REQUIRED "await"
|
|
442
|
+
// "customMethods" may be asynchronous
|
|
443
|
+
const result = await actionFn(exposedMethods(), !activeButtons[buttonId], buttonElement);
|
|
444
|
+
|
|
445
|
+
// If the returned result is an array, it is a dynamic option
|
|
446
|
+
if (Array.isArray(result) && Object.keys(dynamicOptions).length === 0) {
|
|
447
|
+
const options: FloatingButtonSelectOption[] = result.map(item => {
|
|
448
|
+
const [key, value] = Object.entries(item)[0];
|
|
449
|
+
const [label, val, onClick] = (value as string).split('{#}').map((s: string) => s.trim());
|
|
450
|
+
return { label, value: val, onClick };
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
// Update dynamic options
|
|
454
|
+
setDynamicOptions(prev => ({
|
|
455
|
+
...prev,
|
|
456
|
+
[buttonId]: options
|
|
457
|
+
}));
|
|
404
458
|
}
|
|
405
|
-
*/
|
|
406
459
|
|
|
407
460
|
// Update the button status
|
|
408
461
|
const newState = !activeButtons[buttonId];
|
|
@@ -411,17 +464,28 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
411
464
|
[buttonId]: newState
|
|
412
465
|
}));
|
|
413
466
|
|
|
414
|
-
return
|
|
415
|
-
|
|
416
|
-
|
|
467
|
+
return result;
|
|
417
468
|
} catch (error) {
|
|
418
469
|
console.error('Error executing button action:', error);
|
|
419
470
|
}
|
|
420
471
|
};
|
|
472
|
+
|
|
473
|
+
|
|
421
474
|
|
|
422
475
|
// options
|
|
423
476
|
const [selectedOpt, setSelectedOpt] = useState<Record<string, string | number>>({});
|
|
424
|
-
|
|
477
|
+
// Store dynamic options
|
|
478
|
+
const [dynamicOptions, setDynamicOptions] = useState<Record<string, FloatingButtonSelectOption[]>>({});
|
|
479
|
+
|
|
480
|
+
const getButtonOptions = (btn: FloatingButton, buttonId: string): FloatingButtonSelectOption[] => {
|
|
481
|
+
// If you are using the dynamic option and already have a cache, return the option for caching
|
|
482
|
+
//---------
|
|
483
|
+
if (btn.dynamicOptions && dynamicOptions[buttonId]) {
|
|
484
|
+
return dynamicOptions[buttonId];
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Use the static option from "props"
|
|
488
|
+
//---------
|
|
425
489
|
const options: FloatingButtonSelectOption[] = [];
|
|
426
490
|
let index = 1;
|
|
427
491
|
|
|
@@ -1124,7 +1188,7 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
1124
1188
|
{/**------------- NO DATA -------------*/}
|
|
1125
1189
|
{msgList.length === 0 ? <>
|
|
1126
1190
|
|
|
1127
|
-
<div className=
|
|
1191
|
+
<div className={`d-flex flex-column align-items-center justify-content-center ${hasQuestion() ? '' : 'h-50'}`}>
|
|
1128
1192
|
<p>
|
|
1129
1193
|
<svg width="70px" height="70px" viewBox="0 0 24 24" fill="none">
|
|
1130
1194
|
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 13.5997 2.37562 15.1116 3.04346 16.4525C3.22094 16.8088 3.28001 17.2161 3.17712 17.6006L2.58151 19.8267C2.32295 20.793 3.20701 21.677 4.17335 21.4185L6.39939 20.8229C6.78393 20.72 7.19121 20.7791 7.54753 20.9565C8.88837 21.6244 10.4003 22 12 22Z" stroke="#858297" strokeWidth="1.5" />
|
|
@@ -1134,7 +1198,26 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
1134
1198
|
|
|
1135
1199
|
</p>
|
|
1136
1200
|
<p className="text-primary" dangerouslySetInnerHTML={{ __html: `${args().noDataPlaceholder}` }}></p>
|
|
1201
|
+
|
|
1202
|
+
{/** DEFAULT QUESTIONS */}
|
|
1203
|
+
{hasQuestion() && (
|
|
1204
|
+
<div className="default-questions">
|
|
1205
|
+
<div className="default-questions-title" dangerouslySetInnerHTML={{ __html: `${(args().defaultQuestionsRes as QuestionData).title}` }}></div>
|
|
1206
|
+
{(args().defaultQuestionsRes as QuestionData).list?.map((question: string, index: number) => (
|
|
1207
|
+
<div
|
|
1208
|
+
key={index}
|
|
1209
|
+
className="default-question-item"
|
|
1210
|
+
onClick={() => handleQuestionClick(question)}
|
|
1211
|
+
dangerouslySetInnerHTML={{ __html: `${question}` }}
|
|
1212
|
+
/>
|
|
1213
|
+
))}
|
|
1214
|
+
</div>
|
|
1215
|
+
)}
|
|
1216
|
+
{/** /DEFAULT QUESTIONS */}
|
|
1217
|
+
|
|
1137
1218
|
</div>
|
|
1219
|
+
|
|
1220
|
+
|
|
1138
1221
|
</> : null}
|
|
1139
1222
|
{/**------------- /NO DATA -------------*/}
|
|
1140
1223
|
|
|
@@ -1378,7 +1461,7 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
1378
1461
|
|
|
1379
1462
|
|
|
1380
1463
|
{/**------------- SEND LOADING -------------*/}
|
|
1381
|
-
{args().sendLoading ? <div className="loading"><div style={{ display: loading ? 'block' : 'none' }}><PureLoader customClassName="w-100" txt="" /></div></div> : null}
|
|
1464
|
+
{args().sendLoading ? <div className="loading"><div style={{ display: loading ? 'block' : 'none' }}><PureLoader prefix={args().prefix} customClassName="w-100" txt="" /></div></div> : null}
|
|
1382
1465
|
{/**------------- /SEND LOADING -------------*/}
|
|
1383
1466
|
|
|
1384
1467
|
|
|
@@ -1390,8 +1473,8 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
1390
1473
|
const isActive = activeButtons[_id];
|
|
1391
1474
|
|
|
1392
1475
|
if (btn.isSelect) {
|
|
1393
|
-
const options = getButtonOptions(btn);
|
|
1394
|
-
|
|
1476
|
+
const options = getButtonOptions(btn, _id);
|
|
1477
|
+
|
|
1395
1478
|
return (
|
|
1396
1479
|
<div key={index} className="toolkit-select-wrapper">
|
|
1397
1480
|
<button
|
|
@@ -1403,6 +1486,9 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
1403
1486
|
...prev,
|
|
1404
1487
|
[_id]: !prev[_id]
|
|
1405
1488
|
}));
|
|
1489
|
+
|
|
1490
|
+
//
|
|
1491
|
+
executeButtonAction(btn.onClick, _id, e.currentTarget);
|
|
1406
1492
|
}}
|
|
1407
1493
|
>
|
|
1408
1494
|
<span dangerouslySetInnerHTML={{
|
|
@@ -1421,18 +1507,23 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
1421
1507
|
</svg></span>
|
|
1422
1508
|
</button>
|
|
1423
1509
|
|
|
1424
|
-
|
|
1425
|
-
|
|
1510
|
+
{/* OPTIONS */}
|
|
1426
1511
|
<div className={`toolkit-select-options ${isActive ? 'active' : ''}`}>
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1512
|
+
|
|
1513
|
+
{options.length > 0 ? <>
|
|
1514
|
+
{options.map((option: FloatingButtonSelectOption, optIndex: number) => (
|
|
1515
|
+
<div
|
|
1516
|
+
key={optIndex}
|
|
1517
|
+
className={`toolkit-select-option ${option.value || ''} ${selectedOpt.curIndex === optIndex ? 'selected' : ''}`}
|
|
1518
|
+
onClick={() => handleExecuteButtonSelect(_id, option, optIndex, option.value)}
|
|
1519
|
+
>
|
|
1520
|
+
<span dangerouslySetInnerHTML={{ __html: option.label }}></span>
|
|
1521
|
+
</div>
|
|
1522
|
+
))}
|
|
1523
|
+
</> : <>
|
|
1524
|
+
<div className={`${args().prefix || 'custom-'}chatbox-mini-loader`}></div>
|
|
1525
|
+
</>}
|
|
1526
|
+
|
|
1436
1527
|
</div>
|
|
1437
1528
|
</div>
|
|
1438
1529
|
);
|
|
@@ -1444,8 +1535,7 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
1444
1535
|
key={index}
|
|
1445
1536
|
id={_id}
|
|
1446
1537
|
className={`${btn.value || ''} ${isActive ? 'active' : ''}`}
|
|
1447
|
-
onClick={(e: React.MouseEvent<HTMLButtonElement>) =>
|
|
1448
|
-
executeButtonAction(btn.onClick, _id, e.currentTarget)}
|
|
1538
|
+
onClick={(e: React.MouseEvent<HTMLButtonElement>) => executeButtonAction(btn.onClick, _id, e.currentTarget)}
|
|
1449
1539
|
>
|
|
1450
1540
|
<span dangerouslySetInnerHTML={{ __html: btn.label }}></span>
|
|
1451
1541
|
</button>
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
// app.ts
|
|
2
2
|
import type { ChatboxProps } from '../index';
|
|
3
3
|
|
|
4
|
+
export interface HtmlTagPlaceholder {
|
|
5
|
+
original: string;
|
|
6
|
+
placeholder: string;
|
|
7
|
+
type: 'table' | 'img' | 'svg';
|
|
8
|
+
}
|
|
4
9
|
|
|
5
10
|
export function isValidJSON(str: string){
|
|
6
11
|
try {
|
|
@@ -13,7 +18,7 @@ export function isValidJSON(str: string){
|
|
|
13
18
|
|
|
14
19
|
export function formatLatestDisplayContent(str: string) {
|
|
15
20
|
// Regular expression to match <details> tags and their content
|
|
16
|
-
|
|
21
|
+
let output = str.replace(/<details class="think"[^>]*>([\s\S]*?)<\/details>/g, (match, content) => {
|
|
17
22
|
// Use regex to match the content inside the "div.think-content"
|
|
18
23
|
const thinkContentMatch = content.match(/<div class="think-content">([\s\S]*?)<\/div>/);
|
|
19
24
|
|
|
@@ -28,6 +33,12 @@ export function formatLatestDisplayContent(str: string) {
|
|
|
28
33
|
|
|
29
34
|
return match; // If not empty, return the original matched content
|
|
30
35
|
});
|
|
36
|
+
|
|
37
|
+
// Then handle tables without is-init class
|
|
38
|
+
output = output.replace(/<table(?![^>]*\bis-init\b)([^>]*)>([\s\S]*?)<\/table>/g, (match, attributes, content) => {
|
|
39
|
+
// Add is-init class to table and wrap it in container div
|
|
40
|
+
return `<div class="table-container"><table class="is-init"${attributes}>${content}</table></div>`;
|
|
41
|
+
});
|
|
31
42
|
|
|
32
43
|
return output;
|
|
33
44
|
}
|
|
@@ -127,3 +138,43 @@ export function isStreamResponse(response: Response): boolean {
|
|
|
127
138
|
return response.body instanceof ReadableStream;
|
|
128
139
|
};
|
|
129
140
|
|
|
141
|
+
|
|
142
|
+
export function extractHtmlTags(html: string): { processedHtml: string; placeholders: HtmlTagPlaceholder[] } {
|
|
143
|
+
const placeholders: HtmlTagPlaceholder[] = [];
|
|
144
|
+
let processedHtml = html;
|
|
145
|
+
|
|
146
|
+
// <table>
|
|
147
|
+
processedHtml = processedHtml.replace(/<table[^>]*>[\s\S]*?<\/table>/g, (match) => {
|
|
148
|
+
const placeholder = `[TABLE_${placeholders.length}]`;
|
|
149
|
+
placeholders.push({
|
|
150
|
+
original: `<div class="table-container">${match?.replace('<table', '<table class="is-init"')}</div>`,
|
|
151
|
+
placeholder,
|
|
152
|
+
type: 'table'
|
|
153
|
+
});
|
|
154
|
+
return placeholder;
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// <img>
|
|
158
|
+
processedHtml = processedHtml.replace(/<img[^>]*>/g, (match) => {
|
|
159
|
+
const placeholder = `[IMG_${placeholders.length}]`;
|
|
160
|
+
placeholders.push({
|
|
161
|
+
original: match,
|
|
162
|
+
placeholder,
|
|
163
|
+
type: 'img'
|
|
164
|
+
});
|
|
165
|
+
return placeholder;
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// <svg>
|
|
169
|
+
processedHtml = processedHtml.replace(/<svg[^>]*>[\s\S]*?<\/svg>/g, (match) => {
|
|
170
|
+
const placeholder = `[SVG_${placeholders.length}]`;
|
|
171
|
+
placeholders.push({
|
|
172
|
+
original: match,
|
|
173
|
+
placeholder,
|
|
174
|
+
type: 'svg'
|
|
175
|
+
});
|
|
176
|
+
return placeholder;
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
return { processedHtml, placeholders };
|
|
180
|
+
};
|