@angular2-material/core
Version:
Angular 2 Material core
1,150 lines (1,136 loc) • 80.2 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular2-material/core'), require('@angular/platform-browser')) :
typeof define === 'function' && define.amd ? define(['exports', '@angular/core', '@angular2-material/core', '@angular/platform-browser'], factory) :
(factory((global.md = global.md || {}, global.md.core = global.md.core || {}),global.ng.core,global.md.core,global.ng.platformBrowser));
}(this, (function (exports,_angular_core,_angular2Material_core,_angular_platformBrowser) { 'use strict';
var __decorate$1 = (window && window.__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$1 = (window && window.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
/**
* Shared directive to count lines inside a text area, such as a list item.
* Line elements can be extracted with a @ContentChildren(MdLine) query, then
* counted by checking the query list's length.
*/
var MdLine = (function () {
function MdLine() {
}
MdLine = __decorate$1([
_angular_core.Directive({ selector: '[md-line]' }),
__metadata$1('design:paramtypes', [])
], MdLine);
return MdLine;
}());
/* Helper that takes a query list of lines and sets the correct class on the host */
var MdLineSetter = (function () {
function MdLineSetter(_lines, _renderer, _element) {
var _this = this;
this._lines = _lines;
this._renderer = _renderer;
this._element = _element;
this._setLineClass(this._lines.length);
this._lines.changes.subscribe(function () {
_this._setLineClass(_this._lines.length);
});
}
MdLineSetter.prototype._setLineClass = function (count) {
this._resetClasses();
if (count === 2 || count === 3) {
this._setClass("md-" + count + "-line", true);
}
};
MdLineSetter.prototype._resetClasses = function () {
this._setClass('md-2-line', false);
this._setClass('md-3-line', false);
};
MdLineSetter.prototype._setClass = function (className, bool) {
this._renderer.setElementClass(this._element.nativeElement, className, bool);
};
return MdLineSetter;
}());
var MdLineModule = (function () {
function MdLineModule() {
}
MdLineModule = __decorate$1([
_angular_core.NgModule({
exports: [MdLine],
declarations: [MdLine],
}),
__metadata$1('design:paramtypes', [])
], MdLineModule);
return MdLineModule;
}());
var __decorate$2 = (window && window.__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$2 = (window && window.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
/**
* Directive to listen to changes of direction of part of the DOM.
*
* Applications should use this directive instead of the native attribute so that Material
* components can listen on changes of direction.
*/
var Dir = (function () {
function Dir() {
this._dir = 'ltr';
this.dirChange = new _angular_core.EventEmitter();
}
Object.defineProperty(Dir.prototype, "dir", {
get: function () {
return this._dir;
},
set: function (v) {
var old = this._dir;
this._dir = v;
if (old != this._dir) {
this.dirChange.emit(null);
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(Dir.prototype, "value", {
get: function () { return this.dir; },
set: function (v) { this.dir = v; },
enumerable: true,
configurable: true
});
__decorate$2([
_angular_core.Input('dir'),
__metadata$2('design:type', String)
], Dir.prototype, "_dir", void 0);
__decorate$2([
_angular_core.Output(),
__metadata$2('design:type', Object)
], Dir.prototype, "dirChange", void 0);
__decorate$2([
_angular_core.HostBinding('attr.dir'),
__metadata$2('design:type', String)
], Dir.prototype, "dir", null);
Dir = __decorate$2([
_angular_core.Directive({
selector: '[dir]',
// TODO(hansl): maybe `$implicit` isn't the best option here, but for now that's the best we got.
exportAs: '$implicit'
}),
__metadata$2('design:paramtypes', [])
], Dir);
return Dir;
}());
var RtlModule = (function () {
function RtlModule() {
}
RtlModule.forRoot = function () {
return {
ngModule: RtlModule,
providers: []
};
};
RtlModule = __decorate$2([
_angular_core.NgModule({
exports: [Dir],
declarations: [Dir]
}),
__metadata$2('design:paramtypes', [])
], RtlModule);
return RtlModule;
}());
/** TODO: internal */
var ForegroundRippleState;
(function (ForegroundRippleState) {
ForegroundRippleState[ForegroundRippleState["NEW"] = 0] = "NEW";
ForegroundRippleState[ForegroundRippleState["EXPANDING"] = 1] = "EXPANDING";
ForegroundRippleState[ForegroundRippleState["FADING_OUT"] = 2] = "FADING_OUT";
})(ForegroundRippleState || (ForegroundRippleState = {}));
/**
* Wrapper for a foreground ripple DOM element and its animation state.
* TODO: internal
*/
var ForegroundRipple = (function () {
function ForegroundRipple(rippleElement) {
this.rippleElement = rippleElement;
this.state = ForegroundRippleState.NEW;
}
return ForegroundRipple;
}());
var RIPPLE_SPEED_PX_PER_SECOND = 1000;
var MIN_RIPPLE_FILL_TIME_SECONDS = 0.1;
var MAX_RIPPLE_FILL_TIME_SECONDS = 0.3;
/**
* Returns the distance from the point (x, y) to the furthest corner of a rectangle.
*/
var distanceToFurthestCorner = function (x, y, rect) {
var distX = Math.max(Math.abs(x - rect.left), Math.abs(x - rect.right));
var distY = Math.max(Math.abs(y - rect.top), Math.abs(y - rect.bottom));
return Math.sqrt(distX * distX + distY * distY);
};
/**
* Helper service that performs DOM manipulations. Not intended to be used outside this module.
* The constructor takes a reference to the ripple directive's host element and a map of DOM
* event handlers to be installed on the element that triggers ripple animations.
* This will eventually become a custom renderer once Angular support exists.
* TODO: internal
*/
var RippleRenderer = (function () {
function RippleRenderer(_elementRef, _eventHandlers) {
this._eventHandlers = _eventHandlers;
this._rippleElement = _elementRef.nativeElement;
// It might be nice to delay creating the background until it's needed, but doing this in
// fadeInRippleBackground causes the first click event to not be handled reliably.
this._backgroundDiv = document.createElement('div');
this._backgroundDiv.classList.add('md-ripple-background');
this._rippleElement.appendChild(this._backgroundDiv);
}
/**
* Installs event handlers on the given trigger element, and removes event handlers from the
* previous trigger if needed.
*/
RippleRenderer.prototype.setTriggerElement = function (newTrigger) {
var _this = this;
if (this._triggerElement !== newTrigger) {
if (this._triggerElement) {
this._eventHandlers.forEach(function (eventHandler, eventName) {
_this._triggerElement.removeEventListener(eventName, eventHandler);
});
}
this._triggerElement = newTrigger;
if (this._triggerElement) {
this._eventHandlers.forEach(function (eventHandler, eventName) {
_this._triggerElement.addEventListener(eventName, eventHandler);
});
}
}
};
/**
* Installs event handlers on the host element of the md-ripple directive.
*/
RippleRenderer.prototype.setTriggerElementToHost = function () {
this.setTriggerElement(this._rippleElement);
};
/**
* Removes event handlers from the current trigger element if needed.
*/
RippleRenderer.prototype.clearTriggerElement = function () {
this.setTriggerElement(null);
};
/**
* Creates a foreground ripple and sets its animation to expand and fade in from the position
* given by rippleOriginLeft and rippleOriginTop (or from the center of the <md-ripple>
* bounding rect if centered is true).
*/
RippleRenderer.prototype.createForegroundRipple = function (rippleOriginLeft, rippleOriginTop, color, centered, radius, speedFactor, transitionEndCallback) {
var parentRect = this._rippleElement.getBoundingClientRect();
// Create a foreground ripple div with the size and position of the fully expanded ripple.
// When the div is created, it's given a transform style that causes the ripple to be displayed
// small and centered on the event location (or the center of the bounding rect if the centered
// argument is true). Removing that transform causes the ripple to animate to its natural size.
var startX = centered ? (parentRect.left + parentRect.width / 2) : rippleOriginLeft;
var startY = centered ? (parentRect.top + parentRect.height / 2) : rippleOriginTop;
var offsetX = startX - parentRect.left;
var offsetY = startY - parentRect.top;
var maxRadius = radius > 0 ? radius : distanceToFurthestCorner(startX, startY, parentRect);
var rippleDiv = document.createElement('div');
this._rippleElement.appendChild(rippleDiv);
rippleDiv.classList.add('md-ripple-foreground');
rippleDiv.style.left = (offsetX - maxRadius) + "px";
rippleDiv.style.top = (offsetY - maxRadius) + "px";
rippleDiv.style.width = 2 * maxRadius + "px";
rippleDiv.style.height = rippleDiv.style.width;
// If color input is not set, this will default to the background color defined in CSS.
rippleDiv.style.backgroundColor = color;
// Start the ripple tiny.
rippleDiv.style.transform = "scale(0.001)";
var fadeInSeconds = (1 / (speedFactor || 1)) * Math.max(MIN_RIPPLE_FILL_TIME_SECONDS, Math.min(MAX_RIPPLE_FILL_TIME_SECONDS, maxRadius / RIPPLE_SPEED_PX_PER_SECOND));
rippleDiv.style.transitionDuration = fadeInSeconds + "s";
// https://timtaubert.de/blog/2012/09/css-transitions-for-dynamically-created-dom-elements/
window.getComputedStyle(rippleDiv).opacity;
rippleDiv.classList.add('md-ripple-fade-in');
// Clearing the transform property causes the ripple to animate to its full size.
rippleDiv.style.transform = '';
var ripple = new ForegroundRipple(rippleDiv);
ripple.state = ForegroundRippleState.EXPANDING;
rippleDiv.addEventListener('transitionend', function (event) { return transitionEndCallback(ripple, event); });
};
/**
* Fades out a foreground ripple after it has fully expanded and faded in.
*/
RippleRenderer.prototype.fadeOutForegroundRipple = function (ripple) {
ripple.classList.remove('md-ripple-fade-in');
ripple.classList.add('md-ripple-fade-out');
};
/**
* Removes a foreground ripple from the DOM after it has faded out.
*/
RippleRenderer.prototype.removeRippleFromDom = function (ripple) {
ripple.parentElement.removeChild(ripple);
};
/**
* Fades in the ripple background.
*/
RippleRenderer.prototype.fadeInRippleBackground = function (color) {
this._backgroundDiv.classList.add('md-ripple-active');
// If color is not set, this will default to the background color defined in CSS.
this._backgroundDiv.style.backgroundColor = color;
};
/**
* Fades out the ripple background.
*/
RippleRenderer.prototype.fadeOutRippleBackground = function () {
if (this._backgroundDiv) {
this._backgroundDiv.classList.remove('md-ripple-active');
}
};
return RippleRenderer;
}());
var __decorate$3 = (window && window.__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$3 = (window && window.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var MdRipple = (function () {
function MdRipple(_elementRef) {
var _this = this;
/**
* If set, the radius in pixels of foreground ripples when fully expanded. If unset, the radius
* will be the distance from the center of the ripple to the furthest corner of the host element's
* bounding rectangle.
*/
this.maxRadius = 0;
/**
* If set, the normal duration of ripple animations is divided by this value. For example,
* setting it to 0.5 will cause the animations to take twice as long.
*/
this.speedFactor = 1;
// These event handlers are attached to the element that triggers the ripple animations.
var eventHandlers = new Map();
eventHandlers.set('mousedown', function (event) { return _this._mouseDown(event); });
eventHandlers.set('click', function (event) { return _this._click(event); });
eventHandlers.set('mouseleave', function (event) { return _this._mouseLeave(event); });
this._rippleRenderer = new RippleRenderer(_elementRef, eventHandlers);
}
/** TODO: internal */
MdRipple.prototype.ngOnInit = function () {
// If no trigger element was explicity set, use the host element
if (!this.trigger) {
this._rippleRenderer.setTriggerElementToHost();
}
};
/** TODO: internal */
MdRipple.prototype.ngOnDestroy = function () {
// Remove event listeners on the trigger element.
this._rippleRenderer.clearTriggerElement();
};
/** TODO: internal */
MdRipple.prototype.ngOnChanges = function (changes) {
// If the trigger element changed (or is being initially set), add event listeners to it.
var changedInputs = Object.keys(changes);
if (changedInputs.indexOf('trigger') !== -1) {
this._rippleRenderer.setTriggerElement(this.trigger);
}
};
/**
* Responds to the start of a ripple animation trigger by fading the background in.
*/
MdRipple.prototype.start = function () {
this._rippleRenderer.fadeInRippleBackground(this.backgroundColor);
};
/**
* Responds to the end of a ripple animation trigger by fading the background out, and creating a
* foreground ripple that expands from the event location (or from the center of the element if
* the "centered" property is set or forceCenter is true).
*/
MdRipple.prototype.end = function (left, top, forceCenter) {
var _this = this;
if (forceCenter === void 0) { forceCenter = true; }
this._rippleRenderer.createForegroundRipple(left, top, this.color, this.centered || forceCenter, this.maxRadius, this.speedFactor, function (ripple, e) { return _this._rippleTransitionEnded(ripple, e); });
this._rippleRenderer.fadeOutRippleBackground();
};
MdRipple.prototype._rippleTransitionEnded = function (ripple, event) {
if (event.propertyName === 'opacity') {
// If the ripple finished expanding, start fading it out. If it finished fading out,
// remove it from the DOM.
switch (ripple.state) {
case ForegroundRippleState.EXPANDING:
this._rippleRenderer.fadeOutForegroundRipple(ripple.rippleElement);
ripple.state = ForegroundRippleState.FADING_OUT;
break;
case ForegroundRippleState.FADING_OUT:
this._rippleRenderer.removeRippleFromDom(ripple.rippleElement);
break;
}
}
};
/**
* Called when the trigger element receives a mousedown event. Starts the ripple animation by
* fading in the background.
*/
MdRipple.prototype._mouseDown = function (event) {
if (!this.disabled && event.button === 0) {
this.start();
}
};
/**
* Called when the trigger element receives a click event. Creates a foreground ripple and
* runs its animation.
*/
MdRipple.prototype._click = function (event) {
if (!this.disabled && event.button === 0) {
// If screen and page positions are all 0, this was probably triggered by a keypress.
// In that case, use the center of the bounding rect as the ripple origin.
// FIXME: This fails on IE11, which still sets pageX/Y and screenX/Y on keyboard clicks.
var isKeyEvent = (event.screenX === 0 && event.screenY === 0 && event.pageX === 0 && event.pageY === 0);
this.end(event.pageX, event.pageY, isKeyEvent);
}
};
/**
* Called when the trigger element receives a mouseleave event. Fades out the background.
*/
MdRipple.prototype._mouseLeave = function (event) {
// We can always fade out the background here; It's a no-op if it was already inactive.
this._rippleRenderer.fadeOutRippleBackground();
};
__decorate$3([
_angular_core.Input('md-ripple-trigger'),
__metadata$3('design:type', Object)
], MdRipple.prototype, "trigger", void 0);
__decorate$3([
_angular_core.Input('md-ripple-centered'),
__metadata$3('design:type', Boolean)
], MdRipple.prototype, "centered", void 0);
__decorate$3([
_angular_core.Input('md-ripple-disabled'),
__metadata$3('design:type', Boolean)
], MdRipple.prototype, "disabled", void 0);
__decorate$3([
_angular_core.Input('md-ripple-max-radius'),
__metadata$3('design:type', Number)
], MdRipple.prototype, "maxRadius", void 0);
__decorate$3([
_angular_core.Input('md-ripple-speed-factor'),
__metadata$3('design:type', Number)
], MdRipple.prototype, "speedFactor", void 0);
__decorate$3([
_angular_core.Input('md-ripple-color'),
__metadata$3('design:type', String)
], MdRipple.prototype, "color", void 0);
__decorate$3([
_angular_core.Input('md-ripple-background-color'),
__metadata$3('design:type', String)
], MdRipple.prototype, "backgroundColor", void 0);
__decorate$3([
_angular_core.HostBinding('class.md-ripple-focused'),
_angular_core.Input('md-ripple-focused'),
__metadata$3('design:type', Boolean)
], MdRipple.prototype, "focused", void 0);
__decorate$3([
_angular_core.HostBinding('class.md-ripple-unbounded'),
_angular_core.Input('md-ripple-unbounded'),
__metadata$3('design:type', Boolean)
], MdRipple.prototype, "unbounded", void 0);
MdRipple = __decorate$3([
_angular_core.Directive({
selector: '[md-ripple]',
}),
__metadata$3('design:paramtypes', [_angular_core.ElementRef])
], MdRipple);
return MdRipple;
}());
var MdRippleModule = (function () {
function MdRippleModule() {
}
MdRippleModule.forRoot = function () {
return {
ngModule: MdRippleModule,
providers: []
};
};
MdRippleModule = __decorate$3([
_angular_core.NgModule({
exports: [MdRipple],
declarations: [MdRipple],
}),
__metadata$3('design:paramtypes', [])
], MdRippleModule);
return MdRippleModule;
}());
// TODO(kara): Revisit why error messages are not being properly set.
var __extends$3 = (window && window.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
/**
* Wrapper around Error that sets the error message.
*/
var MdError = (function (_super) {
__extends$3(MdError, _super);
function MdError(value) {
_super.call(this);
this.message = value;
}
return MdError;
}(Error));
var __extends$2 = (window && window.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
/** Exception thrown when a ComponentPortal is attached to a DomPortalHost without an origin. */
var MdComponentPortalAttachedToDomWithoutOriginError = (function (_super) {
__extends$2(MdComponentPortalAttachedToDomWithoutOriginError, _super);
function MdComponentPortalAttachedToDomWithoutOriginError() {
_super.call(this, 'A ComponentPortal must have an origin set when attached to a DomPortalHost ' +
'because the DOM element is not part of the Angular application context.');
}
return MdComponentPortalAttachedToDomWithoutOriginError;
}(MdError));
/** Exception thrown when attempting to attach a null portal to a host. */
var MdNullPortalError = (function (_super) {
__extends$2(MdNullPortalError, _super);
function MdNullPortalError() {
_super.call(this, 'Must provide a portal to attach');
}
return MdNullPortalError;
}(MdError));
/** Exception thrown when attempting to attach a portal to a host that is already attached. */
var MdPortalAlreadyAttachedError = (function (_super) {
__extends$2(MdPortalAlreadyAttachedError, _super);
function MdPortalAlreadyAttachedError() {
_super.call(this, 'Host already has a portal attached');
}
return MdPortalAlreadyAttachedError;
}(MdError));
/** Exception thrown when attempting to attach a portal to an already-disposed host. */
var MdPortalHostAlreadyDisposedError = (function (_super) {
__extends$2(MdPortalHostAlreadyDisposedError, _super);
function MdPortalHostAlreadyDisposedError() {
_super.call(this, 'This PortalHost has already been disposed');
}
return MdPortalHostAlreadyDisposedError;
}(MdError));
/** Exception thrown when attempting to attach an unknown portal type. */
var MdUnknownPortalTypeError = (function (_super) {
__extends$2(MdUnknownPortalTypeError, _super);
function MdUnknownPortalTypeError() {
_super.call(this, 'Attempting to attach an unknown Portal type. ' +
'BasePortalHost accepts either a ComponentPortal or a TemplatePortal.');
}
return MdUnknownPortalTypeError;
}(MdError));
/** Exception thrown when attempting to attach a portal to a null host. */
var MdNullPortalHostError = (function (_super) {
__extends$2(MdNullPortalHostError, _super);
function MdNullPortalHostError() {
_super.call(this, 'Attempting to attach a portal to a null PortalHost');
}
return MdNullPortalHostError;
}(MdError));
/** Exception thrown when attempting to detach a portal that is not attached. */
var MdNoPortalAttachedError = (function (_super) {
__extends$2(MdNoPortalAttachedError, _super);
function MdNoPortalAttachedError() {
_super.call(this, 'Attempting to detach a portal that is not attached to a host');
}
return MdNoPortalAttachedError;
}(MdError));
var __extends$1 = (window && window.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
/**
* A `Portal` is something that you want to render somewhere else.
* It can be attach to / detached from a `PortalHost`.
*/
var Portal = (function () {
function Portal() {
}
/** Attach this portal to a host. */
Portal.prototype.attach = function (host) {
if (host == null) {
throw new MdNullPortalHostError();
}
if (host.hasAttached()) {
throw new MdPortalAlreadyAttachedError();
}
this._attachedHost = host;
return host.attach(this);
};
/** Detach this portal from its host */
Portal.prototype.detach = function () {
var host = this._attachedHost;
if (host == null) {
throw new MdNoPortalAttachedError();
}
this._attachedHost = null;
return host.detach();
};
Object.defineProperty(Portal.prototype, "isAttached", {
/** Whether this portal is attached to a host. */
get: function () {
return this._attachedHost != null;
},
enumerable: true,
configurable: true
});
/**
* Sets the PortalHost reference without performing `attach()`. This is used directly by
* the PortalHost when it is performing an `attach()` or `detatch()`.
*/
Portal.prototype.setAttachedHost = function (host) {
this._attachedHost = host;
};
return Portal;
}());
/**
* A `ComponentPortal` is a portal that instantiates some Component upon attachment.
*/
var ComponentPortal = (function (_super) {
__extends$1(ComponentPortal, _super);
function ComponentPortal(component, viewContainerRef, injector) {
if (viewContainerRef === void 0) { viewContainerRef = null; }
if (injector === void 0) { injector = null; }
_super.call(this);
this.component = component;
this.viewContainerRef = viewContainerRef;
this.injector = injector;
}
return ComponentPortal;
}(Portal));
/**
* A `TemplatePortal` is a portal that represents some embedded template (TemplateRef).
*/
var TemplatePortal = (function (_super) {
__extends$1(TemplatePortal, _super);
function TemplatePortal(template, viewContainerRef) {
_super.call(this);
/**
* Additional locals for the instantiated embedded view.
* These locals can be seen as "exports" for the template, such as how ngFor has
* index / event / odd.
* See https://angular.io/docs/ts/latest/api/core/EmbeddedViewRef-class.html
*/
this.locals = new Map();
this.templateRef = template;
this.viewContainerRef = viewContainerRef;
}
Object.defineProperty(TemplatePortal.prototype, "origin", {
get: function () {
return this.templateRef.elementRef;
},
enumerable: true,
configurable: true
});
TemplatePortal.prototype.attach = function (host, locals) {
this.locals = locals == null ? new Map() : locals;
return _super.prototype.attach.call(this, host);
};
TemplatePortal.prototype.detach = function () {
this.locals = new Map();
return _super.prototype.detach.call(this);
};
return TemplatePortal;
}(Portal));
/**
* Partial implementation of PortalHost that only deals with attaching either a
* ComponentPortal or a TemplatePortal.
*/
var BasePortalHost = (function () {
function BasePortalHost() {
/** Whether this host has already been permanently disposed. */
this._isDisposed = false;
}
/** Whether this host has an attached portal. */
BasePortalHost.prototype.hasAttached = function () {
return this._attachedPortal != null;
};
BasePortalHost.prototype.attach = function (portal) {
if (portal == null) {
throw new MdNullPortalError();
}
if (this.hasAttached()) {
throw new MdPortalAlreadyAttachedError();
}
if (this._isDisposed) {
throw new MdPortalHostAlreadyDisposedError();
}
if (portal instanceof ComponentPortal) {
this._attachedPortal = portal;
return this.attachComponentPortal(portal);
}
else if (portal instanceof TemplatePortal) {
this._attachedPortal = portal;
return this.attachTemplatePortal(portal);
}
throw new MdUnknownPortalTypeError();
};
BasePortalHost.prototype.detach = function () {
if (this._attachedPortal) {
this._attachedPortal.setAttachedHost(null);
}
this._attachedPortal = null;
if (this._disposeFn != null) {
this._disposeFn();
this._disposeFn = null;
}
};
BasePortalHost.prototype.dispose = function () {
if (this.hasAttached()) {
this.detach();
}
this._isDisposed = true;
};
BasePortalHost.prototype.setDisposeFn = function (fn) {
this._disposeFn = fn;
};
return BasePortalHost;
}());
var __extends = (window && window.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var __decorate$4 = (window && window.__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$4 = (window && window.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
/**
* Directive version of a `TemplatePortal`. Because the directive *is* a TemplatePortal,
* the directive instance itself can be attached to a host, enabling declarative use of portals.
*
* Usage:
* <template portal #greeting>
* <p> Hello {{name}} </p>
* </template>
*/
var TemplatePortalDirective = (function (_super) {
__extends(TemplatePortalDirective, _super);
function TemplatePortalDirective(templateRef, viewContainerRef) {
_super.call(this, templateRef, viewContainerRef);
}
TemplatePortalDirective = __decorate$4([
_angular_core.Directive({
selector: '[portal]',
exportAs: 'portal',
}),
__metadata$4('design:paramtypes', [_angular_core.TemplateRef, _angular_core.ViewContainerRef])
], TemplatePortalDirective);
return TemplatePortalDirective;
}(TemplatePortal));
/**
* Directive version of a PortalHost. Because the directive *is* a PortalHost, portals can be
* directly attached to it, enabling declarative use.
*
* Usage:
* <template [portalHost]="greeting"></template>
*/
var PortalHostDirective = (function (_super) {
__extends(PortalHostDirective, _super);
function PortalHostDirective(_componentFactoryResolver, _viewContainerRef) {
_super.call(this);
this._componentFactoryResolver = _componentFactoryResolver;
this._viewContainerRef = _viewContainerRef;
}
Object.defineProperty(PortalHostDirective.prototype, "portal", {
get: function () {
return this._portal;
},
set: function (p) {
this._replaceAttachedPortal(p);
},
enumerable: true,
configurable: true
});
/** Attach the given ComponentPortal to this PortlHost using the ComponentFactoryResolver. */
PortalHostDirective.prototype.attachComponentPortal = function (portal) {
portal.setAttachedHost(this);
// If the portal specifies an origin, use that as the logical location of the component
// in the application tree. Otherwise use the location of this PortalHost.
var viewContainerRef = portal.viewContainerRef != null ?
portal.viewContainerRef :
this._viewContainerRef;
var componentFactory = this._componentFactoryResolver.resolveComponentFactory(portal.component);
var ref = viewContainerRef.createComponent(componentFactory, viewContainerRef.length, portal.injector || viewContainerRef.parentInjector);
this.setDisposeFn(function () { return ref.destroy(); });
return ref;
};
/** Attach the given TemplatePortal to this PortlHost as an embedded View. */
PortalHostDirective.prototype.attachTemplatePortal = function (portal) {
var _this = this;
portal.setAttachedHost(this);
this._viewContainerRef.createEmbeddedView(portal.templateRef);
this.setDisposeFn(function () { return _this._viewContainerRef.clear(); });
// TODO(jelbourn): return locals from view
return new Map();
};
/** Detatches the currently attached Portal (if there is one) and attaches the given Portal. */
PortalHostDirective.prototype._replaceAttachedPortal = function (p) {
if (this.hasAttached()) {
this.detach();
}
if (p) {
this.attach(p);
this._portal = p;
}
};
PortalHostDirective = __decorate$4([
_angular_core.Directive({
selector: '[portalHost]',
inputs: ['portal: portalHost']
}),
__metadata$4('design:paramtypes', [_angular_core.ComponentFactoryResolver, _angular_core.ViewContainerRef])
], PortalHostDirective);
return PortalHostDirective;
}(BasePortalHost));
var PortalModule = (function () {
function PortalModule() {
}
PortalModule.forRoot = function () {
return {
ngModule: PortalModule,
providers: []
};
};
PortalModule = __decorate$4([
_angular_core.NgModule({
exports: [TemplatePortalDirective, PortalHostDirective],
declarations: [TemplatePortalDirective, PortalHostDirective],
}),
__metadata$4('design:paramtypes', [])
], PortalModule);
return PortalModule;
}());
/**
* OverlayState is a bag of values for either the initial configuration or current state of an
* overlay.
*/
var OverlayState = (function () {
function OverlayState() {
}
return OverlayState;
}());
var __extends$4 = (window && window.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
/**
* A PortalHost for attaching portals to an arbitrary DOM element outside of the Angular
* application context.
*
* This is the only part of the portal core that directly touches the DOM.
*/
var DomPortalHost = (function (_super) {
__extends$4(DomPortalHost, _super);
function DomPortalHost(_hostDomElement, _componentFactoryResolver) {
_super.call(this);
this._hostDomElement = _hostDomElement;
this._componentFactoryResolver = _componentFactoryResolver;
}
/** Attach the given ComponentPortal to DOM element using the ComponentFactoryResolver. */
DomPortalHost.prototype.attachComponentPortal = function (portal) {
if (portal.viewContainerRef == null) {
throw new MdComponentPortalAttachedToDomWithoutOriginError();
}
var componentFactory = this._componentFactoryResolver.resolveComponentFactory(portal.component);
var ref = portal.viewContainerRef.createComponent(componentFactory, portal.viewContainerRef.length, portal.injector || portal.viewContainerRef.parentInjector);
var hostView = ref.hostView;
this._hostDomElement.appendChild(hostView.rootNodes[0]);
this.setDisposeFn(function () { return ref.destroy(); });
return ref;
};
DomPortalHost.prototype.attachTemplatePortal = function (portal) {
var _this = this;
var viewContainer = portal.viewContainerRef;
var viewRef = viewContainer.createEmbeddedView(portal.templateRef);
viewRef.rootNodes.forEach(function (rootNode) { return _this._hostDomElement.appendChild(rootNode); });
this.setDisposeFn((function () {
var index = viewContainer.indexOf(viewRef);
if (index != -1) {
viewContainer.remove(index);
}
}));
// TODO(jelbourn): Return locals from view.
return new Map();
};
DomPortalHost.prototype.dispose = function () {
_super.prototype.dispose.call(this);
if (this._hostDomElement.parentNode != null) {
this._hostDomElement.parentNode.removeChild(this._hostDomElement);
}
};
return DomPortalHost;
}(BasePortalHost));
/**
* Reference to an overlay that has been created with the Overlay service.
* Used to manipulate or dispose of said overlay.
*/
var OverlayRef = (function () {
function OverlayRef(_portalHost, _pane, _state) {
this._portalHost = _portalHost;
this._pane = _pane;
this._state = _state;
}
OverlayRef.prototype.attach = function (portal) {
var attachResult = this._portalHost.attach(portal);
this.updatePosition();
return attachResult;
};
OverlayRef.prototype.detach = function () {
return this._portalHost.detach();
};
OverlayRef.prototype.dispose = function () {
this._portalHost.dispose();
};
OverlayRef.prototype.hasAttached = function () {
return this._portalHost.hasAttached();
};
/** Gets the current state config of the overlay. */
OverlayRef.prototype.getState = function () {
return this._state;
};
/** Updates the position of the overlay based on the position strategy. */
OverlayRef.prototype.updatePosition = function () {
if (this._state.positionStrategy) {
this._state.positionStrategy.apply(this._pane);
}
};
return OverlayRef;
}());
var __decorate$8 = (window && window.__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$8 = (window && window.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
/**
* Simple utility for getting the bounds of the browser viewport.
* TODO: internal
*/
var ViewportRuler = (function () {
function ViewportRuler() {
}
// TODO(jelbourn): cache the document's bounding rect and only update it when the window
// is resized (debounced).
/** Gets a ClientRect for the viewport's bounds. */
ViewportRuler.prototype.getViewportRect = function () {
// Use the document element's bounding rect rather than the window scroll properties
// (e.g. pageYOffset, scrollY) due to in issue in Chrome and IE where window scroll
// properties and client coordinates (boundingClientRect, clientX/Y, etc.) are in different
// conceptual viewports. Under most circumstances these viewports are equivalent, but they
// can disagree when the page is pinch-zoomed (on devices that support touch).
// See https://bugs.chromium.org/p/chromium/issues/detail?id=489206#c4
// We use the documentElement instead of the body because, by default (without a css reset)
// browsers typically give the document body an 8px margin, which is not included in
// getBoundingClientRect().
var documentRect = document.documentElement.getBoundingClientRect();
var scrollPosition = this.getViewportScrollPosition(documentRect);
var height = window.innerHeight;
var width = window.innerWidth;
return {
top: scrollPosition.top,
left: scrollPosition.left,
bottom: scrollPosition.top + height,
right: scrollPosition.left + width,
height: height,
width: width,
};
};
/**
* Gets the (top, left) scroll position of the viewport.
* @param documentRect
*/
ViewportRuler.prototype.getViewportScrollPosition = function (documentRect) {
if (documentRect === void 0) { documentRect = document.documentElement.getBoundingClientRect(); }
// The top-left-corner of the viewport is determined by the scroll position of the document
// body, normally just (scrollLeft, scrollTop). However, Chrome and Firefox disagree about
// whether `document.body` or `document.documentElement` is the scrolled element, so reading
// `scrollTop` and `scrollLeft` is inconsistent. However, using the bounding rect of
// `document.documentElement` works consistently, where the `top` and `left` values will
// equal negative the scroll position.
var top = documentRect.top < 0 && document.body.scrollTop == 0 ?
-documentRect.top :
document.body.scrollTop;
var left = documentRect.left < 0 && document.body.scrollLeft == 0 ?
-documentRect.left :
document.body.scrollLeft;
return { top: top, left: left };
};
ViewportRuler = __decorate$8([
_angular_core.Injectable(),
__metadata$8('design:paramtypes', [])
], ViewportRuler);
return ViewportRuler;
}());
/** The points of the origin element and the overlay element to connect. */
var ConnectionPositionPair = (function () {
function ConnectionPositionPair(origin, overlay) {
this.originX = origin.originX;
this.originY = origin.originY;
this.overlayX = overlay.overlayX;
this.overlayY = overlay.overlayY;
}
return ConnectionPositionPair;
}());
/**
* A strategy for positioning overlays. Using this strategy, an overlay is given an
* implict position relative some origin element. The relative position is defined in terms of
* a point on the origin element that is connected to a point on the overlay element. For example,
* a basic dropdown is connecting the bottom-left corner of the origin to the top-left corner
* of the overlay.
*/
var ConnectedPositionStrategy = (function () {
function ConnectedPositionStrategy(_connectedTo, _originPos, _overlayPos, _viewportRuler) {
this._connectedTo = _connectedTo;
this._originPos = _originPos;
this._overlayPos = _overlayPos;
this._viewportRuler = _viewportRuler;
// TODO(jelbourn): set RTL to the actual value from the app.
/** Whether the we're dealing with an RTL context */
this._isRtl = false;
/** Ordered list of preferred positions, from most to least desirable. */
this._preferredPositions = [];
this._origin = this._connectedTo.nativeElement;
this.withFallbackPosition(_originPos, _overlayPos);
}
Object.defineProperty(ConnectedPositionStrategy.prototype, "positions", {
get: function () {
return this._preferredPositions;
},
enumerable: true,
configurable: true
});
/**
* Updates the position of the overlay element, using whichever preferred position relative
* to the origin fits on-screen.
* TODO: internal
*/
ConnectedPositionStrategy.prototype.apply = function (element) {
// We need the bounding rects for the origin and the overlay to determine how to position
// the overlay relative to the origin.
var originRect = this._origin.getBoundingClientRect();
var overlayRect = element.getBoundingClientRect();
// We use the viewport rect to determine whether a position would go off-screen.
var viewportRect = this._viewportRuler.getViewportRect();
var firstOverlayPoint = null;
// We want to place the overlay in the first of the preferred positions such that the
// overlay fits on-screen.
for (var _i = 0, _a = this._preferredPositions; _i < _a.length; _i++) {
var pos = _a[_i];
// Get the (x, y) point of connection on the origin, and then use that to get the
// (top, left) coordinate for the overlay at `pos`.
var originPoint = this._getOriginConnectionPoint(originRect, pos);
var overlayPoint = this._getOverlayPoint(originPoint, overlayRect, pos);
firstOverlayPoint = firstOverlayPoint || overlayPoint;
// If the overlay in the calculated position fits on-screen, put it there and we're done.
if (this._willOverlayFitWithinViewport(overlayPoint, overlayRect, viewportRect)) {
this._setElementPosition(element, overlayPoint);
return Promise.resolve(null);
}
}
// TODO(jelbourn): fallback behavior for when none of the preferred positions fit on-screen.
// For now, just stick it in the first position and let it go off-screen.
this._setElementPosition(element, firstOverlayPoint);
return Promise.resolve(null);
};
ConnectedPositionStrategy.prototype.withFallbackPosition = function (originPos, overlayPos) {
this._preferredPositions.push(new ConnectionPositionPair(originPos, overlayPos));
return this;
};
/**
* Gets the horizontal (x) "start" dimension based on whether the overlay is in an RTL context.
* @param rect
*/
ConnectedPositionStrategy.prototype._getStartX = function (rect) {
return this._isRtl ? rect.right : rect.left;
};
/**
* Gets the horizontal (x) "end" dimension based on whether the overlay is in an RTL context.
* @param rect
*/
ConnectedPositionStrategy.prototype._getEndX = function (rect) {
return this._isRtl ? rect.left : rect.right;
};
/**
* Gets the (x, y) coordinate of a connection point on the origin based on a relative position.
* @param originRect
* @param pos
*/
ConnectedPositionStrategy.prototype._getOriginConnectionPoint = function (originRect, pos) {
var originStartX = this._getStartX(originRect);
var originEndX = this._getEndX(originRect);
var x;
if (pos.originX == 'center') {
x = originStartX + (originRect.width / 2);
}
else {
x = pos.originX == 'start' ? originStartX : originEndX;
}
var y;
if (pos.originY == 'center') {
y = originRect.top + (originRect.height / 2);
}
else {
y = pos.originY == 'top' ? originRect.top : originRect.bottom;
}
return { x: x, y: y };
};
/**
* Gets the (x, y) coordinate of the top-left corner of the overlay given a given position and
* origin point to which the overlay should be connected.
* @param originPoint
* @param overlayRect
* @param pos
*/
ConnectedPositionStrategy.prototype._getOverlayPoint = function (originPoint, overlayRect, pos) {
// Calculate the (overlayStartX, overlayStartY), the start of the potential overlay position
// relative to the origin point.
var overlayStartX;
if (pos.overlayX == 'center') {
overlayStartX = -overlayRect.width / 2;
}
else {
overlayStartX = pos.overlayX == 'start' ? 0 : -overlayRect.width;
}
var overlayStartY;
if (pos.overlayY == 'center') {
overlayStartY = -overlayRect.height / 2;
}
else {
overlayStartY = pos.overlayY == 'top' ? 0 : -overlayRect.height;
}
return {
x: originPoint.x + overlayStartX,
y: originPoint.y + overlayStartY
};
};
/**
* Gets whether the overlay positioned at the given point will fit on-screen.
* @param overlayPoint The top-left coordinate of the overlay.
* @param overlayRect Bounding rect of the overlay, used to get its size.
* @param viewportRect The bounding viewport.
*/
ConnectedPositionStrategy.prototype._willOverlayFitWithinViewport = function (overlayPoint, overlayRect, viewportRect) {
// TODO(jelbourn): probably also want