diff --git a/frappe/public/js/frappe/ui/sidebar.js b/frappe/public/js/frappe/ui/sidebar.js index 372072a76b..0a3eaaa269 100644 --- a/frappe/public/js/frappe/ui/sidebar.js +++ b/frappe/public/js/frappe/ui/sidebar.js @@ -50,6 +50,59 @@ frappe.ui.Sidebar = class Sidebar { }); this.apps_switcher = new frappe.ui.AppsSwitcher(this); this.apps_switcher.create_app_data_map(); + + // Add hover functionality for auto-expand + this.setup_hover_expand(); + } + + setup_hover_expand() { + const sidebar = this.wrapper.find(".body-sidebar"); + const placeholder = this.wrapper.find(".body-sidebar-placeholder"); + let hoverTimeout; + + sidebar.on("mouseenter", () => { + // Only expand on hover if sidebar is collapsed and not on mobile + if (!this.sidebar_expanded && !frappe.is_mobile()) { + clearTimeout(hoverTimeout); + hoverTimeout = setTimeout(() => { + this.wrapper.addClass("hover-expanded"); + }, 200); // Small delay to prevent accidental expansion + } + }); + + sidebar.on("mouseleave", () => { + // Only collapse on hover if sidebar is collapsed and not on mobile + if (!this.sidebar_expanded && !frappe.is_mobile()) { + clearTimeout(hoverTimeout); + hoverTimeout = setTimeout(() => { + this.wrapper.removeClass("hover-expanded"); + }, 300); // Slightly longer delay to prevent accidental collapse + } + }); + + // Also handle hover on the placeholder to prevent collapse when moving to main content + placeholder.on("mouseenter", () => { + if (!this.sidebar_expanded && !frappe.is_mobile()) { + clearTimeout(hoverTimeout); + this.wrapper.addClass("hover-expanded"); + } + }); + + // Handle mouse leave on placeholder as well + placeholder.on("mouseleave", () => { + if (!this.sidebar_expanded && !frappe.is_mobile()) { + clearTimeout(hoverTimeout); + hoverTimeout = setTimeout(() => { + this.wrapper.removeClass("hover-expanded"); + }, 300); + } + }); + + // Clear hover state when manually toggling sidebar + this.wrapper.find(".body-sidebar .collapse-sidebar-link").on("click", () => { + clearTimeout(hoverTimeout); + this.wrapper.removeClass("hover-expanded"); + }); } set_hover() { @@ -269,10 +322,12 @@ frappe.ui.Sidebar = class Sidebar { let direction; if (this.sidebar_expanded) { this.wrapper.addClass("expanded"); + this.wrapper.removeClass("hover-expanded"); // Remove hover state when manually expanded // this.sidebar_expanded = false direction = "left"; } else { this.wrapper.removeClass("expanded"); + this.wrapper.removeClass("hover-expanded"); // Remove hover state when manually collapsed // this.sidebar_expanded = true direction = "right"; } diff --git a/frappe/public/scss/desk/sidebar.scss b/frappe/public/scss/desk/sidebar.scss index f10d733c38..fdeea2a0be 100644 --- a/frappe/public/scss/desk/sidebar.scss +++ b/frappe/public/scss/desk/sidebar.scss @@ -90,6 +90,7 @@ body { height: 100vh; z-index: 1030; padding: 8px 8px 10px 8px; + transition: width 0.2s ease-in-out; .body-sidebar-top { flex: 1 1; @@ -289,6 +290,15 @@ body { @include body-sidebar-expanded(); } +.body-sidebar-container.hover-expanded { + @include body-sidebar-expanded(); + + .body-sidebar { + // Ensure smooth transition for hover state + transition: width 0.2s ease-in-out; + } +} + @include media-breakpoint-down(sm) { // body sidebar hidded in mobile view .body-sidebar-container {