1 | /*!
|
---|
2 | * AngularJS Material Design
|
---|
3 | * https://github.com/angular/material
|
---|
4 | * @license MIT
|
---|
5 | * v1.2.3
|
---|
6 | */
|
---|
7 | goog.provide('ngmaterial.components.toast');
|
---|
8 | goog.require('ngmaterial.components.button');
|
---|
9 | goog.require('ngmaterial.core');
|
---|
10 | /**
|
---|
11 | * @ngdoc module
|
---|
12 | * @name material.components.toast
|
---|
13 | * @description
|
---|
14 | * Toast and Snackbar component.
|
---|
15 | */
|
---|
16 | MdToastDirective['$inject'] = ["$mdToast"];
|
---|
17 | MdToastProvider['$inject'] = ["$$interimElementProvider"];
|
---|
18 | angular.module('material.components.toast', [
|
---|
19 | 'material.core',
|
---|
20 | 'material.components.button'
|
---|
21 | ])
|
---|
22 | .directive('mdToast', MdToastDirective)
|
---|
23 | .provider('$mdToast', MdToastProvider);
|
---|
24 |
|
---|
25 | /* ngInject */
|
---|
26 | function MdToastDirective($mdToast) {
|
---|
27 | return {
|
---|
28 | restrict: 'E',
|
---|
29 | link: function postLink(scope, element) {
|
---|
30 | element.addClass('_md'); // private md component indicator for styling
|
---|
31 |
|
---|
32 | // When navigation force destroys an interimElement, then
|
---|
33 | // listen and $destroy() that interim instance...
|
---|
34 | scope.$on('$destroy', function() {
|
---|
35 | $mdToast.destroy();
|
---|
36 | });
|
---|
37 | }
|
---|
38 | };
|
---|
39 | }
|
---|
40 |
|
---|
41 | /**
|
---|
42 | * @ngdoc service
|
---|
43 | * @name $mdToast
|
---|
44 | * @module material.components.toast
|
---|
45 | *
|
---|
46 | * @description
|
---|
47 | * `$mdToast` is a service to build a toast notification on any position
|
---|
48 | * on the screen with an optional duration, and provides a simple promise API.
|
---|
49 | *
|
---|
50 | * The toast will be always positioned at the `bottom`, when the screen size is
|
---|
51 | * between `600px` and `959px` (`sm` breakpoint)
|
---|
52 | *
|
---|
53 | * ## Restrictions on custom toasts
|
---|
54 | * - The toast's template must have an outer `<md-toast>` element.
|
---|
55 | * - For a toast action, use element with class `md-action`.
|
---|
56 | * - Add the class `md-capsule` for curved corners.
|
---|
57 | *
|
---|
58 | * ### Custom Presets
|
---|
59 | * Developers are also able to create their own preset, which can be easily used without repeating
|
---|
60 | * their options each time.
|
---|
61 | *
|
---|
62 | * <hljs lang="js">
|
---|
63 | * $mdToastProvider.addPreset('testPreset', {
|
---|
64 | * options: function() {
|
---|
65 | * return {
|
---|
66 | * template:
|
---|
67 | * '<md-toast>' +
|
---|
68 | * '<div class="md-toast-content">' +
|
---|
69 | * 'This is a custom preset' +
|
---|
70 | * '</div>' +
|
---|
71 | * '</md-toast>',
|
---|
72 | * controllerAs: 'toast',
|
---|
73 | * bindToController: true
|
---|
74 | * };
|
---|
75 | * }
|
---|
76 | * });
|
---|
77 | * </hljs>
|
---|
78 | *
|
---|
79 | * After you created your preset at config phase, you can easily access it.
|
---|
80 | *
|
---|
81 | * <hljs lang="js">
|
---|
82 | * $mdToast.show(
|
---|
83 | * $mdToast.testPreset()
|
---|
84 | * );
|
---|
85 | * </hljs>
|
---|
86 | *
|
---|
87 | * ## Parent container notes
|
---|
88 | *
|
---|
89 | * The toast is positioned using absolute positioning relative to its first non-static parent
|
---|
90 | * container. Thus, if the requested parent container uses static positioning, we will temporarily
|
---|
91 | * set its positioning to `relative` while the toast is visible and reset it when the toast is
|
---|
92 | * hidden.
|
---|
93 | *
|
---|
94 | * Because of this, it is usually best to ensure that the parent container has a fixed height and
|
---|
95 | * prevents scrolling by setting the `overflow: hidden;` style. Since the position is based off of
|
---|
96 | * the parent's height, the toast may be mispositioned if you allow the parent to scroll.
|
---|
97 | *
|
---|
98 | * You can, however, have a scrollable element inside of the container; just make sure the
|
---|
99 | * container itself does not scroll.
|
---|
100 | *
|
---|
101 | * <hljs lang="html">
|
---|
102 | * <div layout-fill id="toast-container">
|
---|
103 | * <md-content>
|
---|
104 | * I can have lots of content and scroll!
|
---|
105 | * </md-content>
|
---|
106 | * </div>
|
---|
107 | * </hljs>
|
---|
108 | *
|
---|
109 | * @usage
|
---|
110 | * <hljs lang="html">
|
---|
111 | * <div ng-controller="MyController">
|
---|
112 | * <md-button ng-click="openToast()">
|
---|
113 | * Open a Toast!
|
---|
114 | * </md-button>
|
---|
115 | * </div>
|
---|
116 | * </hljs>
|
---|
117 | *
|
---|
118 | * <hljs lang="js">
|
---|
119 | * var app = angular.module('app', ['ngMaterial']);
|
---|
120 | * app.controller('MyController', function($scope, $mdToast) {
|
---|
121 | * $scope.openToast = function($event) {
|
---|
122 | * $mdToast.show($mdToast.simple().textContent('Hello!'));
|
---|
123 | * // Could also do $mdToast.showSimple('Hello');
|
---|
124 | * };
|
---|
125 | * });
|
---|
126 | * </hljs>
|
---|
127 | */
|
---|
128 |
|
---|
129 | /**
|
---|
130 | * @ngdoc method
|
---|
131 | * @name $mdToast#showSimple
|
---|
132 | *
|
---|
133 | * @param {string} message The message to display inside the toast
|
---|
134 | * @description
|
---|
135 | * Convenience method which builds and shows a simple toast.
|
---|
136 | *
|
---|
137 | * @returns {promise} A promise that can be resolved with `$mdToast.hide()`.
|
---|
138 | */
|
---|
139 |
|
---|
140 | /**
|
---|
141 | * @ngdoc method
|
---|
142 | * @name $mdToast#simple
|
---|
143 | *
|
---|
144 | * @description
|
---|
145 | * Builds a preconfigured toast.
|
---|
146 | *
|
---|
147 | * @returns {obj} a `$mdToastPreset` with the following chainable configuration methods.
|
---|
148 | *
|
---|
149 | * _**Note:** These configuration methods are provided in addition to the methods provided by
|
---|
150 | * the `build()` and `show()` methods below._
|
---|
151 | *
|
---|
152 | * <table class="md-api-table methods">
|
---|
153 | * <thead>
|
---|
154 | * <tr>
|
---|
155 | * <th>Method</th>
|
---|
156 | * <th>Description</th>
|
---|
157 | * </tr>
|
---|
158 | * </thead>
|
---|
159 | * <tbody>
|
---|
160 | * <tr>
|
---|
161 | * <td>`.textContent(string)`</td>
|
---|
162 | * <td>Sets the toast content to the specified string</td>
|
---|
163 | * </tr>
|
---|
164 | * <tr>
|
---|
165 | * <td>`.action(string)`</td>
|
---|
166 | * <td>
|
---|
167 | * Adds an action button. <br/>
|
---|
168 | * If clicked, the promise (returned from `show()`) will resolve with the value `'ok'`;
|
---|
169 | * otherwise, it is resolved with `true` after a `hideDelay` timeout.
|
---|
170 | * </td>
|
---|
171 | * </tr>
|
---|
172 | * <tr>
|
---|
173 | * <td>`.actionKey(string)`</td>
|
---|
174 | * <td>
|
---|
175 | * Adds a hotkey for the action button to the page. <br/>
|
---|
176 | * If the `actionKey` and `Control` key are pressed, the toast's action will be triggered.
|
---|
177 | * </td>
|
---|
178 | * </tr>
|
---|
179 | * <tr>
|
---|
180 | * <td>`.actionHint(string)`</td>
|
---|
181 | * <td>
|
---|
182 | * Text that a screen reader will announce to let the user know how to activate the
|
---|
183 | * action. <br>
|
---|
184 | * If an `actionKey` is defined, this defaults to:
|
---|
185 | * 'Press Control-"`actionKey`" to ' followed by the `action`.
|
---|
186 | * </td>
|
---|
187 | * </tr>
|
---|
188 | * <tr>
|
---|
189 | * <td>`.dismissHint(string)`</td>
|
---|
190 | * <td>
|
---|
191 | * Text that a screen reader will announce to let the user know how to dismiss the toast.
|
---|
192 | * <br>Defaults to: "Press Escape to dismiss."
|
---|
193 | * </td>
|
---|
194 | * </tr>
|
---|
195 | * <tr>
|
---|
196 | * <td>`.highlightAction(boolean)`</td>
|
---|
197 | * <td>
|
---|
198 | * Whether or not the action button will have an additional highlight class.<br/>
|
---|
199 | * By default the `accent` color will be applied to the action button.
|
---|
200 | * </td>
|
---|
201 | * </tr>
|
---|
202 | * <tr>
|
---|
203 | * <td>`.highlightClass(string)`</td>
|
---|
204 | * <td>
|
---|
205 | * If set, the given class will be applied to the highlighted action button.<br/>
|
---|
206 | * This allows you to specify the highlight color easily. Highlight classes are
|
---|
207 | * `md-primary`, `md-warn`, and `md-accent`
|
---|
208 | * </td>
|
---|
209 | * </tr>
|
---|
210 | * <tr>
|
---|
211 | * <td>`.capsule(boolean)`</td>
|
---|
212 | * <td>
|
---|
213 | * Whether or not to add the `md-capsule` class to the toast to provide rounded corners
|
---|
214 | * </td>
|
---|
215 | * </tr>
|
---|
216 | * <tr>
|
---|
217 | * <td>`.theme(string)`</td>
|
---|
218 | * <td>
|
---|
219 | * Sets the theme on the toast to the requested theme. Default is `$mdThemingProvider`'s
|
---|
220 | * default.
|
---|
221 | * </td>
|
---|
222 | * </tr>
|
---|
223 | * <tr>
|
---|
224 | * <td>`.toastClass(string)`</td>
|
---|
225 | * <td>Sets a class on the toast element</td>
|
---|
226 | * </tr>
|
---|
227 | * </tbody>
|
---|
228 | * </table>
|
---|
229 | */
|
---|
230 |
|
---|
231 | /**
|
---|
232 | * @ngdoc method
|
---|
233 | * @name $mdToast#updateTextContent
|
---|
234 | *
|
---|
235 | * @description
|
---|
236 | * Updates the content of an existing toast. Useful for updating things like counts, etc.
|
---|
237 | */
|
---|
238 |
|
---|
239 | /**
|
---|
240 | * @ngdoc method
|
---|
241 | * @name $mdToast#build
|
---|
242 | *
|
---|
243 | * @description
|
---|
244 | * Creates a custom `$mdToastPreset` that you can configure.
|
---|
245 | *
|
---|
246 | * @returns {obj} a `$mdToastPreset` with the chainable configuration methods for shows' options
|
---|
247 | * (see below).
|
---|
248 | */
|
---|
249 |
|
---|
250 | /**
|
---|
251 | * @ngdoc method
|
---|
252 | * @name $mdToast#show
|
---|
253 | *
|
---|
254 | * @description Shows the toast.
|
---|
255 | *
|
---|
256 | * @param {Object} optionsOrPreset Either provide an `$mdToastPreset` returned from `simple()`
|
---|
257 | * and `build()`, or an options object with the following properties:
|
---|
258 | *
|
---|
259 | * - `templateUrl` - `{string=}`: The url of an html template file that will
|
---|
260 | * be used as the content of the toast. Restrictions: the template must
|
---|
261 | * have an outer `md-toast` element.
|
---|
262 | * - `template` - `{string=}`: Same as templateUrl, except this is an actual
|
---|
263 | * template string.
|
---|
264 | * - `autoWrap` - `{boolean=}`: Whether or not to automatically wrap the template content with a
|
---|
265 | * `<div class="md-toast-content">` if one is not provided. Defaults to true. Can be disabled
|
---|
266 | * if you provide a custom toast directive.
|
---|
267 | * - `scope` - `{Object=}`: the scope to link the template / controller to. If none is specified,
|
---|
268 | * it will create a new child scope. This scope will be destroyed when the toast is removed
|
---|
269 | * unless `preserveScope` is set to true.
|
---|
270 | * - `preserveScope` - `{boolean=}`: whether to preserve the scope when the element is removed.
|
---|
271 | * Default is false
|
---|
272 | * - `hideDelay` - `{number=}`: The number of milliseconds the toast should stay active before
|
---|
273 | * automatically closing. Set to `0` or `false` to have the toast stay open until closed
|
---|
274 | * manually via an action in the toast, a hotkey, or a swipe gesture. For accessibility, toasts
|
---|
275 | * should not automatically close when they contain an action.<br>
|
---|
276 | * Defaults to: `3000`.
|
---|
277 | * - `position` - `{string=}`: Sets the position of the toast. <br/>
|
---|
278 | * Available: any combination of `'bottom'`, `'left'`, `'top'`, `'right'`, `'end'`, and
|
---|
279 | * `'start'`. The properties `'end'` and `'start'` are dynamic and can be used for RTL support.
|
---|
280 | * <br/>
|
---|
281 | * Default combination: `'bottom left'`.
|
---|
282 | * - `toastClass` - `{string=}`: A class to set on the toast element.
|
---|
283 | * - `controller` - `{string=}`: The controller to associate with this toast.
|
---|
284 | * The controller will be injected the local `$mdToast.hide()`, which is a function
|
---|
285 | * used to hide the toast.
|
---|
286 | * - `locals` - `{Object=}`: An object containing key/value pairs. The keys will be used as names
|
---|
287 | * of values to inject into the controller. For example, `locals: {three: 3}` would inject
|
---|
288 | * `three` into the controller with the value of 3.
|
---|
289 | * - `bindToController` - `{boolean=}`: bind the locals to the controller, instead of passing
|
---|
290 | * them in.
|
---|
291 | * - `resolve` - `{Object=}`: Similar to locals, except it takes promises as values
|
---|
292 | * and the toast will not open until the promises resolve.
|
---|
293 | * - `controllerAs` - `{string=}`: An alias to assign the controller to on the scope.
|
---|
294 | * - `parent` - `{element=}`: The element to append the toast to. Defaults to appending
|
---|
295 | * to the root element of the application.
|
---|
296 | *
|
---|
297 | * @returns {promise} A promise that can be resolved with `$mdToast.hide()`. `$mdToast.hide()` will
|
---|
298 | * resolve either with the boolean value `true` or the value passed as an argument to
|
---|
299 | * `$mdToast.hide()`.
|
---|
300 | */
|
---|
301 |
|
---|
302 | /**
|
---|
303 | * @ngdoc method
|
---|
304 | * @name $mdToast#hide
|
---|
305 | *
|
---|
306 | * @description
|
---|
307 | * Hide an existing toast and resolve the promise returned from `$mdToast.show()`.
|
---|
308 | *
|
---|
309 | * @param {*=} response An argument for the resolved promise.
|
---|
310 | *
|
---|
311 | * @returns {promise} A promise that is called when the existing element is removed from the DOM.
|
---|
312 | * The promise is resolved with either the Boolean value `true` or the value passed as the
|
---|
313 | * argument to `$mdToast.hide()`.
|
---|
314 | */
|
---|
315 |
|
---|
316 | function MdToastProvider($$interimElementProvider) {
|
---|
317 | // Differentiate promise resolves: hide timeout (value == true) and hide action clicks
|
---|
318 | // (value == ok).
|
---|
319 | MdToastController['$inject'] = ["$mdToast", "$scope", "$log"];
|
---|
320 | toastDefaultOptions['$inject'] = ["$animate", "$mdToast", "$mdUtil", "$mdMedia", "$document", "$q"];
|
---|
321 | var ACTION_RESOLVE = 'ok';
|
---|
322 |
|
---|
323 | var activeToastContent;
|
---|
324 | var $mdToast = $$interimElementProvider('$mdToast')
|
---|
325 | .setDefaults({
|
---|
326 | methods: ['position', 'hideDelay', 'capsule', 'parent', 'position', 'toastClass'],
|
---|
327 | options: toastDefaultOptions
|
---|
328 | })
|
---|
329 | .addPreset('simple', {
|
---|
330 | argOption: 'textContent',
|
---|
331 | methods: ['textContent', 'action', 'actionKey', 'actionHint', 'highlightAction',
|
---|
332 | 'highlightClass', 'theme', 'parent', 'dismissHint'],
|
---|
333 | options: /* ngInject */ ["$mdToast", "$mdTheming", function($mdToast, $mdTheming) {
|
---|
334 | return {
|
---|
335 | template:
|
---|
336 | '<md-toast md-theme="{{ toast.theme }}" ng-class="{\'md-capsule\': toast.capsule}">' +
|
---|
337 | ' <div class="md-toast-content" aria-live="polite" aria-relevant="all">' +
|
---|
338 | ' <span class="md-toast-text">' +
|
---|
339 | ' {{ toast.content }}' +
|
---|
340 | ' </span>' +
|
---|
341 | ' <span class="md-visually-hidden">{{ toast.dismissHint }}</span>' +
|
---|
342 | ' <span class="md-visually-hidden" ng-if="toast.action && toast.actionKey">' +
|
---|
343 | ' {{ toast.actionHint }}' +
|
---|
344 | ' </span>' +
|
---|
345 | ' <md-button class="md-action" ng-if="toast.action" ng-click="toast.resolve()" ' +
|
---|
346 | ' ng-class="highlightClasses">' +
|
---|
347 | ' {{ toast.action }}' +
|
---|
348 | ' </md-button>' +
|
---|
349 | ' </div>' +
|
---|
350 | '</md-toast>',
|
---|
351 | controller: MdToastController,
|
---|
352 | theme: $mdTheming.defaultTheme(),
|
---|
353 | controllerAs: 'toast',
|
---|
354 | bindToController: true
|
---|
355 | };
|
---|
356 | }]
|
---|
357 | })
|
---|
358 | .addMethod('updateTextContent', updateTextContent);
|
---|
359 |
|
---|
360 | function updateTextContent(newContent) {
|
---|
361 | activeToastContent = newContent;
|
---|
362 | }
|
---|
363 |
|
---|
364 | return $mdToast;
|
---|
365 |
|
---|
366 | /**
|
---|
367 | * Controller for the Toast interim elements.
|
---|
368 | * ngInject
|
---|
369 | */
|
---|
370 | function MdToastController($mdToast, $scope, $log) {
|
---|
371 | // For compatibility with AngularJS 1.6+, we should always use the $onInit hook in
|
---|
372 | // interimElements. The $mdCompiler simulates the $onInit hook for all versions.
|
---|
373 | this.$onInit = function() {
|
---|
374 | var self = this;
|
---|
375 |
|
---|
376 | if (self.highlightAction) {
|
---|
377 | $scope.highlightClasses = [
|
---|
378 | 'md-highlight',
|
---|
379 | self.highlightClass
|
---|
380 | ];
|
---|
381 | }
|
---|
382 |
|
---|
383 | // If an action is defined and no actionKey is specified, then log a warning.
|
---|
384 | if (self.action && !self.actionKey) {
|
---|
385 | $log.warn('Toasts with actions should define an actionKey for accessibility.',
|
---|
386 | 'Details: https://material.angularjs.org/latest/api/service/$mdToast#mdtoast-simple');
|
---|
387 | }
|
---|
388 |
|
---|
389 | if (self.actionKey && !self.actionHint) {
|
---|
390 | self.actionHint = 'Press Control-"' + self.actionKey + '" to ';
|
---|
391 | }
|
---|
392 |
|
---|
393 | if (!self.dismissHint) {
|
---|
394 | self.dismissHint = 'Press Escape to dismiss.';
|
---|
395 | }
|
---|
396 |
|
---|
397 | $scope.$watch(function() { return activeToastContent; }, function() {
|
---|
398 | self.content = activeToastContent;
|
---|
399 | });
|
---|
400 |
|
---|
401 | this.resolve = function() {
|
---|
402 | $mdToast.hide(ACTION_RESOLVE);
|
---|
403 | };
|
---|
404 | };
|
---|
405 | }
|
---|
406 |
|
---|
407 | /* ngInject */
|
---|
408 | function toastDefaultOptions($animate, $mdToast, $mdUtil, $mdMedia, $document, $q) {
|
---|
409 | var SWIPE_EVENTS = '$md.swipeleft $md.swiperight $md.swipeup $md.swipedown';
|
---|
410 | return {
|
---|
411 | onShow: onShow,
|
---|
412 | onRemove: onRemove,
|
---|
413 | toastClass: '',
|
---|
414 | position: 'bottom left',
|
---|
415 | themable: true,
|
---|
416 | hideDelay: 3000,
|
---|
417 | autoWrap: true,
|
---|
418 | transformTemplate: function(template, options) {
|
---|
419 | var shouldAddWrapper = options.autoWrap && template && !/md-toast-content/g.test(template);
|
---|
420 |
|
---|
421 | if (shouldAddWrapper) {
|
---|
422 | // Root element of template will be <md-toast>. We need to wrap all of its content inside
|
---|
423 | // of <div class="md-toast-content">. All templates provided here should be static,
|
---|
424 | // developer-controlled content (meaning we're not attempting to guard against XSS).
|
---|
425 | var templateRoot = document.createElement('md-template');
|
---|
426 | templateRoot.innerHTML = template;
|
---|
427 |
|
---|
428 | // Iterate through all root children, to detect possible md-toast directives.
|
---|
429 | for (var i = 0; i < templateRoot.children.length; i++) {
|
---|
430 | if (templateRoot.children[i].nodeName === 'MD-TOAST') {
|
---|
431 | var wrapper = angular.element('<div class="md-toast-content">');
|
---|
432 |
|
---|
433 | // Wrap the children of the `md-toast` directive in jqLite, to be able to append
|
---|
434 | // multiple nodes with the same execution.
|
---|
435 | wrapper.append(angular.element(templateRoot.children[i].childNodes));
|
---|
436 |
|
---|
437 | // Append the new wrapped element to the `md-toast` directive.
|
---|
438 | templateRoot.children[i].appendChild(wrapper[0]);
|
---|
439 | }
|
---|
440 | }
|
---|
441 |
|
---|
442 | // We have to return the innerHTML, because we do not want to have the `md-template`
|
---|
443 | // element to be the root element of our interimElement.
|
---|
444 | return templateRoot.innerHTML;
|
---|
445 | }
|
---|
446 |
|
---|
447 | return template || '';
|
---|
448 | }
|
---|
449 | };
|
---|
450 |
|
---|
451 | /**
|
---|
452 | * @param {{toast: {actionKey: string=}}=} scope
|
---|
453 | * @param {JQLite} element
|
---|
454 | * @param {Object.<string, string>} options
|
---|
455 | * @return {*}
|
---|
456 | */
|
---|
457 | function onShow(scope, element, options) {
|
---|
458 | activeToastContent = options.textContent;
|
---|
459 |
|
---|
460 | var isSmScreen = !$mdMedia('gt-sm');
|
---|
461 |
|
---|
462 | element = $mdUtil.extractElementByName(element, 'md-toast', true);
|
---|
463 | options.element = element;
|
---|
464 |
|
---|
465 | options.onSwipe = function(ev) {
|
---|
466 | // Add the relevant swipe class to the element so it can animate correctly
|
---|
467 | var swipe = ev.type.replace('$md.','');
|
---|
468 | var direction = swipe.replace('swipe', '');
|
---|
469 |
|
---|
470 | // If the swipe direction is down/up but the toast came from top/bottom don't fade away
|
---|
471 | // Unless the screen is small, then the toast always on bottom
|
---|
472 | if ((direction === 'down' && options.position.indexOf('top') !== -1 && !isSmScreen) ||
|
---|
473 | (direction === 'up' && (options.position.indexOf('bottom') !== -1 || isSmScreen))) {
|
---|
474 | return;
|
---|
475 | }
|
---|
476 |
|
---|
477 | if ((direction === 'left' || direction === 'right') && isSmScreen) {
|
---|
478 | return;
|
---|
479 | }
|
---|
480 |
|
---|
481 | element.addClass('md-' + swipe);
|
---|
482 | $mdUtil.nextTick($mdToast.cancel);
|
---|
483 | };
|
---|
484 | options.openClass = toastOpenClass(options.position);
|
---|
485 |
|
---|
486 | element.addClass(options.toastClass);
|
---|
487 |
|
---|
488 | // 'top left' -> 'md-top md-left'
|
---|
489 | options.parent.addClass(options.openClass);
|
---|
490 |
|
---|
491 | // static is the default position
|
---|
492 | if ($mdUtil.hasComputedStyle(options.parent, 'position', 'static')) {
|
---|
493 | options.parent.css('position', 'relative');
|
---|
494 | }
|
---|
495 |
|
---|
496 | setupActionKeyListener(scope.toast && scope.toast.actionKey ?
|
---|
497 | scope.toast.actionKey : undefined);
|
---|
498 |
|
---|
499 | element.on(SWIPE_EVENTS, options.onSwipe);
|
---|
500 |
|
---|
501 | var verticalPositionDefined = false;
|
---|
502 | var positionClasses = options.position.split(' ').map(function (position) {
|
---|
503 | if (position) {
|
---|
504 | var className = 'md-' + position;
|
---|
505 | if (className === 'md-top' || className === 'md-bottom') {
|
---|
506 | verticalPositionDefined = true;
|
---|
507 | }
|
---|
508 | return className;
|
---|
509 | }
|
---|
510 | return 'md-bottom';
|
---|
511 | });
|
---|
512 | // If only "right" or "left" are defined, default to a vertical position of "bottom"
|
---|
513 | // as documented.
|
---|
514 | if (!verticalPositionDefined) {
|
---|
515 | positionClasses.push('md-bottom');
|
---|
516 | }
|
---|
517 | element.addClass(isSmScreen ? 'md-bottom' : positionClasses.join(' '));
|
---|
518 |
|
---|
519 | if (options.parent) {
|
---|
520 | options.parent.addClass('md-toast-animating');
|
---|
521 | }
|
---|
522 | return $animate.enter(element, options.parent).then(function() {
|
---|
523 | if (options.parent) {
|
---|
524 | options.parent.removeClass('md-toast-animating');
|
---|
525 | }
|
---|
526 | });
|
---|
527 | }
|
---|
528 |
|
---|
529 | /**
|
---|
530 | * @param {Object} scope the toast's scope
|
---|
531 | * @param {JQLite} element the toast to be removed
|
---|
532 | * @param {Object} options
|
---|
533 | * @return {Promise<*>} a Promise to remove the element immediately or to animate it out.
|
---|
534 | */
|
---|
535 | function onRemove(scope, element, options) {
|
---|
536 | if (scope.toast && scope.toast.actionKey) {
|
---|
537 | removeActionKeyListener();
|
---|
538 | }
|
---|
539 | element.off(SWIPE_EVENTS, options.onSwipe);
|
---|
540 | if (options.parent) options.parent.addClass('md-toast-animating');
|
---|
541 | if (options.openClass) options.parent.removeClass(options.openClass);
|
---|
542 |
|
---|
543 | // Don't run the leave animation if the element has already been destroyed.
|
---|
544 | return ((options.$destroy === true) ? $q.when(element.remove()) : $animate.leave(element))
|
---|
545 | .then(function () {
|
---|
546 | if (options.parent) options.parent.removeClass('md-toast-animating');
|
---|
547 | if ($mdUtil.hasComputedStyle(options.parent, 'position', 'static')) {
|
---|
548 | options.parent.css('position', '');
|
---|
549 | }
|
---|
550 | });
|
---|
551 | }
|
---|
552 |
|
---|
553 | function toastOpenClass(position) {
|
---|
554 | // For mobile, always open full-width on bottom
|
---|
555 | if (!$mdMedia('gt-xs')) {
|
---|
556 | return 'md-toast-open-bottom';
|
---|
557 | }
|
---|
558 |
|
---|
559 | return 'md-toast-open-' + (position.indexOf('top') > -1 ? 'top' : 'bottom');
|
---|
560 | }
|
---|
561 |
|
---|
562 | /**
|
---|
563 | * @param {string} actionKey
|
---|
564 | */
|
---|
565 | function setupActionKeyListener(actionKey) {
|
---|
566 | /**
|
---|
567 | * @param {KeyboardEvent} event
|
---|
568 | */
|
---|
569 | var handleKeyDown = function(event) {
|
---|
570 | if (event.key === 'Escape') {
|
---|
571 | $mdToast.hide(false);
|
---|
572 | }
|
---|
573 | if (actionKey && event.key === actionKey && event.ctrlKey) {
|
---|
574 | $mdToast.hide(ACTION_RESOLVE);
|
---|
575 | }
|
---|
576 | };
|
---|
577 | $document.on('keydown', handleKeyDown);
|
---|
578 | }
|
---|
579 |
|
---|
580 | function removeActionKeyListener() {
|
---|
581 | $document.off('keydown');
|
---|
582 | }
|
---|
583 | }
|
---|
584 | }
|
---|
585 |
|
---|
586 | ngmaterial.components.toast = angular.module("material.components.toast"); |
---|