@angular/cdk 12.0.6 → 12.1.0
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.
- package/a11y/focus-monitor/focus-monitor.d.ts +25 -31
- package/a11y/index.metadata.json +1 -1
- package/a11y/input-modality/input-modality-detector.d.ts +105 -0
- package/a11y/public-api.d.ts +1 -0
- package/bundles/cdk-a11y.umd.js +296 -157
- package/bundles/cdk-a11y.umd.js.map +1 -1
- package/bundles/cdk-coercion.umd.js +14 -11
- package/bundles/cdk-coercion.umd.js.map +1 -1
- package/bundles/cdk-collections.umd.js +14 -11
- package/bundles/cdk-collections.umd.js.map +1 -1
- package/bundles/cdk-drag-drop.umd.js +14 -11
- package/bundles/cdk-drag-drop.umd.js.map +1 -1
- package/bundles/cdk-overlay.umd.js +14 -11
- package/bundles/cdk-overlay.umd.js.map +1 -1
- package/bundles/cdk-portal.umd.js +14 -11
- package/bundles/cdk-portal.umd.js.map +1 -1
- package/bundles/cdk-scrolling.umd.js +14 -11
- package/bundles/cdk-scrolling.umd.js.map +1 -1
- package/bundles/cdk-stepper.umd.js +9 -3
- package/bundles/cdk-stepper.umd.js.map +1 -1
- package/bundles/cdk-table.umd.js +14 -11
- package/bundles/cdk-table.umd.js.map +1 -1
- package/bundles/cdk-testing-protractor.umd.js +14 -11
- package/bundles/cdk-testing-protractor.umd.js.map +1 -1
- package/bundles/cdk-testing-selenium-webdriver.umd.js +14 -11
- package/bundles/cdk-testing-selenium-webdriver.umd.js.map +1 -1
- package/bundles/cdk-testing-testbed.umd.js +26 -17
- package/bundles/cdk-testing-testbed.umd.js.map +1 -1
- package/bundles/cdk-testing.umd.js +14 -11
- package/bundles/cdk-testing.umd.js.map +1 -1
- package/bundles/cdk-tree.umd.js +14 -11
- package/bundles/cdk-tree.umd.js.map +1 -1
- package/bundles/cdk.umd.js +1 -1
- package/bundles/cdk.umd.js.map +1 -1
- package/esm2015/a11y/focus-monitor/focus-monitor.js +81 -121
- package/esm2015/a11y/input-modality/input-modality-detector.js +179 -0
- package/esm2015/a11y/public-api.js +2 -1
- package/esm2015/stepper/stepper.js +10 -4
- package/esm2015/testing/testbed/fake-events/dispatch-events.js +3 -3
- package/esm2015/testing/testbed/fake-events/event-objects.js +5 -3
- package/esm2015/version.js +1 -1
- package/fesm2015/a11y.js +276 -149
- package/fesm2015/a11y.js.map +1 -1
- package/fesm2015/cdk.js +1 -1
- package/fesm2015/cdk.js.map +1 -1
- package/fesm2015/stepper.js +9 -3
- package/fesm2015/stepper.js.map +1 -1
- package/fesm2015/testing/testbed.js +6 -4
- package/fesm2015/testing/testbed.js.map +1 -1
- package/package.json +2 -2
- package/schematics/ng-add/index.js +1 -1
- package/schematics/ng-add/index.mjs +1 -1
- package/stepper/index.metadata.json +1 -1
- package/stepper/stepper.d.ts +2 -1
- package/testing/testbed/fake-events/dispatch-events.d.ts +1 -1
- package/testing/testbed/fake-events/event-objects.d.ts +1 -1
package/bundles/cdk-a11y.umd.js
CHANGED
|
@@ -576,18 +576,21 @@
|
|
|
576
576
|
function __importDefault(mod) {
|
|
577
577
|
return (mod && mod.__esModule) ? mod : { default: mod };
|
|
578
578
|
}
|
|
579
|
-
function __classPrivateFieldGet(receiver,
|
|
580
|
-
if (!
|
|
581
|
-
throw new TypeError("
|
|
582
|
-
|
|
583
|
-
|
|
579
|
+
function __classPrivateFieldGet(receiver, state, kind, f) {
|
|
580
|
+
if (kind === "a" && !f)
|
|
581
|
+
throw new TypeError("Private accessor was defined without a getter");
|
|
582
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver))
|
|
583
|
+
throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
584
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
584
585
|
}
|
|
585
|
-
function __classPrivateFieldSet(receiver,
|
|
586
|
-
if (
|
|
587
|
-
throw new TypeError("
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
586
|
+
function __classPrivateFieldSet(receiver, state, value, kind, f) {
|
|
587
|
+
if (kind === "m")
|
|
588
|
+
throw new TypeError("Private method is not writable");
|
|
589
|
+
if (kind === "a" && !f)
|
|
590
|
+
throw new TypeError("Private accessor was defined without a setter");
|
|
591
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver))
|
|
592
|
+
throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
593
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
591
594
|
}
|
|
592
595
|
|
|
593
596
|
/**
|
|
@@ -1854,6 +1857,208 @@
|
|
|
1854
1857
|
{ type: undefined, decorators: [{ type: i0.Optional }, { type: i0.Inject, args: [FOCUS_TRAP_INERT_STRATEGY,] }] }
|
|
1855
1858
|
]; };
|
|
1856
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
|
+
|
|
1857
2062
|
/**
|
|
1858
2063
|
* @license
|
|
1859
2064
|
* Copyright Google LLC All Rights Reserved.
|
|
@@ -2038,36 +2243,6 @@
|
|
|
2038
2243
|
politeness: [{ type: i0.Input, args: ['cdkAriaLive',] }]
|
|
2039
2244
|
};
|
|
2040
2245
|
|
|
2041
|
-
/**
|
|
2042
|
-
* @license
|
|
2043
|
-
* Copyright Google LLC All Rights Reserved.
|
|
2044
|
-
*
|
|
2045
|
-
* Use of this source code is governed by an MIT-style license that can be
|
|
2046
|
-
* found in the LICENSE file at https://angular.io/license
|
|
2047
|
-
*/
|
|
2048
|
-
/** Gets whether an event could be a faked `mousedown` event dispatched by a screen reader. */
|
|
2049
|
-
function isFakeMousedownFromScreenReader(event) {
|
|
2050
|
-
// We can typically distinguish between these faked mousedown events and real mousedown events
|
|
2051
|
-
// using the "buttons" property. While real mousedowns will indicate the mouse button that was
|
|
2052
|
-
// pressed (e.g. "1" for the left mouse button), faked mousedowns will usually set the property
|
|
2053
|
-
// value to 0.
|
|
2054
|
-
return event.buttons === 0;
|
|
2055
|
-
}
|
|
2056
|
-
/** Gets whether an event could be a faked `touchstart` event dispatched by a screen reader. */
|
|
2057
|
-
function isFakeTouchstartFromScreenReader(event) {
|
|
2058
|
-
var touch = (event.touches && event.touches[0]) ||
|
|
2059
|
-
(event.changedTouches && event.changedTouches[0]);
|
|
2060
|
-
// A fake `touchstart` can be distinguished from a real one by looking at the `identifier`
|
|
2061
|
-
// which is typically >= 0 on a real device versus -1 from a screen reader. Just to be safe,
|
|
2062
|
-
// we can also look at `radiusX` and `radiusY`. This behavior was observed against a Windows 10
|
|
2063
|
-
// device with a touch screen running NVDA v2020.4 and Firefox 85 or Chrome 88.
|
|
2064
|
-
return !!touch && touch.identifier === -1 && (touch.radiusX == null || touch.radiusX === 1) &&
|
|
2065
|
-
(touch.radiusY == null || touch.radiusY === 1);
|
|
2066
|
-
}
|
|
2067
|
-
|
|
2068
|
-
// This is the value used by AngularJS Material. Through trial and error (on iPhone 6S) they found
|
|
2069
|
-
// that a value of around 650ms seems appropriate.
|
|
2070
|
-
var TOUCH_BUFFER_MS = 650;
|
|
2071
2246
|
/** InjectionToken for FocusMonitorOptions. */
|
|
2072
2247
|
var FOCUS_MONITOR_DEFAULT_OPTIONS = new i0.InjectionToken('cdk-focus-monitor-default-options');
|
|
2073
2248
|
/**
|
|
@@ -2080,16 +2255,22 @@
|
|
|
2080
2255
|
});
|
|
2081
2256
|
/** Monitors mouse and keyboard events to determine the cause of focus events. */
|
|
2082
2257
|
var FocusMonitor = /** @class */ (function () {
|
|
2083
|
-
function FocusMonitor(_ngZone, _platform,
|
|
2258
|
+
function FocusMonitor(_ngZone, _platform, _inputModalityDetector,
|
|
2084
2259
|
/** @breaking-change 11.0.0 make document required */
|
|
2085
2260
|
document, options) {
|
|
2086
2261
|
var _this = this;
|
|
2087
2262
|
this._ngZone = _ngZone;
|
|
2088
2263
|
this._platform = _platform;
|
|
2264
|
+
this._inputModalityDetector = _inputModalityDetector;
|
|
2089
2265
|
/** The focus origin that the next focus event is a result of. */
|
|
2090
2266
|
this._origin = null;
|
|
2091
2267
|
/** Whether the window has just been focused. */
|
|
2092
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;
|
|
2093
2274
|
/** Map of elements being monitored to their info. */
|
|
2094
2275
|
this._elementInfo = new Map();
|
|
2095
2276
|
/** The number of elements currently being monitored. */
|
|
@@ -2101,50 +2282,6 @@
|
|
|
2101
2282
|
* to the document when focus moves inside of a shadow root.
|
|
2102
2283
|
*/
|
|
2103
2284
|
this._rootNodeFocusListenerCount = new Map();
|
|
2104
|
-
/**
|
|
2105
|
-
* Event listener for `keydown` events on the document.
|
|
2106
|
-
* Needs to be an arrow function in order to preserve the context when it gets bound.
|
|
2107
|
-
*/
|
|
2108
|
-
this._documentKeydownListener = function () {
|
|
2109
|
-
// On keydown record the origin and clear any touch event that may be in progress.
|
|
2110
|
-
_this._lastTouchTarget = null;
|
|
2111
|
-
_this._setOriginForCurrentEventQueue('keyboard');
|
|
2112
|
-
};
|
|
2113
|
-
/**
|
|
2114
|
-
* Event listener for `mousedown` events on the document.
|
|
2115
|
-
* Needs to be an arrow function in order to preserve the context when it gets bound.
|
|
2116
|
-
*/
|
|
2117
|
-
this._documentMousedownListener = function (event) {
|
|
2118
|
-
// On mousedown record the origin only if there is not touch
|
|
2119
|
-
// target, since a mousedown can happen as a result of a touch event.
|
|
2120
|
-
if (!_this._lastTouchTarget) {
|
|
2121
|
-
// In some cases screen readers fire fake `mousedown` events instead of `keydown`.
|
|
2122
|
-
// Resolve the focus source to `keyboard` if we detect one of them.
|
|
2123
|
-
var source = isFakeMousedownFromScreenReader(event) ? 'keyboard' : 'mouse';
|
|
2124
|
-
_this._setOriginForCurrentEventQueue(source);
|
|
2125
|
-
}
|
|
2126
|
-
};
|
|
2127
|
-
/**
|
|
2128
|
-
* Event listener for `touchstart` events on the document.
|
|
2129
|
-
* Needs to be an arrow function in order to preserve the context when it gets bound.
|
|
2130
|
-
*/
|
|
2131
|
-
this._documentTouchstartListener = function (event) {
|
|
2132
|
-
// Some screen readers will fire a fake `touchstart` event if an element is activated using
|
|
2133
|
-
// the keyboard while on a device with a touchsreen. Consider such events as keyboard focus.
|
|
2134
|
-
if (!isFakeTouchstartFromScreenReader(event)) {
|
|
2135
|
-
// When the touchstart event fires the focus event is not yet in the event queue. This means
|
|
2136
|
-
// we can't rely on the trick used above (setting timeout of 1ms). Instead we wait 650ms to
|
|
2137
|
-
// see if a focus happens.
|
|
2138
|
-
if (_this._touchTimeoutId != null) {
|
|
2139
|
-
clearTimeout(_this._touchTimeoutId);
|
|
2140
|
-
}
|
|
2141
|
-
_this._lastTouchTarget = getTarget(event);
|
|
2142
|
-
_this._touchTimeoutId = setTimeout(function () { return _this._lastTouchTarget = null; }, TOUCH_BUFFER_MS);
|
|
2143
|
-
}
|
|
2144
|
-
else if (!_this._lastTouchTarget) {
|
|
2145
|
-
_this._setOriginForCurrentEventQueue('keyboard');
|
|
2146
|
-
}
|
|
2147
|
-
};
|
|
2148
2285
|
/**
|
|
2149
2286
|
* Event listener for `focus` events on the window.
|
|
2150
2287
|
* Needs to be an arrow function in order to preserve the context when it gets bound.
|
|
@@ -2155,6 +2292,8 @@
|
|
|
2155
2292
|
_this._windowFocused = true;
|
|
2156
2293
|
_this._windowFocusTimeoutId = setTimeout(function () { return _this._windowFocused = false; });
|
|
2157
2294
|
};
|
|
2295
|
+
/** Subject for stopping our InputModalityDetector subscription. */
|
|
2296
|
+
this._stopInputModalityDetector = new rxjs.Subject();
|
|
2158
2297
|
/**
|
|
2159
2298
|
* Event listener for `focus` and 'blur' events on the document.
|
|
2160
2299
|
* Needs to be an arrow function in order to preserve the context when it gets bound.
|
|
@@ -2227,7 +2366,7 @@
|
|
|
2227
2366
|
});
|
|
2228
2367
|
}
|
|
2229
2368
|
else {
|
|
2230
|
-
this.
|
|
2369
|
+
this._setOrigin(origin);
|
|
2231
2370
|
// `focus` isn't available on the server
|
|
2232
2371
|
if (typeof nativeElement.focus === 'function') {
|
|
2233
2372
|
nativeElement.focus(options);
|
|
@@ -2255,25 +2394,49 @@
|
|
|
2255
2394
|
element.classList.remove(className);
|
|
2256
2395
|
}
|
|
2257
2396
|
};
|
|
2258
|
-
FocusMonitor.prototype._getFocusOrigin = function (
|
|
2259
|
-
// If we couldn't detect a cause for the focus event, it's due to one of three reasons:
|
|
2260
|
-
// 1) The window has just regained focus, in which case we want to restore the focused state of
|
|
2261
|
-
// the element from before the window blurred.
|
|
2262
|
-
// 2) It was caused by a touch event, in which case we mark the origin as 'touch'.
|
|
2263
|
-
// 3) The element was programmatically focused, in which case we should mark the origin as
|
|
2264
|
-
// 'program'.
|
|
2397
|
+
FocusMonitor.prototype._getFocusOrigin = function (focusEventTarget) {
|
|
2265
2398
|
if (this._origin) {
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
else {
|
|
2275
|
-
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
|
+
}
|
|
2276
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));
|
|
2277
2440
|
};
|
|
2278
2441
|
/**
|
|
2279
2442
|
* Sets the focus classes on the element based on the given focus origin.
|
|
@@ -2288,49 +2451,30 @@
|
|
|
2288
2451
|
this._toggleClass(element, 'cdk-program-focused', origin === 'program');
|
|
2289
2452
|
};
|
|
2290
2453
|
/**
|
|
2291
|
-
*
|
|
2292
|
-
*
|
|
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.
|
|
2293
2457
|
* @param origin The origin to set.
|
|
2458
|
+
* @param isFromInteraction Whether we are setting the origin from an interaction event.
|
|
2294
2459
|
*/
|
|
2295
|
-
FocusMonitor.prototype.
|
|
2460
|
+
FocusMonitor.prototype._setOrigin = function (origin, isFromInteraction) {
|
|
2296
2461
|
var _this = this;
|
|
2462
|
+
if (isFromInteraction === void 0) { isFromInteraction = false; }
|
|
2297
2463
|
this._ngZone.runOutsideAngular(function () {
|
|
2298
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.
|
|
2299
2471
|
if (_this._detectionMode === 0 /* IMMEDIATE */) {
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
_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);
|
|
2304
2475
|
}
|
|
2305
2476
|
});
|
|
2306
2477
|
};
|
|
2307
|
-
/**
|
|
2308
|
-
* Checks whether the given focus event was caused by a touchstart event.
|
|
2309
|
-
* @param event The focus event to check.
|
|
2310
|
-
* @returns Whether the event was caused by a touch.
|
|
2311
|
-
*/
|
|
2312
|
-
FocusMonitor.prototype._wasCausedByTouch = function (event) {
|
|
2313
|
-
// Note(mmalerba): This implementation is not quite perfect, there is a small edge case.
|
|
2314
|
-
// Consider the following dom structure:
|
|
2315
|
-
//
|
|
2316
|
-
// <div #parent tabindex="0" cdkFocusClasses>
|
|
2317
|
-
// <div #child (click)="#parent.focus()"></div>
|
|
2318
|
-
// </div>
|
|
2319
|
-
//
|
|
2320
|
-
// If the user touches the #child element and the #parent is programmatically focused as a
|
|
2321
|
-
// result, this code will still consider it to have been caused by the touch event and will
|
|
2322
|
-
// apply the cdk-touch-focused class rather than the cdk-program-focused class. This is a
|
|
2323
|
-
// relatively small edge-case that can be worked around by using
|
|
2324
|
-
// focusVia(parentEl, 'program') to focus the parent element.
|
|
2325
|
-
//
|
|
2326
|
-
// If we decide that we absolutely must handle this case correctly, we can do so by listening
|
|
2327
|
-
// for the first focus event after the touchstart, and then the first blur event after that
|
|
2328
|
-
// focus event. When that blur event fires we know that whatever follows is not a result of the
|
|
2329
|
-
// touchstart.
|
|
2330
|
-
var focusTarget = getTarget(event);
|
|
2331
|
-
return this._lastTouchTarget instanceof Node && focusTarget instanceof Node &&
|
|
2332
|
-
(focusTarget === this._lastTouchTarget || focusTarget.contains(this._lastTouchTarget));
|
|
2333
|
-
};
|
|
2334
2478
|
/**
|
|
2335
2479
|
* Handles focus events on a registered element.
|
|
2336
2480
|
* @param event The focus event.
|
|
@@ -2344,10 +2488,11 @@
|
|
|
2344
2488
|
// If we are not counting child-element-focus as focused, make sure that the event target is the
|
|
2345
2489
|
// monitored element itself.
|
|
2346
2490
|
var elementInfo = this._elementInfo.get(element);
|
|
2347
|
-
|
|
2491
|
+
var focusEventTarget = getTarget(event);
|
|
2492
|
+
if (!elementInfo || (!elementInfo.checkChildren && element !== focusEventTarget)) {
|
|
2348
2493
|
return;
|
|
2349
2494
|
}
|
|
2350
|
-
this._originChanged(element, this._getFocusOrigin(
|
|
2495
|
+
this._originChanged(element, this._getFocusOrigin(focusEventTarget), elementInfo);
|
|
2351
2496
|
};
|
|
2352
2497
|
/**
|
|
2353
2498
|
* Handles blur events on a registered element.
|
|
@@ -2387,13 +2532,13 @@
|
|
|
2387
2532
|
// Note: we listen to events in the capture phase so we
|
|
2388
2533
|
// can detect them even if the user stops propagation.
|
|
2389
2534
|
this._ngZone.runOutsideAngular(function () {
|
|
2390
|
-
var document = _this._getDocument();
|
|
2391
2535
|
var window = _this._getWindow();
|
|
2392
|
-
document.addEventListener('keydown', _this._documentKeydownListener, captureEventListenerOptions);
|
|
2393
|
-
document.addEventListener('mousedown', _this._documentMousedownListener, captureEventListenerOptions);
|
|
2394
|
-
document.addEventListener('touchstart', _this._documentTouchstartListener, captureEventListenerOptions);
|
|
2395
2536
|
window.addEventListener('focus', _this._windowFocusListener);
|
|
2396
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 */); });
|
|
2397
2542
|
}
|
|
2398
2543
|
};
|
|
2399
2544
|
FocusMonitor.prototype._removeGlobalListeners = function (elementInfo) {
|
|
@@ -2411,15 +2556,12 @@
|
|
|
2411
2556
|
}
|
|
2412
2557
|
// Unregister global listeners when last element is unmonitored.
|
|
2413
2558
|
if (!--this._monitoredElementCount) {
|
|
2414
|
-
var document = this._getDocument();
|
|
2415
2559
|
var window = this._getWindow();
|
|
2416
|
-
document.removeEventListener('keydown', this._documentKeydownListener, captureEventListenerOptions);
|
|
2417
|
-
document.removeEventListener('mousedown', this._documentMousedownListener, captureEventListenerOptions);
|
|
2418
|
-
document.removeEventListener('touchstart', this._documentTouchstartListener, captureEventListenerOptions);
|
|
2419
2560
|
window.removeEventListener('focus', this._windowFocusListener);
|
|
2561
|
+
// Equivalently, stop our InputModalityDetector subscription.
|
|
2562
|
+
this._stopInputModalityDetector.next();
|
|
2420
2563
|
// Clear timeouts for all potentially pending timeouts to prevent the leaks.
|
|
2421
2564
|
clearTimeout(this._windowFocusTimeoutId);
|
|
2422
|
-
clearTimeout(this._touchTimeoutId);
|
|
2423
2565
|
clearTimeout(this._originTimeoutId);
|
|
2424
2566
|
}
|
|
2425
2567
|
};
|
|
@@ -2445,22 +2587,17 @@
|
|
|
2445
2587
|
};
|
|
2446
2588
|
return FocusMonitor;
|
|
2447
2589
|
}());
|
|
2448
|
-
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" });
|
|
2449
2591
|
FocusMonitor.decorators = [
|
|
2450
2592
|
{ type: i0.Injectable, args: [{ providedIn: 'root' },] }
|
|
2451
2593
|
];
|
|
2452
2594
|
FocusMonitor.ctorParameters = function () { return [
|
|
2453
2595
|
{ type: i0.NgZone },
|
|
2454
2596
|
{ type: i1.Platform },
|
|
2597
|
+
{ type: InputModalityDetector },
|
|
2455
2598
|
{ type: undefined, decorators: [{ type: i0.Optional }, { type: i0.Inject, args: [i2.DOCUMENT,] }] },
|
|
2456
2599
|
{ type: undefined, decorators: [{ type: i0.Optional }, { type: i0.Inject, args: [FOCUS_MONITOR_DEFAULT_OPTIONS,] }] }
|
|
2457
2600
|
]; };
|
|
2458
|
-
/** Gets the target of an event, accounting for Shadow DOM. */
|
|
2459
|
-
function getTarget(event) {
|
|
2460
|
-
// If an event is bound outside the Shadow DOM, the `event.target` will
|
|
2461
|
-
// point to the shadow root so we have to use `composedPath` instead.
|
|
2462
|
-
return (event.composedPath ? event.composedPath()[0] : event.target);
|
|
2463
|
-
}
|
|
2464
2601
|
/**
|
|
2465
2602
|
* Directive that determines how a particular element was focused (via keyboard, mouse, touch, or
|
|
2466
2603
|
* programmatically) and adds corresponding classes to the element.
|
|
@@ -2644,6 +2781,9 @@
|
|
|
2644
2781
|
exports.FocusTrap = FocusTrap;
|
|
2645
2782
|
exports.FocusTrapFactory = FocusTrapFactory;
|
|
2646
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;
|
|
2647
2787
|
exports.InteractivityChecker = InteractivityChecker;
|
|
2648
2788
|
exports.IsFocusableConfig = IsFocusableConfig;
|
|
2649
2789
|
exports.LIVE_ANNOUNCER_DEFAULT_OPTIONS = LIVE_ANNOUNCER_DEFAULT_OPTIONS;
|
|
@@ -2652,7 +2792,6 @@
|
|
|
2652
2792
|
exports.ListKeyManager = ListKeyManager;
|
|
2653
2793
|
exports.LiveAnnouncer = LiveAnnouncer;
|
|
2654
2794
|
exports.MESSAGES_CONTAINER_ID = MESSAGES_CONTAINER_ID;
|
|
2655
|
-
exports.TOUCH_BUFFER_MS = TOUCH_BUFFER_MS;
|
|
2656
2795
|
exports.isFakeMousedownFromScreenReader = isFakeMousedownFromScreenReader;
|
|
2657
2796
|
exports.isFakeTouchstartFromScreenReader = isFakeTouchstartFromScreenReader;
|
|
2658
2797
|
exports.ɵangular_material_src_cdk_a11y_a11y_a = FocusTrapManager;
|