@trackunit/filters-filter-bar 1.3.200 → 1.3.202
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 +177 -120
- package/index.esm.js +176 -122
- 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/FilterTooltips/MultipleFilterTooltipLabel.d.ts +3 -2
- package/src/lib/components/FilterTooltips/SingleFilterTooltipLabel.d.ts +3 -2
- 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/index.cjs.js
CHANGED
|
@@ -110,7 +110,7 @@ var defaultTranslations = {
|
|
|
110
110
|
"filter.more.options.if.you.search.title": "Over {{count}} items found.",
|
|
111
111
|
"filtersBar.appliedFiltersTooltip.none": "No filters applied",
|
|
112
112
|
"filtersBar.appliedFiltersTooltip.plural": "{{count}} filters applied:",
|
|
113
|
-
"filtersBar.appliedFiltersTooltip.singular": "{{filterName}} filter applied
|
|
113
|
+
"filtersBar.appliedFiltersTooltip.singular": "{{filterName}} filter applied",
|
|
114
114
|
"filtersBar.defaultAssetFilters.followedFilter.ALL": "All Assets",
|
|
115
115
|
"filtersBar.defaultAssetFilters.followedFilter.allLabel": "All Assets",
|
|
116
116
|
"filtersBar.defaultAssetFilters.followedFilter.FOLLOWED": "Followed Only",
|
|
@@ -645,6 +645,25 @@ const DefaultRadioFilter = ({ filterDefinition, filterBarActions, options, loadi
|
|
|
645
645
|
}, value: selectedRadioId?.key || "", children: jsxRuntime.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" }) })) })] }));
|
|
646
646
|
};
|
|
647
647
|
|
|
648
|
+
/**
|
|
649
|
+
* Tooltip label for the filter button
|
|
650
|
+
*/
|
|
651
|
+
const FilterButtonTooltipLabel = ({ filterBarConfig, }) => {
|
|
652
|
+
const [t] = useTranslation();
|
|
653
|
+
switch (filterBarConfig.appliedFilterKeys().length) {
|
|
654
|
+
case 0:
|
|
655
|
+
return t("filtersBar.appliedFiltersTooltip.none");
|
|
656
|
+
case 1:
|
|
657
|
+
return filterBarConfig.appliedFilterKeys()[0]
|
|
658
|
+
? t("filtersBar.appliedFiltersTooltip.singular", {
|
|
659
|
+
filterName: filterBarConfig.getFilterTitle(filterBarConfig.appliedFilterKeys()[0]),
|
|
660
|
+
})
|
|
661
|
+
: null; // should never happen though
|
|
662
|
+
default:
|
|
663
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [t("filtersBar.appliedFiltersTooltip.plural", { count: filterBarConfig.appliedFilterKeys().length }), jsxRuntime.jsx("ul", { className: "list-inside", children: filterBarConfig.appliedFilterKeys().map((appliedFilterKey, index) => (jsxRuntime.jsx("li", { className: "list-disc", children: filterBarConfig.getFilterTitle(appliedFilterKey) }, index))) })] }));
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
|
|
648
667
|
/**
|
|
649
668
|
* Returns the two first values, appends counter if more.
|
|
650
669
|
*
|
|
@@ -760,6 +779,64 @@ const useGroupFilters = (filterDefinitions, hiddenFilters) => {
|
|
|
760
779
|
};
|
|
761
780
|
const uniqueKeysFromGroups = (filters) => [...new Set(filters.map(filter => filter.group))];
|
|
762
781
|
|
|
782
|
+
/**
|
|
783
|
+
* This hook is used to manage the filters menu.
|
|
784
|
+
* It returns the filters that should be shown in the menu, the filters that should be shown directly in the filter bar,
|
|
785
|
+
* and the filters that should be shown in the search results.
|
|
786
|
+
*/
|
|
787
|
+
const useFiltersMenu = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [], allowShowFiltersDirectly = true, }) => {
|
|
788
|
+
const hideInMenu = react.useMemo(() => {
|
|
789
|
+
return sharedUtils.objectValues(filterBarDefinition)
|
|
790
|
+
.map(filter => {
|
|
791
|
+
const showInFilterBar = filter.showInFilterBar ? filter.showInFilterBar() : true;
|
|
792
|
+
const showInStarredMenu = filter.showInStarredMenu ? filter.showInStarredMenu() : true;
|
|
793
|
+
const showMenuAnywayBecauseFilterHasChanged = filterBarConfig.appliedFilterKeys().includes(filter.filterKey);
|
|
794
|
+
return (!showInFilterBar || !showInStarredMenu) && !showMenuAnywayBecauseFilterHasChanged
|
|
795
|
+
? filter.filterKey
|
|
796
|
+
: null;
|
|
797
|
+
})
|
|
798
|
+
.filter(sharedUtils.truthy);
|
|
799
|
+
}, [filterBarConfig, filterBarDefinition]);
|
|
800
|
+
const removeCustomFieldsGroup = react.useCallback((groupOfFilters) => groupOfFilters.filter(group => group.key !== "CUSTOM_FIELDS"), []);
|
|
801
|
+
const { filtersGrouped } = useGroupFilters(sharedUtils.objectValues(filterBarDefinition), [...hideInMenu, ...hiddenFilters]);
|
|
802
|
+
const { appliedFilters, filtersToShow, showDirectlyFilters, hasCustomFields } = react.useMemo(() => {
|
|
803
|
+
const allFilters = filtersGrouped.map(group => group.filters).flat();
|
|
804
|
+
return {
|
|
805
|
+
appliedFilters: allFilters.filter(filter => filterBarConfig.appliedFilterKeys().includes(filter.filterKey)),
|
|
806
|
+
filtersToShow: allFilters.filter(filter => !filter.showDirectly),
|
|
807
|
+
showDirectlyFilters: allFilters.filter(filter => filter.showDirectly && allowShowFiltersDirectly),
|
|
808
|
+
hasCustomFields: allFilters.some(filter => filter.group === "CUSTOM_FIELDS"),
|
|
809
|
+
};
|
|
810
|
+
}, [filterBarConfig, filtersGrouped, allowShowFiltersDirectly]);
|
|
811
|
+
const [searchResults, searchText, setSearchText] = reactCoreHooks.useTextSearch(filtersToShow, item => [item.title]);
|
|
812
|
+
const { filtersGrouped: searchResultsGrouped } = useGroupFilters(searchResults, []);
|
|
813
|
+
const { filtersGrouped: filtersToShowGrouped } = useGroupFilters(filtersToShow, []);
|
|
814
|
+
const appliedCustomFields = react.useMemo(() => appliedFilters.filter(filter => filter.group === "CUSTOM_FIELDS"), [appliedFilters]);
|
|
815
|
+
return react.useMemo(() => {
|
|
816
|
+
return {
|
|
817
|
+
appliedFilters,
|
|
818
|
+
hasCustomFields,
|
|
819
|
+
showDirectlyFilters,
|
|
820
|
+
appliedCustomFields,
|
|
821
|
+
searchText,
|
|
822
|
+
setSearchText,
|
|
823
|
+
filtersToShowGrouped,
|
|
824
|
+
searchResultsGrouped,
|
|
825
|
+
removeCustomFieldsGroup,
|
|
826
|
+
};
|
|
827
|
+
}, [
|
|
828
|
+
appliedFilters,
|
|
829
|
+
hasCustomFields,
|
|
830
|
+
showDirectlyFilters,
|
|
831
|
+
appliedCustomFields,
|
|
832
|
+
searchText,
|
|
833
|
+
setSearchText,
|
|
834
|
+
filtersToShowGrouped,
|
|
835
|
+
searchResultsGrouped,
|
|
836
|
+
removeCustomFieldsGroup,
|
|
837
|
+
]);
|
|
838
|
+
};
|
|
839
|
+
|
|
763
840
|
/**
|
|
764
841
|
* FiltersRenderer renders an array of Filter components from filter definitions
|
|
765
842
|
* It ignores hidden filters.
|
|
@@ -780,6 +857,60 @@ const FiltersRenderer = ({ filters, filterBarConfig, visualStyle, }) => {
|
|
|
780
857
|
.map(filter => (jsxRuntime.jsx(FilterComponent, { filter: filter, filterBarActions: filterBarConfig, filterState: { values: filterBarConfig.values, setters: filterBarConfig.setters }, visualStyle: visualStyle }, `filter-${filter.filterKey}`)));
|
|
781
858
|
};
|
|
782
859
|
|
|
860
|
+
/**
|
|
861
|
+
* FiltersList is a React component that displays a list of filters within a filter bar.
|
|
862
|
+
*
|
|
863
|
+
* @returns {ReactElement} - Returns the FiltersList component.
|
|
864
|
+
*/
|
|
865
|
+
const GroupedFiltersList = ({ filterBarConfig, filtersGrouped, className, dataTestId = "grouped-filters-list", }) => {
|
|
866
|
+
return (jsxRuntime.jsx("div", { className: className, "data-testid": dataTestId, role: "menu", children: filtersGrouped.map((group, idx) => {
|
|
867
|
+
const isLastGroup = idx === filtersGrouped.length - 1;
|
|
868
|
+
return (jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx(reactComponents.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 }), jsxRuntime.jsx("ul", { "aria-labelledby": `${group.key}-group-title`, className: "grid", "data-testid": `${group.key}-group-list`, children: jsxRuntime.jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: group.filters, visualStyle: "list-item" }) })] }), isLastGroup ? null : jsxRuntime.jsx("div", { className: "bg-secondary-200 h-[1px] w-full", role: "separator" })] }, group.key));
|
|
869
|
+
}) }));
|
|
870
|
+
};
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* ResetFiltersButton is a React component that provides a button for resetting filters.
|
|
874
|
+
*
|
|
875
|
+
* @returns {ReactElement | null} The rendered ResetFiltersButton component, or null if no filters have been applied.
|
|
876
|
+
*/
|
|
877
|
+
const ResetFiltersButton = ({ resetFiltersToInitialState, dataTestId, className, }) => {
|
|
878
|
+
const [t] = useTranslation();
|
|
879
|
+
return (jsxRuntime.jsxs(reactComponents.Button, { className: className, dataTestId: dataTestId ?? "reset-filters-button", onClick: () => {
|
|
880
|
+
resetFiltersToInitialState();
|
|
881
|
+
}, size: "small", variant: "ghost", children: [t("filtersBar.resetFilters"), jsxRuntime.jsx("span", { className: "sr-only", children: "Resets all applied filters" })] }));
|
|
882
|
+
};
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
*
|
|
886
|
+
*/
|
|
887
|
+
const FiltersMenuContent = ({ filterBarConfig, setShowCustomFilters, setSearchText, searchText, searchResultsGrouped, filtersToShowGrouped, removeCustomFieldsGroup, hasCustomFields, appliedCustomFields, showCustomFilters, }) => {
|
|
888
|
+
const [t] = useTranslation();
|
|
889
|
+
return (jsxRuntime.jsxs(reactComponents.Card, { className: "max-h-[min(600px,_calc(100dvh-32px))] w-[300px] overflow-y-hidden", dataTestId: "starred-filters-menu-popover", children: [jsxRuntime.jsxs("div", { className: " flex-col gap-1 p-1", children: [jsxRuntime.jsx(reactFormComponents.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 }), jsxRuntime.jsxs("div", { className: "flex h-7 items-center justify-between gap-1 px-3", children: [jsxRuntime.jsx(reactComponents.Text, { className: "text-secondary-400", size: "small", children: jsxRuntime.jsx(FiltersAppliedCountLabel, { filterBarConfig: filterBarConfig }) }), filterBarConfig.appliedFilterKeys().length > 0 ? (jsxRuntime.jsx(ResetFiltersButton, { resetFiltersToInitialState: filterBarConfig.resetFiltersToInitialState })) : null] })] }), jsxRuntime.jsx(Separator, {}), jsxRuntime.jsxs(reactComponents.CardBody, { className: "gap-1 p-1", density: "none", disableGap: true, children: [jsxRuntime.jsx(GroupedFiltersList, { className: "flex flex-col gap-1", filterBarConfig: filterBarConfig, filtersGrouped: searchText
|
|
890
|
+
? searchResultsGrouped
|
|
891
|
+
: showCustomFilters
|
|
892
|
+
? filtersToShowGrouped
|
|
893
|
+
: removeCustomFieldsGroup(filtersToShowGrouped) }), hasCustomFields && !showCustomFilters && !searchText ? (jsxRuntime.jsx(CustomFieldsHiddenGroup, { appliedCustomFields: appliedCustomFields, filterBarConfig: filterBarConfig, onShow: () => {
|
|
894
|
+
setShowCustomFilters(true);
|
|
895
|
+
} })) : null] })] }));
|
|
896
|
+
};
|
|
897
|
+
const Separator = () => jsxRuntime.jsx("hr", { className: "border-secondary-200", role: "separator" });
|
|
898
|
+
const FiltersAppliedCountLabel = ({ filterBarConfig, }) => {
|
|
899
|
+
const [t] = useTranslation();
|
|
900
|
+
switch (filterBarConfig.appliedFilterKeys().length) {
|
|
901
|
+
case 0:
|
|
902
|
+
return t("filtersBar.appliedFiltersTooltip.none");
|
|
903
|
+
case 1:
|
|
904
|
+
return filterBarConfig.appliedFilterKeys()[0] ? t("filtersMenu.appliedFiltersLabel.singular") : null;
|
|
905
|
+
default:
|
|
906
|
+
return jsxRuntime.jsx(jsxRuntime.Fragment, { children: t("filtersMenu.appliedFiltersLabel.plural", { count: filterBarConfig.appliedFilterKeys().length }) });
|
|
907
|
+
}
|
|
908
|
+
};
|
|
909
|
+
const CustomFieldsHiddenGroup = ({ appliedCustomFields, filterBarConfig, onShow, }) => {
|
|
910
|
+
const [t] = useTranslation();
|
|
911
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(Separator, {}), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx(reactComponents.Button, { "aria-controls": "filters-list", className: "text-primary-600 w-full justify-between px-3", onClick: onShow, prefix: jsxRuntime.jsx(reactComponents.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 ? (jsxRuntime.jsx("ul", { "aria-label": "Visible custom fields", "data-testid": "applied-custom-fields-list", children: jsxRuntime.jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: appliedCustomFields, visualStyle: "list-item" }) })) : null] })] }));
|
|
912
|
+
};
|
|
913
|
+
|
|
783
914
|
/**
|
|
784
915
|
* TooltipValues component that displays formatted tooltip values based on the provided input.
|
|
785
916
|
*
|
|
@@ -788,40 +919,28 @@ const FiltersRenderer = ({ filters, filterBarConfig, visualStyle, }) => {
|
|
|
788
919
|
* @returns {ReactElement} The rendered TooltipValues component.
|
|
789
920
|
*/
|
|
790
921
|
const TooltipValues = ({ values }) => {
|
|
791
|
-
return jsxRuntime.jsx("div", { children: renderTooltipValues(values) });
|
|
792
|
-
};
|
|
793
|
-
const renderTooltipValues = (values) => {
|
|
794
922
|
if (values === undefined) {
|
|
795
|
-
return
|
|
923
|
+
return jsxRuntime.jsx(jsxRuntime.Fragment, {});
|
|
796
924
|
}
|
|
797
925
|
// Array of objects ValueName[]
|
|
798
926
|
if (Array.isArray(values) && typeof values[0] === "object") {
|
|
799
|
-
return (jsxRuntime.jsx("ul", { className: "list-inside", children: values.map((value, index) => (jsxRuntime.jsx("li", { className: "list-disc", children: value.name }, index))) }));
|
|
927
|
+
return (jsxRuntime.jsx("ul", { className: "list-inside pl-2", children: values.map((value, index) => (jsxRuntime.jsx("li", { className: "list-disc text-xs font-normal", children: value.name }, index))) }));
|
|
800
928
|
}
|
|
801
929
|
// Array of strings
|
|
802
|
-
if (Array.isArray(values)) {
|
|
803
|
-
return (jsxRuntime.jsx("ul", { className: "list-inside", children: values.map((value, index) => {
|
|
804
|
-
return (jsxRuntime.jsx("li", { className: "list-disc", children: typeof value === "string" ? value : value.name }, index));
|
|
930
|
+
if (Array.isArray(values) && typeof values[0] === "string") {
|
|
931
|
+
return (jsxRuntime.jsx("ul", { className: "list-inside pl-2", children: values.map((value, index) => {
|
|
932
|
+
return (jsxRuntime.jsx("li", { className: "list-disc text-xs font-normal", children: typeof value === "string" ? value : value.name }, index));
|
|
805
933
|
}) }));
|
|
806
934
|
}
|
|
807
935
|
// single object ValueName
|
|
808
936
|
if (typeof values === "object" && values.name) {
|
|
809
|
-
return (jsxRuntime.jsx("ul", { className: "list-inside", children: jsxRuntime.jsx("li", { className: "list-disc", children: values.name }) }));
|
|
937
|
+
return (jsxRuntime.jsx("ul", { className: "list-inside pl-2", children: jsxRuntime.jsx("li", { className: "list-disc text-xs font-normal", children: values.name }) }));
|
|
810
938
|
}
|
|
811
939
|
// String or number
|
|
812
940
|
if (typeof values === "string" || typeof values === "number") {
|
|
813
|
-
return (jsxRuntime.jsx("ul", { className: "list-inside", children: jsxRuntime.jsx("li", { className: "list-disc", children: values.toString() }) }));
|
|
814
|
-
}
|
|
815
|
-
// BooleanValue
|
|
816
|
-
if (typeof values === "object" && values.booleanValue !== undefined) {
|
|
817
|
-
return values.booleanValue ? "Yes" : "No";
|
|
941
|
+
return (jsxRuntime.jsx("ul", { className: "list-inside pl-2", children: jsxRuntime.jsx("li", { className: "list-disc text-xs font-normal", children: values.toString() }) }));
|
|
818
942
|
}
|
|
819
|
-
|
|
820
|
-
if (typeof values === "object" && "min" in values && "max" in values) {
|
|
821
|
-
const minMax = values;
|
|
822
|
-
return `Min: ${minMax.min}, Max: ${minMax.max}`;
|
|
823
|
-
}
|
|
824
|
-
return null;
|
|
943
|
+
return jsxRuntime.jsx(jsxRuntime.Fragment, {});
|
|
825
944
|
};
|
|
826
945
|
|
|
827
946
|
/**
|
|
@@ -834,13 +953,16 @@ const renderTooltipValues = (values) => {
|
|
|
834
953
|
* @param {string} props.filterKey - The key identifying the filter.
|
|
835
954
|
* @returns {ReactElement} The rendered tooltip label and applied filter values.
|
|
836
955
|
*/
|
|
837
|
-
const SingleFilterTooltipLabel = ({ filterBarConfig, filterKey, }) => {
|
|
838
|
-
const title = filterBarConfig.getFilterTitle(filterKey);
|
|
839
|
-
const values = filterBarConfig.getValuesByKey(filterKey);
|
|
840
|
-
const
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
956
|
+
const SingleFilterTooltipLabel = ({ filterBarConfig, filter, filterKey, }) => {
|
|
957
|
+
const title = react.useMemo(() => filterBarConfig.getFilterTitle(filterKey), [filterBarConfig, filterKey]);
|
|
958
|
+
const values = react.useMemo(() => filterBarConfig.getValuesByKey(filterKey), [filterBarConfig, filterKey]);
|
|
959
|
+
const displayValues = react.useMemo(() => {
|
|
960
|
+
if (filter?.valueAsText) {
|
|
961
|
+
return filter.valueAsText(values);
|
|
962
|
+
}
|
|
963
|
+
return values;
|
|
964
|
+
}, [filter, values]);
|
|
965
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "text-xs font-medium", children: title }), jsxRuntime.jsx(TooltipValues, { values: displayValues })] }));
|
|
844
966
|
};
|
|
845
967
|
|
|
846
968
|
/**
|
|
@@ -854,119 +976,51 @@ const SingleFilterTooltipLabel = ({ filterBarConfig, filterKey, }) => {
|
|
|
854
976
|
* @param {string[]} [props.filterKeys] An optional list of filter keys to filter the currently applied filters.
|
|
855
977
|
* @returns {ReactElement | string} Returns a element or string for the tooltip displaying applied filters.
|
|
856
978
|
*/
|
|
857
|
-
const MultipleFilterTooltipLabel = ({ filterBarConfig, filterKeys, }) => {
|
|
979
|
+
const MultipleFilterTooltipLabel = ({ filterBarConfig, filterKeys, filters, }) => {
|
|
858
980
|
const [t] = useTranslation();
|
|
859
|
-
const
|
|
860
|
-
|
|
861
|
-
|
|
981
|
+
const appliedKeys = filterBarConfig.appliedFilterKeys();
|
|
982
|
+
const appliedFilterKeys = filterKeys ? filterKeys.filter(key => appliedKeys.includes(key)) : appliedKeys;
|
|
983
|
+
const filtersMap = filters.reduce((acc, filter) => {
|
|
984
|
+
if (filter.filterKey) {
|
|
985
|
+
acc[filter.filterKey] = filter;
|
|
986
|
+
}
|
|
987
|
+
return acc;
|
|
988
|
+
}, {});
|
|
862
989
|
switch (appliedFilterKeys.length) {
|
|
863
990
|
case 0:
|
|
864
991
|
return t("filtersBar.appliedFiltersTooltip.none");
|
|
865
992
|
case 1:
|
|
866
|
-
return jsxRuntime.jsx(SingleFilterTooltipLabel, { filterBarConfig: filterBarConfig, filterKey: appliedFilterKeys[0] });
|
|
993
|
+
return (jsxRuntime.jsx(SingleFilterTooltipLabel, { filter: filtersMap[appliedFilterKeys[0]], filterBarConfig: filterBarConfig, filterKey: appliedFilterKeys[0] }));
|
|
867
994
|
default:
|
|
868
|
-
return (jsxRuntime.
|
|
869
|
-
const title = filterBarConfig.getFilterTitle(filterKey);
|
|
870
|
-
const values = filterBarConfig.getValuesByKey(filterKey);
|
|
871
|
-
return filterBarConfig.appliedFilterKeys().includes(filterKey) ? (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("div", { children: [title, ":"] }), jsxRuntime.jsx(TooltipValues, { values: values })] }, filterKey)) : null;
|
|
872
|
-
})] }));
|
|
995
|
+
return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: appliedFilterKeys.map(filterKey => (jsxRuntime.jsx(SingleFilterTooltipLabel, { filter: filtersMap[filterKey], filterBarConfig: filterBarConfig, filterKey: filterKey }, filterKey))) }));
|
|
873
996
|
}
|
|
874
997
|
};
|
|
875
998
|
|
|
876
|
-
/**
|
|
877
|
-
* FiltersList is a React component that displays a list of filters within a filter bar.
|
|
878
|
-
*
|
|
879
|
-
* @returns {ReactElement} - Returns the FiltersList component.
|
|
880
|
-
*/
|
|
881
|
-
const GroupedFiltersList = ({ filterBarConfig, filtersGrouped, className, dataTestId = "grouped-filters-list", }) => {
|
|
882
|
-
return (jsxRuntime.jsx("div", { className: className, "data-testid": dataTestId, role: "menu", children: filtersGrouped.map((group, idx) => {
|
|
883
|
-
const isLastGroup = idx === filtersGrouped.length - 1;
|
|
884
|
-
return (jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx(reactComponents.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 }), jsxRuntime.jsx("ul", { "aria-labelledby": `${group.key}-group-title`, className: "grid", "data-testid": `${group.key}-group-list`, children: jsxRuntime.jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: group.filters, visualStyle: "list-item" }) })] }), isLastGroup ? null : jsxRuntime.jsx("div", { className: "bg-secondary-200 h-[1px] w-full", role: "separator" })] }, group.key));
|
|
885
|
-
}) }));
|
|
886
|
-
};
|
|
887
|
-
|
|
888
|
-
/**
|
|
889
|
-
* ResetFiltersButton is a React component that provides a button for resetting filters.
|
|
890
|
-
*
|
|
891
|
-
* @returns {ReactElement | null} The rendered ResetFiltersButton component, or null if no filters have been applied.
|
|
892
|
-
*/
|
|
893
|
-
const ResetFiltersButton = ({ resetFiltersToInitialState, dataTestId, className, }) => {
|
|
894
|
-
const [t] = useTranslation();
|
|
895
|
-
return (jsxRuntime.jsxs(reactComponents.Button, { className: className, dataTestId: dataTestId ?? "reset-filters-button", onClick: () => {
|
|
896
|
-
resetFiltersToInitialState();
|
|
897
|
-
}, size: "small", variant: "ghost", children: [t("filtersBar.resetFilters"), jsxRuntime.jsx("span", { className: "sr-only", children: "Resets all applied filters" })] }));
|
|
898
|
-
};
|
|
899
|
-
|
|
900
999
|
/**
|
|
901
1000
|
* FilterMenu is a React component that displays a list of filters in a popover menu based on the provided filter bar configuration.
|
|
902
1001
|
*
|
|
903
1002
|
* @template TFilterBarDefinition - The type representing the filter bar definition.
|
|
904
1003
|
* @returns {ReactElement} - Returns the FilterMenu component.
|
|
905
1004
|
*/
|
|
906
|
-
const FiltersMenu = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [], compact, title, dataTestId = "filters-menu", className, }) => {
|
|
1005
|
+
const FiltersMenu = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [], compact, title, dataTestId = "filters-menu", className, showAppliedFiltersCount = true, buttonProps, allowShowFiltersDirectly = true, }) => {
|
|
907
1006
|
const [t] = useTranslation();
|
|
908
1007
|
const { isSm } = reactComponents.useViewportBreakpoints();
|
|
909
1008
|
const [showCustomFilters, setShowCustomFilters] = react.useState(false);
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
const showInStarredMenu = filter.showInStarredMenu ? filter.showInStarredMenu() : true; // TODO: Starred menu concept should be completely removed everywhere
|
|
917
|
-
const showMenuAnywayBecauseFilterHasChanged = filterBarConfig.appliedFilterKeys().includes(filter.filterKey);
|
|
918
|
-
return (!showInFilterBar || !showInStarredMenu) && !showMenuAnywayBecauseFilterHasChanged
|
|
919
|
-
? filter.filterKey
|
|
920
|
-
: null;
|
|
921
|
-
})
|
|
922
|
-
.filter(sharedUtils.truthy);
|
|
923
|
-
}, [filterBarConfig, filterBarDefinition]);
|
|
924
|
-
const { filtersGrouped } = useGroupFilters(sharedUtils.objectValues(filterBarDefinition), [...hideInMenu, ...hiddenFilters]);
|
|
925
|
-
const { appliedFilters, filtersToShow, showDirectlyFilters, hasCustomFields } = react.useMemo(() => {
|
|
926
|
-
const allFilters = filtersGrouped.map(group => group.filters).flat();
|
|
927
|
-
return {
|
|
928
|
-
appliedFilters: allFilters.filter(filter => filterBarConfig.appliedFilterKeys().includes(filter.filterKey)),
|
|
929
|
-
filtersToShow: allFilters.filter(filter => !filter.showDirectly),
|
|
930
|
-
showDirectlyFilters: allFilters.filter(filter => filter.showDirectly),
|
|
931
|
-
hasCustomFields: allFilters.some(filter => filter.group === "CUSTOM_FIELDS"),
|
|
932
|
-
};
|
|
933
|
-
}, [filterBarConfig, filtersGrouped]);
|
|
934
|
-
const [searchResults, searchText, setSearchText] = reactCoreHooks.useTextSearch(filtersToShow, item => [item.title]);
|
|
935
|
-
const { filtersGrouped: searchResultsGrouped } = useGroupFilters(searchResults, []);
|
|
936
|
-
const { filtersGrouped: filtersToShowGrouped } = useGroupFilters(filtersToShow, []);
|
|
937
|
-
const appliedCustomFields = react.useMemo(() => appliedFilters.filter(filter => filter.group === "CUSTOM_FIELDS"), [appliedFilters]);
|
|
1009
|
+
const { appliedFilters, showDirectlyFilters, hasCustomFields, filtersToShowGrouped, searchResultsGrouped, searchText, appliedCustomFields, removeCustomFieldsGroup, setSearchText, } = useFiltersMenu({
|
|
1010
|
+
filterBarDefinition,
|
|
1011
|
+
filterBarConfig,
|
|
1012
|
+
hiddenFilters,
|
|
1013
|
+
allowShowFiltersDirectly,
|
|
1014
|
+
});
|
|
938
1015
|
return (jsxRuntime.jsxs("div", { className: tailwindMerge.twMerge("flex items-center gap-2", className), "data-testid": dataTestId, children: [jsxRuntime.jsx(reactComponents.Popover, { onOpenStateChange: open => {
|
|
939
1016
|
if (!open) {
|
|
940
1017
|
setShowCustomFilters(false);
|
|
941
1018
|
setSearchText("");
|
|
942
1019
|
}
|
|
943
1020
|
}, placement: "bottom-start", children: modalState => {
|
|
944
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(reactComponents.PopoverTrigger, { children: jsxRuntime.jsx("div", { "data-testid": "starred-filters-menu-trigger", id: "starred-filters-menu-trigger", children: jsxRuntime.jsx(reactComponents.Tooltip, { disabled: !compact || modalState.isOpen, label: jsxRuntime.jsx(MultipleFilterTooltipLabel, { filterBarConfig: filterBarConfig }), children: jsxRuntime.jsx(reactComponents.Button, { prefix: jsxRuntime.jsx(reactComponents.Icon, { ariaHidden: true, color: filterBarConfig.appliedFilterKeys().length > 0 ? "primary" : undefined, name: "Filter", size: "small" }), size: "small", suffix: compact && filterBarConfig.appliedFilterKeys().length > 0 && isSm ? (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("span", { "aria-hidden": true, children: ["(", filterBarConfig.appliedFilterKeys().length, ")"] }), jsxRuntime.jsxs("span", { className: "sr-only", children: [filterBarConfig.appliedFilterKeys().length, " filters applied"] })] })) : undefined, variant: "secondary", children: jsxRuntime.jsx("span", { className: "hidden sm:block", children: title ?? t("filtersBar.filtersHeading") }) }) }) }) }), jsxRuntime.jsx(reactComponents.PopoverContent, { cellPadding: 100, children: jsxRuntime.
|
|
945
|
-
? searchResultsGrouped
|
|
946
|
-
: showCustomFilters
|
|
947
|
-
? filtersToShowGrouped
|
|
948
|
-
: removeCustomFields(filtersToShowGrouped) }), hasCustomFields && !showCustomFilters && !searchText ? (jsxRuntime.jsx(CustomFieldsHiddenGroup, { appliedCustomFields: appliedCustomFields, filterBarConfig: filterBarConfig, onShow: () => {
|
|
949
|
-
setShowCustomFilters(true);
|
|
950
|
-
} })) : null] })] }) })] }));
|
|
1021
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(reactComponents.PopoverTrigger, { children: jsxRuntime.jsx("div", { "data-testid": "starred-filters-menu-trigger", id: "starred-filters-menu-trigger", children: jsxRuntime.jsx(reactComponents.Tooltip, { disabled: !compact || modalState.isOpen, label: jsxRuntime.jsx(MultipleFilterTooltipLabel, { filterBarConfig: filterBarConfig, filters: appliedFilters }), children: jsxRuntime.jsx(reactComponents.Button, { prefix: jsxRuntime.jsx(reactComponents.Icon, { ariaHidden: true, color: filterBarConfig.appliedFilterKeys().length > 0 ? "primary" : undefined, name: "Filter", size: "small" }), size: "small", suffix: compact && showAppliedFiltersCount && filterBarConfig.appliedFilterKeys().length > 0 && isSm ? (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("span", { "aria-hidden": true, children: ["(", filterBarConfig.appliedFilterKeys().length, ")"] }), jsxRuntime.jsxs("span", { className: "sr-only", children: [filterBarConfig.appliedFilterKeys().length, " filters applied"] })] })) : undefined, variant: "secondary", ...buttonProps, children: title !== "" ? (jsxRuntime.jsx("span", { className: "hidden sm:block", children: title ?? t("filtersBar.filtersHeading") })) : null }) }) }) }), jsxRuntime.jsx(reactComponents.PopoverContent, { cellPadding: 100, children: jsxRuntime.jsx(FiltersMenuContent, { appliedCustomFields: appliedCustomFields, filterBarConfig: filterBarConfig, filtersToShowGrouped: filtersToShowGrouped, hasCustomFields: hasCustomFields, removeCustomFieldsGroup: removeCustomFieldsGroup, searchResultsGrouped: searchResultsGrouped, searchText: searchText, setSearchText: setSearchText, setShowCustomFilters: setShowCustomFilters, showCustomFilters: showCustomFilters }) })] }));
|
|
951
1022
|
} }), showDirectlyFilters.length > 0 ? (jsxRuntime.jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: showDirectlyFilters })) : null, !compact ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [appliedFilters.filter(filter => !filter.showDirectly).length > 0 ? (jsxRuntime.jsx("div", { className: "h-4 w-[1px] bg-slate-300", "data-testid": "applied-filters-buttons" })) : null, jsxRuntime.jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: appliedFilters }), filterBarConfig.appliedFilterKeys().length > 0 ? (jsxRuntime.jsx(ResetFiltersButton, { resetFiltersToInitialState: filterBarConfig.resetFiltersToInitialState })) : null] })) : null] }));
|
|
952
1023
|
};
|
|
953
|
-
const Separator = () => jsxRuntime.jsx("hr", { className: "border-secondary-200", role: "separator" });
|
|
954
|
-
const FiltersAppliedCountLabel = ({ filterBarConfig, }) => {
|
|
955
|
-
const [t] = useTranslation();
|
|
956
|
-
switch (filterBarConfig.appliedFilterKeys().length) {
|
|
957
|
-
case 0:
|
|
958
|
-
return t("filtersBar.appliedFiltersTooltip.none");
|
|
959
|
-
case 1:
|
|
960
|
-
return filterBarConfig.appliedFilterKeys()[0] ? t("filtersMenu.appliedFiltersLabel.singular") : null;
|
|
961
|
-
default:
|
|
962
|
-
return jsxRuntime.jsx(jsxRuntime.Fragment, { children: t("filtersMenu.appliedFiltersLabel.plural", { count: filterBarConfig.appliedFilterKeys().length }) });
|
|
963
|
-
}
|
|
964
|
-
};
|
|
965
|
-
const CustomFieldsHiddenGroup = ({ appliedCustomFields, filterBarConfig, onShow, }) => {
|
|
966
|
-
const [t] = useTranslation();
|
|
967
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(Separator, {}), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx(reactComponents.Button, { "aria-controls": "filters-list", className: "text-primary-600 w-full justify-between px-3", onClick: onShow, prefix: jsxRuntime.jsx(reactComponents.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 ? (jsxRuntime.jsx("ul", { "aria-label": "Visible custom fields", "data-testid": "applied-custom-fields-list", children: jsxRuntime.jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: appliedCustomFields, visualStyle: "list-item" }) })) : null] })] }));
|
|
968
|
-
};
|
|
969
|
-
const removeCustomFields = (filtersGrouped) => filtersGrouped.filter(group => group.key !== "CUSTOM_FIELDS");
|
|
970
1024
|
|
|
971
1025
|
/**
|
|
972
1026
|
* Filter is a React component that renders a filter element based on the provided filter definition and state.
|
|
@@ -987,14 +1041,14 @@ const FilterTableComponent = ({ filterKey, filterBarDefinition, filterBarConfig,
|
|
|
987
1041
|
if (Array.isArray(filterKey)) {
|
|
988
1042
|
const isActive = filterKey.some(key => filterBarConfig.appliedFilterKeys().includes(key));
|
|
989
1043
|
return (jsxRuntime.jsx(reactComponents.Popover, { placement: "bottom-start", children: modalState => {
|
|
990
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(reactComponents.PopoverTrigger, { children: jsxRuntime.jsx("div", { children: jsxRuntime.jsx(reactComponents.Tooltip, { disabled: modalState.isOpen || !isActive, label: jsxRuntime.jsx(MultipleFilterTooltipLabel, { filterBarConfig: filterBarConfig, filterKeys: filterKey }), children: jsxRuntime.jsx(reactComponents.IconButton, { icon: jsxRuntime.jsx(reactComponents.Icon, { name: "Filter", size: "small" }), size: "extraSmall", variant: isActive ? "filled" : "ghost-neutral" }) }) }) }), jsxRuntime.jsx(reactComponents.PopoverContent, { children: jsxRuntime.jsx(reactComponents.MenuList, { children: filters.map((value, index) => value && (jsxRuntime.jsx(FilterComponent, { filter: value, filterBarActions: filterBarConfig, filterState: filterBarConfig, visualStyle: "list-item" }, index))) }) })] }));
|
|
1044
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(reactComponents.PopoverTrigger, { children: jsxRuntime.jsx("div", { children: jsxRuntime.jsx(reactComponents.Tooltip, { disabled: modalState.isOpen || !isActive, label: jsxRuntime.jsx(MultipleFilterTooltipLabel, { filterBarConfig: filterBarConfig, filterKeys: filterKey, filters: filters }), placement: "bottom", children: jsxRuntime.jsx(reactComponents.IconButton, { icon: jsxRuntime.jsx(reactComponents.Icon, { name: "Filter", size: "small" }), size: "extraSmall", variant: isActive ? "filled" : "ghost-neutral" }) }) }) }), jsxRuntime.jsx(reactComponents.PopoverContent, { children: jsxRuntime.jsx(reactComponents.MenuList, { children: filters.map((value, index) => value && (jsxRuntime.jsx(FilterComponent, { filter: value, filterBarActions: filterBarConfig, filterState: filterBarConfig, visualStyle: "list-item" }, index))) }) })] }));
|
|
991
1045
|
} }));
|
|
992
1046
|
}
|
|
993
1047
|
const filter = filterBarDefinition[ensureFilterKey(filterKey)];
|
|
994
1048
|
if (!filter) {
|
|
995
1049
|
return null;
|
|
996
1050
|
}
|
|
997
|
-
return (jsxRuntime.jsx("div", { onClick: event => event.stopPropagation(), children: jsxRuntime.jsx(reactComponents.Tooltip, { disabled: !filterBarConfig.appliedFilterKeys().includes(filterKey), label: jsxRuntime.jsx(SingleFilterTooltipLabel, { filterBarConfig: filterBarConfig, filterKey: filterKey }), children: jsxRuntime.jsx(FilterComponent, { asIcon: "Filter", filter: filter, filterBarActions: filterBarConfig, filterState: filterBarConfig, size: "extraSmall" }) }) }));
|
|
1051
|
+
return (jsxRuntime.jsx("div", { onClick: event => event.stopPropagation(), children: jsxRuntime.jsx(reactComponents.Tooltip, { disabled: !filterBarConfig.appliedFilterKeys().includes(filterKey), label: jsxRuntime.jsx(SingleFilterTooltipLabel, { filter: filter, filterBarConfig: filterBarConfig, filterKey: filterKey }), placement: "bottom", children: jsxRuntime.jsx(FilterComponent, { asIcon: "Filter", filter: filter, filterBarActions: filterBarConfig, filterState: filterBarConfig, size: "extraSmall" }) }) }));
|
|
998
1052
|
};
|
|
999
1053
|
|
|
1000
1054
|
/**
|
|
@@ -1225,8 +1279,8 @@ const HierarchicalCheckboxFilter = ({ filterDefinition, filterBarActions, option
|
|
|
1225
1279
|
/**
|
|
1226
1280
|
* The FilterBar component serves as a wrapper for managing filters.
|
|
1227
1281
|
*/
|
|
1228
|
-
const FilterBar = ({ hiddenFilters, className, filterBarDefinition, filterBarConfig, compact = true, title, }) => {
|
|
1229
|
-
return (jsxRuntime.jsx(FiltersMenu, { className: className, compact: compact, dataTestId: `${filterBarConfig.name}-filterbar`, filterBarConfig: filterBarConfig, filterBarDefinition: filterBarDefinition, hiddenFilters: hiddenFilters, title: title }));
|
|
1282
|
+
const FilterBar = ({ hiddenFilters, className, filterBarDefinition, filterBarConfig, compact = true, title, allowShowFiltersDirectly = true, }) => {
|
|
1283
|
+
return (jsxRuntime.jsx(FiltersMenu, { allowShowFiltersDirectly: allowShowFiltersDirectly, className: className, compact: compact, dataTestId: `${filterBarConfig.name}-filterbar`, filterBarConfig: filterBarConfig, filterBarDefinition: filterBarDefinition, hiddenFilters: hiddenFilters, title: title }));
|
|
1230
1284
|
};
|
|
1231
1285
|
|
|
1232
1286
|
// Can't import jest.fn so must define a function that does nothing but mimics the jest.fn
|
|
@@ -1888,8 +1942,8 @@ const useFilterBar = ({ name, onValuesChange, filterBarDefinition, }) => {
|
|
|
1888
1942
|
setValue: (key, callback) => setValue(setFilterBarConfig, key, callback),
|
|
1889
1943
|
});
|
|
1890
1944
|
react.useEffect(() => {
|
|
1891
|
-
onValuesChange?.(filterBarConfig.values);
|
|
1892
1945
|
saveData(filterBarConfig, filterBarDefinition);
|
|
1946
|
+
onValuesChange?.(filterBarConfig.values);
|
|
1893
1947
|
}, [filterBarConfig, filterBarDefinition, onValuesChange, saveData]);
|
|
1894
1948
|
return react.useMemo(() => {
|
|
1895
1949
|
return {
|
|
@@ -2048,12 +2102,14 @@ exports.DefaultMinMaxFilter = DefaultMinMaxFilter;
|
|
|
2048
2102
|
exports.DefaultRadioFilter = DefaultRadioFilter;
|
|
2049
2103
|
exports.DynamicFilterList = DynamicFilterList;
|
|
2050
2104
|
exports.FilterBar = FilterBar;
|
|
2105
|
+
exports.FilterButtonTooltipLabel = FilterButtonTooltipLabel;
|
|
2051
2106
|
exports.FilterComponent = FilterComponent;
|
|
2052
2107
|
exports.FilterEvents = FilterEvents;
|
|
2053
2108
|
exports.FilterHeader = FilterHeader;
|
|
2054
2109
|
exports.FilterResults = FilterResults;
|
|
2055
2110
|
exports.FilterTableComponent = FilterTableComponent;
|
|
2056
2111
|
exports.FiltersMenu = FiltersMenu;
|
|
2112
|
+
exports.FiltersMenuContent = FiltersMenuContent;
|
|
2057
2113
|
exports.FiltersRenderer = FiltersRenderer;
|
|
2058
2114
|
exports.GroupedFiltersList = GroupedFiltersList;
|
|
2059
2115
|
exports.HierarchicalCheckboxFilter = HierarchicalCheckboxFilter;
|
|
@@ -2072,5 +2128,6 @@ exports.mockFilterBar = mockFilterBar;
|
|
|
2072
2128
|
exports.toggleFilterValue = toggleFilterValue;
|
|
2073
2129
|
exports.useFilterBar = useFilterBar;
|
|
2074
2130
|
exports.useFilterBarAsync = useFilterBarAsync;
|
|
2131
|
+
exports.useFiltersMenu = useFiltersMenu;
|
|
2075
2132
|
exports.useSearchParamAsFilter = useSearchParamAsFilter;
|
|
2076
2133
|
exports.validateFilter = validateFilter;
|
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';
|
|
@@ -108,7 +108,7 @@ var defaultTranslations = {
|
|
|
108
108
|
"filter.more.options.if.you.search.title": "Over {{count}} items found.",
|
|
109
109
|
"filtersBar.appliedFiltersTooltip.none": "No filters applied",
|
|
110
110
|
"filtersBar.appliedFiltersTooltip.plural": "{{count}} filters applied:",
|
|
111
|
-
"filtersBar.appliedFiltersTooltip.singular": "{{filterName}} filter applied
|
|
111
|
+
"filtersBar.appliedFiltersTooltip.singular": "{{filterName}} filter applied",
|
|
112
112
|
"filtersBar.defaultAssetFilters.followedFilter.ALL": "All Assets",
|
|
113
113
|
"filtersBar.defaultAssetFilters.followedFilter.allLabel": "All Assets",
|
|
114
114
|
"filtersBar.defaultAssetFilters.followedFilter.FOLLOWED": "Followed Only",
|
|
@@ -643,6 +643,25 @@ const DefaultRadioFilter = ({ filterDefinition, filterBarActions, options, loadi
|
|
|
643
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" }) })) })] }));
|
|
644
644
|
};
|
|
645
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
|
+
|
|
646
665
|
/**
|
|
647
666
|
* Returns the two first values, appends counter if more.
|
|
648
667
|
*
|
|
@@ -758,6 +777,64 @@ const useGroupFilters = (filterDefinitions, hiddenFilters) => {
|
|
|
758
777
|
};
|
|
759
778
|
const uniqueKeysFromGroups = (filters) => [...new Set(filters.map(filter => filter.group))];
|
|
760
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
|
+
|
|
761
838
|
/**
|
|
762
839
|
* FiltersRenderer renders an array of Filter components from filter definitions
|
|
763
840
|
* It ignores hidden filters.
|
|
@@ -778,6 +855,60 @@ const FiltersRenderer = ({ filters, filterBarConfig, visualStyle, }) => {
|
|
|
778
855
|
.map(filter => (jsx(FilterComponent, { filter: filter, filterBarActions: filterBarConfig, filterState: { values: filterBarConfig.values, setters: filterBarConfig.setters }, visualStyle: visualStyle }, `filter-${filter.filterKey}`)));
|
|
779
856
|
};
|
|
780
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
|
+
|
|
781
912
|
/**
|
|
782
913
|
* TooltipValues component that displays formatted tooltip values based on the provided input.
|
|
783
914
|
*
|
|
@@ -786,40 +917,28 @@ const FiltersRenderer = ({ filters, filterBarConfig, visualStyle, }) => {
|
|
|
786
917
|
* @returns {ReactElement} The rendered TooltipValues component.
|
|
787
918
|
*/
|
|
788
919
|
const TooltipValues = ({ values }) => {
|
|
789
|
-
return jsx("div", { children: renderTooltipValues(values) });
|
|
790
|
-
};
|
|
791
|
-
const renderTooltipValues = (values) => {
|
|
792
920
|
if (values === undefined) {
|
|
793
|
-
return
|
|
921
|
+
return jsx(Fragment, {});
|
|
794
922
|
}
|
|
795
923
|
// Array of objects ValueName[]
|
|
796
924
|
if (Array.isArray(values) && typeof values[0] === "object") {
|
|
797
|
-
return (jsx("ul", { className: "list-inside", children: values.map((value, index) => (jsx("li", { className: "list-disc", children: value.name }, index))) }));
|
|
925
|
+
return (jsx("ul", { className: "list-inside pl-2", children: values.map((value, index) => (jsx("li", { className: "list-disc text-xs font-normal", children: value.name }, index))) }));
|
|
798
926
|
}
|
|
799
927
|
// Array of strings
|
|
800
|
-
if (Array.isArray(values)) {
|
|
801
|
-
return (jsx("ul", { className: "list-inside", children: values.map((value, index) => {
|
|
802
|
-
return (jsx("li", { className: "list-disc", children: typeof value === "string" ? value : value.name }, index));
|
|
928
|
+
if (Array.isArray(values) && typeof values[0] === "string") {
|
|
929
|
+
return (jsx("ul", { className: "list-inside pl-2", children: values.map((value, index) => {
|
|
930
|
+
return (jsx("li", { className: "list-disc text-xs font-normal", children: typeof value === "string" ? value : value.name }, index));
|
|
803
931
|
}) }));
|
|
804
932
|
}
|
|
805
933
|
// single object ValueName
|
|
806
934
|
if (typeof values === "object" && values.name) {
|
|
807
|
-
return (jsx("ul", { className: "list-inside", children: jsx("li", { className: "list-disc", children: values.name }) }));
|
|
935
|
+
return (jsx("ul", { className: "list-inside pl-2", children: jsx("li", { className: "list-disc text-xs font-normal", children: values.name }) }));
|
|
808
936
|
}
|
|
809
937
|
// String or number
|
|
810
938
|
if (typeof values === "string" || typeof values === "number") {
|
|
811
|
-
return (jsx("ul", { className: "list-inside", children: jsx("li", { className: "list-disc", children: values.toString() }) }));
|
|
812
|
-
}
|
|
813
|
-
// BooleanValue
|
|
814
|
-
if (typeof values === "object" && values.booleanValue !== undefined) {
|
|
815
|
-
return values.booleanValue ? "Yes" : "No";
|
|
939
|
+
return (jsx("ul", { className: "list-inside pl-2", children: jsx("li", { className: "list-disc text-xs font-normal", children: values.toString() }) }));
|
|
816
940
|
}
|
|
817
|
-
|
|
818
|
-
if (typeof values === "object" && "min" in values && "max" in values) {
|
|
819
|
-
const minMax = values;
|
|
820
|
-
return `Min: ${minMax.min}, Max: ${minMax.max}`;
|
|
821
|
-
}
|
|
822
|
-
return null;
|
|
941
|
+
return jsx(Fragment, {});
|
|
823
942
|
};
|
|
824
943
|
|
|
825
944
|
/**
|
|
@@ -832,13 +951,16 @@ const renderTooltipValues = (values) => {
|
|
|
832
951
|
* @param {string} props.filterKey - The key identifying the filter.
|
|
833
952
|
* @returns {ReactElement} The rendered tooltip label and applied filter values.
|
|
834
953
|
*/
|
|
835
|
-
const SingleFilterTooltipLabel = ({ filterBarConfig, filterKey, }) => {
|
|
836
|
-
const title = filterBarConfig.getFilterTitle(filterKey);
|
|
837
|
-
const values = filterBarConfig.getValuesByKey(filterKey);
|
|
838
|
-
const
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
954
|
+
const SingleFilterTooltipLabel = ({ filterBarConfig, filter, filterKey, }) => {
|
|
955
|
+
const title = useMemo(() => filterBarConfig.getFilterTitle(filterKey), [filterBarConfig, filterKey]);
|
|
956
|
+
const values = useMemo(() => filterBarConfig.getValuesByKey(filterKey), [filterBarConfig, filterKey]);
|
|
957
|
+
const displayValues = useMemo(() => {
|
|
958
|
+
if (filter?.valueAsText) {
|
|
959
|
+
return filter.valueAsText(values);
|
|
960
|
+
}
|
|
961
|
+
return values;
|
|
962
|
+
}, [filter, values]);
|
|
963
|
+
return (jsxs(Fragment, { children: [jsx("div", { className: "text-xs font-medium", children: title }), jsx(TooltipValues, { values: displayValues })] }));
|
|
842
964
|
};
|
|
843
965
|
|
|
844
966
|
/**
|
|
@@ -852,119 +974,51 @@ const SingleFilterTooltipLabel = ({ filterBarConfig, filterKey, }) => {
|
|
|
852
974
|
* @param {string[]} [props.filterKeys] An optional list of filter keys to filter the currently applied filters.
|
|
853
975
|
* @returns {ReactElement | string} Returns a element or string for the tooltip displaying applied filters.
|
|
854
976
|
*/
|
|
855
|
-
const MultipleFilterTooltipLabel = ({ filterBarConfig, filterKeys, }) => {
|
|
977
|
+
const MultipleFilterTooltipLabel = ({ filterBarConfig, filterKeys, filters, }) => {
|
|
856
978
|
const [t] = useTranslation();
|
|
857
|
-
const
|
|
858
|
-
|
|
859
|
-
|
|
979
|
+
const appliedKeys = filterBarConfig.appliedFilterKeys();
|
|
980
|
+
const appliedFilterKeys = filterKeys ? filterKeys.filter(key => appliedKeys.includes(key)) : appliedKeys;
|
|
981
|
+
const filtersMap = filters.reduce((acc, filter) => {
|
|
982
|
+
if (filter.filterKey) {
|
|
983
|
+
acc[filter.filterKey] = filter;
|
|
984
|
+
}
|
|
985
|
+
return acc;
|
|
986
|
+
}, {});
|
|
860
987
|
switch (appliedFilterKeys.length) {
|
|
861
988
|
case 0:
|
|
862
989
|
return t("filtersBar.appliedFiltersTooltip.none");
|
|
863
990
|
case 1:
|
|
864
|
-
return jsx(SingleFilterTooltipLabel, { filterBarConfig: filterBarConfig, filterKey: appliedFilterKeys[0] });
|
|
991
|
+
return (jsx(SingleFilterTooltipLabel, { filter: filtersMap[appliedFilterKeys[0]], filterBarConfig: filterBarConfig, filterKey: appliedFilterKeys[0] }));
|
|
865
992
|
default:
|
|
866
|
-
return (
|
|
867
|
-
const title = filterBarConfig.getFilterTitle(filterKey);
|
|
868
|
-
const values = filterBarConfig.getValuesByKey(filterKey);
|
|
869
|
-
return filterBarConfig.appliedFilterKeys().includes(filterKey) ? (jsxs("div", { children: [jsxs("div", { children: [title, ":"] }), jsx(TooltipValues, { values: values })] }, filterKey)) : null;
|
|
870
|
-
})] }));
|
|
993
|
+
return (jsx(Fragment, { children: appliedFilterKeys.map(filterKey => (jsx(SingleFilterTooltipLabel, { filter: filtersMap[filterKey], filterBarConfig: filterBarConfig, filterKey: filterKey }, filterKey))) }));
|
|
871
994
|
}
|
|
872
995
|
};
|
|
873
996
|
|
|
874
|
-
/**
|
|
875
|
-
* FiltersList is a React component that displays a list of filters within a filter bar.
|
|
876
|
-
*
|
|
877
|
-
* @returns {ReactElement} - Returns the FiltersList component.
|
|
878
|
-
*/
|
|
879
|
-
const GroupedFiltersList = ({ filterBarConfig, filtersGrouped, className, dataTestId = "grouped-filters-list", }) => {
|
|
880
|
-
return (jsx("div", { className: className, "data-testid": dataTestId, role: "menu", children: filtersGrouped.map((group, idx) => {
|
|
881
|
-
const isLastGroup = idx === filtersGrouped.length - 1;
|
|
882
|
-
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));
|
|
883
|
-
}) }));
|
|
884
|
-
};
|
|
885
|
-
|
|
886
|
-
/**
|
|
887
|
-
* ResetFiltersButton is a React component that provides a button for resetting filters.
|
|
888
|
-
*
|
|
889
|
-
* @returns {ReactElement | null} The rendered ResetFiltersButton component, or null if no filters have been applied.
|
|
890
|
-
*/
|
|
891
|
-
const ResetFiltersButton = ({ resetFiltersToInitialState, dataTestId, className, }) => {
|
|
892
|
-
const [t] = useTranslation();
|
|
893
|
-
return (jsxs(Button, { className: className, dataTestId: dataTestId ?? "reset-filters-button", onClick: () => {
|
|
894
|
-
resetFiltersToInitialState();
|
|
895
|
-
}, size: "small", variant: "ghost", children: [t("filtersBar.resetFilters"), jsx("span", { className: "sr-only", children: "Resets all applied filters" })] }));
|
|
896
|
-
};
|
|
897
|
-
|
|
898
997
|
/**
|
|
899
998
|
* FilterMenu is a React component that displays a list of filters in a popover menu based on the provided filter bar configuration.
|
|
900
999
|
*
|
|
901
1000
|
* @template TFilterBarDefinition - The type representing the filter bar definition.
|
|
902
1001
|
* @returns {ReactElement} - Returns the FilterMenu component.
|
|
903
1002
|
*/
|
|
904
|
-
const FiltersMenu = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [], compact, title, dataTestId = "filters-menu", className, }) => {
|
|
1003
|
+
const FiltersMenu = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [], compact, title, dataTestId = "filters-menu", className, showAppliedFiltersCount = true, buttonProps, allowShowFiltersDirectly = true, }) => {
|
|
905
1004
|
const [t] = useTranslation();
|
|
906
1005
|
const { isSm } = useViewportBreakpoints();
|
|
907
1006
|
const [showCustomFilters, setShowCustomFilters] = useState(false);
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
const showInStarredMenu = filter.showInStarredMenu ? filter.showInStarredMenu() : true; // TODO: Starred menu concept should be completely removed everywhere
|
|
915
|
-
const showMenuAnywayBecauseFilterHasChanged = filterBarConfig.appliedFilterKeys().includes(filter.filterKey);
|
|
916
|
-
return (!showInFilterBar || !showInStarredMenu) && !showMenuAnywayBecauseFilterHasChanged
|
|
917
|
-
? filter.filterKey
|
|
918
|
-
: null;
|
|
919
|
-
})
|
|
920
|
-
.filter(truthy);
|
|
921
|
-
}, [filterBarConfig, filterBarDefinition]);
|
|
922
|
-
const { filtersGrouped } = useGroupFilters(objectValues(filterBarDefinition), [...hideInMenu, ...hiddenFilters]);
|
|
923
|
-
const { appliedFilters, filtersToShow, showDirectlyFilters, hasCustomFields } = useMemo(() => {
|
|
924
|
-
const allFilters = filtersGrouped.map(group => group.filters).flat();
|
|
925
|
-
return {
|
|
926
|
-
appliedFilters: allFilters.filter(filter => filterBarConfig.appliedFilterKeys().includes(filter.filterKey)),
|
|
927
|
-
filtersToShow: allFilters.filter(filter => !filter.showDirectly),
|
|
928
|
-
showDirectlyFilters: allFilters.filter(filter => filter.showDirectly),
|
|
929
|
-
hasCustomFields: allFilters.some(filter => filter.group === "CUSTOM_FIELDS"),
|
|
930
|
-
};
|
|
931
|
-
}, [filterBarConfig, filtersGrouped]);
|
|
932
|
-
const [searchResults, searchText, setSearchText] = useTextSearch(filtersToShow, item => [item.title]);
|
|
933
|
-
const { filtersGrouped: searchResultsGrouped } = useGroupFilters(searchResults, []);
|
|
934
|
-
const { filtersGrouped: filtersToShowGrouped } = useGroupFilters(filtersToShow, []);
|
|
935
|
-
const appliedCustomFields = useMemo(() => appliedFilters.filter(filter => filter.group === "CUSTOM_FIELDS"), [appliedFilters]);
|
|
1007
|
+
const { appliedFilters, showDirectlyFilters, hasCustomFields, filtersToShowGrouped, searchResultsGrouped, searchText, appliedCustomFields, removeCustomFieldsGroup, setSearchText, } = useFiltersMenu({
|
|
1008
|
+
filterBarDefinition,
|
|
1009
|
+
filterBarConfig,
|
|
1010
|
+
hiddenFilters,
|
|
1011
|
+
allowShowFiltersDirectly,
|
|
1012
|
+
});
|
|
936
1013
|
return (jsxs("div", { className: twMerge("flex items-center gap-2", className), "data-testid": dataTestId, children: [jsx(Popover, { onOpenStateChange: open => {
|
|
937
1014
|
if (!open) {
|
|
938
1015
|
setShowCustomFilters(false);
|
|
939
1016
|
setSearchText("");
|
|
940
1017
|
}
|
|
941
1018
|
}, placement: "bottom-start", children: modalState => {
|
|
942
|
-
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:
|
|
943
|
-
? searchResultsGrouped
|
|
944
|
-
: showCustomFilters
|
|
945
|
-
? filtersToShowGrouped
|
|
946
|
-
: removeCustomFields(filtersToShowGrouped) }), hasCustomFields && !showCustomFilters && !searchText ? (jsx(CustomFieldsHiddenGroup, { appliedCustomFields: appliedCustomFields, filterBarConfig: filterBarConfig, onShow: () => {
|
|
947
|
-
setShowCustomFilters(true);
|
|
948
|
-
} })) : null] })] }) })] }));
|
|
1019
|
+
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, filters: appliedFilters }), 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 }) })] }));
|
|
949
1020
|
} }), 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] }));
|
|
950
1021
|
};
|
|
951
|
-
const Separator = () => jsx("hr", { className: "border-secondary-200", role: "separator" });
|
|
952
|
-
const FiltersAppliedCountLabel = ({ filterBarConfig, }) => {
|
|
953
|
-
const [t] = useTranslation();
|
|
954
|
-
switch (filterBarConfig.appliedFilterKeys().length) {
|
|
955
|
-
case 0:
|
|
956
|
-
return t("filtersBar.appliedFiltersTooltip.none");
|
|
957
|
-
case 1:
|
|
958
|
-
return filterBarConfig.appliedFilterKeys()[0] ? t("filtersMenu.appliedFiltersLabel.singular") : null;
|
|
959
|
-
default:
|
|
960
|
-
return jsx(Fragment, { children: t("filtersMenu.appliedFiltersLabel.plural", { count: filterBarConfig.appliedFilterKeys().length }) });
|
|
961
|
-
}
|
|
962
|
-
};
|
|
963
|
-
const CustomFieldsHiddenGroup = ({ appliedCustomFields, filterBarConfig, onShow, }) => {
|
|
964
|
-
const [t] = useTranslation();
|
|
965
|
-
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] })] }));
|
|
966
|
-
};
|
|
967
|
-
const removeCustomFields = (filtersGrouped) => filtersGrouped.filter(group => group.key !== "CUSTOM_FIELDS");
|
|
968
1022
|
|
|
969
1023
|
/**
|
|
970
1024
|
* Filter is a React component that renders a filter element based on the provided filter definition and state.
|
|
@@ -985,14 +1039,14 @@ const FilterTableComponent = ({ filterKey, filterBarDefinition, filterBarConfig,
|
|
|
985
1039
|
if (Array.isArray(filterKey)) {
|
|
986
1040
|
const isActive = filterKey.some(key => filterBarConfig.appliedFilterKeys().includes(key));
|
|
987
1041
|
return (jsx(Popover, { placement: "bottom-start", children: modalState => {
|
|
988
|
-
return (jsxs(Fragment, { children: [jsx(PopoverTrigger, { children: jsx("div", { children: jsx(Tooltip, { disabled: modalState.isOpen || !isActive, label: jsx(MultipleFilterTooltipLabel, { filterBarConfig: filterBarConfig, filterKeys: filterKey }), children: jsx(IconButton, { icon: jsx(Icon, { name: "Filter", size: "small" }), size: "extraSmall", variant: isActive ? "filled" : "ghost-neutral" }) }) }) }), jsx(PopoverContent, { children: jsx(MenuList, { children: filters.map((value, index) => value && (jsx(FilterComponent, { filter: value, filterBarActions: filterBarConfig, filterState: filterBarConfig, visualStyle: "list-item" }, index))) }) })] }));
|
|
1042
|
+
return (jsxs(Fragment, { children: [jsx(PopoverTrigger, { children: jsx("div", { children: jsx(Tooltip, { disabled: modalState.isOpen || !isActive, label: jsx(MultipleFilterTooltipLabel, { filterBarConfig: filterBarConfig, filterKeys: filterKey, filters: filters }), placement: "bottom", children: jsx(IconButton, { icon: jsx(Icon, { name: "Filter", size: "small" }), size: "extraSmall", variant: isActive ? "filled" : "ghost-neutral" }) }) }) }), jsx(PopoverContent, { children: jsx(MenuList, { children: filters.map((value, index) => value && (jsx(FilterComponent, { filter: value, filterBarActions: filterBarConfig, filterState: filterBarConfig, visualStyle: "list-item" }, index))) }) })] }));
|
|
989
1043
|
} }));
|
|
990
1044
|
}
|
|
991
1045
|
const filter = filterBarDefinition[ensureFilterKey(filterKey)];
|
|
992
1046
|
if (!filter) {
|
|
993
1047
|
return null;
|
|
994
1048
|
}
|
|
995
|
-
return (jsx("div", { onClick: event => event.stopPropagation(), children: jsx(Tooltip, { disabled: !filterBarConfig.appliedFilterKeys().includes(filterKey), label: jsx(SingleFilterTooltipLabel, { filterBarConfig: filterBarConfig, filterKey: filterKey }), children: jsx(FilterComponent, { asIcon: "Filter", filter: filter, filterBarActions: filterBarConfig, filterState: filterBarConfig, size: "extraSmall" }) }) }));
|
|
1049
|
+
return (jsx("div", { onClick: event => event.stopPropagation(), children: jsx(Tooltip, { disabled: !filterBarConfig.appliedFilterKeys().includes(filterKey), label: jsx(SingleFilterTooltipLabel, { filter: filter, filterBarConfig: filterBarConfig, filterKey: filterKey }), placement: "bottom", children: jsx(FilterComponent, { asIcon: "Filter", filter: filter, filterBarActions: filterBarConfig, filterState: filterBarConfig, size: "extraSmall" }) }) }));
|
|
996
1050
|
};
|
|
997
1051
|
|
|
998
1052
|
/**
|
|
@@ -1223,8 +1277,8 @@ const HierarchicalCheckboxFilter = ({ filterDefinition, filterBarActions, option
|
|
|
1223
1277
|
/**
|
|
1224
1278
|
* The FilterBar component serves as a wrapper for managing filters.
|
|
1225
1279
|
*/
|
|
1226
|
-
const FilterBar = ({ hiddenFilters, className, filterBarDefinition, filterBarConfig, compact = true, title, }) => {
|
|
1227
|
-
return (jsx(FiltersMenu, { className: className, compact: compact, dataTestId: `${filterBarConfig.name}-filterbar`, filterBarConfig: filterBarConfig, filterBarDefinition: filterBarDefinition, hiddenFilters: hiddenFilters, title: title }));
|
|
1280
|
+
const FilterBar = ({ hiddenFilters, className, filterBarDefinition, filterBarConfig, compact = true, title, allowShowFiltersDirectly = true, }) => {
|
|
1281
|
+
return (jsx(FiltersMenu, { allowShowFiltersDirectly: allowShowFiltersDirectly, className: className, compact: compact, dataTestId: `${filterBarConfig.name}-filterbar`, filterBarConfig: filterBarConfig, filterBarDefinition: filterBarDefinition, hiddenFilters: hiddenFilters, title: title }));
|
|
1228
1282
|
};
|
|
1229
1283
|
|
|
1230
1284
|
// Can't import jest.fn so must define a function that does nothing but mimics the jest.fn
|
|
@@ -1886,8 +1940,8 @@ const useFilterBar = ({ name, onValuesChange, filterBarDefinition, }) => {
|
|
|
1886
1940
|
setValue: (key, callback) => setValue(setFilterBarConfig, key, callback),
|
|
1887
1941
|
});
|
|
1888
1942
|
useEffect(() => {
|
|
1889
|
-
onValuesChange?.(filterBarConfig.values);
|
|
1890
1943
|
saveData(filterBarConfig, filterBarDefinition);
|
|
1944
|
+
onValuesChange?.(filterBarConfig.values);
|
|
1891
1945
|
}, [filterBarConfig, filterBarDefinition, onValuesChange, saveData]);
|
|
1892
1946
|
return useMemo(() => {
|
|
1893
1947
|
return {
|
|
@@ -2040,4 +2094,4 @@ const mergeFilters = (filterBarDefinition, extraFilters) => {
|
|
|
2040
2094
|
*/
|
|
2041
2095
|
setupLibraryTranslations();
|
|
2042
2096
|
|
|
2043
|
-
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 };
|
|
2097
|
+
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.202",
|
|
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.167",
|
|
20
|
+
"@trackunit/react-date-and-time-components": "1.3.169",
|
|
21
21
|
"@trackunit/shared-utils": "1.5.121",
|
|
22
|
-
"@trackunit/react-form-components": "1.3.
|
|
22
|
+
"@trackunit/react-form-components": "1.3.167",
|
|
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.147",
|
|
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,8 +1,9 @@
|
|
|
1
1
|
import { ReactElement } from "react";
|
|
2
|
-
import { FilterBarDefinition, FilterMapActions, FilterMapGetter, FilterState } from "../../types/FilterTypes";
|
|
2
|
+
import { FilterBarDefinition, FilterDefinition, FilterMapActions, FilterMapGetter, FilterState } from "../../types/FilterTypes";
|
|
3
3
|
interface MultipleFilterTooltipLabelProps<TFilterBarDefinition extends FilterBarDefinition> {
|
|
4
4
|
filterBarConfig: FilterState<TFilterBarDefinition> & FilterMapActions & FilterMapGetter;
|
|
5
5
|
filterKeys?: string[];
|
|
6
|
+
filters: FilterDefinition[];
|
|
6
7
|
}
|
|
7
8
|
/**
|
|
8
9
|
* Component that displays a tooltip for filters.
|
|
@@ -15,5 +16,5 @@ interface MultipleFilterTooltipLabelProps<TFilterBarDefinition extends FilterBar
|
|
|
15
16
|
* @param {string[]} [props.filterKeys] An optional list of filter keys to filter the currently applied filters.
|
|
16
17
|
* @returns {ReactElement | string} Returns a element or string for the tooltip displaying applied filters.
|
|
17
18
|
*/
|
|
18
|
-
declare const MultipleFilterTooltipLabel: <TFilterBarDefinition extends FilterBarDefinition>({ filterBarConfig, filterKeys, }: MultipleFilterTooltipLabelProps<TFilterBarDefinition>) => ReactElement | string;
|
|
19
|
+
declare const MultipleFilterTooltipLabel: <TFilterBarDefinition extends FilterBarDefinition>({ filterBarConfig, filterKeys, filters, }: MultipleFilterTooltipLabelProps<TFilterBarDefinition>) => ReactElement | string;
|
|
19
20
|
export default MultipleFilterTooltipLabel;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { ReactElement } from "react";
|
|
2
|
-
import { FilterBarDefinition, FilterMapActions, FilterMapGetter, FilterState } from "../../types/FilterTypes";
|
|
2
|
+
import { FilterBarDefinition, FilterDefinition, FilterMapActions, FilterMapGetter, FilterState } from "../../types/FilterTypes";
|
|
3
3
|
interface SingleFilterTooltipLabelProps<TFilterBarDefinition extends FilterBarDefinition> {
|
|
4
4
|
filterBarConfig: FilterState<TFilterBarDefinition> & FilterMapActions & FilterMapGetter;
|
|
5
|
+
filter?: FilterDefinition;
|
|
5
6
|
filterKey: string;
|
|
6
7
|
}
|
|
7
8
|
/**
|
|
@@ -14,5 +15,5 @@ interface SingleFilterTooltipLabelProps<TFilterBarDefinition extends FilterBarDe
|
|
|
14
15
|
* @param {string} props.filterKey - The key identifying the filter.
|
|
15
16
|
* @returns {ReactElement} The rendered tooltip label and applied filter values.
|
|
16
17
|
*/
|
|
17
|
-
declare const SingleFilterTooltipLabel: <TFilterBarDefinition extends FilterBarDefinition>({ filterBarConfig, filterKey, }: SingleFilterTooltipLabelProps<TFilterBarDefinition>) => ReactElement;
|
|
18
|
+
declare const SingleFilterTooltipLabel: <TFilterBarDefinition extends FilterBarDefinition>({ filterBarConfig, filter, filterKey, }: SingleFilterTooltipLabelProps<TFilterBarDefinition>) => ReactElement;
|
|
18
19
|
export default SingleFilterTooltipLabel;
|
|
@@ -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";
|