@solidxai/core-ui 0.1.14 → 0.1.15

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.
Files changed (180) hide show
  1. package/dist/components/auth/AuthLayout.js +1 -1
  2. package/dist/components/auth/AuthLayout.js.map +1 -1
  3. package/dist/components/auth/AuthLayout.tsx +2 -2
  4. package/dist/components/common/DropzonePlaceholder.d.ts +2 -1
  5. package/dist/components/common/DropzonePlaceholder.d.ts.map +1 -1
  6. package/dist/components/common/DropzonePlaceholder.js +10 -7
  7. package/dist/components/common/DropzonePlaceholder.js.map +1 -1
  8. package/dist/components/common/DropzonePlaceholder.tsx +14 -9
  9. package/dist/components/common/SettingsComponent.module.css +4 -1
  10. package/dist/components/common/SolidFormStepper.d.ts.map +1 -1
  11. package/dist/components/common/SolidFormStepper.js +7 -6
  12. package/dist/components/common/SolidFormStepper.js.map +1 -1
  13. package/dist/components/common/SolidFormStepper.tsx +7 -1
  14. package/dist/components/common/SolidThemeProvider.d.ts.map +1 -1
  15. package/dist/components/common/SolidThemeProvider.js +127 -11
  16. package/dist/components/common/SolidThemeProvider.js.map +1 -1
  17. package/dist/components/common/SolidThemeProvider.tsx +104 -22
  18. package/dist/components/core/card/CardGrid.d.ts +1 -1
  19. package/dist/components/core/card/CardGrid.d.ts.map +1 -1
  20. package/dist/components/core/card/CardGrid.js +2 -2
  21. package/dist/components/core/card/CardGrid.js.map +1 -1
  22. package/dist/components/core/card/CardGrid.tsx +4 -0
  23. package/dist/components/core/card/CardItem.d.ts +2 -0
  24. package/dist/components/core/card/CardItem.d.ts.map +1 -1
  25. package/dist/components/core/card/CardItem.js +5 -4
  26. package/dist/components/core/card/CardItem.js.map +1 -1
  27. package/dist/components/core/card/CardItem.tsx +16 -1
  28. package/dist/components/core/card/SolidCardView.d.ts +28 -1
  29. package/dist/components/core/card/SolidCardView.d.ts.map +1 -1
  30. package/dist/components/core/card/SolidCardView.js +99 -44
  31. package/dist/components/core/card/SolidCardView.js.map +1 -1
  32. package/dist/components/core/card/SolidCardView.tsx +119 -7
  33. package/dist/components/core/card/SolidCardViewConfigure.d.ts +1 -1
  34. package/dist/components/core/card/SolidCardViewConfigure.d.ts.map +1 -1
  35. package/dist/components/core/card/SolidCardViewConfigure.js +15 -6
  36. package/dist/components/core/card/SolidCardViewConfigure.js.map +1 -1
  37. package/dist/components/core/card/SolidCardViewConfigure.tsx +34 -0
  38. package/dist/components/core/common/PDFViewer.d.ts +9 -5
  39. package/dist/components/core/common/PDFViewer.d.ts.map +1 -1
  40. package/dist/components/core/common/PDFViewer.js +10 -102
  41. package/dist/components/core/common/PDFViewer.js.map +1 -1
  42. package/dist/components/core/common/PDFViewer.tsx +29 -88
  43. package/dist/components/core/common/SolidCreateButton.d.ts.map +1 -1
  44. package/dist/components/core/common/SolidCreateButton.js +2 -2
  45. package/dist/components/core/common/SolidCreateButton.js.map +1 -1
  46. package/dist/components/core/common/SolidCreateButton.tsx +22 -2
  47. package/dist/components/core/common/SolidGlobalSearchElement.d.ts.map +1 -1
  48. package/dist/components/core/common/SolidGlobalSearchElement.js +216 -198
  49. package/dist/components/core/common/SolidGlobalSearchElement.js.map +1 -1
  50. package/dist/components/core/common/SolidGlobalSearchElement.tsx +287 -216
  51. package/dist/components/core/form/SolidFormActionHeader.d.ts.map +1 -1
  52. package/dist/components/core/form/SolidFormActionHeader.js +21 -21
  53. package/dist/components/core/form/SolidFormActionHeader.js.map +1 -1
  54. package/dist/components/core/form/SolidFormActionHeader.tsx +62 -53
  55. package/dist/components/core/form/SolidFormView.d.ts.map +1 -1
  56. package/dist/components/core/form/SolidFormView.js +101 -74
  57. package/dist/components/core/form/SolidFormView.js.map +1 -1
  58. package/dist/components/core/form/SolidFormView.tsx +46 -19
  59. package/dist/components/core/form/fields/SolidMediaMultipleField.d.ts.map +1 -1
  60. package/dist/components/core/form/fields/SolidMediaMultipleField.js +3 -2
  61. package/dist/components/core/form/fields/SolidMediaMultipleField.js.map +1 -1
  62. package/dist/components/core/form/fields/SolidMediaMultipleField.tsx +3 -1
  63. package/dist/components/core/form/fields/SolidMediaSingleField.d.ts.map +1 -1
  64. package/dist/components/core/form/fields/SolidMediaSingleField.js +3 -2
  65. package/dist/components/core/form/fields/SolidMediaSingleField.js.map +1 -1
  66. package/dist/components/core/form/fields/SolidMediaSingleField.tsx +3 -1
  67. package/dist/components/core/form/fields/relations/SolidRelationManyToManyField.d.ts +1 -0
  68. package/dist/components/core/form/fields/relations/SolidRelationManyToManyField.d.ts.map +1 -1
  69. package/dist/components/core/form/fields/relations/SolidRelationManyToManyField.js +20 -2
  70. package/dist/components/core/form/fields/relations/SolidRelationManyToManyField.js.map +1 -1
  71. package/dist/components/core/form/fields/relations/SolidRelationManyToManyField.tsx +29 -2
  72. package/dist/components/core/form/fields/solidFields.module.css +7 -0
  73. package/dist/components/core/kanban/KanbanBoard.d.ts +1 -1
  74. package/dist/components/core/kanban/KanbanBoard.d.ts.map +1 -1
  75. package/dist/components/core/kanban/KanbanBoard.js +2 -2
  76. package/dist/components/core/kanban/KanbanBoard.js.map +1 -1
  77. package/dist/components/core/kanban/KanbanBoard.tsx +3 -1
  78. package/dist/components/core/kanban/KanbanCard.d.ts +2 -0
  79. package/dist/components/core/kanban/KanbanCard.d.ts.map +1 -1
  80. package/dist/components/core/kanban/KanbanCard.js +5 -3
  81. package/dist/components/core/kanban/KanbanCard.js.map +1 -1
  82. package/dist/components/core/kanban/KanbanCard.tsx +15 -2
  83. package/dist/components/core/kanban/KanbanColumn.d.ts +3 -1
  84. package/dist/components/core/kanban/KanbanColumn.d.ts.map +1 -1
  85. package/dist/components/core/kanban/KanbanColumn.js +2 -2
  86. package/dist/components/core/kanban/KanbanColumn.js.map +1 -1
  87. package/dist/components/core/kanban/KanbanColumn.tsx +4 -2
  88. package/dist/components/core/kanban/SolidKanbanView.d.ts +25 -1
  89. package/dist/components/core/kanban/SolidKanbanView.d.ts.map +1 -1
  90. package/dist/components/core/kanban/SolidKanbanView.js +95 -50
  91. package/dist/components/core/kanban/SolidKanbanView.js.map +1 -1
  92. package/dist/components/core/kanban/SolidKanbanView.tsx +87 -9
  93. package/dist/components/core/list/PLAN.md +1 -1
  94. package/dist/components/core/list/SolidListView.d.ts +11 -0
  95. package/dist/components/core/list/SolidListView.d.ts.map +1 -1
  96. package/dist/components/core/list/SolidListView.js +51 -90
  97. package/dist/components/core/list/SolidListView.js.map +1 -1
  98. package/dist/components/core/list/SolidListView.tsx +77 -112
  99. package/dist/components/core/list/SolidListViewColumn.d.ts +2 -1
  100. package/dist/components/core/list/SolidListViewColumn.d.ts.map +1 -1
  101. package/dist/components/core/list/SolidListViewColumn.js +2 -2
  102. package/dist/components/core/list/SolidListViewColumn.js.map +1 -1
  103. package/dist/components/core/list/SolidListViewColumn.tsx +4 -3
  104. package/dist/components/core/list/SolidListViewConfigure.js +1 -1
  105. package/dist/components/core/list/SolidListViewConfigure.js.map +1 -1
  106. package/dist/components/core/list/SolidListViewConfigure.tsx +1 -1
  107. package/dist/components/core/list/columns/SolidRelationColumn.d.ts +1 -1
  108. package/dist/components/core/list/columns/SolidRelationColumn.d.ts.map +1 -1
  109. package/dist/components/core/list/columns/SolidRelationColumn.js +2 -2
  110. package/dist/components/core/list/columns/SolidRelationColumn.js.map +1 -1
  111. package/dist/components/core/list/columns/SolidRelationColumn.tsx +2 -2
  112. package/dist/components/core/list/columns/relations/SolidRelationManyToOneColumn.d.ts +2 -2
  113. package/dist/components/core/list/columns/relations/SolidRelationManyToOneColumn.d.ts.map +1 -1
  114. package/dist/components/core/list/columns/relations/SolidRelationManyToOneColumn.js +5 -4
  115. package/dist/components/core/list/columns/relations/SolidRelationManyToOneColumn.js.map +1 -1
  116. package/dist/components/core/list/columns/relations/SolidRelationManyToOneColumn.tsx +5 -4
  117. package/dist/components/core/model/CreateModel.d.ts.map +1 -1
  118. package/dist/components/core/model/CreateModel.js +2 -1
  119. package/dist/components/core/model/CreateModel.js.map +1 -1
  120. package/dist/components/core/model/CreateModel.tsx +2 -0
  121. package/dist/components/core/model/FieldMetaData.d.ts +1 -1
  122. package/dist/components/core/model/FieldMetaData.d.ts.map +1 -1
  123. package/dist/components/core/model/FieldMetaData.js +8 -8
  124. package/dist/components/core/model/FieldMetaData.js.map +1 -1
  125. package/dist/components/core/model/FieldMetaData.tsx +2 -2
  126. package/dist/components/core/model/FieldMetaDataForm.css +119 -0
  127. package/dist/components/core/model/FieldMetaDataForm.d.ts.map +1 -1
  128. package/dist/components/core/model/FieldMetaDataForm.js +147 -97
  129. package/dist/components/core/model/FieldMetaDataForm.js.map +1 -1
  130. package/dist/components/core/model/FieldMetaDataForm.tsx +124 -32
  131. package/dist/components/core/module/ModuleMetadataExplorer.css +64 -0
  132. package/dist/components/core/module/ModulePackageDialog.css +117 -12
  133. package/dist/components/core/tree/SolidTreeView.d.ts.map +1 -1
  134. package/dist/components/core/tree/SolidTreeView.js +6 -3
  135. package/dist/components/core/tree/SolidTreeView.js.map +1 -1
  136. package/dist/components/core/tree/SolidTreeView.tsx +27 -13
  137. package/dist/components/layout/AdminHeaderActions.d.ts.map +1 -1
  138. package/dist/components/layout/AdminHeaderActions.js +16 -11
  139. package/dist/components/layout/AdminHeaderActions.js.map +1 -1
  140. package/dist/components/layout/AdminHeaderActions.tsx +16 -9
  141. package/dist/components/layout/AdminTopHeader.d.ts.map +1 -1
  142. package/dist/components/layout/AdminTopHeader.js +1 -18
  143. package/dist/components/layout/AdminTopHeader.js.map +1 -1
  144. package/dist/components/layout/AdminTopHeader.tsx +3 -21
  145. package/dist/helpers/getAcceptedFileTypes.d.ts +12 -2
  146. package/dist/helpers/getAcceptedFileTypes.d.ts.map +1 -1
  147. package/dist/helpers/getAcceptedFileTypes.js +72 -16
  148. package/dist/helpers/getAcceptedFileTypes.js.map +1 -1
  149. package/dist/helpers/getAcceptedFileTypes.tsx +101 -18
  150. package/dist/helpers/registry.js +2 -1
  151. package/dist/helpers/registry.js.map +1 -1
  152. package/dist/helpers/registry.ts +3 -1
  153. package/dist/index.d.ts +6 -1
  154. package/dist/index.d.ts.map +1 -1
  155. package/dist/index.js +3 -0
  156. package/dist/index.js.map +1 -1
  157. package/dist/index.ts +6 -2
  158. package/dist/resources/globals.css +274 -109
  159. package/dist/resources/solid-custom.css +11 -0
  160. package/dist/resources/solid-responsive.css +1 -5
  161. package/dist/resources/solid-tailwind-generated.css +1 -1
  162. package/dist/routes/pages/admin/core/CardPage.d.ts.map +1 -1
  163. package/dist/routes/pages/admin/core/CardPage.js +15 -2
  164. package/dist/routes/pages/admin/core/CardPage.js.map +1 -1
  165. package/dist/routes/pages/admin/core/CardPage.tsx +17 -2
  166. package/dist/routes/pages/admin/core/DashboardPage.d.ts.map +1 -1
  167. package/dist/routes/pages/admin/core/DashboardPage.js +31 -3
  168. package/dist/routes/pages/admin/core/DashboardPage.js.map +1 -1
  169. package/dist/routes/pages/admin/core/DashboardPage.tsx +39 -11
  170. package/dist/routes/pages/admin/core/KanbanPage.d.ts.map +1 -1
  171. package/dist/routes/pages/admin/core/KanbanPage.js +15 -2
  172. package/dist/routes/pages/admin/core/KanbanPage.js.map +1 -1
  173. package/dist/routes/pages/admin/core/KanbanPage.tsx +17 -2
  174. package/dist/routes/pages/admin/core/ListPage.d.ts.map +1 -1
  175. package/dist/routes/pages/admin/core/ListPage.js +5 -13
  176. package/dist/routes/pages/admin/core/ListPage.js.map +1 -1
  177. package/dist/routes/pages/admin/core/ListPage.tsx +5 -15
  178. package/dist/types/index.d.ts +2 -4
  179. package/dist/types/solid-core.d.ts +12 -13
  180. package/package.json +1 -1
