@magento/venia-pwa-live-search 1.0.0-alpha6

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 (202) hide show
  1. package/package.json +31 -0
  2. package/postcss.config.js +3 -0
  3. package/src/api/fragments.js +193 -0
  4. package/src/api/graphql.js +26 -0
  5. package/src/api/mutations.js +94 -0
  6. package/src/api/queries.js +225 -0
  7. package/src/api/search.js +222 -0
  8. package/src/components/AddToCartButton/AddToCartButton.jsx +32 -0
  9. package/src/components/AddToCartButton/AddToCartButton.stories.mdx +14 -0
  10. package/src/components/AddToCartButton/index.js +10 -0
  11. package/src/components/Alert/Alert.jsx +155 -0
  12. package/src/components/Alert/index.js +11 -0
  13. package/src/components/Breadcrumbs/Breadcrumbs.jsx +34 -0
  14. package/src/components/Breadcrumbs/MockPages.js +14 -0
  15. package/src/components/Breadcrumbs/index.js +11 -0
  16. package/src/components/ButtonShimmer/ButtonShimmer.css +32 -0
  17. package/src/components/ButtonShimmer/ButtonShimmer.jsx +23 -0
  18. package/src/components/ButtonShimmer/index.js +11 -0
  19. package/src/components/CategoryFilters/CategoryFilters.jsx +59 -0
  20. package/src/components/CategoryFilters/index.js +10 -0
  21. package/src/components/Facets/Facets.jsx +50 -0
  22. package/src/components/Facets/Range/RangeFacet.js +20 -0
  23. package/src/components/Facets/Scalar/ScalarFacet.js +29 -0
  24. package/src/components/Facets/SelectedFilters.js +80 -0
  25. package/src/components/Facets/format.js +52 -0
  26. package/src/components/Facets/index.js +14 -0
  27. package/src/components/Facets/mocks.js +119 -0
  28. package/src/components/FacetsShimmer/FacetsShimmer.css +49 -0
  29. package/src/components/FacetsShimmer/FacetsShimmer.jsx +25 -0
  30. package/src/components/FacetsShimmer/index.js +11 -0
  31. package/src/components/FilterButton/FilterButton.jsx +40 -0
  32. package/src/components/FilterButton/index.js +11 -0
  33. package/src/components/ImageCarousel/Image.jsx +34 -0
  34. package/src/components/ImageCarousel/ImageCarousel.jsx +103 -0
  35. package/src/components/ImageCarousel/index.js +11 -0
  36. package/src/components/InputButtonGroup/InputButtonGroup.jsx +120 -0
  37. package/src/components/InputButtonGroup/index.js +11 -0
  38. package/src/components/LabelledInput/LabelledInput.jsx +51 -0
  39. package/src/components/LabelledInput/index.js +11 -0
  40. package/src/components/Loading/Loading.jsx +32 -0
  41. package/src/components/Loading/index.js +11 -0
  42. package/src/components/NoResults/NoResults.jsx +55 -0
  43. package/src/components/NoResults/index.js +11 -0
  44. package/src/components/Pagination/Pagination.jsx +105 -0
  45. package/src/components/Pagination/index.js +10 -0
  46. package/src/components/PerPagePicker/PerPagePicker.jsx +114 -0
  47. package/src/components/PerPagePicker/index.js +11 -0
  48. package/src/components/Pill/Pill.jsx +34 -0
  49. package/src/components/Pill/index.js +11 -0
  50. package/src/components/Pill/mock.js +23 -0
  51. package/src/components/ProductCardShimmer/ProductCardShimmer.css +72 -0
  52. package/src/components/ProductCardShimmer/ProductCardShimmer.jsx +28 -0
  53. package/src/components/ProductCardShimmer/index.js +11 -0
  54. package/src/components/ProductItem/MockData.js +508 -0
  55. package/src/components/ProductItem/ProductItem.css +84 -0
  56. package/src/components/ProductItem/ProductItem.jsx +347 -0
  57. package/src/components/ProductItem/ProductPrice.jsx +181 -0
  58. package/src/components/ProductItem/index.js +11 -0
  59. package/src/components/ProductList/MockData.js +190 -0
  60. package/src/components/ProductList/ProductList.jsx +127 -0
  61. package/src/components/ProductList/index.js +11 -0
  62. package/src/components/ProductList/product-list.css +18 -0
  63. package/src/components/SearchBar/SearchBar.jsx +33 -0
  64. package/src/components/SearchBar/index.js +11 -0
  65. package/src/components/Shimmer/Shimmer.css +82 -0
  66. package/src/components/Shimmer/Shimmer.jsx +66 -0
  67. package/src/components/Shimmer/index.js +11 -0
  68. package/src/components/Slider/Slider.css +61 -0
  69. package/src/components/Slider/Slider.jsx +103 -0
  70. package/src/components/Slider/index.jsx +11 -0
  71. package/src/components/SliderDoubleControl/SliderDoubleControl.css +83 -0
  72. package/src/components/SliderDoubleControl/SliderDoubleControl.jsx +220 -0
  73. package/src/components/SliderDoubleControl/index.js +11 -0
  74. package/src/components/SortDropdown/SortDropdown.jsx +126 -0
  75. package/src/components/SortDropdown/index.js +11 -0
  76. package/src/components/SwatchButton/SwatchButton.jsx +72 -0
  77. package/src/components/SwatchButton/index.js +11 -0
  78. package/src/components/SwatchButtonGroup/SwatchButtonGroup.jsx +86 -0
  79. package/src/components/SwatchButtonGroup/index.js +11 -0
  80. package/src/components/ViewSwitcher/ViewSwitcher.jsx +46 -0
  81. package/src/components/ViewSwitcher/index.js +11 -0
  82. package/src/components/WishlistButton/WishlistButton.jsx +67 -0
  83. package/src/components/WishlistButton/index.js +11 -0
  84. package/src/containers/App.jsx +145 -0
  85. package/src/containers/LiveSearchPLPLoader.jsx +24 -0
  86. package/src/containers/LiveSearchPopoverLoader.jsx +190 -0
  87. package/src/containers/LiveSearchSRLPLoader.jsx +24 -0
  88. package/src/containers/ProductListingPage.jsx +66 -0
  89. package/src/containers/ProductsContainer.jsx +145 -0
  90. package/src/containers/ProductsHeader.jsx +123 -0
  91. package/src/context/attributeMetadata.js +63 -0
  92. package/src/context/cart.js +97 -0
  93. package/src/context/displayChange.js +90 -0
  94. package/src/context/events.js +160 -0
  95. package/src/context/index.js +19 -0
  96. package/src/context/products.jsx +336 -0
  97. package/src/context/resultsModifierContext.js +35 -0
  98. package/src/context/search.jsx +127 -0
  99. package/src/context/store.jsx +93 -0
  100. package/src/context/translation.jsx +125 -0
  101. package/src/context/widgetConfig.jsx +120 -0
  102. package/src/context/wishlist.jsx +97 -0
  103. package/src/hooks/eventing/useEventListener.js +13 -0
  104. package/src/hooks/eventing/useLocation.js +21 -0
  105. package/src/hooks/eventing/useMagentoExtensionContext.js +28 -0
  106. package/src/hooks/eventing/usePageView.js +36 -0
  107. package/src/hooks/eventing/useShopperContext.js +33 -0
  108. package/src/hooks/eventing/useStorefrontInstanceContext.js +46 -0
  109. package/src/hooks/eventing/useViewedOffsets.js +74 -0
  110. package/src/hooks/useAccessibleDropdown.js +148 -0
  111. package/src/hooks/useLiveSearchPLPConfig.js +112 -0
  112. package/src/hooks/useLiveSearchPopoverConfig.js +83 -0
  113. package/src/hooks/useLiveSearchSRLPConfig.js +97 -0
  114. package/src/hooks/usePagination.js +83 -0
  115. package/src/hooks/useRangeFacet.js +62 -0
  116. package/src/hooks/useScalarFacet.js +61 -0
  117. package/src/hooks/useSliderFacet.js +43 -0
  118. package/src/i18n/Sorani.js +60 -0
  119. package/src/i18n/ar_AE.js +60 -0
  120. package/src/i18n/bg_BG.js +60 -0
  121. package/src/i18n/bn_IN.js +60 -0
  122. package/src/i18n/ca_ES.js +60 -0
  123. package/src/i18n/cs_CZ.js +60 -0
  124. package/src/i18n/da_DK.js +60 -0
  125. package/src/i18n/de_DE.js +60 -0
  126. package/src/i18n/el_GR.js +60 -0
  127. package/src/i18n/en_GA.js +60 -0
  128. package/src/i18n/en_GB.js +60 -0
  129. package/src/i18n/en_US.js +70 -0
  130. package/src/i18n/es_ES.js +60 -0
  131. package/src/i18n/et_EE.js +60 -0
  132. package/src/i18n/eu_ES.js +60 -0
  133. package/src/i18n/fa_IR.js +60 -0
  134. package/src/i18n/fi_FI.js +60 -0
  135. package/src/i18n/fr_FR.js +60 -0
  136. package/src/i18n/gl_ES.js +60 -0
  137. package/src/i18n/hi_IN.js +60 -0
  138. package/src/i18n/hu_HU.js +60 -0
  139. package/src/i18n/hy_AM.js +60 -0
  140. package/src/i18n/id_ID.js +60 -0
  141. package/src/i18n/index.js +89 -0
  142. package/src/i18n/it_IT.js +60 -0
  143. package/src/i18n/ja_JP.js +60 -0
  144. package/src/i18n/ko_KR.js +60 -0
  145. package/src/i18n/lt_LT.js +60 -0
  146. package/src/i18n/lv_LV.js +60 -0
  147. package/src/i18n/nb_NO.js +60 -0
  148. package/src/i18n/nl_NL.js +60 -0
  149. package/src/i18n/pt_BR.js +60 -0
  150. package/src/i18n/pt_PT.js +60 -0
  151. package/src/i18n/ro_RO.js +60 -0
  152. package/src/i18n/ru_RU.js +60 -0
  153. package/src/i18n/sv_SE.js +60 -0
  154. package/src/i18n/th_TH.js +60 -0
  155. package/src/i18n/tr_TR.js +60 -0
  156. package/src/i18n/zh_Hans_CN.js +60 -0
  157. package/src/i18n/zh_Hant_TW.js +60 -0
  158. package/src/icons/NoImage.svg +1 -0
  159. package/src/icons/adjustments.svg +3 -0
  160. package/src/icons/cart.svg +3 -0
  161. package/src/icons/checkmark.svg +3 -0
  162. package/src/icons/chevron.svg +3 -0
  163. package/src/icons/emptyHeart.svg +3 -0
  164. package/src/icons/error.svg +3 -0
  165. package/src/icons/filledHeart.svg +3 -0
  166. package/src/icons/filter.svg +29 -0
  167. package/src/icons/gridView.svg +11 -0
  168. package/src/icons/info.svg +3 -0
  169. package/src/icons/listView.svg +5 -0
  170. package/src/icons/loading.svg +6 -0
  171. package/src/icons/plus.svg +4 -0
  172. package/src/icons/sort.svg +18 -0
  173. package/src/icons/warning.svg +3 -0
  174. package/src/icons/x.svg +3 -0
  175. package/src/index.jsx +65 -0
  176. package/src/queries/customerGroupCode.gql.js +10 -0
  177. package/src/queries/eventing/getMagentoExtensionContext.gql.js +13 -0
  178. package/src/queries/eventing/getPageType.gql.js +14 -0
  179. package/src/queries/eventing/getStorefrontContext.gql.js +27 -0
  180. package/src/queries/index.js +3 -0
  181. package/src/queries/liveSearchPlpConfigs.gql.js +30 -0
  182. package/src/queries/liveSearchPopoverConfigs.gql.js +28 -0
  183. package/src/styles/autocomplete.module.css +56 -0
  184. package/src/styles/index.css +1638 -0
  185. package/src/styles/searchBar.module.css +119 -0
  186. package/src/styles/tokens.css +99 -0
  187. package/src/targets/intercept.js +21 -0
  188. package/src/utils/constants.js +26 -0
  189. package/src/utils/decodeHtmlString.js +13 -0
  190. package/src/utils/dom.js +14 -0
  191. package/src/utils/eventing/getCookie.js +9 -0
  192. package/src/utils/eventing/usePageTypeFromUrl.js +26 -0
  193. package/src/utils/getProductImage.js +94 -0
  194. package/src/utils/getProductPrice.js +83 -0
  195. package/src/utils/getUserViewHistory.js +27 -0
  196. package/src/utils/handleUrlFilters.js +164 -0
  197. package/src/utils/htmlStringDecode.js +13 -0
  198. package/src/utils/modifyResults.js +164 -0
  199. package/src/utils/sort.js +95 -0
  200. package/src/utils/useIntersectionObserver.js +27 -0
  201. package/src/utils/validateStoreDetails.js +39 -0
  202. package/src/wrappers/wrapUseApp.js +28 -0
