mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-17 21:18:16 -06:00
Remove obsolete APISelect code
This commit is contained in:
parent
d164833c6e
commit
bed47a4f59
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.
@ -1,7 +1,7 @@
|
||||
import { isTruthy } from '../../util';
|
||||
import { isDataDynamicParams } from '../../select_old/api/types';
|
||||
import { isDataDynamicParams } from '../types';
|
||||
|
||||
import type { QueryParam } from '../../select_old/api/types';
|
||||
import type { QueryParam } from '../types';
|
||||
|
||||
/**
|
||||
* Extension of built-in `Map` to add convenience functions.
|
||||
|
@ -6,7 +6,7 @@ import type { Stringifiable } from 'query-string';
|
||||
import { DynamicParamsMap } from './dynamicParamsMap';
|
||||
|
||||
// Transitional
|
||||
import { QueryFilter, PathFilter } from '../../select_old/api/types'
|
||||
import { QueryFilter, PathFilter } from '../types'
|
||||
import { getElement, replaceAll } from '../../util';
|
||||
|
||||
|
||||
|
66
netbox/project-static/src/select/types.ts
Normal file
66
netbox/project-static/src/select/types.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import type { Stringifiable } from 'query-string';
|
||||
|
||||
/**
|
||||
* Map of string keys to primitive array values accepted by `query-string`. Keys are used as
|
||||
* URL query parameter keys. Values correspond to query param values, enforced as an array
|
||||
* for easier handling. For example, a mapping of `{ site_id: [1, 2] }` is serialized by
|
||||
* `query-string` as `?site_id=1&site_id=2`. Likewise, `{ site_id: [1] }` is serialized as
|
||||
* `?site_id=1`.
|
||||
*/
|
||||
export type QueryFilter = Map<string, Stringifiable[]>;
|
||||
|
||||
/**
|
||||
* JSON data structure from `data-dynamic-params` attribute.
|
||||
*/
|
||||
export type DataDynamicParam = {
|
||||
/**
|
||||
* Name of form field to track.
|
||||
*
|
||||
* @example [name="tenant_group"]
|
||||
*/
|
||||
fieldName: string;
|
||||
/**
|
||||
* Query param key.
|
||||
*
|
||||
* @example group_id
|
||||
*/
|
||||
queryParam: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* `queryParams` Map value.
|
||||
*/
|
||||
export type QueryParam = {
|
||||
queryParam: string;
|
||||
queryValue: Stringifiable[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Map of string keys to primitive values. Used to track variables within URLs from the server. For
|
||||
* example, `/api/$key/thing`. `PathFilter` tracks `$key` as `{ key: '' }` in the map, and when the
|
||||
* value is later known, the value is set — `{ key: 'value' }`, and the URL is transformed to
|
||||
* `/api/value/thing`.
|
||||
*/
|
||||
export type PathFilter = Map<string, Stringifiable>;
|
||||
|
||||
/**
|
||||
* Strict Type Guard to determine if a deserialized value from the `data-dynamic-params` attribute
|
||||
* is of type `DataDynamicParam[]`.
|
||||
*
|
||||
* @param value Deserialized value from `data-dynamic-params` attribute.
|
||||
*/
|
||||
export function isDataDynamicParams(value: unknown): value is DataDynamicParam[] {
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
if ('fieldName' in item && 'queryParam' in item) {
|
||||
return (
|
||||
typeof (item as DataDynamicParam).fieldName === 'string' &&
|
||||
typeof (item as DataDynamicParam).queryParam === 'string'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,76 +0,0 @@
|
||||
import { isTruthy } from '../../util';
|
||||
import { isDataDynamicParams } from './types';
|
||||
|
||||
import type { QueryParam } from './types';
|
||||
|
||||
/**
|
||||
* Extension of built-in `Map` to add convenience functions.
|
||||
*/
|
||||
export class DynamicParamsMap extends Map<string, QueryParam> {
|
||||
/**
|
||||
* Get the query parameter key based on field name.
|
||||
*
|
||||
* @param fieldName Related field name.
|
||||
* @returns `queryParam` key.
|
||||
*/
|
||||
public queryParam(fieldName: string): Nullable<QueryParam['queryParam']> {
|
||||
const value = this.get(fieldName);
|
||||
if (typeof value !== 'undefined') {
|
||||
return value.queryParam;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query parameter value based on field name.
|
||||
*
|
||||
* @param fieldName Related field name.
|
||||
* @returns `queryValue` value, or an empty array if there is no corresponding Map entry.
|
||||
*/
|
||||
public queryValue(fieldName: string): QueryParam['queryValue'] {
|
||||
const value = this.get(fieldName);
|
||||
if (typeof value !== 'undefined') {
|
||||
return value.queryValue;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the value of a field when the value changes.
|
||||
*
|
||||
* @param fieldName Related field name.
|
||||
* @param queryValue New value.
|
||||
* @returns `true` if the update was successful, `false` if there was no corresponding Map entry.
|
||||
*/
|
||||
public updateValue(fieldName: string, queryValue: QueryParam['queryValue']): boolean {
|
||||
const current = this.get(fieldName);
|
||||
if (isTruthy(current)) {
|
||||
const { queryParam } = current;
|
||||
this.set(fieldName, { queryParam, queryValue });
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the underlying map based on the JSON passed in the `data-dynamic-params` attribute.
|
||||
*
|
||||
* @param json Raw JSON string from `data-dynamic-params` attribute.
|
||||
*/
|
||||
public addFromJson(json: string | null | undefined): void {
|
||||
if (isTruthy(json)) {
|
||||
const deserialized = JSON.parse(json);
|
||||
// Ensure the value is the data structure we expect.
|
||||
if (isDataDynamicParams(deserialized)) {
|
||||
for (const { queryParam, fieldName } of deserialized) {
|
||||
// Populate the underlying map with the initial data.
|
||||
this.set(fieldName, { queryParam, queryValue: [] });
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
`Data from 'data-dynamic-params' attribute is improperly formatted: '${json}'`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
import { getElements } from '../../util';
|
||||
import { APISelect } from './apiSelect';
|
||||
|
||||
export function initApiSelect(): void {
|
||||
for (const select of getElements<HTMLSelectElement>('.netbox-api-select:not([data-ssid])')) {
|
||||
new APISelect(select);
|
||||
}
|
||||
}
|
||||
|
||||
export type { Trigger } from './types';
|
@ -1,199 +0,0 @@
|
||||
import type { Stringifiable } from 'query-string';
|
||||
import type { Option, Optgroup } from 'slim-select/dist/data';
|
||||
|
||||
/**
|
||||
* Map of string keys to primitive array values accepted by `query-string`. Keys are used as
|
||||
* URL query parameter keys. Values correspond to query param values, enforced as an array
|
||||
* for easier handling. For example, a mapping of `{ site_id: [1, 2] }` is serialized by
|
||||
* `query-string` as `?site_id=1&site_id=2`. Likewise, `{ site_id: [1] }` is serialized as
|
||||
* `?site_id=1`.
|
||||
*/
|
||||
export type QueryFilter = Map<string, Stringifiable[]>;
|
||||
|
||||
/**
|
||||
* Tracked data for a related field. This is the value of `APISelect.filterFields`.
|
||||
*/
|
||||
export type FilterFieldValue = {
|
||||
/**
|
||||
* Key to use in the query parameter itself.
|
||||
*/
|
||||
queryParam: string;
|
||||
/**
|
||||
* Value to use in the query parameter for the related field.
|
||||
*/
|
||||
queryValue: Stringifiable[];
|
||||
/**
|
||||
* @see `DataFilterFields.includeNull`
|
||||
*/
|
||||
includeNull: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* JSON data structure from `data-dynamic-params` attribute.
|
||||
*/
|
||||
export type DataDynamicParam = {
|
||||
/**
|
||||
* Name of form field to track.
|
||||
*
|
||||
* @example [name="tenant_group"]
|
||||
*/
|
||||
fieldName: string;
|
||||
/**
|
||||
* Query param key.
|
||||
*
|
||||
* @example group_id
|
||||
*/
|
||||
queryParam: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* `queryParams` Map value.
|
||||
*/
|
||||
export type QueryParam = {
|
||||
queryParam: string;
|
||||
queryValue: Stringifiable[];
|
||||
};
|
||||
|
||||
/**
|
||||
* JSON data structure from `data-static-params` attribute.
|
||||
*/
|
||||
export type DataStaticParam = {
|
||||
queryParam: string;
|
||||
queryValue: Stringifiable | Stringifiable[];
|
||||
};
|
||||
|
||||
/**
|
||||
* JSON data passed from Django on the `data-filter-fields` attribute.
|
||||
*/
|
||||
export type DataFilterFields = {
|
||||
/**
|
||||
* Related field form name (`[name="<fieldName>"]`)
|
||||
*
|
||||
* @example tenant_group
|
||||
*/
|
||||
fieldName: string;
|
||||
/**
|
||||
* Key to use in the query parameter itself.
|
||||
*
|
||||
* @example group_id
|
||||
*/
|
||||
queryParam: string;
|
||||
/**
|
||||
* Optional default value. If set, value will be added to the query parameters prior to the
|
||||
* initial API call and will be maintained until the field `fieldName` references (if one exists)
|
||||
* is updated with a new value.
|
||||
*
|
||||
* @example 1
|
||||
*/
|
||||
defaultValue: Nullable<Stringifiable | Stringifiable[]>;
|
||||
/**
|
||||
* Include `null` on queries for the related field. For example, if `true`, `?<fieldName>=null`
|
||||
* will be added to all API queries for this field.
|
||||
*/
|
||||
includeNull: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Map of string keys to primitive values. Used to track variables within URLs from the server. For
|
||||
* example, `/api/$key/thing`. `PathFilter` tracks `$key` as `{ key: '' }` in the map, and when the
|
||||
* value is later known, the value is set — `{ key: 'value' }`, and the URL is transformed to
|
||||
* `/api/value/thing`.
|
||||
*/
|
||||
export type PathFilter = Map<string, Stringifiable>;
|
||||
|
||||
/**
|
||||
* Merge or replace incoming options with current options.
|
||||
*/
|
||||
export type ApplyMethod = 'merge' | 'replace';
|
||||
|
||||
/**
|
||||
* Trigger for which the select instance should fetch its data from the NetBox API.
|
||||
*/
|
||||
export type Trigger =
|
||||
/**
|
||||
* Load data when the select element is opened.
|
||||
*/
|
||||
| 'open'
|
||||
/**
|
||||
* Load data when the element is loaded.
|
||||
*/
|
||||
| 'load'
|
||||
/**
|
||||
* Load data when a parent element is uncollapsed.
|
||||
*/
|
||||
| 'collapse';
|
||||
|
||||
/**
|
||||
* Strict Type Guard to determine if a deserialized value from the `data-filter-fields` attribute
|
||||
* is of type `DataFilterFields`.
|
||||
*
|
||||
* @param value Deserialized value from `data-filter-fields` attribute.
|
||||
*/
|
||||
export function isDataFilterFields(value: unknown): value is DataFilterFields[] {
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
if ('fieldName' in item && 'queryParam' in item) {
|
||||
return (
|
||||
typeof (item as DataFilterFields).fieldName === 'string' &&
|
||||
typeof (item as DataFilterFields).queryParam === 'string'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strict Type Guard to determine if a deserialized value from the `data-dynamic-params` attribute
|
||||
* is of type `DataDynamicParam[]`.
|
||||
*
|
||||
* @param value Deserialized value from `data-dynamic-params` attribute.
|
||||
*/
|
||||
export function isDataDynamicParams(value: unknown): value is DataDynamicParam[] {
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
if ('fieldName' in item && 'queryParam' in item) {
|
||||
return (
|
||||
typeof (item as DataDynamicParam).fieldName === 'string' &&
|
||||
typeof (item as DataDynamicParam).queryParam === 'string'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strict Type Guard to determine if a deserialized value from the `data-static-params` attribute
|
||||
* is of type `DataStaticParam[]`.
|
||||
*
|
||||
* @param value Deserialized value from `data-static-params` attribute.
|
||||
*/
|
||||
export function isStaticParams(value: unknown): value is DataStaticParam[] {
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
if ('queryParam' in item && 'queryValue' in item) {
|
||||
return (
|
||||
typeof (item as DataStaticParam).queryParam === 'string' &&
|
||||
typeof (item as DataStaticParam).queryValue !== 'undefined'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to determine if a SlimSelect `dataObject` is an `Option`.
|
||||
*
|
||||
* @param data Option or Option Group
|
||||
*/
|
||||
export function isOption(data: Option | Optgroup): data is Option {
|
||||
return !('options' in data);
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
import SlimSelect from 'slim-select';
|
||||
import { readableColor } from 'color2k';
|
||||
import { getElements } from '../util';
|
||||
|
||||
import type { Option } from 'slim-select/dist/data';
|
||||
|
||||
/**
|
||||
* Determine if the option has a valid value (i.e., is not the placeholder).
|
||||
*/
|
||||
function canChangeColor(option: Option | HTMLOptionElement): boolean {
|
||||
return typeof option.value === 'string' && option.value !== '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Style the container element based on the selected option value.
|
||||
*/
|
||||
function styleContainer(
|
||||
instance: InstanceType<typeof SlimSelect>,
|
||||
option: Option | HTMLOptionElement,
|
||||
): void {
|
||||
if (instance.slim.singleSelected !== null) {
|
||||
if (canChangeColor(option)) {
|
||||
// Get the background color from the selected option's value.
|
||||
const bg = `#${option.value}`;
|
||||
// Determine an accessible foreground color based on the background color.
|
||||
const fg = readableColor(bg);
|
||||
|
||||
// Set the container's style attributes.
|
||||
instance.slim.singleSelected.container.style.backgroundColor = bg;
|
||||
instance.slim.singleSelected.container.style.color = fg;
|
||||
} else {
|
||||
// If the color cannot be set (i.e., the placeholder), remove any inline styles.
|
||||
instance.slim.singleSelected.container.removeAttribute('style');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize color selection widget. Dynamically change the style of the select container to match
|
||||
* the selected option.
|
||||
*/
|
||||
export function initColorSelect(): void {
|
||||
for (const select of getElements<HTMLSelectElement>(
|
||||
'select.netbox-color-select:not([data-ssid])',
|
||||
)) {
|
||||
for (const option of select.options) {
|
||||
if (canChangeColor(option)) {
|
||||
// Get the background color from the option's value.
|
||||
const bg = `#${option.value}`;
|
||||
// Determine an accessible foreground color based on the background color.
|
||||
const fg = readableColor(bg);
|
||||
|
||||
// Set the option's style attributes.
|
||||
option.style.backgroundColor = bg;
|
||||
option.style.color = fg;
|
||||
}
|
||||
}
|
||||
|
||||
const instance = new SlimSelect({
|
||||
select,
|
||||
allowDeselect: true,
|
||||
// Inherit the calculated color on the deselect icon.
|
||||
deselectLabel: `<i class="mdi mdi-close-circle" style="color: currentColor;"></i>`,
|
||||
});
|
||||
|
||||
// Style the select container to match any pre-selectd options.
|
||||
for (const option of instance.data.data) {
|
||||
if ('selected' in option && option.selected) {
|
||||
styleContainer(instance, option);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't inherit the select element's classes.
|
||||
for (const className of select.classList) {
|
||||
instance.slim.container.classList.remove(className);
|
||||
}
|
||||
|
||||
// Change the SlimSelect container's style based on the selected option.
|
||||
instance.onChange = option => styleContainer(instance, option);
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
import { initApiSelect } from './api';
|
||||
import { initColorSelect } from './color';
|
||||
import { initStaticSelect } from './static';
|
||||
|
||||
export function initSelect(): void {
|
||||
for (const func of [initApiSelect, initColorSelect, initStaticSelect]) {
|
||||
func();
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
import SlimSelect from 'slim-select';
|
||||
import { getElements } from '../util';
|
||||
|
||||
export function initStaticSelect(): void {
|
||||
for (const select of getElements<HTMLSelectElement>('.netbox-static-select:not([data-ssid])')) {
|
||||
if (select !== null) {
|
||||
const label = document.querySelector(`label[for="${select.id}"]`) as HTMLLabelElement;
|
||||
|
||||
let placeholder;
|
||||
if (label !== null) {
|
||||
placeholder = `Select ${label.innerText.trim()}`;
|
||||
}
|
||||
|
||||
const instance = new SlimSelect({
|
||||
select,
|
||||
allowDeselect: true,
|
||||
deselectLabel: `<i class="mdi mdi-close-circle"></i>`,
|
||||
placeholder,
|
||||
});
|
||||
|
||||
// Don't copy classes from select element to SlimSelect instance.
|
||||
for (const className of select.classList) {
|
||||
instance.slim.container.classList.remove(className);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
import type { Trigger } from './api';
|
||||
|
||||
/**
|
||||
* Determine if an element has the `data-url` attribute set.
|
||||
*/
|
||||
export function hasUrl(el: HTMLSelectElement): el is HTMLSelectElement & { 'data-url': string } {
|
||||
const value = el.getAttribute('data-url');
|
||||
return typeof value === 'string' && value !== '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an element has the `data-query-param-exclude` attribute set.
|
||||
*/
|
||||
export function hasExclusions(
|
||||
el: HTMLSelectElement,
|
||||
): el is HTMLSelectElement & { 'data-query-param-exclude': string } {
|
||||
const exclude = el.getAttribute('data-query-param-exclude');
|
||||
return typeof exclude === 'string' && exclude !== '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a trigger value is valid.
|
||||
*/
|
||||
export function isTrigger(value: unknown): value is Trigger {
|
||||
return typeof value === 'string' && ['load', 'open', 'collapse'].includes(value);
|
||||
}
|
Loading…
Reference in New Issue
Block a user