mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-09-14 20:34:19 +08:00
150 lines
3.3 KiB
JavaScript
150 lines
3.3 KiB
JavaScript
'use strict';
|
||
/* SPDX-License-Identifier: Apache-2.0 */
|
||
/* Copyright 2025-2026 Lucas Albers <lucas.b.albers@gmail.com> */
|
||
'require baseclass'; /* LuCI require() needs Class.isSubclass — plain return {} fails */
|
||
'require fwlive.log as log';
|
||
|
||
/**
|
||
* Filter-chip DOM renderer for luci-app-fwlive.
|
||
*
|
||
* renderFilterChips(host, state, callbacks) → void
|
||
* host - container element (cleared and rebuilt; element itself is kept)
|
||
* state - shallow copy: { filters, chipFields }
|
||
* callbacks - { onInvert(field, ev), onClear(field, ev), onClearAll(ev) }
|
||
*
|
||
* Chips use the labels presentation (include: "is", exclude: "not" + light ≠).
|
||
* Modules must not mutate state. host is cleared then rebuilt (idempotent replace).
|
||
*/
|
||
|
||
function chipValueNodes(field, val) {
|
||
const p = log.parseFilterValue(val);
|
||
if (!p.value) return [''];
|
||
|
||
const valueNode = p.negate ? E('span', { 'class': 'fwlive-chip-strike' }, [p.value]) : p.value;
|
||
|
||
if (!p.negate) {
|
||
return [
|
||
E('span', { 'class': 'fwlive-chip-polarity' }, [_('is')]),
|
||
' ',
|
||
log.formatFilterChipLabel(field, val)
|
||
];
|
||
}
|
||
|
||
if (field === 'q' || field === 'src' || field === 'dst')
|
||
return [
|
||
field + ': ',
|
||
E('strong', { 'class': 'fwlive-chip-not' }, [_('not')]),
|
||
' contains ',
|
||
valueNode
|
||
];
|
||
|
||
return [field + ': ', E('strong', { 'class': 'fwlive-chip-not' }, [_('not')]), ' ', valueNode];
|
||
}
|
||
|
||
function chipLeadingSym(negated) {
|
||
if (!negated) return null;
|
||
return E(
|
||
'span',
|
||
{
|
||
'class': 'fwlive-chip-sym fwlive-chip-sym-light',
|
||
'aria-hidden': 'true'
|
||
},
|
||
['≠']
|
||
);
|
||
}
|
||
|
||
function renderFilterChips(host, state, callbacks) {
|
||
const filters = state.filters || {};
|
||
const chipFields = state.chipFields || [];
|
||
const chips = [];
|
||
|
||
for (let i = 0; i < chipFields.length; i++) {
|
||
const spec = chipFields[i];
|
||
const val = filters[spec.key];
|
||
if (!val) continue;
|
||
|
||
const parsed = log.parseFilterValue(val);
|
||
const negated = parsed.negate;
|
||
const kids = [];
|
||
const lead = chipLeadingSym(negated);
|
||
if (lead) kids.push(lead);
|
||
|
||
kids.push(E('span', { 'class': 'fwlive-chip-label' }, chipValueNodes(spec.label, val)));
|
||
kids.push(
|
||
E(
|
||
'span',
|
||
{
|
||
'class': 'fwlive-chip-invert-wrap',
|
||
'data-tip': negated ? _('Include instead') : _('Exclude instead')
|
||
},
|
||
[
|
||
E(
|
||
'button',
|
||
{
|
||
'type': 'button',
|
||
'class': 'fwlive-chip-invert',
|
||
'click': function (ev) {
|
||
callbacks.onInvert(spec.key, ev);
|
||
}
|
||
},
|
||
['≠']
|
||
)
|
||
]
|
||
)
|
||
);
|
||
kids.push(
|
||
E(
|
||
'a',
|
||
{
|
||
'href': '#',
|
||
'class': 'fwlive-chip-remove',
|
||
'title': _('Remove filter'),
|
||
'click': function (ev) {
|
||
callbacks.onClear(spec.key, ev);
|
||
}
|
||
},
|
||
['×']
|
||
)
|
||
);
|
||
|
||
chips.push(
|
||
E(
|
||
'span',
|
||
{
|
||
'class':
|
||
'fwlive-chip' + (negated ? ' fwlive-chip-negated' : ' fwlive-chip-include')
|
||
},
|
||
kids
|
||
)
|
||
);
|
||
}
|
||
|
||
host.className = 'fwlive-chips fwlive-chips-labels';
|
||
host.innerHTML = '';
|
||
if (!chips.length) {
|
||
host.style.display = 'none';
|
||
return;
|
||
}
|
||
|
||
host.style.display = 'flex';
|
||
for (let i = 0; i < chips.length; i++) host.appendChild(chips[i]);
|
||
|
||
host.appendChild(
|
||
E(
|
||
'a',
|
||
{
|
||
'href': '#',
|
||
'class': 'fwlive-chip-clear',
|
||
'click': function (ev) {
|
||
callbacks.onClearAll(ev);
|
||
}
|
||
},
|
||
[_('Clear all')]
|
||
)
|
||
);
|
||
}
|
||
|
||
return baseclass.extend({
|
||
renderFilterChips: renderFilterChips
|
||
});
|