UNPKG

@angular2-material/core

Version:
1,150 lines (1,136 loc) 80.2 kB
(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