@trackunit/filters-filter-bar 1.8.4 → 1.8.6

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
@@ -451,7 +451,7 @@ const ResetFiltersButtonWithIcon = ({ resetFiltersToInitialState, dataTestId, cl
451
451
  */
452
452
  const AppliedFiltersRenderer = ({ appliedFilters, filterBarConfig, showResetButton = true, }) => {
453
453
  const filtersToRender = react.useMemo(() => appliedFilters.filter(filter => !filter.showDirectly), [appliedFilters]);
454
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [filtersToRender.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: filtersToRender }), appliedFilters.length > 0 && showResetButton ? (jsxRuntime.jsx(ResetFiltersButtonWithIcon, { resetFiltersToInitialState: filterBarConfig.resetFiltersToInitialState })) : null] }));
454
+ return (jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [filtersToRender.length > 0 ? (jsxRuntime.jsx("div", { className: "h-4 w-[1px] bg-neutral-300", "data-testid": "applied-filters-buttons" })) : null, jsxRuntime.jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: filtersToRender }), appliedFilters.length > 0 && showResetButton ? (jsxRuntime.jsx(ResetFiltersButtonWithIcon, { resetFiltersToInitialState: filterBarConfig.resetFiltersToInitialState })) : null] }));
455
455
  };
456
456
 
457
457
  /**
@@ -926,13 +926,23 @@ const FiltersMenuContent = ({ filterBarConfig, setShowCustomFilters, setSearchTe
926
926
  const Separator = () => jsxRuntime.jsx("hr", { className: "border-secondary-200", role: "separator" });
927
927
  const FiltersAppliedCountLabel = ({ filterBarConfig, }) => {
928
928
  const [t] = useTranslation();
929
- switch (filterBarConfig.appliedFilterKeys().length) {
929
+ // Count actual applied values, not just filter keys - same logic as badge
930
+ const appliedValuesCount = react.useMemo(() => {
931
+ return filterBarConfig.appliedFilterKeys().reduce((total, key) => {
932
+ const values = filterBarConfig.getValuesByKey(key);
933
+ if (Array.isArray(values)) {
934
+ return total + values.length;
935
+ }
936
+ return total + (values !== undefined ? 1 : 0);
937
+ }, 0);
938
+ }, [filterBarConfig]);
939
+ switch (appliedValuesCount) {
930
940
  case 0:
931
941
  return t("filtersBar.appliedFiltersTooltip.none");
932
942
  case 1:
933
- return filterBarConfig.appliedFilterKeys()[0] ? t("filtersMenu.appliedFiltersLabel.singular") : null;
943
+ return t("filtersMenu.appliedFiltersLabel.singular");
934
944
  default:
935
- return jsxRuntime.jsx(jsxRuntime.Fragment, { children: t("filtersMenu.appliedFiltersLabel.plural", { count: filterBarConfig.appliedFilterKeys().length }) });
945
+ return jsxRuntime.jsx(jsxRuntime.Fragment, { children: t("filtersMenu.appliedFiltersLabel.plural", { count: appliedValuesCount }) });
936
946
  }
937
947
  };
938
948
  const CustomFieldsHiddenGroup = ({ appliedCustomFields, filterBarConfig, onShow, }) => {
@@ -1029,7 +1039,7 @@ const MultipleFilterTooltipLabel = ({ filterBarConfig, filterKeys, filters, }) =
1029
1039
  }, {});
1030
1040
  switch (appliedFilterKeys.length) {
1031
1041
  case 0:
1032
- return jsxRuntime.jsx("div", { className: "text-xs font-medium", children: t("filtersBar.appliedFiltersTooltip.none") });
1042
+ return jsxRuntime.jsx("div", { className: "text-xs", children: t("filtersBar.appliedFiltersTooltip.none") });
1033
1043
  case 1:
1034
1044
  return (jsxRuntime.jsx(SingleFilterTooltipLabel, { filter: filtersMap[appliedFilterKeys[0]], filterBarConfig: filterBarConfig, filterKey: appliedFilterKeys[0] }));
1035
1045
  default:
@@ -1043,11 +1053,21 @@ const MultipleFilterTooltipLabel = ({ filterBarConfig, filterKeys, filters, }) =
1043
1053
  * @template TFilterBarDefinition - The type representing the filter bar definition.
1044
1054
  * @returns {ReactElement} - Returns the FilterMenu component.
1045
1055
  */
