feat(#994): tabbar overflow menu — settings and logs in ⋮ by default
Add a ⋮ overflow button at the right end of the dashboard tab strip. SETTINGS and LOGS always live in the overflow dropdown (marked data-overflow="default" in index.html — never rendered in the main bar). Dynamic overflow: when the bar is too narrow to fit all remaining tabs, rightmost tabs spill into the dropdown right-to-left. Implemented via JS measurement + ResizeObserver; no CSS-only relayout trick needed. The ⋮ button: - hidden when the dropdown is empty (wide screens with only the default tabs overflowed, which are always there anyway → button always shown) - gets .tabbar-overflow-active when the current hash tab is inside - dropdown items clone the tab label + count badge from the original - closes on outside click / Escape MutationObserver on the tabbar catches P33RS/M4TR1X hidden-attribute changes so the overflow recalculates when those tabs are shown/hidden.
This commit is contained in:
parent
f56b5c5a7b
commit
e9d22ef2d3
3 changed files with 271 additions and 14 deletions
|
|
@ -45,6 +45,7 @@ body.dashboard-shell {
|
|||
|
||||
.tabbar {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
gap: 0.2em;
|
||||
padding: 0.5em 1em 0;
|
||||
|
|
@ -98,6 +99,104 @@ body.dashboard-shell {
|
|||
color: var(--red);
|
||||
}
|
||||
|
||||
/* Overflowed tabs are hidden from the bar (moved to the ⋮ dropdown). */
|
||||
.tabbar .tab[data-overflow="default"],
|
||||
.tabbar .tab.tab-overflowed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ── overflow ⋮ button + dropdown ────────────────────────── */
|
||||
.tabbar-overflow {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
margin-left: auto; /* push to the far right */
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tabbar-overflow-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: none;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px 4px 0 0;
|
||||
color: var(--muted);
|
||||
font-size: 1.2em;
|
||||
line-height: 1;
|
||||
padding: 0.3em 0.5em;
|
||||
cursor: pointer;
|
||||
transition: color 0.15s, background 0.15s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tabbar-overflow-btn:hover {
|
||||
color: var(--purple);
|
||||
background: rgba(203, 166, 247, 0.06);
|
||||
}
|
||||
.tabbar-overflow-btn.tabbar-overflow-active {
|
||||
color: var(--purple);
|
||||
border-color: var(--purple-dim);
|
||||
}
|
||||
|
||||
.tabbar-overflow-dropdown {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: calc(100% + 2px);
|
||||
z-index: 100;
|
||||
background: var(--surface0);
|
||||
border: 1px solid var(--surface2);
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0.3em 0;
|
||||
min-width: 12em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tabbar-overflow-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.6em;
|
||||
width: 100%;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text);
|
||||
font-family: inherit;
|
||||
font-size: 0.88em;
|
||||
letter-spacing: 0.05em;
|
||||
text-align: left;
|
||||
text-decoration: none;
|
||||
padding: 0.45em 1em;
|
||||
cursor: pointer;
|
||||
}
|
||||
.tabbar-overflow-item:hover {
|
||||
background: var(--surface1);
|
||||
color: var(--purple);
|
||||
}
|
||||
.tabbar-overflow-item-active {
|
||||
color: var(--purple);
|
||||
}
|
||||
|
||||
.tabbar-overflow-badge {
|
||||
display: inline-block;
|
||||
background: var(--purple-dim);
|
||||
color: var(--purple);
|
||||
border-radius: 999px;
|
||||
padding: 0 0.5em;
|
||||
min-width: 1.4em;
|
||||
text-align: center;
|
||||
font-size: 0.82em;
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-weight: bold;
|
||||
}
|
||||
.tabbar-overflow-badge.tab-count-attn {
|
||||
background: rgba(243, 139, 168, 0.22);
|
||||
color: var(--red);
|
||||
}
|
||||
|
||||
/* Tab pane visibility — show only the active one. The .tab-pane-active
|
||||
class is set by tabs.js based on the URL hash; default (no hash)
|
||||
resolves to SW4RM. */
|
||||
|
|
|
|||
Loading…
Reference in a new issue