fix: icon-rail sidebar now shows per-item label tooltips instead of full flyout

Hovering the collapsed 56 px rail no longer expands the whole sidebar,
which was pushing the main content left and right. Each nav item now
shows a small floating tooltip (JS-positioned fixed div) next to its
icon on hover — layout stays completely stable.

Bumps to v1.12.1.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mARTin-B78 2026-06-27 16:00:56 +02:00
parent a4128788d6
commit 41f747a7b0
4 changed files with 72 additions and 30 deletions

View File

@ -9,6 +9,13 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
---
## [1.12.1] — 2026-06-27
### Changed
- **Sidebar icon-rail**: hover no longer flies out the whole sidebar (which caused the main content to jump left/right). Individual item labels now appear as a small floating tooltip next to the hovered icon, keeping the rail at a fixed 56 px and the layout completely stable.
---
## [1.12.0] — 2026-06-27
### Added

View File

@ -26,7 +26,7 @@
<!-- ── Core styles (local — no CDN dependency for first paint) ────────── -->
<link rel="stylesheet" href="/static/vendor/mdi/materialdesignicons.min.css">
<link rel="stylesheet" href="/static/style.css?v=1.12.0">
<link rel="stylesheet" href="/static/style.css?v=1.12.1">
<!-- ── Flag icons — non-blocking (loaded async, icons appear after JS) ── -->
@ -336,7 +336,7 @@
<script src="/static/vendor/wavesurfer-regions.min.js"></script>
<!-- loader.js: fetches sections → loads JS modules → removes skeleton -->
<script src="/static/loader.js?v=1.12.0"></script>
<script src="/static/loader.js?v=1.12.1"></script>
</body>
</html>

View File

