@envive-ai/react-toolkit 0.1.9 → 0.1.11

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 (174) hide show
  1. package/dist/Accordion/index.cjs +3 -82
  2. package/dist/Accordion/index.d.cts +2 -2
  3. package/dist/Accordion/index.d.ts +2 -2
  4. package/dist/Accordion/index.js +2 -75
  5. package/dist/Accordion-Cp3Hx2zm.js +77 -0
  6. package/dist/Accordion-DBZpiQe7.cjs +88 -0
  7. package/dist/AppliedFiltersScrollbar/index.d.ts +2 -2
  8. package/dist/ButtonBase/index.cjs +2 -2
  9. package/dist/ButtonBase/index.d.cts +2 -2
  10. package/dist/ButtonBase/index.d.ts +2 -2
  11. package/dist/ButtonBase/index.js +2 -2
  12. package/dist/DynamicFiltersScrollbar/index.d.cts +2 -2
  13. package/dist/DynamicFiltersScrollbar/index.d.ts +2 -2
  14. package/dist/FilterScrollbar/index.cjs +1 -1
  15. package/dist/FilterScrollbar/index.d.cts +3 -3
  16. package/dist/FilterScrollbar/index.d.ts +3 -3
  17. package/dist/FilterScrollbar/index.js +1 -1
  18. package/dist/Headline/index.cjs +1 -1
  19. package/dist/Headline/index.d.cts +2 -2
  20. package/dist/Headline/index.d.ts +2 -2
  21. package/dist/Headline/index.js +1 -1
  22. package/dist/ImageWithFallback/index.cjs +1 -1
  23. package/dist/ImageWithFallback/index.d.cts +2 -2
  24. package/dist/ImageWithFallback/index.d.ts +2 -2
  25. package/dist/ImageWithFallback/index.js +1 -1
  26. package/dist/ModalSheet/index.cjs +3 -0
  27. package/dist/ModalSheet/index.d.cts +38 -0
  28. package/dist/ModalSheet/index.d.ts +38 -0
  29. package/dist/ModalSheet/index.js +3 -0
  30. package/dist/ModalSheet-CXZgyZ4a.cjs +335 -0
  31. package/dist/ModalSheet-CZd5pssv.js +324 -0
  32. package/dist/ProductCard/index.cjs +4 -4
  33. package/dist/ProductCard/index.d.cts +1 -1
  34. package/dist/ProductCard/index.d.ts +1 -1
  35. package/dist/ProductCard/index.js +4 -4
  36. package/dist/{ProductCard-D44lhkxO.js → ProductCard-D-lyh8uV.js} +4 -4
  37. package/dist/{ProductCard-BcQBegyj.cjs → ProductCard-F49krjHk.cjs} +5 -5
  38. package/dist/ProductGrid/index.cjs +5 -5
  39. package/dist/ProductGrid/index.d.cts +3 -3
  40. package/dist/ProductGrid/index.d.ts +3 -3
  41. package/dist/ProductGrid/index.js +5 -5
  42. package/dist/{ProductGrid-ZXF7LBVG.js → ProductGrid-FIf5wFQx.js} +1 -1
  43. package/dist/{ProductGrid-50PmrwA4.cjs → ProductGrid-QeoaIfcq.cjs} +1 -1
  44. package/dist/RadioButton/index.cjs +6 -0
  45. package/dist/RadioButton/index.d.cts +30 -0
  46. package/dist/RadioButton/index.d.ts +30 -0
  47. package/dist/RadioButton/index.js +6 -0
  48. package/dist/RadioButton-Bf68dZl7.js +68 -0
  49. package/dist/RadioButton-DZ6QXkrN.cjs +77 -0
  50. package/dist/RadioButtonGroup/index.cjs +8 -0
  51. package/dist/RadioButtonGroup/index.d.cts +36 -0
  52. package/dist/RadioButtonGroup/index.d.ts +36 -0
  53. package/dist/RadioButtonGroup/index.js +7 -0
  54. package/dist/RadioButtonGroup-8k7hkJYB.js +37 -0
  55. package/dist/RadioButtonGroup-Dc_n5amh.cjs +51 -0
  56. package/dist/SearchAutocomplete/index.cjs +1 -1
  57. package/dist/SearchAutocomplete/index.js +1 -1
  58. package/dist/SearchFilter/index.cjs +9 -2
  59. package/dist/SearchFilter/index.d.cts +15 -9
  60. package/dist/SearchFilter/index.d.ts +15 -9
  61. package/dist/SearchFilter/index.js +9 -2
  62. package/dist/SearchFilter-BOwErEyI.js +258 -0
  63. package/dist/SearchFilter-BtLKnFMm.cjs +279 -0
  64. package/dist/SearchInput/index.cjs +3 -3
  65. package/dist/SearchInput/index.d.cts +1 -1
  66. package/dist/SearchInput/index.d.ts +2 -2
  67. package/dist/SearchInput/index.js +3 -3
  68. package/dist/{SearchInput-DxDC1mcq.js → SearchInput-BBaYEwkR.js} +2 -2
  69. package/dist/{SearchInput-BFlu_3iT.cjs → SearchInput-BTNvgrIa.cjs} +2 -2
  70. package/dist/SearchInputForm/index.cjs +4 -4
  71. package/dist/SearchInputForm/index.d.cts +1 -1
  72. package/dist/SearchInputForm/index.d.ts +1 -1
  73. package/dist/SearchInputForm/index.js +4 -4
  74. package/dist/SearchResultsContent/index.cjs +16 -30
  75. package/dist/SearchResultsContent/index.d.cts +6 -21
  76. package/dist/SearchResultsContent/index.d.ts +6 -21
  77. package/dist/SearchResultsContent/index.js +12 -25
  78. package/dist/SearchResultsFilterSidebar/index.cjs +15 -8
  79. package/dist/SearchResultsFilterSidebar/index.d.cts +1 -1
  80. package/dist/SearchResultsFilterSidebar/index.d.ts +1 -1
  81. package/dist/SearchResultsFilterSidebar/index.js +13 -6
  82. package/dist/SearchResultsStates/index.cjs +10 -10
  83. package/dist/SearchResultsStates/index.d.cts +6 -6
  84. package/dist/SearchResultsStates/index.d.ts +6 -6
  85. package/dist/SearchResultsStates/index.js +10 -10
  86. package/dist/{SearchResultsStates-T--7xKI7.js → SearchResultsStates-CB5k1xkK.js} +5 -5
  87. package/dist/{SearchResultsStates-B7a6B6-P.cjs → SearchResultsStates-DywK7vvp.cjs} +5 -5
  88. package/dist/SparkleAnimation/index.cjs +1 -1
  89. package/dist/SparkleAnimation/index.d.cts +2 -2
  90. package/dist/SparkleAnimation/index.d.ts +2 -2
  91. package/dist/SparkleAnimation/index.js +1 -1
  92. package/dist/Spinner/index.cjs +1 -1
  93. package/dist/Spinner/index.d.cts +2 -2
  94. package/dist/Spinner/index.d.ts +2 -2
  95. package/dist/Spinner/index.js +1 -1
  96. package/dist/Styles/index.cjs +274 -0
  97. package/dist/Styles/index.d.cts +258 -0
  98. package/dist/Styles/index.d.ts +258 -0
  99. package/dist/Styles/index.js +274 -0
  100. package/dist/SuggestionButton/index.cjs +1 -1
  101. package/dist/SuggestionButton/index.d.cts +2 -2
  102. package/dist/SuggestionButton/index.d.ts +2 -2
  103. package/dist/SuggestionButton/index.js +1 -1
  104. package/dist/Text/index.d.cts +2 -2
  105. package/dist/Text/index.d.ts +3 -3
  106. package/dist/TextInput/index.cjs +1 -1
  107. package/dist/TextInput/index.d.ts +1 -1
  108. package/dist/TextInput/index.js +1 -1
  109. package/dist/ToggleButton/index.cjs +6 -0
  110. package/dist/ToggleButton/index.d.cts +30 -0
  111. package/dist/ToggleButton/index.d.ts +30 -0
  112. package/dist/ToggleButton/index.js +6 -0
  113. package/dist/ToggleButton-BKRR_-69.js +60 -0
  114. package/dist/ToggleButton-D90UO4qv.cjs +68 -0
  115. package/dist/{index-vrOLXtJO.d.ts → index-1x_hMlEf.d.cts} +5 -5
  116. package/dist/{index-ozZ5JSZp.d.cts → index-CgjZdKpL.d.cts} +5 -5
  117. package/dist/{index-CC5ru80z.d.ts → index-Cl4d_pDw.d.ts} +5 -5
  118. package/dist/{index-Bl5T42aR.d.ts → index-H_9LhS_1.d.cts} +2 -2
  119. package/dist/{index-C5sr5-A0.d.cts → index-QMTPxKA9.d.ts} +5 -5
  120. package/dist/{index-BkxuyRJ6.d.cts → index-gfYBM7Ul.d.ts} +2 -2
  121. package/dist/{searchFilterSidebarVariants-B8nMp970.js → searchFilterSidebarVariants-CDFCHVeZ.js} +1 -1
  122. package/dist/{searchFilterSidebarVariants-BMZs5kyL.cjs → searchFilterSidebarVariants-CtmuwSBQ.cjs} +1 -1
  123. package/package.json +30 -4
  124. package/src/components/AnimatedChevron/AnimatedChevron.tsx +37 -0
  125. package/src/components/ModalSheet/ModalSheet.tsx +42 -0
  126. package/src/components/ModalSheet/common/closeIcon.tsx +40 -0
  127. package/src/components/ModalSheet/common/enviveWatermark.tsx +30 -0
  128. package/src/components/ModalSheet/desktop/desktopHeader.tsx +24 -0
  129. package/src/components/ModalSheet/desktop/index.tsx +118 -0
  130. package/src/components/ModalSheet/index.ts +1 -0
  131. package/src/components/ModalSheet/mobile/index.tsx +117 -0
  132. package/src/components/ModalSheet/mobile/mobileHeader.tsx +42 -0
  133. package/src/components/ModalSheet/types.ts +33 -0
  134. package/src/components/RadioButton/RadioButton.tsx +88 -0
  135. package/src/components/RadioButton/index.ts +1 -0
  136. package/src/components/RadioButtonGroup/RadioButtonGroup.tsx +72 -0
  137. package/src/components/RadioButtonGroup/index.ts +1 -0
  138. package/src/components/SearchFilter/SearchFilter.tsx +161 -64
  139. package/src/components/SearchFilter/SearchFilterFooter.tsx +55 -0
  140. package/src/components/SearchFilter/types.ts +2 -1
  141. package/src/components/SearchFilter/useHasFilterStateChanged.tsx +40 -0
  142. package/src/components/SearchFilter/utils.ts +15 -0
  143. package/src/components/SearchResultsContent/SearchResultsContent.tsx +1 -1
  144. package/src/components/SearchResultsContent/index.ts +0 -1
  145. package/src/components/SearchResultsFilterSidebar/SearchResultsFilter.tsx +2 -2
  146. package/src/components/Styles/EnviveTailwindPreset.ts +285 -0
  147. package/src/components/Styles/index.ts +2 -0
  148. package/src/components/ToggleButton/ToggleButton.tsx +98 -0
  149. package/src/components/ToggleButton/index.ts +1 -0
  150. package/tailwind-preset.js +3 -0
  151. package/dist/SearchFilter-B3vl3aWK.js +0 -117
  152. package/dist/SearchFilter-QV5AH7gk.cjs +0 -137
  153. package/src/components/SearchResultsContent/utils.ts +0 -28
  154. /package/dist/{ButtonBase-C_uKnl48.js → ButtonBase-0NN6wmX-.js} +0 -0
  155. /package/dist/{ButtonBase-DGbSm0SJ.js → ButtonBase-BAf-nlCm.js} +0 -0
  156. /package/dist/{ButtonBase-DbWQ25n-.cjs → ButtonBase-BIXx56hq.cjs} +0 -0
  157. /package/dist/{ButtonBase-DGpQBeLR.cjs → ButtonBase-Do88ndKa.cjs} +0 -0
  158. /package/dist/{DynamicFiltersScrollbar-D4d6pGke.js → DynamicFiltersScrollbar-C4kdNSJ9.js} +0 -0
  159. /package/dist/{DynamicFiltersScrollbar-DCElnZJa.cjs → DynamicFiltersScrollbar-CVw1PINp.cjs} +0 -0
  160. /package/dist/{Headline-BkOW1lQj.js → Headline-DNEWF8ly.js} +0 -0
  161. /package/dist/{Headline-QpruZlcg.cjs → Headline-DTT4RSv2.cjs} +0 -0
  162. /package/dist/{ImageWithFallback-1LqhQK1q.cjs → ImageWithFallback-Cx-KNi-D.cjs} +0 -0
  163. /package/dist/{ImageWithFallback-Ckwsmd8P.js → ImageWithFallback-DqxjwO3i.js} +0 -0
  164. /package/dist/{SearchAutocomplete-C4RY0IoT.js → SearchAutocomplete-C6omCGJp.js} +0 -0
  165. /package/dist/{SearchAutocomplete-hQDnKtwQ.cjs → SearchAutocomplete-Cofuvwwp.cjs} +0 -0
  166. /package/dist/{SparkleAnimation-DT3coYkB.js → SparkleAnimation-AM4XoegD.js} +0 -0
  167. /package/dist/{SparkleAnimation-2m4gwmrY.cjs → SparkleAnimation-Edzqyb48.cjs} +0 -0
  168. /package/dist/{Spinner-BqTt55uu.js → Spinner-CjGLIRgs.js} +0 -0
  169. /package/dist/{Spinner-DjK8ts9E.cjs → Spinner-DFor2Szi.cjs} +0 -0
  170. /package/dist/{TextInput-BFPXhSAY.js → TextInput-BVPdz7e8.js} +0 -0
  171. /package/dist/{TextInput-DLSgpP6b.cjs → TextInput-CnXhppYn.cjs} +0 -0
  172. /package/dist/{colorsConfig-BQlaCfxi.js → colorsConfig-5Yf4nrEe.js} +0 -0
  173. /package/dist/{colorsConfig-DCvy_dV4.cjs → colorsConfig-DFL3mBwB.cjs} +0 -0
  174. /package/dist/{textVariantClasses-ypYGLq0h.d.ts → textVariantClasses-kyZtL8F5.d.ts} +0 -0
