@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,103 @@
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, { useEffect, useState } from 'react';
11
+
12
+ import '../Slider/Slider.css';
13
+
14
+ import { useProducts, useSearch } from '../../context';
15
+ import useSliderFacet from '../../hooks/useSliderFacet';
16
+
17
+ export const Slider = ({ filterData }) => {
18
+ const productsCtx = useProducts();
19
+ const [isFirstRender, setIsFirstRender] = useState(true);
20
+ const preSelectedToPrice = productsCtx.variables.filter?.find(
21
+ (obj) => obj.attribute === 'price'
22
+ )?.range?.to;
23
+
24
+ const searchCtx = useSearch();
25
+
26
+ const [selectedPrice, setSelectedPrice] = useState(
27
+ !preSelectedToPrice
28
+ ? filterData.buckets[filterData.buckets.length - 1].to
29
+ : preSelectedToPrice
30
+ );
31
+
32
+ const { onChange } = useSliderFacet(filterData);
33
+
34
+ useEffect(() => {
35
+ if (
36
+ searchCtx?.filters?.length === 0 ||
37
+ !searchCtx?.filters?.find((obj) => obj.attribute === 'price')
38
+ ) {
39
+ setSelectedPrice(filterData.buckets[filterData.buckets.length - 1].to);
40
+ }
41
+ }, [searchCtx]);
42
+
43
+ useEffect(() => {
44
+ if (!isFirstRender) {
45
+ setSelectedPrice(filterData.buckets[filterData.buckets.length - 1].to);
46
+ }
47
+ setIsFirstRender(false);
48
+ }, [filterData.buckets]);
49
+
50
+ const handleSliderChange = (event) => {
51
+ onChange(filterData.buckets[0].from, parseInt(event.target.value, 10));
52
+ };
53
+
54
+ const handleNewPrice = (event) => {
55
+ setSelectedPrice(parseInt(event.target.value, 10));
56
+ };
57
+
58
+ const formatLabel = (price) => {
59
+ const currencyRate = productsCtx.currencyRate || '1';
60
+ const currencySymbol = productsCtx.currencySymbol || '$';
61
+
62
+ const value =
63
+ price && parseFloat(currencyRate) * parseInt(price.toFixed(0), 10)
64
+ ? (parseFloat(currencyRate) * parseInt(price.toFixed(0), 10)).toFixed(2)
65
+ : 0;
66
+
67
+ return `${currencySymbol}${value}`;
68
+ };
69
+
70
+ return (
71
+ <>
72
+ <p className="pt-md">{filterData.title}</p>
73
+ <div className="ds-sdk-slider slider-container">
74
+ <input
75
+ type="range"
76
+ id="price-range"
77
+ className="w-full h-3 bg-gray-200 rounded-lg appearance-none cursor-pointer range-lg dark:bg-gray-700"
78
+ min={filterData.buckets[0].from}
79
+ max={filterData.buckets[filterData.buckets.length - 1].to}
80
+ value={selectedPrice}
81
+ onChange={handleNewPrice}
82
+ onMouseUp={handleSliderChange}
83
+ onTouchEnd={handleSliderChange}
84
+ onKeyUp={handleSliderChange}
85
+ />
86
+ <span className="selected-price">{formatLabel(selectedPrice)}</span>
87
+ <div className="price-range-display">
88
+ <span className="min-price">
89
+ {formatLabel(filterData.buckets[0].from)}
90
+ </span>
91
+ <span className="max-price">
92
+ {formatLabel(
93
+ filterData.buckets[filterData.buckets.length - 1].to
94
+ )}
95
+ </span>
96
+ </div>
97
+ </div>
98
+ <div className="ds-sdk-input__border border-t mt-md border-gray-200" />
99
+ </>
100
+ );
101
+ };
102
+
103
+ export default Slider;
@@ -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 './Slider';
11
+ export { default } from './Slider';
@@ -0,0 +1,83 @@
1
+ .range_container {
2
+ display: flex;
3
+ flex-direction: column;
4
+ width: auto;
5
+ margin-top: 10px;
6
+ margin-bottom: 20px;
7
+ }
8
+
9
+ .sliders_control {
10
+ position: relative;
11
+ }
12
+
13
+ .form_control {
14
+ display: none;
15
+ }
16
+
17
+ input[type='range']::-webkit-slider-thumb {
18
+ -webkit-appearance: none;
19
+ pointer-events: all;
20
+ width: 12px;
21
+ height: 12px;
22
+ background-color: #383838;
23
+ border-radius: 50%;
24
+ box-shadow: 0 0 0 1px #c6c6c6;
25
+ cursor: pointer;
26
+ }
27
+
28
+ input[type='range']::-moz-range-thumb {
29
+ -webkit-appearance: none;
30
+ pointer-events: all;
31
+ width: 12px;
32
+ height: 12px;
33
+ background-color: #383838;
34
+ border-radius: 50%;
35
+ box-shadow: 0 0 0 1px #c6c6c6;
36
+ cursor: pointer;
37
+ }
38
+
39
+ input[type='range']::-webkit-slider-thumb:hover {
40
+ background: #383838;
41
+ }
42
+
43
+ input[type='number'] {
44
+ color: #8a8383;
45
+ width: 50px;
46
+ height: 30px;
47
+ font-size: 20px;
48
+ border: none;
49
+ }
50
+
51
+ input[type='number']::-webkit-inner-spin-button,
52
+ input[type='number']::-webkit-outer-spin-button {
53
+ opacity: 1;
54
+ }
55
+
56
+ input[type='range'] {
57
+ -webkit-appearance: none;
58
+ appearance: none;
59
+ height: 2px;
60
+ width: 100%;
61
+ position: absolute;
62
+ background-color: #c6c6c6;
63
+ pointer-events: none;
64
+ }
65
+
66
+ .fromSlider {
67
+ height: 0;
68
+ z-index: 1;
69
+ }
70
+
71
+ .toSlider {
72
+ z-index: 2;
73
+ }
74
+
75
+ .price-range-display {
76
+ text-wrap: nowrap;
77
+ font-size: 0.8em;
78
+ }
79
+
80
+ .fromSlider,
81
+ .toSlider {
82
+ box-shadow: none !important;
83
+ }
@@ -0,0 +1,220 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import './SliderDoubleControl.css';
3
+
4
+ import { useProducts, useSearch } from '../../context';
5
+ import useSliderFacet from '../../hooks/useSliderFacet';
6
+
7
+ export const SliderDoubleControl = ({ filterData }) => {
8
+ const productsCtx = useProducts();
9
+ const searchCtx = useSearch();
10
+ const min = filterData.buckets[0].from;
11
+ const max = filterData.buckets[filterData.buckets.length - 1].to;
12
+
13
+ const preSelectedToPrice = productsCtx.variables.filter?.find(
14
+ obj => obj.attribute === 'price'
15
+ )?.range?.to;
16
+ const preSelectedFromPrice = productsCtx.variables.filter?.find(
17
+ obj => obj.attribute === 'price'
18
+ )?.range?.from;
19
+
20
+ const [minVal, setMinVal] = useState(
21
+ preSelectedFromPrice !== null && preSelectedFromPrice !== undefined ? preSelectedFromPrice : min
22
+ );
23
+ const [maxVal, setMaxVal] = useState(
24
+ preSelectedToPrice !== null && preSelectedToPrice !== undefined ? preSelectedToPrice : max
25
+ );
26
+
27
+ const { onChange } = useSliderFacet(filterData);
28
+
29
+ const fromSliderId = `fromSlider_${filterData.attribute}`;
30
+ const toSliderId = `toSlider_${filterData.attribute}`;
31
+ const fromInputId = `fromInput_${filterData.attribute}`;
32
+ const toInputId = `toInput_${filterData.attribute}`;
33
+
34
+ useEffect(() => {
35
+ if (
36
+ searchCtx?.filters?.length === 0 ||
37
+ !searchCtx?.filters?.find(obj => obj.attribute === filterData.attribute)
38
+ ) {
39
+ setMinVal(min);
40
+ setMaxVal(max);
41
+ }
42
+ }, [searchCtx]);
43
+
44
+ useEffect(() => {
45
+ const getParsed = (fromEl, toEl) => [
46
+ parseInt(fromEl.value, 10),
47
+ parseInt(toEl.value, 10)
48
+ ];
49
+
50
+ const fillSlider = (from, to, sliderColor, rangeColor, controlSlider) => {
51
+ const rangeDistance = to.max - to.min;
52
+ const fromPosition = from.value - to.min;
53
+ const toPosition = to.value - to.min;
54
+ controlSlider.style.background = `linear-gradient(
55
+ to right,
56
+ ${sliderColor} 0%,
57
+ ${sliderColor} ${(fromPosition / rangeDistance) * 100}%,
58
+ ${rangeColor} ${(fromPosition / rangeDistance) * 100}%,
59
+ ${rangeColor} ${(toPosition / rangeDistance) * 100}%,
60
+ ${sliderColor} ${(toPosition / rangeDistance) * 100}%,
61
+ ${sliderColor} 100%)`;
62
+ };
63
+
64
+ const controlFromSlider = (fromSlider, toSlider, fromInput) => {
65
+ const [from, to] = getParsed(fromSlider, toSlider);
66
+ fillSlider(fromSlider, toSlider, '#C6C6C6', '#383838', toSlider);
67
+ if (from > to) {
68
+ setMinVal(to);
69
+ fromSlider.value = to;
70
+ fromInput.value = to;
71
+ } else {
72
+ fromInput.value = from;
73
+ }
74
+ };
75
+
76
+ const controlToSlider = (fromSlider, toSlider, toInput) => {
77
+ const [from, to] = getParsed(fromSlider, toSlider);
78
+ fillSlider(fromSlider, toSlider, '#C6C6C6', '#383838', toSlider);
79
+ if (from <= to) {
80
+ toSlider.value = to;
81
+ toInput.value = to;
82
+ } else {
83
+ setMaxVal(from);
84
+ toInput.value = from;
85
+ toSlider.value = from;
86
+ }
87
+ };
88
+
89
+ const controlFromInput = (fromSlider, fromInput, toInput, controlSlider) => {
90
+ const [from, to] = getParsed(fromInput, toInput);
91
+ fillSlider(fromInput, toInput, '#C6C6C6', '#383838', controlSlider);
92
+ if (from > to) {
93
+ fromSlider.value = to;
94
+ fromInput.value = to;
95
+ } else {
96
+ fromSlider.value = from;
97
+ }
98
+ };
99
+
100
+ const controlToInput = (toSlider, fromInput, toInput, controlSlider) => {
101
+ const [from, to] = getParsed(fromInput, toInput);
102
+ fillSlider(fromInput, toInput, '#C6C6C6', '#383838', controlSlider);
103
+ if (from <= to) {
104
+ toSlider.value = to;
105
+ toInput.value = to;
106
+ } else {
107
+ toInput.value = from;
108
+ }
109
+ };
110
+
111
+ const fromSlider = document.getElementById(fromSliderId);
112
+ const toSlider = document.getElementById(toSliderId);
113
+ const fromInput = document.getElementById(fromInputId);
114
+ const toInput = document.getElementById(toInputId);
115
+
116
+ if (!fromSlider || !toSlider || !fromInput || !toInput) return;
117
+
118
+ fillSlider(fromSlider, toSlider, '#C6C6C6', '#383838', toSlider);
119
+
120
+ fromSlider.oninput = () => controlFromSlider(fromSlider, toSlider, fromInput);
121
+ toSlider.oninput = () => controlToSlider(fromSlider, toSlider, toInput);
122
+ fromInput.oninput = () =>
123
+ controlFromInput(fromSlider, fromInput, toInput, toSlider);
124
+ toInput.oninput = () =>
125
+ controlToInput(toSlider, fromInput, toInput, toSlider);
126
+ }, [minVal, maxVal]);
127
+
128
+ const formatLabel = (price) => {
129
+ const currencyRate = productsCtx.currencyRate || 1;
130
+ const currencySymbol = productsCtx.currencySymbol || '$';
131
+ const label = `${currencySymbol}${
132
+ price ? (parseFloat(currencyRate) * parseInt(price.toFixed(0), 10)).toFixed(2) : 0
133
+ }`;
134
+ return label;
135
+ };
136
+
137
+ return (
138
+ <div className="ds-sdk-input pt-md">
139
+ <label className="ds-sdk-input__label text-base font-normal text-gray-900">
140
+ {filterData.title}
141
+ </label>
142
+
143
+ <div className="ds-sdk-slider range_container">
144
+ <div className="sliders_control">
145
+ <input
146
+ className="ds-sdk-slider__from fromSlider"
147
+ id={fromSliderId}
148
+ type="range"
149
+ value={minVal}
150
+ min={min}
151
+ max={max}
152
+ onInput={({ target }) => setMinVal(Math.round(Number(target.value)))}
153
+ onMouseUp={() => onChange(minVal, maxVal)}
154
+ onTouchEnd={() => onChange(minVal, maxVal)}
155
+ onKeyUp={() => onChange(minVal, maxVal)}
156
+ />
157
+ <input
158
+ className="ds-sdk-slider__to toSlider"
159
+ id={toSliderId}
160
+ type="range"
161
+ value={maxVal}
162
+ min={min}
163
+ max={max}
164
+ onInput={({ target }) => setMaxVal(Math.round(Number(target.value)))}
165
+ onMouseUp={() => onChange(minVal, maxVal)}
166
+ onTouchEnd={() => onChange(minVal, maxVal)}
167
+ onKeyUp={() => onChange(minVal, maxVal)}
168
+ />
169
+ </div>
170
+
171
+ <div className="form_control">
172
+ <div className="form_control_container">
173
+ <div className="form_control_container__time">Min</div>
174
+ <input
175
+ className="form_control_container__time__input"
176
+ type="number"
177
+ id={fromInputId}
178
+ value={minVal}
179
+ min={min}
180
+ max={max}
181
+ onInput={({ target }) => setMinVal(Math.round(Number(target.value)))}
182
+ onMouseUp={() => onChange(minVal, maxVal)}
183
+ onTouchEnd={() => onChange(minVal, maxVal)}
184
+ onKeyUp={() => onChange(minVal, maxVal)}
185
+ />
186
+ </div>
187
+ <div className="form_control_container">
188
+ <div className="form_control_container__time">Max</div>
189
+ <input
190
+ className="form_control_container__time__input"
191
+ type="number"
192
+ id={toInputId}
193
+ value={maxVal}
194
+ min={min}
195
+ max={max}
196
+ onInput={({ target }) => setMaxVal(Math.round(Number(target.value)))}
197
+ onMouseUp={() => onChange(minVal, maxVal)}
198
+ onTouchEnd={() => onChange(minVal, maxVal)}
199
+ onKeyUp={() => onChange(minVal, maxVal)}
200
+ />
201
+ </div>
202
+ </div>
203
+ </div>
204
+
205
+ <div className={`price-range-display__${filterData.attribute} pb-3`}>
206
+ <span className="ml-sm block-display text-sm font-light text-gray-700">
207
+ Between{' '}
208
+ <span className="min-price text-gray-900 font-semibold">
209
+ {formatLabel(minVal)}
210
+ </span>{' '}
211
+ and{' '}
212
+ <span className="max-price text-gray-900 font-semibold">
213
+ {formatLabel(maxVal)}
214
+ </span>
215
+ </span>
216
+ </div>
217
+ <div className="ds-sdk-input__border border-t mt-md border-gray-200" />
218
+ </div>
219
+ );
220
+ };
@@ -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 './SliderDoubleControl';
11
+ export { SliderDoubleControl as default } from './SliderDoubleControl';
@@ -0,0 +1,126 @@
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, { useEffect, useRef } from 'react';
11
+ import { useTranslation } from '../../context/translation';
12
+ import { useAccessibleDropdown } from '../../hooks/useAccessibleDropdown';
13
+ import Chevron from '../../icons/chevron.svg';
14
+ import SortIcon from '../../icons/sort.svg';
15
+
16
+ export const SortDropdown = ({ value, sortOptions, onChange }) => {
17
+ const sortOptionButton = useRef(null);
18
+ const sortOptionMenu = useRef(null);
19
+
20
+ const selectedOption = sortOptions.find(e => e.value === value);
21
+
22
+ const translation = useTranslation();
23
+ const sortOptionTranslation = translation.SortDropdown.option;
24
+ const sortOption = sortOptionTranslation.replace(
25
+ '{selectedOption}',
26
+ `${selectedOption?.label}`
27
+ );
28
+
29
+ const {
30
+ isDropdownOpen,
31
+ setIsDropdownOpen,
32
+ activeIndex,
33
+ setActiveIndex,
34
+ select,
35
+ setIsFocus,
36
+ listRef
37
+ } = useAccessibleDropdown({
38
+ options: sortOptions,
39
+ value,
40
+ onChange
41
+ });
42
+
43
+ useEffect(() => {
44
+ const menuRef = sortOptionMenu.current;
45
+
46
+ const handleBlur = () => {
47
+ setIsFocus(false);
48
+ setIsDropdownOpen(false);
49
+ };
50
+
51
+ const handleFocus = () => {
52
+ if (menuRef?.parentElement?.querySelector(':hover') !== menuRef) {
53
+ setIsFocus(false);
54
+ setIsDropdownOpen(false);
55
+ }
56
+ };
57
+
58
+ menuRef?.addEventListener('blur', handleBlur);
59
+ menuRef?.addEventListener('focusin', handleFocus);
60
+ menuRef?.addEventListener('focusout', handleFocus);
61
+
62
+ return () => {
63
+ menuRef?.removeEventListener('blur', handleBlur);
64
+ menuRef?.removeEventListener('focusin', handleFocus);
65
+ menuRef?.removeEventListener('focusout', handleFocus);
66
+ };
67
+ }, [sortOptionMenu]);
68
+
69
+ return (
70
+ <div
71
+ ref={sortOptionMenu}
72
+ className="ds-sdk-sort-dropdown relative inline-block text-left bg-gray-100 rounded-md outline outline-1 outline-gray-200 hover:outline-gray-600 h-[32px] z-99"
73
+ >
74
+ <button
75
+ className="group flex justify-center items-center font-normal text-sm text-gray-700 rounded-md hover:cursor-pointer border-none bg-transparent hover:border-none hover:bg-transparent focus:border-none focus:bg-transparent active:border-none active:bg-transparent active:shadow-none h-full w-full px-sm"
76
+ ref={sortOptionButton}
77
+ onClick={() => setIsDropdownOpen(!isDropdownOpen)}
78
+ onFocus={() => setIsFocus(false)}
79
+ onBlur={() => setIsFocus(false)}
80
+ >
81
+ <img src={SortIcon} className="h-md w-md mr-sm stroke-gray-600 m-auto" height="32px" width="32px" />
82
+ {/* <SortIcon className="h-md w-md mr-sm stroke-gray-600 m-auto" /> */}
83
+ {selectedOption ? sortOption : translation.SortDropdown.title}
84
+ <img src={Chevron} className={`flex-shrink-0 m-auto ml-sm h-md w-md stroke-1 stroke-gray-600 ${
85
+ isDropdownOpen ? '' : 'rotate-180'
86
+ }`} height="32px" width="32px" />
87
+ {/* <Chevron
88
+ className={`flex-shrink-0 m-auto ml-sm h-md w-md stroke-1 stroke-gray-600 ${
89
+ isDropdownOpen ? '' : 'rotate-180'
90
+ }`}
91
+ /> */}
92
+ </button>
93
+ {isDropdownOpen && (
94
+ <ul
95
+ ref={listRef}
96
+ tabIndex={-1}
97
+ className="ds-sdk-sort-dropdown__items origin-top-right absolute hover:cursor-pointer right-0 w-full rounded-md shadow-2xl bg-white ring-1 ring-black ring-opacity-5 focus:outline-none mt-2 z-20"
98
+ >
99
+ {sortOptions.map((option, i) => (
100
+ <li
101
+ key={i}
102
+ aria-selected={option.value === selectedOption?.value}
103
+ onMouseOver={() => setActiveIndex(i)}
104
+ className={`py-xs hover:bg-gray-100 hover:text-gray-900 ${
105
+ i === activeIndex ? 'bg-gray-100 text-gray-900' : ''
106
+ }`}
107
+ >
108
+ <a
109
+ className={`ds-sdk-sort-dropdown__items--item block-display px-md py-sm text-sm mb-0
110
+ no-underline active:no-underline focus:no-underline hover:no-underline
111
+ hover:text-gray-900 ${
112
+ option.value === selectedOption?.value
113
+ ? 'ds-sdk-sort-dropdown__items--item-selected font-semibold text-gray-900'
114
+ : 'font-normal text-gray-800'
115
+ }`}
116
+ onClick={() => select(option.value)}
117
+ >
118
+ {option.label}
119
+ </a>
120
+ </li>
121
+ ))}
122
+ </ul>
123
+ )}
124
+ </div>
125
+ );
126
+ };
@@ -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 './SortDropdown';
11
+ export { SortDropdown as default } from './SortDropdown';
@@ -0,0 +1,72 @@
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
+ export const SwatchButton = ({ id, value, type, checked, onClick }) => {
13
+ const outlineColor = checked
14
+ ? 'border-black'
15
+ : type === 'COLOR_HEX'
16
+ ? 'border-transparent'
17
+ : 'border-gray';
18
+
19
+ if (type === 'COLOR_HEX') {
20
+ const color = value.toLowerCase();
21
+ const className = `min-w-[32px] rounded-full p-sm border border-[1.5px] ${outlineColor} h-[32px] outline-transparent`;
22
+ const isWhite = color === '#ffffff' || color === '#fff';
23
+ return (
24
+ <div className={`ds-sdk-swatch-button_${id}`}>
25
+ <button
26
+ key={id}
27
+ className={className}
28
+ style={{
29
+ backgroundColor: color,
30
+ border: !checked && isWhite ? '1px solid #ccc' : undefined,
31
+ }}
32
+ onClick={onClick}
33
+ aria-checked={checked}
34
+ />
35
+ </div>
36
+ );
37
+ }
38
+
39
+ if (type === 'IMAGE' && value) {
40
+ const className = `object-cover object-center min-w-[32px] rounded-full p-sm border border-[1.5px] ${outlineColor} h-[32px] outline-transparent`;
41
+ const style = {
42
+ background: `url(${value}) no-repeat center`,
43
+ backgroundSize: 'initial',
44
+ };
45
+ return (
46
+ <div className={`ds-sdk-swatch-button_${value}`}>
47
+ <button
48
+ key={id}
49
+ className={className}
50
+ style={style}
51
+ onClick={onClick}
52
+ aria-checked={checked}
53
+ />
54
+ </div>
55
+ );
56
+ }
57
+
58
+ // Assume TEXT type
59
+ const className = `flex items-center bg-white rounded-full p-sm border border-[1.5px]h-[32px] ${outlineColor} outline-transparent`;
60
+ return (
61
+ <div className={`ds-sdk-swatch-button_${value}`}>
62
+ <button
63
+ key={id}
64
+ className={className}
65
+ onClick={onClick}
66
+ aria-checked={checked}
67
+ >
68
+ {value}
69
+ </button>
70
+ </div>
71
+ );
72
+ };
@@ -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 './SwatchButton';
11
+ export { SwatchButton as default } from './SwatchButton';