@trackunit/filters-filter-bar 1.3.198 → 1.3.201
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.cjs.js +146 -80
- package/index.esm.js +145 -82
- package/package.json +7 -7
- package/src/lib/FilterBar.d.ts +5 -1
- package/src/lib/components/FilterButtonTooltipLabel.d.ts +7 -0
- package/src/lib/components/FiltersMenu.d.ts +15 -3
- package/src/lib/components/FiltersMenuContent.d.ts +17 -0
- package/src/lib/components/index.d.ts +2 -0
- package/src/lib/hooks/useFiltersMenu.d.ts +25 -0
- package/src/lib/index.d.ts +1 -0
- package/src/lib/types/FilterTypes.d.ts +1 -1
- package/src/translation.d.ts +2 -2
- package/translation.cjs.js +1 -0
- package/translation.cjs10.js +1 -0
- package/translation.cjs11.js +1 -0
- package/translation.cjs12.js +1 -0
- package/translation.cjs13.js +1 -0
- package/translation.cjs14.js +1 -0
- package/translation.cjs15.js +1 -0
- package/translation.cjs16.js +1 -0
- package/translation.cjs17.js +1 -0
- package/translation.cjs2.js +1 -0
- package/translation.cjs3.js +1 -0
- package/translation.cjs4.js +1 -0
- package/translation.cjs5.js +1 -0
- package/translation.cjs6.js +1 -0
- package/translation.cjs7.js +1 -0
- package/translation.cjs8.js +1 -0
- package/translation.cjs9.js +1 -0
- package/translation.esm.js +1 -0
- package/translation.esm10.js +1 -0
- package/translation.esm11.js +1 -0
- package/translation.esm12.js +1 -0
- package/translation.esm13.js +1 -0
- package/translation.esm14.js +1 -0
- package/translation.esm15.js +1 -0
- package/translation.esm16.js +1 -0
- package/translation.esm17.js +1 -0
- package/translation.esm2.js +1 -0
- package/translation.esm3.js +1 -0
- package/translation.esm4.js +1 -0
- package/translation.esm5.js +1 -0
- package/translation.esm6.js +1 -0
- package/translation.esm7.js +1 -0
- package/translation.esm8.js +1 -0
- package/translation.esm9.js +1 -0
package/index.esm.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { registerTranslations, useNamespaceTranslation } from '@trackunit/i18n-library-translation';
|
|
3
|
-
import { VirtualizedList, Text, Button, useViewportBreakpoints, Popover, PopoverTrigger, Tooltip, Icon, PopoverContent,
|
|
3
|
+
import { VirtualizedList, Text, Button, Card, CardBody, useViewportBreakpoints, Popover, PopoverTrigger, Tooltip, Icon, PopoverContent, IconButton, MenuList } from '@trackunit/react-components';
|
|
4
4
|
import { useAnalytics, useTextSearch, useCurrentUser } from '@trackunit/react-core-hooks';
|
|
5
5
|
import { FilterBody, RadioFilterItem, CheckBoxFilterItem, FilterHeader as FilterHeader$1, FilterFooter, Filter } from '@trackunit/react-filter-components';
|
|
6
6
|
import { useRef, useMemo, useState, useEffect, useCallback, Fragment as Fragment$1 } from 'react';
|
|
@@ -130,6 +130,7 @@ var defaultTranslations = {
|
|
|
130
130
|
"filtersBar.groups.ASSET": "Asset",
|
|
131
131
|
"filtersBar.groups.CUSTOM_FIELDS": "Custom Fields",
|
|
132
132
|
"filtersBar.groups.CUSTOMERS": "Customers",
|
|
133
|
+
"filtersBar.groups.FLEET_HEALTH": "Fleet Health",
|
|
133
134
|
"filtersBar.groups.INTEGRATION": "Integration",
|
|
134
135
|
"filtersBar.groups.METADATA": "Metadata",
|
|
135
136
|
"filtersBar.groups.MY_NETWORK": "My Network",
|
|
@@ -642,6 +643,25 @@ const DefaultRadioFilter = ({ filterDefinition, filterBarActions, options, loadi
|
|
|
642
643
|
}, value: selectedRadioId?.key || "", children: jsx(DynamicFilterList, { checked: index => filterBarActions.objectIncludesValue(filterDefinition.filterKey, res[index]?.key || ""), className: "m-1 mt-0", count: index => res[index]?.count, keyMapper: index => res[index]?.key || "", labelMapper: index => res[index]?.label || "", rowCount: res.length, showRequestMoreUseSearch: showRequestMoreUseSearch, type: "Radio" }) })) })] }));
|
|
643
644
|
};
|
|
644
645
|
|
|
646
|
+
/**
|
|
647
|
+
* Tooltip label for the filter button
|
|
648
|
+
*/
|
|
649
|
+
const FilterButtonTooltipLabel = ({ filterBarConfig, }) => {
|
|
650
|
+
const [t] = useTranslation();
|
|
651
|
+
switch (filterBarConfig.appliedFilterKeys().length) {
|
|
652
|
+
case 0:
|
|
653
|
+
return t("filtersBar.appliedFiltersTooltip.none");
|
|
654
|
+
case 1:
|
|
655
|
+
return filterBarConfig.appliedFilterKeys()[0]
|
|
656
|
+
? t("filtersBar.appliedFiltersTooltip.singular", {
|
|
657
|
+
filterName: filterBarConfig.getFilterTitle(filterBarConfig.appliedFilterKeys()[0]),
|
|
658
|
+
})
|
|
659
|
+
: null; // should never happen though
|
|
660
|
+
default:
|
|
661
|
+
return (jsxs(Fragment, { children: [t("filtersBar.appliedFiltersTooltip.plural", { count: filterBarConfig.appliedFilterKeys().length }), jsx("ul", { className: "list-inside", children: filterBarConfig.appliedFilterKeys().map((appliedFilterKey, index) => (jsx("li", { className: "list-disc", children: filterBarConfig.getFilterTitle(appliedFilterKey) }, index))) })] }));
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
|
|
645
665
|
/**
|
|
646
666
|
* Returns the two first values, appends counter if more.
|
|
647
667
|
*
|
|
@@ -757,6 +777,64 @@ const useGroupFilters = (filterDefinitions, hiddenFilters) => {
|
|
|
757
777
|
};
|
|
758
778
|
const uniqueKeysFromGroups = (filters) => [...new Set(filters.map(filter => filter.group))];
|
|
759
779
|
|
|
780
|
+
/**
|
|
781
|
+
* This hook is used to manage the filters menu.
|
|
782
|
+
* It returns the filters that should be shown in the menu, the filters that should be shown directly in the filter bar,
|
|
783
|
+
* and the filters that should be shown in the search results.
|
|
784
|
+
*/
|
|
785
|
+
const useFiltersMenu = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [], allowShowFiltersDirectly = true, }) => {
|
|
786
|
+
const hideInMenu = useMemo(() => {
|
|
787
|
+
return objectValues(filterBarDefinition)
|
|
788
|
+
.map(filter => {
|
|
789
|
+
const showInFilterBar = filter.showInFilterBar ? filter.showInFilterBar() : true;
|
|
790
|
+
const showInStarredMenu = filter.showInStarredMenu ? filter.showInStarredMenu() : true;
|
|
791
|
+
const showMenuAnywayBecauseFilterHasChanged = filterBarConfig.appliedFilterKeys().includes(filter.filterKey);
|
|
792
|
+
return (!showInFilterBar || !showInStarredMenu) && !showMenuAnywayBecauseFilterHasChanged
|
|
793
|
+
? filter.filterKey
|
|
794
|
+
: null;
|
|
795
|
+
})
|
|
796
|
+
.filter(truthy);
|
|
797
|
+
}, [filterBarConfig, filterBarDefinition]);
|
|
798
|
+
const removeCustomFieldsGroup = useCallback((groupOfFilters) => groupOfFilters.filter(group => group.key !== "CUSTOM_FIELDS"), []);
|
|
799
|
+
const { filtersGrouped } = useGroupFilters(objectValues(filterBarDefinition), [...hideInMenu, ...hiddenFilters]);
|
|
800
|
+
const { appliedFilters, filtersToShow, showDirectlyFilters, hasCustomFields } = useMemo(() => {
|
|
801
|
+
const allFilters = filtersGrouped.map(group => group.filters).flat();
|
|
802
|
+
return {
|
|
803
|
+
appliedFilters: allFilters.filter(filter => filterBarConfig.appliedFilterKeys().includes(filter.filterKey)),
|
|
804
|
+
filtersToShow: allFilters.filter(filter => !filter.showDirectly),
|
|
805
|
+
showDirectlyFilters: allFilters.filter(filter => filter.showDirectly && allowShowFiltersDirectly),
|
|
806
|
+
hasCustomFields: allFilters.some(filter => filter.group === "CUSTOM_FIELDS"),
|
|
807
|
+
};
|
|
808
|
+
}, [filterBarConfig, filtersGrouped, allowShowFiltersDirectly]);
|
|
809
|
+
const [searchResults, searchText, setSearchText] = useTextSearch(filtersToShow, item => [item.title]);
|
|
810
|
+
const { filtersGrouped: searchResultsGrouped } = useGroupFilters(searchResults, []);
|
|
811
|
+
const { filtersGrouped: filtersToShowGrouped } = useGroupFilters(filtersToShow, []);
|
|
812
|
+
const appliedCustomFields = useMemo(() => appliedFilters.filter(filter => filter.group === "CUSTOM_FIELDS"), [appliedFilters]);
|
|
813
|
+
return useMemo(() => {
|
|
814
|
+
return {
|
|
815
|
+
appliedFilters,
|
|
816
|
+
hasCustomFields,
|
|
817
|
+
showDirectlyFilters,
|
|
818
|
+
appliedCustomFields,
|
|
819
|
+
searchText,
|
|
820
|
+
setSearchText,
|
|
821
|
+
filtersToShowGrouped,
|
|
822
|
+
searchResultsGrouped,
|
|
823
|
+
removeCustomFieldsGroup,
|
|
824
|
+
};
|
|
825
|
+
}, [
|
|
826
|
+
appliedFilters,
|
|
827
|
+
hasCustomFields,
|
|
828
|
+
showDirectlyFilters,
|
|
829
|
+
appliedCustomFields,
|
|
830
|
+
searchText,
|
|
831
|
+
setSearchText,
|
|
832
|
+
filtersToShowGrouped,
|
|
833
|
+
searchResultsGrouped,
|
|
834
|
+
removeCustomFieldsGroup,
|
|
835
|
+
]);
|
|
836
|
+
};
|
|
837
|
+
|
|
760
838
|
/**
|
|
761
839
|
* FiltersRenderer renders an array of Filter components from filter definitions
|
|
762
840
|
* It ignores hidden filters.
|
|
@@ -777,6 +855,60 @@ const FiltersRenderer = ({ filters, filterBarConfig, visualStyle, }) => {
|
|
|
777
855
|
.map(filter => (jsx(FilterComponent, { filter: filter, filterBarActions: filterBarConfig, filterState: { values: filterBarConfig.values, setters: filterBarConfig.setters }, visualStyle: visualStyle }, `filter-${filter.filterKey}`)));
|
|
778
856
|
};
|
|
779
857
|
|
|
858
|
+
/**
|
|
859
|
+
* FiltersList is a React component that displays a list of filters within a filter bar.
|
|
860
|
+
*
|
|
861
|
+
* @returns {ReactElement} - Returns the FiltersList component.
|
|
862
|
+
*/
|
|
863
|
+
const GroupedFiltersList = ({ filterBarConfig, filtersGrouped, className, dataTestId = "grouped-filters-list", }) => {
|
|
864
|
+
return (jsx("div", { className: className, "data-testid": dataTestId, role: "menu", children: filtersGrouped.map((group, idx) => {
|
|
865
|
+
const isLastGroup = idx === filtersGrouped.length - 1;
|
|
866
|
+
return (jsxs("div", { className: "flex flex-col gap-1", children: [jsxs("div", { children: [jsx(Text, { className: "text-secondary-400 h-7 px-3 py-2", dataTestId: `${group.key}-group-title`, size: "small", uppercase: true, weight: "bold", children: group.title }), jsx("ul", { "aria-labelledby": `${group.key}-group-title`, className: "grid", "data-testid": `${group.key}-group-list`, children: jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: group.filters, visualStyle: "list-item" }) })] }), isLastGroup ? null : jsx("div", { className: "bg-secondary-200 h-[1px] w-full", role: "separator" })] }, group.key));
|
|
867
|
+
}) }));
|
|
868
|
+
};
|
|
869
|
+
|
|
870
|
+
/**
|
|
871
|
+
* ResetFiltersButton is a React component that provides a button for resetting filters.
|
|
872
|
+
*
|
|
873
|
+
* @returns {ReactElement | null} The rendered ResetFiltersButton component, or null if no filters have been applied.
|
|
874
|
+
*/
|
|
875
|
+
const ResetFiltersButton = ({ resetFiltersToInitialState, dataTestId, className, }) => {
|
|
876
|
+
const [t] = useTranslation();
|
|
877
|
+
return (jsxs(Button, { className: className, dataTestId: dataTestId ?? "reset-filters-button", onClick: () => {
|
|
878
|
+
resetFiltersToInitialState();
|
|
879
|
+
}, size: "small", variant: "ghost", children: [t("filtersBar.resetFilters"), jsx("span", { className: "sr-only", children: "Resets all applied filters" })] }));
|
|
880
|
+
};
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
*
|
|
884
|
+
*/
|
|
885
|
+
const FiltersMenuContent = ({ filterBarConfig, setShowCustomFilters, setSearchText, searchText, searchResultsGrouped, filtersToShowGrouped, removeCustomFieldsGroup, hasCustomFields, appliedCustomFields, showCustomFilters, }) => {
|
|
886
|
+
const [t] = useTranslation();
|
|
887
|
+
return (jsxs(Card, { className: "max-h-[min(600px,_calc(100dvh-32px))] w-[300px] overflow-y-hidden", dataTestId: "starred-filters-menu-popover", children: [jsxs("div", { className: " flex-col gap-1 p-1", children: [jsx(Search, { autoFocus: true, dataTestId: "starred-filters-menu-search", fieldSize: "small", id: "search-filters-list", onChange: e => setSearchText(e.currentTarget.value), onClear: () => setSearchText(""), placeholder: t("filtersBar.searchFiltersPlaceholder"), value: searchText }), jsxs("div", { className: "flex h-7 items-center justify-between gap-1 px-3", children: [jsx(Text, { className: "text-secondary-400", size: "small", children: jsx(FiltersAppliedCountLabel, { filterBarConfig: filterBarConfig }) }), filterBarConfig.appliedFilterKeys().length > 0 ? (jsx(ResetFiltersButton, { resetFiltersToInitialState: filterBarConfig.resetFiltersToInitialState })) : null] })] }), jsx(Separator, {}), jsxs(CardBody, { className: "gap-1 p-1", density: "none", disableGap: true, children: [jsx(GroupedFiltersList, { className: "flex flex-col gap-1", filterBarConfig: filterBarConfig, filtersGrouped: searchText
|
|
888
|
+
? searchResultsGrouped
|
|
889
|
+
: showCustomFilters
|
|
890
|
+
? filtersToShowGrouped
|
|
891
|
+
: removeCustomFieldsGroup(filtersToShowGrouped) }), hasCustomFields && !showCustomFilters && !searchText ? (jsx(CustomFieldsHiddenGroup, { appliedCustomFields: appliedCustomFields, filterBarConfig: filterBarConfig, onShow: () => {
|
|
892
|
+
setShowCustomFilters(true);
|
|
893
|
+
} })) : null] })] }));
|
|
894
|
+
};
|
|
895
|
+
const Separator = () => jsx("hr", { className: "border-secondary-200", role: "separator" });
|
|
896
|
+
const FiltersAppliedCountLabel = ({ filterBarConfig, }) => {
|
|
897
|
+
const [t] = useTranslation();
|
|
898
|
+
switch (filterBarConfig.appliedFilterKeys().length) {
|
|
899
|
+
case 0:
|
|
900
|
+
return t("filtersBar.appliedFiltersTooltip.none");
|
|
901
|
+
case 1:
|
|
902
|
+
return filterBarConfig.appliedFilterKeys()[0] ? t("filtersMenu.appliedFiltersLabel.singular") : null;
|
|
903
|
+
default:
|
|
904
|
+
return jsx(Fragment, { children: t("filtersMenu.appliedFiltersLabel.plural", { count: filterBarConfig.appliedFilterKeys().length }) });
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
const CustomFieldsHiddenGroup = ({ appliedCustomFields, filterBarConfig, onShow, }) => {
|
|
908
|
+
const [t] = useTranslation();
|
|
909
|
+
return (jsxs(Fragment, { children: [jsx(Separator, {}), jsxs("div", { children: [jsx(Button, { "aria-controls": "filters-list", className: "text-primary-600 w-full justify-between px-3", onClick: onShow, prefix: jsx(Text, { className: "text-secondary-400", size: "small", uppercase: true, weight: "bold", children: t("filtersBar.groups.CUSTOM_FIELDS") }), size: "small", variant: "ghost-neutral", children: t("filtersBar.showAll") }), appliedCustomFields.length > 0 ? (jsx("ul", { "aria-label": "Visible custom fields", "data-testid": "applied-custom-fields-list", children: jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: appliedCustomFields, visualStyle: "list-item" }) })) : null] })] }));
|
|
910
|
+
};
|
|
911
|
+
|
|
780
912
|
/**
|
|
781
913
|
* TooltipValues component that displays formatted tooltip values based on the provided input.
|
|
782
914
|
*
|
|
@@ -870,100 +1002,31 @@ const MultipleFilterTooltipLabel = ({ filterBarConfig, filterKeys, }) => {
|
|
|
870
1002
|
}
|
|
871
1003
|
};
|
|
872
1004
|
|
|
873
|
-
/**
|
|
874
|
-
* FiltersList is a React component that displays a list of filters within a filter bar.
|
|
875
|
-
*
|
|
876
|
-
* @returns {ReactElement} - Returns the FiltersList component.
|
|
877
|
-
*/
|
|
878
|
-
const GroupedFiltersList = ({ filterBarConfig, filtersGrouped, className, dataTestId = "grouped-filters-list", }) => {
|
|
879
|
-
return (jsx("div", { className: className, "data-testid": dataTestId, role: "menu", children: filtersGrouped.map((group, idx) => {
|
|
880
|
-
const isLastGroup = idx === filtersGrouped.length - 1;
|
|
881
|
-
return (jsxs("div", { className: "flex flex-col gap-1", children: [jsxs("div", { children: [jsx(Text, { className: "text-secondary-400 h-7 px-3 py-2", dataTestId: `${group.key}-group-title`, size: "small", uppercase: true, weight: "bold", children: group.title }), jsx("ul", { "aria-labelledby": `${group.key}-group-title`, className: "grid", "data-testid": `${group.key}-group-list`, children: jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: group.filters, visualStyle: "list-item" }) })] }), isLastGroup ? null : jsx("div", { className: "bg-secondary-200 h-[1px] w-full", role: "separator" })] }, group.key));
|
|
882
|
-
}) }));
|
|
883
|
-
};
|
|
884
|
-
|
|
885
|
-
/**
|
|
886
|
-
* ResetFiltersButton is a React component that provides a button for resetting filters.
|
|
887
|
-
*
|
|
888
|
-
* @returns {ReactElement | null} The rendered ResetFiltersButton component, or null if no filters have been applied.
|
|
889
|
-
*/
|
|
890
|
-
const ResetFiltersButton = ({ resetFiltersToInitialState, dataTestId, className, }) => {
|
|
891
|
-
const [t] = useTranslation();
|
|
892
|
-
return (jsxs(Button, { className: className, dataTestId: dataTestId ?? "reset-filters-button", onClick: () => {
|
|
893
|
-
resetFiltersToInitialState();
|
|
894
|
-
}, size: "small", variant: "ghost", children: [t("filtersBar.resetFilters"), jsx("span", { className: "sr-only", children: "Resets all applied filters" })] }));
|
|
895
|
-
};
|
|
896
|
-
|
|
897
1005
|
/**
|
|
898
1006
|
* FilterMenu is a React component that displays a list of filters in a popover menu based on the provided filter bar configuration.
|
|
899
1007
|
*
|
|
900
1008
|
* @template TFilterBarDefinition - The type representing the filter bar definition.
|
|
901
1009
|
* @returns {ReactElement} - Returns the FilterMenu component.
|
|
902
1010
|
*/
|
|
903
|
-
const FiltersMenu = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [], compact, title, dataTestId = "filters-menu", className, }) => {
|
|
1011
|
+
const FiltersMenu = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [], compact, title, dataTestId = "filters-menu", className, showAppliedFiltersCount = true, buttonProps, allowShowFiltersDirectly = true, }) => {
|
|
904
1012
|
const [t] = useTranslation();
|
|
905
1013
|
const { isSm } = useViewportBreakpoints();
|
|
906
1014
|
const [showCustomFilters, setShowCustomFilters] = useState(false);
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
const showInStarredMenu = filter.showInStarredMenu ? filter.showInStarredMenu() : true; // TODO: Starred menu concept should be completely removed everywhere
|
|
914
|
-
const showMenuAnywayBecauseFilterHasChanged = filterBarConfig.appliedFilterKeys().includes(filter.filterKey);
|
|
915
|
-
return (!showInFilterBar || !showInStarredMenu) && !showMenuAnywayBecauseFilterHasChanged
|
|
916
|
-
? filter.filterKey
|
|
917
|
-
: null;
|
|
918
|
-
})
|
|
919
|
-
.filter(truthy);
|
|
920
|
-
}, [filterBarConfig, filterBarDefinition]);
|
|
921
|
-
const { filtersGrouped } = useGroupFilters(objectValues(filterBarDefinition), [...hideInMenu, ...hiddenFilters]);
|
|
922
|
-
const { appliedFilters, filtersToShow, showDirectlyFilters, hasCustomFields } = useMemo(() => {
|
|
923
|
-
const allFilters = filtersGrouped.map(group => group.filters).flat();
|
|
924
|
-
return {
|
|
925
|
-
appliedFilters: allFilters.filter(filter => filterBarConfig.appliedFilterKeys().includes(filter.filterKey)),
|
|
926
|
-
filtersToShow: allFilters.filter(filter => !filter.showDirectly),
|
|
927
|
-
showDirectlyFilters: allFilters.filter(filter => filter.showDirectly),
|
|
928
|
-
hasCustomFields: allFilters.some(filter => filter.group === "CUSTOM_FIELDS"),
|
|
929
|
-
};
|
|
930
|
-
}, [filterBarConfig, filtersGrouped]);
|
|
931
|
-
const [searchResults, searchText, setSearchText] = useTextSearch(filtersToShow, item => [item.title]);
|
|
932
|
-
const { filtersGrouped: searchResultsGrouped } = useGroupFilters(searchResults, []);
|
|
933
|
-
const { filtersGrouped: filtersToShowGrouped } = useGroupFilters(filtersToShow, []);
|
|
934
|
-
const appliedCustomFields = useMemo(() => appliedFilters.filter(filter => filter.group === "CUSTOM_FIELDS"), [appliedFilters]);
|
|
1015
|
+
const { appliedFilters, showDirectlyFilters, hasCustomFields, filtersToShowGrouped, searchResultsGrouped, searchText, appliedCustomFields, removeCustomFieldsGroup, setSearchText, } = useFiltersMenu({
|
|
1016
|
+
filterBarDefinition,
|
|
1017
|
+
filterBarConfig,
|
|
1018
|
+
hiddenFilters,
|
|
1019
|
+
allowShowFiltersDirectly,
|
|
1020
|
+
});
|
|
935
1021
|
return (jsxs("div", { className: twMerge("flex items-center gap-2", className), "data-testid": dataTestId, children: [jsx(Popover, { onOpenStateChange: open => {
|
|
936
1022
|
if (!open) {
|
|
937
1023
|
setShowCustomFilters(false);
|
|
938
1024
|
setSearchText("");
|
|
939
1025
|
}
|
|
940
1026
|
}, placement: "bottom-start", children: modalState => {
|
|
941
|
-
return (jsxs(Fragment, { children: [jsx(PopoverTrigger, { children: jsx("div", { "data-testid": "starred-filters-menu-trigger", id: "starred-filters-menu-trigger", children: jsx(Tooltip, { disabled: !compact || modalState.isOpen, label: jsx(MultipleFilterTooltipLabel, { filterBarConfig: filterBarConfig }), children: jsx(Button, { prefix: jsx(Icon, { ariaHidden: true, color: filterBarConfig.appliedFilterKeys().length > 0 ? "primary" : undefined, name: "Filter", size: "small" }), size: "small", suffix: compact && filterBarConfig.appliedFilterKeys().length > 0 && isSm ? (jsxs("div", { children: [jsxs("span", { "aria-hidden": true, children: ["(", filterBarConfig.appliedFilterKeys().length, ")"] }), jsxs("span", { className: "sr-only", children: [filterBarConfig.appliedFilterKeys().length, " filters applied"] })] })) : undefined, variant: "secondary", children: jsx("span", { className: "hidden sm:block", children: title ?? t("filtersBar.filtersHeading") }) }) }) }) }), jsx(PopoverContent, { cellPadding: 100, children:
|
|
942
|
-
? searchResultsGrouped
|
|
943
|
-
: showCustomFilters
|
|
944
|
-
? filtersToShowGrouped
|
|
945
|
-
: removeCustomFields(filtersToShowGrouped) }), hasCustomFields && !showCustomFilters && !searchText ? (jsx(CustomFieldsHiddenGroup, { appliedCustomFields: appliedCustomFields, filterBarConfig: filterBarConfig, onShow: () => {
|
|
946
|
-
setShowCustomFilters(true);
|
|
947
|
-
} })) : null] })] }) })] }));
|
|
1027
|
+
return (jsxs(Fragment, { children: [jsx(PopoverTrigger, { children: jsx("div", { "data-testid": "starred-filters-menu-trigger", id: "starred-filters-menu-trigger", children: jsx(Tooltip, { disabled: !compact || modalState.isOpen, label: jsx(MultipleFilterTooltipLabel, { filterBarConfig: filterBarConfig }), children: jsx(Button, { prefix: jsx(Icon, { ariaHidden: true, color: filterBarConfig.appliedFilterKeys().length > 0 ? "primary" : undefined, name: "Filter", size: "small" }), size: "small", suffix: compact && showAppliedFiltersCount && filterBarConfig.appliedFilterKeys().length > 0 && isSm ? (jsxs("div", { children: [jsxs("span", { "aria-hidden": true, children: ["(", filterBarConfig.appliedFilterKeys().length, ")"] }), jsxs("span", { className: "sr-only", children: [filterBarConfig.appliedFilterKeys().length, " filters applied"] })] })) : undefined, variant: "secondary", ...buttonProps, children: title !== "" ? (jsx("span", { className: "hidden sm:block", children: title ?? t("filtersBar.filtersHeading") })) : null }) }) }) }), jsx(PopoverContent, { cellPadding: 100, children: jsx(FiltersMenuContent, { appliedCustomFields: appliedCustomFields, filterBarConfig: filterBarConfig, filtersToShowGrouped: filtersToShowGrouped, hasCustomFields: hasCustomFields, removeCustomFieldsGroup: removeCustomFieldsGroup, searchResultsGrouped: searchResultsGrouped, searchText: searchText, setSearchText: setSearchText, setShowCustomFilters: setShowCustomFilters, showCustomFilters: showCustomFilters }) })] }));
|
|
948
1028
|
} }), showDirectlyFilters.length > 0 ? (jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: showDirectlyFilters })) : null, !compact ? (jsxs(Fragment, { children: [appliedFilters.filter(filter => !filter.showDirectly).length > 0 ? (jsx("div", { className: "h-4 w-[1px] bg-slate-300", "data-testid": "applied-filters-buttons" })) : null, jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: appliedFilters }), filterBarConfig.appliedFilterKeys().length > 0 ? (jsx(ResetFiltersButton, { resetFiltersToInitialState: filterBarConfig.resetFiltersToInitialState })) : null] })) : null] }));
|
|
949
1029
|
};
|
|
950
|
-
const Separator = () => jsx("hr", { className: "border-secondary-200", role: "separator" });
|
|
951
|
-
const FiltersAppliedCountLabel = ({ filterBarConfig, }) => {
|
|
952
|
-
const [t] = useTranslation();
|
|
953
|
-
switch (filterBarConfig.appliedFilterKeys().length) {
|
|
954
|
-
case 0:
|
|
955
|
-
return t("filtersBar.appliedFiltersTooltip.none");
|
|
956
|
-
case 1:
|
|
957
|
-
return filterBarConfig.appliedFilterKeys()[0] ? t("filtersMenu.appliedFiltersLabel.singular") : null;
|
|
958
|
-
default:
|
|
959
|
-
return jsx(Fragment, { children: t("filtersMenu.appliedFiltersLabel.plural", { count: filterBarConfig.appliedFilterKeys().length }) });
|
|
960
|
-
}
|
|
961
|
-
};
|
|
962
|
-
const CustomFieldsHiddenGroup = ({ appliedCustomFields, filterBarConfig, onShow, }) => {
|
|
963
|
-
const [t] = useTranslation();
|
|
964
|
-
return (jsxs(Fragment, { children: [jsx(Separator, {}), jsxs("div", { children: [jsx(Button, { "aria-controls": "filters-list", className: "text-primary-600 w-full justify-between px-3", onClick: onShow, prefix: jsx(Text, { className: "text-secondary-400", size: "small", uppercase: true, weight: "bold", children: t("filtersBar.groups.CUSTOM_FIELDS") }), size: "small", variant: "ghost-neutral", children: t("filtersBar.showAll") }), appliedCustomFields.length > 0 ? (jsx("ul", { "aria-label": "Visible custom fields", "data-testid": "applied-custom-fields-list", children: jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: appliedCustomFields, visualStyle: "list-item" }) })) : null] })] }));
|
|
965
|
-
};
|
|
966
|
-
const removeCustomFields = (filtersGrouped) => filtersGrouped.filter(group => group.key !== "CUSTOM_FIELDS");
|
|
967
1030
|
|
|
968
1031
|
/**
|
|
969
1032
|
* Filter is a React component that renders a filter element based on the provided filter definition and state.
|
|
@@ -1222,8 +1285,8 @@ const HierarchicalCheckboxFilter = ({ filterDefinition, filterBarActions, option
|
|
|
1222
1285
|
/**
|
|
1223
1286
|
* The FilterBar component serves as a wrapper for managing filters.
|
|
1224
1287
|
*/
|
|
1225
|
-
const FilterBar = ({ hiddenFilters, className, filterBarDefinition, filterBarConfig, compact = true, title, }) => {
|
|
1226
|
-
return (jsx(FiltersMenu, { className: className, compact: compact, dataTestId: `${filterBarConfig.name}-filterbar`, filterBarConfig: filterBarConfig, filterBarDefinition: filterBarDefinition, hiddenFilters: hiddenFilters, title: title }));
|
|
1288
|
+
const FilterBar = ({ hiddenFilters, className, filterBarDefinition, filterBarConfig, compact = true, title, allowShowFiltersDirectly = true, }) => {
|
|
1289
|
+
return (jsx(FiltersMenu, { allowShowFiltersDirectly: allowShowFiltersDirectly, className: className, compact: compact, dataTestId: `${filterBarConfig.name}-filterbar`, filterBarConfig: filterBarConfig, filterBarDefinition: filterBarDefinition, hiddenFilters: hiddenFilters, title: title }));
|
|
1227
1290
|
};
|
|
1228
1291
|
|
|
1229
1292
|
// Can't import jest.fn so must define a function that does nothing but mimics the jest.fn
|
|
@@ -1885,8 +1948,8 @@ const useFilterBar = ({ name, onValuesChange, filterBarDefinition, }) => {
|
|
|
1885
1948
|
setValue: (key, callback) => setValue(setFilterBarConfig, key, callback),
|
|
1886
1949
|
});
|
|
1887
1950
|
useEffect(() => {
|
|
1888
|
-
onValuesChange?.(filterBarConfig.values);
|
|
1889
1951
|
saveData(filterBarConfig, filterBarDefinition);
|
|
1952
|
+
onValuesChange?.(filterBarConfig.values);
|
|
1890
1953
|
}, [filterBarConfig, filterBarDefinition, onValuesChange, saveData]);
|
|
1891
1954
|
return useMemo(() => {
|
|
1892
1955
|
return {
|
|
@@ -2039,4 +2102,4 @@ const mergeFilters = (filterBarDefinition, extraFilters) => {
|
|
|
2039
2102
|
*/
|
|
2040
2103
|
setupLibraryTranslations();
|
|
2041
2104
|
|
|
2042
|
-
export { DefaultCheckboxFilter, DefaultDateRangeFilter, DefaultMinMaxFilter, DefaultRadioFilter, DynamicFilterList, FilterBar, FilterComponent, FilterEvents, FilterHeader, FilterResults, FilterTableComponent, FiltersMenu, FiltersRenderer, GroupedFiltersList, HierarchicalCheckboxFilter, ResetFiltersButton, areaFilterGeoJsonGeometrySchema, isAreaFilterValue, isArrayFilterValue, isBooleanValue, isDateRangeValue, isMinMaxFilterValue, isStringArrayFilterValue, isValueName, isValueNameArray, mergeFilters, mockFilterBar, toggleFilterValue, useFilterBar, useFilterBarAsync, useSearchParamAsFilter, validateFilter };
|
|
2105
|
+
export { DefaultCheckboxFilter, DefaultDateRangeFilter, DefaultMinMaxFilter, DefaultRadioFilter, DynamicFilterList, FilterBar, FilterButtonTooltipLabel, FilterComponent, FilterEvents, FilterHeader, FilterResults, FilterTableComponent, FiltersMenu, FiltersMenuContent, FiltersRenderer, GroupedFiltersList, HierarchicalCheckboxFilter, ResetFiltersButton, areaFilterGeoJsonGeometrySchema, isAreaFilterValue, isArrayFilterValue, isBooleanValue, isDateRangeValue, isMinMaxFilterValue, isStringArrayFilterValue, isValueName, isValueNameArray, mergeFilters, mockFilterBar, toggleFilterValue, useFilterBar, useFilterBarAsync, useFiltersMenu, useSearchParamAsFilter, validateFilter };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/filters-filter-bar",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.201",
|
|
4
4
|
"repository": "https://github.com/Trackunit/manager",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"engines": {
|
|
@@ -14,17 +14,17 @@
|
|
|
14
14
|
"tailwind-merge": "^2.0.0",
|
|
15
15
|
"string-ts": "^2.0.0",
|
|
16
16
|
"zod": "3.23.4",
|
|
17
|
-
"@trackunit/iris-app-api": "1.3.
|
|
17
|
+
"@trackunit/iris-app-api": "1.3.128",
|
|
18
18
|
"@trackunit/react-core-hooks": "1.3.131",
|
|
19
|
-
"@trackunit/react-filter-components": "1.3.
|
|
20
|
-
"@trackunit/react-date-and-time-components": "1.3.
|
|
19
|
+
"@trackunit/react-filter-components": "1.3.166",
|
|
20
|
+
"@trackunit/react-date-and-time-components": "1.3.168",
|
|
21
21
|
"@trackunit/shared-utils": "1.5.121",
|
|
22
|
-
"@trackunit/react-form-components": "1.3.
|
|
22
|
+
"@trackunit/react-form-components": "1.3.166",
|
|
23
23
|
"@trackunit/react-core-contexts-api": "1.4.127",
|
|
24
24
|
"@trackunit/geo-json-utils": "1.3.121",
|
|
25
|
-
"@trackunit/i18n-library-translation": "1.3.
|
|
25
|
+
"@trackunit/i18n-library-translation": "1.3.137",
|
|
26
26
|
"@trackunit/css-class-variance-utilities": "1.3.121",
|
|
27
|
-
"@trackunit/react-components": "1.4.
|
|
27
|
+
"@trackunit/react-components": "1.4.146",
|
|
28
28
|
"@trackunit/react-test-setup": "1.0.11"
|
|
29
29
|
},
|
|
30
30
|
"module": "./index.esm.js",
|
package/src/lib/FilterBar.d.ts
CHANGED
|
@@ -26,9 +26,13 @@ interface FilterBarProps<TFilterBarDefinition extends FilterBarDefinition> {
|
|
|
26
26
|
* The title of the filter bar default is "Filters" (translated)
|
|
27
27
|
*/
|
|
28
28
|
title?: string;
|
|
29
|
+
/**
|
|
30
|
+
* If true, the filters marked as showDirectly will not be shown directly in the filter bar
|
|
31
|
+
*/
|
|
32
|
+
allowShowFiltersDirectly?: boolean;
|
|
29
33
|
}
|
|
30
34
|
/**
|
|
31
35
|
* The FilterBar component serves as a wrapper for managing filters.
|
|
32
36
|
*/
|
|
33
|
-
export declare const FilterBar: <TFilterBarDefinition extends FilterBarDefinition>({ hiddenFilters, className, filterBarDefinition, filterBarConfig, compact, title, }: FilterBarProps<TFilterBarDefinition>) => import("react/jsx-runtime").JSX.Element;
|
|
37
|
+
export declare const FilterBar: <TFilterBarDefinition extends FilterBarDefinition>({ hiddenFilters, className, filterBarDefinition, filterBarConfig, compact, title, allowShowFiltersDirectly, }: FilterBarProps<TFilterBarDefinition>) => import("react/jsx-runtime").JSX.Element;
|
|
34
38
|
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { FilterBarConfig, FilterBarDefinition, FilterMapActions, FilterMapGetter } from "../types/FilterTypes";
|
|
2
|
+
/**
|
|
3
|
+
* Tooltip label for the filter button
|
|
4
|
+
*/
|
|
5
|
+
export declare const FilterButtonTooltipLabel: <TFilterBarDefinition extends FilterBarDefinition>({ filterBarConfig, }: {
|
|
6
|
+
filterBarConfig: FilterBarConfig<TFilterBarDefinition> & FilterMapActions & FilterMapGetter;
|
|
7
|
+
}) => string | import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import { CommonProps } from "@trackunit/react-components";
|
|
1
|
+
import { ButtonProps, CommonProps } from "@trackunit/react-components";
|
|
2
2
|
import { ReactElement } from "react";
|
|
3
3
|
import { FilterBarConfig, FilterBarDefinition, FilterMapActions, FilterMapGetter } from "../types/FilterTypes";
|
|
4
4
|
interface FiltersMenuProps<TFilterBarDefinition extends FilterBarDefinition> extends CommonProps {
|
|
5
|
+
/**
|
|
6
|
+
* If true, the filters marked as showDirectly will not be shown directly in the filter bar
|
|
7
|
+
*/
|
|
8
|
+
allowShowFiltersDirectly?: boolean;
|
|
5
9
|
/**
|
|
6
10
|
* Configuration for the filter bar.
|
|
7
11
|
*/
|
|
@@ -9,7 +13,7 @@ interface FiltersMenuProps<TFilterBarDefinition extends FilterBarDefinition> ext
|
|
|
9
13
|
/**
|
|
10
14
|
* The definition of the filter bar, specifying its structure and filters.
|
|
11
15
|
*/
|
|
12
|
-
filterBarDefinition:
|
|
16
|
+
filterBarDefinition: TFilterBarDefinition;
|
|
13
17
|
/**
|
|
14
18
|
* If you want some of the filters to be hidden, but still programmatically enabled
|
|
15
19
|
*/
|
|
@@ -18,10 +22,18 @@ interface FiltersMenuProps<TFilterBarDefinition extends FilterBarDefinition> ext
|
|
|
18
22
|
* If true, the starred filters will be displayed in a compact mode
|
|
19
23
|
*/
|
|
20
24
|
compact?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* If true, the applied filters count will be shown in the filter button, only visible when compact is true
|
|
27
|
+
*/
|
|
28
|
+
showAppliedFiltersCount?: boolean;
|
|
21
29
|
/**
|
|
22
30
|
* The title of the filter bar default is "Filters" (translated)
|
|
23
31
|
*/
|
|
24
32
|
title?: string;
|
|
33
|
+
/**
|
|
34
|
+
* The icon props for the filter button to override the icon look and feel.
|
|
35
|
+
*/
|
|
36
|
+
buttonProps?: ButtonProps;
|
|
25
37
|
}
|
|
26
38
|
/**
|
|
27
39
|
* FilterMenu is a React component that displays a list of filters in a popover menu based on the provided filter bar configuration.
|
|
@@ -29,5 +41,5 @@ interface FiltersMenuProps<TFilterBarDefinition extends FilterBarDefinition> ext
|
|
|
29
41
|
* @template TFilterBarDefinition - The type representing the filter bar definition.
|
|
30
42
|
* @returns {ReactElement} - Returns the FilterMenu component.
|
|
31
43
|
*/
|
|
32
|
-
export declare const FiltersMenu: <TFilterBarDefinition extends FilterBarDefinition>({ filterBarDefinition, filterBarConfig, hiddenFilters, compact, title, dataTestId, className, }: FiltersMenuProps<TFilterBarDefinition>) => ReactElement;
|
|
44
|
+
export declare const FiltersMenu: <TFilterBarDefinition extends FilterBarDefinition>({ filterBarDefinition, filterBarConfig, hiddenFilters, compact, title, dataTestId, className, showAppliedFiltersCount, buttonProps, allowShowFiltersDirectly, }: FiltersMenuProps<TFilterBarDefinition>) => ReactElement;
|
|
33
45
|
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { GroupOfFilters } from "../hooks/useGroupFilters";
|
|
2
|
+
import { FilterBarConfig, FilterBarDefinition, FilterDefinition, FilterMapActions, FilterMapGetter } from "../types/FilterTypes";
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
export declare const FiltersMenuContent: ({ filterBarConfig, setShowCustomFilters, setSearchText, searchText, searchResultsGrouped, filtersToShowGrouped, removeCustomFieldsGroup, hasCustomFields, appliedCustomFields, showCustomFilters, }: {
|
|
7
|
+
filterBarConfig: FilterBarConfig<FilterBarDefinition> & FilterMapActions & FilterMapGetter;
|
|
8
|
+
setShowCustomFilters: (show: boolean) => void;
|
|
9
|
+
setSearchText: (text: string) => void;
|
|
10
|
+
searchText: string;
|
|
11
|
+
searchResultsGrouped: GroupOfFilters[];
|
|
12
|
+
filtersToShowGrouped: GroupOfFilters[];
|
|
13
|
+
removeCustomFieldsGroup: (groupOfFilters: GroupOfFilters[]) => GroupOfFilters[];
|
|
14
|
+
hasCustomFields: boolean;
|
|
15
|
+
appliedCustomFields: FilterDefinition[];
|
|
16
|
+
showCustomFilters: boolean;
|
|
17
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -4,10 +4,12 @@ export * from "./DefaultFilterTypes";
|
|
|
4
4
|
export * from "./DefaultMinMaxFilter";
|
|
5
5
|
export * from "./DefaultRadioFilter";
|
|
6
6
|
export * from "./DynamicFilterList";
|
|
7
|
+
export * from "./FilterButtonTooltipLabel";
|
|
7
8
|
export * from "./FilterComponent";
|
|
8
9
|
export * from "./FilterHeader";
|
|
9
10
|
export * from "./FilterResults";
|
|
10
11
|
export * from "./FiltersMenu";
|
|
12
|
+
export * from "./FiltersMenuContent";
|
|
11
13
|
export * from "./FiltersRenderer";
|
|
12
14
|
export * from "./FilterTableComponent";
|
|
13
15
|
export * from "./GroupedFiltersList";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { FilterBarConfig, FilterBarDefinition, FilterMapActions, FilterMapGetter } from "../types/FilterTypes";
|
|
2
|
+
import { GroupOfFilters } from "./useGroupFilters";
|
|
3
|
+
type UseFiltersMenuProps<TFilterBarDefinition extends FilterBarDefinition> = {
|
|
4
|
+
filterBarDefinition: TFilterBarDefinition;
|
|
5
|
+
filterBarConfig: FilterBarConfig<TFilterBarDefinition> & FilterMapActions & FilterMapGetter;
|
|
6
|
+
hiddenFilters?: string[];
|
|
7
|
+
allowShowFiltersDirectly?: boolean;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* This hook is used to manage the filters menu.
|
|
11
|
+
* It returns the filters that should be shown in the menu, the filters that should be shown directly in the filter bar,
|
|
12
|
+
* and the filters that should be shown in the search results.
|
|
13
|
+
*/
|
|
14
|
+
export declare const useFiltersMenu: <TFilterBarDefinition extends FilterBarDefinition>({ filterBarDefinition, filterBarConfig, hiddenFilters, allowShowFiltersDirectly, }: UseFiltersMenuProps<TFilterBarDefinition>) => {
|
|
15
|
+
appliedFilters: import("../types/FilterTypes").FilterDefinition[];
|
|
16
|
+
hasCustomFields: boolean;
|
|
17
|
+
showDirectlyFilters: import("../types/FilterTypes").FilterDefinition[];
|
|
18
|
+
appliedCustomFields: import("../types/FilterTypes").FilterDefinition[];
|
|
19
|
+
searchText: string;
|
|
20
|
+
setSearchText: import("react").Dispatch<string>;
|
|
21
|
+
filtersToShowGrouped: GroupOfFilters[];
|
|
22
|
+
searchResultsGrouped: GroupOfFilters[];
|
|
23
|
+
removeCustomFieldsGroup: (groupOfFilters: GroupOfFilters[]) => GroupOfFilters[];
|
|
24
|
+
};
|
|
25
|
+
export {};
|
package/src/lib/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from "./FilterBar";
|
|
|
3
3
|
export * from "./hooks/mockFilterBar";
|
|
4
4
|
export * from "./hooks/useFilterBar";
|
|
5
5
|
export * from "./hooks/useFilterBarAsync";
|
|
6
|
+
export * from "./hooks/useFiltersMenu";
|
|
6
7
|
export * from "./hooks/useSearchParamAsFilter";
|
|
7
8
|
export * from "./types/FilterTypes";
|
|
8
9
|
export * from "./utils/FilterEvents";
|
|
@@ -66,7 +66,7 @@ export type FilterMapGetter = {
|
|
|
66
66
|
export type FilterName = string;
|
|
67
67
|
export type FilterBarDefinition = Record<FilterName, FilterDefinition>;
|
|
68
68
|
export type FilterTranslation = string;
|
|
69
|
-
export type FilterGroup = "METADATA" | "STATUS" | "SITES" | "CUSTOMERS" | "OTHER" | "CUSTOM_FIELDS" | "MY_NETWORK" | "ASSET" | "OPERATOR" | "SERVICE" | "RENTAL" | "INTEGRATION";
|
|
69
|
+
export type FilterGroup = "METADATA" | "STATUS" | "SITES" | "CUSTOMERS" | "OTHER" | "CUSTOM_FIELDS" | "MY_NETWORK" | "FLEET_HEALTH" | "ASSET" | "OPERATOR" | "SERVICE" | "RENTAL" | "INTEGRATION";
|
|
70
70
|
export interface AbstractFilterDefinition {
|
|
71
71
|
/**
|
|
72
72
|
* The unique key or identifier for the filter.
|