@angular/cdk 12.1.0-next.0 → 12.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/a11y/focus-monitor/focus-monitor.d.ts +25 -31
  2. package/a11y/index.metadata.json +1 -1
  3. package/a11y/input-modality/input-modality-detector.d.ts +105 -0
  4. package/a11y/public-api.d.ts +1 -0
  5. package/bundles/cdk-a11y.umd.js +282 -146
  6. package/bundles/cdk-a11y.umd.js.map +1 -1
  7. package/bundles/cdk-drag-drop.umd.js +64 -20
  8. package/bundles/cdk-drag-drop.umd.js.map +1 -1
  9. package/bundles/cdk-overlay.umd.js +7 -3
  10. package/bundles/cdk-overlay.umd.js.map +1 -1
  11. package/bundles/cdk-portal.umd.js.map +1 -1
  12. package/bundles/cdk-scrolling.umd.js.map +1 -1
  13. package/bundles/cdk-stepper.umd.js +11 -7
  14. package/bundles/cdk-stepper.umd.js.map +1 -1
  15. package/bundles/cdk-table.umd.js.map +1 -1
  16. package/bundles/cdk-tree.umd.js +3 -5
  17. package/bundles/cdk-tree.umd.js.map +1 -1
  18. package/bundles/cdk.umd.js +1 -1
  19. package/bundles/cdk.umd.js.map +1 -1
  20. package/drag-drop/drag-drop-registry.d.ts +14 -2
  21. package/drag-drop/index.metadata.json +1 -1
  22. package/drag-drop/parent-position-tracker.d.ts +2 -0
  23. package/esm2015/a11y/focus-monitor/focus-monitor.js +81 -121
  24. package/esm2015/a11y/focus-trap/configurable-focus-trap.js +1 -1
  25. package/esm2015/a11y/input-modality/input-modality-detector.js +179 -0
  26. package/esm2015/a11y/key-manager/activedescendant-key-manager.js +1 -1
  27. package/esm2015/a11y/key-manager/focus-key-manager.js +1 -1
  28. package/esm2015/a11y/public-api.js +2 -1
  29. package/esm2015/drag-drop/drag-drop-registry.js +37 -3
  30. package/esm2015/drag-drop/drag-ref.js +22 -12
  31. package/esm2015/drag-drop/drop-list-ref.js +4 -2
  32. package/esm2015/drag-drop/parent-position-tracker.js +6 -2
  33. package/esm2015/overlay/dispatchers/overlay-keyboard-dispatcher.js +1 -1
  34. package/esm2015/overlay/dispatchers/overlay-outside-click-dispatcher.js +1 -1
  35. package/esm2015/overlay/fullscreen-overlay-container.js +1 -1
  36. package/esm2015/overlay/overlay-container.js +8 -4
  37. package/esm2015/portal/dom-portal-outlet.js +1 -1
  38. package/esm2015/portal/portal-directives.js +1 -1
  39. package/esm2015/portal/portal.js +1 -1
  40. package/esm2015/scrolling/virtual-scroll-viewport.js +1 -1
  41. package/esm2015/stepper/stepper.js +9 -4
  42. package/esm2015/table/row.js +1 -1
  43. package/esm2015/tree/control/flat-tree-control.js +1 -1
  44. package/esm2015/tree/control/nested-tree-control.js +1 -1
  45. package/esm2015/tree/nested-node.js +4 -6
  46. package/esm2015/version.js +1 -1
  47. package/fesm2015/a11y.js +276 -149
  48. package/fesm2015/a11y.js.map +1 -1
  49. package/fesm2015/cdk.js +1 -1
  50. package/fesm2015/cdk.js.map +1 -1
  51. package/fesm2015/drag-drop.js +63 -13
  52. package/fesm2015/drag-drop.js.map +1 -1
  53. package/fesm2015/overlay.js +7 -3
  54. package/fesm2015/overlay.js.map +1 -1
  55. package/fesm2015/portal.js.map +1 -1
  56. package/fesm2015/scrolling.js.map +1 -1
  57. package/fesm2015/stepper.js +8 -3
  58. package/fesm2015/stepper.js.map +1 -1
  59. package/fesm2015/table.js.map +1 -1
  60. package/fesm2015/tree.js +3 -5
  61. package/fesm2015/tree.js.map +1 -1
  62. package/overlay/index.metadata.json +1 -1
  63. package/package.json +1 -1
  64. package/schematics/migration.json +5 -0
  65. package/schematics/ng-add/index.js +1 -1
  66. package/schematics/ng-add/index.mjs +1 -1
  67. package/schematics/ng-add/package-config.js +5 -2
  68. package/schematics/ng-add/package-config.mjs +5 -2
  69. package/schematics/ng-update/devkit-file-system.d.ts +2 -2
  70. package/schematics/ng-update/devkit-file-system.js +20 -21
  71. package/schematics/ng-update/devkit-file-system.mjs +20 -21
  72. package/schematics/ng-update/find-stylesheets.js +2 -2
  73. package/schematics/ng-update/find-stylesheets.mjs +2 -2
  74. package/schematics/ng-update/html-parsing/elements.d.ts +2 -1
  75. package/schematics/ng-update/html-parsing/elements.js +5 -4
  76. package/schematics/ng-update/html-parsing/elements.mjs +5 -4
  77. package/schematics/ng-update/index.d.ts +2 -0
  78. package/schematics/ng-update/index.js +7 -2
  79. package/schematics/ng-update/index.mjs +7 -2
  80. package/schematics/ng-update/migrations/attribute-selectors.d.ts +3 -2
  81. package/schematics/ng-update/migrations/attribute-selectors.js +1 -1
  82. package/schematics/ng-update/migrations/attribute-selectors.mjs +1 -1
  83. package/schematics/ng-update/migrations/class-inheritance.js +1 -1
  84. package/schematics/ng-update/migrations/class-inheritance.mjs +1 -1
  85. package/schematics/ng-update/migrations/class-names.js +1 -1
  86. package/schematics/ng-update/migrations/class-names.mjs +1 -1
  87. package/schematics/ng-update/migrations/constructor-signature.js +1 -1
  88. package/schematics/ng-update/migrations/constructor-signature.mjs +1 -1
  89. package/schematics/ng-update/migrations/css-selectors.js +1 -1
  90. package/schematics/ng-update/migrations/css-selectors.mjs +1 -1
  91. package/schematics/ng-update/migrations/element-selectors.d.ts +3 -2
  92. package/schematics/ng-update/migrations/element-selectors.js +1 -1
  93. package/schematics/ng-update/migrations/element-selectors.mjs +1 -1
  94. package/schematics/ng-update/migrations/input-names.js +1 -1
  95. package/schematics/ng-update/migrations/input-names.mjs +1 -1
  96. package/schematics/ng-update/migrations/method-call-arguments.js +1 -1
  97. package/schematics/ng-update/migrations/method-call-arguments.mjs +1 -1
  98. package/schematics/ng-update/migrations/misc-template.js +1 -1
  99. package/schematics/ng-update/migrations/misc-template.mjs +1 -1
  100. package/schematics/ng-update/migrations/output-names.js +1 -1
  101. package/schematics/ng-update/migrations/output-names.mjs +1 -1
  102. package/schematics/ng-update/migrations/property-names.js +1 -1
  103. package/schematics/ng-update/migrations/property-names.mjs +1 -1
  104. package/schematics/tsconfig.json +5 -0
  105. package/schematics/update-tool/component-resource-collector.js +2 -2
  106. package/schematics/update-tool/component-resource-collector.mjs +2 -2
  107. package/schematics/update-tool/file-system.d.ts +4 -2
  108. package/schematics/update-tool/file-system.js +1 -1
  109. package/schematics/update-tool/file-system.mjs +1 -1
  110. package/schematics/update-tool/target-version.d.ts +2 -1
  111. package/schematics/update-tool/target-version.js +2 -1
  112. package/schematics/update-tool/target-version.mjs +2 -1
  113. package/schematics/update-tool/utils/virtual-host.js +3 -3
  114. package/schematics/update-tool/utils/virtual-host.mjs +3 -3
  115. package/schematics/utils/build-component.js +7 -3
  116. package/schematics/utils/build-component.mjs +7 -3
  117. package/schematics/utils/html-manipulation.d.ts +2 -2
  118. package/schematics/utils/html-manipulation.js +1 -1
  119. package/schematics/utils/html-manipulation.mjs +1 -1
  120. package/schematics/utils/parse5-element.d.ts +2 -2
  121. package/schematics/utils/parse5-element.js +2 -2
  122. package/schematics/utils/parse5-element.mjs +2 -2
  123. package/schematics/utils/schematic-options.d.ts +2 -6
  124. package/schematics/utils/schematic-options.js +3 -2
  125. package/schematics/utils/schematic-options.mjs +3 -2
  126. package/schematics/utils/vendored-ast-utils/index.js +8 -7
  127. package/schematics/utils/vendored-ast-utils/index.mjs +8 -7
  128. package/scrolling/index.metadata.json +1 -1
  129. package/stepper/index.metadata.json +1 -1
  130. package/stepper/stepper.d.ts +6 -2
  131. package/tree/index.metadata.json +1 -1
  132. package/tree/nested-node.d.ts +1 -3
package/fesm2015/a11y.js CHANGED
@@ -2,9 +2,9 @@ import * as i2 from '@angular/common';
2
2
  import { DOCUMENT } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
4
  import { Injectable, Inject, QueryList, NgZone, Directive, ElementRef, Input, InjectionToken, Optional, EventEmitter, Output, NgModule } from '@angular/core';
5
- import { Subject, Subscription, of } from 'rxjs';
6
- import { hasModifierKey, A, Z, ZERO, NINE, END, HOME, LEFT_ARROW, RIGHT_ARROW, UP_ARROW, DOWN_ARROW, TAB } from '@angular/cdk/keycodes';
7
- import { tap, debounceTime, filter, map, take } from 'rxjs/operators';
5
+ import { Subject, Subscription, BehaviorSubject, of } from 'rxjs';
6
+ import { hasModifierKey, A, Z, ZERO, NINE, END, HOME, LEFT_ARROW, RIGHT_ARROW, UP_ARROW, DOWN_ARROW, TAB, ALT, CONTROL, MAC_META, META, SHIFT } from '@angular/cdk/keycodes';
7
+ import { tap, debounceTime, filter, map, take, skip, distinctUntilChanged, takeUntil } from 'rxjs/operators';
8
8
  import { coerceBooleanProperty, coerceElement } from '@angular/cdk/coercion';
9
9
  import * as i1 from '@angular/cdk/platform';
10
10
  import { Platform, _getFocusedElementPierceShadowDom, normalizePassiveListenerOptions, _getShadowRoot, PlatformModule } from '@angular/cdk/platform';
@@ -1495,6 +1495,202 @@ ConfigurableFocusTrapFactory.ctorParameters = () => [
1495
1495
  { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [FOCUS_TRAP_INERT_STRATEGY,] }] }
1496
1496
  ];
1497
1497
 
