@envive-ai/react-toolkit 0.1.0 → 0.1.1

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 (151) hide show
  1. package/LICENSE +2 -0
  2. package/package.json +10 -7
  3. package/src/atoms/search/types.ts +5 -0
  4. package/src/components/common/ButtonBase/ButtonBase.tsx +70 -0
  5. package/src/components/common/ButtonBase/types.ts +27 -0
  6. package/src/components/common/Headline/Headline.tsx +81 -0
  7. package/src/components/common/ImageWithFallback/ImageWithFallback.tsx +66 -0
  8. package/src/components/common/ProductCard/ProductCard.tsx +305 -0
  9. package/src/components/common/ProductCard/ProductCardSkeleton.tsx +83 -0
  10. package/src/components/common/ProductCard/productCardVariants.ts +63 -0
  11. package/src/components/common/ProductCard/types.ts +49 -0
  12. package/src/components/common/ProductGrid/ProductGrid.tsx +73 -0
  13. package/src/components/common/ProductGrid/productGridVariants.ts +31 -0
  14. package/src/components/common/SparkleAnimation/SparkleAnimation.tsx +105 -0
  15. package/src/components/common/SparkleAnimation/types.ts +6 -0
  16. package/src/components/common/Spinner/Spinner.tsx +30 -0
  17. package/src/components/common/SuggestionButton/SuggestionButton.tsx +258 -0
  18. package/src/components/common/SuggestionButton/types.ts +14 -0
  19. package/src/components/common/Text/Text.tsx +58 -0
  20. package/src/components/common/Text/textVariantClasses.ts +106 -0
  21. package/src/components/common/Text/types.ts +23 -0
  22. package/src/components/common/TextInput/TextInput.tsx +34 -0
  23. package/src/components/models/colorsConfig.ts +28 -0
  24. package/src/components/search/FilterScrollbar/AppliedFiltersScrollbar.tsx +70 -0
  25. package/src/components/search/FilterScrollbar/DynamicFiltersScrollbar.tsx +52 -0
  26. package/src/components/search/SearchFilter/SearchFilter.tsx +84 -0
  27. package/src/components/search/SearchFilter/SearchFilterHeader.tsx +42 -0
  28. package/src/components/search/SearchFilter/SearchFilterItem.tsx +42 -0
  29. package/src/components/search/SearchFilter/types.ts +48 -0
  30. package/src/components/search/SearchInput/SearchInput.tsx +135 -0
  31. package/src/components/search/SearchInput/searchInputVariants.ts +27 -0
  32. package/src/components/search/SearchInputAutocomplete/SearchAutocomplete.tsx +62 -0
  33. package/src/components/search/SearchInputForm/SearchInputForm.tsx +66 -0
  34. package/src/components/search/SearchResultsFilterSidebar/SearchResultsFilter.tsx +82 -0
  35. package/src/components/search/SearchResultsFilterSidebar/searchFilterSidebarVariants.ts +45 -0
  36. package/{dist/packages/components/src/components/search/SearchResultsFilterSidebar/types.d.ts → src/components/search/SearchResultsFilterSidebar/types.ts} +1 -1
  37. package/src/components/search/SearchResultsStates/NoSearchResultsFound.tsx +41 -0
  38. package/src/components/search/SearchResultsStates/SearchResultsGrid.tsx +105 -0
  39. package/src/components/search/SearchResultsStates/SearchResultsLoadingGrid.tsx +50 -0
  40. package/src/components/search/types.ts +5 -0
  41. package/{dist/packages/components/src/components/test/types.d.ts → src/components/test/types.ts} +1 -1
  42. package/src/config/chatElementDisplayLocation.ts +22 -0
  43. package/{dist/packages/components/src/index.js → src/index.ts} +10 -0
  44. package/src/logging/logger.ts +21 -0
  45. package/src/types/external.ts +24 -0
  46. package/{dist/packages/components/src/util/camelCase.d.ts → src/util/camelCase.ts} +33 -11
  47. package/src/util/camelCasedPropertiesDeep.ts +81 -0
  48. package/src/util/formatPrice.ts +14 -0
  49. package/src/util/internal.ts +95 -0
  50. package/{dist/packages/components/src/util/primitive.d.ts → src/util/primitive.ts} +2 -0
  51. package/src/util/splitWords.ts +72 -0
  52. package/{dist/packages/components/src/util/trim.d.ts → src/util/trim.ts} +5 -1
  53. package/dist/packages/components/src/atoms/search/types.d.ts +0 -5
  54. package/dist/packages/components/src/atoms/search/types.js +0 -1
  55. package/dist/packages/components/src/components/common/ButtonBase/ButtonBase.d.ts +0 -2
  56. package/dist/packages/components/src/components/common/ButtonBase/ButtonBase.js +0 -41
  57. package/dist/packages/components/src/components/common/ButtonBase/types.d.ts +0 -24
  58. package/dist/packages/components/src/components/common/ButtonBase/types.js +0 -6
  59. package/dist/packages/components/src/components/common/Headline/Headline.d.ts +0 -10
  60. package/dist/packages/components/src/components/common/Headline/Headline.js +0 -34
  61. package/dist/packages/components/src/components/common/ImageWithFallback/ImageWithFallback.d.ts +0 -12
  62. package/dist/packages/components/src/components/common/ImageWithFallback/ImageWithFallback.js +0 -26
  63. package/dist/packages/components/src/components/common/ProductCard/ProductCard.d.ts +0 -39
  64. package/dist/packages/components/src/components/common/ProductCard/ProductCard.js +0 -83
  65. package/dist/packages/components/src/components/common/ProductCard/ProductCardSkeleton.d.ts +0 -9
  66. package/dist/packages/components/src/components/common/ProductCard/ProductCardSkeleton.js +0 -15
  67. package/dist/packages/components/src/components/common/ProductCard/productCardVariants.d.ts +0 -8
  68. package/dist/packages/components/src/components/common/ProductCard/productCardVariants.js +0 -50
  69. package/dist/packages/components/src/components/common/ProductCard/types.d.ts +0 -39
  70. package/dist/packages/components/src/components/common/ProductCard/types.js +0 -10
  71. package/dist/packages/components/src/components/common/ProductGrid/ProductGrid.d.ts +0 -14
  72. package/dist/packages/components/src/components/common/ProductGrid/ProductGrid.js +0 -13
  73. package/dist/packages/components/src/components/common/ProductGrid/productGridVariants.d.ts +0 -10
  74. package/dist/packages/components/src/components/common/ProductGrid/productGridVariants.js +0 -16
  75. package/dist/packages/components/src/components/common/SparkleAnimation/SparkleAnimation.d.ts +0 -7
  76. package/dist/packages/components/src/components/common/SparkleAnimation/SparkleAnimation.js +0 -40
  77. package/dist/packages/components/src/components/common/SparkleAnimation/types.d.ts +0 -6
  78. package/dist/packages/components/src/components/common/SparkleAnimation/types.js +0 -7
  79. package/dist/packages/components/src/components/common/Spinner/Spinner.d.ts +0 -5
  80. package/dist/packages/components/src/components/common/Spinner/Spinner.js +0 -16
  81. package/dist/packages/components/src/components/common/SuggestionButton/SuggestionButton.d.ts +0 -18
  82. package/dist/packages/components/src/components/common/SuggestionButton/SuggestionButton.js +0 -210
  83. package/dist/packages/components/src/components/common/SuggestionButton/types.d.ts +0 -1
  84. package/dist/packages/components/src/components/common/SuggestionButton/types.js +0 -1
  85. package/dist/packages/components/src/components/common/Text/Text.d.ts +0 -12
  86. package/dist/packages/components/src/components/common/Text/Text.js +0 -26
  87. package/dist/packages/components/src/components/common/Text/textVariantClasses.d.ts +0 -171
  88. package/dist/packages/components/src/components/common/Text/textVariantClasses.js +0 -103
  89. package/dist/packages/components/src/components/common/Text/types.d.ts +0 -16
  90. package/dist/packages/components/src/components/common/Text/types.js +0 -6
  91. package/dist/packages/components/src/components/common/TextInput/TextInput.d.ts +0 -8
  92. package/dist/packages/components/src/components/common/TextInput/TextInput.js +0 -25
  93. package/dist/packages/components/src/components/models/colorsConfig.d.ts +0 -26
  94. package/dist/packages/components/src/components/models/colorsConfig.js +0 -23
  95. package/dist/packages/components/src/components/search/FilterScrollbar/AppliedFiltersScrollbar.d.ts +0 -11
  96. package/dist/packages/components/src/components/search/FilterScrollbar/AppliedFiltersScrollbar.js +0 -18
  97. package/dist/packages/components/src/components/search/FilterScrollbar/DynamicFiltersScrollbar.d.ts +0 -15
  98. package/dist/packages/components/src/components/search/FilterScrollbar/DynamicFiltersScrollbar.js +0 -15
  99. package/dist/packages/components/src/components/search/SearchFilter/SearchFilter.d.ts +0 -2
  100. package/dist/packages/components/src/components/search/SearchFilter/SearchFilter.js +0 -24
  101. package/dist/packages/components/src/components/search/SearchFilter/SearchFilterHeader.d.ts +0 -2
  102. package/dist/packages/components/src/components/search/SearchFilter/SearchFilterHeader.js +0 -9
  103. package/dist/packages/components/src/components/search/SearchFilter/SearchFilterItem.d.ts +0 -2
  104. package/dist/packages/components/src/components/search/SearchFilter/SearchFilterItem.js +0 -13
  105. package/dist/packages/components/src/components/search/SearchFilter/types.d.ts +0 -42
  106. package/dist/packages/components/src/components/search/SearchFilter/types.js +0 -1
  107. package/dist/packages/components/src/components/search/SearchInput/SearchInput.d.ts +0 -16
  108. package/dist/packages/components/src/components/search/SearchInput/SearchInput.js +0 -38
  109. package/dist/packages/components/src/components/search/SearchInput/searchInputVariants.d.ts +0 -13
  110. package/dist/packages/components/src/components/search/SearchInput/searchInputVariants.js +0 -12
  111. package/dist/packages/components/src/components/search/SearchInputAutocomplete/SearchAutocomplete.d.ts +0 -10
  112. package/dist/packages/components/src/components/search/SearchInputAutocomplete/SearchAutocomplete.js +0 -14
  113. package/dist/packages/components/src/components/search/SearchInputForm/SearchInputForm.d.ts +0 -16
  114. package/dist/packages/components/src/components/search/SearchInputForm/SearchInputForm.js +0 -13
  115. package/dist/packages/components/src/components/search/SearchInputForm/types.js +0 -1
  116. package/dist/packages/components/src/components/search/SearchResultsFilterSidebar/SearchResultsFilter.d.ts +0 -14
  117. package/dist/packages/components/src/components/search/SearchResultsFilterSidebar/SearchResultsFilter.js +0 -15
  118. package/dist/packages/components/src/components/search/SearchResultsFilterSidebar/searchFilterSidebarVariants.d.ts +0 -16
  119. package/dist/packages/components/src/components/search/SearchResultsFilterSidebar/searchFilterSidebarVariants.js +0 -29
  120. package/dist/packages/components/src/components/search/SearchResultsFilterSidebar/types.js +0 -1
  121. package/dist/packages/components/src/components/search/SearchResultsStates/NoSearchResultsFound.d.ts +0 -8
  122. package/dist/packages/components/src/components/search/SearchResultsStates/NoSearchResultsFound.js +0 -10
  123. package/dist/packages/components/src/components/search/SearchResultsStates/SearchResultsGrid.d.ts +0 -24
  124. package/dist/packages/components/src/components/search/SearchResultsStates/SearchResultsGrid.js +0 -19
  125. package/dist/packages/components/src/components/search/SearchResultsStates/SearchResultsLoadingGrid.d.ts +0 -6
  126. package/dist/packages/components/src/components/search/SearchResultsStates/SearchResultsLoadingGrid.js +0 -10
  127. package/dist/packages/components/src/components/search/types.d.ts +0 -5
  128. package/dist/packages/components/src/components/search/types.js +0 -6
  129. package/dist/packages/components/src/components/test/types.js +0 -1
  130. package/dist/packages/components/src/config/chatElementDisplayLocation.d.ts +0 -21
  131. package/dist/packages/components/src/config/chatElementDisplayLocation.js +0 -23
  132. package/dist/packages/components/src/index.d.ts +0 -45
  133. package/dist/packages/components/src/logging/logger.d.ts +0 -7
  134. package/dist/packages/components/src/logging/logger.js +0 -16
  135. package/dist/packages/components/src/types/external.d.ts +0 -21
  136. package/dist/packages/components/src/types/external.js +0 -5
  137. package/dist/packages/components/src/util/camelCase.js +0 -2
  138. package/dist/packages/components/src/util/camelCasedPropertiesDeep.d.ts +0 -53
  139. package/dist/packages/components/src/util/camelCasedPropertiesDeep.js +0 -1
  140. package/dist/packages/components/src/util/formatPrice.d.ts +0 -1
  141. package/dist/packages/components/src/util/formatPrice.js +0 -11
  142. package/dist/packages/components/src/util/internal.d.ts +0 -27
  143. package/dist/packages/components/src/util/internal.js +0 -4
  144. package/dist/packages/components/src/util/primitive.js +0 -2
  145. package/dist/packages/components/src/util/splitWords.d.ts +0 -55
  146. package/dist/packages/components/src/util/splitWords.js +0 -2
  147. package/dist/packages/components/src/util/trim.js +0 -2
  148. package/dist/packages/components/src/util/unknownArray.js +0 -1
  149. package/dist/tsconfig.tsbuildinfo +0 -1
  150. /package/{dist/packages/components/src/components/search/SearchInputForm/types.d.ts → src/components/search/SearchInputForm/types.ts} +0 -0
  151. /package/{dist/packages/components/src/util/unknownArray.d.ts → src/util/unknownArray.ts} +0 -0
@@ -0,0 +1,58 @@
1
+ import classNames from 'classnames';
2
+ import { type TextType, type TextVariant } from './types';
3
+ import { TextVariantMap } from './textVariantClasses';
4
+
5
+ type TextProps = {
6
+ children: React.ReactNode;
7
+ noOfLines?: number;
8
+ className?: string;
9
+ style?: React.CSSProperties;
10
+ variant?: TextVariant;
11
+ type?: TextType;
12
+ testId?: string;
13
+ };
14
+
15
+ export const Text = ({
16
+ children,
17
+ className,
18
+ style,
19
+ noOfLines,
20
+ testId,
21
+ type = 'span',
22
+ variant = 'body3',
23
+ }: TextProps) => {
24
+ const variantClassNames = variant ? TextVariantMap[variant] : {};
25
+ const textClassNames = classNames(className, variantClassNames, {
26
+ 'spiffy-tw-font-["Inter Variable,sans-serif"]': true,
27
+ 'spiffy-tw-line-clamp-1': noOfLines === 1,
28
+ 'spiffy-tw-line-clamp-2': noOfLines === 2,
29
+ 'spiffy-tw-line-clamp-3': noOfLines === 3,
30
+ 'spiffy-tw-line-clamp-4': noOfLines === 4,
31
+ 'spiffy-tw-line-clamp-5': noOfLines === 5,
32
+ 'spiffy-tw-line-clamp-6': noOfLines === 6,
33
+ 'spiffy-tw-line-clamp-7': noOfLines === 7,
34
+ });
35
+
36
+ switch (type) {
37
+ case 'p':
38
+ return (
39
+ <p data-testid={testId} className={textClassNames} style={style}>
40
+ {children}
41
+ </p>
42
+ );
43
+ case 'span':
44
+ return (
45
+ <span data-testid={testId} className={textClassNames} style={style}>
46
+ {children}
47
+ </span>
48
+ );
49
+ case 'sub':
50
+ return (
51
+ <sub data-testid={testId} className={textClassNames} style={style}>
52
+ {children}
53
+ </sub>
54
+ );
55
+ default:
56
+ throw new Error(`Invalid text type: ${type}`);
57
+ }
58
+ };
@@ -0,0 +1,106 @@
1
+ // TODO: add the rest of the variants as needed
2
+ export const HeadlineVariantMap = {
3
+ t1: {
4
+ 'spiffy-headline': true,
5
+ 'spiffy-headline-1': true,
6
+ 'spiffy-tw-text-[32px]': true,
7
+ 'spiffy-tw-font-medium': true,
8
+ 'spiffy-tw-leading-[124%]': true,
9
+ },
10
+ t2: {
11
+ 'spiffy-headline': true,
12
+ 'spiffy-headline-1': true,
13
+ 'spiffy-tw-text-[16px]': true,
14
+ 'spiffy-tw-font-medium': true,
15
+ 'spiffy-tw-leading-[124%]': true,
16
+ },
17
+ t3: {
18
+ 'spiffy-headline': true,
19
+ 'spiffy-headline-1': true,
20
+ 'spiffy-tw-text-[24px]': true,
21
+ 'spiffy-tw-leading-[120%]': true,
22
+ },
23
+ h1: {
24
+ 'spiffy-headline': true,
25
+ 'spiffy-headline-1': true,
26
+ 'spiffy-tw-text-[20px]': true,
27
+ 'spiffy-tw-font-normal': true,
28
+ 'spiffy-tw-leading-[120%]': true,
29
+ },
30
+ h2: {
31
+ 'spiffy-headline': true,
32
+ 'spiffy-headline-2': true,
33
+ 'spiffy-tw-text-[16px]': true,
34
+ 'spiffy-tw-font-normal': true,
35
+ 'spiffy-tw-leading-[124%]': true,
36
+ },
37
+
38
+ h3: {
39
+ 'spiffy-headline': true,
40
+ 'spiffy-headline-3': true,
41
+ 'spiffy-tw-text-[15px]': true,
42
+ 'spiffy-tw-font-medium': true,
43
+ 'spiffy-tw-leading-[124%]': true,
44
+ },
45
+ h4: {
46
+ 'spiffy-headline': true,
47
+ 'spiffy-headline-4': true,
48
+ 'spiffy-tw-text-[14px]': true,
49
+ 'spiffy-tw-font-medium': true,
50
+ 'spiffy-tw-leading-[124%]': true,
51
+ },
52
+ };
53
+
54
+ // TODO: add the rest of the variants as needed
55
+ export const TextVariantMap = {
56
+ body1: {
57
+ 'spiffy-body-text': true,
58
+ 'spiffy-body-1': true,
59
+ 'spiffy-tw-text-[20px]': true,
60
+ 'spiffy-tw-font-normal': true,
61
+ 'spiffy-tw-line-height-[120%]': true,
62
+ },
63
+ body2: {
64
+ 'spiffy-body-text': true,
65
+ 'spiffy-body-2': true,
66
+ 'spiffy-tw-text-[16px]': true,
67
+ 'spiffy-tw-font-normal': true,
68
+ 'spiffy-tw-line-height-[148%]': true,
69
+ },
70
+ body3: {
71
+ 'spiffy-body-text': true,
72
+ 'spiffy-body-3': true,
73
+ 'spiffy-tw-text-sm': true,
74
+ 'spiffy-tw-font-normal': true,
75
+ 'spiffy-tw-leading-[140%]': true,
76
+ },
77
+ body4: {
78
+ 'spiffy-body-text': true,
79
+ 'spiffy-body-4': true,
80
+ 'spiffy-tw-text-xs': true,
81
+ 'spiffy-tw-font-medium': true,
82
+ 'spiffy-tw-leading-[130%]': true,
83
+ },
84
+ body5: {
85
+ 'spiffy-body-text': true,
86
+ 'spiffy-body-5': true,
87
+ 'spiffy-tw-text-[10px]': true,
88
+ 'spiffy-tw-font-normal': true,
89
+ },
90
+ };
91
+
92
+ export const TextStyleVariantMap = {
93
+ ...TextVariantMap,
94
+ ...HeadlineVariantMap,
95
+ };
96
+
97
+ export const getVariantPlaceholderClassNames = (classes: Record<string, boolean>) => {
98
+ return Object.keys(classes).reduce((acc, key) => {
99
+ if(key.includes('spiffy-tw')){
100
+ acc[`placeholder:${key}`] = classes[key];
101
+ } else {
102
+ acc[`${key}-placeholder`] = classes[key];
103
+ }
104
+ return acc;
105
+ }, {} as Record<string, boolean>);
106
+ }
@@ -0,0 +1,23 @@
1
+ export enum ExpandableTextOverrides {
2
+ EXPANDABLE_TEXT_CONTAINER = 'spiffy-expandable-text-container',
3
+ EXPANDABLE_TEXT = 'spiffy-expandable-text',
4
+ EXPANDABLE_BUTTON = 'spiffy-expandable-button',
5
+ }
6
+
7
+ // This is an incomplete list of text variants
8
+ // TODO: add the rest of the variants as needed
9
+ export type TextVariant = 'body1' | 'body2' | 'body3' | 'body4' | 'body5';
10
+ export type TextType = 'p' | 'span' | 'sub';
11
+
12
+ export type HeadlineProps = {
13
+ children: React.ReactNode;
14
+ className?: string;
15
+ style?: React.CSSProperties;
16
+ variant?: HeadlineVariant;
17
+ testId?: string;
18
+ id?: string;
19
+ };
20
+
21
+ // This is an incomplete list of text variants
22
+ // TODO: add the rest of the variants as needed
23
+ export type HeadlineVariant = 't1' | 't2' | 't3' | 'h1' | 'h2' | 'h3' | 'h4';
@@ -0,0 +1,34 @@
1
+ // Disabling this prevents an otherwise simple wrapper around input
2
+ // from becoming needlessly verbose while maintaining flexibility.
3
+ // Keeping it enabled requires listing every possible prop.
4
+ import React from 'react';
5
+ import classNames from 'classnames';
6
+ import { getVariantPlaceholderClassNames, TextStyleVariantMap } from '../Text/textVariantClasses';
7
+
8
+ export interface TextInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
9
+ borderRadius: 'sm' | 'md' | 'lg' | 'xl';
10
+ borderColorClass?: string;
11
+ placeholderVariant?: keyof typeof TextStyleVariantMap;
12
+ }
13
+
14
+ export const TextInput = React.forwardRef<HTMLInputElement, TextInputProps>(
15
+ ({ className, borderRadius, borderColorClass, placeholderVariant, ...rest }, ref) => {
16
+ const variantClassNames = placeholderVariant ? TextStyleVariantMap[placeholderVariant] : {};
17
+ const placeholderVariantClassNames = getVariantPlaceholderClassNames(variantClassNames);
18
+ const inputClasses = classNames([
19
+ {
20
+ 'spiffy-text-input': true,
21
+ 'spiffy-tw-outline-none': true,
22
+ 'spiffy-tw-rounded-[64px]': borderRadius === 'xl',
23
+ 'spiffy-tw-rounded-[20px]': borderRadius === 'lg',
24
+ 'spiffy-tw-rounded-[8px]': borderRadius === 'md',
25
+ 'spiffy-tw-rounded-[0px]': borderRadius === 'sm',
26
+ ...placeholderVariantClassNames,
27
+ },
28
+ borderColorClass,
29
+ className,
30
+ ]);
31
+
32
+ return <input ref={ref} {...rest} className={inputClasses} />;
33
+ },
34
+ );
@@ -0,0 +1,28 @@
1
+ enum ColorNames {
2
+ TextPrimary = 'text-primary',
3
+ TextSecondary = 'text-secondary',
4
+ TextAccent = 'text-accent',
5
+ TextLink = 'text-link',
6
+ TextLight = 'text-light',
7
+ BackgroundPrimary = 'background-primary',
8
+ BackgroundSecondary = 'background-secondary',
9
+ BackgroundSecondaryDark = 'background-secondary-dark',
10
+ BackgroundTertiary = 'background-tertiary',
11
+ BackgroundDark = 'background-dark',
12
+ BackgroundLight = 'background-light',
13
+ BackgroundSaturated = 'background-saturated',
14
+ BorderLight = 'border-light',
15
+ BorderMedium = 'border-medium',
16
+ BorderDark = 'border-dark',
17
+ BorderOutline = 'border-outline',
18
+ AccentPrimary = 'accent-primary',
19
+ AccentSecondary = 'accent-secondary',
20
+ }
21
+
22
+ type ColorMapping = {
23
+ [key in ColorNames]: string;
24
+ };
25
+
26
+ const colorVar = (color: ColorNames): string => `var(--spiffy-colors-${color})`;
27
+ export { ColorNames, colorVar };
28
+ export type { ColorMapping };
@@ -0,0 +1,70 @@
1
+ import classNames from 'classnames';
2
+ import ScrollContainer from 'react-indiana-drag-scroll';
3
+ import { Text } from 'src/components/common/Text/Text';
4
+ import type { SelectedFilterOption } from 'src/atoms/search/types';
5
+
6
+ interface AppliedFiltersScrollbarProps {
7
+ selectedFilterOptions: SelectedFilterOption[];
8
+ filterBarClassNames: string;
9
+ filterDefaultClasses: string;
10
+ filterHoverClasses: string;
11
+ appliedFilterBackgroundClasses: string;
12
+ onRemoveFilter: (filter: SelectedFilterOption) => void;
13
+ }
14
+
15
+ export const AppliedFiltersScrollbar = ({
16
+ selectedFilterOptions,
17
+ filterBarClassNames,
18
+ filterDefaultClasses,
19
+ filterHoverClasses,
20
+ appliedFilterBackgroundClasses,
21
+ onRemoveFilter,
22
+ }: AppliedFiltersScrollbarProps) => {
23
+ if (selectedFilterOptions.length === 0) {
24
+ return null;
25
+ }
26
+
27
+ // Extract border and text classes from filterDefaultClasses, excluding background because everything is shared between filters except for the applied filters background
28
+ const filterDefaultWithoutBg = filterDefaultClasses
29
+ .replace(/spiffy-tw-bg-\[--spiffy-colors-[^\]]+\]/g, '')
30
+ .trim();
31
+ const buttonClasses = classNames(
32
+ ' spiffy-tw-flex spiffy-tw-items-center spiffy-tw-rounded-full spiffy-tw-px-[8px] spiffy-tw-py-[4px] spiffy-tw-whitespace-nowrap',
33
+ filterHoverClasses,
34
+ filterDefaultWithoutBg,
35
+ appliedFilterBackgroundClasses,
36
+ );
37
+ const iconColor = 'currentColor'; // match with text color
38
+
39
+ return (
40
+ <ScrollContainer className={filterBarClassNames} hideScrollbars>
41
+ {selectedFilterOptions.map((filter) => {
42
+ return (
43
+ <button
44
+ key={filter.id}
45
+ className={buttonClasses}
46
+ type="button"
47
+ onClick={() => onRemoveFilter(filter)}
48
+ aria-label={`Remove filter: ${filter.displayName}`}
49
+ >
50
+ <div className="spiffy-tw-flex spiffy-tw-items-center spiffy-tw-gap-[8px]">
51
+ <Text variant="body3">{filter.displayName}</Text>
52
+ <svg
53
+ xmlns="http://www.w3.org/2000/svg"
54
+ width="10"
55
+ height="10"
56
+ viewBox="0 0 10 10"
57
+ fill="none"
58
+ >
59
+ <path
60
+ d="M4.59766 4.29297L8.48535 0.405273L9.19238 1.1123L5.30469 5L9.19238 8.8877L8.48535 9.59473L4.59766 5.70703L0.707031 9.59766L0 8.89062L3.89062 5L0 1.10938L0.707031 0.402344L4.59766 4.29297Z"
61
+ fill={iconColor}
62
+ />
63
+ </svg>
64
+ </div>
65
+ </button>
66
+ );
67
+ })}
68
+ </ScrollContainer>
69
+ );
70
+ };
@@ -0,0 +1,52 @@
1
+ import classNames from 'classnames';
2
+ import ScrollContainer from 'react-indiana-drag-scroll';
3
+ import { Text } from 'src/components/common/Text/Text';
4
+
5
+ interface DynamicFiltersScrollbarProps {
6
+ availableDynamicFilters: { name: string; displayName: string }[];
7
+ filterBarClassNames: string;
8
+ filterDefaultClasses: string;
9
+ filterHoverClasses: string;
10
+ onToggleDynamicFilter: ({
11
+ filter,
12
+ dynamicFilterDisplayName,
13
+ }: {
14
+ filter: string;
15
+ dynamicFilterDisplayName: string;
16
+ }) => void;
17
+ }
18
+
19
+ export const DynamicFiltersScrollbar = ({
20
+ availableDynamicFilters,
21
+ filterBarClassNames,
22
+ filterDefaultClasses,
23
+ filterHoverClasses,
24
+ onToggleDynamicFilter,
25
+ }: DynamicFiltersScrollbarProps) => {
26
+ return (
27
+ <ScrollContainer className={filterBarClassNames} hideScrollbars>
28
+ {availableDynamicFilters.map(({ name, displayName }) => {
29
+ const buttonClasses = classNames(
30
+ 'spiffy-tw-px-[12px] spiffy-tw-py-2 spiffy-tw-rounded-full spiffy-tw-border spiffy-tw-whitespace-nowrap ',
31
+ filterHoverClasses,
32
+ filterDefaultClasses,
33
+ );
34
+ return (
35
+ <button
36
+ key={name}
37
+ type="button"
38
+ className={buttonClasses}
39
+ onClick={() => {
40
+ onToggleDynamicFilter({
41
+ filter: name,
42
+ dynamicFilterDisplayName: displayName,
43
+ });
44
+ }}
45
+ >
46
+ <Text variant="body3">{displayName}</Text>
47
+ </button>
48
+ );
49
+ })}
50
+ </ScrollContainer>
51
+ );
52
+ };
@@ -0,0 +1,84 @@
1
+ import classNames from 'classnames';
2
+ import { SearchFilterItem } from './SearchFilterItem';
3
+ import type { SearchFilterProps } from './types';
4
+ import { Text } from 'src/components/common/Text/Text';
5
+ import { ButtonBase } from 'src/components/common/ButtonBase/ButtonBase';
6
+
7
+ export const SearchFilter = ({
8
+ isOpen,
9
+ setIsOpen,
10
+ filters,
11
+ // productCount,
12
+ selectFilterItem,
13
+ clearAllFilters,
14
+ // applyFiltersUnchangedClasses,
15
+ // applyFiltersChangedClasses,
16
+ filterButtonText,
17
+ radioButtonFillColor,
18
+ radioButtonHoverColor,
19
+ radioButtonUncheckedBorderColor,
20
+ // filterCloseIconVariant,
21
+ headerContent,
22
+ }: 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
+ );
30
+
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
+ );
38
+
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
+ );
42
+
43
+ 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.id} 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.id}
58
+ filterItem={item}
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"
74
+ />
75
+ <ButtonBase
76
+ onClick={() => setIsOpen(false)}
77
+ text={filterButtonText}
78
+ buttonClass="spiffy-tw-w-full"
79
+ />
80
+ </div>
81
+ </div>
82
+ </div>
83
+ );
84
+ };
@@ -0,0 +1,42 @@
1
+ import classNames from 'classnames';
2
+ import { Text } from 'src/components/common/Text/Text';
3
+ import type { SearchFilterHeaderProps } from './types';
4
+
5
+ export const SearchFilterHeader = ({
6
+ closeModal,
7
+ productCount,
8
+ headerClassName,
9
+ filterCloseIconVariant,
10
+ }: SearchFilterHeaderProps) => {
11
+ const closeIconColor =
12
+ filterCloseIconVariant === 'dark'
13
+ ? 'var(--spiffy-colors-text-primary)'
14
+ : 'var(--spiffy-colors-text-light)';
15
+
16
+ return (
17
+ <div
18
+ className={classNames(
19
+ 'spiffy-tw-flex spiffy-tw-items-center spiffy-tw-justify-between spiffy-tw-px-4 spiffy-tw-py-3',
20
+ headerClassName,
21
+ )}
22
+ >
23
+ <Text variant="body2" className="spiffy-tw-font-medium">
24
+ Filters ({productCount})
25
+ </Text>
26
+ <button onClick={closeModal} className="spiffy-tw-p-2">
27
+ <svg
28
+ xmlns="http://www.w3.org/2000/svg"
29
+ width="14"
30
+ height="14"
31
+ viewBox="0 0 14 14"
32
+ fill="none"
33
+ >
34
+ <path
35
+ d="M13.7071 1.70711C14.0976 1.31658 14.0976 0.683417 13.7071 0.292893C13.3166 -0.0976311 12.6834 -0.0976311 12.2929 0.292893L7 5.58579L1.70711 0.292893C1.31658 -0.0976311 0.683417 -0.0976311 0.292893 0.292893C-0.0976311 0.683417 -0.0976311 1.31658 0.292893 1.70711L5.58579 7L0.292893 12.2929C-0.0976311 12.6834 -0.0976311 13.3166 0.292893 13.7071C0.683417 14.0976 1.31658 14.0976 1.70711 13.7071L7 8.41421L12.2929 13.7071C12.6834 14.0976 13.3166 14.0976 13.7071 13.7071C14.0976 13.3166 14.0976 12.6834 13.7071 12.2929L8.41421 7L13.7071 1.70711Z"
36
+ fill={closeIconColor}
37
+ />
38
+ </svg>
39
+ </button>
40
+ </div>
41
+ );
42
+ };
@@ -0,0 +1,42 @@
1
+ import classNames from 'classnames';
2
+ import { Text } from 'src/components/common/Text/Text';
3
+ import type { SearchFilterItemProps } from './types';
4
+
5
+ export const SearchFilterItem = ({
6
+ filterItem,
7
+ onSelectFilterItem,
8
+ radioButtonFillColor,
9
+ radioButtonHoverColor,
10
+ radioButtonUncheckedBorderColor,
11
+ }: SearchFilterItemProps) => {
12
+ const { displayName, isSelected } = filterItem;
13
+
14
+ const radioClasses = classNames(
15
+ 'spiffy-tw-w-4 spiffy-tw-h-4 spiffy-tw-rounded-full spiffy-tw-border-2',
16
+ 'spiffy-tw-flex spiffy-tw-items-center spiffy-tw-justify-center',
17
+ radioButtonUncheckedBorderColor,
18
+ radioButtonHoverColor,
19
+ {
20
+ [radioButtonFillColor]: isSelected,
21
+ },
22
+ );
23
+
24
+ return (
25
+ <li
26
+ className="spiffy-tw-flex spiffy-tw-items-center spiffy-tw-justify-between spiffy-tw-py-2 spiffy-tw-cursor-pointer"
27
+ onClick={() => onSelectFilterItem(filterItem)}
28
+ >
29
+ <Text variant="body3">{displayName}</Text>
30
+ <div className={radioClasses}>
31
+ {isSelected && (
32
+ <div
33
+ className={classNames('spiffy-tw-w-2 spiffy-tw-h-2 spiffy-tw-rounded-full', {
34
+ 'spiffy-tw-bg-white': radioButtonFillColor.includes('spiffy-tw-bg-'),
35
+ 'spiffy-tw-bg-black': radioButtonFillColor.includes('spiffy-tw-bg-white'),
36
+ })}
37
+ />
38
+ )}
39
+ </div>
40
+ </li>
41
+ );
42
+ };
@@ -0,0 +1,48 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export type SearchFilterItem = {
4
+ id: string;
5
+ displayName: string;
6
+ value: string;
7
+ isSelected: boolean;
8
+ };
9
+
10
+ export type SearchFilter = {
11
+ id: string;
12
+ displayName: string;
13
+ items: SearchFilterItem[];
14
+ };
15
+
16
+ export type SelectFilterItem = (filterItem: SearchFilterItem) => void;
17
+
18
+ export interface SearchFilterItemProps {
19
+ filterItem: SearchFilterItem;
20
+ onSelectFilterItem: SelectFilterItem;
21
+ radioButtonFillColor: string;
22
+ radioButtonHoverColor: string;
23
+ radioButtonUncheckedBorderColor: string;
24
+ }
25
+
26
+ export interface SearchFilterHeaderProps {
27
+ closeModal: () => void;
28
+ productCount: number;
29
+ headerClassName: string;
30
+ filterCloseIconVariant: 'dark' | 'light' | 'tertiary';
31
+ }
32
+
33
+ export interface SearchFilterProps {
34
+ isOpen: boolean;
35
+ setIsOpen: (isOpen: boolean) => void;
36
+ filters: SearchFilter[];
37
+ productCount: number;
38
+ selectFilterItem: SelectFilterItem;
39
+ clearAllFilters: () => void;
40
+ applyFiltersUnchangedClasses: string;
41
+ applyFiltersChangedClasses: string;
42
+ filterButtonText: string;
43
+ radioButtonFillColor: string;
44
+ radioButtonHoverColor: string;
45
+ radioButtonUncheckedBorderColor: string;
46
+ filterCloseIconVariant: 'dark' | 'light' | 'tertiary';
47
+ headerContent: ReactNode;
48
+ }