mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
Column action menu for sort/faceting, refs #981
This commit is contained in:
parent
5b8b8ae597
commit
ae1f7c3870
4 changed files with 121 additions and 1 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
72
datasette/static/table.js
Normal file
72
datasette/static/table.js
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
var DROPDOWN_HTML = `<div class="dropdown-menu">
|
||||
<div class="hook"></div>
|
||||
<ul>
|
||||
<li><a class="dropdown-sort-desc" href="#">Sort descending</a></li>
|
||||
<li><a class="dropdown-sort-asc" href="#">Sort ascending</a></li>
|
||||
<li><a class="dropdown-facet" href="#">Facet by this</a></li>
|
||||
</ul>
|
||||
</div>`;
|
||||
|
||||
var DROPDOWN_ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="3"></circle>
|
||||
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
|
||||
</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);
|
||||
});
|
||||
})();
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
<thead>
|
||||
<tr>
|
||||
{% for column in display_columns %}
|
||||
<th class="col-{{ column.name|to_css_class }}" scope="col">
|
||||
<th class="col-{{ column.name|to_css_class }}" scope="col" data-column="{{ column.name }}">
|
||||
{% if not column.sortable %}
|
||||
{{ column.name }}
|
||||
{% else %}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
{% block extra_head %}
|
||||
{{ super() }}
|
||||
<script src="{{ base_url }}-/static/table.js" defer></script>
|
||||
<style>
|
||||
@media only screen and (max-width: 576px) {
|
||||
{% for column in display_columns %}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue