Untitled

mail@pastecode.io avatar
unknown
plain_text
5 months ago
12 kB
3
Indexable
import { assignEventAction } from '../utils/analytics';
import { getSelectedStateFromLocalStorage } from '../utils/local-storage';

/**
 * Tabs
 */
export default class BlackstonePerformanceData {
	/**
	 * Component Constructor.
	 */
	constructor() {
		this.blocks = Array.from( document.querySelectorAll( '.performance-data' ) );
		this.selectedState = getSelectedStateFromLocalStorage();

		// Bail if tabs block is not on page
		if ( ! this.blocks.length ) {
			return;
		}

		this.blocks.forEach( block => this.initBlock( block ) );
	}

	/**
	 * Initialize the block.
	 */
	initBlock( block ) {
		const select = block.querySelector( '.performance-data__select' );
		const dropdownHeader = block.querySelector( '.dropdown-header' );
		const dropdownButton = block.querySelector( '.dropdown-button' );
		const dropdownOptions = Array.from( block.querySelectorAll( '.dropdown-option' ) );
		const windowhashdata = window.location.hash ? window.location.hash : '';    // phpcs:ignore WordPressVIPMinimum.JS.Window.location, WordPressVIPMinimum.JS.Window.VarAssignment

		if ( select ) {
			const options = Array.from( select.querySelectorAll( 'option' ) );
			select.addEventListener( 'change', ( e ) => this.selectTab( e, block ) );

			document.addEventListener( 'setGatingState', () => {
				this.selectedState = getSelectedStateFromLocalStorage();
				this.updateTabs( select, options, windowhashdata );
			} );

			this.updateTabs( select, options, windowhashdata );
			this.selectTabOnload( block, windowhashdata );

			block.dataset.initiallyHidden = false;

			if ( block.querySelector( '.is-sticky' ) ) {
				this.positionSelectDropdown( block );
			}
		} else {
			if ( dropdownHeader ) {
				dropdownHeader.addEventListener( 'click', ( e ) => this.toggleDropdown( e, block ) );
			}

			if ( dropdownButton ) {
				dropdownButton.addEventListener( 'click', ( e ) => this.toggleDropdown( e, block ) );
			}

			if ( 0 < dropdownOptions.length ) {
				dropdownOptions.forEach( option => {
					option.addEventListener( 'click', ( e ) => this.selectDropdownOption( e, block, true ) );

					// Add event listener to remove highlighted on hover
					option.addEventListener( 'mouseenter', () => {
						dropdownOptions.forEach( opt => opt.classList.remove( 'selected' ) );
					} );
				} );
			}

			dropdownOptions.forEach( option => {
				option.addEventListener( 'mouseenter', ( e ) => this.handleHover( e ) );
			} );

			document.addEventListener( 'click', ( event ) => {
				if ( !event.target.closest( '.dropdown-container' ) ) {
					this.closeDropdown( block );
				}
			} );

			document.addEventListener( 'setGatingState', () => {
				this.selectedState = getSelectedStateFromLocalStorage();
				this.updateTabsForDropdown( dropdownOptions, windowhashdata, block, false );
			} );

			block.dataset.initiallyHidden = false;
			
			this.updateTabsForDropdown( dropdownOptions, windowhashdata, block, true );
			this.selectTabOnloadForDropdown( block, windowhashdata );

			window.addEventListener( 'scroll', () => this.handleScroll( block ) );
		}
		
		// Set the initial value of the dropdown button based on priority: hash value, gating state, or default option
		const hashValue = windowhashdata ? windowhashdata.replace( '#', '' ).toLowerCase() : null;
		let selectedOption = null;
	
		if ( hashValue ) {
			selectedOption = dropdownOptions.find( option => option.getAttribute( 'value' ) === hashValue );
		}
	
		if ( !selectedOption && this.selectedState ) {
			selectedOption = dropdownOptions.find( option => {
				const defaultInStates = option.dataset.defaultInStates?.split( ' ' );
				return defaultInStates && defaultInStates.includes( this.selectedState );
			} );
		}
	
		if ( !selectedOption ) {
			selectedOption = dropdownOptions[0];
		}
	
		if ( selectedOption && dropdownButton ) {
			dropdownButton.innerText = selectedOption.innerText;
			this.selectDropdownOption( { target: selectedOption, stopPropagation: () => {} }, block, false );
		}
	}

