live: group paging next to play/stop; add grid-split menu before fullscreen

The grid control footer is regrouped: ⏮ play/stop ⏭ now sit together in the
centre (left section empty). On the right, before fullscreen, a new ▦ split
button opens an upward popup menu with the layouts 4/9/16/25/36/49/64 that
calls setDim; the menu respects maxDim (16+ mode), highlights the active
layout, and closes on outside click.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-01 21:49:56 +05:00
parent c6cdcf55b2
commit 5b8f11dbb6
4 changed files with 52 additions and 8 deletions
+15 -4
View File
@@ -29,14 +29,25 @@
<main id="live-main">
<div class="grid g4" id="grid"></div>
<footer class="grid-bar" id="grid-bar">
<div class="gb-left">
<button class="gb-btn" id="gb-prev" title="Предыдущая страница" aria-label="Предыдущая страница"></button>
</div>
<div class="gb-left"></div>
<div class="gb-center">
<button class="gb-btn" id="gb-prev" title="Предыдущая страница" aria-label="Предыдущая страница"></button>
<button class="gb-btn gb-play" id="gb-play" title="Просмотр / стоп" aria-label="Просмотр / стоп"></button>
<button class="gb-btn" id="gb-next" title="Следующая страница" aria-label="Следующая страница"></button>
</div>
<div class="gb-right">
<button class="gb-btn" id="gb-next" title="Следующая страница" aria-label="Следующая страница"></button>
<div class="gb-split">
<button class="gb-btn" id="gb-split" title="Раскладка сетки" aria-label="Раскладка сетки"></button>
<div class="gb-split-menu" id="gb-split-menu">
<button data-grid="2">4</button>
<button data-grid="3">9</button>
<button data-grid="4">16</button>
<button data-grid="5">25</button>
<button data-grid="6">36</button>
<button data-grid="7">49</button>
<button data-grid="8">64</button>
</div>
</div>
<button class="gb-btn" id="gb-fs" title="Полный экран" aria-label="Полный экран"></button>
</div>
</footer>
+20 -3
View File
@@ -517,8 +517,8 @@ const Live = {
},
applyMaxDim() {
// скрыть пункты подменю с числом больше лимита (режим 16+)
document.querySelectorAll(".subnav .subitem[data-grid]").forEach((a) => {
// скрыть пункты с числом больше лимита (режим 16+) — и в сайдбаре, и в меню «Раскладка»
document.querySelectorAll(".subnav .subitem[data-grid], .gb-split-menu button[data-grid]").forEach((a) => {
a.hidden = +a.dataset.grid > this.maxDim;
});
},
@@ -537,7 +537,7 @@ const Live = {
localStorage.setItem("corevision.grid", String(this.dim));
this.page = 0; // смена размера сетки → первая страница
this.fillSelected(); // selected = камеры текущей страницы
document.querySelectorAll("#live-subnav .subitem, .subnav .subitem").forEach((a) =>
document.querySelectorAll("#live-subnav .subitem, .subnav .subitem, .gb-split-menu button").forEach((a) =>
a.classList.toggle("active", +a.dataset.grid === this.dim));
if (rerender) this.renderGrid();
this.updatePager();
@@ -605,6 +605,23 @@ const Live = {
if (gbPlay) gbPlay.onclick = (e) => { e.preventDefault(); this.setPlaying(!this.playing); };
const gbFs = document.getElementById("gb-fs");
if (gbFs) gbFs.onclick = (e) => { e.preventDefault(); fsMain(); };
// меню «Раскладка» (window split): 4/9/16/25/36/49/64 → setDim
const gbSplit = document.getElementById("gb-split");
const splitMenu = document.getElementById("gb-split-menu");
if (gbSplit && splitMenu) {
gbSplit.onclick = (e) => { e.preventDefault(); splitMenu.classList.toggle("open"); };
splitMenu.onclick = (e) => {
const b = e.target.closest("button[data-grid]");
if (!b) return;
e.preventDefault();
history.replaceState(null, "", "/?grid=" + b.dataset.grid);
this.setDim(+b.dataset.grid);
splitMenu.classList.remove("open");
};
document.addEventListener("click", (e) => { // клик вне меню — закрыть
if (!e.target.closest(".gb-split")) splitMenu.classList.remove("open");
});
}
this.updatePlayBtn();
this.updatePager();
},
+16
View File
@@ -190,6 +190,22 @@ footer.grid-bar {
.grid-bar .gb-btn:hover:not(:disabled) { border-color: #c0392b; color: #fff; }
.grid-bar .gb-btn:disabled { opacity: .3; cursor: default; }
.grid-bar .gb-play { min-width: 42px; font-size: 15px; }
/* кнопка «Раскладка» (window split) + всплывающее меню (раскрывается вверх) */
.grid-bar .gb-split { position: relative; display: inline-flex; }
.grid-bar .gb-split-menu {
display: none; position: absolute; bottom: calc(100% + 6px); right: 0; z-index: 50;
flex-direction: column; gap: 2px; padding: 4px; min-width: 60px;
background: var(--panel); border: 1px solid var(--border); border-radius: 8px;
box-shadow: 0 6px 20px rgba(0, 0, 0, .5);
}
.grid-bar .gb-split-menu.open { display: flex; }
.grid-bar .gb-split-menu button {
height: 24px; padding: 0 10px; border: 1px solid transparent; border-radius: 5px;
background: none; color: var(--text); cursor: pointer; font-size: 13px;
font-variant-numeric: tabular-nums; text-align: center;
}
.grid-bar .gb-split-menu button:hover { background: var(--panel-2); }
.grid-bar .gb-split-menu button.active { background: var(--accent); border-color: #c0392b; color: #fff; }
.tile {
position: relative; background: #05070a; border: 1px solid var(--border);
display: flex; align-items: center; justify-content: center; overflow: hidden;