@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
@@ -1857,6 +1857,208 @@
1857
1857
  { type: undefined, decorators: [{ type: i0.Optional }, { type: i0.Inject, args: [FOCUS_TRAP_INERT_STRATEGY,] }] }
1858
1858
  ]; };
1859
1859
 
1860
+ /**
1861
+ * @license
1862
+ * Copyright Google LLC All Rights Reserved.
1863
+ *
1864
+ * Use of this source code is governed by an MIT-style license that can be
1865
+ * found in the LICENSE file at https://angular.io/license
1866
+ */
1867
+ /** Gets whether an event could be a faked `mousedown` event dispatched by a screen reader. */
1868
+ function isFakeMousedownFromScreenReader(event) {
1869
+ // We can typically distinguish between these faked mousedown events and real mousedown events
1870
+ // using the "buttons" property. While real mousedowns will indicate the mouse button that was
1871
+ // pressed (e.g. "1" for the left mouse button), faked mousedowns will usually set the property
1872
+ // value to 0.
1873
+ return event.buttons === 0;
1874
+ }
1875
+ /** Gets whether an event could be a faked `touchstart` event dispatched by a screen reader. */
1876
+ function isFakeTouchstartFromScreenReader(event) {
1877
+ var touch = (event.touches && event.touches[0]) ||
1878
+ (event.changedTouches && event.changedTouches[0]);
1879
+ // A fake `touchstart` can be distinguished from a real one by looking at the `identifier`
1880
+ // which is typically >= 0 on a real device versus -1 from a screen reader. Just to be safe,
1881
+ // we can also look at `radiusX` and `radiusY`. This behavior was observed against a Windows 10
1882
+ // device with a touch screen running NVDA v2020.4 and Firefox 85 or Chrome 88.
1883
+ return !!touch && touch.identifier === -1 && (touch.radiusX == null || touch.radiusX === 1) &&
1884
+ (touch.radiusY == null || touch.radiusY === 1);
1885
+ }
1886
+
1887
+ /**
1888
+ * @license
1889
+ * Copyright Google LLC All Rights Reserved.
1890
+ *
1891
+ * Use of this source code is governed by an MIT-style license that can be
1892
+ * found in the LICENSE file at https://angular.io/license
1893
+ */
1894
+ /**
1895
+ * Injectable options for the InputModalityDetector. These are shallowly merged with the default
1896
+ * options.
1897
+ */
1898
+ var INPUT_MODALITY_DETECTOR_OPTIONS = new i0.InjectionToken('cdk-input-modality-detector-options');
1899
+ /**
1900
+ * Default options for the InputModalityDetector.
1901
+ *
1902
+ * Modifier keys are ignored by default (i.e. when pressed won't cause the service to detect
1903
+ * keyboard input modality) for two reasons:
1904
+ *
1905
+ * 1. Modifier keys are commonly used with mouse to perform actions such as 'right click' or 'open
1906
+ * in new tab', and are thus less representative of actual keyboard interaction.
1907
+ * 2. VoiceOver triggers some keyboard events when linearly navigating with Control + Option (but
1908
+ * confusingly not with Caps Lock). Thus, to have parity with other screen readers, we ignore
1909
+ * these keys so as to not update the input modality.
1910
+ *
1911
+ * Note that we do not by default ignore the right Meta key on Safari because it has the same key
1912
+ * code as the ContextMenu key on other browsers. When we switch to using event.key, we can
1913
+ * distinguish between the two.
1914
+ */
1915
+ var INPUT_MODALITY_DETECTOR_DEFAULT_OPTIONS = {
1916
+ ignoreKeys: [keycodes.ALT, keycodes.CONTROL, keycodes.MAC_META, keycodes.META, keycodes.SHIFT],
1917
+ };
1918
+ /**
1919
+ * The amount of time needed to pass after a touchstart event in order for a subsequent mousedown
1920
+ * event to be attributed as mouse and not touch.
1921
+ *
1922
+ * This is the value used by AngularJS Material. Through trial and error (on iPhone 6S) they found
1923
+ * that a value of around 650ms seems appropriate.
1924
+ */
1925
+ var TOUCH_BUFFER_MS = 650;
1926
+ /**
1927
+ * Event listener options that enable capturing and also mark the listener as passive if the browser
1928
+ * supports it.
1929
+ */
1930
+ var modalityEventListenerOptions = i1.normalizePassiveListenerOptions({
1931
+ passive: true,
1932
+ capture: true,
1933
+ });
1934
+ /**
1935
+ * Service that detects the user's input modality.
1936
+ *
1937
+ * This service does not update the input modality when a user navigates with a screen reader
1938
+ * (e.g. linear navigation with VoiceOver, object navigation / browse mode with NVDA, virtual PC
1939
+ * cursor mode with JAWS). This is in part due to technical limitations (i.e. keyboard events do not
1940
+ * fire as expected in these modes) but is also arguably the correct behavior. Navigating with a
1941
+ * screen reader is akin to visually scanning a page, and should not be interpreted as actual user
1942
+ * input interaction.
1943
+ *
1944
+ * When a user is not navigating but *interacting* with a screen reader, this service attempts to
1945
+ * update the input modality to keyboard, but in general this service's behavior is largely
1946
+ * undefined.
1947
+ */
1948
+ var InputModalityDetector = /** @class */ (function () {
1949
+ function InputModalityDetector(_platform, ngZone, document, options) {
1950
+ var _this = this;
1951
+ this._platform = _platform;
1952
+ /**
1953
+ * The most recently detected input modality event target. Is null if no input modality has been
1954
+ * detected or if the associated event target is null for some unknown reason.
1955
+ */
1956
+ this._mostRecentTarget = null;
1957
+ /** The underlying BehaviorSubject that emits whenever an input modality is detected. */
1958
+ this._modality = new rxjs.BehaviorSubject(null);
1959
+ /**
1960
+ * The timestamp of the last touch input modality. Used to determine whether mousedown events
1961
+ * should be attributed to mouse or touch.
1962
+ */
1963
+ this._lastTouchMs = 0;
1964
+ /**
1965
+ * Handles keydown events. Must be an arrow function in order to preserve the context when it gets
1966
+ * bound.
1967
+ */
1968
+ this._onKeydown = function (event) {
1969
+ var _a, _b;
1970
+ // If this is one of the keys we should ignore, then ignore it and don't update the input
1971
+ // modality to keyboard.
1972
+ if ((_b = (_a = _this._options) === null || _a === void 0 ? void 0 : _a.ignoreKeys) === null || _b === void 0 ? void 0 : _b.some(function (keyCode) { return keyCode === event.keyCode; })) {
1973
+ return;
1974
+ }
1975
+ _this._modality.next('keyboard');
1976
+ _this._mostRecentTarget = getTarget(event);
1977
+ };
1978
+ /**
1979
+ * Handles mousedown events. Must be an arrow function in order to preserve the context when it
1980
+ * gets bound.
1981
+ */
1982
+ this._onMousedown = function (event) {
1983
+ // Touches trigger both touch and mouse events, so we need to distinguish between mouse events
1984
+ // that were triggered via mouse vs touch. To do so, check if the mouse event occurs closely
1985
+ // after the previous touch event.
1986
+ if (Date.now() - _this._lastTouchMs < TOUCH_BUFFER_MS) {
1987
+ return;
1988
+ }
1989
+ // Fake mousedown events are fired by some screen readers when controls are activated by the
1990
+ // screen reader. Attribute them to keyboard input modality.
1991
+ _this._modality.next(isFakeMousedownFromScreenReader(event) ? 'keyboard' : 'mouse');
1992
+ _this._mostRecentTarget = getTarget(event);
1993
+ };
1994
+ /**
1995
+ * Handles touchstart events. Must be an arrow function in order to preserve the context when it
1996
+ * gets bound.
1997
+ */
1998
+ this._onTouchstart = function (event) {
1999
+ // Same scenario as mentioned in _onMousedown, but on touch screen devices, fake touchstart
2000
+ // events are fired. Again, attribute to keyboard input modality.
2001
+ if (isFakeTouchstartFromScreenReader(event)) {
2002
+ _this._modality.next('keyboard');
2003
+ return;
2004
+ }
2005
+ // Store the timestamp of this touch event, as it's used to distinguish between mouse events
2006
+ // triggered via mouse vs touch.
2007
+ _this._lastTouchMs = Date.now();
2008
+ _this._modality.next('touch');
2009
+ _this._mostRecentTarget = getTarget(event);
2010
+ };
2011
+ this._options = Object.assign(Object.assign({}, INPUT_MODALITY_DETECTOR_DEFAULT_OPTIONS), options);
2012
+ // Skip the first emission as it's null.
2013
+ this.modalityDetected = this._modality.pipe(operators.skip(1));
2014
+ this.modalityChanged = this.modalityDetected.pipe(operators.distinctUntilChanged());
2015
+ // If we're not in a browser, this service should do nothing, as there's no relevant input
2016
+ // modality to detect.
2017
+ if (!_platform.isBrowser) {
2018
+ return;
2019
+ }
2020
+ // Add the event listeners used to detect the user's input modality.
2021
+ ngZone.runOutsideAngular(function () {
2022
+ document.addEventListener('keydown', _this._onKeydown, modalityEventListenerOptions);
2023
+ document.addEventListener('mousedown', _this._onMousedown, modalityEventListenerOptions);
2024
+ document.addEventListener('touchstart', _this._onTouchstart, modalityEventListenerOptions);
2025
+ });
2026
+ }
2027
+ Object.defineProperty(InputModalityDetector.prototype, "mostRecentModality", {
2028
+ /** The most recently detected input modality. */
2029
+ get: function () {
2030
+ return this._modality.value;
2031
+ },
2032
+ enumerable: false,
2033
+ configurable: true
2034
+ });
2035
+ InputModalityDetector.prototype.ngOnDestroy = function () {
2036
+ if (!this._platform.isBrowser) {
2037
+ return;
2038
+ }
2039
+ document.removeEventListener('keydown', this._onKeydown, modalityEventListenerOptions);
2040
+ document.removeEventListener('mousedown', this._onMousedown, modalityEventListenerOptions);
2041
+ document.removeEventListener('touchstart', this._onTouchstart, modalityEventListenerOptions);
2042
+ };
2043
+ return InputModalityDetector;
2044
+ }());
2045
+ InputModalityDetector.ɵprov = i0__namespace.ɵɵdefineInjectable({ factory: function InputModalityDetector_Factory() { return new InputModalityDetector(i0__namespace.ɵɵinject(i1__namespace.Platform), i0__namespace.ɵɵinject(i0__namespace.NgZone), i0__namespace.ɵɵinject(i2__namespace.DOCUMENT), i0__namespace.ɵɵinject(INPUT_MODALITY_DETECTOR_OPTIONS, 8)); }, token: InputModalityDetector, providedIn: "root" });
2046
+ InputModalityDetector.decorators = [
2047
+ { type: i0.Injectable, args: [{ providedIn: 'root' },] }
2048
+ ];
2049
+ InputModalityDetector.ctorParameters = function () { return [
2050
+ { type: i1.Platform },
2051
+ { type: i0.NgZone },
2052
+ { type: Document, decorators: [{ type: i0.Inject, args: [i2.DOCUMENT,] }] },
2053
+ { type: undefined, decorators: [{ type: i0.Optional }, { type: i0.Inject, args: [INPUT_MODALITY_DETECTOR_OPTIONS,] }] }
2054
+ ]; };
2055
+ /** Gets the target of an event, accounting for Shadow DOM. */
2056
+ function getTarget(event) {
2057
+ // If an event is bound outside the Shadow DOM, the `event.target` will
2058
+ // point to the shadow root so we have to use `composedPath` instead.
2059
+ return (event.composedPath ? event.composedPath()[0] : event.target);
2060
+ }
2061
+
1860
2062
  /**
1861
2063
  * @license
1862
2064
  * Copyright Google LLC All Rights Reserved.
@@ -2041,36 +2243,6 @@
2041
2243
  politeness: [{ type: i0.Input, args: ['cdkAriaLive',] }]
2042
2244
  };
2043
2245
 
2044
- /**
2045
- * @license
2046
- * Copyright Google LLC All Rights Reserved.
2047
- *
2048
- * Use of this source code is governed by an MIT-style license that can be
2049
- * found in the LICENSE file at https://angular.io/license
2050
- */
2051
- /** Gets whether an event could be a faked `mousedown` event dispatched by a screen reader. */
2052
- function isFakeMousedownFromScreenReader(event) {
2053
- // We can typically distinguish between these faked mousedown events and real mousedown events
2054
- // using the "buttons" property. While real mousedowns will indicate the mouse button that was
2055
- // pressed (e.g. "1" for the left mouse button), faked mousedowns will usually set the property
2056
- // value to 0.
2057
- return event.buttons === 0;
2058
- }
2059
- /** Gets whether an event could be a faked `touchstart` event dispatched by a screen reader. */
2060
- function isFakeTouchstartFromScreenReader(event) {
2061
- var touch = (event.touches && event.touches[0]) ||
2062
- (event.changedTouches && event.changedTouches[0]);
2063
- // A fake `touchstart` can be distinguished from a real one by looking at the `identifier`
2064
- // which is typically >= 0 on a real device versus -1 from a screen reader. Just to be safe,
2065
- // we can also look at `radiusX` and `radiusY`. This behavior was observed against a Windows 10
2066
- // device with a touch screen running NVDA v2020.4 and Firefox 85 or Chrome 88.
2067
- return !!touch && touch.identifier === -1 && (touch.radiusX == null || touch.radiusX === 1) &&
2068
- (touch.radiusY == null || touch.radiusY === 1);
2069
- }
2070
-
2071
- // This is the value used by AngularJS Material. Through trial and error (on iPhone 6S) they found
2072
- // that a value of around 650ms seems appropriate.
2073
- var TOUCH_BUFFER_MS = 650;
2074
2246
  /** InjectionToken for FocusMonitorOptions. */
