Commit 2cab53c8 authored by David Suissa's avatar David Suissa
Browse files

Issue #3558389 by dydave: Admin Toolbar Search: Refactored mobile toolbar item...

Issue #3558389 by dydave: Admin Toolbar Search: Refactored mobile toolbar item to use core toolbar tray.
parent 8faa8777
Loading
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
# Libraries for the Admin Toolbar Search module.

# The main library that provides the search functionality and styling.
search:
admin_toolbar_search:
  css:
    theme:
      # Styles for the search tab, field, and autocomplete dropdown.
@@ -14,8 +14,8 @@ search:
    - core/drupal.autocomplete

# A separate library to handle keyboard shortcuts for the search field.
search.keyboard_shortcut:
admin_toolbar_search.keyboard_shortcut:
  js:
    js/admin_toolbar_search.keyboard_shortcut.js: {}
  dependencies:
    - admin_toolbar_search/search
    - admin_toolbar_search/admin_toolbar_search
+71 −60
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
 */

use Drupal\admin_toolbar_search\Constants\AdminToolbarSearchConstants;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Url;

/**
@@ -27,16 +28,22 @@ function admin_toolbar_search_help($route_name) {
/**
 * Implements hook_toolbar().
 *
 * Inject the admin toolbar search JS autocomplete text field in the Toolbar,
 * either directly or as a toolbar item tab with a tray ('display_menu_item').
 * Add the necessary CSS classes, HTML IDs and load required JS libraries and
 * settings.
 * Inject the admin toolbar search JS autocomplete text field in the Toolbar
 * directly as a toolbar item or as a menu item tab with a tray
 * ('display_menu_item'). Add the necessary CSS classes, HTML IDs and load
 * required JS libraries and settings.
 *
 * Provide support for mobile devices by adding a mobile search tab that
 * displays the search field in a custom tray when the toolbar width is below
 * 769px.
 * Note the display styles for mobile devices is still different depending on
 * whether the 'display_menu_item' setting is enabled or not.
 * The autocomplete search field is always loaded with a menu item tab and tray,
 * but if the 'display_menu_item' setting is disabled, the search field is also
 * added directly in the toolbar as a separate toolbar item.
 * The default search tab is then hidden or displayed with CSS styles when the
 * toolbar width is below '769px', to support mobile devices.
 *
 * In other words, support for smaller devices is not provided by the module,
 * but by Drupal core.
 *
 * @see admin_toolbar_search/css/admin_toolbar_search.css
 * @see admin_toolbar_search/js/admin_toolbar_search.js
 */
