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.es.js
CHANGED
|
@@ -349,7 +349,7 @@ const useSectionStyles$b = createUseStyles(theme => ({
|
|
|
349
349
|
bottom: 0,
|
|
350
350
|
left: 0,
|
|
351
351
|
width: '100%',
|
|
352
|
-
zIndex: '
|
|
352
|
+
zIndex: '10000'
|
|
353
353
|
}
|
|
354
354
|
},
|
|
355
355
|
mobileAppNameClass: {
|
|
@@ -609,7 +609,11 @@ const Button = /*#__PURE__*/forwardRef(function Button({
|
|
|
609
609
|
rel: data?.isExternal ? 'nofollow noopener' : null,
|
|
610
610
|
className: (active ? classes.active : '') + ' ' + classes[type] + ' ' + classes[size] + ' ' + classes.anchorClass,
|
|
611
611
|
style: styling
|
|
612
|
-
}, elementProps),
|
|
612
|
+
}, elementProps), /*#__PURE__*/React.createElement("span", {
|
|
613
|
+
dangerouslySetInnerHTML: {
|
|
614
|
+
__html: data?.value || ''
|
|
615
|
+
}
|
|
616
|
+
})) : /*#__PURE__*/React.createElement("button", _extends({
|
|
613
617
|
ref: ref,
|
|
614
618
|
className: (active ? classes.active : '') + ' ' + classes[type] + ' ' + classes[size],
|
|
615
619
|
onClick: isEdit ? null : onClick,
|
|
@@ -10910,7 +10914,7 @@ const SingleVideoSlide$1 = props => {
|
|
|
10910
10914
|
currencySymbol
|
|
10911
10915
|
} = props;
|
|
10912
10916
|
const showCourseInstallmentData = data?.courseOverviewData;
|
|
10913
|
-
const InstalmentData = isEdit ? data?.courseOverviewData?.installments?.formData?.installments[0].installmentAmount : data?.courseOverviewData?.installments?.formData?.installmentInfo?.installments[0]
|
|
10917
|
+
const InstalmentData = isEdit ? data?.courseOverviewData?.installments?.formData?.installments[0].installmentAmount : data?.courseOverviewData?.installments?.formData?.installmentInfo?.installments?.[0]?.installmentPrice;
|
|
10914
10918
|
const checkIfOfferIsValid = () => moment().diff(moment(props?.data?.endDate || 0)) < 0;
|
|
10915
10919
|
const checkForShowDiscount = () => {
|
|
10916
10920
|
if (props.data.endDate === null || checkIfOfferIsValid()) {
|
|
@@ -12609,29 +12613,125 @@ const useCounterSectionStyles = createUseStyles(theme => {
|
|
|
12609
12613
|
const DURATION_MS = 2000;
|
|
12610
12614
|
const EASING = t => 1 - Math.pow(1 - t, 3); // easeOutCubic
|
|
12611
12615
|
|
|
12612
|
-
function
|
|
12616
|
+
function getPlainTextFromHtml(html) {
|
|
12617
|
+
if (typeof document === 'undefined') {
|
|
12618
|
+
return String(html).replace(/<[^>]*>/g, '');
|
|
12619
|
+
}
|
|
12620
|
+
const div = document.createElement('div');
|
|
12621
|
+
div.innerHTML = html;
|
|
12622
|
+
return div.textContent || div.innerText || '';
|
|
12623
|
+
}
|
|
12624
|
+
function parseStyleString(styleStr) {
|
|
12625
|
+
if (!styleStr || typeof styleStr !== 'string') return {};
|
|
12626
|
+
const obj = {};
|
|
12627
|
+
styleStr.split(';').forEach(part => {
|
|
12628
|
+
const colonIdx = part.indexOf(':');
|
|
12629
|
+
if (colonIdx > 0) {
|
|
12630
|
+
const key = part.slice(0, colonIdx).trim();
|
|
12631
|
+
const val = part.slice(colonIdx + 1).trim();
|
|
12632
|
+
if (key && val) obj[key] = val;
|
|
12633
|
+
}
|
|
12634
|
+
});
|
|
12635
|
+
return obj;
|
|
12636
|
+
}
|
|
12637
|
+
const SEMANTIC_TAG_STYLES = {
|
|
12638
|
+
strong: {
|
|
12639
|
+
fontWeight: 'bold'
|
|
12640
|
+
},
|
|
12641
|
+
b: {
|
|
12642
|
+
fontWeight: 'bold'
|
|
12643
|
+
},
|
|
12644
|
+
em: {
|
|
12645
|
+
fontStyle: 'italic'
|
|
12646
|
+
},
|
|
12647
|
+
i: {
|
|
12648
|
+
fontStyle: 'italic'
|
|
12649
|
+
},
|
|
12650
|
+
u: {
|
|
12651
|
+
textDecoration: 'underline'
|
|
12652
|
+
}
|
|
12653
|
+
};
|
|
12654
|
+
|
|
12655
|
+
/**
|
|
12656
|
+
* Parses value (plain text or HTML) to extract number for animation and wrapper attributes.
|
|
12657
|
+
* Returns { end, suffix, wrapperProps } or null if not parseable.
|
|
12658
|
+
* wrapperProps: { className, style } for the rich text wrapper, or null if plain text.
|
|
12659
|
+
*/
|
|
12660
|
+
function parseCounterValueFromHtml(value) {
|
|
12613
12661
|
if (value == null || value === '') return null;
|
|
12614
|
-
const
|
|
12662
|
+
const str = String(value).trim();
|
|
12663
|
+
const plainText = /<[^>]+>/.test(str) ? getPlainTextFromHtml(str) : str;
|
|
12664
|
+
const match = plainText.match(/^([\d.]+)(.*)$/);
|
|
12615
12665
|
if (!match) return null;
|
|
12616
12666
|
const num = parseFloat(match[1]);
|
|
12617
12667
|
if (Number.isNaN(num)) return null;
|
|
12618
|
-
|
|
12619
|
-
|
|
12620
|
-
|
|
12621
|
-
|
|
12668
|
+
const suffix = match[2] || '';
|
|
12669
|
+
if (!/<[^>]+>/.test(str)) {
|
|
12670
|
+
return {
|
|
12671
|
+
end: num,
|
|
12672
|
+
suffix,
|
|
12673
|
+
wrapperProps: null
|
|
12674
|
+
};
|
|
12675
|
+
}
|
|
12676
|
+
if (typeof document === 'undefined') {
|
|
12677
|
+
return {
|
|
12678
|
+
end: num,
|
|
12679
|
+
suffix,
|
|
12680
|
+
wrapperProps: null
|
|
12681
|
+
};
|
|
12682
|
+
}
|
|
12683
|
+
try {
|
|
12684
|
+
const parser = new DOMParser();
|
|
12685
|
+
const doc = parser.parseFromString(str, 'text/html');
|
|
12686
|
+
const root = doc.body?.firstElementChild;
|
|
12687
|
+
if (!root) return {
|
|
12688
|
+
end: num,
|
|
12689
|
+
suffix,
|
|
12690
|
+
wrapperProps: null
|
|
12691
|
+
};
|
|
12692
|
+
const classes = [];
|
|
12693
|
+
const styleObj = {};
|
|
12694
|
+
const collectFromElement = el => {
|
|
12695
|
+
const c = el.getAttribute('class');
|
|
12696
|
+
if (c) classes.push(...c.split(/\s+/).filter(Boolean));
|
|
12697
|
+
const s = el.getAttribute('style');
|
|
12698
|
+
if (s) Object.assign(styleObj, parseStyleString(s));
|
|
12699
|
+
const tagStyles = SEMANTIC_TAG_STYLES[el.tagName?.toLowerCase()];
|
|
12700
|
+
if (tagStyles) Object.assign(styleObj, tagStyles);
|
|
12701
|
+
for (const child of el.children) {
|
|
12702
|
+
collectFromElement(child);
|
|
12703
|
+
}
|
|
12704
|
+
};
|
|
12705
|
+
collectFromElement(root);
|
|
12706
|
+
const className = [...new Set(classes)].join(' ');
|
|
12707
|
+
return {
|
|
12708
|
+
end: num,
|
|
12709
|
+
suffix,
|
|
12710
|
+
wrapperProps: className || Object.keys(styleObj).length ? {
|
|
12711
|
+
className,
|
|
12712
|
+
style: styleObj
|
|
12713
|
+
} : null
|
|
12714
|
+
};
|
|
12715
|
+
} catch {
|
|
12716
|
+
return {
|
|
12717
|
+
end: num,
|
|
12718
|
+
suffix,
|
|
12719
|
+
wrapperProps: null
|
|
12720
|
+
};
|
|
12721
|
+
}
|
|
12622
12722
|
}
|
|
12623
12723
|
function AnimatedCounter({
|
|
12624
12724
|
value,
|
|
12625
12725
|
className,
|
|
12626
12726
|
refSetter
|
|
12627
12727
|
}) {
|
|
12628
|
-
const
|
|
12728
|
+
const animSpanRef = useRef(null);
|
|
12629
12729
|
const rafIdRef = useRef(null);
|
|
12630
12730
|
const hasAnimatedRef = useRef(false);
|
|
12631
12731
|
useLayoutEffect(() => {
|
|
12632
12732
|
hasAnimatedRef.current = false; // Reset so we can re-animate when value changes (e.g. live edit)
|
|
12633
|
-
const parsed =
|
|
12634
|
-
if (!parsed || !
|
|
12733
|
+
const parsed = parseCounterValueFromHtml(value);
|
|
12734
|
+
if (!parsed || !animSpanRef.current) return;
|
|
12635
12735
|
const observer = new IntersectionObserver(entries => {
|
|
12636
12736
|
const [entry] = entries;
|
|
12637
12737
|
if (!entry.isIntersecting || hasAnimatedRef.current) return;
|
|
@@ -12646,9 +12746,9 @@ function AnimatedCounter({
|
|
|
12646
12746
|
const progress = Math.min(elapsed / DURATION_MS, 1);
|
|
12647
12747
|
const eased = EASING(progress);
|
|
12648
12748
|
const current = eased * end;
|
|
12649
|
-
if (
|
|
12749
|
+
if (animSpanRef.current) {
|
|
12650
12750
|
const displayValue = Number.isInteger(end) ? Math.round(current) : parseFloat(current.toFixed(2));
|
|
12651
|
-
|
|
12751
|
+
animSpanRef.current.textContent = displayValue + suffix;
|
|
12652
12752
|
}
|
|
12653
12753
|
if (progress < 1) {
|
|
12654
12754
|
rafIdRef.current = requestAnimationFrame(tick);
|
|
@@ -12658,22 +12758,34 @@ function AnimatedCounter({
|
|
|
12658
12758
|
}, {
|
|
12659
12759
|
threshold: 0.2
|
|
12660
12760
|
});
|
|
12661
|
-
observer.observe(
|
|
12761
|
+
observer.observe(animSpanRef.current);
|
|
12662
12762
|
return () => {
|
|
12663
12763
|
observer.disconnect();
|
|
12664
12764
|
if (rafIdRef.current) cancelAnimationFrame(rafIdRef.current);
|
|
12665
12765
|
};
|
|
12666
12766
|
}, [value]);
|
|
12667
|
-
const parsed =
|
|
12767
|
+
const parsed = parseCounterValueFromHtml(value);
|
|
12668
12768
|
const setRef = el => {
|
|
12669
|
-
elRef.current = el;
|
|
12670
12769
|
refSetter?.(el);
|
|
12671
12770
|
};
|
|
12672
12771
|
if (parsed) {
|
|
12772
|
+
const {
|
|
12773
|
+
end,
|
|
12774
|
+
suffix,
|
|
12775
|
+
wrapperProps
|
|
12776
|
+
} = parsed;
|
|
12777
|
+
const initialDisplay = Number.isInteger(end) ? '0' : '0.00';
|
|
12778
|
+
const animatedContent = /*#__PURE__*/React.createElement("span", {
|
|
12779
|
+
ref: animSpanRef
|
|
12780
|
+
}, initialDisplay, suffix);
|
|
12781
|
+
const content = wrapperProps ? /*#__PURE__*/React.createElement("span", {
|
|
12782
|
+
className: wrapperProps.className || undefined,
|
|
12783
|
+
style: Object.keys(wrapperProps.style || {}).length ? wrapperProps.style : undefined
|
|
12784
|
+
}, animatedContent) : animatedContent;
|
|
12673
12785
|
return /*#__PURE__*/React.createElement("h2", {
|
|
12674
12786
|
ref: setRef,
|
|
12675
12787
|
className: className
|
|
12676
|
-
},
|
|
12788
|
+
}, content);
|
|
12677
12789
|
}
|
|
12678
12790
|
return /*#__PURE__*/React.createElement("h2", {
|
|
12679
12791
|
ref: refSetter,
|
|
@@ -12725,6 +12837,7 @@ function CounterSection({
|
|
|
12725
12837
|
key: item._id || index,
|
|
12726
12838
|
className: classes.counterItem
|
|
12727
12839
|
}, value != null && value !== '' && /*#__PURE__*/React.createElement(AnimatedCounter, {
|
|
12840
|
+
key: `${item._id || index}-${/<[^>]+>/.test(value || '') ? 'html' : 'plain'}`,
|
|
12728
12841
|
value: value,
|
|
12729
12842
|
className: classes.value,
|
|
12730
12843
|
refSetter: getValueRefSetter(item)
|
|
@@ -12852,8 +12965,8 @@ const getFloatingButtonBase = theme => ({
|
|
|
12852
12965
|
zIndex: 9999,
|
|
12853
12966
|
display: 'inline-flex',
|
|
12854
12967
|
alignItems: 'center',
|
|
12855
|
-
gap: '
|
|
12856
|
-
padding: '
|
|
12968
|
+
gap: '8px',
|
|
12969
|
+
padding: '14px',
|
|
12857
12970
|
backgroundColor: theme?.colors?.ctaColor || '#FFFFFF',
|
|
12858
12971
|
border: `1px solid ${theme?.palette?.border?.secondary ?? '#E5E7EB'}`,
|
|
12859
12972
|
borderRadius: '9999px',
|
|
@@ -12863,7 +12976,6 @@ const getFloatingButtonBase = theme => ({
|
|
|
12863
12976
|
fontFamily: theme?.typography?.fontFamily ?? 'inherit',
|
|
12864
12977
|
fontWeight: theme?.typography?.fontWeight?.bold ?? 700,
|
|
12865
12978
|
fontSize: '14px',
|
|
12866
|
-
textTransform: 'uppercase',
|
|
12867
12979
|
letterSpacing: '0.02em',
|
|
12868
12980
|
color: theme?.colors?.CtaTextColor || 'white',
|
|
12869
12981
|
transition: 'box-shadow 0.2s ease, transform 0.2s ease'
|
|
@@ -12875,7 +12987,7 @@ const getFloatingButtonResponsive = theme => {
|
|
|
12875
12987
|
...base,
|
|
12876
12988
|
bottom: 142,
|
|
12877
12989
|
left: 16,
|
|
12878
|
-
padding: '10px
|
|
12990
|
+
padding: '10px',
|
|
12879
12991
|
gap: '8px',
|
|
12880
12992
|
fontSize: '12px',
|
|
12881
12993
|
background: theme?.colors?.ctaColor || '#FFFFFF'
|
|
@@ -12908,6 +13020,17 @@ const whatsAppIconSvgSize = {
|
|
|
12908
13020
|
};
|
|
12909
13021
|
const getWhatsAppIconSize = breakpoint => whatsAppIconSvgSize[breakpoint] ?? whatsAppIconSvgSize.mobile;
|
|
12910
13022
|
|
|
13023
|
+
/**
|
|
13024
|
+
* Height of the sticky bar (full-width CTA) by breakpoint for page padding.
|
|
13025
|
+
* Used to prevent content overlap when the sticky bar is fixed at the bottom.
|
|
13026
|
+
* Values match the sticky bar's padding + content height per breakpoint.
|
|
13027
|
+
*/
|
|
13028
|
+
const STICKY_BAR_HEIGHT_BY_BREAKPOINT = {
|
|
13029
|
+
mobile: 0,
|
|
13030
|
+
tablet: 78,
|
|
13031
|
+
desktop: 82
|
|
13032
|
+
};
|
|
13033
|
+
|
|
12911
13034
|
const DEFAULT_WHATSAPP_ICON_COLOR = '#25D366';
|
|
12912
13035
|
|
|
12913
13036
|
/** Build WhatsApp wa.me URL from phone number (digits only, optional + prefix). */
|
|
@@ -12999,6 +13122,7 @@ function StickyCta({
|
|
|
12999
13122
|
const contentList = node?.contentList?.components || [];
|
|
13000
13123
|
const showEditHint = sectionData?.isDefaultEditor ?? false;
|
|
13001
13124
|
sectionData?.bgSection?.components?.[0]?.sectionBgData?.metadata?.value;
|
|
13125
|
+
const redirectUrl = node?.subheading?.metadata?.value || '';
|
|
13002
13126
|
const propsFromSection = node ? {
|
|
13003
13127
|
type: node?.title?.metadata?.value ?? type,
|
|
13004
13128
|
stickyMessage: contentList[1]?.contentPara?.metadata?.value ?? stickyMessage,
|
|
@@ -13021,7 +13145,7 @@ function StickyCta({
|
|
|
13021
13145
|
whatsAppButtonBackgroundColor,
|
|
13022
13146
|
iconImageUrl
|
|
13023
13147
|
};
|
|
13024
|
-
|
|
13148
|
+
buildWhatsAppUrl(p?.whatsAppPhoneNumber) ?? p?.whatsAppHref ?? '#';
|
|
13025
13149
|
const theme = useTheme() || {};
|
|
13026
13150
|
const [breakpoint, setBreakpoint] = useState(() => typeof window !== 'undefined' ? getBreakpoint(window.innerWidth, isMobile) : 'mobile');
|
|
13027
13151
|
useEffect(() => {
|
|
@@ -13060,7 +13184,7 @@ function StickyCta({
|
|
|
13060
13184
|
const iconSize = getWhatsAppIconSize(breakpoint);
|
|
13061
13185
|
const iconColor = p.whatsAppIconColor ?? DEFAULT_WHATSAPP_ICON_COLOR;
|
|
13062
13186
|
return /*#__PURE__*/React.createElement(React.Fragment, null, isMobile ? null : editHintBlock, /*#__PURE__*/React.createElement("a", {
|
|
13063
|
-
href:
|
|
13187
|
+
href: redirectUrl,
|
|
13064
13188
|
target: "_blank",
|
|
13065
13189
|
rel: "noopener noreferrer",
|
|
13066
13190
|
"access-cta": "DIY",
|
|
@@ -13078,23 +13202,53 @@ function StickyCta({
|
|
|
13078
13202
|
size: iconSize,
|
|
13079
13203
|
color: iconColor,
|
|
13080
13204
|
iconImageUrl: p.iconImageUrl
|
|
13081
|
-
}), /*#__PURE__*/React.createElement("span",
|
|
13205
|
+
}), p.whatsAppLabel ? /*#__PURE__*/React.createElement("span", {
|
|
13206
|
+
dangerouslySetInnerHTML: {
|
|
13207
|
+
__html: p.whatsAppLabel
|
|
13208
|
+
}
|
|
13209
|
+
}) : null));
|
|
13082
13210
|
}
|
|
13083
13211
|
|
|
13084
13212
|
// type === 'sticky' – use primary Button (same as other sections) linking to WhatsApp
|
|
13085
13213
|
const barStyle = getStickyBarStyle(breakpoint, theme);
|
|
13086
13214
|
const textStyle = getStickyTextStyle(breakpoint, theme);
|
|
13087
|
-
|
|
13215
|
+
useEffect(() => {
|
|
13216
|
+
document.body.classList.add('has-sticky-cta');
|
|
13217
|
+
return () => document.body.classList.remove('has-sticky-cta');
|
|
13218
|
+
}, []);
|
|
13219
|
+
const stickyBarPaddingStyles = `
|
|
13220
|
+
body.has-sticky-cta {
|
|
13221
|
+
padding-bottom: ${STICKY_BAR_HEIGHT_BY_BREAKPOINT.mobile}px;
|
|
13222
|
+
}
|
|
13223
|
+
@media (min-width: ${BREAKPOINTS.tablet}px) {
|
|
13224
|
+
body.has-sticky-cta {
|
|
13225
|
+
padding-bottom: ${STICKY_BAR_HEIGHT_BY_BREAKPOINT.tablet}px;
|
|
13226
|
+
}
|
|
13227
|
+
}
|
|
13228
|
+
@media (min-width: ${BREAKPOINTS.desktop}px) {
|
|
13229
|
+
body.has-sticky-cta {
|
|
13230
|
+
padding-bottom: ${STICKY_BAR_HEIGHT_BY_BREAKPOINT.desktop}px;
|
|
13231
|
+
}
|
|
13232
|
+
}
|
|
13233
|
+
`;
|
|
13234
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("style", {
|
|
13235
|
+
dangerouslySetInnerHTML: {
|
|
13236
|
+
__html: stickyBarPaddingStyles
|
|
13237
|
+
}
|
|
13238
|
+
}), isMobile ? null : editHintBlock, /*#__PURE__*/React.createElement("div", {
|
|
13088
13239
|
style: barStyle,
|
|
13089
13240
|
role: "banner",
|
|
13090
13241
|
"access-cta-sticky": "DIY"
|
|
13091
13242
|
}, /*#__PURE__*/React.createElement("p", {
|
|
13092
|
-
style: textStyle
|
|
13093
|
-
|
|
13243
|
+
style: textStyle,
|
|
13244
|
+
dangerouslySetInnerHTML: {
|
|
13245
|
+
__html: p.stickyMessage || ''
|
|
13246
|
+
}
|
|
13247
|
+
}), /*#__PURE__*/React.createElement(Button, {
|
|
13094
13248
|
data: {
|
|
13095
13249
|
isLink: 1,
|
|
13096
13250
|
isExternal: 1,
|
|
13097
|
-
link:
|
|
13251
|
+
link: redirectUrl,
|
|
13098
13252
|
value: p.stickyButtonText
|
|
13099
13253
|
},
|
|
13100
13254
|
type: "primary",
|
|
@@ -13658,7 +13812,13 @@ function VideoWorkshopPromotion({
|
|
|
13658
13812
|
ref: videoData?.videoTextContent?.metadata?.refSetter,
|
|
13659
13813
|
data: videoData?.videoTextContent?.metadata,
|
|
13660
13814
|
type: 'primary',
|
|
13661
|
-
size: isMobile ? 'small' : 'medium'
|
|
13815
|
+
size: isMobile ? 'small' : 'medium',
|
|
13816
|
+
onClick: () => {
|
|
13817
|
+
const url = videoData?.videoTextHeading?.metadata?.value;
|
|
13818
|
+
if (url) {
|
|
13819
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
13820
|
+
}
|
|
13821
|
+
}
|
|
13662
13822
|
}))), /*#__PURE__*/React.createElement("div", {
|
|
13663
13823
|
className: classes.videoBlock
|
|
13664
13824
|
}, /*#__PURE__*/React.createElement("div", {
|