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

#6372: Don't autopopulate collapsible API select data, improve advanced search

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

Разница между файлами не показана из-за своего большого размера
+ 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.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


+ 1 - 1
netbox/project-static/src/bs.ts

@@ -18,7 +18,7 @@ function initMasonry(): void {
 
 function initTooltips() {
   for (const tooltip of getElements('[data-bs-toggle="tooltip"]')) {
-    new Tooltip(tooltip, { container: 'body', boundary: 'window' });
+    new Tooltip(tooltip, { container: 'body' });
   }
 }
 

+ 45 - 11
netbox/project-static/src/select/api.ts

@@ -1,8 +1,15 @@
 import SlimSelect from 'slim-select';
 import queryString from 'query-string';
-import { getApiData, isApiError, getElements, isTruthy, hasError } from '../util';
+import {
+  getApiData,
+  isApiError,
+  getElements,
+  isTruthy,
+  hasError,
+  findFirstAdjacent,
+} from '../util';
 import { createToast } from '../bs';
-import { setOptionStyles, toggle, getDependencyIds } from './util';
+import { setOptionStyles, toggle, getDependencyIds, initResetButton } from './util';
 
 import type { Option } from 'slim-select/dist/data';
 
@@ -318,18 +325,45 @@ export function initApiSelect() {
         select.addEventListener(`netbox.select.onload.${dep}`, handleEvent);
       }
 
-      // Load data.
-      getOptions(url, select, disabledOptions)
-        .then(options => instance.setData(options))
-        .catch(console.error)
-        .finally(() => {
-          // Set option styles, if the field calls for it (color selectors).
+      /**
+       * Load this element's options from the NetBox API.
+       */
+      async function loadData(): Promise<void> {
+        try {
+          const options = await getOptions(url, select, disabledOptions);
+          instance.setData(options);
+        } catch (err) {
+          console.error(err);
+        } finally {
           setOptionStyles(instance);
-          // Enable the element after data has loaded.
           toggle('enable', instance);
-          // Inform any event listeners that data has updated.
           select.dispatchEvent(event);
-        });
+        }
+      }
+
+      /**
+       * Delete this element's options.
+       */
+      function clearData(): void {
+        return instance.setData([]);
+      }
+
+      // Determine if this element is part of collapsible element.
+      const collapse = findFirstAdjacent(select, '.collapse', '.content-container');
+      console.log('collapse', collapse);
+      if (collapse !== null) {
+        // If this element is part of a collapsible element, only load the data when the
+        // collapsible element is shown.
+        // See: https://getbootstrap.com/docs/5.0/components/collapse/#events
+        collapse.addEventListener('show.bs.collapse', loadData);
+        collapse.addEventListener('hide.bs.collapse', clearData);
+      } else {
+        // Otherwise, load the data on render.
+        Promise.all([loadData()]);
+      }
+
+      // Bind event listener to
+      initResetButton(select, instance);
 
       // Set the underlying select element to the same size as the SlimSelect instance.
       // This is primarily for built-in HTML form validation, which doesn't really work,

+ 21 - 0
netbox/project-static/src/select/util.ts

@@ -1,4 +1,5 @@
 import { readableColor } from 'color2k';
+import { findFirstAdjacent, getElements } from '../util';
 
 import type SlimSelect from 'slim-select';
 
@@ -184,3 +185,23 @@ export function getDependencyIds<E extends HTMLElement>(element: Nullable<E>): s
   const ids = new Set<string>(getAllDependencyIds(element));
   return Array.from(ids).map(i => i.replaceAll('_id', ''));
 }
+
+/**
+ * Initialize any adjacent reset buttons so that when clicked, the instance's selected value is cleared.
+ *
+ * @param select Select Element
+ * @param instance SlimSelect Instance
+ */
+export function initResetButton(select: HTMLSelectElement, instance: SlimSelect) {
+  const resetButton = findFirstAdjacent<HTMLButtonElement>(select, 'button[data-reset-select');
+  if (resetButton !== null) {
+    resetButton.addEventListener('click', () => {
+      select.value = '';
+      if (select.multiple) {
+        instance.setSelected([]);
+      } else {
+        instance.setSelected('');
+      }
+    });
+  }
+}

+ 11 - 1
netbox/project-static/src/util.ts

@@ -251,13 +251,23 @@ export function* getRowValues(table: HTMLTableRowElement): Generator<string> {
  *
  * @param base Base Element
  * @param query CSS Query
+ * @param boundary Optionally specify a CSS Query which, when matched, recursion will cease.
  */
 export function findFirstAdjacent<R extends HTMLElement, B extends Element = Element>(
   base: B,
   query: string,
+  boundary?: string,
 ): Nullable<R> {
+  function atBoundary<E extends Element | null>(element: E): boolean {
+    if (typeof boundary === 'string' && element !== null) {
+      if (element.matches(boundary)) {
+        return true;
+      }
+    }
+    return false;
+  }
   function match<P extends Element | null>(parent: P): Nullable<R> {
-    if (parent !== null && parent.parentElement !== null) {
+    if (parent !== null && parent.parentElement !== null && !atBoundary(parent)) {
       for (const child of parent.parentElement.querySelectorAll<R>(query)) {
         if (child !== null) {
           return child;

+ 6 - 3
netbox/templates/inc/advanced_search.html

@@ -50,9 +50,12 @@
                     {% endif %}
             </div>
             <div class="card-footer text-end noprint border-0">
-                <a href="." class="btn btn-sm btn-outline-dark m-1">
-                    <i class="mdi mdi-close"></i> Clear
-                </a>
+                <button type="button" class="btn btn-sm btn-outline-dark m-1" data-bs-toggle="collapse" data-bs-target="#advanced-search-content">
+                    <i class="mdi mdi-close"></i> Close
+                </button>
+                <button type="button" class="btn btn-sm btn-outline-danger m-1" data-reset-select>
+                    <i class="mdi mdi-backspace"></i> Reset
+                </button>
                 <button type="submit" class="btn btn-sm btn-primary m-1">
                     <i class="mdi mdi-magnify"></i> Search
                 </button>

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