@shopgate/engage 7.32.0-beta.5 → 7.32.0-beta.6

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 (124) hide show
  1. package/back-in-stock/components/Subscriptions/components/Subscription/index.js +2 -5
  2. package/cart/components/CartItem/CartItemProductLayout.js +1 -1
  3. package/cart/components/CartItem/CartItemProductLayoutWide.js +2 -2
  4. package/category/components/CategoryImage/index.js +11 -15
  5. package/components/constants.d.ts +6 -0
  6. package/components/constants.d.ts.map +1 -0
  7. package/core/helpers/getFullImageSource.d.ts +5 -1
  8. package/core/helpers/getFullImageSource.d.ts.map +1 -1
  9. package/core/helpers/getFullImageSource.js +30 -14
  10. package/core/helpers/i18n.d.ts +4 -0
  11. package/core/helpers/i18n.d.ts.map +1 -1
  12. package/core/helpers/i18n.js +3 -0
  13. package/core/hooks/html/useLoadImage.d.ts +3 -3
  14. package/core/hooks/html/useLoadImage.d.ts.map +1 -1
  15. package/core/hooks/html/useLoadImage.js +2 -2
  16. package/favorites/components/Item/Item.d.ts.map +1 -1
  17. package/favorites/components/Item/Item.js +2 -5
  18. package/favorites/components/List/List.d.ts +1 -1
  19. package/locations/components/StoreList/StoreListProduct.js +2 -3
  20. package/package.json +8 -8
  21. package/page/widgets/ProductSlider/ProductSlider.js +1 -15
  22. package/product/components/Media/FeaturedMedia.js +7 -1
  23. package/product/components/Media/MediaImage.js +24 -23
  24. package/product/components/Media/MediaPlaceholder.js +4 -7
  25. package/product/components/MediaSlider/components/MediaImage/index.js +6 -2
  26. package/product/components/MediaSlider/constants.js +1 -12
  27. package/product/components/ProductCard/index.js +3 -10
  28. package/product/components/ProductGrid/components/Item/components/ItemImage/index.js +2 -5
  29. package/product/components/ProductGrid/hooks.d.ts +0 -7
  30. package/product/components/ProductGrid/hooks.d.ts.map +1 -1
  31. package/product/components/ProductGrid/hooks.js +9 -10
  32. package/product/components/ProductImage/ProductImage.d.ts +42 -0
  33. package/product/components/ProductImage/ProductImage.d.ts.map +1 -0
  34. package/product/components/ProductImage/ProductImage.js +153 -0
  35. package/product/components/ProductImage/ProductImagePlaceholder.d.ts +23 -0
  36. package/product/components/ProductImage/ProductImagePlaceholder.d.ts.map +1 -0
  37. package/product/components/ProductImage/ProductImagePlaceholder.js +9 -14
  38. package/product/components/ProductImage/constants.d.ts +7 -0
  39. package/product/components/ProductImage/constants.d.ts.map +1 -0
  40. package/product/components/ProductImage/constants.js +6 -0
  41. package/product/components/ProductImage/hooks.d.ts +8 -0
  42. package/product/components/ProductImage/hooks.d.ts.map +1 -0
  43. package/product/components/ProductImage/hooks.js +32 -0
  44. package/product/components/ProductImage/index.d.ts +3 -0
  45. package/product/components/ProductImage/index.d.ts.map +1 -0
  46. package/product/components/ProductImage/index.js +1 -201
  47. package/product/components/ProductImage/spec.js +115 -60
  48. package/product/components/ProductList/components/Item/index.js +5 -3
  49. package/product/components/ProductRatingStars/ProductRatingStars.js +2 -4
  50. package/product/components/ProductSlider/constants.d.ts +3 -0
  51. package/product/components/ProductSlider/constants.d.ts.map +1 -0
  52. package/product/components/ProductSlider/constants.js +2 -0
  53. package/product/components/ProductSlider/hooks.d.ts +7 -0
  54. package/product/components/ProductSlider/hooks.d.ts.map +1 -0
  55. package/product/components/ProductSlider/hooks.js +40 -0
  56. package/product/components/ProductSlider/index.js +4 -6
  57. package/product/components/ProductSlider/spec.js +2 -2
  58. package/product/components/Rating/index.js +2 -4
  59. package/product/helpers/index.js +29 -11
  60. package/product/hooks/index.js +3 -1
  61. package/product/hooks/useLoadProductImage.js +4 -2
  62. package/product/hooks/useShowEmptyRatingStars.d.ts +7 -0
  63. package/product/hooks/useShowEmptyRatingStars.d.ts.map +1 -0
  64. package/product/hooks/useShowEmptyRatingStars.js +21 -0
  65. package/product/index.js +0 -1
  66. package/settings/constants/appSettings.d.ts +3 -0
  67. package/settings/constants/appSettings.d.ts.map +1 -1
  68. package/settings/constants/appSettings.js +6 -1
  69. package/settings/constants/imageSettings.d.ts +52 -0
  70. package/settings/constants/imageSettings.d.ts.map +1 -0
  71. package/settings/constants/imageSettings.js +69 -0
  72. package/settings/helpers/index.d.ts +3 -0
  73. package/settings/helpers/index.d.ts.map +1 -0
  74. package/settings/helpers/index.js +1 -0
  75. package/settings/helpers/resolveImageSettings.d.ts +70 -0
  76. package/settings/helpers/resolveImageSettings.d.ts.map +1 -0
  77. package/settings/helpers/resolveImageSettings.js +171 -0
  78. package/settings/helpers/toImageQuality.d.ts +7 -0
  79. package/settings/helpers/toImageQuality.d.ts.map +1 -0
  80. package/settings/helpers/toImageQuality.js +44 -0
  81. package/settings/helpers/toThumborColor.d.ts +15 -0
  82. package/settings/helpers/toThumborColor.d.ts.map +1 -0
  83. package/settings/helpers/toThumborColor.js +51 -0
  84. package/settings/hooks/index.d.ts +2 -0
  85. package/settings/hooks/index.d.ts.map +1 -0
  86. package/settings/hooks/index.js +1 -0
  87. package/settings/hooks/useImageSettings.d.ts +28 -0
  88. package/settings/hooks/useImageSettings.d.ts.map +1 -0
  89. package/settings/hooks/useImageSettings.js +39 -0
  90. package/settings/reducers/appSettings.d.ts.map +1 -1
  91. package/settings/reducers/appSettings.js +61 -7
  92. package/settings/reducers/index.d.ts +2 -2
  93. package/settings/selectors/appSettings.d.ts +39 -1
  94. package/settings/selectors/appSettings.d.ts.map +1 -1
  95. package/settings/selectors/appSettings.js +21 -1
  96. package/settings/types/appSettings.d.ts +144 -9
  97. package/settings/types/appSettings.d.ts.map +1 -1
  98. package/styles/reset/root.js +23 -4
  99. package/styles/reset/typographyCustomProps.d.ts +12 -0
  100. package/styles/reset/typographyCustomProps.d.ts.map +1 -0
  101. package/styles/reset/typographyCustomProps.js +11 -0
  102. package/styles/theme/createTheme/createTypography.js +1 -0
  103. package/styles/theme/createTheme/helpers/cssVarsParser.d.ts.map +1 -1
  104. package/styles/theme/createTheme/helpers/cssVarsParser.js +10 -3
  105. package/product/collections/index.js +0 -1
  106. package/product/components/EffectivityDates/constants.d.ts +0 -3
  107. package/product/components/EffectivityDates/constants.d.ts.map +0 -1
  108. package/product/components/EffectivityDates/helpers.d.ts +0 -6
  109. package/product/components/EffectivityDates/helpers.d.ts.map +0 -1
  110. package/product/components/Media/helpers.d.ts +0 -2
  111. package/product/components/Media/helpers.d.ts.map +0 -1
  112. package/product/components/ProductImage/connector.js +0 -12
  113. package/product/helpers/index.d.ts +0 -13
  114. package/product/helpers/index.d.ts.map +0 -1
  115. package/product/helpers/redirects.d.ts +0 -2
  116. package/product/helpers/redirects.d.ts.map +0 -1
  117. package/product/selectors/helpers/filterProperties.d.ts +0 -8
  118. package/product/selectors/helpers/filterProperties.d.ts.map +0 -1
  119. package/product/selectors/helpers/index.d.ts +0 -3
  120. package/product/selectors/helpers/index.d.ts.map +0 -1
  121. package/product/selectors/helpers/wrapMemoizedSelector.d.ts +0 -2
  122. package/product/selectors/helpers/wrapMemoizedSelector.d.ts.map +0 -1
  123. package/product/selectors/product.d.ts +0 -112
  124. package/product/selectors/product.d.ts.map +0 -1
