From 2330d5f39426b2078c8be612034e2ae020b38ba8 Mon Sep 17 00:00:00 2001
From: Elmar Ludwig <elmar.ludwig@uni-osnabrueck.de>
Date: Tue, 19 Sep 2023 10:42:59 +0000
Subject: [PATCH] add separator element to ActionMenu, fixes #3013

Closes #3013

Merge request studip/studip!2017
---
 lib/classes/ActionMenu.php                    | 33 +++++++++++++++++--
 .../assets/stylesheets/scss/actionmenu.scss   |  6 ++++
 resources/vue/components/StudipActionMenu.vue |  9 ++---
 templates/shared/action-menu-single.php       |  2 ++
 templates/shared/action-menu.php              |  2 ++
 5 files changed, 46 insertions(+), 6 deletions(-)

diff --git a/lib/classes/ActionMenu.php b/lib/classes/ActionMenu.php
index 6fc016a2e7c..aa6c5240e47 100644
--- a/lib/classes/ActionMenu.php
+++ b/lib/classes/ActionMenu.php
@@ -96,6 +96,18 @@ class ActionMenu
         return $result;
     }
 
+    /**
+     * Returns the number of action menu items (not counting separators).
+     *
+     * @return int count
+     */
+    protected function countActions(): int
+    {
+        return count(array_filter($this->actions, function($action) {
+            return $action['type'] !== 'separator';
+        }));
+    }
+
     /**
      * Adds a link to the list of actions.
      *
@@ -213,6 +225,23 @@ class ActionMenu
         return $this;
     }
 
+    /**
+     * Adds a separator line to the list of actions.
+     *
+     * @return ActionMenu instance to allow chaining
+     */
+    public function addSeparator(): ActionMenu
+    {
+        if ($this->checkCondition()) {
+            $this->actions[] = [
+                'type'   => 'separator',
+                'index'  => ''
+            ];
+        }
+
+        return $this;
+    }
+
     /**
      * Adds a css classs to the root element in html.
      *
@@ -251,7 +280,7 @@ class ActionMenu
      */
     public function render()
     {
-        if (count($this->actions) === 0) {
+        if ($this->countActions() === 0) {
             return '';
         }
 
@@ -342,7 +371,7 @@ class ActionMenu
         $rendering_mode = $this->rendering_mode;
 
         if ($rendering_mode === null) {
-            $rendering_mode = count($this->actions) <= Config::get()->ACTION_MENU_THRESHOLD
+            $rendering_mode = $this->countActions() <= Config::get()->ACTION_MENU_THRESHOLD
                             ? self::RENDERING_MODE_ICONS
                             : self::RENDERING_MODE_MENU;
         }
diff --git a/resources/assets/stylesheets/scss/actionmenu.scss b/resources/assets/stylesheets/scss/actionmenu.scss
index 588f50c0391..56b1317c412 100644
--- a/resources/assets/stylesheets/scss/actionmenu.scss
+++ b/resources/assets/stylesheets/scss/actionmenu.scss
@@ -141,6 +141,12 @@ $action-menu-shadow: 1px 1px 1px $dark-gray-color-60;
                 color: $active-color;
             }
         }
+
+        > hr {
+            border-style: none;
+            border-top: thin solid var(--dark-gray-color-45);
+            margin: 4px 0;
+        }
     }
 
     &.is-open {
diff --git a/resources/vue/components/StudipActionMenu.vue b/resources/vue/components/StudipActionMenu.vue
index d67f6a230f4..014a1cacefc 100644
--- a/resources/vue/components/StudipActionMenu.vue
+++ b/resources/vue/components/StudipActionMenu.vue
@@ -11,10 +11,10 @@
             </div>
             <ul class="action-menu-list">
                 <li v-for="item in navigationItems" :key="item.id" class="action-menu-item">
-                    <a v-if="item.type === 'link'" v-bind="linkAttributes(item)" v-on="linkEvents(item)">
+                    <hr v-if="item.type === 'separator'">
+                    <a v-else-if="item.type === 'link'" v-bind="linkAttributes(item)" v-on="linkEvents(item)">
                         <studip-icon v-if="item.icon !== false" :shape="item.icon.shape" :role="item.icon.role"></studip-icon>
                         <span v-else class="action-menu-no-icon"></span>
-
                         {{ item.label }}
                     </a>
                     <label v-else-if="item.icon" class="undecorated" v-bind="linkAttributes(item)" v-on="linkEvents(item)">
@@ -33,7 +33,8 @@
     </div>
     <div v-else>
         <a v-for="item in navigationItems" :key="item.id" v-bind="linkAttributes(item)" v-on="linkEvents(item)">
-            <studip-icon :title="item.label" :shape="item.icon.shape" :role="item.icon.role" :size="20"></studip-icon>
+            <span v-if="item.type === 'separator'" class="quiet">|</span>
+            <studip-icon v-else :title="item.label" :shape="item.icon.shape" :role="item.icon.role" :size="20"></studip-icon>
         </a>
     </div>
 </template>
@@ -119,7 +120,7 @@ export default {
             if (collapseAt === true) {
                 return true;
             }
-            return Number.parseInt(collapseAt) <= this.items.length;
+            return Number.parseInt(collapseAt) <= this.items.filter((item) => item.type !== 'separator').length;
         },
         title () {
             return this.context ? this.$gettextInterpolate(this.$gettext('Aktionsmenü für %{context}'), {context: this.context}) : this.$gettext('Aktionsmenü');
diff --git a/templates/shared/action-menu-single.php b/templates/shared/action-menu-single.php
index a34a01e69dc..455bc44a6ba 100644
--- a/templates/shared/action-menu-single.php
+++ b/templates/shared/action-menu-single.php
@@ -30,5 +30,7 @@
         <? endif ?>
     <? elseif ($action['type'] === 'multi-person-search'): ?>
         <?= $action['object']->render(false) ?>
+    <? elseif ($action['type'] === 'separator'): ?>
+        <span class="quiet">|</span>
     <? endif ?>
 <? endforeach ?>
diff --git a/templates/shared/action-menu.php b/templates/shared/action-menu.php
index c781f333859..4f3b5f10ff9 100644
--- a/templates/shared/action-menu.php
+++ b/templates/shared/action-menu.php
@@ -48,6 +48,8 @@
                 <? endif ?>
             <? elseif ($action['type'] === 'multi-person-search'): ?>
                 <?= $action['object']->render() ?>
+            <? elseif ($action['type'] === 'separator'): ?>
+                <hr>
             <? endif ?>
             </li>
         <? endforeach ?>
-- 
GitLab