mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-28 16:32:28 +00:00
Merge pull request #325 from PR0M3TH3AN/codex/fix-sidebar-ui/ux-issues
Improve mobile sidebar interactions
This commit is contained in:
4 files changed
+122
-25
No files matched your search
+41
-7
@@ -1419,26 +1419,52 @@ footer a:hover {
|
||||
left: 0;
|
||||
width: 16rem; /* Tailwind's w-64 */
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
background-color: #0f172a;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
transition: transform 0.3s ease;
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
/* Keep below modal stacks (z-50+) so dialogs overlay the drawer. */
|
||||
z-index: 40;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#sidebar.sidebar-open {
|
||||
box-shadow: 0 0.75rem 1.5rem rgba(15, 23, 42, 0.45);
|
||||
}
|
||||
|
||||
#sidebarOverlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(15, 23, 42, 0.55);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s ease;
|
||||
/* Sits above page chrome but below modals and the sidebar itself. */
|
||||
z-index: 30;
|
||||
}
|
||||
|
||||
body.sidebar-open #sidebarOverlay {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* Mobile (max-width: 767px): Hide sidebar by default */
|
||||
@media (max-width: 767px) {
|
||||
#sidebar {
|
||||
transform: translateX(-100%);
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
width: min(16rem, calc(100vw - 3rem));
|
||||
}
|
||||
/* When the sidebar-open class is added, slide the sidebar in */
|
||||
#sidebar.sidebar-open {
|
||||
transform: translateX(0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
/* Optionally shift main content when sidebar is open */
|
||||
#app.sidebar-open {
|
||||
transform: translateX(16rem);
|
||||
transition: transform 0.3s ease;
|
||||
body.sidebar-open {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1447,6 +1473,14 @@ footer a:hover {
|
||||
#sidebar {
|
||||
transform: translateX(0) !important;
|
||||
}
|
||||
|
||||
#sidebar.sidebar-open {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#sidebarOverlay {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Collapsed/expanded classes if needed on desktop */
|
||||
|
||||
+10
-4
@@ -51,19 +51,21 @@
|
||||
<link href="css/markdown.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body class="bg-gray-100">
|
||||
<!--
|
||||
<!--
|
||||
SIDEBAR CONTAINER:
|
||||
The <aside id="sidebar"> inside handles visibility.
|
||||
The <aside id="sidebar"> inside handles visibility.
|
||||
By default, no special position or z-index on #sidebarContainer.
|
||||
-->
|
||||
<div id="sidebarContainer">
|
||||
<!-- components/sidebar.html gets injected here -->
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div id="sidebarOverlay" class="md:hidden" aria-hidden="true"></div>
|
||||
|
||||
<!--
|
||||
MAIN CONTENT:
|
||||
md:ml-64 ensures content is shifted on desktop so the pinned sidebar doesn't overlap.
|
||||
On mobile, we also toggle .sidebar-open to shift the entire content.
|
||||
On mobile we float the sidebar over the content and dim the background overlay.
|
||||
-->
|
||||
<!--
|
||||
`fade-in` is scrubbed by js/index.js after the first animation so that
|
||||
@@ -84,6 +86,10 @@
|
||||
<button
|
||||
id="mobileMenuBtn"
|
||||
class="md:hidden ml-2 mr-4 flex items-center justify-center w-10 h-10 rounded-full bg-transparent hover:bg-transparent focus:outline-none focus:ring-2 focus:ring-[#0f172a] z-[60] fade-in"
|
||||
type="button"
|
||||
aria-expanded="false"
|
||||
aria-controls="sidebar"
|
||||
aria-label="Toggle navigation menu"
|
||||
>
|
||||
<img
|
||||
src="assets/svg/mobile-sidebar-menu-icon.svg"
|
||||
|
||||
+56
-7
@@ -170,12 +170,61 @@ async function bootstrapInterface() {
|
||||
|
||||
const mobileMenuBtn = document.getElementById("mobileMenuBtn");
|
||||
const sidebar = document.getElementById("sidebar");
|
||||
const appElement = document.getElementById("app");
|
||||
if (mobileMenuBtn && sidebar && appElement) {
|
||||
mobileMenuBtn.addEventListener("click", () => {
|
||||
sidebar.classList.toggle("sidebar-open");
|
||||
appElement.classList.toggle("sidebar-open");
|
||||
});
|
||||
const sidebarOverlay = document.getElementById("sidebarOverlay");
|
||||
|
||||
const setSidebarState = (isOpen) => {
|
||||
if (sidebar) {
|
||||
sidebar.classList.toggle("sidebar-open", isOpen);
|
||||
}
|
||||
document.body.classList.toggle("sidebar-open", isOpen);
|
||||
if (mobileMenuBtn) {
|
||||
mobileMenuBtn.setAttribute("aria-expanded", isOpen ? "true" : "false");
|
||||
}
|
||||
};
|
||||
|
||||
const closeSidebar = () => setSidebarState(false);
|
||||
const isMobileViewport = () => {
|
||||
if (typeof window.matchMedia === "function") {
|
||||
return window.matchMedia("(max-width: 767px)").matches;
|
||||
}
|
||||
return window.innerWidth < 768;
|
||||
};
|
||||
|
||||
const toggleSidebar = () => {
|
||||
if (!sidebar) return;
|
||||
const isMobile = isMobileViewport();
|
||||
if (!isMobile) return;
|
||||
const shouldOpen = !sidebar.classList.contains("sidebar-open");
|
||||
setSidebarState(shouldOpen);
|
||||
};
|
||||
|
||||
if (mobileMenuBtn && sidebar) {
|
||||
mobileMenuBtn.addEventListener("click", toggleSidebar);
|
||||
}
|
||||
|
||||
if (sidebarOverlay) {
|
||||
sidebarOverlay.addEventListener("click", closeSidebar);
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", (event) => {
|
||||
if (event.key === "Escape" && sidebar && sidebar.classList.contains("sidebar-open")) {
|
||||
closeSidebar();
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof window.matchMedia === "function") {
|
||||
const desktopQuery = window.matchMedia("(min-width: 768px)");
|
||||
const onDesktopChange = (event) => {
|
||||
if (event.matches) {
|
||||
closeSidebar();
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof desktopQuery.addEventListener === "function") {
|
||||
desktopQuery.addEventListener("change", onDesktopChange);
|
||||
} else if (typeof desktopQuery.addListener === "function") {
|
||||
desktopQuery.addListener(onDesktopChange);
|
||||
}
|
||||
}
|
||||
|
||||
const footerDropdownButton = document.getElementById("footerDropdownButton");
|
||||
@@ -195,7 +244,7 @@ async function bootstrapInterface() {
|
||||
try {
|
||||
const sidebarModule = await import("./sidebar.js");
|
||||
if (typeof sidebarModule.setupSidebarNavigation === "function") {
|
||||
sidebarModule.setupSidebarNavigation();
|
||||
sidebarModule.setupSidebarNavigation({ closeSidebar });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to set up sidebar navigation:", error);
|
||||
|
||||
+15
-7
@@ -1,7 +1,7 @@
|
||||
// sidebar.js
|
||||
import { setHashView } from "./index.js";
|
||||
|
||||
export function setupSidebarNavigation() {
|
||||
export function setupSidebarNavigation({ closeSidebar } = {}) {
|
||||
const sidebarLinks = document.querySelectorAll('#sidebar a[href^="#view="]');
|
||||
sidebarLinks.forEach((link) => {
|
||||
link.addEventListener("click", (e) => {
|
||||
@@ -16,13 +16,21 @@ export function setupSidebarNavigation() {
|
||||
setHashView(viewName); // This changes the hash and loads the view.
|
||||
|
||||
// --- NEW: if on mobile, close the sidebar automatically. ---
|
||||
if (window.innerWidth < 768) {
|
||||
const sidebar = document.getElementById("sidebar");
|
||||
const app = document.getElementById("app");
|
||||
if (sidebar && app) {
|
||||
sidebar.classList.remove("sidebar-open");
|
||||
app.classList.remove("sidebar-open");
|
||||
const isMobile =
|
||||
typeof window.matchMedia === "function"
|
||||
? window.matchMedia("(max-width: 767px)").matches
|
||||
: window.innerWidth < 768;
|
||||
if (isMobile) {
|
||||
if (typeof closeSidebar === "function") {
|
||||
closeSidebar();
|
||||
return;
|
||||
}
|
||||
|
||||
const sidebar = document.getElementById("sidebar");
|
||||
if (sidebar) {
|
||||
sidebar.classList.remove("sidebar-open");
|
||||
}
|
||||
document.body.classList.remove("sidebar-open");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user