Просмотр исходного кода

Fixes #20077: Fix form field focus bug on Edge

Jeremy Stretch 2 дней назад
Родитель
Сommit
6f5fd26183

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/netbox.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/netbox.js.map


+ 3 - 3
netbox/project-static/src/select/classes/dynamicTomSelect.ts

@@ -1,16 +1,16 @@
 import type { RecursivePartial, TomOption, TomSettings, TomInput } from 'tom-select/dist/cjs/types';
 import { addClasses } from 'tom-select/src/vanilla.ts';
 import queryString from 'query-string';
-import TomSelect from 'tom-select';
 import type { Stringifiable } from 'query-string';
 import { DynamicParamsMap } from './dynamicParamsMap';
+import { NetBoxTomSelect } from './netboxTomSelect';
 
 // Transitional
 import { QueryFilter, PathFilter } from '../types';
 import { getElement, replaceAll } from '../../util';
 
-// Extends TomSelect to provide enhanced fetching of options via the REST API
-export class DynamicTomSelect extends TomSelect {
+// Extends NetBoxTomSelect to provide enhanced fetching of options via the REST API
+export class DynamicTomSelect extends NetBoxTomSelect {
   public readonly nullOption: Nullable<TomOption> = null;
 
   // Transitional code from APISelect

+ 39 - 0
netbox/project-static/src/select/classes/netboxTomSelect.ts

@@ -0,0 +1,39 @@
+import TomSelect from 'tom-select';
+
+/**
+ * Extends TomSelect to work around a browser autofill bug where Edge's "last used" autofill
+ * simultaneously focuses multiple inputs, triggering a cascading focus/open/blur loop between
+ * TomSelect instances.
+ *
+ * Root cause: TomSelect's open() method calls focus(), which synchronously moves browser focus
+ * to this instance's control input, then schedules setTimeout(onFocus, 0). When Edge autofill
+ * has moved focus to a *different* select before the timeout fires, the delayed onFocus() call
+ * re-steals browser focus back, causing the other instance to blur and close. Each instance's
+ * deferred callback then repeats this, creating an infinite ping-pong loop.
+ *
+ * Fix: in the setTimeout callback, only proceed with onFocus() if this instance's element is
+ * still the active element. If focus has already moved elsewhere, skip the call.
+ *
+ * Upstream bug: https://github.com/orchidjs/tom-select/issues/806
+ * NetBox issue:  https://github.com/netbox-community/netbox/issues/20077
+ */
+export class NetBoxTomSelect extends TomSelect {
+  focus(): void {
+    if (this.isDisabled || this.isReadOnly) return;
+
+    this.ignoreFocus = true;
+
+    const focusTarget = this.control_input.offsetWidth ? this.control_input : this.focus_node;
+    focusTarget.focus();
+
+    setTimeout(() => {
+      this.ignoreFocus = false;
+      // Only proceed if this instance's element is still the active element. If Edge autofill
+      // (or anything else) has moved focus to a different element in the interim, calling
+      // onFocus() here would steal focus back and restart the cascade loop.
+      if (document.activeElement === focusTarget || this.control.contains(document.activeElement)) {
+        this.onFocus();
+      }
+    }, 0);
+  }
+}

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

@@ -1,6 +1,6 @@
 import { TomOption } from 'tom-select/src/types';
-import TomSelect from 'tom-select';
 import { escape_html } from 'tom-select/src/utils';
+import { NetBoxTomSelect } from './classes/netboxTomSelect';
 import { getPlugins } from './config';
 import { getElements } from '../util';
 
@@ -9,7 +9,7 @@ export function initStaticSelects(): void {
   for (const select of getElements<HTMLSelectElement>(
     'select:not(.tomselected):not(.no-ts):not([size]):not(.api-select):not(.color-select)',
   )) {
-    new TomSelect(select, {
+    new NetBoxTomSelect(select, {
       ...getPlugins(select),
       maxOptions: undefined,
     });
@@ -25,7 +25,7 @@ export function initColorSelects(): void {
   }
 
   for (const select of getElements<HTMLSelectElement>('select.color-select:not(.tomselected)')) {
-    new TomSelect(select, {
+    new NetBoxTomSelect(select, {
       ...getPlugins(select),
       maxOptions: undefined,
       render: {

Некоторые файлы не были показаны из-за большого количества измененных файлов