Filter menus: None first, documented picks; Custom View drag preview
- Fix: sorting a software column stopped its filter listing apps individually (the sort arrow written into the header changed the label used to recognise list columns) - "None" (nothing missing) is pinned to the top of the value list, so the easy devices are one click away - Software columns get "Documented" / "Undocumented" quick picks that tick only apps with / without a knowledge-base article; documented apps are marked in the list - Custom View: remove Reset Filters / Reset Sort (Clear filters and the column menus replace them); old saved filters are cleared since nothing else could clear them - Custom View column drag: a ghost of the column follows the cursor, the column being moved is dimmed, and a line shows where it will land
This commit is contained in:
1 parent
318c59e610
commit
973ff592a4
4 files changed
+149
-5
No files matched your search
+47
-3
@@ -29,7 +29,7 @@
|
||||
const nativeSort = {}; // table key -> { col, dir } for tables without their own sort (Workbench)
|
||||
|
||||
const norm = s => String(s == null ? '' : s).replace(/\s+/g, ' ').trim();
|
||||
const cleanLabel = t => norm(String(t).replace(/[↕▲▼▾×✕]/g, ''));
|
||||
const cleanLabel = t => norm(String(t).replace(/[↑↓↕▲▼▾×✕]/g, '')); // sort arrows, remove x
|
||||
|
||||
const FUNNEL_SVG = '<svg viewBox="0 0 512 512" aria-hidden="true"><path fill="currentColor" d="M3.9 54.9C10.5 40.9 24.5 32 40 32H472c15.5 0 29.5 8.9 36.1 22.9s4.6 30.5-5.2 42.5L320 320.9V448c0 12.1-6.8 23.2-17.7 28.6s-23.8 4.3-33.5-3l-64-48c-8.1-6-12.8-15.5-12.8-25.6V320.9L9 97.3C-.7 85.4-2.8 68.8 3.9 54.9z"/></svg>';
|
||||
const CARET_SVG = '<svg viewBox="0 0 10 10" aria-hidden="true"><path d="M2 3.5 5 6.5 8 3.5" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>';
|
||||
@@ -334,7 +334,9 @@
|
||||
if (!passes(ad, cols, tr, key)) continue;
|
||||
for (const v of cellValues(tr.cells[idx], col)) { if (v === BLANK) hasBlank = true; else seen.add(v); }
|
||||
}
|
||||
const values = Array.from(seen).sort(collator.compare);
|
||||
// "None" (nothing missing) first so the easy devices are one click away; (Blanks) last
|
||||
const values = Array.from(seen).filter(v => v !== 'None').sort(collator.compare);
|
||||
if (seen.has('None')) values.unshift('None');
|
||||
if (hasBlank) values.push(BLANK);
|
||||
|
||||
menuCtx = { table, idx };
|
||||
@@ -382,8 +384,26 @@
|
||||
list.appendChild(row);
|
||||
return { row, cb };
|
||||
};
|
||||
// Software columns: quick picks for apps that do / don't have a knowledge-base article
|
||||
const isSoftware = /software/i.test(col.label);
|
||||
const docs = (typeof availableDocs !== 'undefined' && Array.isArray(availableDocs)) ? availableDocs.map(d => d.toLowerCase()) : [];
|
||||
const isDocumented = v => docs.includes(String(v).toLowerCase());
|
||||
let pickBar = null;
|
||||
if (isSoftware && values.some(v => v !== 'None' && v !== BLANK)) {
|
||||
pickBar = el('div', 'xf-picks');
|
||||
menu.appendChild(pickBar);
|
||||
}
|
||||
|
||||
const all = mkRow('(Select All)', true, 'xf-all');
|
||||
const opts = values.map(v => ({ v, ...mkRow(v, !current || current.has(v)) }));
|
||||
const opts = values.map(v => {
|
||||
const o = { v, ...mkRow(v, !current || current.has(v)) };
|
||||
if (isSoftware && isDocumented(v)) {
|
||||
o.row.classList.add('xf-doc');
|
||||
o.row.title = 'Has a documentation article';
|
||||
}
|
||||
if (v === 'None') o.row.classList.add('xf-none');
|
||||
return o;
|
||||
});
|
||||
if (!values.length) list.appendChild(el('div', 'xf-empty', 'No values'));
|
||||
menu.appendChild(list);
|
||||
|
||||
@@ -403,6 +423,24 @@
|
||||
};
|
||||
all.cb.onchange = () => { visibleOpts().forEach(o => { o.cb.checked = all.cb.checked; }); sync(); };
|
||||
opts.forEach(o => { o.cb.onchange = sync; });
|
||||
if (pickBar) {
|
||||
const pick = (label, test) => {
|
||||
const n = opts.filter(o => o.v !== 'None' && o.v !== BLANK && test(o.v)).length;
|
||||
const b = el('button', 'xf-pick'); b.type = 'button';
|
||||
b.textContent = `${label} (${n})`;
|
||||
b.title = `Tick only the apps that ${label === 'Documented' ? 'have' : "don't have"} a documentation article`;
|
||||
b.disabled = n === 0;
|
||||
b.onclick = () => {
|
||||
search.value = '';
|
||||
opts.forEach(o => { o.row.hidden = false; o.cb.checked = o.v !== 'None' && o.v !== BLANK && test(o.v); });
|
||||
all.row.querySelector('span').textContent = '(Select All)';
|
||||
sync();
|
||||
};
|
||||
pickBar.appendChild(b);
|
||||
};
|
||||
pick('Documented', isDocumented);
|
||||
pick('Undocumented', v => !isDocumented(v));
|
||||
}
|
||||
search.oninput = () => {
|
||||
const q = search.value.trim().toLowerCase();
|
||||
// Excel: searching shows the matching items, all ticked
|
||||
@@ -558,6 +596,12 @@
|
||||
wrap('filterCustomTab', () => afterFilterPass(document.getElementById('table-custom')));
|
||||
wrap('refreshStackTable', id => afterFilterPass(document.getElementById(`table-${id}`)));
|
||||
wrap('resetCustomFilters', () => { delete filters.custom; filterCustomTab(); });
|
||||
// Custom View's old filters (saved in the browser by earlier versions) have no UI any
|
||||
// more, so they're cleared rather than left silently hiding rows
|
||||
if (typeof customFilters !== 'undefined' && customFilters && Object.keys(customFilters).length) {
|
||||
customFilters = {};
|
||||
if (typeof saveCustomState === 'function') saveCustomState();
|
||||
}
|
||||
// Custom View's own filter icon opens the Excel-style menu instead
|
||||
window.openColFilter = (colId, idx, anchor) => openMenu(document.getElementById('table-custom'), idx, anchor);
|
||||
|
||||
|
||||
Reference in new issue
Block a user