Index: devtools-panel.js |
=================================================================== |
--- a/devtools-panel.js |
+++ b/devtools-panel.js |
@@ -23,17 +23,12 @@ |
(filterTypes) => |
{ |
let filterTypesElem = document.getElementById("filter-type"); |
- let filterStyleElem = document.createElement("style"); |
for (let type of filterTypes) |
{ |
- filterStyleElem.innerHTML += |
- `#items[data-filter-type=${type}] tr:not([data-type=${type}])` + |
- "{display: none;}"; |
let optionNode = document.createElement("option"); |
optionNode.appendChild(document.createTextNode(type)); |
filterTypesElem.appendChild(optionNode); |
} |
- document.body.appendChild(filterStyleElem); |
}); |
function generateFilter(request, domainSpecific) |
@@ -190,11 +185,85 @@ |
row.classList.remove("filtered-by-search"); |
} |
+/** |
Thomas Greiner
2018/02/28 18:18:08
In comparison to how we did the record filtering b
a.giammarchi
2018/02/28 19:07:13
before we were not doing anything, it was a hack w
|
+ * Return a generic <tr> placeholder |
+ * double linked with the <tr> itself to simplify |
+ * tbody swaps / filtering of undesired rows. |
+ * @param {HTMLTableRowElement} tr |
+ * @returns {Comment} |
+ */ |
+function getPlaceholder(tr) |
+{ |
+ let placeholder = tr.placeholder; |
+ if (!placeholder) |
+ { |
+ placeholder = document.createComment("placeholder"); |
+ tr.placeholder = placeholder; |
+ placeholder.placeholder = tr; |
+ } |
+ return placeholder; |
+} |
+ |
+/** |
+ * Put back all hidden table rows and eventually |
+ * filters those that do not match the state or type. |
+ * @param {HTMLTableSectionElement} tbody |
+ * @param {DOMStringMap} dataset |
+ */ |
+function updateRows(tbody, dataset) |
+{ |
+ // use filters to match nodes or retrieve them later on |
+ const selector = getCSSFilter(dataset); |
+ for (const node of tbody.childNodes) |
+ { |
+ // comments linked to table rows that |
+ // won't match selector, will be used |
+ // to place their linked row back in |
+ if ( |
+ node.nodeType === Node.COMMENT_NODE && |
+ (!selector || !node.placeholder.matches(selector)) |
+ ) |
+ { |
+ tbody.replaceChild(node.placeholder, node); |
+ } |
+ } |
+ |
+ // if there is a list of nodes to filter |
+ if (selector) |
+ { |
+ // every matched row should bre replaced with its placeholder |
+ for (const tr of tbody.querySelectorAll(selector)) |
+ { |
+ tbody.replaceChild(getPlaceholder(tr), tr); |
+ } |
+ } |
+} |
+ |
+function getCSSFilter(dataset) |
Thomas Greiner
2018/02/28 18:18:08
Detail: This name confused me a bit because (a) "f
a.giammarchi
2018/02/28 19:07:13
it actually returns a CSS selector that filters ro
|
+{ |
+ const {filterState, filterType} = dataset; |
+ |
+ // create a list of possible CSS filter |
+ // excluding #background-items from the list of rows to consider |
+ const query = []; |
+ if (filterState) |
+ { |
+ query.push(`tr:not(#background-items):not([data-state="${filterState}"])`); |
+ } |
+ if (filterType) |
+ { |
+ query.push(`tr:not(#background-items):not([data-type="${filterType}"])`); |
+ } |
+ return query.join(","); |
+} |
+ |
document.addEventListener("DOMContentLoaded", () => |
{ |
let container = document.getElementById("items"); |
let table = container.querySelector("tbody"); |
+ let bgItems = container.querySelector("#background-items"); |
let template = document.querySelector("template").content.firstElementChild; |
+ const records = []; |
document.getElementById("reload").addEventListener("click", () => |
{ |
@@ -204,11 +273,13 @@ |
document.getElementById("filter-state").addEventListener("change", (event) => |
{ |
container.dataset.filterState = event.target.value; |
+ updateRows(table, container.dataset); |
}, false); |
document.getElementById("filter-type").addEventListener("change", (event) => |
{ |
container.dataset.filterType = event.target.value; |
+ updateRows(table, container.dataset); |
}, false); |
ext.onMessage.addListener((message) => |
@@ -216,26 +287,44 @@ |
switch (message.type) |
{ |
case "add-record": |
- table.appendChild(createRecord(message.request, message.filter, |
- template)); |
+ let record = createRecord(message.request, message.filter, template); |
+ records.push(record); |
+ // avoid pushing rows that are being filtered |
+ // and use their placeholder when that is the case |
+ let selector = getCSSFilter(container.dataset); |
+ let node = (!selector || !record.matches(selector)) ? |
+ record : getPlaceholder(record); |
+ if (message.request.docDomain == null) |
Thomas Greiner
2018/02/28 18:18:08
Note: We'll be able to check `message.request.page
a.giammarchi
2018/02/28 19:07:13
then I prefer making that one a blocking of this o
|
+ { |
+ table.appendChild(node); |
+ } |
+ else |
+ { |
+ table.insertBefore(node, bgItems); |
+ } |
break; |
case "update-record": |
- let oldRow = table.getElementsByTagName("tr")[message.index]; |
+ let oldRow = records[message.index]; |
let newRow = createRecord(message.request, message.filter, template); |
- oldRow.parentNode.replaceChild(newRow, oldRow); |
+ records[message.index] = newRow; |
+ let liveRow = oldRow.parentNode ? oldRow : oldRow.placeholder; |
+ liveRow.parentNode.replaceChild(newRow, liveRow); |
newRow.classList.add("changed"); |
container.classList.add("has-changes"); |
break; |
case "remove-record": |
- let row = table.getElementsByTagName("tr")[message.index]; |
- row.parentNode.removeChild(row); |
+ let row = records.splice(message.index, 1)[0]; |
+ let live = row.parentNode ? row : row.placeholder; |
+ live.parentNode.removeChild(live); |
container.classList.add("has-changes"); |
break; |
case "reset": |
+ records.splice(0); |
table.innerHTML = ""; |
+ table.appendChild(bgItems); |
container.classList.remove("has-changes"); |
break; |
} |