CodePress/templates/layout.html
Edwin Noorlander c68e8cd141 Add sidebar toggle functionality
- Added toggle button for left sidebar collapse/expand
- Sidebar slides in/out with smooth animation
- Toggle button changes icon (list/chevron-right)
- Main content adjusts margin when sidebar is collapsed
- Added CSS transitions for smooth animations
- Button positioned fixed with z-index for accessibility
2025-11-19 14:35:57 +01:00

381 lines
13 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{page_title}} - {{site_title}}</title>
<link rel="icon" type="image/svg+xml" href="assets/favicon.svg">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
height: 100%;
}
body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.main-wrapper {
flex: 1;
display: flex;
flex-direction: column;
}
.content-wrapper {
flex: 1;
display: flex;
overflow: hidden;
}
.sidebar {
width: 250px;
background-color: #f8f9fa;
border-right: 1px solid #dee2e6;
overflow-y: auto;
flex-shrink: 0;
transition: transform 0.3s ease;
position: relative;
z-index: 1000;
}
.sidebar.collapsed {
transform: translateX(-250px);
}
.sidebar-toggle {
position: fixed;
top: 80px;
left: 10px;
z-index: 1001;
background-color: #0d6efd;
color: white;
border: none;
border-radius: 5px;
padding: 8px 12px;
cursor: pointer;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
transition: all 0.3s ease;
}
.sidebar-toggle:hover {
background-color: #0a58ca;
transform: scale(1.05);
}
.sidebar-toggle.shifted {
left: 270px;
}
.main-content.shifted {
margin-left: 0;
}
.main-content {
flex: 1;
overflow-y: auto;
padding: 20px;
transition: margin-left 0.3s ease;
}
.folder-toggle {
font-weight: bold;
color: #212529 !important;
cursor: pointer;
display: flex;
align-items: center;
padding: 0.5rem 0.75rem;
background-color: #f8f9fa !important;
margin: 0.125rem 0;
transition: background-color 0.2s ease;
}
.folder-toggle:hover {
background-color: #e9ecef !important;
}
.folder-toggle[aria-expanded="true"] {
background-color: #dee2e6 !important;
color: #212529 !important;
font-weight: 600;
}
/* Progressive background colors for nested folders */
.nav .nav .folder-toggle {
background-color: #f1f3f4 !important;
}
.nav .nav .nav .folder-toggle {
background-color: #eaedee !important;
}
.nav .nav .nav .nav .folder-toggle {
background-color: #e3e7e8 !important;
}
.nav .nav .nav .nav .nav .folder-toggle {
background-color: #dce1e2 !important;
}
.nav .nav .nav .nav .nav .nav .folder-toggle {
background-color: #d5dbdd !important;
}
.nav .nav .nav .nav .nav .nav .nav .folder-toggle {
background-color: #ced5d8 !important;
}
.nav .nav .nav .nav .nav .nav .nav .nav .folder-toggle {
background-color: #c7cfd3 !important;
}
.nav .nav .nav .nav .nav .nav .nav .nav .nav .folder-toggle {
background-color: #c0c9ce !important;
}
.nav .nav .nav .nav .nav .nav .nav .nav .nav .nav .folder-toggle {
background-color: #b9c3c9 !important;
}
.folder-toggle .arrow {
margin-right: 8px;
transition: transform 0.2s;
font-size: 0.8em;
}
.folder-toggle[aria-expanded="true"] .arrow {
transform: rotate(90deg);
}
.page-link {
color: #495057 !important;
font-weight: 500;
padding: 0.5rem 0.75rem;
padding-left: 2rem;
background-color: #ffffff !important;
margin: 0.125rem 0;
transition: background-color 0.2s ease;
}
/* Progressive background colors for nested pages */
.nav .nav .page-link {
background-color: #fafbfc !important;
}
.nav .nav .nav .page-link {
background-color: #f5f7f8 !important;
}
.nav .nav .nav .nav .page-link {
background-color: #f0f3f4 !important;
}
.nav .nav .nav .nav .nav .page-link {
background-color: #ebefef !important;
}
.nav .nav .nav .nav .nav .nav .page-link {
background-color: #e6eaea !important;
}
.nav .nav .nav .nav .nav .nav .nav .page-link {
background-color: #e1e5e5 !important;
}
.nav .nav .nav .nav .nav .nav .nav .nav .page-link {
background-color: #dce0e0 !important;
}
.nav .nav .nav .nav .nav .nav .nav .nav .nav .page-link {
background-color: #d7dbdb !important;
}
.nav .nav .nav .nav .nav .nav .nav .nav .nav .nav .page-link {
background-color: #d2d6d6 !important;
}
.page-link:hover {
color: #212529 !important;
background-color: #f8f9fa !important;
}
.nav-link.active {
background-color: #0d6efd !important;
color: #212529 !important;
font-weight: 600;
}
.file-info {
font-size: 0.9rem;
color: #6c757d;
}
.site-info a {
color: #0d6efd;
text-decoration: none;
}
.site-info a:hover {
text-decoration: underline;
}
.auto-link {
color: #0d6efd;
text-decoration: none;
border-bottom: 2px dashed #0d6efd;
font-weight: 500;
transition: all 0.2s ease;
}
.auto-link:hover {
color: #0a58ca;
text-decoration: none;
border-bottom-style: solid;
border-bottom-color: #0a58ca;
}
.search-form {
max-width: 300px;
}
.card-title a {
text-decoration: none;
color: inherit;
}
.card-title a:hover {
text-decoration: underline;
}
.folder-disabled {
font-weight: 500;
color: #6c757d !important;
cursor: not-allowed;
display: flex;
align-items: center;
padding: 0.5rem 0.75rem;
background-color: #f8f9fa !important;
margin: 0.125rem 0;
opacity: 0.7;
}
.folder-disabled .arrow {
margin-right: 8px;
font-size: 0.8em;
opacity: 0.6;
}
@media (max-width: 768px) {
.sidebar {
width: 200px;
}
}
</style>
</head>
<body>
<header class="bg-primary text-white py-3">
<div class="container-fluid">
<div class="row align-items-center">
<div class="col">
<div class="d-flex align-items-center">
<img src="assets/icon.svg" alt="CodePress Logo" width="32" height="32" class="me-2">
<h1 class="h3 mb-0">{{site_title}}</h1>
</div>
</div>
<div class="col-auto">
<form class="d-flex" method="GET" action="">
<input class="form-control me-2" type="search" name="search" placeholder="Search..." value="{{search_query}}">
<button class="btn btn-outline-light" type="submit">Search</button>
</form>
</div>
</div>
</div>
</header>
<div class="main-wrapper">
<div class="content-wrapper">
<button class="sidebar-toggle" id="sidebarToggle">
<i class="bi bi-list"></i>
</button>
<nav class="sidebar" id="sidebar">
<div class="pt-3">
<ul class="nav flex-column">
{{menu}}
</ul>
</div>
</nav>
<main class="main-content">
<div>
{{breadcrumb}}
</div>
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h2>{{page_title}}</h2>
</div>
<div class="content">
{{content}}
</div>
</main>
</div>
</div>
<footer class="bg-light border-top py-3">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<div class="d-flex justify-content-between align-items-center">
<div class="file-info">
{{file_info}}
</div>
<div class="site-info">
<small class="text-muted">Powered by <a href="https://git.noorlander.info/E.Noorlander/CodePress.git" target="_blank" rel="noopener">CodePress CMS</a></small>
</div>
</div>
</div>
</div>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Sidebar toggle functionality
const sidebarToggle = document.getElementById('sidebarToggle');
const sidebar = document.getElementById('sidebar');
const mainContent = document.querySelector('.main-content');
sidebarToggle.addEventListener('click', function() {
sidebar.classList.toggle('collapsed');
mainContent.classList.toggle('shifted');
sidebarToggle.classList.toggle('shifted');
// Change icon
const icon = this.querySelector('i');
if (sidebar.classList.contains('collapsed')) {
icon.classList.remove('bi-list');
icon.classList.add('bi-chevron-right');
} else {
icon.classList.remove('bi-chevron-right');
icon.classList.add('bi-list');
}
});
// Open folders that contain the current active page
const activeLink = document.querySelector('.nav-link.active');
if (activeLink) {
let parent = activeLink.closest('.collapse');
while (parent) {
const toggle = document.querySelector('[data-bs-target="#' + parent.id + '"]');
if (toggle) {
const collapse = new bootstrap.Collapse(parent, {
show: true
});
toggle.setAttribute('aria-expanded', 'true');
}
parent = parent.parentElement.closest('.collapse');
}
}
// Close other folders when opening a new one
const folderToggles = document.querySelectorAll('.folder-toggle');
folderToggles.forEach(toggle => {
toggle.addEventListener('click', function(e) {
const targetId = this.getAttribute('data-bs-target');
const isExpanded = this.getAttribute('aria-expanded') === 'true';
if (!isExpanded) {
// Close all other folders
folderToggles.forEach(otherToggle => {
if (otherToggle !== this) {
const otherTargetId = otherToggle.getAttribute('data-bs-target');
if (otherTargetId) {
const otherCollapse = document.querySelector(otherTargetId);
if (otherCollapse) {
const bsCollapse = bootstrap.Collapse.getInstance(otherCollapse);
if (bsCollapse) {
bsCollapse.hide();
} else {
new bootstrap.Collapse(otherCollapse, {
hide: true
});
}
otherToggle.setAttribute('aria-expanded', 'false');
}
}
}
});
}
});
});
});
</script>
</body>
</html>