Procházet zdrojové kódy

Fix eslint misconfiguration and corresponding errors

Matt před 4 roky
rodič
revize
a0ba8380c9
34 změnil soubory, kde provedl 130 přidání a 115 odebrání
  1. 3 4
      netbox/project-static/.eslintrc
  2. 0 0
      netbox/project-static/dist/config.js
  3. 0 0
      netbox/project-static/dist/config.js.map
  4. 0 0
      netbox/project-static/dist/jobs.js
  5. 0 0
      netbox/project-static/dist/jobs.js.map
  6. 0 0
      netbox/project-static/dist/lldp.js
  7. 0 0
      netbox/project-static/dist/lldp.js.map
  8. 0 0
      netbox/project-static/dist/netbox.js
  9. 0 0
      netbox/project-static/dist/netbox.js.map
  10. 0 0
      netbox/project-static/dist/status.js
  11. 0 0
      netbox/project-static/dist/status.js.map
  12. 5 1
      netbox/project-static/src/bs.ts
  13. 11 11
      netbox/project-static/src/buttons.ts
  14. 1 1
      netbox/project-static/src/clipboard.ts
  15. 4 1
      netbox/project-static/src/colorMode.ts
  16. 1 1
      netbox/project-static/src/device/config.ts
  17. 1 1
      netbox/project-static/src/device/lldp.ts
  18. 9 9
      netbox/project-static/src/device/status.ts
  19. 2 2
      netbox/project-static/src/forms.ts
  20. 2 0
      netbox/project-static/src/global.d.ts
  21. 3 0
      netbox/project-static/src/jobs.ts
  22. 1 1
      netbox/project-static/src/links.ts
  23. 2 2
      netbox/project-static/src/netbox.ts
  24. 1 1
      netbox/project-static/src/racks.ts
  25. 7 7
      netbox/project-static/src/search.ts
  26. 4 3
      netbox/project-static/src/select/api.ts
  27. 26 20
      netbox/project-static/src/select/color.ts
  28. 1 1
      netbox/project-static/src/select/index.ts
  29. 1 1
      netbox/project-static/src/select/static.ts
  30. 2 2
      netbox/project-static/src/sidenav.ts
  31. 7 7
      netbox/project-static/src/tableConfig.ts
  32. 3 7
      netbox/project-static/src/tables/interfaceTable.ts
  33. 32 31
      netbox/project-static/src/util.ts
  34. 1 1
      netbox/project-static/styles/overrides.scss

+ 3 - 4
netbox/project-static/.eslintrc

@@ -6,7 +6,7 @@
     "plugin:@typescript-eslint/eslint-recommended",
     "plugin:@typescript-eslint/eslint-recommended",
     "plugin:@typescript-eslint/recommended",
     "plugin:@typescript-eslint/recommended",
     "plugin:prettier/recommended",
     "plugin:prettier/recommended",