1046
- const FiltersMenu = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [], compact, title, dataTestId = "filters-menu", className, showAppliedFiltersCount = true, buttonProps, allowShowFiltersDirectly = true, includeFilterKeys, }) => {
1056
+ const FiltersMenu = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [], compact = false, title, dataTestId = "filters-menu", className, buttonProps, allowShowFiltersDirectly = true, includeFilterKeys, }) => {
1047
1057
  const [t] = useTranslation();
1048
- const { isSm } = reactComponents.useViewportBreakpoints();
1049
1058
  const [showCustomFilters, setShowCustomFilters] = react.useState(false);
1059
+ const { isSm } = reactComponents.useViewportBreakpoints();
1050
1060
  const filterBarDefinitionCount = react.useMemo(() => sharedUtils.objectValues(filterBarDefinition).length, [filterBarDefinition]);
1061
+ const totalAppliedValuesCount = react.useMemo(() => {
1062
+ const appliedKeys = filterBarConfig.appliedFilterKeys();
1063
+ return appliedKeys.reduce((total, key) => {
1064
+ const values = filterBarConfig.getValuesByKey(key);
1065
+ if (Array.isArray(values)) {
1066
+ return total + values.length;
1067
+ }
1068
+ return total + (Boolean(values) ? 1 : 0);
1069
+ }, 0);
1070
+ }, [filterBarConfig]);
1051
1071
  const { appliedFilters, showDirectlyFilters, hasCustomFields, filtersToShowGrouped, searchResultsGrouped, searchText, appliedCustomFields, removeCustomFieldsGroup, setSearchText, } = useFiltersMenu({
1052
1072
  filterBarDefinition,
1053
1073
  filterBarConfig,
@@ -1055,14 +1075,14 @@ const FiltersMenu = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [],
1055
1075
  includeFilterKeys,
1056
1076
  allowShowFiltersDirectly,
1057
1077
  });
1058
- return (jsxRuntime.jsxs("div", { className: tailwindMerge.twMerge("flex items-center gap-2", className), "data-testid": dataTestId, children: [jsxRuntime.jsx(reactComponents.Popover, { onOpenStateChange: open => {
1078
+ return (jsxRuntime.jsxs("div", { className: tailwindMerge.twMerge("flex items-start gap-2", className), "data-testid": dataTestId, children: [jsxRuntime.jsx(reactComponents.Popover, { onOpenStateChange: open => {
1059
1079
  if (!open) {
1060
1080
  setShowCustomFilters(false);
1061
1081
  setSearchText("");
1062
1082
  }
1063
1083
  }, placement: "bottom-start", children: modalState => {
1064
- 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: !isSm ? (jsxRuntime.jsx(reactComponents.IconButton, { "aria-label": title ?? t("filtersBar.filtersHeading"), icon: jsxRuntime.jsx(reactComponents.Icon, { ariaHidden: true, color: filterBarConfig.appliedFilterKeys().length > 0 ? "primary" : undefined, name: "Filter", size: "small" }), size: "small", variant: "secondary", ...buttonProps })) : (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 ? (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 !== "" ? (title ?? t("filtersBar.filtersHeading")) : null })) }) }) }), jsxRuntime.jsx(reactComponents.PopoverContent, { cellPadding: 100, children: jsxRuntime.jsx(FiltersMenuContent, { appliedCustomFields: appliedCustomFields, filterBarConfig: filterBarConfig, filterBarDefinitionCount: filterBarDefinitionCount, filtersToShowGrouped: filtersToShowGrouped, hasCustomFields: hasCustomFields, removeCustomFieldsGroup: removeCustomFieldsGroup, searchResultsGrouped: searchResultsGrouped, searchText: searchText, setSearchText: setSearchText, setShowCustomFilters: setShowCustomFilters, showCustomFilters: showCustomFilters }) })] }));
1065
- } }), showDirectlyFilters.length > 0 ? (jsxRuntime.jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: showDirectlyFilters })) : null, !compact ? jsxRuntime.jsx(AppliedFiltersRenderer, { appliedFilters: appliedFilters, filterBarConfig: filterBarConfig }) : null] }));
1084
+ 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: modalState.isOpen, label: jsxRuntime.jsx(MultipleFilterTooltipLabel, { filterBarConfig: filterBarConfig, filters: appliedFilters }), placement: "bottom", children: !isSm ? (jsxRuntime.jsx(reactComponents.Button, { "aria-label": title ?? t("filtersBar.filtersHeading"), prefix: jsxRuntime.jsx(reactComponents.Icon, { ariaHidden: true, color: filterBarConfig.appliedFilterKeys().length > 0 ? "primary" : undefined, name: "Funnel", size: "small" }), size: "small", suffix: totalAppliedValuesCount > 0 ? (jsxRuntime.jsx(reactComponents.Badge, { color: "primary", count: totalAppliedValuesCount, hideZero: true, size: "condensed" })) : null, variant: "secondary", ...buttonProps })) : (jsxRuntime.jsx(reactComponents.Button, { prefix: jsxRuntime.jsx(reactComponents.Icon, { ariaHidden: true, color: filterBarConfig.appliedFilterKeys().length > 0 ? "primary" : undefined, name: "Funnel", size: "small" }), size: "small", suffix: totalAppliedValuesCount > 0 ? (jsxRuntime.jsx(reactComponents.Badge, { color: "primary", count: totalAppliedValuesCount, hideZero: true, size: "condensed" })) : null, variant: "secondary", ...buttonProps, children: title !== "" ? (title ?? t("filtersBar.filtersHeading")) : null })) }) }) }), jsxRuntime.jsx(reactComponents.PopoverContent, { cellPadding: 100, children: jsxRuntime.jsx(FiltersMenuContent, { appliedCustomFields: appliedCustomFields, filterBarConfig: filterBarConfig, filterBarDefinitionCount: filterBarDefinitionCount, filtersToShowGrouped: filtersToShowGrouped, hasCustomFields: hasCustomFields, removeCustomFieldsGroup: removeCustomFieldsGroup, searchResultsGrouped: searchResultsGrouped, searchText: searchText, setSearchText: setSearchText, setShowCustomFilters: setShowCustomFilters, showCustomFilters: showCustomFilters }) })] }));
1085
+ } }), showDirectlyFilters.length > 0 ? (jsxRuntime.jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: showDirectlyFilters })) : null, compact === false ? (jsxRuntime.jsx(AppliedFiltersRenderer, { appliedFilters: appliedFilters, filterBarConfig: filterBarConfig })) : null] }));
1066
1086
  };
