From 488cc36a546c1d070c734a8b11019919a4d8cc11 Mon Sep 17 00:00:00 2001
From: Alejandro Ortega <a.ortega@1xinternet.de>
Date: Wed, 16 Apr 2025 14:48:49 +0200
Subject: [PATCH 01/15] #3519238: add checkbox to secondary toolbar

---
 frontend_editing.services.yml     |  4 ++
 src/Hook/FrontendEditingHooks.php | 58 +++++++++++++++++++++++
 src/ToolbarItem.php               | 78 +++++++++++++++++++++++++++++++
 3 files changed, 140 insertions(+)
 create mode 100644 src/Hook/FrontendEditingHooks.php
 create mode 100644 src/ToolbarItem.php

diff --git a/frontend_editing.services.yml b/frontend_editing.services.yml
index 696c3d4..2a6c136 100644
--- a/frontend_editing.services.yml
+++ b/frontend_editing.services.yml
@@ -9,3 +9,7 @@ services:
   frontend_editing.form_builder:
     class: Drupal\frontend_editing\FrontendEditingFormBuilder
     arguments: ['@entity_type.manager', '@entity.form_builder', '@form_builder']
+  frontend_editing.toolbar_item:
+    class: Drupal\frontend_editing\ToolbarItem
+    arguments: [ '@plugin.manager.element_info' ]
+  Drupal\frontend_editing\ToolbarItem: '@frontend_editing.toolbar_item'
diff --git a/src/Hook/FrontendEditingHooks.php b/src/Hook/FrontendEditingHooks.php
new file mode 100644
index 0000000..2ca751e
--- /dev/null
+++ b/src/Hook/FrontendEditingHooks.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace Drupal\frontend_editing\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\frontend_editing\RenderCallbacks;
+use Drupal\frontend_editing\ToolbarItem;
+
+/**
+ * Hook implementations for frontend_editing.
+ */
+class FrontendEditingHooks {
+
+  /**
+   * Implements hook_toolbar().
+   */
+  #[Hook('toolbar')]
+  public function toolbar() {
+    if (!\Drupal::currentUser()->hasPermission('access frontend editing')) {
+      return ['#cache' => ['contexts' => ['user.permissions']]];
+    }
+    $items['toggle'] = [
+      '#type' => 'toolbar_item',
+      'tab' => [
+        '#lazy_builder' => [
+          'frontend_editing.toolbar_item:renderToggle',
+          [],
+        ],
+        '#create_placeholder' => TRUE,
+        '#cache' => [
+          'tags' => [
+            'frontend_editing:toggle',
+          ],
+        ],
+      ],
+      '#wrapper_attributes' => [
+        'class' => [
+          'toggle-toolbar-tab',
+        ],
+      ],
+      '#cache' => [
+        'contexts' => [
+          'user.permissions',
+        ],
+      ],
+      '#weight' => -20,
+    ];
+    // \Drupal\toolbar\Element\ToolbarItem::preRenderToolbarItem adds an
+    // #attributes property to each toolbar item's tab child automatically.
+    // Lazy builders don't support an #attributes property so we need to
+    // add another render callback to remove the #attributes property. We start by
+    // adding the defaults, and then we append our own pre render callback.
+    $items['toggle'] += \Drupal::service('plugin.manager.element_info')->getInfo('toolbar_item');
+    $items['toggle']['#pre_render'][] = [ToolbarItem::class, 'removeTabAttributes'];
+    return $items;
+  }
+
+}
diff --git a/src/ToolbarItem.php b/src/ToolbarItem.php
new file mode 100644
index 0000000..810a7b6
--- /dev/null
+++ b/src/ToolbarItem.php
@@ -0,0 +1,78 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\frontend_editing;
+
+use Drupal\Component\Serialization\Json;
+use Drupal\Component\Utility\Html;
+use Drupal\Core\Render\ElementInfoManagerInterface;
+use Drupal\Core\Security\TrustedCallbackInterface;
+use Drupal\Core\Url;
+
+/**
+ * Defines a class for lazy building render arrays.
+ *
+ * @internal
+ */
+final class ToolbarItem implements TrustedCallbackInterface {
+
+  /**
+   * Constructs LazyBuilders object.
+   *
+   * @param \Drupal\Core\Render\ElementInfoManagerInterface $elementInfo
+   *   Element info.
+   */
+  public function __construct(
+    protected ElementInfoManagerInterface $elementInfo,
+  ) {
+  }
+
+  /**
+   * Render announcements.
+   *
+   * @return array
+   *   Render array.
+   */
+  public function renderToggle(): array {
+    $build = [
+      '#type' => 'checkbox',
+      '#cache' => [
+        'context' => ['user.permissions'],
+      ],
+      '#title' => t('Frontend Editing'),
+      '#id' => Html::getId('toolbar-item-toggle'),
+      '#attributes' => [
+        'title' => t('Toggle'),
+        // 'class' => [
+        // ],
+      ],
+      // '#attached' => [
+      //   'library' => [
+      //     'frontend_editing/drupal.frontend_editing.toolbar',
+      //   ],
+      // ],
+    ];
+
+    // The renderer has already added element defaults by the time the lazy
+    // builder is run.
+    // @see https://www.drupal.org/project/drupal/issues/2609250
+    $build += $this->elementInfo->getInfo('checkbox');
+    return $build;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function trustedCallbacks(): array {
+    return ['renderToggle', 'removeTabAttributes'];
+  }
+
+  /**
+   * Render callback.
+   */
+  public static function removeTabAttributes(array $element): array {
+    unset($element['tab']['#attributes']);
+    return $element;
+  }
+}
-- 
GitLab


From 1e65494c91a90142d8da7f869ef254a0280a814c Mon Sep 17 00:00:00 2001
From: Alejandro Ortega <a.ortega@1xinternet.de>
Date: Wed, 16 Apr 2025 17:49:28 +0200
Subject: [PATCH 02/15] #3519238: style the toolbar item

---
 css/ui_toggle.css                 | 42 +++++++++++++++++++++++++++++++
 src/Hook/FrontendEditingHooks.php |  2 +-
 src/ToolbarItem.php               | 26 ++++++++++---------
 3 files changed, 57 insertions(+), 13 deletions(-)

diff --git a/css/ui_toggle.css b/css/ui_toggle.css
index 687609d..f9ef914 100644
--- a/css/ui_toggle.css
+++ b/css/ui_toggle.css
@@ -32,6 +32,30 @@
   text-align: right;
 }
 
+.frontend-editing-toolbar-toggle {
+  background: rgba(var(--fe-editing-primary-color), 0.6);
+  padding: 0.5rem 0.75rem;
+  text-transform: uppercase;
+  color: white;
+  display: block;
+  font-weight: 500;
+  border-radius: 999px;
+  box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px;
+  text-align: right;
+  height: 2rem;
+  padding-left: 2.5rem;
+  margin-right: 1rem;
+}
+
+.frontend-editing-toolbar-toggle a {
+  color: white;
+}
+
+  /* Overrides undesired styles from gin. */
+.gin-secondary-toolbar .toolbar-secondary .toolbar-bar .toolbar-tab.frontend-editing-toolbar-toggle:hover {
+  border-radius: 999px;
+}
+
 .frontend-editing-toggle a::before {
   content: ' ';
   position: absolute;
@@ -50,6 +74,24 @@
   border-radius: 999px;
 }
 