	toggleDropdown( e, block ) {
		e.stopPropagation();
		const dropdownOptions = block.querySelector( '.dropdown-options' );
		const dropdownContainer = block.querySelector( '.dropdown-container' );
		
		dropdownOptions.classList.toggle( 'show' );
		dropdownContainer.classList.toggle( 'selected' );
	}

	closeDropdown( block ) {
		const dropdownOptions = block.querySelector( '.dropdown-options' );
		const dropdownContainer = block.querySelector( '.dropdown-container' );
		
		dropdownOptions.classList.remove( 'show' );
		dropdownContainer.classList.remove( 'selected' );
	}
	
	selectDropdownOption( e, block, sendToGA = true ) {
		e.stopPropagation();
		const selected = e.target.getAttribute( 'value' );
		const dropdownButton = block.querySelector( '.dropdown-button' );
		const target = block.querySelector( `.performance-data-${selected}` );
		const items = Array.from( block.querySelectorAll( '.performance-data__item' ) );
		const dropdownOptions = Array.from( block.querySelectorAll( '.dropdown-option' ) );

		dropdownOptions.forEach( option => {
			option.classList.remove( 'selected' );
			option.classList.remove( 'hovered' );
		} );

		// Mark the clicked option as selected
		e.target.classList.add( 'selected' );

		if ( dropdownButton ) {
			dropdownButton.innerText = e.target.innerText;
		}

		items.forEach( item => item.setAttribute( 'aria-selected', false ) );

		if ( target ) {
			target.setAttribute( 'aria-selected', true );
		}

		this.closeDropdown( block );

		if (sendToGA) {
			assignEventAction( e );
		}
	}

	/**
	 * Update the tabs (for state change);
	 */
	updateTabs( select, options, windowhashdata ) {
		let defaultOption = null;

		if ( this.selectedState ) {
			const defaultOptions = options.filter( option => {
				const defaultInStates = option.dataset.defaultInStates?.split( ' ' );
				return defaultInStates.includes( this.selectedState );
			} ); 
			
			if ( defaultOptions.length && ! windowhashdata ) {
				[defaultOption] = defaultOptions;

				select.value = defaultOption.value;
				const changeEvent = new Event( 'change' );
				select.dispatchEvent( changeEvent );
			}
		}
	}

	updateTabsForDropdown( dropdownOptions, windowhashdata, block, sendToGA = true ) {
		let defaultOption = null;

		if ( this.selectedState ) {
			const defaultOptions = dropdownOptions.filter( option => {
				const defaultInStates = option.dataset.defaultInStates?.split( ' ' );
				return defaultInStates && defaultInStates.includes( this.selectedState );
			} );

			if ( defaultOptions.length && !windowhashdata ) {
				[defaultOption] = defaultOptions;

				const event= new Event( 'click', { bubbles: true } );
				defaultOption.dispatchEvent( event );

				const dropdownButton = block.querySelector( '.dropdown-button' );
				if( dropdownButton ) {
					dropdownButton.innerText = defaultOption.innerText;
				}

				// Ensure the selected option is highlighted
				dropdownOptions.forEach( option => option.classList.remove( 'selected' ) );
				defaultOption.classList.add( 'selected' );
			}
		}
	}

	/**
	 * Select Tab Logic.
	 */
	selectTab( e, block ) {
		const selected = e.target.options[ e.target.selectedIndex ].value;
		const target = block.querySelector( `.performance-data-${ selected }` );
		const items = Array.from( block.querySelectorAll( '.performance-data__item' ) );

		// Unselect selected items.
		items.forEach( item => item.setAttribute( 'aria-selected', false ) );

		// Select target one.
		if ( target ) {
			target.setAttribute( 'aria-selected', true );

			// Remove focus to prevent ipad issue with chart selection.
			e.target.blur();
		}

		/**
		 * Reset the first performance item chart tab to first.
		 * 10up/component-tabs does not provide an api for targetting each instance,
		 * therefore all tabs are updated on the page. `e.isTrusted` ensures it is
		 * an user action and not occuring as a result of `updateTabs` region updates,
		 * which causes onload issues.
		 */
		if ( e.isTrusted ) {
			const firstChartTab = target.querySelector( '.tab-item a[role=tab]' );
			if ( firstChartTab ) {
				firstChartTab.click();
			}
		}

		assignEventAction( e );
	}

