bootstrap_five_layouts-1.0.x-dev/modules/bootstrap_five_layouts_css_loader/js/behaviour.classHelper.js

modules/bootstrap_five_layouts_css_loader/js/behaviour.classHelper.js
/**
 * @file
 * Bootstrap Five Layouts CSS Loader - Class Helper Behavior
 *
 * Provides tab UI functionality for the help page class lists.
 */

(function (Drupal) {
  'use strict';

  /**
   * Class Helper behavior for tab UI functionality.
   */
  Drupal.behaviors.bootstrapFiveLayoutsClassHelper = {
    attach: function (context) {
      // Only attach once per page load
      if (context !== document) {
        return;
      }

      const tabHeader = document.getElementById('classlist-tabs-header');
      if (!tabHeader) {
        return;
      }

      // Get all tab links and panels
      const tabLinks = document.querySelectorAll('#classlist-helper a[data-panel]');
      const panels = document.querySelectorAll('#classlist-helper .panel');
      const tabList = document.querySelector('#classlist-helper ul');

      if (tabLinks.length === 0 || panels.length === 0) {
        return;
      }

      // Add search input element via JavaScript
      addSearchInput();

      // Initialize search functionality
      initializeSearch();

      // Initialize tab functionality with accessibility
      initializeTabs(tabLinks, panels, tabList);

      // Handle tab clicks
      tabLinks.forEach(function(link, index) {
        link.addEventListener('click', function(e) {
          e.preventDefault();
          const targetPanel = this.getAttribute('data-panel');
          showTab(targetPanel, tabLinks, panels, index);
        });

        // Handle keyboard navigation
        link.addEventListener('keydown', function(e) {
          handleKeydown(e, tabLinks, panels, index);
        });
      });

      // Show first tab by default
      if (tabLinks.length > 0) {
        const firstTab = tabLinks[0].getAttribute('data-panel');
        showTab(firstTab, tabLinks, panels, 0);
      }
    }
  };

  /**
   * Add search input element to the page via JavaScript.
   */
  function addSearchInput() {
    const tabHeader = document.getElementById('classlist-tabs-header');
    if (!tabHeader) {
      return;
    }

    // Create search container
    const searchContainer = document.createElement('header');
    searchContainer.className = 'class-search-container';

    // Create label
    const label = document.createElement('label');
    label.setAttribute('for', 'class-search-input');
    label.textContent = Drupal.t('Live filtering across search classes');

    // Create input
    const input = document.createElement('input');
    input.type = 'search';
    input.id = 'class-search-input';
    input.className = 'form-control';
    input.placeholder = Drupal.t('Search classes…');
    input.setAttribute('aria-describedby', 'search-help');

    // Assemble the search container
    searchContainer.appendChild(label);
    searchContainer.appendChild(input);

    // Insert after the header
    tabHeader.insertAdjacentElement('afterend', searchContainer);
  }

  /**
   * Initialize tab functionality by setting up initial state with accessibility.
   */
  function initializeTabs(tabLinks, panels, tabList) {
    // Set up ARIA attributes for tablist
    if (tabList) {
      tabList.setAttribute('role', 'tablist');
      tabList.setAttribute('aria-label', 'Bootstrap class categories');
    }

    // Hide all panels initially and set up ARIA attributes
    panels.forEach(function(panel, index) {
      panel.style.display = 'none';
      panel.setAttribute('role', 'tabpanel');
      panel.setAttribute('aria-hidden', 'true');
      panel.setAttribute('aria-labelledby', tabLinks[index].id || 'tab-' + index);
      panel.querySelector('h2').style.display ='none';
    });

    // Add active class management and ARIA attributes
    tabLinks.forEach(function(link, index) {
      link.classList.add('tab-link');
      link.setAttribute('role', 'tab');
      link.setAttribute('aria-selected', 'false');
      link.setAttribute('aria-controls', panels[index].id);
      link.setAttribute('tabindex', '-1');

      // Add unique ID if not present
      if (!link.id) {
        link.id = 'tab-' + index;
      }
    });
  }

  /**
   * Show the specified tab and hide others with proper accessibility.
   */
  function showTab(targetPanelId, tabLinks, panels, activeIndex) {
    // Hide all panels
    panels.forEach(function(panel, index) {
      panel.style.display = 'none';
      panel.setAttribute('aria-hidden', 'true');
    });

    // Remove active class and ARIA states from all links
    tabLinks.forEach(function(link, index) {
      link.classList.remove('active');
      link.setAttribute('aria-selected', 'false');
      link.setAttribute('tabindex', '-1');
    });

    // Show target panel
    const targetPanel = document.getElementById(targetPanelId);
    if (targetPanel) {
      targetPanel.style.display = 'block';
      targetPanel.setAttribute('aria-hidden', 'false');
    }

    // Add active class and ARIA states to clicked link
    const activeLink = document.querySelector(`a[data-panel="${targetPanelId}"]`);
    if (activeLink) {
      activeLink.classList.add('active');
      activeLink.setAttribute('aria-selected', 'true');
      activeLink.setAttribute('tabindex', '0');

      // Focus the active tab for keyboard users
      activeLink.focus();

      // Announce panel change to screen readers
      const panelTitle = targetPanel ? targetPanel.querySelector('h3') : null;
      const panelName = panelTitle ? panelTitle.textContent : Drupal.t('Panel');
      Drupal.announce(Drupal.t('Switched to @panel panel', {'@panel': panelName}));
    }
  }

  /**
   * Handle keyboard navigation for tabs.
   */
  function handleKeydown(e, tabLinks, panels, currentIndex) {
    let targetIndex = currentIndex;

    switch (e.key) {
      case 'ArrowRight':
      case 'ArrowDown':
        e.preventDefault();
        targetIndex = (currentIndex + 1) % tabLinks.length;
        break;

      case 'ArrowLeft':
      case 'ArrowUp':
        e.preventDefault();
        targetIndex = currentIndex === 0 ? tabLinks.length - 1 : currentIndex - 1;
        break;

      case 'Home':
        e.preventDefault();
        targetIndex = 0;
        break;

      case 'End':
        e.preventDefault();
        targetIndex = tabLinks.length - 1;
        break;

      case 'Enter':
      case ' ':
        e.preventDefault();
        const targetPanel = tabLinks[currentIndex].getAttribute('data-panel');
        showTab(targetPanel, tabLinks, panels, currentIndex);
        return;

      default:
        return; // Let other keys work normally
    }

    // Focus the target tab
    tabLinks[targetIndex].focus();
  }

  /**
   * Initialize search functionality for filtering classes.
   */
  function initializeSearch() {
    const searchInput = document.getElementById('class-search-input');
    if (!searchInput) {
      return;
    }

    // Add event listener for search input
    searchInput.addEventListener('input', function() {
      const searchTerm = this.value.toLowerCase().trim();
      filterClasses(searchTerm);
    });

    // Add keyboard shortcuts
    searchInput.addEventListener('keydown', function(e) {
      // Escape key clears search
      if (e.key === 'Escape') {
        this.value = '';
        filterClasses('');
        this.focus();
      }
    });
  }

  /**
   * Filter classes in all panels based on search term.
   */
  function filterClasses(searchTerm) {
    const panels = document.querySelectorAll('#classlist-helper .panel');
    let totalVisibleCount = 0;

    panels.forEach(function(panel) {
      const table = panel.querySelector('table');
      if (!table) {
        return;
      }

      const tbody = table.querySelector('tbody');
      if (!tbody) {
        return;
      }

      const rows = tbody.querySelectorAll('tr');
      let visibleCount = 0;

      rows.forEach(function(row) {
        const cells = row.querySelectorAll('td');
        let isVisible = false;

        // Check if any cell content matches the search term
        cells.forEach(function(cell) {
          const cellText = cell.textContent.toLowerCase();
          if (cellText.includes(searchTerm)) {
            isVisible = true;
          }
        });

        // Show/hide row based on search match
        if (isVisible || searchTerm === '') {
          row.style.display = '';
          visibleCount++;
        } else {
          row.style.display = 'none';
        }
      });

      // Update panel visibility and add "no results" message if needed
      updatePanelVisibility(panel, visibleCount, searchTerm);

      // Update the count in the corresponding tab header
      updateTabCount(panel, visibleCount, searchTerm);

      totalVisibleCount += visibleCount;
    });

    // Announce search results to screen readers
    if (searchTerm !== '') {
      if (totalVisibleCount === 0) {
        Drupal.announce(Drupal.t('No classes found matching "@search"', {'@search': searchTerm}));
      } else {
        Drupal.announce(Drupal.t('@count classes found matching "@search"', {
          '@count': totalVisibleCount,
          '@search': searchTerm
        }));
      }
    } else {
      Drupal.announce(Drupal.t('Search cleared, showing all classes'));
    }
  }

  /**
   * Update panel visibility and show/hide "no results" message.
   */
  function updatePanelVisibility(panel, visibleCount, searchTerm) {
    const table = panel.querySelector('table');
    const existingNoResults = panel.querySelector('.no-results-message');

    // Remove existing "no results" message
    if (existingNoResults) {
      existingNoResults.remove();
    }

    // Show/hide table based on results
    if (visibleCount === 0 && searchTerm !== '') {
      table.style.display = 'none';

      // Add "no results" message
      const noResultsDiv = document.createElement('div');
      noResultsDiv.className = 'no-results-message alert alert-info';
      noResultsDiv.innerHTML = '<strong>' + Drupal.t('Nothing with the pharse has been found.') + '</strong>';

      // Insert after the panel title
      const title = panel.querySelector('h2');
      if (title) {
        title.insertAdjacentElement('afterend', noResultsDiv);
      }
    } else {
      table.style.display = '';
    }
  }

  /**
   * Update the count displayed in the tab header for the given panel.
   */
  function updateTabCount(panel, visibleCount, searchTerm) {
    const panelId = panel.id;
    if (!panelId) {
      return;
    }

    // Find the corresponding tab link
    const tabLink = document.querySelector(`a[data-panel="${panelId}"]`);
    if (!tabLink) {
      return;
    }

    // Find the count span within the tab link
    const countSpan = tabLink.querySelector('.count');
    if (!countSpan) {
      return;
    }

    // Store original count if not already stored
    if (!countSpan.hasAttribute('data-original-count')) {
      countSpan.setAttribute('data-original-count', countSpan.textContent);
    }

    // Update the count display
    if (searchTerm === '') {
      // Show original count when no filter is applied
      countSpan.textContent = countSpan.getAttribute('data-original-count');
    } else {
      // Show filtered count
      countSpan.textContent = visibleCount;
    }
  }

})(Drupal);

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc