funda-ui 4.6.151 → 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.
@@ -1,24 +1,19 @@
1
1
  /*=================== Chatbox (Loading) =================*/
2
2
  .custom-chatbox-loader-container {
3
+ --custom-chatbox-loader-color: #b9caf7;
4
+ --custom-chatbox-loader-color2: #0d6efd;
3
5
  width: 130px;
4
6
  text-align: start;
5
7
  }
6
8
  .custom-chatbox-loader-container .custom-chatbox-loader {
7
9
  height: 4px;
8
10
  width: 100%;
9
- --c: no-repeat linear-gradient(var(--bs-primary) 0 0);
10
- background: var(--c), var(--c), #b9caf7;
11
+ --c: no-repeat linear-gradient(var(--custom-chatbox-loader-color2) 0 0);
12
+ background: var(--c), var(--c), var(--custom-chatbox-loader-color);
11
13
  background-size: 60% 100%;
12
- animation: cssAnim--loadermove 3s infinite;
14
+ animation: loader-move 3s infinite;
13
15
  }
14
-
15
- .dark-mode .custom-chatbox-loader-container .custom-chatbox-loader,
16
- [data-bs-theme=dark] .custom-chatbox-loader-container .custom-chatbox-loader {
17
- --c: no-repeat linear-gradient(var(--bs-primary) 0 0);
18
- background: var(--c), var(--c), #8692b5;
19
- }
20
-
21
- @keyframes cssAnim--loadermove {
16
+ @keyframes loader-move {
22
17
  0% {
23
18
  background-position: -150% 0, -150% 0;
24
19
  }
@@ -29,6 +24,29 @@
29
24
  background-position: 250% 0, 250% 0;
30
25
  }
31
26
  }
27
+
28
+ .custom-chatbox-mini-loader {
29
+ --custom-chatbox-miniloader-color: rgba(0,0,0,.5);
30
+ width: 15px;
31
+ height: 15px;
32
+ margin: 0.5rem;
33
+ margin-bottom: 0;
34
+ border: 3px dotted var(--custom-chatbox-miniloader-color);
35
+ border-radius: 50%;
36
+ display: inline-block;
37
+ position: relative;
38
+ box-sizing: border-box;
39
+ animation: mini-loader-spin 1s linear infinite;
40
+ }
41
+ @keyframes mini-loader-spin {
42
+ 0% {
43
+ transform: rotate(0deg);
44
+ }
45
+ 100% {
46
+ transform: rotate(360deg);
47
+ }
48
+ }
49
+
32
50
  /*=================== Chatbox (Core) =================*/
33
51
  .custom-chatbox-circle {
34
52
  --custom-chatbox-circle-color: #5A5EB9;
@@ -115,6 +133,8 @@
115
133
  --custom-chatbox-toolkit-btn-radius: 20px;
116
134
  --custom-chatbox-questions-bg: #f5f5f5;
117
135
  --custom-chatbox-questions-hover-bg: #e9e9e9;
136
+ --custom-chatbox-content-html-elem-border-color: #ddd;
137
+ --custom-chatbox-content-html-elem-bg: rgba(0,0,0,.05);
118
138
  min-width: var(--custom-chatbox-w);
119
139
  max-width: var(--custom-chatbox-w);
120
140
  margin: auto;
@@ -142,6 +162,8 @@
142
162
  padding: 0;
143
163
  font-size: 0.75rem;
144
164
  margin-bottom: 0.5rem;
165
+ background: var(--custom-chatbox-msg-bg);
166
+ padding: 0.5rem;
145
167
  }
146
168
  .custom-chatbox-container summary {
147
169
  cursor: pointer;
@@ -171,7 +193,7 @@
171
193
  font-size: 0.75rem;
172
194
  }
