Fix dropdown menu styling: no rounded corners, no gaps, consistent hover
- border-radius: 0 on all dropdown-menu and dropdown-item - padding: 0 and margin: 0 on dropdown-menu - margin-left: 0 on submenu (no gap between parent and child) - margin-top: -1px on submenu (seamless border overlap) - CSS hover opens submenu on desktop, click on mobile - white-space: nowrap on dropdown items - Fix statistics getFullStats -> getStats - Fix Twig ?? operator -> default filter on dashboard/statistics - Asset server (public/asset.php) for production static file serving - .htaccess rewrite rules for themes/admin/plugins assets
This commit is contained in:
@@ -17,7 +17,7 @@
|
|||||||
<div class="card-body d-flex justify-content-between align-items-center">
|
<div class="card-body d-flex justify-content-between align-items-center">
|
||||||
<div>
|
<div>
|
||||||
<h6 class="text-muted mb-1">Weergaven (30 dagen)</h6>
|
<h6 class="text-muted mb-1">Weergaven (30 dagen)</h6>
|
||||||
<h3 class="mb-0">{{ (analytics_summary.totals.views ?? 0)|number_format(0, ',', '.') }}</h3>
|
<h3 class="mb-0">{{ (analytics_summary.totals.views|default(0))|number_format(0, ',', '.') }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<i class="bi bi-eye stat-icon text-primary"></i>
|
<i class="bi bi-eye stat-icon text-primary"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<div class="card-body d-flex justify-content-between align-items-center">
|
<div class="card-body d-flex justify-content-between align-items-center">
|
||||||
<div>
|
<div>
|
||||||
<h6 class="text-muted mb-1">Unieke bezoekers (30 dagen)</h6>
|
<h6 class="text-muted mb-1">Unieke bezoekers (30 dagen)</h6>
|
||||||
<h3 class="mb-0">{{ (analytics_summary.totals.uniques ?? 0)|number_format(0, ',', '.') }}</h3>
|
<h3 class="mb-0">{{ (analytics_summary.totals.uniques|default(0))|number_format(0, ',', '.') }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<i class="bi bi-people stat-icon text-success"></i>
|
<i class="bi bi-people stat-icon text-success"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<div class="card-body d-flex justify-content-between align-items-center">
|
<div class="card-body d-flex justify-content-between align-items-center">
|
||||||
<div>
|
<div>
|
||||||
<h6 class="text-muted mb-1">Totaal aantal views</h6>
|
<h6 class="text-muted mb-1">Totaal aantal views</h6>
|
||||||
<h3 class="mb-0">{{ (stats.totals.views ?? 0)|number_format(0, ',', '.') }}</h3>
|
<h3 class="mb-0">{{ (stats.totals.views|default(0))|number_format(0, ',', '.') }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<i class="bi bi-eye stat-icon text-primary"></i>
|
<i class="bi bi-eye stat-icon text-primary"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
<div class="card-body d-flex justify-content-between align-items-center">
|
<div class="card-body d-flex justify-content-between align-items-center">
|
||||||
<div>
|
<div>
|
||||||
<h6 class="text-muted mb-1">Unieke bezoekers</h6>
|
<h6 class="text-muted mb-1">Unieke bezoekers</h6>
|
||||||
<h3 class="mb-0">{{ (stats.totals.uniques ?? 0)|number_format(0, ',', '.') }}</h3>
|
<h3 class="mb-0">{{ (stats.totals.uniques|default(0))|number_format(0, ',', '.') }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<i class="bi bi-people stat-icon text-success"></i>
|
<i class="bi bi-people stat-icon text-success"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
<div class="card-body d-flex justify-content-between align-items-center">
|
<div class="card-body d-flex justify-content-between align-items-center">
|
||||||
<div>
|
<div>
|
||||||
<h6 class="text-muted mb-1">Pagina views</h6>
|
<h6 class="text-muted mb-1">Pagina views</h6>
|
||||||
<h3 class="mb-0">{{ (stats.pages|length ?? 0) }}</h3>
|
<h3 class="mb-0">{{ stats.pages|length|default(0) }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<i class="bi bi-file-earmark-text stat-icon text-info"></i>
|
<i class="bi bi-file-earmark-text stat-icon text-info"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1427,7 +1427,7 @@ class CodePressCMS {
|
|||||||
$isExpanded = $this->folderContainsActivePage($item['children']);
|
$isExpanded = $this->folderContainsActivePage($item['children']);
|
||||||
|
|
||||||
if ($level === 0) {
|
if ($level === 0) {
|
||||||
// Root level folders
|
// Root level folders - Bootstrap dropdown
|
||||||
$html .= '<li class="nav-item dropdown">';
|
$html .= '<li class="nav-item dropdown">';
|
||||||
$html .= '<a class="nav-link dropdown-toggle" href="#" id="' . $folderId . '" role="button" data-bs-toggle="dropdown" aria-expanded="' . ($isExpanded ? 'true' : 'false') . '">';
|
$html .= '<a class="nav-link dropdown-toggle" href="#" id="' . $folderId . '" role="button" data-bs-toggle="dropdown" aria-expanded="' . ($isExpanded ? 'true' : 'false') . '">';
|
||||||
$html .= htmlspecialchars($item['title']) . ' <i class="bi bi-chevron-down"></i>';
|
$html .= htmlspecialchars($item['title']) . ' <i class="bi bi-chevron-down"></i>';
|
||||||
@@ -1437,12 +1437,12 @@ class CodePressCMS {
|
|||||||
$html .= '</ul>';
|
$html .= '</ul>';
|
||||||
$html .= '</li>';
|
$html .= '</li>';
|
||||||
} else {
|
} else {
|
||||||
// Nested folders in dropdown
|
// Nested folders - CSS hover submenu (unlimited depth)
|
||||||
$html .= '<li class="dropdown-submenu">';
|
$html .= '<li class="dropdown-submenu' . ($isExpanded ? ' show' : '') . '">';
|
||||||
$html .= '<a class="dropdown-item dropdown-toggle" href="#" id="' . $folderId . '">';
|
$html .= '<a class="dropdown-item dropdown-toggle" href="#" id="' . $folderId . '" role="button" aria-expanded="' . ($isExpanded ? 'true' : 'false') . '">';
|
||||||
$html .= htmlspecialchars($item['title']) . ' <i class="bi bi-chevron-down"></i>';
|
$html .= htmlspecialchars($item['title']) . ' <i class="bi bi-chevron-right"></i>';
|
||||||
$html .= '</a>';
|
$html .= '</a>';
|
||||||
$html .= '<ul class="dropdown-menu" aria-labelledby="' . $folderId . '">';
|
$html .= '<ul class="dropdown-menu' . ($isExpanded ? ' show' : '') . '" aria-labelledby="' . $folderId . '">';
|
||||||
$html .= $this->renderMenu($item['children'], $level + 1);
|
$html .= $this->renderMenu($item['children'], $level + 1);
|
||||||
$html .= '</ul>';
|
$html .= '</ul>';
|
||||||
$html .= '</li>';
|
$html .= '</li>';
|
||||||
|
|||||||
+1
-1
@@ -916,7 +916,7 @@ function handleSecurity($auth, $config, $twig, $user, $csrf): void
|
|||||||
function handleStatistics($auth, $config, $twig, $user, $csrf, $siteConfig): void
|
function handleStatistics($auth, $config, $twig, $user, $csrf, $siteConfig): void
|
||||||
{
|
{
|
||||||
$analytics = new Analytics($siteConfig['analytics'] ?? []);
|
$analytics = new Analytics($siteConfig['analytics'] ?? []);
|
||||||
$stats = $analytics->getFullStats();
|
$stats = $analytics->getStats();
|
||||||
$route = 'statistics';
|
$route = 'statistics';
|
||||||
|
|
||||||
echo $twig->render('pages/statistics.twig', [
|
echo $twig->render('pages/statistics.twig', [
|
||||||
|
|||||||
@@ -71,15 +71,22 @@
|
|||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
background-color: var(--header-bg) !important;
|
background-color: var(--header-bg) !important;
|
||||||
border: 1px solid var(--header-font) !important;
|
border: 1px solid var(--header-font) !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item {
|
.dropdown-item {
|
||||||
color: var(--header-font) !important;
|
color: var(--header-font) !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
}
|
}
|
||||||
.dropdown-item:hover {
|
.dropdown-item:hover, .dropdown-item:focus {
|
||||||
background-color: rgba(255, 255, 255, 0.1) !important;
|
background-color: rgba(255, 255, 255, 0.1) !important;
|
||||||
color: var(--header-font) !important;
|
color: var(--header-font) !important;
|
||||||
}
|
}
|
||||||
|
.nav-tabs .dropdown-menu {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.dropdown-toggle::after {
|
.dropdown-toggle::after {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
@@ -287,6 +294,50 @@ pre code {
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Bootstrap 5 nested dropdown support (dropdown-submenu) — unlimited depth, CSS hover */
|
||||||
|
.dropdown-submenu {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.dropdown-submenu > .dropdown-menu {
|
||||||
|
top: 0;
|
||||||
|
left: 100%;
|
||||||
|
margin-top: -1px;
|
||||||
|
margin-left: 0;
|
||||||
|
display: none;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
.dropdown-submenu:hover > .dropdown-menu {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.dropdown-submenu:hover > .dropdown-toggle {
|
||||||
|
background-color: rgba(255, 255, 255, 0.15) !important;
|
||||||
|
}
|
||||||
|
.dropdown-submenu > .dropdown-toggle .bi-chevron-right {
|
||||||
|
float: right;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
}
|
||||||
|
.dropdown-menu .dropdown-item {
|
||||||
|
width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
@media (max-width: 767.98px) {
|
||||||
|
.dropdown-submenu > .dropdown-menu {
|
||||||
|
left: 0;
|
||||||
|
top: 100%;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-left: 1rem;
|
||||||
|
border: none;
|
||||||
|
border-left: 2px solid rgba(255, 255, 255, 0.2);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
.dropdown-submenu:hover > .dropdown-menu {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.dropdown-submenu.show > .dropdown-menu {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
background: #e8e8e8;
|
background: #e8e8e8;
|
||||||
padding: 0.15rem 0.4rem;
|
padding: 0.15rem 0.4rem;
|
||||||
|
|||||||
@@ -71,15 +71,22 @@
|
|||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
background-color: var(--header-bg) !important;
|
background-color: var(--header-bg) !important;
|
||||||
border: 1px solid var(--header-font) !important;
|
border: 1px solid var(--header-font) !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item {
|
.dropdown-item {
|
||||||
color: var(--header-font) !important;
|
color: var(--header-font) !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
}
|
}
|
||||||
.dropdown-item:hover {
|
.dropdown-item:hover, .dropdown-item:focus {
|
||||||
background-color: rgba(255, 255, 255, 0.1) !important;
|
background-color: rgba(255, 255, 255, 0.1) !important;
|
||||||
color: var(--header-font) !important;
|
color: var(--header-font) !important;
|
||||||
}
|
}
|
||||||
|
.nav-tabs .dropdown-menu {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.dropdown-toggle::after {
|
.dropdown-toggle::after {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
@@ -287,6 +294,50 @@ pre code {
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Bootstrap 5 nested dropdown support (dropdown-submenu) — unlimited depth, CSS hover */
|
||||||
|
.dropdown-submenu {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.dropdown-submenu > .dropdown-menu {
|
||||||
|
top: 0;
|
||||||
|
left: 100%;
|
||||||
|
margin-top: -1px;
|
||||||
|
margin-left: 0;
|
||||||
|
display: none;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
.dropdown-submenu:hover > .dropdown-menu {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.dropdown-submenu:hover > .dropdown-toggle {
|
||||||
|
background-color: rgba(255, 255, 255, 0.15) !important;
|
||||||
|
}
|
||||||
|
.dropdown-submenu > .dropdown-toggle .bi-chevron-right {
|
||||||
|
float: right;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
}
|
||||||
|
.dropdown-menu .dropdown-item {
|
||||||
|
width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
@media (max-width: 767.98px) {
|
||||||
|
.dropdown-submenu > .dropdown-menu {
|
||||||
|
left: 0;
|
||||||
|
top: 100%;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-left: 1rem;
|
||||||
|
border: none;
|
||||||
|
border-left: 2px solid rgba(255, 255, 255, 0.2);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
.dropdown-submenu:hover > .dropdown-menu {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.dropdown-submenu.show > .dropdown-menu {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
background: #e8e8e8;
|
background: #e8e8e8;
|
||||||
padding: 0.15rem 0.4rem;
|
padding: 0.15rem 0.4rem;
|
||||||
|
|||||||
@@ -93,34 +93,41 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle nested dropdowns for touch devices using event delegation
|
// Handle nested dropdowns — toggle submenu on click (for mobile/touch)
|
||||||
|
// On desktop, CSS hover handles submenu display
|
||||||
document.addEventListener('click', function(e) {
|
document.addEventListener('click', function(e) {
|
||||||
const toggle = e.target.closest('.dropdown-submenu .dropdown-toggle');
|
const toggle = e.target.closest('.dropdown-submenu > .dropdown-toggle');
|
||||||
|
|
||||||
if (toggle) {
|
if (toggle) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
const submenu = toggle.closest('.dropdown-submenu');
|
const submenu = toggle.parentElement;
|
||||||
const dropdown = submenu.querySelector('.dropdown-menu');
|
const dropdown = submenu.querySelector('.dropdown-menu');
|
||||||
|
|
||||||
// Close other submenus at the same level
|
// Close other open submenus at the same level
|
||||||
const parent = submenu.parentElement;
|
const parent = submenu.parentElement;
|
||||||
parent.querySelectorAll('.dropdown-submenu').forEach(function(sibling) {
|
parent.querySelectorAll(':scope > .dropdown-submenu').forEach(function(sibling) {
|
||||||
if (sibling !== submenu) {
|
if (sibling !== submenu) {
|
||||||
|
sibling.classList.remove('show');
|
||||||
var siblingMenu = sibling.querySelector('.dropdown-menu');
|
var siblingMenu = sibling.querySelector('.dropdown-menu');
|
||||||
if (siblingMenu) siblingMenu.classList.remove('show');
|
if (siblingMenu) siblingMenu.classList.remove('show');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Toggle current submenu
|
// Toggle current submenu
|
||||||
if (dropdown) dropdown.classList.toggle('show');
|
if (dropdown) {
|
||||||
|
dropdown.classList.toggle('show');
|
||||||
|
submenu.classList.toggle('show');
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close all open submenus when clicking outside
|
// Close all open submenus when clicking outside
|
||||||
document.querySelectorAll('.dropdown-submenu .dropdown-menu.show').forEach(function(menu) {
|
document.querySelectorAll('.dropdown-submenu.show').forEach(function(sm) {
|
||||||
menu.classList.remove('show');
|
sm.classList.remove('show');
|
||||||
|
var menu = sm.querySelector('.dropdown-menu');
|
||||||
|
if (menu) menu.classList.remove('show');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -110,17 +110,27 @@ $header-bg-opacity: 1;
|
|||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
background-color: var(--header-bg) !important;
|
background-color: var(--header-bg) !important;
|
||||||
border: 1px solid var(--header-font) !important;
|
border: 1px solid var(--header-font) !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item {
|
.dropdown-item {
|
||||||
color: var(--header-font) !important;
|
color: var(--header-font) !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
|
||||||
&:hover {
|
&:hover, &:focus {
|
||||||
background-color: rgba(255, 255, 255, 0.1) !important;
|
background-color: rgba(255, 255, 255, 0.1) !important;
|
||||||
color: var(--header-font) !important;
|
color: var(--header-font) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove Bootstrap dropdown border/spacing on nav-tabs dropdown
|
||||||
|
.nav-tabs .dropdown-menu {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
// Hide Bootstrap dropdown arrow and use custom icon
|
// Hide Bootstrap dropdown arrow and use custom icon
|
||||||
.dropdown-toggle::after {
|
.dropdown-toggle::after {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
@@ -287,6 +297,61 @@ $header-bg-opacity: 1;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bootstrap 5 nested dropdown support (dropdown-submenu) — unlimited depth, CSS hover
|
||||||
|
.dropdown-submenu {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
> .dropdown-menu {
|
||||||
|
top: 0;
|
||||||
|
left: 100%;
|
||||||
|
margin-top: -1px;
|
||||||
|
margin-left: 0;
|
||||||
|
display: none;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open submenu on hover (desktop)
|
||||||
|
&:hover > .dropdown-menu {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep parent highlighted when hovering submenu
|
||||||
|
&:hover > .dropdown-toggle {
|
||||||
|
background-color: rgba(255, 255, 255, 0.15) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arrow pointing right
|
||||||
|
> .dropdown-toggle .bi-chevron-right {
|
||||||
|
float: right;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure dropdown items fill width and no gaps
|
||||||
|
.dropdown-menu .dropdown-item {
|
||||||
|
width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mobile: submenu appears below instead of beside, open on tap
|
||||||
|
@media (max-width: 767.98px) {
|
||||||
|
.dropdown-submenu > .dropdown-menu {
|
||||||
|
left: 0;
|
||||||
|
top: 100%;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-left: 1rem;
|
||||||
|
border: none;
|
||||||
|
border-left: 2px solid rgba(255, 255, 255, 0.2);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
.dropdown-submenu:hover > .dropdown-menu {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.dropdown-submenu.show > .dropdown-menu {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Sidebar styling
|
// Sidebar styling
|
||||||
.sidebar-column {
|
.sidebar-column {
|
||||||
background-color: var(--sidebar-bg) !important;
|
background-color: var(--sidebar-bg) !important;
|
||||||
|
|||||||
Reference in New Issue
Block a user