feat(ui): add per-block wheel scaling

main
AlexsandrSnytkin 6 hours ago
parent c940399c8a
commit cd2fdf2ae1

@ -1287,6 +1287,8 @@ HTML = """<!doctype html>
.config-scale-control input { width: 74px; height: 16px; margin: 0; accent-color: var(--ui-accent); cursor: pointer; }
.config-scale-control input:disabled { opacity: .45; cursor: default; }
.config-scale-control output { min-width: 34px; color: var(--ui-text); font-variant-numeric: tabular-nums; text-align: right; }
.config-scale-mode { min-height: 26px !important; padding: 0 8px !important; white-space: nowrap; }
.config-scale-mode[aria-pressed="true"] { color: #fff !important; background: var(--ui-accent-bg) !important; border-color: var(--ui-accent) !important; box-shadow: 0 0 0 2px color-mix(in srgb, var(--ui-accent) 16%, transparent); }
.config-layout-state { min-width: 0; color: var(--ui-muted); font-size: 10px; }
.config-workspace { display: grid; grid-template-columns: minmax(0, 1fr); min-width: 0; min-height: 0; }
.config-workspace.has-structure { grid-template-columns: minmax(190px, 260px) minmax(0, 1fr); align-items: start; }
@ -1603,6 +1605,7 @@ HTML = """<!doctype html>
<button id="configResetLayout" type="button">сбросить раскладку</button>
<button id="configRestoreToggle" type="button" aria-expanded="false">структура</button>
<button id="configDeleteSelected" class="command danger" type="button" disabled>удалить выбранные</button>
<button id="configScaleMode" class="config-scale-mode" type="button" aria-pressed="false" disabled>масштаб колесом: выкл</button>
<label class="config-scale-control" title="Масштаб элементов и текста выбранного блока: 50200%"><span id="configScaleLabel">масштаб блока</span><input id="configContentScale" type="range" min="50" max="200" step="5" value="100" aria-label="Масштаб содержимого выбранного блока, от 50 до 200 процентов" disabled><output id="configContentScaleValue">100%</output></label>
<span id="configLayoutState" class="config-layout-state">Ctrl/Cmd + клик выбрать несколько · перетаскивайте группу</span>
</div>
@ -1829,6 +1832,7 @@ HTML = """<!doctype html>
<button id="networkResetLayout" type="button">сбросить раскладку</button>
<button id="networkStructureToggle" type="button" aria-expanded="false">структура</button>
<button id="networkDeleteSelected" class="command danger" type="button" disabled>удалить выбранные</button>
<button id="networkScaleMode" class="config-scale-mode" type="button" aria-pressed="false" disabled>масштаб колесом: выкл</button>
<label class="config-scale-control" title="Масштаб элементов и текста выбранного сетевого блока"><span>масштаб блока</span><input id="networkContentScale" type="range" min="50" max="200" step="5" value="100" aria-label="Масштаб содержимого выбранного сетевого блока" disabled><output id="networkContentScaleValue">100%</output></label>
<span id="networkLayoutState" class="config-layout-state">Ctrl/Cmd + клик выбрать несколько · перетаскивайте группу</span>
</div>
@ -2093,6 +2097,7 @@ HTML = """<!doctype html>
let deletedPanelOpen = false;
let configZIndex = 1;
let configContentScale = 1;
let configScaleWheelEnabled = false;
let configBlockObserver = null;
let selectedConfigBlock = null;
let selectedConfigBlocks = new Set();
@ -2138,6 +2143,14 @@ HTML = """<!doctype html>
updateConfigBlockScale(block);
saveConfigLayout();
}
function setConfigScaleMode(enabled) {
configScaleWheelEnabled = !!enabled && !!selectedConfigBlock;
const button = $('configScaleMode');
if (!button) return;
button.disabled = !selectedConfigBlock;
button.setAttribute('aria-pressed', String(configScaleWheelEnabled));
button.textContent = configScaleWheelEnabled ? 'масштаб колесом: вкл' : 'масштаб колесом: выкл';
}
function selectConfigBlock(block) {
selectedConfigBlocks = new Set(block && !configBlockState(block) ? [block] : []);
updateConfigSelection(block);
@ -2152,6 +2165,7 @@ HTML = """<!doctype html>
if (!selectedConfigBlock) {
if (control) control.disabled = true;
if (remove) remove.disabled = true;
setConfigScaleMode(false);
return;
}
const entry = configLayout[selectedConfigBlock.dataset.section] || {};
@ -2161,6 +2175,7 @@ HTML = """<!doctype html>
setText('configContentScaleValue', `${value}%`);
if (control) control.disabled = selectedConfigBlocks.size !== 1;
if (remove) remove.disabled = !selectedConfigBlocks.size;
setConfigScaleMode(configScaleWheelEnabled);
updateConfigBlockScale(selectedConfigBlock);
if (selectedConfigBlocks.size > 1) setText('configLayoutState', `выбрано ${selectedConfigBlocks.size} блока · перетаскивайте группу`);
}
@ -2637,6 +2652,14 @@ HTML = """<!doctype html>
block.style.cursor = edge ? `${edge}-resize` : '';
}
});
block.addEventListener('wheel', event => {
if (!configScaleWheelEnabled || configBlockState(block) || !event.deltaY) return;
event.preventDefault();
event.stopPropagation();
if (selectedConfigBlock !== block) selectConfigBlock(block);
const current = Number(configLayout[block.dataset.section]?.scale) || 100;
applyConfigContentScale(current + (event.deltaY < 0 ? 5 : -5));
}, {passive: false});
block.addEventListener('pointerleave', () => { if (!block.classList.contains('is-resizing')) block.style.cursor = ''; });
block.addEventListener('pointerdown', event => {
if (event.defaultPrevented || block.classList.contains('is-resizing')) return;
@ -2756,6 +2779,7 @@ HTML = """<!doctype html>
selectConfigBlock(configBlocks.find(block => !configBlockState(block)) || null);
$('configResetLayout')?.addEventListener('click', resetConfigLayout);
$('configDeleteSelected')?.addEventListener('click', deleteSelectedConfigBlocks);
$('configScaleMode')?.addEventListener('click', () => setConfigScaleMode(!configScaleWheelEnabled));
$('configRestoreToggle')?.addEventListener('click', () => {
restorePanelOpen = !restorePanelOpen;
renderConfigRestorePanel();
@ -2811,6 +2835,7 @@ HTML = """<!doctype html>
let selectedNetworkBlocks = new Set();
let networkStructureOpen = false;
let networkContentScale = 1;
let networkScaleWheelEnabled = false;
function readNetworkLayout() {
try {
const saved = JSON.parse(localStorage.getItem('fpv-network-layout-v1') || '{}');
@ -2857,6 +2882,7 @@ HTML = """<!doctype html>
if (!networkSelectedBlock) {
if (control) control.disabled = true;
if (remove) remove.disabled = true;
setNetworkScaleMode(false);
return;
}
const value = Math.max(50, Math.min(200, Math.round(Number(networkLayout[networkSelectedBlock.dataset.section]?.scale) || 100)));
@ -2865,6 +2891,7 @@ HTML = """<!doctype html>
setText('networkContentScaleValue', `${value}%`);
if (control) control.disabled = selectedNetworkBlocks.size !== 1;
if (remove) remove.disabled = !selectedNetworkBlocks.size;
setNetworkScaleMode(networkScaleWheelEnabled);
updateNetworkBlockScale(networkSelectedBlock);
if (selectedNetworkBlocks.size > 1) setText('networkLayoutState', `выбрано ${selectedNetworkBlocks.size} блока · перетаскивайте группу`);
}
@ -2887,6 +2914,14 @@ HTML = """<!doctype html>
updateNetworkBlockScale(block);
saveNetworkLayout();
}
function setNetworkScaleMode(enabled) {
networkScaleWheelEnabled = !!enabled && !!networkSelectedBlock;
const button = $('networkScaleMode');
if (!button) return;
button.disabled = !networkSelectedBlock;
button.setAttribute('aria-pressed', String(networkScaleWheelEnabled));
button.textContent = networkScaleWheelEnabled ? 'масштаб колесом: вкл' : 'масштаб колесом: выкл';
}
function captureNetworkLayout(block) {
const key = block?.dataset.section;
if (!key) return;
@ -3261,6 +3296,14 @@ HTML = """<!doctype html>
block.style.cursor = edge ? `${edge}-resize` : '';
}
});
block.addEventListener('wheel', event => {
if (!networkScaleWheelEnabled || networkState(block) || !event.deltaY) return;
event.preventDefault();
event.stopPropagation();
if (networkSelectedBlock !== block) selectNetworkBlock(block);
const current = Number(networkLayout[block.dataset.section]?.scale) || 100;
applyNetworkContentScale(current + (event.deltaY < 0 ? 5 : -5));
}, {passive: false});
block.addEventListener('pointerdown', event => {
if (event.defaultPrevented || block.classList.contains('is-resizing')) return;
startNetworkDrag(block, event);
@ -3314,6 +3357,7 @@ HTML = """<!doctype html>
selectNetworkBlock(networkBlocks.find(block => !networkState(block)) || null);
$('networkResetLayout')?.addEventListener('click', resetNetworkLayout);
$('networkDeleteSelected')?.addEventListener('click', deleteSelectedNetworkBlocks);
$('networkScaleMode')?.addEventListener('click', () => setNetworkScaleMode(!networkScaleWheelEnabled));
$('networkStructureToggle')?.addEventListener('click', () => {
networkStructureOpen = !networkStructureOpen;
renderNetworkStructure();

Loading…
Cancel
Save