diff --git a/assets/css/app.css b/assets/css/app.css index 54de80e9d..ad5544a48 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -260,6 +260,21 @@ footer { .table tr:target { background: #EEE; } +table tr.exp { + display: none; +} +table tr.exp td { + padding: 0 1em; +} +.exp-indicator-close::before { + content: '▸'; +} +.exp-indicator-open::before { + content: '▾'; +} +table tr.exp:target { + display: table-row; +} .table tbody tr[role="row"]:hover { background: #EEE; } diff --git a/assets/js/app.js b/assets/js/app.js index 559170b95..e24f7a693 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -28,4 +28,5 @@ import "./alert" import "./socket" import "./drop-down-menu" import "./time-convert" +import "./expand-row" import "./pagination" diff --git a/assets/js/expand-row.js b/assets/js/expand-row.js new file mode 100644 index 000000000..c883bfc37 --- /dev/null +++ b/assets/js/expand-row.js @@ -0,0 +1,114 @@ +const items = document.querySelectorAll("td.expand-row"); +Array.prototype.forEach.call(items, convertExpandRow); + +window.addEventListener("resize", function() { + const items = document.querySelectorAll("td.expand-row"); + Array.prototype.forEach.call(items, convertExpandRow); + convertIndicator(); +}); + +window.addEventListener("popstate", convertIndicator); +window.addEventListener("DOMContentLoaded", convertIndicator); + +function convertIndicator() { + var h = Array.prototype.slice.call(document.querySelectorAll("[data-exp-hash]")); + var l = h.length; + for (var i = 0; i < l; ++i) { + h[i].classList.remove("exp-indicator-open"); + h[i].classList.add("exp-indicator-close"); + h[i].setAttribute("aria-expanded", "false"); + } + if (window.location.hash) { + h = document.querySelector("[data-exp-hash=\"" + window.location.hash + "\"]"); + if (h && h.hash === window.location.hash) { + h.classList.add("exp-indicator-open"); + h.classList.remove("exp-indicator-close"); + h.setAttribute("aria-expanded", "true"); + } + } +} + +function getNewID() { + let fullID; + do { + if (!window.borsExpNewID) { + window.borsExpNewID = 0; + } + window.borsExpNewID += 1; + fullID = "row-" + window.borsExpNewID.toString(); + } while (document.getElementById(fullID)); + return fullID; +} + +function convertExpandRow(td) { + if (td.borsExp) { + return; + } + var row = td.parentElement; + if (!row.id) { + row.id = getNewID(); + } + td.classList.add("fill-link"); + let item; + if (item = doExpandRow(td)) { + const a = document.createElement("a"); + a.href = item; + a.hash = item; + a.setAttribute("data-exp-hash", item); + a.addEventListener("click", function(e) { + if (window.location.hash === this.hash && window.history && window.history.state && window.history.state.borsExp) { + window.history.back(); + e.preventDefault(); + } else if (window.location.hash !== this.hash) { + window.location = this.hash; + window.history.replaceState({borsExp: true}, window.title); + e.preventDefault(); + } + convertIndicator(); + }); + while (item = td.firstChild) { + td.removeChild(item); + a.appendChild(item); + } + td.appendChild(a); + } +} + +function doExpandRow(td) { + const row = td.parentElement; + const table = row.parentElement.parentElement; + const headRow = table.querySelector("thead tr[role=\"row\"]"); + const headItems = Array.prototype.slice.call(headRow.children); + const colSpan = row.children.length; + const items = Array.prototype.slice.call(row.children); + const l = items.length; + const borsExp = document.createElement("tr"); + borsExp.className = "exp"; + const borsExpInternal = document.createElement("td"); + borsExp.appendChild(borsExpInternal); + borsExpInternal.setAttribute("colspan", l); + const borsExpDl = document.createElement("dl"); + borsExpInternal.appendChild(borsExpDl); + let foundOne = false; + for (var i = 0; i !== l; ++i) { + if (items[i].scrollHeight === 0) { + foundOne = true; + const dt = document.createElement("dt"); + dt.appendChild(document.createTextNode(headItems[i].innerText)); + const dd = document.createElement("dd"); + dd.appendChild(document.createTextNode(items[i].innerText)); + borsExpDl.appendChild(dt) + borsExpDl.appendChild(dd); + } + } + if (foundOne) { + row.parentElement.insertBefore(borsExp, row.nextElementSibling); + td.borsExp = borsExp; + borsExp.id = "exp-" + row.id; + return "#" + borsExp.id; + } else { + return false; + } +} + +export { convertExpandRow }; \ No newline at end of file diff --git a/assets/js/pagination.js b/assets/js/pagination.js index bf5300ee3..e13606f41 100644 --- a/assets/js/pagination.js +++ b/assets/js/pagination.js @@ -2,7 +2,7 @@ import { convertTime } from './time-convert.js'; var pathParts = window.location.pathname.match(/\/\d+\//); -if (pathParts.length > 0) { +if (pathParts && pathParts.length > 0) { activateRepo(pathParts[0]); } diff --git a/lib/web/templates/project/show.html.eex b/lib/web/templates/project/show.html.eex index e87d6fb9f..9866e7c2b 100644 --- a/lib/web/templates/project/show.html.eex +++ b/lib/web/templates/project/show.html.eex @@ -34,8 +34,8 @@ <%= for patch <- batch.patches do %> - - + + <%= patch.pr_xref %> ok<% else %>error<% end %> aria-label="<%= if patch.is_mergeable do %><%= if patch.is_draft do %>Draft<% else %>Yes<% end %><% else %>No<% end %>" title="<%= if patch.is_mergeable do %><%= if patch.is_draft do %>Draft<% else %>Yes<% end %><% else %>No<% end %>"> @@ -59,7 +59,7 @@ <%= if patch.is_single do %>S<% end %> - <%= truncate_commit(patch.commit) %> + <%= truncate_commit(patch.commit) %> <% end %> @@ -77,8 +77,8 @@ <%= for patch <- @unbatched_patches do %> - - + + <%= patch.pr_xref %> ok<% else %>error<% end %> aria-label="<%= if patch.is_mergeable do %><%= if patch.is_draft do %>Draft<% else %>Yes<% end %><% else %>No<% end %>" title="<%= if patch.is_mergeable do %><%= if patch.is_draft do %>Draft<% else %>Yes<% end %><% else %>No<% end %>"> @@ -102,7 +102,7 @@ <%= if patch.is_single do %>S<% end %> - <%= truncate_commit(patch.commit) %> + <%= truncate_commit(patch.commit) %> <% end %>