1067
1087
 
1068
1088
  /**
@@ -1080,17 +1100,35 @@ const FilterTableComponent = ({ filterKey, filterBarDefinition, filterBarConfig,
1080
1100
  }
1081
1101
  return [];
1082
1102
  }, [filterKey, filterBarDefinition, ensureFilterKey]);
1103
+ // Helper function to get manually applied filter count (excluding default values)
1104
+ const getManuallyAppliedFilterCount = react.useCallback((keys) => {
1105
+ return keys.reduce((total, key) => {
1106
+ const filterValues = filterBarConfig.getValuesByKey(key);
1107
+ const isDefaultValue = filterBarConfig.isDefaultValue(key, filterValues);
1108
+ if (isDefaultValue) {
1109
+ return total; // Don't count default values
1110
+ }
1111
+ if (Array.isArray(filterValues)) {
1112
+ return total + filterValues.length;
1113
+ }
1114
+ return total + (filterValues ? 1 : 0);
1115
+ }, 0);
1116
+ }, [filterBarConfig]);
1083
1117
  if (Array.isArray(filterKey)) {
1084
- const isActive = filterKey.some(key => filterBarConfig.appliedFilterKeys().includes(key));
1118
+ const appliedFilterKeys = filterBarConfig.appliedFilterKeys();
1119
+ const activeFilterKeys = filterKey.filter(key => appliedFilterKeys.includes(key));
1120
+ const multipleFiltersAppliedCount = getManuallyAppliedFilterCount(activeFilterKeys);
1121
+ const multipleFiltersIsActive = multipleFiltersAppliedCount > 0;
1085
1122
  return (jsxRuntime.jsx(reactComponents.Popover, { placement: "bottom-start", children: modalState => {
1086
- 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))) }) })] }));
1123
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(reactComponents.PopoverTrigger, { children: jsxRuntime.jsx("div", { className: "relative", children: jsxRuntime.jsx(reactComponents.Tooltip, { disabled: modalState.isOpen || !multipleFiltersIsActive, label: jsxRuntime.jsx(MultipleFilterTooltipLabel, { filterBarConfig: filterBarConfig, filterKeys: filterKey, filters: filters }), placement: "bottom", children: jsxRuntime.jsx(reactComponents.IconButton, { icon: jsxRuntime.jsx(reactComponents.Icon, { name: "Funnel", size: "small" }), size: "extraSmall", variant: multipleFiltersIsActive ? "ghost" : "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))) }) })] }));
1087
1124
  } }));
1088
1125
  }
1089
1126
  const filter = filterBarDefinition[ensureFilterKey(filterKey)];
1090
1127
  if (!filter) {
1091
1128
  return null;
1092
1129
  }
1093
- 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" }) }) }));
1130
+ const isActive = filterBarConfig.appliedFilterKeys().includes(filterKey);
1131
+ return (jsxRuntime.jsx("div", { onClick: event => event.stopPropagation(), children: jsxRuntime.jsx("div", { className: "relative", children: jsxRuntime.jsx(reactComponents.Tooltip, { disabled: !isActive, label: jsxRuntime.jsx(SingleFilterTooltipLabel, { filter: filter, filterBarConfig: filterBarConfig, filterKey: filterKey }), placement: "bottom", children: jsxRuntime.jsx(FilterComponent, { asIcon: "Funnel", filter: filter, filterBarActions: filterBarConfig, filterState: filterBarConfig, size: "extraSmall" }) }) }) }));
1094
1132
  };
1095
1133
 
1096
1134
  /**
package/index.esm.js CHANGED
@@ -2,7 +2,7 @@ import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
2
2
  import { registerTranslations, useNamespaceTranslation } from '@trackunit/i18n-library-translation';
3
3
  import { useMemo, useRef, useState, useEffect, useCallback, Fragment as Fragment$1 } from 'react';
4
4
  import { Filter, FilterBody, RadioFilterItem, CheckBoxFilterItem, FilterHeader as FilterHeader$1, FilterFooter } from '@trackunit/react-filter-components';
5
- import { Button, Icon, List, Text, Card, CardBody, useViewportBreakpoints, Popover, PopoverTrigger, Tooltip, IconButton, PopoverContent, MenuList } from '@trackunit/react-components';
5
+ import { Button, Icon, List, Text, Card, CardBody, useViewportBreakpoints, Popover, PopoverTrigger, Tooltip, Badge, PopoverContent, IconButton, MenuList } from '@trackunit/react-components';
6
6
  import { useAnalytics, useTextSearch, useCurrentUser, useCustomEncoding } from '@trackunit/react-core-hooks';
7
7
  import { capitalize } from 'string-ts';
8
8
  import { createEvent } from '@trackunit/react-core-contexts-api';
@@ -449,7 +449,7 @@ const ResetFiltersButtonWithIcon = ({ resetFiltersToInitialState, dataTestId, cl
449
449
  */