173
195
  .custom-chatbox-container .messages {
174
- height: calc(100% - 80px);
196
+ height: calc(100% - 110px);
175
197
  overflow-y: auto;
176
198
  margin-bottom: 10px;
177
199
  font-size: 13px;
@@ -216,6 +238,33 @@
216
238
  margin-top: 0.3rem;
217
239
  display: inline-block;
218
240
  text-align: start;
241
+ /* Custom HTML Styles */
242
+ }
243
+ .custom-chatbox-container .messages .qa-content .table-container {
244
+ overflow-x: auto;
245
+ margin-bottom: 0.5rem;
246
+ }
247
+ .custom-chatbox-container .messages .qa-content .table-container::-webkit-scrollbar {
248
+ height: 10px;
249
+ }
250
+ .custom-chatbox-container .messages .qa-content .table-container::-webkit-scrollbar-thumb {
251
+ background: rgba(0, 0, 0, 0.2);
252
+ }
253
+ .custom-chatbox-container .messages .qa-content .table-container table {
254
+ width: 100%;
255
+ border-collapse: collapse;
256
+ border-radius: 0.35rem;
257
+ }
258
+ .custom-chatbox-container .messages .qa-content .table-container table thead {
259
+ background: var(--custom-chatbox-content-html-elem-bg);
260
+ }
261
+ .custom-chatbox-container .messages .qa-content .table-container table thead tr {
262
+ white-space: nowrap;
263
+ }
264
+ .custom-chatbox-container .messages .qa-content .table-container table th, .custom-chatbox-container .messages .qa-content .table-container table td {
265
+ padding: 0.25rem;
266
+ text-align: left;
267
+ border: 1px solid var(--custom-chatbox-content-html-elem-border-color);
219
268
  }
220
269
  .custom-chatbox-container .messages .request {
221
270
  text-align: end;
@@ -232,6 +281,8 @@
232
281
  }
233
282
  .custom-chatbox-container .messages .reply .qa-content {
234
283
  width: var(--custom-chatbox-content-w);
284
+ background: transparent;
285
+ padding-top: 0;
235
286
  }
236
287
  .custom-chatbox-container .msg-dotted-loader-container {
237
288
  font-weight: normal;
@@ -373,6 +424,11 @@
373
424
  }
374
425
  .custom-chatbox-container .newchat-btn {
375
426
  text-align: center;
427
+ position: absolute;
428
+ bottom: 95px;
429
+ left: 50%;
430
+ transform: translateX(-50%);
431
+ z-index: 1;
376
432
  }
