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:
@ -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' });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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('');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
Reference in New Issue
Block a user