From c1033b1e1d12fd4380234950d117144ac015f44f Mon Sep 17 00:00:00 2001
From: Thomas Hackl <hackl@data-quest.de>
Date: Tue, 10 Oct 2023 10:20:09 +0000
Subject: [PATCH] =?UTF-8?q?Resolve=20"Sidebar=20nur=20auf=20der=20Veransta?=
 =?UTF-8?q?ltungs=C3=BCbersicht=20f=C3=BCr=20Admins/Roots=20scrollbar=20ma?=
 =?UTF-8?q?chen,=20falls=20sie=20zu=20lang=20ist"?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Closes #2936, #2934, #2931, #2930, #2929, and #2928

Merge request studip/studip!1977
---
 .../assets/javascripts/bootstrap/sidebar.js   | 15 ++++
 resources/assets/javascripts/entry-base.js    |  1 +
 resources/assets/javascripts/lib/sidebar.js   | 76 ++++++++++++++++---
 .../assets/stylesheets/scss/sidebar.scss      | 61 +++++++++++++++
 .../responsive/ResponsiveNavigation.vue       |  3 +-
 5 files changed, 143 insertions(+), 13 deletions(-)
 create mode 100644 resources/assets/javascripts/bootstrap/sidebar.js

diff --git a/resources/assets/javascripts/bootstrap/sidebar.js b/resources/assets/javascripts/bootstrap/sidebar.js
new file mode 100644
index 00000000000..f308e6cf02b
--- /dev/null
+++ b/resources/assets/javascripts/bootstrap/sidebar.js
@@ -0,0 +1,15 @@
+STUDIP.ready(() => {
+    // Apply sidebar magic only on admin/courses
+    if (
+        document.body.id === 'admin-courses-index'
+        && !document.documentElement.classList.contains('responsive-display')
+    ) {
+        STUDIP.Sidebar.observeFooter();
+        STUDIP.Sidebar.observeSidebar();
+
+        document.defaultView.addEventListener('resize', () => {
+            STUDIP.Sidebar.reset();
+        });
+    }
+
+});
diff --git a/resources/assets/javascripts/entry-base.js b/resources/assets/javascripts/entry-base.js
index f9cab014d4e..af1445a2660 100644
--- a/resources/assets/javascripts/entry-base.js
+++ b/resources/assets/javascripts/entry-base.js
@@ -86,6 +86,7 @@ import "./bootstrap/cache-admin.js"
 import "./bootstrap/oer.js"
 import "./bootstrap/courseware.js"
 import "./bootstrap/responsive-navigation.js"
+import "./bootstrap/sidebar.js"
 
 import "./mvv_course_wizard.js"
 import "./mvv.js"
