md2
Version:
Angular2 based Material Design components, directives and services are Accordion, Autocomplete, Chips(Tags), Collapse, Colorpicker, Data Table, Datepicker, Dialog(Modal), Menu, Multiselect, Select, Tabs, Tags(Chips), Toast and Tooltip.
289 lines • 14.3 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { Directive, ElementRef, EventEmitter, Injectable, NgZone, Optional, Output, Renderer2, SkipSelf, } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import { Platform } from '../platform/platform';
import 'rxjs/add/observable/of';
// This is the value used by AngularJS Material. Through trial and error (on iPhone 6S) they found
// that a value of around 650ms seems appropriate.
export var TOUCH_BUFFER_MS = 650;
/** Monitors mouse and keyboard events to determine the cause of focus events. */
var FocusOriginMonitor = (function () {
function FocusOriginMonitor(_ngZone, _platform) {
var _this = this;
this._ngZone = _ngZone;
this._platform = _platform;
/** The focus origin that the next focus event is a result of. */
this._origin = null;
/** Whether the window has just been focused. */
this._windowFocused = false;
/** Weak map of elements being monitored to their info. */
this._elementInfo = new WeakMap();
this._ngZone.runOutsideAngular(function () { return _this._registerDocumentEvents(); });
}
/**
* Monitors focus on an element and applies appropriate CSS classes.
* @param element The element to monitor
* @param renderer The renderer to use to apply CSS classes to the element.
* @param checkChildren Whether to count the element as focused when its children are focused.
* @returns An observable that emits when the focus state of the element changes.
* When the element is blurred, null will be emitted.
*/
FocusOriginMonitor.prototype.monitor = function (element, renderer, checkChildren) {
var _this = this;
// Do nothing if we're not on the browser platform.
if (!this._platform.isBrowser) {
return Observable.of();
}
// Check if we're already monitoring this element.
if (this._elementInfo.has(element)) {
var info_1 = this._elementInfo.get(element);
info_1.checkChildren = checkChildren;
return info_1.subject.asObservable();
}
// Create monitored element info.
var info = {
unlisten: null,
checkChildren: checkChildren,
renderer: renderer,
subject: new Subject()
};
this._elementInfo.set(element, info);
// Start listening. We need to listen in capture phase since focus events don't bubble.
var focusListener = function (event) { return _this._onFocus(event, element); };
var blurListener = function (event) { return _this._onBlur(event, element); };
this._ngZone.runOutsideAngular(function () {
element.addEventListener('focus', focusListener, true);
element.addEventListener('blur', blurListener, true);
});
// Create an unlisten function for later.
info.unlisten = function () {
element.removeEventListener('focus', focusListener, true);
element.removeEventListener('blur', blurListener, true);
};
return info.subject.asObservable();
};
/**
* Stops monitoring an element and removes all focus classes.
* @param element The element to stop monitoring.
*/
FocusOriginMonitor.prototype.stopMonitoring = function (element) {
var elementInfo = this._elementInfo.get(element);
if (elementInfo) {
elementInfo.unlisten();
elementInfo.subject.complete();
this._setClasses(element, null);
this._elementInfo.delete(element);
}
};
/**
* Focuses the element via the specified focus origin.
* @param element The element to focus.
* @param origin The focus origin.
*/
FocusOriginMonitor.prototype.focusVia = function (element, origin) {
this._setOriginForCurrentEventQueue(origin);
element.focus();
};
/** Register necessary event listeners on the document and window. */
FocusOriginMonitor.prototype._registerDocumentEvents = function () {
var _this = this;
// Do nothing if we're not on the browser platform.
if (!this._platform.isBrowser) {
return;
}
// Note: we listen to events in the capture phase so we can detect them even if the user stops
// propagation.
// On keydown record the origin and clear any touch event that may be in progress.
document.addEventListener('keydown', function () {
_this._lastTouchTarget = null;
_this._setOriginForCurrentEventQueue('keyboard');
}, true);
// On mousedown record the origin only if there is not touch target, since a mousedown can
// happen as a result of a touch event.
document.addEventListener('mousedown', function () {
if (!_this._lastTouchTarget) {
_this._setOriginForCurrentEventQueue('mouse');
}
}, true);
// When the touchstart event fires the focus event is not yet in the event queue. This means
// we can't rely on the trick used above (setting timeout of 0ms). Instead we wait 650ms to
// see if a focus happens.
document.addEventListener('touchstart', function (event) {
if (_this._touchTimeout != null) {
clearTimeout(_this._touchTimeout);
}
_this._lastTouchTarget = event.target;
_this._touchTimeout = setTimeout(function () { return _this._lastTouchTarget = null; }, TOUCH_BUFFER_MS);
}, true);
// Make a note of when the window regains focus, so we can restore the origin info for the
// focused element.
window.addEventListener('focus', function () {
_this._windowFocused = true;
setTimeout(function () { return _this._windowFocused = false; }, 0);
});
};
/**
* Sets the focus classes on the element based on the given focus origin.
* @param element The element to update the classes on.
* @param origin The focus origin.
*/
FocusOriginMonitor.prototype._setClasses = function (element, origin) {
var renderer = this._elementInfo.get(element).renderer;
var toggleClass = function (className, shouldSet) {
shouldSet ? renderer.addClass(element, className) : renderer.removeClass(element, className);
};
toggleClass('cdk-focused', !!origin);
toggleClass('cdk-touch-focused', origin === 'touch');
toggleClass('cdk-keyboard-focused', origin === 'keyboard');
toggleClass('cdk-mouse-focused', origin === 'mouse');
toggleClass('cdk-program-focused', origin === 'program');
};
/**
* Sets the origin and schedules an async function to clear it at the end of the event queue.
* @param origin The origin to set.
*/
FocusOriginMonitor.prototype._setOriginForCurrentEventQueue = function (origin) {
var _this = this;
this._origin = origin;
setTimeout(function () { return _this._origin = null; }, 0);
};
/**
* Checks whether the given focus event was caused by a touchstart event.
* @param event The focus event to check.
* @returns Whether the event was caused by a touch.
*/
FocusOriginMonitor.prototype._wasCausedByTouch = function (event) {
// Note(mmalerba): This implementation is not quite perfect, there is a small edge case.
// Consider the following dom structure:
//
// <div #parent tabindex="0" cdkFocusClasses>
// <div #child (click)="#parent.focus()"></div>
// </div>
//
// If the user touches the #child element and the #parent is programmatically focused as a
// result, this code will still consider it to have been caused by the touch event and will
// apply the cdk-touch-focused class rather than the cdk-program-focused class. This is a
// relatively small edge-case that can be worked around by using
// focusVia(parentEl, renderer, 'program') to focus the parent element.
//
// If we decide that we absolutely must handle this case correctly, we can do so by listening
// for the first focus event after the touchstart, and then the first blur event after that
// focus event. When that blur event fires we know that whatever follows is not a result of the
// touchstart.
var focusTarget = event.target;
return this._lastTouchTarget instanceof Node && focusTarget instanceof Node &&
(focusTarget === this._lastTouchTarget || focusTarget.contains(this._lastTouchTarget));
};
/**
* Handles focus events on a registered element.
* @param event The focus event.
* @param element The monitored element.
*/
FocusOriginMonitor.prototype._onFocus = function (event, element) {
// NOTE(mmalerba): We currently set the classes based on the focus origin of the most recent
// focus event affecting the monitored element. If we want to use the origin of the first event
// instead we should check for the cdk-focused class here and return if the element already has
// it. (This only matters for elements that have includesChildren = true).
// If we are not counting child-element-focus as focused, make sure that the event target is the
// monitored element itself.
if (!this._elementInfo.get(element).checkChildren && element !== event.target) {
return;
}
// If we couldn't detect a cause for the focus event, it's due to one of three reasons:
// 1) The window has just regained focus, in which case we want to restore the focused state of
// the element from before the window blurred.
// 2) It was caused by a touch event, in which case we mark the origin as 'touch'.
// 3) The element was programmatically focused, in which case we should mark the origin as
// 'program'.
if (!this._origin) {
if (this._windowFocused && this._lastFocusOrigin) {
this._origin = this._lastFocusOrigin;
}
else if (this._wasCausedByTouch(event)) {
this._origin = 'touch';
}
else {
this._origin = 'program';
}
}
this._setClasses(element, this._origin);
this._elementInfo.get(element).subject.next(this._origin);
this._lastFocusOrigin = this._origin;
this._origin = null;
};
/**
* Handles blur events on a registered element.
* @param event The blur event.
* @param element The monitored element.
*/
FocusOriginMonitor.prototype._onBlur = function (event, element) {
// If we are counting child-element-focus as focused, make sure that we aren't just blurring in
// order to focus another child of the monitored element.
if (this._elementInfo.get(element).checkChildren && event.relatedTarget instanceof Node &&
element.contains(event.relatedTarget)) {
return;
}
this._setClasses(element, null);
this._elementInfo.get(element).subject.next(null);
};
return FocusOriginMonitor;
}());
FocusOriginMonitor = __decorate([
Injectable(),
__metadata("design:paramtypes", [NgZone, Platform])
], FocusOriginMonitor);
export { FocusOriginMonitor };
/**
* Directive that determines how a particular element was focused (via keyboard, mouse, touch, or
* programmatically) and adds corresponding classes to the element.
*
* There are two variants of this directive:
* 1) cdkMonitorElementFocus: does not consider an element to be focused if one of its children is
* focused.
* 2) cdkMonitorSubtreeFocus: considers an element focused if it or any of its children are focused.
*/
var CdkMonitorFocus = (function () {
function CdkMonitorFocus(_elementRef, _focusOriginMonitor, renderer) {
var _this = this;
this._elementRef = _elementRef;
this._focusOriginMonitor = _focusOriginMonitor;
this.cdkFocusChange = new EventEmitter();
this._focusOriginMonitor.monitor(this._elementRef.nativeElement, renderer, this._elementRef.nativeElement.hasAttribute('cdkMonitorSubtreeFocus'))
.subscribe(function (origin) { return _this.cdkFocusChange.emit(origin); });
}
CdkMonitorFocus.prototype.ngOnDestroy = function () {
this._focusOriginMonitor.stopMonitoring(this._elementRef.nativeElement);
};
return CdkMonitorFocus;
}());
__decorate([
Output(),
__metadata("design:type", Object)
], CdkMonitorFocus.prototype, "cdkFocusChange", void 0);
CdkMonitorFocus = __decorate([
Directive({
selector: '[cdkMonitorElementFocus], [cdkMonitorSubtreeFocus]',
}),
__metadata("design:paramtypes", [ElementRef, FocusOriginMonitor,
Renderer2])
], CdkMonitorFocus);
export { CdkMonitorFocus };
export function FOCUS_ORIGIN_MONITOR_PROVIDER_FACTORY(parentDispatcher, ngZone, platform) {
return parentDispatcher || new FocusOriginMonitor(ngZone, platform);
}
export var FOCUS_ORIGIN_MONITOR_PROVIDER = {
// If there is already a FocusOriginMonitor available, use that. Otherwise, provide a new one.
provide: FocusOriginMonitor,
deps: [[new Optional(), new SkipSelf(), FocusOriginMonitor], NgZone, Platform],
useFactory: FOCUS_ORIGIN_MONITOR_PROVIDER_FACTORY
};
//# sourceMappingURL=focus-origin-monitor.js.map