Verified Commit 3b8df679 authored by Lauri Timmanee's avatar Lauri Timmanee
Browse files

Issue #3355381 by catch, drewcking, stewest, lauriii, bnjmnm, lmoeni, fngatia,...

Issue #3355381 by catch, drewcking, stewest, lauriii, bnjmnm, lmoeni, fngatia, kevinquillen, Danny Englander, Ambient.Impact, gapple, manikandank03, lucasvm: Investigate better ways to add anti-flicker JS
parent 857aadc1
Loading
Loading
Loading
Loading
+72 −0
Original line number Diff line number Diff line
/**
 * @file
 * Prevents flicker of the toolbar on page load.
 */

(() => {
  const toolbarState = sessionStorage.getItem('Drupal.toolbar.toolbarState')
    ? JSON.parse(sessionStorage.getItem('Drupal.toolbar.toolbarState'))
    : false;
  // These are classes that toolbar typically adds to <body>, but this code
  // executes before the first paint, when <body> is not yet present. The
  // classes are added to <html> so styling immediately reflects the current
  // toolbar state. The classes are removed after the toolbar completes
  // initialization.
  const classesToAdd = ['toolbar-loading', 'toolbar-anti-flicker'];
  if (toolbarState) {
    const {
      orientation,
      hasActiveTab,
      isFixed,
      activeTray,
      activeTabId,
      isOriented,
      userButtonMinWidth,
    } = toolbarState;

    classesToAdd.push(
      orientation ? `toolbar-${orientation}` : 'toolbar-horizontal',
    );
    if (hasActiveTab !== false) {
      classesToAdd.push('toolbar-tray-open');
    }
    if (isFixed) {
      classesToAdd.push('toolbar-fixed');
    }
    if (isOriented) {
      classesToAdd.push('toolbar-oriented');
    }

    if (activeTray) {
      // These styles are added so the active tab/tray styles are present
      // immediately instead of "flickering" on as the toolbar initializes. In
      // instances where a tray is lazy loaded, these styles facilitate the
      // lazy loaded tray appearing gracefully and without reflow.
      const styleContent = `
      .toolbar-loading #${activeTabId} {
        background-image: linear-gradient(rgba(255, 255, 255, 0.25) 20%, transparent 200%);
      }
      .toolbar-loading #${activeTabId}-tray {
        display: block; box-shadow: -1px 0 5px 2px rgb(0 0 0 / 33%);
        border-right: 1px solid #aaa; background-color: #f5f5f5;
        z-index: 0;
      }
      .toolbar-loading.toolbar-vertical.toolbar-tray-open #${activeTabId}-tray {
        width: 15rem; height: 100vh;
      }
     .toolbar-loading.toolbar-horizontal :not(#${activeTray}) > .toolbar-lining {opacity: 0}`;

      const style = document.createElement('style');
      style.textContent = styleContent;
      style.setAttribute('data-toolbar-anti-flicker-loading', true);
      document.querySelector('head').appendChild(style);
      if (userButtonMinWidth) {
        const userButtonStyle = document.createElement('style');
        userButtonStyle.textContent = `
        #toolbar-item-user {min-width: ${userButtonMinWidth}.px;}`;
        document.querySelector('head').appendChild(userButtonStyle);
      }
    }
  }
  document.querySelector('html').classList.add(...classesToAdd);
})();
+7 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ toolbar:
    - core/once
    - core/drupal.displace
    - toolbar/toolbar.menu
    - toolbar/toolbar.anti-flicker

toolbar.menu:
  version: VERSION
@@ -50,3 +51,9 @@ toolbar.escapeAdmin:
    - core/drupal
    - core/drupalSettings
    - core/once
toolbar.anti-flicker:
  # Block the page from being loaded until anti-flicker is initialized.
  version: VERSION
  header: true
  js:
    js/toolbar.anti-flicker.js: {}
+0 −96
Original line number Diff line number Diff line
@@ -7,7 +7,6 @@

use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\Markup;
use Drupal\Core\Render\RenderContext;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Template\Attribute;
@@ -49,101 +48,6 @@ function toolbar_theme($existing, $type, $theme, $path) {
  return $items;
}