2075
2247
  var FOCUS_MONITOR_DEFAULT_OPTIONS = new i0.InjectionToken('cdk-focus-monitor-default-options');
2076
2248
  /**
@@ -2083,16 +2255,22 @@
2083
2255
  });
2084
2256
  /** Monitors mouse and keyboard events to determine the cause of focus events. */
2085
2257
  var FocusMonitor = /** @class */ (function () {
2086
- function FocusMonitor(_ngZone, _platform,
2258
+ function FocusMonitor(_ngZone, _platform, _inputModalityDetector,
2087
2259
  /** @breaking-change 11.0.0 make document required */
2088
2260
  document, options) {
2089
2261
  var _this = this;
2090
2262
  this._ngZone = _ngZone;
2091
2263
  this._platform = _platform;
2264
+ this._inputModalityDetector = _inputModalityDetector;
2092
2265
  /** The focus origin that the next focus event is a result of. */
2093
2266
  this._origin = null;
2094
2267
  /** Whether the window has just been focused. */
2095
2268
  this._windowFocused = false;
2269
+ /**
2270
+ * Whether the origin was determined via a touch interaction. Necessary as properly attributing
2271
+ * focus events to touch interactions requires special logic.
2272
+ */
2273
+ this._originFromTouchInteraction = false;
2096
2274
  /** Map of elements being monitored to their info. */
2097
2275
  this._elementInfo = new Map();
2098
2276
  /** The number of elements currently being monitored. */
@@ -2104,50 +2282,6 @@
2104
2282
  * to the document when focus moves inside of a shadow root.
2105
2283
  */
2106
2284
  this._rootNodeFocusListenerCount = new Map();
2107
- /**
2108
- * Event listener for `keydown` events on the document.
2109
- * Needs to be an arrow function in order to preserve the context when it gets bound.
2110
- */
2111
- this._documentKeydownListener = function () {
2112
- // On keydown record the origin and clear any touch event that may be in progress.
2113
- _this._lastTouchTarget = null;
2114
- _this._setOriginForCurrentEventQueue('keyboard');
2115
- };
2116
- /**
2117
- * Event listener for `mousedown` events on the document.
2118
- * Needs to be an arrow function in order to preserve the context when it gets bound.
2119
- */
2120
- this._documentMousedownListener = function (event) {
2121
- // On mousedown record the origin only if there is not touch
2122
- // target, since a mousedown can happen as a result of a touch event.
2123
- if (!_this._lastTouchTarget) {
2124
- // In some cases screen readers fire fake `mousedown` events instead of `keydown`.
2125
- // Resolve the focus source to `keyboard` if we detect one of them.
2126
- var source = isFakeMousedownFromScreenReader(event) ? 'keyboard' : 'mouse';
2127
- _this._setOriginForCurrentEventQueue(source);
2128
- }
2129
- };
2130
- /**
2131
- * Event listener for `touchstart` events on the document.
2132
- * Needs to be an arrow function in order to preserve the context when it gets bound.
2133
- */
2134
- this._documentTouchstartListener = function (event) {
2135
- // Some screen readers will fire a fake `touchstart` event if an element is activated using
2136
- // the keyboard while on a device with a touchsreen. Consider such events as keyboard focus.
2137
- if (!isFakeTouchstartFromScreenReader(event)) {
2138
- // When the touchstart event fires the focus event is not yet in the event queue. This means
2139
- // we can't rely on the trick used above (setting timeout of 1ms). Instead we wait 650ms to
2140
- // see if a focus happens.
2141
- if (_this._touchTimeoutId != null) {
2142
- clearTimeout(_this._touchTimeoutId);
2143
- }
2144
- _this._lastTouchTarget = getTarget(event);
2145
- _this._touchTimeoutId = setTimeout(function () { return _this._lastTouchTarget = null; }, TOUCH_BUFFER_MS);
2146
- }
2147
- else if (!_this._lastTouchTarget) {
2148
- _this._setOriginForCurrentEventQueue('keyboard');
2149
- }
2150
- };
2151
2285
  /**
2152
2286
  * Event listener for `focus` events on the window.
2153
2287
  * Needs to be an arrow function in order to preserve the context when it gets bound.
@@ -2158,6 +2292,8 @@
2158
2292
  _this._windowFocused = true;
2159
2293
  _this._windowFocusTimeoutId = setTimeout(function () { return _this._windowFocused = false; });
2160
2294
  };
2295
+ /** Subject for stopping our InputModalityDetector subscription. */
2296
+ this._stopInputModalityDetector = new rxjs.Subject();
2161
2297
  /**
2162
2298
  * Event listener for `focus` and 'blur' events on the document.
2163
2299
  * Needs to be an arrow function in order to preserve the context when it gets bound.
@@ -2230,7 +2366,7 @@
2230
2366
  });
2231
2367
  }
2232
2368
  else {
2233
- this._setOriginForCurrentEventQueue(origin);
2369
+ this._setOrigin(origin);
2234
2370
  // `focus` isn't available on the server
2235
2371
  if (typeof nativeElement.focus === 'function') {
2236
2372
  nativeElement.focus(options);
@@ -2258,25 +2394,49 @@
2258
2394
  element.classList.remove(className);
2259
2395
  }
2260
2396
  };
2261
- FocusMonitor.prototype._getFocusOrigin = function (event) {
2262
- // If we couldn't detect a cause for the focus event, it's due to one of three reasons:
2263
- // 1) The window has just regained focus, in which case we want to restore the focused state of
2264
- // the element from before the window blurred.
2265
- // 2) It was caused by a touch event, in which case we mark the origin as 'touch'.
2266
- // 3) The element was programmatically focused, in which case we should mark the origin as
2267
- // 'program'.
2397
+ FocusMonitor.prototype._getFocusOrigin = function (focusEventTarget) {
2268
2398
  if (this._origin) {
2269
- return this._origin;
2270
- }
2271
- if (this._windowFocused && this._lastFocusOrigin) {
2272
- return this._lastFocusOrigin;
2273
- }
2274
- else if (this._wasCausedByTouch(event)) {
2275
- return 'touch';
2276
- }
2277
- else {
2278
- return 'program';
2399
+ // If the origin was realized via a touch interaction, we need to perform additional checks
2400
+ // to determine whether the focus origin should be attributed to touch or program.
2401
+ if (this._originFromTouchInteraction) {
2402
+ return this._shouldBeAttributedToTouch(focusEventTarget) ? 'touch' : 'program';
2403
+ }
2404
+ else {
2405
+ return this._origin;
2406
+ }
2279
2407
  }
2408
+ // If the window has just regained focus, we can restore the most recent origin from before the
2409
+ // window blurred. Otherwise, we've reached the point where we can't identify the source of the
2410
+ // focus. This typically means one of two things happened:
2411
+ //
2412
+ // 1) The element was programmatically focused, or
2413
+ // 2) The element was focused via screen reader navigation (which generally doesn't fire
2414
+ // events).
2415
+ //
2416
+ // Because we can't distinguish between these two cases, we default to setting `program`.
2417
+ return (this._windowFocused && this._lastFocusOrigin) ? this._lastFocusOrigin : 'program';
2418
+ };
2419
+ /**
2420
+ * Returns whether the focus event should be attributed to touch. Recall that in IMMEDIATE mode, a
2421
+ * touch origin isn't immediately reset at the next tick (see _setOrigin). This means that when we
2422
+ * handle a focus event following a touch interaction, we need to determine whether (1) the focus
2423
+ * event was directly caused by the touch interaction or (2) the focus event was caused by a
2424
+ * subsequent programmatic focus call triggered by the touch interaction.
2425
+ * @param focusEventTarget The target of the focus event under examination.
2426
+ */
2427
+ FocusMonitor.prototype._shouldBeAttributedToTouch = function (focusEventTarget) {
2428
+ // Please note that this check is not perfect. Consider the following edge case:
2429
+ //
2430
+ // <div #parent tabindex="0">
2431
+ // <div #child tabindex="0" (click)="#parent.focus()"></div>
2432
+ // </div>
2433
+ //
2434
+ // Suppose there is a FocusMonitor in IMMEDIATE mode attached to #parent. When the user touches
2435
+ // #child, #parent is programmatically focused. This code will attribute the focus to touch
2436
+ // instead of program. This is a relatively minor edge-case that can be worked around by using
2437
+ // focusVia(parent, 'program') to focus #parent.
2438
+ return (this._detectionMode === 1 /* EVENTUAL */) ||
2439
+ !!(focusEventTarget === null || focusEventTarget === void 0 ? void 0 : focusEventTarget.contains(this._inputModalityDetector._mostRecentTarget));
2280
2440
  };
2281
2441
  /**
2282
2442
  * Sets the focus classes on the element based on the given focus origin.
@@ -2291,49 +2451,30 @@
2291
2451
  this._toggleClass(element, 'cdk-program-focused', origin === 'program');
2292
2452
  };
2293
2453
  /**
2294
- * Sets the origin and schedules an async function to clear it at the end of the event queue.
2295
- * If the detection mode is 'eventual', the origin is never cleared.
2454
+ * Updates the focus origin. If we're using immediate detection mode, we schedule an async
2455
+ * function to clear the origin at the end of a timeout. The duration of the timeout depends on
2456
+ * the origin being set.
2296
2457
  * @param origin The origin to set.
2458
+ * @param isFromInteraction Whether we are setting the origin from an interaction event.
2297
2459
  */
2298
- FocusMonitor.prototype._setOriginForCurrentEventQueue = function (origin) {
2460
+ FocusMonitor.prototype._setOrigin = function (origin, isFromInteraction) {
2299
2461
  var _this = this;
2462
+ if (isFromInteraction === void 0) { isFromInteraction = false; }
2300
2463
  this._ngZone.runOutsideAngular(function () {
2301
2464
  _this._origin = origin;
2465
+ _this._originFromTouchInteraction = (origin === 'touch') && isFromInteraction;
2466
+ // If we're in IMMEDIATE mode, reset the origin at the next tick (or in `TOUCH_BUFFER_MS` ms
2467
+ // for a touch event). We reset the origin at the next tick because Firefox focuses one tick
2468
+ // after the interaction event. We wait `TOUCH_BUFFER_MS` ms before resetting the origin for
2469
+ // a touch event because when a touch event is fired, the associated focus event isn't yet in
2470
+ // the event queue. Before doing so, clear any pending timeouts.
2302
2471
  if (_this._detectionMode === 0 /* IMMEDIATE */) {
2303
- // Sometimes the focus origin won't be valid in Firefox because Firefox seems to focus *one*
2304
- // tick after the interaction event fired. To ensure the focus origin is always correct,
2305
- // the focus origin will be determined at the beginning of the next tick.
2306
- _this._originTimeoutId = setTimeout(function () { return _this._origin = null; }, 1);
2472
+ clearTimeout(_this._originTimeoutId);
2473
+ var ms = _this._originFromTouchInteraction ? TOUCH_BUFFER_MS : 1;
2474
+ _this._originTimeoutId = setTimeout(function () { return _this._origin = null; }, ms);
2307
2475
  }
2308
2476
  });
2309
2477
  };
2310
- /**
2311
- * Checks whether the given focus event was caused by a touchstart event.
2312
- * @param event The focus event to check.
2313
- * @returns Whether the event was caused by a touch.
2314
- */
2315
- FocusMonitor.prototype._wasCausedByTouch = function (event) {
2316
- // Note(mmalerba): This implementation is not quite perfect, there is a small edge case.
2317
- // Consider the following dom structure:
2318
- //
2319
- // <div #parent tabindex="0" cdkFocusClasses>
2320
- // <div #child (click)="#parent.focus()"></div>
2321
- // </div>
2322
- //
2323
- // If the user touches the #child element and the #parent is programmatically focused as a
2324
- // result, this code will still consider it to have been caused by the touch event and will
2325
- // apply the cdk-touch-focused class rather than the cdk-program-focused class. This is a
2326
- // relatively small edge-case that can be worked around by using
2327
- // focusVia(parentEl, 'program') to focus the parent element.
2328
- //
2329
- // If we decide that we absolutely must handle this case correctly, we can do so by listening
2330
- // for the first focus event after the touchstart, and then the first blur event after that
2331
- // focus event. When that blur event fires we know that whatever follows is not a result of the
2332
- // touchstart.
2333
- var focusTarget = getTarget(event);
2334
- return this._lastTouchTarget instanceof Node && focusTarget instanceof Node &&
2335
- (focusTarget === this._lastTouchTarget || focusTarget.contains(this._lastTouchTarget));
2336
- };
2337
2478
  /**
2338
2479
  * Handles focus events on a registered element.
2339
2480
  * @param event The focus event.
@@ -2347,10 +2488,11 @@
2347
2488
  // If we are not counting child-element-focus as focused, make sure that the event target is the
2348
2489
  // monitored element itself.
2349
2490
  var elementInfo = this._elementInfo.get(element);
2350
- if (!elementInfo || (!elementInfo.checkChildren && element !== getTarget(event))) {
2491
+ var focusEventTarget = getTarget(event);
2492
+ if (!elementInfo || (!elementInfo.checkChildren && element !== focusEventTarget)) {
2351
2493
  return;
2352
2494
  }
2353
- this._originChanged(element, this._getFocusOrigin(event), elementInfo);
2495
+ this._originChanged(element, this._getFocusOrigin(focusEventTarget), elementInfo);
2354
2496
  };
2355
2497
  /**
2356
2498
  * Handles blur events on a registered element.
@@ -2390,13 +2532,13 @@
2390
2532
  // Note: we listen to events in the capture phase so we
2391
2533
  // can detect them even if the user stops propagation.
2392
2534
  this._ngZone.runOutsideAngular(function () {
2393
- var document = _this._getDocument();
2394
2535
  var window = _this._getWindow();
2395
- document.addEventListener('keydown', _this._documentKeydownListener, captureEventListenerOptions);
2396
- document.addEventListener('mousedown', _this._documentMousedownListener, captureEventListenerOptions);
2397
- document.addEventListener('touchstart', _this._documentTouchstartListener, captureEventListenerOptions);
2398
2536
  window.addEventListener('focus', _this._windowFocusListener);
2399
2537
  });
2538
+ // The InputModalityDetector is also just a collection of global listeners.
2539
+ this._inputModalityDetector.modalityDetected
2540
+ .pipe(operators.takeUntil(this._stopInputModalityDetector))
2541
+ .subscribe(function (modality) { _this._setOrigin(modality, true /* isFromInteraction */); });
2400
2542
  }
2401
2543
  };