450
450
  const AppliedFiltersRenderer = ({ appliedFilters, filterBarConfig, showResetButton = true, }) => {
451
451
  const filtersToRender = useMemo(() => appliedFilters.filter(filter => !filter.showDirectly), [appliedFilters]);
452
- return (jsxs(Fragment, { children: [filtersToRender.length > 0 ? (jsx("div", { className: "h-4 w-[1px] bg-slate-300", "data-testid": "applied-filters-buttons" })) : null, jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: filtersToRender }), appliedFilters.length > 0 && showResetButton ? (jsx(ResetFiltersButtonWithIcon, { resetFiltersToInitialState: filterBarConfig.resetFiltersToInitialState })) : null] }));
452
+ return (jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [filtersToRender.length > 0 ? (jsx("div", { className: "h-4 w-[1px] bg-neutral-300", "data-testid": "applied-filters-buttons" })) : null, jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: filtersToRender }), appliedFilters.length > 0 && showResetButton ? (jsx(ResetFiltersButtonWithIcon, { resetFiltersToInitialState: filterBarConfig.resetFiltersToInitialState })) : null] }));
453
453
  };
454
454
 
455
455
  /**
@@ -924,13 +924,23 @@ const FiltersMenuContent = ({ filterBarConfig, setShowCustomFilters, setSearchTe
924
924
  const Separator = () => jsx("hr", { className: "border-secondary-200", role: "separator" });
925
925
  const FiltersAppliedCountLabel = ({ filterBarConfig, }) => {
926
926
  const [t] = useTranslation();
927
- switch (filterBarConfig.appliedFilterKeys().length) {
927
+ // Count actual applied values, not just filter keys - same logic as badge
928
+ const appliedValuesCount = useMemo(() => {
929
+ return filterBarConfig.appliedFilterKeys().reduce((total, key) => {
930
+ const values = filterBarConfig.getValuesByKey(key);
931
+ if (Array.isArray(values)) {
932
+ return total + values.length;
933
+ }
934
+ return total + (values !== undefined ? 1 : 0);
935
+ }, 0);
936
+ }, [filterBarConfig]);
937
+ switch (appliedValuesCount) {
928
938
  case 0:
929
939
  return t("filtersBar.appliedFiltersTooltip.none");
930
940
  case 1:
931
- return filterBarConfig.appliedFilterKeys()[0] ? t("filtersMenu.appliedFiltersLabel.singular") : null;
941
+ return t("filtersMenu.appliedFiltersLabel.singular");
932
942
  default:
933
- return jsx(Fragment, { children: t("filtersMenu.appliedFiltersLabel.plural", { count: filterBarConfig.appliedFilterKeys().length }) });
943
+ return jsx(Fragment, { children: t("filtersMenu.appliedFiltersLabel.plural", { count: appliedValuesCount }) });
934
944
  }
935
945
  };
936
946
  const CustomFieldsHiddenGroup = ({ appliedCustomFields, filterBarConfig, onShow, }) => {
@@ -1027,7 +1037,7 @@ const MultipleFilterTooltipLabel = ({ filterBarConfig, filterKeys, filters, }) =
1027
1037
  }, {});
1028
1038
  switch (appliedFilterKeys.length) {
1029
1039
  case 0:
1030
- return jsx("div", { className: "text-xs font-medium", children: t("filtersBar.appliedFiltersTooltip.none") });
1040
+ return jsx("div", { className: "text-xs", children: t("filtersBar.appliedFiltersTooltip.none") });
1031
1041
  case 1:
1032
1042
  return (jsx(SingleFilterTooltipLabel, { filter: filtersMap[appliedFilterKeys[0]], filterBarConfig: filterBarConfig, filterKey: appliedFilterKeys[0] }));
1033
1043
  default:
@@ -1041,11 +1051,21 @@ const MultipleFilterTooltipLabel = ({ filterBarConfig, filterKeys, filters, }) =
1041
1051
  * @template TFilterBarDefinition - The type representing the filter bar definition.
1042
1052
  * @returns {ReactElement} - Returns the FilterMenu component.
1043
1053
  */