@@ -1,84 +1,181 @@
1
1
  import classNames from 'classnames';
2
- import { SearchFilterItem } from './SearchFilterItem';
3
- import { Text } from 'src/components/Text/Text';
4
- import { ButtonBase } from 'src/components/ButtonBase/ButtonBase';
5
2
  import { SearchFilterProps } from './types';
3
+ import { useCallback } from 'react';
4
+ import { SearchFilterHeader } from './SearchFilterHeader';
5
+ import { useHasFilterStateChanged } from './useHasFilterStateChanged';
6
+ import { getSelectedFilterItemsCount, getTotalSelectedFilterItemsCount } from './utils';
7
+ import { ModalSheet } from '../ModalSheet';
8
+ import { FilterItemProps, FilterProps } from '@envive-ai/react-hooks/types';
9
+ import { ToggleButton } from '../ToggleButton';
10
+ import { Accordion } from '../Accordion';
11
+ import { RadioButtonGroup } from '../RadioButtonGroup';
12
+ import { SearchFilterFooter } from './SearchFilterFooter';
13
+
14
+ const SortFilter = ({
15
+ filter,
16
+ selectFilterItem,
17
+ radioButtonFillColor,
18
+ radioButtonHoverColor,
19
+ radioButtonUncheckedBorderColor,
20
+ }: FilterProps) => {
21
+ const filterTitle = filter.displayName;
22
+
23
+ const options = filter.items.map((item) => ({
24
+ label: item.displayName,
25
+ value: item.filterItemId,
26
+ isSelected: item.isSelected,
27
+ }));
28
+
29
+ const content = (
30
+ <RadioButtonGroup
31
+ options={options}
32
+ name={filterTitle}
33
+ onChange={(value) => {
34
+ const selectedItem = filter.items.find((item) => item.filterItemId === value);
35
+ selectFilterItem({
36
+ filterId: filter.filterId,
37
+ filterItemId: value,
38
+ isSelected: true,
39
+ displayName: selectedItem?.displayName || value,
40
+ });
41
+ }}
42
+ gap="large"
43
+ textButtonGap="large"
44
+ value={filter.items.find((item) => item.isSelected)?.filterItemId}
45
+ fillColor={radioButtonFillColor}
46
+ hoverColor={radioButtonHoverColor}
47
+ uncheckedBorderColor={radioButtonUncheckedBorderColor}
48
+ />
49
+ );
50
+
51
+ return <Accordion title={filterTitle} content={content} />;
52
+ };
53
+
54
+ const FilterItem = ({ filterId, filterItem, selectFilterItem }: FilterItemProps) => (
55
+ <ToggleButton
56
+ label={filterItem.displayName}
57
+ secondaryLabel={`(${filterItem.productCount})`}
58
+ isSelected={filterItem.isSelected}
59
+ onClick={() =>
60
+ selectFilterItem({
61
+ filterId,
62
+ filterItemId: filterItem.filterItemId,
63
+ isSelected: !filterItem.isSelected,
64
+ displayName: filterItem.displayName,
65
+ })
66
+ }
67
+ variant="default"
68
+ />
69
+ );
70
+
71
+ const Filter = ({ filter, selectFilterItem }: FilterProps) => {
72
+ const filterContentClassName = classNames({
73
+ 'spiffy-tw-flex': true,
74
+ 'spiffy-tw-flex-wrap': true,
75
+ 'spiffy-tw-gap-[8px]': true,
76
+ 'spiffy-tw-pb-[1px]': true,
77
+ });
78
+
79
+ const selectedFilterItems = getSelectedFilterItemsCount(filter);
80
+
81
+ const filterTitle =
82
+ selectedFilterItems > 0 ? `${filter.displayName} (${selectedFilterItems})` : filter.displayName;
83
+ const content = (
84
+ <div className={filterContentClassName}>
85
+ {filter.items.map((item) => (
86
+ <FilterItem
87
+ key={item.filterItemId}
88
+ filterId={filter.filterId}
89
+ filterItem={item}
90
+ selectFilterItem={selectFilterItem}
91
+ />
92
+ ))}
93
+ </div>
94
+ );
95
+ return <Accordion title={filterTitle} content={content} />;
96
+ };
6
97
 
