mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 09:51:22 -06:00
Fix eslint misconfiguration and corresponding errors
This commit is contained in:
parent
82a209bc5b
commit
a0ba8380c9
@ -6,7 +6,7 @@
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:prettier/recommended",
|
||||
"prettier/@typescript-eslint"
|
||||
"prettier"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"env": {
|
||||
@ -19,8 +19,7 @@
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"arrowFunctions": true
|
||||
},
|
||||
"project": "./tsconfig.json"
|
||||
}
|
||||
},
|
||||
"plugins": ["@typescript-eslint", "prettier"],
|
||||
"settings": {
|
||||
@ -35,7 +34,7 @@
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"@typescript-eslint/no-unused-vars-experimental": "error",
|
||||
"no-unused-vars": "off",
|
||||
|
||||
"no-inner-declarations": "off",
|
||||
"comma-dangle": ["error", "always-multiline"],
|
||||
"global-require": "off",
|
||||
"import/no-dynamic-require": "off",
|
||||
|
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.
@ -45,12 +45,16 @@ export function createToast(
|
||||
switch (level) {
|
||||
case 'warning':
|
||||
iconName = 'mdi-alert';
|
||||
break;
|
||||
case 'success':
|
||||
iconName = 'mdi-check-circle';
|
||||
break;
|
||||
case 'info':
|
||||
iconName = 'mdi-information';
|
||||
break;
|
||||
case 'danger':
|
||||
iconName = 'mdi-alert';
|
||||
break;
|
||||
}
|
||||
|
||||
const container = document.createElement('div');
|
||||
@ -109,7 +113,7 @@ export function createToast(
|
||||
*/
|
||||
function initTabs() {
|
||||
const { hash } = location;
|
||||
if (hash && hash.match(/^\#tab_.+$/)) {
|
||||
if (hash && hash.match(/^#tab_.+$/)) {
|
||||
// The tab element will have a data-bs-target attribute with a value of the object type for
|
||||
// the corresponding tab. Once we drop the `tab_` prefix, the hash will match the target
|
||||
// element's data-bs-target value. For example, `#tab_frontports` becomes `#frontports`.
|
||||
|
@ -21,7 +21,7 @@ type ObjectDepthState = { hidden: boolean };
|
||||
*
|
||||
* @param element Connection Toggle Button Element
|
||||
*/
|
||||
function toggleConnection(element: HTMLButtonElement) {
|
||||
function toggleConnection(element: HTMLButtonElement): void {
|
||||
const id = element.getAttribute('data');
|
||||
const connected = element.classList.contains('connected');
|
||||
const status = connected ? 'planned' : 'connected';
|
||||
@ -59,7 +59,7 @@ function toggleConnection(element: HTMLButtonElement) {
|
||||
}
|
||||
}
|
||||
|
||||
function initConnectionToggle() {
|
||||
function initConnectionToggle(): void {
|
||||
for (const element of getElements<HTMLButtonElement>('button.cable-toggle')) {
|
||||
element.addEventListener('click', () => toggleConnection(element));
|
||||
}
|
||||
@ -116,7 +116,7 @@ function handleDepthToggle(state: StateManager<ObjectDepthState>, button: HTMLBu
|
||||
/**
|
||||
* Initialize object depth toggle buttons.
|
||||
*/
|
||||
function initDepthToggle() {
|
||||
function initDepthToggle(): void {
|
||||
const initiallyHidden = objectDepthState.get('hidden');
|
||||
|
||||
for (const button of getElements<HTMLButtonElement>('button.toggle-depth')) {
|
||||
@ -190,7 +190,7 @@ function handlePreferenceSave(event: Event): void {
|
||||
/**
|
||||
* Initialize handlers for user profile updates.
|
||||
*/
|
||||
function initPreferenceUpdate() {
|
||||
function initPreferenceUpdate(): void {
|
||||
const form = getElement<HTMLFormElement>('preferences-update');
|
||||
if (form !== null) {
|
||||
form.addEventListener('submit', handlePreferenceSave);
|
||||
@ -203,7 +203,7 @@ function initPreferenceUpdate() {
|
||||
*
|
||||
* @param event Change Event
|
||||
*/
|
||||
function handleSelectAllToggle(event: Event) {
|
||||
function handleSelectAllToggle(event: Event): void {
|
||||
// Select all checkbox in header row.
|
||||
const tableSelectAll = event.currentTarget as HTMLInputElement;
|
||||
// Nearest table to the select all checkbox.
|
||||
@ -248,7 +248,7 @@ function handleSelectAllToggle(event: Event) {
|
||||
*
|
||||
* @param event Change Event
|
||||
*/
|
||||
function handlePkCheck(event: Event) {
|
||||
function handlePkCheck(event: Event): void {
|
||||
const target = event.currentTarget as HTMLInputElement;
|
||||
if (!target.checked) {
|
||||
for (const element of getElements<HTMLInputElement>(
|
||||
@ -267,7 +267,7 @@ function handlePkCheck(event: Event) {
|
||||
*
|
||||
* @param event Change Event
|
||||
*/
|
||||
function handleSelectAll(event: Event) {
|
||||
function handleSelectAll(event: Event): void {
|
||||
const target = event.currentTarget as HTMLInputElement;
|
||||
const selectAllBox = getElement<HTMLDivElement>('select-all-box');
|
||||
if (selectAllBox !== null) {
|
||||
@ -286,7 +286,7 @@ function handleSelectAll(event: Event) {
|
||||
/**
|
||||
* Initialize table select all elements.
|
||||
*/
|
||||
function initSelectAll() {
|
||||
function initSelectAll(): void {
|
||||
for (const element of getElements<HTMLInputElement>(
|
||||
'table tr th > input[type="checkbox"].toggle',
|
||||
)) {
|
||||
@ -302,20 +302,20 @@ function initSelectAll() {
|
||||
}
|
||||
}
|
||||
|
||||
function handlePerPageSelect(event: Event) {
|
||||
function handlePerPageSelect(event: Event): void {
|
||||
const select = event.currentTarget as HTMLSelectElement;
|
||||
if (select.form !== null) {
|
||||
select.form.submit();
|
||||
}
|
||||
}
|
||||
|
||||
function initPerPage() {
|
||||
function initPerPage(): void {
|
||||
for (const element of getElements<HTMLSelectElement>('select.per-page')) {
|
||||
element.addEventListener('change', handlePerPageSelect);
|
||||
}
|
||||
}
|
||||
|
||||
export function initButtons() {
|
||||
export function initButtons(): void {
|
||||
for (const func of [
|
||||
initDepthToggle,
|
||||
initConnectionToggle,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import Clipboard from 'clipboard';
|
||||
import { getElements } from './util';
|
||||
|
||||
export function initClipboard() {
|
||||
export function initClipboard(): void {
|
||||
for (const element of getElements('a.copy-token', 'button.copy-secret')) {
|
||||
new Clipboard(element);
|
||||
}
|
||||
|
@ -6,7 +6,10 @@ const TEXT_WHEN_LIGHT = 'Dark Mode';
|
||||
const ICON_WHEN_DARK = 'mdi-lightbulb-on';
|
||||
const ICON_WHEN_LIGHT = 'mdi-lightbulb';
|
||||
|
||||
function isColorMode(value: string): value is ColorMode {
|
||||
/**
|
||||
* Determine if a value is a supported color mode string value.
|
||||
*/
|
||||
function isColorMode(value: unknown): value is ColorMode {
|
||||
return value === 'dark' || value === 'light';
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ import { apiGetBase, getNetboxData, hasError, toggleLoader } from '../util';
|
||||
/**
|
||||
* Initialize device config elements.
|
||||
*/
|
||||
function initConfig() {
|
||||
function initConfig(): void {
|
||||
toggleLoader('show');
|
||||
const url = getNetboxData('data-object-url');
|
||||
|
||||
|
@ -31,7 +31,7 @@ function updateRowStyle(data: LLDPNeighborDetail) {
|
||||
|
||||
let cInterfaceShort = null;
|
||||
if (isTruthy(cInterface)) {
|
||||
cInterfaceShort = cInterface.replace(/^([A-Z][a-z])[^0-9]*([0-9\/]+)$/, '$1$2');
|
||||
cInterfaceShort = cInterface.replace(/^([A-Z][a-z])[^0-9]*([0-9/]+)$/, '$1$2');
|
||||
}
|
||||
|
||||
const nHost = neighbor.remote_system_name ?? '';
|
||||
|
@ -92,7 +92,7 @@ function getUptime(seconds: number): Uptime {
|
||||
*
|
||||
* @param facts NAPALM Device Facts
|
||||
*/
|
||||
function processFacts(facts: DeviceFacts) {
|
||||
function processFacts(facts: DeviceFacts): void {
|
||||
for (const key of factKeys) {
|
||||
if (key in facts) {
|
||||
// Find the target element which should have its innerHTML/innerText set to a NAPALM value.
|
||||
@ -149,7 +149,7 @@ function insertTitleRow<E extends HTMLElement>(next: E, title1: string, title2:
|
||||
* @param next Next adjacent element.For example, if this is the CPU data, `next` would be the
|
||||
* memory row.
|
||||
*/
|
||||
function insertNoneRow<E extends Nullable<HTMLElement>>(next: E) {
|
||||
function insertNoneRow<E extends Nullable<HTMLElement>>(next: E): void {
|
||||
const none = createElement('td', { colSpan: '2', innerText: 'No Data' }, [
|
||||
'text-muted',
|
||||
'text-center',
|
||||
@ -173,7 +173,7 @@ function getNext<E extends HTMLElement>(id: string): Nullable<E> {
|
||||
*
|
||||
* @param cpu NAPALM CPU data.
|
||||
*/
|
||||
function processCpu(cpu: DeviceEnvironment['cpu']) {
|
||||
function processCpu(cpu: DeviceEnvironment['cpu']): void {
|
||||
// Find the next adjacent element, so we can insert elements before it.
|
||||
const next = getNext<HTMLTableRowElement>('status-cpu');
|
||||
if (typeof cpu !== 'undefined') {
|
||||
@ -200,7 +200,7 @@ function processCpu(cpu: DeviceEnvironment['cpu']) {
|
||||
*
|
||||
* @param mem NAPALM memory data.
|
||||
*/
|
||||
function processMemory(mem: DeviceEnvironment['memory']) {
|
||||
function processMemory(mem: DeviceEnvironment['memory']): void {
|
||||
// Find the next adjacent element, so we can insert elements before it.
|
||||
const next = getNext<HTMLTableRowElement>('status-memory');
|
||||
if (typeof mem !== 'undefined') {
|
||||
@ -222,7 +222,7 @@ function processMemory(mem: DeviceEnvironment['memory']) {
|
||||
*
|
||||
* @param temp NAPALM temperature data.
|
||||
*/
|
||||
function processTemp(temp: DeviceEnvironment['temperature']) {
|
||||
function processTemp(temp: DeviceEnvironment['temperature']): void {
|
||||
// Find the next adjacent element, so we can insert elements before it.
|
||||
const next = getNext<HTMLTableRowElement>('status-temperature');
|
||||
if (typeof temp !== 'undefined') {
|
||||
@ -249,7 +249,7 @@ function processTemp(temp: DeviceEnvironment['temperature']) {
|
||||
*
|
||||
* @param fans NAPALM fan data.
|
||||
*/
|
||||
function processFans(fans: DeviceEnvironment['fans']) {
|
||||
function processFans(fans: DeviceEnvironment['fans']): void {
|
||||
// Find the next adjacent element, so we can insert elements before it.
|
||||
const next = getNext<HTMLTableRowElement>('status-fans');
|
||||
if (typeof fans !== 'undefined') {
|
||||
@ -285,7 +285,7 @@ function processFans(fans: DeviceEnvironment['fans']) {
|
||||
*
|
||||
* @param power NAPALM power data.
|
||||
*/
|
||||
function processPower(power: DeviceEnvironment['power']) {
|
||||
function processPower(power: DeviceEnvironment['power']): void {
|
||||
// Find the next adjacent element, so we can insert elements before it.
|
||||
const next = getNext<HTMLTableRowElement>('status-power');
|
||||
if (typeof power !== 'undefined') {
|
||||
@ -322,7 +322,7 @@ function processPower(power: DeviceEnvironment['power']) {
|
||||
*
|
||||
* @param env NAPALM Device Environment
|
||||
*/
|
||||
function processEnvironment(env: DeviceEnvironment) {
|
||||
function processEnvironment(env: DeviceEnvironment): void {
|
||||
const { cpu, memory, temperature, fans, power } = env;
|
||||
processCpu(cpu);
|
||||
processMemory(memory);
|
||||
@ -334,7 +334,7 @@ function processEnvironment(env: DeviceEnvironment) {
|
||||
/**
|
||||
* Initialize NAPALM device status handlers.
|
||||
*/
|
||||
function initStatus() {
|
||||
function initStatus(): void {
|
||||
// Show loading state for both Facts & Environment cards.
|
||||
toggleLoader('show');
|
||||
|
||||
|
@ -136,7 +136,7 @@ function initFormElements() {
|
||||
function moveOptionUp(element: HTMLSelectElement): void {
|
||||
const options = Array.from(element.options);
|
||||
for (let i = 1; i < options.length; i++) {
|
||||
let option = options[i];
|
||||
const option = options[i];
|
||||
if (option.selected) {
|
||||
element.removeChild(option);
|
||||
element.insertBefore(option, element.options[i - 1]);
|
||||
@ -290,7 +290,7 @@ function initScopeSelector() {
|
||||
}
|
||||
}
|
||||
|
||||
export function initForms() {
|
||||
export function initForms(): void {
|
||||
for (const func of [
|
||||
initFormElements,
|
||||
initFormActions,
|
||||
|
2
netbox/project-static/src/global.d.ts
vendored
2
netbox/project-static/src/global.d.ts
vendored
@ -33,6 +33,8 @@ interface Window {
|
||||
*/
|
||||
type Index<O extends Dict, K extends keyof O> = K extends string ? K : never;
|
||||
|
||||
type APIResponse<T> = T | ErrorBase | APIError;
|
||||
|
||||
type APIAnswer<T> = {
|
||||
count: number;
|
||||
next: Nullable<string>;
|
||||
|
@ -44,10 +44,13 @@ function updateLabel(status: JobStatus) {
|
||||
switch (status.value) {
|
||||
case 'failed' || 'errored':
|
||||
labelClass = 'danger';
|
||||
break;
|
||||
case 'running':
|
||||
labelClass = 'warning';
|
||||
break;
|
||||
case 'completed':
|
||||
labelClass = 'success';
|
||||
break;
|
||||
}
|
||||
element.setAttribute('class', `badge bg-${labelClass}`);
|
||||
element.innerText = status.label;
|
||||
|
@ -3,7 +3,7 @@ import { isTruthy, getElements } from './util';
|
||||
/**
|
||||
* Allow any element to be made "clickable" with the use of the `data-href` attribute.
|
||||
*/
|
||||
export function initLinks() {
|
||||
export function initLinks(): void {
|
||||
for (const link of getElements('*[data-href]')) {
|
||||
const href = link.getAttribute('data-href');
|
||||
if (isTruthy(href)) {
|
||||
|
@ -13,7 +13,7 @@ import { initSideNav } from './sidenav';
|
||||
import { initRackElevation } from './racks';
|
||||
import { initLinks } from './links';
|
||||
|
||||
function initDocument() {
|
||||
function initDocument(): void {
|
||||
for (const init of [
|
||||
initBootstrap,
|
||||
initColorMode,
|
||||
@ -34,7 +34,7 @@ function initDocument() {
|
||||
}
|
||||
}
|
||||
|
||||
function initWindow() {
|
||||
function initWindow(): void {
|
||||
const contentContainer = document.querySelector<HTMLElement>('.content-container');
|
||||
if (contentContainer !== null) {
|
||||
// Focus the content container for accessible navigation.
|
||||
|
@ -67,7 +67,7 @@ function handleRackImageToggle(
|
||||
* Add onClick callback for toggling rack elevation images. Synchronize the image toggle button
|
||||
* text and display state of images with the local state.
|
||||
*/
|
||||
export function initRackElevation() {
|
||||
export function initRackElevation(): void {
|
||||
const initiallyHidden = rackImagesState.get('hidden');
|
||||
for (const button of getElements<HTMLButtonElement>('button.toggle-images')) {
|
||||
toggleRackImagesButton(initiallyHidden, button);
|
||||
|
@ -8,7 +8,7 @@ import { getElements, getRowValues, findFirstAdjacent, isTruthy } from './util';
|
||||
* @param event "click" event for each dropdown item.
|
||||
* @param button Each dropdown item element.
|
||||
*/
|
||||
function handleSearchDropdownClick(event: Event, button: HTMLButtonElement) {
|
||||
function handleSearchDropdownClick(event: Event, button: HTMLButtonElement): void {
|
||||
const dropdown = event.currentTarget as HTMLButtonElement;
|
||||
const selectedValue = findFirstAdjacent<HTMLSpanElement>(dropdown, 'span.search-obj-selected');
|
||||
const selectedType = findFirstAdjacent<HTMLInputElement>(dropdown, 'input.search-obj-type');
|
||||
@ -31,7 +31,7 @@ function handleSearchDropdownClick(event: Event, button: HTMLButtonElement) {
|
||||
/**
|
||||
* Initialize Search Bar Elements.
|
||||
*/
|
||||
function initSearchBar() {
|
||||
function initSearchBar(): void {
|
||||
for (const dropdown of getElements<HTMLUListElement>('.search-obj-selector')) {
|
||||
for (const button of dropdown.querySelectorAll<HTMLButtonElement>(
|
||||
'li > button.dropdown-item',
|
||||
@ -44,7 +44,7 @@ function initSearchBar() {
|
||||
/**
|
||||
* Initialize Interface Table Filter Elements.
|
||||
*/
|
||||
function initInterfaceFilter() {
|
||||
function initInterfaceFilter(): void {
|
||||
for (const input of getElements<HTMLInputElement>('input.interface-filter')) {
|
||||
const table = findFirstAdjacent<HTMLTableElement>(input, 'table');
|
||||
const rows = Array.from(
|
||||
@ -53,7 +53,7 @@ function initInterfaceFilter() {
|
||||
/**
|
||||
* Filter on-page table by input text.
|
||||
*/
|
||||
function handleInput(event: Event) {
|
||||
function handleInput(event: Event): void {
|
||||
const target = event.target as HTMLInputElement;
|
||||
// Create a regex pattern from the input search text to match against.
|
||||
const filter = new RegExp(target.value.toLowerCase().trim());
|
||||
@ -87,7 +87,7 @@ function initInterfaceFilter() {
|
||||
}
|
||||
}
|
||||
|
||||
function initTableFilter() {
|
||||
function initTableFilter(): void {
|
||||
for (const input of getElements<HTMLInputElement>('input.object-filter')) {
|
||||
// Find the first adjacent table element.
|
||||
const table = findFirstAdjacent<HTMLTableElement>(input, 'table');
|
||||
@ -101,7 +101,7 @@ function initTableFilter() {
|
||||
* Filter table rows by matched input text.
|
||||
* @param event
|
||||
*/
|
||||
function handleInput(event: Event) {
|
||||
function handleInput(event: Event): void {
|
||||
const target = event.target as HTMLInputElement;
|
||||
|
||||
// Create a regex pattern from the input search text to match against.
|
||||
@ -132,7 +132,7 @@ function initTableFilter() {
|
||||
}
|
||||
}
|
||||
|
||||
export function initSearch() {
|
||||
export function initSearch(): void {
|
||||
for (const func of [initSearchBar, initTableFilter, initInterfaceFilter]) {
|
||||
func();
|
||||
}
|
||||
|
@ -711,7 +711,7 @@ class APISelect {
|
||||
* @param id DOM ID of the other element.
|
||||
*/
|
||||
private updatePathValues(id: string): void {
|
||||
let key = id.replaceAll(/^id_/gi, '');
|
||||
const key = id.replaceAll(/^id_/gi, '');
|
||||
const element = getElement<HTMLSelectElement>(`id_${key}`);
|
||||
if (element !== null) {
|
||||
// If this element's URL contains Django template tags ({{), replace the template tag
|
||||
@ -982,15 +982,16 @@ class APISelect {
|
||||
'button',
|
||||
{ type: 'button' },
|
||||
['btn', 'btn-sm', 'btn-ghost-dark'],
|
||||
[createElement('i', {}, ['mdi', 'mdi-reload'])],
|
||||
[createElement('i', null, ['mdi', 'mdi-reload'])],
|
||||
);
|
||||
refreshButton.addEventListener('click', () => this.loadData());
|
||||
refreshButton.type = 'button';
|
||||
this.slim.slim.search.container.appendChild(refreshButton);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function initApiSelect() {
|
||||
export function initApiSelect(): void {
|
||||
for (const select of getElements<HTMLSelectElement>('.netbox-api-select')) {
|
||||
new APISelect(select);
|
||||
}
|
||||
|
@ -11,6 +11,30 @@ 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.
|
||||
@ -40,7 +64,7 @@ export function initColorSelect(): void {
|
||||
// Style the select container to match any pre-selectd options.
|
||||
for (const option of instance.data.data) {
|
||||
if ('selected' in option && option.selected) {
|
||||
styleContainer(option);
|
||||
styleContainer(instance, option);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -50,25 +74,7 @@ export function initColorSelect(): void {
|
||||
instance.slim.container.classList.remove(className);
|
||||
}
|
||||
|
||||
function styleContainer(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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Change the SlimSelect container's style based on the selected option.
|
||||
instance.onChange = styleContainer;
|
||||
instance.onChange = option => styleContainer(instance, option);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { initApiSelect } from './api';
|
||||
import { initColorSelect } from './color';
|
||||
import { initStaticSelect } from './static';
|
||||
|
||||
export function initSelect() {
|
||||
export function initSelect(): void {
|
||||
for (const func of [initApiSelect, initColorSelect, initStaticSelect]) {
|
||||
func();
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import SlimSelect from 'slim-select';
|
||||
import { getElements } from '../util';
|
||||
|
||||
export function initStaticSelect() {
|
||||
export function initStaticSelect(): void {
|
||||
for (const select of getElements<HTMLSelectElement>('.netbox-static-select')) {
|
||||
if (select !== null) {
|
||||
const label = document.querySelector(`label[for=${select.id}]`) as HTMLLabelElement;
|
||||
|
@ -237,7 +237,7 @@ class SideNav {
|
||||
'.navbar-nav .nav .nav-item a.nav-link',
|
||||
)) {
|
||||
const href = new RegExp(link.href, 'gi');
|
||||
if (Boolean(window.location.href.match(href))) {
|
||||
if (window.location.href.match(href)) {
|
||||
yield link;
|
||||
}
|
||||
}
|
||||
@ -310,7 +310,7 @@ class SideNav {
|
||||
}
|
||||
}
|
||||
|
||||
export function initSideNav() {
|
||||
export function initSideNav(): void {
|
||||
for (const sidenav of getElements<HTMLDivElement>('.sidenav')) {
|
||||
new SideNav(sidenav);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import { getElements, apiPatch, hasError, getSelectedOptions } from './util';
|
||||
* Mark each option element in the selected columns element as 'selected' so they are submitted to
|
||||
* the API.
|
||||
*/
|
||||
function saveTableConfig() {
|
||||
function saveTableConfig(): void {
|
||||
for (const element of getElements<HTMLOptionElement>('select[name="columns"] option')) {
|
||||
element.selected = true;
|
||||
}
|
||||
@ -14,7 +14,7 @@ function saveTableConfig() {
|
||||
/**
|
||||
* Delete all selected columns, which reverts the user's preferences to the default column set.
|
||||
*/
|
||||
function resetTableConfig() {
|
||||
function resetTableConfig(): void {
|
||||
for (const element of getElements<HTMLSelectElement>('select[name="columns"]')) {
|
||||
element.value = '';
|
||||
}
|
||||
@ -23,7 +23,7 @@ function resetTableConfig() {
|
||||
/**
|
||||
* Add columns to the table config select element.
|
||||
*/
|
||||
function addColumns(event: Event) {
|
||||
function addColumns(event: Event): void {
|
||||
for (const selectedOption of getElements<HTMLOptionElement>('#id_available_columns > option')) {
|
||||
if (selectedOption.selected) {
|
||||
for (const selected of getElements<HTMLSelectElement>('#id_columns')) {
|
||||
@ -38,7 +38,7 @@ function addColumns(event: Event) {
|
||||
/**
|
||||
* Remove columns from the table config select element.
|
||||
*/
|
||||
function removeColumns(event: Event) {
|
||||
function removeColumns(event: Event): void {
|
||||
for (const selectedOption of getElements<HTMLOptionElement>('#id_columns > option')) {
|
||||
if (selectedOption.selected) {
|
||||
for (const available of getElements<HTMLSelectElement>('#id_available_columns')) {
|
||||
@ -53,7 +53,7 @@ function removeColumns(event: Event) {
|
||||
/**
|
||||
* Submit form configuration to the NetBox API.
|
||||
*/
|
||||
async function submitFormConfig(formConfig: Dict<Dict>) {
|
||||
async function submitFormConfig(formConfig: Dict<Dict>): Promise<APIResponse<APIUserConfig>> {
|
||||
return await apiPatch<APIUserConfig>('/api/users/config/', formConfig);
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ async function submitFormConfig(formConfig: Dict<Dict>) {
|
||||
* Handle table config form submission. Sends the selected columns to the NetBox API to update
|
||||
* the user's table configuration preferences.
|
||||
*/
|
||||
function handleSubmit(event: Event) {
|
||||
function handleSubmit(event: Event): void {
|
||||
event.preventDefault();
|
||||
|
||||
const element = event.currentTarget as HTMLFormElement;
|
||||
@ -96,7 +96,7 @@ function handleSubmit(event: Event) {
|
||||
/**
|
||||
* Initialize table configuration elements.
|
||||
*/
|
||||
export function initTableConfig() {
|
||||
export function initTableConfig(): void {
|
||||
for (const element of getElements<HTMLButtonElement>('#save_tableconfig')) {
|
||||
element.addEventListener('click', saveTableConfig);
|
||||
}
|
||||
|
@ -164,18 +164,14 @@ class TableState {
|
||||
private table: HTMLTableElement;
|
||||
/**
|
||||
* Instance of ButtonState for the 'show/hide enabled rows' button.
|
||||
*
|
||||
* TS Error is expected because null handling is performed in the constructor.
|
||||
*/
|
||||
// @ts-expect-error
|
||||
// @ts-expect-error null handling is performed in the constructor
|
||||
private enabledButton: ButtonState;
|
||||
|
||||
/**
|
||||
* Instance of ButtonState for the 'show/hide disabled rows' button.
|
||||
*
|
||||
* TS Error is expected because null handling is performed in the constructor.
|
||||
*/
|
||||
// @ts-expect-error
|
||||
// @ts-expect-error null handling is performed in the constructor
|
||||
private disabledButton: ButtonState;
|
||||
|
||||
/**
|
||||
@ -288,7 +284,7 @@ class TableState {
|
||||
/**
|
||||
* Initialize table states.
|
||||
*/
|
||||
export function initInterfaceTable() {
|
||||
export function initInterfaceTable(): void {
|
||||
for (const element of getElements<HTMLTableElement>('table')) {
|
||||
new TableState(element);
|
||||
}
|
||||
|
@ -1,19 +1,18 @@
|
||||
import Cookie from 'cookie';
|
||||
|
||||
type APIRes<T> = T | ErrorBase | APIError;
|
||||
type Method = 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
|
||||
type ReqData = URLSearchParams | Dict | undefined | unknown;
|
||||
type SelectedOption = { name: string; options: string[] };
|
||||
|
||||
type HTMLElementProperties<E extends HTMLElement> =
|
||||
| {
|
||||
[k in keyof E]: E[k];
|
||||
}
|
||||
| {};
|
||||
|
||||
type InferredProps<T extends keyof HTMLElementTagNameMap> = HTMLElementProperties<
|
||||
HTMLElementTagNameMap[T]
|
||||
>;
|
||||
/**
|
||||
* Infer valid HTMLElement props based on element name.
|
||||
*/
|
||||
type InferredProps<
|
||||
// Element name.
|
||||
T extends keyof HTMLElementTagNameMap,
|
||||
// Element type.
|
||||
E extends HTMLElementTagNameMap[T] = HTMLElementTagNameMap[T]
|
||||
> = Partial<Record<keyof E, E[keyof E]>>;
|
||||
|
||||
export function isApiError(data: Record<string, unknown>): data is APIError {
|
||||
return 'error' in data && 'exception' in data;
|
||||
@ -36,9 +35,9 @@ export function hasMore(data: APIAnswer<APIObjectBase>): data is APIAnswerWithNe
|
||||
*/
|
||||
export function slugify(slug: string, chars: number): string {
|
||||
return slug
|
||||
.replace(/[^\-\.\w\s]/g, '') // Remove unneeded chars
|
||||
.replace(/^[\s\.]+|[\s\.]+$/g, '') // Trim leading/trailing spaces
|
||||
.replace(/[\-\.\s]+/g, '-') // Convert spaces and decimals to hyphens
|
||||
.replace(/[^\-.\w\s]/g, '') // Remove unneeded chars
|
||||
.replace(/^[\s.]+|[\s.]+$/g, '') // Trim leading/trailing spaces
|
||||
.replace(/[-.\s]+/g, '-') // Convert spaces and decimals to hyphens
|
||||
.toLowerCase() // Convert to lowercase
|
||||
.substring(0, chars); // Trim to first chars chars
|
||||
}
|
||||
@ -82,7 +81,7 @@ export async function apiRequest<R extends Dict, D extends ReqData = undefined>(
|
||||
url: string,
|
||||
method: Method,
|
||||
data?: D,
|
||||
): Promise<APIRes<R>> {
|
||||
): Promise<APIResponse<R>> {
|
||||
const token = getCsrfToken();
|
||||
const headers = new Headers({ 'X-CSRFToken': token });
|
||||
|
||||
@ -111,18 +110,18 @@ export async function apiRequest<R extends Dict, D extends ReqData = undefined>(
|
||||
export async function apiPatch<R extends Dict, D extends ReqData = Dict>(
|
||||
url: string,
|
||||
data: D,
|
||||
): Promise<APIRes<R>> {
|
||||
): Promise<APIResponse<R>> {
|
||||
return await apiRequest(url, 'PATCH', data);
|
||||
}
|
||||
|
||||
export async function apiGetBase<R extends Dict>(url: string): Promise<APIRes<R>> {
|
||||
export async function apiGetBase<R extends Dict>(url: string): Promise<APIResponse<R>> {
|
||||
return await apiRequest<R>(url, 'GET');
|
||||
}
|
||||
|
||||
export async function apiPostForm<R extends Dict, D extends Dict>(
|
||||
url: string,
|
||||
data: D,
|
||||
): Promise<APIRes<R>> {
|
||||
): Promise<APIResponse<R>> {
|
||||
const body = new URLSearchParams();
|
||||
for (const [k, v] of Object.entries(data)) {
|
||||
body.append(k, String(v));
|
||||
@ -149,7 +148,7 @@ export function getElements<K extends keyof HTMLElementTagNameMap>(
|
||||
export function getElements<E extends Element>(...key: string[]): Generator<E>;
|
||||
export function* getElements(
|
||||
...key: (string | keyof HTMLElementTagNameMap | keyof SVGElementTagNameMap)[]
|
||||
) {
|
||||
): Generator<Element> {
|
||||
for (const query of key) {
|
||||
for (const element of document.querySelectorAll(query)) {
|
||||
if (element !== null) {
|
||||
@ -249,7 +248,7 @@ export function getNetboxData(key: string): string | null {
|
||||
/**
|
||||
* Toggle visibility of card loader.
|
||||
*/
|
||||
export function toggleLoader(action: 'show' | 'hide') {
|
||||
export function toggleLoader(action: 'show' | 'hide'): void {
|
||||
for (const element of getElements<HTMLDivElement>('div.card-overlay')) {
|
||||
if (action === 'show') {
|
||||
element.classList.remove('d-none');
|
||||
@ -316,25 +315,27 @@ export function findFirstAdjacent<R extends HTMLElement, B extends Element = Ele
|
||||
* @param children Child elements.
|
||||
*/
|
||||
export function createElement<
|
||||
// Element name.
|
||||
T extends keyof HTMLElementTagNameMap,
|
||||
// Element props.
|
||||
P extends InferredProps<T>,
|
||||
// Child element type.
|
||||
C extends HTMLElement = HTMLElement
|
||||
>(
|
||||
tag: T,
|
||||
properties: InferredProps<T>,
|
||||
classes: string[],
|
||||
children: C[] = [],
|
||||
): HTMLElementTagNameMap[T] {
|
||||
>(tag: T, properties: P | null, classes: string[], children: C[] = []): HTMLElementTagNameMap[T] {
|
||||
// Create the base element.
|
||||
const element = document.createElement<T>(tag);
|
||||
|
||||
if (properties !== null) {
|
||||
for (const k of Object.keys(properties)) {
|
||||
// Add each property to the element.
|
||||
const key = k as keyof HTMLElementProperties<HTMLElementTagNameMap[T]>;
|
||||
const value = properties[key];
|
||||
const key = k as keyof InferredProps<T>;
|
||||
const value = properties[key] as NonNullable<P[keyof P]>;
|
||||
if (key in element) {
|
||||
element[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add each CSS class to the element's class list.
|
||||
element.classList.add(...classes);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user