From ae1f7c3870001561392be85d2f4cd4d2aaf32d59 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Wed, 30 Sep 2020 14:43:39 -0700 Subject: [PATCH] Column action menu for sort/faceting, refs #981 --- datasette/static/app.css | 47 +++++++++++++++++++++ datasette/static/table.js | 72 +++++++++++++++++++++++++++++++++ datasette/templates/_table.html | 2 +- datasette/templates/table.html | 1 + 4 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 datasette/static/table.js diff --git a/datasette/static/app.css b/datasette/static/app.css index 8428a9337e..034d355f43 100644 --- a/datasette/static/app.css +++ b/datasette/static/app.css @@ -32,6 +32,7 @@ td em { } th { padding-right: 1em; + white-space: nowrap; } table a:link { text-decoration: none; @@ -386,3 +387,49 @@ button.button-as-link { cursor: pointer; font-size: 1em; } + +.dropdown-menu { + display: inline-flex; + border: 1px solid #ccc; + border-radius: 4px; + line-height: 1.4; + font-size: 16px; + box-shadow: 2px 2px 2px #aaa; + background-color: #fff; +} +.dropdown-menu ul, +.dropdown-menu li { + list-style-type: none; + margin: 0; + padding: 0; +} +.dropdown-menu li { + border-bottom: 1px solid #ccc; +} +.dropdown-menu li:last-child { + border: none; +} +.dropdown-menu a:link, +.dropdown-menu a:visited, +.dropdown-menu a:hover, +.dropdown-menu a:focus +.dropdown-menu a:active { + text-decoration: none; + display: block; + padding: 4px 8px 2px 8px; + color: #222; +} +.dropdown-menu a:hover { + background-color: #eee; +} +.dropdown-menu .hook { + display: block; + position: absolute; + top: -5px; + left: 6px; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 5px solid #666; +} diff --git a/datasette/static/table.js b/datasette/static/table.js new file mode 100644 index 0000000000..75cdd23c66 --- /dev/null +++ b/datasette/static/table.js @@ -0,0 +1,72 @@ +var DROPDOWN_HTML = ``; + +var DROPDOWN_ICON_SVG = ` + + +`; + +(function() { + function sortDescUrl(column) { + return '?_sort_desc=' + encodeURIComponent(column); + } + function sortAscUrl(column) { + return '?_sort=' + encodeURIComponent(column); + } + function facetUrl(column) { + return '?_facet=' + encodeURIComponent(column); + } + + function iconClicked(ev) { + ev.preventDefault(); + var th = ev.target; + while (th.nodeName != 'TH') { + th = th.parentNode; + } + var rect = th.getBoundingClientRect(); + var menuTop = rect.bottom + window.scrollY; + var menuLeft = rect.left + window.scrollX; + var column = th.getAttribute('data-column'); + menu.querySelector('a.dropdown-sort-desc').setAttribute('href', sortDescUrl(column)); + menu.querySelector('a.dropdown-sort-asc').setAttribute('href', sortAscUrl(column)); + /* Only show facet if it's not the first column */ + var isFirstColumn = th.parentElement.querySelector('th:first-of-type') == th; + var facetItem = menu.querySelector('a.dropdown-facet'); + if (isFirstColumn) { + facetItem.style.display = 'none'; + } else { + facetItem.style.display = 'block'; + facetItem.setAttribute('href', facetUrl(column)); + } + menu.style.position = 'absolute'; + menu.style.top = (menuTop + 6) + 'px'; + menu.style.left = menuLeft + 'px'; + menu.style.display = 'block'; + } + var svg = document.createElement('div'); + svg.innerHTML = DROPDOWN_ICON_SVG; + svg = svg.querySelector('*'); + svg.style.display = 'inline-block'; + svg.style.position = 'relative'; + svg.style.top = '1px'; + var menu = document.createElement('div'); + menu.innerHTML = DROPDOWN_HTML; + menu.style.position = 'absolute'; + menu.style.display = 'none'; + menu = menu.querySelector('*'); + document.body.appendChild(menu); + + var ths = Array.from(document.querySelectorAll('.rows-and-columns th')); + ths.forEach(el => { + var icon = svg.cloneNode(true); + icon.addEventListener('click', iconClicked); + icon.style.cursor = 'pointer'; + el.appendChild(icon); + }); +})(); diff --git a/datasette/templates/_table.html b/datasette/templates/_table.html index 8fee77b2be..1cb4110b7d 100644 --- a/datasette/templates/_table.html +++ b/datasette/templates/_table.html @@ -3,7 +3,7 @@ {% for column in display_columns %} - + {% if not column.sortable %} {{ column.name }} {% else %} diff --git a/datasette/templates/table.html b/datasette/templates/table.html index 6b8591329f..5474bf3574 100644 --- a/datasette/templates/table.html +++ b/datasette/templates/table.html @@ -5,6 +5,7 @@ {% block extra_head %} {{ super() }} +