7
98
  export const SearchFilter = ({
8
- isOpen,
9
- setIsOpen,
10
99
  filters,
11
- // productCount,
100
+ productCount,
12
101
  selectFilterItem,
13
102
  clearAllFilters,
14
- // applyFiltersUnchangedClasses,
15
- // applyFiltersChangedClasses,
16
- filterButtonText,
103
+ isOpen,
104
+ setIsOpen,
105
+ applyFiltersUnchangedClasses,
106
+ applyFiltersChangedClasses,
107
+ headerContent,
108
+ footerContent,
109
+ headerClassName,
17
110
  radioButtonFillColor,
18
111
  radioButtonHoverColor,
19
112
  radioButtonUncheckedBorderColor,
20
- // filterCloseIconVariant,
21
- headerContent,
113
+ filterCloseIconVariant,
22
114
  }: SearchFilterProps) => {
23
- const modalClasses = classNames(
24
- 'spiffy-tw-fixed spiffy-tw-inset-0 spiffy-tw-z-50 spiffy-tw-flex spiffy-tw-transform spiffy-tw-transition-all',
25
- {
26
- 'spiffy-tw-translate-x-full': !isOpen,
27
- 'spiffy-tw-translate-x-0': isOpen,
28
- },
29
- );
115
+ const closeModal = useCallback(() => setIsOpen(false), [setIsOpen]);
116
+ const filterCount = getTotalSelectedFilterItemsCount(filters);
30
117
 
31
- const overlayClasses = classNames(
32
- 'spiffy-tw-absolute spiffy-tw-inset-0 spiffy-tw-bg-black spiffy-tw-bg-opacity-50',
33
- {
34
- 'spiffy-tw-opacity-0': !isOpen,
35
- 'spiffy-tw-opacity-100': isOpen,
36
- },
37
- );
118
+ const hasFiltersChanged = useHasFilterStateChanged(filters, isOpen);
38
119
 
39
- const sidebarClasses = classNames(
40
- 'spiffy-tw-relative spiffy-tw-ml-auto spiffy-tw-flex spiffy-tw-h-full spiffy-tw-w-full spiffy-tw-max-w-xs spiffy-tw-flex-col spiffy-tw-overflow-y-auto spiffy-tw-bg-[--spiffy-colors-background-light] spiffy-tw-py-4 spiffy-tw-shadow-xl',
41
- );
120
+ const filterWrapperClassName = classNames({
121
+ 'spiffy-tw-flex': true,
122
+ 'spiffy-tw-flex-col': true,
123
+ 'spiffy-tw-gap-[16px]': true,
124
+ 'spiffy-tw-px-[24px]': true,
125
+ 'spiffy-tw-py-[16px]': true,
126
+ 'spiffy-tw-max-h-screen': true,
127
+ 'spiffy-tw-overflow-y-auto': true,
128
+ });
42
129
 
43
130
  return (
44
- <div className={modalClasses}>
45
- <div className={overlayClasses} onClick={() => setIsOpen(false)} />
46
- <div className={sidebarClasses}>
47
- {headerContent}
48
- <div className="spiffy-tw-flex-1 spiffy-tw-px-4 spiffy-tw-py-6">
49
- {filters.map((filter) => (
50
- <div key={filter.filterId} className="spiffy-tw-mb-6">
51
- <Text variant="body2" className="spiffy-tw-font-medium spiffy-tw-mb-3">
52
- {filter.displayName}
53
- </Text>
54
- <ul>
55
- {filter.items.map((item) => (
56
- <SearchFilterItem
57
- key={item.filterItemId}
58
- filterItem={{ ...item, filterId: filter.filterId }}
59
- onSelectFilterItem={selectFilterItem}
60
- radioButtonFillColor={radioButtonFillColor}
61
- radioButtonHoverColor={radioButtonHoverColor}
62
- radioButtonUncheckedBorderColor={radioButtonUncheckedBorderColor}
63
- />
64
- ))}
65
- </ul>
66
- </div>
67
- ))}
68
- </div>
69
- <div className="spiffy-tw-border-t spiffy-tw-border-[--spiffy-colors-border-light] spiffy-tw-px-4 spiffy-tw-py-3">
70
- <ButtonBase
71
- onClick={clearAllFilters}
72
- text="Clear All"
73
- buttonClass="spiffy-tw-w-full spiffy-tw-mb-2"
131
+ <ModalSheet
132
+ isOpen={isOpen}
133
+ closeModal={closeModal}
134
+ headerProps={{
135
+ headerContent: headerContent || (
136
+ <SearchFilterHeader
137
+ closeModal={closeModal}
138
+ productCount={productCount}
139
+ headerClassName={headerClassName ?? ''}
140
+ filterCloseIconVariant={filterCloseIconVariant}
74
141
  />
75
- <ButtonBase
76
- onClick={() => setIsOpen(false)}
77
- text={filterButtonText}
78
- buttonClass="spiffy-tw-w-full"
142
+ ),
143
+ chevronColor: '#000',
144
+ }}
145
+ footerProps={{
146
+ footerContent: footerContent || (
147
+ <SearchFilterFooter
148
+ closeModal={closeModal}
149
+ clearAllFilters={clearAllFilters}
150
+ filterCount={filterCount}
151
+ hasFiltersChanged={hasFiltersChanged}
152
+ applyFiltersUnchangedClasses={applyFiltersUnchangedClasses}
153
+ applyFiltersChangedClasses={applyFiltersChangedClasses}
79
154
  />
80
- </div>
155
+ ),
156
+ }}
157
+ >
158
+ <div className={filterWrapperClassName}>
159
+ {filters
160
+ .filter((filter) => filter.displayName !== '')
161
+ .map((filter) => {
162
+ if (filter.filterId === 'sort') {
163
+ return (
164
+ <SortFilter
165
+ key={filter.filterId}
166
+ filter={filter}
167
+ selectFilterItem={selectFilterItem}
168
+ radioButtonFillColor={radioButtonFillColor}
169
+ radioButtonHoverColor={radioButtonHoverColor}
170
+ radioButtonUncheckedBorderColor={radioButtonUncheckedBorderColor}
171
+ />
172
+ );
173
+ }
174
+ return (
175
+ <Filter key={filter.filterId} filter={filter} selectFilterItem={selectFilterItem} />
176
+ );
177
+ })}
81
178
  </div>
82
- </div>
179
+ </ModalSheet>
83
180
  );
84
181
  };
