mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-27 19:08:38 -06:00
#6372: Don't autopopulate collapsible API select data, improve advanced search
This commit is contained in:
parent
715fa13c12
commit
ede5adfbbf
BIN
netbox/project-static/dist/config.js
vendored
BIN
netbox/project-static/dist/config.js
vendored
Binary file not shown.
BIN
netbox/project-static/dist/config.js.map
vendored
BIN
netbox/project-static/dist/config.js.map
vendored
Binary file not shown.
BIN
netbox/project-static/dist/jobs.js
vendored
BIN
netbox/project-static/dist/jobs.js
vendored
Binary file not shown.
BIN
netbox/project-static/dist/jobs.js.map
vendored
BIN
netbox/project-static/dist/jobs.js.map
vendored
Binary file not shown.
BIN
netbox/project-static/dist/lldp.js
vendored
BIN
netbox/project-static/dist/lldp.js
vendored
Binary file not shown.
BIN
netbox/project-static/dist/lldp.js.map
vendored
BIN
netbox/project-static/dist/lldp.js.map
vendored
Binary file not shown.
BIN
netbox/project-static/dist/netbox.js
vendored
BIN
netbox/project-static/dist/netbox.js
vendored
Binary file not shown.
BIN
netbox/project-static/dist/netbox.js.map
vendored
BIN
netbox/project-static/dist/netbox.js.map
vendored
Binary file not shown.
BIN
netbox/project-static/dist/status.js
vendored
BIN
netbox/project-static/dist/status.js
vendored
Binary file not shown.
BIN
netbox/project-static/dist/status.js.map
vendored
BIN
netbox/project-static/dist/status.js.map
vendored
Binary file not shown.
@ -18,7 +18,7 @@ function initMasonry(): void {
|
|||||||
|
|
||||||
function initTooltips() {
|
function initTooltips() {
|
||||||
for (const tooltip of getElements('[data-bs-toggle="tooltip"]')) {
|
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 SlimSelect from 'slim-select';
|
||||||
import queryString from 'query-string';
|
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 { createToast } from '../bs';
|
||||||
import { setOptionStyles, toggle, getDependencyIds } from './util';
|
import { setOptionStyles, toggle, getDependencyIds, initResetButton } from './util';
|
||||||
|
|
||||||
import type { Option } from 'slim-select/dist/data';
|
import type { Option } from 'slim-select/dist/data';
|
||||||
|
|
||||||
@ -318,18 +325,45 @@ export function initApiSelect() {
|
|||||||
select.addEventListener(`netbox.select.onload.${dep}`, handleEvent);
|
select.addEventListener(`netbox.select.onload.${dep}`, handleEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load data.
|
/**
|
||||||
getOptions(url, select, disabledOptions)
|
* Load this element's options from the NetBox API.
|
||||||
.then(options => instance.setData(options))
|
*/
|
||||||
.catch(console.error)
|
async function loadData(): Promise<void> {
|
||||||
.finally(() => {
|
try {
|
||||||
// Set option styles, if the field calls for it (color selectors).
|
const options = await getOptions(url, select, disabledOptions);
|
||||||
|
instance.setData(options);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
} finally {
|
||||||
setOptionStyles(instance);
|
setOptionStyles(instance);
|
||||||
// Enable the element after data has loaded.
|
|
||||||
toggle('enable', instance);
|
toggle('enable', instance);
|
||||||
// Inform any event listeners that data has updated.
|
|
||||||
select.dispatchEvent(event);
|
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.
|
// 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,
|
// This is primarily for built-in HTML form validation, which doesn't really work,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { readableColor } from 'color2k';
|
import { readableColor } from 'color2k';
|
||||||
|
import { findFirstAdjacent, getElements } from '../util';
|
||||||
|
|
||||||
import type SlimSelect from 'slim-select';
|
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));
|
const ids = new Set<string>(getAllDependencyIds(element));
|
||||||
return Array.from(ids).map(i => i.replaceAll('_id', ''));
|
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 base Base Element
|
||||||
* @param query CSS Query
|
* @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>(
|
export function findFirstAdjacent<R extends HTMLElement, B extends Element = Element>(
|
||||||
base: B,
|
base: B,
|
||||||
query: string,
|
query: string,
|
||||||
|
boundary?: string,
|
||||||
): Nullable<R> {
|
): 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> {
|
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)) {
|
for (const child of parent.parentElement.querySelectorAll<R>(query)) {
|
||||||
if (child !== null) {
|
if (child !== null) {
|
||||||
return child;
|
return child;
|
||||||
|
@ -50,9 +50,12 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer text-end noprint border-0">
|
<div class="card-footer text-end noprint border-0">
|
||||||
<a href="." class="btn btn-sm btn-outline-dark m-1">
|
<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> Clear
|
<i class="mdi mdi-close"></i> Close
|
||||||
</a>
|
</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">
|
<button type="submit" class="btn btn-sm btn-primary m-1">
|
||||||
<i class="mdi mdi-magnify"></i> Search
|
<i class="mdi mdi-magnify"></i> Search
|
||||||
</button>
|
</button>
|
||||||
|
Loading…
Reference in New Issue
Block a user