Просмотр исходного кода

properly handle select all confirmation on tables

checktheroads 4 лет назад
Родитель
Сommit
5bb7bf8947

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/config.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/config.js.map


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/jobs.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/jobs.js.map


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/lldp.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/lldp.js.map


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/netbox.css


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/netbox.css.map


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/netbox.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/netbox.js.map


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/status.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/status.js.map


+ 115 - 2
netbox/project-static/src/buttons.ts

@@ -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();
   }
 }

+ 31 - 24
netbox/templates/generic/object_list.html

@@ -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 %}

Некоторые файлы не были показаны из-за большого количества измененных файлов