File "s11-floating-links.js"
Full Path: /home/adniftyx/public_html/wp-content/plugins/formidable/js/packages/floating-links/s11-floating-links.js
File size: 10.86 KB
MIME-type: text/plain
Charset: utf-8
/**
* Class representing a floating action button for showing links.
*
* @class S11FloatingLinks
*/
class S11FloatingLinks {
/**
* Create a new S11FloatingLinks instance.
*
* @class
*/
constructor() {
wp.hooks.addAction( 'set_floating_links_config', 'S11FloatingLinks', ( { links, options } ) => {
this.validateInputs( links, options );
this.links = links;
this.options = options;
this.initComponent();
} );
}
/**
* Validate the input parameters.
*
* @param {Array<Object>} links The links array.
* @param {Object} options The options object.
*
* @throws {Error} If the links array is empty or not provided.
* @throws {Error} If the options.logoIcon is not provided or is an empty string.
*/
validateInputs( links, options ) {
if ( ! Array.isArray( links ) || links.length === 0 ) {
throw new Error( 'The "links" array is required and must not be empty.' );
}
if ( ! options || typeof options.logoIcon !== 'string' || options.logoIcon.trim() === '' ) {
throw new Error( 'The "options.logoIcon" is required and must be a non-empty string.' );
}
}
/**
* Initialize the floating links component by creating the required elements, and applying styles.
*
* @memberof S11FloatingLinks
*/
initComponent() {
if ( document.querySelector( '.frm-review-notice' ) ) {
// Do not load if the review notice is being shown.
return;
}
// Create and append elements
this.createWrapper();
this.createNavMenu();
this.createIconButton();
// Add event listener to close the navigation menu when clicking outside of floating links wrapper
this.addOutsideClickListener();
// Add scroll event listener to hide/show floating links on scroll
this.addScrollEventListener();
// Apply styles
this.setCSSVariables();
// Use a timeout to give time for Pro to add hooks.
setTimeout( () => this.setupInboxSlideIn(), 0 );
}
setupInboxSlideIn() {
if ( 'object' !== typeof frmGlobal.inboxSlideIn || ! window.frmDom || ! window.wp ) {
return;
}
const slideIn = this.getInboxSlideIn();
slideIn.classList.add( 's11-fadein' );
document.body.append( slideIn );
// Make sure the events are set for dismissing the inbox message.
if ( ! document.querySelector( '.frm-inbox-wrapper' ) ) {
frmAdminBuild.inboxInit();
}
}
/**
* @return {HTMLElement} The inbox slide-in element.
*/
getInboxSlideIn() {
const h3 = frmDom.tag(
'h3',
{ id: 'frm_inbox_slidein_title' }
);
h3.innerHTML = frmAdminBuild.purifyHtml( frmGlobal.inboxSlideIn.subject );
const messageSpan = frmDom.span( {
id: 'frm_inbox_slidein_message',
text: frmGlobal.inboxSlideIn.slidein
} );
const dismissIcon = frmDom.a( {
className: 'dismiss frm_inbox_dismiss',
child: frmDom.svg( { href: '#frm_close_icon' } )
} );
dismissIcon.setAttribute( 'aria-label', wp.i18n.__( 'Dismiss this notice', 'formidable' ) );
dismissIcon.setAttribute( 'role', 'button' );
const children = frmAdminBuild.hooks.applyFilters(
'frm_inbox_slidein_children',
[ h3, messageSpan ]
);
const slideIn = frmDom.div( {
id: 'frm_inbox_slide_in',
className: 'frm-card-item frm-compact-card-item frm-dismissible frm-box-shadow-xxl',
children
} );
slideIn.setAttribute( 'data-message', frmGlobal.inboxSlideIn.key );
slideIn.setAttribute( 'role', 'alert' );
slideIn.insertAdjacentHTML( 'beforeend', frmAdminBuild.purifyHtml( frmGlobal.inboxSlideIn.cta ) );
slideIn.querySelector( '.frm-button-secondary' )?.remove();
this.updateSlideInCtaUtm( slideIn );
slideIn.append( frmDom.span( { child: dismissIcon } ) );
slideIn.querySelector( 'a[href].frm-button-primary' )?.setAttribute(
'aria-description',
( frmGlobal.inboxSlideIn.subject + ' ' + frmGlobal.inboxSlideIn.slidein ).replace( '&', '&' )
);
this.slideIn = slideIn;
return slideIn;
}
updateSlideInCtaUtm( slideIn ) {
slideIn.querySelectorAll( 'a[href]' ).forEach(
anchor => {
if ( '#' === anchor.href ) {
return;
}
const urlObj = new URL( anchor.href );
const searchParams = new URLSearchParams( urlObj.search );
searchParams.set( 'utm_medium', 'slidein' );
urlObj.search = searchParams.toString();
anchor.href = urlObj.toString();
}
);
}
/**
* Create the wrapper element and add it to the DOM.
*
* @memberof S11FloatingLinks
*/
createWrapper() {
// Create the wrapper element
this.wrapperElement = document.createElement( 'div' );
this.wrapperElement.classList.add( 's11-floating-links', 's11-fadein' );
// Add the wrapper to the DOM
document.body.append( this.wrapperElement );
}
/**
* Create the navigation menu element with the specified links and append it to the wrapper element.
*
* @memberof S11FloatingLinks
*/
createNavMenu() {
// Create the navigation menu element
this.navMenuElement = document.createElement( 'div' );
this.navMenuElement.classList.add( 's11-floating-links-nav-menu', 's11-fadeout' );
// Create and append link elements
this.links.forEach( link => {
if ( ! this.linkHasRequiredProperties( link ) ) {
return;
}
const linkElement = document.createElement( 'a' );
linkElement.classList.add( 's11-floating-links-nav-item' );
linkElement.href = link.url;
linkElement.title = link.title;
if ( link.openInNewTab === true ) {
linkElement.target = '_blank';
linkElement.rel = 'noopener noreferrer';
}
linkElement.innerHTML = `
<span class="s11-floating-links-nav-text">${ link.title }</span>
<span class="s11-floating-links-nav-icon">${ link.icon }</span>
`;
this.navMenuElement.append( linkElement );
} );
// Append the navigation menu to the wrapper element
this.wrapperElement.append( this.navMenuElement );
}
linkHasRequiredProperties( link ) {
const requiredProperties = [ 'title', 'icon', 'url' ];
return requiredProperties.every( prop => link.hasOwnProperty( prop ) );
}
/**
* Create the icon button element, add a click event listener, and append it to the wrapper element.
*
* @memberof S11FloatingLinks
*/
createIconButton() {
// Create the icon button element
this.iconButtonElement = document.createElement( 'div' );
this.iconButtonElement.classList.add( 's11-floating-links-logo-icon' );
this.iconButtonElement.innerHTML = this.options.logoIcon.trim();
// Define close icon
const closeIcon = `
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none" viewBox="0 0 32 32">
<path fill="#1D2939" d="M23.625 21.957c.47.467.47 1.225 0 1.693a1.205 1.205 0 0 1-1.699 0l-5.915-5.937-5.958 5.935c-.47.467-1.23.467-1.7 0a1.194 1.194 0 0 1 0-1.693l5.96-5.933-5.961-5.979a1.194 1.194 0 0 1 0-1.693 1.205 1.205 0 0 1 1.699 0l5.96 5.982 5.957-5.935a1.205 1.205 0 0 1 1.7 0 1.194 1.194 0 0 1 0 1.693l-5.96 5.932 5.917 5.935Z"/>
</svg>
`;
// Add a click event listener
this.iconButtonElement.addEventListener( 'click', () => {
// Toggle the navigation menu element
this.toggleFade( this.navMenuElement );
if ( this.slideIn ) {
this.toggleFade( this.slideIn );
}
// Switch the icon of the icon button element
this.switchIconButton( closeIcon );
} );
// Append the icon button to the wrapper element
this.wrapperElement.append( this.iconButtonElement );
}
/**
* Switch the icon of the icon button element between the logo icon and the close icon.
* This method is called when the icon button is clicked.
*
* @memberof S11FloatingLinks
*
* @param {string} closeIcon - The SVG close icon that will replace the logo icon when the navigation menu is opened.
*/
switchIconButton( closeIcon ) {
this.iconButtonElement.classList.toggle( 's11-show-close-icon' );
if ( this.iconButtonElement.classList.contains( 's11-show-close-icon' ) ) {
this.iconButtonElement.innerHTML = closeIcon.trim();
return;
}
this.iconButtonElement.innerHTML = this.options.logoIcon.trim();
}
/**
* Toggle the fade-in and fade-out animation for the specified element.
* This method is used to show or hide the navigation menu when the icon button is clicked.
*
* @memberof S11FloatingLinks
*
* @param {HTMLElement} element - The element to apply the fade animation on.
*/
toggleFade( element ) {
if ( ! element ) {
return;
}
element.classList.add( 's11-fading' );
element.classList.toggle( 's11-fadein' );
element.classList.toggle( 's11-fadeout' );
element.addEventListener( 'animationend', () => {
element.classList.remove( 's11-fading' );
}, { once: true } );
}
/**
* Add a click event listener to the body to close the navigation when clicked outside of the floating links wrapper.
* Prevents the click event from bubbling up to the body when clicking on the floating links wrapper.
*
* @memberof S11FloatingLinks
*/
addOutsideClickListener() {
document.body.addEventListener( 'click', e => {
if ( ! this.wrapperElement.contains( e.target ) && this.navMenuElement.classList.contains( 's11-fadein' ) ) {
// Toggle the navigation menu element
this.toggleFade( this.navMenuElement );
if ( this.slideIn ) {
this.toggleFade( this.slideIn );
}
// Switch the icon of the icon button element
this.switchIconButton( this.options.logoIcon.trim() );
}
} );
// Prevent click event from bubbling up to the body when clicking on the wrapper element
this.wrapperElement.addEventListener( 'click', e => {
e.stopPropagation();
} );
}
/**
* Add a scroll event listener to the window to toggle visibility of the Floating Links.
* Show the Floating Links when scrolling up, and hide when scrolling down.
*
* @memberof S11FloatingLinks
*/
addScrollEventListener() {
window.addEventListener( 'scroll', () => {
const currentScrollPosition = window.scrollY || document.documentElement.scrollTop;
if ( currentScrollPosition < this.lastScrollPosition ) {
// When scrolling up show the Floating Links
if ( ! this.wrapperElement.classList.contains( 's11-fadein' ) ) {
this.toggleFade( this.wrapperElement );
}
} else if ( ! this.wrapperElement.classList.contains( 's11-fadeout' ) && ! this.inboxSlideInIsVisible() ) {
// When scrolling down hide the Floating Links
this.toggleFade( this.wrapperElement );
}
this.lastScrollPosition = currentScrollPosition;
} );
}
inboxSlideInIsVisible() {
if ( ! this.slideIn ) {
return 'object' === typeof frmGlobal.inboxSlideIn;
}
return null === this.wrapperElement.querySelector( '.s11-show-close-icon' );
}
/**
* Set dynamic CSS properties.
*
* @memberof S11FloatingLinks
*/
setCSSVariables() {
const hoverColor = this.options?.hoverColor ? this.options.hoverColor : '#4199FD';
const bgHoverColor = this.options?.bgHoverColor ? this.options.bgHoverColor : '#F5FAFF';
// Set the CSS variables on the wrapper element
this.wrapperElement.style.setProperty( '--floating-links-hover-color', hoverColor );
this.wrapperElement.style.setProperty( '--floating-links-bg-hover-color', bgHoverColor );
}
}
// Initialize Floating Links
new S11FloatingLinks();