+.frontend-editing-toolbar-toggle a::before {
+  content: ' ';
+  position: absolute;
+  top: 0;
+  left: 0.5rem;
+  bottom: 0;
+  width: 1.5rem;
+  height: 1.5rem;
+  margin-bottom: auto;
+  margin-top: auto;
+  background-color: #fff;
+  background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzIzMjIyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTkgMy4xNzFhMS44MjkgMS44MjkgMCAwIDAtMS4yOTMuNTM2TDQuMzk1IDE3LjAxOWwtLjk3IDMuNTU2IDMuNTU2LS45N0wyMC4yOTMgNi4yOTNBMS44MjkgMS44MjkgMCAwIDAgMTkgMy4xN1ptLTEuNDY1LTEuNzA4YTMuODI5IDMuODI5IDAgMCAxIDQuMTcyIDYuMjQ0bC0xMy41IDEzLjVhMSAxIDAgMCAxLS40NDQuMjU4bC01LjUgMS41YTEgMSAwIDAgMS0xLjIyOC0xLjIyOGwxLjUtNS41YTEgMSAwIDAgMSAuMjU4LS40NDRsMTMuNS0xMy41YTMuODI5IDMuODI5IDAgMCAxIDEuMjQyLS44M1oiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==);
+  background-size: 14px;
+  background-repeat: no-repeat;
+  background-position: center;
+  border-radius: 100%;
+}
+
 .frontend-editing-toggle a.frontend-editing--enabled {
   background: rgb(var(--fe-editing-primary-color));
   text-align: left;
diff --git a/src/Hook/FrontendEditingHooks.php b/src/Hook/FrontendEditingHooks.php
index 2ca751e..0ce23de 100644
--- a/src/Hook/FrontendEditingHooks.php
+++ b/src/Hook/FrontendEditingHooks.php
@@ -35,7 +35,7 @@ class FrontendEditingHooks {
       ],
       '#wrapper_attributes' => [
         'class' => [
-          'toggle-toolbar-tab',
+          'frontend-editing-toolbar-toggle',
         ],
       ],
       '#cache' => [
diff --git a/src/ToolbarItem.php b/src/ToolbarItem.php
index 810a7b6..685c721 100644
--- a/src/ToolbarItem.php
+++ b/src/ToolbarItem.php
@@ -4,7 +4,6 @@ declare(strict_types=1);
 
 namespace Drupal\frontend_editing;
 
-use Drupal\Component\Serialization\Json;
 use Drupal\Component\Utility\Html;
 use Drupal\Core\Render\ElementInfoManagerInterface;
 use Drupal\Core\Security\TrustedCallbackInterface;
@@ -35,29 +34,32 @@ final class ToolbarItem implements TrustedCallbackInterface {
    *   Render array.
    */
   public function renderToggle(): array {
+    $toggle_state = \Drupal::service('user.data')
+      ->get('frontend_editing', \Drupal::currentUser()->id(), 'enabled');
+
     $build = [
-      '#type' => 'checkbox',
+      '#type' => 'link',
+      '#url' => Url::fromRoute('frontend_editing.toggle'),
       '#cache' => [
         'context' => ['user.permissions'],
       ],
-      '#title' => t('Frontend Editing'),
+      '#title' => $toggle_state ? t('On') : t('Off'),
       '#id' => Html::getId('toolbar-item-toggle'),
       '#attributes' => [
-        'title' => t('Toggle'),
-        // 'class' => [
-        // ],
+        'title' => 'Enable frontend editing actions',
+        'class' => [
+          'use-ajax',
+          'frontend-editing-toggle-link',
+        ],
+        'data-once' => "ajax",
+        'data-toggle-state' => TRUE,
       ],
-      // '#attached' => [
-      //   'library' => [
-      //     'frontend_editing/drupal.frontend_editing.toolbar',
-      //   ],
-      // ],
     ];
 
     // The renderer has already added element defaults by the time the lazy
     // builder is run.
     // @see https://www.drupal.org/project/drupal/issues/2609250
-    $build += $this->elementInfo->getInfo('checkbox');
+    $build += $this->elementInfo->getInfo('link');
     return $build;
   }
 
-- 
GitLab


From 9776c01e45702c0a74b77835e260c1cc23a7f204 Mon Sep 17 00:00:00 2001
From: Alejandro Ortega <a.ortega@1xinternet.de>
Date: Thu, 17 Apr 2025 13:38:20 +0200
Subject: [PATCH 03/15] #3519238: add ajax to the tab and style it

---
 css/ui_toggle.css                            | 35 +++++++++++++++++---
 src/Controller/FrontendEditingController.php |  9 +++++
 src/ToolbarItem.php                          | 14 +++++---
 3 files changed, 49 insertions(+), 9 deletions(-)

diff --git a/css/ui_toggle.css b/css/ui_toggle.css
index f9ef914..0c297e6 100644
--- a/css/ui_toggle.css
+++ b/css/ui_toggle.css
@@ -42,9 +42,9 @@
   border-radius: 999px;
   box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px;
   text-align: right;
-  height: 2rem;
-  padding-left: 2.5rem;
+  height: 1.9rem;
   margin-right: 1rem;
+  min-width: 5rem;
 }
 
 .frontend-editing-toolbar-toggle a {
@@ -56,6 +56,25 @@
   border-radius: 999px;
 }
 
+/* Overrides undesired styles from gin. */
+.gin-secondary-toolbar .toolbar-secondary .toolbar-bar .toolbar-tab.frontend-editing-toolbar-toggle:focus-within {
+  border-radius: 999px;
+}
+
+/* Overrides undesired styles from gin. */
+.gin-secondary-toolbar__layout-container a.frontend-editing-toolbar-toggle-link:focus {
+  box-shadow: none;
+}
+
+a.frontend-editing-toolbar-toggle-link, a.frontend-editing-toggle-link, .toolbar-tab a.frontend-editing-toolbar-toggle-link:focus:not(:hover) {
+  text-decoration: none;
+}
+
+.frontend-editing-toolbar-toggle-link:hover, .frontend-editing-toolbar-toggle-link:hover:focus, .frontend-editing-toggle-link:hover {
+  color: white;
+  text-decoration: underline;
+}
+
 .frontend-editing-toggle a::before {
   content: ' ';
   position: absolute;
@@ -85,22 +104,28 @@
   margin-bottom: auto;
   margin-top: auto;
   background-color: #fff;
-  background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzIzMjIyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTkgMy4xNzFhMS44MjkgMS44MjkgMCAwIDAtMS4yOTMuNTM2TDQuMzk1IDE3LjAxOWwtLjk3IDMuNTU2IDMuNTU2LS45N0wyMC4yOTMgNi4yOTNBMS44MjkgMS44MjkgMCAwIDAgMTkgMy4xN1ptLTEuNDY1LTEuNzA4YTMuODI5IDMuODI5IDAgMCAxIDQuMTcyIDYuMjQ0bC0xMy41IDEzLjVhMSAxIDAgMCAxLS40NDQuMjU4bC01LjUgMS41YTEgMSAwIDAgMS0xLjIyOC0xLjIyOGwxLjUtNS41YTEgMSAwIDAgMSAuMjU4LS40NDRsMTMuNS0xMy41YTMuODI5IDMuODI5IDAgMCAxIDEuMjQyLS44M1oiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==);
+  background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzIzMjIyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTkgMy4xNzFhMS44MjkgMS44MjkgMCAwIDAtMS4yOTMuNTM2TDQuMzk1IDE3LjAxOWwtLjk3IDMuNTU2IDMuNTU2LS45N0wyMC4yOTMgNi4yOTNBMS44MjkgMS44MjkgMCAwIDAgMTkgMy4xN1ptLTEuNDY1LTEuNzA4YTMuODI5IDMuODI5IDAgMCAxIDQuMTcyIDYuMjQ0bC0xMy41IDEzLjVhMSAxIDAgMCAxLS40NDQuMjU4bC01LjUgMS41YTEgMSAwIDAgMS0xLjIyOC0xLjIyOGwxLjUtNS41YTEgMSAwIDAgMSAuMjU4LS40NDRsMTMuNS0xMy41YTMuODI5IDMuODI5IDAgMCAxIDEuMjQyLS44M1oiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==");
   background-size: 14px;
   background-repeat: no-repeat;
   background-position: center;
   border-radius: 100%;
 }
 
-.frontend-editing-toggle a.frontend-editing--enabled {
+.frontend-editing-toggle a.frontend-editing--enabled,
+.frontend-editing-toolbar-toggle:has(a.frontend-editing--enabled)
+{
   background: rgb(var(--fe-editing-primary-color));
   text-align: left;
 }
 
-.frontend-editing-toggle a.frontend-editing--enabled::before {
+.frontend-editing-toggle a.frontend-editing--enabled::before{
   left: calc(100% - 1.75rem - 0.5rem);
 }
 
+.frontend-editing-toolbar-toggle a.frontend-editing--enabled::before {
+  left: calc(100% - 1.75rem - 0.25rem);
+}
+
 .frontend-editing-toggle-not-configured {
   box-shadow: 0 0 0 0 rgba(255, 0, 0, 1);
   transform: scale(1);
diff --git a/src/Controller/FrontendEditingController.php b/src/Controller/FrontendEditingController.php
index 6a76076..89acb29 100644
--- a/src/Controller/FrontendEditingController.php
+++ b/src/Controller/FrontendEditingController.php
@@ -122,13 +122,17 @@ class FrontendEditingController extends ControllerBase {
     if ($new_state) {
       $message = $this->t('Frontend editing has been enabled.');
       $response->addCommand(new InvokeCommand('#frontend-editing-toggle-link', 'addClass', ['frontend-editing--enabled']));
+      $response->addCommand(new InvokeCommand('.frontend-editing-toolbar-toggle-link', 'addClass', ['frontend-editing--enabled']));
       $response->addCommand(new InvokeCommand('#frontend-editing-toggle-link', 'text', [$this->t('On')]));
+      $response->addCommand(new InvokeCommand('.frontend-editing-toolbar-toggle-link', 'text', [$this->t('On')]));
       $response->addCommand(new InvokeCommand('body', 'removeClass', ['frontend-editing--hidden']));
     }
     else {
       $message = $this->t('Frontend editing has been disabled.');
       $response->addCommand(new InvokeCommand('#frontend-editing-toggle-link', 'removeClass', ['frontend-editing--enabled']));
+      $response->addCommand(new InvokeCommand('.frontend-editing-toolbar-toggle-link', 'removeClass', ['frontend-editing--enabled']));
       $response->addCommand(new InvokeCommand('#frontend-editing-toggle-link', 'text', [$this->t('Off')]));
+      $response->addCommand(new InvokeCommand('.frontend-editing-toolbar-toggle-link', 'text', [$this->t('Off')]));
       $response->addCommand(new InvokeCommand('body', 'addClass', ['frontend-editing--hidden']));
     }
     $response->addCommand(new MessageCommand($message, NULL, ['type' => 'status']));
@@ -136,7 +140,12 @@ class FrontendEditingController extends ControllerBase {
       'data-toggle-state',
       $new_state,
     ]));
+    $response->addCommand(new InvokeCommand('.frontend-editing-toolbar-toggle-link', 'attr', [
+      'data-toggle-state',
+      $new_state,
+    ]));
     $response->addCommand(new InvokeCommand('#frontend-editing-toggle-link', 'removeClass', ['frontend-editing-toggle-not-configured']));
+    $response->addCommand(new InvokeCommand('.frontend-editing-toolbar-toggle-link', 'removeClass', ['frontend-editing-toggle-not-configured']));
 
     return $response;
   }
diff --git a/src/ToolbarItem.php b/src/ToolbarItem.php
index 685c721..74d8832 100644
--- a/src/ToolbarItem.php
+++ b/src/ToolbarItem.php
@@ -36,6 +36,7 @@ final class ToolbarItem implements TrustedCallbackInterface {
   public function renderToggle(): array {
     $toggle_state = \Drupal::service('user.data')
       ->get('frontend_editing', \Drupal::currentUser()->id(), 'enabled');
+    $active_class = $toggle_state ? 'frontend-editing--enabled' : '';
 
     $build = [
       '#type' => 'link',
@@ -44,15 +45,20 @@ final class ToolbarItem implements TrustedCallbackInterface {
         'context' => ['user.permissions'],
       ],
       '#title' => $toggle_state ? t('On') : t('Off'),
-      '#id' => Html::getId('toolbar-item-toggle'),
+      '#id' => Html::getId('frontend-editing-toolbar-toggle-link'),
       '#attributes' => [
         'title' => 'Enable frontend editing actions',
         'class' => [
           'use-ajax',
-          'frontend-editing-toggle-link',
+          'frontend-editing-toolbar-toggle-link',
+          $active_class,
+        ],
+      ],
+      '#attached' => [
+        'library' => [
+          'core/drupal.ajax',
+          'frontend_editing/ui_toggle',
         ],
-        'data-once' => "ajax",
-        'data-toggle-state' => TRUE,
       ],
     ];
 
-- 
GitLab


From cc08398fce9717b1ddf0f0598fdd1a3e3f865ef7 Mon Sep 17 00:00:00 2001
From: Alejandro Ortega <a.ortega@1xinternet.de>
Date: Thu, 17 Apr 2025 14:12:40 +0200
Subject: [PATCH 04/15] #3519238: mobile styling

---
 css/ui_toggle.css                 | 20 ++++++++++++++++++++
 src/Hook/FrontendEditingHooks.php |  2 +-
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/css/ui_toggle.css b/css/ui_toggle.css
index 0c297e6..d14d50d 100644
--- a/css/ui_toggle.css
+++ b/css/ui_toggle.css
@@ -148,3 +148,23 @@ a.frontend-editing-toolbar-toggle-link, a.frontend-editing-toggle-link, .toolbar
     box-shadow: 0 0 0 0 rgba(255, 0, 0, 0);
   }
 }
+
+@media only screen and (max-width: 600px) {
+  .frontend-editing-toolbar-toggle {
+    margin-top: 0.25rem;
+    margin-right: 0;
+  }
+
+  .frontend-editing-toolbar-toggle a::before {
+    left: auto;
+    right: 2.35rem;
+  }
+
+  .frontend-editing-toolbar-toggle a.frontend-editing--enabled::before {
+    left: calc(100% - 1rem - 0.1rem);
+  }
+
+  a.frontend-editing-toolbar-toggle-link {
+    position: relative;
+  }
+}
diff --git a/src/Hook/FrontendEditingHooks.php b/src/Hook/FrontendEditingHooks.php
index 0ce23de..f02d3ff 100644
--- a/src/Hook/FrontendEditingHooks.php
+++ b/src/Hook/FrontendEditingHooks.php
@@ -43,7 +43,7 @@ class FrontendEditingHooks {
           'user.permissions',
         ],
       ],
-      '#weight' => -20,
+      '#weight' => -11,
     ];
     // \Drupal\toolbar\Element\ToolbarItem::preRenderToolbarItem adds an
     // #attributes property to each toolbar item's tab child automatically.
