funda-ui 4.7.150 → 4.7.152

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.
@@ -13,7 +13,6 @@ import { htmlEncode } from 'funda-utils/dist/cjs/sanitize';
13
13
 
14
14
 
15
15
 
16
-
17
16
  // loader
18
17
  import PureLoader from './PureLoader';
19
18
  import TypingEffect from "./TypingEffect";
@@ -46,16 +45,24 @@ export type QuestionData = {
46
45
  list: Array<string>;
47
46
  };
48
47
 
48
+ export type SelectedOption = {
49
+ [key: string]: string | number;
50
+ curIndex: number;
51
+ curValue: string;
52
+ };
49
53
 
50
54
  export interface FloatingButton {
51
55
  label: string; // HTML string
52
56
  value: string;
53
57
  onClick: string;
58
+ active?: boolean; // Specify if the button should be active by default
54
59
  isSelect?: boolean; // Mark whether it is a drop-down selection button
55
60
  dynamicOptions?: boolean; // Mark whether to use dynamic options
61
+ defaultSelected?: number; // Specify default selected option index
56
62
  [key: string]: any; // Allows dynamic `onSelect__<number>` attributes, such as `onSelect__1`, `onSelect__2`, ...
57
63
  }
58
64
 
65
+
59
66
  export interface FloatingButtonSelectOption {
60
67
  label: string;
61
68
  value: string;
@@ -178,6 +185,11 @@ const Chatbox = (props: ChatboxProps) => {
178
185
  const [enableStreamMode, setEnableStreamMode] = useState<boolean>(true);
179
186
  const animatedMessagesRef = useRef<Set<number>>(new Set()); // Add a ref to keep track of messages that have already been animated
180
187
 
188
+ // Keep track of whether the default values have been initialized
189
+ const [initializedDefaults, setInitializedDefaults] = useState<Record<string, boolean>>({});
190
+
191
+
192
+
181
193
  //
182
194
  const timer = useRef<any>(null);
183
195
 
@@ -508,6 +520,19 @@ const Chatbox = (props: ChatboxProps) => {
508
520
  return newState;
509
521
  });
510
522
  };
523
+
524
+ // The onClick action specifically used to perform the default options
525
+ const executeDefaultOptionAction = async (actionStr: string, buttonId: string) => {
526
+ try {
527
+ const actionFn = new Function('method', 'isActive', 'button', actionStr);
528
+ // To perform the action, pass false as the "isActive" parameter, as this is the default option
529
+ await actionFn(exposedMethods(), false, document.getElementById(buttonId));
530
+ } catch (error) {
531
+ console.error('Error executing default option action:', error);
532
+ }
533
+ };
534
+
535
+
511
536
  const executeButtonAction = async (actionStr: string, buttonId: string, buttonElement: HTMLButtonElement) => {
512
537
  try {
513
538
  const actionFn = new Function('method', 'isActive', 'button', actionStr);
@@ -547,7 +572,11 @@ const Chatbox = (props: ChatboxProps) => {
547
572
 
548
573
 
549
574
  // options
550
- const [selectedOpt, setSelectedOpt] = useState<Record<string, string | number>>({});
575
+ const [selectedOpt, setSelectedOpt] = useState<SelectedOption>({
576
+ curIndex: -1,
577
+ curValue: ''
578
+ });
579
+
551
580
  // Store dynamic options
552
581
  const [dynamicOptions, setDynamicOptions] = useState<Record<string, FloatingButtonSelectOption[]>>({});
553
582
 
@@ -575,7 +604,7 @@ const Chatbox = (props: ChatboxProps) => {
575
604
  return options;
576
605
  };
577
606
 
578
- const handleExecuteButtonSelect = (buttonId: string, option: FloatingButtonSelectOption, index: number, value: string) => {
607
+ const handleExecuteButtonSelect = (buttonId: string, option: FloatingButtonSelectOption, index: number, value: string, isDefaultSelection: boolean = false) => {
579
608
 
580
609
  if (option.value === "cancel") {
581
610
  setSelectedOpt(prev => {
@@ -597,11 +626,15 @@ const Chatbox = (props: ChatboxProps) => {
597
626
  }));
598
627
  }
599
628
 
629
+
630
+ // The button action is performed and the drop-down menu is closed only when it is not the default selection
631
+ if (!isDefaultSelection) {
632
+ executeButtonAction(option.onClick, buttonId, document.getElementById(buttonId) as HTMLButtonElement);
633
+
634
+ // Close the drop-down
635
+ closeDropdowns();
636
+ }
600
637
 
601
- executeButtonAction(option.onClick, buttonId, document.getElementById(buttonId) as HTMLButtonElement);
602
-
603
- // Close the drop-down
604
- closeDropdowns();
605
638
  };
606
639
 
607
640
  // click outside
@@ -1054,10 +1087,11 @@ const Chatbox = (props: ChatboxProps) => {
1054
1087
  customMethodsRef.current,
1055
1088
  conversationHistory.current
1056
1089
  );
1057
-
1058
1090
  const { content, isStream } = customResponse;
1059
1091
  let contentRes: any = content;
1060
1092
 
1093
+
1094
+
1061
1095
  // Update stream mode
1062
1096
  setEnableStreamMode(isStream);
1063
1097
 
@@ -1164,6 +1198,7 @@ const Chatbox = (props: ChatboxProps) => {
1164
1198
  // hide loader
1165
1199
  setLoaderDisplay(false);
1166
1200
 
1201
+
1167
1202
  let result: any = jsonResponse;
1168
1203
  if (extractPath) {
1169
1204
  for (const path of extractPath) {
@@ -1176,6 +1211,7 @@ const Chatbox = (props: ChatboxProps) => {
1176
1211
  // Replace with a valid label
1177
1212
  content = fixHtmlTags(content, args().withReasoning, args().reasoningSwitchLabel);
1178
1213
 
1214
+
1179
1215
  return {
1180
1216
  reply: formatLatestDisplayContent(content),
1181
1217
  useStreamRender: false
@@ -1244,6 +1280,53 @@ const Chatbox = (props: ChatboxProps) => {
1244
1280
  (window as any).chatboxCopyToClipboard = chatboxCopyToClipboard;
1245
1281
  }, []);
1246
1282
 
1283
+
1284
+
1285
+ // Initialize the default value of toolkit buttons
1286
+ useEffect(() => {
1287
+ if (args().toolkitButtons) {
1288
+ args().toolkitButtons.forEach((btn: FloatingButton, index: number) => {
1289
+ const _id = `${args().prefix || 'custom-'}chatbox-btn-tools-${chatId}${index}`;
1290
+
1291
+ if (btn.isSelect) {
1292
+
1293
+ if (!initializedDefaults[_id] && typeof btn.defaultSelected === 'number') {
1294
+ const options = getButtonOptions(btn, _id);
1295
+
1296
+ // If there is a default selected item, initialize the selected state
1297
+ if (btn.defaultSelected >= 0 && btn.defaultSelected < options.length) {
1298
+ const defaultOption = options[btn.defaultSelected];
1299
+ if (defaultOption) {
1300
+ // Update the selected status
1301
+ // console.log('--> defaultOption: ', defaultOption);
1302
+
1303
+ // Pass the "isDefaultSelection" parameter as true
1304
+ handleExecuteButtonSelect(_id, defaultOption, btn.defaultSelected, defaultOption.value, true);
1305
+
1306
+ // Perform the onClick action alone
1307
+ executeDefaultOptionAction(defaultOption.onClick, _id);
1308
+
1309
+
1310
+ // Mark this button with the default value initialized
1311
+ setInitializedDefaults(prev => ({
1312
+ ...prev,
1313
+ [_id]: true
1314
+ }));
1315
+
1316
+ }
1317
+ }
1318
+ }
1319
+ } else if (btn.active) {
1320
+ // For non-select buttons, if defaultActive is true, execute the onClick action
1321
+ executeButtonAction(btn.onClick, _id, document.getElementById(_id) as HTMLButtonElement);
1322
+ }
1323
+
1324
+
1325
+ })
1326
+ }
1327
+ }, [chatId, args().toolkitButtons]); // It is only executed when the component is first rendered and when toolkitButtons changes
1328
+
1329
+
1247
1330
  return (
1248
1331
  <>
1249
1332
 
@@ -1570,12 +1653,13 @@ const Chatbox = (props: ChatboxProps) => {
1570
1653
 
1571
1654
  if (btn.isSelect) {
1572
1655
  const options = getButtonOptions(btn, _id);
1573
-
1656
+
1574
1657
  return (
1575
1658
  <div key={index} className="toolkit-select-wrapper">
1576
1659
  <button
1577
1660
  id={_id}
1578
- className={`toolkit-select-btn ${btn.value || ''} ${isActive ? 'active' : ''} ${selectedOpt.curValue !== 'cancel' && typeof selectedOpt.curValue !== 'undefined' && selectedOpt.curValue !== '' ? 'opt-active' : ''}`}
1661
+ data-value={btn.value || ''}
1662
+ className={`toolkit-select-btn ${isActive ? 'active' : ''} ${selectedOpt.curValue !== 'cancel' && typeof selectedOpt.curValue !== 'undefined' && selectedOpt.curValue !== '' ? 'opt-active' : ''}`}
1579
1663
  onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
1580
1664
  e.preventDefault();
1581
1665
  setActiveButtons(prev => ({
@@ -1610,7 +1694,8 @@ const Chatbox = (props: ChatboxProps) => {
1610
1694
  {options.map((option: FloatingButtonSelectOption, optIndex: number) => (
1611
1695
  <div
1612
1696
  key={optIndex}
1613
- className={`toolkit-select-option ${option.value || ''} ${selectedOpt.curIndex === optIndex ? 'selected' : ''}`}
1697
+ data-value={option.value || ''}
1698
+ className={`toolkit-select-option ${selectedOpt.curIndex === optIndex ? 'selected' : ''}`}
1614
1699
  onClick={() => handleExecuteButtonSelect(_id, option, optIndex, option.value)}
1615
1700
  >
1616
1701
  <span dangerouslySetInnerHTML={{ __html: option.label }}></span>
@@ -7,7 +7,6 @@ import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
7
7
  import { actualPropertyValue, getTextTop } from 'funda-utils/dist/cjs/inputsCalculation';
8
8
  import useDebounce from 'funda-utils/dist/cjs/useDebounce';
9
9
 
10
-
11
10
  export type TextareaProps = {
12
11
  contentRef?: React.ForwardedRef<any>; // could use "Array" on contentRef.current, such as contentRef.current[0], contentRef.current[1]
13
12
  wrapperClassName?: string;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "author": "UIUX Lab",
3
3
  "email": "uiuxlab@gmail.com",
4
4
  "name": "funda-ui",
5
- "version": "4.7.150",
5
+ "version": "4.7.152",
6
6
  "description": "React components using pure Bootstrap 5+ which does not contain any external style and script libraries.",
7
7
  "repository": {
8
8
  "type": "git",