react-core-ts 2.1.30 → 2.1.32

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,4 +1,10 @@
1
- import React, { useCallback, useEffect, useRef, useState } from 'react';
1
+ import React, {
2
+ useCallback,
3
+ useEffect,
4
+ useLayoutEffect,
5
+ useRef,
6
+ useState,
7
+ } from 'react';
2
8
  import { FieldErrors } from 'react-hook-form';
3
9
 
4
10
  import CustomIcons from './components/customIcons';
@@ -18,6 +24,26 @@ type valueProps = {
18
24
  param4?: string | number | null;
19
25
  from?: number;
20
26
  };
27
+
28
+ /** Add new theme ids here when introducing layouts */
29
+ export type DropdownThemeId = 'default' | 'splitDetail';
30
+
31
+ /** Options for `dropdownTheme: 'splitDetail'` (title + subtitle + optional badge pill) */
32
+ export type SplitDetailThemeConfig = {
33
+ subtitleKey?: string;
34
+ badgeKey?: string;
35
+ /** Shown before badge value, e.g. "Acc. No.: " */
36
+ badgePrefix?: string;
37
+ };
38
+
39
+ /**
40
+ * Per-theme configuration. Extend this type with new keys when adding themes
41
+ * (e.g. `compact?: CompactThemeConfig`).
42
+ */
43
+ export type DropdownThemeConfig = {
44
+ splitDetail?: SplitDetailThemeConfig;
45
+ };
46
+
21
47
  interface AutoSuggestionInputProps {
22
48
  id?: string;
23
49
  label?: string;
@@ -62,6 +88,13 @@ interface AutoSuggestionInputProps {
62
88
  key: string; // Field name in the suggestion object
63
89
  label: string; // Label for the column
64
90
  }>; // Column header to display
91
+ dropdownTheme?: DropdownThemeId;
92
+ /** Theme-specific field mapping and options; see `DropdownThemeConfig` */
93
+ dropdownThemeConfig?: DropdownThemeConfig;
94
+ /**
95
+ * When true, typing updates the input only; fetching/opening the list runs from the search control (and Enter), not while typing.
96
+ */
97
+ searchOnButtonOnly?: boolean;
65
98
  }
66
99
 