@@ -0,0 +1,55 @@
1
+ import classNames from 'classnames';
2
+ import { ButtonBase } from '../ButtonBase';
3
+ import { FilterFooterProps } from '@envive-ai/react-hooks/types';
4
+
5
+ export const SearchFilterFooter = ({
6
+ closeModal,
7
+ clearAllFilters,
8
+ filterCount,
9
+ applyFiltersUnchangedClasses,
10
+ applyFiltersChangedClasses,
11
+ hasFiltersChanged,
12
+ }: FilterFooterProps) => {
13
+ const clearAllButtonEnabled = filterCount > 0;
14
+
15
+ const footerWrapperClassName = classNames({
16
+ 'spiffy-tw-flex': true,
17
+ 'spiffy-tw-flex-col': true,
18
+ 'spiffy-tw-items-center': true,
19
+ 'spiffy-tw-justify-between': true,
20
+ 'spiffy-tw-w-full': true,
21
+ 'spiffy-tw-border-t': true,
22
+ 'spiffy-tw-border-t-[--spiffy-colors-border-light]': true,
23
+ 'spiffy-tw-p-[16px]': true,
24
+ 'spiffy-tw-gap-[16px]': true,
25
+ });
26
+ const applyFilterButtonClassName = classNames(
27
+ 'spiffy-tw-flex spiffy-tw-flex-row spiffy-tw-justify-center spiffy-tw-rounded-[8px] spiffy-tw-w-full',
28
+ hasFiltersChanged ? applyFiltersChangedClasses : applyFiltersUnchangedClasses,
29
+ );
30
+ const clearAllButtonClassName = classNames({
31
+ 'spiffy-tw-flex': true,
32
+ 'spiffy-tw-flex-row': true,
33
+ 'spiffy-tw-justify-center': true,
34
+ 'spiffy-tw-w-full': true,
35
+ 'spiffy-tw-text-[--spiffy-colors-text-primary]': clearAllButtonEnabled,
36
+ 'spiffy-tw-text-[--spiffy-colors-text-secondary]': !clearAllButtonEnabled,
37
+ });
38
+
39
+ return (
40
+ <div className={footerWrapperClassName}>
41
+ <ButtonBase
42
+ isDisabled={!hasFiltersChanged}
43
+ buttonClass={applyFilterButtonClassName}
44
+ text="Apply Filters"
45
+ onClick={closeModal}
46
+ />
47
+ <ButtonBase
48
+ isDisabled={!clearAllButtonEnabled}
49
+ buttonClass={clearAllButtonClassName}
50
+ text="Clear All"
51
+ onClick={clearAllFilters}
52
+ />
53
+ </div>
54
+ );
55
+ };
@@ -1,4 +1,5 @@
1
1
  import {
2
+ CloseIconVariant,
2
3
  SearchFilterDatum,
3
4
  SearchFilterItemDatum,
4
5
  SelectFilterItem,
@@ -40,5 +41,5 @@ export type SearchFilterProps = {
40
41
  radioButtonFillColor: string;
41
42
  radioButtonHoverColor?: string;
42
43
  radioButtonUncheckedBorderColor?: string;
43
- // filterCloseIconVariant: CloseIconVariant;
44
+ filterCloseIconVariant: CloseIconVariant;
44
45
  };
@@ -0,0 +1,40 @@
1
+ import { useState, useMemo, useEffect } from 'react';
2
+ import { SearchFilterDatum } from '@envive-ai/react-hooks/types';
3
+
4
+ export const useHasFilterStateChanged = (filters: SearchFilterDatum[], isOpen: boolean) => {
5
+ // Track initial filter states when modal opens
6
+ const [initialFilterStates, setInitialFilterStates] = useState<Record<string, boolean>>({});
7
+
8
+ // Create current filter states map to check for filter changes
9
+ const currentFilterStates = useMemo(() => {
10
+ const states: Record<string, boolean> = {};
11
+ filters.forEach((filter) => {
12
+ filter.items.forEach((item) => {
13
+ states[`${filter.filterId}-${item.filterItemId}`] = item.isSelected;
14
+ });
15
+ });
16
+ return states;
17
+ }, [filters]);
18
+
19
+ // Check if filters have changed by comparing states
20
+ const hasFiltersChanged = useMemo(() => {
21
+ // If no initial states captured yet, no changes
22
+ if (Object.keys(initialFilterStates).length === 0) {
23
+ return false;
24
+ }
25
+
26
+ const changed = Object.keys(currentFilterStates).some(
27
+ (key) => currentFilterStates[key] !== initialFilterStates[key],
28
+ );
29
+
30
+ return changed;
31
+ }, [currentFilterStates, initialFilterStates]);
32
+
33
+ useEffect(() => {
34
+ if (isOpen) {
35
+ setInitialFilterStates(currentFilterStates);
36
+ }
37
+ }, [isOpen]);
38
+
39
+ return hasFiltersChanged;
40
+ };
@@ -0,0 +1,15 @@
1
+ import { SearchFilterDatum, SearchFilterItemDatum } from '@envive-ai/react-hooks/types';
2
+
3
+ export const getSelectedFilterItemsCount = (filter: SearchFilterDatum) => {
4
+ return filter.items.filter((item: SearchFilterItemDatum) => item.isSelected).length;
5
+ };
6
+
7
+ // Gets the count of total active filters excluding sort filters
8
+ export const getTotalSelectedFilterItemsCount = (filters: SearchFilterDatum[]) => {
9
+ return filters.reduce((acc: number, filter: SearchFilterDatum) => {
10
+ if (filter.filterId === 'sort') {
11
+ return acc;
12
+ }
13
+ return acc + getSelectedFilterItemsCount(filter);
14
+ }, 0);
15
+ };
@@ -10,7 +10,7 @@ import {
10
10
  SearchResultsGrid,
11
11
  SearchResultsLoadingGrid,
12
12
  } from '../SearchResultsStates';
13
- import { SearchResultsState } from './utils';
13
+ import { SearchResultsState } from '@envive-ai/react-hooks/hooks';
14
14
 
15
15
  interface SearchResultsContentProps {
16
16
  searchResultsState: SearchResultsState;
@@ -1,2 +1 @@
1
1
  export * from './SearchResultsContent';
2
- export * from './utils';
@@ -1,11 +1,11 @@
1
1
  import { useCallback } from 'react';
2
2
 
3
3
  import { searchFilterSidebarVariantClasses } from './searchFilterSidebarVariants';
4
- import SettingsVariant from '@envive-ai/react-icons/SettingsVariant';
5
4
  import { ButtonBase } from '../ButtonBase';
6
5
  import { SearchFilter, SearchFilterHeader } from '../SearchFilter';
7
6
  import { SearchFilterSidebarVariant } from './types';
8
7
  import { SearchFilterDatum, SelectFilterItem } from '@envive-ai/react-hooks/types';
8
+ import SettingsVariant from '@envive-ai/react-icons/src/SettingsVariant.js';
9
9
 
10
10
  export type SearchResultsFilterProps = {
11
11
  productCount: number;
@@ -58,7 +58,7 @@ export const SearchResultsFilter = ({
58
58
  radioButtonFillColor={radioButtonFillColor}
59
59
  radioButtonHoverColor={radioButtonHoverColor}
60
60
  radioButtonUncheckedBorderColor={radioButtonUncheckedBorderColor}
61
- // filterCloseIconVariant={filterCloseIconVariant}
61
+ filterCloseIconVariant={filterCloseIconVariant}
62
62
  headerContent={
63
63
  <SearchFilterHeader
64
64
  closeModal={() => setIsOpen(false)}
@@ -0,0 +1,285 @@
1
+ /* eslint-disable @typescript-eslint/no-require-imports */
2
+ import typography from '@tailwindcss/typography';
3
+
4
+ /**
5
+ * Helper function to create content paths for the Envive React Toolkit
6
+ * This should be used in the consuming application's tailwind.config.js
7
+ *
8
+ * @param additionalPaths - Additional content paths to include
9
+ * @returns Array of content paths including the toolkit components
10
+ */
11
+ export const createEnviveContentPaths = (additionalPaths: string[] = []): string[] => {
12
+ // Dynamically resolve the path to the toolkit's dist directory
13
+ let toolkitPath: string;
14
+
15
+ try {
16
+ // This will work in Node.js environments (build time)
17
+ if (typeof require !== 'undefined' && typeof require.resolve === 'function') {
18
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
19
+ const path = require('path');
20
+ toolkitPath = path.join(
21
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
22
+ path.dirname(require.resolve('@envive-ai/react-toolkit/package.json')),
23
+ 'dist/**/*.{js,cjs}',
24
+ );
25
+ } else {
26
+ // Fallback for environments where require is not available
27
+ toolkitPath = './node_modules/@envive-ai/react-toolkit/dist/**/*.{js,cjs}';
28
+ }
29
+ } catch {
30
+ // Fallback if resolution fails
31
+ toolkitPath = './node_modules/@envive-ai/react-toolkit/dist/**/*.{js,cjs}';
32
+ }
33
+
34
+ return [
35
+ // Include the toolkit's built components
36
+ toolkitPath,
37
+ // Include any additional paths provided by the consuming application
38
+ ...additionalPaths,
39
+ ];
40
+ };
41
+
42
+ /** @type {import('tailwindcss').Config} */
43
+ export const EnviveTailwindPreset = {
44
+ future: {
45
+ hoverOnlyWhenSupported: true,
46
+ },
47
+ prefix: 'spiffy-tw-',
48
+ theme: {
49
+ screens: {
50
+ sm: '480px',
51
+ md: '768px',
52
+ lg: '976px',
53
+ xl: '1440px',
54
+ },
55
+ fontFamily: {
56
+ sans: ['Inter Variable', 'Poppins', 'sans-serif'],
57
+ },
58
+ fontSize: {
59
+ xs: '12px',
60
+ sm: '14px',
61
+ md: '16px',
62
+ lg: '18px',
63
+ xl: '20px',
64
+ },
65
+ fontWeight: {
66
+ normal: 400,
67
+ medium: 500,
68
+ semibold: 600,
69
+ bold: 700,
70
+ },
71
+ spacing: {
72
+ px: '1px',
73
+ 0.5: '2px',
74
+ 1: '4px',
75
+ 1.5: '6px',
76
+ 2: '8px',
77
+ 2.5: '10px',
78
+ 3: '12px',
79
+ 3.5: '14px',
80
+ 4: '16px',
81
+ 5: '20px',
82
+ 6: '24px',
83
+ 7: '28px',
84
+ 8: '32px',
85
+ 9: '36px',
86
+ 10: '40px',
87
+ 12: '48px',
88
+ 14: '56px',
89
+ 16: '64px',
90
+ 20: '80px',
91
+ 24: '96px',
92
+ 28: '112px',
93
+ 32: '128px',
94
+ 36: '144px',
95
+ 40: '160px',
96
+ 44: '176px',
97
+ 48: '192px',
98
+ 52: '208px',
99
+ 56: '224px',
100
+ 60: '240px',
101
+ 64: '256px',
102
+ 72: '288px',
103
+ 80: '320px',
104
+ 96: '384px',
105
+ },
106
+ borderRadius: {
107
+ none: '0',
108
+ sm: '2px',
109
+ base: '4px',
110
+ md: '6px',
111
+ lg: '8px',
112
+ xl: '12px',
113
+ '2xl': '16px',
114
+ '3xl': '24px',
115
+ full: '9999px',
116
+ },
117
+ extend: {
118
+ keyframes: {
119
+ pulse: {
120
+ '0%': { borderColor: '#EDF2F7', background: '#EDF2F7' },
121
+ '50%': {},
122
+ '100%': { borderColor: '#A0AEC0', background: '#A0AEC0' },
123
+ },
124
+ ellipse_162_1_flow_1: {
125
+ from: {
126
+ transform: 'translate(0px, 0px) rotate(0deg)',
127
+ transformOrigin: '8.5px 9.5px',
128
+ cy: '9.5px',
129
+ },
130
+ to: {
131
+ transform: 'translate(0px, 0px) rotate(0deg)',
132
+ transformOrigin: '8.5px 7.5px',
133
+ cy: '7.5px',
134
+ },
135
+ },
136
+ ellipse_162_1_flow_2: {
137
+ from: {
138
+ transform: 'translate(0px, 0px) rotate(0deg)',
139
+ transformOrigin: '8.5px 7.5px',
140
+ cy: '7.5px',
141
+ },
142
+ to: {
143
+ transform: 'translate(0px, 0px) rotate(0deg)',
144
+ transformOrigin: '8.5px 9.5px',
145
+ cy: '9.5px',
146
+ },
147
+ },
148
+ ellipse_162_1_flow_5: {
149
+ from: {
150
+ transform: 'translate(0px, 0px) rotate(0deg)',
151
+ transformOrigin: '8.5px 9.5px',
152
+ cy: '9.5px',
153
+ },
154
+ to: {
155
+ transform: 'translate(0px, 0px) rotate(0deg)',
156
+ transformOrigin: '8.5px 7.5px',
157
+ cy: '7.5px',
158
+ },
159
+ },
160
+ ellipse_162_1_flow_6: {
161
+ from: {
162
+ transform: 'translate(0px, 0px) rotate(0deg)',
163
+ transformOrigin: '8.5px 7.5px',
164
+ cy: '7.5px',
165
+ },
166
+ to: {
167
+ transform: 'translate(0px, 0px) rotate(0deg)',
168
+ transformOrigin: '8.5px 9.5px',
169
+ cy: '9.5px',
170
+ },
171
+ },
172
+ // Ellipse 163_2 animations
173
+ ellipse_163_2_flow_2: {
174
+ from: {
175
+ transform: 'translate(0px, 0px) rotate(0deg)',
176
+ transformOrigin: '16.5px 9.5px',
177
+ cy: '9.5px',
178
+ },
179
+ to: {
180
+ transform: 'translate(0px, 0px) rotate(0deg)',
181
+ transformOrigin: '16.5px 7.5px',
182
+ cy: '7.5px',
183
+ },
184
+ },
185
+ ellipse_163_2_flow_3: {
186
+ from: {
187
+ transform: 'translate(0px, 0px) rotate(0deg)',
188
+ transformOrigin: '16.5px 7.5px',
189
+ cy: '7.5px',
190
+ },
191
+ to: {
192
+ transform: 'translate(0px, 0px) rotate(0deg)',
193
+ transformOrigin: '16.5px 9.5px',
194
+ cy: '9.5px',
195
+ },
196
+ },
197
+ ellipse_163_2_flow_6: {
198
+ from: {
199
+ transform: 'translate(0px, 0px) rotate(0deg)',
200
+ transformOrigin: '16.5px 9.5px',
201
+ cy: '9.5px',
202
+ },
203
+ to: {
204
+ transform: 'translate(0px, 0px) rotate(0deg)',
205
+ transformOrigin: '16.5px 7.5px',
206
+ cy: '7.5px',
207
+ },
208
+ },
209
+ ellipse_163_2_flow_7: {
210
+ from: {
211
+ transform: 'translate(0px, 0px) rotate(0deg)',
212
+ transformOrigin: '16.5px 7.5px',
213
+ cy: '7.5px',
214
+ },
215
+ to: {
216
+ transform: 'translate(0px, 0px) rotate(0deg)',
217
+ transformOrigin: '16.5px 9.5px',
218
+ cy: '9.5px',
219
+ },
220
+ },
221
+ // Ellipse 164_3 animations
222
+ ellipse_164_3_flow_3: {
223
+ from: {
224
+ transform: 'translate(0px, 0px) rotate(0deg)',
225
+ transformOrigin: '24.5px 9.5px',
226
+ cy: '9.5px',
227
+ },
228
+ to: {
229
+ transform: 'translate(0px, 0px) rotate(0deg)',
230
+ transformOrigin: '24.5px 7.5px',
231
+ cy: '7.5px',
232
+ },
233
+ },
234
+ ellipse_164_3_flow_4: {
235
+ from: {
236
+ transform: 'translate(0px, 0px) rotate(0deg)',
237
+ transformOrigin: '24.5px 7.5px',
238
+ cy: '7.5px',
239
+ },
240
+ to: {
241
+ transform: 'translate(0px, 0px) rotate(0deg)',
242
+ transformOrigin: '24.5px 9.5px',
243
+ cy: '9.5px',
244
+ },
245
+ },
246
+ ellipse_164_3_flow_7: {
247
+ from: {
248
+ transform: 'translate(0px, 0px) rotate(0deg)',
249
+ transformOrigin: '24.5px 9.5px',
250
+ cy: '9.5px',
251
+ },
252
+ to: {
253
+ transform: 'translate(0px, 0px) rotate(0deg)',
254
+ transformOrigin: '24.5px 7.5px',
255
+ cy: '7.5px',
256
+ },
257
+ },
258
+ ellipse_164_3_flow_8: {
259
+ from: {
260
+ transform: 'translate(0px, 0px) rotate(0deg)',
261
+ transformOrigin: '24.5px 7.5px',
262
+ cy: '7.5px',
263
+ },
264
+ to: {
265
+ transform: 'translate(0px, 0px) rotate(0deg)',
266
+ transformOrigin: '24.5px 9.5px',
267
+ cy: '9.5px',
268
+ },
269
+ },
270
+ },
271
+ animation: {
272
+ pulse: 'pulse 0.8s linear infinite alternate',
273
+ 'ellipse-1-complete':
274
+ 'ellipse_162_1_flow_1 0.3s ease-out 0s forwards, ellipse_162_1_flow_2 0.2s ease-out 0.3s forwards, ellipse_162_1_flow_5 0.2s ease-out 0.91s forwards, ellipse_162_1_flow_6 0.2s ease-out 1.11s forwards',
275
+ 'ellipse-2-complete':
276
+ 'ellipse_163_2_flow_2 0.2s ease-out 0.3s forwards, ellipse_163_2_flow_3 0.2s ease-out 0.5s forwards, ellipse_163_2_flow_6 0.2s ease-out 1.11s forwards, ellipse_163_2_flow_7 0.2s ease-out 1.31s forwards',
277
+ 'ellipse-3-complete':
278
+ 'ellipse_164_3_flow_3 0.2s ease-out 0.5s forwards, ellipse_164_3_flow_4 0.2s ease-out 0.7s forwards, ellipse_164_3_flow_7 0.2s ease-out 1.31s forwards, ellipse_164_3_flow_8 0.2s ease-out 1.51s forwards',
279
+ },
280
+ },
281
+ },
282
+ // Content should be defined by the consuming application
283
+ content: [createEnviveContentPaths(['./index.html', './src/**/*.{js,ts,jsx,tsx}'])],
284
+ plugins: [typography],
285
+ };