Skip to content

Commit 61eb945

Browse files
feat: move scroll to top to free version (#4445)
This PR moves Move to Scroll from Pro version to Free version. Few notes: It introduces new strings. It adds a migration for from the option to theme mod for enable/disable.
1 parent 415b3d9 commit 61eb945

File tree

17 files changed

+1569
-93
lines changed

17 files changed

+1569
-93
lines changed

assets/apps/customizer-controls/src/builder/components/Builder.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ const Builder: React.FC<Props> = ({ value, hidden, portalMount }) => {
138138
setMobileOverlayDismissed(true);
139139
}}
140140
>
141-
{__('Enable', 'neve-pro-addon')}
141+
{__('Enable', 'neve')}
142142
</Button>
143143
</div>
144144
)}

assets/apps/customizer-controls/src/controls.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,50 @@ const checkHasElementorTemplates = () => {
297297
}
298298
};
299299

300+
/**
301+
* Find the Scroll to top button within the customizer preview.
302+
*/
303+
function findScrollToTopBtn() {
304+
const iframeElement = document.querySelector('#customize-preview iframe');
305+
306+
if (!iframeElement) {
307+
return;
308+
}
309+
310+
const scrollToTopBtn =
311+
iframeElement.contentWindow.document.querySelector('#scroll-to-top');
312+
313+
return scrollToTopBtn;
314+
}
315+
316+
/**
317+
* Show the Scroll to Top button as soon as the user enters the section in Customizer.
318+
*/
319+
function previewScrollToTopChanges() {
320+
wp.customize.section('neve_scroll_to_top', (section) => {
321+
section.expanded.bind((isExpanded) => {
322+
const scrollToTopBtn = findScrollToTopBtn();
323+
324+
if (!scrollToTopBtn) {
325+
return;
326+
}
327+
328+
// If Scroll to top customizer section is expanded
329+
if (isExpanded) {
330+
wp.customize.previewer.bind('ready', () => {
331+
wp.customize.previewer.send('nv-opened-stt', true);
332+
});
333+
scrollToTopBtn.style.visibility = 'visible';
334+
scrollToTopBtn.style.opacity = '1';
335+
} else {
336+
// Hide the button when we leave the section
337+
scrollToTopBtn.style.visibility = 'hidden';
338+
scrollToTopBtn.style.opacity = '0';
339+
}
340+
});
341+
});
342+
}
343+
300344
window.wp.customize.bind('ready', () => {
301345
initStarterContentNotice();
302346
initDocSection();
@@ -311,6 +355,7 @@ window.wp.customize.bind('ready', () => {
311355
initBlogPageFocus();
312356
initSearchCustomizer();
313357
initLocalGoogleFonts();
358+
previewScrollToTopChanges();
314359
});
315360

316361
window.HFG = {

assets/js/src/customizer-preview/app.js

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/js/src/scroll-to-top.js

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*global neveScrollOffset*/
2+
3+
function scrollTopSafe(to) {
4+
let i = window.scrollY;
5+
to = parseInt(to);
6+
const scrollInterval = setInterval(function () {
7+
if (i < to + 20) i -= 1;
8+
else if (i < to + 40) i -= 6;
9+
else if (i < to + 80) i -= 16;
10+
else if (i < to + 160) i -= 36;
11+
else if (i < to + 200) i -= 48;
12+
else if (i < to + 300) i -= 80;
13+
else i -= 120;
14+
window.scroll(0, i);
15+
if (i <= to) clearInterval(scrollInterval);
16+
}, 15);
17+
}
18+
19+
function runScroll() {
20+
const smoothScrollFeature =
21+
'scrollBehavior' in document.documentElement.style;
22+
if (!smoothScrollFeature) {
23+
scrollTopSafe(0);
24+
} else {
25+
window.scrollTo({
26+
top: 0,
27+
behavior: 'smooth',
28+
});
29+
}
30+
const content = document.getElementById('content');
31+
const scrollButton = document.getElementById('scroll-to-top');
32+
if (content) {
33+
scrollButton.blur();
34+
content.focus();
35+
}
36+
}
37+
function scrollToTop() {
38+
const element = document.getElementById('scroll-to-top');
39+
if (!element) {
40+
return false;
41+
}
42+
43+
element.addEventListener('click', function () {
44+
runScroll();
45+
});
46+
47+
element.addEventListener('keydown', function (event) {
48+
if (event.key === 'Enter') {
49+
runScroll();
50+
}
51+
});
52+
53+
window.addEventListener('scroll', function () {
54+
const yScrollPos = window.scrollY;
55+
const offset = neveScrollOffset.offset;
56+
57+
if (yScrollPos > offset) {
58+
element.style.visibility = 'visible';
59+
element.style.opacity = '1';
60+
}
61+
if (yScrollPos <= offset) {
62+
element.style.opacity = '0';
63+
element.style.visibility = 'hidden';
64+
}
65+
66+
// Change scroll to top position if there is a sticky add to cart in place.
67+
const stickyAddToCart = document.querySelector(
68+
'.sticky-add-to-cart-bottom'
69+
);
70+
if (stickyAddToCart) {
71+
element.style.bottom = '30px';
72+
73+
// Try to get Neve's sticky footer. If it doesn't exist try to get Elementor's.
74+
let stickyFooter = document.querySelector('.hfg_footer');
75+
if (
76+
!stickyFooter ||
77+
!stickyFooter.classList.contains('has-sticky-rows')
78+
) {
79+
stickyFooter = document.querySelector(
80+
'.elementor-location-footer .elementor-sticky'
81+
);
82+
}
83+
const footerHeight = stickyFooter ? stickyFooter.offsetHeight : 0;
84+
85+
if (
86+
stickyAddToCart.classList.contains('sticky-add-to-cart--active')
87+
) {
88+
element.style.bottom =
89+
stickyAddToCart.offsetHeight + footerHeight + 10 + 'px';
90+
}
91+
}
92+
});
93+
}
94+
95+
window.addEventListener('load', function () {
96+
scrollToTop();
97+
});
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"general": {
3+
"neve_scroll_to_top_side": "left",
4+
"neve_scroll_to_top_label": "Go up",
5+
"neve_scroll_to_top_offset": 100,
6+
"neve_scroll_to_top_padding": {
7+
"desktop": { "top": 10, "right": 12, "bottom": 10, "left": 12 },
8+
"tablet": { "top": 6, "right": 8, "bottom": 6, "left": 8 },
9+
"mobile": { "top": 10, "right": 12, "bottom": 10, "left": 12 },
10+
"desktop-unit": "px",
11+
"tablet-unit": "px",
12+
"mobile-unit": "px"
13+
},
14+
"neve_scroll_to_top_border_radius": 100,
15+
"neve_scroll_to_top_icon_color": "#ff0000",
16+
"neve_scroll_to_top_icon_hover_color": "#ff0000",
17+
"neve_scroll_to_top_background_color": "#ffffff",
18+
"neve_scroll_to_top_background_hover_color": "#ffffff",
19+
"neve_scroll_to_top_type": "icon",
20+
"neve_scroll_to_top_image": 0,
21+
"neve_scroll_to_top_on_mobile": false
22+
},
23+
"icon-check": {
24+
"neve_scroll_to_top_side": "left",
25+
"neve_scroll_to_top_icon_size": "{ \"mobile\": \"100\", \"tablet\": \"50\", \"desktop\": \"100\" }",
26+
"neve_scroll_to_top_on_mobile": false
27+
}
28+
}

e2e-tests/specs/customizer/hfg/hfg-logo-component.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ test.describe('Logo Component palette', function () {
128128

129129
await expect(await siteLogo.getAttribute('src')).toBe(logos[1]?.url);
130130

131-
await page.locator('.icon > svg > path').click();
131+
await page.getByRole('link', { name: 'Palette Switch' }).click();
132132

133133
await expect(await siteLogo.getAttribute('src')).toBe(logos[0]?.url);
134134
});

e2e-tests/specs/customizer/hfg/hfg-palette-switch-component.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ test.describe('Palette Switch component', function () {
2828
);
2929
}
3030

31-
await page.locator('.icon > svg > path').click();
31+
await page.getByRole('link', { name: 'Palette Switch' }).click();
3232

3333
for (let i = 0; i < count; i++) {
3434
await expect(headerElements.nth(i)).toHaveCSS(

0 commit comments

Comments
 (0)