2402
2544
  FocusMonitor.prototype._removeGlobalListeners = function (elementInfo) {
@@ -2414,15 +2556,12 @@
2414
2556
  }
2415
2557
  // Unregister global listeners when last element is unmonitored.
2416
2558
  if (!--this._monitoredElementCount) {
2417
- var document = this._getDocument();
2418
2559
  var window = this._getWindow();
2419
- document.removeEventListener('keydown', this._documentKeydownListener, captureEventListenerOptions);
2420
- document.removeEventListener('mousedown', this._documentMousedownListener, captureEventListenerOptions);
2421
- document.removeEventListener('touchstart', this._documentTouchstartListener, captureEventListenerOptions);
2422
2560
  window.removeEventListener('focus', this._windowFocusListener);
2561
+ // Equivalently, stop our InputModalityDetector subscription.
2562
+ this._stopInputModalityDetector.next();
2423
2563
  // Clear timeouts for all potentially pending timeouts to prevent the leaks.
2424
2564
  clearTimeout(this._windowFocusTimeoutId);
2425
- clearTimeout(this._touchTimeoutId);
2426
2565
  clearTimeout(this._originTimeoutId);
2427
2566
  }
2428
2567
  };
@@ -2448,22 +2587,17 @@
2448
2587
  };
2449
2588
  return FocusMonitor;
2450
2589
  }());
2451
- FocusMonitor.ɵprov = i0__namespace.ɵɵdefineInjectable({ factory: function FocusMonitor_Factory() { return new FocusMonitor(i0__namespace.ɵɵinject(i0__namespace.NgZone), i0__namespace.ɵɵinject(i1__namespace.Platform), i0__namespace.ɵɵinject(i2__namespace.DOCUMENT, 8), i0__namespace.ɵɵinject(FOCUS_MONITOR_DEFAULT_OPTIONS, 8)); }, token: FocusMonitor, providedIn: "root" });
2590
+ FocusMonitor.ɵprov = i0__namespace.ɵɵdefineInjectable({ factory: function FocusMonitor_Factory() { return new FocusMonitor(i0__namespace.ɵɵinject(i0__namespace.NgZone), i0__namespace.ɵɵinject(i1__namespace.Platform), i0__namespace.ɵɵinject(InputModalityDetector), i0__namespace.ɵɵinject(i2__namespace.DOCUMENT, 8), i0__namespace.ɵɵinject(FOCUS_MONITOR_DEFAULT_OPTIONS, 8)); }, token: FocusMonitor, providedIn: "root" });
2452
2591
  FocusMonitor.decorators = [
2453
2592
  { type: i0.Injectable, args: [{ providedIn: 'root' },] }
2454
2593
  ];
