@abcagency/hc-ui-components 1.6.5 → 1.6.7

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.
Files changed (45) hide show
  1. package/dist/components/HireControlMap.js +43 -2
  2. package/dist/components/HireControlMap.js.map +1 -1
  3. package/dist/components/containers/accordions/filter-item-container.js +3 -1
  4. package/dist/components/containers/accordions/filter-item-container.js.map +1 -1
  5. package/dist/components/containers/filter/filter-item-container.js +29 -15
  6. package/dist/components/containers/filter/filter-item-container.js.map +1 -1
  7. package/dist/components/containers/list/item-list-container.js +1 -1
  8. package/dist/components/containers/list/item-list-container.js.map +1 -1
  9. package/dist/components/modules/accordions/filterItem.js +8 -3
  10. package/dist/components/modules/accordions/filterItem.js.map +1 -1
  11. package/dist/components/modules/filter/index.js +123 -19
  12. package/dist/components/modules/filter/index.js.map +1 -1
  13. package/dist/components/modules/filter/item.js +8 -3
  14. package/dist/components/modules/filter/item.js.map +1 -1
  15. package/dist/components/modules/filter/search.js +6 -1
  16. package/dist/components/modules/filter/search.js.map +1 -1
  17. package/dist/components/modules/list/list-item/list-item.js.map +1 -1
  18. package/dist/components/modules/maps/tabs.js +1 -1
  19. package/dist/components/modules/maps/tabs.js.map +1 -1
  20. package/dist/contexts/mapListContext.js +48 -3
  21. package/dist/contexts/mapListContext.js.map +1 -1
  22. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +51 -1
  23. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -1
  24. package/dist/styles/index.css +1 -1
  25. package/dist/types/util/filterUtil.d.ts +9 -3
  26. package/dist/util/algoliaSearchUtil.js.map +1 -1
  27. package/dist/util/filterUtil.js +77 -14
  28. package/dist/util/filterUtil.js.map +1 -1
  29. package/dist/util/twMerge.js +9 -0
  30. package/dist/util/twMerge.js.map +1 -0
  31. package/package.json +1 -1
  32. package/src/components/HireControlMap.js +48 -2
  33. package/src/components/containers/accordions/filter-item-container.js +1 -1
  34. package/src/components/containers/filter/filter-item-container.js +27 -14
  35. package/src/components/containers/list/item-list-container.tsx +1 -1
  36. package/src/components/modules/accordions/filterItem.js +16 -3
  37. package/src/components/modules/filter/index.js +161 -43
  38. package/src/components/modules/filter/item.js +15 -3
  39. package/src/components/modules/filter/search.js +7 -1
  40. package/src/components/modules/list/list-item/list-item.jsx +1 -1
  41. package/src/components/modules/maps/tabs.js +10 -10
  42. package/src/contexts/mapListContext.tsx +103 -10
  43. package/src/util/algoliaSearchUtil.js +7 -7
  44. package/src/util/filterUtil.js +63 -17
  45. package/src/util/twMerge.js +6 -0
