diy-template-components 5.12.0 → 5.14.0
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/build/index.es.js +190 -30
- package/build/index.es.js.map +1 -1
- package/build/index.js +190 -30
- package/build/index.js.map +1 -1
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -363,7 +363,7 @@ const useSectionStyles$b = createUseStyles(theme => ({
|
|
|
363
363
|
bottom: 0,
|
|
364
364
|
left: 0,
|
|
365
365
|
width: '100%',
|
|
366
|
-
zIndex: '
|
|
366
|
+
zIndex: '10000'
|
|
367
367
|
}
|
|
368
368
|
},
|
|
369
369
|
mobileAppNameClass: {
|
|
@@ -623,7 +623,11 @@ const Button = /*#__PURE__*/React.forwardRef(function Button({
|
|
|
623
623
|
rel: data?.isExternal ? 'nofollow noopener' : null,
|
|
624
624
|
className: (active ? classes.active : '') + ' ' + classes[type] + ' ' + classes[size] + ' ' + classes.anchorClass,
|
|
625
625
|
style: styling
|
|
626
|
-
}, elementProps),
|
|
626
|
+
}, elementProps), /*#__PURE__*/React__default["default"].createElement("span", {
|
|
627
|
+
dangerouslySetInnerHTML: {
|
|
628
|
+
__html: data?.value || ''
|
|
629
|
+
}
|
|
630
|
+
})) : /*#__PURE__*/React__default["default"].createElement("button", _extends({
|
|
627
631
|
ref: ref,
|
|
628
632
|
className: (active ? classes.active : '') + ' ' + classes[type] + ' ' + classes[size],
|
|
629
633
|
onClick: isEdit ? null : onClick,
|
|
@@ -10924,7 +10928,7 @@ const SingleVideoSlide$1 = props => {
|
|
|
10924
10928
|
currencySymbol
|
|
10925
10929
|
} = props;
|
|
10926
10930
|
const showCourseInstallmentData = data?.courseOverviewData;
|
|
10927
|
-
const InstalmentData = isEdit ? data?.courseOverviewData?.installments?.formData?.installments[0].installmentAmount : data?.courseOverviewData?.installments?.formData?.installmentInfo?.installments[0]
|
|
10931
|
+
const InstalmentData = isEdit ? data?.courseOverviewData?.installments?.formData?.installments[0].installmentAmount : data?.courseOverviewData?.installments?.formData?.installmentInfo?.installments?.[0]?.installmentPrice;
|
|
10928
10932
|
const checkIfOfferIsValid = () => moment__default["default"]().diff(moment__default["default"](props?.data?.endDate || 0)) < 0;
|
|
10929
10933
|
const checkForShowDiscount = () => {
|
|
10930
10934
|
if (props.data.endDate === null || checkIfOfferIsValid()) {
|
|
@@ -12623,29 +12627,125 @@ const useCounterSectionStyles = createUseStyles(theme => {
|
|
|
12623
12627
|
const DURATION_MS = 2000;
|
|
12624
12628
|
const EASING = t => 1 - Math.pow(1 - t, 3); // easeOutCubic
|
|
12625
12629
|
|
|
12626
|
-
function
|
|
12630
|
+
function getPlainTextFromHtml(html) {
|
|
12631
|
+
if (typeof document === 'undefined') {
|
|
12632
|
+
return String(html).replace(/<[^>]*>/g, '');
|
|
12633
|
+
}
|
|
12634
|
+
const div = document.createElement('div');
|
|
12635
|
+
div.innerHTML = html;
|
|
12636
|
+
return div.textContent || div.innerText || '';
|
|
12637
|
+
}
|
|
12638
|
+
function parseStyleString(styleStr) {
|
|
12639
|
+
if (!styleStr || typeof styleStr !== 'string') return {};
|
|
12640
|
+
const obj = {};
|
|
12641
|
+
styleStr.split(';').forEach(part => {
|
|
12642
|
+
const colonIdx = part.indexOf(':');
|
|
12643
|
+
if (colonIdx > 0) {
|
|
12644
|
+
const key = part.slice(0, colonIdx).trim();
|
|
12645
|
+
const val = part.slice(colonIdx + 1).trim();
|
|
12646
|
+
if (key && val) obj[key] = val;
|
|
12647
|
+
}
|
|
12648
|
+
});
|
|
12649
|
+
return obj;
|
|
12650
|
+
}
|
|
12651
|
+
const SEMANTIC_TAG_STYLES = {
|
|
12652
|
+
strong: {
|
|
12653
|
+
fontWeight: 'bold'
|
|
12654
|
+
},
|
|
12655
|
+
b: {
|
|
12656
|
+
fontWeight: 'bold'
|
|
12657
|
+
},
|
|
12658
|
+
em: {
|
|
12659
|
+
fontStyle: 'italic'
|
|
12660
|
+
},
|
|
12661
|
+
i: {
|
|
12662
|
+
fontStyle: 'italic'
|
|
12663
|
+
},
|
|
12664
|
+
u: {
|
|
12665
|
+
textDecoration: 'underline'
|
|
12666
|
+
}
|
|
12667
|
+
};
|
|
12668
|
+
|
|
12669
|
+
/**
|
|
12670
|
+
* Parses value (plain text or HTML) to extract number for animation and wrapper attributes.
|
|
12671
|
+
* Returns { end, suffix, wrapperProps } or null if not parseable.
|
|
12672
|
+
* wrapperProps: { className, style } for the rich text wrapper, or null if plain text.
|
|
12673
|
+
*/
|
|
12674
|
+
function parseCounterValueFromHtml(value) {
|
|
12627
12675
|
if (value == null || value === '') return null;
|
|
12628
|
-
const
|
|
12676
|
+
const str = String(value).trim();
|
|
12677
|
+
const plainText = /<[^>]+>/.test(str) ? getPlainTextFromHtml(str) : str;
|
|
12678
|
+
const match = plainText.match(/^([\d.]+)(.*)$/);
|
|
12629
12679
|
if (!match) return null;
|
|
12630
12680
|
const num = parseFloat(match[1]);
|
|
12631
12681
|
if (Number.isNaN(num)) return null;
|
|
12632
|
-
|
|
12633
|
-
|
|
12634
|
-
|
|
12635
|
-
|
|
12682
|
+
const suffix = match[2] || '';
|
|
12683
|
+
if (!/<[^>]+>/.test(str)) {
|
|
12684
|
+
return {
|
|
12685
|
+
end: num,
|
|
12686
|
+
suffix,
|
|
12687
|
+
wrapperProps: null
|
|
12688
|
+
};
|
|
12689
|
+
}
|
|
12690
|
+
if (typeof document === 'undefined') {
|
|
12691
|
+
return {
|
|
12692
|
+
end: num,
|
|
12693
|
+
suffix,
|
|
12694
|
+
wrapperProps: null
|
|
12695
|
+
};
|
|
12696
|
+
}
|
|
12697
|
+
try {
|
|
12698
|
+
const parser = new DOMParser();
|
|
12699
|
+
const doc = parser.parseFromString(str, 'text/html');
|
|
12700
|
+
const root = doc.body?.firstElementChild;
|
|
12701
|
+
if (!root) return {
|
|
12702
|
+
end: num,
|
|
12703
|
+
suffix,
|
|
12704
|
+
wrapperProps: null
|
|
12705
|
+
};
|
|
12706
|
+
const classes = [];
|
|
12707
|
+
const styleObj = {};
|
|
12708
|
+
const collectFromElement = el => {
|
|
12709
|
+
const c = el.getAttribute('class');
|
|
12710
|
+
if (c) classes.push(...c.split(/\s+/).filter(Boolean));
|
|
12711
|
+
const s = el.getAttribute('style');
|
|
12712
|
+
if (s) Object.assign(styleObj, parseStyleString(s));
|
|
12713
|
+
const tagStyles = SEMANTIC_TAG_STYLES[el.tagName?.toLowerCase()];
|
|
12714
|
+
if (tagStyles) Object.assign(styleObj, tagStyles);
|
|
12715
|
+
for (const child of el.children) {
|
|
12716
|
+
collectFromElement(child);
|
|
12717
|
+
}
|
|
12718
|
+
};
|
|
12719
|
+
collectFromElement(root);
|
|
12720
|
+
const className = [...new Set(classes)].join(' ');
|
|
12721
|
+
return {
|
|
12722
|
+
end: num,
|
|
12723
|
+
suffix,
|
|
12724
|
+
wrapperProps: className || Object.keys(styleObj).length ? {
|
|
12725
|
+
className,
|
|
12726
|
+
style: styleObj
|
|
12727
|
+
} : null
|
|
12728
|
+
};
|
|
12729
|
+
} catch {
|
|
12730
|
+
return {
|
|
12731
|
+
end: num,
|
|
12732
|
+
suffix,
|
|
12733
|
+
wrapperProps: null
|
|
12734
|
+
};
|
|
12735
|
+
}
|
|
12636
12736
|
}
|
|
12637
12737
|
function AnimatedCounter({
|
|
12638
12738
|
value,
|
|
12639
12739
|
className,
|
|
12640
12740
|
refSetter
|
|
12641
12741
|
}) {
|
|
12642
|
-
const
|
|
12742
|
+
const animSpanRef = React.useRef(null);
|
|
12643
12743
|
const rafIdRef = React.useRef(null);
|
|
12644
12744
|
const hasAnimatedRef = React.useRef(false);
|
|
12645
12745
|
React.useLayoutEffect(() => {
|
|
12646
12746
|
hasAnimatedRef.current = false; // Reset so we can re-animate when value changes (e.g. live edit)
|
|
12647
|
-
const parsed =
|
|
12648
|
-
if (!parsed || !
|
|
12747
|
+
const parsed = parseCounterValueFromHtml(value);
|
|
12748
|
+
if (!parsed || !animSpanRef.current) return;
|
|
12649
12749
|
const observer = new IntersectionObserver(entries => {
|
|
12650
12750
|
const [entry] = entries;
|
|
12651
12751
|
if (!entry.isIntersecting || hasAnimatedRef.current) return;
|
|
@@ -12660,9 +12760,9 @@ function AnimatedCounter({
|
|
|
12660
12760
|
const progress = Math.min(elapsed / DURATION_MS, 1);
|
|
12661
12761
|
const eased = EASING(progress);
|
|
12662
12762
|
const current = eased * end;
|
|
12663
|
-
if (
|
|
12763
|
+
if (animSpanRef.current) {
|
|
12664
12764
|
const displayValue = Number.isInteger(end) ? Math.round(current) : parseFloat(current.toFixed(2));
|
|
12665
|
-
|
|
12765
|
+
animSpanRef.current.textContent = displayValue + suffix;
|
|
12666
12766
|
}
|
|
12667
12767
|
if (progress < 1) {
|
|
12668
12768
|
rafIdRef.current = requestAnimationFrame(tick);
|
|
@@ -12672,22 +12772,34 @@ function AnimatedCounter({
|
|
|
12672
12772
|
}, {
|
|
12673
12773
|
threshold: 0.2
|
|
12674
12774
|
});
|
|
12675
|
-
observer.observe(
|
|
12775
|
+
observer.observe(animSpanRef.current);
|
|
12676
12776
|
return () => {
|
|
12677
12777
|
observer.disconnect();
|
|
12678
12778
|
if (rafIdRef.current) cancelAnimationFrame(rafIdRef.current);
|
|
12679
12779
|
};
|
|
12680
12780
|
}, [value]);
|
|
12681
|
-
const parsed =
|
|
12781
|
+
const parsed = parseCounterValueFromHtml(value);
|
|
12682
12782
|
const setRef = el => {
|
|
12683
|
-
elRef.current = el;
|
|
12684
12783
|
refSetter?.(el);
|
|
12685
12784
|
};
|
|
12686
12785
|
if (parsed) {
|
|
12786
|
+
const {
|
|
12787
|
+
end,
|
|
12788
|
+
suffix,
|
|
12789
|
+
wrapperProps
|
|
12790
|
+
} = parsed;
|
|
12791
|
+
const initialDisplay = Number.isInteger(end) ? '0' : '0.00';
|
|
12792
|
+
const animatedContent = /*#__PURE__*/React__default["default"].createElement("span", {
|
|
12793
|
+
ref: animSpanRef
|
|
12794
|
+
}, initialDisplay, suffix);
|
|
12795
|
+
const content = wrapperProps ? /*#__PURE__*/React__default["default"].createElement("span", {
|
|
12796
|
+
className: wrapperProps.className || undefined,
|
|
12797
|
+
style: Object.keys(wrapperProps.style || {}).length ? wrapperProps.style : undefined
|
|
12798
|
+
}, animatedContent) : animatedContent;
|
|
12687
12799
|
return /*#__PURE__*/React__default["default"].createElement("h2", {
|
|
12688
12800
|
ref: setRef,
|
|
12689
12801
|
className: className
|
|
12690
|
-
},
|
|
12802
|
+
}, content);
|
|
12691
12803
|
}
|
|
12692
12804
|
return /*#__PURE__*/React__default["default"].createElement("h2", {
|
|
12693
12805
|
ref: refSetter,
|
|
@@ -12739,6 +12851,7 @@ function CounterSection({
|
|
|
12739
12851
|
key: item._id || index,
|
|
12740
12852
|
className: classes.counterItem
|
|
12741
12853
|
}, value != null && value !== '' && /*#__PURE__*/React__default["default"].createElement(AnimatedCounter, {
|
|
12854
|
+
key: `${item._id || index}-${/<[^>]+>/.test(value || '') ? 'html' : 'plain'}`,
|
|
12742
12855
|
value: value,
|
|
12743
12856
|
className: classes.value,
|
|
12744
12857
|
refSetter: getValueRefSetter(item)
|
|
@@ -12866,8 +12979,8 @@ const getFloatingButtonBase = theme => ({
|
|
|
12866
12979
|
zIndex: 9999,
|
|
12867
12980
|
display: 'inline-flex',
|
|
12868
12981
|
alignItems: 'center',
|
|
12869
|
-
gap: '
|
|
12870
|
-
padding: '
|
|
12982
|
+
gap: '8px',
|
|
12983
|
+
padding: '14px',
|
|
12871
12984
|
backgroundColor: theme?.colors?.ctaColor || '#FFFFFF',
|
|
12872
12985
|
border: `1px solid ${theme?.palette?.border?.secondary ?? '#E5E7EB'}`,
|
|
12873
12986
|
borderRadius: '9999px',
|
|
@@ -12877,7 +12990,6 @@ const getFloatingButtonBase = theme => ({
|
|
|
12877
12990
|
fontFamily: theme?.typography?.fontFamily ?? 'inherit',
|
|
12878
12991
|
fontWeight: theme?.typography?.fontWeight?.bold ?? 700,
|
|
12879
12992
|
fontSize: '14px',
|
|
12880
|
-
textTransform: 'uppercase',
|
|
12881
12993
|
letterSpacing: '0.02em',
|
|
12882
12994
|
color: theme?.colors?.CtaTextColor || 'white',
|
|
12883
12995
|
transition: 'box-shadow 0.2s ease, transform 0.2s ease'
|
|
@@ -12889,7 +13001,7 @@ const getFloatingButtonResponsive = theme => {
|
|
|
12889
13001
|
...base,
|
|
12890
13002
|
bottom: 142,
|
|
12891
13003
|
left: 16,
|
|
12892
|
-
padding: '10px
|
|
13004
|
+
padding: '10px',
|
|
12893
13005
|
gap: '8px',
|
|
12894
13006
|
fontSize: '12px',
|
|
12895
13007
|
background: theme?.colors?.ctaColor || '#FFFFFF'
|
|
@@ -12922,6 +13034,17 @@ const whatsAppIconSvgSize = {
|
|
|
12922
13034
|
};
|
|
12923
13035
|
const getWhatsAppIconSize = breakpoint => whatsAppIconSvgSize[breakpoint] ?? whatsAppIconSvgSize.mobile;
|
|
12924
13036
|
|
|
13037
|
+
/**
|
|
13038
|
+
* Height of the sticky bar (full-width CTA) by breakpoint for page padding.
|
|
13039
|
+
* Used to prevent content overlap when the sticky bar is fixed at the bottom.
|
|
13040
|
+
* Values match the sticky bar's padding + content height per breakpoint.
|
|
13041
|
+
*/
|
|
13042
|
+
const STICKY_BAR_HEIGHT_BY_BREAKPOINT = {
|
|
13043
|
+
mobile: 0,
|
|
13044
|
+
tablet: 78,
|
|
13045
|
+
desktop: 82
|
|
13046
|
+
};
|
|
13047
|
+
|
|
12925
13048
|
const DEFAULT_WHATSAPP_ICON_COLOR = '#25D366';
|
|
12926
13049
|
|
|
12927
13050
|
/** Build WhatsApp wa.me URL from phone number (digits only, optional + prefix). */
|
|
@@ -13013,6 +13136,7 @@ function StickyCta({
|
|
|
13013
13136
|
const contentList = node?.contentList?.components || [];
|
|
13014
13137
|
const showEditHint = sectionData?.isDefaultEditor ?? false;
|
|
13015
13138
|
sectionData?.bgSection?.components?.[0]?.sectionBgData?.metadata?.value;
|
|
13139
|
+
const redirectUrl = node?.subheading?.metadata?.value || '';
|
|
13016
13140
|
const propsFromSection = node ? {
|
|
13017
13141
|
type: node?.title?.metadata?.value ?? type,
|
|
13018
13142
|
stickyMessage: contentList[1]?.contentPara?.metadata?.value ?? stickyMessage,
|
|
@@ -13035,7 +13159,7 @@ function StickyCta({
|
|
|
13035
13159
|
whatsAppButtonBackgroundColor,
|
|
13036
13160
|
iconImageUrl
|
|
13037
13161
|
};
|
|
13038
|
-
|
|
13162
|
+
buildWhatsAppUrl(p?.whatsAppPhoneNumber) ?? p?.whatsAppHref ?? '#';
|
|
13039
13163
|
const theme = useTheme() || {};
|
|
13040
13164
|
const [breakpoint, setBreakpoint] = React.useState(() => typeof window !== 'undefined' ? getBreakpoint(window.innerWidth, isMobile) : 'mobile');
|
|
13041
13165
|
React.useEffect(() => {
|
|
@@ -13074,7 +13198,7 @@ function StickyCta({
|
|
|
13074
13198
|
const iconSize = getWhatsAppIconSize(breakpoint);
|
|
13075
13199
|
const iconColor = p.whatsAppIconColor ?? DEFAULT_WHATSAPP_ICON_COLOR;
|
|
13076
13200
|
return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, isMobile ? null : editHintBlock, /*#__PURE__*/React__default["default"].createElement("a", {
|
|
13077
|
-
href:
|
|
13201
|
+
href: redirectUrl,
|
|
13078
13202
|
target: "_blank",
|
|
13079
13203
|
rel: "noopener noreferrer",
|
|
13080
13204
|
"access-cta": "DIY",
|
|
@@ -13092,23 +13216,53 @@ function StickyCta({
|
|
|
13092
13216
|
size: iconSize,
|
|
13093
13217
|
color: iconColor,
|
|
13094
13218
|
iconImageUrl: p.iconImageUrl
|
|
13095
|
-
}), /*#__PURE__*/React__default["default"].createElement("span",
|
|
13219
|
+
}), p.whatsAppLabel ? /*#__PURE__*/React__default["default"].createElement("span", {
|
|
13220
|
+
dangerouslySetInnerHTML: {
|
|
13221
|
+
__html: p.whatsAppLabel
|
|
13222
|
+
}
|
|
13223
|
+
}) : null));
|
|
13096
13224
|
}
|
|
13097
13225
|
|
|
13098
13226
|
// type === 'sticky' – use primary Button (same as other sections) linking to WhatsApp
|
|
13099
13227
|
const barStyle = getStickyBarStyle(breakpoint, theme);
|
|
13100
13228
|
const textStyle = getStickyTextStyle(breakpoint, theme);
|
|
13101
|
-
|
|
13229
|
+
React.useEffect(() => {
|
|
13230
|
+
document.body.classList.add('has-sticky-cta');
|
|
13231
|
+
return () => document.body.classList.remove('has-sticky-cta');
|
|
13232
|
+
}, []);
|
|
13233
|
+
const stickyBarPaddingStyles = `
|
|
13234
|
+
body.has-sticky-cta {
|
|
13235
|
+
padding-bottom: ${STICKY_BAR_HEIGHT_BY_BREAKPOINT.mobile}px;
|
|
13236
|
+
}
|
|
13237
|
+
@media (min-width: ${BREAKPOINTS.tablet}px) {
|
|
13238
|
+
body.has-sticky-cta {
|
|
13239
|
+
padding-bottom: ${STICKY_BAR_HEIGHT_BY_BREAKPOINT.tablet}px;
|
|
13240
|
+
}
|
|
13241
|
+
}
|
|
13242
|
+
@media (min-width: ${BREAKPOINTS.desktop}px) {
|
|
13243
|
+
body.has-sticky-cta {
|
|
13244
|
+
padding-bottom: ${STICKY_BAR_HEIGHT_BY_BREAKPOINT.desktop}px;
|
|
13245
|
+
}
|
|
13246
|
+
}
|
|
13247
|
+
`;
|
|
13248
|
+
return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement("style", {
|
|
13249
|
+
dangerouslySetInnerHTML: {
|
|
13250
|
+
__html: stickyBarPaddingStyles
|
|
13251
|
+
}
|
|
13252
|
+
}), isMobile ? null : editHintBlock, /*#__PURE__*/React__default["default"].createElement("div", {
|
|
13102
13253
|
style: barStyle,
|
|
13103
13254
|
role: "banner",
|
|
13104
13255
|
"access-cta-sticky": "DIY"
|
|
13105
13256
|
}, /*#__PURE__*/React__default["default"].createElement("p", {
|
|
13106
|
-
style: textStyle
|
|
13107
|
-
|
|
13257
|
+
style: textStyle,
|
|
13258
|
+
dangerouslySetInnerHTML: {
|
|
13259
|
+
__html: p.stickyMessage || ''
|
|
13260
|
+
}
|
|
13261
|
+
}), /*#__PURE__*/React__default["default"].createElement(Button, {
|
|
13108
13262
|
data: {
|
|
13109
13263
|
isLink: 1,
|
|
13110
13264
|
isExternal: 1,
|
|
13111
|
-
link:
|
|
13265
|
+
link: redirectUrl,
|
|
13112
13266
|
value: p.stickyButtonText
|
|
13113
13267
|
},
|
|
13114
13268
|
type: "primary",
|
|
@@ -13672,7 +13826,13 @@ function VideoWorkshopPromotion({
|
|
|
13672
13826
|
ref: videoData?.videoTextContent?.metadata?.refSetter,
|
|
13673
13827
|
data: videoData?.videoTextContent?.metadata,
|
|
13674
13828
|
type: 'primary',
|
|
13675
|
-
size: isMobile ? 'small' : 'medium'
|
|
13829
|
+
size: isMobile ? 'small' : 'medium',
|
|
13830
|
+
onClick: () => {
|
|
13831
|
+
const url = videoData?.videoTextHeading?.metadata?.value;
|
|
13832
|
+
if (url) {
|
|
13833
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
13834
|
+
}
|
|
13835
|
+
}
|
|
13676
13836
|
}))), /*#__PURE__*/React__default["default"].createElement("div", {
|
|
13677
13837
|
className: classes.videoBlock
|
|
13678
13838
|
}, /*#__PURE__*/React__default["default"].createElement("div", {
|