-- 
GitLab


From 3fdfe0bee79ad1304728ee53269686457ad90c9d Mon Sep 17 00:00:00 2001
From: Alejandro Ortega <a.ortega@1xinternet.de>
Date: Thu, 17 Apr 2025 14:36:45 +0200
Subject: [PATCH 05/15] #3519238: show the tab only if the setting is not
 enabled

---
 src/Hook/FrontendEditingHooks.php | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/Hook/FrontendEditingHooks.php b/src/Hook/FrontendEditingHooks.php
index f02d3ff..3b98dee 100644
--- a/src/Hook/FrontendEditingHooks.php
+++ b/src/Hook/FrontendEditingHooks.php
@@ -19,6 +19,12 @@ class FrontendEditingHooks {
     if (!\Drupal::currentUser()->hasPermission('access frontend editing')) {
       return ['#cache' => ['contexts' => ['user.permissions']]];
     }
+
+    $config = \Drupal::config('frontend_editing.settings');
+    if ($config->get('ui_toggle')) {
+      return [];
+    }
+
     $items['toggle'] = [
       '#type' => 'toolbar_item',
       'tab' => [
-- 
GitLab


From 2cd2332eaacebce27aa5cb8b43bdd6988a82cf7b Mon Sep 17 00:00:00 2001
From: Alejandro Ortega <a.ortega@1xinternet.de>
Date: Thu, 17 Apr 2025 14:48:45 +0200
Subject: [PATCH 06/15] #3519238: run code sniffer

---
 css/ui_toggle.css                            | 16 ++++++++--------
 frontend_editing.module                      |  2 ++
 src/Controller/FrontendEditingController.php |  1 -
 src/Controller/PreviewController.php         |  4 ++--
 src/Form/EntityReferenceAddForm.php          |  2 +-
 src/Hook/FrontendEditingHooks.php            |  1 -
 src/ToolbarItem.php                          |  1 +
 7 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/css/ui_toggle.css b/css/ui_toggle.css
index d14d50d..39367b1 100644
--- a/css/ui_toggle.css
+++ b/css/ui_toggle.css
@@ -51,26 +51,27 @@
   color: white;
 }
 
-  /* Overrides undesired styles from gin. */
 .gin-secondary-toolbar .toolbar-secondary .toolbar-bar .toolbar-tab.frontend-editing-toolbar-toggle:hover {
   border-radius: 999px;
 }
 
-/* Overrides undesired styles from gin. */
 .gin-secondary-toolbar .toolbar-secondary .toolbar-bar .toolbar-tab.frontend-editing-toolbar-toggle:focus-within {
   border-radius: 999px;
 }
 
-/* Overrides undesired styles from gin. */
 .gin-secondary-toolbar__layout-container a.frontend-editing-toolbar-toggle-link:focus {
   box-shadow: none;
 }
 
-a.frontend-editing-toolbar-toggle-link, a.frontend-editing-toggle-link, .toolbar-tab a.frontend-editing-toolbar-toggle-link:focus:not(:hover) {
+a.frontend-editing-toolbar-toggle-link,
+a.frontend-editing-toggle-link,
+.toolbar-tab a.frontend-editing-toolbar-toggle-link:focus:not(:hover) {
   text-decoration: none;
 }
 
-.frontend-editing-toolbar-toggle-link:hover, .frontend-editing-toolbar-toggle-link:hover:focus, .frontend-editing-toggle-link:hover {
+.frontend-editing-toolbar-toggle-link:hover,
+.frontend-editing-toolbar-toggle-link:hover:focus,
+.frontend-editing-toggle-link:hover {
   color: white;
   text-decoration: underline;
 }
@@ -112,13 +113,12 @@ a.frontend-editing-toolbar-toggle-link, a.frontend-editing-toggle-link, .toolbar
 }
 
 .frontend-editing-toggle a.frontend-editing--enabled,
-.frontend-editing-toolbar-toggle:has(a.frontend-editing--enabled)
-{
+.frontend-editing-toolbar-toggle:has(a.frontend-editing--enabled) {
   background: rgb(var(--fe-editing-primary-color));
   text-align: left;
 }
 
-.frontend-editing-toggle a.frontend-editing--enabled::before{
+.frontend-editing-toggle a.frontend-editing--enabled::before {
   left: calc(100% - 1.75rem - 0.5rem);
 }
 
diff --git a/frontend_editing.module b/frontend_editing.module
index 1ee9a3e..52dd156 100644
--- a/frontend_editing.module
+++ b/frontend_editing.module
@@ -274,6 +274,7 @@ function frontend_editing_field_formatter_third_party_settings_form(FormatterInt
   }
   return $element;
 }
+
 /**
  * Check formatter third party settings.
  */
@@ -289,6 +290,7 @@ function frontend_editing_check_third_party_formatter_settings(&$element) {
     }
   }
 }
+
 /**
  * Implements hook_field_formatter_settings_summary_alter().
  */
diff --git a/src/Controller/FrontendEditingController.php b/src/Controller/FrontendEditingController.php
index 89acb29..79030c6 100644
--- a/src/Controller/FrontendEditingController.php
+++ b/src/Controller/FrontendEditingController.php
@@ -11,7 +11,6 @@ use Drupal\Core\Ajax\InvokeCommand;
 use Drupal\Core\Ajax\MessageCommand;
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\Entity\ContentEntityInterface;
-use Drupal\Core\Entity\EntityFormBuilder;
 use Drupal\Core\Entity\EntityRepositoryInterface;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Render\RendererInterface;
diff --git a/src/Controller/PreviewController.php b/src/Controller/PreviewController.php
index 41977a8..02423c4 100644
--- a/src/Controller/PreviewController.php
+++ b/src/Controller/PreviewController.php
@@ -34,7 +34,7 @@ class PreviewController extends PreviewControllerBase {
   /**
    * {@inheritdoc}
    */
-  public function view(EntityInterface $entity_preview, $view_mode_id = 'default', $langcode = NULL, Request $request = NULL) {
+  public function view(EntityInterface $entity_preview, $view_mode_id = 'default', $langcode = NULL, ?Request $request = NULL) {
     $build = parent::view($entity_preview, $view_mode_id, $langcode);
     // In case it is ajax request, respond with ajax response.
     if ($request->isXmlHttpRequest()) {
@@ -52,7 +52,7 @@ class PreviewController extends PreviewControllerBase {
   /**
    * {@inheritdoc}
    */
-  public function nodeView(EntityInterface $node_preview, $view_mode_id = 'full', $langcode = NULL, Request $request = NULL) {
+  public function nodeView(EntityInterface $node_preview, $view_mode_id = 'full', $langcode = NULL, ?Request $request = NULL) {
     $build = parent::view($node_preview, $view_mode_id, $langcode);
     // In case it is ajax request, respond with ajax response.
     if ($request->isXmlHttpRequest()) {
diff --git a/src/Form/EntityReferenceAddForm.php b/src/Form/EntityReferenceAddForm.php
index 8a12b26..ea7cbf2 100644
--- a/src/Form/EntityReferenceAddForm.php
+++ b/src/Form/EntityReferenceAddForm.php
@@ -48,7 +48,7 @@ class EntityReferenceAddForm extends FormBase {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state, FieldDefinitionInterface $field_definition = NULL) {
+  public function buildForm(array $form, FormStateInterface $form_state, ?FieldDefinitionInterface $field_definition = NULL) {
     if (!empty($field_definition)) {
       $settings = $field_definition->getSettings();
       if ($settings['target_type'] == 'media' && $this->moduleHandler->moduleExists('media_library_form_element')) {
diff --git a/src/Hook/FrontendEditingHooks.php b/src/Hook/FrontendEditingHooks.php
index 3b98dee..3bb4c0e 100644
--- a/src/Hook/FrontendEditingHooks.php
+++ b/src/Hook/FrontendEditingHooks.php
@@ -3,7 +3,6 @@
 namespace Drupal\frontend_editing\Hook;
 
 use Drupal\Core\Hook\Attribute\Hook;
-use Drupal\frontend_editing\RenderCallbacks;
 use Drupal\frontend_editing\ToolbarItem;
 
 /**
diff --git a/src/ToolbarItem.php b/src/ToolbarItem.php
index 74d8832..e1d7533 100644
--- a/src/ToolbarItem.php
+++ b/src/ToolbarItem.php
@@ -83,4 +83,5 @@ final class ToolbarItem implements TrustedCallbackInterface {
     unset($element['tab']['#attributes']);
     return $element;
   }
+
 }
-- 
GitLab


From 5464a1a789b18ddbf35b627af3028bae4904506f Mon Sep 17 00:00:00 2001
From: Alejandro Ortega <a.ortega@1xinternet.de>
Date: Thu, 17 Apr 2025 15:00:16 +0200
Subject: [PATCH 07/15] #3519238: unify selector

---
 css/ui_toggle.css                            | 10 ++++------
 src/Controller/FrontendEditingController.php | 21 ++++++--------------
 src/ToolbarItem.php                          |  2 +-
 3 files changed, 11 insertions(+), 22 deletions(-)

diff --git a/css/ui_toggle.css b/css/ui_toggle.css
index 39367b1..613e24e 100644
--- a/css/ui_toggle.css
+++ b/css/ui_toggle.css
@@ -59,18 +59,16 @@
   border-radius: 999px;
 }
 
-.gin-secondary-toolbar__layout-container a.frontend-editing-toolbar-toggle-link:focus {
+.gin-secondary-toolbar__layout-container a.frontend-editing-toggle-link:focus {
   box-shadow: none;
 }
 
-a.frontend-editing-toolbar-toggle-link,
 a.frontend-editing-toggle-link,
-.toolbar-tab a.frontend-editing-toolbar-toggle-link:focus:not(:hover) {
+.toolbar-tab a.frontend-editing-toggle-link:focus:not(:hover) {
   text-decoration: none;
 }
 
-.frontend-editing-toolbar-toggle-link:hover,
-.frontend-editing-toolbar-toggle-link:hover:focus,
+.frontend-editing-toggle-link:hover:focus,
 .frontend-editing-toggle-link:hover {
   color: white;
   text-decoration: underline;
@@ -164,7 +162,7 @@ a.frontend-editing-toggle-link,
     left: calc(100% - 1rem - 0.1rem);
   }
 
-  a.frontend-editing-toolbar-toggle-link {
+  a.frontend-editing-toggle-link {
     position: relative;
   }
 }
diff --git a/src/Controller/FrontendEditingController.php b/src/Controller/FrontendEditingController.php
index 79030c6..9eb36e4 100644
--- a/src/Controller/FrontendEditingController.php
+++ b/src/Controller/FrontendEditingController.php
@@ -120,31 +120,22 @@ class FrontendEditingController extends ControllerBase {
     $response = new AjaxResponse();
     if ($new_state) {
       $message = $this->t('Frontend editing has been enabled.');
-      $response->addCommand(new InvokeCommand('#frontend-editing-toggle-link', 'addClass', ['frontend-editing--enabled']));
-      $response->addCommand(new InvokeCommand('.frontend-editing-toolbar-toggle-link', 'addClass', ['frontend-editing--enabled']));
-      $response->addCommand(new InvokeCommand('#frontend-editing-toggle-link', 'text', [$this->t('On')]));
-      $response->addCommand(new InvokeCommand('.frontend-editing-toolbar-toggle-link', 'text', [$this->t('On')]));
+      $response->addCommand(new InvokeCommand('.frontend-editing-toggle-link', 'addClass', ['frontend-editing--enabled']));
+      $response->addCommand(new InvokeCommand('.frontend-editing-toggle-link', 'text', [$this->t('On')]));
       $response->addCommand(new InvokeCommand('body', 'removeClass', ['frontend-editing--hidden']));
     }
     else {
       $message = $this->t('Frontend editing has been disabled.');
-      $response->addCommand(new InvokeCommand('#frontend-editing-toggle-link', 'removeClass', ['frontend-editing--enabled']));
-      $response->addCommand(new InvokeCommand('.frontend-editing-toolbar-toggle-link', 'removeClass', ['frontend-editing--enabled']));
-      $response->addCommand(new InvokeCommand('#frontend-editing-toggle-link', 'text', [$this->t('Off')]));
-      $response->addCommand(new InvokeCommand('.frontend-editing-toolbar-toggle-link', 'text', [$this->t('Off')]));
+      $response->addCommand(new InvokeCommand('.frontend-editing-toggle-link', 'removeClass', ['frontend-editing--enabled']));
+      $response->addCommand(new InvokeCommand('.frontend-editing-toggle-link', 'text', [$this->t('Off')]));
       $response->addCommand(new InvokeCommand('body', 'addClass', ['frontend-editing--hidden']));
     }
     $response->addCommand(new MessageCommand($message, NULL, ['type' => 'status']));
-    $response->addCommand(new InvokeCommand('#frontend-editing-toggle-link', 'attr', [
+    $response->addCommand(new InvokeCommand('.frontend-editing-toggle-link', 'attr', [
       'data-toggle-state',
       $new_state,
     ]));
-    $response->addCommand(new InvokeCommand('.frontend-editing-toolbar-toggle-link', 'attr', [
-      'data-toggle-state',
-      $new_state,
-    ]));
-    $response->addCommand(new InvokeCommand('#frontend-editing-toggle-link', 'removeClass', ['frontend-editing-toggle-not-configured']));
-    $response->addCommand(new InvokeCommand('.frontend-editing-toolbar-toggle-link', 'removeClass', ['frontend-editing-toggle-not-configured']));
+    $response->addCommand(new InvokeCommand('.frontend-editing-toggle-link', 'removeClass', ['frontend-editing-toggle-not-configured']));
 
     return $response;
   }
diff --git a/src/ToolbarItem.php b/src/ToolbarItem.php
index e1d7533..ae5b76a 100644
--- a/src/ToolbarItem.php
+++ b/src/ToolbarItem.php
@@ -50,7 +50,7 @@ final class ToolbarItem implements TrustedCallbackInterface {
         'title' => 'Enable frontend editing actions',
         'class' => [
           'use-ajax',
-          'frontend-editing-toolbar-toggle-link',
+          'frontend-editing-toggle-link',
           $active_class,
         ],
       ],
-- 
GitLab


From deaf8027930fec0f0271a70fd433fa013e81c190 Mon Sep 17 00:00:00 2001
From: Alejandro Ortega <a.ortega@1xinternet.de>
Date: Thu, 17 Apr 2025 15:12:33 +0200
Subject: [PATCH 08/15] #3519238: fix linting error

---
 src/Controller/FrontendEditingController.php |  2 ++
 src/Hook/FrontendEditingHooks.php            | 26 +++++++++++++++++---
 2 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/src/Controller/FrontendEditingController.php b/src/Controller/FrontendEditingController.php
index 9eb36e4..b447d9f 100644
--- a/src/Controller/FrontendEditingController.php
+++ b/src/Controller/FrontendEditingController.php
@@ -78,6 +78,8 @@ class FrontendEditingController extends ControllerBase {
    *   The user data storage.
    * @param \Drupal\frontend_editing\FieldReferenceHelperInterface $field_reference_helper
    *   The field reference helper.
+   * @param \Drupal\frontend_editing\FrontendEditingFormBuilderInterface $frontend_editing_form_builder
+   *   The frontend editing form builder.
    */
   public function __construct(RendererInterface $renderer, EntityRepositoryInterface $entity_repository, UserDataInterface $userData, FieldReferenceHelperInterface $field_reference_helper, FrontendEditingFormBuilderInterface $frontend_editing_form_builder) {
     $this->renderer = $renderer;
diff --git a/src/Hook/FrontendEditingHooks.php b/src/Hook/FrontendEditingHooks.php
index 3bb4c0e..53bee03 100644
--- a/src/Hook/FrontendEditingHooks.php
+++ b/src/Hook/FrontendEditingHooks.php
@@ -2,24 +2,42 @@
 
 namespace Drupal\frontend_editing\Hook;
 
+use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\Session\AccountProxyInterface;
 use Drupal\frontend_editing\ToolbarItem;
+use Symfony\Component\DependencyInjection\Attribute\Autowire;
 
 /**
  * Hook implementations for frontend_editing.
  */
 class FrontendEditingHooks {
 
+  /**
+   * Constructs FrontendEditing hooks.
+   *
+   * @param \Drupal\Core\Session\AccountProxyInterface $currentUser
+   *   The current user.
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
+   *   The config factory object.
+   */
+  public function __construct(
+    #[Autowire(service: 'current_user')]
+    protected AccountProxyInterface $currentUser,
+    #[Autowire(service: 'config.factory')]
+    protected ConfigFactoryInterface $configFactory,
+  ) {}
+
   /**
    * Implements hook_toolbar().
    */
   #[Hook('toolbar')]
   public function toolbar() {
-    if (!\Drupal::currentUser()->hasPermission('access frontend editing')) {
+    if (!$this->currentUser->hasPermission('access frontend editing')) {
       return ['#cache' => ['contexts' => ['user.permissions']]];
     }
 
-    $config = \Drupal::config('frontend_editing.settings');
+    $config = $this->configFactory->get('frontend_editing.settings');
     if ($config->get('ui_toggle')) {
       return [];
     }
@@ -53,8 +71,8 @@ class FrontendEditingHooks {
     // \Drupal\toolbar\Element\ToolbarItem::preRenderToolbarItem adds an
     // #attributes property to each toolbar item's tab child automatically.
     // Lazy builders don't support an #attributes property so we need to
-    // add another render callback to remove the #attributes property. We start by
-    // adding the defaults, and then we append our own pre render callback.
+    // add another render callback to remove the #attributes property. We start
+    // by adding the defaults, and then we append our own pre render callback.
     $items['toggle'] += \Drupal::service('plugin.manager.element_info')->getInfo('toolbar_item');
     $items['toggle']['#pre_render'][] = [ToolbarItem::class, 'removeTabAttributes'];
     return $items;
-- 
GitLab


From 4a3407c1611cd827d4230d32244441bf61da6311 Mon Sep 17 00:00:00 2001
From: Alejandro Ortega <a.ortega@1xinternet.de>
Date: Thu, 17 Apr 2025 15:21:25 +0200
Subject: [PATCH 09/15] #3519238: hode on admin route

---
 src/Hook/FrontendEditingHooks.php | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/Hook/FrontendEditingHooks.php b/src/Hook/FrontendEditingHooks.php
index 53bee03..5418b7e 100644
--- a/src/Hook/FrontendEditingHooks.php
+++ b/src/Hook/FrontendEditingHooks.php
@@ -4,6 +4,7 @@ namespace Drupal\frontend_editing\Hook;
 
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\Routing\AdminContext;
 use Drupal\Core\Session\AccountProxyInterface;
 use Drupal\frontend_editing\ToolbarItem;
 use Symfony\Component\DependencyInjection\Attribute\Autowire;
@@ -26,6 +27,8 @@ class FrontendEditingHooks {
     protected AccountProxyInterface $currentUser,
     #[Autowire(service: 'config.factory')]
     protected ConfigFactoryInterface $configFactory,
+    #[Autowire(service: 'router.admin_context')]
+    protected AdminContext $adminContext,
   ) {}
 
   /**
@@ -38,7 +41,7 @@ class FrontendEditingHooks {
     }
 
     $config = $this->configFactory->get('frontend_editing.settings');
-    if ($config->get('ui_toggle')) {
+    if ($config->get('ui_toggle') || $this->adminContext->isAdminRoute()) {
       return [];
     }
 
-- 
GitLab


From 5ee7dfa05cddc363067d98d794f52edc7140dac6 Mon Sep 17 00:00:00 2001
From: Alejandro Ortega <a.ortega@1xinternet.de>
Date: Fri, 18 Apr 2025 09:51:31 +0200
Subject: [PATCH 10/15] #3519238: fix css for mobile

---
 css/ui_toggle.css | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/css/ui_toggle.css b/css/ui_toggle.css
index 613e24e..3d44375 100644
--- a/css/ui_toggle.css
+++ b/css/ui_toggle.css
@@ -147,7 +147,7 @@ a.frontend-editing-toggle-link,
   }
 }
 
-@media only screen and (max-width: 600px) {
+@media only screen and (max-width: 975px) {
   .frontend-editing-toolbar-toggle {
     margin-top: 0.25rem;
     margin-right: 0;
-- 
GitLab


From 337a2d3f59c78c7e8b6483c1c70602b4b1876421 Mon Sep 17 00:00:00 2001
From: Alejandro Ortega <a.ortega@1xinternet.de>
Date: Fri, 18 Apr 2025 09:57:13 +0200
Subject: [PATCH 11/15] #3519238: add legacy hook for < 10.4

---
 frontend_editing.module | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/frontend_editing.module b/frontend_editing.module
index 52dd156..8914538 100644
--- a/frontend_editing.module
+++ b/frontend_editing.module
@@ -17,6 +17,7 @@ use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\RevisionableInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FormatterInterface;
+use Drupal\Core\Field\WidgetInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Render\Markup;
@@ -25,6 +26,7 @@ use Drupal\Core\Url;
 use Drupal\frontend_editing\Ajax\CloseSidePanelCommand;
 use Drupal\frontend_editing\Ajax\EntityPreviewCommand;
 use Drupal\frontend_editing\Ajax\ScrollTopCommand;
+use Drupal\frontend_editing\Hook\FrontendEditingHooks;
 use Drupal\node\NodeInterface;
 
 /**
@@ -804,3 +806,11 @@ function frontend_editing_preprocess_page_title(&$variables) {
     }
   }
 }
+
+/**
+ * Implements field_widget_third_party_settings_form().
+ */
+#[LegacyHook]
+function frontend_editing_field_widget_third_party_settings_form(WidgetInterface $plugin, FieldDefinitionInterface $field_definition, $form_mode, array $form, FormStateInterface $form_state) {
+  return \Drupal::service(FrontendEditingHooks::class)->fieldWidgetThirdPartySettingsForm($plugin, $field_definition, $form_mode, $form, $form_state);
+}
-- 
GitLab


From 10a68d7ea647246c01606f012d8b61fd06b11595 Mon Sep 17 00:00:00 2001
From: Alejandro Ortega <a.ortega@1xinternet.de>
Date: Fri, 18 Apr 2025 11:07:35 +0200
Subject: [PATCH 12/15] #3519238: fix legacy hook and clean code

---
 frontend_editing.module           |  8 ++++----
 frontend_editing.services.yml     |  5 ++++-
 src/Hook/FrontendEditingHooks.php |  9 ++++++++-
 src/ToolbarItem.php               | 23 ++++++++++++++++++-----
 4 files changed, 34 insertions(+), 11 deletions(-)

diff --git a/frontend_editing.module b/frontend_editing.module
index 8914538..f55ebee 100644
--- a/frontend_editing.module
+++ b/frontend_editing.module
@@ -17,8 +17,8 @@ use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\RevisionableInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FormatterInterface;
-use Drupal\Core\Field\WidgetInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Hook\Attribute\LegacyHook;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Render\Markup;
 use Drupal\Core\Routing\RouteMatchInterface;
@@ -808,9 +808,9 @@ function frontend_editing_preprocess_page_title(&$variables) {
 }
 
 /**
- * Implements field_widget_third_party_settings_form().
+ * Implements hook_toolbar().
  */
 #[LegacyHook]
-function frontend_editing_field_widget_third_party_settings_form(WidgetInterface $plugin, FieldDefinitionInterface $field_definition, $form_mode, array $form, FormStateInterface $form_state) {
-  return \Drupal::service(FrontendEditingHooks::class)->fieldWidgetThirdPartySettingsForm($plugin, $field_definition, $form_mode, $form, $form_state);
+function frontend_editing_toolbar() {
+  return \Drupal::service(FrontendEditingHooks::class)->toolbar();
 }
diff --git a/frontend_editing.services.yml b/frontend_editing.services.yml
index 2a6c136..159ff00 100644
--- a/frontend_editing.services.yml
+++ b/frontend_editing.services.yml
@@ -11,5 +11,8 @@ services:
     arguments: ['@entity_type.manager', '@entity.form_builder', '@form_builder']
   frontend_editing.toolbar_item:
     class: Drupal\frontend_editing\ToolbarItem
-    arguments: [ '@plugin.manager.element_info' ]
+    arguments: [ '@plugin.manager.element_info', '@user.data', '@current_user' ]
   Drupal\frontend_editing\ToolbarItem: '@frontend_editing.toolbar_item'
+  Drupal\frontend_editing\Hook\FrontendEditingHooks:
+    class: Drupal\frontend_editing\Hook\FrontendEditingHooks
+    autowire: true
diff --git a/src/Hook/FrontendEditingHooks.php b/src/Hook/FrontendEditingHooks.php
index 5418b7e..45bf521 100644
--- a/src/Hook/FrontendEditingHooks.php
+++ b/src/Hook/FrontendEditingHooks.php
@@ -4,6 +4,7 @@ namespace Drupal\frontend_editing\Hook;
 
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\Render\ElementInfoManager;
 use Drupal\Core\Routing\AdminContext;
 use Drupal\Core\Session\AccountProxyInterface;
 use Drupal\frontend_editing\ToolbarItem;
@@ -21,6 +22,10 @@ class FrontendEditingHooks {
    *   The current user.
    * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
    *   The config factory object.
+   * @param \Drupal\Core\Routing\AdminContext $adminContext
+   *   The admin context.
+   * @param \Drupal\Core\Render\ElementInfoManager $elementInfoManager
+   *   The element info manager.
    */
   public function __construct(
     #[Autowire(service: 'current_user')]
@@ -29,6 +34,8 @@ class FrontendEditingHooks {
     protected ConfigFactoryInterface $configFactory,
     #[Autowire(service: 'router.admin_context')]
     protected AdminContext $adminContext,
+    #[Autowire(service: 'plugin.manager.element_info')]
+    protected ElementInfoManager $elementInfoManager,
   ) {}
 
   /**
@@ -76,7 +83,7 @@ class FrontendEditingHooks {
     // Lazy builders don't support an #attributes property so we need to
     // add another render callback to remove the #attributes property. We start
     // by adding the defaults, and then we append our own pre render callback.
-    $items['toggle'] += \Drupal::service('plugin.manager.element_info')->getInfo('toolbar_item');
+    $items['toggle'] += $this->elementInfoManager->getInfo('toolbar_item');
     $items['toggle']['#pre_render'][] = [ToolbarItem::class, 'removeTabAttributes'];
     return $items;
   }
diff --git a/src/ToolbarItem.php b/src/ToolbarItem.php
index ae5b76a..f33cbee 100644
--- a/src/ToolbarItem.php
+++ b/src/ToolbarItem.php
@@ -7,7 +7,10 @@ namespace Drupal\frontend_editing;
 use Drupal\Component\Utility\Html;
 use Drupal\Core\Render\ElementInfoManagerInterface;
 use Drupal\Core\Security\TrustedCallbackInterface;
+use Drupal\Core\Session\AccountProxyInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\Url;
+use Drupal\user\UserDataInterface;
 
 /**
  * Defines a class for lazy building render arrays.
@@ -16,16 +19,23 @@ use Drupal\Core\Url;
  */
 final class ToolbarItem implements TrustedCallbackInterface {
 
+  use StringTranslationTrait;
+
   /**
    * Constructs LazyBuilders object.
    *
    * @param \Drupal\Core\Render\ElementInfoManagerInterface $elementInfo
    *   Element info.
+   * @param \Drupal\user\UserDataInterface $userData
+   *   The user data.
+   * @param \Drupal\Core\Session\AccountProxyInterface $currentUser
+   *   The current user.
    */
   public function __construct(
     protected ElementInfoManagerInterface $elementInfo,
-  ) {
-  }
+    protected UserDataInterface $userData,
+    protected AccountProxyInterface $currentUser,
+  ) {}
 
   /**
    * Render announcements.
@@ -34,8 +44,11 @@ final class ToolbarItem implements TrustedCallbackInterface {
    *   Render array.
    */
   public function renderToggle(): array {
-    $toggle_state = \Drupal::service('user.data')
-      ->get('frontend_editing', \Drupal::currentUser()->id(), 'enabled');
+    $toggle_state = $this->userData->get(
+      'frontend_editing',
+      $this->currentUser->id(),
+      'enabled'
+    );
     $active_class = $toggle_state ? 'frontend-editing--enabled' : '';
 
     $build = [
@@ -44,7 +57,7 @@ final class ToolbarItem implements TrustedCallbackInterface {
       '#cache' => [
         'context' => ['user.permissions'],
       ],
-      '#title' => $toggle_state ? t('On') : t('Off'),
+      '#title' => $toggle_state ? $this->t('On') : $this->t('Off'),
       '#id' => Html::getId('frontend-editing-toolbar-toggle-link'),
       '#attributes' => [
         'title' => 'Enable frontend editing actions',
-- 
GitLab


From 16d9a911d49676b049e3de29297d50d27b0c11d4 Mon Sep 17 00:00:00 2001
From: Alejandro Ortega <a.ortega@1xinternet.de>
Date: Sat, 19 Apr 2025 19:51:03 +0200
Subject: [PATCH 13/15] #3519238: translate title

---
 src/ToolbarItem.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/ToolbarItem.php b/src/ToolbarItem.php
index f33cbee..e9320ab 100644
--- a/src/ToolbarItem.php
+++ b/src/ToolbarItem.php
@@ -60,7 +60,7 @@ final class ToolbarItem implements TrustedCallbackInterface {
       '#title' => $toggle_state ? $this->t('On') : $this->t('Off'),
       '#id' => Html::getId('frontend-editing-toolbar-toggle-link'),
       '#attributes' => [
-        'title' => 'Enable frontend editing actions',
+        'title' => $this->t('Enable frontend editing actions'),
         'class' => [
           'use-ajax',
           'frontend-editing-toggle-link',
-- 
GitLab


From f860df481caa6691a6a7a6cad9fb7429f2425804 Mon Sep 17 00:00:00 2001
From: Alejandro Ortega <a.ortega@1xinternet.de>
Date: Sat, 19 Apr 2025 19:51:11 +0200
Subject: [PATCH 14/15] #3519238: return config metadata

---
 src/Hook/FrontendEditingHooks.php | 21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/src/Hook/FrontendEditingHooks.php b/src/Hook/FrontendEditingHooks.php
index 45bf521..81fb52b 100644
--- a/src/Hook/FrontendEditingHooks.php
+++ b/src/Hook/FrontendEditingHooks.php
@@ -44,12 +44,22 @@ class FrontendEditingHooks {
   #[Hook('toolbar')]
   public function toolbar() {
     if (!$this->currentUser->hasPermission('access frontend editing')) {
-      return ['#cache' => ['contexts' => ['user.permissions']]];
+      return [
+        '#cache' => [
+          'contexts' => ['user.permissions'],
+        ],
+      ];
     }
 
     $config = $this->configFactory->get('frontend_editing.settings');
-    if ($config->get('ui_toggle') || $this->adminContext->isAdminRoute()) {
-      return [];
+    $ui_toggle_enabled = $config->get('ui_toggle');
+    if ($ui_toggle_enabled || $this->adminContext->isAdminRoute()) {
+      return [
+        '#cache' => [
+          'contexts' => ['user.permissions'],
+          'tags' => $config->getCacheTags(),
+        ],
+      ];
     }
 
     $items['toggle'] = [
@@ -72,9 +82,8 @@ class FrontendEditingHooks {
         ],
       ],
       '#cache' => [
-        'contexts' => [
-          'user.permissions',
-        ],
+        'contexts' => ['user.permissions'],
+        'tags' => $config->getCacheTags(),
       ],
       '#weight' => -11,
     ];
-- 
GitLab


From 358a1f3595202a545f11bb56e5fa29a3c52b8532 Mon Sep 17 00:00:00 2001
From: Artem  Dmitriiev <a.dmitriiev@1xinternet.de>
Date: Mon, 21 Apr 2025 09:58:46 +0200
Subject: [PATCH 15/15] Take into account caching

---
 src/Hook/FrontendEditingHooks.php | 26 +++++++++++---------------
 1 file changed, 11 insertions(+), 15 deletions(-)

diff --git a/src/Hook/FrontendEditingHooks.php b/src/Hook/FrontendEditingHooks.php
index 81fb52b..c991559 100644
--- a/src/Hook/FrontendEditingHooks.php
+++ b/src/Hook/FrontendEditingHooks.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\frontend_editing\Hook;
 
+use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Hook\Attribute\Hook;
 use Drupal\Core\Render\ElementInfoManager;
@@ -43,23 +44,21 @@ class FrontendEditingHooks {
    */
   #[Hook('toolbar')]
   public function toolbar() {
+    $items = [];
+    $cache_metadata = new CacheableMetadata();
+    $cache_metadata->addCacheContexts(['user.permissions']);
     if (!$this->currentUser->hasPermission('access frontend editing')) {
-      return [
-        '#cache' => [
-          'contexts' => ['user.permissions'],
-        ],
-      ];
+      $cache_metadata->applyTo($items);
+      return $items;
     }
 
     $config = $this->configFactory->get('frontend_editing.settings');
+    $cache_metadata->addCacheableDependency($config);
+    $cache_metadata->addCacheContexts(['route']);
     $ui_toggle_enabled = $config->get('ui_toggle');
     if ($ui_toggle_enabled || $this->adminContext->isAdminRoute()) {
-      return [
-        '#cache' => [
-          'contexts' => ['user.permissions'],
-          'tags' => $config->getCacheTags(),
-        ],
-      ];
+      $cache_metadata->applyTo($items);
+      return $items;
     }
 
     $items['toggle'] = [
@@ -81,10 +80,6 @@ class FrontendEditingHooks {
           'frontend-editing-toolbar-toggle',
         ],
       ],
-      '#cache' => [
-        'contexts' => ['user.permissions'],
-        'tags' => $config->getCacheTags(),
-      ],
       '#weight' => -11,
     ];
     // \Drupal\toolbar\Element\ToolbarItem::preRenderToolbarItem adds an
@@ -94,6 +89,7 @@ class FrontendEditingHooks {
     // by adding the defaults, and then we append our own pre render callback.
     $items['toggle'] += $this->elementInfoManager->getInfo('toolbar_item');
     $items['toggle']['#pre_render'][] = [ToolbarItem::class, 'removeTabAttributes'];
+    $cache_metadata->applyTo($items);
     return $items;
   }
 
-- 
GitLab