@@ -122,7 +122,52 @@ export const HireControlMap = ({
122
122
  // Algolia search configuration (optional)
123
123
  algoliaAppId = null,
124
124
  algoliaApiKey = null,
125
- algoliaIndexName = null
125
+ algoliaIndexName = null,
126
+ /**
127
+ * Filter configuration (optional)
128
+ * @param {boolean} hideZeroResults - Hide filter options with 0 results (default: false)
129
+ * @param {boolean} dynamicCounts - Update counts based on active filters.
130
+ * If false, shows initial/total counts (default: true)
131
+ * @param {boolean} showFavorites - Show favorites special feature filter (default: true)
132
+ * @param {boolean} collapsedByDefault - Start with all filter sections collapsed (default: false)
133
+ * @param {boolean} sortAlphabetically - Sort filter options alphabetically instead of by count (default: false)
134
+ * @param {object} classNames - Custom Tailwind classes to apply to filter components
135
+ * @param {string} classNames.filterContainer - Classes for the main filter container wrapper
136
+ * @param {string} classNames.filterContent - Classes for the scrollable filter content area
137
+ * @param {string} classNames.filterAccordion - Classes for each filter accordion section
138
+ * @param {string} classNames.filterAccordionHeader - Classes for accordion header/trigger
139
+ * @param {string} classNames.filterAccordionContent - Classes for accordion content area
140
+ * @param {string} classNames.filterItem - Classes for individual filter items (checkboxes/options)
141
+ * @param {string} classNames.filterItemLabel - Classes for filter item labels
142
+ * @param {string} classNames.filterItemCheckbox - Classes for filter item checkboxes
143
+ * @param {string} classNames.filterItemCount - Classes for filter item count badges
144
+ * @param {string} classNames.searchInput - Classes for search input field
145
+ * @param {string} classNames.resetButton - Classes for reset all button
146
+ * @param {string} classNames.showJobsButton - Classes for show jobs button
147
+ * @param {string} classNames.filterFooter - Classes for the footer containing buttons
148
+ */
149
+ filterConfig = {
150
+ hideZeroResults: false, // Hide filter options with 0 results
151
+ dynamicCounts: true, // Update counts based on other active filters (false = show initial counts only)
152
+ showFavorites: true, // Show favorites filter
153
+ collapsedByDefault: false, // Start with all filter sections collapsed
154
+ sortAlphabetically: false, // Sort filter options alphabetically instead of by count
155
+ classNames: {
156
+ filterContainer: '', // Main filter container wrapper
157
+ filterContent: '', // Scrollable content area
158
+ filterAccordion: '', // Filter accordion section
159
+ filterAccordionHeader: '', // Accordion header/trigger
160
+ filterAccordionContent: '', // Accordion content body
161
+ filterItem: '', // Individual filter items
162
+ filterItemLabel: '', // Filter item labels
163
+ filterItemCheckbox: '', // Filter item checkboxes
164
+ filterItemCount: '', // Filter item count badges
165
+ searchInput: '', // Search input field
166
+ resetButton: '', // Reset all button
167
+ showJobsButton: '', // Show jobs button
168
+ filterFooter: '' // Footer with buttons
169
+ }
170
+ }
126
171
  }) => {
127
172
  // Load site configuration
128
173
  const { siteConfig, error } = useSiteConfig(clientToken, siteConfiguration);
@@ -190,7 +235,8 @@ export const HireControlMap = ({
190
235
  localStorageKey,
191
236
  hideMap,
192
237
  hideFilters,
193
- noEntities
238
+ noEntities,
239
+ filterConfig
194
240
  };
195
241
 
196
242
  // Calculate effective showMap value (hideMap overrides siteConfig.showMap)
@@ -47,7 +47,7 @@ const FilterItemContainer = ({
47
47
 
48
48
  const body = (
49
49
  <>
50
- {filter.items.sort().map(item => (
50
+ {filter.items.filter(item => item != null).sort().map(item => (
51
51
  <FilterItem
52
52
  key={item.name}
53
53
  item={item}
@@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
2
2
  import { useTrackEvent } from '~/contexts/trackEventContext';
3
3
  import FilterItem from '~/components/modules/filter/item';
4
4
  import { useMapList } from '~/contexts/mapListContext';
5
+ import { twMerge } from '~/util/twMerge';
5
6
 
6
7
  const FilterItemContainer = ({
7
8
  className,
@@ -17,18 +18,37 @@ const FilterItemContainer = ({
17
18
  subcategoryRequireCategory = false,
18
19
  ...rest
19
20
  }) => {
21
+ // All hooks MUST be called before any early returns
20
22
  const { trackEvent, eventTypes } = useTrackEvent();
21
- const itemName = item.name ? item.name : item;
22
- itemKey = itemKey === null ? itemName : itemKey;
23
- const isActive = selectedFilters != undefined && !!selectedFilters[field]?.[itemKey];
23
+ const { filteredListings, filterConfig } = useMapList();
24
+
25
+ // Initialize itemName and itemKey safely
26
+ const itemName = item?.name ? item.name : item;
27
+ const safeItemKey = itemKey === null ? itemName : itemKey;
28
+ const isActive = selectedFilters != undefined && !!selectedFilters[field]?.[safeItemKey];
24
29
  const [activeItem, setActiveItem] = useState(isActive);
25
30
 
26
31
  useEffect(() => {
27
32
  if (!selectedFilters || isExternalLink) return;
28
- setActiveItem(!!selectedFilters[field]?.[itemKey]);
29
- }, [selectedFilters, field, itemKey]);
33
+ setActiveItem(!!selectedFilters[field]?.[safeItemKey]);
34
+ }, [selectedFilters, field, safeItemKey, isExternalLink]);
35
+
36
+ useEffect(() => {
37
+ if (selectedFilters && selectedFilters[field] && Object.keys(selectedFilters[field])?.length > 0) return;
38
+ else if (activeItem === true) {
39
+ setActiveItem(false);
40
+ }
41
+ }, [selectedFilters, activeItem, field]);
42
+
43
+ // NOW we can do the guard check after all hooks
44
+ if (!item) {
45
+ console.warn('FilterItemContainer received undefined item for field:', field);
46
+ return null;
47
+ }
48
+
49
+ // Re-assign itemKey for the rest of the component
50
+ itemKey = safeItemKey;
30
51
 
31
- const { filteredListings } = useMapList();
32
52
  const changeHandler = () => {
33
53
  if (!isActive || isExternalLink) {
34
54
  trackEvent(eventTypes.FILTER_APPLIED, { filterType: field, filterChecked: itemKey });
@@ -87,16 +107,9 @@ const FilterItemContainer = ({
87
107
  });
88
108
  };
89
109
 
90
- useEffect(() => {
91
- if (selectedFilters && selectedFilters[field] && Object.keys(selectedFilters[field])?.length > 0) return;
92
- else if (activeItem === true) {
93
- setActiveItem(false);
94
- }
95
- }, [selectedFilters]);
96
-
97
110
  return (
98
111
  <FilterItem
99
- className={className}
112
+ className={twMerge(className, filterConfig?.classNames?.filterItem)}
100
113
  item={item}
101
114
  type={type}
102
115
  itemKey={itemKey}
@@ -59,7 +59,7 @@ const ItemsListContainer: React.FC<ItemsListContainerProps> = ({
59
59
  selectedListItem={selectedListItem}
60
60
  includeFavorite={true}
61
61
  >
62
- <Accordion className="hc-divide-y hc-divide-uiAccent/10 hc-max-h-[100vh]" defaultValue={selectedListItem?.id}>
62
+ <Accordion className="hc-divide-y hc-divide-uiAccent/10" defaultValue={selectedListItem?.id}>
63
63
  {(sortSetting ? dynamicSort(filteredListings, sortSetting.field, sortSetting.type, favorites as any) : filteredListings)
64
64
  .slice(0, itemLimit)
65
65
  .map((item: Listing) => (
@@ -1,5 +1,7 @@
1
1
  import React from 'react';
2
2
  import Accordion from '~/components/modules/accordions/default';
3
+ import { twMerge } from '~/util/twMerge';
4
+ import { useMapList } from '~/contexts/mapListContext';
3
5
 
4
6
  const AccordionFilterItem = ({
5
7
  id,
@@ -7,17 +9,28 @@ const AccordionFilterItem = ({
7
9
  header,
8
10
  body
9
11
  }) => {
12
+ const { filterConfig } = useMapList();
13
+
10
14
  return (
11
15
  <Accordion.Item key={id} id={id}>
12
16
  <Accordion.Trigger.HasHeader
13
17
  onClick={() => setDefaultValue(id)}
14
- className="hc-stretched-link hc-text-left"
18
+ className={twMerge(
19
+ "hc-stretched-link hc-text-left",
20
+ filterConfig?.classNames?.filterAccordion
21
+ )}
15
22
  iconClassName="hc-order-last"
16
- headerClassName="hc-relative hc-py-2 hc-rounded hc-border hc-border-uiAccent/20 hc-bg-white hc-text-sm hc-transition data-[state=open]:hc-border-b-transparent data-[state=open]:hc-rounded-b-none"
23
+ headerClassName={twMerge(
24
+ "hc-relative hc-py-2 hc-rounded hc-border hc-border-uiAccent/20 hc-bg-white hc-text-sm hc-transition data-[state=open]:hc-border-b-transparent data-[state=open]:hc-rounded-b-none",
25
+ filterConfig?.classNames?.filterAccordionHeader
26
+ )}
17
27
  >
18
28
  {header}
19
29
  </Accordion.Trigger.HasHeader>
20
- <Accordion.Content bodyClassName="hc-px-2 hc-py-1 hc-bg-white hc-rounded-b hc-border hc-border-uiAccent/20 hc-border-t-0 hc-max-h-[20vh] md:hc-max-h-[25vh] hc-overflow-auto">
30
+ <Accordion.Content bodyClassName={twMerge(
31
+ "hc-px-2 hc-py-1 hc-bg-white hc-rounded-b hc-border hc-border-uiAccent/20 hc-border-t-0 hc-max-h-[20vh] md:hc-max-h-[25vh] hc-overflow-auto",
32
+ filterConfig?.classNames?.filterAccordionContent
33
+ )}>
21
34
  {body}
22
35
  </Accordion.Content>
23
36
  </Accordion.Item>
@@ -1,6 +1,9 @@
1
1
  "use client";
2
2
  import Button from "~/components/modules/buttons/default";
3
- import React from "react";
3
+ import React, { useEffect, useRef, useState } from "react";
4
+ import { twMerge } from '~/util/twMerge';
5
+ import { useMapList } from "~/contexts/mapListContext";
6
+
4
7
  const Filter = ({
5
8
  className,
6
9
  hasActiveFilters,
@@ -12,65 +15,180 @@ const Filter = ({
12
15
  style,
13
16
  children
14
17
  }) => {
18
+ const { filterConfig } = useMapList();
19
+ const contentRef = useRef(null);
20
+ const containerRef = useRef(null);
21
+ const [hasOverflow, setHasOverflow] = useState(false);
22
+ const [showFixedFooter, setShowFixedFooter] = useState(false);
23
+ const transitionTimeoutRef = useRef(null);
24
+
25
+ useEffect(() => {
26
+ const checkOverflow = () => {
27
+ if (contentRef.current) {
28
+ // Add a small tolerance (5px) to avoid false positives from rounding/padding
29
+ const hasScroll = contentRef.current.scrollHeight > contentRef.current.clientHeight + 5;
30
+
31
+ if (hasScroll !== hasOverflow) {
32
+ setHasOverflow(hasScroll);
33
+
34
+ // Clear any pending transitions
35
+ if (transitionTimeoutRef.current) {
36
+ clearTimeout(transitionTimeoutRef.current);
37
+ }
38
+
39
+ // Add a delay when transitioning to fixed footer to smooth the transition
40
+ if (hasScroll) {
41
+ transitionTimeoutRef.current = setTimeout(() => setShowFixedFooter(true), 200);
42
+ } else {
43
+ // Delay hiding fixed footer to allow animation to complete and prevent flashing
44
+ transitionTimeoutRef.current = setTimeout(() => setShowFixedFooter(false), 200);
45
+ }
46
+ }
47
+ }
48
+ };
49
+
50
+ // Delay initial check to ensure layout is complete
51
+ setTimeout(checkOverflow, 100);
52
+
53
+ // Check on resize
54
+ window.addEventListener('resize', checkOverflow);
55
+
56
+ // Use ResizeObserver to detect container size changes
57
+ const resizeObserver = new ResizeObserver(() => {
58
+ setTimeout(checkOverflow, 50);
59
+ });
60
+ if (containerRef.current) {
61
+ resizeObserver.observe(containerRef.current);
62
+ }
63
+ if (contentRef.current) {
64
+ resizeObserver.observe(contentRef.current);
65
+ }
66
+
67
+ // Use MutationObserver to detect content changes
68
+ const mutationObserver = new MutationObserver(() => {
69
+ setTimeout(checkOverflow, 50);
70
+ });
71
+ if (contentRef.current) {
72
+ mutationObserver.observe(contentRef.current, { childList: true, subtree: true, attributes: true });
73
+ }
74
+
75
+ return () => {
76
+ window.removeEventListener('resize', checkOverflow);
77
+ resizeObserver.disconnect();
78
+ mutationObserver.disconnect();
79
+ // Clear timeout on unmount
80
+ if (transitionTimeoutRef.current) {
81
+ clearTimeout(transitionTimeoutRef.current);
82
+ }
83
+ };
84
+ }, [children, hasOverflow]);
85
+
86
+ // Reusable button component
87
+ const MobileButtons = () => (
88
+ <>
89
+ <Button.Btn
90
+ onClick={handleReset}
91
+ variant="outline"
92
+ size="sm"
93
+ className={filterConfig?.classNames?.resetButton}
94
+ >
95
+ Reset All
96
+ </Button.Btn>
97
+ {selectedFilters && Object.keys(selectedFilters).length > 0 && (
98
+ <Button.Btn
99
+ onClick={() => setMobileTab("listTab")}
100
+ variant="primary"
101
+ size="sm"
102
+ className={twMerge(
103
+ `${hasActiveFilters ? "hc-opacity-0 hc-pointer-events-none" : "hc-opacity-100"}`,
104
+ filterConfig?.classNames?.showJobsButton
105
+ )}
106
+ >
107
+ <Button.Body>
108
+ <Button.Icon icon="fluent:search-12-filled" size="hc-size-3.5" />
109
+ Show {filteredListings.length} Jobs
110
+ </Button.Body>
111
+ </Button.Btn>
112
+ )}
113
+ </>
114
+ );
115
+
15
116
  return (
16
117
  <div
118
+ ref={containerRef}
17
119
  style={isDesktop ? style : undefined}
18
- className={`
19
- /* Mobile layout */
120
+ className={twMerge(
121
+ `/* Mobile layout */
20
122
  hc-relative hc-w-full hc-h-full hc-flex hc-flex-col
21
123
 
22
124
  /* Desktop layout */
23
- md:hc-block md:hc-max-h-[100%] md:hc-overflow-y-auto md:hc-overflow-x-auto
125
+ md:hc-relative md:hc-flex md:hc-flex-col md:hc-max-h-[100%]
24
126
 
25
- ${className ?? ""}
26
- `}
127
+ ${className ?? ""}`,
128
+ filterConfig?.classNames?.filterContainer
129
+ )}
27
130
  >
28
131
  {/* Content area with scroll */}
29
- <div className="hc-w-full hc-flex-grow hc-overflow-auto hc-pb-16 md:hc-pb-0">
30
- <div className="hc-px-4 md:hc-pt-4 hc-space-y-4">{children}</div>
31
- </div>
132
+ <div
133
+ ref={contentRef}
134
+ className={twMerge(
135
+ `hc-w-full hc-flex-grow hc-max-h-full hc-overflow-auto ${hasOverflow ? 'hc-pb-16' : ''} md:hc-overflow-y-auto ${hasOverflow ? 'md:hc-pb-16' : 'md:hc-pb-0'}`,
136
+ filterConfig?.classNames?.filterContent
137
+ )}
138
+ >
139
+ <div className="hc-px-4 md:hc-pt-4 hc-space-y-4">
140
+ {children}
32
141
 
33
- {/* Desktop Reset Button - left aligned */}
34
- <div className="hc-hidden md:hc-flex md:hc-justify-start hc-px-4 hc-py-4">
35
- <Button.Btn onClick={handleReset} variant="outline" size="sm">
36
- Reset All
37
- </Button.Btn>
142
+ {/* Inline buttons when no overflow - both mobile and desktop */}
143
+ {!hasOverflow && !showFixedFooter && (
144
+ <div className={twMerge(
145
+ "hc-flex hc-items-center hc-justify-between hc-gap-2 hc-py-2 hc-border-t hc-border-gray-200 hc-animate-in hc-fade-in hc-duration-300",
146
+ filterConfig?.classNames?.filterFooter
147
+ )}>
148
+ {/* Mobile buttons */}
149
+ <div className="hc-flex hc-items-center hc-justify-between hc-gap-2 hc-w-full md:hc-hidden">
150
+ <MobileButtons />
151
+ </div>
152
+ {/* Desktop button */}
153
+ <div className="hc-hidden md:hc-flex">
154
+ <Button.Btn
155
+ onClick={handleReset}
156
+ variant="outline"
157
+ size="sm"
158
+ className={filterConfig?.classNames?.resetButton}
159
+ >
160
+ Reset All
161
+ </Button.Btn>
162
+ </div>
163
+ </div>
164
+ )}
165
+ </div>
38
166
  </div>
39
167
 
40
- {/* Mobile Footer - fixed at bottom */}
41
- <div
42
- className="
43
- hc-w-full
44
- hc-absolute hc-bottom-0 hc-left-0 hc-right-0
45
- hc-flex hc-items-center hc-justify-between hc-gap-2 hc-py-2 hc-px-4
46
- hc-bg-white hc-border-t hc-border-gray-200
47
- hc-z-10
48
-
49
- /* Hide on desktop */
50
- md:hc-hidden
51
- "
52
- >
53
- <Button.Btn onClick={handleReset} variant="outline" size="sm">
54
- Reset All
55
- </Button.Btn>
56
- {selectedFilters && Object.keys(selectedFilters).length > 0 && (
168
+ {/* Mobile Footer - fixed at bottom (only when overflow) */}
169
+ {showFixedFooter && (
170
+ <div
171
+ className="hc-w-full hc-absolute hc-bottom-0 hc-left-0 hc-right-0 hc-flex hc-items-center hc-justify-between hc-gap-2 hc-py-2 hc-px-4 hc-bg-gray-100 hc-border-t hc-border-gray-200 hc-z-50 md:hc-hidden hc-animate-in hc-fade-in hc-slide-in-from-bottom-2 hc-duration-200"
172
+ >
173
+ <MobileButtons />
174
+ </div>
175
+ )}
176
+
177
+ {/* Desktop Footer - fixed at bottom (only when overflow) */}
178
+ {showFixedFooter && (
179
+ <div className="hc-hidden md:hc-flex md:hc-justify-start hc-px-4 hc-py-4 hc-absolute hc-bottom-0 hc-left-0 hc-right-0 hc-bg-gray-100 hc-border-t hc-border-gray-200 hc-z-50 hc-animate-in hc-fade-in hc-slide-in-from-bottom-2 hc-duration-200">
57
180
  <Button.Btn
58
- onClick={() => setMobileTab("listTab")}
59
- variant="primary"
181
+ onClick={handleReset}
182
+ variant="outline"
60
183
  size="sm"
61
- className={`
62
- ${hasActiveFilters ? "hc-opacity-0 hc-pointer-events-none" : "hc-opacity-100"}
63
- `}
184
+ className={filterConfig?.classNames?.resetButton}
64
185
  >
65
- <Button.Body>
66
- <Button.Icon icon="fluent:search-12-filled" size="hc-size-3.5" />
67
- Show {filteredListings.length} Jobs
68
- </Button.Body>
186
+ Reset All
69
187
  </Button.Btn>
70
- )}
71
- </div>
188
+ </div>
189
+ )}
72
190
  </div>
73
191
  );
74
192
  };
75
193
 
76
- export default Filter;
194
+ export default Filter;
@@ -1,5 +1,7 @@
1
1
  import React from 'react';
2
2
  import Icon from '~/components/modules/icon';
3
+ import { twMerge } from '~/util/twMerge';
4
+ import { useMapList } from '~/contexts/mapListContext';
3
5
 
4
6
  const FilterItem = ({
5
7
  className,
@@ -17,6 +19,7 @@ const FilterItem = ({
17
19
  eventTypes,
18
20
  ...rest
19
21
  }) => {
22
+ const { filterConfig } = useMapList();
20
23
  const itemName = item.name ? item.name : item;
21
24
 
22
25
  return (
@@ -47,7 +50,10 @@ const FilterItem = ({
47
50
  disabled={item.count === 0}
48
51
  value={itemName}
49
52
  type={type}
50
- className="hc-size-4 hc-mt-px hc-text-primary hc-border-uiAccent/30 hc-transition-colors hc-rounded-sm"
53
+ className={twMerge(
54
+ "hc-size-4 hc-mt-px hc-text-primary hc-border-uiAccent/30 hc-transition-colors hc-rounded-sm",
55
+ filterConfig?.classNames?.filterItemCheckbox
56
+ )}
51
57
  checked={activeItem}
52
58
  onChange={() => {
53
59
  setActiveItem(!activeItem);
@@ -56,9 +62,15 @@ const FilterItem = ({
56
62
  />
57
63
  )}
58
64
 
59
- <span className="hc-text-left hc-font-medium">{itemName}</span>
65
+ <span className={twMerge(
66
+ "hc-text-left hc-font-medium",
67
+ filterConfig?.classNames?.filterItemLabel
68
+ )}>{itemName}</span>
60
69
  {hasCount && !isExternalLink && (
61
- <span className="hc-inline-block hc-mt-1 hc-ml-auto hc-text-xs hc-leading-none hc-text-primary">
70
+ <span className={twMerge(
71
+ "hc-inline-block hc-mt-1 hc-ml-auto hc-text-xs hc-leading-none hc-text-primary",
72
+ filterConfig?.classNames?.filterItemCount
73
+ )}>
62
74
  ({item.count})
63
75
  </span>
64
76
  )}
@@ -2,6 +2,8 @@ import React, { useRef } from 'react';
2
2
  import Button from '~/components/modules/buttons/default';
3
3
  import Icon from '~/components/modules/icon';
4
4
  import FilterCard from '~/components/modules/cards/filter';
5
+ import { twMerge } from '~/util/twMerge';
6
+ import { useMapList } from '~/contexts/mapListContext';
5
7
 
6
8
  const Search = ({
7
9
  inputPlaceholder,
@@ -14,6 +16,7 @@ const Search = ({
14
16
  handleReset,
15
17
  label
16
18
  }) => {
19
+ const { filterConfig } = useMapList();
17
20
  const inputRef = useRef(null);
18
21
  const buttonRef = useRef(null);
19
22
 
@@ -47,7 +50,10 @@ const Search = ({
47
50
  }}
48
51
  placeholder={inputPlaceholder}
49
52
  value={inputValue}
50
- className="hc-w-full hc-px-0 hc-py-2 hc-text-sm hc-border-0 hc-transition-colors placeholder:hc-text-uiText/50 focus:hc-ring-0 focus:hc-outline-none"
53
+ className={twMerge(
54
+ "hc-w-full hc-px-0 hc-py-2 hc-text-sm hc-border-0 hc-transition-colors placeholder:hc-text-uiText/50 focus:hc-ring-0 focus:hc-outline-none",
55
+ filterConfig?.classNames?.searchInput
56
+ )}
51
57
  onChange={handleInputChange}
52
58
  />
53
59
 
@@ -137,4 +137,4 @@ export default React.memo(ListItem, (prevProps, nextProps) => {
137
137
  prevProps.bodyClassName === nextProps.bodyClassName &&
138
138
  prevProps.className === nextProps.className
139
139
  );
140
- });
140
+ });
@@ -61,17 +61,17 @@ const MapTabs = ({
61
61
  </Button.Body>
62
62
  </Button.Btn>
63
63
  </Tabs.Trigger>
64
- </Tabs.List>
64
+ </Tabs.List>
65
65
 
66
- {/* Tab content */}
67
- <div className="hc-flex-grow hc-h-[100vh] md:hc-h-[calc(100%-48px)]">
68
- {/* List Tab Content */}
69
- <Tabs.Content
70
- className="hc-h-full hc-bg-white hc-outline-none"
71
- value="listTab"
72
- >
73
- {list}
74
- </Tabs.Content>
66
+ {/* Tab content */}
67
+ <div className="hc-flex-grow hc-h-[calc(100vh-180px)] md:hc-h-[calc(100%-48px)]">
68
+ {/* List Tab Content */}
69
+ <Tabs.Content
70
+ className="hc-h-full hc-bg-white hc-outline-none"
71
+ value="listTab"
72
+ >
73
+ {list}
74
+ </Tabs.Content>
75
75
 
76
76
  {/* Map Tab Content */}
77
77
  {showMap && (