@@ -1,10 +1,7 @@
1
- import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
1
+ import React, { forwardRef, useCallback, useEffect, useId, useImperativeHandle, useMemo, useRef, useState } from "react";
2
2
  import FilterComponent, { FilterOperator, FilterRule, FilterRuleType } from "../../../components/core/common/FilterComponent";
3
3
  import { toTitleCase } from "../../../helpers/helpers";
4
- import { usePathname } from "../../../hooks/usePathname";
5
- import { useRouter } from "../../../hooks/useRouter";
6
- import { useSearchParams } from "../../../hooks/useSearchParams";
7
- import {clearFilterObjectFromLocalStorage,getFilterObjectFromLocalStorage,
4
+ import {clearFilterObjectFromLocalStorage,getFilterObjectFromLocalStorage,setFilterObjectToLocalStorage,
8
5
  } from "./globalSearchPersistence";
9
6
  import { createSolidEntityApi } from "../../../redux/api/solidEntityApi";
10
7
  import qs from "qs";
@@ -12,8 +9,9 @@ import { SolidSaveCustomFilterForm } from "./SolidSaveCustomFilterForm";
12
9
  import { ERROR_MESSAGES } from "../../../constants/error-messages";
13
10
  import { hydrateRelationRules } from "../../../helpers/hydrateRelationRules";
14
11
  import { useSession } from '../../../hooks/useSession'
12
+ import { getUnboundSavedFilterVariableNames, parseSavedFilterQueryJson, resolveSavedFilterVariables } from '../../../helpers/resolveActiveUserId';
15
13
  import GroupingComponent, { AggregationRule, GroupingRule, DateGroupingFormat } from "./GroupingComponent";
16
- import { Bookmark, Check, Filter, ListFilter, Pencil, Plus, Search, SearchX, Trash2, X } from "lucide-react";
14
+ import { Bookmark, Check, Filter, Pencil, Plus, Search, SearchX, Sparkles, Trash2, X } from "lucide-react";
17
15
  import { SolidButton } from "../../shad-cn-ui/SolidButton";
18
16
  import { SolidInput } from "../../shad-cn-ui/SolidInput";
19
17
  import {
@@ -420,8 +418,10 @@ export const mergeAllDiffFilters = (customFilter: any, searchFilter: any, savedF
420
418
  return filters;
421
419
  }
422
420
 
423
- const SavedFilterList = ({ savedfilter, activeSavedFilter, applySavedFilter, openSavedCustomFilter, setSavedFilterTobeDeleted, setIsDeleteSQDialogVisible, isFocused, onMouseEnter }: any) => {
424
- const isActive = savedfilter?.systemKey ? activeSavedFilter === savedfilter.systemKey : Number(activeSavedFilter) == savedfilter.id;
421
+ const SavedFilterList = ({ savedfilter, activeSavedFilterReference, applySavedFilter, openSavedCustomFilter, setSavedFilterTobeDeleted, setIsDeleteSQDialogVisible, isFocused, onMouseEnter, optionId }: any) => {
422
+ const isActive = savedfilter?.systemKey
423
+ ? activeSavedFilterReference === savedfilter.systemKey
424
+ : String(activeSavedFilterReference) === String(savedfilter.id);
425
425
 
426
426
  return (
427
427
  <div className="solid-saved-filter-item" onMouseEnter={onMouseEnter}>
@@ -430,13 +430,32 @@ const SavedFilterList = ({ savedfilter, activeSavedFilter, applySavedFilter, ope
430
430
  variant="ghost"
431
431
  size="sm"
432
432
  className={`solid-saved-filter-main w-full ${isActive ? "is-active" : ""} ${isFocused ? "solid-search-overlay-option-active" : ""}`}
433
+ id={optionId}
434
+ role="option"
435
+ aria-selected={isFocused}
436
+ aria-label={`${savedfilter.name}${isActive ? ", applied" : ""}`}
433
437
  onMouseDown={(e) => {
434
438
  e.preventDefault();
435
439
  applySavedFilter(savedfilter);
436
440
  }}
437
441
  title={savedfilter?.description}
438
442
  >
439
- {savedfilter.name}
443
+ <span className="solid-search-overlay-option-content">
444
+ <span className="solid-search-overlay-option-icon" aria-hidden="true">
445
+ <Bookmark size={15} />
446
+ </span>
447
+ <span className="solid-search-overlay-option-copy">
448
+ <span className="solid-search-overlay-option-title">{savedfilter.name}</span>
449
+ {savedfilter?.description && (
450
+ <span className="solid-search-overlay-option-description">{savedfilter.description}</span>
451
+ )}
452
+ </span>
453
+ {isActive && (
454
+ <span className="solid-search-overlay-option-check" aria-hidden="true">
455
+ <Check size={14} />
456
+ </span>
457
+ )}
458
+ </span>
440
459
  </SolidButton>
441
460
  </div>
442
461
  <div className="solid-saved-filter-actions">
@@ -450,6 +469,7 @@ const SavedFilterList = ({ savedfilter, activeSavedFilter, applySavedFilter, ope
450
469
  e.preventDefault();
451
470
  openSavedCustomFilter(savedfilter);
452
471
  }}
472
+ aria-label={`Edit ${savedfilter.name}`}
453
473
  >
454
474
  <Pencil size={14} />
455
475
  </SolidButton>
@@ -462,6 +482,7 @@ const SavedFilterList = ({ savedfilter, activeSavedFilter, applySavedFilter, ope
462
482
  setSavedFilterTobeDeleted(savedfilter.id),
463
483
  setIsDeleteSQDialogVisible(true);
464
484
  }}
485
+ aria-label={`Delete ${savedfilter.name}`}
465
486
  >
466
487
  <Trash2 size={14} />
467
488
  </SolidButton>
@@ -550,16 +571,19 @@ type RelationCache = Map<string, { label: string; value: number }>;
550
571
 
551
572
 
552
573
 
553
- export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handleApplyCustomFilter, showSaveFilterPopup, setShowSaveFilterPopup, filterPredicates, definedFilters, onRemoveDefinedFilter, onApplyDefinedFilter }: any, ref) => {
574
+ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handleApplyCustomFilter, showSaveFilterPopup, setShowSaveFilterPopup, filterPredicates }: any, ref) => {
554
575
  type OverlayOption =
555
576
  | { id: string; kind: "field"; field: any }
556
577
  | { id: string; kind: "predefined"; predefined: any }
557
578
  | { id: string; kind: "saved"; saved: any }
558
579
  | { id: string; kind: "custom" }
559
580
  | { id: string; kind: "grouping" };
581
+ const overlayInstanceId = useId();
582
+ const overlayListboxId = `${overlayInstanceId}-search-options`;
583
+ const getOverlayOptionId = (index: number) => `${overlayInstanceId}-search-option-${index}`;
560
584
  type ManagedChipItem = {
561
585
  id: string;
562
- type: "saved" | "search" | "predefined" | "custom" | "grouping" | "handler";
586
+ type: "saved" | "search" | "predefined" | "custom" | "grouping";
563
587
  label: string;
564
588
  onRemove: () => void;
565
589
  onOpen?: () => void;
@@ -608,18 +632,11 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
608
632
  ];
609
633
 
610
634
  const [initialState, setInitialState] = useState(defaultState);
611
- const pathname = usePathname();
612
635
  const searchableEntityLabel =
613
636
  viewData?.data?.solidView?.displayName ||
614
637
  viewData?.data?.solidView?.model?.displayName ||
615
638
  "records";
616
639
 
617
-
618
- const searchParams = useSearchParams() // Converts the query params to a string
619
- const activeSavedFilter = searchParams?.get("savedQuery");
620
-
621
- const router = useRouter();
622
-
623
640
  const chipsRef = useRef<HTMLDivElement | null | any>(null);
624
641
 
625
642
  // filterRules is used to maintian the ui of custom filter
@@ -662,6 +679,7 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
662
679
  // currentSavedFilterData is used to save the whole object of saved filter
663
680
  const [currentSavedFilterData, setCurrentSavedFilterData] = useState<any>();
664
681
  const [currentSavedFilterQuery, setCurrentSavedFilterQuery] = useState<any>();
682
+ const [currentSavedFilterVariables, setCurrentSavedFilterVariables] = useState<Record<string, any>>({});
665
683
  const [currentSavedFilterRules, setCurrentSavedFilterRules] = useState<any>();
666
684
  const [showSavedFilterComponent, setShowSavedFilterComponent] = useState<boolean>(false);
667
685
 
@@ -684,7 +702,7 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
684
702
 
685
703
  useEffect(() => {
686
704
  if (focusedIndex >= 0 && showOverlay) {
687
- const activeElement = document.querySelector(`.solid-search-overlay-option-active`);
705
+ const activeElement = overlayRef.current?.querySelector(`.solid-search-overlay-option-active`);
688
706
  if (activeElement) {
689
707
  activeElement.scrollIntoView({
690
708
  block: 'nearest',
@@ -720,12 +738,23 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
720
738
  };
721
739
  }, [isDraftPublishWorkflowEnabled]);
722
740
 
723
- const availableSavedFilters = useMemo(
741
+ const allAvailableSavedFilters = useMemo(
724
742
  () => workflowPublishedVersionsSavedFilter
725
743
  ? [workflowPublishedVersionsSavedFilter, ...(savedFilters || [])]
726
744
  : (savedFilters || []),
727
745
  [workflowPublishedVersionsSavedFilter, savedFilters]
728
746
  );
747
+ const availableSavedFilters = allAvailableSavedFilters;
748
+ const selectableSavedFilters = useMemo(
749
+ () => allAvailableSavedFilters.filter((filter: any) => {
750
+ try {
751
+ return getUnboundSavedFilterVariableNames(parseSavedFilterQueryJson(filter.filterQueryJson || "{}")).length === 0;
752
+ } catch {
753
+ return false;
754
+ }
755
+ }),
756
+ [allAvailableSavedFilters]
757
+ );
729
758
 
730
759
  const entityApi = createSolidEntityApi("savedFilters");
731
760
  const {
@@ -755,20 +784,6 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
755
784
 
756
785
  const [savedFilterFetchDataRefreshKey, setSavedFilterFetchDataRefreshKey] = useState(0);
757
786
 
758
- const buildSavedFilterUrl = (savedQueryId?: string | number | null) => {
759
- const params = new URLSearchParams(searchParams.toString());
760
-
761
- if (savedQueryId === null || savedQueryId === undefined || savedQueryId === "") {
762
- params.delete("savedQuery");
763
- } else {
764
- params.set("savedQuery", String(savedQueryId));
765
- }
766
-
767
- const queryString = params.toString();
768
- return queryString ? `${pathname}?${queryString}` : pathname;
769
- };
770
-
771
-
772
787
  useEffect(() => {
773
788
  const fn = async () => {
774
789
  if (!viewData?.data?.solidView?.model?.id || !viewData?.data?.solidView?.id || !user?.id) {
@@ -814,7 +829,6 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
814
829
  }
815
830
  fn()
816
831
  }, [
817
- activeSavedFilter,
818
832
  savedFilterFetchDataRefreshKey,
819
833
  viewData?.data?.solidView?.id,
820
834
  viewData?.data?.solidView?.model?.id,
@@ -829,7 +843,12 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
829
843
  ? queryObject.custom_filter_predicate
830
844
  : null;
831
845
  const nextPredefinedBaseFilter = queryObject?.predefined_search_predicate || null;
846
+ const nextPredefinedSearchChip =
847
+ nextPredefinedBaseFilter && queryObject?.predefined_search_chip
848
+ ? queryObject.predefined_search_chip
849
+ : null;
832
850
  const nextSavedFilter = queryObject?.saved_filter_predicate || null;
851
+ const nextSavedFilterVariables = queryObject?.saved_filter_variables ?? {};
833
852
  const nextSavedFilterData = nextSavedFilter
834
853
  ? availableSavedFilters.find((savedFilter: any) => {
835
854
  if (queryObject?.saved_filter_system_key) {
@@ -851,8 +870,10 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
851
870
  setSearchChips((prev) => areFilterStateValuesEqual(prev, nextSearchChips) ? prev : nextSearchChips);
852
871
  setCustomFilter((prev: any) => areFilterStateValuesEqual(prev, nextCustomFilter) ? prev : nextCustomFilter);
853
872
  setPredefinedSearchBaseFilter((prev: any) => areFilterStateValuesEqual(prev, nextPredefinedBaseFilter) ? prev : nextPredefinedBaseFilter);
873
+ setPredefinedSearchChip((prev) => areFilterStateValuesEqual(prev, nextPredefinedSearchChip) ? prev : nextPredefinedSearchChip);
854
874
  setCurrentSavedFilterData((prev: any) => areFilterStateValuesEqual(prev, nextSavedFilterData) ? prev : nextSavedFilterData);
855
875
  setCurrentSavedFilterQuery((prev: any) => areFilterStateValuesEqual(prev, nextSavedFilter) ? prev : nextSavedFilter);
876
+ setCurrentSavedFilterVariables((prev) => areFilterStateValuesEqual(prev, nextSavedFilterVariables) ? prev : nextSavedFilterVariables);
856
877
 
857
878
  if (nextCustomFilter && viewData?.data) {
858
879
  const rules: FilterRule = transformFiltersToRules(nextCustomFilter);
@@ -899,6 +920,7 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
899
920
  setPredefinedSearchBaseFilter(null);
900
921
  setCurrentSavedFilterData(null);
901
922
  setCurrentSavedFilterQuery(null);
923
+ setCurrentSavedFilterVariables({});
902
924
  setCurrentSavedFilterRules(null);
903
925
  if (!preserveGrouping) {
904
926
  setGroupingRules(defaultGroupingRules);
@@ -909,13 +931,6 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
909
931
  setInputValue("");
910
932
  clearFilterObjectFromLocalStorage();
911
933
 
912
- if (activeSavedFilter) {
913
- const params = new URLSearchParams(searchParams.toString());
914
- params.delete("savedQuery");
915
- const nextUrl = params.toString() ? `${pathname}?${params.toString()}` : pathname;
916
- router.replace(nextUrl);
917
- }
918
-
919
934
  setHasSearched(true);
920
935
  setRefreshKey((prev) => prev + 1);
921
936
  };
@@ -924,6 +939,15 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
924
939
  clearFilter: () => {
925
940
  resetAppliedFilters();
926
941
  },
942
+ getSavedFilters: () => availableSavedFilters.map((filter: any) => ({ ...filter })),
943
+ applySavedFilterByName: (name: string, variables: Record<string, any> = {}) => {
944
+ const savedFilter = allAvailableSavedFilters.find((filter: any) => filter?.name === name);
945
+ if (!savedFilter) {
946
+ return false;
947
+ }
948
+
949
+ return applySavedFilter(savedFilter, variables);
950
+ },
927
951
  clearAppliedFilters: (options?: { preserveGrouping?: boolean }) => {
928
952
  resetAppliedFilters(options);
929
953
  },
@@ -945,35 +969,15 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
945
969
  useEffect(() => {
946
970
  const fn = async () => {
947
971
  if (savedFiltersLoaded) {
948
-
949
- if (activeSavedFilter && availableSavedFilters.length === 0) return;
950
-
951
972
  const queryObject = getFilterObjectFromLocalStorage();
952
- let nextQueryObject = queryObject;
953
- if (activeSavedFilter) {
954
- const currentSavedFilterId = Number(activeSavedFilter);
955
- const currentSavedFilterData: any = availableSavedFilters.find((savedFilter: any) => savedFilter.id === currentSavedFilterId);
956
- if (currentSavedFilterData) {
957
- const filterJson = JSON.parse(currentSavedFilterData?.filterQueryJson);
958
- if (filterJson) {
959
- nextQueryObject = {
960
- ...(queryObject || {}),
961
- saved_filter_predicate: filterJson,
962
- saved_filter_id: currentSavedFilterData.id,
963
- saved_filter_system_key: currentSavedFilterData.systemKey,
964
- saved_filter_name: currentSavedFilterData.name,
965
- };
966
- }
967
- }
968
- }
969
- await syncFilterUiStateFromSharedSource(nextQueryObject);
973
+ await syncFilterUiStateFromSharedSource(queryObject);
970
974
 
971
975
  setRefreshKey((prev) => prev + 1)
972
976
  setHasSearched(true);
973
977
  }
974
978
  }
975
979
  fn()
976
- }, [activeSavedFilter, availableSavedFilters, savedFiltersLoaded, syncFilterUiStateFromSharedSource, viewData?.data?.solidView?.id])
980
+ }, [availableSavedFilters, savedFiltersLoaded, syncFilterUiStateFromSharedSource, viewData?.data?.solidView?.id])
977
981
 
978
982
  useEffect(() => {
979
983
  const fn = async () => {
@@ -1263,6 +1267,16 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
1263
1267
 
1264
1268
  const finalCustomFilter = customFilter
1265
1269
  const finalFilter = mergeAllDiffFilters(finalCustomFilter, finalSearchFilter, finalSavedFilter, finalPredefinedFilter, groupingRules, aggregationRules)
1270
+ finalFilter.predefined_search_chip =
1271
+ finalPredefinedFilter && predefinedSearchChip
1272
+ ? predefinedSearchChip
1273
+ : null;
1274
+ if (finalSavedFilter && Object.keys(finalSavedFilter).length > 0) {
1275
+ const resolved = resolveSavedFilterVariables(finalSavedFilter, user?.id, currentSavedFilterVariables);
1276
+ if (resolved.missingVariables.length > 0) return;
1277
+ finalFilter.resolved_saved_filter_predicate = resolved.value;
1278
+ finalFilter.saved_filter_variables = currentSavedFilterVariables;
1279
+ }
1266
1280
  if (currentSavedFilterData?.id) {
1267
1281
  finalFilter.saved_filter_id = currentSavedFilterData.id;
1268
1282
  }
@@ -1291,7 +1305,6 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
1291
1305
 
1292
1306
  const openSavedCustomFilter = async (savedfilter: any) => {
1293
1307
  //Open custom filter popup
1294
- // router.push(`?savedQuery=${savedfilter.id}`);
1295
1308
  // setShowGlobalSearchElement(true);
1296
1309
  // // dont refetch the data yet
1297
1310
  // const customFilter = JSON.parse(savedfilter.filterQueryJson);
@@ -1309,15 +1322,13 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
1309
1322
  setPredefinedSearchChip(null);
1310
1323
  setPredefinedSearchBaseFilter(null);
1311
1324
 
1312
- // push the savedQuery=1 in url
1313
1325
  if (savedfilter?.id) {
1314
1326
  const savedfilterId = savedfilter.id;
1315
- // router.push(`?savedQuery=${savedfilter.id}`);
1316
1327
  setShowOverlay(false);
1317
1328
  const currentSavedFilterData: any = availableSavedFilters.find((savedFilter: any) => savedFilter.id === savedfilterId);
1318
1329
  setCurrentSavedFilterData(currentSavedFilterData);
1319
1330
  if (currentSavedFilterData) {
1320
- const filterJson = JSON.parse(currentSavedFilterData?.filterQueryJson);
1331
+ const filterJson = parseSavedFilterQueryJson(currentSavedFilterData?.filterQueryJson);
1321
1332
  if (filterJson) {
1322
1333
  let finalSavedFilter = filterJson
1323
1334
  setCurrentSavedFilterQuery(finalSavedFilter)
@@ -1335,14 +1346,50 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
1335
1346
  }
1336
1347
  }
1337
1348
 
1349
+ const persistActiveSavedFilter = (savedFilter: any, filterJson: any, variables: Record<string, any>) => {
1350
+ const persistedFilter = {
1351
+ ...(getFilterObjectFromLocalStorage() || {}),
1352
+ custom_filter_predicate: null,
1353
+ search_predicate: null,
1354
+ predefined_search_predicate: null,
1355
+ predefined_search_chip: null,
1356
+ saved_filter_predicate: filterJson,
1357
+ saved_filter_variables: variables,
1358
+ saved_filter_id: savedFilter?.id ?? null,
1359
+ saved_filter_system_key: savedFilter?.systemKey ?? null,
1360
+ saved_filter_name: savedFilter?.name ?? null,
1361
+ };
1362
+ delete persistedFilter.finalFullFilter;
1363
+ setFilterObjectToLocalStorage(persistedFilter);
1364
+ };
1365
+
1366
+ const clearActiveSavedFilter = () => {
1367
+ setCurrentSavedFilterData(null);
1368
+ setCurrentSavedFilterQuery(null);
1369
+ setCurrentSavedFilterVariables({});
1370
+ setCurrentSavedFilterRules(null);
1371
+ setShowSavedFilterComponent(false);
1372
+
1373
+ const persistedFilter = {
1374
+ ...(getFilterObjectFromLocalStorage() || {}),
1375
+ saved_filter_predicate: null,
1376
+ saved_filter_variables: {},
1377
+ saved_filter_id: null,
1378
+ saved_filter_system_key: null,
1379
+ saved_filter_name: null,
1380
+ };
1381
+ delete persistedFilter.finalFullFilter;
1382
+ setFilterObjectToLocalStorage(persistedFilter);
1383
+
1384
+ setHasSearched(true);
1385
+ setRefreshKey((prev) => prev + 1);
1386
+ };
1387
+
1338
1388
  const deleteSavedFilter = async () => {
1339
1389
  // delte the saved filter with id
1340
1390
  await deleteEntity(savedFilterTobeDeleted);
1341
- // triggerGetSolidEntities(savedFilterQueryString);
1342
- let parsedSearchParams = searchParams;
1343
- const savedQuery = parsedSearchParams?.get("savedQuery");
1344
- if (savedFilterTobeDeleted == savedQuery) {
1345
- router.push(buildSavedFilterUrl(null));
1391
+ if (String(savedFilterTobeDeleted) === String(currentSavedFilterData?.id)) {
1392
+ clearActiveSavedFilter();
1346
1393
  }
1347
1394
  setIsDeleteSQDialogVisible(false);
1348
1395
  setTimeout(() => {
@@ -1371,8 +1418,6 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
1371
1418
  setCustomFilter(null)
1372
1419
  setPredefinedSearchChip(null);
1373
1420
  setPredefinedSearchBaseFilter(null);
1374
- clearFilterObjectFromLocalStorage();
1375
-
1376
1421
  // Apply the rename immediately, both to the active filter and to
1377
1422
  // the cached list — otherwise the list still holds the pre-rename
1378
1423
  // entry, which wins the id-based lookup the next time saved-filter
@@ -1382,12 +1427,9 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
1382
1427
  setSavedFilters((prev) => prev.map((f: any) => String(f?.id) === String(formValues.id) ? { ...f, ...savedFilterRecord } : f));
1383
1428
  setCurrentSavedFilterData(savedFilterRecord);
1384
1429
  setCurrentSavedFilterQuery(filterJson);
1430
+ persistActiveSavedFilter(savedFilterRecord, filterJson, currentSavedFilterVariables);
1385
1431
  setHasSearched(true);
1386
1432
  setRefreshKey((prev) => prev + 1);
1387
-
1388
- setTimeout(() => {
1389
- router.push(buildSavedFilterUrl(formValues.id));
1390
- }, 500)
1391
1433
  } else {
1392
1434
 
1393
1435
  const filterJson = customFilter;
@@ -1406,21 +1448,16 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
1406
1448
  setCustomFilter(null)
1407
1449
  setPredefinedSearchChip(null);
1408
1450
  setPredefinedSearchBaseFilter(null);
1409
- clearFilterObjectFromLocalStorage();
1410
-
1411
1451
  // The custom filter just became this saved filter — apply it, and
1412
1452
  // seed the cached list with it, immediately (see rename branch above).
1413
1453
  const savedFilterRecord = { id: result.data.id, name: formValues.name, isPrivate: formValues.isPrivate, filterQueryJson: JSON.stringify(filterJson, null, 2) };
1414
1454
  setSavedFilters((prev) => [savedFilterRecord, ...prev]);
1415
1455
  setCurrentSavedFilterData(savedFilterRecord);
1416
1456
  setCurrentSavedFilterQuery(filterJson);
1457
+ setCurrentSavedFilterVariables({});
1458
+ persistActiveSavedFilter(savedFilterRecord, filterJson, {});
1417
1459
  setHasSearched(true);
1418
1460
  setRefreshKey((prev) => prev + 1);
1419
-
1420
- setTimeout(() => {
1421
- router.push(buildSavedFilterUrl(result.data.id));
1422
- }, 500)
1423
-
1424
1461
  }
1425
1462
  } catch (error) {
1426
1463
 
@@ -1496,7 +1533,44 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
1496
1533
 
1497
1534
  //saved filter related code start
1498
1535
 
1499
- const applySavedFilter = (savedfilter: any) => {
1536
+ const applySavedFilter = (savedfilter: any, variables: Record<string, any> = {}) => {
1537
+ if (!savedfilter?.id && !savedfilter?.systemKey) {
1538
+ console.error(ERROR_MESSAGES.SAVE_FILTER_UNDEFINED_NULL);
1539
+ return false;
1540
+ }
1541
+
1542
+ let filterJson: any;
1543
+ try {
1544
+ filterJson = parseSavedFilterQueryJson(savedfilter?.filterQueryJson);
1545
+ } catch {
1546
+ console.error(ERROR_MESSAGES.SAVE_FILTER_UNDEFINED_NULL);
1547
+ return false;
1548
+ }
1549
+
1550
+ const resolved = resolveSavedFilterVariables(filterJson, user?.id, variables);
1551
+ if (resolved.missingVariables.length > 0) {
1552
+ console.error(`Unable to apply saved filter '${savedfilter?.name}': missing variables ${resolved.missingVariables.join(", ")}`);
1553
+ return false;
1554
+ }
1555
+
1556
+ // Applying the already-active filter must be a no-op. This is important
1557
+ // for lifecycle extensions: applying a filter causes a new list load,
1558
+ // which invokes onBeforeListDataLoad/onListLoad again.
1559
+ const sameSavedFilterReference =
1560
+ (currentSavedFilterData?.id != null &&
1561
+ savedfilter?.id != null &&
1562
+ areFilterStateValuesEqual(currentSavedFilterData.id, savedfilter.id)) ||
1563
+ (currentSavedFilterData?.systemKey != null &&
1564
+ savedfilter?.systemKey != null &&
1565
+ areFilterStateValuesEqual(currentSavedFilterData.systemKey, savedfilter.systemKey));
1566
+ const sameSavedFilter =
1567
+ sameSavedFilterReference &&
1568
+ areFilterStateValuesEqual(currentSavedFilterQuery, filterJson) &&
1569
+ areFilterStateValuesEqual(currentSavedFilterVariables, variables);
1570
+
1571
+ if (sameSavedFilter) {
1572
+ return true;
1573
+ }
1500
1574
 
1501
1575
  setSearchChips([]);
1502
1576
  setSearchFilter(null);
@@ -1504,41 +1578,18 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
1504
1578
  setCustomFilter(null)
1505
1579
  setPredefinedSearchChip(null);
1506
1580
  setPredefinedSearchBaseFilter(null);
1507
- clearFilterObjectFromLocalStorage();
1508
- if (savedfilter?.systemKey) {
1509
- setCurrentSavedFilterData(savedfilter);
1510
- try {
1511
- const filterJson = JSON.parse(savedfilter.filterQueryJson);
1512
- if (filterJson) {
1513
- setCurrentSavedFilterQuery(filterJson);
1514
- }
1515
- } catch {
1516
- console.error(ERROR_MESSAGES.SAVE_FILTER_UNDEFINED_NULL);
1517
- }
1518
- setShowOverlay(false);
1519
- setHasSearched(true);
1520
- setRefreshKey((prev) => prev + 1);
1521
- return;
1522
- }
1523
- // push the savedQuery=1 in url
1524
- if (savedfilter?.id) {
1525
- router.push(buildSavedFilterUrl(savedfilter.id));
1526
- setShowOverlay(false);
1527
- } else {
1528
- console.error(ERROR_MESSAGES.SAVE_FILTER_UNDEFINED_NULL);
1529
- }
1581
+ setCurrentSavedFilterData(savedfilter);
1582
+ setCurrentSavedFilterQuery(filterJson);
1583
+ setCurrentSavedFilterVariables(variables);
1584
+ persistActiveSavedFilter(savedfilter, filterJson, variables);
1585
+ setShowOverlay(false);
1586
+ setHasSearched(true);
1587
+ setRefreshKey((prev) => prev + 1);
1588
+ return true;
1530
1589
  }
1531
1590
 
1532
1591
  const removeSavedFilter = () => {
1533
- setCurrentSavedFilterData(null);
1534
- setCurrentSavedFilterQuery(null);
1535
- setCurrentSavedFilterRules(null);
1536
- setShowSavedFilterComponent(false);
1537
- if (searchParams.has("savedQuery")) {
1538
- router.replace(buildSavedFilterUrl(null));
1539
- }
1540
- setHasSearched(true);
1541
- setRefreshKey((prev) => prev + 1);
1592
+ clearActiveSavedFilter();
1542
1593
  }
1543
1594
 
1544
1595
  const SavedFiltersChip = () => {
@@ -1697,16 +1748,6 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
1697
1748
  });
1698
1749
  });
1699
1750
 
1700
- (definedFilters || []).forEach((definedFilter: any) => {
1701
- if (!definedFilter?.applied) return;
1702
- items.push({
1703
- id: `handler:${definedFilter.key}`,
1704
- type: "handler",
1705
- label: definedFilter.label,
1706
- onRemove: () => onRemoveDefinedFilter?.(definedFilter.key),
1707
- });
1708
- });
1709
-
1710
1751
  return items;
1711
1752
  }, [
1712
1753
  currentSavedFilterData,
@@ -1716,8 +1757,6 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
1716
1757
  groupingRules,
1717
1758
  groupedSearchChips,
1718
1759
  searchableFields,
1719
- definedFilters,
1720
- onRemoveDefinedFilter,
1721
1760
  ]);
1722
1761
 
1723
1762
  const MAX_VISIBLE_CHIPS = 3;
@@ -1759,7 +1798,7 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
1759
1798
  predefined: p,
1760
1799
  }))
1761
1800
  : [];
1762
- const saved: OverlayOption[] = (availableSavedFilters || []).map((s: any) => ({
1801
+ const saved: OverlayOption[] = (selectableSavedFilters || []).map((s: any) => ({
1763
1802
  id: `saved:${s.id}`,
1764
1803
  kind: "saved" as const,
1765
1804
  saved: s,
@@ -1776,8 +1815,8 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
1776
1815
  }]
1777
1816
  : [];
1778
1817
 
1779
- return [...fields, ...predefined, ...saved, ...custom, ...grouping];
1780
- }, [inputValue, searchableFields, predefinedSearches, availableSavedFilters, viewType]);
1818
+ return [...predefined, ...fields, ...saved, ...custom, ...grouping];
1819
+ }, [inputValue, searchableFields, predefinedSearches, selectableSavedFilters, viewType]);
1781
1820
 
1782
1821
  const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
1783
1822
  const currentValue = inputValue?.trim() || "";
@@ -1800,6 +1839,12 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
1800
1839
  setShowOverlay(false);
1801
1840
  };
1802
1841
 
1842
+ if (e.key === "Escape") {
1843
+ e.preventDefault();
1844
+ setShowOverlay(false);
1845
+ return;
1846
+ }
1847
+
1803
1848
  if ((e.key === "ArrowDown" || e.key === "ArrowUp") && overlayOptions.length > 0) {
1804
1849
  e.preventDefault();
1805
1850
  setShowOverlay(true);
@@ -1811,6 +1856,12 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
1811
1856
  return;
1812
1857
  }
1813
1858
 
1859
+ if ((e.key === "Home" || e.key === "End") && showOverlay && overlayOptions.length > 0) {
1860
+ e.preventDefault();
1861
+ setFocusedIndex(e.key === "Home" ? 0 : overlayOptions.length - 1);
1862
+ return;
1863
+ }
1864
+
1814
1865
  if (e.key === "Enter") {
1815
1866
  if (currentValue || focusedIndex >= 0) {
1816
1867
  e.preventDefault();
@@ -1842,7 +1893,7 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
1842
1893
  } else if (predefinedSearchChip) {
1843
1894
  setPredefinedSearchChip(null);
1844
1895
  setPredefinedSearchBaseFilter(null);
1845
- } else if (activeSavedFilter) {
1896
+ } else if (currentSavedFilterData) {
1846
1897
  removeSavedFilter();
1847
1898
  }
1848
1899
  setHasSearched(true);
@@ -2028,7 +2079,13 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
2028
2079
  ref={searchInputRef}
2029
2080
  className="solid-global-search-input"
2030
2081
  value={inputValue || ""}
2031
- placeholder="Search."
2082
+ placeholder="Search…"
2083
+ role="combobox"
2084
+ aria-label="Search and filter"
2085
+ aria-autocomplete="list"
2086
+ aria-expanded={showOverlay}
2087
+ aria-controls={showOverlay ? overlayListboxId : undefined}
2088
+ aria-activedescendant={showOverlay && focusedIndex >= 0 ? getOverlayOptionId(focusedIndex) : undefined}
2032
2089
  onChange={(e) => {
2033
2090
  setInputValue(e.target.value);
2034
2091
  setShowChipManager(false);
@@ -2059,6 +2116,7 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
2059
2116
  focusSearchInput();
2060
2117
  }}
2061
2118
  className="custom-filter-button solid-global-search-trigger"
2119
+ aria-label="Open search options"
2062
2120
  >
2063
2121
  <Search size={14} />
2064
2122
  </SolidButton>
@@ -2103,53 +2161,77 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
2103
2161
  )} */}
2104
2162
 
2105
2163
  {showOverlay && (
2106
- <div ref={overlayRef} className="absolute w-full shadow-md solid-search-overlay-pannel">
2164
+ <div
2165
+ ref={overlayRef}
2166
+ id={overlayListboxId}
2167
+ role="listbox"
2168
+ aria-label="Search and filter options"
2169
+ className="solid-search-overlay-panel"
2170
+ >
2107
2171
  <div className="solid-search-overlay-scroll">
2108
- {(definedFilters || []).some((f: any) => !f?.applied) && (
2109
- <>
2110
- <div className="custom-filter-search-options solid-search-overlay-section flex flex-col px-3 py-2">
2111
- <h6 className="my-1 solid-search-overlay-heading solid-search-overlay-section-title solid-search-overlay-heading-with-icon">
2112
- <ListFilter size={13} />
2113
- <span>Defined filters</span>
2114
- </h6>
2115
- <div className="solid-defined-filter-badge-row">
2116
- {(definedFilters || [])
2117
- .filter((f: any) => !f?.applied)
2118
- .map((definedFilter: any) => (
2119
- <button
2120
- key={definedFilter.key}
2121
- type="button"
2122
- className="solid-defined-filter-badge"
2123
- onMouseDown={(e) => {
2124
- e.preventDefault();
2125
- onApplyDefinedFilter?.(definedFilter.key);
2126
- setShowOverlay(false);
2127
- }}
2128
- >
2129
- <Plus size={12} />
2130
- <span className="solid-defined-filter-badge-label">{definedFilter.label}</span>
2131
- </button>
2132
- ))}
2133
- </div>
2134
- </div>
2135
- <div className="solid-filter-dialog-sep" />
2136
- </>
2137
- )}
2138
2172
  {inputValue ? (
2139
2173
  <>
2140
- <div className="custom-filter-search-options solid-search-overlay-section flex flex-col px-3 py-1">
2174
+ {predefinedSearches && predefinedSearches.length > 0 && (
2175
+ <>
2176
+ <div className="solid-search-overlay-section solid-search-overlay-section--predefined">
2177
+ <h6 className="my-1 solid-search-overlay-heading solid-search-overlay-section-title solid-search-overlay-heading-with-icon">
2178
+ <Sparkles size={14} />
2179
+ <span>Suggested searches</span>
2180
+ <span className="solid-search-overlay-section-count">{predefinedSearches.length}</span>
2181
+ </h6>
2182
+ {predefinedSearches.map((predefinedSearch: any, index: number) => {
2183
+ const optionIndex = overlayOptions.findIndex((option) => option.id === `predefined:${predefinedSearch.name || index}`);
2184
+ return (
2185
+ <SolidButton
2186
+ key={index}
2187
+ id={getOverlayOptionId(optionIndex)}
2188
+ role="option"
2189
+ aria-selected={focusedIndex === optionIndex}
2190
+ variant="ghost"
2191
+ size="sm"
2192
+ className={`text-color solid-search-overlay-option ${focusedIndex === optionIndex ? "solid-search-overlay-option-active" : ""}`}
2193
+ onMouseDown={(e) => {
2194
+ e.preventDefault();
2195
+ handlePredefinedSearch(predefinedSearch);
2196
+ }}
2197
+ onMouseEnter={() => setFocusedIndex(optionIndex)}
2198
+ >
2199
+ <span className="solid-search-overlay-option-content">
2200
+ <span className="solid-search-overlay-option-icon" aria-hidden="true">
2201
+ <Sparkles size={15} />
2202
+ </span>
2203
+ <span className="solid-search-overlay-option-copy">
2204
+ <span className="solid-search-overlay-option-title">{predefinedSearch.name}</span>
2205
+ <span className="solid-search-overlay-option-description">
2206
+ Search for “{inputValue}”{predefinedSearch.description ? ` — ${predefinedSearch.description}` : ""}
2207
+ </span>
2208
+ </span>
2209
+ </span>
2210
+ </SolidButton>
2211
+ );
2212
+ })}
2213
+ </div>
2214
+ <div className="solid-filter-dialog-sep" />
2215
+ </>
2216
+ )}
2217
+ <div className="solid-search-overlay-section solid-search-overlay-section--search">
2141
2218
  <h6 className="my-1 solid-search-overlay-heading solid-search-overlay-section-title solid-search-overlay-heading-with-icon">
2142
2219
  <Search size={13} />
2143
- <span>Search by fields</span>
2220
+ <span>Search individual fields</span>
2221
+ <span className="solid-search-overlay-section-count">{searchableFields.length}</span>
2144
2222
  </h6>
2145
2223
  {searchableFields.length > 0 ? (
2146
2224
  searchableFields.map((value: any, index: number) => {
2225
+ const optionIndex = overlayOptions.findIndex((option) => option.id === `field:${value.fieldName}`);
2147
2226
  return (
2148
2227
  <SolidButton
2149
2228
  key={index}
2229
+ id={getOverlayOptionId(optionIndex)}
2230
+ role="option"
2231
+ aria-selected={focusedIndex === optionIndex}
2150
2232
  variant="ghost"
2151
2233
  size="sm"
2152
- className={`flex justify-start gap-1 text-color solid-search-overlay-option ${focusedIndex === index ? "solid-search-overlay-option-active" : ""}`}
2234
+ className={`text-color solid-search-overlay-option ${focusedIndex === optionIndex ? "solid-search-overlay-option-active" : ""}`}
2153
2235
  onMouseDown={(e) => {
2154
2236
  e.preventDefault();
2155
2237
  const currentValue = inputValue?.trim();
@@ -2169,9 +2251,17 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
2169
2251
  setShowOverlay(false);
2170
2252
  }
2171
2253
  }}
2172
- onMouseEnter={() => setFocusedIndex(index)}
2254
+ onMouseEnter={() => setFocusedIndex(optionIndex)}
2173
2255
  >
2174
- Search <strong style={{ paddingLeft: "2px" }}>{toTitleCase(value.displayName.trim())}</strong>&nbsp;for:&nbsp; <span className="font-bold text-color">{inputValue}</span>
2256
+ <span className="solid-search-overlay-option-content">
2257
+ <span className="solid-search-overlay-option-icon" aria-hidden="true">
2258
+ <Search size={15} />
2259
+ </span>
2260
+ <span className="solid-search-overlay-option-copy">
2261
+ <span className="solid-search-overlay-option-title">{toTitleCase(value.displayName.trim())}</span>
2262
+ <span className="solid-search-overlay-option-description">Search for “{inputValue}”</span>
2263
+ </span>
2264
+ </span>
2175
2265
  </SolidButton>
2176
2266
  )
2177
2267
  })
@@ -2186,37 +2276,10 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
2186
2276
  </div>
2187
2277
  )}
2188
2278
  </div>
2189
- {predefinedSearches && predefinedSearches.length > 0 && (
2190
- <>
2191
- <div className="solid-filter-dialog-sep" />
2192
- <div className="custom-filter-search-options solid-search-overlay-section flex flex-col px-3 py-1">
2193
- <h6 className="my-1 solid-search-overlay-heading solid-search-overlay-section-title">Predefined searches</h6>
2194
- {predefinedSearches.map((predefinedSearch: any, index: number) => (
2195
- <SolidButton
2196
- key={index}
2197
- variant="ghost"
2198
- size="sm"
2199
- className={`flex flex-col items-start gap-1 text-color solid-search-overlay-option solid-search-overlay-option-stacked ${focusedIndex === searchableFields.length + index ? "solid-search-overlay-option-active" : ""}`}
2200
- onMouseDown={(e) => {
2201
- e.preventDefault();
2202
- handlePredefinedSearch(predefinedSearch);
2203
- }}
2204
- onMouseEnter={() => setFocusedIndex(searchableFields.length + index)}
2205
- >
2206
- <div className="flex items-center gap-1">
2207
- <strong>{predefinedSearch.name}:</strong>
2208
- <span className="font-bold text-color pr-1">{inputValue} </span>
2209
- </div>
2210
- <div className="text-xs">{predefinedSearch.description}</div>
2211
- </SolidButton>
2212
- ))}
2213
- </div>
2214
- </>
2215
- )}
2216
2279
  </>
2217
2280
  ) :
2218
2281
  <>
2219
- <div className="p-3 solid-search-overlay-empty">
2282
+ <div className="solid-search-overlay-empty">
2220
2283
  <div className="solid-search-overlay-panel-callout">
2221
2284
  <div className="solid-search-overlay-panel-callout-icon">
2222
2285
  <Search size={13} />
@@ -2226,7 +2289,7 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
2226
2289
  Start typing to search
2227
2290
  </div>
2228
2291
  <div className="solid-search-overlay-empty-subtitle">
2229
- Start typing in search input to see all fields on which you can search {searchableEntityLabel} by.
2292
+ Enter a value to see suggested searches and searchable {searchableEntityLabel} fields.
2230
2293
  </div>
2231
2294
  </div>
2232
2295
  </div>
@@ -2234,26 +2297,27 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
2234
2297
  <div className="solid-filter-dialog-sep" />
2235
2298
  </>
2236
2299
  }
2237
- {availableSavedFilters.length > 0 &&
2300
+ {selectableSavedFilters.length > 0 &&
2238
2301
  <>
2239
- <div className="p-3 solid-search-overlay-section">
2302
+ <hr className="solid-search-overlay-section-separator" />
2303
+ <div className="solid-search-overlay-section solid-search-overlay-section--saved">
2240
2304
  <div className="solid-search-overlay-panel-callout solid-search-overlay-panel-callout-compact">
2241
2305
  <div className="solid-search-overlay-panel-callout-icon">
2242
2306
  <Bookmark size={13} />
2243
2307
  </div>
2244
2308
  <div className="solid-search-overlay-panel-callout-copy">
2245
- <p className="solid-search-overlay-heading solid-search-overlay-section-title">Saved filters</p>
2246
- <div className="solid-search-overlay-empty-subtitle">
2247
- Reusable filter sets available for this view.
2248
- </div>
2309
+ <p className="solid-search-overlay-heading solid-search-overlay-section-title">
2310
+ <span>Saved filters</span>
2311
+ <span className="solid-search-overlay-section-count">{selectableSavedFilters.length}</span>
2312
+ </p>
2249
2313
  </div>
2250
2314
  </div>
2251
- <div className="flex flex-col solid-search-overlay-saved-list">
2252
- {availableSavedFilters.map((savedfilter: any, index: number) => (
2315
+ <div className="solid-search-overlay-saved-list">
2316
+ {selectableSavedFilters.map((savedfilter: any, index: number) => (
2253
2317
  <SavedFilterList
2254
2318
  key={savedfilter.id}
2255
2319
  savedfilter={savedfilter}
2256
- activeSavedFilter={activeSavedFilter || currentSavedFilterData?.systemKey || currentSavedFilterData?.id}
2320
+ activeSavedFilterReference={currentSavedFilterData?.systemKey || currentSavedFilterData?.id}
2257
2321
  applySavedFilter={applySavedFilter}
2258
2322
  openSavedCustomFilter={openSavedCustomFilter}
2259
2323
  setSavedFilterTobeDeleted={setSavedFilterTobeDeleted}
@@ -2263,6 +2327,7 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
2263
2327
  const optIndex = overlayOptions.findIndex(o => o.id === `saved:${savedfilter.id}`);
2264
2328
  if (optIndex !== -1) setFocusedIndex(optIndex);
2265
2329
  }}
2330
+ optionId={getOverlayOptionId(overlayOptions.findIndex(o => o.id === `saved:${savedfilter.id}`))}
2266
2331
  />
2267
2332
  ))}
2268
2333
  </div>
@@ -2274,7 +2339,10 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
2274
2339
  <div className="solid-search-overlay-footer">
2275
2340
  <button
2276
2341
  type="button"
2277
- className={`flex flex-col px-3 py-2 solid-search-overlay-footer-action ${overlayOptions[focusedIndex]?.id === "footer:custom" ? "solid-search-overlay-option-active" : ""}`}
2342
+ id={getOverlayOptionId(overlayOptions.findIndex((option) => option.id === "footer:custom"))}
2343
+ role="option"
2344
+ aria-selected={overlayOptions[focusedIndex]?.id === "footer:custom"}
2345
+ className={`solid-search-overlay-footer-action solid-search-overlay-footer-action--custom ${overlayOptions[focusedIndex]?.id === "footer:custom" ? "solid-search-overlay-option-active" : ""}`}
2278
2346
  onMouseDown={(event) => {
2279
2347
  event.preventDefault();
2280
2348
  openCustomFilterDialog();
@@ -2290,10 +2358,10 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
2290
2358
  </div>
2291
2359
  <div className="solid-search-overlay-panel-callout-copy">
2292
2360
  <div className="solid-search-overlay-footer-title solid-search-overlay-section-title">
2293
- Click here to apply custom filters
2361
+ Advanced filters
2294
2362
  </div>
2295
2363
  <div className="solid-search-overlay-footer-subtitle">
2296
- Use custom filters to apply conditions on any field using any operator.
2364
+ Combine fields, operators and values.
2297
2365
  </div>
2298
2366
  </div>
2299
2367
  </div>
@@ -2301,7 +2369,10 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
2301
2369
  {viewType === "tree" && (
2302
2370
  <button
2303
2371
  type="button"
2304
- className={`flex flex-col px-3 py-2 solid-search-overlay-footer-action solid-search-overlay-footer-action-secondary ${overlayOptions[focusedIndex]?.id === "footer:grouping" ? "solid-search-overlay-option-active" : ""}`}
2372
+ id={getOverlayOptionId(overlayOptions.findIndex((option) => option.id === "footer:grouping"))}
2373
+ role="option"
2374
+ aria-selected={overlayOptions[focusedIndex]?.id === "footer:grouping"}
2375
+ className={`solid-search-overlay-footer-action solid-search-overlay-footer-action-secondary ${overlayOptions[focusedIndex]?.id === "footer:grouping" ? "solid-search-overlay-option-active" : ""}`}
2305
2376
  onMouseDown={(event) => {
2306
2377
  event.preventDefault();
2307
2378
  openGroupingDialog();
@@ -2317,10 +2388,10 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, viewType, handle
2317
2388
  </div>
2318
2389
  <div className="solid-search-overlay-panel-callout-copy">
2319
2390
  <div className="solid-search-overlay-footer-title solid-search-overlay-section-title">
2320
- Click here to do custom grouping
2391
+ Group and aggregate
2321
2392
  </div>
2322
2393
  <div className="solid-search-overlay-footer-subtitle">
2323
- Configure grouping rules and aggregations for the tree view.
2394
+ Organise records and calculate aggregate values.
2324
2395
  </div>
2325
2396
  </div>
2326
2397
  </div>