]> git.agnieray.net Git - galette.git/commitdiff
New toggable compact rendering on navigation aside
authorGuillaume AGNIERAY <dev@agnieray.net>
Tue, 3 Oct 2023 08:31:27 +0000 (10:31 +0200)
committerGuillaume AGNIERAY <dev@agnieray.net>
Tue, 3 Oct 2023 08:31:27 +0000 (10:31 +0200)
galette/lib/Galette/Core/Authentication.php
galette/templates/default/elements/logout.html.twig
galette/templates/default/elements/navigation/navigation_aside.html.twig
galette/templates/default/elements/navigation/navigation_items.html.twig
galette/templates/default/elements/navigation/navigation_topbar.html.twig
galette/templates/default/elements/scripts.html.twig
galette/templates/default/macros.twig
galette/templates/default/page.html.twig
ui/semantic/galette/globals/site.overrides

index 206c7e30e67055dc18768b49e216fbba43bfe70e..da32eb55c2f6531d77b2adda0aea8ea804b3cae2 100644 (file)
@@ -77,6 +77,7 @@ abstract class Authentication
     protected $uptodate = false;
     protected $managed_groups = [];
     protected $cron = false;
+    protected $compact_menu = false;
 
     /**
      * Logs in user.
@@ -261,6 +262,16 @@ abstract class Authentication
         return $this->managed_groups;
     }
 
+    /**
+     * Get compact menu mode
+     *
+     * @return bool
+     */
+    public function getCompactMenu(): bool
+    {
+        return ($this->logged && isset($_COOKIE['galette_compact_menu']) && $_COOKIE['galette_compact_menu']) ? true : false;
+    }
+
     /**
      * Is user currently up to date?
      * An up to date member is active and either due free, or with up to date
index f9b4ffbb34a0a9a12cb0afc4a657b140037c3401..16f9afd685c5275fedb303720287d6e36b7d1154 100644 (file)
@@ -5,38 +5,72 @@
        {% set component_classes = "ui text compact small fluid menu" %}
     {% endif %}
 {% endif %}
-
-{% if ui == 'item' %}
-    <div class="{{ component_classes }}">
-        <div class="ui basic center aligned fitted segment">
-            <span class="ui tiny header">{{ login.loggedInAs()|raw }}</span>
-        </div>
-        <a
-            class="ui fluid {% if login.isImpersonated() %}purple{% else %}red{% endif %} basic button"
-            href="{% if login.isImpersonated() %}{{ url_for("unimpersonate") }}{% else %}{{ url_for("logout") }}{% endif %}"
-        >
-            <i class="icon {% if login.isImpersonated() %}user secret{% else %}sign out alt{% endif %}"></i>
-            {% if login.isImpersonated() %}{{ _T("Unimpersonate") }}{% else %}{{ _T("Log off") }}{% endif %}
-        </a>
-    </div>
-{% else %}
-    <div class="{{ component_classes }}">
-        <div class="ui item">
-            <i class="user circle big icon"></i>
-            {{ login.loggedInAs()|raw }}
-        </div>
-        <div class="right menu">
-            <div class="item">
-                <a
-                    class="ui {% if login.isImpersonated() %}purple{% else %}red{% endif %} icon button"
-                    href="{% if login.isImpersonated() %}{{ url_for("unimpersonate") }}{% else %}{{ url_for("logout") }}{% endif %}"
-                    title="{% if login.isImpersonated() %}{{ _T("Unimpersonate") }}{% else %}{{ _T("Log off") }}{% endif %}"
-                    data-position="bottom right"
-                >
-                    <i class="icon {% if login.isImpersonated() %}user secret{% else %}sign out alt{% endif %}"></i>
-                    <span class="displaynone">{% if login.isImpersonated() %}{{ _T("Unimpersonate") }}{% else %}{{ _T("Log off") }}{% endif %}</span>
-                </a>
+{% if login.isLogged() %}
+    {% if ui == 'item' %}
+        <div class="{{ component_classes }}">
+            <div class="ui basic center aligned fitted segment">
+                <span class="ui tiny header">{{ login.loggedInAs()|raw }}</span>
             </div>
+            <a
+                class="ui fluid {% if login.isImpersonated() %}purple{% else %}red{% endif %} basic button"
+                href="{% if login.isImpersonated() %}{{ url_for("unimpersonate") }}{% else %}{{ url_for("logout") }}{% endif %}"
+            >
+                <i class="icon {% if login.isImpersonated() %}user secret{% else %}sign out alt{% endif %}"></i>
+                {% if login.isImpersonated() %}{{ _T("Unimpersonate") }}{% else %}{{ _T("Log off") }}{% endif %}
+            </a>
         </div>
-    </div>
+    {% else %}
+        {% if not login.getCompactMenu() %}
+            <div class="{{ component_classes }}">
+                <div class="ui item">
+                    <i class="user circle big icon"></i>
+                    {{ login.loggedInAs()|raw }}
+                </div>
+                <div class="right menu">
+                    <div class="item">
+                        <a
+                            class="ui {% if login.isImpersonated() %}purple{% else %}red{% endif %} icon button"
+                            href="{% if login.isImpersonated() %}{{ url_for("unimpersonate") }}{% else %}{{ url_for("logout") }}{% endif %}"
+                            title="{% if login.isImpersonated() %}{{ _T("Unimpersonate") }}{% else %}{{ _T("Log off") }}{% endif %}"
+                            data-position="bottom right"
+                        >
+                            <i class="icon {% if login.isImpersonated() %}user secret{% else %}sign out alt{% endif %}"></i>
+                            <span class="displaynone">{% if login.isImpersonated() %}{{ _T("Unimpersonate") }}{% else %}{{ _T("Log off") }}{% endif %}</span>
+                        </a>
+                    </div>
+                </div>
+            </div>
+            {% include "elements/modes.html.twig" %}
+        {% else %}
+            {% set component_classes = "ui vertical centered tiny fluid icon menu" %}
+            <div id="logoutmenu" class="{{ component_classes }}">
+                <div class="ui dropdown item no-touch tooltip" data-html="{{ login.loggedInAs()|raw }}" data-position="right center">
+                    <i class="user circle icon"></i>
+                    <span class="text displaynone">{{ login.loggedInAs()|raw }}</span>
+                    <div class="menu">
+                        <div class="item">
+                            <div class="ui basic center aligned fitted segment">
+                                <img src="{{ url_for('logo') }}" width="{{ logo.getOptimalWidth() }}" height="{{ logo.getOptimalHeight() }}" alt="{{ preferences.pref_nom }}" class="icon"/>
+                                <div class="ui block huge brand header">
+                                    {{ preferences.pref_nom }}
+                                    {% if preferences.pref_slogan %}<div class="sub tiny header">{{ __(preferences.pref_slogan) }}</div>{% endif %}
+                                </div>
+                            </div>
+                            {{ login.loggedInAs()|raw }}
+                            <div class="ui basic fitted segment">
+                                {% include "elements/modes.html.twig" %}
+                            </div>
+                            <a
+                                class="ui {% if login.isImpersonated() %}purple{% else %}red{% endif %} icon button"
+                                href="{% if login.isImpersonated() %}{{ url_for("unimpersonate") }}{% else %}{{ url_for("logout") }}{% endif %}"
+                            >
+                                <i class="icon {% if login.isImpersonated() %}user secret{% else %}sign out alt{% endif %}"></i>
+                                {% if login.isImpersonated() %}{{ _T("Unimpersonate") }}{% else %}{{ _T("Log off") }}{% endif %}
+                            </a>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        {% endif %}
+    {% endif %}
 {% endif %}
index 17a4253506153264e5a1c227264b44003da892bd..a8c29141e29696064fc576dafdd99d478efbd022 100644 (file)
@@ -1,12 +1,9 @@
-<aside class="ui computer only toc">
-{% if login.isLogged() %}
+<aside id="sidemenu" class="ui computer only toc{% if login.getCompactMenu() %} compact_menu{% endif %}">
     {% include "elements/logout.html.twig" with {
             ui: "menu"
     } %}
-{% endif %}
-
-    {% include "elements/modes.html.twig" %}
 
+{% if not login.getCompactMenu() %}
     <div class="ui basic center aligned fitted segment">
         <img src="{{ url_for('logo') }}" width="{{ logo.getOptimalWidth() }}" height="{{ logo.getOptimalHeight() }}" alt="{{ preferences.pref_nom }}" class="icon"/>
         <div class="ui block huge brand header">
             {% if preferences.pref_slogan %}<div class="sub tiny header">{{ __(preferences.pref_slogan) }}</div>{% endif %}
         </div>
     </div>
+{% endif %}
+
+    <div class="ui vertical{% if not login.getCompactMenu() %} accordion compact{% else %} tiny icon{% endif %} fluid menu">
+        {% set mode = login.getCompactMenu() ? "compact" : "accordion" %}
+        {% include "elements/navigation/navigation_items.html.twig" with {
+                mode: mode
+        } %}
+    </div>
 
-    <div class="ui vertical accordion compact fluid menu">
-        {% include "elements/navigation/navigation_items.html.twig" %}
+    <div class="ui basic fitted segment">
+        <div class="ui toggle mini checkbox">
+            <input type="checkbox" name="compactmenu" id="compactmenu" class="hidden"{% if login.getCompactMenu() %} checked="checked"{% endif %}>
+            <label for="compactmenu"{% if login.getCompactMenu() %} class="no-touch tooltip" data-html="{{ _T("Compact menu") }}"{% endif %}>
+                {% if not login.getCompactMenu() %}{{ _T("Compact menu") }}{% endif %}
+            </label>
+        </div>
     </div>
 </aside>
index 6e1bb9480b4a851b25ac56ce308d8a167f1c09f5..3eebec78208441ec77d7d6b154a2a675a8b2ae09 100644 (file)
@@ -1,18 +1,25 @@
 {% import "macros.twig" as menus_macros %}
 
+{% set mode = (mode is defined) ? mode %}
+
 {% if login.isLogged() %}
         <a href="{{ url_for('dashboard') }}"
             title="{{ _T("Go to Galette's dashboard") }}"
             class="{% if cur_route == "dashboard" %}active {% endif %}item"
+            data-position="right center"
         >
-            <div class="image header title">
+        {% if mode != "compact" %}
+            <div class="image header title{% if mode == "compact" %} no-touch tooltip" data-html="{{ _T("Dashboard") }}"{% else %}"{% endif %}>
+        {% endif %}
                 <i class="compass icon" aria-hidden="true"></i>
+        {% if mode != "compact" %}
                 {{ _T("Dashboard") }}
             </div>
+        {% endif %}
         </a>
 {% endif %}
 
 {% set menus = callstatic('\\Galette\\Core\\Galette', 'getAllMenus') %}
 {% for menu in menus %}
-    {{ menus_macros.renderMenu(menu.title, menu.icon, menu.items) }}
+    {{ menus_macros.renderMenu(menu.title, menu.icon, menu.items, mode) }}
 {% endfor %}
index 34bf2fa914e1eb5dfbbcc0522b7ab65d1a4bb047..ab76de9aa8697d21f7c12e622c56a27e7c6ea037 100644 (file)
@@ -1,4 +1,4 @@
-<header id="top-navbar" class="ui large top fixed menu">
+<header id="top-navbar" class="ui top fixed menu">
     <div class="ui fluid container">
         <a class="toc item">
             <i class="sidebar icon"></i>
index e9990630a2544566fd73e0ed655dc920c2f12672..fc781b96c5d92f98a1e2013d5ddb96095e90b06c 100644 (file)
                     }
                 });
 
+    {% if login.isLogged() %}
+                // Display user menu in compact mode
+                var _compactMenu = function() {
+                    var _hidden = Cookies.get('galette_compact_menu');
+                    var _menu = document.getElementById('sidemenu');
+                    var _content = _menu.nextElementSibling;
+                    var _trigger = document.getElementById('compactmenu');
+                    if (_hidden & _hidden == 1) {
+                        _trigger.checked = true;
+                        _menu.classList.add('compact_menu');
+                        _content.classList.add('extended');
+                    }
+                    _trigger.addEventListener('change', function() {
+                        var _checked = $(this).is(':checked');
+                        Cookies.set(
+                            'galette_compact_menu',
+                            (_checked ? 1 : 0),
+                                {
+                                    expires: 365,
+                                    path: '/'
+                                }
+                        );
+                        window.location.reload();
+                    });
+                }
+
+                _compactMenu();
+    {% endif %}
+
     {% if autocomplete %}
                 $('#ville_adh_field, #lieu_naissance_field').search({
                     apiSettings: {
index 3f3c4c542283bd25b1638444f4b489f1ab6433ec..fe21d3986a6fbcbe9988dc3e967865ad17130c4c 100644 (file)
@@ -1,20 +1,32 @@
-{% macro renderMenu(title, icon, items) %}
+{% macro renderMenu(title, icon, items, mode) %}
     {% set my_routes = [] %}
     {% for item in items %}
         {% set my_routes = my_routes|merge([item.route.name])|merge(item.route.aliases ?? []) %}
     {% endfor %}
-    <div class="item">
-        <div class="image header title{% if cur_route in my_routes %} active{% endif %}">
+    {% if mode == "compact" %}
+        <div class="ui{% if cur_route in my_routes %} active-menu{% endif %} dropdown item no-touch tooltip" data-html="{{ title }}" data-position="right center">
             <i class="{{ icon }} icon"></i>
-            {{ title }}
-            <i class="dropdown icon"></i>
+            <span class="displaynone">{{ title }}</span>
+            <div class="menu">
+                {% for item in items %}
+                    {{ _self.renderMenuItem(item.label, item.title, item.route, item.icon ?? null, null, 'right center') }}
+                {% endfor %}
+            </div>
         </div>
-        <div class="content{% if cur_route in my_routes %} active{% endif %}">
-            {% for item in items %}
-                {{ _self.renderMenuItem(item.label, item.title, item.route, item.icon ?? null) }}
-            {% endfor %}
+    {% else %}
+        <div class="item">
+            <div class="image header title{% if cur_route in my_routes %} active{% endif %}">
+                <i class="{{ icon }} icon"></i>
+                {{ title }}
+                <i class="dropdown icon"></i>
+            </div>
+            <div class="content{% if cur_route in my_routes %} active{% endif %}">
+                {% for item in items %}
+                    {{ _self.renderMenuItem(item.label, item.title, item.route, item.icon ?? null) }}
+                {% endfor %}
+            </div>
         </div>
-    </div>
+    {% endif %}
 {% endmacro %}
 
 {% macro renderMenuItem(label, title, route, icon, class, tips_position) %}
index bb916849e8331d4df71f73c961d55463d8d6eaee..e51228deb169932daed309cde7de52754dba5c39 100644 (file)
@@ -14,7 +14,7 @@
 {%  if login.isLogged() %}
     {% include 'elements/navigation/navigation_aside.html.twig' %}
 {% endif %}
-                <section class="content{% if contentcls is defined %} {{ contentcls }}{% endif %}">
+                <section class="content{% if contentcls is defined %} {{ contentcls }}{% endif %}{% if login.getCompactMenu() %} extended{% endif %}">
 {% if not login.isLogged() %}
                     <div class="ui basic center aligned fitted segment">
                         <img src="{{ url_for("logo") }}" width="{{ logo.getOptimalWidth() }}" height="{{ logo.getOptimalHeight() }}" alt="{{ preferences.pref_nom }}" class="icon"/>
index 39e53fa147cafab56585e47b677e114b888c959d..0c7a7a2e6e3e2579ede35b1cb7c0a037fe0d8d65 100644 (file)
@@ -125,11 +125,57 @@ footer .ui.horizontal.list > .item{
     overflow-y: scroll;
     width: 260px;
     padding: 1.25rem;
+    &.compact_menu {
+      z-index: 1001;
+      width: 85px;
+      overflow-y: initial;
+      #logoutmenu {
+        .ui.dropdown .menu > .item:hover,
+        .ui.dropdown .menu > .active.item {
+          background: @white !important;
+          color: @textColor !important;
+          font-weight: 400 !important;
+          cursor: default;
+        }
+        .brand.header {
+          max-width: 500px;
+          white-space: normal;
+        }
+      }
+      .ui.menu .dropdown.item.active-menu {
+        background: @lightGaletteColor;
+      }
+      .ui.toggle.checkbox {
+        line-height: 1rem;
+        input {
+          width: 3rem;
+          height: 1rem;
+          &:checked ~ label::after {
+            left: 1.35rem;
+          }
+        }
+        label {
+          min-height: 1rem;
+          padding-left: 4rem;
+          &::before {
+            width: 2.4rem;
+            height: 1rem;
+          }
+          &::after {
+            width: 1rem;
+            height: 1rem;
+          }
+        }
+      }
+    }
   }
 
   body:not(.front-page) section.content {
     margin-left: 260px;
     flex: 1 1 auto;
+    &.extended {
+      margin-left: 85px;
+    }
   }
 
   section.desktop .container {