UNPKG

@angular/cdk

Version:

Angular Material Component Development Kit

1,037 lines (1,028 loc) 129 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/common'), require('@angular/core'), require('@angular/cdk/platform'), require('rxjs'), require('@angular/cdk/keycodes'), require('rxjs/operators'), require('@angular/cdk/coercion'), require('@angular/cdk/observers')) : typeof define === 'function' && define.amd ? define('@angular/cdk/a11y', ['exports', '@angular/common', '@angular/core', '@angular/cdk/platform', 'rxjs', '@angular/cdk/keycodes', 'rxjs/operators', '@angular/cdk/coercion', '@angular/cdk/observers'], factory) : (global = global || self, factory((global.ng = global.ng || {}, global.ng.cdk = global.ng.cdk || {}, global.ng.cdk.a11y = {}), global.ng.common, global.ng.core, global.ng.cdk.platform, global.rxjs, global.ng.cdk.keycodes, global.rxjs.operators, global.ng.cdk.coercion, global.ng.cdk.observers)); }(this, (function (exports, i2, i0, i1, rxjs, keycodes, operators, coercion, observers) { 'use strict'; /** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** IDs are delimited by an empty space, as per the spec. */ var 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) { var ids = getAriaReferenceIds(el, attr); if (ids.some(function (existingId) { return existingId.trim() == id.trim(); })) { return; } ids.push(id.trim()); 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) { var ids = getAriaReferenceIds(el, attr); var filteredIds = ids.filter(function (val) { return val != id.trim(); }); 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 return (el.getAttribute(attr) || '').match(/\S+/g) || []; } /** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** ID used for the body container where all messages are appended. */ var MESSAGES_CONTAINER_ID = 'cdk-describedby-message-container'; /** ID prefix used for each created message element. */ var CDK_DESCRIBEDBY_ID_PREFIX = 'cdk-describedby-message'; /** Attribute given to each host element that is described by a message element. */ var CDK_DESCRIBEDBY_HOST_ATTRIBUTE = 'cdk-describedby-host'; /** Global incremental identifier for each registered message element. */ var nextId = 0; /** Global map of all registered message elements that have been placed into the document. */ var messageRegistry = new Map(); /** Container for all registered messages. */ var messagesContainer = null; /** * 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. */ var AriaDescriber = /** @class */ (function () { function AriaDescriber(_document, /** * @breaking-change 8.0.0 `_platform` parameter to be made required. */ _platform) { this._platform = _platform; this._document = _document; } /** * Adds to the host element an aria-describedby reference to a hidden element that contains * the message. If the same message has already been registered, then it will reuse the created * message element. */ AriaDescriber.prototype.describe = function (hostElement, message) { if (!this._canBeDescribed(hostElement, message)) { return; } if (typeof message !== 'string') { // We need to ensure that the element has an ID. this._setMessageId(message); messageRegistry.set(message, { messageElement: message, referenceCount: 0 }); } else if (!messageRegistry.has(message)) { this._createMessageElement(message); } if (!this._isElementDescribedByMessage(hostElement, message)) { this._addMessageReference(hostElement, message); } }; /** Removes the host element's aria-describedby reference to the message element. */ AriaDescriber.prototype.removeDescription = function (hostElement, message) { if (!message || !this._isElementNode(hostElement)) { return; } if (this._isElementDescribedByMessage(hostElement, message)) { this._removeMessageReference(hostElement, message); } // 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') { var registeredMessage = messageRegistry.get(message); if (registeredMessage && registeredMessage.referenceCount === 0) { this._deleteMessageElement(message); } } if (messagesContainer && messagesContainer.childNodes.length === 0) { this._deleteMessagesContainer(); } }; /** Unregisters all created message elements and removes the message container. */ AriaDescriber.prototype.ngOnDestroy = function () { var describedElements = this._document.querySelectorAll("[" + CDK_DESCRIBEDBY_HOST_ATTRIBUTE + "]"); for (var i = 0; i < describedElements.length; i++) { this._removeCdkDescribedByReferenceIds(describedElements[i]); describedElements[i].removeAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE); } if (messagesContainer) { this._deleteMessagesContainer(); } 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. */ AriaDescriber.prototype._createMessageElement = function (message) { var messageElement = this._document.createElement('div'); this._setMessageId(messageElement); messageElement.textContent = message; this._createMessagesContainer(); messagesContainer.appendChild(messageElement); messageRegistry.set(message, { messageElement: messageElement, referenceCount: 0 }); }; /** Assigns a unique ID to an element, if it doesn't have one already. */ AriaDescriber.prototype._setMessageId = function (element) { if (!element.id) { element.id = CDK_DESCRIBEDBY_ID_PREFIX + "-" + nextId++; } }; /** Deletes the message element from the global messages container. */ AriaDescriber.prototype._deleteMessageElement = function (message) { var registeredMessage = messageRegistry.get(message); var messageElement = registeredMessage && registeredMessage.messageElement; if (messagesContainer && messageElement) { messagesContainer.removeChild(messageElement); } messageRegistry.delete(message); }; /** Creates the global container for all aria-describedby messages. */ AriaDescriber.prototype._createMessagesContainer = function () { if (!messagesContainer) { // @breaking-change 8.0.0 `_platform` null check can be removed once the parameter is required var canBeAriaHidden = !this._platform || (!this._platform.EDGE && !this._platform.TRIDENT); var preExistingContainer = this._document.getElementById(MESSAGES_CONTAINER_ID); // 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. if (preExistingContainer && preExistingContainer.parentNode) { preExistingContainer.parentNode.removeChild(preExistingContainer); } messagesContainer = this._document.createElement('div'); messagesContainer.id = MESSAGES_CONTAINER_ID; messagesContainer.classList.add('cdk-visually-hidden'); // IE and Edge won't read out the messages if they're in an `aria-hidden` container. // We only disable `aria-hidden` for these platforms, because it comes with the // disadvantage that people might hit the messages when they've navigated past // the end of the document using the arrow keys. messagesContainer.setAttribute('aria-hidden', canBeAriaHidden + ''); this._document.body.appendChild(messagesContainer); } }; /** Deletes the global messages container. */ AriaDescriber.prototype._deleteMessagesContainer = function () { if (messagesContainer && messagesContainer.parentNode) { messagesContainer.parentNode.removeChild(messagesContainer); messagesContainer = null; } }; /** Removes all cdk-describedby messages that are hosted through the element. */ AriaDescriber.prototype._removeCdkDescribedByReferenceIds = function (element) { // Remove all aria-describedby reference IDs that are prefixed by CDK_DESCRIBEDBY_ID_PREFIX var originalReferenceIds = getAriaReferenceIds(element, 'aria-describedby') .filter(function (id) { return 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. */ AriaDescriber.prototype._addMessageReference = function (element, message) { var registeredMessage = messageRegistry.get(message); // 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, ''); registeredMessage.referenceCount++; }; /** * Removes a message reference from the element using aria-describedby * and decrements the registered message's reference count. */ AriaDescriber.prototype._removeMessageReference = function (element, message) { var registeredMessage = messageRegistry.get(message); 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. */ AriaDescriber.prototype._isElementDescribedByMessage = function (element, message) { var referenceIds = getAriaReferenceIds(element, 'aria-describedby'); var registeredMessage = messageRegistry.get(message); var messageId = registeredMessage && registeredMessage.messageElement.id; return !!messageId && referenceIds.indexOf(messageId) != -1; }; /** Determines whether a message can be described on a particular element. */ AriaDescriber.prototype._canBeDescribed = function (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; } var trimmedMessage = message == null ? '' : ("" + message).trim(); var 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. */ AriaDescriber.prototype._isElementNode = function (element) { return element.nodeType === this._document.ELEMENT_NODE; }; return AriaDescriber; }()); AriaDescriber.ɵprov = i0.ɵɵdefineInjectable({ factory: function AriaDescriber_Factory() { return new AriaDescriber(i0.ɵɵinject(i2.DOCUMENT), i0.ɵɵinject(i1.Platform)); }, token: AriaDescriber, providedIn: "root" }); AriaDescriber.decorators = [ { type: i0.Injectable, args: [{ providedIn: 'root' },] } ]; AriaDescriber.ctorParameters = function () { return [ { type: undefined, decorators: [{ type: i0.Inject, args: [i2.DOCUMENT,] }] }, { type: i1.Platform } ]; }; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function () { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; } function __decorate(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } function __param(paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); }; } function __metadata(metadataKey, metadataValue) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue); } function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function () { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function () { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } var __createBinding = Object.create ? (function (o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function () { return m[k]; } }); }) : (function (o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; }); function __exportStar(m, o) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p); } function __values(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); } function __read(o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; } function __spread() { for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); return ar; } function __spreadArrays() { for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; for (var r = Array(s), k = 0, i = 0; i < il; i++) for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; } ; function __await(v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } function __asyncGenerator(thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var g = generator.apply(thisArg, _arguments || []), i, q = []; return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } function fulfill(value) { resume("next", value); } function reject(value) { resume("throw", value); } function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } } function __asyncDelegator(o) { var i, p; return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; } } function __asyncValues(o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator], i; return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function (v) { resolve({ value: v, done: d }); }, reject); } } function __makeTemplateObject(cooked, raw) { if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } return cooked; } ; var __setModuleDefault = Object.create ? (function (o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function (o, v) { o["default"] = v; }; function __importStar(mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; } function __importDefault(mod) { return (mod && mod.__esModule) ? mod : { default: mod }; } function __classPrivateFieldGet(receiver, privateMap) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return privateMap.get(receiver); } function __classPrivateFieldSet(receiver, privateMap, value) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to set private field on non-instance"); } privateMap.set(receiver, value); return value; } /** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** * This class manages keyboard events for selectable lists. If you pass it a query list * of items, it will set the active item correctly when arrow events occur. */ var ListKeyManager = /** @class */ (function () { function ListKeyManager(_items) { var _this = this; this._items = _items; this._activeItemIndex = -1; this._activeItem = null; this._wrap = false; this._letterKeyStream = new rxjs.Subject(); this._typeaheadSubscription = rxjs.Subscription.EMPTY; this._vertical = true; this._allowedModifierKeys = []; this._homeAndEnd = false; /** * Predicate function that can be used to check whether an item should be skipped * by the key manager. By default, disabled items are skipped. */ this._skipPredicateFn = function (item) { return item.disabled; }; // Buffer for the letters that the user has pressed when the typeahead option is turned on. this._pressedLetters = []; /** * Stream that emits any time the TAB key is pressed, so components can react * when focus is shifted off of the list. */ this.tabOut = new rxjs.Subject(); /** Stream that emits whenever the active item of the list manager changes. */ this.change = new rxjs.Subject(); // We allow for the items to be an array because, in some cases, the consumer may // not have access to a QueryList of the items they want to manage (e.g. when the // items aren't being collected via `ViewChildren` or `ContentChildren`). if (_items instanceof i0.QueryList) { _items.changes.subscribe(function (newItems) { if (_this._activeItem) { var itemArray = newItems.toArray(); var newIndex = itemArray.indexOf(_this._activeItem); if (newIndex > -1 && newIndex !== _this._activeItemIndex) { _this._activeItemIndex = newIndex; } } }); } } /** * Sets the predicate function that determines which items should be skipped by the * list key manager. * @param predicate Function that determines whether the given item should be skipped. */ ListKeyManager.prototype.skipPredicate = function (predicate) { this._skipPredicateFn = predicate; return this; }; /** * Configures wrapping mode, which determines whether the active item will wrap to * the other end of list when there are no more items in the given direction. * @param shouldWrap Whether the list should wrap when reaching the end. */ ListKeyManager.prototype.withWrap = function (shouldWrap) { if (shouldWrap === void 0) { shouldWrap = true; } this._wrap = shouldWrap; return this; }; /** * Configures whether the key manager should be able to move the selection vertically. * @param enabled Whether vertical selection should be enabled. */ ListKeyManager.prototype.withVerticalOrientation = function (enabled) { if (enabled === void 0) { enabled = true; } this._vertical = enabled; return this; }; /** * Configures the key manager to move the selection horizontally. * Passing in `null` will disable horizontal movement. * @param direction Direction in which the selection can be moved. */ ListKeyManager.prototype.withHorizontalOrientation = function (direction) { this._horizontal = direction; return this; }; /** * Modifier keys which are allowed to be held down and whose default actions will be prevented * as the user is pressing the arrow keys. Defaults to not allowing any modifier keys. */ ListKeyManager.prototype.withAllowedModifierKeys = function (keys) { this._allowedModifierKeys = keys; return this; }; /** * Turns on typeahead mode which allows users to set the active item by typing. * @param debounceInterval Time to wait after the last keystroke before setting the active item. */ ListKeyManager.prototype.withTypeAhead = function (debounceInterval) { var _this = this; if (debounceInterval === void 0) { debounceInterval = 200; } if ((typeof ngDevMode === 'undefined' || ngDevMode) && (this._items.length && this._items.some(function (item) { return typeof item.getLabel !== 'function'; }))) { throw Error('ListKeyManager items in typeahead mode must implement the `getLabel` method.'); } this._typeaheadSubscription.unsubscribe(); // Debounce the presses of non-navigational keys, collect the ones that correspond to letters // and convert those letters back into a string. Afterwards find the first item that starts // with that string and select it. this._typeaheadSubscription = this._letterKeyStream.pipe(operators.tap(function (letter) { return _this._pressedLetters.push(letter); }), operators.debounceTime(debounceInterval), operators.filter(function () { return _this._pressedLetters.length > 0; }), operators.map(function () { return _this._pressedLetters.join(''); })).subscribe(function (inputString) { var items = _this._getItemsArray(); // Start at 1 because we want to start searching at the item immediately // following the current active item. for (var i = 1; i < items.length + 1; i++) { var index = (_this._activeItemIndex + i) % items.length; var item = items[index]; if (!_this._skipPredicateFn(item) && item.getLabel().toUpperCase().trim().indexOf(inputString) === 0) { _this.setActiveItem(index); break; } } _this._pressedLetters = []; }); return this; }; /** * Configures the key manager to activate the first and last items * respectively when the Home or End key is pressed. * @param enabled Whether pressing the Home or End key activates the first/last item. */ ListKeyManager.prototype.withHomeAndEnd = function (enabled) { if (enabled === void 0) { enabled = true; } this._homeAndEnd = enabled; return this; }; ListKeyManager.prototype.setActiveItem = function (item) { var previousActiveItem = this._activeItem; this.updateActiveItem(item); if (this._activeItem !== previousActiveItem) { this.change.next(this._activeItemIndex); } }; /** * Sets the active item depending on the key event passed in. * @param event Keyboard event to be used for determining which element should be active. */ ListKeyManager.prototype.onKeydown = function (event) { var _this = this; var keyCode = event.keyCode; var modifiers = ['altKey', 'ctrlKey', 'metaKey', 'shiftKey']; var isModifierAllowed = modifiers.every(function (modifier) { return !event[modifier] || _this._allowedModifierKeys.indexOf(modifier) > -1; }); switch (keyCode) { case keycodes.TAB: this.tabOut.next(); return; case keycodes.DOWN_ARROW: if (this._vertical && isModifierAllowed) { this.setNextItemActive(); break; } else { return; } case keycodes.UP_ARROW: if (this._vertical && isModifierAllowed) { this.setPreviousItemActive(); break; } else { return; } case keycodes.RIGHT_ARROW: if (this._horizontal && isModifierAllowed) { this._horizontal === 'rtl' ? this.setPreviousItemActive() : this.setNextItemActive(); break; } else { return; } case keycodes.LEFT_ARROW: if (this._horizontal && isModifierAllowed) { this._horizontal === 'rtl' ? this.setNextItemActive() : this.setPreviousItemActive(); break; } else { return; } case keycodes.HOME: if (this._homeAndEnd && isModifierAllowed) { this.setFirstItemActive(); break; } else { return; } case keycodes.END: if (this._homeAndEnd && isModifierAllowed) { this.setLastItemActive(); break; } else { return; } default: if (isModifierAllowed || keycodes.hasModifierKey(event, 'shiftKey')) { // Attempt to use the `event.key` which also maps it to the user's keyboard language, // otherwise fall back to resolving alphanumeric characters via the keyCode. if (event.key && event.key.length === 1) { this._letterKeyStream.next(event.key.toLocaleUpperCase()); } else if ((keyCode >= keycodes.A && keyCode <= keycodes.Z) || (keyCode >= keycodes.ZERO && keyCode <= keycodes.NINE)) { this._letterKeyStream.next(String.fromCharCode(keyCode)); } } // Note that we return here, in order to avoid preventing // the default action of non-navigational keys. return; } this._pressedLetters = []; event.preventDefault(); }; Object.defineProperty(ListKeyManager.prototype, "activeItemIndex", { /** Index of the currently active item. */ get: function () { return this._activeItemIndex; }, enumerable: false, configurable: true }); Object.defineProperty(ListKeyManager.prototype, "activeItem", { /** The active item. */ get: function () { return this._activeItem; }, enumerable: false, configurable: true }); /** Gets whether the user is currently typing into the manager using the typeahead feature. */ ListKeyManager.prototype.isTyping = function () { return this._pressedLetters.length > 0; }; /** Sets the active item to the first enabled item in the list. */ ListKeyManager.prototype.setFirstItemActive = function () { this._setActiveItemByIndex(0, 1); }; /** Sets the active item to the last enabled item in the list. */ ListKeyManager.prototype.setLastItemActive = function () { this._setActiveItemByIndex(this._items.length - 1, -1); }; /** Sets the active item to the next enabled item in the list. */ ListKeyManager.prototype.setNextItemActive = function () { this._activeItemIndex < 0 ? this.setFirstItemActive() : this._setActiveItemByDelta(1); }; /** Sets the active item to a previous enabled item in the list. */ ListKeyManager.prototype.setPreviousItemActive = function () { this._activeItemIndex < 0 && this._wrap ? this.setLastItemActive() : this._setActiveItemByDelta(-1); }; ListKeyManager.prototype.updateActiveItem = function (item) { var itemArray = this._getItemsArray(); var index = typeof item === 'number' ? item : itemArray.indexOf(item); var activeItem = itemArray[index]; // Explicitly check for `null` and `undefined` because other falsy values are valid. this._activeItem = activeItem == null ? null : activeItem; this._activeItemIndex = index; }; /** * This method sets the active item, given a list of items and the delta between the * currently active item and the new active item. It will calculate differently * depending on whether wrap mode is turned on. */ ListKeyManager.prototype._setActiveItemByDelta = function (delta) { this._wrap ? this._setActiveInWrapMode(delta) : this._setActiveInDefaultMode(delta); }; /** * Sets the active item properly given "wrap" mode. In other words, it will continue to move * down the list until it finds an item that is not disabled, and it will wrap if it * encounters either end of the list. */ ListKeyManager.prototype._setActiveInWrapMode = function (delta) { var items = this._getItemsArray(); for (var i = 1; i <= items.length; i++) { var index = (this._activeItemIndex + (delta * i) + items.length) % items.length; var item = items[index]; if (!this._skipPredicateFn(item)) { this.setActiveItem(index); return; } } }; /** * Sets the active item properly given the default mode. In other words, it will * continue to move down the list until it finds an item that is not disabled. If * it encounters either end of the list, it will stop and not wrap. */ ListKeyManager.prototype._setActiveInDefaultMode = function (delta) { this._setActiveItemByIndex(this._activeItemIndex + delta, delta); }; /** * Sets the active item to the first enabled item starting at the index specified. If the * item is disabled, it will move in the fallbackDelta direction until it either * finds an enabled item or encounters the end of the list. */ ListKeyManager.prototype._setActiveItemByIndex = function (index, fallbackDelta) { var items = this._getItemsArray(); if (!items[index]) { return; } while (this._skipPredicateFn(items[index])) { index += fallbackDelta; if (!items[index]) { return; } } this.setActiveItem(index); }; /** Returns the items as an array. */ ListKeyManager.prototype._getItemsArray = function () { return this._items instanceof i0.QueryList ? this._items.toArray() : this._items; }; return ListKeyManager; }()); var ActiveDescendantKeyManager = /** @class */ (function (_super) { __extends(ActiveDescendantKeyManager, _super); function ActiveDescendantKeyManager() { return _super !== null && _super.apply(this, arguments) || this; } ActiveDescendantKeyManager.prototype.setActiveItem = function (index) { if (this.activeItem) { this.activeItem.setInactiveStyles(); } _super.prototype.setActiveItem.call(this, index); if (this.activeItem) { this.activeItem.setActiveStyles(); } }; return ActiveDescendantKeyManager; }(ListKeyManager)); var FocusKeyManager = /** @class */ (function (_super) { __extends(FocusKeyManager, _super); function FocusKeyManager() { var _this = _super.apply(this, __spread(arguments)) || this; _this._origin = 'program'; return _this; } /** * Sets the focus origin that will be passed in to the items for any subsequent `focus` calls. * @param origin Focus origin to be used when focusing items. */ FocusKeyManager.prototype.setFocusOrigin = function (origin) { this._origin = origin; return this; }; FocusKeyManager.prototype.setActiveItem = function (item) { _super.prototype.setActiveItem.call(this, item); if (this.activeItem) { this.activeItem.focus(this._origin); } }; return FocusKeyManager; }(ListKeyManager)); /** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** * Configuration for the isFocusable method. */ var IsFocusableConfig = /** @class */ (function () { function IsFocusableConfig() { /** * Whether to count an element as focusable even if it is not currently visible. */ this.ignoreVisibility = false; } return IsFocusableConfig; }()); // The InteractivityChecker leans heavily on the ally.js accessibility utilities. // Methods like `isTabbable` are only covering specific edge-cases for the browsers which are // supported. /** * Utility for checking the interactivity of an element, such as whether is is focusable or * tabbable. */ var InteractivityChecker = /** @class */ (function () { function InteractivityChecker(_platform) { this._platform = _platform; } /** * Gets whether an element is disabled. * * @param element Element to be checked. * @returns Whether the element is disabled. */ InteractivityChecker.prototype.isDisabled = function (element) { // This does not capture some cases, such as a non-form control with a disabled attribute or // a form control inside of a disabled form, but should capture the most common cases. return element.hasAttribute('disabled'); }; /** * Gets whether an element is visible for the purposes of interactivity. * * This will capture states like `display: none` and `visibility: hidden`, but not things like * being clipped by an `overflow: hidden` parent or being outside the viewport. * * @returns Whether the element is visible. */ InteractivityChecker.prototype.isVisible = function (element) { return hasGeometry(element) && getComputedStyle(element).visibility === 'visible'; }; /** * Gets whether an element can be reached via Tab key. * Assumes that the element has already been checked with isFocusable. * * @param element Element to be checked. * @returns Whether the element is tabbable. */ InteractivityChecker.prototype.isTabbable = function (element) { // Nothing is tabbable on the server 😎 if (!this._platform.isBrowser) { return false; } var frameElement = getFrameElement(getWindow(element)); if (frameElement) { // Frame elements inherit their tabindex onto all child elements. if (getTabIndexValue(frameElement) === -1) { return false; } // Browsers disable tabbing to an element inside of an invisible frame. if (!this.isVisible(frameElement)) { return false; } } var nodeName = element.nodeName.toLowerCase(); var tabIndexValue = getTabIndexValue(element); if (element.hasAttribute('contenteditable')) { return tabIndexValue !== -1; } if (nodeName === 'iframe' || nodeName === 'object') { // The frame or object's content may be tabbable depending on the content, but it's // not possibly to reliably detect the content of the frames. We always consider such // elements as non-tabbable. return false; } // In iOS, the browser only considers some specific elements as tabbable. if (this._platform.WEBKIT && this._platform.IOS && !isPotentiallyTabbableIOS(element)) { return false; } if (nodeName === 'audio') { // Audio elements without controls enabled are never tabbable, regardless // of the tabindex attribute explicitly being set. if (!element.hasAttribute('controls')) { return false; } // Audio elements with controls are by default tabbable unless the // tabindex attribute is set to `-1` explicitly. return tabIndexValue !== -1; } if (nodeName === 'video') { // For all video elements, if the tabindex attribute is set to `-1`, the video // is not tabbable. Note: We cannot rely on the default `HTMLElement.tabIndex` // proper