properly handle select all confirmation on tables

This commit is contained in:
checktheroads 2021-04-22 18:58:23 -07:00
parent 2c9aa86bf0
commit 5bb7bf8947
14 changed files with 147 additions and 27 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +1,13 @@
import { createToast } from './bs';
import { isTruthy, getElements, apiPatch, hasError, slugify } from './util';
import {
isTruthy,
getElements,
apiPatch,
hasError,
slugify,
findFirstAdjacent,
getElement,
} from './util';
/**
* Toggle the visibility of device images and update the toggle button style.
@ -123,8 +131,113 @@ function initReslug(): void {
});
}
/**
* Show the select all card when the select all checkbox is checked, and sync the checkbox state
* with all the PK checkboxes in the table.
*
* @param event Change Event
*/
function handleSelectAllToggle(event: Event) {
// Select all checkbox in header row.
const tableSelectAll = event.currentTarget as HTMLInputElement;
// Nearest table to the select all checkbox.
const table = findFirstAdjacent<HTMLInputElement>(tableSelectAll, 'table');
// Select all confirmation card.
const confirmCard = document.getElementById('select-all-box');
// Checkbox in confirmation card to signal if all objects should be selected.
const confirmCheckbox = document.getElementById('select-all') as Nullable<HTMLInputElement>;
if (table !== null) {
for (const element of table.querySelectorAll<HTMLInputElement>(
'input[type="checkbox"][name="pk"]',
)) {
if (tableSelectAll.checked) {
// Check all PK checkboxes if the select all checkbox is checked.
element.checked = true;
} else {
// Uncheck all PK checkboxes if the select all checkbox is unchecked.
element.checked = false;
}
}
if (confirmCard !== null) {
if (tableSelectAll.checked) {
// Unhide the select all confirmation card if the select all checkbox is checked.
confirmCard.classList.remove('d-none');
} else {
// Hide the select all confirmation card if the select all checkbox is unchecked.
confirmCard.classList.add('d-none');
if (confirmCheckbox !== null) {
// Uncheck the confirmation checkbox when the table checkbox is unchecked (after which
// the confirmation card will be hidden).
confirmCheckbox.checked = false;
}
}
}
}
}
/**
* If any PK checkbox is checked, uncheck the select all table checkbox and the select all
* confirmation checkbox.
*
* @param event Change Event
*/
function handlePkCheck(event: Event) {
const target = event.currentTarget as HTMLInputElement;
if (!target.checked) {
for (const element of getElements<HTMLInputElement>(
'input[type="checkbox"].toggle',
'input#select-all',
)) {
element.checked = false;
}
}
}
/**
* Synchronize the select all confirmation checkbox state with the select all confirmation button
* disabled state. If the select all confirmation checkbox is checked, the buttons should be
* enabled. If not, the buttons should be disabled.
*
* @param event Change Event
*/
function handleSelectAll(event: Event) {
const target = event.currentTarget as HTMLInputElement;
const selectAllBox = getElement<HTMLDivElement>('select-all-box');
if (selectAllBox !== null) {
for (const button of selectAllBox.querySelectorAll<HTMLButtonElement>(
'button[type="submit"]',
)) {
if (target.checked) {
button.disabled = false;
} else {
button.disabled = true;
}
}
}
}
/**
* Initialize table select all elements.
*/
function initSelectAll() {
for (const element of getElements<HTMLInputElement>(
'table tr th > input[type="checkbox"].toggle',
)) {
element.addEventListener('change', handleSelectAllToggle);
}
for (const element of getElements<HTMLInputElement>('input[type="checkbox"][name="pk"]')) {
element.addEventListener('change', handlePkCheck);
}
const selectAll = getElement<HTMLInputElement>('select-all');
if (selectAll !== null) {
selectAll.addEventListener('change', handleSelectAll);
}
}
export function initButtons() {
for (const func of [initRackElevation, initConnectionToggle, initReslug]) {
for (const func of [initRackElevation, initConnectionToggle, initReslug, initSelectAll]) {
func();
}
}

View File

@ -24,6 +24,37 @@
{% endblock %}
{% block content %}
{% if table.paginator.num_pages > 1 %}
{% with bulk_edit_url=content_type.model_class|validated_viewname:"bulk_edit" bulk_delete_url=content_type.model_class|validated_viewname:"bulk_delete" %}
<div class="row mb-3">
<form method="post" class="form col-md-12">
{% csrf_token %}
<div id="select-all-box" class="d-none card noprint">
<div class="card-body d-inline-flex justify-content-between align-items-center">
<div class="form-check">
<input type="checkbox" id="select-all" name="_all" class="form-check-input" />
<label for="select-all" class="form-check-label">
Select <strong>all {{ table.rows|length }} {{ table.data.verbose_name_plural }}</strong> Matching Query
</label>
</div>
<div class="float-end">
{% if bulk_edit_url and permissions.change %}
<button type="submit" name="_edit" formaction="{% url bulk_edit_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-warning btn-sm" disabled>
<span class="mdi mdi-pencil" aria-hidden="true"></span> Edit All
</button>
{% endif %}
{% if bulk_delete_url and permissions.delete %}
<button type="submit" name="_delete" formaction="{% url bulk_delete_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-danger btn-sm" disabled>
<span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete All
</button>
{% endif %}
</div>
</div>
</div>
</form>
</div>
{% endwith %}
{% endif %}
<div class="row mb-3">
<div class="{% if filter_form %}col-9{% else %}col-12{% endif %}">
<div class="card">
@ -45,30 +76,6 @@
<form method="post" class="form form-horizontal">
{% csrf_token %}
<input type="hidden" name="return_url" value="{% if return_url %}{{ return_url }}{% else %}{{ request.path }}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}{% endif %}" />
{% if table.paginator.num_pages > 1 %}
<div id="select_all_box" class="hidden card noprint">
<div class="card-body">
<div class="checkbox-inline">
<label for="select_all">
<input type="checkbox" id="select_all" name="_all" />
Select <strong>all {{ table.rows|length }} {{ table.data.verbose_name_plural }}</strong> Matching Query
</label>
</div>
<div class="float-end">
{% if bulk_edit_url and permissions.change %}
<button type="submit" name="_edit" formaction="{% url bulk_edit_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-warning btn-sm" disabled="disabled">
<span class="mdi mdi-pencil" aria-hidden="true"></span> Edit All
</button>
{% endif %}
{% if bulk_delete_url and permissions.delete %}
<button type="submit" name="_delete" formaction="{% url bulk_delete_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-danger btn-sm" disabled="disabled">
<span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete All
</button>
{% endif %}
</div>
</div>
</div>
{% endif %}
{% include table_template|default:'responsive_table.html' %}
<div class="float-start noprint bulk-buttons">
{% block bulk_buttons %}{% endblock %}