hooks_use-hijack-wp-menu.js

import { React } from '@gravityforms/libraries';

const { useEffect } = React;

/**
 * @function addCurrentToMenuItem
 * @description Add the current class and attributes to a menu item.
 *
 * @since 4.0.2
 *
 * @param {HTMLElement} item The menu item to add the current class and attributes to.
 */
const addCurrentToMenuItem = ( item ) => {
	item.classList.add( 'current' );
	const itemLink = item.querySelector( 'a' );
	itemLink.classList.add( 'current' );
	itemLink.setAttribute( 'aria-current', 'page' );
};

/**
 * @function removeCurrentFromMenuItem
 * @description Remove the current class and attributes from a menu item.
 *
 * @since 4.0.2
 *
 * @param {HTMLElement} item The menu item to remove the current class and attributes from.
 */
const removeCurrentFromMenuItem = ( item ) => {
	item.classList.remove( 'current' );
	const itemLink = item.querySelector( 'a' );
	itemLink.classList.remove( 'current' );
	itemLink.removeAttribute( 'aria-current' );
};

/**
 * @module useHijackWpMenu
 * @description Hijacks the click event on the menu.
 *
 * @since 4.0.2
 *
 * @param {string}   menuId       The ID of the menu to hijack.
 * @param {Function} routeHandler The function to handle the routing.
 * @param {object}   options      The options for the hook.
 */
const useHijackWpMenu = (
	menuId = '',
	routeHandler = () => {},
	options = {
		updateCurrent: true,
	},
) => {
	const menu = document.getElementById( menuId );

	/**
	 * @function updateCurrentMenuItem
	 * @description Update the current menu item.
	 *
	 * @since 4.0.2
	 *
	 * @param {HTMLElement} link The link to update the current menu item.
	 */
	const updateCurrentMenuItem = ( link ) => {
		const menuItems = menu.querySelectorAll( 'li:not(.wp-submenu-head)' );
		const href = link.getAttribute( 'href' );
		menuItems.forEach( removeCurrentFromMenuItem );
		if ( link.classList.contains( 'menu-top' ) ) {
			// Link is a top-level menu item, update classes and aria attributes.
			menuItems.forEach( ( item ) => {
				if ( item.querySelector( 'a' )?.getAttribute( 'href' ) !== href ) {
					return;
				}
				addCurrentToMenuItem( item );
			} );
		} else {
			// Link is a submenu item, update classes and aria attributes.
			addCurrentToMenuItem( link.parentElement );
		}
	};

	useEffect( () => {
		if ( ! menu ) {
			return;
		}

		/**
		 * @function hijackMenuClick
		 * @description Hijacks the click event on the menu.
		 *
		 * @since 4.0.2
		 *
		 * @param {Event} event The click event.
		 */
		const hijackMenuClick = ( event ) => {
			event.preventDefault();
			const link = event.currentTarget;
			const href = link.getAttribute( 'href' );

			if ( options.updateCurrent ) {
				updateCurrentMenuItem( link );
			}

			const linkSearchParams = new URLSearchParams( href.split( '?' )[ 1 ] );
			routeHandler( linkSearchParams );
		};

		const menuLinks = menu.querySelectorAll( 'a' );
		menuLinks.forEach( ( link ) => {
			link.addEventListener( 'click', hijackMenuClick );
		} );

		return () => {
			menuLinks.forEach( ( link ) => {
				link.removeEventListener( 'click', hijackMenuClick );
			} );
		};
	}, [ menuId, routeHandler ] );

	return {
		updateCurrentMenuItem,
	};
};

export default useHijackWpMenu;