2455
2594
  FocusMonitor.ctorParameters = function () { return [
2456
2595
  { type: i0.NgZone },
2457
2596
  { type: i1.Platform },
2597
+ { type: InputModalityDetector },
2458
2598
  { type: undefined, decorators: [{ type: i0.Optional }, { type: i0.Inject, args: [i2.DOCUMENT,] }] },
2459
2599
  { type: undefined, decorators: [{ type: i0.Optional }, { type: i0.Inject, args: [FOCUS_MONITOR_DEFAULT_OPTIONS,] }] }
2460
2600
  ]; };
2461
- /** Gets the target of an event, accounting for Shadow DOM. */
2462
- function getTarget(event) {
2463
- // If an event is bound outside the Shadow DOM, the `event.target` will
2464
- // point to the shadow root so we have to use `composedPath` instead.
2465
- return (event.composedPath ? event.composedPath()[0] : event.target);
2466
- }
2467
2601
  /**
2468
2602
  * Directive that determines how a particular element was focused (via keyboard, mouse, touch, or
2469
2603
  * programmatically) and adds corresponding classes to the element.
@@ -2647,6 +2781,9 @@
2647
2781
  exports.FocusTrap = FocusTrap;
2648
2782
  exports.FocusTrapFactory = FocusTrapFactory;
2649
2783
  exports.HighContrastModeDetector = HighContrastModeDetector;
2784
+ exports.INPUT_MODALITY_DETECTOR_DEFAULT_OPTIONS = INPUT_MODALITY_DETECTOR_DEFAULT_OPTIONS;
2785
+ exports.INPUT_MODALITY_DETECTOR_OPTIONS = INPUT_MODALITY_DETECTOR_OPTIONS;
2786
+ exports.InputModalityDetector = InputModalityDetector;
2650
2787
  exports.InteractivityChecker = InteractivityChecker;
2651
2788
  exports.IsFocusableConfig = IsFocusableConfig;
2652
2789
  exports.LIVE_ANNOUNCER_DEFAULT_OPTIONS = LIVE_ANNOUNCER_DEFAULT_OPTIONS;
@@ -2655,7 +2792,6 @@
2655
2792
  exports.ListKeyManager = ListKeyManager;
2656
2793
  exports.LiveAnnouncer = LiveAnnouncer;
2657
2794
  exports.MESSAGES_CONTAINER_ID = MESSAGES_CONTAINER_ID;
2658
- exports.TOUCH_BUFFER_MS = TOUCH_BUFFER_MS;
2659
2795
  exports.isFakeMousedownFromScreenReader = isFakeMousedownFromScreenReader;
2660
2796
  exports.isFakeTouchstartFromScreenReader = isFakeTouchstartFromScreenReader;
2661
2797
  exports.ɵangular_material_src_cdk_a11y_a11y_a = FocusTrapManager;