1498
+ /**
1499
+ * @license
1500
+ * Copyright Google LLC All Rights Reserved.
1501
+ *
1502
+ * Use of this source code is governed by an MIT-style license that can be
1503
+ * found in the LICENSE file at https://angular.io/license
1504
+ */
1505
+ /** Gets whether an event could be a faked `mousedown` event dispatched by a screen reader. */
1506
+ function isFakeMousedownFromScreenReader(event) {
1507
+ // We can typically distinguish between these faked mousedown events and real mousedown events
1508
+ // using the "buttons" property. While real mousedowns will indicate the mouse button that was
1509
+ // pressed (e.g. "1" for the left mouse button), faked mousedowns will usually set the property
1510
+ // value to 0.
1511
+ return event.buttons === 0;
1512
+ }
1513
+ /** Gets whether an event could be a faked `touchstart` event dispatched by a screen reader. */
1514
+ function isFakeTouchstartFromScreenReader(event) {
1515
+ const touch = (event.touches && event.touches[0]) ||
1516
+ (event.changedTouches && event.changedTouches[0]);
1517
+ // A fake `touchstart` can be distinguished from a real one by looking at the `identifier`
1518
+ // which is typically >= 0 on a real device versus -1 from a screen reader. Just to be safe,
1519
+ // we can also look at `radiusX` and `radiusY`. This behavior was observed against a Windows 10
1520
+ // device with a touch screen running NVDA v2020.4 and Firefox 85 or Chrome 88.
1521
+ return !!touch && touch.identifier === -1 && (touch.radiusX == null || touch.radiusX === 1) &&
1522
+ (touch.radiusY == null || touch.radiusY === 1);
1523
+ }
1524
+
1525
+ /**
1526
+ * @license
1527
+ * Copyright Google LLC All Rights Reserved.
1528
+ *
1529
+ * Use of this source code is governed by an MIT-style license that can be
1530
+ * found in the LICENSE file at https://angular.io/license
1531
+ */
1532
+ /**
1533
+ * Injectable options for the InputModalityDetector. These are shallowly merged with the default
1534
+ * options.
1535
+ */
1536
+ const INPUT_MODALITY_DETECTOR_OPTIONS = new InjectionToken('cdk-input-modality-detector-options');
1537
+ /**
1538
+ * Default options for the InputModalityDetector.
1539
+ *
1540
+ * Modifier keys are ignored by default (i.e. when pressed won't cause the service to detect
1541
+ * keyboard input modality) for two reasons:
1542
+ *
1543
+ * 1. Modifier keys are commonly used with mouse to perform actions such as 'right click' or 'open
1544
+ * in new tab', and are thus less representative of actual keyboard interaction.
1545
+ * 2. VoiceOver triggers some keyboard events when linearly navigating with Control + Option (but
1546
+ * confusingly not with Caps Lock). Thus, to have parity with other screen readers, we ignore
1547
+ * these keys so as to not update the input modality.
1548
+ *
1549
+ * Note that we do not by default ignore the right Meta key on Safari because it has the same key
1550
+ * code as the ContextMenu key on other browsers. When we switch to using event.key, we can
1551
+ * distinguish between the two.
1552
+ */
1553
+ const INPUT_MODALITY_DETECTOR_DEFAULT_OPTIONS = {
1554
+ ignoreKeys: [ALT, CONTROL, MAC_META, META, SHIFT],
1555
+ };
1556
+ /**
1557
+ * The amount of time needed to pass after a touchstart event in order for a subsequent mousedown
1558
+ * event to be attributed as mouse and not touch.
1559
+ *
1560
+ * This is the value used by AngularJS Material. Through trial and error (on iPhone 6S) they found
1561
+ * that a value of around 650ms seems appropriate.
1562
+ */
1563
+ const TOUCH_BUFFER_MS = 650;
1564
+ /**
1565
+ * Event listener options that enable capturing and also mark the listener as passive if the browser
1566
+ * supports it.
1567
+ */
1568
+ const modalityEventListenerOptions = normalizePassiveListenerOptions({
1569
+ passive: true,
1570
+ capture: true,
1571
+ });
1572
+ /**
1573
+ * Service that detects the user's input modality.
1574
+ *
1575
+ * This service does not update the input modality when a user navigates with a screen reader
1576
+ * (e.g. linear navigation with VoiceOver, object navigation / browse mode with NVDA, virtual PC
1577
+ * cursor mode with JAWS). This is in part due to technical limitations (i.e. keyboard events do not
1578
+ * fire as expected in these modes) but is also arguably the correct behavior. Navigating with a
1579
+ * screen reader is akin to visually scanning a page, and should not be interpreted as actual user
1580
+ * input interaction.
1581
+ *
1582
+ * When a user is not navigating but *interacting* with a screen reader, this service attempts to
1583
+ * update the input modality to keyboard, but in general this service's behavior is largely
1584
+ * undefined.
1585
+ */
1586
+ class InputModalityDetector {
1587
+ constructor(_platform, ngZone, document, options) {
1588
+ this._platform = _platform;
1589
+ /**
1590
+ * The most recently detected input modality event target. Is null if no input modality has been
1591
+ * detected or if the associated event target is null for some unknown reason.
1592
+ */
1593
+ this._mostRecentTarget = null;
1594
+ /** The underlying BehaviorSubject that emits whenever an input modality is detected. */
1595
+ this._modality = new BehaviorSubject(null);
1596
+ /**
1597
+ * The timestamp of the last touch input modality. Used to determine whether mousedown events
1598
+ * should be attributed to mouse or touch.
1599
+ */
1600
+ this._lastTouchMs = 0;
1601
+ /**
1602
+ * Handles keydown events. Must be an arrow function in order to preserve the context when it gets
1603
+ * bound.
1604
+ */
1605
+ this._onKeydown = (event) => {
1606
+ var _a, _b;
1607
+ // If this is one of the keys we should ignore, then ignore it and don't update the input
1608
+ // modality to keyboard.
1609
+ if ((_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.ignoreKeys) === null || _b === void 0 ? void 0 : _b.some(keyCode => keyCode === event.keyCode)) {
1610
+ return;
1611
+ }
1612
+ this._modality.next('keyboard');
1613
+ this._mostRecentTarget = getTarget(event);
1614
+ };
1615
+ /**
1616
+ * Handles mousedown events. Must be an arrow function in order to preserve the context when it
1617
+ * gets bound.
1618
+ */
1619
+ this._onMousedown = (event) => {
1620
+ // Touches trigger both touch and mouse events, so we need to distinguish between mouse events
1621
+ // that were triggered via mouse vs touch. To do so, check if the mouse event occurs closely
1622
+ // after the previous touch event.
1623
+ if (Date.now() - this._lastTouchMs < TOUCH_BUFFER_MS) {
1624
+ return;
1625
+ }
1626
+ // Fake mousedown events are fired by some screen readers when controls are activated by the
1627
+ // screen reader. Attribute them to keyboard input modality.
1628
+ this._modality.next(isFakeMousedownFromScreenReader(event) ? 'keyboard' : 'mouse');
1629
+ this._mostRecentTarget = getTarget(event);
1630
+ };
1631
+ /**
1632
+ * Handles touchstart events. Must be an arrow function in order to preserve the context when it
1633
+ * gets bound.
1634
+ */
1635
+ this._onTouchstart = (event) => {
1636
+ // Same scenario as mentioned in _onMousedown, but on touch screen devices, fake touchstart
1637
+ // events are fired. Again, attribute to keyboard input modality.
1638
+ if (isFakeTouchstartFromScreenReader(event)) {
1639
+ this._modality.next('keyboard');
1640
+ return;
1641
+ }
1642
+ // Store the timestamp of this touch event, as it's used to distinguish between mouse events
1643
+ // triggered via mouse vs touch.
1644
+ this._lastTouchMs = Date.now();
1645
+ this._modality.next('touch');
1646
+ this._mostRecentTarget = getTarget(event);
1647
+ };
1648
+ this._options = Object.assign(Object.assign({}, INPUT_MODALITY_DETECTOR_DEFAULT_OPTIONS), options);
1649
+ // Skip the first emission as it's null.
1650
+ this.modalityDetected = this._modality.pipe(skip(1));
1651
+ this.modalityChanged = this.modalityDetected.pipe(distinctUntilChanged());
1652
+ // If we're not in a browser, this service should do nothing, as there's no relevant input
1653
+ // modality to detect.
1654
+ if (!_platform.isBrowser) {
1655
+ return;
1656
+ }
1657
+ // Add the event listeners used to detect the user's input modality.
1658
+ ngZone.runOutsideAngular(() => {
1659
+ document.addEventListener('keydown', this._onKeydown, modalityEventListenerOptions);
1660
+ document.addEventListener('mousedown', this._onMousedown, modalityEventListenerOptions);
1661
+ document.addEventListener('touchstart', this._onTouchstart, modalityEventListenerOptions);
1662
+ });
1663
+ }
1664
+ /** The most recently detected input modality. */
1665
+ get mostRecentModality() {
1666
+ return this._modality.value;
1667
+ }
1668
+ ngOnDestroy() {
1669
+ if (!this._platform.isBrowser) {
1670
+ return;
1671
+ }
1672
+ document.removeEventListener('keydown', this._onKeydown, modalityEventListenerOptions);
1673
+ document.removeEventListener('mousedown', this._onMousedown, modalityEventListenerOptions);
1674
+ document.removeEventListener('touchstart', this._onTouchstart, modalityEventListenerOptions);
1675
+ }
1676
+ }
1677
+ InputModalityDetector.ɵprov = i0.ɵɵdefineInjectable({ factory: function InputModalityDetector_Factory() { return new InputModalityDetector(i0.ɵɵinject(i1.Platform), i0.ɵɵinject(i0.NgZone), i0.ɵɵinject(i2.DOCUMENT), i0.ɵɵinject(INPUT_MODALITY_DETECTOR_OPTIONS, 8)); }, token: InputModalityDetector, providedIn: "root" });
1678
+ InputModalityDetector.decorators = [
1679
+ { type: Injectable, args: [{ providedIn: 'root' },] }
1680
+ ];
1681
+ InputModalityDetector.ctorParameters = () => [
1682
+ { type: Platform },
1683
+ { type: NgZone },
1684
+ { type: Document, decorators: [{ type: Inject, args: [DOCUMENT,] }] },
1685
+ { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [INPUT_MODALITY_DETECTOR_OPTIONS,] }] }
1686
+ ];
1687
+ /** Gets the target of an event, accounting for Shadow DOM. */
1688
+ function getTarget(event) {
1689
+ // If an event is bound outside the Shadow DOM, the `event.target` will
1690
+ // point to the shadow root so we have to use `composedPath` instead.
1691
+ return (event.composedPath ? event.composedPath()[0] : event.target);
1692
+ }
1693
+
1498
1694
  /**
1499
1695
  * @license
1500
1696
  * Copyright Google LLC All Rights Reserved.
@@ -1680,36 +1876,6 @@ CdkAriaLive.propDecorators = {
1680
1876
  * Use of this source code is governed by an MIT-style license that can be
1681
1877
  * found in the LICENSE file at https://angular.io/license
1682
1878
  */