	/**
	 * On page load, select the appropriate tab based on hash value or the first item.
	 */
	selectTabOnload( block, windowhashdata ) {
		const select = block.querySelector( '.performance-data__select' );
		const options = Array.from( select.querySelectorAll( 'option' ) );
		const hashValue = windowhashdata ? windowhashdata.replace( '#', '' ).toLowerCase() : null;
		let selectedOption = null;

		if ( hashValue ) {
			selectedOption = options.find( option => option.value === hashValue );
		}

		if ( ! selectedOption && this.selectedState ) {
			selectedOption = options.find( option => {
				const defaultInStates = option.dataset.defaultInStates?.split( ' ' );
				return defaultInStates && defaultInStates.includes( this.selectedState );
			} );
		}

		if ( ! selectedOption ) {
			selectedOption = options[0];
		}

		select.value = selectedOption.value;
		const changeEvent = new Event( 'change' );
		select.dispatchEvent( changeEvent );
	}

	selectTabOnloadForDropdown( block, windowhashdata ) {
		const dropdownOptions = Array.from( block.querySelectorAll( '.dropdown-option' ) );
		const hashValue = windowhashdata ? windowhashdata.replace( '#', '' ).toLowerCase() : null;
		let selectedOption = null;

		if ( hashValue ) {
			selectedOption = dropdownOptions.find( option => option.getAttribute( 'value' ) === hashValue );
		}

		if ( ! selectedOption && this.selectedState ) {
			selectedOption = dropdownOptions.find( option => {
				const defaultInStates = option.dataset.defaultInStates?.split( ' ' );
				return defaultInStates && defaultInStates.includes( this.selectedState );
			} );
		}

		if ( ! selectedOption ) {
			selectedOption = dropdownOptions[0];
		}

		const event= new Event( 'click', { bubbles: true } );
		selectedOption.dispatchEvent( event );

		// Ensure the selected option is highlighted
		dropdownOptions.forEach( option => option.classList.remove( 'selected' ) );
		selectedOption.classList.add( 'selected' );
	}

	handleScroll( block ) {
		const dropdownButton = block.querySelector( '.dropdown-button' );
		const dropdownContainer = block.querySelector( '.dropdown-container' );
		const dropdownOptions = block.querySelector( '.dropdown-options' );
		const isSticky = dropdownButton && dropdownButton.classList.contains( 'sticky' );

		if ( ! isSticky ) {
			return;
		}

		if ( window.scrollY > dropdownButton.offsetTop ) {
			dropdownContainer.classList.add( 'stuck' );
			dropdownOptions.classList.add( 'stuck' );
		} else {
			dropdownContainer.classList.remove( 'stuck' );
			dropdownOptions.classList.remove( 'stuck' );
		}
	}

	handleHover( e ) {
		const dropdownOption = e.target;

		// Ensure hover is removed from any previously hovered option
		const dropdownOptions = Array.from( dropdownOption.parentElement.querySelectorAll( '.dropdown-option' ) );
		dropdownOptions.forEach( option => option.classList.remove( 'hovered' ) );

		// Highlight the hovered option
		dropdownOption.classList.add( 'hovered' );
	}

	positionSelectDropdown( block ) {
		const stickyContainer = block.querySelector( '.sticky-container' );
		const dropdownContainer = block.querySelector( '.dropdown-container' );

		const topOfStickyContainer = stickyContainer.getBoundingClientRect().top + window.scrollY;
		const topOfDropdownContainer = dropdownContainer.getBoundingClientRect().top + window.scrollY;

		if ( topOfDropdownContainer > topOfStickyContainer ) {
			dropdownContainer.classList.add( 'above-sticky' );
		} else {
			dropdownContainer.classList.remove( 'above-sticky' );
		}
	}
}
Leave a Comment