1044
- const FiltersMenu = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [], compact, title, dataTestId = "filters-menu", className, showAppliedFiltersCount = true, buttonProps, allowShowFiltersDirectly = true, includeFilterKeys, }) => {
1054
+ const FiltersMenu = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [], compact = false, title, dataTestId = "filters-menu", className, buttonProps, allowShowFiltersDirectly = true, includeFilterKeys, }) => {
1045
1055
  const [t] = useTranslation();
1046
- const { isSm } = useViewportBreakpoints();
1047
1056
  const [showCustomFilters, setShowCustomFilters] = useState(false);
1057
+ const { isSm } = useViewportBreakpoints();
1048
1058
  const filterBarDefinitionCount = useMemo(() => objectValues(filterBarDefinition).length, [filterBarDefinition]);
1059
+ const totalAppliedValuesCount = useMemo(() => {
1060
+ const appliedKeys = filterBarConfig.appliedFilterKeys();
1061
+ return appliedKeys.reduce((total, key) => {
1062
+ const values = filterBarConfig.getValuesByKey(key);
1063
+ if (Array.isArray(values)) {
1064
+ return total + values.length;
1065
+ }
1066
+ return total + (Boolean(values) ? 1 : 0);
1067
+ }, 0);
1068
+ }, [filterBarConfig]);
1049
1069
  const { appliedFilters, showDirectlyFilters, hasCustomFields, filtersToShowGrouped, searchResultsGrouped, searchText, appliedCustomFields, removeCustomFieldsGroup, setSearchText, } = useFiltersMenu({
1050
1070
  filterBarDefinition,
1051
1071
  filterBarConfig,
@@ -1053,14 +1073,14 @@ const FiltersMenu = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [],
1053
1073
  includeFilterKeys,
1054
1074
  allowShowFiltersDirectly,
1055
1075
  });
1056
- return (jsxs("div", { className: twMerge("flex items-center gap-2", className), "data-testid": dataTestId, children: [jsx(Popover, { onOpenStateChange: open => {
1076
+ return (jsxs("div", { className: twMerge("flex items-start gap-2", className), "data-testid": dataTestId, children: [jsx(Popover, { onOpenStateChange: open => {
1057
1077
  if (!open) {
1058
1078
  setShowCustomFilters(false);
1059
1079
  setSearchText("");
1060
1080
  }
1061
1081
  }, placement: "bottom-start", children: modalState => {
1062
- 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: !isSm ? (jsx(IconButton, { "aria-label": title ?? t("filtersBar.filtersHeading"), icon: jsx(Icon, { ariaHidden: true, color: filterBarConfig.appliedFilterKeys().length > 0 ? "primary" : undefined, name: "Filter", size: "small" }), size: "small", variant: "secondary", ...buttonProps })) : (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 ? (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 !== "" ? (title ?? t("filtersBar.filtersHeading")) : null })) }) }) }), jsx(PopoverContent, { cellPadding: 100, children: jsx(FiltersMenuContent, { appliedCustomFields: appliedCustomFields, filterBarConfig: filterBarConfig, filterBarDefinitionCount: filterBarDefinitionCount, filtersToShowGrouped: filtersToShowGrouped, hasCustomFields: hasCustomFields, removeCustomFieldsGroup: removeCustomFieldsGroup, searchResultsGrouped: searchResultsGrouped, searchText: searchText, setSearchText: setSearchText, setShowCustomFilters: setShowCustomFilters, showCustomFilters: showCustomFilters }) })] }));
1063
- } }), showDirectlyFilters.length > 0 ? (jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: showDirectlyFilters })) : null, !compact ? jsx(AppliedFiltersRenderer, { appliedFilters: appliedFilters, filterBarConfig: filterBarConfig }) : null] }));
1082
+ return (jsxs(Fragment, { children: [jsx(PopoverTrigger, { children: jsx("div", { "data-testid": "starred-filters-menu-trigger", id: "starred-filters-menu-trigger", children: jsx(Tooltip, { disabled: modalState.isOpen, label: jsx(MultipleFilterTooltipLabel, { filterBarConfig: filterBarConfig, filters: appliedFilters }), placement: "bottom", children: !isSm ? (jsx(Button, { "aria-label": title ?? t("filtersBar.filtersHeading"), prefix: jsx(Icon, { ariaHidden: true, color: filterBarConfig.appliedFilterKeys().length > 0 ? "primary" : undefined, name: "Funnel", size: "small" }), size: "small", suffix: totalAppliedValuesCount > 0 ? (jsx(Badge, { color: "primary", count: totalAppliedValuesCount, hideZero: true, size: "condensed" })) : null, variant: "secondary", ...buttonProps })) : (jsx(Button, { prefix: jsx(Icon, { ariaHidden: true, color: filterBarConfig.appliedFilterKeys().length > 0 ? "primary" : undefined, name: "Funnel", size: "small" }), size: "small", suffix: totalAppliedValuesCount > 0 ? (jsx(Badge, { color: "primary", count: totalAppliedValuesCount, hideZero: true, size: "condensed" })) : null, variant: "secondary", ...buttonProps, children: title !== "" ? (title ?? t("filtersBar.filtersHeading")) : null })) }) }) }), jsx(PopoverContent, { cellPadding: 100, children: jsx(FiltersMenuContent, { appliedCustomFields: appliedCustomFields, filterBarConfig: filterBarConfig, filterBarDefinitionCount: filterBarDefinitionCount, filtersToShowGrouped: filtersToShowGrouped, hasCustomFields: hasCustomFields, removeCustomFieldsGroup: removeCustomFieldsGroup, searchResultsGrouped: searchResultsGrouped, searchText: searchText, setSearchText: setSearchText, setShowCustomFilters: setShowCustomFilters, showCustomFilters: showCustomFilters }) })] }));
1083
+ } }), showDirectlyFilters.length > 0 ? (jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: showDirectlyFilters })) : null, compact === false ? (jsx(AppliedFiltersRenderer, { appliedFilters: appliedFilters, filterBarConfig: filterBarConfig })) : null] }));
1064
1084
  };
