Give the pages one switch and one prefix each
Three pages had reimplemented the same sliding toggle, and two both claimed vv-set-* with disjoint class names, which reads as shared and is not.
This commit is contained in:
@@ -95,13 +95,6 @@
|
||||
|
||||
/* ── Settings card ───────────────────────────────────────── */
|
||||
.vv-arr-set-card { grid-column:1/-1; }
|
||||
.vv-arr-tog-wrap { display:inline-flex;align-items:center;gap:8px;cursor:pointer;user-select:none; }
|
||||
.vv-arr-tog-track{ width:28px;height:16px;border-radius:8px;background:#1e1e1e;border:1px solid #2a2a2a;
|
||||
position:relative;transition:background .15s,border-color .15s;flex-shrink:0; }
|
||||
.vv-arr-tog-track.on { background:#1a3a1a;border-color:#2d5a2d; }
|
||||
.vv-arr-tog-track::after { content:'';position:absolute;top:2px;left:2px;width:10px;height:10px;
|
||||
border-radius:50%;background:#444;transition:left .15s,background .15s; }
|
||||
.vv-arr-tog-track.on::after { left:14px;background:#4caf50; }
|
||||
.vv-arr-tog-lbl { font-size:11px;color:#666; }
|
||||
.vv-arr-set-inp { background:#0d0d0d;border:1px solid #252525;border-radius:3px;color:#888;
|
||||
font-size:11px;padding:3px 7px;outline:none;width:60px; }
|
||||
@@ -348,8 +341,8 @@ function _settingsCard(s) {
|
||||
const myId = s.my_id || 'HOST1';
|
||||
|
||||
const _tog = (id, on, key, file, label) =>
|
||||
`<label class="vv-arr-tog-wrap">
|
||||
<div class="vv-arr-tog-track${on?' on':''}" id="vv-arr-tog-${id}"
|
||||
`<label class="vv-cf-toggle">
|
||||
<div class="vv-cf-track${on?' on':''}" id="vv-arr-tog-${id}"
|
||||
onclick="event.stopPropagation();vvArrToggle(this,'${key}','${file}')"></div>
|
||||
<span class="vv-arr-tog-lbl">${label}</span>
|
||||
</label>`;
|
||||
|
||||
@@ -72,26 +72,26 @@
|
||||
}
|
||||
|
||||
/* ── Settings panel ─────────────────────────────────────────── */
|
||||
.vv-set-file { border:1px solid #222; border-radius:5px; margin-bottom:8px; overflow:hidden; }
|
||||
.vv-set-file-hdr { display:flex; justify-content:space-between; align-items:center; padding:8px 12px;
|
||||
.vv-pt-set-file { border:1px solid #222; border-radius:5px; margin-bottom:8px; overflow:hidden; }
|
||||
.vv-pt-set-file-hdr { display:flex; justify-content:space-between; align-items:center; padding:8px 12px;
|
||||
background:#161616; cursor:pointer; font-size:12px; font-weight:bold; color:#bbb; font-family:monospace; }
|
||||
.vv-set-file-hdr:hover { background:#1a1a1a; }
|
||||
.vv-set-file-body { padding:6px 10px 10px; }
|
||||
.vv-set-sec { border-top:1px solid #1c1c1c; }
|
||||
.vv-set-sec:first-child { border-top:none; }
|
||||
.vv-set-sec-hdr { display:flex; justify-content:space-between; align-items:center; padding:6px 4px;
|
||||
.vv-pt-set-file-hdr:hover { background:#1a1a1a; }
|
||||
.vv-pt-set-file-body { padding:6px 10px 10px; }
|
||||
.vv-pt-set-sec { border-top:1px solid #1c1c1c; }
|
||||
.vv-pt-set-sec:first-child { border-top:none; }
|
||||
.vv-pt-set-sec-hdr { display:flex; justify-content:space-between; align-items:center; padding:6px 4px;
|
||||
cursor:pointer; font-size:11px; color:#888; text-transform:uppercase; letter-spacing:.04em; }
|
||||
.vv-set-sec-hdr:hover { color:#ccc; }
|
||||
.vv-set-sec-body { padding:4px 0 8px 8px; }
|
||||
.vv-set-chev { color:#444; font-size:10px; }
|
||||
.vv-set-field { margin-bottom:10px; }
|
||||
.vv-set-key { font-size:11px; color:#7ab; font-family:monospace; margin-bottom:2px; }
|
||||
.vv-set-desc { font-size:10px; color:#555; margin-bottom:3px; line-height:1.4; }
|
||||
.vv-set-input { width:100%; box-sizing:border-box; background:#0d0d0d; border:1px solid #2a2a2a;
|
||||
.vv-pt-set-sec-hdr:hover { color:#ccc; }
|
||||
.vv-pt-set-sec-body { padding:4px 0 8px 8px; }
|
||||
.vv-pt-set-chev { color:#444; font-size:10px; }
|
||||
.vv-pt-set-field { margin-bottom:10px; }
|
||||
.vv-pt-set-key { font-size:11px; color:#7ab; font-family:monospace; margin-bottom:2px; }
|
||||
.vv-pt-set-desc { font-size:10px; color:#555; margin-bottom:3px; line-height:1.4; }
|
||||
.vv-pt-set-input { width:100%; box-sizing:border-box; background:#0d0d0d; border:1px solid #2a2a2a;
|
||||
color:#ddd; padding:5px 8px; border-radius:3px; font-family:monospace; font-size:11px; }
|
||||
.vv-set-input:focus { outline:none; border-color:#4a8; }
|
||||
.vv-set-input.changed { border-color:#ff9800; }
|
||||
textarea.vv-set-input { resize:vertical; white-space:pre; }
|
||||
.vv-pt-set-input:focus { outline:none; border-color:#4a8; }
|
||||
.vv-pt-set-input.changed { border-color:#ff9800; }
|
||||
textarea.vv-pt-set-input { resize:vertical; white-space:pre; }
|
||||
</style>
|
||||
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;padding:0 2px;">
|
||||
@@ -361,9 +361,9 @@ function _renderHostSettings(nodes, isOwner) {
|
||||
for (const n of nodes) {
|
||||
const rdonly = isOwner ? '' : 'readonly style="opacity:.5;cursor:default;"';
|
||||
html += `<div>
|
||||
<div class="vv-set-key" style="margin-bottom:4px;">${_vvSetEsc(n.id)}</div>
|
||||
<div class="vv-pt-set-key" style="margin-bottom:4px;">${_vvSetEsc(n.id)}</div>
|
||||
<input type="text" ${rdonly}
|
||||
class="vv-set-input vv-pt-host-inp"
|
||||
class="vv-pt-set-input vv-pt-host-inp"
|
||||
data-key="${_vvSetEsc(n.id)}" data-file="master.conf" data-type="scalar"
|
||||
data-orig="${_vvSetEsc(n.hostname)}"
|
||||
value="${_vvSetEsc(n.hostname)}"
|
||||
@@ -441,18 +441,18 @@ function _vvRenderArrayCards() {
|
||||
if (fld.type === 'scalar') continue;
|
||||
hasArrays = true;
|
||||
const rows = Math.min(16, (fld.value.match(/\n/g) || []).length + 2);
|
||||
const attrs = `class="vv-set-input" data-key="${_vvSetEsc(fld.key)}" `
|
||||
const attrs = `class="vv-pt-set-input" data-key="${_vvSetEsc(fld.key)}" `
|
||||
+ `data-file="${_vvSetEsc(fld.file)}" data-type="${_vvSetEsc(fld.type)}" `
|
||||
+ `data-orig="${_vvSetEsc(fld.value)}" oninput="vvPtArrChanged(this)"`;
|
||||
html += `<div class="vv-set-file">
|
||||
<div class="vv-set-file-hdr" onclick="vvPtToggleNode(this)">
|
||||
html += `<div class="vv-pt-set-file">
|
||||
<div class="vv-pt-set-file-hdr" onclick="vvPtToggleNode(this)">
|
||||
<span>${_vvSetEsc(fld.key)}</span>
|
||||
<div style="display:flex;align-items:center;gap:6px;">
|
||||
${fld.desc ? `<span style="font-size:9px;color:#444;font-weight:normal;font-family:inherit;">${_vvSetEsc(fld.desc)}</span>` : ''}
|
||||
<span class="vv-set-chev">▸</span>
|
||||
<span class="vv-pt-set-chev">▸</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vv-set-file-body" style="display:none;">
|
||||
<div class="vv-pt-set-file-body" style="display:none;">
|
||||
<textarea ${attrs} rows="${rows}">${_vvSetEsc(fld.value)}</textarea>
|
||||
</div>
|
||||
</div>`;
|
||||
@@ -476,13 +476,13 @@ function _vvRenderArrayCards() {
|
||||
|
||||
function vvPtArrChanged(el) {
|
||||
el.classList.toggle('changed', el.value !== el.dataset.orig);
|
||||
const any = document.querySelector('#vv-pt-arrays-wrap .vv-set-input.changed');
|
||||
const any = document.querySelector('#vv-pt-arrays-wrap .vv-pt-set-input.changed');
|
||||
const saveBtn = document.getElementById('vv-pt-arrays-save');
|
||||
if (saveBtn) saveBtn.style.display = any ? '' : 'none';
|
||||
}
|
||||
|
||||
async function vvPtSaveArrays(btn) {
|
||||
const changedEls = document.querySelectorAll('#vv-pt-arrays-wrap .vv-set-input.changed');
|
||||
const changedEls = document.querySelectorAll('#vv-pt-arrays-wrap .vv-pt-set-input.changed');
|
||||
if (!changedEls.length) return;
|
||||
const changes = [];
|
||||
changedEls.forEach(el => changes.push({key: el.dataset.key, file: el.dataset.file, type: el.dataset.type, value: el.value}));
|
||||
@@ -556,18 +556,18 @@ function vvPtRenderSettings(files) {
|
||||
const label = f.file === 'master.conf' ? 'master.conf — shared' : f.file + ' — this host';
|
||||
const scalarFields = f.groups.flatMap(g => g.fields.filter(fld => fld.type === 'scalar'));
|
||||
if (!scalarFields.length) continue;
|
||||
html += `<div class="vv-set-file">
|
||||
<div class="vv-set-file-hdr" onclick="vvPtToggleNode(this)">
|
||||
<span>${_vvSetEsc(label)}</span><span class="vv-set-chev">▾</span>
|
||||
html += `<div class="vv-pt-set-file">
|
||||
<div class="vv-pt-set-file-hdr" onclick="vvPtToggleNode(this)">
|
||||
<span>${_vvSetEsc(label)}</span><span class="vv-pt-set-chev">▾</span>
|
||||
</div>
|
||||
<div class="vv-set-file-body">`;
|
||||
<div class="vv-pt-set-file-body">`;
|
||||
for (const fld of scalarFields) {
|
||||
const attrs = `class="vv-set-input" data-key="${_vvSetEsc(fld.key)}" `
|
||||
const attrs = `class="vv-pt-set-input" data-key="${_vvSetEsc(fld.key)}" `
|
||||
+ `data-file="${_vvSetEsc(fld.file)}" data-type="${_vvSetEsc(fld.type)}" `
|
||||
+ `data-orig="${_vvSetEsc(fld.value)}" oninput="vvPtSetChanged(this)"`;
|
||||
html += `<div class="vv-set-field">
|
||||
<div class="vv-set-key">${_vvSetEsc(fld.key)}</div>`;
|
||||
if (fld.desc) html += `<div class="vv-set-desc">${_vvSetEsc(fld.desc)}</div>`;
|
||||
html += `<div class="vv-pt-set-field">
|
||||
<div class="vv-pt-set-key">${_vvSetEsc(fld.key)}</div>`;
|
||||
if (fld.desc) html += `<div class="vv-pt-set-desc">${_vvSetEsc(fld.desc)}</div>`;
|
||||
html += `<input type="text" ${attrs} value="${_vvSetEsc(fld.value)}"></div>`;
|
||||
}
|
||||
html += `</div></div>`;
|
||||
@@ -577,7 +577,7 @@ function vvPtRenderSettings(files) {
|
||||
|
||||
function vvPtToggleNode(hdr) {
|
||||
const body = hdr.nextElementSibling;
|
||||
const chev = hdr.querySelector('.vv-set-chev');
|
||||
const chev = hdr.querySelector('.vv-pt-set-chev');
|
||||
const open = body.style.display !== 'none';
|
||||
body.style.display = open ? 'none' : '';
|
||||
if (chev) chev.textContent = open ? '▸' : '▾';
|
||||
@@ -587,12 +587,12 @@ function vvPtSetChanged(el) {
|
||||
const changed = el.value !== el.dataset.orig;
|
||||
el.classList.toggle('changed', changed);
|
||||
// Show Save button if any field is changed
|
||||
const any = document.querySelector('#vv-pt-settings-body .vv-set-input.changed');
|
||||
const any = document.querySelector('#vv-pt-settings-body .vv-pt-set-input.changed');
|
||||
document.getElementById('vv-pt-settings-save').style.display = any ? '' : 'none';
|
||||
}
|
||||
|
||||
async function vvPtSaveSettings(btn) {
|
||||
const changedEls = document.querySelectorAll('#vv-pt-settings-body .vv-set-input.changed');
|
||||
const changedEls = document.querySelectorAll('#vv-pt-settings-body .vv-pt-set-input.changed');
|
||||
if (!changedEls.length) return;
|
||||
const changes = [];
|
||||
changedEls.forEach(el => changes.push({
|
||||
|
||||
@@ -95,10 +95,6 @@
|
||||
/* Settings */
|
||||
.vv-ry-set-grid { display:grid;grid-template-columns:repeat(2,1fr);gap:8px;margin-top:6px; }
|
||||
.vv-ry-toggle { display:inline-flex;align-items:center;gap:8px;cursor:pointer;user-select:none; }
|
||||
.vv-ry-toggle-track { width:32px;height:18px;border-radius:9px;background:#222;border:1px solid #333;position:relative;transition:background .15s,border-color .15s;flex-shrink:0; }
|
||||
.vv-ry-toggle-track.on { background:#1a3a1a;border-color:#2d5a2d; }
|
||||
.vv-ry-toggle-track::after { content:'';position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:50%;background:#555;transition:left .15s,background .15s; }
|
||||
.vv-ry-toggle-track.on::after { left:16px;background:#4caf50; }
|
||||
.vv-ry-toggle-lbl { font-size:11px;color:#666; }
|
||||
.vv-ry-set-row { display:flex;align-items:center;gap:10px;margin:4px 0; }
|
||||
.vv-ry-set-lbl { font-size:11px;color:#555;width:130px;flex-shrink:0; }
|
||||
@@ -852,7 +848,7 @@ function _startLogPoll() {
|
||||
function _toggle(name, currentVal, label) {
|
||||
const id = 'vv-ry-tog-' + name;
|
||||
return `<label class="vv-ry-toggle" title="${name}">
|
||||
<div class="vv-ry-toggle-track ${currentVal ? 'on' : ''}" id="${id}"
|
||||
<div class="vv-cf-track ${currentVal ? 'on' : ''}" id="${id}"
|
||||
onclick="vvRyToggle(this,'${name}')"></div>
|
||||
<span class="vv-ry-toggle-lbl">${label}</span>
|
||||
</label>`;
|
||||
|
||||
@@ -77,13 +77,6 @@ $_aiEnabled = strtolower(trim($_vars['AI_ENABLED'] ?? 'false')) === 'true';
|
||||
.vv-set-info { font-size:11px;color:#444;line-height:1.6;margin-bottom:10px; }
|
||||
.vv-set-warn-box { background:#1a1200;border:1px solid #3a2800;border-radius:4px;
|
||||
padding:8px 12px;font-size:11px;color:#ffb74d;margin-top:8px;display:none; }
|
||||
.vv-set-tog-wrap { display:flex;align-items:center;gap:10px;cursor:pointer;user-select:none; }
|
||||
.vv-set-tog-track{ width:32px;height:18px;border-radius:9px;background:#222;border:1px solid #333;
|
||||
position:relative;transition:background .15s,border-color .15s;flex-shrink:0; }
|
||||
.vv-set-tog-track.on { background:#1a3a1a;border-color:#2d5a2d; }
|
||||
.vv-set-tog-track::after { content:'';position:absolute;top:2px;left:2px;width:12px;height:12px;
|
||||
border-radius:50%;background:#555;transition:left .15s,background .15s; }
|
||||
.vv-set-tog-track.on::after { left:16px;background:#4caf50; }
|
||||
.vv-set-tog-lbl { font-size:12px;color:#888; }
|
||||
.vv-set-tog-sub { font-size:10px;color:#3a3a3a;margin-top:1px; }
|
||||
.vv-set-inp { background:#0d0d0d;border:1px solid #2a2a2a;border-radius:3px;color:#888;
|
||||
@@ -137,16 +130,16 @@ $_aiEnabled = strtolower(trim($_vars['AI_ENABLED'] ?? 'false')) === 'true';
|
||||
<div class="vv-set-hdr">Notifications</div>
|
||||
|
||||
<div style="display:flex;flex-direction:column;gap:10px;margin-bottom:14px;">
|
||||
<label class="vv-set-tog-wrap">
|
||||
<div class="vv-set-tog-track<?= $_notifyUnraid ? ' on' : '' ?>" id="vv-ntf-unraid"
|
||||
<label class="vv-cf-toggle">
|
||||
<div class="vv-cf-track<?= $_notifyUnraid ? ' on' : '' ?>" id="vv-ntf-unraid"
|
||||
onclick="vvNtfToggle(this,'NOTIFY_UNRAID')"></div>
|
||||
<div>
|
||||
<div class="vv-set-tog-lbl">Unraid native notifications</div>
|
||||
<div class="vv-set-tog-sub">Appears in the Unraid bell icon — job completions, warnings, errors</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="vv-set-tog-wrap">
|
||||
<div class="vv-set-tog-track<?= $_enableLogging ? ' on' : '' ?>" id="vv-ntf-logging"
|
||||
<label class="vv-cf-toggle">
|
||||
<div class="vv-cf-track<?= $_enableLogging ? ' on' : '' ?>" id="vv-ntf-logging"
|
||||
onclick="vvNtfToggle(this,'ENABLE_LOGGING')"></div>
|
||||
<div>
|
||||
<div class="vv-set-tog-lbl">Verbose logging</div>
|
||||
@@ -179,8 +172,8 @@ $_aiEnabled = strtolower(trim($_vars['AI_ENABLED'] ?? 'false')) === 'true';
|
||||
<div class="vv-set-card">
|
||||
<div class="vv-set-hdr">AI</div>
|
||||
|
||||
<label class="vv-set-tog-wrap" style="margin-bottom:12px;">
|
||||
<div class="vv-set-tog-track<?= $_aiEnabled ? ' on' : '' ?>" id="vv-set-ai-tog"
|
||||
<label class="vv-cf-toggle" style="margin-bottom:12px;">
|
||||
<div class="vv-cf-track<?= $_aiEnabled ? ' on' : '' ?>" id="vv-set-ai-tog"
|
||||
onclick="vvSetAiToggle(this)"></div>
|
||||
<div>
|
||||
<div class="vv-set-tog-lbl">AI features</div>
|
||||
|
||||
Reference in New Issue
Block a user