1683
- /** Gets whether an event could be a faked `mousedown` event dispatched by a screen reader. */
1684
- function isFakeMousedownFromScreenReader(event) {
1685
- // We can typically distinguish between these faked mousedown events and real mousedown events
1686
- // using the "buttons" property. While real mousedowns will indicate the mouse button that was
1687
- // pressed (e.g. "1" for the left mouse button), faked mousedowns will usually set the property
1688
- // value to 0.
1689
- return event.buttons === 0;
1690
- }
1691
- /** Gets whether an event could be a faked `touchstart` event dispatched by a screen reader. */
1692
- function isFakeTouchstartFromScreenReader(event) {
1693
- const touch = (event.touches && event.touches[0]) ||
1694
- (event.changedTouches && event.changedTouches[0]);
1695
- // A fake `touchstart` can be distinguished from a real one by looking at the `identifier`
1696
- // which is typically >= 0 on a real device versus -1 from a screen reader. Just to be safe,
1697
- // we can also look at `radiusX` and `radiusY`. This behavior was observed against a Windows 10
1698
- // device with a touch screen running NVDA v2020.4 and Firefox 85 or Chrome 88.
1699
- return !!touch && touch.identifier === -1 && (touch.radiusX == null || touch.radiusX === 1) &&
1700
- (touch.radiusY == null || touch.radiusY === 1);
1701
- }
1702
-
1703
- /**
1704
- * @license
1705
- * Copyright Google LLC All Rights Reserved.
1706
- *
1707
- * Use of this source code is governed by an MIT-style license that can be
1708
- * found in the LICENSE file at https://angular.io/license
1709
- */
1710
- // This is the value used by AngularJS Material. Through trial and error (on iPhone 6S) they found
1711
- // that a value of around 650ms seems appropriate.
1712
- const TOUCH_BUFFER_MS = 650;
1713
1879
  /** InjectionToken for FocusMonitorOptions. */