67
100
  const ReactAutoCompleteTableView: React.FC<AutoSuggestionInputProps> = ({
@@ -100,7 +133,16 @@ const ReactAutoCompleteTableView: React.FC<AutoSuggestionInputProps> = ({
100
133
  tableView = false,
101
134
  additionalColumns = [],
102
135
  columnHeader = [],
136
+ dropdownTheme = 'default',
137
+ dropdownThemeConfig,
138
+ searchOnButtonOnly = false,
103
139
  }) => {
140
+ const splitDetailOptions = dropdownThemeConfig?.splitDetail;
141
+ const showSearchOnButtonChrome =
142
+ searchOnButtonOnly &&
143
+ (type === 'auto_complete' || type === 'custom_search_select') &&
144
+ !disabled &&
145
+ !readOnly;
104
146
  const [inputValue, setInputValue] = useState<any>(value?.name ?? "");
105
147
  const [isHovered, setIsHovered] = useState<boolean>(false);
106
148
  const [isLoading, setIsLoading] = useState<boolean>(false);
@@ -193,13 +235,52 @@ const ReactAutoCompleteTableView: React.FC<AutoSuggestionInputProps> = ({
193
235
  timerRef.current = 0;
194
236
 
195
237
  setInputValue(value);
196
- handleValChange(value);
238
+ if (!searchOnButtonOnly) {
239
+ handleValChange(value);
240
+ }
197
241
  if (!value) {
198
242
  setInputValue('');
199
243
  onChange({ id: undefined, name: '', from: 2 });
200
244
  }
201
245
  };
202
246
 
247
+ const runSearchFromInput = (value: string) => {
248
+ setDropOpen(true);
249
+ onChange({ id: undefined, name: '', from: 1 });
250
+ if (value.trim() === '' && type === 'auto_complete') {
251
+ setSuggestions([]);
252
+ if (autoFilter) {
253
+ handleDropData('*');
254
+ } else {
255
+ setDropOpen(false);
256
+ }
257
+ } else {
258
+ handleDropData(value);
259
+ }
260
+ setTimeout(() => {
261
+ timerRef.current = 1;
262
+ }, 200);
263
+ };
264
+
265
+ const handleSearchButtonClick = (e: React.MouseEvent) => {
266
+ e.preventDefault();
267
+ e.stopPropagation();
268
+ if (disabled || readOnly) return;
269
+ timerRef.current = 0;
270
+ const q = inputRef.current?.value ?? inputValue ?? '';
271
+ runSearchFromInput(q);
272
+ };
273
+
274
+ const handleSearchInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
275
+ if (!searchOnButtonOnly || disabled || readOnly) return;
276
+ if (e.key === 'Enter') {
277
+ e.preventDefault();
278
+ timerRef.current = 0;
279
+ const q = inputRef.current?.value ?? inputValue ?? '';
280
+ runSearchFromInput(q);
281
+ }
282
+ };
283
+
203
284
  const handleValChange = useCallback(
204
285
  debounce(
205
286
  (value: string) => {
@@ -283,6 +364,9 @@ const ReactAutoCompleteTableView: React.FC<AutoSuggestionInputProps> = ({
283
364
  const onLabelClick = () => {
284
365
  if (!isDisabled) {
285
366
  inputRef?.current?.focus();
367
+ if (searchOnButtonOnly) {
368
+ return;
369
+ }
286
370
  if (autoFilter && inputValue === '') {
287
371
  handleValChange('*');
288
372
  } else if (
@@ -344,6 +428,9 @@ const ReactAutoCompleteTableView: React.FC<AutoSuggestionInputProps> = ({
344
428
  }, [autoFocus]);
345
429
  const onInputFocus = () => {
346
430
  if (!isDisabled) {
431
+ if (searchOnButtonOnly) {
432
+ return;
433
+ }
347
434
  if (autoFilter && inputValue === '') {
348
435
  handleValChange('*');
349
436
  } else if (
@@ -379,6 +466,12 @@ const ReactAutoCompleteTableView: React.FC<AutoSuggestionInputProps> = ({
379
466
  };
380
467
 
381
468
  const handleOpenDropdown = (e: any) => {
469
+ if (searchOnButtonOnly) {
470
+ if (suggestions && suggestions.length > 0 && !isLoading) {
471
+ setDropOpen((open) => !open);
472
+ }
473
+ return;
474
+ }
382
475
  if (!suggestions || suggestions?.length === 0 || refetchData) {
383
476
  if (autoDropdown && (inputValue === '' || inputValue.trim() === '')) {
384
477
  setSuggestions([]);
@@ -554,21 +647,50 @@ const ReactAutoCompleteTableView: React.FC<AutoSuggestionInputProps> = ({
554
647
  // const getPosition = () => {
555
648
  // return 'bottom'
556
649
  // }
650
+ const itemMatchesLocalFilter = (item: any): boolean => {
651
+ const baseMatch = checkIncludes(
652
+ item.name,
653
+ inputValue,
654
+ item.param1 ?? '',
655
+ item.param2 ?? '',
656
+ item.param3 ?? '',
657
+ item.param4 ?? ''
658
+ );
659
+ if (
660
+ dropdownTheme !== 'splitDetail' ||
661
+ !splitDetailOptions ||
662
+ !inputValue
663
+ ) {
664
+ return baseMatch;
665
+ }
666
+ const q = inputValue.trim().toLowerCase();
667
+ if (!q) return baseMatch;
668
+
669
+ const hay = (v: unknown) =>
670
+ v != null && String(v).toLowerCase().includes(q);
671
+
672
+ if (item.label != null && hay(item.label)) return true;
673
+ if (
674
+ splitDetailOptions.subtitleKey &&
675
+ hay(item[splitDetailOptions.subtitleKey])
676
+ ) {
677
+ return true;
678
+ }
679
+ if (
680
+ splitDetailOptions.badgeKey &&
681
+ hay(item[splitDetailOptions.badgeKey])
682
+ ) {
683
+ return true;
684
+ }
685
+ return baseMatch;
686
+ };
687
+
557
688
  const filteredData =
558
689
  inputValue !== '*' &&
559
690
  inputValue !== '' &&
560
691
  type !== 'custom_select' &&
561
692
  !noLocalFilter
562
- ? suggestions?.filter((item: valueProps) =>
563
- checkIncludes(
564
- item.name,
565
- inputValue,
566
- item.param1 ?? '',
567
- item.param2 ?? '',
568
- item.param3 ?? '',
569
- item.param4 ?? ''
570
- )
571
- )
693
+ ? suggestions?.filter((item: valueProps) => itemMatchesLocalFilter(item))
572
694
  : suggestions;
573
695
 
574
696
  const handleError = (data: any) => {
@@ -764,7 +886,11 @@ const ReactAutoCompleteTableView: React.FC<AutoSuggestionInputProps> = ({
764
886
  <div
765
887
  ref={dropdownContentRef}
766
888
  style={{ ...dropPosition, overflow: 'hidden' }}
767
- className="autocomplete-suggections autocomplete-suggections-tableview absolute bg-white shadow-gray-300 shadow-md border border-grey-light z-50 mt-9"
889
+ className={`autocomplete-suggections autocomplete-suggections-tableview absolute bg-white shadow-gray-300 shadow-md border border-grey-light z-50 mt-9${
890
+ dropdownTheme === 'splitDetail'
891
+ ? ' autocomplete-suggections--split-detail'
892
+ : ''
893
+ }`}
768
894
  >
769
895
  <ul
770
896
  className={`h-auto overflow-auto w-full ${tableView ? '' : 'py-1.5'}`}
@@ -773,7 +899,9 @@ const ReactAutoCompleteTableView: React.FC<AutoSuggestionInputProps> = ({
773
899
  >
774
900
  {filteredData?.length > 0 ? (
775
901
  <>
776
- {columnHeader && columnHeader.length > 0 && (
902
+ {dropdownTheme === 'default' &&
903
+ columnHeader &&
904
+ columnHeader.length > 0 && (
777
905
  <li className="sticky top-0 auto-suggections-tableview-header z-10 bg-gray-100 border-b border-grey-light">
778
906
  <ul className="flex items-stretch w-full list-none p-0 m-0">
779
907
  {(() => {
@@ -855,9 +983,9 @@ const ReactAutoCompleteTableView: React.FC<AutoSuggestionInputProps> = ({
855
983
  index === selectedIndex ? 'is-selected' : ''
856
984
  } hover:bg-table-hover`
857
985
  } cursor-pointer text-xxs qbs-autocomplete-suggections-items ${
858
- tableView
859
- ? "border-b border-grey-light last:border-b-0"
860
- : "p-1 ps-3.5 pl-[10px]"
986
+ dropdownTheme === 'splitDetail' || tableView
987
+ ? 'border-b border-grey-light last:border-b-0'
988
+ : 'p-1 ps-3.5 pl-[10px]'
861
989
  }`}
862
990
  key={suggestion?.id}
863
991
  data-testid={suggestion.name}
@@ -865,70 +993,144 @@ const ReactAutoCompleteTableView: React.FC<AutoSuggestionInputProps> = ({
865
993
  tabIndex={index}
866
994
  ref={(el) => (itemRefs.current[index] = el)}
867
995
  >
868
- <ul className="flex items-stretch w-full list-none p-0 m-0">
869
- {(() => {
870
- // Sort columns by order if specified, otherwise maintain array order
871
- const sortedColumns = additionalColumns
872
- ? [...additionalColumns].sort((a, b) => {
873
- const orderA = a.order !== undefined ? a.order : Infinity;
874
- const orderB = b.order !== undefined ? b.order : Infinity;
875
- return orderA - orderB;
876
- })
877
- : [];
878
-
879
- // Separate columns before first column (order < 0) and after (order >= 0 or undefined)
880
- const columnsBefore = sortedColumns.filter(col => col.order !== undefined && col.order < 0);
881
- const columnsAfter = sortedColumns.filter(col => col.order === undefined || col.order >= 0);
882
-
883
- // Determine if first column needs a separator
884
- const hasColumnsAfter = columnsAfter.length > 0;
885
-
886
- return (
887
- <>
888
- {/* Columns before first column */}
889
- {columnsBefore.map((column, colIndex) => {
890
- const hasValue = suggestion?.[column.key];
891
- const isLastBefore = false;
892
- return hasValue ? (
893
- <li
894
- key={column.key}
895
- className={`${column.width ? 'flex-shrink-0' : 'flex-1'} min-w-0 px-3 ${tableView ? 'py-2' : ''} ${!isLastBefore ? 'border-r border-grey-light' : ''} break-words flex flex-col`}
896
- style={column.width ? { width: `${column.width}px` } : undefined}
897
- >
898
- <span className="block whitespace-normal">
899
- {suggestion?.[column.key]}
900
- </span>
901
- </li>
902
- ) : null;
903
- })}
904
-
905
- {/* First column (label/name) */}
906
- <li className={`flex-1 min-w-0 px-3 ${tableView ? 'py-2' : ''} ${hasColumnsAfter ? 'border-r border-grey-light' : ''} break-words flex flex-col`}>
907
- <span className="block whitespace-normal">
908
- {suggestion?.label ? suggestion?.label : suggestion.name}
996
+ {dropdownTheme === 'splitDetail' ? (
997
+ <div className="qbs-split-detail-row">
998
+ <div className="qbs-split-detail-main">
999
+ <div className="qbs-split-detail-title">
1000
+ {suggestion?.label ? suggestion?.label : suggestion.name}
1001
+ </div>
1002
+ {splitDetailOptions?.subtitleKey &&
1003
+ suggestion?.[splitDetailOptions.subtitleKey] != null &&
1004
+ String(
1005
+ suggestion[splitDetailOptions.subtitleKey]
1006
+ ).trim() !== '' && (
1007
+ <div className="qbs-split-detail-subtitle">
1008
+ {String(
1009
+ suggestion[splitDetailOptions.subtitleKey]
1010
+ )}
1011
+ </div>
1012
+ )}
1013
+ </div>
1014
+ {splitDetailOptions?.badgeKey &&
1015
+ suggestion?.[splitDetailOptions.badgeKey] != null &&
1016
+ String(suggestion[splitDetailOptions.badgeKey]).trim() !==
1017
+ '' && (
1018
+ <div className="qbs-split-detail-badge-wrap">
1019
+ <span className="qbs-split-detail-badge">
1020
+ {splitDetailOptions.badgePrefix ?? ''}
1021
+ {String(suggestion[splitDetailOptions.badgeKey])}
909
1022
  </span>
910
- </li>
911
-
912
- {/* Columns after first column */}
913
- {columnsAfter.map((column, colIndex) => {
914
- const hasValue = suggestion?.[column.key];
915
- const isLastColumn = colIndex === columnsAfter.length - 1;
916
- return hasValue ? (
917
- <li
918
- key={column.key}
919
- className={`${column.width ? 'flex-shrink-0' : 'flex-1'} min-w-0 px-3 ${tableView ? 'py-2' : ''} ${!isLastColumn ? 'border-r border-grey-light' : ''} break-words flex flex-col`}
920
- style={column.width ? { width: `${column.width}px` } : undefined}
921
- >
922
- <span className="block whitespace-normal">
923
- {suggestion?.[column.key]}
924
- </span>
925
- </li>
926
- ) : null;
927
- })}
928
- </>
929
- );
930
- })()}
931
- </ul>
1023
+ </div>
1024
+ )}
1025
+ </div>
1026
+ ) : (
1027
+ <ul className="flex items-stretch w-full list-none p-0 m-0">
1028
+ {(() => {
1029
+ // Sort columns by order if specified, otherwise maintain array order
1030
+ const sortedColumns = additionalColumns
1031
+ ? [...additionalColumns].sort((a, b) => {
1032
+ const orderA =
1033
+ a.order !== undefined ? a.order : Infinity;
1034
+ const orderB =
1035
+ b.order !== undefined ? b.order : Infinity;
1036
+ return orderA - orderB;
1037
+ })
1038
+ : [];
1039
+
1040
+ // Separate columns before first column (order < 0) and after (order >= 0 or undefined)
1041
+ const columnsBefore = sortedColumns.filter(
1042
+ (col) => col.order !== undefined && col.order < 0
1043
+ );
1044
+ const columnsAfter = sortedColumns.filter(
1045
+ (col) =>
1046
+ col.order === undefined || col.order >= 0
1047
+ );
1048
+
1049
+ // Determine if first column needs a separator
1050
+ const hasColumnsAfter = columnsAfter.length > 0;
1051
+
1052
+ return (
1053
+ <>
1054
+ {/* Columns before first column */}
1055
+ {columnsBefore.map((column, colIndex) => {
1056
+ const hasValue = suggestion?.[column.key];
1057
+ const isLastBefore = false;
1058
+ return hasValue ? (
1059
+ <li
1060
+ key={column.key}
1061
+ className={`${
1062
+ column.width ? 'flex-shrink-0' : 'flex-1'
1063
+ } min-w-0 px-3 ${
1064
+ tableView ? 'py-2' : ''
1065
+ } ${
1066
+ !isLastBefore
1067
+ ? 'border-r border-grey-light'
1068
+ : ''
1069
+ } break-words flex flex-col`}
1070
+ style={
1071
+ column.width
1072
+ ? { width: `${column.width}px` }
1073
+ : undefined
1074
+ }
1075
+ >
1076
+ <span className="block whitespace-normal">
1077
+ {suggestion?.[column.key]}
1078
+ </span>
1079
+ </li>
1080
+ ) : null;
1081
+ })}
1082
+
1083
+ {/* First column (label/name) */}
1084
+ <li
1085
+ className={`flex-1 min-w-0 px-3 ${
1086
+ tableView ? 'py-2' : ''
1087
+ } ${
1088
+ hasColumnsAfter
1089
+ ? 'border-r border-grey-light'
1090
+ : ''
1091
+ } break-words flex flex-col`}
1092
+ >
1093
+ <span className="block whitespace-normal">
1094
+ {suggestion?.label
1095
+ ? suggestion?.label
1096
+ : suggestion.name}
1097
+ </span>
1098
+ </li>
1099
+
1100
+ {/* Columns after first column */}
1101
+ {columnsAfter.map((column, colIndex) => {
1102
+ const hasValue = suggestion?.[column.key];
1103
+ const isLastColumn =
1104
+ colIndex === columnsAfter.length - 1;
1105
+ return hasValue ? (
1106
+ <li
1107
+ key={column.key}
1108
+ className={`${
1109
+ column.width ? 'flex-shrink-0' : 'flex-1'
1110
+ } min-w-0 px-3 ${
1111
+ tableView ? 'py-2' : ''
1112
+ } ${
1113
+ !isLastColumn
1114
+ ? 'border-r border-grey-light'
1115
+ : ''
1116
+ } break-words flex flex-col`}
1117
+ style={
1118
+ column.width
1119
+ ? { width: `${column.width}px` }
1120
+ : undefined
1121
+ }
1122
+ >
1123
+ <span className="block whitespace-normal">
1124
+ {suggestion?.[column.key]}
1125
+ </span>
1126
+ </li>
1127
+ ) : null;
1128
+ })}
1129
+ </>
1130
+ );
1131
+ })()}
1132
+ </ul>
1133
+ )}
932
1134
  </li>
933
1135
  ))}
934
1136
  </>
@@ -1034,8 +1236,11 @@ const ReactAutoCompleteTableView: React.FC<AutoSuggestionInputProps> = ({
1034
1236
  });
1035
1237
  }, []);
1036
1238
 
1037
- useEffect(() => {
1038
- if (!(tooltipIsHovered && showToolTip && !dropOpen && inputValue)) return;
1239
+ useLayoutEffect(() => {
1240
+ if (!(tooltipIsHovered && showToolTip && !dropOpen && inputValue)) {
1241
+ setTooltipPosition(null);
1242
+ return;
1243
+ }
1039
1244
 
1040
1245
  updateTooltipPosition();
1041
1246
  window.addEventListener('scroll', updateTooltipPosition, true);
@@ -1062,8 +1267,11 @@ const ReactAutoCompleteTableView: React.FC<AutoSuggestionInputProps> = ({
1062
1267
  });
1063
1268
  }, []);
1064
1269
 
1065
- useEffect(() => {
1066
- if (!(isHovered && errors && errors[name])) return;
1270
+ useLayoutEffect(() => {
1271
+ if (!(isHovered && errors && errors[name])) {
1272
+ setErrorTooltipPosition(null);
1273
+ return;
1274
+ }
1067
1275
  updateErrorTooltipPosition();
1068
1276
  window.addEventListener('scroll', updateErrorTooltipPosition, true);
1069
1277
  window.addEventListener('resize', updateErrorTooltipPosition);
@@ -1090,17 +1298,16 @@ const ReactAutoCompleteTableView: React.FC<AutoSuggestionInputProps> = ({
1090
1298
  </div>
1091
1299
  )}
1092
1300
  <div className="tooltip-container">
1093
- {isHovered && errors && errors[name] && (
1301
+ {isHovered && errors && errors[name] && errorTooltipPosition && (
1094
1302
  <Portal>
1095
1303
  <span
1096
1304
  className="tooltip tooltip-portal-error"
1097
1305
  style={{
1098
1306
  position: 'fixed',
1099
- top: errorTooltipPosition?.top ?? -9999,
1100
- left: errorTooltipPosition?.left ?? -9999,
1307
+ top: errorTooltipPosition.top,
1308
+ left: errorTooltipPosition.left,
1101
1309
  transform: 'translateX(-100%)',
1102
1310
  zIndex: 9999,
1103
- visibility: errorTooltipPosition ? 'visible' : 'hidden',
1104
1311
  }}
1105
1312
  >
1106
1313
  {handleError(errors)}
@@ -1111,18 +1318,17 @@ const ReactAutoCompleteTableView: React.FC<AutoSuggestionInputProps> = ({
1111
1318
  showToolTip &&
1112
1319
  !dropOpen &&
1113
1320
  inputValue &&
1114
- (
1321
+ tooltipPosition && (
1115
1322
  <Portal>
1116
1323
  <div
1117
1324
  className="tooltip-info tooltip-info-portal"
1118
1325
  style={{
1119
1326
  position: 'fixed',
1120
- top: tooltipPosition?.top ?? -9999,
1121
- left: tooltipPosition?.left ?? -9999,
1122
- maxWidth: tooltipPosition?.width ?? undefined,
1327
+ top: tooltipPosition.top,
1328
+ left: tooltipPosition.left,
1329
+ maxWidth: tooltipPosition.width,
1123
1330
  transform: 'translateX(-100%)',
1124
1331
  zIndex: 9999,
1125
- visibility: tooltipPosition ? 'visible' : 'hidden',
1126
1332
  }}
1127
1333
  >
1128
1334
  {inputValue}
@@ -1175,10 +1381,13 @@ const ReactAutoCompleteTableView: React.FC<AutoSuggestionInputProps> = ({
1175
1381
  : placeholder ?? '--Select--'
1176
1382
  }
1177
1383
  onFocus={onInputFocus}
1384
+ onKeyDown={handleSearchInputKeyDown}
1178
1385
  onClick={(e) => {
1179
1386
  if (type === 'custom_select') {
1180
1387
  setDropOpen(!dropOpen);
1181
1388
  handleOpen(e);
1389
+ } else if (searchOnButtonOnly) {
1390
+ return;
1182
1391
  } else {
1183
1392
  if (dropOpen || filteredData?.length > 0)
1184
1393
  setDropOpen(!dropOpen);
@@ -1216,7 +1425,26 @@ const ReactAutoCompleteTableView: React.FC<AutoSuggestionInputProps> = ({
1216
1425
  <CustomIcons name="close" type="large-m" />
1217
1426
  </button>
1218
1427
  )}
1219
- {isLoading && <Spinner />}
1428
+ {showSearchOnButtonChrome && (
1429
+ <button
1430
+ type="button"
1431
+ aria-label={isLoading ? 'Loading' : 'Search'}
1432
+ aria-busy={isLoading}
1433
+ data-testid="autocomplete-search-button"
1434
+ onClick={handleSearchButtonClick}
1435
+ disabled={isLoading}
1436
+ className="text-table-bodyColor text-[#667085] focus-visible:outline-slate-100 p-0.5 inline-flex items-center justify-center min-w-[28px] min-h-[28px] disabled:opacity-70"
1437
+ >
1438
+ {isLoading ? (
1439
+ <span className="inline-flex items-center justify-center [&_svg]:mr-0 [&_svg]:h-5 [&_svg]:w-5">
1440
+ <Spinner />
1441
+ </span>
1442
+ ) : (
1443
+ <CustomIcons name="search" type="medium" />
1444
+ )}
1445
+ </button>
1446
+ )}
1447
+ {isLoading && !showSearchOnButtonChrome && <Spinner />}
1220
1448
  {(type !== 'auto_complete' || autoDropdown) &&
1221
1449
  !disabled &&
1222
1450
  !readOnly && (
package/src/index.tsx CHANGED
@@ -21,3 +21,8 @@ export { default as ExpandableToolTip } from './utilities/expandableTootltip';
21
21
  export { default as ToolTip } from './utilities/tooltip';
22
22
  export { default as ModernAutoCompleteDropdown } from './ReactAutoCompleteDropdown';
23
23
  export { default as ModernAutoCompleteTableView } from './ReactAutoCompleteTableView';
24
+ export type {
25
+ DropdownThemeConfig,
26
+ DropdownThemeId,
27
+ SplitDetailThemeConfig,
28
+ } from './ReactAutoCompleteTableView';
@@ -748,6 +748,75 @@ span.dropdown-search-icon {
748
748
  flex: 0 1 auto;
749
749
  }
750
750
 
751
+ /* Split-detail dropdown theme (bank-style row: title + subtitle | badge) */
752
+ .autocomplete-suggections--split-detail .qbs-split-detail-row {
753
+ display: flex;
754
+ align-items: center;
755
+ gap: 12px;
756
+ width: 100%;
757
+ padding: 10px 12px;
758
+ box-sizing: border-box;
759
+ }
760
+
761
+ .autocomplete-suggections--split-detail .qbs-split-detail-main {
762
+ flex: 1;
763
+ min-width: 0;
764
+ }
765
+
766
+ .autocomplete-suggections--split-detail .qbs-split-detail-title {
767
+ font-size: 14px;
768
+ font-weight: 600;
769
+ color: #101828;
770
+ line-height: 1.35;
771
+ }
772
+
773
+ .autocomplete-suggections--split-detail .qbs-split-detail-subtitle {
774
+ margin-top: 4px;
775
+ font-size: 12px;
776
+ font-weight: 400;
777
+ color: #667085;
778
+ line-height: 1.45;
779
+ white-space: normal;
780
+ word-break: break-word;
781
+ }
782
+
783
+ .autocomplete-suggections--split-detail .qbs-split-detail-badge-wrap {
784
+ flex-shrink: 0;
785
+ align-self: center;
786
+ max-width: 45%;
787
+ }
788
+
789
+ .autocomplete-suggections--split-detail .qbs-split-detail-badge {
790
+ display: inline-block;
791
+ padding: 6px 10px;
792
+ font-size: 12px;
793
+ font-weight: 600;
794
+ line-height: 1.3;
795
+ color: #194185;
796
+ background: #d1e9ff;
797
+ border: 1px solid #b2ddff;
798
+ border-radius: 6px;
799
+ white-space: normal;
800
+ word-break: break-word;
801
+ }
802
+
803
+ .autocomplete-suggections--split-detail
804
+ .qbs-autocomplete-suggections-items.bg-blue-navy
805
+ .qbs-split-detail-title,
806
+ .autocomplete-suggections--split-detail
807
+ .qbs-autocomplete-suggections-items.bg-blue-navy
808
+ .qbs-split-detail-subtitle {
809
+ color: #fff;
810
+ }
811
+
812
+ .autocomplete-suggections--split-detail
813
+ .qbs-autocomplete-suggections-items.bg-blue-navy
814
+ .qbs-split-detail-badge {
815
+ color: #194185;
816
+ background: #eff8ff;
817
+ border-color: #b2ddff;
818
+ }
819
+
751
820
  /* TextField Inner Search Button */
752
821
  .inner-search-button {
753
822
  background: #f8f8f8;