/**
 * Implements hook_page_attachments().
 */
function toolbar_page_attachments(array &$page) {
  // This JavaScript code provides temporary styles while the toolbar loads, so
  // it better visually resembles the appearance it will have once fully loaded.
  // @todo investigate potential alternatives to this approach in
  //   https://www.drupal.org/i/3355381
  $anti_flicker_js = <<<JS
(function() {
  const toolbarState = sessionStorage.getItem('Drupal.toolbar.toolbarState')
    ? JSON.parse(sessionStorage.getItem('Drupal.toolbar.toolbarState'))
    : false;
  // These are classes that toolbar typically adds to <body>, but this code
  // executes before the first paint, when <body> is not yet present. The
  // classes are added to <html> so styling immediately reflects the current
  // toolbar state. The classes are removed after the toolbar completes
  // initialization.
  const classesToAdd = ['toolbar-loading', 'toolbar-anti-flicker'];
  if (toolbarState) {
    const {
      orientation,
      hasActiveTab,
      isFixed,
      activeTray,
      activeTabId,
      isOriented,
      userButtonMinWidth
    } = toolbarState;

    classesToAdd.push(
      orientation ? `toolbar-` + orientation + `` : 'toolbar-horizontal',
    );
    if (hasActiveTab !== false) {
      classesToAdd.push('toolbar-tray-open');
    }
    if (isFixed) {
      classesToAdd.push('toolbar-fixed');
    }
    if (isOriented) {
      classesToAdd.push('toolbar-oriented');
    }

    if (activeTray) {
      // These styles are added so the active tab/tray styles are present
      // immediately instead of "flickering" on as the toolbar initializes. In
      // instances where a tray is lazy loaded, these styles facilitate the
      // lazy loaded tray appearing gracefully and without reflow.
      const styleContent = `
      .toolbar-loading #` + activeTabId + ` {
        background-image: linear-gradient(rgba(255, 255, 255, 0.25) 20%, transparent 200%);
      }
      .toolbar-loading #` + activeTabId + `-tray {
        display: block; box-shadow: -1px 0 5px 2px rgb(0 0 0 / 33%);
        border-right: 1px solid #aaa; background-color: #f5f5f5;
        z-index: 0;
      }
      .toolbar-loading.toolbar-vertical.toolbar-tray-open #` + activeTabId + `-tray {
        width: 15rem; height: 100vh;
      }
     .toolbar-loading.toolbar-horizontal :not(#` + activeTray + `) > .toolbar-lining {opacity: 0}`;

      const style = document.createElement('style');
      style.textContent = styleContent;
      style.setAttribute('data-toolbar-anti-flicker-loading', true);
      document.querySelector('head').appendChild(style);

      if (userButtonMinWidth) {
        const userButtonStyle = document.createElement('style');
        userButtonStyle.textContent = `#toolbar-item-user {min-width: ` + userButtonMinWidth +`px;}`
        document.querySelector('head').appendChild(userButtonStyle);
      }
    }
  }
  document.querySelector('html').classList.add(...classesToAdd);
})();
JS;

  // The anti flicker javascript is added as an inline tag so it is executed
  // as early as possible. This enables it to add classes that prevent
  // flickering before the first paint.
  $page['#attached']['html_head'][] = [
    [
      '#tag' => 'script',
      '#attributes' => [
        'type' => 'text/javascript',
        'data-toolbar-anti-flicker-loading' => TRUE,
      ],
      // Process through Markup to prevent character escaping.
      '#value' => Markup::create($anti_flicker_js),
    ],
    'anti_flicker_js',
  ];
}

/**
 * Implements hook_page_top().
 *
+1 −1
Original line number Diff line number Diff line
@@ -35,7 +35,7 @@ public function testFrontPagePerformance(): void {
    $this->drupalGet('<front>');
    $this->assertSession()->pageTextContains('Umami');
    $this->assertSame(2, $this->stylesheetCount);
    $this->assertSame(1, $this->scriptCount);
    $this->assertSame(2, $this->scriptCount);
  }

}