diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index 2763f793e..b3769bdd0 100644 Binary files a/netbox/project-static/dist/netbox.js and b/netbox/project-static/dist/netbox.js differ diff --git a/netbox/project-static/dist/netbox.js.map b/netbox/project-static/dist/netbox.js.map index 6c6bf1d1f..8988e2e49 100644 Binary files a/netbox/project-static/dist/netbox.js.map and b/netbox/project-static/dist/netbox.js.map differ diff --git a/netbox/project-static/src/tomSelect.ts b/netbox/project-static/src/tomSelect.ts index 22cc6d707..74039db24 100644 --- a/netbox/project-static/src/tomSelect.ts +++ b/netbox/project-static/src/tomSelect.ts @@ -1,15 +1,124 @@ import { getElements } from './util'; -import { TomOption } from 'tom-select/src/types'; +import { RecursivePartial, TomInput, TomOption, TomSettings } from 'tom-select/src/types'; +import { addClasses } from 'tom-select/src/vanilla' +import queryString from 'query-string'; import TomSelect from 'tom-select'; +import type { Stringifiable } from 'query-string'; + +// Transitional +import { QueryFilter } from './select/api/types' class DynamicTomSelect extends TomSelect { - // Override load() to automatically clear any cached options. (Only options included - // in the API response should be present.) + /* + * Transitional code from APISelect + */ + private readonly queryParams: QueryFilter = new Map(); + private readonly staticParams: QueryFilter = new Map(); + + /** + * Overrides + */ + + constructor( input_arg: string|TomInput, user_settings: RecursivePartial ) { + super(input_arg, user_settings); + + this.api_url = this.input.getAttribute('data-url') as string; + + // Populate static query parameters. + this.getStaticParams(); + for (const [key, value] of this.staticParams.entries()) { + this.queryParams.set(key, value); + } + } + load(value: string) { - this.clearOptions(); - super.load(value); + const self = this; + + // Automatically clear any cached options. (Only options included + // in the API response should be present.) + if (value) { + self.clearOptions(); + } + + addClasses(self.wrapper, self.settings.loadingClass); + self.loading++; + + const callback = self.loadCallback.bind(self); + const url = self.getRequestUrl(value); + + // Make the API request + fetch(url) + .then(response => response.json()) + .then(json => { + callback(json.results); + }).catch(()=>{ + callback(); + }); + + } + + /** + * Custom methods + */ + + /** + * Formulate and return the complete URL for an API request, including any query parameters. + */ + getRequestUrl(search: string): string { + const url = this.api_url; + + // Create new URL query parameters based on the current state of `queryParams` and create an + // updated API query URL. + const query = {} as Dict; + for (const [key, value] of this.queryParams.entries()) { + query[key] = value; + } + + // Append the search query, if any + if (search) { + query['q'] = [search]; + } + + // Enable "brief" mode + query['brief'] = ['True']; + + return queryString.stringifyUrl({ url, query }); + } + + /** + * Transitional methods + */ + + /** + * Determine if this instance's options should be filtered by static values passed from the + * server. + * + * Looks for the DOM attribute `data-static-params`, the value of which is a JSON array of + * objects containing key/value pairs to add to `this.staticParams`. + */ + private getStaticParams(): void { + const serialized = this.input.getAttribute('data-static-params'); + + try { + if (serialized) { + const deserialized = JSON.parse(serialized); + if (deserialized) { + for (const { queryParam, queryValue } of deserialized) { + if (Array.isArray(queryValue)) { + this.staticParams.set(queryParam, queryValue); + } else { + this.staticParams.set(queryParam, [queryValue]); + } + } + } + } + } catch (err) { + console.group(`Unable to determine static query parameters for select field '${this.name}'`); + console.warn(err); + console.groupEnd(); + } } } @@ -29,7 +138,6 @@ function initStaticSelects(): void { function initDynamicSelects(): void { for (const select of getElements('select.api-select')) { - const api_url = select.getAttribute('data-url') as string; new DynamicTomSelect(select, { plugins: ['clear_button'], valueField: 'id', @@ -39,16 +147,6 @@ function initDynamicSelects(): void { dropdownParent: 'body', controlInput: '', preload: 'focus', - load: function(query: string, callback: Function) { - let url = api_url + '?brief=True&q=' + encodeURIComponent(query); - fetch(url) - .then(response => response.json()) - .then(json => { - callback(json.results); - }).catch(()=>{ - callback(); - }); - }, }); }