@trackunit/filters-filter-bar 1.3.190 → 1.3.192

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 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",
@@ -779,6 +779,99 @@ const FiltersRenderer = ({ filters, filterBarConfig, visualStyle, }) => {
779
779
  .map(filter => (jsxRuntime.jsx(FilterComponent, { filter: filter, filterBarActions: filterBarConfig, filterState: { values: filterBarConfig.values, setters: filterBarConfig.setters }, visualStyle: visualStyle }, `filter-${filter.filterKey}`)));
780
780
  };
781
781
 
782
+ /**
783
+ * TooltipValues component that displays formatted tooltip values based on the provided input.
784
+ *
785
+ * @param {TooltipValuesProps} props - The props for the TooltipValues component.
786
+ * @param {FilterValueType} props.values - The input values to render in the tooltip.
787
+ * @returns {ReactElement} The rendered TooltipValues component.
788
+ */
789
+ const TooltipValues = ({ values }) => {
790
+ return jsxRuntime.jsx("div", { children: renderTooltipValues(values) });
791
+ };
792
+ const renderTooltipValues = (values) => {
793
+ if (values === undefined) {
794
+ return null;
795
+ }
796
+ // Array of objects ValueName[]
797
+ if (Array.isArray(values) && typeof values[0] === "object") {
798
+ return (jsxRuntime.jsx("ul", { className: "list-inside", children: values.map((value, index) => (jsxRuntime.jsx("li", { className: "list-disc", children: value.name }, index))) }));
799
+ }
800
+ // Array of strings
801
+ if (Array.isArray(values)) {
802
+ return (jsxRuntime.jsx("ul", { className: "list-inside", children: values.map((value, index) => {
803
+ return (jsxRuntime.jsx("li", { className: "list-disc", children: typeof value === "string" ? value : value.name }, index));
804
+ }) }));
805
+ }
806
+ // single object ValueName
807
+ if (typeof values === "object" && values.name) {
808
+ return (jsxRuntime.jsx("ul", { className: "list-inside", children: jsxRuntime.jsx("li", { className: "list-disc", children: values.name }) }));
809
+ }
810
+ // String or number
811
+ if (typeof values === "string" || typeof values === "number") {
812
+ return (jsxRuntime.jsx("ul", { className: "list-inside", children: jsxRuntime.jsx("li", { className: "list-disc", children: values.toString() }) }));
813
+ }
814
+ // BooleanValue
815
+ if (typeof values === "object" && values.booleanValue !== undefined) {
816
+ return values.booleanValue ? "Yes" : "No";
817
+ }
818
+ // MinMaxFilterValue
819
+ if (typeof values === "object" && "min" in values && "max" in values) {
820
+ const minMax = values;
821
+ return `Min: ${minMax.min}, Max: ${minMax.max}`;
822
+ }
823
+ return null;
824
+ };
825
+
826
+ /**
827
+ * Component that displays a tooltip label with filter details.
828
+ *
829
+ * @template TFilterBarDefinition - The type of the filter bar definition.
830
+ * @param {SingleFilterTooltipLabelProps} props - Component properties.
831
+ * @param {FilterState<TFilterBarDefinition> & FilterMapActions & FilterMapGetter} props.filterBarConfig -
832
+ * Configuration object for the filter bar, containing state, actions, and accessors.
833
+ * @param {string} props.filterKey - The key identifying the filter.
834
+ * @returns {ReactElement} The rendered tooltip label and applied filter values.
835
+ */
836
+ const SingleFilterTooltipLabel = ({ filterBarConfig, filterKey, }) => {
837
+ const title = filterBarConfig.getFilterTitle(filterKey);
838
+ const values = filterBarConfig.getValuesByKey(filterKey);
839
+ const [t] = useTranslation();
840
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [t("filtersBar.appliedFiltersTooltip.singular", {
841
+ filterName: title,
842
+ }), jsxRuntime.jsx(TooltipValues, { values: values })] }));
843
+ };
844
+
845
+ /**
846
+ * Component that displays a tooltip for filters.
847
+ * Differentiates cases with no applied filters, a single filter, or multiple filters.
848
+ *
849
+ * @template TFilterBarDefinition The type definition for the FilterBar.
850
+ * @param {MultipleFilterTooltipLabelProps} props The component's props.
851
+ * @param {FilterState<TFilterBarDefinition> & FilterMapActions & FilterMapGetter} props.filterBarConfig
852
+ * The FilterBar configuration, including filter state, actions, and getter methods.
853
+ * @param {string[]} [props.filterKeys] An optional list of filter keys to filter the currently applied filters.
854
+ * @returns {ReactElement | string} Returns a element or string for the tooltip displaying applied filters.
855
+ */
856
+ const MultipleFilterTooltipLabel = ({ filterBarConfig, filterKeys, }) => {
857
+ const [t] = useTranslation();
858
+ const appliedFilterKeys = filterKeys
859
+ ? filterKeys.filter(key => filterBarConfig.appliedFilterKeys().includes(key))
860
+ : filterBarConfig.appliedFilterKeys();
861
+ switch (appliedFilterKeys.length) {
862
+ case 0:
863
+ return t("filtersBar.appliedFiltersTooltip.none");
864
+ case 1:
865
+ return jsxRuntime.jsx(SingleFilterTooltipLabel, { filterBarConfig: filterBarConfig, filterKey: appliedFilterKeys[0] });
866
+ default:
867
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [t("filtersBar.appliedFiltersTooltip.plural", { count: appliedFilterKeys.length }), appliedFilterKeys.map(filterKey => {
868
+ const title = filterBarConfig.getFilterTitle(filterKey);
869
+ const values = filterBarConfig.getValuesByKey(filterKey);
870
+ return filterBarConfig.appliedFilterKeys().includes(filterKey) ? (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("div", { children: [title, ":"] }), jsxRuntime.jsx(TooltipValues, { values: values })] }, filterKey)) : null;
871
+ })] }));
872
+ }
873
+ };
874
+
782
875
  /**
783
876
  * FiltersList is a React component that displays a list of filters within a filter bar.
784
877
  *
@@ -847,7 +940,7 @@ const FiltersMenu = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [],
847
940
  setSearchText("");
848
941
  }
849
942
  }, placement: "bottom-start", children: modalState => {
850
- 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(FilterButtonTooltipLabel, { 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.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 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
943
+ 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.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 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
851
944
  ? searchResultsGrouped
852
945
  : showCustomFilters
853
946
  ? filtersToShowGrouped
@@ -857,21 +950,6 @@ const FiltersMenu = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [],
857
950
  } }), 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] }));
858
951
  };
859
952
  const Separator = () => jsxRuntime.jsx("hr", { className: "border-secondary-200", role: "separator" });
860
- const FilterButtonTooltipLabel = ({ filterBarConfig, }) => {
861
- const [t] = useTranslation();
862
- switch (filterBarConfig.appliedFilterKeys().length) {
863
- case 0:
864
- return t("filtersBar.appliedFiltersTooltip.none");
865
- case 1:
866
- return filterBarConfig.appliedFilterKeys()[0]
867
- ? t("filtersBar.appliedFiltersTooltip.singular", {
868
- filterName: filterBarConfig.getFilterTitle(filterBarConfig.appliedFilterKeys()[0]),
869
- })
870
- : null; // should never happen though
871
- default:
872
- 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))) })] }));
873
- }
874
- };
875
953
  const FiltersAppliedCountLabel = ({ filterBarConfig, }) => {
876
954
  const [t] = useTranslation();
877
955
  switch (filterBarConfig.appliedFilterKeys().length) {
@@ -906,13 +984,16 @@ const FilterTableComponent = ({ filterKey, filterBarDefinition, filterBarConfig,
906
984
  return [];
907
985
  }, [filterKey, filterBarDefinition, ensureFilterKey]);
908
986
  if (Array.isArray(filterKey)) {
909
- return (jsxRuntime.jsx(reactComponents.MoreMenu, { customButton: jsxRuntime.jsx(reactComponents.IconButton, { icon: jsxRuntime.jsx(reactComponents.Icon, { color: filterKey.some(key => filterBarConfig.appliedFilterKeys().includes(key)) ? "primary" : undefined, name: "Filter", size: "small" }), onClick: event => event.stopPropagation(), size: "extraSmall", variant: "ghost-neutral" }), children: () => (jsxRuntime.jsx(reactComponents.MenuList, { children: filters.map((value, index) => value && (jsxRuntime.jsx(FilterComponent, { filter: value, filterBarActions: filterBarConfig, filterState: filterBarConfig, visualStyle: "list-item" }, index))) })) }));
987
+ const isActive = filterKey.some(key => filterBarConfig.appliedFilterKeys().includes(key));
988
+ return (jsxRuntime.jsx(reactComponents.Popover, { placement: "bottom-start", children: modalState => {
989
+ 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))) }) })] }));
990
+ } }));
910
991
  }
911
992
  const filter = filterBarDefinition[ensureFilterKey(filterKey)];
912
993
  if (!filter) {
913
994
  return null;
914
995
  }
915
- return (jsxRuntime.jsx("div", { onClick: event => event.stopPropagation(), children: jsxRuntime.jsx(FilterComponent, { asIcon: "Filter", filter: filter, filterBarActions: filterBarConfig, filterState: filterBarConfig, size: "extraSmall" }) }));
996
+ 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" }) }) }));
916
997
  };
917
998
 
918
999
  /**
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, Card, CardBody, MoreMenu, IconButton, MenuList } from '@trackunit/react-components';
3
+ import { VirtualizedList, Text, Button, useViewportBreakpoints, Popover, PopoverTrigger, Tooltip, Icon, PopoverContent, Card, CardBody, 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",
@@ -777,6 +777,99 @@ const FiltersRenderer = ({ filters, filterBarConfig, visualStyle, }) => {
777
777
  .map(filter => (jsx(FilterComponent, { filter: filter, filterBarActions: filterBarConfig, filterState: { values: filterBarConfig.values, setters: filterBarConfig.setters }, visualStyle: visualStyle }, `filter-${filter.filterKey}`)));
778
778
  };
779
779
 
780
+ /**
781
+ * TooltipValues component that displays formatted tooltip values based on the provided input.
782
+ *
783
+ * @param {TooltipValuesProps} props - The props for the TooltipValues component.
784
+ * @param {FilterValueType} props.values - The input values to render in the tooltip.
785
+ * @returns {ReactElement} The rendered TooltipValues component.
786
+ */
787
+ const TooltipValues = ({ values }) => {
788
+ return jsx("div", { children: renderTooltipValues(values) });
789
+ };
790
+ const renderTooltipValues = (values) => {
791
+ if (values === undefined) {
792
+ return null;
793
+ }
794
+ // Array of objects ValueName[]
795
+ if (Array.isArray(values) && typeof values[0] === "object") {
796
+ return (jsx("ul", { className: "list-inside", children: values.map((value, index) => (jsx("li", { className: "list-disc", children: value.name }, index))) }));
797
+ }
798
+ // Array of strings
799
+ if (Array.isArray(values)) {
800
+ return (jsx("ul", { className: "list-inside", children: values.map((value, index) => {
801
+ return (jsx("li", { className: "list-disc", children: typeof value === "string" ? value : value.name }, index));
802
+ }) }));
803
+ }
804
+ // single object ValueName
805
+ if (typeof values === "object" && values.name) {
806
+ return (jsx("ul", { className: "list-inside", children: jsx("li", { className: "list-disc", children: values.name }) }));
807
+ }
808
+ // String or number
809
+ if (typeof values === "string" || typeof values === "number") {
810
+ return (jsx("ul", { className: "list-inside", children: jsx("li", { className: "list-disc", children: values.toString() }) }));
811
+ }
812
+ // BooleanValue
813
+ if (typeof values === "object" && values.booleanValue !== undefined) {
814
+ return values.booleanValue ? "Yes" : "No";
815
+ }
816
+ // MinMaxFilterValue
817
+ if (typeof values === "object" && "min" in values && "max" in values) {
818
+ const minMax = values;
819
+ return `Min: ${minMax.min}, Max: ${minMax.max}`;
820
+ }
821
+ return null;
822
+ };
823
+
824
+ /**
825
+ * Component that displays a tooltip label with filter details.
826
+ *
827
+ * @template TFilterBarDefinition - The type of the filter bar definition.
828
+ * @param {SingleFilterTooltipLabelProps} props - Component properties.
829
+ * @param {FilterState<TFilterBarDefinition> & FilterMapActions & FilterMapGetter} props.filterBarConfig -
830
+ * Configuration object for the filter bar, containing state, actions, and accessors.
831
+ * @param {string} props.filterKey - The key identifying the filter.
832
+ * @returns {ReactElement} The rendered tooltip label and applied filter values.
833
+ */
834
+ const SingleFilterTooltipLabel = ({ filterBarConfig, filterKey, }) => {
835
+ const title = filterBarConfig.getFilterTitle(filterKey);
836
+ const values = filterBarConfig.getValuesByKey(filterKey);
837
+ const [t] = useTranslation();
838
+ return (jsxs(Fragment, { children: [t("filtersBar.appliedFiltersTooltip.singular", {
839
+ filterName: title,
840
+ }), jsx(TooltipValues, { values: values })] }));
841
+ };
842
+
843
+ /**
844
+ * Component that displays a tooltip for filters.
845
+ * Differentiates cases with no applied filters, a single filter, or multiple filters.
846
+ *
847
+ * @template TFilterBarDefinition The type definition for the FilterBar.
848
+ * @param {MultipleFilterTooltipLabelProps} props The component's props.
849
+ * @param {FilterState<TFilterBarDefinition> & FilterMapActions & FilterMapGetter} props.filterBarConfig
850
+ * The FilterBar configuration, including filter state, actions, and getter methods.
851
+ * @param {string[]} [props.filterKeys] An optional list of filter keys to filter the currently applied filters.
852
+ * @returns {ReactElement | string} Returns a element or string for the tooltip displaying applied filters.
853
+ */
854
+ const MultipleFilterTooltipLabel = ({ filterBarConfig, filterKeys, }) => {
855
+ const [t] = useTranslation();
856
+ const appliedFilterKeys = filterKeys
857
+ ? filterKeys.filter(key => filterBarConfig.appliedFilterKeys().includes(key))
858
+ : filterBarConfig.appliedFilterKeys();
859
+ switch (appliedFilterKeys.length) {
860
+ case 0:
861
+ return t("filtersBar.appliedFiltersTooltip.none");
862
+ case 1:
863
+ return jsx(SingleFilterTooltipLabel, { filterBarConfig: filterBarConfig, filterKey: appliedFilterKeys[0] });
864
+ default:
865
+ return (jsxs(Fragment, { children: [t("filtersBar.appliedFiltersTooltip.plural", { count: appliedFilterKeys.length }), appliedFilterKeys.map(filterKey => {
866
+ const title = filterBarConfig.getFilterTitle(filterKey);
867
+ const values = filterBarConfig.getValuesByKey(filterKey);
868
+ return filterBarConfig.appliedFilterKeys().includes(filterKey) ? (jsxs("div", { children: [jsxs("div", { children: [title, ":"] }), jsx(TooltipValues, { values: values })] }, filterKey)) : null;
869
+ })] }));
870
+ }
871
+ };
872
+
780
873
  /**
781
874
  * FiltersList is a React component that displays a list of filters within a filter bar.
782
875
  *
@@ -845,7 +938,7 @@ const FiltersMenu = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [],
845
938
  setSearchText("");
846
939
  }
847
940
  }, placement: "bottom-start", children: modalState => {
848
- 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(FilterButtonTooltipLabel, { 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: 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 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
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: 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 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
849
942
  ? searchResultsGrouped
850
943
  : showCustomFilters
851
944
  ? filtersToShowGrouped
@@ -855,21 +948,6 @@ const FiltersMenu = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [],
855
948
  } }), 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] }));
856
949
  };
857
950
  const Separator = () => jsx("hr", { className: "border-secondary-200", role: "separator" });
858
- const FilterButtonTooltipLabel = ({ filterBarConfig, }) => {
859
- const [t] = useTranslation();
860
- switch (filterBarConfig.appliedFilterKeys().length) {
861
- case 0:
862
- return t("filtersBar.appliedFiltersTooltip.none");
863
- case 1:
864
- return filterBarConfig.appliedFilterKeys()[0]
865
- ? t("filtersBar.appliedFiltersTooltip.singular", {
866
- filterName: filterBarConfig.getFilterTitle(filterBarConfig.appliedFilterKeys()[0]),
867
- })
868
- : null; // should never happen though
869
- default:
870
- 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))) })] }));
871
- }
872
- };
873
951
  const FiltersAppliedCountLabel = ({ filterBarConfig, }) => {
874
952
  const [t] = useTranslation();
875
953
  switch (filterBarConfig.appliedFilterKeys().length) {
@@ -904,13 +982,16 @@ const FilterTableComponent = ({ filterKey, filterBarDefinition, filterBarConfig,
904
982
  return [];
905
983
  }, [filterKey, filterBarDefinition, ensureFilterKey]);
906
984
  if (Array.isArray(filterKey)) {
907
- return (jsx(MoreMenu, { customButton: jsx(IconButton, { icon: jsx(Icon, { color: filterKey.some(key => filterBarConfig.appliedFilterKeys().includes(key)) ? "primary" : undefined, name: "Filter", size: "small" }), onClick: event => event.stopPropagation(), size: "extraSmall", variant: "ghost-neutral" }), children: () => (jsx(MenuList, { children: filters.map((value, index) => value && (jsx(FilterComponent, { filter: value, filterBarActions: filterBarConfig, filterState: filterBarConfig, visualStyle: "list-item" }, index))) })) }));
985
+ const isActive = filterKey.some(key => filterBarConfig.appliedFilterKeys().includes(key));
986
+ return (jsx(Popover, { placement: "bottom-start", children: modalState => {
987
+ 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))) }) })] }));
988
+ } }));
908
989
  }
909
990
  const filter = filterBarDefinition[ensureFilterKey(filterKey)];
910
991
  if (!filter) {
911
992
  return null;
912
993
  }
913
- return (jsx("div", { onClick: event => event.stopPropagation(), children: jsx(FilterComponent, { asIcon: "Filter", filter: filter, filterBarActions: filterBarConfig, filterState: filterBarConfig, size: "extraSmall" }) }));
994
+ 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" }) }) }));
914
995
  };
915
996
 
916
997
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/filters-filter-bar",
3
- "version": "1.3.190",
3
+ "version": "1.3.192",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -13,19 +13,19 @@
13
13
  "jest-fetch-mock": "^3.0.3",
14
14
  "tailwind-merge": "^2.0.0",
15
15
  "string-ts": "^2.0.0",
16
- "@trackunit/iris-app-api": "1.3.123",
17
- "@trackunit/react-core-hooks": "1.3.128",
18
- "@trackunit/react-filter-components": "1.3.159",
19
- "@trackunit/react-date-and-time-components": "1.3.162",
20
16
  "zod": "3.23.4",
21
- "@trackunit/shared-utils": "1.5.118",
22
- "@trackunit/react-form-components": "1.3.159",
23
- "@trackunit/react-core-contexts-api": "1.4.124",
24
- "@trackunit/geo-json-utils": "1.3.118",
25
- "@trackunit/i18n-library-translation": "1.3.132",
26
- "@trackunit/css-class-variance-utilities": "1.3.118",
27
- "@trackunit/react-components": "1.4.141",
28
- "@trackunit/react-test-setup": "1.0.8"
17
+ "@trackunit/iris-app-api": "1.3.124",
18
+ "@trackunit/react-core-hooks": "1.3.129",
19
+ "@trackunit/react-filter-components": "1.3.161",
20
+ "@trackunit/react-date-and-time-components": "1.3.164",
21
+ "@trackunit/shared-utils": "1.5.119",
22
+ "@trackunit/react-form-components": "1.3.161",
23
+ "@trackunit/react-core-contexts-api": "1.4.125",
24
+ "@trackunit/geo-json-utils": "1.3.119",
25
+ "@trackunit/i18n-library-translation": "1.3.133",
26
+ "@trackunit/css-class-variance-utilities": "1.3.119",
27
+ "@trackunit/react-components": "1.4.143",
28
+ "@trackunit/react-test-setup": "1.0.9"
29
29
  },
30
30
  "module": "./index.esm.js",
31
31
  "main": "./index.cjs.js",
@@ -0,0 +1,19 @@
1
+ import { ReactElement } from "react";
2
+ import { FilterBarDefinition, FilterMapActions, FilterMapGetter, FilterState } from "../../types/FilterTypes";
3
+ interface MultipleFilterTooltipLabelProps<TFilterBarDefinition extends FilterBarDefinition> {
4
+ filterBarConfig: FilterState<TFilterBarDefinition> & FilterMapActions & FilterMapGetter;
5
+ filterKeys?: string[];
6
+ }
7
+ /**
8
+ * Component that displays a tooltip for filters.
9
+ * Differentiates cases with no applied filters, a single filter, or multiple filters.
10
+ *
11
+ * @template TFilterBarDefinition The type definition for the FilterBar.
12
+ * @param {MultipleFilterTooltipLabelProps} props The component's props.
13
+ * @param {FilterState<TFilterBarDefinition> & FilterMapActions & FilterMapGetter} props.filterBarConfig
14
+ * The FilterBar configuration, including filter state, actions, and getter methods.
15
+ * @param {string[]} [props.filterKeys] An optional list of filter keys to filter the currently applied filters.
16
+ * @returns {ReactElement | string} Returns a element or string for the tooltip displaying applied filters.
17
+ */
18
+ declare const MultipleFilterTooltipLabel: <TFilterBarDefinition extends FilterBarDefinition>({ filterBarConfig, filterKeys, }: MultipleFilterTooltipLabelProps<TFilterBarDefinition>) => ReactElement | string;
19
+ export default MultipleFilterTooltipLabel;
@@ -0,0 +1,18 @@
1
+ import { ReactElement } from "react";
2
+ import { FilterBarDefinition, FilterMapActions, FilterMapGetter, FilterState } from "../../types/FilterTypes";
3
+ interface SingleFilterTooltipLabelProps<TFilterBarDefinition extends FilterBarDefinition> {
4
+ filterBarConfig: FilterState<TFilterBarDefinition> & FilterMapActions & FilterMapGetter;
5
+ filterKey: string;
6
+ }
7
+ /**
8
+ * Component that displays a tooltip label with filter details.
9
+ *
10
+ * @template TFilterBarDefinition - The type of the filter bar definition.
11
+ * @param {SingleFilterTooltipLabelProps} props - Component properties.
12
+ * @param {FilterState<TFilterBarDefinition> & FilterMapActions & FilterMapGetter} props.filterBarConfig -
13
+ * Configuration object for the filter bar, containing state, actions, and accessors.
14
+ * @param {string} props.filterKey - The key identifying the filter.
15
+ * @returns {ReactElement} The rendered tooltip label and applied filter values.
16
+ */
17
+ declare const SingleFilterTooltipLabel: <TFilterBarDefinition extends FilterBarDefinition>({ filterBarConfig, filterKey, }: SingleFilterTooltipLabelProps<TFilterBarDefinition>) => ReactElement;
18
+ export default SingleFilterTooltipLabel;
@@ -0,0 +1,14 @@
1
+ import { ReactElement } from "react";
2
+ import { FilterValueType } from "../../types/FilterTypes";
3
+ type TooltipValuesProps = {
4
+ values: FilterValueType;
5
+ };
6
+ /**
7
+ * TooltipValues component that displays formatted tooltip values based on the provided input.
8
+ *
9
+ * @param {TooltipValuesProps} props - The props for the TooltipValues component.
10
+ * @param {FilterValueType} props.values - The input values to render in the tooltip.
11
+ * @returns {ReactElement} The rendered TooltipValues component.
12
+ */
13
+ declare const TooltipValues: ({ values }: TooltipValuesProps) => ReactElement;
14
+ export default TooltipValues;