Skip to content
Snippets Groups Projects
Verified Commit 3ef3cfe2 authored by Théodore Biadala's avatar Théodore Biadala
Browse files

Issue #3484564 by plopesc, finnsky, m4olivei, ckrina, catch, larowlan: Define...

Issue #3484564 by plopesc, finnsky, m4olivei, ckrina, catch, larowlan: Define the 3 areas the Top Bar will provide

(cherry picked from commit dc129a55)
parent a67ab833
No related branches found
No related tags found
28 merge requests!12227Issue #3181946 by jonmcl, mglaman,!12079Issue #3523476 by matthiasm11: Add empty check on operator,!12024Fix: DocBlock comment for return value of Drupal\Core\Database\Connection::transactionManager(),!11974Draft: Issue #3495165 by catch, joeyroth, berdir, texas-bronius: Better warning...,!11934Issue #3520997: DefaultLazyPluginCollection unnecessarily instantiates plugins when sorting collection,!11887Issue #3520065: The migrate Row class API is incomplete,!11636Draft: Issue #3515643 by macsim: fieldNameExists method is inconsistent,!11515Issue #3480419 by mondrake, smustgrave, catch: Method...,!11380Issue #3490698 by catch, spokje: Bump MINIMUM_STABILITY back to 'stable' when...,!11281Use Drupal Core Leadership terminology in MAINTAINERS.txt,!11239Issue #3507548: Allow workspace changes listing to show all items, without a pager,!11238Fix issue #3051797,!11213Issue #3506743 by tomislav.matokovic: Increasing the color contrast for the navigation block title against the background of the navigation sidebar to at least 4.5:1,!11147Draft: Try to avoid manually setting required cache contexts,!11108Issue #3490298 by nicxvan: Profiles can be missed in OOP hooks,!11093Drupal on MongoDB 11.1.x,!11017Issue #3502540: Add date filter for moderated content.,!11009Issue #3486972 migrate feed icon,!10999Cleaning up Taxonomy hooks and updating baseline.,!10977Issue #3501457: Fix path used in a A11y Test Admin,!10881Issue #3489329 by mfb, casey: symfony/http-foundation commit 32310ff breaks PathValidator,!10570Issue #3494197: Convert Twig engine hooks,!10567Issue #3494154: Index is not added if entity doesn't support revisions,!10548Revert "Issue #3478621 by catch, longwave, nicxvan: Add filecache to OOP hook attribute parsing",!10404Margin has been added,!10391Issue #3485117 by nexusnovaz, godotislate, nicxvan: Fix return type on...,!10388Issue #3485117 by nexusnovaz, godotislate, nicxvan: Fix return type on...,!10376Issue #3485117 by nexusnovaz, godotislate, nicxvan: Fix return type on...
Pipeline #350782 canceled
Pipeline: drupal