1714
1880
  const FOCUS_MONITOR_DEFAULT_OPTIONS = new InjectionToken('cdk-focus-monitor-default-options');
1715
1881
  /**
@@ -1722,15 +1888,21 @@ const captureEventListenerOptions = normalizePassiveListenerOptions({
1722
1888
  });
1723
1889
  /** Monitors mouse and keyboard events to determine the cause of focus events. */
1724
1890
  class FocusMonitor {
1725
- constructor(_ngZone, _platform,
1891
+ constructor(_ngZone, _platform, _inputModalityDetector,
1726
1892
  /** @breaking-change 11.0.0 make document required */
1727
1893
  document, options) {
1728
1894
  this._ngZone = _ngZone;
1729
1895
  this._platform = _platform;
1896
+ this._inputModalityDetector = _inputModalityDetector;
1730
1897
  /** The focus origin that the next focus event is a result of. */
1731
1898
  this._origin = null;
1732
1899
  /** Whether the window has just been focused. */
1733
1900
  this._windowFocused = false;
1901
+ /**
1902
+ * Whether the origin was determined via a touch interaction. Necessary as properly attributing
1903
+ * focus events to touch interactions requires special logic.
1904
+ */
1905
+ this._originFromTouchInteraction = false;
1734
1906
  /** Map of elements being monitored to their info. */
1735
1907
  this._elementInfo = new Map();
1736
1908
  /** The number of elements currently being monitored. */
@@ -1742,50 +1914,6 @@ class FocusMonitor {
1742
1914
  * to the document when focus moves inside of a shadow root.
1743
1915
  */
1744
1916
  this._rootNodeFocusListenerCount = new Map();
1745
- /**
1746
- * Event listener for `keydown` events on the document.
1747
- * Needs to be an arrow function in order to preserve the context when it gets bound.
1748
- */
1749
- this._documentKeydownListener = () => {
1750
- // On keydown record the origin and clear any touch event that may be in progress.
1751
- this._lastTouchTarget = null;
1752
- this._setOriginForCurrentEventQueue('keyboard');
1753
- };
1754
- /**
1755
- * Event listener for `mousedown` events on the document.
1756
- * Needs to be an arrow function in order to preserve the context when it gets bound.
1757
- */
1758
- this._documentMousedownListener = (event) => {
1759
- // On mousedown record the origin only if there is not touch
1760
- // target, since a mousedown can happen as a result of a touch event.
1761
- if (!this._lastTouchTarget) {
1762
- // In some cases screen readers fire fake `mousedown` events instead of `keydown`.
1763
- // Resolve the focus source to `keyboard` if we detect one of them.
1764
- const source = isFakeMousedownFromScreenReader(event) ? 'keyboard' : 'mouse';
1765
- this._setOriginForCurrentEventQueue(source);
1766
- }
1767
- };
1768
- /**
1769
- * Event listener for `touchstart` events on the document.
1770
- * Needs to be an arrow function in order to preserve the context when it gets bound.
1771
- */
1772
- this._documentTouchstartListener = (event) => {
1773
- // Some screen readers will fire a fake `touchstart` event if an element is activated using
1774
- // the keyboard while on a device with a touchsreen. Consider such events as keyboard focus.
1775
- if (!isFakeTouchstartFromScreenReader(event)) {
1776
- // When the touchstart event fires the focus event is not yet in the event queue. This means
1777
- // we can't rely on the trick used above (setting timeout of 1ms). Instead we wait 650ms to
1778
- // see if a focus happens.
1779
- if (this._touchTimeoutId != null) {
1780
- clearTimeout(this._touchTimeoutId);
1781
- }
1782
- this._lastTouchTarget = getTarget(event);
1783
- this._touchTimeoutId = setTimeout(() => this._lastTouchTarget = null, TOUCH_BUFFER_MS);
1784
- }
1785
- else if (!this._lastTouchTarget) {
1786
- this._setOriginForCurrentEventQueue('keyboard');
1787
- }
1788
- };
1789
1917
  /**
1790
1918
  * Event listener for `focus` events on the window.
1791
1919
  * Needs to be an arrow function in order to preserve the context when it gets bound.
@@ -1796,6 +1924,8 @@ class FocusMonitor {
1796
1924
  this._windowFocused = true;
1797
1925
  this._windowFocusTimeoutId = setTimeout(() => this._windowFocused = false);
1798
1926
  };
1927
+ /** Subject for stopping our InputModalityDetector subscription. */
1928
+ this._stopInputModalityDetector = new Subject();
1799
1929
  /**
1800
1930
  * Event listener for `focus` and 'blur' events on the document.
1801
1931
  * Needs to be an arrow function in order to preserve the context when it gets bound.
@@ -1863,7 +1993,7 @@ class FocusMonitor {
1863
1993
  .forEach(([currentElement, info]) => this._originChanged(currentElement, origin, info));
1864
1994
  }
1865
1995
  else {
1866
- this._setOriginForCurrentEventQueue(origin);
1996
+ this._setOrigin(origin);
1867
1997
  // `focus` isn't available on the server
1868
1998
  if (typeof nativeElement.focus === 'function') {
1869
1999
  nativeElement.focus(options);
@@ -1890,25 +2020,49 @@ class FocusMonitor {
1890
2020
  element.classList.remove(className);
1891
2021
  }
1892
2022
  }
1893
- _getFocusOrigin(event) {
1894
- // If we couldn't detect a cause for the focus event, it's due to one of three reasons:
1895
- // 1) The window has just regained focus, in which case we want to restore the focused state of
1896
- // the element from before the window blurred.
1897
- // 2) It was caused by a touch event, in which case we mark the origin as 'touch'.
1898
- // 3) The element was programmatically focused, in which case we should mark the origin as
1899
- // 'program'.
2023
+ _getFocusOrigin(focusEventTarget) {
1900
2024
  if (this._origin) {
1901
- return this._origin;
1902
- }
1903
- if (this._windowFocused && this._lastFocusOrigin) {
1904
- return this._lastFocusOrigin;
1905
- }
1906
- else if (this._wasCausedByTouch(event)) {
1907
- return 'touch';
1908
- }
1909
- else {
1910
- return 'program';
2025
+ // If the origin was realized via a touch interaction, we need to perform additional checks
2026
+ // to determine whether the focus origin should be attributed to touch or program.
2027
+ if (this._originFromTouchInteraction) {
2028
+ return this._shouldBeAttributedToTouch(focusEventTarget) ? 'touch' : 'program';
2029
+ }
2030
+ else {
2031
+ return this._origin;
2032
+ }
1911
2033
  }
2034
+ // If the window has just regained focus, we can restore the most recent origin from before the
2035
+ // window blurred. Otherwise, we've reached the point where we can't identify the source of the
2036
+ // focus. This typically means one of two things happened:
2037
+ //
2038
+ // 1) The element was programmatically focused, or
2039
+ // 2) The element was focused via screen reader navigation (which generally doesn't fire
2040
+ // events).
2041
+ //
2042
+ // Because we can't distinguish between these two cases, we default to setting `program`.
2043
+ return (this._windowFocused && this._lastFocusOrigin) ? this._lastFocusOrigin : 'program';
2044
+ }
2045
+ /**
2046
+ * Returns whether the focus event should be attributed to touch. Recall that in IMMEDIATE mode, a
2047
+ * touch origin isn't immediately reset at the next tick (see _setOrigin). This means that when we
2048
+ * handle a focus event following a touch interaction, we need to determine whether (1) the focus
2049
+ * event was directly caused by the touch interaction or (2) the focus event was caused by a
2050
+ * subsequent programmatic focus call triggered by the touch interaction.
2051
+ * @param focusEventTarget The target of the focus event under examination.
2052
+ */
2053
+ _shouldBeAttributedToTouch(focusEventTarget) {
2054
+ // Please note that this check is not perfect. Consider the following edge case:
2055
+ //
2056
+ // <div #parent tabindex="0">
2057
+ // <div #child tabindex="0" (click)="#parent.focus()"></div>
2058
+ // </div>
2059
+ //
2060
+ // Suppose there is a FocusMonitor in IMMEDIATE mode attached to #parent. When the user touches
2061
+ // #child, #parent is programmatically focused. This code will attribute the focus to touch
2062
+ // instead of program. This is a relatively minor edge-case that can be worked around by using
2063
+ // focusVia(parent, 'program') to focus #parent.
2064
+ return (this._detectionMode === 1 /* EVENTUAL */) ||
2065
+ !!(focusEventTarget === null || focusEventTarget === void 0 ? void 0 : focusEventTarget.contains(this._inputModalityDetector._mostRecentTarget));
1912
2066
  }
1913
2067
  /**
1914
2068
  * Sets the focus classes on the element based on the given focus origin.
@@ -1923,48 +2077,28 @@ class FocusMonitor {
1923
2077
  this._toggleClass(element, 'cdk-program-focused', origin === 'program');
1924
2078
  }
1925
2079
  /**
1926
- * Sets the origin and schedules an async function to clear it at the end of the event queue.
1927
- * If the detection mode is 'eventual', the origin is never cleared.
2080
+ * Updates the focus origin. If we're using immediate detection mode, we schedule an async
2081
+ * function to clear the origin at the end of a timeout. The duration of the timeout depends on
2082
+ * the origin being set.
1928
2083
  * @param origin The origin to set.
2084
+ * @param isFromInteraction Whether we are setting the origin from an interaction event.
1929
2085
  */
1930
- _setOriginForCurrentEventQueue(origin) {
2086
+ _setOrigin(origin, isFromInteraction = false) {
1931
2087
  this._ngZone.runOutsideAngular(() => {
1932
2088
  this._origin = origin;
2089
+ this._originFromTouchInteraction = (origin === 'touch') && isFromInteraction;
2090
+ // If we're in IMMEDIATE mode, reset the origin at the next tick (or in `TOUCH_BUFFER_MS` ms
2091
+ // for a touch event). We reset the origin at the next tick because Firefox focuses one tick
2092
+ // after the interaction event. We wait `TOUCH_BUFFER_MS` ms before resetting the origin for
2093
+ // a touch event because when a touch event is fired, the associated focus event isn't yet in
2094
+ // the event queue. Before doing so, clear any pending timeouts.
1933
2095
  if (this._detectionMode === 0 /* IMMEDIATE */) {
1934
- // Sometimes the focus origin won't be valid in Firefox because Firefox seems to focus *one*
1935
- // tick after the interaction event fired. To ensure the focus origin is always correct,
1936
- // the focus origin will be determined at the beginning of the next tick.
1937
- this._originTimeoutId = setTimeout(() => this._origin = null, 1);
2096
+ clearTimeout(this._originTimeoutId);
2097
+ const ms = this._originFromTouchInteraction ? TOUCH_BUFFER_MS : 1;
2098
+ this._originTimeoutId = setTimeout(() => this._origin = null, ms);
1938
2099
  }
1939
2100
  });
1940
2101
  }
1941
- /**
1942
- * Checks whether the given focus event was caused by a touchstart event.
1943
- * @param event The focus event to check.
1944
- * @returns Whether the event was caused by a touch.
1945
- */
1946
- _wasCausedByTouch(event) {
1947
- // Note(mmalerba): This implementation is not quite perfect, there is a small edge case.
1948
- // Consider the following dom structure:
1949
- //
1950
- // <div #parent tabindex="0" cdkFocusClasses>
1951
- // <div #child (click)="#parent.focus()"></div>
1952
- // </div>
1953
- //
1954
- // If the user touches the #child element and the #parent is programmatically focused as a
1955
- // result, this code will still consider it to have been caused by the touch event and will
1956
- // apply the cdk-touch-focused class rather than the cdk-program-focused class. This is a
1957
- // relatively small edge-case that can be worked around by using
1958
- // focusVia(parentEl, 'program') to focus the parent element.
1959
- //
1960
- // If we decide that we absolutely must handle this case correctly, we can do so by listening
1961
- // for the first focus event after the touchstart, and then the first blur event after that
1962
- // focus event. When that blur event fires we know that whatever follows is not a result of the
1963
- // touchstart.
1964
- const focusTarget = getTarget(event);
1965
- return this._lastTouchTarget instanceof Node && focusTarget instanceof Node &&
1966
- (focusTarget === this._lastTouchTarget || focusTarget.contains(this._lastTouchTarget));
1967
- }
1968
2102
  /**
1969
2103
  * Handles focus events on a registered element.
1970
2104
  * @param event The focus event.
@@ -1978,10 +2112,11 @@ class FocusMonitor {
1978
2112
  // If we are not counting child-element-focus as focused, make sure that the event target is the
1979
2113
  // monitored element itself.
1980
2114
  const elementInfo = this._elementInfo.get(element);
1981
- if (!elementInfo || (!elementInfo.checkChildren && element !== getTarget(event))) {
2115
+ const focusEventTarget = getTarget(event);
2116
+ if (!elementInfo || (!elementInfo.checkChildren && element !== focusEventTarget)) {
1982
2117
  return;
1983
2118
  }
1984
- this._originChanged(element, this._getFocusOrigin(event), elementInfo);
2119
+ this._originChanged(element, this._getFocusOrigin(focusEventTarget), elementInfo);
1985
2120
  }
1986
2121
  /**
1987
2122
  * Handles blur events on a registered element.
@@ -2020,13 +2155,13 @@ class FocusMonitor {
2020
2155
  // Note: we listen to events in the capture phase so we
2021
2156
  // can detect them even if the user stops propagation.
2022
2157
  this._ngZone.runOutsideAngular(() => {
2023
- const document = this._getDocument();
2024
2158
  const window = this._getWindow();
2025
- document.addEventListener('keydown', this._documentKeydownListener, captureEventListenerOptions);
2026
- document.addEventListener('mousedown', this._documentMousedownListener, captureEventListenerOptions);
2027
- document.addEventListener('touchstart', this._documentTouchstartListener, captureEventListenerOptions);
2028
2159
  window.addEventListener('focus', this._windowFocusListener);
2029
2160
  });
2161
+ // The InputModalityDetector is also just a collection of global listeners.
2162
+ this._inputModalityDetector.modalityDetected
2163
+ .pipe(takeUntil(this._stopInputModalityDetector))
2164
+ .subscribe(modality => { this._setOrigin(modality, true /* isFromInteraction */); });
2030
2165
  }
2031
2166
  }
