mirror of
https://github.com/simonw/datasette.git
synced 2026-06-23 09:14:34 +02:00
Visual improvements to table filter UI
- Looks nicer - Add / remove buttons work properly Closes #2798
This commit is contained in:
parent
f831352551
commit
86ea1d4722
4 changed files with 411 additions and 86 deletions
|
|
@ -609,9 +609,6 @@ button.core[type=button] {
|
|||
border-color: #007bff;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
margin-bottom: 0.6em;
|
||||
}
|
||||
.search-row {
|
||||
margin-bottom: 1.8em;
|
||||
}
|
||||
|
|
@ -623,72 +620,239 @@ button.core[type=button] {
|
|||
width: 80px;
|
||||
}
|
||||
|
||||
.select-wrapper {
|
||||
border: 1px solid #ccc;
|
||||
width: 120px;
|
||||
border-radius: 3px;
|
||||
.filters .search-row {
|
||||
box-sizing: border-box;
|
||||
display: grid;
|
||||
grid-template-columns: max-content minmax(16rem, 1fr);
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
margin: 0 0 0.45rem;
|
||||
padding-right: var(--filter-two-icon-space);
|
||||
}
|
||||
|
||||
.filters .search-row label {
|
||||
width: auto;
|
||||
padding: 0;
|
||||
background-color: #fafafa;
|
||||
position: relative;
|
||||
color: var(--filter-muted);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.filters {
|
||||
--filter-ink: #0f0f0f;
|
||||
--filter-paper: #eef6ff;
|
||||
--filter-muted: #6b6b6b;
|
||||
--filter-rule: #d8e6f5;
|
||||
--filter-accent: #1a56db;
|
||||
--filter-control-border: #bfccd9;
|
||||
--filter-control-height: 2.125rem;
|
||||
--filter-control-gap: 0.4rem;
|
||||
--filter-row-icon-size: 2rem;
|
||||
--filter-two-icon-space: calc(
|
||||
(2 * var(--filter-row-icon-size)) + (2 * var(--filter-control-gap))
|
||||
);
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
gap: 0.55rem;
|
||||
max-width: 760px;
|
||||
margin: 0 0 1rem;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid var(--filter-rule);
|
||||
border-radius: 8px;
|
||||
background: var(--filter-paper);
|
||||
color: var(--filter-ink);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.filters .filter-row {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.filters .filter-controls-row {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
grid-template-columns:
|
||||
minmax(8rem, 0.75fr)
|
||||
minmax(6.5rem, 0.5fr)
|
||||
minmax(12rem, 1.2fr)
|
||||
var(--filter-row-icon-size)
|
||||
var(--filter-row-icon-size);
|
||||
gap: var(--filter-control-gap);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.filters .filter-actions-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.6rem;
|
||||
}
|
||||
|
||||
.select-wrapper {
|
||||
display: inline-block;
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
.select-wrapper:focus-within {
|
||||
border: 1px solid black;
|
||||
width: 120px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.select-wrapper.filter-op {
|
||||
width: 80px;
|
||||
}
|
||||
.select-wrapper::after {
|
||||
content: "\25BE";
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0.4em;
|
||||
color: #bbb;
|
||||
pointer-events: none;
|
||||
font-size: 1.2em;
|
||||
padding-top: 0.16em;
|
||||
|
||||
.filters .select-wrapper {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.select-wrapper select {
|
||||
padding: 9px 8px;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
background-image: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
height: var(--filter-control-height);
|
||||
border: 1px solid var(--filter-control-border, #ccc);
|
||||
border-radius: 5px;
|
||||
background-color: #fff;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25;
|
||||
padding: 7px 8px;
|
||||
}
|
||||
.select-wrapper select {
|
||||
font-size: 1em;
|
||||
font-family: Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.select-wrapper option {
|
||||
font-size: 1em;
|
||||
font-family: Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.select-wrapper select:focus {
|
||||
border-color: var(--filter-accent, #000);
|
||||
box-shadow: 0 0 0 2px rgba(26, 86, 219, 0.14);
|
||||
outline: none;
|
||||
}
|
||||
.filters {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
.filters input.filter-value {
|
||||
width: 200px;
|
||||
border-radius: 3px;
|
||||
-webkit-appearance: none;
|
||||
padding: 9px 4px;
|
||||
font-size: 16px;
|
||||
font-family: Helvetica, sans-serif;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
height: var(--filter-control-height);
|
||||
border: 1px solid var(--filter-control-border);
|
||||
border-radius: 5px;
|
||||
background: #fff;
|
||||
color: inherit;
|
||||
font-family: inherit;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25;
|
||||
padding: 7px 9px;
|
||||
}
|
||||
|
||||
.filters input.filter-value:focus {
|
||||
border-color: var(--filter-accent);
|
||||
box-shadow: 0 0 0 2px rgba(26, 86, 219, 0.14);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.filters input[type=submit] {
|
||||
border-color: var(--filter-accent);
|
||||
background: var(--filter-accent);
|
||||
border-radius: 5px;
|
||||
font-weight: 500;
|
||||
padding: 0.55rem 0.85rem;
|
||||
}
|
||||
|
||||
.filters input[type=submit]:hover,
|
||||
.filters input[type=submit]:focus {
|
||||
background: #1949b8;
|
||||
border-color: #1949b8;
|
||||
}
|
||||
|
||||
.filters button.filter-row-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
width: var(--filter-row-icon-size);
|
||||
height: var(--filter-row-icon-size);
|
||||
min-width: var(--filter-row-icon-size);
|
||||
padding: 0;
|
||||
border: 1px solid var(--filter-rule);
|
||||
border-radius: 5px;
|
||||
background: #fff;
|
||||
color: var(--filter-muted);
|
||||
font-family: inherit;
|
||||
font-size: 1.15rem;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.filters button.filter-row-icon[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.filters button.filter-row-icon:focus-visible {
|
||||
outline: 3px solid rgba(26, 86, 219, 0.14);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.filters .filter-row-remove-icon {
|
||||
display: block;
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
.filters button.filter-row-remove:hover,
|
||||
.filters button.filter-row-remove:focus {
|
||||
border-color: #c9d5e3;
|
||||
background: #f8fbff;
|
||||
color: var(--filter-ink);
|
||||
}
|
||||
|
||||
.filters button.filter-row-add {
|
||||
border-color: var(--filter-accent);
|
||||
background: var(--filter-accent);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.filters .filter-row-add-icon {
|
||||
display: block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.filters button.filter-row-add:hover,
|
||||
.filters button.filter-row-add:focus {
|
||||
border-color: #1949b8;
|
||||
background: #1949b8;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.filters button.filter-row-add:focus-visible svg {
|
||||
color: #fff;
|
||||
stroke: currentColor;
|
||||
}
|
||||
|
||||
#_search {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.filters #_search {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: var(--filter-control-height);
|
||||
border: 1px solid var(--filter-control-border);
|
||||
border-radius: 5px;
|
||||
background: #fff;
|
||||
color: var(--filter-ink);
|
||||
font: inherit;
|
||||
padding: 7px 9px;
|
||||
}
|
||||
|
||||
.filters #_search:focus {
|
||||
border-color: var(--filter-accent);
|
||||
box-shadow: 0 0 0 2px rgba(26, 86, 219, 0.14);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -3021,14 +3185,56 @@ select.table-alter-input {
|
|||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.select-wrapper {
|
||||
width: 100px;
|
||||
.filters {
|
||||
max-width: none;
|
||||
padding: 0.65rem;
|
||||
}
|
||||
.select-wrapper.filter-op {
|
||||
width: 60px;
|
||||
.filters .search-row {
|
||||
grid-template-columns: max-content minmax(0, 1fr);
|
||||
padding-right: 0;
|
||||
}
|
||||
.filters .filter-controls-row {
|
||||
--filter-value-button-space: 0px;
|
||||
--filter-remove-button-offset: 0px;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
.filters .filter-controls-row-one-button {
|
||||
--filter-value-button-space: calc(
|
||||
var(--filter-row-icon-size) + var(--filter-control-gap)
|
||||
);
|
||||
}
|
||||
.filters .filter-controls-row-two-buttons {
|
||||
--filter-value-button-space: var(--filter-two-icon-space);
|
||||
--filter-remove-button-offset: calc(
|
||||
var(--filter-row-icon-size) + var(--filter-control-gap)
|
||||
);
|
||||
}
|
||||
.filters .filter-controls-row .select-wrapper {
|
||||
grid-row: 1;
|
||||
grid-column: 1 / 2;
|
||||
}
|
||||
.filters .filter-controls-row .select-wrapper.filter-op {
|
||||
grid-column: 2 / 3;
|
||||
}
|
||||
.filters input.filter-value {
|
||||
width: 140px;
|
||||
grid-row: 2;
|
||||
grid-column: 1 / 3;
|
||||
justify-self: start;
|
||||
width: calc(100% - var(--filter-value-button-space));
|
||||
}
|
||||
.filters button.filter-row-icon {
|
||||
grid-row: 2;
|
||||
grid-column: 1 / 3;
|
||||
justify-self: end;
|
||||
}
|
||||
.filters .filter-controls-row-two-buttons button.filter-row-remove:not([hidden]) {
|
||||
margin-right: var(--filter-remove-button-offset);
|
||||
}
|
||||
.filters .filter-actions-row {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.filters .filter-actions-row input[type=submit] {
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
button.choose-columns-mobile,
|
||||
button.table-insert-row,
|
||||
|
|
|
|||
|
|
@ -633,32 +633,151 @@ const initDatasetteTable = function (manager) {
|
|||
});
|
||||
};
|
||||
|
||||
/* Add x buttons to the filter rows */
|
||||
function addButtonsToFilterRows(manager) {
|
||||
var x = "✖";
|
||||
var rows = Array.from(
|
||||
document.querySelectorAll(manager.selectors.filterRow),
|
||||
function filterRowSelector(manager) {
|
||||
return manager.selectors.filterRows || manager.selectors.filterRow;
|
||||
}
|
||||
|
||||
function filterRowsWithControls(manager) {
|
||||
return Array.from(
|
||||
document.querySelectorAll(filterRowSelector(manager)),
|
||||
).filter((el) => el.querySelector(".filter-op"));
|
||||
rows.forEach((row) => {
|
||||
var a = document.createElement("a");
|
||||
a.setAttribute("href", "#");
|
||||
a.setAttribute("aria-label", "Remove this filter");
|
||||
a.style.textDecoration = "none";
|
||||
a.innerText = x;
|
||||
a.addEventListener("click", (ev) => {
|
||||
ev.preventDefault();
|
||||
let row = ev.target.closest("div");
|
||||
row.querySelector("select").value = "";
|
||||
row.querySelector(".filter-op select").value = "exact";
|
||||
row.querySelector("input.filter-value").value = "";
|
||||
ev.target.closest("a").style.display = "none";
|
||||
});
|
||||
row.appendChild(a);
|
||||
}
|
||||
|
||||
function filterRowNumberFromName(name) {
|
||||
var match = name && name.match(/^_filter_column_(\d+)$/);
|
||||
return match ? parseInt(match[1], 10) : 0;
|
||||
}
|
||||
|
||||
function nextFilterRowNumber(manager) {
|
||||
return filterRowsWithControls(manager).reduce((max, row) => {
|
||||
var column = row.querySelector("select");
|
||||
if (!column.value) {
|
||||
a.style.display = "none";
|
||||
return Math.max(max, filterRowNumberFromName(column && column.name));
|
||||
}, 0) + 1;
|
||||
}
|
||||
|
||||
function setFilterRowNumber(row, number) {
|
||||
row.querySelector("select").name = `_filter_column_${number}`;
|
||||
row.querySelector(".filter-op select").name = `_filter_op_${number}`;
|
||||
row.querySelector("input.filter-value").name = `_filter_value_${number}`;
|
||||
}
|
||||
|
||||
function resetFilterRow(row) {
|
||||
row.querySelector("select").value = "";
|
||||
row.querySelector(".filter-op select").value = "exact";
|
||||
row.querySelector("input.filter-value").value = "";
|
||||
}
|
||||
|
||||
function updateFilterRowButtons(manager) {
|
||||
var rows = filterRowsWithControls(manager);
|
||||
rows.forEach((row, index) => {
|
||||
var removeButton = row.querySelector(".filter-row-remove");
|
||||
var addButton = row.querySelector(".filter-row-add");
|
||||
var column = row.querySelector("select");
|
||||
if (removeButton) {
|
||||
removeButton.hidden = index === 0;
|
||||
}
|
||||
if (addButton) {
|
||||
addButton.hidden = index !== rows.length - 1 || !column.value;
|
||||
}
|
||||
var visibleButtonCount = [removeButton, addButton].filter(function (button) {
|
||||
return button && !button.hidden;
|
||||
}).length;
|
||||
row.classList.toggle(
|
||||
"filter-controls-row-has-buttons",
|
||||
visibleButtonCount > 0,
|
||||
);
|
||||
row.classList.toggle(
|
||||
"filter-controls-row-one-button",
|
||||
visibleButtonCount === 1,
|
||||
);
|
||||
row.classList.toggle(
|
||||
"filter-controls-row-two-buttons",
|
||||
visibleButtonCount === 2,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function cloneFilterRow(row) {
|
||||
var clone = row.cloneNode(true);
|
||||
clone.querySelector("select").name = "_filter_column";
|
||||
clone.querySelector(".filter-op select").name = "_filter_op";
|
||||
clone.querySelector("input.filter-value").name = "_filter_value";
|
||||
resetFilterRow(clone);
|
||||
clone.querySelectorAll(".filter-row-icon").forEach((button) => button.remove());
|
||||
return clone;
|
||||
}
|
||||
|
||||
var FILTER_REMOVE_ICON_SVG = `<svg class="filter-row-remove-icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.1" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M3 6h18"></path>
|
||||
<path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
||||
<path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"></path>
|
||||
<path d="M10 11v6"></path>
|
||||
<path d="M14 11v6"></path>
|
||||
</svg>`;
|
||||
|
||||
var FILTER_ADD_ICON_SVG = `<svg class="filter-row-add-icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M5 12h14"></path>
|
||||
<path d="M12 5v14"></path>
|
||||
</svg>`;
|
||||
|
||||
function addFilterRowButtons(row, manager) {
|
||||
var removeButton = document.createElement("button");
|
||||
removeButton.type = "button";
|
||||
removeButton.className = "filter-row-icon filter-row-remove";
|
||||
removeButton.setAttribute("aria-label", "Remove this filter");
|
||||
removeButton.title = "Remove this filter";
|
||||
removeButton.tabIndex = 0;
|
||||
removeButton.innerHTML = FILTER_REMOVE_ICON_SVG;
|
||||
removeButton.addEventListener("click", (ev) => {
|
||||
var row = ev.currentTarget.closest(filterRowSelector(manager));
|
||||
var rows = filterRowsWithControls(manager);
|
||||
var rowIndex = rows.indexOf(row);
|
||||
var focusRow = rows[rowIndex + 1] || rows[rowIndex - 1] || null;
|
||||
row.remove();
|
||||
updateFilterRowButtons(manager);
|
||||
if (focusRow) {
|
||||
var focusTarget =
|
||||
focusRow.querySelector(".filter-row-add:not([hidden])") ||
|
||||
focusRow.querySelector("select");
|
||||
if (focusTarget) {
|
||||
focusTarget.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
row.appendChild(removeButton);
|
||||
|
||||
var addButton = document.createElement("button");
|
||||
addButton.type = "button";
|
||||
addButton.className = "filter-row-icon filter-row-add";
|
||||
addButton.setAttribute("aria-label", "Add another filter");
|
||||
addButton.title = "Add another filter";
|
||||
addButton.tabIndex = 0;
|
||||
addButton.innerHTML = FILTER_ADD_ICON_SVG;
|
||||
addButton.addEventListener("click", (ev) => {
|
||||
var row = ev.currentTarget.closest(filterRowSelector(manager));
|
||||
if (row.querySelector("select").name === "_filter_column") {
|
||||
setFilterRowNumber(row, nextFilterRowNumber(manager));
|
||||
}
|
||||
var clone = cloneFilterRow(row);
|
||||
addFilterRowButtons(clone, manager);
|
||||
row.parentNode.insertBefore(clone, row.nextSibling);
|
||||
updateFilterRowButtons(manager);
|
||||
clone.querySelector("select").focus();
|
||||
});
|
||||
row.appendChild(addButton);
|
||||
|
||||
row.querySelector("select").addEventListener("change", () => {
|
||||
updateFilterRowButtons(manager);
|
||||
});
|
||||
}
|
||||
|
||||
/* Add buttons to the filter rows */
|
||||
function addButtonsToFilterRows(manager) {
|
||||
var rows = filterRowsWithControls(manager);
|
||||
rows.forEach((row) => {
|
||||
addFilterRowButtons(row, manager);
|
||||
});
|
||||
updateFilterRowButtons(manager);
|
||||
}
|
||||
|
||||
/* Set up datalist autocomplete for filter values */
|
||||
|
|
@ -687,11 +806,11 @@ function initAutocompleteForFilterValues(manager) {
|
|||
});
|
||||
}
|
||||
createDataLists();
|
||||
// When any select with name=_filter_column changes, update the datalist
|
||||
// When any filter column select changes, update the datalist
|
||||
document.body.addEventListener("change", function (event) {
|
||||
if (event.target.name === "_filter_column") {
|
||||
if (event.target.name && event.target.name.startsWith("_filter_column")) {
|
||||
event.target
|
||||
.closest(manager.selectors.filterRow)
|
||||
.closest(filterRowSelector(manager))
|
||||
.querySelector(".filter-value")
|
||||
.setAttribute("list", "datalist-" + event.target.value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,9 +202,9 @@
|
|||
<h3>3 rows
|
||||
where characteristic_id = 2
|
||||
</h3>
|
||||
<form class="filters" action="{{ base_url }}fixtures/roadside_attraction_characteristics" method="get">
|
||||
<form class="core filters" action="{{ base_url }}fixtures/roadside_attraction_characteristics" method="get">
|
||||
<div class="search-row"><label for="_search">Search:</label><input id="_search" type="search" name="_search" value=""></div>
|
||||
<div class="filter-row">
|
||||
<div class="filter-row filter-controls-row">
|
||||
<div class="select-wrapper">
|
||||
<select name="_filter_column_1">
|
||||
<option value="">- remove filter -</option>
|
||||
|
|
@ -238,7 +238,7 @@
|
|||
</select>
|
||||
</div><input type="text" name="_filter_value_1" class="filter-value" value="2">
|
||||
</div>
|
||||
<div class="filter-row">
|
||||
<div class="filter-row filter-controls-row">
|
||||
<div class="select-wrapper">
|
||||
<select name="_filter_column">
|
||||
<option value="">- column -</option>
|
||||
|
|
@ -272,8 +272,8 @@
|
|||
</select>
|
||||
</div><input type="text" name="_filter_value" class="filter-value">
|
||||
</div>
|
||||
<div class="filter-row">
|
||||
<div class="select-wrapper small-screen-only">
|
||||
<div class="filter-row filter-actions-row">
|
||||
<div class="select-wrapper">
|
||||
<select name="_sort" id="sort_by">
|
||||
<option value="">Sort...</option>
|
||||
<option value="rowid" selected>Sort by rowid</option>
|
||||
|
|
@ -281,8 +281,8 @@
|
|||
<option value="characteristic_id">Sort by characteristic_id</option>
|
||||
</select>
|
||||
</div>
|
||||
<label class="sort_by_desc small-screen-only"><input type="checkbox" name="_sort_by_desc"> descending</label>
|
||||
<input type="submit" value="Apply">
|
||||
<label class="sort_by_desc"><input type="checkbox" name="_sort_by_desc"> descending</label>
|
||||
<input type="submit" value="Apply filters">
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
|
|
|||
|
|
@ -55,12 +55,12 @@
|
|||
</h3>
|
||||
{% endif %}
|
||||
|
||||
<form class="core" class="filters" action="{{ urls.table(database, table) }}" method="get">
|
||||
<form class="core filters" action="{{ urls.table(database, table) }}" method="get">
|
||||
{% if supports_search %}
|
||||
<div class="search-row"><label for="_search">Search:</label><input id="_search" type="search" name="_search" value="{{ search }}"></div>
|
||||
{% endif %}
|
||||
{% for column, lookup, value in filters.selections() %}
|
||||
<div class="filter-row">
|
||||
<div class="filter-row filter-controls-row">
|
||||
<div class="select-wrapper">
|
||||
<select name="_filter_column_{{ loop.index }}">
|
||||
<option value="">- remove filter -</option>
|
||||
|
|
@ -77,7 +77,7 @@
|
|||
</div><input type="text" name="_filter_value_{{ loop.index }}" class="filter-value" value="{{ value }}">
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="filter-row">
|
||||
<div class="filter-row filter-controls-row">
|
||||
<div class="select-wrapper">
|
||||
<select name="_filter_column">
|
||||
<option value="">- column -</option>
|
||||
|
|
@ -93,9 +93,9 @@
|
|||
</select>
|
||||
</div><input type="text" name="_filter_value" class="filter-value">
|
||||
</div>
|
||||
<div class="filter-row">
|
||||
<div class="filter-row filter-actions-row">
|
||||
{% if is_sortable %}
|
||||
<div class="select-wrapper small-screen-only">
|
||||
<div class="select-wrapper">
|
||||
<select name="_sort" id="sort_by">
|
||||
<option value="">Sort...</option>
|
||||
{% for column in display_columns %}
|
||||
|
|
@ -105,12 +105,12 @@
|
|||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<label class="sort_by_desc small-screen-only"><input type="checkbox" name="_sort_by_desc"{% if sort_desc %} checked{% endif %}> descending</label>
|
||||
<label class="sort_by_desc"><input type="checkbox" name="_sort_by_desc" tabindex="0"{% if sort_desc %} checked{% endif %}> descending</label>
|
||||
{% endif %}
|
||||
{% for key, value in form_hidden_args %}
|
||||
<input type="hidden" name="{{ key }}" value="{{ value }}">
|
||||
{% endfor %}
|
||||
<input type="submit" value="Apply">
|
||||
<input type="submit" value="Apply filters" tabindex="0">
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue