154 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			154 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Toolbar popover.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @type {Drupal~behavior}
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @prop {Drupal~behaviorAttach} attach
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const POPOVER_OPEN_DELAY = 150;
							 | 
						||
| 
								 | 
							
								const POPOVER_CLOSE_DELAY = 400;
							 | 
						||
| 
								 | 
							
								const POPOVER_NO_CLICK_DELAY = 500;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								((Drupal, once) => {
							 | 
						||
| 
								 | 
							
								  Drupal.behaviors.navigationProcessPopovers = {
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Attaches the behavior to the context element.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param {HTMLElement} context The context element to attach the behavior to.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    attach: (context) => {
							 | 
						||
| 
								 | 
							
								      once(
							 | 
						||
| 
								 | 
							
								        'toolbar-popover',
							 | 
						||
| 
								 | 
							
								        context.querySelectorAll('[data-toolbar-popover]'),
							 | 
						||
| 
								 | 
							
								      ).forEach((popover) => {
							 | 
						||
| 
								 | 
							
								        // This is trigger of popover. Currently only first level button.
							 | 
						||
| 
								 | 
							
								        const button = popover.querySelector('[data-toolbar-popover-control]');
							 | 
						||
| 
								 | 
							
								        // This is tooltip content. Currently child menus only.
							 | 
						||
| 
								 | 
							
								        const tooltip = popover.querySelector('[data-toolbar-popover-wrapper]');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (!button || !tooltip) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        const expandPopover = () => {
							 | 
						||
| 
								 | 
							
								          popover.classList.add('toolbar-popover--expanded');
							 | 
						||
| 
								 | 
							
								          button.dataset.drupalNoClick = 'true';
							 | 
						||
| 
								 | 
							
								          tooltip.removeAttribute('inert');
							 | 
						||
| 
								 | 
							
								          setTimeout(() => {
							 | 
						||
| 
								 | 
							
								            delete button.dataset.drupalNoClick;
							 | 
						||
| 
								 | 
							
								          }, POPOVER_NO_CLICK_DELAY);
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        const collapsePopover = () => {
							 | 
						||
| 
								 | 
							
								          popover.classList.remove('toolbar-popover--expanded');
							 | 
						||
| 
								 | 
							
								          tooltip.setAttribute('inert', true);
							 | 
						||
| 
								 | 
							
								          delete button.dataset.drupalNoClick;
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        /**
							 | 
						||
| 
								 | 
							
								         * We need to change state of trigger and popover.
							 | 
						||
| 
								 | 
							
								         *
							 | 
						||
| 
								 | 
							
								         * @param {boolean} state The popover state.
							 | 
						||
| 
								 | 
							
								         *
							 | 
						||
| 
								 | 
							
								         * @param {boolean} initialLoad Happens on page loads.
							 | 
						||
| 
								 | 
							
								         */
							 | 
						||
| 
								 | 
							
								        const toggleState = (state, initialLoad = false) => {
							 | 
						||
| 
								 | 
							
								          /* eslint-disable-next-line no-unused-expressions */
							 | 
						||
| 
								 | 
							
								          state && !initialLoad ? expandPopover() : collapsePopover();
							 | 
						||
| 
								 | 
							
								          button.setAttribute('aria-expanded', state && !initialLoad);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          const text = button.querySelector('[data-toolbar-action]');
							 | 
						||
| 
								 | 
							
								          if (text) {
							 | 
						||
| 
								 | 
							
								            text.textContent = state
							 | 
						||
| 
								 | 
							
								              ? Drupal.t('Collapse')
							 | 
						||
| 
								 | 
							
								              : Drupal.t('Extend');
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          // Dispatch event to sidebar.js
							 | 
						||
| 
								 | 
							
								          popover.dispatchEvent(
							 | 
						||
| 
								 | 
							
								            new CustomEvent('toolbar-popover-toggled', {
							 | 
						||
| 
								 | 
							
								              bubbles: true,
							 | 
						||
| 
								 | 
							
								              detail: {
							 | 
						||
| 
								 | 
							
								                state,
							 | 
						||
| 
								 | 
							
								              },
							 | 
						||
| 
								 | 
							
								            }),
							 | 
						||
| 
								 | 
							
								          );
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        const isPopoverHoverOrFocus = () =>
							 | 
						||
| 
								 | 
							
								          popover.contains(document.activeElement) || popover.matches(':hover');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        const delayedClose = () => {
							 | 
						||
| 
								 | 
							
								          setTimeout(() => {
							 | 
						||
| 
								 | 
							
								            if (isPopoverHoverOrFocus()) return;
							 | 
						||
| 
								 | 
							
								            // eslint-disable-next-line no-use-before-define
							 | 
						||
| 
								 | 
							
								            close();
							 | 
						||
| 
								 | 
							
								          }, POPOVER_CLOSE_DELAY);
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        const open = () => {
							 | 
						||
| 
								 | 
							
								          ['mouseleave', 'focusout'].forEach((e) => {
							 | 
						||
| 
								 | 
							
								            button.addEventListener(e, delayedClose, false);
							 | 
						||
| 
								 | 
							
								            tooltip.addEventListener(e, delayedClose, false);
							 | 
						||
| 
								 | 
							
								          });
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        const close = () => {
							 | 
						||
| 
								 | 
							
								          toggleState(false);
							 | 
						||
| 
								 | 
							
								          ['mouseleave', 'focusout'].forEach((e) => {
							 | 
						||
| 
								 | 
							
								            button.removeEventListener(e, delayedClose);
							 | 
						||
| 
								 | 
							
								            tooltip.removeEventListener(e, delayedClose);
							 | 
						||
| 
								 | 
							
								          });
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        button.addEventListener('mouseover', () => {
							 | 
						||
| 
								 | 
							
								          // This is not needed because no hover on mobile.
							 | 
						||
| 
								 | 
							
								          // @todo test is after.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if (window.matchMedia('(max-width: 1023px)').matches) {
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          setTimeout(() => {
							 | 
						||
| 
								 | 
							
								            // If it is accident hover ignore it.
							 | 
						||
| 
								 | 
							
								            // If in this timeout popover already opened by click.
							 | 
						||
| 
								 | 
							
								            if (
							 | 
						||
| 
								 | 
							
								              !button.matches(':hover') ||
							 | 
						||
| 
								 | 
							
								              !button.getAttribute('aria-expanded') === 'false'
							 | 
						||
| 
								 | 
							
								            ) {
							 | 
						||
| 
								 | 
							
								              return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            toggleState(true);
							 | 
						||
| 
								 | 
							
								            open();
							 | 
						||
| 
								 | 
							
								          }, POPOVER_OPEN_DELAY);
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        button.addEventListener('click', (e) => {
							 | 
						||
| 
								 | 
							
								          const state =
							 | 
						||
| 
								 | 
							
								            e.currentTarget.getAttribute('aria-expanded') === 'false';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if (!e.currentTarget.dataset.drupalNoClick) {
							 | 
						||
| 
								 | 
							
								            toggleState(state);
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // Listens events from sidebar.js.
							 | 
						||
| 
								 | 
							
								        popover.addEventListener('toolbar-popover-close', () => {
							 | 
						||
| 
								 | 
							
								          close();
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // TODO: Add toggle with state.
							 | 
						||
| 
								 | 
							
								        popover.addEventListener('toolbar-popover-open', () => {
							 | 
						||
| 
								 | 
							
								          toggleState(true);
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // Listens events from toolbar-menu.js
							 | 
						||
| 
								 | 
							
								        popover.addEventListener('toolbar-active-url', () => {
							 | 
						||
| 
								 | 
							
								          toggleState(true, true);
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								})(Drupal, once);
							 |