2032
2167
  _removeGlobalListeners(elementInfo) {
@@ -2044,15 +2179,12 @@ class FocusMonitor {
2044
2179
  }
2045
2180
  // Unregister global listeners when last element is unmonitored.
2046
2181
  if (!--this._monitoredElementCount) {
2047
- const document = this._getDocument();
2048
2182
  const window = this._getWindow();
2049
- document.removeEventListener('keydown', this._documentKeydownListener, captureEventListenerOptions);
2050
- document.removeEventListener('mousedown', this._documentMousedownListener, captureEventListenerOptions);
2051
- document.removeEventListener('touchstart', this._documentTouchstartListener, captureEventListenerOptions);
2052
2183
  window.removeEventListener('focus', this._windowFocusListener);
2184
+ // Equivalently, stop our InputModalityDetector subscription.
2185
+ this._stopInputModalityDetector.next();
2053
2186
  // Clear timeouts for all potentially pending timeouts to prevent the leaks.
2054
2187
  clearTimeout(this._windowFocusTimeoutId);
2055
- clearTimeout(this._touchTimeoutId);
2056
2188
  clearTimeout(this._originTimeoutId);
2057
2189
  }
2058
2190
  }
@@ -2077,22 +2209,17 @@ class FocusMonitor {
2077
2209
  return results;
2078
2210
  }
