material_design_lite-sass 1.0.2.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/lib/material_design_lite/sass/version.rb +1 -1
- data/vendor/assets/javascripts/material.js +2562 -2993
- data/vendor/assets/javascripts/material/button.js +114 -112
- data/vendor/assets/javascripts/material/checkbox.js +255 -260
- data/vendor/assets/javascripts/material/data-table.js +140 -124
- data/vendor/assets/javascripts/material/icon-toggle.js +239 -243
- data/vendor/assets/javascripts/material/layout.js +392 -388
- data/vendor/assets/javascripts/material/mdlComponentHandler.js +68 -27
- data/vendor/assets/javascripts/material/menu.js +430 -414
- data/vendor/assets/javascripts/material/progress.js +110 -97
- data/vendor/assets/javascripts/material/radio.js +244 -247
- data/vendor/assets/javascripts/material/ripple.js +220 -211
- data/vendor/assets/javascripts/material/slider.js +228 -228
- data/vendor/assets/javascripts/material/spinner.js +122 -119
- data/vendor/assets/javascripts/material/switch.js +246 -250
- data/vendor/assets/javascripts/material/tabs.js +129 -127
- data/vendor/assets/javascripts/material/textfield.js +221 -222
- data/vendor/assets/javascripts/material/tooltip.js +126 -122
- data/vendor/assets/stylesheets/material/_badge.scss +1 -1
- data/vendor/assets/stylesheets/material/_button.scss +15 -8
- data/vendor/assets/stylesheets/material/_card.scss +1 -1
- data/vendor/assets/stylesheets/material/_checkbox.scss +1 -1
- data/vendor/assets/stylesheets/material/_data-table.scss +5 -3
- data/vendor/assets/stylesheets/material/_functions.scss +16 -0
- data/vendor/assets/stylesheets/material/_grid.scss +11 -20
- data/vendor/assets/stylesheets/material/_layout.scss +7 -5
- data/vendor/assets/stylesheets/material/_menu.scss +4 -1
- data/vendor/assets/stylesheets/material/_radio.scss +1 -1
- data/vendor/assets/stylesheets/material/_slider.scss +1 -0
- data/vendor/assets/stylesheets/material/_switch.scss +1 -1
- data/vendor/assets/stylesheets/material/_tabs.scss +1 -1
- data/vendor/assets/stylesheets/material/_textfield.scss +15 -5
- data/vendor/assets/stylesheets/material/_tooltip.scss +2 -2
- data/vendor/assets/stylesheets/material/_variables.scss +18 -43
- data/vendor/assets/stylesheets/material/resets/_h5bp.scss +28 -21
- metadata +1 -1
@@ -19,23 +19,29 @@
|
|
19
19
|
* A component handler interface using the revealing module design pattern.
|
20
20
|
* More details on this design pattern here:
|
21
21
|
* https://github.com/jasonmayes/mdl-component-design-pattern
|
22
|
+
*
|
22
23
|
* @author Jason Mayes.
|
23
24
|
*/
|
24
25
|
/* exported componentHandler */
|
25
|
-
|
26
|
+
window.componentHandler = (function() {
|
26
27
|
'use strict';
|
27
28
|
|
29
|
+
/** @type {!Array<componentHandler.ComponentConfig>} */
|
28
30
|
var registeredComponents_ = [];
|
31
|
+
|
32
|
+
/** @type {!Array<componentHandler.Component>} */
|
29
33
|
var createdComponents_ = [];
|
34
|
+
|
30
35
|
var downgradeMethod_ = 'mdlDowngrade_';
|
31
36
|
var componentConfigProperty_ = 'mdlComponentConfigInternal_';
|
32
37
|
|
33
38
|
/**
|
34
39
|
* Searches registered components for a class we are interested in using.
|
35
40
|
* Optionally replaces a match with passed object if specified.
|
36
|
-
*
|
37
|
-
* @param {
|
38
|
-
* @
|
41
|
+
*
|
42
|
+
* @param {String} name The name of a class we want to use.
|
43
|
+
* @param {componentHandler.ComponentConfig=} optReplace Optional object to replace match with.
|
44
|
+
* @return {!Object|Boolean}
|
39
45
|
* @private
|
40
46
|
*/
|
41
47
|
function findRegisteredClass_(name, optReplace) {
|
@@ -52,8 +58,9 @@ var componentHandler = (function() {
|
|
52
58
|
|
53
59
|
/**
|
54
60
|
* Returns an array of the classNames of the upgraded classes on the element.
|
55
|
-
*
|
56
|
-
* @
|
61
|
+
*
|
62
|
+
* @param {!HTMLElement} element The element to fetch data from.
|
63
|
+
* @return {!Array<String>}
|
57
64
|
* @private
|
58
65
|
*/
|
59
66
|
function getUpgradedListOfElement_(element) {
|
@@ -65,9 +72,10 @@ var componentHandler = (function() {
|
|
65
72
|
/**
|
66
73
|
* Returns true if the given element has already been upgraded for the given
|
67
74
|
* class.
|
68
|
-
*
|
69
|
-
* @param {
|
70
|
-
* @
|
75
|
+
*
|
76
|
+
* @param {!HTMLElement} element The element we want to check.
|
77
|
+
* @param {String} jsClass The class to check for.
|
78
|
+
* @returns {Boolean}
|
71
79
|
* @private
|
72
80
|
*/
|
73
81
|
function isElementUpgraded_(element, jsClass) {
|
@@ -78,9 +86,10 @@ var componentHandler = (function() {
|
|
78
86
|
/**
|
79
87
|
* Searches existing DOM for elements of our component type and upgrades them
|
80
88
|
* if they have not already been upgraded.
|
81
|
-
*
|
89
|
+
*
|
90
|
+
* @param {String=} optJsClass the programatic name of the element class we
|
82
91
|
* need to create a new instance of.
|
83
|
-
* @param {
|
92
|
+
* @param {String=} optCssClass the name of the CSS class elements of this
|
84
93
|
* type will have.
|
85
94
|
*/
|
86
95
|
function upgradeDomInternal(optJsClass, optCssClass) {
|
@@ -90,7 +99,7 @@ var componentHandler = (function() {
|
|
90
99
|
registeredComponents_[i].cssClass);
|
91
100
|
}
|
92
101
|
} else {
|
93
|
-
var jsClass = /** @type {
|
102
|
+
var jsClass = /** @type {String} */ (optJsClass);
|
94
103
|
if (optCssClass === undefined) {
|
95
104
|
var registeredClass = findRegisteredClass_(jsClass);
|
96
105
|
if (registeredClass) {
|
@@ -107,8 +116,9 @@ var componentHandler = (function() {
|
|
107
116
|
|
108
117
|
/**
|
109
118
|
* Upgrades a specific element rather than all in the DOM.
|
110
|
-
*
|
111
|
-
* @param {!
|
119
|
+
*
|
120
|
+
* @param {!HTMLElement} element The element we wish to upgrade.
|
121
|
+
* @param {String=} optJsClass Optional name of the class we want to upgrade
|
112
122
|
* the element to.
|
113
123
|
*/
|
114
124
|
function upgradeElementInternal(element, optJsClass) {
|
@@ -122,7 +132,7 @@ var componentHandler = (function() {
|
|
122
132
|
// ones matching the element's CSS classList.
|
123
133
|
if (!optJsClass) {
|
124
134
|
var classList = element.classList;
|
125
|
-
registeredComponents_.forEach(function
|
135
|
+
registeredComponents_.forEach(function(component) {
|
126
136
|
// Match CSS & Not to be upgraded & Not upgraded.
|
127
137
|
if (classList.contains(component.cssClass) &&
|
128
138
|
classesToUpgrade.indexOf(component) === -1 &&
|
@@ -166,13 +176,14 @@ var componentHandler = (function() {
|
|
166
176
|
|
167
177
|
/**
|
168
178
|
* Upgrades a specific list of elements rather than all in the DOM.
|
169
|
-
*
|
179
|
+
*
|
180
|
+
* @param {!HTMLElement|!Array<!HTMLElement>|!NodeList|!HTMLCollection} elements
|
170
181
|
* The elements we wish to upgrade.
|
171
182
|
*/
|
172
183
|
function upgradeElementsInternal(elements) {
|
173
184
|
if (!Array.isArray(elements)) {
|
174
185
|
if (typeof elements.item === 'function') {
|
175
|
-
elements = Array.prototype.slice.call(elements);
|
186
|
+
elements = Array.prototype.slice.call(/** @type {Array} */ (elements));
|
176
187
|
} else {
|
177
188
|
elements = [elements];
|
178
189
|
}
|
@@ -190,17 +201,17 @@ var componentHandler = (function() {
|
|
190
201
|
|
191
202
|
/**
|
192
203
|
* Registers a class for future use and attempts to upgrade existing DOM.
|
193
|
-
*
|
194
|
-
* {constructor:
|
204
|
+
*
|
205
|
+
* @param {{constructor: !Function, classAsString: String, cssClass: String, widget: String}} config
|
195
206
|
*/
|
196
207
|
function registerInternal(config) {
|
197
|
-
var newConfig = {
|
208
|
+
var newConfig = /** @type {componentHandler.ComponentConfig} */ ({
|
198
209
|
'classConstructor': config.constructor,
|
199
210
|
'className': config.classAsString,
|
200
211
|
'cssClass': config.cssClass,
|
201
212
|
'widget': config.widget === undefined ? true : config.widget,
|
202
213
|
'callbacks': []
|
203
|
-
};
|
214
|
+
});
|
204
215
|
|
205
216
|
registeredComponents_.forEach(function(item) {
|
206
217
|
if (item.cssClass === newConfig.cssClass) {
|
@@ -214,7 +225,7 @@ var componentHandler = (function() {
|
|
214
225
|
if (config.constructor.prototype
|
215
226
|
.hasOwnProperty(componentConfigProperty_)) {
|
216
227
|
throw new Error(
|
217
|
-
|
228
|
+
'MDL component classes must not have ' + componentConfigProperty_ +
|
218
229
|
' defined as a property.');
|
219
230
|
}
|
220
231
|
|
@@ -228,10 +239,12 @@ var componentHandler = (function() {
|
|
228
239
|
/**
|
229
240
|
* Allows user to be alerted to any upgrades that are performed for a given
|
230
241
|
* component type
|
231
|
-
*
|
242
|
+
*
|
243
|
+
* @param {String} jsClass The class name of the MDL component we wish
|
232
244
|
* to hook into for any upgrades performed.
|
233
|
-
* @param {!
|
234
|
-
* function should expect 1 parameter - the HTMLElement which
|
245
|
+
* @param {function(!HTMLElement)} callback The function to call upon an
|
246
|
+
* upgrade. This function should expect 1 parameter - the HTMLElement which
|
247
|
+
* got upgraded.
|
235
248
|
*/
|
236
249
|
function registerUpgradedCallbackInternal(jsClass, callback) {
|
237
250
|
var regClass = findRegisteredClass_(jsClass);
|
@@ -253,7 +266,7 @@ var componentHandler = (function() {
|
|
253
266
|
/**
|
254
267
|
* Finds a created component by a given DOM node.
|
255
268
|
*
|
256
|
-
* @param {!
|
269
|
+
* @param {!Node} node
|
257
270
|
* @return {*}
|
258
271
|
*/
|
259
272
|
function findCreatedComponentByNodeInternal(node) {
|
@@ -296,7 +309,7 @@ var componentHandler = (function() {
|
|
296
309
|
/**
|
297
310
|
* Downgrade either a given node, an array of nodes, or a NodeList.
|
298
311
|
*
|
299
|
-
* @param {
|
312
|
+
* @param {!Node|!Array<!Node>|!NodeList} nodes
|
300
313
|
*/
|
301
314
|
function downgradeNodesInternal(nodes) {
|
302
315
|
var downgradeNode = function(node) {
|
@@ -344,3 +357,31 @@ window.addEventListener('load', function() {
|
|
344
357
|
componentHandler.register = function() {};
|
345
358
|
}
|
346
359
|
});
|
360
|
+
|
361
|
+
/**
|
362
|
+
* Describes the type of a registered component type managed by
|
363
|
+
* componentHandler. Provided for benefit of the Closure compiler.
|
364
|
+
*
|
365
|
+
* @typedef {{
|
366
|
+
* constructor: !Function,
|
367
|
+
* className: String,
|
368
|
+
* cssClass: String,
|
369
|
+
* widget: String,
|
370
|
+
* callbacks: !Array<function(!HTMLElement)>
|
371
|
+
* }}
|
372
|
+
*/
|
373
|
+
componentHandler.ComponentConfig; // jshint ignore:line
|
374
|
+
|
375
|
+
/**
|
376
|
+
* Created component (i.e., upgraded element) type as managed by
|
377
|
+
* componentHandler. Provided for benefit of the Closure compiler.
|
378
|
+
*
|
379
|
+
* @typedef {{
|
380
|
+
* element_: !HTMLElement,
|
381
|
+
* className: String,
|
382
|
+
* classAsString: String,
|
383
|
+
* cssClass: String,
|
384
|
+
* widget: String
|
385
|
+
* }}
|
386
|
+
*/
|
387
|
+
componentHandler.Component; // jshint ignore:line
|
@@ -15,454 +15,470 @@
|
|
15
15
|
* limitations under the License.
|
16
16
|
*/
|
17
17
|
|
18
|
-
|
19
|
-
* Class constructor for dropdown MDL component.
|
20
|
-
* Implements MDL component design pattern defined at:
|
21
|
-
* https://github.com/jasonmayes/mdl-component-design-pattern
|
22
|
-
* @param {HTMLElement} element The element that will be upgraded.
|
23
|
-
*/
|
24
|
-
function MaterialMenu(element) {
|
18
|
+
(function() {
|
25
19
|
'use strict';
|
26
20
|
|
27
|
-
|
21
|
+
/**
|
22
|
+
* Class constructor for dropdown MDL component.
|
23
|
+
* Implements MDL component design pattern defined at:
|
24
|
+
* https://github.com/jasonmayes/mdl-component-design-pattern
|
25
|
+
*
|
26
|
+
* @param {HTMLElement} element The element that will be upgraded.
|
27
|
+
*/
|
28
|
+
var MaterialMenu = function MaterialMenu(element) {
|
29
|
+
this.element_ = element;
|
30
|
+
|
31
|
+
// Initialize instance.
|
32
|
+
this.init();
|
33
|
+
};
|
34
|
+
window.MaterialMenu = MaterialMenu;
|
35
|
+
|
36
|
+
/**
|
37
|
+
* Store constants in one place so they can be updated easily.
|
38
|
+
*
|
39
|
+
* @enum {String | Number}
|
40
|
+
* @private
|
41
|
+
*/
|
42
|
+
MaterialMenu.prototype.Constant_ = {
|
43
|
+
// Total duration of the menu animation.
|
44
|
+
TRANSITION_DURATION_SECONDS: 0.3,
|
45
|
+
// The fraction of the total duration we want to use for menu item animations.
|
46
|
+
TRANSITION_DURATION_FRACTION: 0.8,
|
47
|
+
// How long the menu stays open after choosing an option (so the user can see
|
48
|
+
// the ripple).
|
49
|
+
CLOSE_TIMEOUT: 150
|
50
|
+
};
|
51
|
+
|
52
|
+
/**
|
53
|
+
* Keycodes, for code readability.
|
54
|
+
*
|
55
|
+
* @enum {Number}
|
56
|
+
* @private
|
57
|
+
*/
|
58
|
+
MaterialMenu.prototype.Keycodes_ = {
|
59
|
+
ENTER: 13,
|
60
|
+
ESCAPE: 27,
|
61
|
+
SPACE: 32,
|
62
|
+
UP_ARROW: 38,
|
63
|
+
DOWN_ARROW: 40
|
64
|
+
};
|
65
|
+
|
66
|
+
/**
|
67
|
+
* Store strings for class names defined by this component that are used in
|
68
|
+
* JavaScript. This allows us to simply change it in one place should we
|
69
|
+
* decide to modify at a later date.
|
70
|
+
*
|
71
|
+
* @enum {String}
|
72
|
+
* @private
|
73
|
+
*/
|
74
|
+
MaterialMenu.prototype.CssClasses_ = {
|
75
|
+
CONTAINER: 'mdl-menu__container',
|
76
|
+
OUTLINE: 'mdl-menu__outline',
|
77
|
+
ITEM: 'mdl-menu__item',
|
78
|
+
ITEM_RIPPLE_CONTAINER: 'mdl-menu__item-ripple-container',
|
79
|
+
RIPPLE_EFFECT: 'mdl-js-ripple-effect',
|
80
|
+
RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
|
81
|
+
RIPPLE: 'mdl-ripple',
|
82
|
+
// Statuses
|
83
|
+
IS_UPGRADED: 'is-upgraded',
|
84
|
+
IS_VISIBLE: 'is-visible',
|
85
|
+
IS_ANIMATING: 'is-animating',
|
86
|
+
// Alignment options
|
87
|
+
BOTTOM_LEFT: 'mdl-menu--bottom-left', // This is the default.
|
88
|
+
BOTTOM_RIGHT: 'mdl-menu--bottom-right',
|
89
|
+
TOP_LEFT: 'mdl-menu--top-left',
|
90
|
+
TOP_RIGHT: 'mdl-menu--top-right',
|
91
|
+
UNALIGNED: 'mdl-menu--unaligned'
|
92
|
+
};
|
93
|
+
|
94
|
+
/**
|
95
|
+
* Initialize element.
|
96
|
+
*/
|
97
|
+
MaterialMenu.prototype.init = function() {
|
98
|
+
if (this.element_) {
|
99
|
+
// Create container for the menu.
|
100
|
+
var container = document.createElement('div');
|
101
|
+
container.classList.add(this.CssClasses_.CONTAINER);
|
102
|
+
this.element_.parentElement.insertBefore(container, this.element_);
|
103
|
+
this.element_.parentElement.removeChild(this.element_);
|
104
|
+
container.appendChild(this.element_);
|
105
|
+
this.container_ = container;
|
106
|
+
|
107
|
+
// Create outline for the menu (shadow and background).
|
108
|
+
var outline = document.createElement('div');
|
109
|
+
outline.classList.add(this.CssClasses_.OUTLINE);
|
110
|
+
this.outline_ = outline;
|
111
|
+
container.insertBefore(outline, this.element_);
|
112
|
+
|
113
|
+
// Find the "for" element and bind events to it.
|
114
|
+
var forElId = this.element_.getAttribute('for');
|
115
|
+
var forEl = null;
|
116
|
+
if (forElId) {
|
117
|
+
forEl = document.getElementById(forElId);
|
118
|
+
if (forEl) {
|
119
|
+
this.forElement_ = forEl;
|
120
|
+
forEl.addEventListener('click', this.handleForClick_.bind(this));
|
121
|
+
forEl.addEventListener('keydown',
|
122
|
+
this.handleForKeyboardEvent_.bind(this));
|
123
|
+
}
|
124
|
+
}
|
28
125
|
|
29
|
-
|
30
|
-
|
31
|
-
|
126
|
+
var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);
|
127
|
+
this.boundItemKeydown = this.handleItemKeyboardEvent_.bind(this);
|
128
|
+
this.boundItemClick = this.handleItemClick_.bind(this);
|
129
|
+
for (var i = 0; i < items.length; i++) {
|
130
|
+
// Add a listener to each menu item.
|
131
|
+
items[i].addEventListener('click', this.boundItemClick);
|
132
|
+
// Add a tab index to each menu item.
|
133
|
+
items[i].tabIndex = '-1';
|
134
|
+
// Add a keyboard listener to each menu item.
|
135
|
+
items[i].addEventListener('keydown', this.boundItemKeydown);
|
136
|
+
}
|
32
137
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
* @private
|
37
|
-
*/
|
38
|
-
MaterialMenu.prototype.Constant_ = {
|
39
|
-
// Total duration of the menu animation.
|
40
|
-
TRANSITION_DURATION_SECONDS: 0.3,
|
41
|
-
// The fraction of the total duration we want to use for menu item animations.
|
42
|
-
TRANSITION_DURATION_FRACTION: 0.8,
|
43
|
-
// How long the menu stays open after choosing an option (so the user can see
|
44
|
-
// the ripple).
|
45
|
-
CLOSE_TIMEOUT: 150
|
46
|
-
};
|
138
|
+
// Add ripple classes to each item, if the user has enabled ripples.
|
139
|
+
if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {
|
140
|
+
this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);
|
47
141
|
|
48
|
-
|
49
|
-
|
50
|
-
* @enum {number}
|
51
|
-
* @private
|
52
|
-
*/
|
53
|
-
MaterialMenu.prototype.Keycodes_ = {
|
54
|
-
ENTER: 13,
|
55
|
-
ESCAPE: 27,
|
56
|
-
SPACE: 32,
|
57
|
-
UP_ARROW: 38,
|
58
|
-
DOWN_ARROW: 40
|
59
|
-
};
|
142
|
+
for (i = 0; i < items.length; i++) {
|
143
|
+
var item = items[i];
|
60
144
|
|
61
|
-
|
62
|
-
|
63
|
-
* JavaScript. This allows us to simply change it in one place should we
|
64
|
-
* decide to modify at a later date.
|
65
|
-
* @enum {string}
|
66
|
-
* @private
|
67
|
-
*/
|
68
|
-
MaterialMenu.prototype.CssClasses_ = {
|
69
|
-
CONTAINER: 'mdl-menu__container',
|
70
|
-
OUTLINE: 'mdl-menu__outline',
|
71
|
-
ITEM: 'mdl-menu__item',
|
72
|
-
ITEM_RIPPLE_CONTAINER: 'mdl-menu__item-ripple-container',
|
73
|
-
RIPPLE_EFFECT: 'mdl-js-ripple-effect',
|
74
|
-
RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
|
75
|
-
RIPPLE: 'mdl-ripple',
|
76
|
-
// Statuses
|
77
|
-
IS_UPGRADED: 'is-upgraded',
|
78
|
-
IS_VISIBLE: 'is-visible',
|
79
|
-
IS_ANIMATING: 'is-animating',
|
80
|
-
// Alignment options
|
81
|
-
BOTTOM_LEFT: 'mdl-menu--bottom-left', // This is the default.
|
82
|
-
BOTTOM_RIGHT: 'mdl-menu--bottom-right',
|
83
|
-
TOP_LEFT: 'mdl-menu--top-left',
|
84
|
-
TOP_RIGHT: 'mdl-menu--top-right',
|
85
|
-
UNALIGNED: 'mdl-menu--unaligned'
|
86
|
-
};
|
145
|
+
var rippleContainer = document.createElement('span');
|
146
|
+
rippleContainer.classList.add(this.CssClasses_.ITEM_RIPPLE_CONTAINER);
|
87
147
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
MaterialMenu.prototype.init = function() {
|
92
|
-
'use strict';
|
148
|
+
var ripple = document.createElement('span');
|
149
|
+
ripple.classList.add(this.CssClasses_.RIPPLE);
|
150
|
+
rippleContainer.appendChild(ripple);
|
93
151
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
container.classList.add(this.CssClasses_.CONTAINER);
|
98
|
-
this.element_.parentElement.insertBefore(container, this.element_);
|
99
|
-
this.element_.parentElement.removeChild(this.element_);
|
100
|
-
container.appendChild(this.element_);
|
101
|
-
this.container_ = container;
|
102
|
-
|
103
|
-
// Create outline for the menu (shadow and background).
|
104
|
-
var outline = document.createElement('div');
|
105
|
-
outline.classList.add(this.CssClasses_.OUTLINE);
|
106
|
-
this.outline_ = outline;
|
107
|
-
container.insertBefore(outline, this.element_);
|
108
|
-
|
109
|
-
// Find the "for" element and bind events to it.
|
110
|
-
var forElId = this.element_.getAttribute('for');
|
111
|
-
var forEl = null;
|
112
|
-
if (forElId) {
|
113
|
-
forEl = document.getElementById(forElId);
|
114
|
-
if (forEl) {
|
115
|
-
this.forElement_ = forEl;
|
116
|
-
forEl.addEventListener('click', this.handleForClick_.bind(this));
|
117
|
-
forEl.addEventListener('keydown',
|
118
|
-
this.handleForKeyboardEvent_.bind(this));
|
152
|
+
item.appendChild(rippleContainer);
|
153
|
+
item.classList.add(this.CssClasses_.RIPPLE_EFFECT);
|
154
|
+
}
|
119
155
|
}
|
120
|
-
}
|
121
156
|
|
122
|
-
|
157
|
+
// Copy alignment classes to the container, so the outline can use them.
|
158
|
+
if (this.element_.classList.contains(this.CssClasses_.BOTTOM_LEFT)) {
|
159
|
+
this.outline_.classList.add(this.CssClasses_.BOTTOM_LEFT);
|
160
|
+
}
|
161
|
+
if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {
|
162
|
+
this.outline_.classList.add(this.CssClasses_.BOTTOM_RIGHT);
|
163
|
+
}
|
164
|
+
if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {
|
165
|
+
this.outline_.classList.add(this.CssClasses_.TOP_LEFT);
|
166
|
+
}
|
167
|
+
if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
|
168
|
+
this.outline_.classList.add(this.CssClasses_.TOP_RIGHT);
|
169
|
+
}
|
170
|
+
if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {
|
171
|
+
this.outline_.classList.add(this.CssClasses_.UNALIGNED);
|
172
|
+
}
|
123
173
|
|
124
|
-
|
125
|
-
// Add a listener to each menu item.
|
126
|
-
items[i].addEventListener('click', this.handleItemClick_.bind(this));
|
127
|
-
// Add a tab index to each menu item.
|
128
|
-
items[i].tabIndex = '-1';
|
129
|
-
// Add a keyboard listener to each menu item.
|
130
|
-
items[i].addEventListener('keydown',
|
131
|
-
this.handleItemKeyboardEvent_.bind(this));
|
174
|
+
container.classList.add(this.CssClasses_.IS_UPGRADED);
|
132
175
|
}
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
176
|
+
};
|
177
|
+
|
178
|
+
/**
|
179
|
+
* Handles a click on the "for" element, by positioning the menu and then
|
180
|
+
* toggling it.
|
181
|
+
*
|
182
|
+
* @param {Event} evt The event that fired.
|
183
|
+
* @private
|
184
|
+
*/
|
185
|
+
MaterialMenu.prototype.handleForClick_ = function(evt) {
|
186
|
+
if (this.element_ && this.forElement_) {
|
187
|
+
var rect = this.forElement_.getBoundingClientRect();
|
188
|
+
var forRect = this.forElement_.parentElement.getBoundingClientRect();
|
189
|
+
|
190
|
+
if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {
|
191
|
+
// Do not position the menu automatically. Requires the developer to
|
192
|
+
// manually specify position.
|
193
|
+
} else if (this.element_.classList.contains(
|
194
|
+
this.CssClasses_.BOTTOM_RIGHT)) {
|
195
|
+
// Position below the "for" element, aligned to its right.
|
196
|
+
this.container_.style.right = (forRect.right - rect.right) + 'px';
|
197
|
+
this.container_.style.top =
|
198
|
+
this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';
|
199
|
+
} else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {
|
200
|
+
// Position above the "for" element, aligned to its left.
|
201
|
+
this.container_.style.left = this.forElement_.offsetLeft + 'px';
|
202
|
+
this.container_.style.bottom = (forRect.bottom - rect.top) + 'px';
|
203
|
+
} else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
|
204
|
+
// Position above the "for" element, aligned to its right.
|
205
|
+
this.container_.style.right = (forRect.right - rect.right) + 'px';
|
206
|
+
this.container_.style.bottom = (forRect.bottom - rect.top) + 'px';
|
207
|
+
} else {
|
208
|
+
// Default: position below the "for" element, aligned to its left.
|
209
|
+
this.container_.style.left = this.forElement_.offsetLeft + 'px';
|
210
|
+
this.container_.style.top =
|
211
|
+
this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';
|
150
212
|
}
|
151
213
|
}
|
152
214
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
215
|
+
this.toggle(evt);
|
216
|
+
};
|
217
|
+
|
218
|
+
/**
|
219
|
+
* Handles a keyboard event on the "for" element.
|
220
|
+
*
|
221
|
+
* @param {Event} evt The event that fired.
|
222
|
+
* @private
|
223
|
+
*/
|
224
|
+
MaterialMenu.prototype.handleForKeyboardEvent_ = function(evt) {
|
225
|
+
if (this.element_ && this.container_ && this.forElement_) {
|
226
|
+
var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM +
|
227
|
+
':not([disabled])');
|
228
|
+
|
229
|
+
if (items && items.length > 0 &&
|
230
|
+
this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {
|
231
|
+
if (evt.keyCode === this.Keycodes_.UP_ARROW) {
|
232
|
+
evt.preventDefault();
|
233
|
+
items[items.length - 1].focus();
|
234
|
+
} else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) {
|
235
|
+
evt.preventDefault();
|
236
|
+
items[0].focus();
|
237
|
+
}
|
238
|
+
}
|
159
239
|
}
|
160
|
-
|
161
|
-
|
240
|
+
};
|
241
|
+
|
242
|
+
/**
|
243
|
+
* Handles a keyboard event on an item.
|
244
|
+
*
|
245
|
+
* @param {Event} evt The event that fired.
|
246
|
+
* @private
|
247
|
+
*/
|
248
|
+
MaterialMenu.prototype.handleItemKeyboardEvent_ = function(evt) {
|
249
|
+
if (this.element_ && this.container_) {
|
250
|
+
var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM +
|
251
|
+
':not([disabled])');
|
252
|
+
|
253
|
+
if (items && items.length > 0 &&
|
254
|
+
this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {
|
255
|
+
var currentIndex = Array.prototype.slice.call(items).indexOf(evt.target);
|
256
|
+
|
257
|
+
if (evt.keyCode === this.Keycodes_.UP_ARROW) {
|
258
|
+
evt.preventDefault();
|
259
|
+
if (currentIndex > 0) {
|
260
|
+
items[currentIndex - 1].focus();
|
261
|
+
} else {
|
262
|
+
items[items.length - 1].focus();
|
263
|
+
}
|
264
|
+
} else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) {
|
265
|
+
evt.preventDefault();
|
266
|
+
if (items.length > currentIndex + 1) {
|
267
|
+
items[currentIndex + 1].focus();
|
268
|
+
} else {
|
269
|
+
items[0].focus();
|
270
|
+
}
|
271
|
+
} else if (evt.keyCode === this.Keycodes_.SPACE ||
|
272
|
+
evt.keyCode === this.Keycodes_.ENTER) {
|
273
|
+
evt.preventDefault();
|
274
|
+
// Send mousedown and mouseup to trigger ripple.
|
275
|
+
var e = new MouseEvent('mousedown');
|
276
|
+
evt.target.dispatchEvent(e);
|
277
|
+
e = new MouseEvent('mouseup');
|
278
|
+
evt.target.dispatchEvent(e);
|
279
|
+
// Send click.
|
280
|
+
evt.target.click();
|
281
|
+
} else if (evt.keyCode === this.Keycodes_.ESCAPE) {
|
282
|
+
evt.preventDefault();
|
283
|
+
this.hide();
|
284
|
+
}
|
285
|
+
}
|
162
286
|
}
|
163
|
-
|
164
|
-
|
287
|
+
};
|
288
|
+
|
289
|
+
/**
|
290
|
+
* Handles a click event on an item.
|
291
|
+
*
|
292
|
+
* @param {Event} evt The event that fired.
|
293
|
+
* @private
|
294
|
+
*/
|
295
|
+
MaterialMenu.prototype.handleItemClick_ = function(evt) {
|
296
|
+
if (evt.target.getAttribute('disabled') !== null) {
|
297
|
+
evt.stopPropagation();
|
298
|
+
} else {
|
299
|
+
// Wait some time before closing menu, so the user can see the ripple.
|
300
|
+
this.closing_ = true;
|
301
|
+
window.setTimeout(function(evt) {
|
302
|
+
this.hide();
|
303
|
+
this.closing_ = false;
|
304
|
+
}.bind(this), this.Constant_.CLOSE_TIMEOUT);
|
165
305
|
}
|
306
|
+
};
|
307
|
+
|
308
|
+
/**
|
309
|
+
* Calculates the initial clip (for opening the menu) or final clip (for closing
|
310
|
+
* it), and applies it. This allows us to animate from or to the correct point,
|
311
|
+
* that is, the point it's aligned to in the "for" element.
|
312
|
+
*
|
313
|
+
* @param {Number} height Height of the clip rectangle
|
314
|
+
* @param {Number} width Width of the clip rectangle
|
315
|
+
* @private
|
316
|
+
*/
|
317
|
+
MaterialMenu.prototype.applyClip_ = function(height, width) {
|
166
318
|
if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
/**
|
175
|
-
* Handles a click on the "for" element, by positioning the menu and then
|
176
|
-
* toggling it.
|
177
|
-
* @private
|
178
|
-
*/
|
179
|
-
MaterialMenu.prototype.handleForClick_ = function(evt) {
|
180
|
-
'use strict';
|
181
|
-
|
182
|
-
if (this.element_ && this.forElement_) {
|
183
|
-
var rect = this.forElement_.getBoundingClientRect();
|
184
|
-
var forRect = this.forElement_.parentElement.getBoundingClientRect();
|
185
|
-
|
186
|
-
if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {
|
187
|
-
// Do not position the menu automatically. Requires the developer to
|
188
|
-
// manually specify position.
|
189
|
-
} else if (this.element_.classList.contains(
|
190
|
-
this.CssClasses_.BOTTOM_RIGHT)) {
|
191
|
-
// Position below the "for" element, aligned to its right.
|
192
|
-
this.container_.style.right = (forRect.right - rect.right) + 'px';
|
193
|
-
this.container_.style.top =
|
194
|
-
this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';
|
319
|
+
// Do not clip.
|
320
|
+
this.element_.style.clip = null;
|
321
|
+
} else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {
|
322
|
+
// Clip to the top right corner of the menu.
|
323
|
+
this.element_.style.clip =
|
324
|
+
'rect(0 ' + width + 'px ' + '0 ' + width + 'px)';
|
195
325
|
} else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {
|
196
|
-
//
|
197
|
-
this.
|
198
|
-
|
326
|
+
// Clip to the bottom left corner of the menu.
|
327
|
+
this.element_.style.clip =
|
328
|
+
'rect(' + height + 'px 0 ' + height + 'px 0)';
|
199
329
|
} else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
|
200
|
-
//
|
201
|
-
this.
|
202
|
-
|
330
|
+
// Clip to the bottom right corner of the menu.
|
331
|
+
this.element_.style.clip = 'rect(' + height + 'px ' + width + 'px ' +
|
332
|
+
height + 'px ' + width + 'px)';
|
203
333
|
} else {
|
204
|
-
// Default:
|
205
|
-
this.
|
206
|
-
this.container_.style.top =
|
207
|
-
this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';
|
208
|
-
}
|
209
|
-
}
|
210
|
-
|
211
|
-
this.toggle(evt);
|
212
|
-
};
|
213
|
-
|
214
|
-
/**
|
215
|
-
* Handles a keyboard event on the "for" element.
|
216
|
-
* @private
|
217
|
-
*/
|
218
|
-
MaterialMenu.prototype.handleForKeyboardEvent_ = function(evt) {
|
219
|
-
'use strict';
|
220
|
-
|
221
|
-
if (this.element_ && this.container_ && this.forElement_) {
|
222
|
-
var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM +
|
223
|
-
':not([disabled])');
|
224
|
-
|
225
|
-
if (items && items.length > 0 &&
|
226
|
-
this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {
|
227
|
-
if (evt.keyCode === this.Keycodes_.UP_ARROW) {
|
228
|
-
evt.preventDefault();
|
229
|
-
items[items.length - 1].focus();
|
230
|
-
} else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) {
|
231
|
-
evt.preventDefault();
|
232
|
-
items[0].focus();
|
233
|
-
}
|
334
|
+
// Default: do not clip (same as clipping to the top left corner).
|
335
|
+
this.element_.style.clip = null;
|
234
336
|
}
|
235
|
-
}
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
MaterialMenu.prototype.
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
if (items && items.length > 0 &&
|
250
|
-
this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {
|
251
|
-
var currentIndex = Array.prototype.slice.call(items).indexOf(evt.target);
|
337
|
+
};
|
338
|
+
|
339
|
+
/**
|
340
|
+
* Adds an event listener to clean up after the animation ends.
|
341
|
+
*
|
342
|
+
* @private
|
343
|
+
*/
|
344
|
+
MaterialMenu.prototype.addAnimationEndListener_ = function() {
|
345
|
+
var cleanup = function() {
|
346
|
+
this.element_.removeEventListener('transitionend', cleanup);
|
347
|
+
this.element_.removeEventListener('webkitTransitionEnd', cleanup);
|
348
|
+
this.element_.classList.remove(this.CssClasses_.IS_ANIMATING);
|
349
|
+
}.bind(this);
|
252
350
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
351
|
+
// Remove animation class once the transition is done.
|
352
|
+
this.element_.addEventListener('transitionend', cleanup);
|
353
|
+
this.element_.addEventListener('webkitTransitionEnd', cleanup);
|
354
|
+
};
|
355
|
+
|
356
|
+
/**
|
357
|
+
* Displays the menu.
|
358
|
+
*
|
359
|
+
* @public
|
360
|
+
*/
|
361
|
+
MaterialMenu.prototype.show = function(evt) {
|
362
|
+
if (this.element_ && this.container_ && this.outline_) {
|
363
|
+
// Measure the inner element.
|
364
|
+
var height = this.element_.getBoundingClientRect().height;
|
365
|
+
var width = this.element_.getBoundingClientRect().width;
|
366
|
+
|
367
|
+
// Apply the inner element's size to the container and outline.
|
368
|
+
this.container_.style.width = width + 'px';
|
369
|
+
this.container_.style.height = height + 'px';
|
370
|
+
this.outline_.style.width = width + 'px';
|
371
|
+
this.outline_.style.height = height + 'px';
|
372
|
+
|
373
|
+
var transitionDuration = this.Constant_.TRANSITION_DURATION_SECONDS *
|
374
|
+
this.Constant_.TRANSITION_DURATION_FRACTION;
|
375
|
+
|
376
|
+
// Calculate transition delays for individual menu items, so that they fade
|
377
|
+
// in one at a time.
|
378
|
+
var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);
|
379
|
+
for (var i = 0; i < items.length; i++) {
|
380
|
+
var itemDelay = null;
|
381
|
+
if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT) ||
|
382
|
+
this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
|
383
|
+
itemDelay = ((height - items[i].offsetTop - items[i].offsetHeight) /
|
384
|
+
height * transitionDuration) + 's';
|
264
385
|
} else {
|
265
|
-
items[
|
386
|
+
itemDelay = (items[i].offsetTop / height * transitionDuration) + 's';
|
266
387
|
}
|
267
|
-
|
268
|
-
evt.keyCode === this.Keycodes_.ENTER) {
|
269
|
-
evt.preventDefault();
|
270
|
-
// Send mousedown and mouseup to trigger ripple.
|
271
|
-
var e = new MouseEvent('mousedown');
|
272
|
-
evt.target.dispatchEvent(e);
|
273
|
-
e = new MouseEvent('mouseup');
|
274
|
-
evt.target.dispatchEvent(e);
|
275
|
-
// Send click.
|
276
|
-
evt.target.click();
|
277
|
-
} else if (evt.keyCode === this.Keycodes_.ESCAPE) {
|
278
|
-
evt.preventDefault();
|
279
|
-
this.hide();
|
388
|
+
items[i].style.transitionDelay = itemDelay;
|
280
389
|
}
|
281
|
-
}
|
282
|
-
}
|
283
|
-
};
|
284
|
-
|
285
|
-
/**
|
286
|
-
* Handles a click event on an item.
|
287
|
-
* @private
|
288
|
-
*/
|
289
|
-
MaterialMenu.prototype.handleItemClick_ = function(evt) {
|
290
|
-
'use strict';
|
291
|
-
|
292
|
-
if (evt.target.getAttribute('disabled') !== null) {
|
293
|
-
evt.stopPropagation();
|
294
|
-
} else {
|
295
|
-
// Wait some time before closing menu, so the user can see the ripple.
|
296
|
-
this.closing_ = true;
|
297
|
-
window.setTimeout(function(evt) {
|
298
|
-
this.hide();
|
299
|
-
this.closing_ = false;
|
300
|
-
}.bind(this), this.Constant_.CLOSE_TIMEOUT);
|
301
|
-
}
|
302
|
-
};
|
303
|
-
|
304
|
-
/**
|
305
|
-
* Calculates the initial clip (for opening the menu) or final clip (for closing
|
306
|
-
* it), and applies it. This allows us to animate from or to the correct point,
|
307
|
-
* that is, the point it's aligned to in the "for" element.
|
308
|
-
* @private
|
309
|
-
*/
|
310
|
-
MaterialMenu.prototype.applyClip_ = function(height, width) {
|
311
|
-
'use strict';
|
312
|
-
|
313
|
-
if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {
|
314
|
-
// Do not clip.
|
315
|
-
this.element_.style.clip = null;
|
316
|
-
} else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {
|
317
|
-
// Clip to the top right corner of the menu.
|
318
|
-
this.element_.style.clip =
|
319
|
-
'rect(0 ' + width + 'px ' + '0 ' + width + 'px)';
|
320
|
-
} else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {
|
321
|
-
// Clip to the bottom left corner of the menu.
|
322
|
-
this.element_.style.clip =
|
323
|
-
'rect(' + height + 'px 0 ' + height + 'px 0)';
|
324
|
-
} else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
|
325
|
-
// Clip to the bottom right corner of the menu.
|
326
|
-
this.element_.style.clip = 'rect(' + height + 'px ' + width + 'px ' +
|
327
|
-
height + 'px ' + width + 'px)';
|
328
|
-
} else {
|
329
|
-
// Default: do not clip (same as clipping to the top left corner).
|
330
|
-
this.element_.style.clip = null;
|
331
|
-
}
|
332
|
-
};
|
333
|
-
|
334
|
-
/**
|
335
|
-
* Adds an event listener to clean up after the animation ends.
|
336
|
-
* @private
|
337
|
-
*/
|
338
|
-
MaterialMenu.prototype.addAnimationEndListener_ = function() {
|
339
|
-
'use strict';
|
340
|
-
|
341
|
-
var cleanup = function () {
|
342
|
-
this.element_.removeEventListener('transitionend', cleanup);
|
343
|
-
this.element_.removeEventListener('webkitTransitionEnd', cleanup);
|
344
|
-
this.element_.classList.remove(this.CssClasses_.IS_ANIMATING);
|
345
|
-
}.bind(this);
|
346
|
-
|
347
|
-
// Remove animation class once the transition is done.
|
348
|
-
this.element_.addEventListener('transitionend', cleanup);
|
349
|
-
this.element_.addEventListener('webkitTransitionEnd', cleanup);
|
350
|
-
};
|
351
|
-
|
352
|
-
/**
|
353
|
-
* Displays the menu.
|
354
|
-
* @public
|
355
|
-
*/
|
356
|
-
MaterialMenu.prototype.show = function(evt) {
|
357
|
-
'use strict';
|
358
|
-
|
359
|
-
if (this.element_ && this.container_ && this.outline_) {
|
360
|
-
// Measure the inner element.
|
361
|
-
var height = this.element_.getBoundingClientRect().height;
|
362
|
-
var width = this.element_.getBoundingClientRect().width;
|
363
|
-
|
364
|
-
// Apply the inner element's size to the container and outline.
|
365
|
-
this.container_.style.width = width + 'px';
|
366
|
-
this.container_.style.height = height + 'px';
|
367
|
-
this.outline_.style.width = width + 'px';
|
368
|
-
this.outline_.style.height = height + 'px';
|
369
|
-
|
370
|
-
var transitionDuration = this.Constant_.TRANSITION_DURATION_SECONDS *
|
371
|
-
this.Constant_.TRANSITION_DURATION_FRACTION;
|
372
390
|
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
}
|
383
|
-
|
384
|
-
|
385
|
-
|
391
|
+
// Apply the initial clip to the text before we start animating.
|
392
|
+
this.applyClip_(height, width);
|
393
|
+
|
394
|
+
// Wait for the next frame, turn on animation, and apply the final clip.
|
395
|
+
// Also make it visible. This triggers the transitions.
|
396
|
+
window.requestAnimationFrame(function() {
|
397
|
+
this.element_.classList.add(this.CssClasses_.IS_ANIMATING);
|
398
|
+
this.element_.style.clip = 'rect(0 ' + width + 'px ' + height + 'px 0)';
|
399
|
+
this.container_.classList.add(this.CssClasses_.IS_VISIBLE);
|
400
|
+
}.bind(this));
|
401
|
+
|
402
|
+
// Clean up after the animation is complete.
|
403
|
+
this.addAnimationEndListener_();
|
404
|
+
|
405
|
+
// Add a click listener to the document, to close the menu.
|
406
|
+
var callback = function(e) {
|
407
|
+
// Check to see if the document is processing the same event that
|
408
|
+
// displayed the menu in the first place. If so, do nothing.
|
409
|
+
// Also check to see if the menu is in the process of closing itself, and
|
410
|
+
// do nothing in that case.
|
411
|
+
if (e !== evt && !this.closing_) {
|
412
|
+
document.removeEventListener('click', callback);
|
413
|
+
this.hide();
|
414
|
+
}
|
415
|
+
}.bind(this);
|
416
|
+
document.addEventListener('click', callback);
|
386
417
|
}
|
418
|
+
};
|
419
|
+
|
420
|
+
/**
|
421
|
+
* Hides the menu.
|
422
|
+
*
|
423
|
+
* @public
|
424
|
+
*/
|
425
|
+
MaterialMenu.prototype.hide = function() {
|
426
|
+
if (this.element_ && this.container_ && this.outline_) {
|
427
|
+
var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);
|
428
|
+
|
429
|
+
// Remove all transition delays; menu items fade out concurrently.
|
430
|
+
for (var i = 0; i < items.length; i++) {
|
431
|
+
items[i].style.transitionDelay = null;
|
432
|
+
}
|
387
433
|
|
388
|
-
|
389
|
-
|
434
|
+
// Measure the inner element.
|
435
|
+
var height = this.element_.getBoundingClientRect().height;
|
436
|
+
var width = this.element_.getBoundingClientRect().width;
|
390
437
|
|
391
|
-
|
392
|
-
|
393
|
-
window.requestAnimationFrame(function() {
|
438
|
+
// Turn on animation, and apply the final clip. Also make invisible.
|
439
|
+
// This triggers the transitions.
|
394
440
|
this.element_.classList.add(this.CssClasses_.IS_ANIMATING);
|
395
|
-
this.
|
396
|
-
this.container_.classList.
|
397
|
-
}.bind(this));
|
398
|
-
|
399
|
-
// Clean up after the animation is complete.
|
400
|
-
this.addAnimationEndListener_();
|
401
|
-
|
402
|
-
// Add a click listener to the document, to close the menu.
|
403
|
-
var callback = function(e) {
|
404
|
-
// Check to see if the document is processing the same event that
|
405
|
-
// displayed the menu in the first place. If so, do nothing.
|
406
|
-
// Also check to see if the menu is in the process of closing itself, and
|
407
|
-
// do nothing in that case.
|
408
|
-
if (e !== evt && !this.closing_) {
|
409
|
-
document.removeEventListener('click', callback);
|
410
|
-
this.hide();
|
411
|
-
}
|
412
|
-
}.bind(this);
|
413
|
-
document.addEventListener('click', callback);
|
414
|
-
}
|
415
|
-
};
|
441
|
+
this.applyClip_(height, width);
|
442
|
+
this.container_.classList.remove(this.CssClasses_.IS_VISIBLE);
|
416
443
|
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
444
|
+
// Clean up after the animation is complete.
|
445
|
+
this.addAnimationEndListener_();
|
446
|
+
}
|
447
|
+
};
|
448
|
+
|
449
|
+
/**
|
450
|
+
* Displays or hides the menu, depending on current state.
|
451
|
+
*
|
452
|
+
* @public
|
453
|
+
*/
|
454
|
+
MaterialMenu.prototype.toggle = function(evt) {
|
455
|
+
if (this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {
|
456
|
+
this.hide();
|
457
|
+
} else {
|
458
|
+
this.show(evt);
|
459
|
+
}
|
460
|
+
};
|
461
|
+
|
462
|
+
/**
|
463
|
+
* Downgrade the component.
|
464
|
+
*
|
465
|
+
* @private
|
466
|
+
*/
|
467
|
+
MaterialMenu.prototype.mdlDowngrade_ = function() {
|
425
468
|
var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);
|
426
469
|
|
427
|
-
// Remove all transition delays; menu items fade out concurrently.
|
428
470
|
for (var i = 0; i < items.length; i++) {
|
429
|
-
items[i].
|
471
|
+
items[i].removeEventListener('click', this.boundItemClick);
|
472
|
+
items[i].removeEventListener('keydown', this.boundItemKeydown);
|
430
473
|
}
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
// Clean up after the animation is complete.
|
443
|
-
this.addAnimationEndListener_();
|
444
|
-
}
|
445
|
-
};
|
446
|
-
|
447
|
-
/**
|
448
|
-
* Displays or hides the menu, depending on current state.
|
449
|
-
* @public
|
450
|
-
*/
|
451
|
-
MaterialMenu.prototype.toggle = function(evt) {
|
452
|
-
'use strict';
|
453
|
-
|
454
|
-
if (this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {
|
455
|
-
this.hide();
|
456
|
-
} else {
|
457
|
-
this.show(evt);
|
458
|
-
}
|
459
|
-
};
|
460
|
-
|
461
|
-
// The component registers itself. It can assume componentHandler is available
|
462
|
-
// in the global scope.
|
463
|
-
componentHandler.register({
|
464
|
-
constructor: MaterialMenu,
|
465
|
-
classAsString: 'MaterialMenu',
|
466
|
-
cssClass: 'mdl-js-menu',
|
467
|
-
widget: true
|
468
|
-
});
|
474
|
+
};
|
475
|
+
|
476
|
+
// The component registers itself. It can assume componentHandler is available
|
477
|
+
// in the global scope.
|
478
|
+
componentHandler.register({
|
479
|
+
constructor: MaterialMenu,
|
480
|
+
classAsString: 'MaterialMenu',
|
481
|
+
cssClass: 'mdl-js-menu',
|
482
|
+
widget: true
|
483
|
+
});
|
484
|
+
})();
|