@ -377,8 +377,40 @@
try { localStorage.setItem('ttsvc_sidebar_collapsed', collapsed ? '1' : '0'); } catch (_) {}
var btn = document.getElementById('sidebar-collapse-btn');
if (btn) btn.title = collapsed ? 'Expand sidebar' : 'Collapse sidebar';
if (!collapsed) _railTip.style.display = 'none';
};
// ── Icon-rail per-item label tooltip ────────────────────────────────────
var _railTip = document.createElement('div');
_railTip.id = 'nav-rail-tooltip';
document.body.appendChild(_railTip);
var _railTipActive = null;
var _sidebarEl = document.getElementById('sidebar');
_sidebarEl.addEventListener('mouseover', function (e) {
if (!document.body.classList.contains('sidebar-collapsed')) return;
var item = e.target.closest('.nav-item, .nav-tree-item, .nav-tree-head');
if (!item || item === _railTipActive) return;
var label = item.querySelector('.nav-label');
if (!label) return;
var text = label.textContent.trim();
if (!text) return;
var r = item.getBoundingClientRect();
_railTip.textContent = text;
_railTip.style.left = (r.right + 10) + 'px';
_railTip.style.top = (r.top + r.height / 2) + 'px';
_railTip.style.display = 'block';
_railTipActive = item;
});
_sidebarEl.addEventListener('mouseout', function (e) {
if (!e.relatedTarget || !e.relatedTarget.closest('#sidebar')) {
_railTip.style.display = 'none';
_railTipActive = null;
}
});
// Close drawer when a nav item is tapped on mobile
document.querySelectorAll('#sidebar .nav-item, #sidebar .nav-tree-item').forEach(function (el) {
el.addEventListener('click', function () {

View File

@ -114,38 +114,41 @@ body {
#sidebar { transition: width .18s ease; }
.sidebar-expand-btn { display: none; }
/* Collapsed = slim icon rail; hovering it flies the full menu out as an
overlay (icons always visible, titles + nested items appear on hover) */
/* Collapsed = slim 56 px icon rail; individual item labels appear as
floating tooltips via JS (#nav-rail-tooltip) layout never shifts */
@media (min-width: 768px) {
body.sidebar-collapsed #sidebar { width: 56px; overflow-x: hidden; }
body.sidebar-collapsed #sidebar:hover {
width: var(--sidebar-width); overflow-x: hidden;
position: absolute; top: 0; bottom: 0; left: 0; z-index: 60;
box-shadow: 6px 0 24px rgba(0,0,0,.22);
}
/* Rail (not hovered): show only the leading icons, centered */
body.sidebar-collapsed #sidebar:not(:hover) .nav-label,
body.sidebar-collapsed #sidebar:not(:hover) .nav-badge,
body.sidebar-collapsed #sidebar:not(:hover) .ntc,
body.sidebar-collapsed #sidebar:not(:hover) .nav-chevron,
body.sidebar-collapsed #sidebar:not(:hover) .nav-group-label,
body.sidebar-collapsed #sidebar:not(:hover) .nav-group-head,
body.sidebar-collapsed #sidebar:not(:hover) .sidebar-brand-text { display: none; }
body.sidebar-collapsed #sidebar:not(:hover) .sidebar-brand { justify-content: center; padding-left: 0; padding-right: 0; }
body.sidebar-collapsed #sidebar:not(:hover) .nav-item,
body.sidebar-collapsed #sidebar:not(:hover) .nav-tree-item,
body.sidebar-collapsed #sidebar:not(:hover) .nav-tree-head {
/* Rail: hide all text/badges/chevrons, keep leading icons only */
body.sidebar-collapsed #sidebar .nav-label,
body.sidebar-collapsed #sidebar .nav-badge,
body.sidebar-collapsed #sidebar .ntc,
body.sidebar-collapsed #sidebar .nav-chevron,
body.sidebar-collapsed #sidebar .nav-group-label,
body.sidebar-collapsed #sidebar .nav-group-head,
body.sidebar-collapsed #sidebar .sidebar-brand-text { display: none; }
body.sidebar-collapsed #sidebar .sidebar-brand { justify-content: center; padding-left: 0; padding-right: 0; }
body.sidebar-collapsed #sidebar .nav-item,
body.sidebar-collapsed #sidebar .nav-tree-item,
body.sidebar-collapsed #sidebar .nav-tree-head {
padding-left: 18px; padding-right: 0; gap: 0; white-space: nowrap;
font-size: 0; /* hide raw-text labels + counts, keep the icon */
font-size: 0;
}
/* restore the leading icon size (its text was zeroed above) */
body.sidebar-collapsed #sidebar:not(:hover) .nav-item .mdi,
body.sidebar-collapsed #sidebar:not(:hover) .nav-tree-item .mdi,
body.sidebar-collapsed #sidebar:not(:hover) .nav-tree-head .mdi { font-size: 15px; }
/* nested tree-items: align their icon into the rail too */
body.sidebar-collapsed #sidebar:not(:hover) .nav-tree .nav-tree-item,
body.sidebar-collapsed #sidebar:not(:hover) .nav-tree .nav-tree-head.nav-subhead,
body.sidebar-collapsed #sidebar:not(:hover) .nav-tree .nav-tree .nav-tree-item { padding-left: 18px; }
body.sidebar-collapsed #sidebar .nav-item .mdi,
body.sidebar-collapsed #sidebar .nav-tree-item .mdi,
body.sidebar-collapsed #sidebar .nav-tree-head .mdi { font-size: 15px; }
body.sidebar-collapsed #sidebar .nav-tree .nav-tree-item,
body.sidebar-collapsed #sidebar .nav-tree .nav-tree-head.nav-subhead,
body.sidebar-collapsed #sidebar .nav-tree .nav-tree .nav-tree-item { padding-left: 18px; }
}
/* Floating label tooltip for icon-rail items */
#nav-rail-tooltip {
position: fixed; z-index: 200;
background: var(--surface); border: 1px solid var(--border);
border-radius: 6px; padding: 5px 11px;
font-size: 13px; font-weight: 500; color: var(--text);
white-space: nowrap; pointer-events: none; display: none;
box-shadow: 2px 4px 14px rgba(0,0,0,.18);
transform: translateY(-50%);
}
@media (max-width: 767px) {
.sidebar-collapse-btn, .sidebar-expand-btn { display: none !important; }