-    "prettier/@typescript-eslint"
+    "prettier"
   ],
   ],
   "parser": "@typescript-eslint/parser",
   "parser": "@typescript-eslint/parser",
   "env": {
   "env": {
@@ -19,8 +19,7 @@
     "sourceType": "module",
     "sourceType": "module",
     "ecmaFeatures": {
     "ecmaFeatures": {
       "arrowFunctions": true
       "arrowFunctions": true
-    },
-    "project": "./tsconfig.json"
+    }
   },
   },
   "plugins": ["@typescript-eslint", "prettier"],
   "plugins": ["@typescript-eslint", "prettier"],
   "settings": {
   "settings": {
@@ -35,7 +34,7 @@
     "@typescript-eslint/no-unused-vars": "off",
     "@typescript-eslint/no-unused-vars": "off",
     "@typescript-eslint/no-unused-vars-experimental": "error",
     "@typescript-eslint/no-unused-vars-experimental": "error",
     "no-unused-vars": "off",
     "no-unused-vars": "off",
-
+    "no-inner-declarations": "off",
     "comma-dangle": ["error", "always-multiline"],
     "comma-dangle": ["error", "always-multiline"],
     "global-require": "off",
     "global-require": "off",
     "import/no-dynamic-require": "off",
     "import/no-dynamic-require": "off",

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
netbox/project-static/dist/config.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
netbox/project-static/dist/config.js.map


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
netbox/project-static/dist/jobs.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
netbox/project-static/dist/jobs.js.map


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
netbox/project-static/dist/lldp.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
netbox/project-static/dist/lldp.js.map


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
netbox/project-static/dist/netbox.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
netbox/project-static/dist/netbox.js.map


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
netbox/project-static/dist/status.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
netbox/project-static/dist/status.js.map


+ 5 - 1
netbox/project-static/src/bs.ts

@@ -45,12 +45,16 @@ export function createToast(
   switch (level) {
   switch (level) {
     case 'warning':
     case 'warning':
       iconName = 'mdi-alert';
       iconName = 'mdi-alert';
+      break;
     case 'success':
     case 'success':
       iconName = 'mdi-check-circle';
       iconName = 'mdi-check-circle';
+      break;
     case 'info':
     case 'info':
       iconName = 'mdi-information';
       iconName = 'mdi-information';
+      break;
     case 'danger':
     case 'danger':
       iconName = 'mdi-alert';
       iconName = 'mdi-alert';
+      break;
   }
   }
 
 
   const container = document.createElement('div');
   const container = document.createElement('div');
@@ -109,7 +113,7 @@ export function createToast(
  */
  */
 function initTabs() {
 function initTabs() {
   const { hash } = location;
   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 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
     // 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`.
     // element's data-bs-target value. For example, `#tab_frontports` becomes `#frontports`.

+ 11 - 11
netbox/project-static/src/buttons.ts

@@ -21,7 +21,7 @@ type ObjectDepthState = { hidden: boolean };
  *
  *
  * @param element Connection Toggle Button Element
  * @param element Connection Toggle Button Element
  */
  */
-function toggleConnection(element: HTMLButtonElement) {
+function toggleConnection(element: HTMLButtonElement): void {
   const id = element.getAttribute('data');
   const id = element.getAttribute('data');
   const connected = element.classList.contains('connected');
   const connected = element.classList.contains('connected');
   const status = connected ? 'planned' : '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')) {
   for (const element of getElements<HTMLButtonElement>('button.cable-toggle')) {
     element.addEventListener('click', () => toggleConnection(element));
     element.addEventListener('click', () => toggleConnection(element));
   }
   }
@@ -116,7 +116,7 @@ function handleDepthToggle(state: StateManager<ObjectDepthState>, button: HTMLBu
 /**
 /**
  * Initialize object depth toggle buttons.
  * Initialize object depth toggle buttons.
  */
  */
-function initDepthToggle() {
+function initDepthToggle(): void {
   const initiallyHidden = objectDepthState.get('hidden');
   const initiallyHidden = objectDepthState.get('hidden');
 
 
   for (const button of getElements<HTMLButtonElement>('button.toggle-depth')) {
   for (const button of getElements<HTMLButtonElement>('button.toggle-depth')) {
@@ -190,7 +190,7 @@ function handlePreferenceSave(event: Event): void {
 /**
 /**
  * Initialize handlers for user profile updates.
  * Initialize handlers for user profile updates.
  */
  */
-function initPreferenceUpdate() {
+function initPreferenceUpdate(): void {
   const form = getElement<HTMLFormElement>('preferences-update');
   const form = getElement<HTMLFormElement>('preferences-update');
   if (form !== null) {
   if (form !== null) {
     form.addEventListener('submit', handlePreferenceSave);
     form.addEventListener('submit', handlePreferenceSave);
@@ -203,7 +203,7 @@ function initPreferenceUpdate() {
  *
  *
  * @param event Change Event
  * @param event Change Event
  */
  */
-function handleSelectAllToggle(event: Event) {
+function handleSelectAllToggle(event: Event): void {
   // Select all checkbox in header row.
   // Select all checkbox in header row.
   const tableSelectAll = event.currentTarget as HTMLInputElement;
   const tableSelectAll = event.currentTarget as HTMLInputElement;
   // Nearest table to the select all checkbox.
   // Nearest table to the select all checkbox.
@@ -248,7 +248,7 @@ function handleSelectAllToggle(event: Event) {
  *
  *
  * @param event Change Event
  * @param event Change Event
  */
  */
-function handlePkCheck(event: Event) {
+function handlePkCheck(event: Event): void {
   const target = event.currentTarget as HTMLInputElement;
   const target = event.currentTarget as HTMLInputElement;
   if (!target.checked) {
   if (!target.checked) {
     for (const element of getElements<HTMLInputElement>(
     for (const element of getElements<HTMLInputElement>(
@@ -267,7 +267,7 @@ function handlePkCheck(event: Event) {
  *
  *
  * @param event Change Event
  * @param event Change Event
  */
  */
-function handleSelectAll(event: Event) {
+function handleSelectAll(event: Event): void {
   const target = event.currentTarget as HTMLInputElement;
   const target = event.currentTarget as HTMLInputElement;
   const selectAllBox = getElement<HTMLDivElement>('select-all-box');
   const selectAllBox = getElement<HTMLDivElement>('select-all-box');
   if (selectAllBox !== null) {
   if (selectAllBox !== null) {
@@ -286,7 +286,7 @@ function handleSelectAll(event: Event) {
 /**
 /**
  * Initialize table select all elements.
  * Initialize table select all elements.
  */
  */
-function initSelectAll() {
+function initSelectAll(): void {
   for (const element of getElements<HTMLInputElement>(
   for (const element of getElements<HTMLInputElement>(
     'table tr th > input[type="checkbox"].toggle',
     '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;
   const select = event.currentTarget as HTMLSelectElement;
   if (select.form !== null) {
   if (select.form !== null) {
     select.form.submit();
     select.form.submit();
   }
   }
 }
 }
 
 
-function initPerPage() {
+function initPerPage(): void {
   for (const element of getElements<HTMLSelectElement>('select.per-page')) {
   for (const element of getElements<HTMLSelectElement>('select.per-page')) {
     element.addEventListener('change', handlePerPageSelect);
     element.addEventListener('change', handlePerPageSelect);
   }
   }
 }
 }
 
 
-export function initButtons() {
+export function initButtons(): void {
   for (const func of [
   for (const func of [
     initDepthToggle,
     initDepthToggle,
     initConnectionToggle,
     initConnectionToggle,

+ 1 - 1
netbox/project-static/src/clipboard.ts

@@ -1,7 +1,7 @@
 import Clipboard from 'clipboard';
 import Clipboard from 'clipboard';
 import { getElements } from './util';
 import { getElements } from './util';
 
 
-export function initClipboard() {
+export function initClipboard(): void {
   for (const element of getElements('a.copy-token', 'button.copy-secret')) {
   for (const element of getElements('a.copy-token', 'button.copy-secret')) {
     new Clipboard(element);
     new Clipboard(element);
   }
   }

+ 4 - 1
netbox/project-static/src/colorMode.ts

@@ -6,7 +6,10 @@ const TEXT_WHEN_LIGHT = 'Dark Mode';
 const ICON_WHEN_DARK = 'mdi-lightbulb-on';
 const ICON_WHEN_DARK = 'mdi-lightbulb-on';
 const ICON_WHEN_LIGHT = 'mdi-lightbulb';
 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';
   return value === 'dark' || value === 'light';
 }
 }
 
 

+ 1 - 1
netbox/project-static/src/device/config.ts

@@ -4,7 +4,7 @@ import { apiGetBase, getNetboxData, hasError, toggleLoader } from '../util';
 /**
 /**
  * Initialize device config elements.
  * Initialize device config elements.
  */
  */
-function initConfig() {
+function initConfig(): void {
   toggleLoader('show');
   toggleLoader('show');
   const url = getNetboxData('data-object-url');
   const url = getNetboxData('data-object-url');
 
 

+ 1 - 1
netbox/project-static/src/device/lldp.ts

@@ -31,7 +31,7 @@ function updateRowStyle(data: LLDPNeighborDetail) {
 
 
         let cInterfaceShort = null;
         let cInterfaceShort = null;
         if (isTruthy(cInterface)) {
         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 ?? '';
         const nHost = neighbor.remote_system_name ?? '';

+ 9 - 9
netbox/project-static/src/device/status.ts

@@ -92,7 +92,7 @@ function getUptime(seconds: number): Uptime {
  *
  *
  * @param facts NAPALM Device Facts
  * @param facts NAPALM Device Facts
  */
  */
-function processFacts(facts: DeviceFacts) {
+function processFacts(facts: DeviceFacts): void {
   for (const key of factKeys) {
   for (const key of factKeys) {
     if (key in facts) {
     if (key in facts) {
       // Find the target element which should have its innerHTML/innerText set to a NAPALM value.
       // 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
  * @param next Next adjacent element.For example, if this is the CPU data, `next` would be the
  *             memory row.
  *             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' }, [
   const none = createElement('td', { colSpan: '2', innerText: 'No Data' }, [
     'text-muted',
     'text-muted',
     'text-center',
     'text-center',
@@ -173,7 +173,7 @@ function getNext<E extends HTMLElement>(id: string): Nullable<E> {
  *
  *
  * @param cpu NAPALM CPU data.
  * @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.
   // Find the next adjacent element, so we can insert elements before it.
   const next = getNext<HTMLTableRowElement>('status-cpu');
   const next = getNext<HTMLTableRowElement>('status-cpu');
   if (typeof cpu !== 'undefined') {
   if (typeof cpu !== 'undefined') {
@@ -200,7 +200,7 @@ function processCpu(cpu: DeviceEnvironment['cpu']) {
  *
  *
  * @param mem NAPALM memory data.
  * @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.
   // Find the next adjacent element, so we can insert elements before it.
   const next = getNext<HTMLTableRowElement>('status-memory');
   const next = getNext<HTMLTableRowElement>('status-memory');
   if (typeof mem !== 'undefined') {
   if (typeof mem !== 'undefined') {
@@ -222,7 +222,7 @@ function processMemory(mem: DeviceEnvironment['memory']) {
  *
  *
  * @param temp NAPALM temperature data.
  * @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.
   // Find the next adjacent element, so we can insert elements before it.
   const next = getNext<HTMLTableRowElement>('status-temperature');
   const next = getNext<HTMLTableRowElement>('status-temperature');
   if (typeof temp !== 'undefined') {
   if (typeof temp !== 'undefined') {
@@ -249,7 +249,7 @@ function processTemp(temp: DeviceEnvironment['temperature']) {
  *
  *
  * @param fans NAPALM fan data.
  * @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.
   // Find the next adjacent element, so we can insert elements before it.
   const next = getNext<HTMLTableRowElement>('status-fans');
   const next = getNext<HTMLTableRowElement>('status-fans');
   if (typeof fans !== 'undefined') {
   if (typeof fans !== 'undefined') {
@@ -285,7 +285,7 @@ function processFans(fans: DeviceEnvironment['fans']) {
  *
  *
  * @param power NAPALM power data.
  * @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.
   // Find the next adjacent element, so we can insert elements before it.
   const next = getNext<HTMLTableRowElement>('status-power');
   const next = getNext<HTMLTableRowElement>('status-power');
   if (typeof power !== 'undefined') {
   if (typeof power !== 'undefined') {
@@ -322,7 +322,7 @@ function processPower(power: DeviceEnvironment['power']) {
  *
  *
  * @param env NAPALM Device Environment
  * @param env NAPALM Device Environment
  */
  */
-function processEnvironment(env: DeviceEnvironment) {
+function processEnvironment(env: DeviceEnvironment): void {
   const { cpu, memory, temperature, fans, power } = env;
   const { cpu, memory, temperature, fans, power } = env;
   processCpu(cpu);
   processCpu(cpu);
   processMemory(memory);
   processMemory(memory);
@@ -334,7 +334,7 @@ function processEnvironment(env: DeviceEnvironment) {
 /**
 /**
  * Initialize NAPALM device status handlers.
  * Initialize NAPALM device status handlers.
  */
  */
-function initStatus() {
+function initStatus(): void {
   // Show loading state for both Facts & Environment cards.
   // Show loading state for both Facts & Environment cards.
   toggleLoader('show');
   toggleLoader('show');
 
 

+ 2 - 2
netbox/project-static/src/forms.ts

@@ -136,7 +136,7 @@ function initFormElements() {
 function moveOptionUp(element: HTMLSelectElement): void {
 function moveOptionUp(element: HTMLSelectElement): void {
   const options = Array.from(element.options);
   const options = Array.from(element.options);
   for (let i = 1; i < options.length; i++) {
   for (let i = 1; i < options.length; i++) {
-    let option = options[i];
+    const option = options[i];
     if (option.selected) {
     if (option.selected) {
       element.removeChild(option);
       element.removeChild(option);
       element.insertBefore(option, element.options[i - 1]);
       element.insertBefore(option, element.options[i - 1]);
@@ -290,7 +290,7 @@ function initScopeSelector() {
   }
   }
 }
 }
 
 
-export function initForms() {
+export function initForms(): void {
   for (const func of [
   for (const func of [
     initFormElements,
     initFormElements,
     initFormActions,
     initFormActions,

+ 2 - 0
netbox/project-static/src/global.d.ts

@@ -33,6 +33,8 @@ interface Window {
  */
  */
 type Index<O extends Dict, K extends keyof O> = K extends string ? K : never;
 type Index<O extends Dict, K extends keyof O> = K extends string ? K : never;
 
 
+type APIResponse<T> = T | ErrorBase | APIError;
+
 type APIAnswer<T> = {
 type APIAnswer<T> = {
   count: number;
   count: number;
   next: Nullable<string>;
   next: Nullable<string>;

+ 3 - 0
netbox/project-static/src/jobs.ts

@@ -44,10 +44,13 @@ function updateLabel(status: JobStatus) {
     switch (status.value) {
     switch (status.value) {
       case 'failed' || 'errored':
       case 'failed' || 'errored':
         labelClass = 'danger';
         labelClass = 'danger';
+        break;
       case 'running':
       case 'running':
         labelClass = 'warning';
         labelClass = 'warning';
+        break;
       case 'completed':
       case 'completed':
         labelClass = 'success';
         labelClass = 'success';
+        break;
     }
     }
     element.setAttribute('class', `badge bg-${labelClass}`);
     element.setAttribute('class', `badge bg-${labelClass}`);
     element.innerText = status.label;
     element.innerText = status.label;

+ 1 - 1
netbox/project-static/src/links.ts

@@ -3,7 +3,7 @@ import { isTruthy, getElements } from './util';
 /**
 /**
  * Allow any element to be made "clickable" with the use of the `data-href` attribute.
  * 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]')) {
   for (const link of getElements('*[data-href]')) {
     const href = link.getAttribute('data-href');
     const href = link.getAttribute('data-href');
     if (isTruthy(href)) {
     if (isTruthy(href)) {

+ 2 - 2
netbox/project-static/src/netbox.ts

@@ -13,7 +13,7 @@ import { initSideNav } from './sidenav';
 import { initRackElevation } from './racks';
 import { initRackElevation } from './racks';
 import { initLinks } from './links';
 import { initLinks } from './links';
 
 
-function initDocument() {
+function initDocument(): void {
   for (const init of [
   for (const init of [
     initBootstrap,
     initBootstrap,
     initColorMode,
     initColorMode,
@@ -34,7 +34,7 @@ function initDocument() {
   }
   }
 }
 }
 
 
-function initWindow() {
+function initWindow(): void {
   const contentContainer = document.querySelector<HTMLElement>('.content-container');
   const contentContainer = document.querySelector<HTMLElement>('.content-container');
   if (contentContainer !== null) {
   if (contentContainer !== null) {
     // Focus the content container for accessible navigation.
     // Focus the content container for accessible navigation.

+ 1 - 1
netbox/project-static/src/racks.ts

@@ -67,7 +67,7 @@ function handleRackImageToggle(
  * Add onClick callback for toggling rack elevation images. Synchronize the image toggle button
  * Add onClick callback for toggling rack elevation images. Synchronize the image toggle button
  * text and display state of images with the local state.
  * text and display state of images with the local state.
  */
  */
-export function initRackElevation() {
+export function initRackElevation(): void {
   const initiallyHidden = rackImagesState.get('hidden');
   const initiallyHidden = rackImagesState.get('hidden');
   for (const button of getElements<HTMLButtonElement>('button.toggle-images')) {
   for (const button of getElements<HTMLButtonElement>('button.toggle-images')) {
     toggleRackImagesButton(initiallyHidden, button);
     toggleRackImagesButton(initiallyHidden, button);

+ 7 - 7
netbox/project-static/src/search.ts

@@ -8,7 +8,7 @@ import { getElements, getRowValues, findFirstAdjacent, isTruthy } from './util';
  * @param event "click" event for each dropdown item.
  * @param event "click" event for each dropdown item.
  * @param button Each dropdown item element.
  * @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 dropdown = event.currentTarget as HTMLButtonElement;
   const selectedValue = findFirstAdjacent<HTMLSpanElement>(dropdown, 'span.search-obj-selected');
   const selectedValue = findFirstAdjacent<HTMLSpanElement>(dropdown, 'span.search-obj-selected');
   const selectedType = findFirstAdjacent<HTMLInputElement>(dropdown, 'input.search-obj-type');
   const selectedType = findFirstAdjacent<HTMLInputElement>(dropdown, 'input.search-obj-type');
@@ -31,7 +31,7 @@ function handleSearchDropdownClick(event: Event, button: HTMLButtonElement) {
 /**
 /**
  * Initialize Search Bar Elements.
  * Initialize Search Bar Elements.
  */
  */
-function initSearchBar() {
+function initSearchBar(): void {
   for (const dropdown of getElements<HTMLUListElement>('.search-obj-selector')) {
   for (const dropdown of getElements<HTMLUListElement>('.search-obj-selector')) {
     for (const button of dropdown.querySelectorAll<HTMLButtonElement>(
     for (const button of dropdown.querySelectorAll<HTMLButtonElement>(
       'li > button.dropdown-item',
       'li > button.dropdown-item',
@@ -44,7 +44,7 @@ function initSearchBar() {
 /**
 /**
  * Initialize Interface Table Filter Elements.
  * Initialize Interface Table Filter Elements.
  */
  */
-function initInterfaceFilter() {
+function initInterfaceFilter(): void {
   for (const input of getElements<HTMLInputElement>('input.interface-filter')) {
   for (const input of getElements<HTMLInputElement>('input.interface-filter')) {
     const table = findFirstAdjacent<HTMLTableElement>(input, 'table');
     const table = findFirstAdjacent<HTMLTableElement>(input, 'table');
     const rows = Array.from(
     const rows = Array.from(
@@ -53,7 +53,7 @@ function initInterfaceFilter() {
     /**
     /**
      * Filter on-page table by input text.
      * Filter on-page table by input text.
      */
      */
-    function handleInput(event: Event) {
+    function handleInput(event: Event): void {
       const target = event.target as HTMLInputElement;
       const target = event.target as HTMLInputElement;
       // Create a regex pattern from the input search text to match against.
       // Create a regex pattern from the input search text to match against.
       const filter = new RegExp(target.value.toLowerCase().trim());
       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')) {
   for (const input of getElements<HTMLInputElement>('input.object-filter')) {
     // Find the first adjacent table element.
     // Find the first adjacent table element.
     const table = findFirstAdjacent<HTMLTableElement>(input, 'table');
     const table = findFirstAdjacent<HTMLTableElement>(input, 'table');
@@ -101,7 +101,7 @@ function initTableFilter() {
      * Filter table rows by matched input text.
      * Filter table rows by matched input text.
      * @param event
      * @param event
      */
      */
-    function handleInput(event: Event) {
+    function handleInput(event: Event): void {
       const target = event.target as HTMLInputElement;
       const target = event.target as HTMLInputElement;
 
 
       // Create a regex pattern from the input search text to match against.
       // 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]) {
   for (const func of [initSearchBar, initTableFilter, initInterfaceFilter]) {
     func();
     func();
   }
   }

+ 4 - 3
netbox/project-static/src/select/api.ts

@@ -711,7 +711,7 @@ class APISelect {
    * @param id DOM ID of the other element.
    * @param id DOM ID of the other element.
    */
    */
   private updatePathValues(id: string): void {
   private updatePathValues(id: string): void {
-    let key = id.replaceAll(/^id_/gi, '');
+    const key = id.replaceAll(/^id_/gi, '');
     const element = getElement<HTMLSelectElement>(`id_${key}`);
     const element = getElement<HTMLSelectElement>(`id_${key}`);
     if (element !== null) {
     if (element !== null) {
       // If this element's URL contains Django template tags ({{), replace the template tag
       // If this element's URL contains Django template tags ({{), replace the template tag
@@ -982,15 +982,16 @@ class APISelect {
         'button',
         'button',
         { type: 'button' },
         { type: 'button' },
         ['btn', 'btn-sm', 'btn-ghost-dark'],
         ['btn', 'btn-sm', 'btn-ghost-dark'],
-        [createElement('i', {}, ['mdi', 'mdi-reload'])],
+        [createElement('i', null, ['mdi', 'mdi-reload'])],
       );
       );
       refreshButton.addEventListener('click', () => this.loadData());
       refreshButton.addEventListener('click', () => this.loadData());
+      refreshButton.type = 'button';
       this.slim.slim.search.container.appendChild(refreshButton);
       this.slim.slim.search.container.appendChild(refreshButton);
     }
     }
   }
   }
 }
 }
 
 
-export function initApiSelect() {
+export function initApiSelect(): void {
   for (const select of getElements<HTMLSelectElement>('.netbox-api-select')) {
   for (const select of getElements<HTMLSelectElement>('.netbox-api-select')) {
     new APISelect(select);
     new APISelect(select);
   }
   }

+ 26 - 20
netbox/project-static/src/select/color.ts

@@ -11,6 +11,30 @@ function canChangeColor(option: Option | HTMLOptionElement): boolean {
   return typeof option.value === 'string' && option.value !== '';
   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
  * Initialize color selection widget. Dynamically change the style of the select container to match
  * the selected option.
  * the selected option.
@@ -40,7 +64,7 @@ export function initColorSelect(): void {
     // Style the select container to match any pre-selectd options.
     // Style the select container to match any pre-selectd options.
     for (const option of instance.data.data) {
     for (const option of instance.data.data) {
       if ('selected' in option && option.selected) {
       if ('selected' in option && option.selected) {
-        styleContainer(option);
+        styleContainer(instance, option);
         break;
         break;
       }
       }
     }
     }
@@ -50,25 +74,7 @@ export function initColorSelect(): void {
       instance.slim.container.classList.remove(className);
       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.
     // Change the SlimSelect container's style based on the selected option.
-    instance.onChange = styleContainer;
+    instance.onChange = option => styleContainer(instance, option);
   }
   }
 }
 }

+ 1 - 1
netbox/project-static/src/select/index.ts

@@ -2,7 +2,7 @@ import { initApiSelect } from './api';
 import { initColorSelect } from './color';
 import { initColorSelect } from './color';
 import { initStaticSelect } from './static';
 import { initStaticSelect } from './static';
 
 
-export function initSelect() {
+export function initSelect(): void {
   for (const func of [initApiSelect, initColorSelect, initStaticSelect]) {
   for (const func of [initApiSelect, initColorSelect, initStaticSelect]) {
     func();
     func();
   }
   }

+ 1 - 1
netbox/project-static/src/select/static.ts

@@ -1,7 +1,7 @@
 import SlimSelect from 'slim-select';
 import SlimSelect from 'slim-select';
 import { getElements } from '../util';
 import { getElements } from '../util';
 
 
-export function initStaticSelect() {
+export function initStaticSelect(): void {
   for (const select of getElements<HTMLSelectElement>('.netbox-static-select')) {
   for (const select of getElements<HTMLSelectElement>('.netbox-static-select')) {
     if (select !== null) {
     if (select !== null) {
       const label = document.querySelector(`label[for=${select.id}]`) as HTMLLabelElement;
       const label = document.querySelector(`label[for=${select.id}]`) as HTMLLabelElement;

+ 2 - 2
netbox/project-static/src/sidenav.ts

@@ -237,7 +237,7 @@ class SideNav {
       '.navbar-nav .nav .nav-item a.nav-link',
       '.navbar-nav .nav .nav-item a.nav-link',
     )) {
     )) {
       const href = new RegExp(link.href, 'gi');
       const href = new RegExp(link.href, 'gi');
-      if (Boolean(window.location.href.match(href))) {
+      if (window.location.href.match(href)) {
         yield link;
         yield link;
       }
       }
     }
     }
@@ -310,7 +310,7 @@ class SideNav {
   }
   }
 }
 }
 
 
-export function initSideNav() {
+export function initSideNav(): void {
   for (const sidenav of getElements<HTMLDivElement>('.sidenav')) {
   for (const sidenav of getElements<HTMLDivElement>('.sidenav')) {
     new SideNav(sidenav);
     new SideNav(sidenav);
   }
   }

+ 7 - 7
netbox/project-static/src/tableConfig.ts

@@ -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
  * Mark each option element in the selected columns element as 'selected' so they are submitted to
  * the API.
  * the API.
  */
  */
-function saveTableConfig() {
+function saveTableConfig(): void {
   for (const element of getElements<HTMLOptionElement>('select[name="columns"] option')) {
   for (const element of getElements<HTMLOptionElement>('select[name="columns"] option')) {
     element.selected = true;
     element.selected = true;
   }
   }
@@ -14,7 +14,7 @@ function saveTableConfig() {
 /**
 /**
  * Delete all selected columns, which reverts the user's preferences to the default column set.
  * 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"]')) {
   for (const element of getElements<HTMLSelectElement>('select[name="columns"]')) {
     element.value = '';
     element.value = '';
   }
   }
@@ -23,7 +23,7 @@ function resetTableConfig() {
 /**
 /**
  * Add columns to the table config select element.
  * 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')) {
   for (const selectedOption of getElements<HTMLOptionElement>('#id_available_columns > option')) {
     if (selectedOption.selected) {
     if (selectedOption.selected) {
       for (const selected of getElements<HTMLSelectElement>('#id_columns')) {
       for (const selected of getElements<HTMLSelectElement>('#id_columns')) {
@@ -38,7 +38,7 @@ function addColumns(event: Event) {
 /**
 /**
  * Remove columns from the table config select element.
  * 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')) {
   for (const selectedOption of getElements<HTMLOptionElement>('#id_columns > option')) {
     if (selectedOption.selected) {
     if (selectedOption.selected) {
       for (const available of getElements<HTMLSelectElement>('#id_available_columns')) {
       for (const available of getElements<HTMLSelectElement>('#id_available_columns')) {
@@ -53,7 +53,7 @@ function removeColumns(event: Event) {
 /**
 /**
  * Submit form configuration to the NetBox API.
  * 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);
   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
  * Handle table config form submission. Sends the selected columns to the NetBox API to update
  * the user's table configuration preferences.
  * the user's table configuration preferences.
  */
  */
-function handleSubmit(event: Event) {
+function handleSubmit(event: Event): void {
   event.preventDefault();
   event.preventDefault();
 
 
   const element = event.currentTarget as HTMLFormElement;
   const element = event.currentTarget as HTMLFormElement;
@@ -96,7 +96,7 @@ function handleSubmit(event: Event) {
 /**
 /**
  * Initialize table configuration elements.
  * Initialize table configuration elements.
  */
  */
-export function initTableConfig() {
+export function initTableConfig(): void {
   for (const element of getElements<HTMLButtonElement>('#save_tableconfig')) {
   for (const element of getElements<HTMLButtonElement>('#save_tableconfig')) {
     element.addEventListener('click', saveTableConfig);
     element.addEventListener('click', saveTableConfig);
   }
   }

+ 3 - 7
netbox/project-static/src/tables/interfaceTable.ts

@@ -164,18 +164,14 @@ class TableState {
   private table: HTMLTableElement;
   private table: HTMLTableElement;
   /**
   /**
    * Instance of ButtonState for the 'show/hide enabled rows' button.
    * 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;
   private enabledButton: ButtonState;
 
 
   /**
   /**
    * Instance of ButtonState for the 'show/hide disabled rows' button.
    * 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;
   private disabledButton: ButtonState;
 
 
   /**
   /**
@@ -288,7 +284,7 @@ class TableState {
 /**
 /**
  * Initialize table states.
  * Initialize table states.
  */
  */
-export function initInterfaceTable() {
+export function initInterfaceTable(): void {
   for (const element of getElements<HTMLTableElement>('table')) {
   for (const element of getElements<HTMLTableElement>('table')) {
     new TableState(element);
     new TableState(element);
   }
   }

+ 32 - 31
netbox/project-static/src/util.ts

@@ -1,19 +1,18 @@
 import Cookie from 'cookie';
 import Cookie from 'cookie';
 
 
-type APIRes<T> = T | ErrorBase | APIError;
 type Method = 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
 type Method = 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
 type ReqData = URLSearchParams | Dict | undefined | unknown;
 type ReqData = URLSearchParams | Dict | undefined | unknown;
 type SelectedOption = { name: string; options: string[] };
 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 {
 export function isApiError(data: Record<string, unknown>): data is APIError {
   return 'error' in data && 'exception' in data;
   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 {
 export function slugify(slug: string, chars: number): string {
   return slug
   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
     .toLowerCase() // Convert to lowercase
     .substring(0, chars); // Trim to first chars chars
     .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,
   url: string,
   method: Method,
   method: Method,
   data?: D,
   data?: D,
-): Promise<APIRes<R>> {
+): Promise<APIResponse<R>> {
   const token = getCsrfToken();
   const token = getCsrfToken();
   const headers = new Headers({ 'X-CSRFToken': token });
   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>(
 export async function apiPatch<R extends Dict, D extends ReqData = Dict>(
   url: string,
   url: string,
   data: D,
   data: D,
-): Promise<APIRes<R>> {
+): Promise<APIResponse<R>> {
   return await apiRequest(url, 'PATCH', data);
   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');
   return await apiRequest<R>(url, 'GET');
 }
 }
 
 
 export async function apiPostForm<R extends Dict, D extends Dict>(
 export async function apiPostForm<R extends Dict, D extends Dict>(
   url: string,
   url: string,
   data: D,
   data: D,
-): Promise<APIRes<R>> {
+): Promise<APIResponse<R>> {
   const body = new URLSearchParams();
   const body = new URLSearchParams();
   for (const [k, v] of Object.entries(data)) {
   for (const [k, v] of Object.entries(data)) {
     body.append(k, String(v));
     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<E extends Element>(...key: string[]): Generator<E>;
 export function* getElements(
 export function* getElements(
   ...key: (string | keyof HTMLElementTagNameMap | keyof SVGElementTagNameMap)[]
   ...key: (string | keyof HTMLElementTagNameMap | keyof SVGElementTagNameMap)[]
-) {
+): Generator<Element> {
   for (const query of key) {
   for (const query of key) {
     for (const element of document.querySelectorAll(query)) {
     for (const element of document.querySelectorAll(query)) {
       if (element !== null) {
       if (element !== null) {
@@ -249,7 +248,7 @@ export function getNetboxData(key: string): string | null {
 /**
 /**
  * Toggle visibility of card loader.
  * 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')) {
   for (const element of getElements<HTMLDivElement>('div.card-overlay')) {
     if (action === 'show') {
     if (action === 'show') {
       element.classList.remove('d-none');
       element.classList.remove('d-none');
@@ -316,25 +315,27 @@ export function findFirstAdjacent<R extends HTMLElement, B extends Element = Ele
  * @param children Child elements.
  * @param children Child elements.
  */
  */
 export function createElement<
 export function createElement<
+  // Element name.
   T extends keyof HTMLElementTagNameMap,
   T extends keyof HTMLElementTagNameMap,
+  // Element props.
+  P extends InferredProps<T>,
+  // Child element type.
   C extends HTMLElement = HTMLElement
   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.
   // Create the base element.
   const element = document.createElement<T>(tag);
   const element = document.createElement<T>(tag);
 
 
-  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];
-    if (key in element) {
-      element[key] = value;
+  if (properties !== null) {
+    for (const k of Object.keys(properties)) {
+      // Add each property to the element.
+      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.
   // Add each CSS class to the element's class list.
   element.classList.add(...classes);
   element.classList.add(...classes);
 
 

+ 1 - 1
netbox/project-static/styles/overrides.scss

@@ -37,4 +37,4 @@ a[type='button'] {
 
 
 .badge {
 .badge {
   font-size: $font-size-xs;
   font-size: $font-size-xs;
-}
+}

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů