@shopgate/engage 7.32.0-beta.4 → 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.
- package/back-in-stock/components/Subscriptions/components/Subscription/index.js +2 -5
- package/cart/components/CartItem/CartItemProductLayout.js +1 -1
- package/cart/components/CartItem/CartItemProductLayoutWide.js +2 -2
- package/category/components/CategoryImage/index.js +11 -15
- package/components/constants.d.ts +6 -0
- package/components/constants.d.ts.map +1 -0
- package/core/helpers/getFullImageSource.d.ts +5 -1
- package/core/helpers/getFullImageSource.d.ts.map +1 -1
- package/core/helpers/getFullImageSource.js +30 -14
- package/core/helpers/i18n.d.ts +4 -0
- package/core/helpers/i18n.d.ts.map +1 -1
- package/core/helpers/i18n.js +3 -0
- package/core/hooks/html/useLoadImage.d.ts +3 -3
- package/core/hooks/html/useLoadImage.d.ts.map +1 -1
- package/core/hooks/html/useLoadImage.js +2 -2
- package/favorites/components/Item/Item.d.ts.map +1 -1
- package/favorites/components/Item/Item.js +2 -5
- package/favorites/components/List/List.d.ts +1 -1
- package/locations/components/StoreList/StoreListProduct.js +2 -3
- package/package.json +8 -8
- package/page/components/NotFound.d.ts +7 -0
- package/page/components/NotFound.d.ts.map +1 -0
- package/page/components/NotFound.js +1 -2
- package/page/components/ResponsiveWidgetImage/ResponsiveWidgetImage.d.ts +52 -0
- package/page/components/ResponsiveWidgetImage/ResponsiveWidgetImage.d.ts.map +1 -0
- package/page/components/ResponsiveWidgetImage/index.d.ts +2 -0
- package/page/components/ResponsiveWidgetImage/index.d.ts.map +1 -0
- package/page/components/WidgetRichText/WidgetRichText.d.ts +25 -0
- package/page/components/WidgetRichText/WidgetRichText.d.ts.map +1 -0
- package/page/components/WidgetRichText/index.d.ts +2 -0
- package/page/components/WidgetRichText/index.d.ts.map +1 -0
- package/page/components/WidgetVideo/WidgetVideo.d.ts +43 -0
- package/page/components/WidgetVideo/WidgetVideo.d.ts.map +1 -0
- package/page/components/WidgetVideo/WidgetVideo.js +33 -39
- package/page/components/WidgetVideo/index.d.ts +2 -0
- package/page/components/WidgetVideo/index.d.ts.map +1 -0
- package/page/components/Widgets/Overlay.d.ts +17 -0
- package/page/components/Widgets/Overlay.d.ts.map +1 -0
- package/page/components/Widgets/Overlay.js +24 -38
- package/page/components/Widgets/Tooltip.d.ts +20 -0
- package/page/components/Widgets/Tooltip.d.ts.map +1 -0
- package/page/components/Widgets/Tooltip.js +6 -10
- package/page/components/Widgets/Widget.d.ts +32 -0
- package/page/components/Widgets/Widget.d.ts.map +1 -0
- package/page/components/Widgets/Widget.js +7 -18
- package/page/components/Widgets/WidgetContext.d.ts +32 -38
- package/page/components/Widgets/WidgetContext.d.ts.map +1 -0
- package/page/components/Widgets/WidgetContext.js +2 -8
- package/page/components/Widgets/WidgetProvider.d.ts +25 -0
- package/page/components/Widgets/WidgetProvider.d.ts.map +1 -0
- package/page/components/Widgets/WidgetProvider.js +5 -12
- package/page/components/Widgets/Widgets.d.ts +16 -0
- package/page/components/Widgets/Widgets.d.ts.map +1 -0
- package/page/components/Widgets/Widgets.js +5 -12
- package/page/components/Widgets/WidgetsPreviewContext.d.ts +21 -0
- package/page/components/Widgets/WidgetsPreviewContext.d.ts.map +1 -0
- package/page/components/Widgets/WidgetsPreviewContext.js +5 -7
- package/page/components/Widgets/WidgetsPreviewProvider.d.ts +18 -0
- package/page/components/Widgets/WidgetsPreviewProvider.d.ts.map +1 -0
- package/page/components/Widgets/WidgetsPreviewProvider.js +5 -6
- package/page/components/Widgets/constants.d.ts +5 -0
- package/page/components/Widgets/constants.d.ts.map +1 -0
- package/page/components/Widgets/constants.js +3 -1
- package/page/components/Widgets/events.d.ts +38 -0
- package/page/components/Widgets/events.d.ts.map +1 -0
- package/page/components/Widgets/events.js +8 -12
- package/page/components/Widgets/helpers.d.ts +50 -0
- package/page/components/Widgets/helpers.d.ts.map +1 -0
- package/page/components/Widgets/helpers.js +8 -13
- package/page/components/Widgets/hooks.d.ts +13 -0
- package/page/components/Widgets/hooks.d.ts.map +1 -0
- package/page/components/Widgets/hooks.js +13 -11
- package/page/components/Widgets/index.d.ts +3 -0
- package/page/components/Widgets/index.d.ts.map +1 -0
- package/page/components/Widgets/types.d.ts +111 -113
- package/page/components/Widgets/types.d.ts.map +1 -0
- package/page/components/Widgets/types.js +1 -0
- package/page/components/index.d.ts +5 -0
- package/page/components/index.d.ts.map +1 -0
- package/page/constants/actionTypes.d.ts +4 -0
- package/page/constants/actionTypes.d.ts.map +1 -0
- package/page/constants/index.d.ts +25 -0
- package/page/constants/index.d.ts.map +1 -0
- package/page/helpers/index.d.ts +67 -62
- package/page/helpers/index.d.ts.map +1 -0
- package/page/helpers/index.js +24 -25
- package/page/hooks/index.d.ts +54 -53
- package/page/hooks/index.d.ts.map +1 -0
- package/page/hooks/index.js +20 -15
- package/page/selectors/index.d.ts +32 -0
- package/page/selectors/index.d.ts.map +1 -0
- package/page/widgets/ProductSlider/ProductSlider.js +1 -15
- package/page/widgets/Video/Video.d.ts +6 -0
- package/page/widgets/Video/Video.d.ts.map +1 -0
- package/page/widgets/Video/Video.js +0 -2
- package/page/widgets/Video/hooks.d.ts +53 -0
- package/page/widgets/Video/hooks.d.ts.map +1 -0
- package/page/widgets/Video/hooks.js +4 -12
- package/page/widgets/Video/index.d.ts +2 -0
- package/page/widgets/Video/index.d.ts.map +1 -0
- package/product/components/Media/FeaturedMedia.js +7 -1
- package/product/components/Media/MediaImage.js +24 -23
- package/product/components/Media/MediaPlaceholder.js +4 -7
- package/product/components/MediaSlider/components/MediaImage/index.js +6 -2
- package/product/components/MediaSlider/constants.js +1 -12
- package/product/components/ProductCard/index.js +3 -10
- package/product/components/ProductGrid/components/Item/components/ItemImage/index.js +2 -5
- package/product/components/ProductGrid/constants.d.ts +2 -0
- package/product/components/ProductGrid/constants.d.ts.map +1 -0
- package/product/components/ProductGrid/constants.js +1 -0
- package/product/components/ProductGrid/hooks.d.ts +11 -0
- package/product/components/ProductGrid/hooks.d.ts.map +1 -0
- package/product/components/ProductGrid/hooks.js +48 -0
- package/product/components/ProductGrid/index.js +3 -6
- package/product/components/ProductGrid/spec.js +3 -0
- package/product/components/ProductImage/ProductImage.d.ts +42 -0
- package/product/components/ProductImage/ProductImage.d.ts.map +1 -0
- package/product/components/ProductImage/ProductImage.js +153 -0
- package/product/components/ProductImage/ProductImagePlaceholder.d.ts +23 -0
- package/product/components/ProductImage/ProductImagePlaceholder.d.ts.map +1 -0
- package/product/components/ProductImage/ProductImagePlaceholder.js +9 -14
- package/product/components/ProductImage/constants.d.ts +7 -0
- package/product/components/ProductImage/constants.d.ts.map +1 -0
- package/product/components/ProductImage/constants.js +6 -0
- package/product/components/ProductImage/hooks.d.ts +8 -0
- package/product/components/ProductImage/hooks.d.ts.map +1 -0
- package/product/components/ProductImage/hooks.js +32 -0
- package/product/components/ProductImage/index.d.ts +3 -0
- package/product/components/ProductImage/index.d.ts.map +1 -0
- package/product/components/ProductImage/index.js +1 -201
- package/product/components/ProductImage/spec.js +115 -60
- package/product/components/ProductList/components/Item/index.js +5 -3
- package/product/components/ProductRatingStars/ProductRatingStars.js +2 -4
- package/product/components/ProductSlider/constants.d.ts +3 -0
- package/product/components/ProductSlider/constants.d.ts.map +1 -0
- package/product/components/ProductSlider/constants.js +2 -0
- package/product/components/ProductSlider/hooks.d.ts +7 -0
- package/product/components/ProductSlider/hooks.d.ts.map +1 -0
- package/product/components/ProductSlider/hooks.js +40 -0
- package/product/components/ProductSlider/index.js +4 -6
- package/product/components/ProductSlider/spec.js +2 -2
- package/product/components/Rating/index.js +2 -4
- package/product/helpers/index.js +29 -11
- package/product/hooks/index.js +3 -1
- package/product/hooks/useLoadProductImage.js +4 -2
- package/product/hooks/useShowEmptyRatingStars.d.ts +7 -0
- package/product/hooks/useShowEmptyRatingStars.d.ts.map +1 -0
- package/product/hooks/useShowEmptyRatingStars.js +21 -0
- package/product/index.js +0 -1
- package/settings/constants/appSettings.d.ts +3 -0
- package/settings/constants/appSettings.d.ts.map +1 -1
- package/settings/constants/appSettings.js +6 -1
- package/settings/constants/imageSettings.d.ts +52 -0
- package/settings/constants/imageSettings.d.ts.map +1 -0
- package/settings/constants/imageSettings.js +69 -0
- package/settings/helpers/index.d.ts +3 -0
- package/settings/helpers/index.d.ts.map +1 -0
- package/settings/helpers/index.js +1 -0
- package/settings/helpers/resolveImageSettings.d.ts +70 -0
- package/settings/helpers/resolveImageSettings.d.ts.map +1 -0
- package/settings/helpers/resolveImageSettings.js +171 -0
- package/settings/helpers/toImageQuality.d.ts +7 -0
- package/settings/helpers/toImageQuality.d.ts.map +1 -0
- package/settings/helpers/toImageQuality.js +44 -0
- package/settings/helpers/toThumborColor.d.ts +15 -0
- package/settings/helpers/toThumborColor.d.ts.map +1 -0
- package/settings/helpers/toThumborColor.js +51 -0
- package/settings/hooks/index.d.ts +2 -0
- package/settings/hooks/index.d.ts.map +1 -0
- package/settings/hooks/index.js +1 -0
- package/settings/hooks/useImageSettings.d.ts +28 -0
- package/settings/hooks/useImageSettings.d.ts.map +1 -0
- package/settings/hooks/useImageSettings.js +39 -0
- package/settings/reducers/appSettings.d.ts.map +1 -1
- package/settings/reducers/appSettings.js +66 -4
- package/settings/reducers/index.d.ts +2 -2
- package/settings/selectors/appSettings.d.ts +46 -0
- package/settings/selectors/appSettings.d.ts.map +1 -1
- package/settings/selectors/appSettings.js +26 -1
- package/settings/types/appSettings.d.ts +156 -0
- package/settings/types/appSettings.d.ts.map +1 -1
- package/styles/reset/root.js +23 -4
- package/styles/reset/typographyCustomProps.d.ts +12 -0
- package/styles/reset/typographyCustomProps.d.ts.map +1 -0
- package/styles/reset/typographyCustomProps.js +11 -0
- package/styles/theme/createTheme/createTypography.js +1 -0
- package/styles/theme/createTheme/helpers/cssVarsParser.d.ts.map +1 -1
- package/styles/theme/createTheme/helpers/cssVarsParser.js +10 -3
- package/product/collections/index.js +0 -1
- package/product/components/EffectivityDates/constants.d.ts +0 -3
- package/product/components/EffectivityDates/constants.d.ts.map +0 -1
- package/product/components/EffectivityDates/helpers.d.ts +0 -6
- package/product/components/EffectivityDates/helpers.d.ts.map +0 -1
- package/product/components/Media/helpers.d.ts +0 -2
- package/product/components/Media/helpers.d.ts.map +0 -1
- package/product/components/ProductImage/connector.js +0 -12
- package/product/helpers/index.d.ts +0 -13
- package/product/helpers/index.d.ts.map +0 -1
- package/product/helpers/redirects.d.ts +0 -2
- package/product/helpers/redirects.d.ts.map +0 -1
- package/product/selectors/helpers/filterProperties.d.ts +0 -8
- package/product/selectors/helpers/filterProperties.d.ts.map +0 -1
- package/product/selectors/helpers/index.d.ts +0 -3
- package/product/selectors/helpers/index.d.ts.map +0 -1
- package/product/selectors/helpers/wrapMemoizedSelector.d.ts +0 -2
- package/product/selectors/helpers/wrapMemoizedSelector.d.ts.map +0 -1
- package/product/selectors/product.d.ts +0 -112
- package/product/selectors/product.d.ts.map +0 -1
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Imported via its module path rather than the "core" barrel, since that barrel pulls in
|
|
2
|
+
// getFullImageSource which reads these settings back, and would create a circular import.
|
|
3
|
+
import appConfig from '@shopgate/pwa-common/helpers/config';
|
|
4
|
+
import { getThemeSettings } from '@shopgate/engage/core/config/getThemeSettings';
|
|
5
|
+
/**
|
|
6
|
+
* The base widths every product image context requests from the image service, ordered ascending.
|
|
7
|
+
*
|
|
8
|
+
* Each array is a progressive loading ladder, not a responsive pick: the Image component loads the
|
|
9
|
+
* second to last entry first as a low resolution preview and the last entry as the actual source.
|
|
10
|
+
* Heights are derived from the configured aspect ratio, so at the default 1:1 these reproduce the
|
|
11
|
+
* legacy AppImages resolutions exactly.
|
|
12
|
+
*/
|
|
13
|
+
export const PRODUCT_IMAGE_BASE_WIDTHS = {
|
|
14
|
+
list: [440],
|
|
15
|
+
pdp: [440, 1024],
|
|
16
|
+
gallery: [1024, 2048]
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The largest width or height that may be requested from the image service.
|
|
21
|
+
*
|
|
22
|
+
* A configured aspect ratio is applied to the base widths, so an extreme one would otherwise ask for
|
|
23
|
+
* dimensions the service rejects. Comfortably above the largest base width in use.
|
|
24
|
+
*/
|
|
25
|
+
export const MAX_IMAGE_DIMENSION = 4096;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The key the legacy image settings live under in the theme configuration.
|
|
29
|
+
*/
|
|
30
|
+
export const LEGACY_IMAGE_SETTINGS_KEY = 'AppImages';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Compression quality used when the theme configuration does not provide one.
|
|
34
|
+
*/
|
|
35
|
+
const FALLBACK_IMAGE_QUALITY = 75;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The compression quality passed to the image service when nothing is configured.
|
|
39
|
+
*
|
|
40
|
+
* Derived from the theme configuration, so an app whose settings never carry a quality keeps the
|
|
41
|
+
* one its theme configured.
|
|
42
|
+
*
|
|
43
|
+
* Safe to read once at module scope: the theme config is inlined into the bundle at build time.
|
|
44
|
+
*/
|
|
45
|
+
export const DEFAULT_IMAGE_QUALITY = (getThemeSettings(LEGACY_IMAGE_SETTINGS_KEY) ?? {}).quality ?? FALLBACK_IMAGE_QUALITY;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Whether product images get an inset shadow when nothing is configured.
|
|
49
|
+
*
|
|
50
|
+
* Derived from the legacy app config flag rather than hardcoded: that flag is inverted, and its
|
|
51
|
+
* default differs per theme - gmd hides the shadow, ios11 shows it - so a fixed value here would
|
|
52
|
+
* flip the shadow for one of them.
|
|
53
|
+
*
|
|
54
|
+
* Safe to read once at module scope for the same reason as DEFAULT_IMAGE_QUALITY: the app config is
|
|
55
|
+
* inlined into the bundle at build time.
|
|
56
|
+
*/
|
|
57
|
+
export const DEFAULT_SHOW_INNER_SHADOW = !appConfig.hideProductImageShadow;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Fill color used when no valid one is configured. Already in Thumbor's expected format - a
|
|
61
|
+
* hexadecimal RGB expression without the leading "#".
|
|
62
|
+
*/
|
|
63
|
+
export const DEFAULT_IMAGE_FILL_COLOR = 'FFFFFF';
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Whether transparent areas of the source image are filled too, when nothing is configured.
|
|
67
|
+
* Matches what the legacy "FFFFFF,1" value expressed.
|
|
68
|
+
*/
|
|
69
|
+
export const DEFAULT_IMAGE_FILL_TRANSPARENT = true;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../settings/helpers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,2BAA2B,EAC3B,2BAA2B,GAC5B,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,4BAA4B,EAC5B,2BAA2B,EAC3B,4BAA4B,GAC7B,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { resolveImageServiceSettings, resolveProductImageSettings } from "./resolveImageSettings";
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { ImageResolution, ImageSettings, ProductImageContext } from '../types/appSettings';
|
|
2
|
+
/**
|
|
3
|
+
* The resolved settings for one product image context: the resolution ladder to request from the
|
|
4
|
+
* image service, and the aspect ratio to apply to the rendered element.
|
|
5
|
+
*
|
|
6
|
+
* ratio is null while the legacy settings are in effect, because those configure explicit
|
|
7
|
+
* dimensions rather than a ratio - consumers then let the Image component derive the ratio from
|
|
8
|
+
* the largest resolution, exactly as before.
|
|
9
|
+
*/
|
|
10
|
+
export interface ResolvedProductImageContext {
|
|
11
|
+
/**
|
|
12
|
+
* The ladder to request, ordered ascending. The last entry is the displayed image, the second
|
|
13
|
+
* to last a low resolution preview.
|
|
14
|
+
*/
|
|
15
|
+
resolutions: ImageResolution[];
|
|
16
|
+
/**
|
|
17
|
+
* Width and height parts to apply to the rendered element, taken from the image that is
|
|
18
|
+
* requested rather than from the configured ratio, which may have been bounded. Null leaves the
|
|
19
|
+
* Image component to derive one from the largest resolution.
|
|
20
|
+
*/
|
|
21
|
+
ratio: [number, number] | null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The resolved settings for every product image context, keyed by context.
|
|
25
|
+
*/
|
|
26
|
+
export type ResolvedProductImageSettings = Record<ProductImageContext, ResolvedProductImageContext>;
|
|
27
|
+
/**
|
|
28
|
+
* What getFullImageSource needs to build an image url.
|
|
29
|
+
*
|
|
30
|
+
* The color and the transparency flag stay separate rather than being joined into one token,
|
|
31
|
+
* because the image sources spell them differently.
|
|
32
|
+
*/
|
|
33
|
+
export interface ResolvedImageServiceSettings {
|
|
34
|
+
/**
|
|
35
|
+
* Compression quality, 0 to 100.
|
|
36
|
+
*/
|
|
37
|
+
quality: number;
|
|
38
|
+
/**
|
|
39
|
+
* A hash free hex, or one of the image service's own keywords.
|
|
40
|
+
*/
|
|
41
|
+
fillColor: string;
|
|
42
|
+
/**
|
|
43
|
+
* Whether transparent areas of the source image are filled too. Not every image source supports
|
|
44
|
+
* this, so it is kept apart from the color rather than encoded into it.
|
|
45
|
+
*/
|
|
46
|
+
fillTransparent: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Resolves the settings for every product image context.
|
|
50
|
+
*
|
|
51
|
+
* Precedence:
|
|
52
|
+
* - Once the app settings are hydrated from a source (admin sync / jsonp), they are authoritative.
|
|
53
|
+
* The configured ratio applies to every context unless that context overrides it.
|
|
54
|
+
* - Before hydration the legacy AppImages resolutions are passed through as-is, with a null ratio.
|
|
55
|
+
* They already carry their own implied proportions, so reverse engineering a ratio from them
|
|
56
|
+
* would only introduce rounding - passing them through unchanged keeps the pre-hydration path
|
|
57
|
+
* identical to the legacy behavior.
|
|
58
|
+
* @param areAppSettingsHydrated Whether the app settings arrived from a source.
|
|
59
|
+
* @param imageSettings The image settings from the store.
|
|
60
|
+
* @returns The resolved settings, keyed by context.
|
|
61
|
+
*/
|
|
62
|
+
export declare const resolveProductImageSettings: (areAppSettingsHydrated: boolean, imageSettings: ImageSettings) => ResolvedProductImageSettings;
|
|
63
|
+
/**
|
|
64
|
+
* Resolves the quality and fill that image urls are built with.
|
|
65
|
+
* @param areAppSettingsHydrated Whether the app settings arrived from a source.
|
|
66
|
+
* @param imageSettings The image settings from the store.
|
|
67
|
+
* @returns The image service settings.
|
|
68
|
+
*/
|
|
69
|
+
export declare const resolveImageServiceSettings: (areAppSettingsHydrated: boolean, imageSettings: ImageSettings) => ResolvedImageServiceSettings;
|
|
70
|
+
//# sourceMappingURL=resolveImageSettings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveImageSettings.d.ts","sourceRoot":"","sources":["../../../settings/helpers/resolveImageSettings.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,eAAe,EACf,aAAa,EACb,mBAAmB,EACpB,MAAM,sBAAsB,CAAC;AA2D9B;;;;;;;GAOG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;OAGG;IACH,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B;;;;OAIG;IACH,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,MAAM,4BAA4B,GACtC,MAAM,CAAC,mBAAmB,EAAE,2BAA2B,CAAC,CAAC;AAE3D;;;;;GAKG;AACH,MAAM,WAAW,4BAA4B;IAC3C;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,eAAe,EAAE,OAAO,CAAC;CAC1B;AA4DD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,2BAA2B,GACtC,wBAAwB,OAAO,EAC/B,eAAe,aAAa,KAC3B,4BAiCF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,GACtC,wBAAwB,OAAO,EAC/B,eAAe,aAAa,KAC3B,4BAyBF,CAAC"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import "core-js/modules/es.array.reduce.js";
|
|
2
|
+
// Imported via its module path rather than the "core" barrel, since that barrel pulls in
|
|
3
|
+
// getFullImageSource which reads these settings back, and would create a circular import.
|
|
4
|
+
import { getThemeSettings } from '@shopgate/engage/core/config/getThemeSettings';
|
|
5
|
+
import { DEFAULT_IMAGE_FILL_COLOR, DEFAULT_IMAGE_FILL_TRANSPARENT, DEFAULT_IMAGE_QUALITY, LEGACY_IMAGE_SETTINGS_KEY, MAX_IMAGE_DIMENSION, PRODUCT_IMAGE_BASE_WIDTHS } from "../constants/imageSettings";
|
|
6
|
+
import { toThumborColor } from "./toThumborColor";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The shape of the legacy AppImages theme settings. Every field is optional - the key is absent
|
|
10
|
+
* entirely in most shops, and undefined in every jest run, since the test theme config mock has no
|
|
11
|
+
* settings at all.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Maps a product image context onto the legacy AppImages key that configured it.
|
|
16
|
+
*/
|
|
17
|
+
const LEGACY_RESOLUTION_KEYS = {
|
|
18
|
+
pdp: 'HeroImage',
|
|
19
|
+
gallery: 'GalleryImage',
|
|
20
|
+
list: 'ListImage'
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The ratio the legacy resolutions are assumed to have when they are not configured at all - the
|
|
25
|
+
* built-in base widths then produce exactly the resolutions the legacy defaults shipped with.
|
|
26
|
+
*/
|
|
27
|
+
const DEFAULT_RATIO = {
|
|
28
|
+
width: 1,
|
|
29
|
+
height: 1
|
|
30
|
+
};
|
|
31
|
+
const PRODUCT_IMAGE_CONTEXTS = Object.keys(PRODUCT_IMAGE_BASE_WIDTHS);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The resolved settings for one product image context: the resolution ladder to request from the
|
|
35
|
+
* image service, and the aspect ratio to apply to the rendered element.
|
|
36
|
+
*
|
|
37
|
+
* ratio is null while the legacy settings are in effect, because those configure explicit
|
|
38
|
+
* dimensions rather than a ratio - consumers then let the Image component derive the ratio from
|
|
39
|
+
* the largest resolution, exactly as before.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The resolved settings for every product image context, keyed by context.
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* What getFullImageSource needs to build an image url.
|
|
48
|
+
*
|
|
49
|
+
* The color and the transparency flag stay separate rather than being joined into one token,
|
|
50
|
+
* because the image sources spell them differently.
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Reads the legacy AppImages settings from the theme configuration.
|
|
55
|
+
* @returns The legacy settings, or an empty object when they are not configured.
|
|
56
|
+
*/
|
|
57
|
+
const getLegacyImageSettings = () => getThemeSettings(LEGACY_IMAGE_SETTINGS_KEY) ?? {};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Whether a ratio part can be calculated with.
|
|
61
|
+
* @param value The value to check.
|
|
62
|
+
* @returns Whether it is usable.
|
|
63
|
+
*/
|
|
64
|
+
const isUsableRatioPart = value => typeof value === 'number' && Number.isFinite(value) && value > 0;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Substitutes a ratio that cannot be calculated with. Anything reading a configured ratio goes
|
|
68
|
+
* through here first - a source may send a half typed value, or none at all.
|
|
69
|
+
* @param ratio The configured ratio.
|
|
70
|
+
* @returns The ratio, or the default when it is unusable.
|
|
71
|
+
*/
|
|
72
|
+
const toUsableRatio = ratio => {
|
|
73
|
+
if (!ratio || !isUsableRatioPart(ratio.width) || !isUsableRatioPart(ratio.height)) {
|
|
74
|
+
return DEFAULT_RATIO;
|
|
75
|
+
}
|
|
76
|
+
return ratio;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Derives a resolution ladder from a context's base widths and an aspect ratio.
|
|
81
|
+
* @param context The product image context.
|
|
82
|
+
* @param ratio The aspect ratio to apply, already passed through toUsableRatio.
|
|
83
|
+
* @returns The resolutions, ordered ascending.
|
|
84
|
+
*/
|
|
85
|
+
const deriveResolutions = (context, ratio) => PRODUCT_IMAGE_BASE_WIDTHS[context].map(baseWidth => {
|
|
86
|
+
const height = Math.max(1, Math.round(baseWidth * ratio.height / ratio.width));
|
|
87
|
+
if (height <= MAX_IMAGE_DIMENSION) {
|
|
88
|
+
return {
|
|
89
|
+
width: baseWidth,
|
|
90
|
+
height
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Scaled down as a whole rather than capping the height alone, so the requested image keeps the
|
|
95
|
+
// shape the layout reserves for it.
|
|
96
|
+
return {
|
|
97
|
+
width: Math.max(1, Math.round(baseWidth * MAX_IMAGE_DIMENSION / height)),
|
|
98
|
+
height: MAX_IMAGE_DIMENSION
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Resolves the settings for every product image context.
|
|
104
|
+
*
|
|
105
|
+
* Precedence:
|
|
106
|
+
* - Once the app settings are hydrated from a source (admin sync / jsonp), they are authoritative.
|
|
107
|
+
* The configured ratio applies to every context unless that context overrides it.
|
|
108
|
+
* - Before hydration the legacy AppImages resolutions are passed through as-is, with a null ratio.
|
|
109
|
+
* They already carry their own implied proportions, so reverse engineering a ratio from them
|
|
110
|
+
* would only introduce rounding - passing them through unchanged keeps the pre-hydration path
|
|
111
|
+
* identical to the legacy behavior.
|
|
112
|
+
* @param areAppSettingsHydrated Whether the app settings arrived from a source.
|
|
113
|
+
* @param imageSettings The image settings from the store.
|
|
114
|
+
* @returns The resolved settings, keyed by context.
|
|
115
|
+
*/
|
|
116
|
+
export const resolveProductImageSettings = (areAppSettingsHydrated, imageSettings) => {
|
|
117
|
+
const legacy = areAppSettingsHydrated ? null : getLegacyImageSettings();
|
|
118
|
+
return PRODUCT_IMAGE_CONTEXTS.reduce((resolved, context) => {
|
|
119
|
+
if (legacy) {
|
|
120
|
+
const legacyResolutions = legacy[LEGACY_RESOLUTION_KEYS[context]];
|
|
121
|
+
return {
|
|
122
|
+
...resolved,
|
|
123
|
+
[context]: {
|
|
124
|
+
resolutions: legacyResolutions?.length ? legacyResolutions : deriveResolutions(context, DEFAULT_RATIO),
|
|
125
|
+
ratio: null
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
const product = imageSettings?.product;
|
|
130
|
+
const ratio = toUsableRatio(product?.[context]?.ratio ?? product?.ratio);
|
|
131
|
+
const resolutions = deriveResolutions(context, ratio);
|
|
132
|
+
const largest = resolutions[resolutions.length - 1];
|
|
133
|
+
return {
|
|
134
|
+
...resolved,
|
|
135
|
+
[context]: {
|
|
136
|
+
resolutions,
|
|
137
|
+
ratio: [largest.width, largest.height]
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}, {});
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Resolves the quality and fill that image urls are built with.
|
|
145
|
+
* @param areAppSettingsHydrated Whether the app settings arrived from a source.
|
|
146
|
+
* @param imageSettings The image settings from the store.
|
|
147
|
+
* @returns The image service settings.
|
|
148
|
+
*/
|
|
149
|
+
export const resolveImageServiceSettings = (areAppSettingsHydrated, imageSettings) => {
|
|
150
|
+
let color;
|
|
151
|
+
let fillTransparent;
|
|
152
|
+
if (areAppSettingsHydrated) {
|
|
153
|
+
color = imageSettings?.fillColor ?? DEFAULT_IMAGE_FILL_COLOR;
|
|
154
|
+
fillTransparent = imageSettings?.fillTransparent ?? DEFAULT_IMAGE_FILL_TRANSPARENT;
|
|
155
|
+
} else {
|
|
156
|
+
// The legacy value packs both parts into one string, e.g. "FFFFFF,1". Its color is normalized
|
|
157
|
+
// like a configured one - theme configurations carry values the image service will not take.
|
|
158
|
+
const {
|
|
159
|
+
fillColor: legacyFillColor = ''
|
|
160
|
+
} = getLegacyImageSettings();
|
|
161
|
+
const [legacyColor, legacyFillTransparent] = legacyFillColor.split(',');
|
|
162
|
+
color = legacyColor ? toThumborColor(legacyColor) : DEFAULT_IMAGE_FILL_COLOR;
|
|
163
|
+
fillTransparent = legacyFillTransparent === undefined ? DEFAULT_IMAGE_FILL_TRANSPARENT : legacyFillTransparent.trim() === '1';
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
// No hydration branch, unlike the fill: the unhydrated slice already holds the legacy quality.
|
|
167
|
+
quality: imageSettings?.quality ?? DEFAULT_IMAGE_QUALITY,
|
|
168
|
+
fillColor: color,
|
|
169
|
+
fillTransparent
|
|
170
|
+
};
|
|
171
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a configured value into a compression quality the image service accepts.
|
|
3
|
+
* @param value The value to convert.
|
|
4
|
+
* @returns A whole number between 1 and 100.
|
|
5
|
+
*/
|
|
6
|
+
export declare const toImageQuality: (value: unknown) => number;
|
|
7
|
+
//# sourceMappingURL=toImageQuality.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toImageQuality.d.ts","sourceRoot":"","sources":["../../../settings/helpers/toImageQuality.ts"],"names":[],"mappings":"AA4BA;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,KAAG,MAY/C,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { logger } from '@shopgate/pwa-core/helpers';
|
|
2
|
+
import { DEFAULT_IMAGE_QUALITY } from "../constants/imageSettings";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The range the image service accepts.
|
|
6
|
+
*/
|
|
7
|
+
const MIN_IMAGE_QUALITY = 1;
|
|
8
|
+
const MAX_IMAGE_QUALITY = 100;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Coerces a configured value into a number.
|
|
12
|
+
* @param value The configured value.
|
|
13
|
+
* @returns The number, or NaN when the value does not express one.
|
|
14
|
+
*/
|
|
15
|
+
const toNumber = value => {
|
|
16
|
+
if (typeof value === 'number') {
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Emptiness is handled here rather than left to Number(), which would read both "" and null as a
|
|
21
|
+
// perfectly valid zero.
|
|
22
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
23
|
+
return Number(value);
|
|
24
|
+
}
|
|
25
|
+
return NaN;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Converts a configured value into a compression quality the image service accepts.
|
|
30
|
+
* @param value The value to convert.
|
|
31
|
+
* @returns A whole number between 1 and 100.
|
|
32
|
+
*/
|
|
33
|
+
export const toImageQuality = value => {
|
|
34
|
+
const quality = toNumber(value);
|
|
35
|
+
if (!Number.isFinite(quality)) {
|
|
36
|
+
logger.warn(`Invalid image quality "${value}", falling back to ${DEFAULT_IMAGE_QUALITY}`);
|
|
37
|
+
return DEFAULT_IMAGE_QUALITY;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Out of range numbers are clamped rather than rejected, so a value that is still being typed
|
|
41
|
+
// degrades into the nearest usable one instead of jumping back to the default. Zero is included
|
|
42
|
+
// in that - the service takes it, but the images it returns are unusable.
|
|
43
|
+
return Math.min(Math.max(Math.round(quality), MIN_IMAGE_QUALITY), MAX_IMAGE_QUALITY);
|
|
44
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a color into the format Thumbor's fill filter expects - a hexadecimal RGB expression
|
|
3
|
+
* without the leading "#", or one of its own keywords.
|
|
4
|
+
*
|
|
5
|
+
* Any alpha channel on the input is dropped: Thumbor's fill color cannot carry one, and Color.hex()
|
|
6
|
+
* returns the opaque six digit form. Transparency is only expressible by configuring the
|
|
7
|
+
* "transparent" keyword explicitly.
|
|
8
|
+
*
|
|
9
|
+
* Everything else is canonicalized to hex rather than passing HTML color names through, so that
|
|
10
|
+
* "white", "#fff" and "rgb(255, 255, 255)" all produce the same url and share a CDN cache entry.
|
|
11
|
+
* @param value The color to convert.
|
|
12
|
+
* @returns The Thumbor color token.
|
|
13
|
+
*/
|
|
14
|
+
export declare const toThumborColor: (value: string) => string;
|
|
15
|
+
//# sourceMappingURL=toThumborColor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toThumborColor.d.ts","sourceRoot":"","sources":["../../../settings/helpers/toThumborColor.ts"],"names":[],"mappings":"AAqBA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,KAAG,MAiB9C,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import "core-js/modules/es.array.includes.js";
|
|
2
|
+
import "core-js/modules/es.string.replace.js";
|
|
3
|
+
import Color from 'color';
|
|
4
|
+
import { logger } from '@shopgate/pwa-core/helpers';
|
|
5
|
+
// Imported via its module path rather than the "styles" barrel, since that barrel pulls in the
|
|
6
|
+
// ThemeProvider and would create a circular import.
|
|
7
|
+
import { isColor } from '@shopgate/engage/styles/theme/utils/color';
|
|
8
|
+
import { DEFAULT_IMAGE_FILL_COLOR } from "../constants/imageSettings";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Values Thumbor's fill filter accepts that are not CSS colors, so they have to bypass the color
|
|
12
|
+
* parser and be passed through untouched.
|
|
13
|
+
* @see https://thumbor.readthedocs.io/en/latest/filling.html
|
|
14
|
+
*/
|
|
15
|
+
const THUMBOR_COLOR_KEYWORDS = ['auto', 'blur', 'transparent'];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A hexadecimal color without the leading "#" - the format the image service itself expects, and
|
|
19
|
+
* therefore what the legacy AppImages config stores. It is not a valid CSS color, so it has to be
|
|
20
|
+
* recognized before the color parser sees it.
|
|
21
|
+
*/
|
|
22
|
+
const BARE_HEX_PATTERN = /^([\da-f]{3}|[\da-f]{4}|[\da-f]{6}|[\da-f]{8})$/i;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Converts a color into the format Thumbor's fill filter expects - a hexadecimal RGB expression
|
|
26
|
+
* without the leading "#", or one of its own keywords.
|
|
27
|
+
*
|
|
28
|
+
* Any alpha channel on the input is dropped: Thumbor's fill color cannot carry one, and Color.hex()
|
|
29
|
+
* returns the opaque six digit form. Transparency is only expressible by configuring the
|
|
30
|
+
* "transparent" keyword explicitly.
|
|
31
|
+
*
|
|
32
|
+
* Everything else is canonicalized to hex rather than passing HTML color names through, so that
|
|
33
|
+
* "white", "#fff" and "rgb(255, 255, 255)" all produce the same url and share a CDN cache entry.
|
|
34
|
+
* @param value The color to convert.
|
|
35
|
+
* @returns The Thumbor color token.
|
|
36
|
+
*/
|
|
37
|
+
export const toThumborColor = value => {
|
|
38
|
+
const trimmed = typeof value === 'string' ? value.trim() : '';
|
|
39
|
+
if (THUMBOR_COLOR_KEYWORDS.includes(trimmed.toLowerCase())) {
|
|
40
|
+
return trimmed.toLowerCase();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// A bare hex is what the image service and the legacy config use, but not something the color
|
|
44
|
+
// parser recognizes, so it gets its "#" back before parsing.
|
|
45
|
+
const parsable = BARE_HEX_PATTERN.test(trimmed) ? `#${trimmed}` : trimmed;
|
|
46
|
+
if (!isColor(parsable)) {
|
|
47
|
+
logger.warn(`Invalid image fill color "${value}", falling back to ${DEFAULT_IMAGE_FILL_COLOR}`);
|
|
48
|
+
return DEFAULT_IMAGE_FILL_COLOR;
|
|
49
|
+
}
|
|
50
|
+
return Color(parsable).hex().replace('#', '').toUpperCase();
|
|
51
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../settings/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useImageServiceSettings, useProductImageSettings } from "./useImageSettings";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ResolvedImageServiceSettings, ResolvedProductImageSettings } from '../helpers';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves the resolutions and aspect ratio to request for every product image context, keyed by
|
|
4
|
+
* `pdp`, `gallery` and `list`.
|
|
5
|
+
*
|
|
6
|
+
* Prefer giving ProductImage a `context` prop, which resolves its own settings. This hook is for
|
|
7
|
+
* the places that render the plain Image component instead, e.g. the fullscreen gallery sliders,
|
|
8
|
+
* which need its unwrapped mode.
|
|
9
|
+
*
|
|
10
|
+
* `ratio` may be null, which the Image component reads as "derive one from the largest
|
|
11
|
+
* resolution" - pass it through rather than substituting a value.
|
|
12
|
+
* @returns The resolved settings, keyed by context.
|
|
13
|
+
*/
|
|
14
|
+
export declare const useProductImageSettings: () => ResolvedProductImageSettings;
|
|
15
|
+
/**
|
|
16
|
+
* Resolves the quality and fill color for image service urls, in the shape getFullImageSource
|
|
17
|
+
* takes as its third argument.
|
|
18
|
+
*
|
|
19
|
+
* Components rarely need this - the Image component already calls it and applies the result. It is
|
|
20
|
+
* separate from useProductImageSettings because both values apply to every image the service
|
|
21
|
+
* serves, not just product images.
|
|
22
|
+
*
|
|
23
|
+
* `fillColor` is a hash free hex, not a CSS color, and the transparency flag is a separate
|
|
24
|
+
* `fillTransparent` - the image sources spell the pair differently.
|
|
25
|
+
* @returns The image service settings.
|
|
26
|
+
*/
|
|
27
|
+
export declare const useImageServiceSettings: () => ResolvedImageServiceSettings;
|
|
28
|
+
//# sourceMappingURL=useImageSettings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useImageSettings.d.ts","sourceRoot":"","sources":["../../../settings/hooks/useImageSettings.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,4BAA4B,EAC5B,4BAA4B,EAC7B,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,uBAAuB,QAAO,4BAQ1C,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,uBAAuB,QAAO,4BAQ1C,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { useSelector } from 'react-redux';
|
|
3
|
+
import { getAreAppSettingsHydrated, getImageSettings } from "../selectors/appSettings";
|
|
4
|
+
import { resolveImageServiceSettings, resolveProductImageSettings } from "../helpers";
|
|
5
|
+
/**
|
|
6
|
+
* Resolves the resolutions and aspect ratio to request for every product image context, keyed by
|
|
7
|
+
* `pdp`, `gallery` and `list`.
|
|
8
|
+
*
|
|
9
|
+
* Prefer giving ProductImage a `context` prop, which resolves its own settings. This hook is for
|
|
10
|
+
* the places that render the plain Image component instead, e.g. the fullscreen gallery sliders,
|
|
11
|
+
* which need its unwrapped mode.
|
|
12
|
+
*
|
|
13
|
+
* `ratio` may be null, which the Image component reads as "derive one from the largest
|
|
14
|
+
* resolution" - pass it through rather than substituting a value.
|
|
15
|
+
* @returns The resolved settings, keyed by context.
|
|
16
|
+
*/
|
|
17
|
+
export const useProductImageSettings = () => {
|
|
18
|
+
const areAppSettingsHydrated = useSelector(getAreAppSettingsHydrated);
|
|
19
|
+
const imageSettings = useSelector(getImageSettings);
|
|
20
|
+
return useMemo(() => resolveProductImageSettings(areAppSettingsHydrated, imageSettings), [areAppSettingsHydrated, imageSettings]);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Resolves the quality and fill color for image service urls, in the shape getFullImageSource
|
|
25
|
+
* takes as its third argument.
|
|
26
|
+
*
|
|
27
|
+
* Components rarely need this - the Image component already calls it and applies the result. It is
|
|
28
|
+
* separate from useProductImageSettings because both values apply to every image the service
|
|
29
|
+
* serves, not just product images.
|
|
30
|
+
*
|
|
31
|
+
* `fillColor` is a hash free hex, not a CSS color, and the transparency flag is a separate
|
|
32
|
+
* `fillTransparent` - the image sources spell the pair differently.
|
|
33
|
+
* @returns The image service settings.
|
|
34
|
+
*/
|
|
35
|
+
export const useImageServiceSettings = () => {
|
|
36
|
+
const areAppSettingsHydrated = useSelector(getAreAppSettingsHydrated);
|
|
37
|
+
const imageSettings = useSelector(getImageSettings);
|
|
38
|
+
return useMemo(() => resolveImageServiceSettings(areAppSettingsHydrated, imageSettings), [areAppSettingsHydrated, imageSettings]);
|
|
39
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appSettings.d.ts","sourceRoot":"","sources":["../../../settings/reducers/appSettings.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"appSettings.d.ts","sourceRoot":"","sources":["../../../settings/reducers/appSettings.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAa/E,KAAK,iBAAiB,GAAG,wBAAwB,GAAG,aAAa,CAAC;AAQlE;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,gBA+ClC,CAAC;AAEF;;;;;GAKG;AACH,QAAA,MAAM,WAAW,EAAE,OAAO,CAAC,gBAAgB,EAAE,iBAAiB,CAgC5D,CAAC;AAEH,eAAe,WAAW,CAAC"}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import _merge from "lodash/merge";
|
|
2
|
+
import _cloneDeep from "lodash/cloneDeep";
|
|
2
3
|
import { produce } from 'immer';
|
|
3
4
|
import { RECEIVE_APP_SETTINGS } from "../constants/appSettings";
|
|
5
|
+
import { DEFAULT_IMAGE_FILL_COLOR, DEFAULT_IMAGE_FILL_TRANSPARENT, DEFAULT_IMAGE_QUALITY, DEFAULT_SHOW_INNER_SHADOW } from "../constants/imageSettings";
|
|
6
|
+
// Deliberately not exported from the "helpers" barrel - they normalize values on their way into
|
|
7
|
+
// the slice, which is nothing a consumer of the settings needs.
|
|
8
|
+
import { toImageQuality } from "../helpers/toImageQuality";
|
|
9
|
+
import { toThumborColor } from "../helpers/toThumborColor";
|
|
4
10
|
const isReceiveAppSettingsAction = action => action.type === RECEIVE_APP_SETTINGS && 'settings' in action;
|
|
5
11
|
|
|
6
12
|
/**
|
|
@@ -17,8 +23,42 @@ export const DEFAULT_APP_SETTINGS = {
|
|
|
17
23
|
transition: 'fade',
|
|
18
24
|
fixed: {
|
|
19
25
|
borderEnabled: true
|
|
26
|
+
},
|
|
27
|
+
favorites: {
|
|
28
|
+
showCounter: true
|
|
20
29
|
}
|
|
21
30
|
}
|
|
31
|
+
},
|
|
32
|
+
product: {
|
|
33
|
+
grid: {
|
|
34
|
+
columns: {
|
|
35
|
+
small: 2,
|
|
36
|
+
large: 4
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
slider: {
|
|
40
|
+
slidesPerView: {
|
|
41
|
+
small: 2.3,
|
|
42
|
+
medium: 3.3,
|
|
43
|
+
large: 4.3
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
rating: {
|
|
47
|
+
showEmptyStars: false
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
images: {
|
|
51
|
+
quality: DEFAULT_IMAGE_QUALITY,
|
|
52
|
+
// Already in the image service's format, so the unhydrated path needs no conversion.
|
|
53
|
+
fillColor: DEFAULT_IMAGE_FILL_COLOR,
|
|
54
|
+
fillTransparent: DEFAULT_IMAGE_FILL_TRANSPARENT,
|
|
55
|
+
product: {
|
|
56
|
+
ratio: {
|
|
57
|
+
width: 1,
|
|
58
|
+
height: 1
|
|
59
|
+
},
|
|
60
|
+
showInnerShadow: DEFAULT_SHOW_INNER_SHADOW
|
|
61
|
+
}
|
|
22
62
|
}
|
|
23
63
|
};
|
|
24
64
|
|
|
@@ -32,10 +72,32 @@ const appSettings = (state, action = {
|
|
|
32
72
|
type: ''
|
|
33
73
|
}) => produce(state ?? DEFAULT_APP_SETTINGS, draft => {
|
|
34
74
|
if (isReceiveAppSettingsAction(action)) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
75
|
+
const {
|
|
76
|
+
images
|
|
77
|
+
} = action.settings ?? {};
|
|
78
|
+
|
|
79
|
+
// A cleared branch is replaced outright, then kept out of the merge below. Merging the defaults
|
|
80
|
+
// in would only add to it, leaving whatever an earlier payload configured in place.
|
|
81
|
+
if (images === null) {
|
|
82
|
+
draft.images = _cloneDeep(DEFAULT_APP_SETTINGS.images);
|
|
83
|
+
} else if (images?.product === null) {
|
|
84
|
+
draft.images.product = _cloneDeep(DEFAULT_APP_SETTINGS.images.product);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Deep merged so a partial payload keeps the defaults for whatever the source omits.
|
|
88
|
+
_merge(draft, {
|
|
89
|
+
...action.settings,
|
|
90
|
+
images: images === null ? undefined : {
|
|
91
|
+
...images,
|
|
92
|
+
product: images?.product ?? undefined
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Converted on the way in, so the slice holds wire ready values and every image url does not
|
|
97
|
+
// pay for it. Unconditional, because an empty string or a null reaches here like any other
|
|
98
|
+
// value - both converters are idempotent.
|
|
99
|
+
draft.images.fillColor = toThumborColor(draft.images.fillColor);
|
|
100
|
+
draft.images.quality = toImageQuality(draft.images.quality);
|
|
39
101
|
draft.isHydrated = true;
|
|
40
102
|
}
|
|
41
103
|
});
|
|
@@ -3,10 +3,10 @@ export declare const settings: import("redux").Reducer<{
|
|
|
3
3
|
shopSettings: Object;
|
|
4
4
|
merchantSettings: Object;
|
|
5
5
|
appSettings: import("../types/appSettings").AppSettingsSlice;
|
|
6
|
-
}, (
|
|
6
|
+
}, ({
|
|
7
7
|
type: string;
|
|
8
8
|
settings: import("../types/appSettings").AppSettings;
|
|
9
|
-
}) | import("redux").Action<string>, Partial<{
|
|
9
|
+
} | import("redux").UnknownAction) | import("redux").Action<string>, Partial<{
|
|
10
10
|
config: Object | undefined;
|
|
11
11
|
shopSettings: Object | undefined;
|
|
12
12
|
merchantSettings: Object | undefined;
|