#350783

    Showing
    with 430 additions and 66 deletions
    ......@@ -63,7 +63,7 @@ body {
    z-index: var(--admin-toolbar-z-index);
    display: flex;
    flex-direction: column;
    height: 100vh;
    block-size: 100vh;
    transform: translateX(-100%);
    background-color: var(--admin-toolbar-color-white);
    font-family: var(--admin-toolbar-font-family);
    ......@@ -92,7 +92,9 @@ body {
    }
    @media (min-width: 64rem) {
    .admin-toolbar {
    block-size: calc(100vh - var(--drupal-displace-offset-top, 0px));
    transform: none;
    inset-block-start: var(--drupal-displace-offset-top, 0);
    }
    }
    @media only screen and (max-height: 18.75rem) {
    ......
    ......@@ -63,7 +63,7 @@ body {
    z-index: var(--admin-toolbar-z-index);
    display: flex;
    flex-direction: column;
    height: 100vh;
    block-size: 100vh;
    transform: translateX(-100%);
    background-color: var(--admin-toolbar-color-white);
    font-family: var(--admin-toolbar-font-family);
    ......@@ -93,7 +93,9 @@ body {
    }
    @media (--admin-toolbar-desktop) {
    block-size: calc(100vh - var(--drupal-displace-offset-top, 0px));
    transform: none;
    inset-block-start: var(--drupal-displace-offset-top, 0);
    }
    @media only screen and (max-height: 300px) {
    ......
    ......@@ -44,6 +44,7 @@
    [data-toolbar-popover-wrapper] {
    --admin-toolbar-z-index-popover: var(--drupal-admin-z-index-popover, -1);
    block-size: calc(100vh - var(--drupal-displace-offset-top, 0px));
    padding-block-start: var(--admin-toolbar-space-16);
    transform: translateX(0);
    box-shadow:
    ......@@ -51,7 +52,7 @@
    0 0 8px rgba(0, 0, 0, 0.04),
    0 0 40px rgba(0, 0, 0, 0.06);
    inline-size: var(--admin-toolbar-popover-width);
    inset-block-start: 0;
    inset-block-start: var(--drupal-displace-offset-top, 0);
    inset-inline-start: 1px;
    }
    }
    ......
    ......@@ -43,6 +43,7 @@
    @media (--admin-toolbar-desktop) {
    --admin-toolbar-z-index-popover: var(--drupal-admin-z-index-popover, -1);
    block-size: calc(100vh - var(--drupal-displace-offset-top, 0px));
    padding-block-start: var(--admin-toolbar-space-16);
    transform: translateX(0);
    box-shadow:
    ......@@ -50,7 +51,7 @@
    0 0 8px rgba(0, 0, 0, 0.04),
    0 0 40px rgba(0, 0, 0, 0.06);
    inline-size: var(--admin-toolbar-popover-width);
    inset-block-start: 0;
    inset-block-start: var(--drupal-displace-offset-top, 0);
    inset-inline-start: 1px;
    }
    }
    ......
    ......@@ -22,10 +22,12 @@
    font-variation-settings: "wght" 600;
    line-height: var(--admin-toolbar-line-height-info-sm);
    }
    @media (min-width: 64rem) {
    [data-drupal-tooltip]:hover + .toolbar-tooltip,
    [data-drupal-tooltip]:focus + .toolbar-tooltip {
    display: block;
    }
    }
    [data-admin-toolbar="expanded"] [data-drupal-tooltip]:hover + .toolbar-block__title-tooltip {
    display: none;
    }
    ......@@ -4,6 +4,8 @@
    * Tooltip styles.
    */
    @import "../base/media-queries.pcss.css";
    .toolbar-tooltip {
    position: fixed;
    z-index: var(--admin-toolbar-z-index-tooltip);
    ......@@ -18,10 +20,12 @@
    line-height: var(--admin-toolbar-line-height-info-sm);
    }
    @media (--admin-toolbar-desktop) {
    [data-drupal-tooltip]:hover + .toolbar-tooltip,
    [data-drupal-tooltip]:focus + .toolbar-tooltip {
    display: block;
    }
    }
    [data-admin-toolbar="expanded"] [data-drupal-tooltip]:hover + .toolbar-block__title-tooltip {
    display: none;
    ......
    ......@@ -11,17 +11,18 @@
    position: relative;
    z-index: var(--admin-toolbar-z-index-top-bar);
    display: flex;
    padding-inline: var(--admin-toolbar-space-4);
    padding-block: var(--admin-toolbar-space-4);
    display: none;
    background-color: white;
    box-shadow: 0 0 8px 0 var(--admin-toolbar-color-shadow-15);
    font-family: var(--admin-toolbar-font-family);
    padding-inline: var(--admin-toolbar-space-4);
    padding-block: var(--admin-toolbar-space-4);
    }
    @media (min-width: 64rem) {
    .top-bar {
    block-size: var(--admin-toolbar-top-bar-height);
    position: fixed;
    inset-block-start: 0;
    inset-block-start: var(--drupal-displace-offset-top, 0);
    inset-inline-start: 0;
    width: 100vw;
    padding-block: var(--admin-toolbar-space-12);
    ......@@ -35,23 +36,22 @@
    padding-inline: calc(var(--drupal-displace-offset-right, var(--admin-toolbar-sidebar-width)) + var(--admin-toolbar-space-32)) var(--admin-toolbar-space-32);
    }
    }
    /* When only one burger button hide top bar on desktop. */
    @media (min-width: 64rem) {
    .top-bar:has(.top-bar__burger:only-child) {
    display: none;
    }
    .top-bar:has(.top-bar__tools:not(:empty), .top-bar__context:not(:empty), .top-bar__actions:not(:empty)) {
    display: block;
    }
    @media (min-width: 64rem) {
    .top-bar:not(:has(.top-bar__burger:only-child)) ~ .dialog-off-canvas-main-canvas {
    .top-bar:has(.top-bar__tools:not(:empty), .top-bar__context:not(:empty), .top-bar__actions:not(:empty)) ~ .dialog-off-canvas-main-canvas {
    margin-block-start: var(--admin-toolbar-top-bar-height);
    }
    }
    .top-bar__burger {
    align-self: start;
    .top-bar__actions {
    display: flex;
    gap: 0.5rem;
    }
    @media (min-width: 64rem) {
    .top-bar__burger {
    display: none;
    .top-bar__actions {
    justify-content: end;
    gap: var(--admin-toolbar-space-4);
    }
    }
    .top-bar__content {
    ......@@ -71,3 +71,13 @@
    gap: var(--admin-toolbar-space-8);
    }
    }
    .top-bar__context {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    justify-content: start;
    }
    .top-bar__tools {
    display: flex;
    gap: 0.5rem;
    }
    ......@@ -8,16 +8,17 @@
    position: relative;
    z-index: var(--admin-toolbar-z-index-top-bar);
    display: flex;
    padding-inline: var(--admin-toolbar-space-4);
    padding-block: var(--admin-toolbar-space-4);
    display: none;
    background-color: white;
    box-shadow: 0 0 8px 0 var(--admin-toolbar-color-shadow-15);
    font-family: var(--admin-toolbar-font-family);
    padding-inline: var(--admin-toolbar-space-4);
    padding-block: var(--admin-toolbar-space-4);
    @media (--admin-toolbar-desktop) {
    block-size: var(--admin-toolbar-top-bar-height);
    position: fixed;
    inset-block-start: 0;
    inset-block-start: var(--drupal-displace-offset-top, 0);
    inset-inline-start: 0;
    width: 100vw;
    padding-block: var(--admin-toolbar-space-12);
    ......@@ -32,23 +33,23 @@
    }
    }
    /* When only one burger button hide top bar on desktop. */
    .top-bar:has(.top-bar__burger:only-child) {
    @media (--admin-toolbar-desktop) {
    display: none;
    }
    }
    .top-bar:has(.top-bar__tools:not(:empty), .top-bar__context:not(:empty), .top-bar__actions:not(:empty)) {
    display: block;
    .top-bar:not(:has(.top-bar__burger:only-child)) ~ .dialog-off-canvas-main-canvas {
    ~ .dialog-off-canvas-main-canvas {
    @media (--admin-toolbar-desktop) {
    margin-block-start: var(--admin-toolbar-top-bar-height);
    }
    }
    }
    .top-bar__actions {
    display: flex;
    gap: 0.5rem;
    .top-bar__burger {
    align-self: start;
    @media (--admin-toolbar-desktop) {
    display: none;
    justify-content: end;
    gap: var(--admin-toolbar-space-4);
    }
    }
    ......@@ -69,3 +70,15 @@
    gap: var(--admin-toolbar-space-8);
    }
    }
    .top-bar__context {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    justify-content: start;
    }
    .top-bar__tools {
    display: flex;
    gap: 0.5rem;
    }
    ......@@ -18,7 +18,7 @@
    #}
    {% set control_bar_attributes = create_attribute() %}
    <div {{ control_bar_attributes.addClass('admin-toolbar-control-bar').setAttribute('data-drupal-admin-styles', '').setAttribute('data-offset-top', '') }}>
    <div {{ control_bar_attributes.addClass('admin-toolbar-control-bar').setAttribute('data-drupal-admin-styles', '') }}>
    <div class="admin-toolbar-control-bar__content">
    {% include 'navigation:toolbar-button' with {
    attributes: create_attribute({'aria-expanded': 'false', 'aria-controls': 'admin-toolbar', 'type': 'button'}),
    ......
    ......@@ -4,6 +4,8 @@
    * @file
    */
    use Drupal\navigation\TopBarRegion;
    /**
    * Implements hook_module_implements_alter().
    */
    ......@@ -18,3 +20,21 @@ function navigation_module_implements_alter(&$implementations, $hook) {
    unset($implementations['layout_builder']);
    }
    }
    /**
    * Prepares variables for navigation top bar template.
    *
    * Default template: top-bar.html.twig
    *
    * @param $variables
    * An associative array containing:
    * - element: An associative array containing the properties and children of
    * the top bar.
    */
    function template_preprocess_top_bar(&$variables): void {
    $element = $variables['element'];
    foreach (TopBarRegion::cases() as $region) {
    $variables[$region->value] = $element[$region->value] ?? NULL;
    }
    }
    ......@@ -14,7 +14,8 @@ services:
    '@file_url_generator',
    '@plugin.manager.layout_builder.section_storage',
    '@request_stack',
    '@extension.list.module'
    '@extension.list.module',
    '@current_user',
    ]
    Drupal\navigation\NavigationRenderer: '@navigation.renderer'
    ......@@ -33,3 +34,8 @@ services:
    class: Drupal\navigation\UserLazyBuilder
    arguments: ['@current_user']
    Drupal\navigation\UserLazyBuilders: '@navigation.user_lazy_builder'
    plugin.manager.top_bar_item:
    class: Drupal\navigation\TopBarItemManager
    parent: default_plugin_manager
    Drupal\navigation\TopBarItemManagerInterface: '@plugin.manager.top_bar_item'
    <?php
    declare(strict_types=1);
    namespace Drupal\navigation\Attribute;
    use Drupal\Component\Plugin\Attribute\Plugin;
    use Drupal\Core\StringTranslation\TranslatableMarkup;
    use Drupal\navigation\TopBarRegion;
    /**
    * The top bar item attribute.
    */
    #[\Attribute(\Attribute::TARGET_CLASS)]
    final class TopBarItem extends Plugin {
    /**
    * Constructs a new TopBarItem instance.
    *
    * @param string $id
    * The top bar item ID.
    * @param \Drupal\navigation\TopBarRegion $region
    * The region where the top bar item belongs to.
    * @param \Drupal\Core\StringTranslation\TranslatableMarkup|null $label
    * (optional) The human-readable name of the top bar item.
    * @param class-string|null $deriver
    * (optional) The deriver class.
    */
    public function __construct(
    public readonly string $id,
    public readonly TopBarRegion $region,
    public readonly ?TranslatableMarkup $label = NULL,
    public readonly ?string $deriver = NULL,
    ) {}
    }
    <?php
    declare(strict_types=1);
    namespace Drupal\navigation\Element;
    use Drupal\Core\Render\Attribute\RenderElement;
    use Drupal\Core\Render\Element\RenderElementBase;
    use Drupal\navigation\TopBarItemManagerInterface;
    use Drupal\navigation\TopBarRegion;
    /**
    * Provides a render element for the default Drupal toolbar.
    */
    #[RenderElement('top_bar')]
    class TopBar extends RenderElementBase {
    /**
    * {@inheritdoc}
    */
    public function getInfo(): array {
    $class = static::class;
    return [
    '#pre_render' => [
    [$class, 'preRenderTopBar'],
    ],
    '#theme' => 'top_bar',
    '#attached' => [
    'library' => [
    'navigation/internal.navigation',
    ],
    ],
    ];
    }
    /**
    * Builds the TopBar as a structured array ready for rendering.
    *
    * Since building the TopBar takes some time, it is done just prior to
    * rendering to ensure that it is built only if it will be displayed.
    *
    * @param array $element
    * A renderable array.
    *
    * @return array
    * A renderable array.
    *
    * @see navigation_page_top()
    */
    public static function preRenderTopBar($element): array {
    $top_bar_item_manager = static::topBarItemManager();
    // Group the items by region.
    foreach (TopBarRegion::cases() as $region) {
    $items = $top_bar_item_manager->getRenderedTopBarItemsByRegion($region);
    $element = array_merge($element, [$region->value => $items]);
    }
    return $element;
    }
    /**
    * Wraps the top bar item manager.
    *
    * @return \Drupal\navigation\TopBarItemManager
    * The top bar item manager.
    */
    protected static function topBarItemManager(): TopBarItemManagerInterface {
    return \Drupal::service(TopBarItemManagerInterface::class);
    }
    }
    ......@@ -76,7 +76,7 @@ public function pageTop(array &$page_top) {
    */
    #[Hook('theme')]
    public function theme($existing, $type, $theme, $path) : array {
    $items['top_bar'] = ['variables' => ['local_tasks' => []]];
    $items['top_bar'] = ['render element' => 'element'];
    $items['top_bar_local_tasks'] = ['variables' => ['local_tasks' => []]];
    $items['top_bar_local_task'] = ['variables' => ['link' => []]];
    $items['big_pipe_interface_preview__navigation_shortcut_lazy_builder_lazyLinks__Shortcuts'] = [
    ......
    ......@@ -18,6 +18,7 @@
    use Drupal\Core\Plugin\Context\Context;
    use Drupal\Core\Plugin\Context\ContextDefinition;
    use Drupal\Core\Routing\RouteMatchInterface;
    use Drupal\Core\Session\AccountInterface;
    use Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface;
    use Symfony\Component\HttpFoundation\RequestStack;
    ......@@ -71,6 +72,7 @@ public function __construct(
    private SectionStorageManagerInterface $sectionStorageManager,
    private RequestStack $requestStack,
    private ModuleExtensionList $moduleExtensionList,
    private AccountInterface $currentUser,
    ) {}
    /**
    ......@@ -170,32 +172,13 @@ public function buildTopBar(array &$page_top): void {
    }
    $page_top['top_bar'] = [
    '#theme' => 'top_bar',
    '#attached' => [
    'library' => [
    'navigation/internal.navigation',
    ],
    ],
    '#type' => 'top_bar',
    '#access' => $this->currentUser->hasPermission('access navigation'),
    '#cache' => [
    'contexts' => [
    'url.path',
    'user.permissions',
    ],
    'keys' => ['top_bar'],
    'contexts' => ['user.permissions'],
    ],
    ];
    // Local tasks for content entities.
    if ($this->hasLocalTasks()) {
    $local_tasks = $this->getLocalTasks();
    $page_top['top_bar']['#local_tasks'] = [
    '#theme' => 'top_bar_local_tasks',
    '#local_tasks' => $local_tasks['tasks'],
    ];
    assert($local_tasks['cacheability'] instanceof CacheableMetadata);
    CacheableMetadata::createFromRenderArray($page_top['top_bar'])
    ->addCacheableDependency($local_tasks['cacheability'])
    ->applyTo($page_top['top_bar']);
    }
    }
    /**
    ......@@ -227,7 +210,7 @@ public function removeLocalTasks(array &$build, BlockPluginInterface $block): vo
    * @return array
    * Local tasks keyed by route name.
    */
    private function getLocalTasks(): array {
    public function getLocalTasks(): array {
    if (isset($this->localTasks)) {
    return $this->localTasks;
    }
    ......@@ -279,7 +262,7 @@ private function getLocalTasks(): array {
    * @return bool
    * TRUE if there are local tasks available for the top bar, FALSE otherwise.
    */
    private function hasLocalTasks(): bool {
    public function hasLocalTasks(): bool {
    $local_tasks = $this->getLocalTasks();
    return !empty($local_tasks['tasks']);
    }
    ......
    <?php
    declare(strict_types=1);
    namespace Drupal\navigation\Plugin\TopBarItem;
    use Drupal\Core\Cache\CacheableMetadata;
    use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
    use Drupal\Core\StringTranslation\TranslatableMarkup;
    use Drupal\navigation\Attribute\TopBarItem;
    use Drupal\navigation\NavigationRenderer;
    use Drupal\navigation\TopBarItemBase;
    use Drupal\navigation\TopBarRegion;
    use Symfony\Component\DependencyInjection\ContainerInterface;
    /**
    * Provides the Page Actions basic top bar item.
    */
    #[TopBarItem(
    id: 'page_actions',
    region: TopBarRegion::Actions,
    label: new TranslatableMarkup('Page Actions'),
    )]
    final class PageActions extends TopBarItemBase implements ContainerFactoryPluginInterface {
    public function __construct(array $configuration, $plugin_id, $plugin_definition, private NavigationRenderer $navigationRenderer) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    }
    public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
    return new static(
    $configuration,
    $plugin_id,
    $plugin_definition,
    $container->get(NavigationRenderer::class)
    );
    }
    /**
    * {@inheritdoc}
    */
    public function build(): array {
    $build = [];
    // Local tasks for content entities.
    if ($this->navigationRenderer->hasLocalTasks()) {
    $local_tasks = $this->navigationRenderer->getLocalTasks();
    $build = [
    '#theme' => 'top_bar_local_tasks',
    '#local_tasks' => $local_tasks['tasks'],
    ];
    assert($local_tasks['cacheability'] instanceof CacheableMetadata);
    $local_tasks['cacheability']->applyTo($build);
    }
    return $build;
    }
    }
    <?php
    declare(strict_types=1);
    namespace Drupal\navigation;
    use Drupal\Component\Plugin\PluginBase;
    /**
    * Base class for top bar item plugins.
    */
    abstract class TopBarItemBase extends PluginBase implements TopBarItemPluginInterface {
    /**
    * {@inheritdoc}
    */
    public function label(): string|\Stringable {
    return $this->pluginDefinition['label'];
    }
    /**
    * {@inheritdoc}
    */
    public function region(): TopBarRegion {
    return $this->pluginDefinition['region'];
    }
    /**
    * {@inheritdoc}
    */
    abstract public function build(): array;
    }
    <?php
    declare(strict_types=1);
    namespace Drupal\navigation;
    use Drupal\Core\Cache\CacheBackendInterface;
    use Drupal\Core\Extension\ModuleHandlerInterface;
    use Drupal\Core\Plugin\DefaultPluginManager;
    use Drupal\navigation\Attribute\TopBarItem;
    /**
    * Top bar item plugin manager.
    */
    final class TopBarItemManager extends DefaultPluginManager implements TopBarItemManagerInterface {
    /**
    * Constructs the object.
    */
    public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
    parent::__construct('Plugin/TopBarItem', $namespaces, $module_handler, TopBarItemPluginInterface::class, TopBarItem::class);
    $this->alterInfo('top_bar_item');
    $this->setCacheBackend($cache_backend, 'top_bar_item_plugins');
    }
    /**
    * {@inheritdoc}
    */
    public function getDefinitionsByRegion(TopBarRegion $region): array {
    return array_filter($this->getDefinitions(), fn (array $definition) => $definition['region'] === $region);
    }
    /**
    * {@inheritdoc}
    */
    public function getRenderedTopBarItemsByRegion(TopBarRegion $region): array {
    $instances = [];
    foreach ($this->getDefinitionsByRegion($region) as $plugin_id => $plugin_definition) {
    $instances[$plugin_id] = $this->createInstance($plugin_id)->build();
    }
    return $instances;
    }
    }
    <?php
    declare(strict_types=1);
    namespace Drupal\navigation;
    /**
    * Top bar item plugin manager.
    */
    interface TopBarItemManagerInterface {
    /**
    * Gets the top bar item plugins by region.
    *
    * @param \Drupal\navigation\TopBarRegion $region
    * The region.
    *
    * @return array
    * A list of top bar item plugin definitions.
    */
    public function getDefinitionsByRegion(TopBarRegion $region): array;
    /**
    * Gets the top bar items prepared as render array.
    *
    * @param \Drupal\navigation\TopBarRegion $region
    * The region.
    *
    * @return array
    * An array of rendered top bar items, keyed by the plugin ID and sorted by
    * weight.
    */
    public function getRenderedTopBarItemsByRegion(TopBarRegion $region): array;
    }
    <?php
    declare(strict_types=1);
    namespace Drupal\navigation;
    /**
    * Interface for top bar plugins.
    */
    interface TopBarItemPluginInterface {
    /**
    * Returns the translated plugin label.
    *
    * @return string|\Stringable
    * The translated plugin label.
    */
    public function label(): string|\Stringable;
    /**
    * Returns the plugin region.
    *
    * @return \Drupal\navigation\TopBarRegion
    * The plugin region.
    */
    public function region(): TopBarRegion;
    /**
    * Builds and returns the renderable array for this top bar item plugin.
    *
    * If a top bar item should not be rendered because it has no content, then
    * this method must also ensure to return no content: it must then only return
    * an empty array, or an empty array with #cache set (with cacheability
    * metadata indicating the circumstances for it being empty).
    *
    * @return array
    * A renderable array representing the content of the top bar item.
    */
    public function build();
    }
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Please register or to comment