pidgin/nest

17992eed4c1e
Parents 76f0f3877802
Children 6a1b26a72894
Adds client side filtering for plugin table
--- a/layouts/shortcodes/plugintable.html Mon Jan 14 18:57:59 2019 +0000
+++ b/layouts/shortcodes/plugintable.html Mon Jan 14 23:09:21 2019 +0000
@@ -1,21 +1,151 @@
-<table>
+<div id="plugin-filters">
+ <input type="text" id="plugin-filter-search" placeholder="Search plugins" />
+ <div id="protocol-selector"></div>
+</div>
+
+<table id="protocol-table">
<thead>
- <th>logo</th>
+ <th>logo</th>
<th>heading</th>
<th>info</th>
- <th>repo</th>
+ <th>
+ maintainer<br />
+ repo
+ </th>
</thead>
<tbody>
{{ range .Site.Data.plugins }}
-
- <tr>
- <td><img src="{{.logo}}" /></td>
- <td>{{.heading}}</td>
- <td><b>{{.type}}</b> {{.info | markdownify }}</td>
- <td>
- <a href="{{.repo}}">{{.maintainer}}</a>
- </td>
- </tr>
+ <tr data-type="{{.type}}">
+ <td>
+ <img src="{{.logo}}" />
+ </td>
+ <td class="pulgin-heading">
+ {{.heading}}
+ </td>
+ <td class="pulgin-info">
+ <b>{{.type}}</b> {{.info | markdownify}}
+ </td>
+ <td class="pulgin-repo">
+ <a href="{{.repo}}">{{.maintainer}}</a>
+ </td>
+ </tr>
{{ end }}
</tbody>
</table>
+
+<script>
+ document.addEventListener("DOMContentLoaded", () => {
+ const selectorsContainer = document.getElementById("protocol-selector");
+ const search = document.getElementById("plugin-filter-search");
+ search.addEventListener("input", debounce(1000 * 0.5, filterRows));
+ const [, ...rows] = (
+ document.getElementById("protocol-table")
+ .getElementsByTagName("tr")
+ );
+ const types = {};
+ const typeFilter = new Set();
+ const rowinfo = rows.map(elem => {
+ const type = elem.dataset.type;
+ if (type) {
+ if (!types[type]) types[type] = []
+
+ types[type].push(elem);
+ }
+
+ return {
+ elem,
+ head: getContents("pulgin-heading", elem),
+ info: getContents("pulgin-info", elem),
+ repo: getContents("pulgin-repo", elem),
+ }
+ })
+
+ Object.keys(types).forEach(type => {
+ const label = createAndAppend("label", selectorsContainer);
+ label.classList.add('pidgin-plugin-filter-checkbox')
+ const input = createAndAppend("input", label);
+ input.type = "checkbox";
+ input.dataset.type = type
+ input.addEventListener("click", clickEvent);
+ label.appendChild(document.createTextNode(type));
+ });
+
+ /////////////////////////
+
+ function getContents(className, elem = document){
+ return (
+ elem.getElementsByClassName(className)[0]
+ .textContent.toLowerCase()
+ )
+ }
+
+ function clickEvent({target}) {
+ typeFilter[target.checked ? "add" : "delete"](target.dataset.type);
+ filterRows();
+ }
+
+ function filterRows() {
+ const str = (search.value || "").toLowerCase()
+
+ rowinfo.forEach((row) => {
+ if (shouldFilter(row, str))
+ row.elem.classList.remove("filter-hide");
+ else
+ row.elem.classList.add("filter-hide");
+ });
+ }
+
+ function shouldFilter(row, str) {
+ return (
+ // Checkboxes
+ (!typeFilter.size || typeFilter.has(row.elem.dataset.type))
+ // Search box
+ && (
+ !str
+ || row.head.includes(str)
+ || row.info.includes(str)
+ || row.repo.includes(str)
+ )
+ )
+ }
+
+ function debounce(t, fn) {
+ let timer;
+
+ return function (...args) {
+ clearTimeout(timer);
+ timer = setTimeout(() => fn(...args), t);
+ };
+ }
+
+ function createAndAppend(tag, elem = document) {
+ return elem.appendChild(document.createElement(tag));
+ }
+ });
+</script>
+
+<style>
+ .filter-hide {
+ display: none;
+ }
+
+ #plugin-filters {
+ text-align: right;
+ }
+
+ #plugin-filter-search {
+ text-align: right;
+ max-width: 250px;
+ display: inline-block;
+ }
+
+ #protocol-selector label {
+ text-align: right;
+ display: inline-block;
+ }
+
+ #protocol-selector input {
+ margin: 8px;
+ display: inline-block;
+ }
+</style>
\ No newline at end of file