Development

This commit is contained in:
Matthew Grotke 2026-05-25 02:51:38 -04:00
parent e2d33e04d5
commit 7825b9cd1a
2 changed files with 59 additions and 21 deletions

View file

@ -1128,15 +1128,17 @@ def _render_field(item, tokens):
if input_type == 'number':
min_attr = f' min="{item["min"]}"' if 'min' in item else ''
max_attr = f' max="{item["max"]}"' if 'max' in item else ''
dyn_hint_html = '<p class="form-hint field-dyn-hint" style="display:none"></p>'
inp = (f'<input type="number" name="{name}" value="{e(value)}"{min_attr}{max_attr}'
f' class="form-input{extra_cls}"{readonly}/>')
f' class="form-input{extra_cls}"{readonly}'
f' data-validate="positive_int" />')
if item.get('layout') == 'inline':
return (f'<div class="form-group" style="display:flex;align-items:center;gap:0.75em">'
f'<label class="form-label" style="margin:0;white-space:nowrap">{label}</label>'
f'<div style="width:6rem">{inp}</div>'
f'<div style="width:6rem">{inp}{dyn_hint_html}</div>'
f'{hint_html}</div>')
return (f'<div class="form-group"><label class="form-label">{label}</label>'
f'{inp}{hint_html}</div>')
f'{inp}{dyn_hint_html}{hint_html}</div>')
if input_type == 'textarea':
rows = item.get('rows', 4)
@ -1878,6 +1880,17 @@ function classifyTime24h(s) {
return 'complete';
}
function classifyPositiveInt(s, el) {
if (el && el.validity && el.validity.badInput) return 'invalid_char';
if (!s && s !== '0') return 'empty';
if (/[^0-9]/.test(s)) return 'invalid_char';
var n = parseInt(s, 10);
var min = (el && el.min !== '') ? parseInt(el.min, 10) : 0;
var max = (el && el.max !== '') ? parseInt(el.max, 10) : null;
if (n < min || (max !== null && n > max)) return 'out_of_range';
return 'complete';
}
function classifySubnet(s) {
if (!s) return 'empty';
if (/[^0-9.]/.test(s)) return 'invalid_char';
@ -2078,8 +2091,10 @@ document.addEventListener('click', function(e) {
if (inputType === 'checkbox') {
var checked = (val === true || val === 'true' || val === 1 || val === '1');
td.innerHTML = '<input type="checkbox" name="' + field + '"' +
(checked ? ' checked' : '') + ' class="inline-edit-checkbox"/>';
var cbLabel = fDef.checkbox_label ? ' <span class="form-checkbox-label">' + esc(fDef.checkbox_label) + '</span>' : '';
td.innerHTML = '<label class="inline-edit-checkbox-wrap">' +
'<input type="checkbox" name="' + field + '"' +
(checked ? ' checked' : '') + ' class="inline-edit-checkbox"/>' + cbLabel + '</label>';
} else if (inputType === 'checkbox_multi') {
var opts = fDef.options || [];
var checked = [];
@ -2106,7 +2121,9 @@ document.addEventListener('click', function(e) {
var minAttr = fDef.min !== undefined ? ' min="' + esc(String(fDef.min)) + '"' : '';
var maxAttr = fDef.max !== undefined ? ' max="' + esc(String(fDef.max)) + '"' : '';
td.innerHTML = '<input type="number" name="' + field + '" value="' + esc(String(val)) +
'"' + minAttr + maxAttr + ' class="form-input inline-edit-input"/>';
'"' + minAttr + maxAttr + ' class="form-input inline-edit-input" data-validate="positive_int"/>' +
'<p class="form-hint field-dyn-hint" style="display:none"></p>';
if (typeof validateEl === 'function') validateEl(td.querySelector('input'));
} else if (inputType === 'textarea') {
var textVal;
try { var arr = JSON.parse(val); textVal = Array.isArray(arr) ? arr.join('\n') : String(val||''); }
@ -2254,21 +2271,30 @@ var validateEl;
invalid_struct: 'Invalid domain format' },
networkname: { invalid_char: 'Letters, digits, hyphens and underscores only',
invalid_struct: 'No leading, trailing or consecutive special characters' },
time_24h: { invalid_char: 'Digits and colon only', invalid_struct: 'Must be HH:MM in 24-hour format (e.g. 02:30)' }
time_24h: { invalid_char: 'Digits and colon only', invalid_struct: 'Must be HH:MM in 24-hour format (e.g. 02:30)' },
positive_int: { invalid_char: 'Digits only',
out_of_range: function(el) {
var mn = (el && el.min !== '') ? el.min : null;
var mx = (el && el.max !== '') ? el.max : null;
if (mn !== null && mx !== null) return 'Must be between ' + mn + ' and ' + mx;
if (mn !== null) return 'Must be ≥ ' + mn;
if (mx !== null) return 'Must be ≤ ' + mx;
return 'Out of range';
}}
};
var _classifiers = { ip: classifyIp, ipv4: classifyIpv4, ipv6: classifyIpv6, mac: classifyMac,
subnet: classifySubnet, url: classifyUrl,
port: classifyPort, ipv4cidr: classifyIpv4Cidr,
endpoint: classifyEndpoint,
dashname: classifyDashname, domainname: classifyDomainname, networkname: classifyNetworkname,
time_24h: classifyTime24h };
time_24h: classifyTime24h, positive_int: classifyPositiveInt };
validateEl = function(el) {
var list = el.closest('.editable-list[data-validate]');
var vtype = el.dataset.validate || (list ? list.dataset.validate : '');
var classify = _classifiers[vtype];
if (!classify) return;
var cls = classify(el.value);
var cls = classify(el.value, el);
if (list) {
el.classList.remove('field-invalid', 'field-warning');
if (cls === 'incomplete') el.classList.add('field-warning');
@ -2280,7 +2306,8 @@ var validateEl;
} else if (cls === 'incomplete') {
setFieldHint(el, el._postValidate ? el._postValidate(cls) : '', 'warning');
} else {
setFieldHint(el, msgs[cls] || 'Invalid', 'error');
var msgVal = msgs[cls];
setFieldHint(el, typeof msgVal === 'function' ? msgVal(el) : (msgVal || 'Invalid'), 'error');
}
}
};

View file

@ -974,7 +974,8 @@
},
{
"col": "enabled",
"input_type": "checkbox"
"input_type": "checkbox",
"checkbox_label": "Enabled"
}
]
},
@ -1100,7 +1101,8 @@
},
{
"col": "enabled",
"input_type": "checkbox"
"input_type": "checkbox",
"checkbox_label": "Enabled"
}
]
},
@ -1519,15 +1521,18 @@
},
{
"col": "radius_default",
"input_type": "checkbox"
"input_type": "checkbox",
"checkbox_label": "Enabled"
},
{
"col": "mdns_reflection",
"input_type": "checkbox"
"input_type": "checkbox",
"checkbox_label": "Enabled"
},
{
"col": "dnsmasq_log_queries",
"input_type": "checkbox"
"input_type": "checkbox",
"checkbox_label": "Record"
},
{
"col": "use_blocklists",
@ -1754,7 +1759,8 @@
},
{
"col": "enabled",
"input_type": "checkbox"
"input_type": "checkbox",
"checkbox_label": "Enabled"
}
]
},
@ -1920,7 +1926,8 @@
},
{
"col": "enabled",
"input_type": "checkbox"
"input_type": "checkbox",
"checkbox_label": "Enabled"
}
]
},
@ -2128,11 +2135,13 @@
},
{
"col": "radius_client",
"input_type": "checkbox"
"input_type": "checkbox",
"checkbox_label": "Enabled"
},
{
"col": "enabled",
"input_type": "checkbox"
"input_type": "checkbox",
"checkbox_label": "Enabled"
}
]
},
@ -2326,11 +2335,13 @@
},
{
"col": "split_tunnel",
"input_type": "checkbox"
"input_type": "checkbox",
"checkbox_label": "Enabled"
},
{
"col": "enabled",
"input_type": "checkbox"
"input_type": "checkbox",
"checkbox_label": "Enabled"
}
]
},