Files
reflector-dashboard/lib/ui_field.js
T

77 lines
3.2 KiB
JavaScript

// Reflector Dashboard — web front-end for SvxReflector
// Copyright (C) 2026 SA7AUX (Björn)
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any
// later version. See the LICENSE file for details.
// ui_field.js — shared form-field builders (dmr-web, svx-web, svxlink-web).
// Canonical copy: <dmr-portal>/lib/ui_field.js; other repos carry a COPY.
//
// makeToggleBtn(isOn, onFlip(next, btn), extraCls) Enable/Disable button; .value 'true'/'false', .setOn()
// makeBoolSelect(cls, val, disabled) same button, fires 'change' — drop-in for a true/false <select>
// isBoolVal(v) only 'true'/'false'/true/false count (never 0/1: those are numbers)
// initTooltips(container) / cleanupTooltips() Bootstrap tooltips for [data-bs-toggle="tooltip"]
(function () {
'use strict';
function makeToggleBtn(isOn, onFlip, extraCls) {
const b = document.createElement('button');
b.type = 'button';
const paint = (state) => {
b.className = 'btn btn-sm ' + (state ? 'btn-warning' : 'btn-success') + (extraCls ? ' ' + extraCls : '');
b.textContent = state ? 'Disable' : 'Enable';
b.value = state ? 'true' : 'false'; // callers read .value === 'true'
b.dataset.on = state ? '1' : '0';
};
paint(!!isOn);
b.setOn = (state) => paint(!!state); // extern styrning (t.ex. mutual-exclusion)
b.addEventListener('click', () => {
if (b.disabled) return;
const next = b.dataset.on !== '1';
paint(next);
if (onFlip) onFlip(next, b);
});
return b;
}
function makeBoolSelect(cls, val, disabled) {
const on = (val === true || val === 'true' || val === '1' || val === 1);
const b = makeToggleBtn(on, (_next, btn) => {
btn.dispatchEvent(new Event('change', { bubbles: true })); // drives existing change handlers
}, cls);
if (disabled) b.disabled = true;
return b;
}
function isBoolVal(v) {
// Only unambiguous 'true'/'false' count as bool. '0'/'1' are REMOVED here —
// they collide with numeric fields (spacing_ms=0, burst_count=1, timeouts/
// intervals of 0/1) that would otherwise be wrongly rendered as a true/false
// toggle. The daemon stores bools as 'true'/'false'; any legacy '1'/'0' bool
// becomes a number field but is still type-corrected server-side on save
// (coerce_to_template_type in sysconfig.php).
return v === 'true' || v === 'false';
}
function cleanupTooltips() {
document.querySelectorAll('.tooltip').forEach(el => el.remove());
}
function initTooltips(container) {
cleanupTooltips();
if (!window.bootstrap || !window.bootstrap.Tooltip) return;
const root = container || document;
root.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(el => {
try { bootstrap.Tooltip.getOrCreateInstance(el, {delay: {show: 400, hide: 100}}); } catch(_) {}
});
}
window.makeToggleBtn = makeToggleBtn;
window.makeBoolSelect = makeBoolSelect;
window.isBoolVal = isBoolVal;
window.initTooltips = initTooltips;
window.cleanupTooltips = cleanupTooltips;
})();