mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-08 05:04:21 +08:00
💋 Sync 2026-10-04 04:16:09
This commit is contained in:
1 parent
f6b2df73ec
commit
f62992fd80
745 files changed
+212416
-1173
No files matched your search
@@ -8,12 +8,14 @@
|
||||
* Filter-chip DOM renderer for luci-app-fwlive.
|
||||
*
|
||||
* renderFilterChips(host, state, callbacks) → void
|
||||
* host - container element (cleared and rebuilt; element itself is kept)
|
||||
* host - stable container element; unchanged strips keep their nodes
|
||||
* state - shallow copy: { filters, chipFields }
|
||||
* callbacks - { onInvert(field, ev), onClear(field, ev), onClearAll(ev) }
|
||||
* callbacks - { onInvert(field, ev), onClear(field, ev), onClearAll(ev), onFocusFallback(field, action) }
|
||||
*
|
||||
* Chips use the labels presentation (include: "is"/"contains", exclude: "not"/"does not contain" + light ≠).
|
||||
* Modules must not mutate state. host is cleared then rebuilt (idempotent replace).
|
||||
* Modules must not mutate state. Changed strips are replaced; chip focus follows
|
||||
* the same field/action or moves to Clear all/the associated filter (a null
|
||||
* field means the strip became empty and should use the view's main input).
|
||||
*/
|
||||
|
||||
function chipFieldLabel(spec) {
|
||||
@@ -67,9 +69,114 @@ function chipLeadingSym(negated) {
|
||||
);
|
||||
}
|
||||
|
||||
function renderSignature(state, callbacks) {
|
||||
const filters = state.filters || {};
|
||||
const chipFields = state.chipFields || [];
|
||||
const fields = [];
|
||||
let visible = false;
|
||||
|
||||
for (let i = 0; i < chipFields.length; i++) {
|
||||
const spec = chipFields[i];
|
||||
const value = String(filters[spec.key] || '');
|
||||
fields.push([
|
||||
String(spec.key),
|
||||
String(chipFieldLabel(spec)),
|
||||
value,
|
||||
log.isSubstringFilterField(spec.key)
|
||||
]);
|
||||
if (value && log.parseFilterValue(value).value) visible = true;
|
||||
}
|
||||
|
||||
const labels = [
|
||||
String(_('Exclude instead')),
|
||||
String(_('Include instead')),
|
||||
String(_('Remove filter')),
|
||||
String(_('Clear all'))
|
||||
];
|
||||
|
||||
return {
|
||||
value: JSON.stringify([fields, labels]),
|
||||
visible: visible,
|
||||
onInvert: callbacks && callbacks.onInvert,
|
||||
onClear: callbacks && callbacks.onClear,
|
||||
onClearAll: callbacks && callbacks.onClearAll,
|
||||
onFocusFallback: callbacks && callbacks.onFocusFallback
|
||||
};
|
||||
}
|
||||
|
||||
function signatureMatches(host, signature) {
|
||||
const previous = host._fwliveChipRenderState;
|
||||
return !!(
|
||||
previous &&
|
||||
previous.value === signature.value &&
|
||||
previous.onInvert === signature.onInvert &&
|
||||
previous.onClear === signature.onClear &&
|
||||
previous.onClearAll === signature.onClearAll &&
|
||||
previous.onFocusFallback === signature.onFocusFallback &&
|
||||
host.className === 'fwlive-chips fwlive-chips-labels' &&
|
||||
host.style &&
|
||||
host.style.display === (signature.visible ? 'flex' : 'none')
|
||||
);
|
||||
}
|
||||
|
||||
function focusedChipControl(host) {
|
||||
if (typeof document === 'undefined' || !document.activeElement) return null;
|
||||
const active = document.activeElement;
|
||||
let node = active;
|
||||
while (node && node !== host) node = node.parentNode;
|
||||
if (node !== host || typeof active.getAttribute !== 'function') return null;
|
||||
|
||||
const action = active.getAttribute('data-fwlive-chip-action');
|
||||
if (!action) return null;
|
||||
return {
|
||||
action: action,
|
||||
field: active.getAttribute('data-fwlive-chip-field')
|
||||
};
|
||||
}
|
||||
|
||||
function findChipControl(root, action, field) {
|
||||
const pending = [root];
|
||||
while (pending.length) {
|
||||
const node = pending.pop();
|
||||
if (!node || node.nodeType !== 1) continue;
|
||||
if (
|
||||
node.getAttribute &&
|
||||
node.getAttribute('data-fwlive-chip-action') === action &&
|
||||
(field == null || node.getAttribute('data-fwlive-chip-field') === field)
|
||||
)
|
||||
return node;
|
||||
|
||||
const children = node.childNodes || [];
|
||||
for (let i = children.length - 1; i >= 0; i--) pending.push(children[i]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function restoreChipFocus(host, focused, callbacks) {
|
||||
if (!focused) return;
|
||||
|
||||
let target = findChipControl(host, focused.action, focused.field);
|
||||
if (!target && focused.action !== 'clear-all')
|
||||
target = findChipControl(host, 'clear-all', null);
|
||||
|
||||
if (target && typeof target.focus === 'function') {
|
||||
target.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (callbacks && typeof callbacks.onFocusFallback === 'function')
|
||||
callbacks.onFocusFallback(
|
||||
focused.action === 'clear-all' ? null : focused.field,
|
||||
focused.action
|
||||
);
|
||||
}
|
||||
|
||||
function renderFilterChips(host, state, callbacks) {
|
||||
const filters = state.filters || {};
|
||||
const chipFields = state.chipFields || [];
|
||||
const signature = renderSignature(state, callbacks);
|
||||
if (signatureMatches(host, signature)) return;
|
||||
const focused = focusedChipControl(host);
|
||||
const chips = [];
|
||||
|
||||
for (let i = 0; i < chipFields.length; i++) {
|
||||
@@ -99,6 +206,8 @@ function renderFilterChips(host, state, callbacks) {
|
||||
{
|
||||
'type': 'button',
|
||||
'class': 'fwlive-chip-invert',
|
||||
'data-fwlive-chip-field': String(spec.key),
|
||||
'data-fwlive-chip-action': 'invert',
|
||||
'aria-label': invertLabel,
|
||||
'click': function (ev) {
|
||||
callbacks.onInvert(spec.key, ev);
|
||||
@@ -115,6 +224,9 @@ function renderFilterChips(host, state, callbacks) {
|
||||
{
|
||||
'href': '#',
|
||||
'class': 'fwlive-chip-remove',
|
||||
'data-fwlive-chip-field': String(spec.key),
|
||||
'data-fwlive-chip-action': 'remove',
|
||||
'aria-label': String(_('Remove filter')),
|
||||
'title': _('Remove filter'),
|
||||
'click': function (ev) {
|
||||
callbacks.onClear(spec.key, ev);
|
||||
@@ -140,6 +252,8 @@ function renderFilterChips(host, state, callbacks) {
|
||||
host.innerHTML = '';
|
||||
if (!chips.length) {
|
||||
host.style.display = 'none';
|
||||
host._fwliveChipRenderState = signature;
|
||||
restoreChipFocus(host, focused, callbacks);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -152,6 +266,7 @@ function renderFilterChips(host, state, callbacks) {
|
||||
{
|
||||
'href': '#',
|
||||
'class': 'fwlive-chip-clear',
|
||||
'data-fwlive-chip-action': 'clear-all',
|
||||
'click': function (ev) {
|
||||
callbacks.onClearAll(ev);
|
||||
}
|
||||
@@ -159,6 +274,8 @@ function renderFilterChips(host, state, callbacks) {
|
||||
[_('Clear all')]
|
||||
)
|
||||
);
|
||||
host._fwliveChipRenderState = signature;
|
||||
restoreChipFocus(host, focused, callbacks);
|
||||
}
|
||||
|
||||
return baseclass.extend({
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*/
|
||||
return baseclass.extend({
|
||||
/* Keep in sync with openwrt-feed/luci-app-fwlive/Makefile PKG_VERSION. */
|
||||
APP_VERSION: '0.1.50',
|
||||
APP_VERSION: '0.1.51',
|
||||
ROW_LIMIT_OPTIONS: [25, 50, 100, 250, 500, 1000, 2000],
|
||||
DEFAULT_ROW_LIMIT: 100,
|
||||
FETCH_MODE_OPTIONS: ['auto', 'manual'],
|
||||
|
||||
@@ -11,5 +11,5 @@
|
||||
* GENERATED — do not edit. Edit fwlive.css and run: node scripts/embed-fwlive-css.js
|
||||
*/
|
||||
return baseclass.extend({
|
||||
styleText: "\n/* SPDX-License-Identifier: Apache-2.0 */\n.fwlive-map {\n\tmax-width: none;\n\twidth: 100%;\n\t--fwlive-pass-color: var(--success-color-high, var(--success-color, #46a546));\n\t--fwlive-deny-color: var(--error-color-high, var(--error-color, #ca3c3c));\n\t--fwlive-bg-medium: var(--background-color-medium, var(--white-color-low, #f9f9f9));\n\t--fwlive-paused-color: #b45309;\n}\n/* Wide page in Detail only — Simple keeps LuCI’s 940px column. */\n#maincontent.container:has(.fwlive-map[data-view=\"detailed\"]) {\n\tmax-width: none;\n\tpadding-left: 16px;\n\tpadding-right: 16px;\n}\n.fwlive-map[data-row-tint=\"accessible\"] {\n\t--fwlive-pass-color: #0d9488;\n\t--fwlive-deny-color: #c2410c;\n}\n.fwlive-map.fwlive-watch-paused .fwlive-title-row,\n.fwlive-map.fwlive-watch-paused .fwlive-watch-strip {\n\tbackground: rgba(180, 83, 9, 0.08);\n\tbackground: color-mix(in srgb, var(--fwlive-paused-color) 8%, transparent);\n}\n.fwlive-map.fwlive-watch-paused .fwlive-title-row {\n\tmargin: 0 -8px;\n\tpadding: 6px 8px 0;\n\tborder-radius: 3px 3px 0 0;\n}\n.fwlive-map.fwlive-watch-paused .fwlive-watch-strip {\n\tmargin: 0 -8px 10px;\n\tpadding: 8px;\n\tborder-radius: 0 0 3px 3px;\n\tborder-bottom-color: transparent;\n}\n.fwlive-title-row {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: baseline;\n\tjustify-content: space-between;\n\tgap: 8px 16px;\n\tmargin: 0 0 4px;\n}\n.fwlive-title-row h2 {\n\tmargin: 0;\n\tflex: 0 1 auto;\n}\n.fwlive-title-status {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 8px;\n\tflex-wrap: wrap;\n\tmin-width: 0;\n\tmargin-left: auto;\n}\n.fwlive-watch-strip {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: stretch;\n\tjustify-content: flex-start;\n\tgap: 0;\n\tmin-height: 40px;\n\tpadding: 8px 0 10px;\n\tborder-bottom: 1px solid var(--border-color-medium);\n\tmargin-bottom: 10px;\n}\n.fwlive-watch-group {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tmin-height: 28px;\n\tpadding: 0 10px;\n\tborder-left: 1px solid var(--border-color-medium);\n\tflex-wrap: wrap;\n}\n.fwlive-watch-group:last-child {\n\tpadding-right: 0;\n}\n.fwlive-watch-group-label {\n\tmargin-right: 2px;\n\tcolor: var(--text-color-medium);\n\tfont-size: 0.72em;\n\tfont-weight: 700;\n\tletter-spacing: 0.08em;\n\ttext-transform: uppercase;\n\twhite-space: nowrap;\n}\n.fwlive-watch-seg {\n\tdisplay: inline-flex;\n\talign-items: stretch;\n\tborder: 1px solid var(--border-color-medium);\n\tborder-radius: 3px;\n\toverflow: hidden;\n}\n.fwlive-watch-seg .fwlive-seg-btn {\n\tmargin: 0;\n\tborder: 0;\n\tborder-radius: 0;\n\tborder-right: 1px solid var(--border-color-medium);\n\tbackground: transparent;\n\tpadding: 4px 10px;\n\tfont-size: 0.88em;\n\tline-height: 1.25;\n\tmin-height: 28px;\n}\n.fwlive-watch-seg .fwlive-seg-btn:last-child {\n\tborder-right: 0;\n}\n.fwlive-watch-seg .fwlive-seg-btn[aria-pressed=\"true\"] {\n\tbackground: rgba(26, 111, 156, 0.12);\n\tbackground: color-mix(in srgb, var(--primary-color-high) 12%, transparent);\n\tcolor: var(--primary-color-high);\n\tfont-weight: 600;\n}\n.fwlive-watch-label { font-weight: 600; }\n.fwlive-watch-paused .fwlive-watch-label { color: var(--fwlive-paused-color); }\n.fwlive-map.fwlive-watch-paused .fwlive-title-status .fwlive-watch-label { color: var(--fwlive-paused-color); }\n.fwlive-dot {\n\twidth: 8px;\n\theight: 8px;\n\tborder-radius: 50%;\n\tbackground: var(--text-color-low, var(--border-color-high));\n\tdisplay: inline-block;\n\tflex-shrink: 0;\n}\n.fwlive-dot.fwlive-dot-on {\n\tbackground: var(--fwlive-pass-color);\n\tbox-shadow: 0 0 0 3px color-mix(in srgb, var(--fwlive-pass-color) 22%, transparent);\n}\n.fwlive-btn-ghost {\n\tbackground: transparent;\n\tborder-color: var(--border-color-medium);\n}\n.fwlive-btn-quiet {\n\tbackground: transparent;\n\tborder-color: var(--border-color-medium);\n\tcolor: var(--text-color-medium);\n\tfont-weight: 500;\n}\n.fwlive-log-merged {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 10px;\n\tborder: 1px solid var(--border-color-medium);\n\tborder-radius: 3px;\n\tbackground: var(--background-color-medium, var(--white-color-low, var(--background-color-high)));\n\tcolor: var(--text-color-high);\n\tfont-weight: 500;\n\tfont-size: 0.88em;\n\tline-height: 1.25;\n\tmin-height: 28px;\n\tcursor: pointer;\n}\n.fwlive-log-merged:hover {\n\tbackground: var(--background-color-low, var(--background-color-medium));\n}\n.fwlive-log-merged:disabled {\n\topacity: 0.65;\n\tcursor: default;\n}\n.fwlive-log-on-dot {\n\twidth: 6px;\n\theight: 6px;\n\tborder-radius: 50%;\n\tbackground: var(--fwlive-pass-color);\n\tflex-shrink: 0;\n}\n.fwlive-log-rate {\n\tcolor: var(--text-color-medium);\n\tfont-weight: 400;\n}\n.fwlive-display-bar {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px 14px;\n\tmargin: 0 0 12px;\n\tpadding: 8px 12px;\n\tborder: 1px solid var(--border-color-medium);\n\tborder-radius: 3px;\n\tbackground: var(--background-color-high);\n}\n.fwlive-dLine truncated
|
||||
styleText: "\n/* SPDX-License-Identifier: Apache-2.0 */\n.fwlive-map {\n\tmax-width: none;\n\twidth: 100%;\n\t--fwlive-pass-color: var(--success-color-high, var(--success-color, #46a546));\n\t--fwlive-deny-color: var(--error-color-high, var(--error-color, #ca3c3c));\n\t--fwlive-bg-medium: var(--background-color-medium, var(--white-color-low, #f9f9f9));\n\t--fwlive-paused-color: #b45309;\n}\n/* Wide page in Detail only — Simple keeps LuCI’s 940px column. */\n#maincontent.container:has(.fwlive-map[data-view=\"detailed\"]) {\n\tmax-width: none;\n\tpadding-left: 16px;\n\tpadding-right: 16px;\n}\n.fwlive-map[data-row-tint=\"accessible\"] {\n\t--fwlive-pass-color: #0d9488;\n\t--fwlive-deny-color: #c2410c;\n}\n.fwlive-map.fwlive-watch-paused .fwlive-title-row,\n.fwlive-map.fwlive-watch-paused .fwlive-watch-strip {\n\tbackground: rgba(180, 83, 9, 0.08);\n\tbackground: color-mix(in srgb, var(--fwlive-paused-color) 8%, transparent);\n}\n.fwlive-map.fwlive-watch-paused .fwlive-title-row {\n\tmargin: 0 -8px;\n\tpadding: 6px 8px 0;\n\tborder-radius: 3px 3px 0 0;\n}\n.fwlive-map.fwlive-watch-paused .fwlive-watch-strip {\n\tmargin: 0 -8px 10px;\n\tpadding: 8px;\n\tborder-radius: 0 0 3px 3px;\n\tborder-bottom-color: transparent;\n}\n.fwlive-title-row {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: baseline;\n\tjustify-content: space-between;\n\tgap: 8px 16px;\n\tmargin: 0 0 4px;\n}\n.fwlive-title-row h2 {\n\tmargin: 0;\n\tflex: 0 1 auto;\n}\n.fwlive-title-status {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 8px;\n\tflex-wrap: wrap;\n\tmin-width: 0;\n\tmargin-left: auto;\n}\n.fwlive-watch-strip {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: stretch;\n\tjustify-content: flex-start;\n\tgap: 0;\n\tmin-height: 40px;\n\tpadding: 8px 0 10px;\n\tborder-bottom: 1px solid var(--border-color-medium);\n\tmargin-bottom: 10px;\n}\n.fwlive-watch-group {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tmin-height: 28px;\n\tpadding: 0 10px;\n\tborder-left: 1px solid var(--border-color-medium);\n\tflex-wrap: wrap;\n}\n.fwlive-watch-group:last-child {\n\tpadding-right: 0;\n}\n.fwlive-watch-group-label {\n\tmargin-right: 2px;\n\tcolor: var(--text-color-medium);\n\tfont-size: 0.72em;\n\tfont-weight: 700;\n\tletter-spacing: 0.08em;\n\ttext-transform: uppercase;\n\twhite-space: nowrap;\n}\n.fwlive-watch-seg {\n\tdisplay: inline-flex;\n\talign-items: stretch;\n\tborder: 1px solid var(--border-color-medium);\n\tborder-radius: 3px;\n\toverflow: hidden;\n}\n.fwlive-watch-seg .fwlive-seg-btn {\n\tmargin: 0;\n\tborder: 0;\n\tborder-radius: 0;\n\tborder-right: 1px solid var(--border-color-medium);\n\tbackground: transparent;\n\tpadding: 4px 10px;\n\tfont-size: 0.88em;\n\tline-height: 1.25;\n\tmin-height: 28px;\n}\n.fwlive-watch-seg .fwlive-seg-btn:last-child {\n\tborder-right: 0;\n}\n.fwlive-watch-seg .fwlive-seg-btn[aria-pressed=\"true\"] {\n\tbackground: rgba(26, 111, 156, 0.12);\n\tbackground: color-mix(in srgb, var(--primary-color-high) 12%, transparent);\n\tcolor: var(--primary-color-high);\n\tfont-weight: 600;\n}\n.fwlive-watch-label { font-weight: 600; }\n.fwlive-watch-paused .fwlive-watch-label { color: var(--fwlive-paused-color); }\n.fwlive-map.fwlive-watch-paused .fwlive-title-status .fwlive-watch-label { color: var(--fwlive-paused-color); }\n.fwlive-dot {\n\twidth: 8px;\n\theight: 8px;\n\tborder-radius: 50%;\n\tbackground: var(--text-color-low, var(--border-color-high));\n\tdisplay: inline-block;\n\tflex-shrink: 0;\n}\n.fwlive-dot.fwlive-dot-on {\n\tbackground: var(--fwlive-pass-color);\n\tbox-shadow: 0 0 0 3px color-mix(in srgb, var(--fwlive-pass-color) 22%, transparent);\n}\n.fwlive-btn-ghost {\n\tbackground: transparent;\n\tborder-color: var(--border-color-medium);\n}\n.fwlive-btn-quiet {\n\tbackground: transparent;\n\tborder-color: var(--border-color-medium);\n\tcolor: var(--text-color-medium);\n\tfont-weight: 500;\n}\n.fwlive-log-merged {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 10px;\n\tborder: 1px solid var(--border-color-medium);\n\tborder-radius: 3px;\n\tbackground: var(--background-color-medium, var(--white-color-low, var(--background-color-high)));\n\tcolor: var(--text-color-high);\n\tfont-weight: 500;\n\tfont-size: 0.88em;\n\tline-height: 1.25;\n\tmin-height: 28px;\n\tcursor: pointer;\n}\n.fwlive-log-merged:hover {\n\tbackground: var(--background-color-low, var(--background-color-medium));\n}\n.fwlive-log-merged:disabled {\n\topacity: 0.65;\n\tcursor: default;\n}\n.fwlive-log-on-dot {\n\twidth: 6px;\n\theight: 6px;\n\tborder-radius: 50%;\n\tbackground: var(--fwlive-pass-color);\n\tflex-shrink: 0;\n}\n.fwlive-log-rate {\n\tcolor: var(--text-color-medium);\n\tfont-weight: 400;\n}\n.fwlive-display-bar {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px 14px;\n\tmargin: 0 0 12px;\n\tpadding: 8px 12px;\n\tborder: 1px solid var(--border-color-medium);\n\tborder-radius: 3px;\n\tbackground: var(--background-color-high);\n}\n.fwlive-dLine truncated
|
||||
});
|
||||
@@ -284,7 +284,8 @@
|
||||
}
|
||||
#fwlive-table { margin: 0; width: 100%; table-layout: auto; border-collapse: collapse; }
|
||||
.fwlive-map[data-view="simple"] #fwlive-table { table-layout: fixed; width: 100%; }
|
||||
.fwlive-map[data-view="simple"] col.fwlive-col-action { width: 5rem; }
|
||||
/* Keep the localized action label and message button inside the first cell. */
|
||||
.fwlive-map[data-view="simple"] col.fwlive-col-action { width: 11.5em; }
|
||||
.fwlive-map[data-view="simple"] col.fwlive-col-time { width: 5rem; }
|
||||
.fwlive-map[data-view="simple"] col.fwlive-col-iface { width: 5.5rem; }
|
||||
.fwlive-map[data-view="simple"] col.fwlive-col-proto { width: 4rem; }
|
||||
|
||||
@@ -14,7 +14,8 @@ function createBatch() {
|
||||
|
||||
/* Own request serialization and scheduling. The runner owns result application
|
||||
* and must check its epoch before applying asynchronous results. Adapters supply
|
||||
* poll.add/remove, visibility.add/remove, isHidden, run, and initialCadence. */
|
||||
* poll.add/remove, visibility.add/remove, isHidden, run, and initialCadence;
|
||||
* optional options.onVisible is called on a hidden-to-visible transition. */
|
||||
function createCoordinator(options) {
|
||||
const { poll, visibility, isHidden, run, initialCadence } = options;
|
||||
if (!Number.isFinite(initialCadence) || initialCadence <= 0)
|
||||
|
||||
@@ -180,7 +180,9 @@ function buildColumnCell(col, row, state, callbacks) {
|
||||
case 'time': {
|
||||
const timeAttrs = { 'class': columnCellClass(col) };
|
||||
if (state.viewMode === 'simple')
|
||||
timeAttrs.title = _('Click a row for the full message');
|
||||
timeAttrs.title = _(
|
||||
'Activate the message button or click a row to show or hide the full message'
|
||||
);
|
||||
return E('td', timeAttrs, [
|
||||
state.viewMode === 'simple'
|
||||
? log.formatTimestampCompact(row.timestamp)
|
||||
|
||||
@@ -219,6 +219,7 @@ return view.extend({
|
||||
consentDismissedSession: false,
|
||||
_loggingToolbarSig: '',
|
||||
_loggingEmptySig: '',
|
||||
_filterChipCallbacks: null,
|
||||
tintFallbackActive: false,
|
||||
tintProbeDone: false,
|
||||
|
||||
@@ -2140,9 +2141,42 @@ return view.extend({
|
||||
this.onFilterInput();
|
||||
},
|
||||
|
||||
focusFilterFallback(field) {
|
||||
if (this.viewDisposed) return;
|
||||
let target = field ? document.getElementById('fwlive-' + field) : null;
|
||||
if (!target || typeof target.focus !== 'function')
|
||||
target = document.getElementById('fwlive-q');
|
||||
if (target && typeof target.focus === 'function') {
|
||||
const details = typeof target.closest === 'function' ? target.closest('details') : null;
|
||||
if (details && !details.open) details.open = true;
|
||||
target.focus();
|
||||
}
|
||||
},
|
||||
|
||||
renderFilterChips() {
|
||||
const bar = document.getElementById('fwlive-chips');
|
||||
if (!bar) return;
|
||||
if (!bar || this.viewDisposed) return;
|
||||
|
||||
if (!this._filterChipCallbacks) {
|
||||
this._filterChipCallbacks = {
|
||||
onInvert: (field, ev) => {
|
||||
if (!this.viewDisposed) this.invertFilter(field, ev);
|
||||
else {
|
||||
if (ev && ev.preventDefault) ev.preventDefault();
|
||||
if (ev && ev.stopPropagation) ev.stopPropagation();
|
||||
}
|
||||
},
|
||||
onClear: (field, ev) => {
|
||||
if (!this.viewDisposed) this.clearFilter(field, ev);
|
||||
else if (ev && ev.preventDefault) ev.preventDefault();
|
||||
},
|
||||
onClearAll: (ev) => {
|
||||
if (!this.viewDisposed) this.clearAllFilters(ev);
|
||||
else if (ev && ev.preventDefault) ev.preventDefault();
|
||||
},
|
||||
onFocusFallback: (field) => this.focusFilterFallback(field)
|
||||
};
|
||||
}
|
||||
|
||||
chips.renderFilterChips(
|
||||
bar,
|
||||
@@ -2150,11 +2184,7 @@ return view.extend({
|
||||
filters: Object.assign({}, this.readFilters()),
|
||||
chipFields: this.FILTER_CHIP_FIELDS
|
||||
},
|
||||
{
|
||||
onInvert: (field, ev) => this.invertFilter(field, ev),
|
||||
onClear: (field, ev) => this.clearFilter(field, ev),
|
||||
onClearAll: (ev) => this.clearAllFilters(ev)
|
||||
}
|
||||
this._filterChipCallbacks
|
||||
);
|
||||
},
|
||||
|
||||
@@ -2576,7 +2606,7 @@ return view.extend({
|
||||
E('li', { 'id': 'fwlive-manual-test' }, []),
|
||||
E('li', {}, [
|
||||
_(
|
||||
'In Simple view, activate the message button or click a row to show or hide the full log line.'
|
||||
'In Simple view, activate the message button or click a row to show or hide the full message.'
|
||||
)
|
||||
]),
|
||||
E('li', {}, [
|
||||
@@ -2788,19 +2818,27 @@ return view.extend({
|
||||
'class': 'cbi-input-text',
|
||||
'placeholder': _('Quick search')
|
||||
}),
|
||||
E('select', { 'id': 'fwlive-action', 'class': 'cbi-input-select' }, [
|
||||
E('option', { 'value': '' }, [_('Any action')]),
|
||||
E('option', { 'value': 'pass' }, [_('pass')]),
|
||||
E('option', { 'value': 'block' }, [_('block')]),
|
||||
E('option', { 'value': 'drop' }, [_('drop')]),
|
||||
E('option', { 'value': 'reject' }, [_('reject')]),
|
||||
E('option', { 'value': 'unknown' }, [_('unknown')]),
|
||||
E('option', { 'value': '!pass' }, [_('not pass')]),
|
||||
E('option', { 'value': '!drop' }, [_('not drop')]),
|
||||
E('option', { 'value': '!block' }, [_('not block')]),
|
||||
E('option', { 'value': '!reject' }, [_('not reject')]),
|
||||
E('option', { 'value': '!unknown' }, [_('not unknown')])
|
||||
]),
|
||||
E(
|
||||
'select',
|
||||
{
|
||||
'id': 'fwlive-action',
|
||||
'class': 'cbi-input-select',
|
||||
'aria-label': String(_('Filter by %s').format(_('Action')))
|
||||
},
|
||||
[
|
||||
E('option', { 'value': '' }, [_('Any action')]),
|
||||
E('option', { 'value': 'pass' }, [_('pass')]),
|
||||
E('option', { 'value': 'block' }, [_('block')]),
|
||||
E('option', { 'value': 'drop' }, [_('drop')]),
|
||||
E('option', { 'value': 'reject' }, [_('reject')]),
|
||||
E('option', { 'value': 'unknown' }, [_('unknown')]),
|
||||
E('option', { 'value': '!pass' }, [_('not pass')]),
|
||||
E('option', { 'value': '!drop' }, [_('not drop')]),
|
||||
E('option', { 'value': '!block' }, [_('not block')]),
|
||||
E('option', { 'value': '!reject' }, [_('not reject')]),
|
||||
E('option', { 'value': '!unknown' }, [_('not unknown')])
|
||||
]
|
||||
),
|
||||
E('div', { 'class': 'fwlive-proto-pair' }, [
|
||||
E(
|
||||
'select',
|
||||
@@ -2861,7 +2899,7 @@ return view.extend({
|
||||
E('div', { 'id': 'fwlive-chips', 'class': 'fwlive-chips' }, []),
|
||||
E('p', { 'class': 'fwlive-hint-line' }, [
|
||||
_(
|
||||
'Click a cell to filter · ≠ on a chip to exclude · Ctrl+click a rule for firewall settings · in Simple view, click a row for the full message'
|
||||
'Click a cell to filter · ≠ on a chip to exclude · Ctrl+click a rule for firewall settings · in Simple view, activate the message button or click a row to show or hide the full message'
|
||||
)
|
||||
]),
|
||||
E(
|
||||
|
||||
Reference in new issue
Block a user