377
433
  .custom-chatbox-container .newchat-btn > button {
378
434
  padding: 3px 6px;
@@ -462,6 +518,7 @@
462
518
  box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
463
519
  margin-bottom: 10px;
464
520
  max-height: 300px;
521
+ min-width: 120px;
465
522
  overflow-y: auto;
466
523
  animation: dropupAnimation 0.2s ease;
467
524
  position: absolute;
@@ -489,7 +546,7 @@
489
546
  .custom-chatbox-container .toolkit-select-wrapper .toolkit-select-option.cancel {
490
547
  color: var(--custom-chatbox-gray-color);
491
548
  }
492
- .custom-chatbox-container .toolkit-select-wrapper .toolkit-select-option:hover {
549
+ .custom-chatbox-container .toolkit-select-wrapper .toolkit-select-option.selected:not(.cancel), .custom-chatbox-container .toolkit-select-wrapper .toolkit-select-option:hover {
493
550
  background-color: var(--custom-chatbox-toolkit-opt-active-color);
494
551
  }
495
552
  .custom-chatbox-container .default-questions-title {
@@ -2,6 +2,7 @@ import React from "react";
2
2
 
3
3
 
4
4
  type PureLoaderProps = {
5
+ prefix?: string;
5
6
  txt?: React.ReactNode;
6
7
  center?: boolean;
7
8
  customClassName?: string;
@@ -12,6 +13,7 @@ type PureLoaderProps = {
12
13
  const PureLoader = (props: PureLoaderProps) => {
13
14
 
14
15
  const {
16
+ prefix = 'custom-',
15
17
  txt,
16
18
  center,
17
19
  customClassName
@@ -21,11 +23,11 @@ const PureLoader = (props: PureLoaderProps) => {
21
23
  return (
22
24
  <>
23
25
 
24
- <div className={`app-chatbox-loader-container ${customClassName || ''}`}>
26
+ <div className={`${prefix}chatbox-loader-container ${customClassName || ''}`}>
25
27
  <div className="row g-0 align-items-center">
26
28
  <div className={center ? 'text-center' : ''}><small>{txt || txt === '' ? txt : 'Loading...'}</small></div>
27
29
  <div>
28
- <div className="app-chatbox-loader"></div>
30
+ <div className="${prefix}chatbox-loader"></div>
29
31
  </div>
30
32
  </div>
31
33
  </div>
@@ -1,57 +1,28 @@
1
1
  import React, { useEffect, useState } from 'react';
2
2
 
3
- interface TypingEffectProps {
3
+ // extract
4
+ import { extractHtmlTags } from './utils/func';
5
+ import type { HtmlTagPlaceholder } from './utils/func';
6
+
7
+ export interface TypingEffectProps {
4
8
  content: string; // The content to display
5
9
  speed: number; // Speed of typing in milliseconds
6
10
  onComplete?: () => void; // Callback when typing is complete
7
11
  onUpdate?: () => void; // Callback when typing
8
12
  }
9
13
 
10
- interface ImagePlaceholder {
11
- original: string;
12
- placeholder: string;
13
- type: 'img' | 'svg';
14
- }
14
+
15
15
  const TypingEffect: React.FC<TypingEffectProps> = ({ content, speed, onComplete, onUpdate }) => {
16
16
  const [displayedContent, setDisplayedContent] = useState<string>('');
17
17
  const [index, setIndex] = useState<number>(0);
18
- const [imagePlaceholders, setImagePlaceholders] = useState<ImagePlaceholder[]>([]);
18
+ const [htmlTagPlaceholder, setHtmlTagPlaceholders] = useState<HtmlTagPlaceholder[]>([]);
19
19
  const [processedContent, setProcessedContent] = useState<string>('');
20
20
 
21
21
  // Extract and replace image tags
22
22
  useEffect(() => {
23
- const extractImages = (html: string): { processedHtml: string; placeholders: ImagePlaceholder[] } => {
24
- const placeholders: ImagePlaceholder[] = [];
25
- let processedHtml = html;
26
-
27
- // <img>
28
- processedHtml = processedHtml.replace(/<img[^>]*>/g, (match) => {
29
- const placeholder = `[IMG_${placeholders.length}]`;
30
- placeholders.push({
31
- original: match,
32
- placeholder,
33
- type: 'img'
34
- });
35
- return placeholder;
36
- });
37
-
38
- // <svg>
39
- processedHtml = processedHtml.replace(/<svg[^>]*>[\s\S]*?<\/svg>/g, (match) => {
40
- const placeholder = `[SVG_${placeholders.length}]`;
41
- placeholders.push({
42
- original: match,
43
- placeholder,
44
- type: 'svg'
45
- });
46
- return placeholder;
47
- });
48
-
49
- return { processedHtml, placeholders };
50
- };
51
-
52
- const { processedHtml, placeholders } = extractImages(content);
23
+ const { processedHtml, placeholders } = extractHtmlTags(content);
53
24
  setProcessedContent(processedHtml);
54
- setImagePlaceholders(placeholders);
25
+ setHtmlTagPlaceholders(placeholders);
55
26
  }, [content]);
56
27
 
57
28
  // Handle typing effects
@@ -61,7 +32,7 @@ const TypingEffect: React.FC<TypingEffectProps> = ({ content, speed, onComplete,
61
32
  let newContent = processedContent.substring(0, index + 1);
62
33
 
63
34
  // Replace the completed placeholder
64
- imagePlaceholders.forEach(({ original, placeholder }) => {
35
+ htmlTagPlaceholder.forEach(({ original, placeholder }) => {
65
36
  if (newContent.includes(placeholder)) {
66
37
  newContent = newContent.replace(placeholder, original);
67
38
  }
@@ -77,9 +48,9 @@ const TypingEffect: React.FC<TypingEffectProps> = ({ content, speed, onComplete,
77
48
  }, speed);
78
49
 
79
50
  return () => clearInterval(timer);
80
- }, [processedContent, index, speed, onComplete, onUpdate, imagePlaceholders]);
51
+ }, [processedContent, index, speed, onComplete, onUpdate, htmlTagPlaceholder]);
81
52
 
82
- return <span dangerouslySetInnerHTML={{ __html: displayedContent }} />;
53
+ return <div dangerouslySetInnerHTML={{ __html: displayedContent }} />;
83
54
  };
84
55
 
85
56
  export default TypingEffect;
@@ -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(--bs-primary) 0 0);
12
- background: var(--c), var(--c), #b9caf7;
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: cssAnim--loadermove 3s infinite;
18
+ animation: loader-move 3s infinite;
15
19
  }
16
- }
17
20
 
18
21
 
19
- .dark-mode,
20
- [data-bs-theme=dark] {
21
-
22
- .custom-chatbox-loader-container {
23
- .custom-chatbox-loader {
24
- --c: no-repeat linear-gradient(var(--bs-primary) 0 0);
25
- background: var(--c), var(--c), #8692b5;
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
- 66% {
38
- background-position: 250% 0, -150% 0
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 {
@@ -144,7 +164,8 @@
144
164
  --custom-chatbox-toolkit-btn-radius: 20px;
145
165
  --custom-chatbox-questions-bg: #f5f5f5;
146
166
  --custom-chatbox-questions-hover-bg: #e9e9e9;
147
-
167
+ --custom-chatbox-content-html-elem-border-color: #ddd;
168
+ --custom-chatbox-content-html-elem-bg: rgba(0,0,0,.05);
148
169
 
149
170
  min-width: var(--custom-chatbox-w);
150
171
  max-width: var(--custom-chatbox-w);
@@ -165,6 +186,8 @@
165
186
  padding: 0;
166
187
  font-size: 0.75rem;
167
188
  margin-bottom: .5rem;
189
+ background: var(--custom-chatbox-msg-bg);
190
+ padding: .5rem;
168
191
  }
169
192
 
170
193
  summary {
@@ -201,7 +224,7 @@
201
224
 
202
225
  /* message list */
203
226
  .messages {
204
- height: calc(100% - 80px);
227
+ height: calc(100% - 110px);
205
228
  overflow-y: auto;
206
229
  margin-bottom: 10px;
207
230
  font-size: 13px;
@@ -254,6 +277,43 @@
254
277
  margin-top: .3rem;
255
278
  display: inline-block;
256
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
+
257
317
  }
258
318
 
259
319
 
@@ -275,6 +335,8 @@
275
335
 
276
336
  .qa-content {
277
337
  width: var(--custom-chatbox-content-w);
338
+ background: transparent;
339
+ padding-top: 0;
278
340
  }
279
341
  }
280
342
 
@@ -456,6 +518,11 @@
456
518
  /* new chat button */
457
519
  .newchat-btn {
458
520
  text-align: center;
521
+ position: absolute;
522
+ bottom: 95px;
523
+ left: 50%;
524
+ transform: translateX(-50%);
525
+ z-index: 1;
459
526
 
460
527
  > button {
461
528
  padding: 3px 6px;
@@ -572,6 +639,7 @@
572
639
  box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
573
640
  margin-bottom: 10px;
574
641
  max-height: 300px;
642
+ min-width: 120px;
575
643
  overflow-y: auto;
576
644
  animation: dropupAnimation 0.2s ease;
577
645
 
@@ -606,6 +674,7 @@
606
674
  color: var(--custom-chatbox-gray-color);
607
675
  }
608
676
 
677
+ &.selected:not(.cancel),
609
678
  &:hover {
610
679
  background-color: var(--custom-chatbox-toolkit-opt-active-color);
611
680
  }
@@ -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";
@@ -51,6 +50,7 @@ export interface FloatingButton {
51
50
  value: string;
52
51
  onClick: string;
53
52
  isSelect?: boolean; // Mark whether it is a drop-down selection button
53
+ dynamicOptions?: boolean; // Mark whether to use dynamic options
54
54
  [key: string]: any; // Allows dynamic `onSelect__<number>` attributes, such as `onSelect__1`, `onSelect__2`, ...
55
55
  }
56
56
 
@@ -201,8 +201,10 @@ const Chatbox = (props: ChatboxProps) => {
201
201
  setShow(false);
202
202
  },
203
203
  clearData: () => {
204
- setMsgList([]);
204
+ // Update both the conversation history and displayed messages
205
205
  conversationHistory.current = [];
206
+ setMsgList([]);
207
+
206
208
  },
207
209
  sendMsg: () => {
208
210
  handleClickSafe();
@@ -214,6 +216,11 @@ const Chatbox = (props: ChatboxProps) => {
214
216
  conversationHistory.current = conversationHistory.current.slice(-maxLength);
215
217
  }
216
218
  },
219
+ setHistory: (messages: MessageDetail[]) => {
220
+ // Update both the conversation history and displayed messages
221
+ conversationHistory.current = [...messages];
222
+ setMsgList([...messages]);
223
+ },
217
224
  setVal: (v: string) => {
218
225
  if (inputContentRef.current) inputContentRef.current.set(v);
219
226
  },
@@ -427,16 +434,28 @@ const Chatbox = (props: ChatboxProps) => {
427
434
  return newState;
428
435
  });
429
436
  };
430
- const executeButtonAction = (actionStr: string, buttonId: string, buttonElement: HTMLButtonElement) => {
437
+ const executeButtonAction = async (actionStr: string, buttonId: string, buttonElement: HTMLButtonElement) => {
431
438
  try {
432
- // Create a new function to execute
433
439
  const actionFn = new Function('method', 'isActive', 'button', actionStr);
434
- /*
435
- function (method, isActive, button) {
436
- console.log('Clearing chat');
437
- method.clearData();
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
+ }));
438
458
  }
439
- */
440
459
 
441
460
  // Update the button status
442
461
  const newState = !activeButtons[buttonId];
@@ -445,17 +464,28 @@ const Chatbox = (props: ChatboxProps) => {
445
464
  [buttonId]: newState
446
465
  }));
447
466
 
448
- return actionFn(exposedMethods(), newState, buttonElement);
449
-
450
-
467
+ return result;
451
468
  } catch (error) {
452
469
  console.error('Error executing button action:', error);
453
470
  }
454
471
  };
472
+
473
+
455
474
 
456
475
  // options
457
476
  const [selectedOpt, setSelectedOpt] = useState<Record<string, string | number>>({});
458
- const getButtonOptions = (btn: FloatingButton): FloatingButtonSelectOption[] => {
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
+ //---------
459
489
  const options: FloatingButtonSelectOption[] = [];
460
490
  let index = 1;
461
491
 
@@ -1431,7 +1461,7 @@ const Chatbox = (props: ChatboxProps) => {
1431
1461
 
1432
1462
 
1433
1463
  {/**------------- SEND LOADING -------------*/}
1434
- {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}
1435
1465
  {/**------------- /SEND LOADING -------------*/}
1436
1466
 
1437
1467
 
@@ -1443,8 +1473,8 @@ const Chatbox = (props: ChatboxProps) => {
1443
1473
  const isActive = activeButtons[_id];
1444
1474
 
1445
1475
  if (btn.isSelect) {
1446
- const options = getButtonOptions(btn);
1447
-
1476
+ const options = getButtonOptions(btn, _id);
1477
+
1448
1478
  return (
1449
1479
  <div key={index} className="toolkit-select-wrapper">
1450
1480
  <button
@@ -1456,6 +1486,9 @@ const Chatbox = (props: ChatboxProps) => {
1456
1486
  ...prev,
1457
1487
  [_id]: !prev[_id]
1458
1488
  }));
1489
+
1490
+ //
1491
+ executeButtonAction(btn.onClick, _id, e.currentTarget);
1459
1492
  }}
1460
1493
  >
1461
1494
  <span dangerouslySetInnerHTML={{
@@ -1474,18 +1507,23 @@ const Chatbox = (props: ChatboxProps) => {
1474
1507
  </svg></span>
1475
1508
  </button>
1476
1509
 
1477
-
1478
-
1510
+ {/* OPTIONS */}
1479
1511
  <div className={`toolkit-select-options ${isActive ? 'active' : ''}`}>
1480
- {options.map((option: FloatingButtonSelectOption, optIndex: number) => (
1481
- <div
1482
- key={optIndex}
1483
- className={`toolkit-select-option ${option.value || ''} ${selectedOpt.curIndex === optIndex ? 'selected' : ''}`}
1484
- onClick={() => handleExecuteButtonSelect(_id, option, optIndex, option.value)}
1485
- >
1486
- <span dangerouslySetInnerHTML={{ __html: option.label }}></span>
1487
- </div>
1488
- ))}
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
+
1489
1527
  </div>
1490
1528
  </div>
1491
1529
  );
@@ -1497,8 +1535,7 @@ const Chatbox = (props: ChatboxProps) => {
1497
1535
  key={index}
1498
1536
  id={_id}
1499
1537
  className={`${btn.value || ''} ${isActive ? 'active' : ''}`}
1500
- onClick={(e: React.MouseEvent<HTMLButtonElement>) =>
1501
- executeButtonAction(btn.onClick, _id, e.currentTarget)}
1538
+ onClick={(e: React.MouseEvent<HTMLButtonElement>) => executeButtonAction(btn.onClick, _id, e.currentTarget)}
1502
1539
  >
1503
1540
  <span dangerouslySetInnerHTML={{ __html: btn.label }}></span>
1504
1541
  </button>