@shopgate/engage 7.26.0 → 7.27.0-beta.1
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/cart/components/PaymentBar/PaymentBarContent.js +2 -2
- package/cart/components/PaymentBar/PaymentBarContent.style.js +1 -1
- package/components/Footer/Footer.js +4 -23
- package/components/Footer/Footer.style.js +1 -7
- package/components/Footer/constants.js +1 -0
- package/components/Footer/helpers.js +75 -0
- package/components/ScrollHeader/index.js +2 -2
- package/components/View/components/Content/index.js +1 -1
- package/core/actions/updateStatusBarBackground.js +3 -3
- package/core/constants/appFeatures.js +1 -1
- package/core/helpers/appFeatures.js +18 -9
- package/core/helpers/updateLegacyNavigationBar.js +4 -3
- package/core/hooks/events/index.js +1 -0
- package/core/hooks/events/useLongPress.js +25 -0
- package/core/hooks/events/useScrollDirectionChange.js +33 -0
- package/core/hooks/index.js +1 -1
- package/core/hooks/layout/index.js +1 -0
- package/core/hooks/layout/useElementSize.js +26 -0
- package/development/action-creators/index.js +1 -0
- package/development/action-creators/settings.js +9 -0
- package/development/action-creators/storage.js +6 -0
- package/development/components/DevelopmentTools/DevelopmentTools.js +6 -0
- package/development/components/DevelopmentTools/Shortcuts.js +4 -0
- package/development/components/DevelopmentTools/hooks.js +8 -0
- package/development/components/DevelopmentTools/index.js +1 -0
- package/development/components/SimulatedInsets/SimulatedInsetBottom.js +7 -0
- package/development/components/SimulatedInsets/SimulatedInsetTop.js +12 -0
- package/development/components/SimulatedInsets/SimulatedInsets.js +6 -0
- package/development/components/SimulatedInsets/index.js +1 -0
- package/development/components/index.js +1 -0
- package/development/constants/actionTypes.js +1 -0
- package/development/constants/index.js +1 -0
- package/development/reducers/index.js +1 -0
- package/development/reducers/settings.js +10 -0
- package/development/reducers/storage.js +9 -0
- package/development/selectors/index.js +1 -0
- package/development/selectors/settings.js +21 -0
- package/development/selectors/storage.js +16 -0
- package/development/streams/index.js +1 -0
- package/development/streams/insets.js +4 -0
- package/development/subscriptions/index.js +6 -0
- package/package.json +7 -7
- package/core/hooks/useScroll.js +0 -7
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import*as React from'react';import PropTypes from'prop-types';import Grid from'@shopgate/pwa-common/components/Grid';import{SurroundPortals}from'@shopgate/engage/components';import{CART_PAYMENT_BAR,CART_PAYMENT_BAR_TOTALS}from'@shopgate/pwa-common-commerce/cart/constants/Portals';import PaymentBarShippingCost from"./PaymentBarShippingCost";import PaymentBarDiscounts from"./PaymentBarDiscounts";import PaymentBarTax from"./PaymentBarTax";import PaymentBarSubTotal from"./PaymentBarSubTotal";import PaymentBarGrandTotal from"./PaymentBarGrandTotal";import PaymentBarCheckoutButton from"./PaymentBarCheckoutButton";import PaymentBarPromotionCoupons from"./PaymentBarPromotionCoupons";import PaymentBarAppliedPromotions from"./PaymentBarAppliedPromotions";import{wrapper,container,checkoutButtonContainer,checkoutButton}from"./PaymentBarContent.style";/**
|
|
1
|
+
import*as React from'react';import PropTypes from'prop-types';import classNames from'classnames';import Grid from'@shopgate/pwa-common/components/Grid';import{SurroundPortals}from'@shopgate/engage/components';import{CART_PAYMENT_BAR,CART_PAYMENT_BAR_TOTALS}from'@shopgate/pwa-common-commerce/cart/constants/Portals';import PaymentBarShippingCost from"./PaymentBarShippingCost";import PaymentBarDiscounts from"./PaymentBarDiscounts";import PaymentBarTax from"./PaymentBarTax";import PaymentBarSubTotal from"./PaymentBarSubTotal";import PaymentBarGrandTotal from"./PaymentBarGrandTotal";import PaymentBarCheckoutButton from"./PaymentBarCheckoutButton";import PaymentBarPromotionCoupons from"./PaymentBarPromotionCoupons";import PaymentBarAppliedPromotions from"./PaymentBarAppliedPromotions";import{wrapper,container,checkoutButtonContainer,checkoutButton}from"./PaymentBarContent.style";/**
|
|
2
2
|
* The PaymentBarContent component.
|
|
3
3
|
* @returns {JSX}
|
|
4
|
-
*/function PaymentBarContent(_ref){var showSeparator=_ref.showSeparator;return React.createElement("div",{className:wrapper},React.createElement(SurroundPortals,{portalName:CART_PAYMENT_BAR},React.createElement(Grid,{className:container},React.createElement(SurroundPortals,{portalName:CART_PAYMENT_BAR_TOTALS},React.createElement(PaymentBarSubTotal,{showSeparator:showSeparator}),React.createElement(PaymentBarAppliedPromotions,{showSeparator:showSeparator}),React.createElement(PaymentBarPromotionCoupons,{showSeparator:showSeparator}),React.createElement(PaymentBarDiscounts,{showSeparator:showSeparator}),React.createElement(PaymentBarShippingCost,{showSeparator:showSeparator}),React.createElement(PaymentBarTax,{showSeparator:showSeparator}),React.createElement(PaymentBarGrandTotal,{showSeparator:showSeparator}))),React.createElement("div",{className:checkoutButtonContainer},React.createElement("div",{className:checkoutButton},React.createElement(PaymentBarCheckoutButton,null)))));}PaymentBarContent.defaultProps={showSeparator:true};export default PaymentBarContent;
|
|
4
|
+
*/function PaymentBarContent(_ref){var showSeparator=_ref.showSeparator;return React.createElement("div",{className:classNames(wrapper,'theme__cart__payment-bar')},React.createElement(SurroundPortals,{portalName:CART_PAYMENT_BAR},React.createElement(Grid,{className:container},React.createElement(SurroundPortals,{portalName:CART_PAYMENT_BAR_TOTALS},React.createElement(PaymentBarSubTotal,{showSeparator:showSeparator}),React.createElement(PaymentBarAppliedPromotions,{showSeparator:showSeparator}),React.createElement(PaymentBarPromotionCoupons,{showSeparator:showSeparator}),React.createElement(PaymentBarDiscounts,{showSeparator:showSeparator}),React.createElement(PaymentBarShippingCost,{showSeparator:showSeparator}),React.createElement(PaymentBarTax,{showSeparator:showSeparator}),React.createElement(PaymentBarGrandTotal,{showSeparator:showSeparator}))),React.createElement("div",{className:checkoutButtonContainer},React.createElement("div",{className:checkoutButton},React.createElement(PaymentBarCheckoutButton,null)))));}PaymentBarContent.defaultProps={showSeparator:true};export default PaymentBarContent;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{css}from'glamor';import{isIOSTheme}from'@shopgate/engage/core';import{themeConfig}from'@shopgate/pwa-common/helpers/config';var colors=themeConfig.colors,variables=themeConfig.variables,shadows=themeConfig.shadows;export var wrapper=css({background:colors.light,boxShadow:shadows.cart.paymentBar,position:'relative',zIndex:2});export var container=css({padding:isIOSTheme()?variables.gap.small:variables.gap.big,paddingBottom:0,lineHeight:1.45,flexWrap:'wrap',flexDirection:'column',minWidth:'auto'}).toString();export var checkoutButton=css({display:'flex',justifyContent:'flex-end',flexDirection:'column'});export var checkoutButtonContainer=css({background:colors.light,alignItems:'center',padding:isIOSTheme()?variables.gap.small:variables.gap.big,position:'relative',zIndex:2});export var spacer=css({width:isIOSTheme()?27:32,order:1,flexShrink:0}).toString();
|
|
1
|
+
import{css}from'glamor';import{isIOSTheme}from'@shopgate/engage/core';import{themeConfig}from'@shopgate/pwa-common/helpers/config';var colors=themeConfig.colors,variables=themeConfig.variables,shadows=themeConfig.shadows;export var wrapper=css({background:colors.light,boxShadow:shadows.cart.paymentBar,position:'relative',zIndex:2,paddingBottom:'var(--safe-area-inset-bottom)'});export var container=css({padding:isIOSTheme()?variables.gap.small:variables.gap.big,paddingBottom:0,lineHeight:1.45,flexWrap:'wrap',flexDirection:'column',minWidth:'auto'}).toString();export var checkoutButton=css({display:'flex',justifyContent:'flex-end',flexDirection:'column'});export var checkoutButtonContainer=css({background:colors.light,alignItems:'center',padding:isIOSTheme()?variables.gap.small:variables.gap.big,position:'relative',zIndex:2});export var spacer=css({width:isIOSTheme()?27:32,order:1,flexShrink:0}).toString();
|
|
@@ -1,24 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import React,{useRef,useEffect,useCallback}from'react';import PropTypes from'prop-types';import classNames from'classnames';import{UIEvents}from'@shopgate/engage/core/events';import{getAbsoluteHeight}from'@shopgate/engage/core/helpers';import{SHEET_EVENTS}from'@shopgate/engage/components';import*as classes from"./Footer.style";import{handleSafeAreaInsets,updateFooterHeight}from"./helpers";import{APP_FOOTER_ID,DATA_IGNORED}from"./constants";/**
|
|
2
2
|
* The footer component.
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* @returns {string|null}
|
|
7
|
-
*/function getInsetBackgroundColor(elements){var _this4=this;/**
|
|
8
|
-
* The background color of the bottom inset needs to identical to the last entry of the footer.
|
|
9
|
-
* So we loop backwards to the elements to find the first visible one.
|
|
10
|
-
*/var color=Array.from(elements).reverse().reduce(function(result,element){var ignore=element.getAttribute(DATA_IGNORED)==='true';if(result||ignore){// Nothing to do, since the color was already determined or the element can be ignored.
|
|
11
|
-
return result;}if(element.id===APP_FOOTER_ID){// Inspect core portal.
|
|
12
|
-
return _this4.getInsetBackgroundColor(element.children);}if('clientHeight'in element){// Take the background color of the last visible element from the end of the footer.
|
|
13
|
-
return getStyle(element,'backgroundColor');}// Nothing happened within this loop - proceed with the next one.
|
|
14
|
-
return result;},null);if(color==='rgba(0, 0, 0, 0)'||color==='transparent'){return null;}return color||null;}},{key:"performFooterUpdate",value:/**
|
|
15
|
-
* Performs an update of the footer: background color, height.
|
|
16
|
-
*/function performFooterUpdate(){if(this.ref.current){updateFooterHeight(getAbsoluteHeight(this.ref.current));updateInsetBackgroundColor(this.getInsetBackgroundColor(this.ref.current.children));}}/**
|
|
17
|
-
* Checks if the footer has visible content.
|
|
18
|
-
* @returns {boolean}
|
|
19
|
-
*/},{key:"hasVisibleContent",value:function hasVisibleContent(){if(this.ref.current){var elements=this.ref.current.parentElement.querySelectorAll("div.".concat(footer.toString()," > *:not(#").concat(APP_FOOTER_ID,"), #").concat(APP_FOOTER_ID," > *"));return Array.from(elements).filter(function(element){return element.getAttribute(DATA_IGNORED)!=='true'&&element.clientHeight>0;}).length>0;}return false;}/**
|
|
20
|
-
* @returns {JSX}
|
|
21
|
-
*/},{key:"render",value:function render(){return React.createElement("div",{className:"".concat(footer," engage__footer"),ref:this.ref},React.createElement(Portal,{name:APP_FOOTER_CONTENT_BEFORE}),React.createElement("div",{id:APP_FOOTER_ID},this.props.children),React.createElement(Portal,{name:APP_FOOTER_CONTENT_AFTER}));}}]);}(PureComponent);_defineProperty(Footer,"defaultProps",{children:null/**
|
|
22
|
-
* Sets up the DOM Mutation Observer to take care that the footer inset always has the correct
|
|
23
|
-
* background color, which matches the background color of the last element within the footer.
|
|
24
|
-
*/});export default Footer;
|
|
3
|
+
* @param {Object} props The component props.
|
|
4
|
+
* @returns {JSX.Element}
|
|
5
|
+
*/var Footer=function Footer(_ref){var children=_ref.children;var footerRef=useRef(null);var performFooterUpdate=useCallback(function(){handleSafeAreaInsets(footerRef.current);updateFooterHeight(getAbsoluteHeight(footerRef.current));},[]);var handleShow=useCallback(function(){updateFooterHeight(getAbsoluteHeight(footerRef.current));},[]);var handleHide=useCallback(function(){updateFooterHeight(0);},[]);useEffect(function(){UIEvents.addListener(SHEET_EVENTS.OPEN,handleHide);UIEvents.addListener(SHEET_EVENTS.CLOSE,handleShow);return function(){UIEvents.removeListener(SHEET_EVENTS.OPEN,handleHide);UIEvents.removeListener(SHEET_EVENTS.CLOSE,handleShow);};},[handleHide,handleShow]);useEffect(function(){performFooterUpdate();var observer=new MutationObserver(function(mutations){var update=mutations.filter(function(mutation){return mutation.target.getAttribute(DATA_IGNORED)!=='true';}).length>0;if(update){performFooterUpdate();}});observer.observe(footerRef.current,{childList:true,subtree:true,attributes:true,attributeFilter:['style','class']});return function(){observer.disconnect();};},[performFooterUpdate]);return React.createElement("div",{className:classNames(classes.footer,'engage__footer')},React.createElement("div",{id:APP_FOOTER_ID,ref:footerRef},children));};Footer.defaultProps={children:null};export default Footer;
|
|
@@ -1,7 +1 @@
|
|
|
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{css}from'glamor';import{useScrollContainer}from'@shopgate/engage/core/helpers
|
|
2
|
-
* Updates the background color of the bottom inset.
|
|
3
|
-
* @param {string} color The new background color
|
|
4
|
-
*/export var updateInsetBackgroundColor=function updateInsetBackgroundColor(color){if(style.getPropertyValue('--footer-inset-background-color')!==color){style.setProperty('--footer-inset-background-color',color);}};/**
|
|
5
|
-
* Update the footer height
|
|
6
|
-
* @param {number} height height
|
|
7
|
-
*/export var updateFooterHeight=function updateFooterHeight(height){var inset=Number(style.getPropertyValue('--safe-area-inset-bottom').replace(/\D/g,''));var footerHeight="".concat(inset+height,"px");if(style.getPropertyValue('--footer-height')!==footerHeight){style.setProperty('--footer-height',footerHeight);}};export var footer=css(_extends({bottom:0,flexShrink:1,position:'relative',zIndex:1},!useScrollContainer()?{position:'sticky'}:{},{':after':{backgroundColor:'var(--footer-inset-background-color, var(--page-background-color))',height:'var(--safe-area-inset-bottom)',content:' ',display:'inherit',position:'relative',zIndex:15}}));
|
|
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{css}from'glamor';import{useScrollContainer}from'@shopgate/engage/core/helpers';export var footer=css(_extends({bottom:0,flexShrink:1,position:'relative',zIndex:1},!useScrollContainer()?{position:'sticky'}:{}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export var APP_FOOTER_ID='AppFooter';export var DATA_IGNORED='data-footer-inset-update-ignore';
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import{logger,isDev}from'@shopgate/engage/core/helpers';import{DATA_IGNORED}from"./constants";/**
|
|
2
|
+
* Determines if an element is visually present in the DOM.
|
|
3
|
+
* Checks for display, visibility, opacity, and transform-based hiding.
|
|
4
|
+
*
|
|
5
|
+
* @param {HTMLElement} el The element to evaluate.
|
|
6
|
+
* @returns {boolean} True if the element is visually visible; false otherwise.
|
|
7
|
+
*/export var isElementVisible=function isElementVisible(el){if(!el||!(el instanceof HTMLElement))return false;var style=window.getComputedStyle(el);if(style.display==='none'||style.visibility==='hidden'||parseFloat(style.opacity)===0)return false;var transform=style.transform;if(transform&&transform!=='none'){var match=transform.match(/translateY\((-?\d+)(px)?\)/);if(match){var translateY=parseInt(match[1],10);if(translateY>window.innerHeight)return false;}}return true;};/**
|
|
8
|
+
* Finds the widest nested element inside a given container that has a background color
|
|
9
|
+
* explicitly set via CSS (ignores transparent, inherited, or unset values), and returns
|
|
10
|
+
* that background color.
|
|
11
|
+
*
|
|
12
|
+
* @param {HTMLElement} container The parent element to search within. Must be an actual DOM node.
|
|
13
|
+
* @returns {string|null} The detected background color (e.g., "rgb(255, 0, 0)")
|
|
14
|
+
*/export var getElementBackgroundColor=function getElementBackgroundColor(container){if(!container){return null;}var widestElement=null;var maxWidth=-Infinity;/**
|
|
15
|
+
* Recursively traverses the DOM tree starting from the given node,
|
|
16
|
+
* tracking the widest element that has a background color explicitly set via CSS.
|
|
17
|
+
*
|
|
18
|
+
* @param {HTMLElement} node The DOM node to begin traversal from.
|
|
19
|
+
*/function walk(node){if(!(node instanceof HTMLElement))return;var style=window.getComputedStyle(node);var bgColor=style.backgroundColor;var isStyledColor=bgColor&&!['transparent','rgba(0, 0, 0, 0)','inherit','initial','unset'].includes(bgColor);var rect=node.getBoundingClientRect();if(isElementVisible(node)&&isStyledColor&&rect.width>maxWidth){maxWidth=rect.width;widestElement=node;}Array.from(node.children).forEach(walk);}walk(container);if(widestElement){var result=window.getComputedStyle(widestElement).backgroundColor;return result;}return null;};/**
|
|
20
|
+
* Checks if any of the provided class names reference the custom property in any loaded stylesheet.
|
|
21
|
+
*
|
|
22
|
+
* @param {string[]} classList Array of class names to check.
|
|
23
|
+
* @param {string} customProp The custom property to search for in the stylesheets.
|
|
24
|
+
* @returns {boolean} True if any class rule uses the custom property.
|
|
25
|
+
*/var classNamesUseCustomProp=function classNamesUseCustomProp(classList,customProp){var allRules=Array.from(document.styleSheets).filter(function(sheet){try{return sheet.cssRules;}catch(e){return false;// Skip cross-origin or restricted stylesheets
|
|
26
|
+
}}).flatMap(function(sheet){return Array.from(sheet.cssRules||[]);});// eslint-disable-next-line no-restricted-syntax
|
|
27
|
+
var _iteratorNormalCompletion=true;var _didIteratorError=false;var _iteratorError=undefined;try{for(var _iterator=allRules[Symbol.iterator](),_step;!(_iteratorNormalCompletion=(_step=_iterator.next()).done);_iteratorNormalCompletion=true){var rule=_step.value;// eslint-disable-next-line no-continue
|
|
28
|
+
if(!rule.selectorText||!rule.cssText.includes("var(".concat(customProp,")")))continue;// eslint-disable-next-line no-restricted-syntax
|
|
29
|
+
var _iteratorNormalCompletion2=true;var _didIteratorError2=false;var _iteratorError2=undefined;try{for(var _iterator2=classList[Symbol.iterator](),_step2;!(_iteratorNormalCompletion2=(_step2=_iterator2.next()).done);_iteratorNormalCompletion2=true){var className=_step2.value;if(rule.selectorText.includes(".".concat(className))){return true;}}}catch(err){_didIteratorError2=true;_iteratorError2=err;}finally{try{if(!_iteratorNormalCompletion2&&_iterator2["return"]!=null){_iterator2["return"]();}}finally{if(_didIteratorError2){throw _iteratorError2;}}}}}catch(err){_didIteratorError=true;_iteratorError=err;}finally{try{if(!_iteratorNormalCompletion&&_iterator["return"]!=null){_iterator["return"]();}}finally{if(_didIteratorError){throw _iteratorError;}}}return false;};/**
|
|
30
|
+
* Checks if a single element uses the custom property via inline styles or class-based rules.
|
|
31
|
+
*
|
|
32
|
+
* @param {HTMLElement} el The element to check.
|
|
33
|
+
* @param {string} customProp The CSS custom property to search for.
|
|
34
|
+
* @returns {boolean} True if the element uses the custom property.
|
|
35
|
+
*/var elementUsesCustomProp=function elementUsesCustomProp(el,customProp){var _el$getAttribute;if(!(el instanceof Element))return false;var styleAttr=(_el$getAttribute=el.getAttribute)===null||_el$getAttribute===void 0?void 0:_el$getAttribute.call(el,'style');if(styleAttr&&styleAttr.includes("var(".concat(customProp,")"))){return true;}var classList=Array.from(el.classList||[]);return classList.length>0&&classNamesUseCustomProp(classList,customProp);};/**
|
|
36
|
+
* Checks if an element or any of its descendants use the custom property.
|
|
37
|
+
*
|
|
38
|
+
* @param {HTMLElement} el The root element to inspect.
|
|
39
|
+
* @param {string} customProp The CSS custom property to look for.
|
|
40
|
+
* @returns {boolean} True if the element or any descendant uses the custom property.
|
|
41
|
+
*/var elementOrDescendantsUseCustomProp=function elementOrDescendantsUseCustomProp(el,customProp){// Check if the element itself uses the custom property
|
|
42
|
+
if(elementUsesCustomProp(el,customProp))return true;var descendants=el.querySelectorAll('*');// eslint-disable-next-line no-restricted-syntax
|
|
43
|
+
var _iteratorNormalCompletion3=true;var _didIteratorError3=false;var _iteratorError3=undefined;try{for(var _iterator3=descendants[Symbol.iterator](),_step3;!(_iteratorNormalCompletion3=(_step3=_iterator3.next()).done);_iteratorNormalCompletion3=true){var node=_step3.value;// eslint-disable-next-line no-continue
|
|
44
|
+
if(!(node instanceof Element))continue;if(elementUsesCustomProp(node,customProp))return true;}}catch(err){_didIteratorError3=true;_iteratorError3=err;}finally{try{if(!_iteratorNormalCompletion3&&_iterator3["return"]!=null){_iterator3["return"]();}}finally{if(_didIteratorError3){throw _iteratorError3;}}}return false;};/**
|
|
45
|
+
* Returns footer entries that do NOT have safe area insets applied,
|
|
46
|
+
* either on themselves or in any of their descendants.
|
|
47
|
+
*
|
|
48
|
+
* @param {HTMLElement[]} footerElements The footer elements to check.
|
|
49
|
+
* @returns {HTMLElement[]} An array of direct children that do not use safe area insets.
|
|
50
|
+
*/var getFooterEntriesWithoutSafeAreaInsets=function getFooterEntriesWithoutSafeAreaInsets(footerElements){return footerElements.filter(function(child){return!elementOrDescendantsUseCustomProp(child,'--safe-area-inset-bottom');});};/**
|
|
51
|
+
* Searches for footer elements that do not have safe area insets applied, and adds a fallback.
|
|
52
|
+
* @param {HTMLElement} footerEl The footer element whose children are to be checked.
|
|
53
|
+
*/export var handleSafeAreaInsets=function handleSafeAreaInsets(footerEl){if(!footerEl||!(footerEl instanceof HTMLElement)){return;}var directChildren=Array.from(footerEl.children);// Filter out elements that where already handled before
|
|
54
|
+
var childrenToInspect=directChildren.filter(function(child){return child.getAttribute('data-has-safe-area-inset')!=='true';}).filter(function(child){return child.getAttribute(DATA_IGNORED)!=='true';});// Detect footer elements without safe area insets
|
|
55
|
+
var childrenWithoutInsets=getFooterEntriesWithoutSafeAreaInsets(childrenToInspect);// Apply fallback and mark the elements as handled
|
|
56
|
+
childrenWithoutInsets.forEach(function(child){child.style.setProperty('padding-bottom','var(--safe-area-inset-bottom)');child.style.setProperty('background-color',getElementBackgroundColor(child));child.setAttribute('data-has-safe-area-inset','true');});if(isDev&&childrenWithoutInsets.length>0){logger.warn('Footer elements without safe area insets detected. Please use the "--safe-area-inset-bottom" CSS custom property for bottom insets.',childrenWithoutInsets);}// Mark all other elements which already had insets as handled
|
|
57
|
+
directChildren.filter(function(child){return childrenWithoutInsets.indexOf(child)===-1;}).forEach(function(child){if(!child.hasAttribute('data-has-safe-area-inset')){child.setAttribute('data-has-safe-area-inset','true');}});};var style=document.documentElement.style;/**
|
|
58
|
+
* Update the footer height custom property
|
|
59
|
+
* @param {number} height height
|
|
60
|
+
*/export var updateFooterHeight=function updateFooterHeight(height){// The TabBar is positioned with `position: fixed`, so it doesn’t contribute to the measured
|
|
61
|
+
// height of the Footer. Additionally, it’s sometimes animated in/out, which makes dynamic
|
|
62
|
+
// measurement via JavaScript more complex and error-prone.
|
|
63
|
+
//
|
|
64
|
+
// To simplify everything, we include the --tabbar-height CSS custom property to the calculation
|
|
65
|
+
// of the --footer-height value.
|
|
66
|
+
var footerHeight="max(".concat(height,"px, var(--tabbar-height, 0px))");if(style.getPropertyValue('--footer-height')!==footerHeight){style.setProperty('--footer-height',footerHeight);}// The View component wraps every app page and applies a bottom offset to centrally manage
|
|
67
|
+
// safe area insets across screens.
|
|
68
|
+
//
|
|
69
|
+
// If the measured footer height is > 0px, it means the footer is rendering content that already
|
|
70
|
+
// accounts for the safe area, so no additional offset is needed.
|
|
71
|
+
//
|
|
72
|
+
// If the measured footer height is 0px, the footer is either empty or only contains the tab bar
|
|
73
|
+
// (which is measured via the CSS variable --footer-height). In that case, we still need to apply
|
|
74
|
+
// an offset equal to the larger of the bottom safe area inset or the tab bar height.
|
|
75
|
+
var pageContentOffset=height===0?'max(var(--footer-height), var(--safe-area-inset-bottom))':'0px';if(style.getPropertyValue('--page-content-offset-bottom')!==pageContentOffset){style.setProperty('--page-content-offset-bottom',pageContentOffset);}};
|
|
@@ -1,5 +1,5 @@
|
|
|
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,{useState
|
|
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,{useState}from'react';import PropTypes from'prop-types';import classNames from'classnames';import{useScrollDirectionChange}from'@shopgate/engage/core/hooks';import{root,scrolledIn,scrolledOut,transition}from"./style";/**
|
|
2
2
|
* Scroll Header component
|
|
3
3
|
* @param {Object} props props
|
|
4
4
|
* @returns {JSX}
|
|
5
|
-
*/function ScrollHeader(_ref){var className=_ref.className,children=_ref.children,hideOnScroll=_ref.hideOnScroll,scrollOffset=_ref.scrollOffset;var _useState=useState(false),_useState2=_slicedToArray(_useState,2),shouldHideHeader=_useState2[0],setShouldHideHeader=_useState2[1];
|
|
5
|
+
*/function ScrollHeader(_ref){var className=_ref.className,children=_ref.children,hideOnScroll=_ref.hideOnScroll,scrollOffset=_ref.scrollOffset;var _useState=useState(false),_useState2=_slicedToArray(_useState,2),shouldHideHeader=_useState2[0],setShouldHideHeader=_useState2[1];useScrollDirectionChange({enabled:hideOnScroll,offset:scrollOffset,onScrollDown:function onScrollDown(){setShouldHideHeader(true);},onScrollUp:function onScrollUp(){setShouldHideHeader(false);}});return React.createElement("div",{className:classNames(root,transition,className,_defineProperty(_defineProperty({},scrolledIn,!shouldHideHeader),scrolledOut,shouldHideHeader))},children);}ScrollHeader.defaultProps={className:null,hideOnScroll:true,scrollOffset:100};export default ScrollHeader;
|
|
@@ -23,6 +23,6 @@ function _extends(){_extends=Object.assign||function(target){for(var i=1;i<argum
|
|
|
23
23
|
* Removes the keyboardWillChange listener.
|
|
24
24
|
*/},{key:"componentWillUnmount",value:function componentWillUnmount(){var scrollTop;if(this.ref.current===window){scrollTop=window.scrollY;}else{scrollTop=this.ref.current.scrollTop;}router.update(this.context.id,{scrollTop:scrollTop},false);event.removeCallback(EVENT_KEYBOARD_WILL_CHANGE,this.handleKeyboardChange);}/**
|
|
25
25
|
* @returns {Object}
|
|
26
|
-
*/},{key:"style",get:function get(){var noScrollOnKeyboard=this.props.noScrollOnKeyboard;var keyboardHeight=this.state.keyboardHeight;var overflow='inherit';if(this.scrollContainer){overflow=noScrollOnKeyboard&&keyboardHeight>0?'hidden':'auto';}return{overflow:overflow,paddingBottom:"calc(var(--
|
|
26
|
+
*/},{key:"style",get:function get(){var noScrollOnKeyboard=this.props.noScrollOnKeyboard;var keyboardHeight=this.state.keyboardHeight;var overflow='inherit';if(this.scrollContainer){overflow=noScrollOnKeyboard&&keyboardHeight>0?'hidden':'auto';}return{overflow:overflow,paddingBottom:"calc(var(--page-content-offset-bottom) + ".concat(keyboardHeight,"px)")};}},{key:"render",value:/**
|
|
27
27
|
* @return {JSX.Element}
|
|
28
28
|
*/function render(){return React.createElement(Swipeable,{onSwiped:this.handleSwipe,flickThreshold:0.6,delta:10},React.createElement("article",{className:"".concat(styles," engage__view__content ").concat(this.props.className),ref:this.scrollContainer?this.ref:null,style:this.style,role:"none"},React.createElement(Helmet,{title:appConfig.shopName}),React.createElement(Above,null),React.createElement(ResponsiveContainer,{breakpoint:">xs",webOnly:true},this.props.visible?React.createElement("div",{id:"PageHeaderBelow"}):null),React.createElement(ConditionalWrapper,{condition:!this.props.noContentPortal,wrapper:function wrapper(children){return React.createElement(SurroundPortals,{portalName:VIEW_CONTENT},children);}},this.props.children),React.createElement(Below,null)));}}],[{key:"getDerivedStateFromProps",value:function getDerivedStateFromProps(props,state){if(props.visible||state.keyboardHeight===0){return null;}return{keyboardHeight:0};}}]);}(Component);_defineProperty(ViewContent,"contextType",RouteContext);_defineProperty(ViewContent,"defaultProps",{className:'',children:null,noScrollOnKeyboard:false,noContentPortal:false,noKeyboardListener:false});export default(function(props){return React.createElement(RouteContext.Consumer,null,function(_ref2){var visible=_ref2.visible,_ref2$pattern=_ref2.pattern,pattern=_ref2$pattern===void 0?'':_ref2$pattern,_ref2$is=_ref2.is404,is404=_ref2$is===void 0?false:_ref2$is;return React.createElement(ViewContent,_extends({},props,{visible:visible,className:"route_".concat(is404?'404':pattern.replace(/[:/]/g,'_'))}));});});
|
|
@@ -1,6 +1,6 @@
|
|
|
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{isIos}from'@shopgate/pwa-common/selectors/client';import{updateLegacyNavigationBar}from"../helpers/updateLegacyNavigationBar";/**
|
|
2
|
-
* Updates the status bar background on iOS devices.
|
|
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{isIos}from'@shopgate/pwa-common/selectors/client';import{appSupportsAndroidEdgeToEdge}from'@shopgate/engage/core/helpers';import{updateLegacyNavigationBar}from"../helpers/updateLegacyNavigationBar";/**
|
|
2
|
+
* Updates the status bar background on iOS and Android devices that support edge-to-edge screens.
|
|
3
3
|
* @param {string} color The background color.
|
|
4
4
|
* @param {boolean} isDefault When set, the status bar will init with the color on next app start.
|
|
5
5
|
* @return {Function} A redux thunk.
|
|
6
|
-
*/export default function updateStatusBarBackground(color){var isDefault=arguments.length>1&&arguments[1]!==undefined?arguments[1]:false;return function(dispatch,getState){if(!isIos(getState())){return;}updateLegacyNavigationBar(_extends({},color&&{statusBarBackground:color},{isDefault:isDefault}));};}
|
|
6
|
+
*/export default function updateStatusBarBackground(color){var isDefault=arguments.length>1&&arguments[1]!==undefined?arguments[1]:false;return function(dispatch,getState){if(!isIos(getState())&&!appSupportsAndroidEdgeToEdge()){return;}updateLegacyNavigationBar(_extends({},color&&{statusBarBackground:color},{isDefault:isDefault}));};}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export var APP_FEATURE_PUSH_OPT_IN='optIn';export var APP_FEATURE_COOKIE_CONSENT='cookieConsent';
|
|
1
|
+
export var APP_FEATURE_PUSH_OPT_IN='optIn';export var APP_FEATURE_COOKIE_CONSENT='cookieConsent';export var APP_FEATURE_ANDROID_EDGE_TO_EDGE='androidEdgeToEdge';
|
|
@@ -1,15 +1,24 @@
|
|
|
1
|
-
import{APP_FEATURE_PUSH_OPT_IN,APP_FEATURE_COOKIE_CONSENT}from'@shopgate/engage/core/constants';import{hasSGJavaScriptBridge,hasWebBridge,hasNewServices}from'@shopgate/engage/core/helpers';/**
|
|
1
|
+
import{APP_FEATURE_PUSH_OPT_IN,APP_FEATURE_COOKIE_CONSENT,APP_FEATURE_ANDROID_EDGE_TO_EDGE}from'@shopgate/engage/core/constants';import{hasSGJavaScriptBridge,hasWebBridge,hasNewServices}from'@shopgate/engage/core/helpers';/**
|
|
2
|
+
* Checks if a specific feature flag is enabled.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} category The top-level category of the featureFlags object
|
|
5
|
+
* (e.g., 'Push', 'Analytics', 'StatusBar').
|
|
6
|
+
* @param {string} flag The feature flag constant to look for.
|
|
7
|
+
* @returns {boolean}
|
|
8
|
+
*/function isFeatureFlagEnabled(category,flag){var _window,_window$SGAppInfo,_window$SGAppInfo$fea,_window$SGAppInfo$fea2,_window$SGAppInfo$fea3;// Check if app provides the feature flags of the requested category
|
|
9
|
+
var flags=(_window=window)===null||_window===void 0?void 0:(_window$SGAppInfo=_window.SGAppInfo)===null||_window$SGAppInfo===void 0?void 0:(_window$SGAppInfo$fea=_window$SGAppInfo.featureFlags)===null||_window$SGAppInfo$fea===void 0?void 0:(_window$SGAppInfo$fea2=_window$SGAppInfo$fea[category])===null||_window$SGAppInfo$fea2===void 0?void 0:(_window$SGAppInfo$fea3=_window$SGAppInfo$fea2['1'])===null||_window$SGAppInfo$fea3===void 0?void 0:_window$SGAppInfo$fea3.flags;// CHeck if the feature flag is provided
|
|
10
|
+
return Array.isArray(flags)&&flags.includes(flag);}/**
|
|
2
11
|
* Determines if the app supports the push opt-in feature
|
|
3
12
|
* @returns {boolean}
|
|
4
|
-
*/export var appSupportsPushOptIn=function appSupportsPushOptIn(){
|
|
13
|
+
*/export var appSupportsPushOptIn=function appSupportsPushOptIn(){if(hasWebBridge()||hasNewServices()){// Not push notifications in browser mode. Deactivated for new system for now
|
|
5
14
|
return false;}if(!hasSGJavaScriptBridge()){// Always supported in development
|
|
6
|
-
return true;}
|
|
7
|
-
return false;}// Supported when the feature flags contain the push opt-in flag
|
|
8
|
-
return window.SGAppInfo.featureFlags.Push['1'].flags.includes(APP_FEATURE_PUSH_OPT_IN);};/**
|
|
15
|
+
return true;}return isFeatureFlagEnabled('Push',APP_FEATURE_PUSH_OPT_IN);};/**
|
|
9
16
|
* Determines if the app supports the cookie consent feature
|
|
10
17
|
* @returns {boolean}
|
|
11
|
-
*/export var appSupportsCookieConsent=function appSupportsCookieConsent(){
|
|
18
|
+
*/export var appSupportsCookieConsent=function appSupportsCookieConsent(){if(hasWebBridge()){// Deactivated in browser mode for now
|
|
12
19
|
return false;}if(!hasSGJavaScriptBridge()){// Always supported in development
|
|
13
|
-
return true;}
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
return true;}return isFeatureFlagEnabled('Analytics',APP_FEATURE_COOKIE_CONSENT);};/**
|
|
21
|
+
* Determines if the app supports Android with edge-to-edge screens
|
|
22
|
+
* @returns {boolean}
|
|
23
|
+
*/export var appSupportsAndroidEdgeToEdge=function appSupportsAndroidEdgeToEdge(){if(hasWebBridge()||!hasSGJavaScriptBridge()){// Deactivated in browser mode and development for now
|
|
24
|
+
return false;}return isFeatureFlagEnabled('StatusBar',APP_FEATURE_ANDROID_EDGE_TO_EDGE);};
|
|
@@ -1,6 +1,7 @@
|
|
|
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 Color from'color';import{isAvailable,StatusBar}from'@shopgate/native-modules';import
|
|
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 Color from'color';import{isAvailable,StatusBar}from'@shopgate/native-modules';import{broadcastEvent}from'@shopgate/engage/core/commands';import{isDev}from'@shopgate/engage/core/helpers';import{UIEvents}from'@shopgate/engage/core/events';/**
|
|
2
2
|
* Updates the styles of the navigation bar of iOS devices.
|
|
3
3
|
* @param {Object} options Options for the status bar.
|
|
4
|
-
*/export var updateLegacyNavigationBar=function updateLegacyNavigationBar(){var options=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};var targetTab=options.targetTab||'main';var isDefault=options.isDefault;var styles=_extends({},options.color&&{color:options.color},{},options.background&&{background:options.background},{},options.buttonColor&&{buttonColor:options.buttonColor},{},options.buttonColorDisabled&&{buttonColorDisabled:options.buttonColorDisabled},{},options.statusBarBackground&&{statusBarBackground:options.statusBarBackground});if(!styles.statusBarBackground&&styles.background){styles.statusBarBackground=styles.background;}var statusBarStyle;if(options.statusBarStyle){statusBarStyle=options.statusBarStyle;}else if(styles.statusBarBackground){statusBarStyle=Color(styles.statusBarBackground).isDark()?'light':'dark';}// Status bar update via native-modules deactivated for now since it doesn't work
|
|
4
|
+
*/export var updateLegacyNavigationBar=function updateLegacyNavigationBar(){var options=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};var targetTab=options.targetTab||'main';var isDefault=options.isDefault;var styles=_extends({},options.color&&{color:options.color},{},options.background&&{background:options.background},{},options.buttonColor&&{buttonColor:options.buttonColor},{},options.buttonColorDisabled&&{buttonColorDisabled:options.buttonColorDisabled},{},options.statusBarBackground&&{statusBarBackground:options.statusBarBackground});if(!styles.statusBarBackground&&styles.background){styles.statusBarBackground=styles.background;}var statusBarStyle;if(options.statusBarStyle){statusBarStyle=options.statusBarStyle;}else if(styles.statusBarBackground){if(styles.statusBarBackground==='transparent'){statusBarStyle='none';}else{statusBarStyle=Color(styles.statusBarBackground).isDark()?'light':'dark';}}// Status bar update via native-modules deactivated for now since it doesn't work
|
|
5
5
|
// reliable when opening a page inside the In-App-Browser.
|
|
6
|
-
if(false&&isAvailable()){var style=statusBarStyle==='dark'?'dark-content':'light-content';StatusBar.setBarStyle({style:style});if(styles.statusBarBackground){StatusBar.setBackgroundColor({color:styles.statusBarBackground});}return;}
|
|
6
|
+
if(false&&isAvailable()){var style=statusBarStyle==='dark'?'dark-content':'light-content';StatusBar.setBarStyle({style:style});if(styles.statusBarBackground){StatusBar.setBackgroundColor({color:styles.statusBarBackground});}return;}var payload=_extends({},statusBarStyle&&{statusBarStyle:statusBarStyle},{},isDefault&&{isDefault:isDefault},{targetTab:targetTab,styles:styles});broadcastEvent({event:'updateNavigationBarStyle',parameters:[payload]});if(isDev){// Dispatch the payload in dev as regular event, so that simulated top inset can adopt the color
|
|
7
|
+
UIEvents.emit('devInternalUpdateStatusBarStyle',payload);}};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{default as useLongPress}from"./useLongPress";export{default as useScrollDirectionChange}from"./useScrollDirectionChange";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import{useRef,useCallback}from'react';/**
|
|
2
|
+
* @typedef {Object} LongPressHandlers
|
|
3
|
+
* @property {Function} onMouseDown - Attach to `onMouseDown` event.
|
|
4
|
+
* @property {Function} onTouchStart - Attach to `onTouchStart` event.
|
|
5
|
+
* @property {Function} onMouseUp - Attach to `onMouseUp` event.
|
|
6
|
+
* @property {Function} onMouseLeave - Attach to `onMouseLeave` event.
|
|
7
|
+
* @property {Function} onTouchEnd - Attach to `onTouchEnd` event.
|
|
8
|
+
* @property {Function} onContextMenu - Attach to `onContextMenu` event to prevent the native
|
|
9
|
+
* context menu.
|
|
10
|
+
*/ /**
|
|
11
|
+
* Prevents the default context menu from appearing on long press.
|
|
12
|
+
* @param {MouseEvent} e The event object.
|
|
13
|
+
*/var preventContextMenu=function preventContextMenu(e){e.preventDefault();};/**
|
|
14
|
+
* Custom hook to handle long press interactions.
|
|
15
|
+
*
|
|
16
|
+
* @param {Function} callback - Function to call on long press.
|
|
17
|
+
* @param {Object} [options={}] - Configuration and lifecycle callbacks.
|
|
18
|
+
* @param {number} [options.threshold=1000] - Duration in milliseconds to trigger long press.
|
|
19
|
+
* @param {Function} [options.onStart] - Called when the press starts.
|
|
20
|
+
* @param {Function} [options.onFinish] - Called when the long press completes.
|
|
21
|
+
* @param {Function} [options.onCancel] - Called when the press is cancelled before the threshold.
|
|
22
|
+
*
|
|
23
|
+
* @returns {LongPressHandlers} An object containing event handlers for mouse and touch events.
|
|
24
|
+
*/export default function useLongPress(callback){var _ref=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{},_ref$threshold=_ref.threshold,threshold=_ref$threshold===void 0?1000:_ref$threshold,onStart=_ref.onStart,onFinish=_ref.onFinish,onCancel=_ref.onCancel;var timerRef=useRef(null);var triggeredRef=useRef(false);var start=useCallback(function(e){if(onStart)onStart(e);triggeredRef.current=false;timerRef.current=setTimeout(function(){callback(e);triggeredRef.current=true;if(onFinish)onFinish(e);},threshold);},[onStart,threshold,callback,onFinish]);var cancel=useCallback(function(e){clearTimeout(timerRef.current);if(!triggeredRef.current&&onCancel){onCancel(e);}},[onCancel]);return{onMouseDown:start,onTouchStart:start,onMouseUp:cancel,onMouseLeave:cancel,onTouchEnd:cancel,// prevents right-click or long-press menu
|
|
25
|
+
onContextMenu:preventContextMenu};}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
var _excluded=["scrollIn","scrollOut","scrolled"],_excluded2=["scrollIn","scrollOut","scrolled"];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{useEffect,useRef,useCallback}from'react';import{viewScroll$}from'@shopgate/engage/core/streams';/**
|
|
2
|
+
* @typedef {Object} ViewScrollEvent
|
|
3
|
+
* @property {Event} event The original scroll event object
|
|
4
|
+
* @property {number} scrollTop Current vertical scroll position
|
|
5
|
+
* @property {number} previousScrollTop Previous scrollTop value
|
|
6
|
+
* @property {boolean} scrollDown True if scrolling down
|
|
7
|
+
* @property {boolean} scrollUp True if scrolling up
|
|
8
|
+
* @property {'up' | 'down' | null} direction Scroll direction
|
|
9
|
+
*/ /**
|
|
10
|
+
* @callback ScrollCallback
|
|
11
|
+
* @param {ViewScrollEvent} event
|
|
12
|
+
* @returns {void}
|
|
13
|
+
*/ /**
|
|
14
|
+
* A scroll hook that detects scroll direction changes (up/down) and
|
|
15
|
+
* triggers the appropriate callbacks. Commonly used to show/hide
|
|
16
|
+
* UI elements based on scroll behavior.
|
|
17
|
+
*
|
|
18
|
+
* @param {Object} params The hook parameters
|
|
19
|
+
* @param {boolean} params.enabled Whether the hook is active
|
|
20
|
+
* @param {number} [params.offset=100] ScrollTop threshold for down scroll triggers. When set,
|
|
21
|
+
* onScrollDown will first be triggered when the scroll position is greater than this value.
|
|
22
|
+
* @param {boolean} [params.onlyFireOnDirectionChange=true]
|
|
23
|
+
* If true, callbacks fire only once per direction change
|
|
24
|
+
* @param {ScrollCallback} [params.onScrollUp] Triggered on scroll up
|
|
25
|
+
* @param {ScrollCallback} [params.onScrollDown] Triggered on scroll down past offset
|
|
26
|
+
*/export default function useScrollDirectionChange(_ref){var enabled=_ref.enabled,_ref$offset=_ref.offset,offset=_ref$offset===void 0?100:_ref$offset,_ref$onlyFireOnDirect=_ref.onlyFireOnDirectionChange,onlyFireOnDirectionChange=_ref$onlyFireOnDirect===void 0?true:_ref$onlyFireOnDirect,onScrollUp=_ref.onScrollUp,onScrollDown=_ref.onScrollDown;var lastDirectionRef=useRef(null);var downTriggeredRef=useRef(false);var upTriggeredRef=useRef(false);/**
|
|
27
|
+
* Scroll event handler.
|
|
28
|
+
* Uses `event.direction` and triggers callbacks accordingly.
|
|
29
|
+
*/var handleScroll=useCallback(/** @param {ViewScrollEvent} event The event */function(event){if(!enabled||!event.scrolled||!event.direction)return;var scrollTop=event.scrollTop,direction=event.direction;var prevDirection=lastDirectionRef.current;var directionChanged=direction!==prevDirection;// Store current direction and reset flags if direction changed
|
|
30
|
+
if(directionChanged){lastDirectionRef.current=direction;if(direction==='down')downTriggeredRef.current=false;if(direction==='up')upTriggeredRef.current=false;}// 🔽 Handle downward scroll
|
|
31
|
+
if(direction==='down'){var shouldFire=(!onlyFireOnDirectionChange||directionChanged||!downTriggeredRef.current)&&scrollTop>=offset;if(shouldFire&&typeof onScrollDown==='function'){downTriggeredRef.current=true;// Strip internal/legacy properties
|
|
32
|
+
var scrollIn=event.scrollIn,scrollOut=event.scrollOut,scrolled=event.scrolled,publicEvent=_objectWithoutProperties(event,_excluded);onScrollDown(publicEvent);}}// 🔼 Handle upward scroll
|
|
33
|
+
if(direction==='up'){var _shouldFire=!onlyFireOnDirectionChange||directionChanged||!upTriggeredRef.current;if(_shouldFire&&typeof onScrollUp==='function'){upTriggeredRef.current=true;var _scrollIn=event.scrollIn,_scrollOut=event.scrollOut,_scrolled=event.scrolled,_publicEvent=_objectWithoutProperties(event,_excluded2);onScrollUp(_publicEvent);}}},[enabled,offset,onlyFireOnDirectionChange,onScrollUp,onScrollDown]);useEffect(function(){if(!enabled)return undefined;var subscription=viewScroll$.subscribe(handleScroll);return function(){return subscription.unsubscribe();};},[enabled,handleScroll]);}
|
package/core/hooks/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
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*from"./html";export{
|
|
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*from"./html";export{usePrevious}from"./usePrevious";export{useResponsiveValue}from'@shopgate/engage/components/ResponsiveContainer/hooks';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{default as useElementSize}from"./useElementSize";
|
|
@@ -0,0 +1,26 @@
|
|
|
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);}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{useState,useEffect,useRef}from'react';import throttle from'lodash/throttle';/**
|
|
2
|
+
* @typedef {Object} ElementSize
|
|
3
|
+
* @property {number} height - The element's height in pixels.
|
|
4
|
+
* @property {number} [width] - The element's width in pixels (optional).
|
|
5
|
+
*/ /**
|
|
6
|
+
* @typedef {Object} UseElementSizeOptions
|
|
7
|
+
* @property {number} [throttleMs=100] - Throttle delay in milliseconds.
|
|
8
|
+
* @property {boolean} [includeWidth=false] - Whether to measure the element's width.
|
|
9
|
+
*/ /**
|
|
10
|
+
* Tracks the height (and optionally width) of a DOM element using ResizeObserver,
|
|
11
|
+
* with a fallback to window resize and MutationObserver in older browsers.
|
|
12
|
+
*
|
|
13
|
+
* @param {React.RefObject<HTMLElement>} ref - A ref to the element being measured.
|
|
14
|
+
* @param {UseElementSizeOptions} [options={}] - Optional settings.
|
|
15
|
+
* @returns {ElementSize} The current height (and optionally width) of the element.
|
|
16
|
+
*/export default function useElementSize(ref){var options=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};var _options$throttleMs=options.throttleMs,throttleMs=_options$throttleMs===void 0?100:_options$throttleMs,_options$includeWidth=options.includeWidth,includeWidth=_options$includeWidth===void 0?false:_options$includeWidth;/**
|
|
17
|
+
* Holds the current element size in state.
|
|
18
|
+
*/var _useState=useState(_extends({height:0},includeWidth&&{width:0})),_useState2=_slicedToArray(_useState,2),size=_useState2[0],setSize=_useState2[1];/**
|
|
19
|
+
* Tracks the most recent measured size to prevent unnecessary state updates.
|
|
20
|
+
*/var sizeRef=useRef(size);useEffect(function(){var element=ref.current;if(!element)return undefined;/**
|
|
21
|
+
* Measures the current size of the element and updates state if it changed.
|
|
22
|
+
*/var updateSize=function updateSize(){var newHeight=element.offsetHeight;var newWidth=includeWidth?element.offsetWidth:undefined;var hasChanged=newHeight!==sizeRef.current.height||includeWidth&&newWidth!==sizeRef.current.width;if(hasChanged){var newSize=_extends({height:newHeight},includeWidth?{width:newWidth}:{});sizeRef.current=newSize;setSize(newSize);}};/**
|
|
23
|
+
* Throttles the update function to limit how often measurements occur.
|
|
24
|
+
*/var throttledUpdate=throttle(updateSize,throttleMs);/**
|
|
25
|
+
* Sets up ResizeObserver or fallback observers to trigger measurement.
|
|
26
|
+
*/var cleanup=function cleanup(){};if('ResizeObserver'in window){var resizeObserver=new ResizeObserver(throttledUpdate);resizeObserver.observe(element);updateSize();cleanup=function cleanup(){resizeObserver.disconnect();throttledUpdate.cancel();};}else{window.addEventListener('resize',throttledUpdate);var mutationObserver=new MutationObserver(throttledUpdate);mutationObserver.observe(element,{childList:true,subtree:true,characterData:true});updateSize();cleanup=function cleanup(){window.removeEventListener('resize',throttledUpdate);mutationObserver.disconnect();throttledUpdate.cancel();};}return cleanup;},[ref,throttleMs,includeWidth]);return size;}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export*from"./settings";export*from"./storage";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import{DEVELOPMENT_TOOLS_TOGGLE_INSETS,DEVELOPMENT_TOOLS_TOGGLE_INSET_HIGHLIGHT}from"../constants";/**
|
|
2
|
+
* Toggles the simulation of iOS safe area insets.
|
|
3
|
+
* @param {boolean} visible Whether the insets should be visible or not.
|
|
4
|
+
* @returns {Object} The action object.
|
|
5
|
+
*/export var toggleInsets=function toggleInsets(){var visible=arguments.length>0&&arguments[0]!==undefined?arguments[0]:true;return{type:DEVELOPMENT_TOOLS_TOGGLE_INSETS,visible:visible};};/**
|
|
6
|
+
* Toggles the highlighting of the simulated iOS safe area insets.
|
|
7
|
+
* @param {boolean} visible Whether the insets should be visible or not.
|
|
8
|
+
* @returns {Object} The action object.
|
|
9
|
+
*/export var toggleInsetHighlight=function toggleInsetHighlight(){var visible=arguments.length>0&&arguments[0]!==undefined?arguments[0]:true;return{type:DEVELOPMENT_TOOLS_TOGGLE_INSET_HIGHLIGHT,visible:visible};};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import{DEVELOPMENT_TOOLS_UPDATE_STATUS_BAR_STYLE_STORAGE}from"../constants";/**
|
|
2
|
+
* Updates the status bar style storage where latest payload from the updateNavigationBarStyle
|
|
3
|
+
* app event is stored.
|
|
4
|
+
* @param {boolean} style The event payload
|
|
5
|
+
* @returns {Object} The action object.
|
|
6
|
+
*/export var updateStatusBarStyleStorage=function updateStatusBarStyleStorage(){var style=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};return{type:DEVELOPMENT_TOOLS_UPDATE_STATUS_BAR_STYLE_STORAGE,style:style};};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import React,{memo}from'react';import PropTypes from'prop-types';import{isDev}from'@shopgate/engage/core/helpers';import Shortcuts from"./Shortcuts";import SimulatedInsets from"../SimulatedInsets";/**
|
|
2
|
+
* Provides development tools for the app.
|
|
3
|
+
* @param {Object} props The component props.
|
|
4
|
+
* @param {React.ReactNode} props.children The child components.
|
|
5
|
+
* @returns {JSX.Element}
|
|
6
|
+
*/var DevelopmentTools=function DevelopmentTools(_ref){var children=_ref.children;if(!isDev){return children;}return React.createElement(React.Fragment,null,React.createElement(Shortcuts,null),React.createElement(SimulatedInsets,null,children));};export default memo(DevelopmentTools);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{useSelector,useDispatch}from'react-redux';import{toggleInsets}from'@shopgate/engage/development/action-creators';import{getAreSimulatedInsetsInjected}from'@shopgate/engage/development/selectors';import{useShortcut}from"./hooks";/**
|
|
2
|
+
* The Shortcuts component maps shortcuts to actions in development mode.
|
|
3
|
+
* @returns {JSX.Element}
|
|
4
|
+
*/var Shortcuts=function Shortcuts(){var dispatch=useDispatch();var areInsetsInjected=useSelector(getAreSimulatedInsetsInjected);useShortcut('ctrl+i',function(){dispatch(toggleInsets(!areInsetsInjected));});return null;};export default Shortcuts;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import{useEffect,useCallback}from'react';/**
|
|
2
|
+
* Hook to handle a single keyboard shortcut.
|
|
3
|
+
* Supports both 'cmd' (meta) and 'ctrl' keys explicitly.
|
|
4
|
+
*
|
|
5
|
+
* @param {string} shortcut - Shortcut string using mac-style notation, e.g. 'cmd+i'.
|
|
6
|
+
* @param {Function} callback - Function to call when the shortcut is triggered.
|
|
7
|
+
*/export function useShortcut(shortcut,callback){var normalizeShortcut=useCallback(function(){return shortcut.toLowerCase().split('+').map(function(k){return k.trim();}).sort().join('+');},[shortcut]);var handleKeyDown=useCallback(function(event){var keys=[];if(event.ctrlKey)keys.push('ctrl');if(event.metaKey)keys.push('cmd');// Treat meta as cmd
|
|
8
|
+
if(event.altKey)keys.push('alt');if(event.shiftKey)keys.push('shift');var key=event.key.toLowerCase();if(!['control','meta','alt','shift'].includes(key)){keys.push(key);}var pressed=keys.sort().join('+');if(pressed===normalizeShortcut()){event.preventDefault();callback(event);}},[callback,normalizeShortcut]);useEffect(function(){window.addEventListener('keydown',handleKeyDown);return function(){return window.removeEventListener('keydown',handleKeyDown);};},[handleKeyDown]);}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{default}from"./DevelopmentTools";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
var _excluded=["highlightInset","onClick"];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 _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 _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,{useMemo}from'react';import PropTypes from'prop-types';import classNames from'classnames';import{css}from'glamor';var classes={container:css({position:'fixed',bottom:0,display:'flex',justifyContent:'center',alignItems:'center',height:'var(--safe-area-inset-bottom)',width:'100%',zIndex:10000000,pointerEvents:'auto',transition:'background 0.2s ease'}),containerHighlight:css({background:'rgba(255, 0, 0, 0.7)'}),handle:css({width:120,height:3,borderRadius:3,background:'rgba(0, 0, 0, 0.4)',border:'1px solid rgba(255, 255, 255, 0.5)',boxSizing:'content-box'})};/**
|
|
2
|
+
* Renders a simulated iOS bottom inset in development.
|
|
3
|
+
* @param {Object} props The component props.
|
|
4
|
+
* @param {boolean} props.highlightInset Whether the inset is highlighted.
|
|
5
|
+
* @param {Function} props.onClick The function to call when the inset is clicked.
|
|
6
|
+
* @returns {JSX.Element}
|
|
7
|
+
*/var SimulatedInsetBottom=function SimulatedInsetBottom(_ref){var highlightInset=_ref.highlightInset,onClick=_ref.onClick,props=_objectWithoutProperties(_ref,_excluded);var containerClasses=useMemo(function(){return classNames(classes.container,_defineProperty({},classes.containerHighlight,highlightInset));},[highlightInset]);return React.createElement("div",_extends({"aria-hidden":true,role:"presentation",className:containerClasses},props,{onClick:onClick}),React.createElement("div",{className:classes.handle}));};export default SimulatedInsetBottom;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
var _excluded=["highlightInset","onClick"];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 _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,{useState,useEffect,useMemo}from'react';import PropTypes from'prop-types';import classNames from'classnames';import{useSelector}from'react-redux';import{css}from'glamor';import{getStatusBarStyleStorage}from'@shopgate/engage/development/selectors';var classes={container:css({position:'fixed',top:0,display:'flex',justifyContent:'space-between',alignItems:'center',height:'var(--safe-area-inset-top)',width:'100%',zIndex:10000000,pointerEvents:'auto',transition:'background 0.2s ease',fontSize:'16px'}),containerHighlight:css({background:'rgba(255, 0, 0, 0.7)'}),styleLight:css({color:'white'}),styleDark:css({color:'black'}),styleNone:css({color:'transparent'}),info:css({flex:1,textAlign:'center',fontWeight:500}),notch:css({flex:1,background:'black',height:'calc(var(--safe-area-inset-top) - 16px)',maxWidth:150,borderRadius:16,border:'1px solid rgba(255, 255, 255, 0.5)'})};/**
|
|
2
|
+
* Creates a human readable time string to mimic the iOS clock.
|
|
3
|
+
* @returns {string} The current time in a human readable format.
|
|
4
|
+
*/var getTime=function getTime(){var now=new Date();return now.toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'});};/**
|
|
5
|
+
* Renders a simulated iOS top inset in development.
|
|
6
|
+
* @param {Object} props The component props.
|
|
7
|
+
* @param {boolean} props.highlightInset Whether the inset is highlighted.
|
|
8
|
+
* @param {Function} props.onClick The function to call when the inset is clicked.
|
|
9
|
+
* @returns {JSX.Element}
|
|
10
|
+
*/var SimulatedInsetTop=function SimulatedInsetTop(_ref){var highlightInset=_ref.highlightInset,onClick=_ref.onClick,props=_objectWithoutProperties(_ref,_excluded);// State to hold the current time string for the status bar
|
|
11
|
+
var _useState=useState(getTime()),_useState2=_slicedToArray(_useState,2),time=_useState2[0],setTime=_useState2[1];// Effect to update the time on regular intervals
|
|
12
|
+
useEffect(function(){var interval=setInterval(function(){setTime(getTime());},10*1000);return function(){return clearInterval(interval);};},[]);var _useSelector=useSelector(getStatusBarStyleStorage),statusBarStyle=_useSelector.statusBarStyle;var containerClasses=useMemo(function(){return classNames(classes.container,_defineProperty(_defineProperty(_defineProperty(_defineProperty({},classes.containerHighlight,highlightInset),classes.styleDark,statusBarStyle==='dark'),classes.styleLight,statusBarStyle==='light'),classes.styleNone,statusBarStyle==='none'));},[highlightInset,statusBarStyle]);return React.createElement("div",_extends({"aria-hidden":true,role:"presentation",className:classNames(containerClasses)},props,{onClick:onClick}),React.createElement("div",{className:classes.info},time),React.createElement("div",{className:classes.notch}),React.createElement("div",{className:classes.info},"5G"));};export default SimulatedInsetTop;
|
|
@@ -0,0 +1,6 @@
|
|
|
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 React,{useCallback}from'react';import{useSelector,useDispatch}from'react-redux';import PropTypes from'prop-types';import{useLongPress}from'@shopgate/engage/core/hooks';import{getAreSimulatedInsetsInjected,getIsInsetHighlightVisible}from'@shopgate/engage/development/selectors';import{toggleInsetHighlight,toggleInsets}from'@shopgate/engage/development/action-creators';import SimulatedInsetTop from"./SimulatedInsetTop";import SimulatedInsetBottom from"./SimulatedInsetBottom";/**
|
|
2
|
+
* Simulates iOS insets in development mode.
|
|
3
|
+
* @param {Object} props The component props.
|
|
4
|
+
* @param {React.ReactNode} props.children The child components.
|
|
5
|
+
* @returns {JSX.Element}
|
|
6
|
+
*/var SimulatedInsets=function SimulatedInsets(_ref){var children=_ref.children;var hasSimulatedSafeAreaInsets=useSelector(getAreSimulatedInsetsInjected);var dispatch=useDispatch();var highlightInset=useSelector(getIsInsetHighlightVisible);var handleClick=useCallback(function(){dispatch(toggleInsetHighlight(!highlightInset));},[dispatch,highlightInset]);var attrs=useLongPress(function(){dispatch(toggleInsets(!hasSimulatedSafeAreaInsets));});return React.createElement(React.Fragment,null,hasSimulatedSafeAreaInsets&&React.createElement(SimulatedInsetTop,_extends({onClick:handleClick,highlightInset:highlightInset},attrs)),children,hasSimulatedSafeAreaInsets&&React.createElement(SimulatedInsetBottom,_extends({onClick:handleClick,highlightInset:highlightInset},attrs)));};export default SimulatedInsets;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{default}from"./SimulatedInsets";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{default as DevelopmentTools}from"./DevelopmentTools";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export var DEVELOPMENT_TOOLS_TOGGLE_INSETS='DEVELOPMENT_TOOLS_TOGGLE_INSETS';export var DEVELOPMENT_TOOLS_TOGGLE_INSET_HIGHLIGHT='DEVELOPMENT_TOOLS_TOGGLE_INSET_HIGHLIGHT';export var DEVELOPMENT_TOOLS_UPDATE_STATUS_BAR_STYLE_STORAGE='DEVELOPMENT_TOOLS_UPDATE_STATUS_BAR_STYLE_STORAGE';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export*from"./actionTypes";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{combineReducers}from'redux';import settings from"./settings";import storage from"./storage";export default combineReducers({settings:settings,storage:storage});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import{produce}from'immer';import{isDev}from'@shopgate/engage/core/helpers';import{DEVELOPMENT_TOOLS_TOGGLE_INSETS,DEVELOPMENT_TOOLS_TOGGLE_INSET_HIGHLIGHT}from"../constants";/**
|
|
2
|
+
* @typedef {Object} DevToolsSettingsState
|
|
3
|
+
* @property {boolean} showInsets
|
|
4
|
+
* @property {boolean} showInsetHighlight
|
|
5
|
+
*/ /** @type DevToolsSettingsState */var initialState={showInsets:null,showInsetHighlight:false};/**
|
|
6
|
+
* The reducer for all development tools settings related states.
|
|
7
|
+
* @param {Object} state The application state.
|
|
8
|
+
* @param {Object} action The redux action.
|
|
9
|
+
* @returns {Object}
|
|
10
|
+
*/export default function settingsReducer(){var state=arguments.length>0&&arguments[0]!==undefined?arguments[0]:initialState;var action=arguments.length>1?arguments[1]:undefined;/* eslint-disable no-param-reassign */var producer=produce(/** @param {DevToolsSettingsState} draft The draft */function(draft){if(!isDev){return;}switch(action.type){case DEVELOPMENT_TOOLS_TOGGLE_INSETS:{draft.showInsets=action.visible;break;}case DEVELOPMENT_TOOLS_TOGGLE_INSET_HIGHLIGHT:{draft.showInsetHighlight=action.visible;break;}default:break;}});/* eslint-enable no-param-reassign */return producer(state);}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import{produce}from'immer';import{isDev}from'@shopgate/engage/core/helpers';import{DEVELOPMENT_TOOLS_UPDATE_STATUS_BAR_STYLE_STORAGE}from"../constants";/**
|
|
2
|
+
* @typedef {Object} DevToolsStorageState
|
|
3
|
+
* @property {Object} statusBarStyle
|
|
4
|
+
*/ /** @type DevToolsStorageState */var initialState={statusBarStyle:{styles:{}}};/**
|
|
5
|
+
* The reducer for all developer tools storage related states.
|
|
6
|
+
* @param {Object} state The application state.
|
|
7
|
+
* @param {Object} action The redux action.
|
|
8
|
+
* @returns {Object}
|
|
9
|
+
*/export default function storageReducer(){var state=arguments.length>0&&arguments[0]!==undefined?arguments[0]:initialState;var action=arguments.length>1?arguments[1]:undefined;/* eslint-disable no-param-reassign */var producer=produce(/** @param {DevToolsStorageState} draft The draft */function(draft){if(!isDev){return;}switch(action.type){case DEVELOPMENT_TOOLS_UPDATE_STATUS_BAR_STYLE_STORAGE:{draft.statusBarStyle=action.style;break;}default:break;}});/* eslint-enable no-param-reassign */return producer(state);}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export*from"./settings";export*from"./storage";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import{createSelector}from'reselect';import MobileDetect from'mobile-detect';import{isDev as isDevelopment,hasSGJavaScriptBridge}from'@shopgate/engage/core/helpers';var md=new MobileDetect(navigator.userAgent);/**
|
|
2
|
+
* Retrieves the development settings state from the store.
|
|
3
|
+
* @param {Object} state The current application state.
|
|
4
|
+
* @return {Object} The development settings state.
|
|
5
|
+
*/var getState=function getState(state){return state.development.settings;};/**
|
|
6
|
+
* Creates a selector to determine if development mode is enabled.
|
|
7
|
+
* @type {(state: any) => boolean}
|
|
8
|
+
*/export var getIsDev=createSelector(function(){return isDevelopment;});/**
|
|
9
|
+
* Creates a selector to determine if the simulated iOS insets are supposed to be shown.
|
|
10
|
+
* @type {(state: any) => boolean}
|
|
11
|
+
*/export var getAreInsetsVisible=createSelector(getIsDev,getState,function(isDev,settings){if(!isDev){return false;}return settings.showInsets;});/**
|
|
12
|
+
* Creates a selector to determine if the inset highlight is visible.
|
|
13
|
+
* @type {(state: any) => boolean}
|
|
14
|
+
*/export var getIsInsetHighlightVisible=createSelector(getIsDev,getState,function(isDev,settings){if(!isDev){return false;}return settings.showInsetHighlight;});/**
|
|
15
|
+
* Creates a selector to check if simulated safe area insets are supposed to be injected.
|
|
16
|
+
* @type {(state: any) => boolean}
|
|
17
|
+
*/export var getAreSimulatedInsetsInjected=createSelector(getIsDev,getAreInsetsVisible,function(isDev,insetsVisible){// No insets injected if the app is not in development mode.
|
|
18
|
+
if(!isDev){return false;}// No insets injected if PWA is running inside the app
|
|
19
|
+
if(hasSGJavaScriptBridge()){return false;}// If the state contains a bool value, respect is.
|
|
20
|
+
if(typeof insetsVisible==='boolean'){return insetsVisible;}// Show insets on simulated iOS devices by default if insets decision is not set.
|
|
21
|
+
return insetsVisible===null&&md.os()==='iOS';});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import{createSelector}from'reselect';/**
|
|
2
|
+
* Retrieves the development storage state from the store.
|
|
3
|
+
* @param {Object} state The current application state.
|
|
4
|
+
* @return {Object} The development storage state.
|
|
5
|
+
*/var getState=function getState(state){return state.development.storage;};/**
|
|
6
|
+
* @typedef {Object} StatusBarStyleStyles
|
|
7
|
+
* @property {string} statusBarBackground Current background color of the app bar
|
|
8
|
+
*/ /**
|
|
9
|
+
* @typedef {Object} StatusBarStyle
|
|
10
|
+
* @property {boolean} isDefault Whether the style is the default one which was initially applied
|
|
11
|
+
* @property {"light"|"dark"} statusBarStyle The status style for the iOS status bar
|
|
12
|
+
* @property {StatusBarStyleStyles} styles Additional styles for the status bar
|
|
13
|
+
*/ /**
|
|
14
|
+
* Creates a selector that returns the current status bar style object from the storage.
|
|
15
|
+
* @type {(state: any) => StatusBarStyle}
|
|
16
|
+
*/export var getStatusBarStyleStorage=createSelector(getState,function(state){return state.statusBarStyle;});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export*from"./insets";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{main$}from'@shopgate/engage/core/streams';import{DEVELOPMENT_TOOLS_TOGGLE_INSETS}from"../constants";/**
|
|
2
|
+
* Gets triggered after the simulated page insets were updated
|
|
3
|
+
* @type {Observable}
|
|
4
|
+
*/export var simulatedPageInsetsDidUpdate$=main$.filter(function(_ref){var action=_ref.action;return action.type===DEVELOPMENT_TOOLS_TOGGLE_INSETS;});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import{appWillStart$}from'@shopgate/engage/core/streams';import{UIEvents}from'@shopgate/engage/core/events';import{updateStatusBarStyleStorage}from'@shopgate/engage/development/action-creators';/**
|
|
2
|
+
* Development subscriptions
|
|
3
|
+
* @param {Function} subscribe The subscribe function
|
|
4
|
+
*/export default function development(subscribe){subscribe(appWillStart$,function(_ref){var dispatch=_ref.dispatch;// Listen for the app event which updates the status bar style and store it into the
|
|
5
|
+
// development storage redux store.
|
|
6
|
+
UIEvents.addListener('devInternalUpdateStatusBarStyle',function(event){dispatch(updateStatusBarStyleStorage(event));});});}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shopgate/engage",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.27.0-beta.1",
|
|
4
4
|
"description": "Shopgate's ENGAGE library.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Shopgate <support@shopgate.com>",
|
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@shopgate/native-modules": "^1.0.0-beta.22",
|
|
19
|
-
"@shopgate/pwa-common": "7.
|
|
20
|
-
"@shopgate/pwa-common-commerce": "7.
|
|
21
|
-
"@shopgate/pwa-core": "7.
|
|
22
|
-
"@shopgate/pwa-ui-ios": "7.
|
|
23
|
-
"@shopgate/pwa-ui-material": "7.
|
|
24
|
-
"@shopgate/pwa-ui-shared": "7.
|
|
19
|
+
"@shopgate/pwa-common": "7.27.0-beta.1",
|
|
20
|
+
"@shopgate/pwa-common-commerce": "7.27.0-beta.1",
|
|
21
|
+
"@shopgate/pwa-core": "7.27.0-beta.1",
|
|
22
|
+
"@shopgate/pwa-ui-ios": "7.27.0-beta.1",
|
|
23
|
+
"@shopgate/pwa-ui-material": "7.27.0-beta.1",
|
|
24
|
+
"@shopgate/pwa-ui-shared": "7.27.0-beta.1",
|
|
25
25
|
"@stripe/react-stripe-js": "^1.16.5",
|
|
26
26
|
"@stripe/stripe-js": "^1.3.1",
|
|
27
27
|
"@virtuous/conductor": "~2.5.0",
|
package/core/hooks/useScroll.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import _throttle from"lodash/throttle";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{useEffect,useState,useCallback}from'react';/**
|
|
2
|
-
*
|
|
3
|
-
* @param {Function} callback callback
|
|
4
|
-
* @param {Object} [element] element
|
|
5
|
-
*/export function useScroll(callback,element){var _useState=useState(0),_useState2=_slicedToArray(_useState,2),setScrollPosition=_useState2[1];var previousScrollTop=0;/**
|
|
6
|
-
* Scroll handler
|
|
7
|
-
*/var handleDocumentScroll=useCallback(_throttle(function(){var container=element||document.documentElement||document.body;var currentScrollTop=container.scrollY===undefined?container.scrollTop:container.scrollY;setScrollPosition(function(previousPosition){previousScrollTop=previousPosition;return currentScrollTop;});callback({previousScrollTop:previousScrollTop,currentScrollTop:currentScrollTop});},250),[setScrollPosition,element,callback]);useEffect(function(){(element||window).addEventListener('scroll',handleDocumentScroll);return function(){return(element||window).removeEventListener('scroll',handleDocumentScroll);};},[handleDocumentScroll,element]);}
|