From 881d6c1c4a5caefbe59b2cb7fedd537cd137050a Mon Sep 17 00:00:00 2001
From: "Philipps, Marc" <Marc.Philipps@adesso.de>
Date: Wed, 26 Feb 2025 07:22:13 +0100
Subject: [PATCH 01/10] feat(frontend_editing): display icon_default from
 Paragraphs in overlay with improved styling

Adjusted rendering logic so that when adding Paragraph entities via the frontend editing overlay, the icon_default from the Paragraphs module is displayed if available.
Added CSS classes to enhance the visual appearance of the icon, ensuring a consistent and appealing user interface.
---
 css/frontend_editing.css                     | 171 +++++-----
 src/Controller/FrontendEditingController.php | 308 +++++++++++++------
 2 files changed, 308 insertions(+), 171 deletions(-)

diff --git a/css/frontend_editing.css b/css/frontend_editing.css
index 4bd208a..bf4b9d1 100644
--- a/css/frontend_editing.css
+++ b/css/frontend_editing.css
@@ -5,29 +5,29 @@ body.path-frontend-editing {
 }
 
 .editing-container {
-  height: 100%;
   position: fixed;
+  z-index: 1000;
   top: 0;
   right: 0;
-  z-index: 1000;
-  background-color: white;
-  box-shadow: -0.2rem 0 1rem rgb(116 116 116 / 40%);
+  overflow: auto;
   width: 30%;
   min-width: 300px;
+  height: 100%;
   transition: width 0.5s;
-  overflow: auto;
+  background-color: white;
+  box-shadow: -0.2rem 0 1rem rgb(116, 116, 116, 0.4);
 }
 .editing-container--wide {
   width: 80%;
 }
 .editing-container--loading {
-  animation-duration: 1.8s;
-  animation-fill-mode: forwards;
-  animation-iteration-count: infinite;
   animation-name: placeholder-shimmer;
+  animation-duration: 1.8s;
   animation-timing-function: linear;
+  animation-iteration-count: infinite;
   background: linear-gradient(to right, #f5f7fb 8%, #e7e7e7 38%, #f5f7fb 54%);
   background-size: 1000px 640px;
+  animation-fill-mode: forwards;
 }
 @keyframes placeholder-shimmer {
   0% {
@@ -40,14 +40,14 @@ body.path-frontend-editing {
 
 /* Styles for action buttons */
 .editing-container__actions {
+  position: absolute;
   display: flex;
-  height: 0;
   align-items: center;
   justify-content: space-between;
-  padding: 0 1rem;
-  position: absolute;
-  width: 100%;
   box-sizing: border-box;
+  width: 100%;
+  height: 0;
+  padding: 0 1rem;
 }
 
 .editing-container__actions button {
@@ -61,7 +61,8 @@ button.editing-container__close {
   height: 24px;
   cursor: pointer;
   border: none;
-  background: url('data:image/svg+xml,%3Csvg width="18" height="18" fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Cpath d="M17 1L9 9l8 8M1 1l8 8-8 8" stroke="%23222330" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/%3E%3C/svg%3E') center no-repeat;
+  background: url('data:image/svg+xml,%3Csvg width="18" height="18" fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Cpath d="M17 1L9 9l8 8M1 1l8 8-8 8" stroke="%23222330" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/%3E%3C/svg%3E')
+    center no-repeat;
 }
 
 /* Styles for width button */
@@ -70,28 +71,30 @@ button.editing-container__toggle {
   height: 24px;
   cursor: pointer;
   border: none;
-  background: url('data:image/svg+xml,%3Csvg width="10" height="18" fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Cpath d="M9 1L1 9l8 8" stroke="%23222330" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/%3E%3C/svg%3E') center no-repeat;
+  background: url('data:image/svg+xml,%3Csvg width="10" height="18" fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Cpath d="M9 1L1 9l8 8" stroke="%23222330" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/%3E%3C/svg%3E')
+    center no-repeat;
 }
 
 .editing-container--wide .editing-container__toggle {
-  background: url('data:image/svg+xml,%3Csvg width="10" height="18" fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Cpath d="M9 1L1 9l8 8" stroke="%23222330" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round" transform="rotate(180 5 9)"/%3E%3C/svg%3E') center no-repeat;
+  background: url('data:image/svg+xml,%3Csvg width="10" height="18" fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Cpath d="M9 1L1 9l8 8" stroke="%23222330" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round" transform="rotate(180 5 9)"/%3E%3C/svg%3E')
+    center no-repeat;
 }
 
-/* Styles for iframe*/
+/* Styles for iframe */
 #editing-container iframe {
+  width: 100%;
   height: 100%;
   border: none;
-  width: 100%;
 }
 
 .frontend-editing-preview-content,
 .frontend-editing-update-content {
+  visibility: hidden;
   overflow: hidden;
-  text-indent: -9999rem;
+  order: 100;
   width: 32px;
   height: 32px;
-  visibility: hidden;
-  order: 100;
+  text-indent: -9999rem;
 }
 
 /* Style container of editable elements */
@@ -99,7 +102,9 @@ button.editing-container__toggle {
   position: relative;
 }
 
-.frontend-editing:has(.frontend-editing:hover) > .frontend-editing-actions .add-paragraphs {
+.frontend-editing:has(.frontend-editing:hover)
+  > .frontend-editing-actions
+  .add-paragraphs {
   display: none;
 }
 
@@ -107,20 +112,23 @@ button.editing-container__toggle {
   display: none;
 }
 
-body:not(.frontend-editing--hidden) .frontend-editing:has(.add-paragraphs.hover-highlight) {
+body:not(.frontend-editing--hidden)
+  .frontend-editing:has(.add-paragraphs.hover-highlight) {
   transition: padding ease-in-out 0.4s;
 }
 
-body:not(.frontend-editing--hidden) .frontend-editing:has(.add-paragraphs.hover-highlight):hover {
+body:not(.frontend-editing--hidden)
+  .frontend-editing:has(.add-paragraphs.hover-highlight):hover {
   padding: 25px 0;
 }
 
 body:not(.frontend-editing--hidden) .frontend-editing:hover {
-  outline: 3px solid rgba(var(--fe-editing-primary-color), 0.8);
   z-index: 10;
+  outline: 3px solid rgba(var(--fe-editing-primary-color), 0.8);
 }
 
-body:not(.frontend-editing--hidden) .frontend-editing:has(.frontend-editing:hover) {
+body:not(.frontend-editing--hidden)
+  .frontend-editing:has(.frontend-editing:hover) {
   outline: 3px solid rgba(var(--fe-editing-primary-color), 0.2);
 }
 
@@ -128,40 +136,42 @@ body:not(.frontend-editing--hidden) .frontend-editing:has(.frontend-editing:hove
   display: none;
 }
 
-.frontend-editing:hover .frontend-editing:not(:hover) .frontend-editing-actions {
+.frontend-editing:hover
+  .frontend-editing:not(:hover)
+  .frontend-editing-actions {
   display: none;
 }
 
 /* Styled for editing action */
 .common-actions-container {
-  display: flex;
   position: absolute;
+  z-index: 2;
   top: 10px;
   left: 0;
+  display: flex;
   width: auto;
-  z-index: 2;
 }
 
 .title-edit-container,
 .icons-container {
-  margin-left: 0.5rem;
   margin-right: 0.5rem;
+  margin-left: 0.5rem;
 }
 
 .action-title {
-  opacity: 0;
   display: block;
+  align-self: center;
   width: 100%;
+  padding-top: 5px;
+  padding-bottom: 5px;
+  padding-left: 15px;
+  white-space: nowrap;
   letter-spacing: 0.2em;
   pointer-events: none;
-  font-weight: 500;
-  font-size: 0.75rem;
+  opacity: 0;
   color: #222330;
-  white-space: nowrap;
-  align-self: center;
-  padding-left: 15px;
-  padding-top: 5px;
-  padding-bottom: 5px;
+  font-size: 0.75rem;
+  font-weight: 500;
 }
 
 .frontend-editing:hover .frontend-editing__action,
@@ -172,23 +182,23 @@ body:not(.frontend-editing--hidden) .frontend-editing:has(.frontend-editing:hove
 .frontend-editing:hover .title-edit-container,
 .frontend-editing:hover .icons-container {
   display: flex;
-  border-radius: 8px;
-  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
   border: 1px solid #d3d3d3;
+  border-radius: 8px;
   background-color: white;
+  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
 }
 
 .frontend-editing-actions .ajax-progress {
   display: inline-flex;
   justify-content: center;
+  /* Ensure AJAX throbber is always last item */
+  order: 6;
   width: 32px;
   height: 32px;
   padding: 0;
-  /* Ensure AJAX throbber is always last item */
-  order: 6;
   opacity: 1;
-  background: #e8e8e8;
   border-left: 1px solid #dddcdc;
+  background: #e8e8e8;
 }
 
 .frontend-editing-actions .ajax-progress .throbber {
@@ -197,28 +207,28 @@ body:not(.frontend-editing--hidden) .frontend-editing:has(.frontend-editing:hove
   padding: 0;
   opacity: 0.4;
   background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='36' viewBox='0 0 24 24'%3E%3Cstyle%3E@keyframes spinner%7Bto%7Btransform:rotate(360deg)%7D%7D%3C/style%3E%3Cpath d='M12 4a8 8 0 0 1 7.89 6.7 1.53 1.53 0 0 0 1.49 1.3 1.5 1.5 0 0 0 1.48-1.75 11 11 0 0 0-21.72 0A1.5 1.5 0 0 0 2.62 12a1.53 1.53 0 0 0 1.49-1.3A8 8 0 0 1 12 4Z' style='transform-origin:center;animation:spinner .75s infinite linear'/%3E%3C/svg%3E");
-  background-size: 18px;
   background-position: center;
+  background-size: 18px;
 }
 
-.frontend-editing--outline:hover .frontend-editing-actions:before {
+.frontend-editing--outline:hover .frontend-editing-actions::before {
   opacity: 1;
 }
 
 /* Style editing icon */
 .frontend-editing__action {
+  z-index: 1;
   display: inline-block;
+  overflow: hidden;
   width: 37px;
   height: 32px;
-  z-index: 1;
-  opacity: 0;
   transition-duration: 0s;
-  background-position: center;
-  overflow: hidden;
   text-indent: -9999rem;
-  font-size: 0px;
+  opacity: 0;
   color: transparent;
   border-right: 1px solid #dddcdc;
+  background-position: center;
+  font-size: 0;
 }
 
 .frontend-editing__action--edit {
@@ -239,23 +249,25 @@ body:not(.frontend-editing--hidden) .frontend-editing:has(.frontend-editing:hove
 
 .frontend-editing__action--edit:hover,
 .frontend-editing__action--down:hover {
-  background-color: rgba(var(--fe-editing-primary-color), 0.25);
   border-top-right-radius: 7px;
   border-bottom-right-radius: 7px;
+  background-color: rgba(var(--fe-editing-primary-color), 0.25);
 }
 
 .frontend-editing__action--up:hover {
-  background-color: rgba(var(--fe-editing-primary-color), 0.25);
   border-top-left-radius: 7px;
   border-bottom-left-radius: 7px;
+  background-color: rgba(var(--fe-editing-primary-color), 0.25);
 }
 
-.move-paragraphs.icons-container:not(:has(.frontend-editing__action--up)) .frontend-editing__action--down:hover {
+.move-paragraphs.icons-container:not(:has(.frontend-editing__action--up))
+  .frontend-editing__action--down:hover {
   border-top-left-radius: 7px;
   border-bottom-left-radius: 7px;
 }
 
-.move-paragraphs.icons-container:not(:has(.frontend-editing__action--down)) .frontend-editing__action--up:hover {
+.move-paragraphs.icons-container:not(:has(.frontend-editing__action--down))
+  .frontend-editing__action--up:hover {
   border-top-right-radius: 7px;
   border-bottom-right-radius: 7px;
 }
@@ -264,31 +276,31 @@ body:not(.frontend-editing--hidden) .frontend-editing:has(.frontend-editing:hove
   border-right-width: 0;
 }
 
-.frontend-editing__action:hover:before {
+.frontend-editing__action:hover::before {
   opacity: 1;
 }
 
 .add-paragraphs {
-  height: 0;
   width: 0;
+  height: 0;
 }
 
 .add-paragraphs a {
-  display: flex;
   position: absolute;
-  left: 0;
+  z-index: 1;
   right: 0;
+  left: 0;
+  display: flex;
+  overflow: visible;
   width: fit-content;
   height: 25px;
   margin: auto;
   border: 0;
-  overflow: visible;
-  z-index: 1;
 }
 
 .add-paragraphs.hover-highlight a {
-  background-color: rgba(var(--fe-editing-primary-color), 0.25);
   width: 100%;
+  background-color: rgba(var(--fe-editing-primary-color), 0.25);
 }
 
 .add-paragraphs a:hover {
@@ -296,7 +308,7 @@ body:not(.frontend-editing--hidden) .frontend-editing:has(.frontend-editing:hove
 }
 
 .frontend-editing:hover .add-paragraphs.icons-container {
-  border-width: 0px;
+  border-width: 0;
 }
 
 .frontend-editing .move-paragraphs.icons-container:not(:has(*)) {
@@ -315,26 +327,29 @@ body:not(.frontend-editing--hidden) .frontend-editing:has(.frontend-editing:hove
   pointer-events: none;
 }
 
-.frontend-editing__action:not(.frontend-editing__action--before):not(.frontend-editing__action--after):before {
+.frontend-editing__action:not(
+    .frontend-editing__action--before,
+    .frontend-editing__action--after
+  )::before {
   display: block;
-  content: '';
   width: 100%;
   height: 100%;
-  background-size: 16px;
+  content: "";
   background-repeat: no-repeat;
   background-position: center;
+  background-size: 16px;
 }
 
 .frontend-editing__action--before::before,
 .frontend-editing__action--after::before {
-  content: ' ';
+  position: absolute;
   z-index: 10;
-  width: 40px;
-  height: 40px;
-  left: 0;
   right: 0;
+  left: 0;
   display: block;
-  position: absolute;
+  width: 40px;
+  height: 40px;
+  content: " ";
 }
 
 .frontend-editing__action--before::before {
@@ -342,41 +357,41 @@ body:not(.frontend-editing--hidden) .frontend-editing:has(.frontend-editing:hove
 }
 
 .frontend-editing__action--before svg {
-  pointer-events: none;
   z-index: 10;
-  margin: -1.5rem auto 0 auto;
   width: 40px;
   height: 40px;
+  margin: -1.5rem auto 0 auto;
+  pointer-events: none;
   color: rgb(var(--fe-editing-primary-color));
 }
 
 .frontend-editing__action--after::before {
+  bottom: 0;
   margin: auto;
   margin-bottom: -1.5rem;
-  bottom: 0;
 }
 
 .frontend-editing__action--after svg {
-  pointer-events: none;
   z-index: 10;
-  margin: auto;
-  margin-bottom: -1.5rem;
   width: 40px;
   height: 40px;
+  margin: auto;
+  margin-bottom: -1.5rem;
+  pointer-events: none;
   color: rgb(var(--fe-editing-primary-color));
 }
 
-.frontend-editing__action--up:before {
+.frontend-editing__action--up::before {
   background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzIzMjIyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTIgNGExIDEgMCAwIDEgLjcwNy4yOTNsNyA3YTEgMSAwIDAgMS0xLjQxNCAxLjQxNEwxMyA3LjQxNFYxOWExIDEgMCAxIDEtMiAwVjcuNDE0bC01LjI5MyA1LjI5M2ExIDEgMCAwIDEtMS40MTQtMS40MTRsNy03QTEgMSAwIDAgMSAxMiA0WiIgY2xpcC1ydWxlPSJldmVub2RkIi8+PC9zdmc+");
   background-size: 20px;
 }
 
-.frontend-editing__action--down:before {
+.frontend-editing__action--down::before {
   background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzIzMjIyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTMgNWExIDEgMCAxIDAtMiAwdjExLjU4NmwtNS4yOTMtNS4yOTNhMSAxIDAgMSAwLTEuNDE0IDEuNDE0bDYuOTk1IDYuOTk1QS45OTMuOTkzIDAgMCAwIDEyIDIwYS45OTcuOTk3IDAgMCAwIC43MTItLjI5OGw2Ljk5NS02Ljk5NWExIDEgMCAwIDAtMS40MTQtMS40MTRMMTMgMTYuNTg2VjVaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4=");
   background-size: 20px;
 }
 
-.frontend-editing__action--edit:before {
+.frontend-editing__action--edit::before {
   background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzIzMjIyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTkgMy4xNzFhMS44MjkgMS44MjkgMCAwIDAtMS4yOTMuNTM2TDQuMzk1IDE3LjAxOWwtLjk3IDMuNTU2IDMuNTU2LS45N0wyMC4yOTMgNi4yOTNBMS44MjkgMS44MjkgMCAwIDAgMTkgMy4xN1ptLTEuNDY1LTEuNzA4YTMuODI5IDMuODI5IDAgMCAxIDQuMTcyIDYuMjQ0bC0xMy41IDEzLjVhMSAxIDAgMCAxLS40NDQuMjU4bC01LjUgMS41YTEgMSAwIDAgMS0xLjIyOC0xLjIyOGwxLjUtNS41YTEgMSAwIDAgMSAuMjU4LS40NDRsMTMuNS0xMy41YTMuODI5IDMuODI5IDAgMCAxIDEuMjQyLS44M1oiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==");
 }
 
diff --git a/src/Controller/FrontendEditingController.php b/src/Controller/FrontendEditingController.php
index b1dc981..26ff965 100644
--- a/src/Controller/FrontendEditingController.php
+++ b/src/Controller/FrontendEditingController.php
@@ -11,6 +11,7 @@ use Drupal\Core\Ajax\InvokeCommand;
 use Drupal\Core\Ajax\MessageCommand;
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\Entity\EntityFormBuilder;
+use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityRepositoryInterface;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Render\RendererInterface;
@@ -82,7 +83,13 @@ class FrontendEditingController extends ControllerBase {
    * @param \Drupal\user\UserDataInterface $userData
    *   The user data storage.
    */
-  public function __construct(RendererInterface $renderer, EntityFormBuilder $builder, EntityRepositoryInterface $entity_repository, ParagraphsHelperInterface $paragraphs_helper, UserDataInterface $userData) {
+  public function __construct(
+    RendererInterface $renderer,
+    EntityFormBuilder $builder,
+    EntityRepositoryInterface $entity_repository,
+    ParagraphsHelperInterface $paragraphs_helper,
+    UserDataInterface $userData,
+  ) {
     $this->renderer = $renderer;
     $this->builder = $builder;
     $this->entityRepository = $entity_repository;
@@ -94,13 +101,11 @@ class FrontendEditingController extends ControllerBase {
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('renderer'),
+    return new static($container->get('renderer'),
       $container->get('entity.form_builder'),
       $container->get('entity.repository'),
       $container->get('frontend_editing.paragraphs_helper'),
-      $container->get('user.data')
-    );
+      $container->get('user.data'));
   }
 
   /**
@@ -114,31 +119,42 @@ class FrontendEditingController extends ControllerBase {
       throw new NotFoundHttpException();
     }
     // Get current state.
-    $current_state = (bool) $this->userData->get('frontend_editing', $this->currentUser()->id(), 'enabled');
+    $current_state = (bool) $this->userData->get('frontend_editing',
+      $this->currentUser()->id(), 'enabled');
     // Revert it, as requested.
     $new_state = !$current_state;
     // Set the new value.
-    $this->userData->set('frontend_editing', $this->currentUser()->id(), 'enabled', $new_state);
+    $this->userData->set('frontend_editing', $this->currentUser()->id(),
+      'enabled', $new_state);
     // Prepare the response.
     $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-toggle-link', 'text', [$this->t('On')]));
-      $response->addCommand(new InvokeCommand('body', 'removeClass', ['frontend-editing--hidden']));
+      $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-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', [
-      '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-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', [
+        'data-toggle-state',
+        $new_state,
+      ]));
+    $response->addCommand(new InvokeCommand('#frontend-editing-toggle-link',
+      'removeClass', ['frontend-editing-toggle-not-configured']));
 
     return $response;
   }
@@ -169,9 +185,9 @@ class FrontendEditingController extends ControllerBase {
     }
     $entity = $storage->load($id);
     if (!$entity) {
-      $this->messenger()->addWarning($this->t('Entity of type @type and id @id was not found',
-        ['@type' => $type, '@id' => $id]
-      ));
+      $this->messenger()
+        ->addWarning($this->t('Entity of type @type and id @id was not found',
+             ['@type' => $type, '@id' => $id]));
       return [];
     }
     // Remove all messages.
@@ -232,7 +248,8 @@ class FrontendEditingController extends ControllerBase {
           $url = Url::fromRoute('entity.' . $type . '.edit_form', [$type => $id]);
         }
 
-        $entityForm = $this->builder->getForm($entity, $display, $form_state_additions);
+        $entityForm = $this->builder->getForm($entity, $display,
+          $form_state_additions);
         $entityForm['#action'] = $url->toString();
 
         $delete_url = Url::fromRoute('frontend_editing.form', [
@@ -244,7 +261,8 @@ class FrontendEditingController extends ControllerBase {
         if ($entity instanceof ParagraphInterface) {
           $parent_field_name = $entity->get('parent_field_name')->value;
           $parent_entity = $entity->getParentEntity();
-          $parent_field_definition = $parent_entity->get($parent_field_name)->getFieldDefinition();
+          $parent_field_definition = $parent_entity->get($parent_field_name)
+            ->getFieldDefinition();
           if ($parent_entity->isTranslatable() && !$parent_entity->isDefaultTranslation() && !$parent_field_definition->isTranslatable()) {
             $delete_access = FALSE;
           }
@@ -297,7 +315,8 @@ class FrontendEditingController extends ControllerBase {
           // definition will identify that the field is translatable.
           $parent_field_name = $entity->get('parent_field_name')->value;
           $parent_entity = $entity->getParentEntity();
-          $parent_field_definition = $parent_entity->get($parent_field_name)->getFieldDefinition();
+          $parent_field_definition = $parent_entity->get($parent_field_name)
+            ->getFieldDefinition();
           if ($parent_entity->isTranslatable() && !$parent_entity->isDefaultTranslation() && !$parent_field_definition->isTranslatable()) {
             throw new AccessDeniedHttpException('You are not allowed to delete this paragraph, because paragraph parent field is not translatable.');
           }
@@ -314,13 +333,17 @@ class FrontendEditingController extends ControllerBase {
         else {
           $url = Url::fromRoute('entity.' . $type . '.delete_form', [$type => $id]);
         }
-        $entityForm = $this->builder->getForm($entity, $display, $form_state_additions);
+        $entityForm = $this->builder->getForm($entity, $display,
+          $form_state_additions);
         if (function_exists('_gin_form_actions')) {
           // Remove sticky class from form actions.
           array_shift($entityForm['actions']['#attributes']['class']);
         }
         $entityForm['title'] = [
-          '#markup' => '<h3>' . $this->t('Are you sure you want to delete this @type?', ['@type' => $entity->getEntityType()->getSingularLabel()]) . '</h3>',
+          '#markup' => '<h3>' . $this->t('Are you sure you want to delete this @type?',
+              [
+                '@type' => $entity->getEntityType()->getSingularLabel(),
+              ]) . '</h3>',
           '#weight' => -10,
         ];
         $entityForm['#action'] = $url->toString();
@@ -358,8 +381,14 @@ class FrontendEditingController extends ControllerBase {
    * @return \Drupal\Core\Access\AccessResultInterface
    *   The access result.
    */
-  public function accessAddType(ParagraphsTypeInterface $paragraphs_type, $parent_type, $parent, $parent_field_name) {
-    return $this->paragraphsHelper->allowAddType($paragraphs_type, $parent_type, $parent, $parent_field_name);
+  public function accessAddType(
+    ParagraphsTypeInterface $paragraphs_type,
+    $parent_type,
+    $parent,
+    $parent_field_name,
+  ) {
+    return $this->paragraphsHelper->allowAddType($paragraphs_type, $parent_type,
+      $parent, $parent_field_name);
   }
 
   /**
@@ -369,7 +398,8 @@ class FrontendEditingController extends ControllerBase {
    *   The access result.
    */
   public function accessAdd($parent_type, $parent, $parent_field_name) {
-    return $this->paragraphsHelper->allowAdd($parent_type, $parent, $parent_field_name);
+    return $this->paragraphsHelper->allowAdd($parent_type, $parent,
+      $parent_field_name);
   }
 
   /**
@@ -400,8 +430,15 @@ class FrontendEditingController extends ControllerBase {
    * @return \Drupal\Core\Access\AccessResultInterface
    *   The access result.
    */
-  public function accessUpdateContent($entity_type_id, $entity_id, $field_name, $view_mode) {
-    $entity = $this->entityTypeManager()->getStorage($entity_type_id)->load($entity_id);
+  public function accessUpdateContent(
+    $entity_type_id,
+    $entity_id,
+    $field_name,
+    $view_mode,
+  ) {
+    $entity = $this->entityTypeManager()
+      ->getStorage($entity_type_id)
+      ->load($entity_id);
     if (!$entity) {
       $result = AccessResult::forbidden('Entity does not exist.');
     }
@@ -429,26 +466,14 @@ class FrontendEditingController extends ControllerBase {
       $message = $this->t('The paragraph could not be moved up.');
     }
     if ($request->isXmlHttpRequest()) {
-      return $this->ajaxUpdateParagraphs($paragraph, $message, $request->get('view_mode_id', 'default'));
+      return $this->ajaxUpdateParagraphs($paragraph, $message,
+        $request->get('view_mode_id', 'default'));
     }
     if (!empty($message)) {
       $this->messenger()->addError($message);
     }
-    return new RedirectResponse($this->paragraphsHelper->getRedirectUrl($paragraph)->toString());
-  }
-
-  /**
-   * Shift down a single paragraph.
-   */
-  public function down(ParagraphInterface $paragraph, Request $request) {
-    $message = FALSE;
-    if (!$this->paragraphsHelper->move($paragraph, 'down')) {
-      $message = $this->t('The paragraph could not be moved down.');
-    }
-    if ($request->isXmlHttpRequest()) {
-      return $this->ajaxUpdateParagraphs($paragraph, $message, $request->get('view_mode_id', 'default'));
-    }
-    return new RedirectResponse($this->paragraphsHelper->getRedirectUrl($paragraph)->toString());
+    return new RedirectResponse($this->paragraphsHelper->getRedirectUrl($paragraph)
+      ->toString());
   }
 
   /**
@@ -464,10 +489,15 @@ class FrontendEditingController extends ControllerBase {
    * @return \Drupal\Core\Ajax\AjaxResponse
    *   The ajax response.
    */
-  protected function ajaxUpdateParagraphs(ParagraphInterface $paragraph, $message, $view_mode_id = 'default') {
+  protected function ajaxUpdateParagraphs(
+    ParagraphInterface $paragraph,
+    $message,
+    $view_mode_id = 'default',
+  ) {
     $response = new AjaxResponse();
     if ($message) {
-      $response->addCommand(new MessageCommand($message, NULL, ['type' => 'error']));
+      $response->addCommand(new MessageCommand($message, NULL,
+        ['type' => 'error']));
     }
     if ($view_mode_id == '_custom') {
       $view_mode_id = 'default';
@@ -476,11 +506,18 @@ class FrontendEditingController extends ControllerBase {
     $parent_entity = $paragraph->getParentEntity();
     $parent_field_name = $paragraph->get('parent_field_name')->value;
     // Load the view display. We need to know whether it uses layout builder.
-    $view_display_id = implode('.', [$parent_entity->getEntityTypeId(), $parent_entity->bundle(), $view_mode_id]);
+    $view_display_id = implode('.', [
+      $parent_entity->getEntityTypeId(),
+      $parent_entity->bundle(),
+      $view_mode_id,
+    ]);
     /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $view_display */
-    $view_display = $this->entityTypeManager()->getStorage('entity_view_display')->load($view_display_id);
+    $view_display = $this->entityTypeManager()
+      ->getStorage('entity_view_display')
+      ->load($view_display_id);
     if ($view_display) {
-      $is_layout_builder = (bool) $view_display->getThirdPartySetting('layout_builder', 'enabled');
+      $is_layout_builder = (bool) $view_display->getThirdPartySetting('layout_builder',
+        'enabled');
       if ($is_layout_builder) {
         $layout_builder_field_block_id_parts = [
           'field_block',
@@ -488,8 +525,10 @@ class FrontendEditingController extends ControllerBase {
           $parent_entity->bundle(),
           $parent_field_name,
         ];
-        $layout_builder_field_block_id = implode(':', $layout_builder_field_block_id_parts);
-        $sections = $view_display->getThirdPartySetting('layout_builder', 'sections');
+        $layout_builder_field_block_id = implode(':',
+          $layout_builder_field_block_id_parts);
+        $sections = $view_display->getThirdPartySetting('layout_builder',
+          'sections');
         /** @var \Drupal\layout_builder\Section $section */
         foreach ($sections as $section) {
           foreach ($section->getComponents() as $component) {
@@ -512,11 +551,29 @@ class FrontendEditingController extends ControllerBase {
         $updated_content[$delta]['#parent_field_view_mode'] = $updated_content['#view_mode'];
       }
     }
-    $selector = '[data-frontend-editing="' . $paragraph->getParentEntity()->getEntityTypeId() . '--' . $paragraph->getParentEntity()->id() . '--' . $paragraph->get('parent_field_name')->value . '--' . $view_mode_id . '"]';
+    $selector = '[data-frontend-editing="' . $paragraph->getParentEntity()
+      ->getEntityTypeId() . '--' . $paragraph->getParentEntity()
+      ->id() . '--' . $paragraph->get('parent_field_name')->value . '--' . $view_mode_id . '"]';
     $response->addCommand(new HtmlCommand($selector, $updated_content));
     return $response;
   }
 
+  /**
+   * Shift down a single paragraph.
+   */
+  public function down(ParagraphInterface $paragraph, Request $request) {
+    $message = FALSE;
+    if (!$this->paragraphsHelper->move($paragraph, 'down')) {
+      $message = $this->t('The paragraph could not be moved down.');
+    }
+    if ($request->isXmlHttpRequest()) {
+      return $this->ajaxUpdateParagraphs($paragraph, $message,
+        $request->get('view_mode_id', 'default'));
+    }
+    return new RedirectResponse($this->paragraphsHelper->getRedirectUrl($paragraph)
+      ->toString());
+  }
+
   /**
    * Update content with ajax.
    *
@@ -534,7 +591,13 @@ class FrontendEditingController extends ControllerBase {
    * @return \Drupal\Core\Ajax\AjaxResponse
    *   The ajax response.
    */
-  public function updateContent($entity_type_id, $entity_id, $field_name, $view_mode, Request $request) {
+  public function updateContent(
+    $entity_type_id,
+    $entity_id,
+    $field_name,
+    $view_mode,
+    Request $request,
+  ) {
     if (!$request->isXmlHttpRequest()) {
       throw new NotFoundHttpException();
     }
@@ -550,15 +613,15 @@ class FrontendEditingController extends ControllerBase {
     }
     catch (InvalidPluginDefinitionException | PluginNotFoundException $e) {
       $message = $this->t('Entity of type @type and id @id was not found',
-        ['@type' => $entity_type_id, '@id' => $entity_id]
-      );
-      $response->addCommand(new MessageCommand($message, NULL, ['type' => 'error']));
+        ['@type' => $entity_type_id, '@id' => $entity_id]);
+      $response->addCommand(new MessageCommand($message, NULL,
+        ['type' => 'error']));
     }
     if (!$entity) {
       $message = $this->t('Entity of type @type and id @id was not found',
-        ['@type' => $entity_type_id, '@id' => $entity_id]
-      );
-      $response->addCommand(new MessageCommand($message, NULL, ['type' => 'error']));
+        ['@type' => $entity_type_id, '@id' => $entity_id]);
+      $response->addCommand(new MessageCommand($message, NULL,
+        ['type' => 'error']));
     }
     // If there are errors, early return and reload the page.
     if (!empty($response->getCommands())) {
@@ -604,9 +667,17 @@ class FrontendEditingController extends ControllerBase {
    * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
    * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
    */
-  public function paragraphAddPage($parent_type, $parent, $parent_field_name, $current_paragraph, $before, Request $request) {
+  public function paragraphAddPage(
+    $parent_type,
+    $parent,
+    $parent_field_name,
+    $current_paragraph,
+    $before,
+    Request $request,
+  ) {
     try {
-      $parent_entity = $this->entityTypeManager()->getStorage($parent_type)
+      $parent_entity = $this->entityTypeManager()
+        ->getStorage($parent_type)
         ->load($parent);
     }
     catch (InvalidPluginDefinitionException | PluginNotFoundException $e) {
@@ -639,43 +710,64 @@ class FrontendEditingController extends ControllerBase {
       ->loadMultiple($allowed_paragraphs);
     $items = [];
     foreach ($allowed_paragraphs as $paragraphs_type) {
-      $items[$paragraphs_type->id()] = [
-        '#type' => 'link',
-        '#title' => $this->t('Add @type', ['@type' => $paragraphs_type->label()]),
-        '#url' => Url::fromRoute('frontend_editing.paragraph_add', [
-          'parent_type' => $parent_type,
-          'parent' => $parent,
-          'parent_field_name' => $parent_field_name,
-          'paragraphs_type' => $paragraphs_type->id(),
-          'current_paragraph' => $current_paragraph,
-          'before' => $before,
-        ], [
-          'query' => $request->query->all(),
-        ]),
+      $item = [
+        '#type' => 'container',
         '#attributes' => [
-          'class' => 'field-add-more-submit button--small button js-form-submit form-submit',
-          'name' => $parent_field_name . '_' . $paragraphs_type->id() . '_add_more',
+          'class' => [
+            'paragraphs-add-dialog-row',
+            'field-add-more-submit',
+            'button--large',
+            'button',
+            'js-form-submit',
+            'form-submit',
+          ],
         ],
-        '#wrapper_attributes' => [
-          'class' => ['paragraphs-add-dialog-row'],
+        'link' => [
+          '#type' => 'link',
+          '#title' => $this->t('Add @type',
+            ['@type' => $paragraphs_type->label()]),
+          '#url' => Url::fromRoute('frontend_editing.paragraph_add', [
+            'parent_type' => $parent_type,
+            'parent' => $parent,
+            'parent_field_name' => $parent_field_name,
+            'paragraphs_type' => $paragraphs_type->id(),
+            'current_paragraph' => $current_paragraph,
+            'before' => $before,
+          ], [
+            'query' => $request->query->all(),
+          ]),
+          '#attributes' => [
+            'class' => [''],
+            'name' => $parent_field_name . '_' . $paragraphs_type->id() . '_add_more',
+          ],
         ],
       ];
+
+      if (!empty($paragraphs_type->get('icon_default'))) {
+        $item['icon'] = $this->buildIconElement($paragraphs_type);
+      }
+
+      $items[$paragraphs_type->id()] = $item;
     }
     if (!empty($settings['handler_settings']['target_bundles_drag_drop'])) {
-      uasort($settings['handler_settings']['target_bundles_drag_drop'], function ($a, $b) {
-        return $a['weight'] <=> $b['weight'];
-      });
-      if (!empty($settings['handler_settings']['negate'])) {
-        $settings['handler_settings']['target_bundles_drag_drop'] = array_filter($settings['handler_settings']['target_bundles_drag_drop'], function ($value) {
-          return !$value['enabled'];
+      uasort($settings['handler_settings']['target_bundles_drag_drop'],
+        function ($a, $b) {
+          return $a['weight'] <=> $b['weight'];
         });
+      if (!empty($settings['handler_settings']['negate'])) {
+        $settings['handler_settings']['target_bundles_drag_drop'] = array_filter($settings['handler_settings']['target_bundles_drag_drop'],
+          function ($value) {
+            return !$value['enabled'];
+          });
       }
       else {
-        $settings['handler_settings']['target_bundles_drag_drop'] = array_filter($settings['handler_settings']['target_bundles_drag_drop'], function ($value) {
-          return $value['enabled'];
-        });
+        $settings['handler_settings']['target_bundles_drag_drop'] = array_filter($settings['handler_settings']['target_bundles_drag_drop'],
+          function ($value) {
+            return $value['enabled'];
+          });
       }
-      $items = array_replace(array_flip(array_keys($settings['handler_settings']['target_bundles_drag_drop'])), $items);
+      $items = array_replace(array_flip(array_keys($settings['handler_settings']['target_bundles_drag_drop'])),
+        $items);
       $items = array_values($items);
     }
     return [
@@ -693,4 +785,34 @@ class FrontendEditingController extends ControllerBase {
     ];
   }
 
+  /**
+   * Builds the icon element for a given Paragraphs type.
+   *
+   * This method generates a render array for the icon element using the value
+   * from the 'icon_default' field. It assumes that an icon is present and
+   * should only be called when an icon is defined.
+   *
+   * @param \Drupal\paragraphs\ParagraphsTypeInterface $paragraphs_type
+   *   The Paragraphs type entity.
+   *
+   * @return array
+   *   A render array for the icon element.
+   */
+  protected function buildIconElement(
+    EntityInterface $paragraphs_type,
+  ): array {
+    $icon = $paragraphs_type->get('icon_default');
+    return [
+      '#type' => 'container',
+      '#attributes' => [
+        'class' => ['paragraph-type-icon', $paragraphs_type->id()],
+      ],
+      'image' => [
+        '#theme' => 'image',
+        '#uri' => $icon,
+        '#alt' => $paragraphs_type->label(),
+      ],
+    ];
+  }
+
 }
-- 
GitLab


From 9d24982ce61ba45cd634b0cde631df26c2080405 Mon Sep 17 00:00:00 2001
From: "Philipps, Marc" <Marc.Philipps@adesso.de>
Date: Thu, 27 Feb 2025 10:43:39 +0100
Subject: [PATCH 02/10] feat(frontend_editing): display icon_default from
 Paragraphs in overlay with improved styling

Adjusted rendering logic so that when adding Paragraph entities via the frontend editing overlay, the icon_default from the Paragraphs module is displayed if available.
Added CSS classes to enhance the visual appearance of the icon, ensuring a consistent and appealing user interface.
---
 css/forms_helper.css                         |  27 ++
 src/Controller/FrontendEditingController.php | 264 +++++++------------
 2 files changed, 122 insertions(+), 169 deletions(-)

diff --git a/css/forms_helper.css b/css/forms_helper.css
index 923516b..4a03ff1 100644
--- a/css/forms_helper.css
+++ b/css/forms_helper.css
@@ -35,6 +35,8 @@ body.path-frontend-editing {
   width: 100%;
   z-index: 2;
   background-color: #f5f7fb;
+  cursor: pointer;
+  text-decoration: none;
 }
 
 .path-frontend-editing .block-system-main-block > form {
@@ -54,3 +56,28 @@ body.path-frontend-editing {
 .frontend-editing-hide-sticky .region-sticky {
   display: none;
 }
+
+.path-frontend-editing .paragraphs-add-dialog-row {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  width: 100%;
+  gap: 10px;
+}
+.path-frontend-editing .paragraphs-add-dialog-row img {
+  width: 60px;
+  height: 60px;
+  object-fit: contain;
+  border-radius: 5px;
+  background: #fff;
+}
+.path-frontend-editing ul.paragraphs-add-dialog-list li {
+  list-style: none;
+}
+.path-frontend-editing .field-add-more-submit:hover a {
+  color: #fff;
+}
+.path-frontend-editing ul.paragraphs-add-dialog-list .button.button--small {
+  padding-left: unset;
+  border: unset;
+}
\ No newline at end of file
diff --git a/src/Controller/FrontendEditingController.php b/src/Controller/FrontendEditingController.php
index 26ff965..e2c4d84 100644
--- a/src/Controller/FrontendEditingController.php
+++ b/src/Controller/FrontendEditingController.php
@@ -83,13 +83,7 @@ class FrontendEditingController extends ControllerBase {
    * @param \Drupal\user\UserDataInterface $userData
    *   The user data storage.
    */
-  public function __construct(
-    RendererInterface $renderer,
-    EntityFormBuilder $builder,
-    EntityRepositoryInterface $entity_repository,
-    ParagraphsHelperInterface $paragraphs_helper,
-    UserDataInterface $userData,
-  ) {
+  public function __construct(RendererInterface $renderer, EntityFormBuilder $builder, EntityRepositoryInterface $entity_repository, ParagraphsHelperInterface $paragraphs_helper, UserDataInterface $userData) {
     $this->renderer = $renderer;
     $this->builder = $builder;
     $this->entityRepository = $entity_repository;
@@ -101,11 +95,13 @@ class FrontendEditingController extends ControllerBase {
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container) {
-    return new static($container->get('renderer'),
+    return new static(
+      $container->get('renderer'),
       $container->get('entity.form_builder'),
       $container->get('entity.repository'),
       $container->get('frontend_editing.paragraphs_helper'),
-      $container->get('user.data'));
+      $container->get('user.data')
+    );
   }
 
   /**
@@ -119,42 +115,31 @@ class FrontendEditingController extends ControllerBase {
       throw new NotFoundHttpException();
     }
     // Get current state.
-    $current_state = (bool) $this->userData->get('frontend_editing',
-      $this->currentUser()->id(), 'enabled');
+    $current_state = (bool) $this->userData->get('frontend_editing', $this->currentUser()->id(), 'enabled');
     // Revert it, as requested.
     $new_state = !$current_state;
     // Set the new value.
-    $this->userData->set('frontend_editing', $this->currentUser()->id(),
-      'enabled', $new_state);
+    $this->userData->set('frontend_editing', $this->currentUser()->id(), 'enabled', $new_state);
     // Prepare the response.
     $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-toggle-link',
-        'text', [$this->t('On')]));
-      $response->addCommand(new InvokeCommand('body', 'removeClass',
-        ['frontend-editing--hidden']));
+      $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-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', [
-        '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-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', [
+      'data-toggle-state',
+      $new_state,
+    ]));
+    $response->addCommand(new InvokeCommand('#frontend-editing-toggle-link', 'removeClass', ['frontend-editing-toggle-not-configured']));
 
     return $response;
   }
@@ -185,9 +170,9 @@ class FrontendEditingController extends ControllerBase {
     }
     $entity = $storage->load($id);
     if (!$entity) {
-      $this->messenger()
-        ->addWarning($this->t('Entity of type @type and id @id was not found',
-             ['@type' => $type, '@id' => $id]));
+      $this->messenger()->addWarning($this->t('Entity of type @type and id @id was not found',
+        ['@type' => $type, '@id' => $id]
+      ));
       return [];
     }
     // Remove all messages.
@@ -248,8 +233,7 @@ class FrontendEditingController extends ControllerBase {
           $url = Url::fromRoute('entity.' . $type . '.edit_form', [$type => $id]);
         }
 
-        $entityForm = $this->builder->getForm($entity, $display,
-          $form_state_additions);
+        $entityForm = $this->builder->getForm($entity, $display, $form_state_additions);
         $entityForm['#action'] = $url->toString();
 
         $delete_url = Url::fromRoute('frontend_editing.form', [
@@ -261,8 +245,7 @@ class FrontendEditingController extends ControllerBase {
         if ($entity instanceof ParagraphInterface) {
           $parent_field_name = $entity->get('parent_field_name')->value;
           $parent_entity = $entity->getParentEntity();
-          $parent_field_definition = $parent_entity->get($parent_field_name)
-            ->getFieldDefinition();
+          $parent_field_definition = $parent_entity->get($parent_field_name)->getFieldDefinition();
           if ($parent_entity->isTranslatable() && !$parent_entity->isDefaultTranslation() && !$parent_field_definition->isTranslatable()) {
             $delete_access = FALSE;
           }
@@ -315,8 +298,7 @@ class FrontendEditingController extends ControllerBase {
           // definition will identify that the field is translatable.
           $parent_field_name = $entity->get('parent_field_name')->value;
           $parent_entity = $entity->getParentEntity();
-          $parent_field_definition = $parent_entity->get($parent_field_name)
-            ->getFieldDefinition();
+          $parent_field_definition = $parent_entity->get($parent_field_name)->getFieldDefinition();
           if ($parent_entity->isTranslatable() && !$parent_entity->isDefaultTranslation() && !$parent_field_definition->isTranslatable()) {
             throw new AccessDeniedHttpException('You are not allowed to delete this paragraph, because paragraph parent field is not translatable.');
           }
@@ -333,17 +315,13 @@ class FrontendEditingController extends ControllerBase {
         else {
           $url = Url::fromRoute('entity.' . $type . '.delete_form', [$type => $id]);
         }
-        $entityForm = $this->builder->getForm($entity, $display,
-          $form_state_additions);
+        $entityForm = $this->builder->getForm($entity, $display, $form_state_additions);
         if (function_exists('_gin_form_actions')) {
           // Remove sticky class from form actions.
           array_shift($entityForm['actions']['#attributes']['class']);
         }
         $entityForm['title'] = [
-          '#markup' => '<h3>' . $this->t('Are you sure you want to delete this @type?',
-              [
-                '@type' => $entity->getEntityType()->getSingularLabel(),
-              ]) . '</h3>',
+          '#markup' => '<h3>' . $this->t('Are you sure you want to delete this @type?', ['@type' => $entity->getEntityType()->getSingularLabel()]) . '</h3>',
           '#weight' => -10,
         ];
         $entityForm['#action'] = $url->toString();
@@ -381,14 +359,8 @@ class FrontendEditingController extends ControllerBase {
    * @return \Drupal\Core\Access\AccessResultInterface
    *   The access result.
    */
-  public function accessAddType(
-    ParagraphsTypeInterface $paragraphs_type,
-    $parent_type,
-    $parent,
-    $parent_field_name,
-  ) {
-    return $this->paragraphsHelper->allowAddType($paragraphs_type, $parent_type,
-      $parent, $parent_field_name);
+  public function accessAddType(ParagraphsTypeInterface $paragraphs_type, $parent_type, $parent, $parent_field_name) {
+    return $this->paragraphsHelper->allowAddType($paragraphs_type, $parent_type, $parent, $parent_field_name);
   }
 
   /**
@@ -398,8 +370,7 @@ class FrontendEditingController extends ControllerBase {
    *   The access result.
    */
   public function accessAdd($parent_type, $parent, $parent_field_name) {
-    return $this->paragraphsHelper->allowAdd($parent_type, $parent,
-      $parent_field_name);
+    return $this->paragraphsHelper->allowAdd($parent_type, $parent, $parent_field_name);
   }
 
   /**
@@ -430,15 +401,8 @@ class FrontendEditingController extends ControllerBase {
    * @return \Drupal\Core\Access\AccessResultInterface
    *   The access result.
    */
-  public function accessUpdateContent(
-    $entity_type_id,
-    $entity_id,
-    $field_name,
-    $view_mode,
-  ) {
-    $entity = $this->entityTypeManager()
-      ->getStorage($entity_type_id)
-      ->load($entity_id);
+  public function accessUpdateContent($entity_type_id, $entity_id, $field_name, $view_mode) {
+    $entity = $this->entityTypeManager()->getStorage($entity_type_id)->load($entity_id);
     if (!$entity) {
       $result = AccessResult::forbidden('Entity does not exist.');
     }
@@ -466,14 +430,26 @@ class FrontendEditingController extends ControllerBase {
       $message = $this->t('The paragraph could not be moved up.');
     }
     if ($request->isXmlHttpRequest()) {
-      return $this->ajaxUpdateParagraphs($paragraph, $message,
-        $request->get('view_mode_id', 'default'));
+      return $this->ajaxUpdateParagraphs($paragraph, $message, $request->get('view_mode_id', 'default'));
     }
     if (!empty($message)) {
       $this->messenger()->addError($message);
     }
-    return new RedirectResponse($this->paragraphsHelper->getRedirectUrl($paragraph)
-      ->toString());
+    return new RedirectResponse($this->paragraphsHelper->getRedirectUrl($paragraph)->toString());
+  }
+
+  /**
+   * Shift down a single paragraph.
+   */
+  public function down(ParagraphInterface $paragraph, Request $request) {
+    $message = FALSE;
+    if (!$this->paragraphsHelper->move($paragraph, 'down')) {
+      $message = $this->t('The paragraph could not be moved down.');
+    }
+    if ($request->isXmlHttpRequest()) {
+      return $this->ajaxUpdateParagraphs($paragraph, $message, $request->get('view_mode_id', 'default'));
+    }
+    return new RedirectResponse($this->paragraphsHelper->getRedirectUrl($paragraph)->toString());
   }
 
   /**
@@ -489,15 +465,10 @@ class FrontendEditingController extends ControllerBase {
    * @return \Drupal\Core\Ajax\AjaxResponse
    *   The ajax response.
    */
-  protected function ajaxUpdateParagraphs(
-    ParagraphInterface $paragraph,
-    $message,
-    $view_mode_id = 'default',
-  ) {
+  protected function ajaxUpdateParagraphs(ParagraphInterface $paragraph, $message, $view_mode_id = 'default') {
     $response = new AjaxResponse();
     if ($message) {
-      $response->addCommand(new MessageCommand($message, NULL,
-        ['type' => 'error']));
+      $response->addCommand(new MessageCommand($message, NULL, ['type' => 'error']));
     }
     if ($view_mode_id == '_custom') {
       $view_mode_id = 'default';
@@ -506,18 +477,11 @@ class FrontendEditingController extends ControllerBase {
     $parent_entity = $paragraph->getParentEntity();
     $parent_field_name = $paragraph->get('parent_field_name')->value;
     // Load the view display. We need to know whether it uses layout builder.
-    $view_display_id = implode('.', [
-      $parent_entity->getEntityTypeId(),
-      $parent_entity->bundle(),
-      $view_mode_id,
-    ]);
+    $view_display_id = implode('.', [$parent_entity->getEntityTypeId(), $parent_entity->bundle(), $view_mode_id]);
     /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $view_display */
-    $view_display = $this->entityTypeManager()
-      ->getStorage('entity_view_display')
-      ->load($view_display_id);
+    $view_display = $this->entityTypeManager()->getStorage('entity_view_display')->load($view_display_id);
     if ($view_display) {
-      $is_layout_builder = (bool) $view_display->getThirdPartySetting('layout_builder',
-        'enabled');
+      $is_layout_builder = (bool) $view_display->getThirdPartySetting('layout_builder', 'enabled');
       if ($is_layout_builder) {
         $layout_builder_field_block_id_parts = [
           'field_block',
@@ -525,10 +489,8 @@ class FrontendEditingController extends ControllerBase {
           $parent_entity->bundle(),
           $parent_field_name,
         ];
-        $layout_builder_field_block_id = implode(':',
-          $layout_builder_field_block_id_parts);
-        $sections = $view_display->getThirdPartySetting('layout_builder',
-          'sections');
+        $layout_builder_field_block_id = implode(':', $layout_builder_field_block_id_parts);
+        $sections = $view_display->getThirdPartySetting('layout_builder', 'sections');
         /** @var \Drupal\layout_builder\Section $section */
         foreach ($sections as $section) {
           foreach ($section->getComponents() as $component) {
@@ -551,29 +513,11 @@ class FrontendEditingController extends ControllerBase {
         $updated_content[$delta]['#parent_field_view_mode'] = $updated_content['#view_mode'];
       }
     }
-    $selector = '[data-frontend-editing="' . $paragraph->getParentEntity()
-      ->getEntityTypeId() . '--' . $paragraph->getParentEntity()
-      ->id() . '--' . $paragraph->get('parent_field_name')->value . '--' . $view_mode_id . '"]';
+    $selector = '[data-frontend-editing="' . $paragraph->getParentEntity()->getEntityTypeId() . '--' . $paragraph->getParentEntity()->id() . '--' . $paragraph->get('parent_field_name')->value . '--' . $view_mode_id . '"]';
     $response->addCommand(new HtmlCommand($selector, $updated_content));
     return $response;
   }
 
-  /**
-   * Shift down a single paragraph.
-   */
-  public function down(ParagraphInterface $paragraph, Request $request) {
-    $message = FALSE;
-    if (!$this->paragraphsHelper->move($paragraph, 'down')) {
-      $message = $this->t('The paragraph could not be moved down.');
-    }
-    if ($request->isXmlHttpRequest()) {
-      return $this->ajaxUpdateParagraphs($paragraph, $message,
-        $request->get('view_mode_id', 'default'));
-    }
-    return new RedirectResponse($this->paragraphsHelper->getRedirectUrl($paragraph)
-      ->toString());
-  }
-
   /**
    * Update content with ajax.
    *
@@ -591,13 +535,7 @@ class FrontendEditingController extends ControllerBase {
    * @return \Drupal\Core\Ajax\AjaxResponse
    *   The ajax response.
    */
-  public function updateContent(
-    $entity_type_id,
-    $entity_id,
-    $field_name,
-    $view_mode,
-    Request $request,
-  ) {
+  public function updateContent($entity_type_id, $entity_id, $field_name, $view_mode, Request $request) {
     if (!$request->isXmlHttpRequest()) {
       throw new NotFoundHttpException();
     }
@@ -613,15 +551,15 @@ class FrontendEditingController extends ControllerBase {
     }
     catch (InvalidPluginDefinitionException | PluginNotFoundException $e) {
       $message = $this->t('Entity of type @type and id @id was not found',
-        ['@type' => $entity_type_id, '@id' => $entity_id]);
-      $response->addCommand(new MessageCommand($message, NULL,
-        ['type' => 'error']));
+        ['@type' => $entity_type_id, '@id' => $entity_id]
+      );
+      $response->addCommand(new MessageCommand($message, NULL, ['type' => 'error']));
     }
     if (!$entity) {
       $message = $this->t('Entity of type @type and id @id was not found',
-        ['@type' => $entity_type_id, '@id' => $entity_id]);
-      $response->addCommand(new MessageCommand($message, NULL,
-        ['type' => 'error']));
+        ['@type' => $entity_type_id, '@id' => $entity_id]
+      );
+      $response->addCommand(new MessageCommand($message, NULL, ['type' => 'error']));
     }
     // If there are errors, early return and reload the page.
     if (!empty($response->getCommands())) {
@@ -667,17 +605,9 @@ class FrontendEditingController extends ControllerBase {
    * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
    * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
    */
-  public function paragraphAddPage(
-    $parent_type,
-    $parent,
-    $parent_field_name,
-    $current_paragraph,
-    $before,
-    Request $request,
-  ) {
+  public function paragraphAddPage($parent_type, $parent, $parent_field_name, $current_paragraph, $before, Request $request) {
     try {
-      $parent_entity = $this->entityTypeManager()
-        ->getStorage($parent_type)
+      $parent_entity = $this->entityTypeManager()->getStorage($parent_type)
         ->load($parent);
     }
     catch (InvalidPluginDefinitionException | PluginNotFoundException $e) {
@@ -722,52 +652,48 @@ class FrontendEditingController extends ControllerBase {
             'form-submit',
           ],
         ],
-        'link' => [
-          '#type' => 'link',
-          '#title' => $this->t('Add @type',
-            ['@type' => $paragraphs_type->label()]),
-          '#url' => Url::fromRoute('frontend_editing.paragraph_add', [
-            'parent_type' => $parent_type,
-            'parent' => $parent,
-            'parent_field_name' => $parent_field_name,
-            'paragraphs_type' => $paragraphs_type->id(),
-            'current_paragraph' => $current_paragraph,
-            'before' => $before,
-          ], [
-            'query' => $request->query->all(),
-          ]),
-          '#attributes' => [
-            'class' => [''],
-            'name' => $parent_field_name . '_' . $paragraphs_type->id() . '_add_more',
-          ],
-        ],
       ];
 
       if (!empty($paragraphs_type->get('icon_default'))) {
         $item['icon'] = $this->buildIconElement($paragraphs_type);
       }
 
+      $item['link'] = [
+        '#type' => 'link',
+        '#title' => $this->t('Add @type', ['@type' => $paragraphs_type->label()]),
+        '#url' => Url::fromRoute('frontend_editing.paragraph_add', [
+          'parent_type' => $parent_type,
+          'parent' => $parent,
+          'parent_field_name' => $parent_field_name,
+          'paragraphs_type' => $paragraphs_type->id(),
+          'current_paragraph' => $current_paragraph,
+          'before' => $before,
+        ], [
+          'query' => $request->query->all(),
+        ]),
+        '#attributes' => [
+          'class' => 'field-add-more-submit js-form-submit form-submit',
+          'name' => $parent_field_name . '_' . $paragraphs_type->id() . '_add_more',
+        ],
+      ];
+
       $items[$paragraphs_type->id()] = $item;
     }
     if (!empty($settings['handler_settings']['target_bundles_drag_drop'])) {
-      uasort($settings['handler_settings']['target_bundles_drag_drop'],
-        function ($a, $b) {
-          return $a['weight'] <=> $b['weight'];
-        });
+      uasort($settings['handler_settings']['target_bundles_drag_drop'], function ($a, $b) {
+        return $a['weight'] <=> $b['weight'];
+      });
       if (!empty($settings['handler_settings']['negate'])) {
-        $settings['handler_settings']['target_bundles_drag_drop'] = array_filter($settings['handler_settings']['target_bundles_drag_drop'],
-          function ($value) {
-            return !$value['enabled'];
-          });
+        $settings['handler_settings']['target_bundles_drag_drop'] = array_filter($settings['handler_settings']['target_bundles_drag_drop'], function ($value) {
+          return !$value['enabled'];
+        });
       }
       else {
-        $settings['handler_settings']['target_bundles_drag_drop'] = array_filter($settings['handler_settings']['target_bundles_drag_drop'],
-          function ($value) {
-            return $value['enabled'];
-          });
+        $settings['handler_settings']['target_bundles_drag_drop'] = array_filter($settings['handler_settings']['target_bundles_drag_drop'], function ($value) {
+          return $value['enabled'];
+        });
       }
-      $items = array_replace(array_flip(array_keys($settings['handler_settings']['target_bundles_drag_drop'])),
-        $items);
+      $items = array_replace(array_flip(array_keys($settings['handler_settings']['target_bundles_drag_drop'])), $items);
       $items = array_values($items);
     }
     return [
-- 
GitLab


From 80dfdece0e5fc424c13772155ef7cab6aa104e56 Mon Sep 17 00:00:00 2001
From: "Philipps, Marc" <Marc.Philipps@adesso.de>
Date: Wed, 26 Feb 2025 07:22:13 +0100
Subject: [PATCH 03/10] feat(frontend_editing): display icon_default from
 Paragraphs in overlay with improved styling

Adjusted rendering logic so that when adding Paragraph entities via the frontend editing overlay, the icon_default from the Paragraphs module is displayed if available.
Added CSS classes to enhance the visual appearance of the icon, ensuring a consistent and appealing user interface.
---
 css/frontend_editing.css                     | 171 +++++-----
 src/Controller/FrontendEditingController.php | 308 +++++++++++++------
 2 files changed, 308 insertions(+), 171 deletions(-)

diff --git a/css/frontend_editing.css b/css/frontend_editing.css
index 4bd208a..bf4b9d1 100644
--- a/css/frontend_editing.css
+++ b/css/frontend_editing.css
@@ -5,29 +5,29 @@ body.path-frontend-editing {
 }
 
 .editing-container {
-  height: 100%;
   position: fixed;
+  z-index: 1000;
   top: 0;
   right: 0;
-  z-index: 1000;
-  background-color: white;
-  box-shadow: -0.2rem 0 1rem rgb(116 116 116 / 40%);
+  overflow: auto;
   width: 30%;
   min-width: 300px;
+  height: 100%;
   transition: width 0.5s;
-  overflow: auto;
+  background-color: white;
+  box-shadow: -0.2rem 0 1rem rgb(116, 116, 116, 0.4);
 }
 .editing-container--wide {
   width: 80%;
 }
 .editing-container--loading {
-  animation-duration: 1.8s;
-  animation-fill-mode: forwards;
-  animation-iteration-count: infinite;
   animation-name: placeholder-shimmer;
+  animation-duration: 1.8s;
   animation-timing-function: linear;
+  animation-iteration-count: infinite;
   background: linear-gradient(to right, #f5f7fb 8%, #e7e7e7 38%, #f5f7fb 54%);
   background-size: 1000px 640px;
+  animation-fill-mode: forwards;
 }
 @keyframes placeholder-shimmer {
   0% {
@@ -40,14 +40,14 @@ body.path-frontend-editing {
 
 /* Styles for action buttons */
 .editing-container__actions {
+  position: absolute;
   display: flex;
-  height: 0;
   align-items: center;
   justify-content: space-between;
-  padding: 0 1rem;
-  position: absolute;
-  width: 100%;
   box-sizing: border-box;
+  width: 100%;
+  height: 0;
+  padding: 0 1rem;
 }
 
 .editing-container__actions button {
@@ -61,7 +61,8 @@ button.editing-container__close {
   height: 24px;
   cursor: pointer;
   border: none;
-  background: url('data:image/svg+xml,%3Csvg width="18" height="18" fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Cpath d="M17 1L9 9l8 8M1 1l8 8-8 8" stroke="%23222330" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/%3E%3C/svg%3E') center no-repeat;
+  background: url('data:image/svg+xml,%3Csvg width="18" height="18" fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Cpath d="M17 1L9 9l8 8M1 1l8 8-8 8" stroke="%23222330" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/%3E%3C/svg%3E')
+    center no-repeat;
 }
 
 /* Styles for width button */
@@ -70,28 +71,30 @@ button.editing-container__toggle {
   height: 24px;
   cursor: pointer;
   border: none;
-  background: url('data:image/svg+xml,%3Csvg width="10" height="18" fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Cpath d="M9 1L1 9l8 8" stroke="%23222330" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/%3E%3C/svg%3E') center no-repeat;
+  background: url('data:image/svg+xml,%3Csvg width="10" height="18" fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Cpath d="M9 1L1 9l8 8" stroke="%23222330" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/%3E%3C/svg%3E')
+    center no-repeat;
 }
 
 .editing-container--wide .editing-container__toggle {
-  background: url('data:image/svg+xml,%3Csvg width="10" height="18" fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Cpath d="M9 1L1 9l8 8" stroke="%23222330" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round" transform="rotate(180 5 9)"/%3E%3C/svg%3E') center no-repeat;
+  background: url('data:image/svg+xml,%3Csvg width="10" height="18" fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Cpath d="M9 1L1 9l8 8" stroke="%23222330" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round" transform="rotate(180 5 9)"/%3E%3C/svg%3E')
+    center no-repeat;
 }
 
-/* Styles for iframe*/
+/* Styles for iframe */
 #editing-container iframe {
+  width: 100%;
   height: 100%;
   border: none;
-  width: 100%;
 }
 
 .frontend-editing-preview-content,
 .frontend-editing-update-content {
+  visibility: hidden;
   overflow: hidden;
-  text-indent: -9999rem;
+  order: 100;
   width: 32px;
   height: 32px;
-  visibility: hidden;
-  order: 100;
+  text-indent: -9999rem;
 }
 
 /* Style container of editable elements */
@@ -99,7 +102,9 @@ button.editing-container__toggle {
   position: relative;
 }
 
-.frontend-editing:has(.frontend-editing:hover) > .frontend-editing-actions .add-paragraphs {
+.frontend-editing:has(.frontend-editing:hover)
+  > .frontend-editing-actions
+  .add-paragraphs {
   display: none;
 }
 
@@ -107,20 +112,23 @@ button.editing-container__toggle {
   display: none;
 }
 
-body:not(.frontend-editing--hidden) .frontend-editing:has(.add-paragraphs.hover-highlight) {
+body:not(.frontend-editing--hidden)
+  .frontend-editing:has(.add-paragraphs.hover-highlight) {
   transition: padding ease-in-out 0.4s;
 }
 
-body:not(.frontend-editing--hidden) .frontend-editing:has(.add-paragraphs.hover-highlight):hover {
+body:not(.frontend-editing--hidden)
+  .frontend-editing:has(.add-paragraphs.hover-highlight):hover {
   padding: 25px 0;
 }
 
 body:not(.frontend-editing--hidden) .frontend-editing:hover {
-  outline: 3px solid rgba(var(--fe-editing-primary-color), 0.8);
   z-index: 10;
+  outline: 3px solid rgba(var(--fe-editing-primary-color), 0.8);
 }
 
-body:not(.frontend-editing--hidden) .frontend-editing:has(.frontend-editing:hover) {
+body:not(.frontend-editing--hidden)
+  .frontend-editing:has(.frontend-editing:hover) {
   outline: 3px solid rgba(var(--fe-editing-primary-color), 0.2);
 }
 
@@ -128,40 +136,42 @@ body:not(.frontend-editing--hidden) .frontend-editing:has(.frontend-editing:hove
   display: none;
 }
 
-.frontend-editing:hover .frontend-editing:not(:hover) .frontend-editing-actions {
+.frontend-editing:hover
+  .frontend-editing:not(:hover)
+  .frontend-editing-actions {
   display: none;
 }
 
 /* Styled for editing action */
 .common-actions-container {
-  display: flex;
   position: absolute;
+  z-index: 2;
   top: 10px;
   left: 0;
+  display: flex;
   width: auto;
-  z-index: 2;
 }
 
 .title-edit-container,
 .icons-container {
-  margin-left: 0.5rem;
   margin-right: 0.5rem;
+  margin-left: 0.5rem;
 }
 
 .action-title {
-  opacity: 0;
   display: block;
+  align-self: center;
   width: 100%;
+  padding-top: 5px;
+  padding-bottom: 5px;
+  padding-left: 15px;
+  white-space: nowrap;
   letter-spacing: 0.2em;
   pointer-events: none;
-  font-weight: 500;
-  font-size: 0.75rem;
+  opacity: 0;
   color: #222330;
-  white-space: nowrap;
-  align-self: center;
-  padding-left: 15px;
-  padding-top: 5px;
-  padding-bottom: 5px;
+  font-size: 0.75rem;
+  font-weight: 500;
 }
 
 .frontend-editing:hover .frontend-editing__action,
@@ -172,23 +182,23 @@ body:not(.frontend-editing--hidden) .frontend-editing:has(.frontend-editing:hove
 .frontend-editing:hover .title-edit-container,
 .frontend-editing:hover .icons-container {
   display: flex;
-  border-radius: 8px;
-  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
   border: 1px solid #d3d3d3;
+  border-radius: 8px;
   background-color: white;
+  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
 }
 
 .frontend-editing-actions .ajax-progress {
   display: inline-flex;
   justify-content: center;
+  /* Ensure AJAX throbber is always last item */
+  order: 6;
   width: 32px;
   height: 32px;
   padding: 0;
-  /* Ensure AJAX throbber is always last item */
-  order: 6;
   opacity: 1;
-  background: #e8e8e8;
   border-left: 1px solid #dddcdc;
+  background: #e8e8e8;
 }
 
 .frontend-editing-actions .ajax-progress .throbber {
@@ -197,28 +207,28 @@ body:not(.frontend-editing--hidden) .frontend-editing:has(.frontend-editing:hove
   padding: 0;
   opacity: 0.4;
   background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='36' viewBox='0 0 24 24'%3E%3Cstyle%3E@keyframes spinner%7Bto%7Btransform:rotate(360deg)%7D%7D%3C/style%3E%3Cpath d='M12 4a8 8 0 0 1 7.89 6.7 1.53 1.53 0 0 0 1.49 1.3 1.5 1.5 0 0 0 1.48-1.75 11 11 0 0 0-21.72 0A1.5 1.5 0 0 0 2.62 12a1.53 1.53 0 0 0 1.49-1.3A8 8 0 0 1 12 4Z' style='transform-origin:center;animation:spinner .75s infinite linear'/%3E%3C/svg%3E");
-  background-size: 18px;
   background-position: center;
+  background-size: 18px;
 }
 
-.frontend-editing--outline:hover .frontend-editing-actions:before {
+.frontend-editing--outline:hover .frontend-editing-actions::before {
   opacity: 1;
 }
 
 /* Style editing icon */
 .frontend-editing__action {
+  z-index: 1;
   display: inline-block;
+  overflow: hidden;
   width: 37px;
   height: 32px;
-  z-index: 1;
-  opacity: 0;
   transition-duration: 0s;
-  background-position: center;
-  overflow: hidden;
   text-indent: -9999rem;
-  font-size: 0px;
+  opacity: 0;
   color: transparent;
   border-right: 1px solid #dddcdc;
+  background-position: center;
+  font-size: 0;
 }
 
 .frontend-editing__action--edit {
@@ -239,23 +249,25 @@ body:not(.frontend-editing--hidden) .frontend-editing:has(.frontend-editing:hove
 
 .frontend-editing__action--edit:hover,
 .frontend-editing__action--down:hover {
-  background-color: rgba(var(--fe-editing-primary-color), 0.25);
   border-top-right-radius: 7px;
   border-bottom-right-radius: 7px;
+  background-color: rgba(var(--fe-editing-primary-color), 0.25);
 }
 
 .frontend-editing__action--up:hover {
-  background-color: rgba(var(--fe-editing-primary-color), 0.25);
   border-top-left-radius: 7px;
   border-bottom-left-radius: 7px;
+  background-color: rgba(var(--fe-editing-primary-color), 0.25);
 }
 
-.move-paragraphs.icons-container:not(:has(.frontend-editing__action--up)) .frontend-editing__action--down:hover {
+.move-paragraphs.icons-container:not(:has(.frontend-editing__action--up))
+  .frontend-editing__action--down:hover {
   border-top-left-radius: 7px;
   border-bottom-left-radius: 7px;
 }
 
-.move-paragraphs.icons-container:not(:has(.frontend-editing__action--down)) .frontend-editing__action--up:hover {
+.move-paragraphs.icons-container:not(:has(.frontend-editing__action--down))
+  .frontend-editing__action--up:hover {
   border-top-right-radius: 7px;
   border-bottom-right-radius: 7px;
 }
@@ -264,31 +276,31 @@ body:not(.frontend-editing--hidden) .frontend-editing:has(.frontend-editing:hove
   border-right-width: 0;
 }
 
-.frontend-editing__action:hover:before {
+.frontend-editing__action:hover::before {
   opacity: 1;
 }
 
 .add-paragraphs {
-  height: 0;
   width: 0;
+  height: 0;
 }
 
 .add-paragraphs a {
-  display: flex;
   position: absolute;
-  left: 0;
+  z-index: 1;
   right: 0;
+  left: 0;
+  display: flex;
+  overflow: visible;
   width: fit-content;
   height: 25px;
   margin: auto;
   border: 0;
-  overflow: visible;
-  z-index: 1;
 }
 
 .add-paragraphs.hover-highlight a {
-  background-color: rgba(var(--fe-editing-primary-color), 0.25);
   width: 100%;
+  background-color: rgba(var(--fe-editing-primary-color), 0.25);
 }
 
 .add-paragraphs a:hover {
@@ -296,7 +308,7 @@ body:not(.frontend-editing--hidden) .frontend-editing:has(.frontend-editing:hove
 }
 
 .frontend-editing:hover .add-paragraphs.icons-container {
-  border-width: 0px;
+  border-width: 0;
 }
 
 .frontend-editing .move-paragraphs.icons-container:not(:has(*)) {
@@ -315,26 +327,29 @@ body:not(.frontend-editing--hidden) .frontend-editing:has(.frontend-editing:hove
   pointer-events: none;
 }
 
-.frontend-editing__action:not(.frontend-editing__action--before):not(.frontend-editing__action--after):before {
+.frontend-editing__action:not(
+    .frontend-editing__action--before,
+    .frontend-editing__action--after
+  )::before {
   display: block;
-  content: '';
   width: 100%;
   height: 100%;
-  background-size: 16px;
+  content: "";
   background-repeat: no-repeat;
   background-position: center;
+  background-size: 16px;
 }
 
 .frontend-editing__action--before::before,
 .frontend-editing__action--after::before {
-  content: ' ';
+  position: absolute;
   z-index: 10;
-  width: 40px;
-  height: 40px;
-  left: 0;
   right: 0;
+  left: 0;
   display: block;
-  position: absolute;
+  width: 40px;
+  height: 40px;
+  content: " ";
 }
 
 .frontend-editing__action--before::before {
@@ -342,41 +357,41 @@ body:not(.frontend-editing--hidden) .frontend-editing:has(.frontend-editing:hove
 }
 
 .frontend-editing__action--before svg {
-  pointer-events: none;
   z-index: 10;
-  margin: -1.5rem auto 0 auto;
   width: 40px;
   height: 40px;
+  margin: -1.5rem auto 0 auto;
+  pointer-events: none;
   color: rgb(var(--fe-editing-primary-color));
 }
 
 .frontend-editing__action--after::before {
+  bottom: 0;
   margin: auto;
   margin-bottom: -1.5rem;
-  bottom: 0;
 }
 
 .frontend-editing__action--after svg {
-  pointer-events: none;
   z-index: 10;
-  margin: auto;
-  margin-bottom: -1.5rem;
   width: 40px;
   height: 40px;
+  margin: auto;
+  margin-bottom: -1.5rem;
+  pointer-events: none;
   color: rgb(var(--fe-editing-primary-color));
 }
 
-.frontend-editing__action--up:before {
+.frontend-editing__action--up::before {
   background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzIzMjIyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTIgNGExIDEgMCAwIDEgLjcwNy4yOTNsNyA3YTEgMSAwIDAgMS0xLjQxNCAxLjQxNEwxMyA3LjQxNFYxOWExIDEgMCAxIDEtMiAwVjcuNDE0bC01LjI5MyA1LjI5M2ExIDEgMCAwIDEtMS40MTQtMS40MTRsNy03QTEgMSAwIDAgMSAxMiA0WiIgY2xpcC1ydWxlPSJldmVub2RkIi8+PC9zdmc+");
   background-size: 20px;
 }
 
-.frontend-editing__action--down:before {
+.frontend-editing__action--down::before {
   background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzIzMjIyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTMgNWExIDEgMCAxIDAtMiAwdjExLjU4NmwtNS4yOTMtNS4yOTNhMSAxIDAgMSAwLTEuNDE0IDEuNDE0bDYuOTk1IDYuOTk1QS45OTMuOTkzIDAgMCAwIDEyIDIwYS45OTcuOTk3IDAgMCAwIC43MTItLjI5OGw2Ljk5NS02Ljk5NWExIDEgMCAwIDAtMS40MTQtMS40MTRMMTMgMTYuNTg2VjVaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4=");
   background-size: 20px;
 }
 
-.frontend-editing__action--edit:before {
+.frontend-editing__action--edit::before {
   background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzIzMjIyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTkgMy4xNzFhMS44MjkgMS44MjkgMCAwIDAtMS4yOTMuNTM2TDQuMzk1IDE3LjAxOWwtLjk3IDMuNTU2IDMuNTU2LS45N0wyMC4yOTMgNi4yOTNBMS44MjkgMS44MjkgMCAwIDAgMTkgMy4xN1ptLTEuNDY1LTEuNzA4YTMuODI5IDMuODI5IDAgMCAxIDQuMTcyIDYuMjQ0bC0xMy41IDEzLjVhMSAxIDAgMCAxLS40NDQuMjU4bC01LjUgMS41YTEgMSAwIDAgMS0xLjIyOC0xLjIyOGwxLjUtNS41YTEgMSAwIDAgMSAuMjU4LS40NDRsMTMuNS0xMy41YTMuODI5IDMuODI5IDAgMCAxIDEuMjQyLS44M1oiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==");
 }
 
diff --git a/src/Controller/FrontendEditingController.php b/src/Controller/FrontendEditingController.php
index b1dc981..26ff965 100644
--- a/src/Controller/FrontendEditingController.php
+++ b/src/Controller/FrontendEditingController.php
@@ -11,6 +11,7 @@ use Drupal\Core\Ajax\InvokeCommand;
 use Drupal\Core\Ajax\MessageCommand;
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\Entity\EntityFormBuilder;
+use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityRepositoryInterface;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Render\RendererInterface;
@@ -82,7 +83,13 @@ class FrontendEditingController extends ControllerBase {
    * @param \Drupal\user\UserDataInterface $userData
    *   The user data storage.
    */
-  public function __construct(RendererInterface $renderer, EntityFormBuilder $builder, EntityRepositoryInterface $entity_repository, ParagraphsHelperInterface $paragraphs_helper, UserDataInterface $userData) {
+  public function __construct(
+    RendererInterface $renderer,
+    EntityFormBuilder $builder,
+    EntityRepositoryInterface $entity_repository,
+    ParagraphsHelperInterface $paragraphs_helper,
+    UserDataInterface $userData,
+  ) {
     $this->renderer = $renderer;
     $this->builder = $builder;
     $this->entityRepository = $entity_repository;
@@ -94,13 +101,11 @@ class FrontendEditingController extends ControllerBase {
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('renderer'),
+    return new static($container->get('renderer'),
       $container->get('entity.form_builder'),
       $container->get('entity.repository'),
       $container->get('frontend_editing.paragraphs_helper'),
-      $container->get('user.data')
-    );
+      $container->get('user.data'));
   }
 
   /**
@@ -114,31 +119,42 @@ class FrontendEditingController extends ControllerBase {
       throw new NotFoundHttpException();
     }
     // Get current state.
-    $current_state = (bool) $this->userData->get('frontend_editing', $this->currentUser()->id(), 'enabled');
+    $current_state = (bool) $this->userData->get('frontend_editing',
+      $this->currentUser()->id(), 'enabled');
     // Revert it, as requested.
     $new_state = !$current_state;
     // Set the new value.
-    $this->userData->set('frontend_editing', $this->currentUser()->id(), 'enabled', $new_state);
+    $this->userData->set('frontend_editing', $this->currentUser()->id(),
+      'enabled', $new_state);
     // Prepare the response.
     $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-toggle-link', 'text', [$this->t('On')]));
-      $response->addCommand(new InvokeCommand('body', 'removeClass', ['frontend-editing--hidden']));
+      $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-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', [
-      '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-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', [
+        'data-toggle-state',
+        $new_state,
+      ]));
+    $response->addCommand(new InvokeCommand('#frontend-editing-toggle-link',
+      'removeClass', ['frontend-editing-toggle-not-configured']));
 
     return $response;
   }
@@ -169,9 +185,9 @@ class FrontendEditingController extends ControllerBase {
     }
     $entity = $storage->load($id);
     if (!$entity) {
-      $this->messenger()->addWarning($this->t('Entity of type @type and id @id was not found',
-        ['@type' => $type, '@id' => $id]
-      ));
+      $this->messenger()
+        ->addWarning($this->t('Entity of type @type and id @id was not found',
+             ['@type' => $type, '@id' => $id]));
       return [];
     }
     // Remove all messages.
@@ -232,7 +248,8 @@ class FrontendEditingController extends ControllerBase {
           $url = Url::fromRoute('entity.' . $type . '.edit_form', [$type => $id]);
         }
 
-        $entityForm = $this->builder->getForm($entity, $display, $form_state_additions);
+        $entityForm = $this->builder->getForm($entity, $display,
+          $form_state_additions);
         $entityForm['#action'] = $url->toString();
 
         $delete_url = Url::fromRoute('frontend_editing.form', [
@@ -244,7 +261,8 @@ class FrontendEditingController extends ControllerBase {
         if ($entity instanceof ParagraphInterface) {
           $parent_field_name = $entity->get('parent_field_name')->value;
           $parent_entity = $entity->getParentEntity();
-          $parent_field_definition = $parent_entity->get($parent_field_name)->getFieldDefinition();
+          $parent_field_definition = $parent_entity->get($parent_field_name)
+            ->getFieldDefinition();
           if ($parent_entity->isTranslatable() && !$parent_entity->isDefaultTranslation() && !$parent_field_definition->isTranslatable()) {
             $delete_access = FALSE;
           }
@@ -297,7 +315,8 @@ class FrontendEditingController extends ControllerBase {
           // definition will identify that the field is translatable.
           $parent_field_name = $entity->get('parent_field_name')->value;
           $parent_entity = $entity->getParentEntity();
-          $parent_field_definition = $parent_entity->get($parent_field_name)->getFieldDefinition();
+          $parent_field_definition = $parent_entity->get($parent_field_name)
+            ->getFieldDefinition();
           if ($parent_entity->isTranslatable() && !$parent_entity->isDefaultTranslation() && !$parent_field_definition->isTranslatable()) {
             throw new AccessDeniedHttpException('You are not allowed to delete this paragraph, because paragraph parent field is not translatable.');
           }
@@ -314,13 +333,17 @@ class FrontendEditingController extends ControllerBase {
         else {
           $url = Url::fromRoute('entity.' . $type . '.delete_form', [$type => $id]);
         }
-        $entityForm = $this->builder->getForm($entity, $display, $form_state_additions);
+        $entityForm = $this->builder->getForm($entity, $display,
+          $form_state_additions);
         if (function_exists('_gin_form_actions')) {
           // Remove sticky class from form actions.
           array_shift($entityForm['actions']['#attributes']['class']);
         }
         $entityForm['title'] = [
-          '#markup' => '<h3>' . $this->t('Are you sure you want to delete this @type?', ['@type' => $entity->getEntityType()->getSingularLabel()]) . '</h3>',
+          '#markup' => '<h3>' . $this->t('Are you sure you want to delete this @type?',
+              [
+                '@type' => $entity->getEntityType()->getSingularLabel(),
+              ]) . '</h3>',
           '#weight' => -10,
         ];
         $entityForm['#action'] = $url->toString();
@@ -358,8 +381,14 @@ class FrontendEditingController extends ControllerBase {
    * @return \Drupal\Core\Access\AccessResultInterface
    *   The access result.
    */
-  public function accessAddType(ParagraphsTypeInterface $paragraphs_type, $parent_type, $parent, $parent_field_name) {
-    return $this->paragraphsHelper->allowAddType($paragraphs_type, $parent_type, $parent, $parent_field_name);
+  public function accessAddType(
+    ParagraphsTypeInterface $paragraphs_type,
+    $parent_type,
+    $parent,
+    $parent_field_name,
+  ) {
+    return $this->paragraphsHelper->allowAddType($paragraphs_type, $parent_type,
+      $parent, $parent_field_name);
   }
 
   /**
@@ -369,7 +398,8 @@ class FrontendEditingController extends ControllerBase {
    *   The access result.
    */
   public function accessAdd($parent_type, $parent, $parent_field_name) {
-    return $this->paragraphsHelper->allowAdd($parent_type, $parent, $parent_field_name);
+    return $this->paragraphsHelper->allowAdd($parent_type, $parent,
+      $parent_field_name);
   }
 
   /**
@@ -400,8 +430,15 @@ class FrontendEditingController extends ControllerBase {
    * @return \Drupal\Core\Access\AccessResultInterface
    *   The access result.
    */
-  public function accessUpdateContent($entity_type_id, $entity_id, $field_name, $view_mode) {
-    $entity = $this->entityTypeManager()->getStorage($entity_type_id)->load($entity_id);
+  public function accessUpdateContent(
+    $entity_type_id,
+    $entity_id,
+    $field_name,
+    $view_mode,
+  ) {
+    $entity = $this->entityTypeManager()
+      ->getStorage($entity_type_id)
+      ->load($entity_id);
     if (!$entity) {
       $result = AccessResult::forbidden('Entity does not exist.');
     }
@@ -429,26 +466,14 @@ class FrontendEditingController extends ControllerBase {
       $message = $this->t('The paragraph could not be moved up.');
     }
     if ($request->isXmlHttpRequest()) {
-      return $this->ajaxUpdateParagraphs($paragraph, $message, $request->get('view_mode_id', 'default'));
+      return $this->ajaxUpdateParagraphs($paragraph, $message,
+        $request->get('view_mode_id', 'default'));
     }
     if (!empty($message)) {
       $this->messenger()->addError($message);
     }
-    return new RedirectResponse($this->paragraphsHelper->getRedirectUrl($paragraph)->toString());
-  }
-
-  /**
-   * Shift down a single paragraph.
-   */
-  public function down(ParagraphInterface $paragraph, Request $request) {
-    $message = FALSE;
-    if (!$this->paragraphsHelper->move($paragraph, 'down')) {
-      $message = $this->t('The paragraph could not be moved down.');
-    }
-    if ($request->isXmlHttpRequest()) {
-      return $this->ajaxUpdateParagraphs($paragraph, $message, $request->get('view_mode_id', 'default'));
-    }
-    return new RedirectResponse($this->paragraphsHelper->getRedirectUrl($paragraph)->toString());
+    return new RedirectResponse($this->paragraphsHelper->getRedirectUrl($paragraph)
+      ->toString());
   }
 
   /**
@@ -464,10 +489,15 @@ class FrontendEditingController extends ControllerBase {
    * @return \Drupal\Core\Ajax\AjaxResponse
    *   The ajax response.
    */
-  protected function ajaxUpdateParagraphs(ParagraphInterface $paragraph, $message, $view_mode_id = 'default') {
+  protected function ajaxUpdateParagraphs(
+    ParagraphInterface $paragraph,
+    $message,
+    $view_mode_id = 'default',
+  ) {
     $response = new AjaxResponse();
     if ($message) {
-      $response->addCommand(new MessageCommand($message, NULL, ['type' => 'error']));
+      $response->addCommand(new MessageCommand($message, NULL,
+        ['type' => 'error']));
     }
     if ($view_mode_id == '_custom') {
       $view_mode_id = 'default';
@@ -476,11 +506,18 @@ class FrontendEditingController extends ControllerBase {
     $parent_entity = $paragraph->getParentEntity();
     $parent_field_name = $paragraph->get('parent_field_name')->value;
     // Load the view display. We need to know whether it uses layout builder.
-    $view_display_id = implode('.', [$parent_entity->getEntityTypeId(), $parent_entity->bundle(), $view_mode_id]);
+    $view_display_id = implode('.', [
+      $parent_entity->getEntityTypeId(),
+      $parent_entity->bundle(),
+      $view_mode_id,
+    ]);
     /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $view_display */
-    $view_display = $this->entityTypeManager()->getStorage('entity_view_display')->load($view_display_id);
+    $view_display = $this->entityTypeManager()
+      ->getStorage('entity_view_display')
+      ->load($view_display_id);
     if ($view_display) {
-      $is_layout_builder = (bool) $view_display->getThirdPartySetting('layout_builder', 'enabled');
+      $is_layout_builder = (bool) $view_display->getThirdPartySetting('layout_builder',
+        'enabled');
       if ($is_layout_builder) {
         $layout_builder_field_block_id_parts = [
           'field_block',
@@ -488,8 +525,10 @@ class FrontendEditingController extends ControllerBase {
           $parent_entity->bundle(),
           $parent_field_name,
         ];
-        $layout_builder_field_block_id = implode(':', $layout_builder_field_block_id_parts);
-        $sections = $view_display->getThirdPartySetting('layout_builder', 'sections');
+        $layout_builder_field_block_id = implode(':',
+          $layout_builder_field_block_id_parts);
+        $sections = $view_display->getThirdPartySetting('layout_builder',
+          'sections');
         /** @var \Drupal\layout_builder\Section $section */
         foreach ($sections as $section) {
           foreach ($section->getComponents() as $component) {
@@ -512,11 +551,29 @@ class FrontendEditingController extends ControllerBase {
         $updated_content[$delta]['#parent_field_view_mode'] = $updated_content['#view_mode'];
       }
     }
-    $selector = '[data-frontend-editing="' . $paragraph->getParentEntity()->getEntityTypeId() . '--' . $paragraph->getParentEntity()->id() . '--' . $paragraph->get('parent_field_name')->value . '--' . $view_mode_id . '"]';
+    $selector = '[data-frontend-editing="' . $paragraph->getParentEntity()
+      ->getEntityTypeId() . '--' . $paragraph->getParentEntity()
+      ->id() . '--' . $paragraph->get('parent_field_name')->value . '--' . $view_mode_id . '"]';
     $response->addCommand(new HtmlCommand($selector, $updated_content));
     return $response;
   }
 
+  /**
+   * Shift down a single paragraph.
+   */
+  public function down(ParagraphInterface $paragraph, Request $request) {
+    $message = FALSE;
+    if (!$this->paragraphsHelper->move($paragraph, 'down')) {
+      $message = $this->t('The paragraph could not be moved down.');
+    }
+    if ($request->isXmlHttpRequest()) {
+      return $this->ajaxUpdateParagraphs($paragraph, $message,
+        $request->get('view_mode_id', 'default'));
+    }
+    return new RedirectResponse($this->paragraphsHelper->getRedirectUrl($paragraph)
+      ->toString());
+  }
+
   /**
    * Update content with ajax.
    *
@@ -534,7 +591,13 @@ class FrontendEditingController extends ControllerBase {
    * @return \Drupal\Core\Ajax\AjaxResponse
    *   The ajax response.
    */
-  public function updateContent($entity_type_id, $entity_id, $field_name, $view_mode, Request $request) {
+  public function updateContent(
+    $entity_type_id,
+    $entity_id,
+    $field_name,
+    $view_mode,
+    Request $request,
+  ) {
     if (!$request->isXmlHttpRequest()) {
       throw new NotFoundHttpException();
     }
@@ -550,15 +613,15 @@ class FrontendEditingController extends ControllerBase {
     }
     catch (InvalidPluginDefinitionException | PluginNotFoundException $e) {
       $message = $this->t('Entity of type @type and id @id was not found',
-        ['@type' => $entity_type_id, '@id' => $entity_id]
-      );
-      $response->addCommand(new MessageCommand($message, NULL, ['type' => 'error']));
+        ['@type' => $entity_type_id, '@id' => $entity_id]);
+      $response->addCommand(new MessageCommand($message, NULL,
+        ['type' => 'error']));
     }
     if (!$entity) {
       $message = $this->t('Entity of type @type and id @id was not found',
-        ['@type' => $entity_type_id, '@id' => $entity_id]
-      );
-      $response->addCommand(new MessageCommand($message, NULL, ['type' => 'error']));
+        ['@type' => $entity_type_id, '@id' => $entity_id]);
+      $response->addCommand(new MessageCommand($message, NULL,
+        ['type' => 'error']));
     }
     // If there are errors, early return and reload the page.
     if (!empty($response->getCommands())) {
@@ -604,9 +667,17 @@ class FrontendEditingController extends ControllerBase {
    * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
    * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
    */
-  public function paragraphAddPage($parent_type, $parent, $parent_field_name, $current_paragraph, $before, Request $request) {
+  public function paragraphAddPage(
+    $parent_type,
+    $parent,
+    $parent_field_name,
+    $current_paragraph,
+    $before,
+    Request $request,
+  ) {
     try {
-      $parent_entity = $this->entityTypeManager()->getStorage($parent_type)
+      $parent_entity = $this->entityTypeManager()
+        ->getStorage($parent_type)
         ->load($parent);
     }
     catch (InvalidPluginDefinitionException | PluginNotFoundException $e) {
@@ -639,43 +710,64 @@ class FrontendEditingController extends ControllerBase {
       ->loadMultiple($allowed_paragraphs);
     $items = [];
     foreach ($allowed_paragraphs as $paragraphs_type) {
-      $items[$paragraphs_type->id()] = [
-        '#type' => 'link',
-        '#title' => $this->t('Add @type', ['@type' => $paragraphs_type->label()]),
-        '#url' => Url::fromRoute('frontend_editing.paragraph_add', [
-          'parent_type' => $parent_type,
-          'parent' => $parent,
-          'parent_field_name' => $parent_field_name,
-          'paragraphs_type' => $paragraphs_type->id(),
-          'current_paragraph' => $current_paragraph,
-          'before' => $before,
-        ], [
-          'query' => $request->query->all(),
-        ]),
+      $item = [
+        '#type' => 'container',
         '#attributes' => [
-          'class' => 'field-add-more-submit button--small button js-form-submit form-submit',
-          'name' => $parent_field_name . '_' . $paragraphs_type->id() . '_add_more',
+          'class' => [
+            'paragraphs-add-dialog-row',
+            'field-add-more-submit',
+            'button--large',
+            'button',
+            'js-form-submit',
+            'form-submit',
+          ],
         ],
-        '#wrapper_attributes' => [
-          'class' => ['paragraphs-add-dialog-row'],
+        'link' => [
+          '#type' => 'link',
+          '#title' => $this->t('Add @type',
+            ['@type' => $paragraphs_type->label()]),
+          '#url' => Url::fromRoute('frontend_editing.paragraph_add', [
+            'parent_type' => $parent_type,
+            'parent' => $parent,
+            'parent_field_name' => $parent_field_name,
+            'paragraphs_type' => $paragraphs_type->id(),
+            'current_paragraph' => $current_paragraph,
+            'before' => $before,
+          ], [
+            'query' => $request->query->all(),
+          ]),
+          '#attributes' => [
+            'class' => [''],
+            'name' => $parent_field_name . '_' . $paragraphs_type->id() . '_add_more',
+          ],
         ],
       ];
+
+      if (!empty($paragraphs_type->get('icon_default'))) {
+        $item['icon'] = $this->buildIconElement($paragraphs_type);
+      }
+
+      $items[$paragraphs_type->id()] = $item;
     }
     if (!empty($settings['handler_settings']['target_bundles_drag_drop'])) {
-      uasort($settings['handler_settings']['target_bundles_drag_drop'], function ($a, $b) {
-        return $a['weight'] <=> $b['weight'];
-      });
-      if (!empty($settings['handler_settings']['negate'])) {
-        $settings['handler_settings']['target_bundles_drag_drop'] = array_filter($settings['handler_settings']['target_bundles_drag_drop'], function ($value) {
-          return !$value['enabled'];
+      uasort($settings['handler_settings']['target_bundles_drag_drop'],
+        function ($a, $b) {
+          return $a['weight'] <=> $b['weight'];
         });
+      if (!empty($settings['handler_settings']['negate'])) {
+        $settings['handler_settings']['target_bundles_drag_drop'] = array_filter($settings['handler_settings']['target_bundles_drag_drop'],
+          function ($value) {
+            return !$value['enabled'];
+          });
       }
       else {
-        $settings['handler_settings']['target_bundles_drag_drop'] = array_filter($settings['handler_settings']['target_bundles_drag_drop'], function ($value) {
-          return $value['enabled'];
-        });
+        $settings['handler_settings']['target_bundles_drag_drop'] = array_filter($settings['handler_settings']['target_bundles_drag_drop'],
+          function ($value) {
+            return $value['enabled'];
+          });
       }
-      $items = array_replace(array_flip(array_keys($settings['handler_settings']['target_bundles_drag_drop'])), $items);
+      $items = array_replace(array_flip(array_keys($settings['handler_settings']['target_bundles_drag_drop'])),
+        $items);
       $items = array_values($items);
     }
     return [
@@ -693,4 +785,34 @@ class FrontendEditingController extends ControllerBase {
     ];
   }
 
+  /**
+   * Builds the icon element for a given Paragraphs type.
+   *
+   * This method generates a render array for the icon element using the value
+   * from the 'icon_default' field. It assumes that an icon is present and
+   * should only be called when an icon is defined.
+   *
+   * @param \Drupal\paragraphs\ParagraphsTypeInterface $paragraphs_type
+   *   The Paragraphs type entity.
+   *
+   * @return array
+   *   A render array for the icon element.
+   */
+  protected function buildIconElement(
+    EntityInterface $paragraphs_type,
+  ): array {
+    $icon = $paragraphs_type->get('icon_default');
+    return [
+      '#type' => 'container',
+      '#attributes' => [
+        'class' => ['paragraph-type-icon', $paragraphs_type->id()],
+      ],
+      'image' => [
+        '#theme' => 'image',
+        '#uri' => $icon,
+        '#alt' => $paragraphs_type->label(),
+      ],
+    ];
+  }
+
 }
-- 
GitLab


From 457aecf0fc929407c5ffd08c53aa9fa796fd2687 Mon Sep 17 00:00:00 2001
From: "Philipps, Marc" <Marc.Philipps@adesso.de>
Date: Thu, 27 Feb 2025 10:43:39 +0100
Subject: [PATCH 04/10] feat(frontend_editing): display icon_default from
 Paragraphs in overlay with improved styling

Adjusted rendering logic so that when adding Paragraph entities via the frontend editing overlay, the icon_default from the Paragraphs module is displayed if available.
Added CSS classes to enhance the visual appearance of the icon, ensuring a consistent and appealing user interface.
---
 css/forms_helper.css                         |  31 +++
 src/Controller/FrontendEditingController.php | 264 +++++++------------
 2 files changed, 126 insertions(+), 169 deletions(-)

diff --git a/css/forms_helper.css b/css/forms_helper.css
index da3443a..7e41bc1 100644
--- a/css/forms_helper.css
+++ b/css/forms_helper.css
@@ -35,6 +35,8 @@ body.path-frontend-editing {
   width: 100%;
   z-index: 2;
   background-color: #f5f7fb;
+  cursor: pointer;
+  text-decoration: none;
 }
 
 .path-frontend-editing .block-system-main-block > form {
@@ -68,3 +70,32 @@ body.frontend-editing-hide-sidebar.gin--horizontal-toolbar,
 body.frontend-editing-hide-sidebar.gin--classic-toolbar {
   padding-inline-start: 0 !important;
 }
+
+.path-frontend-editing .paragraphs-add-dialog-row {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  width: 100%;
+  gap: 10px;
+}
+
+.path-frontend-editing .paragraphs-add-dialog-row img {
+  width: 60px;
+  height: 60px;
+  object-fit: contain;
+  border-radius: 5px;
+  background: #fff;
+}
+
+.path-frontend-editing ul.paragraphs-add-dialog-list li {
+  list-style: none;
+}
+
+.path-frontend-editing .field-add-more-submit:hover a {
+  color: #fff;
+}
+
+.path-frontend-editing ul.paragraphs-add-dialog-list .button.button--small {
+  padding-left: unset;
+  border: unset;
+}
diff --git a/src/Controller/FrontendEditingController.php b/src/Controller/FrontendEditingController.php
index 26ff965..e2c4d84 100644
--- a/src/Controller/FrontendEditingController.php
+++ b/src/Controller/FrontendEditingController.php
@@ -83,13 +83,7 @@ class FrontendEditingController extends ControllerBase {
    * @param \Drupal\user\UserDataInterface $userData
    *   The user data storage.
    */
-  public function __construct(
-    RendererInterface $renderer,
-    EntityFormBuilder $builder,
-    EntityRepositoryInterface $entity_repository,
-    ParagraphsHelperInterface $paragraphs_helper,
-    UserDataInterface $userData,
-  ) {
+  public function __construct(RendererInterface $renderer, EntityFormBuilder $builder, EntityRepositoryInterface $entity_repository, ParagraphsHelperInterface $paragraphs_helper, UserDataInterface $userData) {
     $this->renderer = $renderer;
     $this->builder = $builder;
     $this->entityRepository = $entity_repository;
@@ -101,11 +95,13 @@ class FrontendEditingController extends ControllerBase {
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container) {
-    return new static($container->get('renderer'),
+    return new static(
+      $container->get('renderer'),
       $container->get('entity.form_builder'),
       $container->get('entity.repository'),
       $container->get('frontend_editing.paragraphs_helper'),
-      $container->get('user.data'));
+      $container->get('user.data')
+    );
   }
 
   /**
@@ -119,42 +115,31 @@ class FrontendEditingController extends ControllerBase {
       throw new NotFoundHttpException();
     }
     // Get current state.
-    $current_state = (bool) $this->userData->get('frontend_editing',
-      $this->currentUser()->id(), 'enabled');
+    $current_state = (bool) $this->userData->get('frontend_editing', $this->currentUser()->id(), 'enabled');
     // Revert it, as requested.
     $new_state = !$current_state;
     // Set the new value.
-    $this->userData->set('frontend_editing', $this->currentUser()->id(),
-      'enabled', $new_state);
+    $this->userData->set('frontend_editing', $this->currentUser()->id(), 'enabled', $new_state);
     // Prepare the response.
     $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-toggle-link',
-        'text', [$this->t('On')]));
-      $response->addCommand(new InvokeCommand('body', 'removeClass',
-        ['frontend-editing--hidden']));
+      $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-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', [
-        '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-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', [
+      'data-toggle-state',
+      $new_state,
+    ]));
+    $response->addCommand(new InvokeCommand('#frontend-editing-toggle-link', 'removeClass', ['frontend-editing-toggle-not-configured']));
 
     return $response;
   }
@@ -185,9 +170,9 @@ class FrontendEditingController extends ControllerBase {
     }
     $entity = $storage->load($id);
     if (!$entity) {
-      $this->messenger()
-        ->addWarning($this->t('Entity of type @type and id @id was not found',
-             ['@type' => $type, '@id' => $id]));
+      $this->messenger()->addWarning($this->t('Entity of type @type and id @id was not found',
+        ['@type' => $type, '@id' => $id]
+      ));
       return [];
     }
     // Remove all messages.
@@ -248,8 +233,7 @@ class FrontendEditingController extends ControllerBase {
           $url = Url::fromRoute('entity.' . $type . '.edit_form', [$type => $id]);
         }
 
-        $entityForm = $this->builder->getForm($entity, $display,
-          $form_state_additions);
+        $entityForm = $this->builder->getForm($entity, $display, $form_state_additions);
         $entityForm['#action'] = $url->toString();
 
         $delete_url = Url::fromRoute('frontend_editing.form', [
@@ -261,8 +245,7 @@ class FrontendEditingController extends ControllerBase {
         if ($entity instanceof ParagraphInterface) {
           $parent_field_name = $entity->get('parent_field_name')->value;
           $parent_entity = $entity->getParentEntity();
-          $parent_field_definition = $parent_entity->get($parent_field_name)
-            ->getFieldDefinition();
+          $parent_field_definition = $parent_entity->get($parent_field_name)->getFieldDefinition();
           if ($parent_entity->isTranslatable() && !$parent_entity->isDefaultTranslation() && !$parent_field_definition->isTranslatable()) {
             $delete_access = FALSE;
           }
@@ -315,8 +298,7 @@ class FrontendEditingController extends ControllerBase {
           // definition will identify that the field is translatable.
           $parent_field_name = $entity->get('parent_field_name')->value;
           $parent_entity = $entity->getParentEntity();
-          $parent_field_definition = $parent_entity->get($parent_field_name)
-            ->getFieldDefinition();
+          $parent_field_definition = $parent_entity->get($parent_field_name)->getFieldDefinition();
           if ($parent_entity->isTranslatable() && !$parent_entity->isDefaultTranslation() && !$parent_field_definition->isTranslatable()) {
             throw new AccessDeniedHttpException('You are not allowed to delete this paragraph, because paragraph parent field is not translatable.');
           }
@@ -333,17 +315,13 @@ class FrontendEditingController extends ControllerBase {
         else {
           $url = Url::fromRoute('entity.' . $type . '.delete_form', [$type => $id]);
         }
-        $entityForm = $this->builder->getForm($entity, $display,
-          $form_state_additions);
+        $entityForm = $this->builder->getForm($entity, $display, $form_state_additions);
         if (function_exists('_gin_form_actions')) {
           // Remove sticky class from form actions.
           array_shift($entityForm['actions']['#attributes']['class']);
         }
         $entityForm['title'] = [
-          '#markup' => '<h3>' . $this->t('Are you sure you want to delete this @type?',
-              [
-                '@type' => $entity->getEntityType()->getSingularLabel(),
-              ]) . '</h3>',
+          '#markup' => '<h3>' . $this->t('Are you sure you want to delete this @type?', ['@type' => $entity->getEntityType()->getSingularLabel()]) . '</h3>',
           '#weight' => -10,
         ];
         $entityForm['#action'] = $url->toString();
@@ -381,14 +359,8 @@ class FrontendEditingController extends ControllerBase {
    * @return \Drupal\Core\Access\AccessResultInterface
    *   The access result.
    */
-  public function accessAddType(
-    ParagraphsTypeInterface $paragraphs_type,
-    $parent_type,
-    $parent,
-    $parent_field_name,
-  ) {
-    return $this->paragraphsHelper->allowAddType($paragraphs_type, $parent_type,
-      $parent, $parent_field_name);
+  public function accessAddType(ParagraphsTypeInterface $paragraphs_type, $parent_type, $parent, $parent_field_name) {
+    return $this->paragraphsHelper->allowAddType($paragraphs_type, $parent_type, $parent, $parent_field_name);
   }
 
   /**
@@ -398,8 +370,7 @@ class FrontendEditingController extends ControllerBase {
    *   The access result.
    */
   public function accessAdd($parent_type, $parent, $parent_field_name) {
-    return $this->paragraphsHelper->allowAdd($parent_type, $parent,
-      $parent_field_name);
+    return $this->paragraphsHelper->allowAdd($parent_type, $parent, $parent_field_name);
   }
 
   /**
@@ -430,15 +401,8 @@ class FrontendEditingController extends ControllerBase {
    * @return \Drupal\Core\Access\AccessResultInterface
    *   The access result.
    */
-  public function accessUpdateContent(
-    $entity_type_id,
-    $entity_id,
-    $field_name,
-    $view_mode,
-  ) {
-    $entity = $this->entityTypeManager()
-      ->getStorage($entity_type_id)
-      ->load($entity_id);
+  public function accessUpdateContent($entity_type_id, $entity_id, $field_name, $view_mode) {
+    $entity = $this->entityTypeManager()->getStorage($entity_type_id)->load($entity_id);
     if (!$entity) {
       $result = AccessResult::forbidden('Entity does not exist.');
     }
@@ -466,14 +430,26 @@ class FrontendEditingController extends ControllerBase {
       $message = $this->t('The paragraph could not be moved up.');
     }
     if ($request->isXmlHttpRequest()) {
-      return $this->ajaxUpdateParagraphs($paragraph, $message,
-        $request->get('view_mode_id', 'default'));
+      return $this->ajaxUpdateParagraphs($paragraph, $message, $request->get('view_mode_id', 'default'));
     }
     if (!empty($message)) {
       $this->messenger()->addError($message);
     }
-    return new RedirectResponse($this->paragraphsHelper->getRedirectUrl($paragraph)
-      ->toString());
+    return new RedirectResponse($this->paragraphsHelper->getRedirectUrl($paragraph)->toString());
+  }
+
+  /**
+   * Shift down a single paragraph.
+   */
+  public function down(ParagraphInterface $paragraph, Request $request) {
+    $message = FALSE;
+    if (!$this->paragraphsHelper->move($paragraph, 'down')) {
+      $message = $this->t('The paragraph could not be moved down.');
+    }
+    if ($request->isXmlHttpRequest()) {
+      return $this->ajaxUpdateParagraphs($paragraph, $message, $request->get('view_mode_id', 'default'));
+    }
+    return new RedirectResponse($this->paragraphsHelper->getRedirectUrl($paragraph)->toString());
   }
 
   /**
@@ -489,15 +465,10 @@ class FrontendEditingController extends ControllerBase {
    * @return \Drupal\Core\Ajax\AjaxResponse
    *   The ajax response.
    */
-  protected function ajaxUpdateParagraphs(
-    ParagraphInterface $paragraph,
-    $message,
-    $view_mode_id = 'default',
-  ) {
+  protected function ajaxUpdateParagraphs(ParagraphInterface $paragraph, $message, $view_mode_id = 'default') {
     $response = new AjaxResponse();
     if ($message) {
-      $response->addCommand(new MessageCommand($message, NULL,
-        ['type' => 'error']));
+      $response->addCommand(new MessageCommand($message, NULL, ['type' => 'error']));
     }
     if ($view_mode_id == '_custom') {
       $view_mode_id = 'default';
@@ -506,18 +477,11 @@ class FrontendEditingController extends ControllerBase {
     $parent_entity = $paragraph->getParentEntity();
     $parent_field_name = $paragraph->get('parent_field_name')->value;
     // Load the view display. We need to know whether it uses layout builder.
-    $view_display_id = implode('.', [
-      $parent_entity->getEntityTypeId(),
-      $parent_entity->bundle(),
-      $view_mode_id,
-    ]);
+    $view_display_id = implode('.', [$parent_entity->getEntityTypeId(), $parent_entity->bundle(), $view_mode_id]);
     /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $view_display */
-    $view_display = $this->entityTypeManager()
-      ->getStorage('entity_view_display')
-      ->load($view_display_id);
+    $view_display = $this->entityTypeManager()->getStorage('entity_view_display')->load($view_display_id);
     if ($view_display) {
-      $is_layout_builder = (bool) $view_display->getThirdPartySetting('layout_builder',
-        'enabled');
+      $is_layout_builder = (bool) $view_display->getThirdPartySetting('layout_builder', 'enabled');
       if ($is_layout_builder) {
         $layout_builder_field_block_id_parts = [
           'field_block',
@@ -525,10 +489,8 @@ class FrontendEditingController extends ControllerBase {
           $parent_entity->bundle(),
           $parent_field_name,
         ];
-        $layout_builder_field_block_id = implode(':',
-          $layout_builder_field_block_id_parts);
-        $sections = $view_display->getThirdPartySetting('layout_builder',
-          'sections');
+        $layout_builder_field_block_id = implode(':', $layout_builder_field_block_id_parts);
+        $sections = $view_display->getThirdPartySetting('layout_builder', 'sections');
         /** @var \Drupal\layout_builder\Section $section */
         foreach ($sections as $section) {
           foreach ($section->getComponents() as $component) {
@@ -551,29 +513,11 @@ class FrontendEditingController extends ControllerBase {
         $updated_content[$delta]['#parent_field_view_mode'] = $updated_content['#view_mode'];
       }
     }
-    $selector = '[data-frontend-editing="' . $paragraph->getParentEntity()
-      ->getEntityTypeId() . '--' . $paragraph->getParentEntity()
-      ->id() . '--' . $paragraph->get('parent_field_name')->value . '--' . $view_mode_id . '"]';
+    $selector = '[data-frontend-editing="' . $paragraph->getParentEntity()->getEntityTypeId() . '--' . $paragraph->getParentEntity()->id() . '--' . $paragraph->get('parent_field_name')->value . '--' . $view_mode_id . '"]';
     $response->addCommand(new HtmlCommand($selector, $updated_content));
     return $response;
   }
 
-  /**
-   * Shift down a single paragraph.
-   */
-  public function down(ParagraphInterface $paragraph, Request $request) {
-    $message = FALSE;
-    if (!$this->paragraphsHelper->move($paragraph, 'down')) {
-      $message = $this->t('The paragraph could not be moved down.');
-    }
-    if ($request->isXmlHttpRequest()) {
-      return $this->ajaxUpdateParagraphs($paragraph, $message,
-        $request->get('view_mode_id', 'default'));
-    }
-    return new RedirectResponse($this->paragraphsHelper->getRedirectUrl($paragraph)
-      ->toString());
-  }
-
   /**
    * Update content with ajax.
    *
@@ -591,13 +535,7 @@ class FrontendEditingController extends ControllerBase {
    * @return \Drupal\Core\Ajax\AjaxResponse
    *   The ajax response.
    */
-  public function updateContent(
-    $entity_type_id,
-    $entity_id,
-    $field_name,
-    $view_mode,
-    Request $request,
-  ) {
+  public function updateContent($entity_type_id, $entity_id, $field_name, $view_mode, Request $request) {
     if (!$request->isXmlHttpRequest()) {
       throw new NotFoundHttpException();
     }
@@ -613,15 +551,15 @@ class FrontendEditingController extends ControllerBase {
     }
     catch (InvalidPluginDefinitionException | PluginNotFoundException $e) {
       $message = $this->t('Entity of type @type and id @id was not found',
-        ['@type' => $entity_type_id, '@id' => $entity_id]);
-      $response->addCommand(new MessageCommand($message, NULL,
-        ['type' => 'error']));
+        ['@type' => $entity_type_id, '@id' => $entity_id]
+      );
+      $response->addCommand(new MessageCommand($message, NULL, ['type' => 'error']));
     }
     if (!$entity) {
       $message = $this->t('Entity of type @type and id @id was not found',
-        ['@type' => $entity_type_id, '@id' => $entity_id]);
-      $response->addCommand(new MessageCommand($message, NULL,
-        ['type' => 'error']));
+        ['@type' => $entity_type_id, '@id' => $entity_id]
+      );
+      $response->addCommand(new MessageCommand($message, NULL, ['type' => 'error']));
     }
     // If there are errors, early return and reload the page.
     if (!empty($response->getCommands())) {
@@ -667,17 +605,9 @@ class FrontendEditingController extends ControllerBase {
    * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
    * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
    */
-  public function paragraphAddPage(
-    $parent_type,
-    $parent,
-    $parent_field_name,
-    $current_paragraph,
-    $before,
-    Request $request,
-  ) {
+  public function paragraphAddPage($parent_type, $parent, $parent_field_name, $current_paragraph, $before, Request $request) {
     try {
-      $parent_entity = $this->entityTypeManager()
-        ->getStorage($parent_type)
+      $parent_entity = $this->entityTypeManager()->getStorage($parent_type)
         ->load($parent);
     }
     catch (InvalidPluginDefinitionException | PluginNotFoundException $e) {
@@ -722,52 +652,48 @@ class FrontendEditingController extends ControllerBase {
             'form-submit',
           ],
         ],
-        'link' => [
-          '#type' => 'link',
-          '#title' => $this->t('Add @type',
-            ['@type' => $paragraphs_type->label()]),
-          '#url' => Url::fromRoute('frontend_editing.paragraph_add', [
-            'parent_type' => $parent_type,
-            'parent' => $parent,
-            'parent_field_name' => $parent_field_name,
-            'paragraphs_type' => $paragraphs_type->id(),
-            'current_paragraph' => $current_paragraph,
-            'before' => $before,
-          ], [
-            'query' => $request->query->all(),
-          ]),
-          '#attributes' => [
-            'class' => [''],
-            'name' => $parent_field_name . '_' . $paragraphs_type->id() . '_add_more',
-          ],
-        ],
       ];
 
       if (!empty($paragraphs_type->get('icon_default'))) {
         $item['icon'] = $this->buildIconElement($paragraphs_type);
       }
 
+      $item['link'] = [
+        '#type' => 'link',
+        '#title' => $this->t('Add @type', ['@type' => $paragraphs_type->label()]),
+        '#url' => Url::fromRoute('frontend_editing.paragraph_add', [
+          'parent_type' => $parent_type,
+          'parent' => $parent,
+          'parent_field_name' => $parent_field_name,
+          'paragraphs_type' => $paragraphs_type->id(),
+          'current_paragraph' => $current_paragraph,
+          'before' => $before,
+        ], [
+          'query' => $request->query->all(),
+        ]),
+        '#attributes' => [
+          'class' => 'field-add-more-submit js-form-submit form-submit',
+          'name' => $parent_field_name . '_' . $paragraphs_type->id() . '_add_more',
+        ],
+      ];
+
       $items[$paragraphs_type->id()] = $item;
     }
     if (!empty($settings['handler_settings']['target_bundles_drag_drop'])) {
-      uasort($settings['handler_settings']['target_bundles_drag_drop'],
-        function ($a, $b) {
-          return $a['weight'] <=> $b['weight'];
-        });
+      uasort($settings['handler_settings']['target_bundles_drag_drop'], function ($a, $b) {
+        return $a['weight'] <=> $b['weight'];
+      });
       if (!empty($settings['handler_settings']['negate'])) {
-        $settings['handler_settings']['target_bundles_drag_drop'] = array_filter($settings['handler_settings']['target_bundles_drag_drop'],
-          function ($value) {
-            return !$value['enabled'];
-          });
+        $settings['handler_settings']['target_bundles_drag_drop'] = array_filter($settings['handler_settings']['target_bundles_drag_drop'], function ($value) {
+          return !$value['enabled'];
+        });
       }
       else {
-        $settings['handler_settings']['target_bundles_drag_drop'] = array_filter($settings['handler_settings']['target_bundles_drag_drop'],
-          function ($value) {
-            return $value['enabled'];
-          });
+        $settings['handler_settings']['target_bundles_drag_drop'] = array_filter($settings['handler_settings']['target_bundles_drag_drop'], function ($value) {
+          return $value['enabled'];
+        });
       }
-      $items = array_replace(array_flip(array_keys($settings['handler_settings']['target_bundles_drag_drop'])),
-        $items);
+      $items = array_replace(array_flip(array_keys($settings['handler_settings']['target_bundles_drag_drop'])), $items);
       $items = array_values($items);
     }
     return [
-- 
GitLab


From 4956ca370e7cd154414f29f59abf377487678262 Mon Sep 17 00:00:00 2001
From: Artem  Dmitriiev <a.dmitriiev@1xinternet.de>
Date: Thu, 27 Feb 2025 16:56:35 +0100
Subject: [PATCH 05/10] Fix style linting

---
 css/forms_helper.css | 22 +++++++++++-----------
 css/ui_toggle.css    | 26 +++++++++++++-------------
 2 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/css/forms_helper.css b/css/forms_helper.css
index 7e41bc1..89657a3 100644
--- a/css/forms_helper.css
+++ b/css/forms_helper.css
@@ -3,16 +3,16 @@
 }
 
 .path-frontend-editing .layout-node-form__actions {
-  display: block;
   z-index: 2;
+  display: block;
 }
 
 .path-frontend-editing .layout-node-form__actions .gin-sticky {
   position: fixed;
   bottom: 0;
-  background: white;
-  border: 1px solid gray;
   padding: 0 15px;
+  border: 1px solid gray;
+  background: white;
   border-radius: 8px;
 }
 
@@ -24,25 +24,25 @@ body.path-frontend-editing {
 }
 
 .path-frontend-editing > h1 {
-  align-items: center;
   display: flex;
+  align-items: center;
   font-size: 1.125em;
-  font-weight: 500;
-  height: 80px;
   justify-content: center;
-  margin: 0;
+  height: 80px;
+  font-weight: 500;
   position: fixed;
-  width: 100%;
+  margin: 0;
   z-index: 2;
-  background-color: #f5f7fb;
+  width: 100%;
   cursor: pointer;
+  background-color: #f5f7fb;
   text-decoration: none;
 }
 
 .path-frontend-editing .block-system-main-block > form {
-  border-radius: 0;
-  border: 0;
   position: relative;
+  border: 0;
+  border-radius: 0;
 }
 
 .path-frontend-editing .dialog-off-canvas-main-canvas {
diff --git a/css/ui_toggle.css b/css/ui_toggle.css
index 687609d..37d914f 100644
--- a/css/ui_toggle.css
+++ b/css/ui_toggle.css
@@ -3,12 +3,12 @@
   top: var(--fe-editing-toggle-top, auto);
   right: var(--fe-editing-toggle-right, auto);
   bottom: var(--fe-editing-toggle-bottom, auto);
-  left: var(--fe-editing-toggle-left, auto);
   z-index: 20;
+  left: var(--fe-editing-toggle-left, auto);
   border-radius: 9999px;
+  width: fit-content !important;
   background-color: white;
   /*  Required to prevent the toggle's position from being affected by external styles. */
-  width: fit-content !important;
   height: fit-content !important;
   margin: 0 !important;
   padding: 0 !important;
@@ -19,40 +19,40 @@
 }
 
 .frontend-editing-toggle a {
-  background: rgba(var(--fe-editing-primary-color), 0.6);
   padding: 0.5rem 0.75rem;
+  background: rgba(var(--fe-editing-primary-color), 0.6);
   text-transform: uppercase;
-  color: white;
   display: block;
+  color: white;
   width: 5.5rem;
-  font-weight: 500;
   border-radius: 999px;
-  box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px;
+  font-weight: 500;
   position: relative;
+  box-shadow: rgba(0, 0, 0, 0.16) 0 1px 4px;
   text-align: right;
 }
 
 .frontend-editing-toggle a::before {
-  content: ' ';
   position: absolute;
+  content: " ";
   top: 0;
-  left: 0.5rem;
   bottom: 0;
+  left: 0.5rem;
   width: 1.75rem;
   height: 1.75rem;
-  margin-bottom: auto;
   margin-top: auto;
+  margin-bottom: auto;
   background-color: #fff;
   background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzIzMjIyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTkgMy4xNzFhMS44MjkgMS44MjkgMCAwIDAtMS4yOTMuNTM2TDQuMzk1IDE3LjAxOWwtLjk3IDMuNTU2IDMuNTU2LS45N0wyMC4yOTMgNi4yOTNBMS44MjkgMS44MjkgMCAwIDAgMTkgMy4xN1ptLTEuNDY1LTEuNzA4YTMuODI5IDMuODI5IDAgMCAxIDQuMTcyIDYuMjQ0bC0xMy41IDEzLjVhMSAxIDAgMCAxLS40NDQuMjU4bC01LjUgMS41YTEgMSAwIDAgMS0xLjIyOC0xLjIyOGwxLjUtNS41YTEgMSAwIDAgMSAuMjU4LS40NDRsMTMuNS0xMy41YTMuODI5IDMuODI5IDAgMCAxIDEuMjQyLS44M1oiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==");
-  background-size: 16px;
   background-repeat: no-repeat;
-  background-position: center;
+  background-size: 16px;
   border-radius: 999px;
+  background-position: center;
 }
 
 .frontend-editing-toggle a.frontend-editing--enabled {
-  background: rgb(var(--fe-editing-primary-color));
   text-align: left;
+  background: rgb(var(--fe-editing-primary-color));
 }
 
 .frontend-editing-toggle a.frontend-editing--enabled::before {
@@ -60,8 +60,8 @@
 }
 
 .frontend-editing-toggle-not-configured {
-  box-shadow: 0 0 0 0 rgba(255, 0, 0, 1);
   transform: scale(1);
+  box-shadow: 0 0 0 0 rgba(255, 0, 0, 1);
   animation: pulse 2s infinite;
 }
 
-- 
GitLab


From 6826df3aca969ea0798f6d91d80b2696feae765c Mon Sep 17 00:00:00 2001
From: Artem  Dmitriiev <a.dmitriiev@1xinternet.de>
Date: Thu, 27 Feb 2025 17:19:26 +0100
Subject: [PATCH 06/10] Remove 100% width as it goes out of screen with Gin
 theme

---
 css/forms_helper.css | 1 -
 1 file changed, 1 deletion(-)

diff --git a/css/forms_helper.css b/css/forms_helper.css
index 89657a3..6d7a3ab 100644
--- a/css/forms_helper.css
+++ b/css/forms_helper.css
@@ -75,7 +75,6 @@ body.frontend-editing-hide-sidebar.gin--classic-toolbar {
   display: flex;
   flex-direction: row;
   align-items: center;
-  width: 100%;
   gap: 10px;
 }
 
-- 
GitLab


From d7dab5a0b213e988a0d5b67c79b6a4f42f67ca83 Mon Sep 17 00:00:00 2001
From: Artem  Dmitriiev <a.dmitriiev@1xinternet.de>
Date: Thu, 27 Feb 2025 17:25:30 +0100
Subject: [PATCH 07/10] Fix linting

---
 css/forms_helper.css | 10 +++++-----
 css/ui_toggle.css    | 24 ++++++++++++------------
 2 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/css/forms_helper.css b/css/forms_helper.css
index 6d7a3ab..bfd4bf5 100644
--- a/css/forms_helper.css
+++ b/css/forms_helper.css
@@ -12,8 +12,8 @@
   bottom: 0;
   padding: 0 15px;
   border: 1px solid gray;
-  background: white;
   border-radius: 8px;
+  background: white;
 }
 
 body.gin--vertical-toolbar.path-frontend-editing,
@@ -26,17 +26,17 @@ body.path-frontend-editing {
 .path-frontend-editing > h1 {
   display: flex;
   align-items: center;
-  font-size: 1.125em;
   justify-content: center;
+  font-size: 1.125em;
   height: 80px;
-  font-weight: 500;
   position: fixed;
-  margin: 0;
+  font-weight: 500;
   z-index: 2;
+  margin: 0;
   width: 100%;
   cursor: pointer;
-  background-color: #f5f7fb;
   text-decoration: none;
+  background-color: #f5f7fb;
 }
 
 .path-frontend-editing .block-system-main-block > form {
diff --git a/css/ui_toggle.css b/css/ui_toggle.css
index 37d914f..34b99f2 100644
--- a/css/ui_toggle.css
+++ b/css/ui_toggle.css
@@ -1,15 +1,15 @@
 .frontend-editing-toggle {
   position: fixed;
+  z-index: 20;
   top: var(--fe-editing-toggle-top, auto);
   right: var(--fe-editing-toggle-right, auto);
   bottom: var(--fe-editing-toggle-bottom, auto);
-  z-index: 20;
   left: var(--fe-editing-toggle-left, auto);
-  border-radius: 9999px;
-  width: fit-content !important;
-  background-color: white;
   /*  Required to prevent the toggle's position from being affected by external styles. */
+  width: fit-content !important;
   height: fit-content !important;
+  border-radius: 9999px;
+  background-color: white;
   margin: 0 !important;
   padding: 0 !important;
 }
@@ -20,33 +20,33 @@
 
 .frontend-editing-toggle a {
   padding: 0.5rem 0.75rem;
-  background: rgba(var(--fe-editing-primary-color), 0.6);
-  text-transform: uppercase;
   display: block;
-  color: white;
+  text-transform: uppercase;
+  background: rgba(var(--fe-editing-primary-color), 0.6);
   width: 5.5rem;
+  color: white;
   border-radius: 999px;
-  font-weight: 500;
   position: relative;
-  box-shadow: rgba(0, 0, 0, 0.16) 0 1px 4px;
+  font-weight: 500;
   text-align: right;
+  box-shadow: rgba(0, 0, 0, 0.16) 0 1px 4px;
 }
 
 .frontend-editing-toggle a::before {
   position: absolute;
-  content: " ";
   top: 0;
+  content: " ";
   bottom: 0;
   left: 0.5rem;
   width: 1.75rem;
   height: 1.75rem;
   margin-top: auto;
   margin-bottom: auto;
+  border-radius: 999px;
   background-color: #fff;
   background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzIzMjIyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTkgMy4xNzFhMS44MjkgMS44MjkgMCAwIDAtMS4yOTMuNTM2TDQuMzk1IDE3LjAxOWwtLjk3IDMuNTU2IDMuNTU2LS45N0wyMC4yOTMgNi4yOTNBMS44MjkgMS44MjkgMCAwIDAgMTkgMy4xN1ptLTEuNDY1LTEuNzA4YTMuODI5IDMuODI5IDAgMCAxIDQuMTcyIDYuMjQ0bC0xMy41IDEzLjVhMSAxIDAgMCAxLS40NDQuMjU4bC01LjUgMS41YTEgMSAwIDAgMS0xLjIyOC0xLjIyOGwxLjUtNS41YTEgMSAwIDAgMSAuMjU4LS40NDRsMTMuNS0xMy41YTMuODI5IDMuODI5IDAgMCAxIDEuMjQyLS44M1oiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==");
   background-repeat: no-repeat;
   background-size: 16px;
-  border-radius: 999px;
   background-position: center;
 }
 
@@ -61,8 +61,8 @@
 
 .frontend-editing-toggle-not-configured {
   transform: scale(1);
-  box-shadow: 0 0 0 0 rgba(255, 0, 0, 1);
   animation: pulse 2s infinite;
+  box-shadow: 0 0 0 0 rgba(255, 0, 0, 1);
 }
 
 @keyframes pulse {
-- 
GitLab


From 73044989e38e9e077e6ebec04eb525578737eb5b Mon Sep 17 00:00:00 2001
From: Artem  Dmitriiev <a.dmitriiev@1xinternet.de>
Date: Fri, 28 Feb 2025 08:15:28 +0100
Subject: [PATCH 08/10] Fix style lint

---
 css/forms_helper.css |  8 ++++----
 css/ui_toggle.css    | 14 +++++++-------
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/css/forms_helper.css b/css/forms_helper.css
index bfd4bf5..3ced948 100644
--- a/css/forms_helper.css
+++ b/css/forms_helper.css
@@ -27,13 +27,13 @@ body.path-frontend-editing {
   display: flex;
   align-items: center;
   justify-content: center;
-  font-size: 1.125em;
-  height: 80px;
   position: fixed;
-  font-weight: 500;
+  height: 80px;
   z-index: 2;
-  margin: 0;
+  font-size: 1.125em;
+  font-weight: 500;
   width: 100%;
+  margin: 0;
   cursor: pointer;
   text-decoration: none;
   background-color: #f5f7fb;
diff --git a/css/ui_toggle.css b/css/ui_toggle.css
index 34b99f2..4927323 100644
--- a/css/ui_toggle.css
+++ b/css/ui_toggle.css
@@ -9,8 +9,8 @@
   width: fit-content !important;
   height: fit-content !important;
   border-radius: 9999px;
-  background-color: white;
   margin: 0 !important;
+  background-color: white;
   padding: 0 !important;
 }
 
@@ -19,24 +19,24 @@
 }
 
 .frontend-editing-toggle a {
-  padding: 0.5rem 0.75rem;
   display: block;
+  padding: 0.5rem 0.75rem;
   text-transform: uppercase;
-  background: rgba(var(--fe-editing-primary-color), 0.6);
   width: 5.5rem;
+  background: rgba(var(--fe-editing-primary-color), 0.6);
   color: white;
-  border-radius: 999px;
   position: relative;
-  font-weight: 500;
+  border-radius: 999px;
   text-align: right;
+  font-weight: 500;
   box-shadow: rgba(0, 0, 0, 0.16) 0 1px 4px;
 }
 
 .frontend-editing-toggle a::before {
   position: absolute;
   top: 0;
-  content: " ";
   bottom: 0;
+  content: " ";
   left: 0.5rem;
   width: 1.75rem;
   height: 1.75rem;
@@ -46,8 +46,8 @@
   background-color: #fff;
   background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzIzMjIyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTkgMy4xNzFhMS44MjkgMS44MjkgMCAwIDAtMS4yOTMuNTM2TDQuMzk1IDE3LjAxOWwtLjk3IDMuNTU2IDMuNTU2LS45N0wyMC4yOTMgNi4yOTNBMS44MjkgMS44MjkgMCAwIDAgMTkgMy4xN1ptLTEuNDY1LTEuNzA4YTMuODI5IDMuODI5IDAgMCAxIDQuMTcyIDYuMjQ0bC0xMy41IDEzLjVhMSAxIDAgMCAxLS40NDQuMjU4bC01LjUgMS41YTEgMSAwIDAgMS0xLjIyOC0xLjIyOGwxLjUtNS41YTEgMSAwIDAgMSAuMjU4LS40NDRsMTMuNS0xMy41YTMuODI5IDMuODI5IDAgMCAxIDEuMjQyLS44M1oiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==");
   background-repeat: no-repeat;
-  background-size: 16px;
   background-position: center;
+  background-size: 16px;
 }
 
 .frontend-editing-toggle a.frontend-editing--enabled {
-- 
GitLab


From d2d5f5e6e6abb78d5bf18ff357fa2d5ef4d4dee2 Mon Sep 17 00:00:00 2001
From: Artem  Dmitriiev <a.dmitriiev@1xinternet.de>
Date: Fri, 28 Feb 2025 09:53:30 +0100
Subject: [PATCH 09/10] Fix all style lint issues

---
 css/forms_helper.css | 10 +++++-----
 css/ui_toggle.css    | 16 ++++++++--------
 2 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/css/forms_helper.css b/css/forms_helper.css
index 3ced948..65e84d9 100644
--- a/css/forms_helper.css
+++ b/css/forms_helper.css
@@ -24,19 +24,19 @@ body.path-frontend-editing {
 }
 
 .path-frontend-editing > h1 {
+  position: fixed;
+  z-index: 2;
   display: flex;
   align-items: center;
   justify-content: center;
-  position: fixed;
-  height: 80px;
-  z-index: 2;
-  font-size: 1.125em;
-  font-weight: 500;
   width: 100%;
+  height: 80px;
   margin: 0;
   cursor: pointer;
   text-decoration: none;
   background-color: #f5f7fb;
+  font-size: 1.125em;
+  font-weight: 500;
 }
 
 .path-frontend-editing .block-system-main-block > form {
diff --git a/css/ui_toggle.css b/css/ui_toggle.css
index 4927323..ab7bd6d 100644
--- a/css/ui_toggle.css
+++ b/css/ui_toggle.css
@@ -8,10 +8,10 @@
   /*  Required to prevent the toggle's position from being affected by external styles. */
   width: fit-content !important;
   height: fit-content !important;
-  border-radius: 9999px;
   margin: 0 !important;
-  background-color: white;
   padding: 0 !important;
+  border-radius: 9999px;
+  background-color: white;
 }
 
 .frontend-editing-toggle *:not(.frontend-editing-toggle-link) {
@@ -19,29 +19,29 @@
 }
 
 .frontend-editing-toggle a {
+  position: relative;
   display: block;
+  width: 5.5rem;
   padding: 0.5rem 0.75rem;
+  text-align: right;
   text-transform: uppercase;
-  width: 5.5rem;
-  background: rgba(var(--fe-editing-primary-color), 0.6);
   color: white;
-  position: relative;
   border-radius: 999px;
-  text-align: right;
-  font-weight: 500;
+  background: rgba(var(--fe-editing-primary-color), 0.6);
   box-shadow: rgba(0, 0, 0, 0.16) 0 1px 4px;
+  font-weight: 500;
 }
 
 .frontend-editing-toggle a::before {
   position: absolute;
   top: 0;
   bottom: 0;
-  content: " ";
   left: 0.5rem;
   width: 1.75rem;
   height: 1.75rem;
   margin-top: auto;
   margin-bottom: auto;
+  content: " ";
   border-radius: 999px;
   background-color: #fff;
   background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzIzMjIyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTkgMy4xNzFhMS44MjkgMS44MjkgMCAwIDAtMS4yOTMuNTM2TDQuMzk1IDE3LjAxOWwtLjk3IDMuNTU2IDMuNTU2LS45N0wyMC4yOTMgNi4yOTNBMS44MjkgMS44MjkgMCAwIDAgMTkgMy4xN1ptLTEuNDY1LTEuNzA4YTMuODI5IDMuODI5IDAgMCAxIDQuMTcyIDYuMjQ0bC0xMy41IDEzLjVhMSAxIDAgMCAxLS40NDQuMjU4bC01LjUgMS41YTEgMSAwIDAgMS0xLjIyOC0xLjIyOGwxLjUtNS41YTEgMSAwIDAgMSAuMjU4LS40NDRsMTMuNS0xMy41YTMuODI5IDMuODI5IDAgMCAxIDEuMjQyLS44M1oiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==");
-- 
GitLab


From 9dd7acd9f500100dca2984cbadbe0add3c19fbeb Mon Sep 17 00:00:00 2001
From: Artem  Dmitriiev <a.dmitriiev@1xinternet.de>
Date: Fri, 28 Feb 2025 11:17:24 +0100
Subject: [PATCH 10/10] No div inside li

---
 css/forms_helper.css                         | 49 +++++++++++++++++---
 src/Controller/FrontendEditingController.php | 19 ++++----
 2 files changed, 52 insertions(+), 16 deletions(-)

diff --git a/css/forms_helper.css b/css/forms_helper.css
index 65e84d9..89698f5 100644
--- a/css/forms_helper.css
+++ b/css/forms_helper.css
@@ -71,23 +71,60 @@ body.frontend-editing-hide-sidebar.gin--classic-toolbar {
   padding-inline-start: 0 !important;
 }
 
+.path-frontend-editing .paragraphs-add-dialog-list {
+  padding: 0 20px;
+  column-count: 1;
+  list-style: none;
+}
+
+@media (min-width: 48em) {
+  .path-frontend-editing .paragraphs-add-dialog-list {
+    column-count: 3;
+    column-gap: 14px;
+  }
+}
+
 .path-frontend-editing .paragraphs-add-dialog-row {
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  gap: 10px;
+  position: relative;
+}
+
+.path-frontend-editing .paragraphs-add-dialog-row .paragraph-type-icon {
+  position: absolute;
+  top: 10px;
+  left: 10px;
+  display: block;
+  box-sizing: border-box;
+  border: none;
+  box-shadow: none;
 }
 
 .path-frontend-editing .paragraphs-add-dialog-row img {
-  width: 60px;
-  height: 60px;
+  width: 50px;
+  height: 50px;
   object-fit: contain;
   border-radius: 5px;
   background: #fff;
 }
 
 .path-frontend-editing ul.paragraphs-add-dialog-list li {
+  width: 100%;
+  margin: 0 0 10px 0;
   list-style: none;
+  text-align: left;
+}
+
+.path-frontend-editing ul.paragraphs-add-dialog-list li a {
+  display: block;
+  box-sizing: border-box;
+  width: 100%;
+  min-height: 70px;
+  margin: 0;
+  text-align: left;
+  line-height: 45px;
+}
+
+.path-frontend-editing ul.paragraphs-add-dialog-list li.with-icon a {
+  padding-left: 75px;
 }
 
 .path-frontend-editing .field-add-more-submit:hover a {
diff --git a/src/Controller/FrontendEditingController.php b/src/Controller/FrontendEditingController.php
index e2c4d84..41281c3 100644
--- a/src/Controller/FrontendEditingController.php
+++ b/src/Controller/FrontendEditingController.php
@@ -641,21 +641,15 @@ class FrontendEditingController extends ControllerBase {
     $items = [];
     foreach ($allowed_paragraphs as $paragraphs_type) {
       $item = [
-        '#type' => 'container',
-        '#attributes' => [
+        '#wrapper_attributes' => [
           'class' => [
             'paragraphs-add-dialog-row',
-            'field-add-more-submit',
-            'button--large',
-            'button',
-            'js-form-submit',
-            'form-submit',
           ],
         ],
       ];
-
       if (!empty($paragraphs_type->get('icon_default'))) {
         $item['icon'] = $this->buildIconElement($paragraphs_type);
+        $item['#wrapper_attributes']['class'][] = 'with-icon';
       }
 
       $item['link'] = [
@@ -672,11 +666,16 @@ class FrontendEditingController extends ControllerBase {
           'query' => $request->query->all(),
         ]),
         '#attributes' => [
-          'class' => 'field-add-more-submit js-form-submit form-submit',
+          'class' => [
+            'js-form-submit',
+            'form-submit',
+            'button',
+            'button--large',
+            'field-add-more-submit',
+          ],
           'name' => $parent_field_name . '_' . $paragraphs_type->id() . '_add_more',
         ],
       ];
-
       $items[$paragraphs_type->id()] = $item;
     }
     if (!empty($settings['handler_settings']['target_bundles_drag_drop'])) {
-- 
GitLab