UNPKG

@angular/cdk

Version:

Angular Material Component Development Kit

630 lines (626 loc) 31.2 kB
import * as i0 from '@angular/core'; import { InjectionToken, inject, NgZone, RendererFactory2, Injectable, ElementRef, EventEmitter, Directive, Output } from '@angular/core'; import { BehaviorSubject, Subject, of } from 'rxjs'; import { skip, distinctUntilChanged, takeUntil } from 'rxjs/operators'; import { DOCUMENT } from '@angular/common'; import { i as isFakeMousedownFromScreenReader, a as isFakeTouchstartFromScreenReader } from './fake-event-detection-84590b88.mjs'; import { A as ALT, C as CONTROL, M as MAC_META, a as META, S as SHIFT } from './keycodes-0e4398c6.mjs'; import { _ as _getEventTarget, a as _getShadowRoot } from './shadow-dom-318658ae.mjs'; import { _ as _bindEventWithOptions } from './backwards-compatibility-08253a84.mjs'; import { P as Platform } from './platform-20fc4de8.mjs'; import { n as normalizePassiveListenerOptions } from './passive-listeners-93cf8be8.mjs'; import { c as coerceElement } from './element-15999318.mjs'; /** * Injectable options for the InputModalityDetector. These are shallowly merged with the default * options. */ const INPUT_MODALITY_DETECTOR_OPTIONS = new InjectionToken('cdk-input-modality-detector-options'); /** * Default options for the InputModalityDetector. * * Modifier keys are ignored by default (i.e. when pressed won't cause the service to detect * keyboard input modality) for two reasons: * * 1. Modifier keys are commonly used with mouse to perform actions such as 'right click' or 'open * in new tab', and are thus less representative of actual keyboard interaction. * 2. VoiceOver triggers some keyboard events when linearly navigating with Control + Option (but * confusingly not with Caps Lock). Thus, to have parity with other screen readers, we ignore * these keys so as to not update the input modality. * * Note that we do not by default ignore the right Meta key on Safari because it has the same key * code as the ContextMenu key on other browsers. When we switch to using event.key, we can * distinguish between the two. */ const INPUT_MODALITY_DETECTOR_DEFAULT_OPTIONS = { ignoreKeys: [ALT, CONTROL, MAC_META, META, SHIFT], }; /** * The amount of time needed to pass after a touchstart event in order for a subsequent mousedown * event to be attributed as mouse and not touch. * * This is the value used by AngularJS Material. Through trial and error (on iPhone 6S) they found * that a value of around 650ms seems appropriate. */ const TOUCH_BUFFER_MS = 650; /** * Event listener options that enable capturing and also mark the listener as passive if the browser * supports it. */ const modalityEventListenerOptions = { passive: true, capture: true, }; /** * Service that detects the user's input modality. * * This service does not update the input modality when a user navigates with a screen reader * (e.g. linear navigation with VoiceOver, object navigation / browse mode with NVDA, virtual PC * cursor mode with JAWS). This is in part due to technical limitations (i.e. keyboard events do not * fire as expected in these modes) but is also arguably the correct behavior. Navigating with a * screen reader is akin to visually scanning a page, and should not be interpreted as actual user * input interaction. * * When a user is not navigating but *interacting* with a screen reader, this service attempts to * update the input modality to keyboard, but in general this service's behavior is largely * undefined. */ class InputModalityDetector { _platform = inject(Platform); _listenerCleanups; /** Emits whenever an input modality is detected. */ modalityDetected; /** Emits when the input modality changes. */ modalityChanged; /** The most recently detected input modality. */ get mostRecentModality() { return this._modality.value; } /** * The most recently detected input modality event target. Is null if no input modality has been * detected or if the associated event target is null for some unknown reason. */ _mostRecentTarget = null; /** The underlying BehaviorSubject that emits whenever an input modality is detected. */ _modality = new BehaviorSubject(null); /** Options for this InputModalityDetector. */ _options; /** * The timestamp of the last touch input modality. Used to determine whether mousedown events * should be attributed to mouse or touch. */ _lastTouchMs = 0; /** * Handles keydown events. Must be an arrow function in order to preserve the context when it gets * bound. */ _onKeydown = (event) => { // If this is one of the keys we should ignore, then ignore it and don't update the input // modality to keyboard. if (this._options?.ignoreKeys?.some(keyCode => keyCode === event.keyCode)) { return; } this._modality.next('keyboard'); this._mostRecentTarget = _getEventTarget(event); }; /** * Handles mousedown events. Must be an arrow function in order to preserve the context when it * gets bound. */ _onMousedown = (event) => { // Touches trigger both touch and mouse events, so we need to distinguish between mouse events // that were triggered via mouse vs touch. To do so, check if the mouse event occurs closely // after the previous touch event. if (Date.now() - this._lastTouchMs < TOUCH_BUFFER_MS) { return; } // Fake mousedown events are fired by some screen readers when controls are activated by the // screen reader. Attribute them to keyboard input modality. this._modality.next(isFakeMousedownFromScreenReader(event) ? 'keyboard' : 'mouse'); this._mostRecentTarget = _getEventTarget(event); }; /** * Handles touchstart events. Must be an arrow function in order to preserve the context when it * gets bound. */ _onTouchstart = (event) => { // Same scenario as mentioned in _onMousedown, but on touch screen devices, fake touchstart // events are fired. Again, attribute to keyboard input modality. if (isFakeTouchstartFromScreenReader(event)) { this._modality.next('keyboard'); return; } // Store the timestamp of this touch event, as it's used to distinguish between mouse events // triggered via mouse vs touch. this._lastTouchMs = Date.now(); this._modality.next('touch'); this._mostRecentTarget = _getEventTarget(event); }; constructor() { const ngZone = inject(NgZone); const document = inject(DOCUMENT); const options = inject(INPUT_MODALITY_DETECTOR_OPTIONS, { optional: true }); this._options = { ...INPUT_MODALITY_DETECTOR_DEFAULT_OPTIONS, ...options, }; // Skip the first emission as it's null. this.modalityDetected = this._modality.pipe(skip(1)); this.modalityChanged = this.modalityDetected.pipe(distinctUntilChanged()); // If we're not in a browser, this service should do nothing, as there's no relevant input // modality to detect. if (this._platform.isBrowser) { const renderer = inject(RendererFactory2).createRenderer(null, null); this._listenerCleanups = ngZone.runOutsideAngular(() => { return [ _bindEventWithOptions(renderer, document, 'keydown', this._onKeydown, modalityEventListenerOptions), _bindEventWithOptions(renderer, document, 'mousedown', this._onMousedown, modalityEventListenerOptions), _bindEventWithOptions(renderer, document, 'touchstart', this._onTouchstart, modalityEventListenerOptions), ]; }); } } ngOnDestroy() { this._modality.complete(); this._listenerCleanups?.forEach(cleanup => cleanup()); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: InputModalityDetector, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: InputModalityDetector, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: InputModalityDetector, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [] }); /** Detection mode used for attributing the origin of a focus event. */ var FocusMonitorDetectionMode; (function (FocusMonitorDetectionMode) { /** * Any mousedown, keydown, or touchstart event that happened in the previous * tick or the current tick will be used to assign a focus event's origin (to * either mouse, keyboard, or touch). This is the default option. */ FocusMonitorDetectionMode[FocusMonitorDetectionMode["IMMEDIATE"] = 0] = "IMMEDIATE"; /** * A focus event's origin is always attributed to the last corresponding * mousedown, keydown, or touchstart event, no matter how long ago it occurred. */ FocusMonitorDetectionMode[FocusMonitorDetectionMode["EVENTUAL"] = 1] = "EVENTUAL"; })(FocusMonitorDetectionMode || (FocusMonitorDetectionMode = {})); /** InjectionToken for FocusMonitorOptions. */ const FOCUS_MONITOR_DEFAULT_OPTIONS = new InjectionToken('cdk-focus-monitor-default-options'); /** * Event listener options that enable capturing and also * mark the listener as passive if the browser supports it. */ const captureEventListenerOptions = normalizePassiveListenerOptions({ passive: true, capture: true, }); /** Monitors mouse and keyboard events to determine the cause of focus events. */ class FocusMonitor { _ngZone = inject(NgZone); _platform = inject(Platform); _inputModalityDetector = inject(InputModalityDetector); /** The focus origin that the next focus event is a result of. */ _origin = null; /** The FocusOrigin of the last focus event tracked by the FocusMonitor. */ _lastFocusOrigin; /** Whether the window has just been focused. */ _windowFocused = false; /** The timeout id of the window focus timeout. */ _windowFocusTimeoutId; /** The timeout id of the origin clearing timeout. */ _originTimeoutId; /** * Whether the origin was determined via a touch interaction. Necessary as properly attributing * focus events to touch interactions requires special logic. */ _originFromTouchInteraction = false; /** Map of elements being monitored to their info. */ _elementInfo = new Map(); /** The number of elements currently being monitored. */ _monitoredElementCount = 0; /** * Keeps track of the root nodes to which we've currently bound a focus/blur handler, * as well as the number of monitored elements that they contain. We have to treat focus/blur * handlers differently from the rest of the events, because the browser won't emit events * to the document when focus moves inside of a shadow root. */ _rootNodeFocusListenerCount = new Map(); /** * The specified detection mode, used for attributing the origin of a focus * event. */ _detectionMode; /** * Event listener for `focus` events on the window. * Needs to be an arrow function in order to preserve the context when it gets bound. */ _windowFocusListener = () => { // Make a note of when the window regains focus, so we can // restore the origin info for the focused element. this._windowFocused = true; this._windowFocusTimeoutId = setTimeout(() => (this._windowFocused = false)); }; /** Used to reference correct document/window */ _document = inject(DOCUMENT, { optional: true }); /** Subject for stopping our InputModalityDetector subscription. */ _stopInputModalityDetector = new Subject(); constructor() { const options = inject(FOCUS_MONITOR_DEFAULT_OPTIONS, { optional: true, }); this._detectionMode = options?.detectionMode || FocusMonitorDetectionMode.IMMEDIATE; } /** * Event listener for `focus` and 'blur' events on the document. * Needs to be an arrow function in order to preserve the context when it gets bound. */ _rootNodeFocusAndBlurListener = (event) => { const target = _getEventTarget(event); // We need to walk up the ancestor chain in order to support `checkChildren`. for (let element = target; element; element = element.parentElement) { if (event.type === 'focus') { this._onFocus(event, element); } else { this._onBlur(event, element); } } }; monitor(element, checkChildren = false) { const nativeElement = coerceElement(element); // Do nothing if we're not on the browser platform or the passed in node isn't an element. if (!this._platform.isBrowser || nativeElement.nodeType !== 1) { // Note: we don't want the observable to emit at all so we don't pass any parameters. return of(); } // If the element is inside the shadow DOM, we need to bind our focus/blur listeners to // the shadow root, rather than the `document`, because the browser won't emit focus events // to the `document`, if focus is moving within the same shadow root. const rootNode = _getShadowRoot(nativeElement) || this._getDocument(); const cachedInfo = this._elementInfo.get(nativeElement); // Check if we're already monitoring this element. if (cachedInfo) { if (checkChildren) { // TODO(COMP-318): this can be problematic, because it'll turn all non-checkChildren // observers into ones that behave as if `checkChildren` was turned on. We need a more // robust solution. cachedInfo.checkChildren = true; } return cachedInfo.subject; } // Create monitored element info. const info = { checkChildren: checkChildren, subject: new Subject(), rootNode, }; this._elementInfo.set(nativeElement, info); this._registerGlobalListeners(info); return info.subject; } stopMonitoring(element) { const nativeElement = coerceElement(element); const elementInfo = this._elementInfo.get(nativeElement); if (elementInfo) { elementInfo.subject.complete(); this._setClasses(nativeElement); this._elementInfo.delete(nativeElement); this._removeGlobalListeners(elementInfo); } } focusVia(element, origin, options) { const nativeElement = coerceElement(element); const focusedElement = this._getDocument().activeElement; // If the element is focused already, calling `focus` again won't trigger the event listener // which means that the focus classes won't be updated. If that's the case, update the classes // directly without waiting for an event. if (nativeElement === focusedElement) { this._getClosestElementsInfo(nativeElement).forEach(([currentElement, info]) => this._originChanged(currentElement, origin, info)); } else { this._setOrigin(origin); // `focus` isn't available on the server if (typeof nativeElement.focus === 'function') { nativeElement.focus(options); } } } ngOnDestroy() { this._elementInfo.forEach((_info, element) => this.stopMonitoring(element)); } /** Access injected document if available or fallback to global document reference */ _getDocument() { return this._document || document; } /** Use defaultView of injected document if available or fallback to global window reference */ _getWindow() { const doc = this._getDocument(); return doc.defaultView || window; } _getFocusOrigin(focusEventTarget) { if (this._origin) { // If the origin was realized via a touch interaction, we need to perform additional checks // to determine whether the focus origin should be attributed to touch or program. if (this._originFromTouchInteraction) { return this._shouldBeAttributedToTouch(focusEventTarget) ? 'touch' : 'program'; } else { return this._origin; } } // If the window has just regained focus, we can restore the most recent origin from before the // window blurred. Otherwise, we've reached the point where we can't identify the source of the // focus. This typically means one of two things happened: // // 1) The element was programmatically focused, or // 2) The element was focused via screen reader navigation (which generally doesn't fire // events). // // Because we can't distinguish between these two cases, we default to setting `program`. if (this._windowFocused && this._lastFocusOrigin) { return this._lastFocusOrigin; } // If the interaction is coming from an input label, we consider it a mouse interactions. // This is a special case where focus moves on `click`, rather than `mousedown` which breaks // our detection, because all our assumptions are for `mousedown`. We need to handle this // special case, because it's very common for checkboxes and radio buttons. if (focusEventTarget && this._isLastInteractionFromInputLabel(focusEventTarget)) { return 'mouse'; } return 'program'; } /** * Returns whether the focus event should be attributed to touch. Recall that in IMMEDIATE mode, a * touch origin isn't immediately reset at the next tick (see _setOrigin). This means that when we * handle a focus event following a touch interaction, we need to determine whether (1) the focus * event was directly caused by the touch interaction or (2) the focus event was caused by a * subsequent programmatic focus call triggered by the touch interaction. * @param focusEventTarget The target of the focus event under examination. */ _shouldBeAttributedToTouch(focusEventTarget) { // Please note that this check is not perfect. Consider the following edge case: // // <div #parent tabindex="0"> // <div #child tabindex="0" (click)="#parent.focus()"></div> // </div> // // Suppose there is a FocusMonitor in IMMEDIATE mode attached to #parent. When the user touches // #child, #parent is programmatically focused. This code will attribute the focus to touch // instead of program. This is a relatively minor edge-case that can be worked around by using // focusVia(parent, 'program') to focus #parent. return (this._detectionMode === FocusMonitorDetectionMode.EVENTUAL || !!focusEventTarget?.contains(this._inputModalityDetector._mostRecentTarget)); } /** * Sets the focus classes on the element based on the given focus origin. * @param element The element to update the classes on. * @param origin The focus origin. */ _setClasses(element, origin) { element.classList.toggle('cdk-focused', !!origin); element.classList.toggle('cdk-touch-focused', origin === 'touch'); element.classList.toggle('cdk-keyboard-focused', origin === 'keyboard'); element.classList.toggle('cdk-mouse-focused', origin === 'mouse'); element.classList.toggle('cdk-program-focused', origin === 'program'); } /** * Updates the focus origin. If we're using immediate detection mode, we schedule an async * function to clear the origin at the end of a timeout. The duration of the timeout depends on * the origin being set. * @param origin The origin to set. * @param isFromInteraction Whether we are setting the origin from an interaction event. */ _setOrigin(origin, isFromInteraction = false) { this._ngZone.runOutsideAngular(() => { this._origin = origin; this._originFromTouchInteraction = origin === 'touch' && isFromInteraction; // If we're in IMMEDIATE mode, reset the origin at the next tick (or in `TOUCH_BUFFER_MS` ms // for a touch event). We reset the origin at the next tick because Firefox focuses one tick // after the interaction event. We wait `TOUCH_BUFFER_MS` ms before resetting the origin for // a touch event because when a touch event is fired, the associated focus event isn't yet in // the event queue. Before doing so, clear any pending timeouts. if (this._detectionMode === FocusMonitorDetectionMode.IMMEDIATE) { clearTimeout(this._originTimeoutId); const ms = this._originFromTouchInteraction ? TOUCH_BUFFER_MS : 1; this._originTimeoutId = setTimeout(() => (this._origin = null), ms); } }); } /** * Handles focus events on a registered element. * @param event The focus event. * @param element The monitored element. */ _onFocus(event, element) { // NOTE(mmalerba): We currently set the classes based on the focus origin of the most recent // focus event affecting the monitored element. If we want to use the origin of the first event // instead we should check for the cdk-focused class here and return if the element already has // it. (This only matters for elements that have includesChildren = true). // If we are not counting child-element-focus as focused, make sure that the event target is the // monitored element itself. const elementInfo = this._elementInfo.get(element); const focusEventTarget = _getEventTarget(event); if (!elementInfo || (!elementInfo.checkChildren && element !== focusEventTarget)) { return; } this._originChanged(element, this._getFocusOrigin(focusEventTarget), elementInfo); } /** * Handles blur events on a registered element. * @param event The blur event. * @param element The monitored element. */ _onBlur(event, element) { // If we are counting child-element-focus as focused, make sure that we aren't just blurring in // order to focus another child of the monitored element. const elementInfo = this._elementInfo.get(element); if (!elementInfo || (elementInfo.checkChildren && event.relatedTarget instanceof Node && element.contains(event.relatedTarget))) { return; } this._setClasses(element); this._emitOrigin(elementInfo, null); } _emitOrigin(info, origin) { if (info.subject.observers.length) { this._ngZone.run(() => info.subject.next(origin)); } } _registerGlobalListeners(elementInfo) { if (!this._platform.isBrowser) { return; } const rootNode = elementInfo.rootNode; const rootNodeFocusListeners = this._rootNodeFocusListenerCount.get(rootNode) || 0; if (!rootNodeFocusListeners) { this._ngZone.runOutsideAngular(() => { rootNode.addEventListener('focus', this._rootNodeFocusAndBlurListener, captureEventListenerOptions); rootNode.addEventListener('blur', this._rootNodeFocusAndBlurListener, captureEventListenerOptions); }); } this._rootNodeFocusListenerCount.set(rootNode, rootNodeFocusListeners + 1); // Register global listeners when first element is monitored. if (++this._monitoredElementCount === 1) { // Note: we listen to events in the capture phase so we // can detect them even if the user stops propagation. this._ngZone.runOutsideAngular(() => { const window = this._getWindow(); window.addEventListener('focus', this._windowFocusListener); }); // The InputModalityDetector is also just a collection of global listeners. this._inputModalityDetector.modalityDetected .pipe(takeUntil(this._stopInputModalityDetector)) .subscribe(modality => { this._setOrigin(modality, true /* isFromInteraction */); }); } } _removeGlobalListeners(elementInfo) { const rootNode = elementInfo.rootNode; if (this._rootNodeFocusListenerCount.has(rootNode)) { const rootNodeFocusListeners = this._rootNodeFocusListenerCount.get(rootNode); if (rootNodeFocusListeners > 1) { this._rootNodeFocusListenerCount.set(rootNode, rootNodeFocusListeners - 1); } else { rootNode.removeEventListener('focus', this._rootNodeFocusAndBlurListener, captureEventListenerOptions); rootNode.removeEventListener('blur', this._rootNodeFocusAndBlurListener, captureEventListenerOptions); this._rootNodeFocusListenerCount.delete(rootNode); } } // Unregister global listeners when last element is unmonitored. if (!--this._monitoredElementCount) { const window = this._getWindow(); window.removeEventListener('focus', this._windowFocusListener); // Equivalently, stop our InputModalityDetector subscription. this._stopInputModalityDetector.next(); // Clear timeouts for all potentially pending timeouts to prevent the leaks. clearTimeout(this._windowFocusTimeoutId); clearTimeout(this._originTimeoutId); } } /** Updates all the state on an element once its focus origin has changed. */ _originChanged(element, origin, elementInfo) { this._setClasses(element, origin); this._emitOrigin(elementInfo, origin); this._lastFocusOrigin = origin; } /** * Collects the `MonitoredElementInfo` of a particular element and * all of its ancestors that have enabled `checkChildren`. * @param element Element from which to start the search. */ _getClosestElementsInfo(element) { const results = []; this._elementInfo.forEach((info, currentElement) => { if (currentElement === element || (info.checkChildren && currentElement.contains(element))) { results.push([currentElement, info]); } }); return results; } /** * Returns whether an interaction is likely to have come from the user clicking the `label` of * an `input` or `textarea` in order to focus it. * @param focusEventTarget Target currently receiving focus. */ _isLastInteractionFromInputLabel(focusEventTarget) { const { _mostRecentTarget: mostRecentTarget, mostRecentModality } = this._inputModalityDetector; // If the last interaction used the mouse on an element contained by one of the labels // of an `input`/`textarea` that is currently focused, it is very likely that the // user redirected focus using the label. if (mostRecentModality !== 'mouse' || !mostRecentTarget || mostRecentTarget === focusEventTarget || (focusEventTarget.nodeName !== 'INPUT' && focusEventTarget.nodeName !== 'TEXTAREA') || focusEventTarget.disabled) { return false; } const labels = focusEventTarget.labels; if (labels) { for (let i = 0; i < labels.length; i++) { if (labels[i].contains(mostRecentTarget)) { return true; } } } return false; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: FocusMonitor, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: FocusMonitor, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: FocusMonitor, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [] }); /** * Directive that determines how a particular element was focused (via keyboard, mouse, touch, or * programmatically) and adds corresponding classes to the element. * * There are two variants of this directive: * 1) cdkMonitorElementFocus: does not consider an element to be focused if one of its children is * focused. * 2) cdkMonitorSubtreeFocus: considers an element focused if it or any of its children are focused. */ class CdkMonitorFocus { _elementRef = inject(ElementRef); _focusMonitor = inject(FocusMonitor); _monitorSubscription; _focusOrigin = null; cdkFocusChange = new EventEmitter(); constructor() { } get focusOrigin() { return this._focusOrigin; } ngAfterViewInit() { const element = this._elementRef.nativeElement; this._monitorSubscription = this._focusMonitor .monitor(element, element.nodeType === 1 && element.hasAttribute('cdkMonitorSubtreeFocus')) .subscribe(origin => { this._focusOrigin = origin; this.cdkFocusChange.emit(origin); }); } ngOnDestroy() { this._focusMonitor.stopMonitoring(this._elementRef); if (this._monitorSubscription) { this._monitorSubscription.unsubscribe(); } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: CdkMonitorFocus, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.0", type: CdkMonitorFocus, isStandalone: true, selector: "[cdkMonitorElementFocus], [cdkMonitorSubtreeFocus]", outputs: { cdkFocusChange: "cdkFocusChange" }, exportAs: ["cdkMonitorFocus"], ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: CdkMonitorFocus, decorators: [{ type: Directive, args: [{ selector: '[cdkMonitorElementFocus], [cdkMonitorSubtreeFocus]', exportAs: 'cdkMonitorFocus', }] }], ctorParameters: () => [], propDecorators: { cdkFocusChange: [{ type: Output }] } }); export { CdkMonitorFocus as C, FocusMonitorDetectionMode as F, InputModalityDetector as I, INPUT_MODALITY_DETECTOR_DEFAULT_OPTIONS as a, INPUT_MODALITY_DETECTOR_OPTIONS as b, FOCUS_MONITOR_DEFAULT_OPTIONS as c, FocusMonitor as d }; //# sourceMappingURL=focus-monitor-28b6c826.mjs.map