@@ -2,12 +2,9 @@ import React, { useMemo, memo } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { SurroundPortals } from '@shopgate/engage/components';
4
4
  import { PRODUCT_ITEM_IMAGE } from '@shopgate/engage/category/constants';
5
- import { getProductImageSettings, ProductImage } from '@shopgate/engage/product';
5
+ import { ProductImage } from '@shopgate/engage/product';
6
6
  import { makeStyles } from '@shopgate/engage/styles';
7
7
  import { jsx as _jsx } from "react/jsx-runtime";
8
- const {
9
- ListImage: gridResolutions
10
- } = getProductImageSettings();
11
8
  const useStyles = makeStyles()(theme => ({
12
9
  image: {
13
10
  background: theme.palette.background.surface
@@ -37,7 +34,7 @@ const ItemImage = ({
37
34
  className: classes.image,
38
35
  alt: name,
39
36
  src: imageUrl,
40
- resolutions: gridResolutions,
37
+ context: "list",
41
38
  itemProp: "image"
42
39
  })
43
40
  });
@@ -5,13 +5,6 @@
5
5
  * Precedence:
6
6
  * - Once the app settings are hydrated from a source (admin sync / jsonp), they
7
7
  * are authoritative everywhere.
8
- * - Before hydration, when the grid is rendered outside a page-builder widget
9
- * (Category / Search / legacy Products widget), the single legacy
10
- * `useWidgetSettings` scalar is honored flat across all breakpoints. This
11
- * reproduces the old theme grid and preserves the merchant's configured value.
12
- * - Otherwise (inside a widget, pre-hydration) the built-in responsive default
13
- * from the app settings is used ({ xs: 2, md: 4 }), so the inherited legacy
14
- * `columns: 2` default no longer pins tablet to 2.
15
8
  * @returns The resolved number of columns for the active breakpoint.
16
9
  */
17
10
  export declare const useProductGridColumns: () => number;
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../../../product/components/ProductGrid/hooks.ts"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,qBAAqB,QAAO,MAwBxC,CAAC"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../../../product/components/ProductGrid/hooks.ts"],"names":[],"mappings":"AAiBA;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,QAAO,MAmCxC,CAAC"}
@@ -1,9 +1,11 @@
1
+ import "core-js/modules/es.array.reduce.js";
1
2
  import { useContext, useMemo } from 'react';
2
3
  import { useSelector } from 'react-redux';
3
4
  import { WidgetContext } from '@shopgate/engage/page/components/Widgets/WidgetContext';
4
5
  import { useWidgetSettings } from '@shopgate/engage/core/hooks';
5
6
  import { useResponsiveValue } from '@shopgate/engage/styles';
6
7
  import { getAreAppSettingsHydrated, getProductGridColumns } from '@shopgate/engage/settings/selectors/appSettings';
8
+ import { SCREEN_SIZE_BREAKPOINTS } from '@shopgate/engage/settings/constants/appSettings';
7
9
  import { WIDGET_ID } from "./constants";
8
10
 
9
11
  /**
@@ -13,13 +15,6 @@ import { WIDGET_ID } from "./constants";
13
15
  * Precedence:
14
16
  * - Once the app settings are hydrated from a source (admin sync / jsonp), they
15
17
  * are authoritative everywhere.
16
- * - Before hydration, when the grid is rendered outside a page-builder widget
17
- * (Category / Search / legacy Products widget), the single legacy
18
- * `useWidgetSettings` scalar is honored flat across all breakpoints. This
19
- * reproduces the old theme grid and preserves the merchant's configured value.
20
- * - Otherwise (inside a widget, pre-hydration) the built-in responsive default
21
- * from the app settings is used ({ xs: 2, md: 4 }), so the inherited legacy
22
- * `columns: 2` default no longer pins tablet to 2.
23
18
  * @returns The resolved number of columns for the active breakpoint.
24
19
  */
25
20
  export const useProductGridColumns = () => {
@@ -30,20 +25,24 @@ export const useProductGridColumns = () => {
30
25
  } = useContext(WidgetContext);
31
26
  const isInsideWidget = Boolean(widgetCode);
32
27
  const areAppSettingsHydrated = useSelector(getAreAppSettingsHydrated);
33
- // The built-in default { xs: 2, md: 4 } until hydrated, the source value after.
28
+ // The built-in default { small: 2, large: 4 } until hydrated, the source value after.
34
29
  const appSettingsColumns = useSelector(getProductGridColumns);
35
30
  const {
36
31
  columns: legacyColumns
37
32
  } = useWidgetSettings(WIDGET_ID) || {};
38
- const breakpoints = useMemo(() => {
33
+ const sizes = useMemo(() => {
39
34
  // Only special case: pre-hydration, outside a widget, honor the legacy
40
35
  // scalar flat across every breakpoint.
41
36
  if (!areAppSettingsHydrated && !isInsideWidget && typeof legacyColumns === 'number') {
42
37
  return {
43
- xs: legacyColumns
38
+ small: legacyColumns
44
39
  };
45
40
  }
46
41
  return appSettingsColumns;
47
42
  }, [areAppSettingsHydrated, isInsideWidget, legacyColumns, appSettingsColumns]);
43
+ const breakpoints = useMemo(() => Object.entries(sizes).reduce((acc, [size, value]) => {
44
+ acc[SCREEN_SIZE_BREAKPOINTS[size]] = value;
45
+ return acc;
46
+ }, {}), [sizes]);
48
47
  return useResponsiveValue(breakpoints);
49
48
  };
@@ -0,0 +1,42 @@
1
+ import type { ImageProps } from '@shopgate/pwa-common/components/Image';
2
+ import type { ImageResolution, ProductImageContext } from '@shopgate/engage/settings/types/appSettings';
3
+ /**
4
+ * Everything the underlying Image accepts is forwarded, minus the two this component owns:
5
+ * `backgroundColor`, derived from `noBackground`, and `placeholder`.
6
+ */
7
+ export interface ProductImageProps extends Omit<ImageProps, 'backgroundColor' | 'placeholder'> {
8
+ /**
9
+ * The surface this image is rendered on. Determines the resolutions and aspect ratio, which are
10
+ * configured app wide. Ignored when an explicit `resolutions` prop is passed.
11
+ */
12
+ context?: ProductImageContext | null;
13
+ /**
14
+ * Drops the background behind the image.
15
+ */
16
+ noBackground?: boolean;
17
+ /**
18
+ * Image to show instead of the placeholder icon. The shop wide setting wins over this.
19
+ */
20
+ placeholderSrc?: string | null;
21
+ /**
22
+ * @deprecated Pass a `context` instead, so the image follows the configured aspect ratio.
23
+ */
24
+ ratio?: number[] | null;
25
+ /**
26
+ * @deprecated Pass a `context` instead, so the image follows the configured aspect ratio.
27
+ * Still honored, and still takes precedence over `context`.
28
+ */
29
+ resolutions?: ImageResolution[];
30
+ }
31
+ /**
32
+ * The product image component.
33
+ *
34
+ * Behaves like the core Image component, with a product placeholder for a missing or failed image.
35
+ * @param props The component props.
36
+ * @returns The rendered component.
37
+ */
38
+ declare const ProductImage: (props: ProductImageProps) => import("react").JSX.Element;
39
+ export { ProductImage as UnwrappedProductImage };
40
+ declare const _default: import("react").MemoExoticComponent<(props: ProductImageProps) => import("react").JSX.Element>;
41
+ export default _default;
42
+ //# sourceMappingURL=ProductImage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProductImage.d.ts","sourceRoot":"","sources":["../../../../product/components/ProductImage/ProductImage.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uCAAuC,CAAC;AAMxE,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACpB,MAAM,6CAA6C,CAAC;AA0BrD;;;GAGG;AACH,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,UAAU,EAAE,iBAAiB,GAAG,aAAa,CAAC;IAC5F;;;OAGG;IACH,OAAO,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACrC;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACxB;;;OAGG;IACH,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;CACjC;AAmCD;;;;;;GAMG;AACH,QAAA,MAAM,YAAY,GAAI,OAAO,iBAAiB,gCA0E7C,CAAC;AAEF,OAAO,EAAE,YAAY,IAAI,qBAAqB,EAAE,CAAC;oEAOX,iBAAiB;AAOvD,wBAA2C"}
@@ -0,0 +1,153 @@
1
+ import { useLayoutEffect, useMemo, memo } from 'react';
2
+ import { useSelector } from 'react-redux';
3
+ import { logger } from '@shopgate/pwa-core';
4
+ import Image from '@shopgate/pwa-common/components/Image';
5
+ import PlaceholderIcon from '@shopgate/pwa-ui-shared/icons/PlaceholderIcon';
6
+ import SurroundPortals from '@shopgate/pwa-common/components/SurroundPortals';
7
+ import { makeStyles, useTheme } from '@shopgate/engage/styles';
8
+ import { useProductImageSettings } from '@shopgate/engage/settings/hooks';
9
+ import { getProductImagePlaceholder } from '@shopgate/engage/settings/selectors/shopSettings';
10
+ import { PORTAL_PRODUCT_IMAGE } from "../../../components/constants";
11
+ import ProductImagePlaceholder from "./ProductImagePlaceholder";
12
+ import { useProductImageShadow } from "./hooks";
13
+ import { jsx as _jsx } from "react/jsx-runtime";
14
+ const placeholderIconScale = 0.65;
15
+
16
+ /**
17
+ * The context applied when a caller passes neither a context nor explicit resolutions.
18
+ */
19
+ const DEFAULT_PRODUCT_IMAGE_CONTEXT = 'list';
20
+
21
+ /**
22
+ * The resolutions and aspect ratio the image is rendered with.
23
+ */
24
+
25
+ /**
26
+ * Everything the underlying Image accepts is forwarded, minus the two this component owns:
27
+ * `backgroundColor`, derived from `noBackground`, and `placeholder`.
28
+ */
29
+
30
+ const useStyles = makeStyles()(theme => ({
31
+ placeholderContent: {
32
+ position: 'absolute',
33
+ width: '100%',
34
+ height: '100%',
35
+ top: 0,
36
+ textAlign: 'center'
37
+ },
38
+ placeholder: {
39
+ position: 'absolute',
40
+ width: `${placeholderIconScale * 100}% !important`,
41
+ height: `${placeholderIconScale * 100}% !important`,
42
+ top: `${(1.0 - placeholderIconScale) * 50}%`,
43
+ left: `${(1.0 - placeholderIconScale) * 50}%`,
44
+ color: theme.palette.background.emphasized
45
+ },
46
+ innerShadow: {
47
+ position: 'relative',
48
+ overflow: 'hidden',
49
+ ':after': {
50
+ display: 'block',
51
+ content: '""',
52
+ position: 'absolute',
53
+ top: 0,
54
+ right: 0,
55
+ bottom: 0,
56
+ left: 0,
57
+ boxShadow: 'inset 0 0 20px rgba(0, 0, 0, .05)',
58
+ pointerEvents: 'none'
59
+ }
60
+ }
61
+ }));
62
+
63
+ /**
64
+ * The product image component.
65
+ *
66
+ * Behaves like the core Image component, with a product placeholder for a missing or failed image.
67
+ * @param props The component props.
68
+ * @returns The rendered component.
69
+ */
70
+ const ProductImage = props => {
71
+ const {
72
+ alt = null,
73
+ className = null,
74
+ context = null,
75
+ noBackground = false,
76
+ placeholderSrc = null,
77
+ ratio = null,
78
+ resolutions = null,
79
+ src = null
80
+ } = props;
81
+ const productImageSettings = useProductImageSettings();
82
+
83
+ // An explicit resolutions prop wins over the context, which is only consulted without one.
84
+ const resolved = useMemo(() => {
85
+ if (resolutions) {
86
+ return {
87
+ resolutions,
88
+ ratio
89
+ };
90
+ }
91
+ return productImageSettings[context || DEFAULT_PRODUCT_IMAGE_CONTEXT] ?? productImageSettings[DEFAULT_PRODUCT_IMAGE_CONTEXT];
92
+ }, [context, productImageSettings, ratio, resolutions]);
93
+ useLayoutEffect(() => {
94
+ logger.assert(!(resolutions && context), 'ProductImage received both "resolutions" and "context". The explicit resolutions win - drop one of them.');
95
+ /* eslint-disable-next-line react-hooks/exhaustive-deps -- legacy: assert once at mount */
96
+ }, []);
97
+ const {
98
+ classes,
99
+ cx
100
+ } = useStyles();
101
+ const theme = useTheme();
102
+ const showInnerShadow = useProductImageShadow();
103
+
104
+ // Image decides when to show this.
105
+ const placeholder = placeholderSrc ? /*#__PURE__*/_jsx(ProductImagePlaceholder, {
106
+ src: placeholderSrc,
107
+ showInnerShadow: showInnerShadow,
108
+ noBackground: noBackground
109
+ }) : /*#__PURE__*/_jsx("div", {
110
+ "aria-hidden": true,
111
+ className: classes.placeholderContent,
112
+ "data-test-id": "placeHolder",
113
+ children: /*#__PURE__*/_jsx(PlaceholderIcon, {
114
+ className: classes.placeholder
115
+ })
116
+ });
117
+ return /*#__PURE__*/_jsx(SurroundPortals, {
118
+ portalName: PORTAL_PRODUCT_IMAGE,
119
+ portalProps: src ? {
120
+ src,
121
+ resolutions: resolved.resolutions,
122
+ ratio: resolved.ratio
123
+ } : undefined,
124
+ children: /*#__PURE__*/_jsx("div", {
125
+ className: cx(className, 'engage__product__product-image'),
126
+ children: /*#__PURE__*/_jsx(Image, {
127
+ ...props,
128
+ resolutions: resolved.resolutions,
129
+ ratio: resolved.ratio,
130
+ placeholder: placeholder,
131
+ className: showInnerShadow ? classes.innerShadow : '',
132
+ backgroundColor: noBackground ? 'transparent' : theme.palette.background.surface,
133
+ "aria-hidden": !alt
134
+ })
135
+ })
136
+ });
137
+ };
138
+ export { ProductImage as UnwrappedProductImage };
139
+
140
+ /**
141
+ * Supplies the shop wide placeholder image.
142
+ * @param props The component props.
143
+ * @returns The rendered component.
144
+ */
145
+ const ConnectedProductImage = props => {
146
+ // The shop wide placeholder takes precedence over a passed one.
147
+ const placeholderSrc = useSelector(getProductImagePlaceholder) ?? props.placeholderSrc ?? null;
148
+ return /*#__PURE__*/_jsx(ProductImage, {
149
+ ...props,
150
+ placeholderSrc: placeholderSrc
151
+ });
152
+ };
153
+ export default /*#__PURE__*/memo(ConnectedProductImage);
@@ -0,0 +1,23 @@
1
+ export interface ProductImagePlaceholderProps {
2
+ /**
3
+ * Drops the background behind the placeholder.
4
+ */
5
+ noBackground?: boolean;
6
+ /**
7
+ * Whether the placeholder gets an inset shadow, matching the image it stands in for.
8
+ */
9
+ showInnerShadow?: boolean;
10
+ /**
11
+ * The merchant configured placeholder image. While it loads, and when it is absent, the
12
+ * built-in icon is shown instead.
13
+ */
14
+ src?: string | null;
15
+ }
16
+ /**
17
+ * The ProductImagePlaceholder component.
18
+ * @param props The components props.
19
+ * @returns The rendered component.
20
+ */
21
+ declare const ProductImagePlaceholder: (props: ProductImagePlaceholderProps) => import("react").JSX.Element;
22
+ export default ProductImagePlaceholder;
23
+ //# sourceMappingURL=ProductImagePlaceholder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProductImagePlaceholder.d.ts","sourceRoot":"","sources":["../../../../product/components/ProductImage/ProductImagePlaceholder.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,4BAA4B;IAC3C;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAqCD;;;;GAIG;AACH,QAAA,MAAM,uBAAuB,GAAI,OAAO,4BAA4B,gCAwCnE,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
@@ -1,5 +1,4 @@
1
- import React, { useEffect, useMemo, useState } from 'react';
2
- import PropTypes from 'prop-types';
1
+ import { useEffect, useMemo, useState } from 'react';
3
2
  import PlaceholderIcon from '@shopgate/pwa-ui-shared/icons/PlaceholderIcon';
4
3
  import { makeStyles, useTheme } from '@shopgate/engage/styles';
5
4
  import { useLoadImage } from '@shopgate/engage/core';
@@ -40,14 +39,15 @@ const useStyles = makeStyles()(theme => ({
40
39
 
41
40
  /**
42
41
  * The ProductImagePlaceholder component.
43
- * @param {Object} props The components props.
44
- * @returns {JSX}
42
+ * @param props The components props.
43
+ * @returns The rendered component.
45
44
  */
46
- const ProductImagePlaceholder = ({
47
- src,
48
- showInnerShadow,
49
- noBackground
50
- }) => {
45
+ const ProductImagePlaceholder = props => {
46
+ const {
47
+ src = null,
48
+ showInnerShadow = false,
49
+ noBackground = false
50
+ } = props;
51
51
  const {
52
52
  classes,
53
53
  cx
@@ -85,9 +85,4 @@ const ProductImagePlaceholder = ({
85
85
  }), !showPlaceholder && ' ']
86
86
  });
87
87
  };
88
- ProductImagePlaceholder.defaultProps = {
89
- src: null,
90
- noBackground: false,
91
- showInnerShadow: false
92
- };
93
88
  export default ProductImagePlaceholder;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The widget id the legacy inner shadow setting is configured under.
3
+ *
4
+ * Extracted so the resolution hook can read it without importing the component.
5
+ */
6
+ export declare const WIDGET_ID = "@shopgate/engage/product/ProductImage";
7
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../product/components/ProductImage/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,SAAS,0CAA0C,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * The widget id the legacy inner shadow setting is configured under.
3
+ *
4
+ * Extracted so the resolution hook can read it without importing the component.
5
+ */
6
+ export const WIDGET_ID = '@shopgate/engage/product/ProductImage';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Resolves whether product images render with an inset shadow.
3
+ * @param widgetId The widget id the legacy setting is configured under, for the product image
4
+ * components that have their own.
5
+ * @returns Whether to render the shadow.
6
+ */
7
+ export declare const useProductImageShadow: (widgetId?: string) => boolean;
8
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../../../product/components/ProductImage/hooks.ts"],"names":[],"mappings":"AAYA;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAAI,WAAU,MAAkB,KAAG,OAiBpE,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { useMemo } from 'react';
2
+ import { useSelector } from 'react-redux';
3
+ // Imported via its module path rather than the "core/hooks" barrel, to keep this out of the
4
+ // import cycle the barrel pulls in.
5
+ import { useWidgetSettings } from '@shopgate/engage/core/hooks/useWidgetSettings';
6
+ import { getAreAppSettingsHydrated, getImageSettings } from '@shopgate/engage/settings/selectors/appSettings';
7
+ import { DEFAULT_SHOW_INNER_SHADOW } from '@shopgate/engage/settings/constants/imageSettings';
8
+ import { WIDGET_ID } from "./constants";
9
+
10
+ /**
11
+ * Resolves whether product images render with an inset shadow.
12
+ * @param widgetId The widget id the legacy setting is configured under, for the product image
13
+ * components that have their own.
14
+ * @returns Whether to render the shadow.
15
+ */
16
+ export const useProductImageShadow = (widgetId = WIDGET_ID) => {
17
+ const areAppSettingsHydrated = useSelector(getAreAppSettingsHydrated);
18
+ const configured = useSelector(getImageSettings)?.product?.showInnerShadow;
19
+ const {
20
+ showInnerShadow: legacy
21
+ } = useWidgetSettings(widgetId) || {};
22
+ return useMemo(() => {
23
+ if (areAppSettingsHydrated) {
24
+ return configured ?? DEFAULT_SHOW_INNER_SHADOW;
25
+ }
26
+
27
+ // Until a source hydrates the app settings the legacy widget configuration wins, and failing
28
+ // that the built-in default, which is derived from the legacy app config flag so that each
29
+ // theme keeps its own behavior.
30
+ return legacy ?? DEFAULT_SHOW_INNER_SHADOW;
31
+ }, [areAppSettingsHydrated, configured, legacy]);
32
+ };
@@ -0,0 +1,3 @@
1
+ export { default } from './ProductImage';
2
+ export type { ProductImageProps } from './ProductImage';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../product/components/ProductImage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -1,201 +1 @@
1
- import React, { useState, useEffect, useLayoutEffect, useCallback, memo } from 'react';
2
- import PropTypes from 'prop-types';
3
- import { logger } from '@shopgate/pwa-core';
4
- import appConfig from '@shopgate/pwa-common/helpers/config';
5
- import Image from '@shopgate/pwa-common/components/Image';
6
- import PlaceholderIcon from '@shopgate/pwa-ui-shared/icons/PlaceholderIcon';
7
- import SurroundPortals from '@shopgate/pwa-common/components/SurroundPortals';
8
- import { makeStyles, useTheme } from '@shopgate/engage/styles';
9
- import { withWidgetSettings } from "../../../core/hocs/withWidgetSettings";
10
- import { PORTAL_PRODUCT_IMAGE } from "../../../components/constants";
11
- import ProductImagePlaceholder from "./ProductImagePlaceholder";
12
- import connect from "./connector";
13
- import { jsx as _jsx } from "react/jsx-runtime";
14
- const placeholderIconScale = 0.65;
15
-
16
- /**
17
- * Derives the image aspect ratio from explicit ratio props or the largest resolution.
18
- * @param {Object} props The component props.
19
- * @param {number[]} [props.ratio] Optional width/height ratio tuple.
20
- * @param {Array<{ width: number, height: number }>} props.resolutions Available image resolutions.
21
- * @returns {string} The computed ratio as a fixed decimal string.
22
- */
23
- const getImageRatio = ({
24
- ratio,
25
- resolutions
26
- } = {}) => {
27
- if (Array.isArray(ratio) && ratio.length === 2 && ratio[0]) {
28
- const [x, y] = ratio;
29
- return (y / x).toFixed(3);
30
- }
31
- if (Array.isArray(resolutions) && resolutions.length > 0) {
32
- const {
33
- width,
34
- height
35
- } = resolutions[resolutions.length - 1];
36
- if (width && height) {
37
- return (height / width).toFixed(3);
38
- }
39
- }
40
- return '1.000';
41
- };
42
- const useStyles = makeStyles()((theme, {
43
- ratio
44
- }) => ({
45
- placeholderContainer: {
46
- position: 'relative',
47
- width: '100%',
48
- ':before': {
49
- display: 'block',
50
- content: '""',
51
- width: '100%',
52
- paddingTop: `${100 * ratio}%`
53
- }
54
- },
55
- placeholderContent: {
56
- position: 'absolute',
57
- width: '100%',
58
- height: '100%',
59
- top: 0,
60
- textAlign: 'center'
61
- },
62
- placeholder: {
63
- position: 'absolute',
64
- width: `${placeholderIconScale * 100}% !important`,
65
- height: `${placeholderIconScale * 100}% !important`,
66
- top: `${(1.0 - placeholderIconScale) * 50}%`,
67
- left: `${(1.0 - placeholderIconScale) * 50}%`,
68
- color: theme.palette.background.emphasized
69
- },
70
- innerShadow: {
71
- position: 'relative',
72
- overflow: 'hidden',
73
- ':after': {
74
- display: 'block',
75
- content: '""',
76
- position: 'absolute',
77
- top: 0,
78
- right: 0,
79
- bottom: 0,
80
- left: 0,
81
- boxShadow: 'inset 0 0 20px rgba(0, 0, 0, .05)',
82
- pointerEvents: 'none'
83
- }
84
- }
85
- }));
86
-
87
- /**
88
- * The product image component.
89
- * This component will behave like the core Image component with the additional
90
- * feature of showing a placeholder in case no src property has been passed
91
- * or the given source image cannot be loaded.
92
- * @param {Object} props Props.
93
- * @returns {JSX.Element}
94
- */
95
- const ProductImage = props => {
96
- const {
97
- alt,
98
- className,
99
- noBackground,
100
- placeholderSrc,
101
- ratio,
102
- resolutions,
103
- src,
104
- srcmap,
105
- widgetSettings
106
- } = props;
107
- const ratioValue = Number.parseFloat(getImageRatio({
108
- ratio,
109
- resolutions
110
- })) || 1;
111
- const {
112
- classes,
113
- cx
114
- } = useStyles({
115
- ratio: ratioValue
116
- });
117
- const theme = useTheme();
118
- const [showPlaceholder, setShowPlaceholder] = useState(!src && (srcmap === null || srcmap.length === 0));
119
- const [imageLoadingFailed, setImageLoadingFailed] = useState(false);
120
- useLayoutEffect(() => {
121
- logger.assert(!srcmap, 'Use of srcmap prop is deprecated. Use resolutions instead');
122
- /* eslint-disable-next-line react-hooks/exhaustive-deps -- legacy: assert once at mount */
123
- }, []);
124
- useEffect(() => {
125
- const nextShowPlaceholder = !src && (!srcmap || srcmap.length === 0);
126
- setShowPlaceholder(nextShowPlaceholder);
127
- }, [src, srcmap]);
128
- const imageLoadingFailedHandler = useCallback(() => {
129
- setShowPlaceholder(true);
130
- setImageLoadingFailed(true);
131
- }, []);
132
- let {
133
- showInnerShadow
134
- } = widgetSettings;
135
- if (typeof showInnerShadow === 'undefined') {
136
- showInnerShadow = !appConfig.hideProductImageShadow;
137
- }
138
- if (imageLoadingFailed || showPlaceholder) {
139
- return /*#__PURE__*/_jsx(SurroundPortals, {
140
- portalName: PORTAL_PRODUCT_IMAGE,
141
- children: /*#__PURE__*/_jsx("div", {
142
- className: cx(classes.placeholderContainer, {
143
- [classes.innerShadow]: showInnerShadow,
144
- [className]: !!className
145
- }),
146
- children: placeholderSrc ? /*#__PURE__*/_jsx(ProductImagePlaceholder, {
147
- src: placeholderSrc,
148
- showInnerShadow: showInnerShadow,
149
- noBackground: noBackground
150
- }) : /*#__PURE__*/_jsx("div", {
151
- "aria-hidden": true,
152
- className: classes.placeholderContent,
153
- "data-test-id": "placeHolder",
154
- children: /*#__PURE__*/_jsx(PlaceholderIcon, {
155
- className: classes.placeholder
156
- })
157
- })
158
- })
159
- });
160
- }
161
- return /*#__PURE__*/_jsx(SurroundPortals, {
162
- portalName: PORTAL_PRODUCT_IMAGE,
163
- portalProps: {
164
- src,
165
- resolutions
166
- },
167
- children: /*#__PURE__*/_jsx("div", {
168
- className: cx(className, 'engage__product__product-image'),
169
- children: /*#__PURE__*/_jsx(Image, {
170
- ...props,
171
- className: showInnerShadow ? classes.innerShadow : '',
172
- backgroundColor: noBackground ? 'transparent' : theme.palette.background.surface,
173
- onError: imageLoadingFailedHandler,
174
- "aria-hidden": !alt
175
- })
176
- })
177
- });
178
- };
179
- ProductImage.defaultProps = {
180
- alt: null,
181
- animating: true,
182
- className: null,
183
- forcePlaceholder: false,
184
- highestResolutionLoaded: () => {},
185
- noBackground: false,
186
- ratio: null,
187
- resolutions: [{
188
- width: 50,
189
- height: 50,
190
- blur: 2
191
- }, {
192
- width: 440,
193
- height: 440
194
- }],
195
- src: null,
196
- srcmap: null,
197
- placeholderSrc: null,
198
- widgetSettings: {}
199
- };
200
- export { ProductImage as UnwrappedProductImage };
201
- export default connect(withWidgetSettings(/*#__PURE__*/memo(ProductImage), '@shopgate/engage/product/ProductImage'));
1
+ export { default } from "./ProductImage";