function admin_toolbar_search_toolbar() {
  // Load the admin toolbar search only if the user has the required permission.
@@ -57,17 +64,14 @@ function admin_toolbar_search_toolbar() {
  $search_field_placeholder = t('Search for menu links');
  $search_field_title_attribute = t('Type text to search for menu links in the admin toolbar.');

  // Render array of properties for the admin search tab menu link, when it is
  // *not* displayed directly in the toolbar or as a fallback for mobile search.
  $administration_search_tab_link = [
    '#type' => 'link',
    '#title' => $search_field_title,
    // Since the search field is handled with JS, the tab does not need a link.
    '#url' => Url::fromRoute('<nolink>'),
    '#attributes' => [
      'class' => [
        'toolbar-icon',
  // Ensure the toolbar items are rebuilt when the user permissions or module's
  // settings change and keep the render arrays cached.
  $toolbar_search_item_cache = [
    'contexts' => [
      'user.permissions',
    ],
    'tags' => [
      'config:admin_toolbar_search.settings',
    ],
  ];
  // Render array of properties for the admin toolbar search JS autocomplete
@@ -92,6 +96,24 @@ function admin_toolbar_search_toolbar() {
  // Add the admin toolbar search as a toolbar item render array.
  $items['administration_search'] = [
    '#type' => 'toolbar_item',
    'tab' => [
      // Render array of properties for the admin search tab menu link, when it
      // is *not* displayed directly in the toolbar or as a fallback for mobile.
      '#type' => 'link',
      '#title' => $search_field_title,
      // Since the search field is handled with JS, there is no link url for the
      // tab, so a simple 'span' element could be used instead.
      '#url' => Url::fromRoute('<nolink>'),
      '#attributes' => [
        'class' => [
          // Ensures compatibility with Drupal core toolbar styles for the
          // search tab magnifying glass icon.
          'toolbar-icon',
        ],
      ],
    ],
    // Move the search tab to the end of the toolbar items, by default.
    '#weight' => 110,
    // Set the ID of the HTML element wrapping the toolbar item.
    '#wrapper_attributes' => [
      'id' => AdminToolbarSearchConstants::ADMIN_TOOLBAR_SEARCH_HTML_IDS['search_tab'],
@@ -99,7 +121,7 @@ function admin_toolbar_search_toolbar() {
    // Load the required JS library and settings by default.
    '#attached' => [
      'library' => [
        'admin_toolbar_search/search',
        'admin_toolbar_search/admin_toolbar_search',
      ],
      // Add a setting to determine if extra links should be loaded via AJAX.
      'drupalSettings' => [
@@ -108,24 +130,13 @@ function admin_toolbar_search_toolbar() {
        ],
      ],
    ],
    // Ensure the toolbar item is rebuilt when the user permissions or module's
    // settings change and keep this render array cached.
    '#cache' => [
      'contexts' => [
        'user.permissions',
      ],
      'tags' => [
        'config:admin_toolbar_search.settings',
      ],
    ],
    // Set the cache metadata for the administration search toolbar item.
    '#cache' => $toolbar_search_item_cache,
  ];

  // Add the keyboard shortcut library if enabled ('enable_keyboard_shortcut').
  if ($admin_toolbar_search_settings->get('enable_keyboard_shortcut')) {
    $items['administration_search']['#attached']['library'][] = 'admin_toolbar_search/search.keyboard_shortcut';
    // The display menu item setting is used to determine if the search input
    // should be displayed as a menu item or directly in the toolbar.
    $items['administration_search']['#attached']['drupalSettings']['adminToolbarSearch']['displayMenuItem'] = $display_menu_item;
    $items['administration_search']['#attached']['library'][] = 'admin_toolbar_search/admin_toolbar_search.keyboard_shortcut';

    // Add the keyboard shortcut label to the search field placeholder and
    // title attributes.
@@ -136,39 +147,39 @@ function admin_toolbar_search_toolbar() {
    ]);
  }

  // Build the toolbar item depending on the whether it is displayed as a menu
  // item with a tray or directly in the toolbar ('display_menu_item' setting).
  if ($display_menu_item) {
    // The search field is displayed in a specific Tray under the Search Tab.
    $items['administration_search'] += [
      'tab' => $administration_search_tab_link,
      'tray' => $administration_search_field,
    ];
  }
  else {
    // Reduce the size and hide the label of the search input field.
    $administration_search_field['search']['#title_display'] = 'invisible';
    $administration_search_field['search']['#size'] = 30;
  // The search field is always displayed in a specific tray under the 'Search'
  // tab menu item. This tab is hidden or shown with CSS styles depending on the
  // toolbar width breakpoint and the 'display_menu_item' setting.
  $items['administration_search']['tray'] = $administration_search_field;

  // Add the autocomplete search field directly in the toolbar by default, as
  // a toolbar item ('display_menu_item:false').
  if (!$display_menu_item) {
    // The search field is displayed directly in the Toolbar without a tray.
    $items['administration_search'] += [
      '#weight' => 110,
      'tab' => $administration_search_field,
    ];

    // Add toolbar item render array for the mobile search tab that displays the
    // search field in a custom tray, see module's CSS for styling. The display
    // of the mobile search tab is toggled with a CSS breakpoint at 769px.
    $items['administration_mobile_search'] = [
    $items['administration_search_field'] = [
      '#type' => 'toolbar_item',
      'tab' => $administration_search_tab_link,
      // It is important the mobile search tab is displayed *before* the search
      // toolbar item otherwise module's CSS rules will not work.
      // Display the search field directly in the toolbar tab: Modify the search
      // field properties for the toolbar item by merging the new options with
      // the existing ones.
      'tab' => NestedArray::mergeDeep($administration_search_field, [
        'search' => [
          // Reduce the size and hide the label of the search input field.
          '#size' => 30,
          '#title_display' => 'invisible',
          // Set a different HTML ID for the search input field when displayed
          // directly in the toolbar.
          '#id' => AdminToolbarSearchConstants::ADMIN_TOOLBAR_SEARCH_HTML_IDS['search_field_input'],
        ],
      ]),
      // The search field toolbar item should be right before the main search
      // tab so it stays at the same position when the tab is hidden or shown.
      '#weight' => $items['administration_search']['#weight'] - 1,
      // Set the ID of the HTML element wrapping the toolbar item.
      '#wrapper_attributes' => [
        'id' => AdminToolbarSearchConstants::ADMIN_TOOLBAR_SEARCH_HTML_IDS['search_tab_mobile'],
        'id' => AdminToolbarSearchConstants::ADMIN_TOOLBAR_SEARCH_HTML_IDS['search_field_tab'],
      ],
      // Set the cache metadata for the admin search field toolbar item.
      '#cache' => $toolbar_search_item_cache,
    ];
  }

+42 −46
Original line number Diff line number Diff line
/* CSS styles for the Admin Toolbar Search module. */

/* Styles for the search tab menu link and the mobile search tab. */
#admin-toolbar-search-tab,
#admin-toolbar-mobile-search-tab {
/* Styles for the search tab menu link used for desktop and mobile. */
#admin-toolbar-search-tab {
  /* Add a small magnifier icon before the search tab link. */
  .toolbar-item {
    display: block;
@@ -13,41 +12,31 @@
  }
}

/* Styles for the mobile search tab and its interaction with the search tab. */
#admin-toolbar-mobile-search-tab {
  /* These styles only work if the mobile tab is before the search tab. */
  ~ #admin-toolbar-search-tab {
    /* Show the search tab and hide the search field by default on mobile. */
    display: none;
    /* Set the search field to full width when visible. */
    &.visible {
      display: block;
      width: 100%;
    }
    /* Add padding to the wrapper around the search label and input field. */
    .js-form-item {
      margin: 0;
      padding: 0.3rem 1rem;
    }
    /* Make the search input field take the full width on mobile. */
    #admin-toolbar-search-input {
      width: 100%;
    }
  }
}

/* Styles for the search JS autocomplete input text field. */
#admin-toolbar-search-input {
/* Styles for the search JS autocomplete input text fields. */
#admin-toolbar-search-input,
#admin-toolbar-search-field-input {
  height: 100%;
  min-height: 1.875rem;
  margin: 0;
  padding: 0 0.4rem;
  padding: 0 0.5rem;
  color: #3b3b3b;
  border: 1px solid #ccc;
  border-radius: unset;
  background: #fcfcfa;
  font-size: 0.813rem;
  line-height: 1.75rem;
  font-size: 0.8125rem;
  line-height: 1.625rem;
}

/* Styles for the field tab and its interactions with the search tab. */
#admin-toolbar-search-field-tab {
  /* Hide the field tab and show the search tab by default on mobile. */
  display: none;
  .form-item {
    /* Unset default form item margin to prevent a scrollbar from appearing. */
    margin-block: auto;
    /* Add padding to the wrapper around the search label and input field. */
    padding: 0.3125rem 1rem;
  }
}

/* Styles for the autocomplete suggestion list. Class added through the JS. */
@@ -56,7 +45,7 @@
  vertical scrolling if needed. */
  overflow-y: scroll;
  max-height: 18.75rem;
  font-size: 0.7rem;
  font-size: 0.8125rem;

  /* Styles for each suggestion item in the autocomplete list. */
  .ui-menu-item {
@@ -88,39 +77,46 @@

/* Styles for the search form within the admin toolbar tray. */
#toolbar-item-administration-search-tray {
  /* Add margin to the wrapper around the search label and input field. */
  .js-form-item {
    margin: 0.6rem;
  .form-item {
    /* Unset default form item margin. */
    margin-block: auto;
    /* Add padding to the wrapper around the search label and input field. */
    padding: 0.625rem;
  }
  /* Basic styles for the search label and input field. */
  label {
    display: inline-block;
    margin-right: 0.45rem;
    margin-right: 0.5rem;
    color: #000;
    font-weight: bold;
  }
  input {
    display: inline-block;
    padding: 0.3rem 0.5rem;
    font-size: 0.86rem;
    padding: 0.3125rem 0.5rem;
    font-size: 0.85rem;
  }
}

/* Mobile first media query for screens wider than 769px (desktop, tablets). */
@media only screen and (min-width: 769px) {
  .admin-toolbar-search-autocomplete-list {
    /* Increase font size of autocomplete suggestions for wider screens. */
    font-size: 1rem;
  }
  #admin-toolbar-mobile-search-tab {
    /* Hide the mobile search tab on wider screens by default. */
    display: none;
    /* Display the search field and adjust its styles for wider screens. */
    ~ #admin-toolbar-search-tab {
  /* Display the search field and and hide the search tab for wider screens. */
  #admin-toolbar-search-field-tab {
    display: block;
      /* Reset width to auto for wider screens so it fits in the toolbar. */
      &.visible {
        width: auto;
    /* These styles only work if the search field is before the search tab. */
    ~ #admin-toolbar-search-tab {
      /* Hide the search tab when the field tab is visible on wider screens. */
      display: none;
    }
  }
  /* Add a bit more margin space on the left of the search label in the toolbar
  tray when displayed on wider screens ('display menu item'). */
  #toolbar-item-administration-search-tray {
    label {
      margin-left: 0.45rem;
    }
  }
}
+12 −11
Original line number Diff line number Diff line
@@ -23,14 +23,14 @@
      $(elements).each(function () {
        $self.links = [];

        var $searchTab = $(this).find('#admin-toolbar-search-tab')
        var $searchInput = $searchTab.find('#admin-toolbar-search-input');
        var $searchInputs = $(this).find('#admin-toolbar-search-input, #admin-toolbar-search-field-input');

        if ($searchInput.length === 0) {
        if ($searchInputs.length === 0) {
          return;
        }

        $searchInput.autocomplete({
        $searchInputs.each(function (index, $searchInput) {
          $($searchInput).autocomplete({
            minLength: 2,
            position: { collision : 'fit' },
            source: function (request, response) {
@@ -72,18 +72,19 @@
              .append('<div ><a href="' + item.value + '">' + item.labelRaw + ' <span class="admin-toolbar-search-url">' + item.value + '</span></a></div>')
              .appendTo(ul);
          });

        });
        // Populate the links for search results when the input is pressed.
        $searchInput.focus(function () {
        $searchInputs.focus(function () {
          Drupal.behaviors.adminToolbarSearch.populateLinks($self);
        });

        // Show/hide search input field when mobile tab item is pressed.
        $('#admin-toolbar-mobile-search-tab .toolbar-item', context).click(function (e) {
        // Focus the search input field when the search tab is pressed.
        context.querySelector('#admin-toolbar-search-tab .toolbar-item')
          .addEventListener('click', (e) => {
            e.preventDefault();
          $(this).toggleClass('is-active');
          $searchTab.toggleClass('visible');
          $searchInput.focus();
            const searchTabTray = e.target.nextElementSibling;
            searchTabTray.classList.toggle('is-active');
            searchTabTray.querySelector('#admin-toolbar-search-input').focus();
          });
      });
    },
+32 −11
Original line number Diff line number Diff line
@@ -7,6 +7,10 @@
  /**
   * Implements the Admin Toolbar Search configured keyboard shortcut.
   *
   * Depending on whether the search input field is displayed as a toolbar
   * menu tray or as a standalone input field, the behavior will focus on the
   * appropriate element when the keyboard shortcut is triggered.
   *
   * @type {Drupal~behavior}
   *
   * @prop {Drupal~behaviorAttach} attach
@@ -14,7 +18,7 @@
   *   shortcut combination of keys, currently defaults to: 'Alt + a'.
   */
  Drupal.behaviors.adminToolbarSearchKeyboardShortcut = {
    attach: (context, settings) => {
    attach: (context) => {
      if (context !== document) {
        return;
      }
@@ -26,18 +30,35 @@
        // Shortcut 'Alt + a' will focus on the search form.
        document.addEventListener('keydown', (event) => {
          if (event.altKey && (event.key === 'a' || event.keyCode === 65)) {
            const searchInputField = context.getElementById(
              'admin-toolbar-search-input',
            // Get the search tab which should *always* be loaded.
            const searchTab = context.getElementById(
              'admin-toolbar-search-tab',
            );
            // Get the computed style of the search tab to check if it is
            // visible based on the toolbar width breakpoint.
            const searchTabStyle = window.getComputedStyle(searchTab);
            // Get the search field tab which could be empty ('null') if the
            // setting 'display_menu_item' is enabled.
            const searchFieldTab = context.getElementById(
              'admin-toolbar-search-field-tab',
            );
            // If the search input field is displayed as a toolbar menu tray,
            // toggle the toolbar item so the field could be focused.
            if (settings.adminToolbarSearch.displayMenuItem) {
              searchInputField
                .closest('.toolbar-tab')
                .querySelector('.toolbar-item')
                .click();

            // If the search field tab is loaded and the search tab is not
            // visible, focus on the search input field directly.
            if (searchFieldTab !== null && searchTabStyle.display === 'none') {
              searchFieldTab
                .querySelector('#admin-toolbar-search-field-input')
                .focus();
            } else {
              // If the search input field is displayed as a toolbar menu tray
              // and is not visible, toggle its display to focus its field.
              const searchTabToolbarItem =
                searchTab.querySelector('.toolbar-item');
              if (!searchTabToolbarItem.classList.contains('is-active')) {
                searchTabToolbarItem.click();
              }
              searchTab.querySelector('#admin-toolbar-search-input').focus();
            }
            searchInputField.focus();
            // Don't transmit the keystroke.
            event.preventDefault();
          }
Loading