@trackunit/filters-filter-bar 0.0.491 → 0.0.493

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.esm.js CHANGED
@@ -1,18 +1,20 @@
1
- import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { registerTranslations, useNamespaceTranslation } from '@trackunit/i18n-library-translation';
3
- import { twMerge } from 'tailwind-merge';
4
- import { cvaMerge } from '@trackunit/css-class-variance-utilities';
5
- import { Button, Popover, PopoverTrigger, IconButton, Icon, PopoverContent, MenuList, Text, StarButton, useViewportSize, Collapse, VirtualizedList } from '@trackunit/react-components';
6
- import { objectValues, truthy, capitalize, nonNullable } from '@trackunit/shared-utils';
7
- import { useMemo, useState, Fragment as Fragment$1, useRef, useEffect, useCallback } from 'react';
8
- import { Filter as Filter$1, FilterBody, RadioFilterItem, CheckBoxFilterItem, FilterHeader as FilterHeader$1, FilterFooter } from '@trackunit/react-filter-components';
9
3
  import { useAnalytics, useTextSearch } from '@trackunit/react-core-hooks';
4
+ import { FilterBody, RadioFilterItem, CheckBoxFilterItem, FilterHeader as FilterHeader$1, FilterFooter, Filter as Filter$1 } from '@trackunit/react-filter-components';
5
+ import { capitalize, nonNullable, objectValues, truthy } from '@trackunit/shared-utils';
6
+ import { useRef, useMemo, useState, useEffect, useCallback, Fragment as Fragment$1 } from 'react';
10
7
  import { createEvent } from '@trackunit/react-core-contexts-api';
8
+ import { VirtualizedList, Text, Button, useGeometry, usePrevious, useDebounce, useLocalStorage, Popover, PopoverTrigger, PopoverContent, MenuList, MenuItem, Tooltip, IconButton, Icon, StarButton, useViewportSize, Card, CardBody } from '@trackunit/react-components';
11
9
  import { Search, NumberField, RadioGroup } from '@trackunit/react-form-components';
12
10
  import { DayRangePicker } from '@trackunit/react-date-and-time-components';
11
+ import { cvaMerge } from '@trackunit/css-class-variance-utilities';
12
+ import { twMerge } from 'tailwind-merge';
13
+ import { z } from 'zod';
13
14
  import { dequal } from 'dequal';
14
- import { capitalize as capitalize$1 } from 'string-ts';
15
15
  import isEqual from 'lodash/isEqual';
16
+ import reduce from 'lodash/reduce';
17
+ import { capitalize as capitalize$1 } from 'string-ts';
16
18
 
17
19
  var defaultTranslations = {
18
20
  "access.management.filter.operator.role.keyAdmin": "Key Admin",
@@ -80,8 +82,8 @@ var defaultTranslations = {
80
82
  "assetFilters.regions.WESTERN_ASIA": "Western Asia",
81
83
  "assetFilters.regions.WESTERN_EUROPE": "Western Europe",
82
84
  "assetFilters.regions.WHOLE_WORLD": "Whole world",
83
- "assetFilters.searchFilter.label": "Search filter",
84
- "assetFilters.searchPlaceholder": "Search...",
85
+ "assetFilters.searchFilter.label": "Text search",
86
+ "assetFilters.searchPlaceholder": "Search by text...",
85
87
  "assetFilters.serviceAssignmentStatusFilter.label": "Assignment Status",
86
88
  "assetFilters.servicePlanFilter.fullyConfigured": "Fully configured",
87
89
  "assetFilters.servicePlanFilter.label": "Service Plan",
@@ -103,6 +105,12 @@ var defaultTranslations = {
103
105
  "auditlog.filter.users": "Users",
104
106
  "filter.more.options.if.you.search.description": "Use search to refine results.",
105
107
  "filter.more.options.if.you.search.title": "Over {{count}} items found.",
108
+ "filters.shared.clear": "clear",
109
+ "filters.shared.clearAll": "clear all",
110
+ "filters.shared.recentSearches": "Recent searches",
111
+ "filtersBar.appliedFiltersTooltip.none": "No filters applied",
112
+ "filtersBar.appliedFiltersTooltip.plural": "{{count}} filters applied:",
113
+ "filtersBar.appliedFiltersTooltip.singular": "{{filterName}} filter applied",
106
114
  "filtersBar.closeFilter": "Close",
107
115
  "filtersBar.defaultAssetFilters.followedFilter.ALL": "All Assets",
108
116
  "filtersBar.defaultAssetFilters.followedFilter.allLabel": "All Assets",
@@ -302,128 +310,6 @@ const setupLibraryTranslations = () => {
302
310
  registerTranslations(translations);
303
311
  };
304
312
 
305
- /**
306
- * Custom React hook for grouping and filtering a list of filters.
307
- *
308
- * @param {FilterDefinition[]} filterDefinitions - The list of filter definitions to group and filter.
309
- * @param {string[]} hiddenFilters - An array of filter keys representing filters to be hidden.
310
- * @returns {UseGroupFiltersReturnType} An object containing the grouped filters.
311
- */
312
- const useStarredGroupFilters = (filterDefinitions, hiddenFilters) => {
313
- const [t] = useTranslation();
314
- const filtersGrouped = useMemo(() => {
315
- const keys = uniqueKeysFromGroups(filterDefinitions);
316
- return keys
317
- .map(key => ({
318
- key: t(`filtersBar.groups.${key}`),
319
- filters: filterDefinitions.filter(rawFilter => rawFilter.group === key && hiddenFilters.includes(rawFilter.filterKey) === false),
320
- }))
321
- .filter(filter => filter.filters.length > 0);
322
- }, [filterDefinitions, hiddenFilters, t]);
323
- return { filtersGrouped };
324
- };
325
- const uniqueKeysFromGroups = (filters) => [...new Set(filters.map(filter => filter.group))];
326
-
327
- /**
328
- * Returns the two first values, appends counter if more.
329
- *
330
- * @param {string[]} [input] Array of values to reduce
331
- * @returns {*} {string}
332
- */
333
- const reduceFilterText = (input) => {
334
- if (!Array.isArray(input)) {
335
- return input;
336
- }
337
- // Only first two elements
338
- const firstTwo = input.slice(0, 2);
339
- const remainder = input.slice(2, input.length).length;
340
- const firstTwoJoined = firstTwo.join(", ");
341
- const remainderStr = remainder > 0 ? `, +${remainder}` : "";
342
- return firstTwoJoined + remainderStr;
343
- };
344
-
345
- /**
346
- * Filter is a React component that renders a filter element based on the provided filter definition and state.
347
- *
348
- * @returns {JSX.Element} - Returns the Filter component.
349
- */
350
- const Filter = ({ filter, filterBarActions, filterState, }) => {
351
- const values = filterBarActions.getValuesByKey(filter.filterKey);
352
- const filterText = () => {
353
- if (values) {
354
- if (filter.valueAsText) {
355
- return reduceFilterText(filter.valueAsText(values));
356
- }
357
- else if (filter.type === "valueName") {
358
- return reduceFilterText(values.map(value => value.name));
359
- }
360
- return values.toString();
361
- }
362
- return "";
363
- };
364
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
365
- const setValue = (callback) => {
366
- const newValue = callback(filterBarActions.getValuesByKey(filter.filterKey));
367
- if (filter.type === "string") {
368
- filterBarActions.setStringValue(filter.filterKey, newValue);
369
- }
370
- else if (filter.type === "stringArray") {
371
- filterBarActions.setArrayValue(filter.filterKey, newValue);
372
- }
373
- else if (filter.type === "dateRange") {
374
- filterBarActions.setDateRange(filter.filterKey, newValue);
375
- }
376
- else if (filter.type === "boundingBox") {
377
- filterBarActions.setBoundingBox(newValue);
378
- }
379
- else if (filter.type === "valueName") {
380
- filterBarActions.setArrayObjectValue(filter.filterKey, newValue);
381
- }
382
- else if (filter.type === "minMax") {
383
- filterBarActions.setMinMaxValue(filter.filterKey, newValue);
384
- }
385
- else if (filter.type === "boolean") {
386
- filterBarActions.setBooleanValue(filter.filterKey, newValue);
387
- }
388
- else {
389
- filterBarActions.setNumberValue(filter.filterKey, newValue);
390
- }
391
- };
392
- const text = filterText();
393
- const activeFilterText = Array.isArray(text) ? text.join(", ") : text;
394
- const showDirectly = filter.showDirectly || false;
395
- return showDirectly ? (jsx(Fragment, { children: filter.component({
396
- filterDefinition: filter,
397
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
398
- value: values,
399
- setValue,
400
- filterBarActions,
401
- filterState,
402
- }) })) : (jsx(Filter$1, { activeLabel: activeFilterText, dataTestId: `${filter.filterKey}-filter-button`, isActive: filterText().length > 0, title: filter.title, withStickyHeader: true, children: filter.component({
403
- filterDefinition: filter,
404
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
405
- value: values,
406
- setValue,
407
- filterBarActions,
408
- filterState,
409
- }) }));
410
- };
411
-
412
- /**
413
- * ResetFiltersButton is a React component that provides a button for resetting filters.
414
- *
415
- * @returns {ReactElement | null} The rendered ResetFiltersButton component, or null if no filters have been applied.
416
- */
417
- const ResetFiltersButton = ({ filtersHaveBeenApplied, resetFiltersToInitialState, }) => {
418
- const [t] = useTranslation();
419
- if (!filtersHaveBeenApplied) {
420
- return null;
421
- }
422
- return (jsx(Button, { dataTestId: "reset-filters-button", onClick: () => {
423
- resetFiltersToInitialState();
424
- }, size: "small", variant: "ghost-neutral", children: t("filtersBar.resetFilters") }));
425
- };
426
-
427
313
  /**
428
314
  * Events — each with a name, and eventType.
429
315
  * Adding a Description is encouraged.
@@ -439,101 +325,6 @@ const FilterEvents = {
439
325
  "Starring Filter - Toggled": createEvent(),
440
326
  };
441
327
 
442
- /**
443
- * StarredFiltersMenu is a React component that displays a menu of starred filters within a filter bar.
444
- *
445
- * @returns {JSX.Element} - Returns the StarredFiltersMenu component.
446
- */
447
- const StarredFiltersMenu = ({ filterBarDefinition, updateStarredFilters, starredFilterKeys, hiddenFilters = [], }) => {
448
- const [t] = useTranslation();
449
- const { logEvent } = useAnalytics(FilterEvents);
450
- const hideInStarredMenu = useMemo(() => {
451
- return objectValues(filterBarDefinition)
452
- .map(filter => {
453
- const showInStarredMenu = filter.showInStarredMenu ? filter.showInStarredMenu() : true;
454
- const showInFilterBar = filter.showInFilterBar ? filter.showInFilterBar() : true;
455
- return !showInStarredMenu || !showInFilterBar ? filter.filterKey : null;
456
- })
457
- .filter(truthy);
458
- }, [filterBarDefinition]);
459
- const { filtersGrouped } = useStarredGroupFilters(objectValues(filterBarDefinition), [
460
- ...hideInStarredMenu,
461
- ...hiddenFilters,
462
- ]);
463
- return (jsxs(Fragment, { children: [jsxs(Popover, { placement: "bottom-start", children: [jsx(PopoverTrigger, { children: jsx(IconButton, { dataTestId: "starred-filters-menu", icon: jsx(Icon, { name: "Funnel", size: "small" }), size: "small", title: t("filtersBar.starredFiltersTitle") }) }), jsx(PopoverContent, { children: jsx(MenuList, { className: "max-h-[55vh] w-72 overflow-y-auto", dataTestId: "starred-filters-menu-popover", children: jsx("div", { children: filtersGrouped.map((group, idx) => {
464
- const isLast = idx === filtersGrouped.length - 1;
465
- return (jsxs("div", { className: "mb-2", children: [jsx("div", { className: "mt-2 mb-1 flex px-2", children: jsx(Text, { dataTestId: "starred-filters-group-title", size: "small", subtle: true, uppercase: true, weight: "bold", children: group.key }) }), group.filters.map(filter => {
466
- return (jsxs("div", { className: "flex w-full cursor-pointer items-center justify-between rounded-sm py-0 px-2 transition hover:bg-neutral-50", "data-testid": `${filter.filterKey}-star-filter`, onClick: () => {
467
- updateStarredFilters(filter.filterKey);
468
- logEvent("Starring Filter - Toggled", {
469
- type: filter.filterKey,
470
- });
471
- }, children: [jsx(Text, { weight: "thick", children: filter.title }), jsx(StarButton, { onClick: e => {
472
- e.preventDefault();
473
- e.stopPropagation();
474
- updateStarredFilters(filter.filterKey);
475
- logEvent("Starring Filter - Toggled", {
476
- type: filter.filterKey,
477
- });
478
- }, starred: starredFilterKeys.includes(filter.filterKey) })] }, filter.filterKey));
479
- }), !isLast && jsx("div", { className: "mt-2 h-[1px] w-full bg-neutral-300" })] }, group.key));
480
- }) }) }) })] }), starredFilterKeys.length > 0 ? jsx("div", { className: "h-7 w-[1px] bg-neutral-300" }) : null] }));
481
- };
482
-
483
- const cvaCollapse = cvaMerge("bg-white", {
484
- variants: {
485
- disableCollapse: { true: "hidden" },
486
- },
487
- });
488
- const cvaFilterWithoutCollapse = cvaMerge([], {
489
- variants: { disableCollapse: { true: "flex", false: ["hidden", "md:flex"] } },
490
- });
491
- /**
492
- * StarredFilters is a React component that displays a list of starred filters based on the provided filter bar configuration.
493
- *
494
- * @template TFilterBarDefinition - The type representing the filter bar definition.
495
- * @returns {JSX.Element} - Returns the StarredFilters component.
496
- */
497
- const StarredFilters = ({ filterBarDefinition, filterBarConfig, disableCollapse, hiddenFilters = [], }) => {
498
- const [expanded, setExpanded] = useState(false);
499
- const [t] = useTranslation();
500
- const { isMd } = useViewportSize();
501
- return isMd || disableCollapse ? (jsx(FilterSelection, { className: cvaFilterWithoutCollapse({ disableCollapse }), filterBarConfig: filterBarConfig, filterBarDefinition: filterBarDefinition, hiddenFilters: hiddenFilters })) : (jsx(Collapse, { className: cvaCollapse({ disableCollapse }), id: "storybook_collapsible", initialExpanded: expanded, label: t("filtersBar.filtersHeading") + (filterBarConfig.filtersHaveBeenApplied ? " *" : ""), onToggle: () => setExpanded(prev => !prev), children: jsx(FilterSelection, { className: "flex", filterBarConfig: filterBarConfig, filterBarDefinition: filterBarDefinition, hiddenFilters: hiddenFilters }) }));
502
- };
503
- const FilterSelection = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [], className, }) => {
504
- const hideInFilterBar = useMemo(() => {
505
- return objectValues(filterBarDefinition)
506
- .map(filter => {
507
- const showInFilterBar = filter.showInFilterBar ? filter.showInFilterBar() : true;
508
- return !showInFilterBar ? filter.filterKey : null;
509
- })
510
- .filter(truthy);
511
- }, [filterBarDefinition]);
512
- const { filtersGrouped } = useStarredGroupFilters(objectValues(filterBarDefinition), [
513
- ...hideInFilterBar,
514
- ...hiddenFilters,
515
- ]);
516
- return (jsxs("div", { className: twMerge([className, "flex-wrap", "items-center", "gap-3"]), "data-testid": "starred-filters", children: [jsx(StarredFiltersMenu, { filterBarDefinition: filterBarDefinition, hiddenFilters: hiddenFilters, starredFilterKeys: filterBarConfig.starredFilterKeys, updateStarredFilters: filterBarConfig.updateStarredFilters }), filtersGrouped.map(group => group.filters
517
- .filter(filter => !hiddenFilters.includes(filter.filterKey))
518
- .map(filter => {
519
- const showMenuAnyway = filterBarConfig.filterHasChanged(filter.filterKey) &&
520
- filter.showInStarredMenu &&
521
- !filter.showInStarredMenu();
522
- if (filterBarConfig.starredFilterKeys.includes(filter.filterKey) ||
523
- showMenuAnyway) {
524
- return (jsx(Fragment$1, { children: jsx(Filter, { filter: filter, filterBarActions: filterBarConfig, filterState: { values: filterBarConfig.values, setters: filterBarConfig.setters } }, `filter-${filter.filterKey}`) }, filter.filterKey));
525
- }
526
- return null;
527
- })), jsx(ResetFiltersButton, { filtersHaveBeenApplied: filterBarConfig.filtersHaveBeenApplied, resetFiltersToInitialState: filterBarConfig.resetFiltersToInitialState })] }));
528
- };
529
-
530
- /**
531
- * The FilterBar component serves as a wrapper for managing filters.
532
- */
533
- const FilterBar = ({ hiddenFilters, className, filterBarDefinition, filterBarConfig, disableCollapse = false, }) => {
534
- return (jsx("div", { className: twMerge("flex", className), "data-testid": `${filterBarConfig.name}-filterbar`, children: jsx(StarredFilters, { disableCollapse: disableCollapse, filterBarConfig: filterBarConfig, filterBarDefinition: filterBarDefinition, hiddenFilters: hiddenFilters }) }));
535
- };
536
-
537
328
  /**
538
329
  * Toggles a filter value in an array.
539
330
  *
@@ -559,7 +350,7 @@ const DynamicFilterList = ({ rowCount, keyMapper, labelMapper, onChange, checked
559
350
  const [t] = useTranslation();
560
351
  const parentRef = useRef(null);
561
352
  const updatedRowCount = useMemo(() => (showRequestMoreUseSearch ? rowCount + 1 : rowCount), [rowCount, showRequestMoreUseSearch]);
562
- return (jsx(FilterBody, { dataTestId: "FUUUCK", limitSize: true, ref: parentRef, children: jsx(VirtualizedList, { count: updatedRowCount, rowHeight: 40, separator: "space", children: index => {
353
+ return (jsx(FilterBody, { limitSize: true, ref: parentRef, children: jsx(VirtualizedList, { count: updatedRowCount, rowHeight: 40, separator: "space", children: index => {
563
354
  return (jsx("div", { children: showRequestMoreUseSearch && index === rowCount ? (jsxs("div", { className: "p-3 pt-2", children: [jsx("span", { className: "text-sm text-gray-600", children: t("filter.more.options.if.you.search.title", { count: rowCount }) }), jsx("br", {}), jsx("span", { className: "text-sm italic text-gray-400", children: t("filter.more.options.if.you.search.description") })] })) : type === "Radio" ? (jsx(RadioFilterItem, { dataTestId: "dynamic-filter-radio-" + keyMapper(index), itemCount: count(index), label: labelMapper(index), selected: checked(index), value: keyMapper(index) }, keyMapper(index))) : (jsx(CheckBoxFilterItem, { checked: checked(index), dataTestId: "dynamic-filter-check-box-" + keyMapper(index), itemCount: count(index), label: labelMapper(index), name: keyMapper(index), onChange: () => {
564
355
  onChange === null || onChange === void 0 ? void 0 : onChange(index);
565
356
  } }, keyMapper(index))) }));
@@ -571,13 +362,17 @@ const DynamicFilterList = ({ rowCount, keyMapper, labelMapper, onChange, checked
571
362
  *
572
363
  * @returns {JSX.Element} - Returns the FilterHeader component.
573
364
  */
574
- const FilterHeader = ({ filterKey, title, searchEnabled, searchProps, filterHasChanged, resetIndividualFilterToInitialState, onResetFilter, loading = false, }) => {
365
+ const FilterHeader = ({ filterKey, title, searchEnabled, searchProps, filterHasChanged, resetIndividualFilterToInitialState, onResetFilter, loading = false, className, dataTestId, }) => {
575
366
  const [t] = useTranslation();
576
367
  const handleResetFilter = () => {
577
368
  resetIndividualFilterToInitialState(filterKey);
578
369
  onResetFilter === null || onResetFilter === void 0 ? void 0 : onResetFilter();
579
370
  };
580
- return (jsx(FilterHeader$1, { dataTestId: `${filterKey}-filter-header`, loading: loading, onReset: handleResetFilter, resetLabel: t("filtersBar.resetFilter"), showReset: filterHasChanged(filterKey), title: title, children: searchEnabled ? (jsx(Search, { autoFocus: true, fieldSize: "small", id: `${filterKey}-search`, onChange: e => searchProps.onChange(e.currentTarget.value), placeholder: t("assetFilters.searchPlaceholder"), suffix: jsx(Text, { size: "small", subtle: true, children: searchProps.count }), value: searchProps.value })) : null }));
371
+ return (jsx(FilterHeader$1, { className: className, dataTestId: dataTestId !== null && dataTestId !== void 0 ? dataTestId : `${filterKey}-filter-header`, loading: loading, onReset: handleResetFilter, resetLabel: t("filtersBar.resetFilter"), showReset: filterHasChanged(filterKey), title: title, children: searchEnabled ? (jsx(Search, { autoFocus: true, fieldSize: "small", id: `${filterKey}-search`, onChange: e => searchProps.onChange(e.currentTarget.value), onKeyDown: e => {
372
+ if (e.key === "Enter" && searchProps.onEnter) {
373
+ searchProps.onEnter(searchProps.value);
374
+ }
375
+ }, placeholder: t("assetFilters.searchPlaceholder"), suffix: jsx(Text, { size: "small", subtle: true, children: searchProps.count }), value: searchProps.value })) : null }));
581
376
  };
582
377
 
583
378
  /**
@@ -792,6 +587,351 @@ const DefaultRadioFilter = ({ filterDefinition, filterBarActions, options, loadi
792
587
  }, value: selectedRadioId, children: jsx(DynamicFilterList, { checked: index => { var _a; return filterBarActions.objectArrayIncludesValue(filterDefinition.filterKey, ((_a = res[index]) === null || _a === void 0 ? void 0 : _a.key) || ""); }, count: index => { var _a; return (_a = res[index]) === null || _a === void 0 ? void 0 : _a.count; }, keyMapper: index => { var _a; return ((_a = res[index]) === null || _a === void 0 ? void 0 : _a.key) || ""; }, labelMapper: index => { var _a; return ((_a = res[index]) === null || _a === void 0 ? void 0 : _a.label) || ""; }, rowCount: res.length, showRequestMoreUseSearch: showRequestMoreUseSearch, type: "Radio" }) })) })] }));
793
588
  };
794
589
 
590
+ /**
591
+ * Custom React hook for grouping and filtering a list of filters.
592
+ *
593
+ * @param {FilterDefinition[]} filterDefinitions - The list of filter definitions to group and filter.
594
+ * @param {string[]} hiddenFilters - An array of filter keys representing filters to be hidden.
595
+ * @returns {UseGroupFiltersReturnType} An object containing the grouped filters.
596
+ */
597
+ const useStarredGroupFilters = (filterDefinitions, hiddenFilters) => {
598
+ const [t] = useTranslation();
599
+ const filtersGrouped = useMemo(() => {
600
+ const keys = uniqueKeysFromGroups(filterDefinitions);
601
+ return keys
602
+ .map(key => ({
603
+ key: t(`filtersBar.groups.${key}`),
604
+ filters: filterDefinitions.filter(rawFilter => rawFilter.group === key && hiddenFilters.includes(rawFilter.filterKey) === false),
605
+ }))
606
+ .filter(filter => filter.filters.length > 0);
607
+ }, [filterDefinitions, hiddenFilters, t]);
608
+ return { filtersGrouped };
609
+ };
610
+ const uniqueKeysFromGroups = (filters) => [...new Set(filters.map(filter => filter.group))];
611
+
612
+ /**
613
+ * Returns the two first values, appends counter if more.
614
+ *
615
+ * @param {string[]} [input] Array of values to reduce
616
+ * @returns {*} {string}
617
+ */
618
+ const reduceFilterText = (input) => {
619
+ if (!Array.isArray(input)) {
620
+ return input;
621
+ }
622
+ // Only first two elements
623
+ const firstTwo = input.slice(0, 2);
624
+ const remainder = input.slice(2, input.length).length;
625
+ const firstTwoJoined = firstTwo.join(", ");
626
+ const remainderStr = remainder > 0 ? `, +${remainder}` : "";
627
+ return firstTwoJoined + remainderStr;
628
+ };
629
+
630
+ /**
631
+ * Filter is a React component that renders a filter element based on the provided filter definition and state.
632
+ *
633
+ * @returns {JSX.Element} - Returns the Filter component.
634
+ */
635
+ const Filter = ({ filter, filterBarActions, filterState, }) => {
636
+ const values = filterBarActions.getValuesByKey(filter.filterKey);
637
+ const filterText = () => {
638
+ if (values) {
639
+ if (filter.valueAsText) {
640
+ return reduceFilterText(filter.valueAsText(values));
641
+ }
642
+ else if (filter.type === "valueName") {
643
+ return reduceFilterText(values.map(value => value.name));
644
+ }
645
+ return values.toString();
646
+ }
647
+ return "";
648
+ };
649
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
650
+ const setValue = (callback) => {
651
+ const newValue = callback(filterBarActions.getValuesByKey(filter.filterKey));
652
+ if (filter.type === "string") {
653
+ filterBarActions.setStringValue(filter.filterKey, newValue);
654
+ }
655
+ else if (filter.type === "stringArray") {
656
+ filterBarActions.setArrayValue(filter.filterKey, newValue);
657
+ }
658
+ else if (filter.type === "dateRange") {
659
+ filterBarActions.setDateRange(filter.filterKey, newValue);
660
+ }
661
+ else if (filter.type === "boundingBox") {
662
+ filterBarActions.setBoundingBox(newValue);
663
+ }
664
+ else if (filter.type === "valueName") {
665
+ filterBarActions.setArrayObjectValue(filter.filterKey, newValue);
666
+ }
667
+ else if (filter.type === "minMax") {
668
+ filterBarActions.setMinMaxValue(filter.filterKey, newValue);
669
+ }
670
+ else if (filter.type === "boolean") {
671
+ filterBarActions.setBooleanValue(filter.filterKey, newValue);
672
+ }
673
+ else {
674
+ filterBarActions.setNumberValue(filter.filterKey, newValue);
675
+ }
676
+ };
677
+ const text = filterText();
678
+ const activeFilterText = Array.isArray(text) ? text.join(", ") : text;
679
+ const showDirectly = filter.showDirectly || false;
680
+ return showDirectly ? (jsx(Fragment, { children: filter.component({
681
+ filterDefinition: filter,
682
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
683
+ value: values,
684
+ setValue,
685
+ filterBarActions,
686
+ filterState,
687
+ }) })) : (jsx(Filter$1, { activeLabel: activeFilterText, dataTestId: `${filter.filterKey}-filter-button`, isActive: filterText().length > 0, title: filter.title, withStickyHeader: true, children: filter.component({
688
+ filterDefinition: filter,
689
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
690
+ value: values,
691
+ setValue,
692
+ filterBarActions,
693
+ filterState,
694
+ }) }));
695
+ };
696
+
697
+ /**
698
+ * ResetFiltersButton is a React component that provides a button for resetting filters.
699
+ *
700
+ * @returns {ReactElement | null} The rendered ResetFiltersButton component, or null if no filters have been applied.
701
+ */
702
+ const ResetFiltersButton = ({ filtersHaveBeenApplied, resetFiltersToInitialState, }) => {
703
+ const [t] = useTranslation();
704
+ if (!filtersHaveBeenApplied) {
705
+ return null;
706
+ }
707
+ return (jsx(Button, { dataTestId: "reset-filters-button", onClick: () => {
708
+ resetFiltersToInitialState();
709
+ }, size: "small", variant: "ghost-neutral", children: t("filtersBar.resetFilters") }));
710
+ };
711
+
712
+ const MAX_RECENT_SEARCHES = 20;
713
+ /**
714
+ *
715
+ */
716
+ const SearchFilterInline = ({ filterBarActions, filterState, filter, className, dataTestId, }) => {
717
+ const { logEvent } = useAnalytics(FilterEvents);
718
+ const searchElementRef = useRef(null);
719
+ const geometry = useGeometry(searchElementRef);
720
+ const filterValue = (filter === null || filter === void 0 ? void 0 : filter.filterKey) ? String(filterState.values[filter.filterKey]) : undefined;
721
+ const [searchString, setSearchString] = useState(filterValue !== null && filterValue !== void 0 ? filterValue : "");
722
+ const previousFilterValue = usePrevious(filterValue);
723
+ useDebounce(searchString, 500, "both", debouncedSearchString => {
724
+ if (filterValue === debouncedSearchString) {
725
+ return;
726
+ }
727
+ apply({ value: debouncedSearchString, setRecent: false });
728
+ });
729
+ useEffect(() => {
730
+ // Catch a change in filter value from outside the component and update the search string to match
731
+ if (filterValue === searchString || previousFilterValue === filterValue) {
732
+ return;
733
+ }
734
+ filterValue !== undefined && setSearchString(filterValue);
735
+ }, [filterValue, previousFilterValue, searchString]);
736
+ const [t] = useTranslation();
737
+ const [recentSearches, setRecentSearches] = useLocalStorage({
738
+ defaultState: [],
739
+ key: "free-text-filter-recent-searches",
740
+ schema: z.array(z.string()),
741
+ });
742
+ const apply = useCallback(({ value, wasPreviousSearch = false, setRecent = true, }) => {
743
+ if (setRecent !== false) {
744
+ setRecentSearches(prev => {
745
+ const noDuplicatesRecent = prev.filter(r => r !== value);
746
+ const cappedLengthRecent = noDuplicatesRecent.slice(0, MAX_RECENT_SEARCHES - 1);
747
+ return [value, ...cappedLengthRecent];
748
+ });
749
+ }
750
+ logEvent("Filters Applied - V2", {
751
+ type: "free-text-search",
752
+ value,
753
+ additionalProperties: {
754
+ wasPreviousSearch: Boolean(wasPreviousSearch),
755
+ },
756
+ });
757
+ (filter === null || filter === void 0 ? void 0 : filter.filterKey) && filterBarActions.setStringValue(filter.filterKey, value);
758
+ }, [filter === null || filter === void 0 ? void 0 : filter.filterKey, filterBarActions, logEvent, setRecentSearches]);
759
+ return (jsxs(Popover, { activation: defaultActivation => ({ ...defaultActivation, keyboardHandlers: false }), dataTestId: dataTestId ? `${dataTestId}-popover` : undefined, placement: "bottom-start", children: [jsx(PopoverTrigger, { children: jsx(Search
760
+ //TODO: [Buss] remove height restruction once baseinput sixes matches buttons
761
+ , {
762
+ //TODO: [Buss] remove height restruction once baseinput sixes matches buttons
763
+ className: twMerge(className, "h-7"), fieldSize: "small", onChange: e => {
764
+ const parsedValue = z.string().safeParse(e.target.value);
765
+ parsedValue.success ? setSearchString(parsedValue.data) : setSearchString("");
766
+ }, onClear: () => setSearchString(""), onKeyDown: e => {
767
+ if (e.key === "Enter") {
768
+ apply({ value: searchString });
769
+ }
770
+ }, ref: searchElementRef, value: searchString }) }), recentSearches.length > 0 ? (jsx(PopoverContent, { children: jsx("div", { style: { minWidth: geometry.width }, children: jsxs(MenuList, { className: "overflow-hidden", children: [jsx(FilterBody, { className: "max-w-none p-1", limitSize: true, children: jsx("ul", { className: "grid h-full gap-0.5", children: recentSearches.map((option, index) => (jsx(MenuItem, { className: "max-h-fit overflow-hidden", id: "free-text-search-filter-inline", label: option, onClick: () => {
771
+ apply({ value: option, wasPreviousSearch: true });
772
+ setRecentSearches(prev => {
773
+ const noDuplicatesRecent = prev.filter(r => r !== option);
774
+ const cappedLengthRecent = noDuplicatesRecent.slice(0, MAX_RECENT_SEARCHES - 1);
775
+ return [option, ...cappedLengthRecent];
776
+ });
777
+ setSearchString(option);
778
+ }, selected: option === filterValue, size: "small", suffix: jsx(Tooltip, { label: t("filters.shared.clear"), children: jsx(IconButton, { circular: true, icon: jsx(Icon, { name: "Trash", size: "small" }), onClick: e => {
779
+ e.stopPropagation();
780
+ setRecentSearches(prev => prev.filter(r => r !== option));
781
+ }, size: "small", variant: "ghost-neutral" }) }) }, `${option}-${index}`))) }) }), jsxs("div", { className: "flex items-baseline justify-between gap-x-2 border-t-2 border-slate-300 px-2 py-0.5", children: [jsx(Text, { className: "py-0.5", size: "small", subtle: true, children: t("filters.shared.recentSearches") }), recentSearches.length > 1 ? (jsx(Button, { onClick: () => setRecentSearches([]), size: "small", variant: "ghost", children: t("filters.shared.clearAll") })) : null] })] }) }) })) : null] }));
782
+ };
783
+
784
+ /**
785
+ * StarredFiltersMenu is a React component that displays a menu of starred filters within a filter bar.
786
+ *
787
+ * @returns {JSX.Element} - Returns the StarredFiltersMenu component.
788
+ */
789
+ const StarredFiltersMenu = ({ filterBarDefinition, updateStarredFilters, starredFilterKeys, hiddenFilters = [], className, dataTestId, }) => {
790
+ const [t] = useTranslation();
791
+ const { logEvent } = useAnalytics(FilterEvents);
792
+ const hideInStarredMenu = useMemo(() => {
793
+ return objectValues(filterBarDefinition)
794
+ .map(filter => {
795
+ const showInStarredMenu = filter.showInStarredMenu ? filter.showInStarredMenu() : true;
796
+ const showInFilterBar = filter.showInFilterBar ? filter.showInFilterBar() : true;
797
+ return !showInStarredMenu || !showInFilterBar ? filter.filterKey : null;
798
+ })
799
+ .filter(truthy);
800
+ }, [filterBarDefinition]);
801
+ const { filtersGrouped } = useStarredGroupFilters(objectValues(filterBarDefinition), [
802
+ ...hideInStarredMenu,
803
+ ...hiddenFilters,
804
+ ]);
805
+ return (jsxs(Popover, { placement: "bottom-start", children: [jsx(PopoverTrigger, { children: jsx(IconButton, { className: className, dataTestId: dataTestId !== null && dataTestId !== void 0 ? dataTestId : "starred-filters-menu", icon: jsx(Icon, { name: "Star", size: "small" }), size: "small", title: t("filtersBar.starredFiltersTitle"), variant: "secondary" }) }), jsx(PopoverContent, { children: jsx(MenuList, { className: "overflow-hidden", dataTestId: "starred-filters-menu-popover", children: jsx("div", { className: "max-h-[55vh] w-72 overflow-y-auto", children: filtersGrouped.map((group, idx) => {
806
+ const isLast = idx === filtersGrouped.length - 1;
807
+ return (jsxs("div", { className: "mb-2", children: [jsx("div", { className: "mb-1 mt-2 flex px-2", children: jsx(Text, { dataTestId: "starred-filters-group-title", size: "small", subtle: true, uppercase: true, weight: "bold", children: group.key }) }), group.filters.map(filter => {
808
+ return (jsxs("div", { className: "flex w-full cursor-pointer items-center justify-between rounded-sm px-2 py-0 transition hover:bg-neutral-50", "data-testid": `${filter.filterKey}-star-filter`, onClick: () => {
809
+ updateStarredFilters(filter.filterKey);
810
+ logEvent("Starring Filter - Toggled", {
811
+ type: filter.filterKey,
812
+ });
813
+ }, children: [jsx(Text, { weight: "thick", children: filter.title }), jsx(StarButton, { onClick: e => {
814
+ e.preventDefault();
815
+ e.stopPropagation();
816
+ updateStarredFilters(filter.filterKey);
817
+ logEvent("Starring Filter - Toggled", {
818
+ type: filter.filterKey,
819
+ });
820
+ }, starred: starredFilterKeys.includes(filter.filterKey) })] }, filter.filterKey));
821
+ }), !isLast && jsx("div", { className: "mt-2 h-[1px] w-full bg-neutral-300" })] }, group.key));
822
+ }) }) }) })] }));
823
+ };
824
+
825
+ /**
826
+ * StarredFilters is a React component that displays a list of starred filters based on the provided filter bar configuration.
827
+ *
828
+ * @template TFilterBarDefinition - The type representing the filter bar definition.
829
+ * @returns {JSX.Element} - Returns the StarredFilters component.
830
+ */
831
+ const StarredFilters = ({ filterBarDefinition, filterBarConfig, disableCollapse, hiddenFilters = [], }) => {
832
+ const [t] = useTranslation();
833
+ const { isLg } = useViewportSize();
834
+ return (jsxs("div", { className: cvaFiltersAndSearchContainer({ collapsed: !(isLg || disableCollapse) }), children: [isLg || disableCollapse ? (jsx(FilterSelection, { filterBarConfig: filterBarConfig, filterBarDefinition: filterBarDefinition, hiddenFilters: hiddenFilters })) : (jsxs(Popover, { placement: "bottom-start", children: [jsx(PopoverTrigger, { children: jsx("div", { children: jsxs(Tooltip, { label: jsx(FilterButtonTooltipLabel, { filterBarConfig: filterBarConfig }), children: [jsx(Button, { className: "@xs:flex hidden", prefix: jsx(Icon, { color: filterBarConfig.appliedFilters.length > 0 ? "primary" : undefined, name: "Funnel", size: "small" }), size: "small", variant: "secondary", children: t("filtersBar.filtersHeading") }), jsx(IconButton, { className: "@xs:hidden", icon: jsx(Icon, { color: filterBarConfig.appliedFilters.length > 0 ? "primary" : undefined, name: "Funnel", size: "small" }), size: "small", variant: "secondary" })] }) }) }), jsx(PopoverContent, { cellPadding: 100, children: jsx(Card, { children: jsx(CardBody, { children: jsx(FilterSelection, { filterBarConfig: filterBarConfig, filterBarDefinition: filterBarDefinition, hiddenFilters: hiddenFilters }) }) }) })] })), filterBarDefinition.search ? (jsx("div", { className: "flex w-full", children: jsx(SearchFilterInline, { className: "w-full max-w-80", filter: filterBarDefinition.search, filterBarActions: filterBarConfig, filterState: { values: filterBarConfig.values, setters: filterBarConfig.setters } }, "filter-search") })) : null] }));
835
+ };
836
+ const FilterSelection = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [], className, }) => {
837
+ const hideInFilterBar = useMemo(() => {
838
+ return objectValues(filterBarDefinition)
839
+ .map(filter => {
840
+ const showInFilterBar = filter.showInFilterBar ? filter.showInFilterBar() : true;
841
+ return !showInFilterBar ? filter.filterKey : null;
842
+ })
843
+ .filter(truthy);
844
+ }, [filterBarDefinition]);
845
+ const { filtersGrouped } = useStarredGroupFilters(objectValues(filterBarDefinition), [
846
+ ...hideInFilterBar,
847
+ ...hiddenFilters,
848
+ ]);
849
+ return (jsxs("div", { className: twMerge([className, "flex-wrap", "items-center", "gap-3", "flex"]), "data-testid": "starred-filters", children: [jsx(StarredFiltersMenu, { filterBarDefinition: filterBarDefinition, hiddenFilters: hiddenFilters, starredFilterKeys: filterBarConfig.starredFilterKeys, updateStarredFilters: filterBarConfig.updateStarredFilters }), filterBarConfig.starredFilterKeys.length > 0 ? jsx("div", { className: "h-4 w-[1px] bg-slate-300" }) : null, filtersGrouped.map(group => group.filters
850
+ .filter(filter => !hiddenFilters.includes(filter.filterKey))
851
+ .map(filter => {
852
+ const showMenuAnyway = filterBarConfig.filterHasChanged(filter.filterKey) &&
853
+ filter.showInStarredMenu &&
854
+ !filter.showInStarredMenu();
855
+ if (filter.filterKey !== "search" &&
856
+ (filterBarConfig.starredFilterKeys.includes(filter.filterKey) ||
857
+ showMenuAnyway)) {
858
+ return (jsx(Fragment$1, { children: jsx(Filter, { filter: filter, filterBarActions: filterBarConfig, filterState: { values: filterBarConfig.values, setters: filterBarConfig.setters } }, `filter-${filter.filterKey}`) }, filter.filterKey));
859
+ }
860
+ return null;
861
+ })), jsx(ResetFiltersButton, { filtersHaveBeenApplied: filterBarConfig.appliedFilters.length > 0, resetFiltersToInitialState: filterBarConfig.resetFiltersToInitialState })] }));
862
+ };
863
+ const cvaFiltersAndSearchContainer = cvaMerge(["grid", "gap-2", "w-full"], {
864
+ variants: {
865
+ collapsed: {
866
+ true: ["grid-cols-min-fr"],
867
+ false: ["grid-cols-[minmax(0,1fr)_300px]"],
868
+ },
869
+ },
870
+ defaultVariants: {
871
+ collapsed: false,
872
+ },
873
+ });
874
+ const FilterButtonTooltipLabel = ({ filterBarConfig, }) => {
875
+ const [t] = useTranslation();
876
+ switch (filterBarConfig.appliedFilters.length) {
877
+ case 0:
878
+ return t("filtersBar.filtersHeading");
879
+ case 1:
880
+ return filterBarConfig.appliedFilters[0]
881
+ ? t("filtersBar.appliedFiltersTooltip.singular", {
882
+ filterName: filterBarConfig.getFilterTitle(filterBarConfig.appliedFilters[0]),
883
+ })
884
+ : null; // should never happen though
885
+ default:
886
+ return (jsxs(Fragment, { children: [t("filtersBar.appliedFiltersTooltip.plural", { count: filterBarConfig.appliedFilters.length }), jsx("ul", { className: "list-inside", children: filterBarConfig.appliedFilters.map((appliedFilterKey, index) => (jsx("li", { className: "list-disc", children: filterBarConfig.getFilterTitle(appliedFilterKey) }, index))) })] }));
887
+ }
888
+ };
889
+
890
+ /**
891
+ * The FilterBar component serves as a wrapper for managing filters.
892
+ */
893
+ const FilterBar = ({ hiddenFilters, className, filterBarDefinition, filterBarConfig, disableCollapse = false, }) => {
894
+ return (jsx("div", { className: twMerge("flex", className), "data-testid": `${filterBarConfig.name}-filterbar`, children: jsx(StarredFilters, { disableCollapse: disableCollapse, filterBarConfig: filterBarConfig, filterBarDefinition: filterBarDefinition, hiddenFilters: hiddenFilters }) }));
895
+ };
896
+
897
+ // Can't import jest.fn so must define a function that does nothing but mimics the jest.fn
898
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
899
+ const doNothing = (args) => { };
900
+ const mockFilterBar = {
901
+ filterBarConfig: {
902
+ isFilterIncludedByKey: doNothing,
903
+ appliedFilters: [],
904
+ arrayIncludesValue: doNothing,
905
+ getFilterTitle: doNothing,
906
+ getFilterBarName: doNothing,
907
+ initialState: { customerType: [] },
908
+ name: "test",
909
+ objectArrayIncludesValue: doNothing,
910
+ resetFiltersToInitialState: doNothing,
911
+ resetIndividualFilterToInitialState: doNothing,
912
+ updateStarredFilters: doNothing,
913
+ setArrayObjectValue: doNothing,
914
+ setArrayValue: doNothing,
915
+ setBooleanValue: doNothing,
916
+ setBoundingBox: doNothing,
917
+ setDateRange: doNothing,
918
+ setMinMaxValue: doNothing,
919
+ setNumberValue: doNothing,
920
+ setStringValue: doNothing,
921
+ toggleArrayObjectValue: doNothing,
922
+ toggleArrayValue: doNothing,
923
+ values: { customerType: [] },
924
+ filterHasChanged: doNothing,
925
+ starredFilterKeys: [],
926
+ getValuesByKey: doNothing,
927
+ setters: {
928
+ setCriticality: doNothing,
929
+ setServicePlan: doNothing,
930
+ },
931
+ },
932
+ filterBarDefinition: {},
933
+ };
934
+
795
935
  /**
796
936
  * A helper function that returns a default value based on the filter type.
797
937
  *
@@ -1040,9 +1180,13 @@ const useFilterBar = ({ name, onValuesChange, filterBarDefinition, initialState,
1040
1180
  }, [filterBarConfig, name]);
1041
1181
  const filterMapGetter = useMemo(() => {
1042
1182
  return {
1043
- getFilterName: () => {
1183
+ getFilterBarName: () => {
1044
1184
  return filterBarConfig.name;
1045
1185
  },
1186
+ getFilterTitle(key) {
1187
+ var _a, _b;
1188
+ return (_b = (_a = filterBarDefinition[key]) === null || _a === void 0 ? void 0 : _a.title) !== null && _b !== void 0 ? _b : key;
1189
+ },
1046
1190
  arrayIncludesValue(key, value) {
1047
1191
  const filter = filterBarConfig.values[key];
1048
1192
  return (filter === null || filter === void 0 ? void 0 : filter.includes(value)) || false;
@@ -1054,15 +1198,18 @@ const useFilterBar = ({ name, onValuesChange, filterBarDefinition, initialState,
1054
1198
  const values = filterBarConfig.values[key];
1055
1199
  return Boolean(values);
1056
1200
  },
1057
- get filtersHaveBeenApplied() {
1201
+ get appliedFilters() {
1058
1202
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1059
1203
  const initialStateValues = filterBarConfig.initialState || {};
1060
1204
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1061
1205
  const currentFilters = filterBarConfig.values || {};
1062
1206
  // parse and stringify to remove empty references before comparing
1207
+ // eslint-disable-next-line local-rules/no-typescript-assertion
1063
1208
  const currentValuesWithoutEmptyRefs = JSON.parse(JSON.stringify(currentFilters));
1209
+ // eslint-disable-next-line local-rules/no-typescript-assertion
1064
1210
  const initialStateValuesWithoutEmptyRefs = JSON.parse(JSON.stringify(initialStateValues));
1065
- return !dequal(currentValuesWithoutEmptyRefs, initialStateValuesWithoutEmptyRefs);
1211
+ return getKeysOfUnequalProperties(currentValuesWithoutEmptyRefs, initialStateValuesWithoutEmptyRefs).map(key => key.toString() // TODO: FilterName type should be PropertyKey instead of string to avoid this .toString() bs but out-of-scope for this round of improvements
1212
+ );
1066
1213
  },
1067
1214
  filterHasChanged(key) {
1068
1215
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
@@ -1079,7 +1226,7 @@ const useFilterBar = ({ name, onValuesChange, filterBarDefinition, initialState,
1079
1226
  return (filter === null || filter === void 0 ? void 0 : filter.find(f => f.value === value)) !== undefined || false;
1080
1227
  },
1081
1228
  };
1082
- }, [filterBarConfig.initialState, filterBarConfig.name, filterBarConfig.values]);
1229
+ }, [filterBarConfig.initialState, filterBarConfig.name, filterBarConfig.values, filterBarDefinition]);
1083
1230
  const filterMapActions = useMemo(() => {
1084
1231
  // Reset an individual filter to its initial state
1085
1232
  const resetIndividualFilterToInitialState = (key) => {
@@ -1292,6 +1439,14 @@ const useFilterBar = ({ name, onValuesChange, filterBarDefinition, initialState,
1292
1439
  };
1293
1440
  }, [filterBarConfig, filterMapActions, filterMapGetter, filterBarDefinition]);
1294
1441
  };
1442
+ const getKeysOfUnequalProperties = (obj1, obj2) => {
1443
+ return reduce(obj1, (result, value, key) => {
1444
+ if (!isEqual(value, obj2[key])) {
1445
+ result.push(key);
1446
+ }
1447
+ return result;
1448
+ }, []);
1449
+ };
1295
1450
 
1296
1451
  /**
1297
1452
  * Merges additional filters into an existing filter bar definition.
@@ -1314,4 +1469,4 @@ const mergeFilters = (filterBarDefinition, extraFilters) => {
1314
1469
  */
1315
1470
  setupLibraryTranslations();
1316
1471
 
1317
- export { DefaultCheckboxFilter, DefaultDateRangeFilter, DefaultMinMaxFilter, DefaultRadioFilter, DynamicFilterList, FilterBar, FilterEvents, FilterHeader, FilterResults, StarredFilters, isArrayFilterValue, isBooleanValue, isBoundingBox, isDateRangeValue, isMinMaxFilterValue, isStringArrayFilterValue, isValueNameFilterValue, mergeFilters, toggleFilterValue, useFilterBar, validateFilter };
1472
+ export { DefaultCheckboxFilter, DefaultDateRangeFilter, DefaultMinMaxFilter, DefaultRadioFilter, DynamicFilterList, FilterBar, FilterEvents, FilterHeader, FilterResults, StarredFilters, isArrayFilterValue, isBooleanValue, isBoundingBox, isDateRangeValue, isMinMaxFilterValue, isStringArrayFilterValue, isValueNameFilterValue, mergeFilters, mockFilterBar, toggleFilterValue, useFilterBar, validateFilter };