Skip to content
Snippets Groups Projects
Verified Commit dc129a55 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
parent f99f1989
No related branches found
No related tags found
11 merge requests!11197Issue #3506427 by eduardo morales alberti: Remove responsive_image.ajax from hook,!11131[10.4.x-only-DO-NOT-MERGE]: Issue ##2842525 Ajax attached to Views exposed filter form does not trigger callbacks,!10786Issue #3490579 by shalini_jha, mstrelan: Add void return to all views...,!3878Removed unused condition head title for views,!3818Issue #2140179: $entity->original gets stale between updates,!3154Fixes #2987987 - CSRF token validation broken on routes with optional parameters.,!2964Issue #2865710 : Dependencies from only one instance of a widget are used in display modes,!2062Issue #3246454: Add weekly granularity to views date sort,!10223132456: Fix issue where views instances are emptied before an ajax request is complete,!617Issue #3043725: Provide a Entity Handler for user cancelation,!579Issue #2230909: Simple decimals fail to pass validation
Pipeline #350740 passed with warnings
Pipeline: drupal

#350776

    Pipeline: drupal

    #350759

      Pipeline: drupal

      #350748

        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,9 +22,11 @@
        font-variation-settings: "wght" 600;
        line-height: var(--admin-toolbar-line-height-info-sm);
        }
        [data-drupal-tooltip]:hover + .toolbar-tooltip,
        [data-drupal-tooltip]:focus + .toolbar-tooltip {
        display: block;
        @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,9 +20,11 @@
        line-height: var(--admin-toolbar-line-height-info-sm);
        }
        [data-drupal-tooltip]:hover + .toolbar-tooltip,
        [data-drupal-tooltip]:focus + .toolbar-tooltip {
        display: block;
        @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 {
        ......
        ......@@ -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 {
        @media (--admin-toolbar-desktop) {
        margin-block-start: var(--admin-toolbar-top-bar-height);
        ~ .dialog-off-canvas-main-canvas {
        @media (--admin-toolbar-desktop) {
        margin-block-start: var(--admin-toolbar-top-bar-height);
        }
        }
        }
        .top-bar__burger {
        align-self: start;
        .top-bar__actions {
        display: flex;
        gap: 0.5rem;
        @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.
        Finish editing this message first!
        Please register or to comment