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

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,120 @@
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, { useState } from 'react';
11
+
12
+ import { useProducts, useTranslation } from '../../context';
13
+ import PlusIcon from '../../icons/plus.svg';
14
+ import { BOOLEAN_NO, BOOLEAN_YES } from '../../utils/constants';
15
+ import { LabelledInput } from '../LabelledInput';
16
+
17
+ const numberOfOptionsShown = 5;
18
+
19
+ const InputButtonGroup = ({
20
+ title,
21
+ attribute,
22
+ buckets,
23
+ isSelected,
24
+ onChange,
25
+ type,
26
+ inputGroupTitleSlot
27
+ }) => {
28
+ const translation = useTranslation();
29
+ const productsCtx = useProducts();
30
+
31
+ const [showMore, setShowMore] = useState(buckets.length < numberOfOptionsShown);
32
+
33
+ const numberOfOptions = showMore ? buckets.length : numberOfOptionsShown;
34
+
35
+ const onInputChange = (title, e) => {
36
+ onChange({
37
+ value: title,
38
+ selected: e?.target?.checked
39
+ });
40
+ };
41
+
42
+ const formatLabel = (title, bucket) => {
43
+ if (bucket.__typename === 'RangeBucket') {
44
+ const currencyRate = productsCtx.currencyRate || '1';
45
+ const currencySymbol = productsCtx.currencySymbol || '$';
46
+ const from = bucket?.from
47
+ ? (parseFloat(currencyRate) * parseInt(bucket.from.toFixed(0), 10)).toFixed(2)
48
+ : 0;
49
+ const to = bucket?.to
50
+ ? ` - ${currencySymbol}${(
51
+ parseFloat(currencyRate) * parseInt(bucket.to.toFixed(0), 10)
52
+ ).toFixed(2)}`
53
+ : translation.InputButtonGroup.priceRange;
54
+ return `${currencySymbol}${from}${to}`;
55
+ } else if (bucket.__typename === 'CategoryView') {
56
+ return productsCtx.categoryPath ? (bucket.name !== undefined && bucket.name !== null ? bucket.name : bucket.title) : bucket.title;
57
+ } else if (bucket.title === BOOLEAN_YES) {
58
+ return title;
59
+ } else if (bucket.title === BOOLEAN_NO) {
60
+ const excludedMessageTranslation = translation.InputButtonGroup.priceExcludedMessage;
61
+ const excludedMessage = excludedMessageTranslation.replace('{title}', `${title}`);
62
+ return excludedMessage;
63
+ }
64
+ return bucket.title;
65
+ };
66
+
67
+ return (
68
+ <div className="ds-sdk-input pt-md">
69
+ {inputGroupTitleSlot ? (
70
+ inputGroupTitleSlot(title)
71
+ ) : (
72
+ <label className="ds-sdk-input__label text-base font-normal text-gray-900">
73
+ {title}
74
+ </label>
75
+ )}
76
+ <fieldset className="ds-sdk-input__options mt-md">
77
+ <div className="space-y-4">
78
+ {buckets.slice(0, numberOfOptions).map((option) => {
79
+ if (!option.title) {
80
+ return null;
81
+ }
82
+ const checked = isSelected(option.title);
83
+ const noShowPriceBucketCount = option.__typename === 'RangeBucket';
84
+ return (
85
+ <LabelledInput
86
+ key={formatLabel(title, option)}
87
+ name={`${option.title}-${attribute}`}
88
+ attribute={attribute}
89
+ label={formatLabel(title, option)}
90
+ checked={!!checked}
91
+ value={option.title}
92
+ count={noShowPriceBucketCount ? null : option.count}
93
+ onChange={(e) => onInputChange(option.title, e)}
94
+ type={type}
95
+ />
96
+ );
97
+ })}
98
+ {!showMore && buckets.length > numberOfOptionsShown && (
99
+ <div
100
+ className="ds-sdk-input__fieldset__show-more flex items-center text-gray-700 cursor-pointer"
101
+ onClick={() => setShowMore(true)}
102
+ >
103
+ <img src={PlusIcon} height="16px" width="16px" className="h-md w-md fill-gray-500" />
104
+ {/* <PlusIcon className="h-md w-md fill-gray-500" /> */}
105
+ <button
106
+ type="button"
107
+ className="ml-sm font-light 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 text-sm"
108
+ >
109
+ {translation.InputButtonGroup.showmore}
110
+ </button>
111
+ </div>
112
+ )}
113
+ </div>
114
+ </fieldset>
115
+ <div className="ds-sdk-input__border border-t mt-md border-gray-200" />
116
+ </div>
117
+ );
118
+ };
119
+
120
+ export default InputButtonGroup;
@@ -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 './InputButtonGroup';
11
+ export { default as InputButtonGroup } from './InputButtonGroup';
@@ -0,0 +1,51 @@
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 LabelledInput = ({
13
+ type,
14
+ checked,
15
+ onChange,
16
+ name,
17
+ label,
18
+ attribute,
19
+ value,
20
+ count,
21
+ }) => {
22
+ return (
23
+ <div className="ds-sdk-labelled-input flex items-center">
24
+ <input
25
+ id={name}
26
+ name={
27
+ type === 'checkbox'
28
+ ? `checkbox-group-${attribute}`
29
+ : `radio-group-${attribute}`
30
+ }
31
+ type={type}
32
+ className="ds-sdk-labelled-input__input focus:ring-0 h-md w-md border-0 cursor-pointer accent-gray-600 min-w-[16px]"
33
+ checked={checked}
34
+ aria-checked={checked}
35
+ onChange={onChange}
36
+ value={value}
37
+ />
38
+ <label
39
+ htmlFor={name}
40
+ className="ds-sdk-labelled-input__label ml-sm block-display text-sm font-light text-gray-700 cursor-pointer"
41
+ >
42
+ {label}
43
+ {count != null && (
44
+ <span className="text-[12px] font-light text-gray-700 ml-1">
45
+ {`(${count})`}
46
+ </span>
47
+ )}
48
+ </label>
49
+ </div>
50
+ );
51
+ };
@@ -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 './LabelledInput';
11
+ export { LabelledInput as default } from './LabelledInput';
@@ -0,0 +1,32 @@
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 LoadingIcon from '../../icons/loading.svg';
12
+
13
+ export const Loading = ({ label }) => {
14
+ const isMobile = typeof window !== 'undefined' &&
15
+ window.matchMedia('only screen and (max-width: 768px)').matches;
16
+
17
+ return (
18
+ <div
19
+ className={`ds-sdk-loading flex h-screen justify-center items-center ${
20
+ isMobile ? 'loading-spinner-on-mobile' : ''
21
+ }`}
22
+ >
23
+ <div className="ds-sdk-loading__spinner bg-gray-100 rounded-full p-xs flex w-fit my-lg outline-gray-200">
24
+ {/* <LoadingIcon className="inline-block mr-xs ml-xs w-md animate-spin fill-primary" /> */}
25
+ <img src={LoadingIcon} className={`inline-block mr-xs ml-xs w-md animate-spin fill-primary`} />
26
+ <span className="ds-sdk-loading__spinner-label p-xs">{label}</span>
27
+ </div>
28
+ </div>
29
+ );
30
+ };
31
+
32
+ export default Loading;
@@ -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 './Loading';
11
+ export { default } from './Loading';
@@ -0,0 +1,55 @@
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 NoResults = ({ heading, subheading, isError }) => {
13
+ return (
14
+ <div className="ds-sdk-no-results__page">
15
+ {isError ? (
16
+ <svg
17
+ xmlns="http://www.w3.org/2000/svg"
18
+ className="h-28 w-28 my-0 mx-auto stroke-gray-400"
19
+ fill="none"
20
+ viewBox="0 0 24 24"
21
+ stroke="currentColor"
22
+ strokeWidth="2"
23
+ >
24
+ <path
25
+ strokeLinecap="round"
26
+ strokeLinejoin="round"
27
+ d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
28
+ />
29
+ </svg>
30
+ ) : (
31
+ <svg
32
+ xmlns="http://www.w3.org/2000/svg"
33
+ className="h-28 w-28 my-0 mx-auto stroke-gray-400"
34
+ fill="none"
35
+ viewBox="0 0 24 24"
36
+ strokeWidth="1.5"
37
+ >
38
+ <path
39
+ strokeLinecap="round"
40
+ strokeLinejoin="round"
41
+ d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
42
+ />
43
+ </svg>
44
+ )}
45
+ <h1 className="ds-sdk-no-results__page__heading text-2xl text-center text-gray-600">
46
+ {heading}
47
+ </h1>
48
+ <h3 className="ds-sdk-no-results__page__subheading text-lg text-center text-gray-500">
49
+ {subheading}
50
+ </h3>
51
+ </div>
52
+ );
53
+ };
54
+
55
+ export default NoResults;
@@ -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 './NoResults';
11
+ export { default } from './NoResults';
@@ -0,0 +1,105 @@
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 } from 'react';
11
+
12
+ import { useProducts } from '../../context';
13
+ import { ELLIPSIS, usePagination } from '../../hooks/usePagination';
14
+ import Chevron from '../../icons/chevron.svg';
15
+
16
+ export const Pagination = ({ onPageChange, totalPages, currentPage }) => {
17
+ const productsCtx = useProducts();
18
+ const paginationRange = usePagination({
19
+ currentPage,
20
+ totalPages,
21
+ });
22
+
23
+ useEffect(() => {
24
+ const { currentPage: ctxCurrentPage, totalPages: ctxTotalPages } = productsCtx;
25
+ if (ctxCurrentPage > ctxTotalPages) {
26
+ onPageChange(ctxTotalPages);
27
+ }
28
+ }, []); // Only run on mount
29
+
30
+ const onPrevious = () => {
31
+ if (currentPage > 1) {
32
+ onPageChange(currentPage - 1);
33
+ }
34
+ };
35
+
36
+ const onNext = () => {
37
+ if (currentPage < totalPages) {
38
+ onPageChange(currentPage + 1);
39
+ }
40
+ };
41
+
42
+ return (
43
+ <ul className="ds-plp-pagination flex justify-center items-center mt-2 mb-6 list-none">
44
+ <img src={Chevron} className={`h-sm w-sm transform rotate-90 ${
45
+ currentPage === 1
46
+ ? 'stroke-gray-400 cursor-not-allowed'
47
+ : 'stroke-gray-600 cursor-pointer'
48
+ }`}
49
+ onClick={onPrevious}
50
+ />
51
+ {/* <Chevron
52
+ className={`h-sm w-sm transform rotate-90 ${
53
+ currentPage === 1
54
+ ? 'stroke-gray-400 cursor-not-allowed'
55
+ : 'stroke-gray-600 cursor-pointer'
56
+ }`}
57
+ onClick={onPrevious}
58
+ /> */}
59
+
60
+ {paginationRange?.map((page) => {
61
+ if (page === ELLIPSIS) {
62
+ return (
63
+ <li
64
+ key={`ellipsis-${Math.random()}`}
65
+ className="ds-plp-pagination__dots text-gray-500 mx-sm my-auto"
66
+ >
67
+ ...
68
+ </li>
69
+ );
70
+ }
71
+
72
+ return (
73
+ <li
74
+ key={page}
75
+ className={`ds-plp-pagination__item flex items-center cursor-pointer text-center text-gray-500 my-auto mx-sm ${
76
+ currentPage === page
77
+ ? 'ds-plp-pagination__item--current text-black font-medium underline underline-offset-4 decoration-black'
78
+ : ''
79
+ }`}
80
+ onClick={() => onPageChange(page)}
81
+ >
82
+ {page}
83
+ </li>
84
+ );
85
+ })}
86
+ <img src={Chevron} className={`h-sm w-sm transform -rotate-90 ${
87
+ currentPage === totalPages
88
+ ? 'stroke-gray-400 cursor-not-allowed'
89
+ : 'stroke-gray-600 cursor-pointer'
90
+ }`}
91
+ onClick={onNext}
92
+ />
93
+ {/* <Chevron
94
+ className={`h-sm w-sm transform -rotate-90 ${
95
+ currentPage === totalPages
96
+ ? 'stroke-gray-400 cursor-not-allowed'
97
+ : 'stroke-gray-600 cursor-pointer'
98
+ }`}
99
+ onClick={onNext}
100
+ /> */}
101
+ </ul>
102
+ );
103
+ };
104
+
105
+ export default Pagination;
@@ -0,0 +1,10 @@
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 './Pagination';
@@ -0,0 +1,114 @@
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 { useAccessibleDropdown } from '../../hooks/useAccessibleDropdown';
12
+ import Chevron from '../../icons/chevron.svg';
13
+
14
+ export const PerPagePicker = ({ value, pageSizeOptions, onChange }) => {
15
+ const pageSizeButton = useRef(null);
16
+ const pageSizeMenu = useRef(null);
17
+
18
+ const selectedOption = pageSizeOptions.find((e) => e.value === value);
19
+
20
+ const {
21
+ isDropdownOpen,
22
+ setIsDropdownOpen,
23
+ activeIndex,
24
+ setActiveIndex,
25
+ select,
26
+ setIsFocus,
27
+ listRef,
28
+ } = useAccessibleDropdown({
29
+ options: pageSizeOptions,
30
+ value,
31
+ onChange,
32
+ });
33
+
34
+ useEffect(() => {
35
+ const menuRef = pageSizeMenu.current;
36
+ const handleBlur = () => {
37
+ setIsFocus(false);
38
+ setIsDropdownOpen(false);
39
+ };
40
+
41
+ const handleFocus = () => {
42
+ if (menuRef?.parentElement?.querySelector(':hover') !== menuRef) {
43
+ setIsFocus(false);
44
+ setIsDropdownOpen(false);
45
+ }
46
+ };
47
+
48
+ menuRef?.addEventListener('blur', handleBlur);
49
+ menuRef?.addEventListener('focusin', handleFocus);
50
+ menuRef?.addEventListener('focusout', handleFocus);
51
+
52
+ return () => {
53
+ menuRef?.removeEventListener('blur', handleBlur);
54
+ menuRef?.removeEventListener('focusin', handleFocus);
55
+ menuRef?.removeEventListener('focusout', handleFocus);
56
+ };
57
+ }, []);
58
+
59
+ return (
60
+ <div
61
+ ref={pageSizeMenu}
62
+ className="ds-sdk-per-page-picker ml-2 mr-2 relative inline-block text-left bg-gray-100 rounded-md outline outline-1 outline-gray-200 hover:outline-gray-600 h-[32px]"
63
+ >
64
+ <button
65
+ 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"
66
+ ref={pageSizeButton}
67
+ onClick={() => setIsDropdownOpen(!isDropdownOpen)}
68
+ onFocus={() => setIsFocus(false)}
69
+ onBlur={() => setIsFocus(false)}
70
+ >
71
+ {selectedOption ? `${selectedOption.label}` : '24'}
72
+ <img src={Chevron} className={`flex-shrink-0 m-auto ml-sm h-md w-md stroke-1 stroke-gray-600 ${
73
+ isDropdownOpen ? '' : 'rotate-180'
74
+ }`} />
75
+ {/* <Chevron
76
+ className={`flex-shrink-0 m-auto ml-sm h-md w-md stroke-1 stroke-gray-600 ${
77
+ isDropdownOpen ? '' : 'rotate-180'
78
+ }`}
79
+ /> */}
80
+ </button>
81
+
82
+ {isDropdownOpen && (
83
+ <ul
84
+ ref={listRef}
85
+ className="ds-sdk-per-page-picker__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"
86
+ >
87
+ {pageSizeOptions.map((option, i) => (
88
+ <li
89
+ key={i}
90
+ aria-selected={option.value === selectedOption?.value}
91
+ onMouseOver={() => setActiveIndex(i)}
92
+ className={`py-xs hover:bg-gray-100 hover:text-gray-900 ${
93
+ i === activeIndex ? 'bg-gray-100 text-gray-900' : ''
94
+ }`}
95
+ >
96
+ <a
97
+ className={`ds-sdk-per-page-picker__items--item block-display px-md py-sm text-sm mb-0
98
+ no-underline active:no-underline focus:no-underline hover:no-underline
99
+ hover:text-gray-900 ${
100
+ option.value === selectedOption?.value
101
+ ? 'ds-sdk-per-page-picker__items--item-selected font-semibold text-gray-900'
102
+ : 'font-normal text-gray-800'
103
+ }`}
104
+ onClick={() => select(option.value)}
105
+ >
106
+ {option.label}
107
+ </a>
108
+ </li>
109
+ ))}
110
+ </ul>
111
+ )}
112
+ </div>
113
+ );
114
+ };
@@ -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 { PerPagePicker } from './PerPagePicker';
11
+ export { PerPagePicker as default } from './PerPagePicker';
@@ -0,0 +1,34 @@
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 CloseIcon from '../../icons/plus.svg';
12
+
13
+ const defaultIcon = (
14
+ <img src={CloseIcon} className="h-[12px] w-[12px] rotate-45 inline-block ml-sm cursor-pointer fill-gray-700" />
15
+ // <CloseIcon className="h-[12px] w-[12px] rotate-45 inline-block ml-sm cursor-pointer fill-gray-700" />
16
+ );
17
+
18
+ export const Pill = ({ label, onClick, CTA = defaultIcon, type }) => {
19
+ const containerClasses =
20
+ type === 'transparent'
21
+ ? 'ds-sdk-pill inline-flex justify-content items-center rounded-full w-fit min-h-[32px] px-4 py-1'
22
+ : 'ds-sdk-pill inline-flex justify-content items-center bg-gray-100 rounded-full w-fit outline outline-gray-200 min-h-[32px] px-4 py-1';
23
+
24
+ return (
25
+ <div key={label} className={containerClasses}>
26
+ <span className="ds-sdk-pill__label font-normal text-sm">{label}</span>
27
+ <span className="ds-sdk-pill__cta" onClick={onClick}>
28
+ {CTA}
29
+ </span>
30
+ </div>
31
+ );
32
+ };
33
+
34
+ export default Pill;
@@ -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 './Pill';
11
+ export { default } from './Pill';
@@ -0,0 +1,23 @@
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 const CTA = (
11
+ <svg
12
+ xmlns="http://www.w3.org/2000/svg"
13
+ className="h-5 w-5 inline-block ml-2 cursor-pointer mb-0.5"
14
+ viewBox="0 0 20 20"
15
+ fill="currentColor"
16
+ >
17
+ <path
18
+ fillRule="evenodd"
19
+ d="M10 5a1 1 0 011 1v3h3a1 1 0 110 2h-3v3a1 1 0 11-2 0v-3H6a1 1 0 110-2h3V6a1 1 0 011-1z"
20
+ clipRule="evenodd"
21
+ />
22
+ </svg>
23
+ );