1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

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

This commit is contained in:
checktheroads
2021-05-23 19:01:05 -07:00
parent 429d995270
commit e8c91ea3a1
15 changed files with 102 additions and 34 deletions

View File

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

View File

@ -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,

View File

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

View File

@ -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;