1065
1085
 
1066
1086
  /**
@@ -1078,17 +1098,35 @@ const FilterTableComponent = ({ filterKey, filterBarDefinition, filterBarConfig,
1078
1098
  }
1079
1099
  return [];
1080
1100
  }, [filterKey, filterBarDefinition, ensureFilterKey]);
1101
+ // Helper function to get manually applied filter count (excluding default values)
1102
+ const getManuallyAppliedFilterCount = useCallback((keys) => {
1103
+ return keys.reduce((total, key) => {
1104
+ const filterValues = filterBarConfig.getValuesByKey(key);
1105
+ const isDefaultValue = filterBarConfig.isDefaultValue(key, filterValues);
1106
+ if (isDefaultValue) {
1107
+ return total; // Don't count default values
1108
+ }
1109
+ if (Array.isArray(filterValues)) {
1110
+ return total + filterValues.length;
1111
+ }
1112
+ return total + (filterValues ? 1 : 0);
1113
+ }, 0);
1114
+ }, [filterBarConfig]);
1081
1115
  if (Array.isArray(filterKey)) {
1082
- const isActive = filterKey.some(key => filterBarConfig.appliedFilterKeys().includes(key));
1116
+ const appliedFilterKeys = filterBarConfig.appliedFilterKeys();
1117
+ const activeFilterKeys = filterKey.filter(key => appliedFilterKeys.includes(key));
1118
+ const multipleFiltersAppliedCount = getManuallyAppliedFilterCount(activeFilterKeys);
1119
+ const multipleFiltersIsActive = multipleFiltersAppliedCount > 0;
1083
1120
  return (jsx(Popover, { placement: "bottom-start", children: modalState => {
1084
- 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))) }) })] }));
1121
+ return (jsxs(Fragment, { children: [jsx(PopoverTrigger, { children: jsx("div", { className: "relative", children: jsx(Tooltip, { disabled: modalState.isOpen || !multipleFiltersIsActive, label: jsx(MultipleFilterTooltipLabel, { filterBarConfig: filterBarConfig, filterKeys: filterKey, filters: filters }), placement: "bottom", children: jsx(IconButton, { icon: jsx(Icon, { name: "Funnel", size: "small" }), size: "extraSmall", variant: multipleFiltersIsActive ? "ghost" : "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))) }) })] }));
1085
1122
  } }));
1086
1123
  }
1087
1124
  const filter = filterBarDefinition[ensureFilterKey(filterKey)];
1088
1125
  if (!filter) {
1089
1126
  return null;
1090
1127
  }
1091
- 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" }) }) }));
1128
+ const isActive = filterBarConfig.appliedFilterKeys().includes(filterKey);
1129
+ return (jsx("div", { onClick: event => event.stopPropagation(), children: jsx("div", { className: "relative", children: jsx(Tooltip, { disabled: !isActive, label: jsx(SingleFilterTooltipLabel, { filter: filter, filterBarConfig: filterBarConfig, filterKey: filterKey }), placement: "bottom", children: jsx(FilterComponent, { asIcon: "Funnel", filter: filter, filterBarActions: filterBarConfig, filterState: filterBarConfig, size: "extraSmall" }) }) }) }));
1092
1130
  };
1093
1131
 
1094
1132
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/filters-filter-bar",
3
- "version": "1.8.4",
3
+ "version": "1.8.6",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -14,16 +14,16 @@
14
14
  "string-ts": "^2.0.0",
15
15
  "zod": "^3.23.8",
16
16
  "@trackunit/iris-app-api": "1.7.4",
17
- "@trackunit/react-core-hooks": "1.7.4",
18
- "@trackunit/react-filter-components": "1.7.4",
19
- "@trackunit/react-date-and-time-components": "1.10.4",
17
+ "@trackunit/react-core-hooks": "1.7.5",
18
+ "@trackunit/react-filter-components": "1.7.6",
19
+ "@trackunit/react-date-and-time-components": "1.10.6",
20
20
  "@trackunit/shared-utils": "1.9.4",
21
- "@trackunit/react-form-components": "1.8.4",
22
- "@trackunit/react-core-contexts-api": "1.8.4",
21
+ "@trackunit/react-form-components": "1.8.6",
22
+ "@trackunit/react-core-contexts-api": "1.8.5",
23
23
  "@trackunit/geo-json-utils": "1.7.4",
24
- "@trackunit/i18n-library-translation": "1.7.4",
24
+ "@trackunit/i18n-library-translation": "1.7.5",
25
25
  "@trackunit/css-class-variance-utilities": "1.7.4",
26
- "@trackunit/react-components": "1.9.4",
26
+ "@trackunit/react-components": "1.9.6",
27
27
  "@trackunit/react-test-setup": "1.4.4",
28
28
  "@tanstack/react-router": "1.114.29"
29
29
  },
@@ -22,10 +22,6 @@ interface FiltersMenuProps<TFilterBarDefinition extends FilterBarDefinition> ext
22
22
  * If true, the starred filters will be displayed in a compact mode
23
23
  */
24
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;
29
25
  /**
30
26
  * The title of the filter bar default is "Filters" (translated)
31
27
  */
@@ -45,5 +41,5 @@ interface FiltersMenuProps<TFilterBarDefinition extends FilterBarDefinition> ext
45
41
  * @template TFilterBarDefinition - The type representing the filter bar definition.
46
42
  * @returns {ReactElement} - Returns the FilterMenu component.
47
43
  */
48
- export declare const FiltersMenu: <TFilterBarDefinition extends FilterBarDefinition>({ filterBarDefinition, filterBarConfig, hiddenFilters, compact, title, dataTestId, className, showAppliedFiltersCount, buttonProps, allowShowFiltersDirectly, includeFilterKeys, }: FiltersMenuProps<TFilterBarDefinition>) => ReactElement;
44
+ export declare const FiltersMenu: <TFilterBarDefinition extends FilterBarDefinition>({ filterBarDefinition, filterBarConfig, hiddenFilters, compact, title, dataTestId, className, buttonProps, allowShowFiltersDirectly, includeFilterKeys, }: FiltersMenuProps<TFilterBarDefinition>) => ReactElement;
49
45
  export {};