2079
2211
  }
2080
- FocusMonitor.ɵprov = i0.ɵɵdefineInjectable({ factory: function FocusMonitor_Factory() { return new FocusMonitor(i0.ɵɵinject(i0.NgZone), i0.ɵɵinject(i1.Platform), i0.ɵɵinject(i2.DOCUMENT, 8), i0.ɵɵinject(FOCUS_MONITOR_DEFAULT_OPTIONS, 8)); }, token: FocusMonitor, providedIn: "root" });
2212
+ FocusMonitor.ɵprov = i0.ɵɵdefineInjectable({ factory: function FocusMonitor_Factory() { return new FocusMonitor(i0.ɵɵinject(i0.NgZone), i0.ɵɵinject(i1.Platform), i0.ɵɵinject(InputModalityDetector), i0.ɵɵinject(i2.DOCUMENT, 8), i0.ɵɵinject(FOCUS_MONITOR_DEFAULT_OPTIONS, 8)); }, token: FocusMonitor, providedIn: "root" });
2081
2213
  FocusMonitor.decorators = [
2082
2214
  { type: Injectable, args: [{ providedIn: 'root' },] }
2083
2215
  ];
2084
2216
  FocusMonitor.ctorParameters = () => [
2085
2217
  { type: NgZone },
2086
2218
  { type: Platform },
2219
+ { type: InputModalityDetector },
2087
2220
  { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [DOCUMENT,] }] },