@@ -0,0 +1,50 @@
1
+ /*
2
+ Copyright 2024 Adobe
3
+ All Rights Reserved.
4
+
5
+ NOTICE: Adobe permits you to use, modify, and distribute this file in
6
+ accordance with the terms of the Adobe license agreement accompanying
7
+ it.
8
+ */
9
+
10
+ import React from 'react';
11
+
12
+ import { useStore } from '../../context';
13
+ import RangeFacet from './Range/RangeFacet';
14
+ import ScalarFacet from './Scalar/ScalarFacet';
15
+ import SliderDoubleControl from '../SliderDoubleControl';
16
+
17
+ export const Facets = ({ searchFacets }) => {
18
+ const {
19
+ config: { priceSlider },
20
+ } = useStore();
21
+
22
+ return (
23
+ <div className="ds-plp-facets flex flex-col">
24
+ <form className="ds-plp-facets__list border-t border-gray-200">
25
+ {searchFacets?.map((facet) => {
26
+ const bucketType = facet?.buckets[0]?.__typename;
27
+ switch (bucketType) {
28
+ case 'ScalarBucket':
29
+ return <ScalarFacet key={facet.attribute} filterData={facet} />;
30
+ case 'RangeBucket':
31
+ return priceSlider ? (
32
+ <SliderDoubleControl filterData={facet} />
33
+ ) : (
34
+ <RangeFacet
35
+ key={facet.attribute}
36
+ filterData={facet}
37
+ />
38
+ );
39
+ case 'CategoryView':
40
+ return <ScalarFacet key={facet.attribute} filterData={facet} />;
41
+ default:
42
+ return null;
43
+ }
44
+ })}
45
+ </form>
46
+ </div>
47
+ );
48
+ };
49
+
50
+ export default Facets;
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import useRangeFacet from '../../../hooks/useRangeFacet';
3
+ import { InputButtonGroup } from '../../InputButtonGroup';
4
+
5
+ const RangeFacet = ({ filterData }) => {
6
+ const { isSelected, onChange } = useRangeFacet(filterData);
7
+
8
+ return (
9
+ <InputButtonGroup
10
+ title={filterData.title}
11
+ attribute={filterData.attribute}
12
+ buckets={filterData.buckets}
13
+ type="radio"
14
+ isSelected={isSelected}
15
+ onChange={e => onChange(e.value)}
16
+ />
17
+ );
18
+ };
19
+
20
+ export default RangeFacet;
@@ -0,0 +1,29 @@
1
+ /*
2
+ Copyright 2024 Adobe
3
+ All Rights Reserved.
4
+
5
+ NOTICE: Adobe permits you to use, modify, and distribute this file in
6
+ accordance with the terms of the Adobe license agreement accompanying
7
+ it.
8
+ */
9
+
10
+ import React from 'react';
11
+ import { InputButtonGroup } from '../../InputButtonGroup';
12
+ import useScalarFacet from '../../../hooks/useScalarFacet';
13
+
14
+ const ScalarFacet = ({ filterData }) => {
15
+ const { isSelected, onChange } = useScalarFacet(filterData);
16
+
17
+ return (
18
+ <InputButtonGroup
19
+ title={filterData.title}
20
+ attribute={filterData.attribute}
21
+ buckets={filterData.buckets}
22
+ type="checkbox"
23
+ isSelected={isSelected}
24
+ onChange={args => onChange(args.value, args.selected)}
25
+ />
26
+ );
27
+ };
28
+
29
+ export default ScalarFacet;
@@ -0,0 +1,80 @@
1
+ /*
2
+ Copyright 2024 Adobe
3
+ All Rights Reserved.
4
+
5
+ NOTICE: Adobe permits you to use, modify, and distribute this file in
6
+ accordance with the terms of the Adobe license agreement accompanying
7
+ it.
8
+ */
9
+ import React from 'react';
10
+ import { useSearch, useProducts, useTranslation } from '../../context';
11
+ import Pill from '../Pill';
12
+ import { formatBinaryLabel, formatRangeLabel } from './format';
13
+
14
+ const SelectedFilters = () => {
15
+ const searchCtx = useSearch();
16
+ const productsCtx = useProducts();
17
+ const translation = useTranslation();
18
+
19
+ return (
20
+ <div className="w-full h-full">
21
+ {searchCtx.filters?.length > 0 && (
22
+ <div className="ds-plp-facets__pills pb-6 sm:pb-6 flex flex-wrap mt-8 justify-start">
23
+ {searchCtx.filters.map(filter => (
24
+ <div key={filter.attribute}>
25
+ {filter.in?.map(option => (
26
+ <Pill
27
+ key={formatBinaryLabel(
28
+ filter,
29
+ option,
30
+ searchCtx.categoryNames,
31
+ productsCtx.categoryPath
32
+ )}
33
+ label={formatBinaryLabel(
34
+ filter,
35
+ option,
36
+ searchCtx.categoryNames,
37
+ productsCtx.categoryPath
38
+ )}
39
+ type="transparent"
40
+ onClick={() =>
41
+ searchCtx.updateFilterOptions(
42
+ filter,
43
+ option
44
+ )
45
+ }
46
+ />
47
+ ))}
48
+ {filter.range && (
49
+ <Pill
50
+ label={formatRangeLabel(
51
+ filter,
52
+ productsCtx.currencyRate,
53
+ productsCtx.currencySymbol
54
+ )}
55
+ type="transparent"
56
+ onClick={() => {
57
+ searchCtx.removeFilter(
58
+ filter.attribute
59
+ );
60
+ }}
61
+ />
62
+ )}
63
+ </div>
64
+ ))}
65
+ <div className="py-1">
66
+ <button
67
+ className="ds-plp-facets__header__clear-all border-none bg-transparent hover:border-none hover:bg-transparent
68
+ focus:border-none focus:bg-transparent active:border-none active:bg-transparent active:shadow-none text-sm px-4"
69
+ onClick={() => searchCtx.clearFilters()}
70
+ >
71
+ {translation.Filter.clearAll}
72
+ </button>
73
+ </div>
74
+ </div>
75
+ )}
76
+ </div>
77
+ );
78
+ };
79
+
80
+ export default SelectedFilters;
@@ -0,0 +1,52 @@
1
+ /*
2
+ Copyright 2024 Adobe
3
+ All Rights Reserved.
4
+
5
+ NOTICE: Adobe permits you to use, modify, and distribute this file in
6
+ accordance with the terms of the Adobe license agreement accompanying
7
+ it.
8
+ */
9
+
10
+ const formatRangeLabel = (filter, currencyRate = '1', currencySymbol = '$') => {
11
+ const range = filter.range;
12
+
13
+ const rate = currencyRate;
14
+ const symbol = currencySymbol;
15
+ const label = `${symbol}${
16
+ range?.from && parseFloat(rate) * parseInt(range.from.toFixed(0), 10)
17
+ ? (
18
+ parseFloat(rate) * parseInt(range.from?.toFixed(0), 10)
19
+ )?.toFixed(2)
20
+ : 0
21
+ }${
22
+ range?.to && parseFloat(rate) * parseInt(range.to.toFixed(0), 10)
23
+ ? ` - ${symbol}${(
24
+ parseFloat(rate) * parseInt(range.to.toFixed(0), 10)
25
+ ).toFixed(2)}`
26
+ : ' and above'
27
+ }`;
28
+ return label;
29
+ };
30
+
31
+ const formatBinaryLabel = (filter, option, categoryNames, categoryPath) => {
32
+ if (categoryPath && categoryNames) {
33
+ const category = categoryNames.find(
34
+ facet =>
35
+ facet.attribute === filter.attribute && facet.value === option
36
+ );
37
+
38
+ if (category?.name) {
39
+ return category.name;
40
+ }
41
+ }
42
+
43
+ const title = filter.attribute?.split('_');
44
+ if (option === 'yes') {
45
+ return title.join(' ');
46
+ } else if (option === 'no') {
47
+ return `not ${title.join(' ')}`;
48
+ }
49
+ return option;
50
+ };
51
+
52
+ export { formatBinaryLabel, formatRangeLabel };
@@ -0,0 +1,14 @@
1
+ /*
2
+ Copyright 2024 Adobe
3
+ All Rights Reserved.
4
+
5
+ NOTICE: Adobe permits you to use, modify, and distribute this file in
6
+ accordance with the terms of the Adobe license agreement accompanying
7
+ it.
8
+ */
9
+
10
+ export * from './Facets';
11
+ export { default as SelectedFilters } from './SelectedFilters';
12
+ export * from './Range/RangeFacet';
13
+ export * from './Scalar/ScalarFacet';
14
+ export { Facets as default } from './Facets';
@@ -0,0 +1,119 @@
1
+ /*
2
+ Copyright 2024 Adobe
3
+ All Rights Reserved.
4
+
5
+ NOTICE: Adobe permits you to use, modify, and distribute this file in
6
+ accordance with the terms of the Adobe license agreement accompanying
7
+ it.
8
+ */
9
+
10
+ const colorBuckets = [
11
+ {
12
+ count: 5,
13
+ title: 'Green',
14
+ __typename: 'ScalarBucket'
15
+ },
16
+ {
17
+ count: 4,
18
+ title: 'Black',
19
+ __typename: 'ScalarBucket'
20
+ },
21
+ {
22
+ count: 3,
23
+ title: 'Blue',
24
+ __typename: 'ScalarBucket'
25
+ },
26
+ {
27
+ count: 2,
28
+ title: 'Gray',
29
+ __typename: 'ScalarBucket'
30
+ },
31
+ {
32
+ count: 1,
33
+ title: 'Pink',
34
+ __typename: 'ScalarBucket'
35
+ },
36
+ {
37
+ count: 0,
38
+ title: 'Yellow',
39
+ __typename: 'ScalarBucket'
40
+ }
41
+ ];
42
+
43
+ const sizeBuckets = [
44
+ {
45
+ count: 2,
46
+ title: '32',
47
+ __typename: 'ScalarBucket'
48
+ },
49
+ {
50
+ count: 2,
51
+ title: '33',
52
+ __typename: 'ScalarBucket'
53
+ },
54
+ {
55
+ count: 1,
56
+ title: 'L',
57
+ __typename: 'ScalarBucket'
58
+ }
59
+ ];
60
+
61
+ const priceBuckets = [
62
+ {
63
+ title: '0.0-25.0',
64
+ __typename: 'RangeBucket',
65
+ from: 0,
66
+ to: 25,
67
+ count: 45
68
+ },
69
+ {
70
+ title: '25.0-50.0',
71
+ __typename: 'RangeBucket',
72
+ from: 25,
73
+ to: 50,
74
+ count: 105
75
+ },
76
+ {
77
+ title: '75.0-100.0',
78
+ __typename: 'RangeBucket',
79
+ from: 75,
80
+ to: 100,
81
+ count: 6
82
+ },
83
+ {
84
+ title: '200.0-225.0',
85
+ __typename: 'RangeBucket',
86
+ from: 200,
87
+ to: 225,
88
+ count: 2
89
+ }
90
+ ];
91
+
92
+ export const mockFilters = [
93
+ {
94
+ title: 'Color',
95
+ attribute: 'color',
96
+ buckets: colorBuckets,
97
+ __typename: 'ScalarBucket'
98
+ },
99
+ {
100
+ title: 'Size',
101
+ attribute: 'size',
102
+ buckets: sizeBuckets,
103
+ __typename: 'ScalarBucket'
104
+ }
105
+ ];
106
+
107
+ export const mockColorFilter = {
108
+ title: 'Color',
109
+ attribute: 'color',
110
+ buckets: colorBuckets,
111
+ __typename: 'ScalarBucket'
112
+ };
113
+
114
+ export const mockPriceFilter = {
115
+ title: 'Price',
116
+ attribute: 'price',
117
+ buckets: priceBuckets,
118
+ __typename: 'RangeBucket'
119
+ };
@@ -0,0 +1,49 @@
1
+ @keyframes placeholderShimmer {
2
+ 0% {
3
+ background-position: calc(-100vw + 40px); /* 40px offset */
4
+ }
5
+ 100% {
6
+ background-position: calc(100vw - 40px); /* 40px offset */
7
+ }
8
+ }
9
+
10
+ .shimmer-animation-facet {
11
+ background-color: #f6f7f8;
12
+ background-image: linear-gradient(
13
+ to right,
14
+ #f6f7f8 0%,
15
+ #edeef1 20%,
16
+ #f6f7f8 40%,
17
+ #f6f7f8 100%
18
+ );
19
+ background-repeat: no-repeat;
20
+ background-size: 100vw 4rem;
21
+ animation-duration: 1s;
22
+ animation-fill-mode: forwards;
23
+ animation-iteration-count: infinite;
24
+ animation-name: placeholderShimmer;
25
+ animation-timing-function: linear;
26
+ }
27
+
28
+ .ds-sdk-input__header {
29
+ display: flex;
30
+ justify-content: space-between;
31
+ margin-bottom: 1rem;
32
+ margin-top: 0.75rem;
33
+ }
34
+
35
+ .ds-sdk-input__title {
36
+ height: 2.5rem;
37
+ flex: 0 0 auto;
38
+ width: 50%;
39
+ }
40
+
41
+ .ds-sdk-input__item {
42
+ height: 2rem;
43
+ width: 80%;
44
+ margin-bottom: 0.3125rem;
45
+ }
46
+
47
+ .ds-sdk-input__item:last-child {
48
+ margin-bottom: 0;
49
+ }
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import './FacetsShimmer.css';
3
+
4
+ export const FacetsShimmer = () => {
5
+ return (
6
+ <>
7
+ <div className="ds-sdk-input ds-sdk-input--loading">
8
+ <div className="ds-sdk-input__content">
9
+ <div className="ds-sdk-input__header">
10
+ <div className="ds-sdk-input__title shimmer-animation-facet" />
11
+ </div>
12
+ <div className="ds-sdk-input__list">
13
+ <div className="ds-sdk-input__item shimmer-animation-facet" />
14
+ <div className="ds-sdk-input__item shimmer-animation-facet" />
15
+ <div className="ds-sdk-input__item shimmer-animation-facet" />
16
+ <div className="ds-sdk-input__item shimmer-animation-facet" />
17
+ </div>
18
+ </div>
19
+ </div>
20
+ <div className="ds-sdk-input__border border-t mt-md border-gray-200" />
21
+ </>
22
+ );
23
+ };
24
+
25
+ //export default FacetsShimmer;
@@ -0,0 +1,11 @@
1
+ /*
2
+ Copyright 2024 Adobe
3
+ All Rights Reserved.
4
+
5
+ NOTICE: Adobe permits you to use, modify, and distribute this file in
6
+ accordance with the terms of the Adobe license agreement accompanying
7
+ it.
8
+ */
9
+
10
+ export * from './FacetsShimmer';
11
+ export { FacetsShimmer as default } from './FacetsShimmer';
@@ -0,0 +1,40 @@
1
+ /*
2
+ Copyright 2024 Adobe
3
+ All Rights Reserved.
4
+
5
+ NOTICE: Adobe permits you to use, modify, and distribute this file in
6
+ accordance with the terms of the Adobe license agreement accompanying
7
+ it.
8
+ */
9
+
10
+ import React from 'react';
11
+ import { useTranslation } from '../../context/translation';
12
+ import AdjustmentsIcon from '../../icons/adjustments.svg';
13
+
14
+ export const FilterButton = ({ displayFilter, type, title }) => {
15
+ const translation = useTranslation();
16
+
17
+ return type === 'mobile' ? (
18
+ <div className="ds-sdk-filter-button">
19
+ <button
20
+ className="flex items-center bg-gray-100 ring-black ring-opacity-5 rounded-md p-sm outline outline-gray-200 hover:outline-gray-800 h-[32px]"
21
+ onClick={displayFilter}
22
+ >
23
+ {/* <AdjustmentsIcon className="w-md" /> */}
24
+ <img src={AdjustmentsIcon} className={`w-md`} />
25
+ {translation.Filter.title}
26
+ </button>
27
+ </div>
28
+ ) : (
29
+ <div className="ds-sdk-filter-button-desktop">
30
+ <button
31
+ className="flex items-center bg-gray-100 ring-black ring-opacity-5 rounded-md p-sm text-sm h-[32px]"
32
+ onClick={displayFilter}
33
+ >
34
+ {title}
35
+ </button>
36
+ </div>
37
+ );
38
+ };
39
+
40
+ export default FilterButton;
@@ -0,0 +1,11 @@
1
+ /*
2
+ Copyright 2024 Adobe
3
+ All Rights Reserved.
4
+
5
+ NOTICE: Adobe permits you to use, modify, and distribute this file in
6
+ accordance with the terms of the Adobe license agreement accompanying
7
+ it.
8
+ */
9
+
10
+ export * from './FilterButton';
11
+ export { default as FilterButton } from './FilterButton';
@@ -0,0 +1,34 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import { useIntersectionObserver } from '../../utils/useIntersectionObserver';
3
+
4
+ export const Image = ({
5
+ image,
6
+ alt,
7
+ carouselIndex,
8
+ index,
9
+ }) => {
10
+ const imageRef = useRef(null);
11
+ const [imageUrl, setImageUrl] = useState('');
12
+ const [isVisible, setIsVisible] = useState(false);
13
+ const entry = useIntersectionObserver(imageRef, { rootMargin: '200px' });
14
+
15
+ useEffect(() => {
16
+ if (!entry) return;
17
+
18
+ if (entry?.isIntersecting && index === carouselIndex) {
19
+ setIsVisible(true);
20
+ setImageUrl(entry?.target?.dataset.src || '');
21
+ }
22
+ }, [entry, carouselIndex, index, image]);
23
+
24
+ return (
25
+ <img
26
+ className={`aspect-auto w-100 h-auto ${isVisible ? 'visible' : 'invisible'}`}
27
+ ref={imageRef}
28
+ src={imageUrl}
29
+ data-src={typeof image === 'object' ? image.src : image}
30
+ srcSet={typeof image === 'object' ? image.srcset : null}
31
+ alt={alt}
32
+ />
33
+ );
34
+ };
@@ -0,0 +1,103 @@
1
+ import React, { useState } from 'react';
2
+ import { Image } from './Image';
3
+
4
+ export const ImageCarousel = ({
5
+ images,
6
+ productName,
7
+ carouselIndex,
8
+ setCarouselIndex,
9
+ }) => {
10
+ const [swipeIndex, setSwipeIndex] = useState(0);
11
+
12
+ const cirHandler = (index) => {
13
+ setCarouselIndex(index);
14
+ };
15
+
16
+ const prevHandler = () => {
17
+ if (carouselIndex === 0) {
18
+ setCarouselIndex(0);
19
+ } else {
20
+ setCarouselIndex((prev) => prev - 1);
21
+ }
22
+ };
23
+
24
+ const nextHandler = () => {
25
+ if (carouselIndex === images.length - 1) {
26
+ setCarouselIndex(0);
27
+ } else {
28
+ setCarouselIndex((prev) => prev + 1);
29
+ }
30
+ };
31
+
32
+ return (
33
+ <div className="ds-sdk-product-image-carousel max-h-[250px] max-w-2xl m-auto">
34
+ <div
35
+ className="flex flex-nowrap overflow-hidden relative rounded-lg w-full h-full"
36
+ onTouchStart={(e) => setSwipeIndex(e.touches[0].clientX)}
37
+ onTouchEnd={(e) => {
38
+ const endIndex = e.changedTouches[0].clientX;
39
+ if (swipeIndex > endIndex) {
40
+ nextHandler();
41
+ } else if (swipeIndex < endIndex) {
42
+ prevHandler();
43
+ }
44
+ }}
45
+ >
46
+ <div className="overflow-hidden relative max-w-[200px]">
47
+ <div
48
+ className={`flex transition ease-out duration-40`}
49
+ style={{
50
+ transform: `translateX(-${carouselIndex * 100}%)`,
51
+ }}
52
+ >
53
+ {images.map((item, index) => {
54
+ return (
55
+ <Image
56
+ image={item}
57
+ carouselIndex={carouselIndex}
58
+ index={index}
59
+ key={index}
60
+ alt={productName}
61
+ />
62
+ );
63
+ })}
64
+ </div>
65
+ </div>
66
+ </div>
67
+ {images.length > 1 && (
68
+ <div className="absolute z-1 flex space-x-3 -translate-x-1/2 bottom-0 left-1/2 pb-2 ">
69
+ {images.map((_item, index) => {
70
+ return (
71
+ <span
72
+ key={index}
73
+ style={
74
+ carouselIndex === index
75
+ ? {
76
+ width: `12px`,
77
+ height: `12px`,
78
+ borderRadius: `50%`,
79
+ border: `1px solid black`,
80
+ cursor: `pointer`,
81
+ backgroundColor: `#252525`,
82
+ }
83
+ : {
84
+ width: `12px`,
85
+ height: `12px`,
86
+ borderRadius: `50%`,
87
+ border: `1px solid silver`,
88
+ cursor: `pointer`,
89
+ backgroundColor: `silver`,
90
+ }
91
+ }
92
+ onClick={(e) => {
93
+ e.preventDefault();
94
+ cirHandler(index);
95
+ }}
96
+ />
97
+ );
98
+ })}
99
+ </div>
100
+ )}
101
+ </div>
102
+ );
103
+ };
@@ -0,0 +1,11 @@
1
+ /*
2
+ Copyright 2024 Adobe
3
+ All Rights Reserved.
4
+
5
+ NOTICE: Adobe permits you to use, modify, and distribute this file in
6
+ accordance with the terms of the Adobe license agreement accompanying
7
+ it.
8
+ */
9
+
10
+ export * from './ImageCarousel';
11
+ export { ImageCarousel as default } from './ImageCarousel';