@shopgate/engage 7.29.5 → 7.29.6-beta.2
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/core/hooks/index.js +1 -1
- package/core/hooks/useLocalStorage.d.ts +8 -0
- package/core/hooks/useLocalStorage.js +9 -0
- package/package.json +7 -7
- package/page/components/WidgetVideo/WidgetVideo.js +18 -0
- package/page/components/WidgetVideo/index.js +1 -0
- package/page/widgets/HeroBanner/HeroBanner.js +2 -2
- package/page/widgets/HeroBanner/hooks.js +8 -6
- package/page/widgets/NestedCategoryFilter/NestedCategoryFilter.js +13 -0
- package/page/widgets/NestedCategoryFilter/components/Picker/components/Sheet/index.js +11 -0
- package/page/widgets/NestedCategoryFilter/components/Picker/components/SheetItem/index.js +8 -0
- package/page/widgets/NestedCategoryFilter/components/Picker/index.js +9 -0
- package/page/widgets/NestedCategoryFilter/hooks.js +18 -0
- package/page/widgets/NestedCategoryFilter/index.js +1 -0
- package/page/widgets/Video/Video.js +2 -7
- package/page/widgets/Video/hooks.js +1 -1
- package/page/widgets/index.js +1 -1
- package/page/widgets/widgets.json +3 -0
package/core/hooks/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from"./layout";export*from"./events";export{useAsyncMemo}from"./useAsyncMemo";export{useRoute}from"./useRoute";export{useTheme}from"./useTheme";export{useApp}from"./useApp";export{useCurrentProduct}from"./useCurrentProduct";export{useNavigation}from"./useNavigation";export{usePageConfig}from"./usePageConfig";export{usePageSettings}from"./usePageSettings";export{useWidgetConfig}from"./useWidgetConfig";export{useWidgetSettings}from"./useWidgetSettings";export{useWidgetStyles}from"./useWidgetStyles";export{useThemeResources,useThemeComponents,useThemeWidgets}from"./useThemeResources";export*from"./html";export{usePrevious}from"./usePrevious";export{useResponsiveValue}from'@shopgate/engage/components/ResponsiveContainer/hooks';
|
|
1
|
+
export*from"./layout";export*from"./events";export{useAsyncMemo}from"./useAsyncMemo";export{useRoute}from"./useRoute";export{useTheme}from"./useTheme";export{useApp}from"./useApp";export{useCurrentProduct}from"./useCurrentProduct";export{useNavigation}from"./useNavigation";export{usePageConfig}from"./usePageConfig";export{usePageSettings}from"./usePageSettings";export{useWidgetConfig}from"./useWidgetConfig";export{useWidgetSettings}from"./useWidgetSettings";export{useWidgetStyles}from"./useWidgetStyles";export{useThemeResources,useThemeComponents,useThemeWidgets}from"./useThemeResources";export*from"./html";export{usePrevious}from"./usePrevious";export{default as useLocalStorage}from"./useLocalStorage";export{useResponsiveValue}from'@shopgate/engage/components/ResponsiveContainer/hooks';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom hook to manage and persist state in localStorage.
|
|
3
|
+
* @param key The key under which the value is stored in localStorage.
|
|
4
|
+
* @returns A tuple containing the stored value and a function to update it.
|
|
5
|
+
*/
|
|
6
|
+
export default function useLocalStorage<T = any>(
|
|
7
|
+
key: string
|
|
8
|
+
): [T | null, (value: T) => void];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
function _slicedToArray(arr,i){return _arrayWithHoles(arr)||_iterableToArrayLimit(arr,i)||_nonIterableRest();}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance");}function _iterableToArrayLimit(arr,i){var _arr=[];var _n=true;var _d=false;var _e=undefined;try{for(var _i=arr[Symbol.iterator](),_s;!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break;}}catch(err){_d=true;_e=err;}finally{try{if(!_n&&_i["return"]!=null)_i["return"]();}finally{if(_d)throw _e;}}return _arr;}function _arrayWithHoles(arr){if(Array.isArray(arr))return arr;}import{useCallback,useEffect,useState}from'react';import{appConfig}from'@shopgate/engage';var appId=appConfig.appId;/**
|
|
2
|
+
* Custom hook to manage and persist state in localStorage.
|
|
3
|
+
* @param {string} key The key under which the value is stored in localStorage.
|
|
4
|
+
* @returns {[any, Function]} A tuple containing the stored value and a function to update it.
|
|
5
|
+
*/export default function useLocalStorage(key){var readValue=useCallback(function(){try{return JSON.parse(window.localStorage.getItem("".concat(appId,"_").concat(key)));}catch(error){return null;}},[key]);var _useState=useState(readValue),_useState2=_slicedToArray(_useState,2),storedValue=_useState2[0],setStoredValue=_useState2[1];/**
|
|
6
|
+
* Sets a new value both in state and in localStorage.
|
|
7
|
+
* @param {any} value The new value to store.
|
|
8
|
+
*/var setValue=useCallback(function(value){try{setStoredValue(value);window.localStorage.setItem("".concat(appId,"_").concat(key),JSON.stringify(value));}catch(error){//
|
|
9
|
+
}},[key]);useEffect(function(){setValue(readValue());},[readValue,setValue]);return[storedValue,setValue];}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shopgate/engage",
|
|
3
|
-
"version": "7.29.
|
|
3
|
+
"version": "7.29.6-beta.2",
|
|
4
4
|
"description": "Shopgate's ENGAGE library.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Shopgate <support@shopgate.com>",
|
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@emotion/react": "^11.14.0",
|
|
19
19
|
"@shopgate/native-modules": "1.0.0-beta.25",
|
|
20
|
-
"@shopgate/pwa-common": "7.29.
|
|
21
|
-
"@shopgate/pwa-common-commerce": "7.29.
|
|
22
|
-
"@shopgate/pwa-core": "7.29.
|
|
23
|
-
"@shopgate/pwa-ui-ios": "7.29.
|
|
24
|
-
"@shopgate/pwa-ui-material": "7.29.
|
|
25
|
-
"@shopgate/pwa-ui-shared": "7.29.
|
|
20
|
+
"@shopgate/pwa-common": "7.29.6-beta.2",
|
|
21
|
+
"@shopgate/pwa-common-commerce": "7.29.6-beta.2",
|
|
22
|
+
"@shopgate/pwa-core": "7.29.6-beta.2",
|
|
23
|
+
"@shopgate/pwa-ui-ios": "7.29.6-beta.2",
|
|
24
|
+
"@shopgate/pwa-ui-material": "7.29.6-beta.2",
|
|
25
|
+
"@shopgate/pwa-ui-shared": "7.29.6-beta.2",
|
|
26
26
|
"@stripe/react-stripe-js": "^1.16.5",
|
|
27
27
|
"@stripe/stripe-js": "^1.3.1",
|
|
28
28
|
"@virtuous/conductor": "~2.5.0",
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
function _defineProperty(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true});}else{obj[key]=value;}return obj;}function _slicedToArray(arr,i){return _arrayWithHoles(arr)||_iterableToArrayLimit(arr,i)||_nonIterableRest();}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance");}function _iterableToArrayLimit(arr,i){var _arr=[];var _n=true;var _d=false;var _e=undefined;try{for(var _i=arr[Symbol.iterator](),_s;!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break;}}catch(err){_d=true;_e=err;}finally{try{if(!_n&&_i["return"]!=null)_i["return"]();}finally{if(_d)throw _e;}}return _arr;}function _arrayWithHoles(arr){if(Array.isArray(arr))return arr;}import React,{useEffect,useMemo,useState}from'react';import{makeStyles}from'@shopgate/engage/styles';import{useReduceMotion}from'@shopgate/engage/a11y/hooks';import{usePrevious,useAppEventOnReturnFromBackground}from'@shopgate/engage/core/hooks';import{ConditionalWrapper,Link}from'@shopgate/engage/components';import PropTypes from'prop-types';import{isHttpsUrl}from"../../helpers";var useStyles=makeStyles()(function(_theme,_ref){var borderRadius=_ref.borderRadius;return{root:{width:'100%',display:'flex',overflow:'hidden',borderRadius:borderRadius},video:{// Add additional pixels to the width to prevent visible horizontal hairlines on some browsers
|
|
2
|
+
width:'calc(100% + 3px)',display:'flex'},banner:{position:'absolute',width:'100%',height:'100%',objectFit:'cover'},bannerContainer:{position:'absolute',height:'100%',top:0,left:0,justifyContent:'center',alignItems:'center'}};});/**
|
|
3
|
+
* The WidgetVideo component is used to display a video in a widget.
|
|
4
|
+
* @param {Object} props The component props.
|
|
5
|
+
* @param {boolean} props.isBanner Whether the video is used in a banner.
|
|
6
|
+
* @param {string} props.url The video URL.
|
|
7
|
+
* @param {boolean} props.muted Whether the video is muted.
|
|
8
|
+
* @param {boolean} props.loop Whether the video is looping.
|
|
9
|
+
* @param {boolean} props.controls Whether the video controls are shown.
|
|
10
|
+
* @param {boolean} props.autoplay Whether the video should autoplay.
|
|
11
|
+
* @param {number} props.borderRadius The border radius value.
|
|
12
|
+
* @param {string} props.link The link URL.
|
|
13
|
+
* @returns {JSX.Element}
|
|
14
|
+
*/var WidgetVideo=function WidgetVideo(_ref2){var isBanner=_ref2.isBanner,url=_ref2.url,muted=_ref2.muted,loop=_ref2.loop,controls=_ref2.controls,autoplay=_ref2.autoplay,borderRadius=_ref2.borderRadius,link=_ref2.link;var reduceMotion=useReduceMotion();var autoplayValue=reduceMotion?false:isBanner||autoplay;var _useStyles=useStyles({borderRadius:borderRadius}),classes=_useStyles.classes,cx=_useStyles.cx;var _useState=useState(false),_useState2=_slicedToArray(_useState,2),hasError=_useState2[0],setHasError=_useState2[1];var videoRef=React.useRef(null);var prevUrl=usePrevious(url);var isValidUrl=useMemo(function(){return url?isHttpsUrl(url):false;},[url]);var showControls=useMemo(function(){if(link||isBanner){// When a link is set we never show controls to avoid side effects due to two clickable areas.
|
|
15
|
+
return false;}return!autoplayValue||reduceMotion?true:controls;},[autoplayValue,controls,isBanner,link,reduceMotion]);// Resume video playback when app returned from background
|
|
16
|
+
useAppEventOnReturnFromBackground(function(){if(!videoRef.current||reduceMotion||!autoplayValue){return;}videoRef.current.play();});useEffect(function(){if(!videoRef.current){return;}if(reduceMotion){// Pause playback when reduced motion settings changed after video was rendered
|
|
17
|
+
videoRef.current.pause();return;}if(autoplayValue){videoRef.current.play();}else{videoRef.current.pause();videoRef.current.currentTime=0;}},[autoplayValue,reduceMotion]);useEffect(function(){if(!url||url!==prevUrl){setHasError(false);}},[hasError,prevUrl,url]);if(!url||hasError||!isValidUrl)return null;return React.createElement("div",{className:cx(classes.root,_defineProperty({},classes.bannerContainer,isBanner))},React.createElement(ConditionalWrapper,{condition:!!link,wrapper:function wrapper(children){return React.createElement(Link,{href:link},children);}},React.createElement("video",{ref:videoRef// Set play position to 0.001s to guarantee that there is always a frame shown
|
|
18
|
+
,src:"".concat(url,"#t=0.001"),muted:isBanner?true:muted,controls:showControls,autoPlay:autoplayValue,className:cx(classes.video,_defineProperty({},classes.banner,isBanner)),preload:"auto",playsInline:true,loop:isBanner?true:loop,"aria-hidden":true,onError:function onError(){setHasError(true);}},React.createElement("track",{kind:"captions",src:"",srcLang:"de",label:"Deutsch"}))));};WidgetVideo.defaultProps={isBanner:false,link:null,url:null,muted:false,loop:false,controls:false,autoplay:false,borderRadius:0};export default WidgetVideo;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{default}from"./WidgetVideo";
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
function _slicedToArray(arr,i){return _arrayWithHoles(arr)||_iterableToArrayLimit(arr,i)||_nonIterableRest();}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance");}function _iterableToArrayLimit(arr,i){var _arr=[];var _n=true;var _d=false;var _e=undefined;try{for(var _i=arr[Symbol.iterator](),_s;!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break;}}catch(err){_d=true;_e=err;}finally{try{if(!_n&&_i["return"]!=null)_i["return"]();}finally{if(_d)throw _e;}}return _arr;}function _arrayWithHoles(arr){if(Array.isArray(arr))return arr;}function _defineProperty(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true});}else{obj[key]=value;}return obj;}import React,{useCallback,useState,useMemo}from'react';import{ConditionalWrapper,Link}from'@shopgate/engage/components';import{WidgetRichText,ResponsiveWidgetImage}from'@shopgate/engage/page/components';import{makeStyles}from'@shopgate/engage/styles';import{useHeroBannerWidget}from"./hooks";var useStyles=makeStyles()(function(theme){return{link:{width:'100%'},content:_defineProperty(_defineProperty({width:'100%',position:'relative',display:'flex',alignItems:'center',minHeight:300},theme.breakpoints.up('md'),{minHeight:400}),"overflow",'hidden'),richText:{position:'relative',zIndex:2,padding:theme.spacing(2)},imageContainer:{position:'absolute',width:'100%',height:'100%',top:0,left:0}};});/**
|
|
1
|
+
function _slicedToArray(arr,i){return _arrayWithHoles(arr)||_iterableToArrayLimit(arr,i)||_nonIterableRest();}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance");}function _iterableToArrayLimit(arr,i){var _arr=[];var _n=true;var _d=false;var _e=undefined;try{for(var _i=arr[Symbol.iterator](),_s;!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break;}}catch(err){_d=true;_e=err;}finally{try{if(!_n&&_i["return"]!=null)_i["return"]();}finally{if(_d)throw _e;}}return _arr;}function _arrayWithHoles(arr){if(Array.isArray(arr))return arr;}function _defineProperty(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true});}else{obj[key]=value;}return obj;}import React,{useCallback,useState,useMemo}from'react';import{ConditionalWrapper,Link}from'@shopgate/engage/components';import{WidgetRichText,ResponsiveWidgetImage}from'@shopgate/engage/page/components';import{makeStyles}from'@shopgate/engage/styles';import{useHeroBannerWidget}from"./hooks";import WidgetVideo from"../../components/WidgetVideo";var useStyles=makeStyles()(function(theme){return{link:{width:'100%'},content:_defineProperty(_defineProperty({width:'100%',position:'relative',display:'flex',alignItems:'center',minHeight:300},theme.breakpoints.up('md'),{minHeight:400}),"overflow",'hidden'),richText:{position:'relative',zIndex:2,padding:theme.spacing(2)},imageContainer:{position:'absolute',width:'100%',height:'100%',top:0,left:0}};});/**
|
|
2
2
|
* @returns {JSX.Element}
|
|
3
|
-
*/var HeroBanner=function HeroBanner(){var _useHeroBannerWidget=useHeroBannerWidget(),text=_useHeroBannerWidget.text,
|
|
3
|
+
*/var HeroBanner=function HeroBanner(){var _useHeroBannerWidget=useHeroBannerWidget(),text=_useHeroBannerWidget.text,altText=_useHeroBannerWidget.altText,link=_useHeroBannerWidget.link,borderRadius=_useHeroBannerWidget.borderRadius,parallax=_useHeroBannerWidget.parallax,_useHeroBannerWidget$=_useHeroBannerWidget.imageFit,imageFit=_useHeroBannerWidget$===void 0?'fillAndCrop':_useHeroBannerWidget$,mediaType=_useHeroBannerWidget.mediaType,mediaUrl=_useHeroBannerWidget.mediaUrl;var _useStyles=useStyles(),classes=_useStyles.classes;var _useState=useState(null),_useState2=_slicedToArray(_useState,2),aspectRatio=_useState2[0],setAspectRatio=_useState2[1];var contentStyle=useMemo(function(){if(imageFit!=='showFull'){return null;}return{aspectRatio:aspectRatio,minHeight:'unset'};},[aspectRatio,imageFit]);var handleImageRatioChange=useCallback(function(ratio){setAspectRatio(ratio);},[]);return React.createElement(ConditionalWrapper,{condition:!!link,wrapper:function wrapper(children){return React.createElement(Link,{href:link,className:classes.link},children);}},React.createElement("div",{className:classes.content,style:contentStyle},React.createElement(WidgetRichText,{content:text,className:classes.richText}),React.createElement("div",{className:classes.imageContainer},mediaType==='image'?React.createElement(ResponsiveWidgetImage,{src:mediaUrl,alt:altText,borderRadius:borderRadius,enableParallax:parallax,isBanner:imageFit==='fillAndCrop',onImageRatioChange:handleImageRatioChange}):null,mediaType==='video'?React.createElement(WidgetVideo,{isBanner:true,url:mediaUrl,borderRadius:borderRadius}):null)));};export default HeroBanner;
|
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
var _excluded=["borderRadius","borderRadiusCustom"];function _extends(){_extends=Object.assign||function(target){for(var i=1;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(Object.prototype.hasOwnProperty.call(source,key)){target[key]=source[key];}}}return target;};return _extends.apply(this,arguments);}function _objectWithoutProperties(source,excluded){if(source==null)return{};var target=_objectWithoutPropertiesLoose(source,excluded);var key,i;if(Object.getOwnPropertySymbols){var sourceSymbolKeys=Object.getOwnPropertySymbols(source);for(i=0;i<sourceSymbolKeys.length;i++){key=sourceSymbolKeys[i];if(excluded.indexOf(key)>=0)continue;if(!Object.prototype.propertyIsEnumerable.call(source,key))continue;target[key]=source[key];}}return target;}function _objectWithoutPropertiesLoose(source,excluded){if(source==null)return{};var target={};var sourceKeys=Object.keys(source);var key,i;for(i=0;i<sourceKeys.length;i++){key=sourceKeys[i];if(excluded.indexOf(key)>=0)continue;target[key]=source[key];}return target;}import{useWidget}from'@shopgate/engage/page/hooks';import{resolveBorderRadiusFromWidgetConfig}from"../../helpers";/**
|
|
1
|
+
var _excluded=["borderRadius","borderRadiusCustom","mediaType","backgroundImage","url"];function _extends(){_extends=Object.assign||function(target){for(var i=1;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(Object.prototype.hasOwnProperty.call(source,key)){target[key]=source[key];}}}return target;};return _extends.apply(this,arguments);}function _objectWithoutProperties(source,excluded){if(source==null)return{};var target=_objectWithoutPropertiesLoose(source,excluded);var key,i;if(Object.getOwnPropertySymbols){var sourceSymbolKeys=Object.getOwnPropertySymbols(source);for(i=0;i<sourceSymbolKeys.length;i++){key=sourceSymbolKeys[i];if(excluded.indexOf(key)>=0)continue;if(!Object.prototype.propertyIsEnumerable.call(source,key))continue;target[key]=source[key];}}return target;}function _objectWithoutPropertiesLoose(source,excluded){if(source==null)return{};var target={};var sourceKeys=Object.keys(source);var key,i;for(i=0;i<sourceKeys.length;i++){key=sourceKeys[i];if(excluded.indexOf(key)>=0)continue;target[key]=source[key];}return target;}import{useWidget}from'@shopgate/engage/page/hooks';import{resolveBorderRadiusFromWidgetConfig}from"../../helpers";/**
|
|
2
2
|
* @typedef {Object} HeroBanner
|
|
3
3
|
* @property {string} text Banner text content
|
|
4
|
-
* @property {Object} backgroundImage Banner background image
|
|
5
|
-
* @property {string} backgroundImage.url Banner background image URL
|
|
6
|
-
* @property {string} backgroundImage.alt Banner background image alt text
|
|
7
4
|
* @property {string} link Optional banner link
|
|
8
5
|
* @property {"default"|"none"|"rounded"|"custom"} borderRadius The border radius option.
|
|
9
6
|
* @property {number} [borderRadiusCustom] The custom border radius value.
|
|
10
7
|
* @property {boolean} parallax Whether to apply a parallax effect to the image
|
|
8
|
+
* @property {Object} backgroundImage Banner background image
|
|
9
|
+
* @property {string} backgroundImage.url Banner background image URL
|
|
10
|
+
* @property {string} backgroundImage.alt Banner background image alt text
|
|
11
11
|
* @property {"fillAndCrop"|"showFull"} imageFit How the image should be displayed
|
|
12
|
+
* @property {"image"|"video"} mediaType The type of media to display
|
|
13
|
+
* @property {string} url The URL of the media to display
|
|
12
14
|
*/ /**
|
|
13
15
|
* @typedef {ReturnType< typeof import('@shopgate/engage/page/hooks')
|
|
14
16
|
* .useWidget<HeroBanner> >} UseWidgetReturnType
|
|
15
17
|
*/ // eslint-disable-next-line valid-jsdoc
|
|
16
18
|
/**
|
|
17
|
-
* Hook to access the
|
|
18
|
-
*/export var useHeroBannerWidget=function useHeroBannerWidget(){/** @type {UseWidgetReturnType} */var _useWidget=useWidget(),config=_useWidget.config;var _ref=config||{},borderRadius=_ref.borderRadius,borderRadiusCustom=_ref.borderRadiusCustom,rest=_objectWithoutProperties(_ref,_excluded);var borderRadiusResolved=resolveBorderRadiusFromWidgetConfig({borderRadius:borderRadius,borderRadiusCustom:borderRadiusCustom});return _extends({},rest,{borderRadius:borderRadiusResolved});};
|
|
19
|
+
* Hook to access the hero banner widget configuration and data.
|
|
20
|
+
*/export var useHeroBannerWidget=function useHeroBannerWidget(){/** @type {UseWidgetReturnType} */var _useWidget=useWidget(),config=_useWidget.config;var _ref=config||{},borderRadius=_ref.borderRadius,borderRadiusCustom=_ref.borderRadiusCustom,_ref$mediaType=_ref.mediaType,mediaType=_ref$mediaType===void 0?'image':_ref$mediaType,backgroundImage=_ref.backgroundImage,url=_ref.url,rest=_objectWithoutProperties(_ref,_excluded);var mediaUrl;if(mediaType==='video'){mediaUrl=url;}else if(mediaType==='image'){mediaUrl=backgroundImage===null||backgroundImage===void 0?void 0:backgroundImage.url;}var borderRadiusResolved=resolveBorderRadiusFromWidgetConfig({borderRadius:borderRadius,borderRadiusCustom:borderRadiusCustom});return _extends({},rest,{mediaType:mediaType,mediaUrl:mediaUrl,altText:(backgroundImage===null||backgroundImage===void 0?void 0:backgroundImage.alt)||'',borderRadius:borderRadiusResolved});};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
var _excluded=["maxDepth","category","code","showHeadline","headline","rememberSelection"];function _defineProperty(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true});}else{obj[key]=value;}return obj;}function _slicedToArray(arr,i){return _arrayWithHoles(arr)||_iterableToArrayLimit(arr,i)||_nonIterableRest();}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance");}function _iterableToArrayLimit(arr,i){var _arr=[];var _n=true;var _d=false;var _e=undefined;try{for(var _i=arr[Symbol.iterator](),_s;!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break;}}catch(err){_d=true;_e=err;}finally{try{if(!_n&&_i["return"]!=null)_i["return"]();}finally{if(_d)throw _e;}}return _arr;}function _arrayWithHoles(arr){if(Array.isArray(arr))return arr;}function _objectWithoutProperties(source,excluded){if(source==null)return{};var target=_objectWithoutPropertiesLoose(source,excluded);var key,i;if(Object.getOwnPropertySymbols){var sourceSymbolKeys=Object.getOwnPropertySymbols(source);for(i=0;i<sourceSymbolKeys.length;i++){key=sourceSymbolKeys[i];if(excluded.indexOf(key)>=0)continue;if(!Object.prototype.propertyIsEnumerable.call(source,key))continue;target[key]=source[key];}}return target;}function _objectWithoutPropertiesLoose(source,excluded){if(source==null)return{};var target={};var sourceKeys=Object.keys(source);var key,i;for(i=0;i<sourceKeys.length;i++){key=sourceKeys[i];if(excluded.indexOf(key)>=0)continue;target[key]=source[key];}return target;}import React,{useCallback,useEffect,useMemo,useState}from'react';import{I18n,ButtonLink}from'@shopgate/engage/components';import{bin2hex}from'@shopgate/engage/core/helpers';import{CATEGORY_PATH}from'@shopgate/engage/category';import{makeStyles}from'@shopgate/engage/styles';import{themeConfig}from'@shopgate/engage';import{useRoute,usePrevious,useLocalStorage}from'@shopgate/engage/core/hooks';import{router}from'@virtuous/conductor';import CategoryPicker from"./components/Picker";import{useNestedCategoryFilterWidget}from"./hooks";import WidgetHeadline from"../../components/WidgetHeadline";var colors=themeConfig.colors,variables=themeConfig.variables;var useStyles=makeStyles()({container:{background:colors.light,display:'flex',flexDirection:'column',paddingBottom:variables.gap.big},buttonContainer:{padding:"0 ".concat(variables.gap.big,"px")},button:{width:'100%'}});/**
|
|
2
|
+
* The NestedCategoryFilter component
|
|
3
|
+
* @returns {JSX.Element}
|
|
4
|
+
*/var NestedCategoryFilter=function NestedCategoryFilter(){var _useStyles=useStyles(),classes=_useStyles.classes;var _useRoute=useRoute(),routeState=_useRoute.state,routeId=_useRoute.id;var _useNestedCategoryFil=useNestedCategoryFilterWidget(),_useNestedCategoryFil2=_useNestedCategoryFil.maxDepth,maxDepth=_useNestedCategoryFil2===void 0?3:_useNestedCategoryFil2,_useNestedCategoryFil3=_useNestedCategoryFil.category,category=_useNestedCategoryFil3===void 0?'':_useNestedCategoryFil3,code=_useNestedCategoryFil.code,showHeadline=_useNestedCategoryFil.showHeadline,headline=_useNestedCategoryFil.headline,rememberSelection=_useNestedCategoryFil.rememberSelection,labels=_objectWithoutProperties(_useNestedCategoryFil,_excluded);var prevCategory=usePrevious(category);var LOCAL_STORAGE_KEY="nestedCategoryFilterState-".concat(code);var _useLocalStorage=useLocalStorage(LOCAL_STORAGE_KEY),_useLocalStorage2=_slicedToArray(_useLocalStorage,2),localStorageState=_useLocalStorage2[0],setLocalStorageState=_useLocalStorage2[1];var defaultState=useMemo(function(){return{buttonCategoryId:null,pickers:[{categoryId:category,selectedId:null}]};},[category]);var initialState=useMemo(function(){if(rememberSelection&&Array.isArray(localStorageState===null||localStorageState===void 0?void 0:localStorageState.pickers)&&localStorageState.pickers.length>0){var _localStorageState$pi2=_slicedToArray(localStorageState.pickers,1),firstPicker=_localStorageState$pi2[0];// Only rehydrate state from local storage if the pickers belong to the current category
|
|
5
|
+
if(firstPicker.categoryId===category){return localStorageState;}}if(routeState[code]){return routeState[code];}return defaultState;},[category,code,defaultState,localStorageState,rememberSelection,routeState]);var _useState=useState(initialState),_useState2=_slicedToArray(_useState,2),state=_useState2[0],setState=_useState2[1];useEffect(function(){var stateToPersist={pickers:state.pickers,buttonCategoryId:state.buttonCategoryId};if(rememberSelection){// Store state in local storage when selection should should survive app restarts
|
|
6
|
+
setLocalStorageState(stateToPersist);}else{// Store state in route otherwise
|
|
7
|
+
router.update(routeId,_defineProperty({},code,stateToPersist));}},[code,rememberSelection,routeId,setLocalStorageState,state]);useEffect(function(){if(typeof prevCategory==='undefined')return;if(typeof category==='undefined')return;if(prevCategory!==category){setState(defaultState);}},[prevCategory,category,defaultState,rememberSelection,initialState]);/**
|
|
8
|
+
* Handles the selection of a subcategory within a category picker.
|
|
9
|
+
* @param {string} categoryId The categoryId of the picker where a subcategory was selected.
|
|
10
|
+
* @param {Object} subcategory The subcategory entity which was selected.
|
|
11
|
+
*/var handleSelection=useCallback(function(categoryId,subcategory){var _updatedPickers;var subcategoryId=subcategory.id,childrenCount=subcategory.childrenCount;var selectedIndex=state.pickers.findIndex(function(picker){return picker.categoryId===categoryId;});// Get all pickers up to the selected picker and update its selectedId
|
|
12
|
+
var updatedPickers=state.pickers.slice(0,selectedIndex+1);updatedPickers[updatedPickers.length-1].selectedId=subcategoryId;// Check if a new picker should be added
|
|
13
|
+
var limitReached=((_updatedPickers=updatedPickers)===null||_updatedPickers===void 0?void 0:_updatedPickers.length)===Number(maxDepth);var appendNewPicker=!!childrenCount&&!limitReached;if(appendNewPicker){updatedPickers=updatedPickers.concat([{categoryId:subcategoryId,selectedId:null}]);}setState({pickers:updatedPickers,buttonCategoryId:!appendNewPicker?subcategoryId:null});},[maxDepth,state]);var categoryPickers=useMemo(function(){return React.createElement(React.Fragment,null,state.pickers.slice(0,maxDepth).map(function(entry,index){var categoryId=entry.categoryId,selectedId=entry.selectedId;return React.createElement(CategoryPicker,{key:"".concat(categoryId,"-").concat(code),categoryId:categoryId,selectedId:selectedId,onSelect:handleSelection,label:labels["level".concat(index+1,"Label")]||''});}));},[code,handleSelection,labels,maxDepth,state.pickers]);return React.createElement("div",{className:classes.container},showHeadline&&headline?React.createElement(WidgetHeadline,{headline:headline}):null,categoryPickers,React.createElement("div",{className:classes.buttonContainer},React.createElement(ButtonLink,{className:classes.button,href:"".concat(CATEGORY_PATH,"/").concat(bin2hex(state.buttonCategoryId)),disabled:!state.buttonCategoryId,flat:false},React.createElement(I18n.Text,{string:"common.show_products"}))));};export default NestedCategoryFilter;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React,{useCallback}from'react';import PropTypes from'prop-types';import{SheetDrawer,SheetList}from'@shopgate/engage/components';import Item from"../SheetItem";/**
|
|
2
|
+
* The CategorySheet component.
|
|
3
|
+
* @param {Object} props The component props.
|
|
4
|
+
* @param {Array} props.items The list of items to display.
|
|
5
|
+
* @param {string} props.label The sheet label.
|
|
6
|
+
* @param {boolean} props.open Whether the sheet is open.
|
|
7
|
+
* @param {Function} props.onClose The close handler.
|
|
8
|
+
* @param {Function} props.onSelect The selection handler.
|
|
9
|
+
* @param {string} props.selectedId The currently selected item id.
|
|
10
|
+
* @return {JSX.Element}
|
|
11
|
+
*/var CategorySheet=function CategorySheet(_ref){var items=_ref.items,label=_ref.label,onClose=_ref.onClose,open=_ref.open,selectedId=_ref.selectedId,onSelect=_ref.onSelect;var handleItemClick=useCallback(function(event){event.stopPropagation();onSelect(event.target.value);},[onSelect]);return React.createElement(SheetDrawer,{title:label,isOpen:open,onClose:onClose},React.createElement(SheetList,null,items.map(function(item){return React.createElement(Item,{item:item,key:item.id,onClick:handleItemClick,selected:item.id===selectedId});})));};CategorySheet.defaultProps={onClose:function onClose(){},onSelect:function onSelect(){},selectedId:null};export default CategorySheet;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
function _defineProperty(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true});}else{obj[key]=value;}return obj;}import React from'react';import PropTypes from'prop-types';import{makeStyles}from'@shopgate/engage/styles';import{themeConfig}from'@shopgate/engage';var colors=themeConfig.colors,variables=themeConfig.variables;var bgColor=colors.darkGray;var boxShadowOffset=variables.gap.bigger;var useStyles=makeStyles()({button:{outline:0,padding:'16px 16px 16px 0',textAlign:'left',width:'100%',color:'var(--color-text-high-emphasis)'},buttonSelected:{background:"var(--color-background-accent, ".concat(bgColor,")"),boxShadow:"-".concat(boxShadowOffset,"px 0px 0px var(--color-background-accent, ").concat(bgColor,"), ").concat(boxShadowOffset,"px 0px 0px var(--color-background-accent,").concat(bgColor,")"),margin:'-1px 0',paddingTop:17,paddingBottom:17}});/**
|
|
2
|
+
* The SheetItem component.
|
|
3
|
+
* @param {Object} props The component props.
|
|
4
|
+
* @param {Object} props.item The item data.
|
|
5
|
+
* @param {boolean} props.selected Whether the item is selected.
|
|
6
|
+
* @param {Function} props.onClick The click handler.
|
|
7
|
+
* @return {JSX.Element}
|
|
8
|
+
*/var SheetItem=function SheetItem(_ref){var item=_ref.item,selected=_ref.selected,onClick=_ref.onClick;var _useStyles=useStyles(),classes=_useStyles.classes,cx=_useStyles.cx;return React.createElement("button",{className:cx(classes.button,_defineProperty({},classes.buttonSelected,selected)),value:item.id,onClick:onClick,type:"button"},item.name);};SheetItem.defaultProps={onClick:function onClick(){},selected:false};export default SheetItem;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
function _defineProperty(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true});}else{obj[key]=value;}return obj;}function _slicedToArray(arr,i){return _arrayWithHoles(arr)||_iterableToArrayLimit(arr,i)||_nonIterableRest();}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance");}function _iterableToArrayLimit(arr,i){var _arr=[];var _n=true;var _d=false;var _e=undefined;try{for(var _i=arr[Symbol.iterator](),_s;!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break;}}catch(err){_d=true;_e=err;}finally{try{if(!_n&&_i["return"]!=null)_i["return"]();}finally{if(_d)throw _e;}}return _arr;}function _arrayWithHoles(arr){if(Array.isArray(arr))return arr;}import React,{useCallback,useState,useEffect,useMemo}from'react';import PropTypes from'prop-types';import{useSelector,useDispatch}from'react-redux';import{fetchCategoryOrRootCategories}from'@shopgate/engage/category';import{makeStyles}from'@shopgate/engage/styles';import{themeConfig}from'@shopgate/engage';import{i18n}from'@shopgate/engage/core/helpers';import Sheet from"./components/Sheet";import{getCategoriesById}from"../../../CategoryList/selectors";var colors=themeConfig.colors;var useStyles=makeStyles()({button:{background:"var(--color-background-accent, ".concat(colors.overlay,")"),color:'var(--color-text-high-emphasis)',display:'flex',flexDirection:'column',justifyContent:'center',minHeight:56,outline:0,padding:'12px 16px',marginBottom:8,transition:'background 250ms ease-in, color 250ms ease-in',cursor:'pointer'},buttonDisabled:{color:colors.shade4,cursor:'not-allowed'},label:{fontSize:12,marginTop:-2,marginBottom:4},selection:{fontWeight:500,lineHeight:1.125}});/**
|
|
2
|
+
* The CategoryPicker component.
|
|
3
|
+
* @param {Object} props The component props.
|
|
4
|
+
* @param {Function} props.onSelect The selection handler.
|
|
5
|
+
* @param {string} props.categoryId The categoryId for which to show subcategories.
|
|
6
|
+
* @param {string} props.label The label for the picker.
|
|
7
|
+
* @param {string|null} props.selectedId The currently selected subcategoryId.
|
|
8
|
+
* @return {JSX.Element}
|
|
9
|
+
*/var CategoryPicker=function CategoryPicker(_ref){var onSelect=_ref.onSelect,categoryId=_ref.categoryId,label=_ref.label,selectedId=_ref.selectedId;var subcategories=useSelector(function(state){return getCategoriesById(state,{categoryId:categoryId});});var dispatch=useDispatch();var _useStyles=useStyles(),classes=_useStyles.classes,cx=_useStyles.cx;var _useState=useState(false),_useState2=_slicedToArray(_useState,2),sheetIsOpen=_useState2[0],setSheetIsOpen=_useState2[1];var hasSubcategories=Array.isArray(subcategories)&&subcategories.length>0;var disabled=!hasSubcategories||categoryId===null;useEffect(function(){if(categoryId!==null&&subcategories===null){dispatch(fetchCategoryOrRootCategories(categoryId));}},[categoryId,dispatch,subcategories]);var buttonLabel=useMemo(function(){if(!selectedId){return i18n.text('common.please_choose');}var subcategory=subcategories===null||subcategories===void 0?void 0:subcategories.find(function(val){return val.id===selectedId;});return subcategory===null||subcategory===void 0?void 0:subcategory.name;},[selectedId,subcategories]);var closeSheet=useCallback(function(){setSheetIsOpen(false);},[]);var handlePickerClick=useCallback(function(event){event.preventDefault();if(disabled){return;}setSheetIsOpen(true);},[disabled]);var handleSheetSelect=useCallback(function(subcategoryId){var subcategory=subcategories.find(function(item){return item.id===subcategoryId;});onSelect(categoryId,subcategory);closeSheet();},[categoryId,closeSheet,onSelect,subcategories]);return React.createElement(React.Fragment,null,React.createElement("div",{onClick:handlePickerClick,className:cx(classes.button,_defineProperty({},classes.buttonDisabled,disabled))},label&&React.createElement("div",{className:classes.label},label),React.createElement("div",{className:classes.selection},buttonLabel)),React.createElement(Sheet,{items:subcategories||[],onSelect:handleSheetSelect,onClose:closeSheet,open:sheetIsOpen,selectedId:selectedId,label:label}));};CategoryPicker.defaultProps={categoryId:null,label:'',selectedId:null};export default CategoryPicker;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
function _extends(){_extends=Object.assign||function(target){for(var i=1;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(Object.prototype.hasOwnProperty.call(source,key)){target[key]=source[key];}}}return target;};return _extends.apply(this,arguments);}import{useWidget}from'@shopgate/engage/page/hooks';/**
|
|
2
|
+
* @typedef {Object} NestedCategoryFilter
|
|
3
|
+
* @property {number} [maxDepth=3] The maximum depth of categories to display.
|
|
4
|
+
* @property {string} category The category ID.
|
|
5
|
+
* @property {string} level1Label The label for level 1 categories.
|
|
6
|
+
* @property {string} level2Label The label for level 2 categories.
|
|
7
|
+
* @property {string} level3Label The label for level 3 categories.
|
|
8
|
+
* @property {boolean} showHeadline Whether to show the headline.
|
|
9
|
+
* @property {Object} headline The headline data including text and styles.
|
|
10
|
+
* @property {boolean} rememberSelection Whether to remember user selection
|
|
11
|
+
* in order to prefill the widget with the shopper’s last chosen path on future visits .
|
|
12
|
+
*/ /**
|
|
13
|
+
* @typedef {ReturnType< typeof import('@shopgate/engage/page/hooks')
|
|
14
|
+
* .useWidget<NestedCategoryFilter> >} UseWidgetReturnType
|
|
15
|
+
*/ // eslint-disable-next-line valid-jsdoc
|
|
16
|
+
/**
|
|
17
|
+
* Hook to access the Nested Category Filter widget configuration and data.
|
|
18
|
+
*/export var useNestedCategoryFilterWidget=function useNestedCategoryFilterWidget(){/** @type {UseWidgetReturnType} */var _useWidget=useWidget(),_useWidget$config=_useWidget.config,config=_useWidget$config===void 0?{}:_useWidget$config,code=_useWidget.code;return _extends({},config,{code:code});};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{default}from"./NestedCategoryFilter";
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
width:'calc(100% + 3px)',display:'flex'}};});/**
|
|
1
|
+
import React from'react';import{useVideoWidget}from"./hooks";import WidgetVideo from"../../components/WidgetVideo";/**
|
|
3
2
|
* The VideoWidget is used to display a video.
|
|
4
3
|
* @returns {JSX.Element}
|
|
5
|
-
*/var Video=function Video(){var _useVideoWidget=useVideoWidget(),url=_useVideoWidget.url,muted=_useVideoWidget.muted,loop=_useVideoWidget.loop,controls=_useVideoWidget.controls,autoplay=_useVideoWidget.autoplay,borderRadius=_useVideoWidget.borderRadius,link=_useVideoWidget.link;
|
|
6
|
-
return false;}return!autoplay||reduceMotion?true:controls;},[autoplay,controls,link,reduceMotion]);// Resume video playback when app returned from background
|
|
7
|
-
useAppEventOnReturnFromBackground(function(){if(!videoRef.current||reduceMotion||!autoplay){return;}videoRef.current.play();});useEffect(function(){if(!videoRef.current){return;}if(reduceMotion){// Pause playback when reduced motion settings changed after video was rendered
|
|
8
|
-
videoRef.current.pause();return;}if(autoplay){videoRef.current.play();}else{videoRef.current.pause();videoRef.current.currentTime=0;}},[autoplay,reduceMotion]);useEffect(function(){if(!url||url!==prevUrl){setHasError(false);}},[hasError,prevUrl,url]);if(!url||hasError||!isValidUrl)return null;return React.createElement("div",{className:classes.root},React.createElement(ConditionalWrapper,{condition:!!link,wrapper:function wrapper(children){return React.createElement(Link,{href:link},children);}},React.createElement("video",{ref:videoRef// Set play position to 0.001s to guarantee that there is always a frame shown
|
|
9
|
-
,src:"".concat(url,"#t=0.001"),muted:muted,controls:showControls,autoPlay:reduceMotion?false:autoplay,className:classes.video,preload:"auto",playsInline:true,loop:loop,"aria-hidden":true,onError:function onError(){setHasError(true);}},React.createElement("track",{kind:"captions",src:"",srcLang:"de",label:"Deutsch"}))));};export default Video;
|
|
4
|
+
*/var Video=function Video(){var _useVideoWidget=useVideoWidget(),url=_useVideoWidget.url,muted=_useVideoWidget.muted,loop=_useVideoWidget.loop,controls=_useVideoWidget.controls,autoplay=_useVideoWidget.autoplay,borderRadius=_useVideoWidget.borderRadius,link=_useVideoWidget.link;return React.createElement(WidgetVideo,{url:url,loop:loop,muted:muted,controls:controls,autoplay:autoplay,borderRadius:borderRadius,link:link});};export default Video;
|
|
@@ -5,7 +5,7 @@ import{useWidget}from'@shopgate/engage/page/hooks';import{resolveBorderRadiusFro
|
|
|
5
5
|
* @property {boolean} [loop] Whether the video should loop.
|
|
6
6
|
* @property {boolean} [controls] Whether the video should display controls.
|
|
7
7
|
* @property {boolean} [autoplay] Whether the video should autoplay.
|
|
8
|
-
* @property {
|
|
8
|
+
* @property {number} [borderRadius] The border radius preset.
|
|
9
9
|
* @property {string} [link] The link URL.
|
|
10
10
|
*/ /**
|
|
11
11
|
* @typedef {ReturnType< typeof import('@shopgate/engage/page/hooks')
|
package/page/widgets/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{default as PlaceholderWidget}from"./Placeholder";export{default as ProductListWidget}from"./ProductList";export{default as CategoryListWidget}from"./CategoryList";export{default as HtmlWidget}from"./HTML";export{default as VideoWidget}from"./Video";export{default as ProductSliderWidget}from"./ProductSlider";export{default as HeadlineWidget}from"./Headline";export{default as ImageWidget}from"./Image";export{default as ImageRowWidget}from"./ImageRow";export{default as ImageSliderWidget}from"./ImageSlider";export{default as RichTextWidget}from"./RichText";export{default as HeroBannerWidget}from"./HeroBanner";export{default as ButtonWidget}from"./Button";
|
|
1
|
+
export{default as PlaceholderWidget}from"./Placeholder";export{default as ProductListWidget}from"./ProductList";export{default as CategoryListWidget}from"./CategoryList";export{default as HtmlWidget}from"./HTML";export{default as VideoWidget}from"./Video";export{default as ProductSliderWidget}from"./ProductSlider";export{default as HeadlineWidget}from"./Headline";export{default as ImageWidget}from"./Image";export{default as ImageRowWidget}from"./ImageRow";export{default as ImageSliderWidget}from"./ImageSlider";export{default as RichTextWidget}from"./RichText";export{default as HeroBannerWidget}from"./HeroBanner";export{default as ButtonWidget}from"./Button";export{default as NestedCategoryFilterWidget}from"./NestedCategoryFilter";
|
|
@@ -35,6 +35,9 @@
|
|
|
35
35
|
"@shopgate/widgets/buttonWidget": {
|
|
36
36
|
"path": "@shopgate/engage/page/widgets/Button"
|
|
37
37
|
},
|
|
38
|
+
"@shopgate/widgets/nestedCategoryFilterWidget": {
|
|
39
|
+
"path": "@shopgate/engage/page/widgets/NestedCategoryFilter"
|
|
40
|
+
},
|
|
38
41
|
"@shopgate/widgetsInternal/Placeholder": {
|
|
39
42
|
"path": "@shopgate/engage/page/widgets/Placeholder"
|
|
40
43
|
}
|