2088
2221
  { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [FOCUS_MONITOR_DEFAULT_OPTIONS,] }] }
2089
2222
  ];
2090
- /** Gets the target of an event, accounting for Shadow DOM. */
2091
- function getTarget(event) {
2092
- // If an event is bound outside the Shadow DOM, the `event.target` will
2093
- // point to the shadow root so we have to use `composedPath` instead.
2094
- return (event.composedPath ? event.composedPath()[0] : event.target);
2095
- }
2096
2223
  /**
2097
2224
  * Directive that determines how a particular element was focused (via keyboard, mouse, touch, or
2098
2225
  * programmatically) and adds corresponding classes to the element.
@@ -2254,5 +2381,5 @@ A11yModule.ctorParameters = () => [
2254
2381
  * Generated bundle index. Do not edit.
2255
2382
  */
2256
2383
 
2257
- export { A11yModule, ActiveDescendantKeyManager, AriaDescriber, CDK_DESCRIBEDBY_HOST_ATTRIBUTE, CDK_DESCRIBEDBY_ID_PREFIX, CdkAriaLive, CdkMonitorFocus, CdkTrapFocus, ConfigurableFocusTrap, ConfigurableFocusTrapFactory, EventListenerFocusTrapInertStrategy, FOCUS_MONITOR_DEFAULT_OPTIONS, FOCUS_TRAP_INERT_STRATEGY, FocusKeyManager, FocusMonitor, FocusTrap, FocusTrapFactory, HighContrastModeDetector, InteractivityChecker, IsFocusableConfig, LIVE_ANNOUNCER_DEFAULT_OPTIONS, LIVE_ANNOUNCER_ELEMENT_TOKEN, LIVE_ANNOUNCER_ELEMENT_TOKEN_FACTORY, ListKeyManager, LiveAnnouncer, MESSAGES_CONTAINER_ID, TOUCH_BUFFER_MS, isFakeMousedownFromScreenReader, isFakeTouchstartFromScreenReader, FocusTrapManager as ɵangular_material_src_cdk_a11y_a11y_a };
2384
+ export { A11yModule, ActiveDescendantKeyManager, AriaDescriber, CDK_DESCRIBEDBY_HOST_ATTRIBUTE, CDK_DESCRIBEDBY_ID_PREFIX, CdkAriaLive, CdkMonitorFocus, CdkTrapFocus, ConfigurableFocusTrap, ConfigurableFocusTrapFactory, EventListenerFocusTrapInertStrategy, FOCUS_MONITOR_DEFAULT_OPTIONS, FOCUS_TRAP_INERT_STRATEGY, FocusKeyManager, FocusMonitor, FocusTrap, FocusTrapFactory, HighContrastModeDetector, INPUT_MODALITY_DETECTOR_DEFAULT_OPTIONS, INPUT_MODALITY_DETECTOR_OPTIONS, InputModalityDetector, InteractivityChecker, IsFocusableConfig, LIVE_ANNOUNCER_DEFAULT_OPTIONS, LIVE_ANNOUNCER_ELEMENT_TOKEN, LIVE_ANNOUNCER_ELEMENT_TOKEN_FACTORY, ListKeyManager, LiveAnnouncer, MESSAGES_CONTAINER_ID, isFakeMousedownFromScreenReader, isFakeTouchstartFromScreenReader, FocusTrapManager as ɵangular_material_src_cdk_a11y_a11y_a };
2258
2385
  //# sourceMappingURL=a11y.js.map