UNPKG

10.3 kBJavaScriptView Raw
1var __extends = (this && this.__extends) || function (d, b) {
2 for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
3 function __() { this.constructor = d; }
4 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
5};
6import { Component, Input, ViewChild, ContentChildren, Renderer, ViewEncapsulation, Injectable } from '@angular/core';
7import { MdlMenuItemComponent } from './mdl-menu-item.component';
8import { MdlError } from '../common/mdl-error';
9var BOTTOM_LEFT = 'bottom-left';
10var BOTTOM_RIGHT = 'bottom-right';
11var TOP_LEFT = 'top-left';
12var TOP_RIGHT = 'top-right';
13var UNALIGNED = 'unaligned';
14// Total duration of the menu animation.
15var TRANSITION_DURATION_SECONDS = 0.3;
16// The fraction of the total duration we want to use for menu item animations.
17var TRANSITION_DURATION_FRACTION = 0.8;
18// How long the menu stays open after choosing an option (so the user can see
19// the ripple).
20var CLOSE_TIMEOUT = 175;
21var CSS_ALIGN_MAP = {};
22CSS_ALIGN_MAP[BOTTOM_LEFT] = 'mdl-menu--bottom-left';
23CSS_ALIGN_MAP[BOTTOM_RIGHT] = 'mdl-menu--bottom-right';
24CSS_ALIGN_MAP[TOP_LEFT] = 'mdl-menu--top-left';
25CSS_ALIGN_MAP[TOP_RIGHT] = 'mdl-menu--top-right';
26CSS_ALIGN_MAP[UNALIGNED] = 'mdl-menu--unaligned';
27export var MdlMenuError = (function (_super) {
28 __extends(MdlMenuError, _super);
29 function MdlMenuError() {
30 _super.apply(this, arguments);
31 }
32 return MdlMenuError;
33}(MdlError));
34export var MdlMenuRegisty = (function () {
35 function MdlMenuRegisty() {
36 this.menuComponents = [];
37 }
38 MdlMenuRegisty.prototype.add = function (menuComponent) {
39 this.menuComponents.push(menuComponent);
40 };
41 MdlMenuRegisty.prototype.remove = function (menuComponent) {
42 this.menuComponents.slice(this.menuComponents.indexOf(menuComponent), 1);
43 };
44 MdlMenuRegisty.prototype.hideAllExcept = function (menuComponent) {
45 this.menuComponents.forEach(function (component) {
46 if (component !== menuComponent) {
47 component.hide();
48 }
49 });
50 };
51 MdlMenuRegisty.decorators = [
52 { type: Injectable },
53 ];
54 /** @nocollapse */
55 MdlMenuRegisty.ctorParameters = function () { return []; };
56 return MdlMenuRegisty;
57}());
58export var MdlMenuComponent = (function () {
59 function MdlMenuComponent(renderer, menuRegistry) {
60 this.renderer = renderer;
61 this.menuRegistry = menuRegistry;
62 this.cssPosition = 'mdl-menu--bottom-left';
63 this.isVisible = false;
64 this.menuRegistry.add(this);
65 }
66 MdlMenuComponent.prototype.ngOnInit = function () {
67 this.cssPosition = CSS_ALIGN_MAP[this.position] || BOTTOM_LEFT;
68 };
69 MdlMenuComponent.prototype.ngAfterViewInit = function () {
70 var _this = this;
71 this.container = this.containerChild.nativeElement;
72 this.menuElement = this.menuElementChild.nativeElement;
73 this.outline = this.outlineChild.nativeElement;
74 // Add a click listener to the document, to close the menu.
75 var callback = function () {
76 if (_this.isVisible) {
77 _this.hide();
78 }
79 };
80 this.renderer.listenGlobal('window', 'click', callback);
81 this.renderer.listenGlobal('window', 'touchstart', callback);
82 };
83 MdlMenuComponent.prototype.toggle = function (event, mdlButton) {
84 if (!mdlButton) {
85 throw new MdlMenuError("MdlButtonComponent is required");
86 }
87 if (this.isVisible) {
88 this.hide();
89 }
90 else {
91 this.show(event, mdlButton);
92 }
93 };
94 MdlMenuComponent.prototype.hideOnItemClicked = function () {
95 var _this = this;
96 // Wait some time before closing menu, so the user can see the ripple.
97 setTimeout(function () {
98 _this.hide();
99 }, CLOSE_TIMEOUT);
100 };
101 MdlMenuComponent.prototype.hide = function () {
102 // Remove all transition delays; menu items fade out concurrently.
103 this.menuItemComponents.toArray().forEach(function (mi) {
104 mi.element.style.removeProperty('transition-delay');
105 });
106 // Measure the inner element.
107 var rect = this.menuElement.getBoundingClientRect();
108 var height = rect.height;
109 var width = rect.width;
110 // Turn on animation, and apply the final clip. Also make invisible.
111 // This triggers the transitions.
112 this.renderer.setElementClass(this.menuElement, 'is-animating', true);
113 this.applyClip(height, width);
114 this.renderer.setElementClass(this.container, 'is-visible', false);
115 // Clean up after the animation is complete.
116 this.addAnimationEndListener();
117 this.isVisible = false;
118 };
119 MdlMenuComponent.prototype.show = function (event, mdlButton) {
120 var _this = this;
121 this.menuRegistry.hideAllExcept(this);
122 event.stopPropagation();
123 var forElement = mdlButton.element;
124 var rect = forElement.getBoundingClientRect();
125 var forRect = forElement.parentElement.getBoundingClientRect();
126 if (this.position == UNALIGNED) {
127 }
128 else if (this.position == BOTTOM_RIGHT) {
129 // Position below the "for" element, aligned to its right.
130 this.container.style.right = (forRect.right - rect.right) + 'px';
131 this.container.style.top = forElement.offsetTop + forElement.offsetHeight + 'px';
132 }
133 else if (this.position == TOP_LEFT) {
134 // Position above the "for" element, aligned to its left.
135 this.container.style.left = forElement.offsetLeft + 'px';
136 this.container.style.bottom = (forRect.bottom - rect.top) + 'px';
137 }
138 else if (this.position == TOP_RIGHT) {
139 // Position above the "for" element, aligned to its right.
140 this.container.style.right = (forRect.right - rect.right) + 'px';
141 this.container.style.bottom = (forRect.bottom - rect.top) + 'px';
142 }
143 else {
144 // Default: position below the "for" element, aligned to its left.
145 this.container.style.left = forElement.offsetLeft + 'px';
146 this.container.style.top = forElement.offsetTop + forElement.offsetHeight + 'px';
147 }
148 // Measure the inner element.
149 var height = this.menuElement.getBoundingClientRect().height;
150 var width = this.menuElement.getBoundingClientRect().width;
151 this.container.style.width = width + 'px';
152 this.container.style.height = height + 'px';
153 this.outline.style.width = width + 'px';
154 this.outline.style.height = height + 'px';
155 var transitionDuration = TRANSITION_DURATION_SECONDS * TRANSITION_DURATION_FRACTION;
156 this.menuItemComponents.toArray().forEach(function (mi) {
157 var itemDelay = null;
158 if ((_this.position == TOP_LEFT) || _this.position == TOP_RIGHT) {
159 itemDelay = ((height - mi.element.offsetTop - mi.element.offsetHeight) / height * transitionDuration) + 's';
160 }
161 else {
162 itemDelay = (mi.element.offsetTop / height * transitionDuration) + 's';
163 }
164 mi.element.style.transitionDelay = itemDelay;
165 });
166 // Apply the initial clip to the text before we start animating.
167 this.applyClip(height, width);
168 this.renderer.setElementClass(this.container, 'is-visible', true);
169 this.menuElement.style.clip = 'rect(0 ' + width + 'px ' + height + 'px 0)';
170 this.renderer.setElementClass(this.menuElement, 'is-animating', true);
171 this.addAnimationEndListener();
172 this.isVisible = true;
173 };
174 MdlMenuComponent.prototype.addAnimationEndListener = function () {
175 var _this = this;
176 this.renderer.listen(this.menuElement, 'transitionend', function () {
177 _this.renderer.setElementClass(_this.menuElement, 'is-animating', false);
178 });
179 };
180 MdlMenuComponent.prototype.applyClip = function (height, width) {
181 if (this.position == UNALIGNED) {
182 // Do not clip.
183 this.menuElement.style.clip = '';
184 }
185 else if (this.position == BOTTOM_RIGHT) {
186 // Clip to the top right corner of the menu.
187 this.menuElement.style.clip = 'rect(0 ' + width + 'px ' + '0 ' + width + 'px)';
188 }
189 else if (this.position == TOP_LEFT) {
190 // Clip to the bottom left corner of the menu.
191 this.menuElement.style.clip = 'rect(' + height + 'px 0 ' + height + 'px 0)';
192 }
193 else if (this.position == TOP_RIGHT) {
194 // Clip to the bottom right corner of the menu.
195 this.menuElement.style.clip = 'rect(' + height + 'px ' + width + 'px ' + height + 'px ' + width + 'px)';
196 }
197 else {
198 // Default: do not clip (same as clipping to the top left corner).
199 this.menuElement.style.clip = '';
200 }
201 };
202 MdlMenuComponent.prototype.ngOnDestroy = function () {
203 this.menuRegistry.remove(this);
204 };
205 MdlMenuComponent.decorators = [
206 { type: Component, args: [{
207 selector: 'mdl-menu',
208 host: {},
209 exportAs: 'mdlMenu',
210 template: "\n <div #container class=\"mdl-menu__container is-upgraded\">\n <div #outline class=\"mdl-menu__outline\"\n [ngClass]=\"cssPosition\"\n ></div>\n <div class=\"mdl-menu\" #menuElement>\n <ng-content></ng-content>\n </div>\n </div>\n ",
211 encapsulation: ViewEncapsulation.None
212 },] },
213 ];
214 /** @nocollapse */
215 MdlMenuComponent.ctorParameters = function () { return [
216 { type: Renderer, },
217 { type: MdlMenuRegisty, },
218 ]; };
219 MdlMenuComponent.propDecorators = {
220 'position': [{ type: Input, args: ['mdl-menu-position',] },],
221 'containerChild': [{ type: ViewChild, args: ['container',] },],
222 'menuElementChild': [{ type: ViewChild, args: ['menuElement',] },],
223 'outlineChild': [{ type: ViewChild, args: ['outline',] },],
224 'menuItemComponents': [{ type: ContentChildren, args: [MdlMenuItemComponent,] },],
225 };
226 return MdlMenuComponent;
227}());
228//# sourceMappingURL=mdl-menu.component.js.map
\No newline at end of file