539 lines
24 KiB
JavaScript
Executable File
539 lines
24 KiB
JavaScript
Executable File
export { CdkMonitorFocus, FOCUS_MONITOR_DEFAULT_OPTIONS, FocusMonitor, FocusMonitorDetectionMode, INPUT_MODALITY_DETECTOR_DEFAULT_OPTIONS, INPUT_MODALITY_DETECTOR_OPTIONS, InputModalityDetector } from './focus-monitor.mjs';
|
|
import { FocusTrap, InteractivityChecker } from './a11y-module.mjs';
|
|
export { A11yModule, CdkAriaLive, CdkTrapFocus, FocusTrapFactory, HighContrastMode, HighContrastModeDetector, IsFocusableConfig, LIVE_ANNOUNCER_DEFAULT_OPTIONS, LIVE_ANNOUNCER_ELEMENT_TOKEN, LIVE_ANNOUNCER_ELEMENT_TOKEN_FACTORY, LiveAnnouncer } from './a11y-module.mjs';
|
|
export { _IdGenerator } from './id-generator.mjs';
|
|
import * as i0 from '@angular/core';
|
|
import { inject, DOCUMENT, APP_ID, Injectable, InjectionToken, NgZone, Injector } from '@angular/core';
|
|
import { Platform } from './platform2.mjs';
|
|
import { _CdkPrivateStyleLoader } from './style-loader.mjs';
|
|
import { _VisuallyHiddenLoader } from './private.mjs';
|
|
export { ActiveDescendantKeyManager } from './activedescendant-key-manager.mjs';
|
|
export { FocusKeyManager } from './focus-key-manager.mjs';
|
|
export { ListKeyManager } from './list-key-manager.mjs';
|
|
import { Subject } from 'rxjs';
|
|
import { TREE_KEY_MANAGER } from './tree-key-manager.mjs';
|
|
export { TREE_KEY_MANAGER_FACTORY, TREE_KEY_MANAGER_FACTORY_PROVIDER, TreeKeyManager } from './tree-key-manager.mjs';
|
|
export { isFakeMousedownFromScreenReader, isFakeTouchstartFromScreenReader } from './fake-event-detection.mjs';
|
|
import 'rxjs/operators';
|
|
import './keycodes2.mjs';
|
|
import './shadow-dom.mjs';
|
|
import './passive-listeners.mjs';
|
|
import './element.mjs';
|
|
import './breakpoints-observer.mjs';
|
|
import './array.mjs';
|
|
import './observers.mjs';
|
|
import '@angular/common';
|
|
import './typeahead.mjs';
|
|
import './keycodes.mjs';
|
|
import './coercion/private.mjs';
|
|
|
|
/** IDs are delimited by an empty space, as per the spec. */
|
|
const ID_DELIMITER = ' ';
|
|
/**
|
|
* Adds the given ID to the specified ARIA attribute on an element.
|
|
* Used for attributes such as aria-labelledby, aria-owns, etc.
|
|
*/
|
|
function addAriaReferencedId(el, attr, id) {
|
|
const ids = getAriaReferenceIds(el, attr);
|
|
id = id.trim();
|
|
if (ids.some(existingId => existingId.trim() === id)) {
|
|
return;
|
|
}
|
|
ids.push(id);
|
|
el.setAttribute(attr, ids.join(ID_DELIMITER));
|
|
}
|
|
/**
|
|
* Removes the given ID from the specified ARIA attribute on an element.
|
|
* Used for attributes such as aria-labelledby, aria-owns, etc.
|
|
*/
|
|
function removeAriaReferencedId(el, attr, id) {
|
|
const ids = getAriaReferenceIds(el, attr);
|
|
id = id.trim();
|
|
const filteredIds = ids.filter(val => val !== id);
|
|
if (filteredIds.length) {
|
|
el.setAttribute(attr, filteredIds.join(ID_DELIMITER));
|
|
}
|
|
else {
|
|
el.removeAttribute(attr);
|
|
}
|
|
}
|
|
/**
|
|
* Gets the list of IDs referenced by the given ARIA attribute on an element.
|
|
* Used for attributes such as aria-labelledby, aria-owns, etc.
|
|
*/
|
|
function getAriaReferenceIds(el, attr) {
|
|
// Get string array of all individual ids (whitespace delimited) in the attribute value
|
|
const attrValue = el.getAttribute(attr);
|
|
return attrValue?.match(/\S+/g) ?? [];
|
|
}
|
|
|
|
/**
|
|
* ID used for the body container where all messages are appended.
|
|
* @deprecated No longer being used. To be removed.
|
|
* @breaking-change 14.0.0
|
|
*/
|
|
const MESSAGES_CONTAINER_ID = 'cdk-describedby-message-container';
|
|
/**
|
|
* ID prefix used for each created message element.
|
|
* @deprecated To be turned into a private variable.
|
|
* @breaking-change 14.0.0
|
|
*/
|
|
const CDK_DESCRIBEDBY_ID_PREFIX = 'cdk-describedby-message';
|
|
/**
|
|
* Attribute given to each host element that is described by a message element.
|
|
* @deprecated To be turned into a private variable.
|
|
* @breaking-change 14.0.0
|
|
*/
|
|
const CDK_DESCRIBEDBY_HOST_ATTRIBUTE = 'cdk-describedby-host';
|
|
/** Global incremental identifier for each registered message element. */
|
|
let nextId = 0;
|
|
/**
|
|
* Utility that creates visually hidden elements with a message content. Useful for elements that
|
|
* want to use aria-describedby to further describe themselves without adding additional visual
|
|
* content.
|
|
*/
|
|
class AriaDescriber {
|
|
_platform = inject(Platform);
|
|
_document = inject(DOCUMENT);
|
|
/** Map of all registered message elements that have been placed into the document. */
|
|
_messageRegistry = new Map();
|
|
/** Container for all registered messages. */
|
|
_messagesContainer = null;
|
|
/** Unique ID for the service. */
|
|
_id = `${nextId++}`;
|
|
constructor() {
|
|
inject(_CdkPrivateStyleLoader).load(_VisuallyHiddenLoader);
|
|
this._id = inject(APP_ID) + '-' + nextId++;
|
|
}
|
|
describe(hostElement, message, role) {
|
|
if (!this._canBeDescribed(hostElement, message)) {
|
|
return;
|
|
}
|
|
const key = getKey(message, role);
|
|
if (typeof message !== 'string') {
|
|
// We need to ensure that the element has an ID.
|
|
setMessageId(message, this._id);
|
|
this._messageRegistry.set(key, { messageElement: message, referenceCount: 0 });
|
|
}
|
|
else if (!this._messageRegistry.has(key)) {
|
|
this._createMessageElement(message, role);
|
|
}
|
|
if (!this._isElementDescribedByMessage(hostElement, key)) {
|
|
this._addMessageReference(hostElement, key);
|
|
}
|
|
}
|
|
removeDescription(hostElement, message, role) {
|
|
if (!message || !this._isElementNode(hostElement)) {
|
|
return;
|
|
}
|
|
const key = getKey(message, role);
|
|
if (this._isElementDescribedByMessage(hostElement, key)) {
|
|
this._removeMessageReference(hostElement, key);
|
|
}
|
|
// If the message is a string, it means that it's one that we created for the
|
|
// consumer so we can remove it safely, otherwise we should leave it in place.
|
|
if (typeof message === 'string') {
|
|
const registeredMessage = this._messageRegistry.get(key);
|
|
if (registeredMessage && registeredMessage.referenceCount === 0) {
|
|
this._deleteMessageElement(key);
|
|
}
|
|
}
|
|
if (this._messagesContainer?.childNodes.length === 0) {
|
|
this._messagesContainer.remove();
|
|
this._messagesContainer = null;
|
|
}
|
|
}
|
|
/** Unregisters all created message elements and removes the message container. */
|
|
ngOnDestroy() {
|
|
const describedElements = this._document.querySelectorAll(`[${CDK_DESCRIBEDBY_HOST_ATTRIBUTE}="${this._id}"]`);
|
|
for (let i = 0; i < describedElements.length; i++) {
|
|
this._removeCdkDescribedByReferenceIds(describedElements[i]);
|
|
describedElements[i].removeAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE);
|
|
}
|
|
this._messagesContainer?.remove();
|
|
this._messagesContainer = null;
|
|
this._messageRegistry.clear();
|
|
}
|
|
/**
|
|
* Creates a new element in the visually hidden message container element with the message
|
|
* as its content and adds it to the message registry.
|
|
*/
|
|
_createMessageElement(message, role) {
|
|
const messageElement = this._document.createElement('div');
|
|
setMessageId(messageElement, this._id);
|
|
messageElement.textContent = message;
|
|
if (role) {
|
|
messageElement.setAttribute('role', role);
|
|
}
|
|
this._createMessagesContainer();
|
|
this._messagesContainer.appendChild(messageElement);
|
|
this._messageRegistry.set(getKey(message, role), { messageElement, referenceCount: 0 });
|
|
}
|
|
/** Deletes the message element from the global messages container. */
|
|
_deleteMessageElement(key) {
|
|
this._messageRegistry.get(key)?.messageElement?.remove();
|
|
this._messageRegistry.delete(key);
|
|
}
|
|
/** Creates the global container for all aria-describedby messages. */
|
|
_createMessagesContainer() {
|
|
if (this._messagesContainer) {
|
|
return;
|
|
}
|
|
const containerClassName = 'cdk-describedby-message-container';
|
|
const serverContainers = this._document.querySelectorAll(`.${containerClassName}[platform="server"]`);
|
|
for (let i = 0; i < serverContainers.length; i++) {
|
|
// When going from the server to the client, we may end up in a situation where there's
|
|
// already a container on the page, but we don't have a reference to it. Clear the
|
|
// old container so we don't get duplicates. Doing this, instead of emptying the previous
|
|
// container, should be slightly faster.
|
|
serverContainers[i].remove();
|
|
}
|
|
const messagesContainer = this._document.createElement('div');
|
|
// We add `visibility: hidden` in order to prevent text in this container from
|
|
// being searchable by the browser's Ctrl + F functionality.
|
|
// Screen-readers will still read the description for elements with aria-describedby even
|
|
// when the description element is not visible.
|
|
messagesContainer.style.visibility = 'hidden';
|
|
// Even though we use `visibility: hidden`, we still apply `cdk-visually-hidden` so that
|
|
// the description element doesn't impact page layout.
|
|
messagesContainer.classList.add(containerClassName);
|
|
messagesContainer.classList.add('cdk-visually-hidden');
|
|
if (!this._platform.isBrowser) {
|
|
messagesContainer.setAttribute('platform', 'server');
|
|
}
|
|
this._document.body.appendChild(messagesContainer);
|
|
this._messagesContainer = messagesContainer;
|
|
}
|
|
/** Removes all cdk-describedby messages that are hosted through the element. */
|
|
_removeCdkDescribedByReferenceIds(element) {
|
|
// Remove all aria-describedby reference IDs that are prefixed by CDK_DESCRIBEDBY_ID_PREFIX
|
|
const originalReferenceIds = getAriaReferenceIds(element, 'aria-describedby').filter(id => id.indexOf(CDK_DESCRIBEDBY_ID_PREFIX) != 0);
|
|
element.setAttribute('aria-describedby', originalReferenceIds.join(' '));
|
|
}
|
|
/**
|
|
* Adds a message reference to the element using aria-describedby and increments the registered
|
|
* message's reference count.
|
|
*/
|
|
_addMessageReference(element, key) {
|
|
const registeredMessage = this._messageRegistry.get(key);
|
|
// Add the aria-describedby reference and set the
|
|
// describedby_host attribute to mark the element.
|
|
addAriaReferencedId(element, 'aria-describedby', registeredMessage.messageElement.id);
|
|
element.setAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE, this._id);
|
|
registeredMessage.referenceCount++;
|
|
}
|
|
/**
|
|
* Removes a message reference from the element using aria-describedby
|
|
* and decrements the registered message's reference count.
|
|
*/
|
|
_removeMessageReference(element, key) {
|
|
const registeredMessage = this._messageRegistry.get(key);
|
|
registeredMessage.referenceCount--;
|
|
removeAriaReferencedId(element, 'aria-describedby', registeredMessage.messageElement.id);
|
|
element.removeAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE);
|
|
}
|
|
/** Returns true if the element has been described by the provided message ID. */
|
|
_isElementDescribedByMessage(element, key) {
|
|
const referenceIds = getAriaReferenceIds(element, 'aria-describedby');
|
|
const registeredMessage = this._messageRegistry.get(key);
|
|
const messageId = registeredMessage && registeredMessage.messageElement.id;
|
|
return !!messageId && referenceIds.indexOf(messageId) != -1;
|
|
}
|
|
/** Determines whether a message can be described on a particular element. */
|
|
_canBeDescribed(element, message) {
|
|
if (!this._isElementNode(element)) {
|
|
return false;
|
|
}
|
|
if (message && typeof message === 'object') {
|
|
// We'd have to make some assumptions about the description element's text, if the consumer
|
|
// passed in an element. Assume that if an element is passed in, the consumer has verified
|
|
// that it can be used as a description.
|
|
return true;
|
|
}
|
|
const trimmedMessage = message == null ? '' : `${message}`.trim();
|
|
const ariaLabel = element.getAttribute('aria-label');
|
|
// We shouldn't set descriptions if they're exactly the same as the `aria-label` of the
|
|
// element, because screen readers will end up reading out the same text twice in a row.
|
|
return trimmedMessage ? !ariaLabel || ariaLabel.trim() !== trimmedMessage : false;
|
|
}
|
|
/** Checks whether a node is an Element node. */
|
|
_isElementNode(element) {
|
|
return element.nodeType === this._document.ELEMENT_NODE;
|
|
}
|
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: AriaDescriber, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: AriaDescriber, providedIn: 'root' });
|
|
}
|
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: AriaDescriber, decorators: [{
|
|
type: Injectable,
|
|
args: [{ providedIn: 'root' }]
|
|
}], ctorParameters: () => [] });
|
|
/** Gets a key that can be used to look messages up in the registry. */
|
|
function getKey(message, role) {
|
|
return typeof message === 'string' ? `${role || ''}/${message}` : message;
|
|
}
|
|
/** Assigns a unique ID to an element, if it doesn't have one already. */
|
|
function setMessageId(element, serviceId) {
|
|
if (!element.id) {
|
|
element.id = `${CDK_DESCRIBEDBY_ID_PREFIX}-${serviceId}-${nextId++}`;
|
|
}
|
|
}
|
|
|
|
// NoopTreeKeyManager is a "noop" implementation of TreeKeyMangerStrategy. Methods are noops. Does
|
|
// not emit to streams.
|
|
//
|
|
// Used for applications built before TreeKeyManager to opt-out of TreeKeyManager and revert to
|
|
// legacy behavior.
|
|
/**
|
|
* @docs-private
|
|
*
|
|
* Opt-out of Tree of key manager behavior.
|
|
*
|
|
* When provided, Tree has same focus management behavior as before TreeKeyManager was introduced.
|
|
* - Tree does not respond to keyboard interaction
|
|
* - Tree node allows tabindex to be set by Input binding
|
|
* - Tree node allows tabindex to be set by attribute binding
|
|
*
|
|
* @deprecated NoopTreeKeyManager deprecated. Use TreeKeyManager or inject a
|
|
* TreeKeyManagerStrategy instead. To be removed in a future version.
|
|
*
|
|
* @breaking-change 21.0.0
|
|
*/
|
|
class NoopTreeKeyManager {
|
|
_isNoopTreeKeyManager = true;
|
|
// Provide change as required by TreeKeyManagerStrategy. NoopTreeKeyManager is a "noop"
|
|
// implementation that does not emit to streams.
|
|
change = new Subject();
|
|
destroy() {
|
|
this.change.complete();
|
|
}
|
|
onKeydown() {
|
|
// noop
|
|
}
|
|
getActiveItemIndex() {
|
|
// Always return null. NoopTreeKeyManager is a "noop" implementation that does not maintain
|
|
// the active item.
|
|
return null;
|
|
}
|
|
getActiveItem() {
|
|
// Always return null. NoopTreeKeyManager is a "noop" implementation that does not maintain
|
|
// the active item.
|
|
return null;
|
|
}
|
|
focusItem() {
|
|
// noop
|
|
}
|
|
}
|
|
/**
|
|
* @docs-private
|
|
*
|
|
* Opt-out of Tree of key manager behavior.
|
|
*
|
|
* When provided, Tree has same focus management behavior as before TreeKeyManager was introduced.
|
|
* - Tree does not respond to keyboard interaction
|
|
* - Tree node allows tabindex to be set by Input binding
|
|
* - Tree node allows tabindex to be set by attribute binding
|
|
*
|
|
* @deprecated NoopTreeKeyManager deprecated. Use TreeKeyManager or inject a
|
|
* TreeKeyManagerStrategy instead. To be removed in a future version.
|
|
*
|
|
* @breaking-change 21.0.0
|
|
*/
|
|
function NOOP_TREE_KEY_MANAGER_FACTORY() {
|
|
return () => new NoopTreeKeyManager();
|
|
}
|
|
/**
|
|
* @docs-private
|
|
*
|
|
* Opt-out of Tree of key manager behavior.
|
|
*
|
|
* When provided, Tree has same focus management behavior as before TreeKeyManager was introduced.
|
|
* - Tree does not respond to keyboard interaction
|
|
* - Tree node allows tabindex to be set by Input binding
|
|
* - Tree node allows tabindex to be set by attribute binding
|
|
*
|
|
* @deprecated NoopTreeKeyManager deprecated. Use TreeKeyManager or inject a
|
|
* TreeKeyManagerStrategy instead. To be removed in a future version.
|
|
*
|
|
* @breaking-change 21.0.0
|
|
*/
|
|
const NOOP_TREE_KEY_MANAGER_FACTORY_PROVIDER = {
|
|
provide: TREE_KEY_MANAGER,
|
|
useFactory: NOOP_TREE_KEY_MANAGER_FACTORY,
|
|
};
|
|
|
|
/**
|
|
* Class that allows for trapping focus within a DOM element.
|
|
*
|
|
* This class uses a strategy pattern that determines how it traps focus.
|
|
* See FocusTrapInertStrategy.
|
|
*/
|
|
class ConfigurableFocusTrap extends FocusTrap {
|
|
_focusTrapManager;
|
|
_inertStrategy;
|
|
/** Whether the FocusTrap is enabled. */
|
|
get enabled() {
|
|
return this._enabled;
|
|
}
|
|
set enabled(value) {
|
|
this._enabled = value;
|
|
if (this._enabled) {
|
|
this._focusTrapManager.register(this);
|
|
}
|
|
else {
|
|
this._focusTrapManager.deregister(this);
|
|
}
|
|
}
|
|
constructor(_element, _checker, _ngZone, _document, _focusTrapManager, _inertStrategy, config, injector) {
|
|
super(_element, _checker, _ngZone, _document, config.defer, injector);
|
|
this._focusTrapManager = _focusTrapManager;
|
|
this._inertStrategy = _inertStrategy;
|
|
this._focusTrapManager.register(this);
|
|
}
|
|
/** Notifies the FocusTrapManager that this FocusTrap will be destroyed. */
|
|
destroy() {
|
|
this._focusTrapManager.deregister(this);
|
|
super.destroy();
|
|
}
|
|
/** @docs-private Implemented as part of ManagedFocusTrap. */
|
|
_enable() {
|
|
this._inertStrategy.preventFocus(this);
|
|
this.toggleAnchors(true);
|
|
}
|
|
/** @docs-private Implemented as part of ManagedFocusTrap. */
|
|
_disable() {
|
|
this._inertStrategy.allowFocus(this);
|
|
this.toggleAnchors(false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Lightweight FocusTrapInertStrategy that adds a document focus event
|
|
* listener to redirect focus back inside the FocusTrap.
|
|
*/
|
|
class EventListenerFocusTrapInertStrategy {
|
|
/** Focus event handler. */
|
|
_listener = null;
|
|
/** Adds a document event listener that keeps focus inside the FocusTrap. */
|
|
preventFocus(focusTrap) {
|
|
// Ensure there's only one listener per document
|
|
if (this._listener) {
|
|
focusTrap._document.removeEventListener('focus', this._listener, true);
|
|
}
|
|
this._listener = (e) => this._trapFocus(focusTrap, e);
|
|
focusTrap._ngZone.runOutsideAngular(() => {
|
|
focusTrap._document.addEventListener('focus', this._listener, true);
|
|
});
|
|
}
|
|
/** Removes the event listener added in preventFocus. */
|
|
allowFocus(focusTrap) {
|
|
if (!this._listener) {
|
|
return;
|
|
}
|
|
focusTrap._document.removeEventListener('focus', this._listener, true);
|
|
this._listener = null;
|
|
}
|
|
/**
|
|
* Refocuses the first element in the FocusTrap if the focus event target was outside
|
|
* the FocusTrap.
|
|
*
|
|
* This is an event listener callback. The event listener is added in runOutsideAngular,
|
|
* so all this code runs outside Angular as well.
|
|
*/
|
|
_trapFocus(focusTrap, event) {
|
|
const target = event.target;
|
|
const focusTrapRoot = focusTrap._element;
|
|
// Don't refocus if target was in an overlay, because the overlay might be associated
|
|
// with an element inside the FocusTrap, ex. mat-select.
|
|
if (target && !focusTrapRoot.contains(target) && !target.closest?.('div.cdk-overlay-pane')) {
|
|
// Some legacy FocusTrap usages have logic that focuses some element on the page
|
|
// just before FocusTrap is destroyed. For backwards compatibility, wait
|
|
// to be sure FocusTrap is still enabled before refocusing.
|
|
setTimeout(() => {
|
|
// Check whether focus wasn't put back into the focus trap while the timeout was pending.
|
|
if (focusTrap.enabled && !focusTrapRoot.contains(focusTrap._document.activeElement)) {
|
|
focusTrap.focusFirstTabbableElement();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/** The injection token used to specify the inert strategy. */
|
|
const FOCUS_TRAP_INERT_STRATEGY = new InjectionToken('FOCUS_TRAP_INERT_STRATEGY');
|
|
|
|
/** Injectable that ensures only the most recently enabled FocusTrap is active. */
|
|
class FocusTrapManager {
|
|
// A stack of the FocusTraps on the page. Only the FocusTrap at the
|
|
// top of the stack is active.
|
|
_focusTrapStack = [];
|
|
/**
|
|
* Disables the FocusTrap at the top of the stack, and then pushes
|
|
* the new FocusTrap onto the stack.
|
|
*/
|
|
register(focusTrap) {
|
|
// Dedupe focusTraps that register multiple times.
|
|
this._focusTrapStack = this._focusTrapStack.filter(ft => ft !== focusTrap);
|
|
let stack = this._focusTrapStack;
|
|
if (stack.length) {
|
|
stack[stack.length - 1]._disable();
|
|
}
|
|
stack.push(focusTrap);
|
|
focusTrap._enable();
|
|
}
|
|
/**
|
|
* Removes the FocusTrap from the stack, and activates the
|
|
* FocusTrap that is the new top of the stack.
|
|
*/
|
|
deregister(focusTrap) {
|
|
focusTrap._disable();
|
|
const stack = this._focusTrapStack;
|
|
const i = stack.indexOf(focusTrap);
|
|
if (i !== -1) {
|
|
stack.splice(i, 1);
|
|
if (stack.length) {
|
|
stack[stack.length - 1]._enable();
|
|
}
|
|
}
|
|
}
|
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FocusTrapManager, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FocusTrapManager, providedIn: 'root' });
|
|
}
|
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: FocusTrapManager, decorators: [{
|
|
type: Injectable,
|
|
args: [{ providedIn: 'root' }]
|
|
}] });
|
|
|
|
/** Factory that allows easy instantiation of configurable focus traps. */
|
|
class ConfigurableFocusTrapFactory {
|
|
_checker = inject(InteractivityChecker);
|
|
_ngZone = inject(NgZone);
|
|
_focusTrapManager = inject(FocusTrapManager);
|
|
_document = inject(DOCUMENT);
|
|
_inertStrategy;
|
|
_injector = inject(Injector);
|
|
constructor() {
|
|
const inertStrategy = inject(FOCUS_TRAP_INERT_STRATEGY, { optional: true });
|
|
// TODO split up the strategies into different modules, similar to DateAdapter.
|
|
this._inertStrategy = inertStrategy || new EventListenerFocusTrapInertStrategy();
|
|
}
|
|
create(element, config = { defer: false }) {
|
|
let configObject;
|
|
if (typeof config === 'boolean') {
|
|
configObject = { defer: config };
|
|
}
|
|
else {
|
|
configObject = config;
|
|
}
|
|
return new ConfigurableFocusTrap(element, this._checker, this._ngZone, this._document, this._focusTrapManager, this._inertStrategy, configObject, this._injector);
|
|
}
|
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: ConfigurableFocusTrapFactory, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: ConfigurableFocusTrapFactory, providedIn: 'root' });
|
|
}
|
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: ConfigurableFocusTrapFactory, decorators: [{
|
|
type: Injectable,
|
|
args: [{ providedIn: 'root' }]
|
|
}], ctorParameters: () => [] });
|
|
|
|
export { AriaDescriber, CDK_DESCRIBEDBY_HOST_ATTRIBUTE, CDK_DESCRIBEDBY_ID_PREFIX, ConfigurableFocusTrap, ConfigurableFocusTrapFactory, EventListenerFocusTrapInertStrategy, FOCUS_TRAP_INERT_STRATEGY, FocusTrap, InteractivityChecker, MESSAGES_CONTAINER_ID, NOOP_TREE_KEY_MANAGER_FACTORY, NOOP_TREE_KEY_MANAGER_FACTORY_PROVIDER, NoopTreeKeyManager, TREE_KEY_MANAGER, addAriaReferencedId, getAriaReferenceIds, removeAriaReferencedId };
|
|
//# sourceMappingURL=a11y.mjs.map
|