Skip to content

Commit 9ef7071

Browse files
authored
Merge pull request #346 from rezen/alerts_plus
#244 Filtering for alerts
2 parents 9afa079 + ab63734 commit 9ef7071

File tree

2 files changed

+112
-18
lines changed

2 files changed

+112
-18
lines changed

site/layouts/alert/list.html

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,27 @@ <h1 class="text--white">{{ .Title }}</h1>
77
<div class="wrapper py-70">
88
{{ .Content }}
99
<div class="flex latest-versions">
10-
<table>
11-
<TR>
12-
<TH>Id</TH>
13-
<TH>Alert</TH>
14-
<TH>Status</TH>
15-
<TH>Risk</TH>
16-
<TH>Type</TH>
17-
</TR>
18-
{{ range (.Pages.ByParam "alertindex") }}
19-
<tr>
20-
<td><a href="{{ .Permalink }}">{{ .Params.alertid }}</a></td>
21-
<td><a href="{{ .Permalink }}">{{ .Title }}</a></td>
22-
<td>{{ .Params.status }}</td>
23-
<td>{{ .Params.risk }}</td>
24-
<td>{{ .Params.alerttype }}</td>
25-
</tr>
26-
</li>
27-
{{ end }}
10+
<table data-sort-filter>
11+
<thead>
12+
<tr>
13+
<th>Id</th>
14+
<th>Alert</th>
15+
<th data-suggest>Status</th>
16+
<th data-suggest>Risk</th>
17+
<th data-suggest>Type</th>
18+
</tr>
19+
</thead>
20+
<tbody>
21+
{{ range (.Pages.ByParam "alertindex") }}
22+
<tr>
23+
<td><a href="{{ .Permalink }}">{{ .Params.alertid }}</a></td>
24+
<td><a href="{{ .Permalink }}">{{ .Title }}</a></td>
25+
<td>{{ .Params.status }}</td>
26+
<td>{{ .Params.risk }}</td>
27+
<td>{{ .Params.alerttype }}</td>
28+
</tr>
29+
{{ end }}
30+
</tbody>
2831
</table>
2932
</div>
3033
</div>

src/index.js

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ new BadgerAccordion(".js-badger-accordion", {
88
openHeadersOnLoad: [0]
99
});
1010

11+
function removeAllChildNodes(parent) {
12+
while (parent.firstChild) {
13+
parent.removeChild(parent.firstChild);
14+
}
15+
}
16+
1117
document.addEventListener("DOMContentLoaded", function() {
1218
// Basic attempt to obfuscate emails
1319
Array.from(document.querySelectorAll("[data-mail]")).map(function(el) {
@@ -18,6 +24,91 @@ document.addEventListener("DOMContentLoaded", function() {
1824
}, {once: true});
1925
});
2026

27+
// Make tables filterable
28+
Array.from(document.querySelectorAll("[data-sort-filter]")).map(function(el) {
29+
const widget = {
30+
options: {},
31+
filters: {}
32+
};
33+
34+
// Checks if row matches against filter
35+
function isFilterMatch(row) {
36+
for (let index in widget.filters) {
37+
const filter = widget.filters[index].toLowerCase();
38+
const rowValue = row.columns[index].toLowerCase();
39+
if (rowValue.indexOf(filter) === -1) {
40+
return false;
41+
}
42+
}
43+
return true;
44+
}
45+
46+
// Create datalist that input can use for suggetions
47+
function setupDatalist(el, label, idx) {
48+
widget.options[idx] = document.createElement('datalist');
49+
widget.options[idx].setAttribute('id', 'opts_for_' + label);
50+
widget.options[idx]._options = []
51+
el.appendChild(widget.options[idx]);
52+
}
53+
54+
// Add input for filtering
55+
function addInput(el, label, idx) {
56+
const input = document.createElement('input');
57+
input.addEventListener("change", function(e) {
58+
widget.filters[idx] = e.target.value;
59+
removeAllChildNodes(tbody);
60+
rows.filter(isFilterMatch).map(r => {
61+
tbody.appendChild(r.el)
62+
});
63+
});
64+
input.setAttribute('style', 'width:100%;display:block')
65+
input.setAttribute('type', 'text');
66+
input.setAttribute('name', 'filter_' + label);
67+
input.setAttribute('list', 'opts_for_' + label);
68+
el.appendChild(input);
69+
}
70+
const tbody = el.querySelector('tbody');
71+
const headings = Array.from(el.querySelectorAll('thead th')).map((el, idx) => {
72+
const isSuggested = el.getAttribute("data-suggest") !== null;
73+
const label = el.innerText.toLowerCase();
74+
el.appendChild(document.createElement('br'));
75+
addInput(el, label, idx);
76+
77+
if (isSuggested) {
78+
setupDatalist(el, label, idx);
79+
}
80+
return {idx, isSuggested, label};
81+
});
82+
83+
const rows = Array.from(el.querySelectorAll('tbody tr')).map(tr => {
84+
const columns = Array.from(tr.querySelectorAll('td')).map((c, idx) => {
85+
// For columns that match the index of the `data-suggest` headers
86+
// ... add the text value to options
87+
if (widget.options[idx]) {
88+
widget.options[idx]._options.push(c.innerText)
89+
}
90+
return c.innerText;
91+
});
92+
return {
93+
el: tr, // Needed for writing to dom
94+
columns, // Needed for filtered
95+
};
96+
});
97+
98+
// Go through options elements and populate lists with column aggregates
99+
// gathered in previous loop
100+
Object.entries(widget.options).map(pair => {
101+
const [idx, el] = pair;
102+
const opts = [...new Set(el._options)];
103+
opts.sort((a, b) => a.length - b.length);
104+
opts.map(o => {
105+
const ol = document.createElement('option');
106+
ol.innerText = o;
107+
return ol;
108+
}).map(ol => el.appendChild(ol));
109+
});
110+
});
111+
21112
function clearFilter(menu) {
22113
menu.classList.remove("is-filtering");
23114
Array.from(menu.getElementsByTagName('li')).map(function(el) {

0 commit comments

Comments
 (0)