diff --git a/resources/assets/javascripts/lib/sidebar.js b/resources/assets/javascripts/lib/sidebar.js
index 3cb1c05eba8..13418761fdb 100644
--- a/resources/assets/javascripts/lib/sidebar.js
+++ b/resources/assets/javascripts/lib/sidebar.js
@@ -1,20 +1,72 @@
-import Scroll from './scroll.js';
-
 const Sidebar = {
-    open () {
-        this.toggle(true);
+
+    observeSidebar() {
+        const options = {
+            root: null,
+            rootMargin: '0px 0px 35px 0px',
+            threshold: 1
+        };
+
+        /**
+         * Observe if sidebar fits into viewport.
+         */
+        const sidebar = document.getElementById('sidebar');
+        if (sidebar) {
+            const sObserver = new IntersectionObserver(STUDIP.Sidebar.fits, options);
+            sObserver.observe(sidebar, options);
+        }
     },
-    close () {
-        this.toggle(false);
+
+    observeFooter() {
+        const options = {
+            root: null,
+            rootMargin: '0px',
+            threshold: 1
+        };
+
+        /**
+         * Observe if the footer is visible in viewport.
+         */
+        const fObserver = new IntersectionObserver(STUDIP.Sidebar.footerVisible, options);
+        fObserver.observe(document.getElementById('main-footer'), options);
+
+    },
+
+    reset() {
+        const sidebar = document.getElementById('sidebar');
+        if (sidebar) {
+            sidebar.classList.remove('oversized', 'was-oversized', 'fixed');
+            sidebar.style.top = '';
+            STUDIP.Sidebar.observeSidebar();
+        }
     },
-    toggle (visible = null) {
-        visible = visible ?? !$('#sidebar').hasClass('visible-sidebar');
 
-        // Hide navigation
-        $('#responsive-toggle').prop('checked', false);
-        $('#responsive-navigation').removeClass('visible');
+    fits(entries, observer) {
+        const sidebar = document.getElementById('sidebar');
+        if (sidebar) {
+            entries.forEach(entry => {
+                // Sidebar fits onto current page.
+                if (entry.isIntersecting) {
+                    sidebar.classList.remove('oversized');
+                } else {
+                    sidebar.classList.add('oversized', 'was-oversized');
+                }
+            });
+        }
+    },
 
-        $('#sidebar').toggleClass('visible-sidebar', visible);
+    footerVisible(entries, observer) {
+        const sidebar = document.getElementById('sidebar');
+        if (sidebar) {
+            entries.forEach(entry => {
+                // Footer is visible on current page.
+                if (entry.isIntersecting) {
+                    sidebar.classList.remove('no-footer');
+                } else {
+                    sidebar.classList.add('no-footer');
+                }
+            });
+        }
     }
 };
 
diff --git a/resources/assets/stylesheets/scss/sidebar.scss b/resources/assets/stylesheets/scss/sidebar.scss
index a699940ffb0..08335d09212 100644
--- a/resources/assets/stylesheets/scss/sidebar.scss
+++ b/resources/assets/stylesheets/scss/sidebar.scss
@@ -112,6 +112,67 @@
     }
 }
 
+html {
+    &:not(.responsive-display) {
+        #admin-courses-index {
+            #sidebar {
+                top: 50px;
+                transition: all var(--transition-duration) ease-in-out;
+
+                &.was-oversized {
+                    height: calc(100vh - 250px);
+                    overflow-y: auto;
+                    position: fixed;
+                    top: 155px;
+
+                    &.no-footer {
+                        height: calc(100vh - 200px);
+                    }
+                }
+
+                .widget-links {
+                    li.active {
+                        &::before,
+                        &::after {
+                            border: 0;
+                        }
+                    }
+                }
+            }
+
+            &.fixed {
+                #sidebar {
+                    height: calc(100vh - 125px);
+                    top: 35px;
+
+                    &.no-footer {
+                        height: calc(100vh - 100px);
+                    }
+                }
+            }
+
+            &.fullscreen-sidebar-shown {
+                #sidebar {
+                    top: 110px;
+
+                    &.was-oversized {
+                        height: calc(100vh - 200px);
+
+                        &.no-footer {
+                            height: calc(100vh - 150px);
+                        }
+
+                        &.fixed {
+                            top: 110px;
+                        }
+                    }
+                }
+            }
+
+        }
+    }
+}
+
 ul.widget-list {
     list-style: none;
     margin: 0;
diff --git a/resources/vue/components/responsive/ResponsiveNavigation.vue b/resources/vue/components/responsive/ResponsiveNavigation.vue
index eda2bfa86ee..6e56fdaf10a 100644
--- a/resources/vue/components/responsive/ResponsiveNavigation.vue
+++ b/resources/vue/components/responsive/ResponsiveNavigation.vue
@@ -321,7 +321,8 @@ export default {
                 this.showMenu = false;
                 cache.remove('fullscreen-mode');
                 document.getElementById('responsive-toggle-focusmode').style.display = 'none';
-                document.body.style.display = null;
+                document.body.classList.remove('fullscreen-sidebar-shown');
+                document.body.style.display = '';
 
                 const siteTitle = document.getElementById('site-title');
                 if (siteTitle.dataset.originalTitle) {
-- 
GitLab