123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. import { Component, ElementRef, HostListener, Renderer, ViewEncapsulation } from '@angular/core';
  2. import { Config } from '../../config/config';
  3. import { NON_TEXT_INPUT_REGEX } from '../../util/dom';
  4. import { BLOCK_ALL, GestureController } from '../../gestures/gesture-controller';
  5. import { isPresent } from '../../util/util';
  6. import { KEY_ENTER, KEY_ESCAPE } from '../../platform/key';
  7. import { NavParams } from '../../navigation/nav-params';
  8. import { Platform } from '../../platform/platform';
  9. import { ViewController } from '../../navigation/view-controller';
  10. /**
  11. * @hidden
  12. */
  13. var AlertCmp = (function () {
  14. function AlertCmp(_viewCtrl, _elementRef, config, gestureCtrl, params, _renderer, _plt) {
  15. this._viewCtrl = _viewCtrl;
  16. this._elementRef = _elementRef;
  17. this._renderer = _renderer;
  18. this._plt = _plt;
  19. // gesture blocker is used to disable gestures dynamically
  20. this.gestureBlocker = gestureCtrl.createBlocker(BLOCK_ALL);
  21. this.d = params.data;
  22. this.mode = this.d.mode || config.get('mode');
  23. this.keyboardResizes = config.getBoolean('keyboardResizes', false);
  24. _renderer.setElementClass(_elementRef.nativeElement, "alert-" + this.mode, true);
  25. if (this.d.cssClass) {
  26. this.d.cssClass.split(' ').forEach(function (cssClass) {
  27. // Make sure the class isn't whitespace, otherwise it throws exceptions
  28. if (cssClass.trim() !== '')
  29. _renderer.setElementClass(_elementRef.nativeElement, cssClass, true);
  30. });
  31. }
  32. this.id = (++alertIds);
  33. this.descId = '';
  34. this.hdrId = 'alert-hdr-' + this.id;
  35. this.subHdrId = 'alert-subhdr-' + this.id;
  36. this.msgId = 'alert-msg-' + this.id;
  37. this.activeId = '';
  38. this.lastClick = 0;
  39. if (this.d.message) {
  40. this.descId = this.msgId;
  41. }
  42. else if (this.d.subTitle) {
  43. this.descId = this.subHdrId;
  44. }
  45. if (!this.d.message) {
  46. this.d.message = '';
  47. }
  48. }
  49. AlertCmp.prototype.ionViewDidLoad = function () {
  50. var _this = this;
  51. // normalize the data
  52. var data = this.d;
  53. data.buttons = data.buttons.map(function (button) {
  54. if (typeof button === 'string') {
  55. return { text: button };
  56. }
  57. return button;
  58. });
  59. data.inputs = data.inputs.map(function (input, index) {
  60. var r = {
  61. type: input.type || 'text',
  62. name: isPresent(input.name) ? input.name : index + '',
  63. placeholder: isPresent(input.placeholder) ? input.placeholder : '',
  64. value: isPresent(input.value) ? input.value : '',
  65. label: input.label,
  66. checked: !!input.checked,
  67. disabled: !!input.disabled,
  68. id: isPresent(input.id) ? input.id : "alert-input-" + _this.id + "-" + index,
  69. handler: isPresent(input.handler) ? input.handler : null,
  70. min: isPresent(input.min) ? input.min : null,
  71. max: isPresent(input.max) ? input.max : null
  72. };
  73. return r;
  74. });
  75. // An alert can be created with several different inputs. Radios,
  76. // checkboxes and inputs are all accepted, but they cannot be mixed.
  77. var inputTypes = [];
  78. data.inputs.forEach(function (input) {
  79. if (inputTypes.indexOf(input.type) < 0) {
  80. inputTypes.push(input.type);
  81. }
  82. });
  83. if (inputTypes.length > 1 && (inputTypes.indexOf('checkbox') > -1 || inputTypes.indexOf('radio') > -1)) {
  84. console.warn("Alert cannot mix input types: " + (inputTypes.join('/')) + ". Please see alert docs for more info.");
  85. }
  86. this.inputType = inputTypes.length ? inputTypes[0] : null;
  87. var checkedInput = this.d.inputs.find(function (input) { return input.checked; });
  88. if (checkedInput) {
  89. this.activeId = checkedInput.id;
  90. }
  91. var hasTextInput = (this.d.inputs.length && this.d.inputs.some(function (i) { return !(NON_TEXT_INPUT_REGEX.test(i.type)); }));
  92. if (!this.keyboardResizes && hasTextInput && this._plt.is('mobile')) {
  93. // this alert has a text input and it's on a mobile device so we should align
  94. // the alert up high because we need to leave space for the virtual keboard
  95. // this also helps prevent the layout getting all messed up from
  96. // the browser trying to scroll the input into a safe area
  97. this._renderer.setElementClass(this._elementRef.nativeElement, 'alert-top', true);
  98. }
  99. };
  100. AlertCmp.prototype.ionViewWillEnter = function () {
  101. this.gestureBlocker.block();
  102. };
  103. AlertCmp.prototype.ionViewDidLeave = function () {
  104. this.gestureBlocker.unblock();
  105. };
  106. AlertCmp.prototype.ionViewDidEnter = function () {
  107. // set focus on the first input or button in the alert
  108. // note that this does not always work and bring up the keyboard on
  109. // devices since the focus command must come from the user's touch event
  110. // and ionViewDidEnter is not in the same callstack as the touch event :(
  111. var focusableEle = this._elementRef.nativeElement.querySelector('input,button');
  112. if (focusableEle) {
  113. setTimeout(function () { return focusableEle.focus(); });
  114. }
  115. this.enabled = true;
  116. };
  117. AlertCmp.prototype.keyUp = function (ev) {
  118. if (this.enabled && this._viewCtrl.isLast()) {
  119. if (ev.keyCode === KEY_ENTER) {
  120. if (this.lastClick + 1000 < Date.now()) {
  121. // do not fire this click if there recently was already a click
  122. // this can happen when the button has focus and used the enter
  123. // key to click the button. However, both the click handler and
  124. // this keyup event will fire, so only allow one of them to go.
  125. (void 0) /* console.debug */;
  126. var button = this.d.buttons[this.d.buttons.length - 1];
  127. this.btnClick(button);
  128. }
  129. }
  130. else if (ev.keyCode === KEY_ESCAPE) {
  131. (void 0) /* console.debug */;
  132. this.bdClick();
  133. }
  134. }
  135. };
  136. AlertCmp.prototype.btnClick = function (button) {
  137. if (!this.enabled) {
  138. return;
  139. }
  140. // keep the time of the most recent button click
  141. this.lastClick = Date.now();
  142. var shouldDismiss = true;
  143. if (button.handler) {
  144. // a handler has been provided, execute it
  145. // pass the handler the values from the inputs
  146. if (button.handler(this.getValues()) === false) {
  147. // if the return value of the handler is false then do not dismiss
  148. shouldDismiss = false;
  149. }
  150. }
  151. if (shouldDismiss) {
  152. this.dismiss(button.role);
  153. }
  154. };
  155. AlertCmp.prototype.rbClick = function (checkedInput) {
  156. if (this.enabled) {
  157. this.d.inputs.forEach(function (input) {
  158. input.checked = (checkedInput === input);
  159. });
  160. this.activeId = checkedInput.id;
  161. if (checkedInput.handler) {
  162. checkedInput.handler(checkedInput);
  163. }
  164. }
  165. };
  166. AlertCmp.prototype.cbClick = function (checkedInput) {
  167. if (this.enabled) {
  168. checkedInput.checked = !checkedInput.checked;
  169. if (checkedInput.handler) {
  170. checkedInput.handler(checkedInput);
  171. }
  172. }
  173. };
  174. AlertCmp.prototype.bdClick = function () {
  175. if (this.enabled && this.d.enableBackdropDismiss) {
  176. var cancelBtn = this.d.buttons.find(function (b) { return b.role === 'cancel'; });
  177. if (cancelBtn) {
  178. this.btnClick(cancelBtn);
  179. }
  180. else {
  181. this.dismiss('backdrop');
  182. }
  183. }
  184. };
  185. AlertCmp.prototype.dismiss = function (role) {
  186. var opts = {
  187. minClickBlockDuration: 400
  188. };
  189. return this._viewCtrl.dismiss(this.getValues(), role, opts);
  190. };
  191. AlertCmp.prototype.getValues = function () {
  192. if (this.inputType === 'radio') {
  193. // this is an alert with radio buttons (single value select)
  194. // return the one value which is checked, otherwise undefined
  195. var checkedInput = this.d.inputs.find(function (i) { return i.checked; });
  196. return checkedInput ? checkedInput.value : undefined;
  197. }
  198. if (this.inputType === 'checkbox') {
  199. // this is an alert with checkboxes (multiple value select)
  200. // return an array of all the checked values
  201. return this.d.inputs.filter(function (i) { return i.checked; }).map(function (i) { return i.value; });
  202. }
  203. if (this.d.inputs.length === 0) {
  204. // this is an alert without any options/inputs at all
  205. return undefined;
  206. }
  207. // this is an alert with text inputs
  208. // return an object of all the values with the input name as the key
  209. var values = {};
  210. this.d.inputs.forEach(function (i) {
  211. values[i.name] = i.value;
  212. });
  213. return values;
  214. };
  215. AlertCmp.prototype.ngOnDestroy = function () {
  216. (void 0) /* assert */;
  217. this.gestureBlocker.destroy();
  218. };
  219. AlertCmp.decorators = [
  220. { type: Component, args: [{
  221. selector: 'ion-alert',
  222. template: '<ion-backdrop (click)="bdClick()" [class.backdrop-no-tappable]="!d.enableBackdropDismiss"></ion-backdrop>' +
  223. '<div class="alert-wrapper">' +
  224. '<div class="alert-head">' +
  225. '<h2 id="{{hdrId}}" class="alert-title" *ngIf="d.title" [innerHTML]="d.title"></h2>' +
  226. '<h3 id="{{subHdrId}}" class="alert-sub-title" *ngIf="d.subTitle" [innerHTML]="d.subTitle"></h3>' +
  227. '</div>' +
  228. '<div id="{{msgId}}" class="alert-message" [innerHTML]="d.message"></div>' +
  229. '<div *ngIf="d.inputs.length" [ngSwitch]="inputType">' +
  230. '<ng-template ngSwitchCase="radio">' +
  231. '<div class="alert-radio-group" role="radiogroup" [attr.aria-labelledby]="hdrId" [attr.aria-activedescendant]="activeId">' +
  232. '<button ion-button="alert-radio-button" *ngFor="let i of d.inputs" (click)="rbClick(i)" [attr.aria-checked]="i.checked" [disabled]="i.disabled" [attr.id]="i.id" class="alert-tappable alert-radio" role="radio">' +
  233. '<div class="alert-radio-icon"><div class="alert-radio-inner"></div></div>' +
  234. '<div class="alert-radio-label">' +
  235. '{{i.label}}' +
  236. '</div>' +
  237. '</button>' +
  238. '</div>' +
  239. '</ng-template>' +
  240. '<ng-template ngSwitchCase="checkbox">' +
  241. '<div class="alert-checkbox-group">' +
  242. '<button ion-button="alert-checkbox-button" *ngFor="let i of d.inputs" (click)="cbClick(i)" [attr.aria-checked]="i.checked" [attr.id]="i.id" [disabled]="i.disabled" class="alert-tappable alert-checkbox" role="checkbox">' +
  243. '<div class="alert-checkbox-icon"><div class="alert-checkbox-inner"></div></div>' +
  244. '<div class="alert-checkbox-label">' +
  245. '{{i.label}}' +
  246. '</div>' +
  247. '</button>' +
  248. '</div>' +
  249. '</ng-template>' +
  250. '<ng-template ngSwitchDefault>' +
  251. '<div class="alert-input-group">' +
  252. '<div *ngFor="let i of d.inputs" class="alert-input-wrapper">' +
  253. '<input [placeholder]="i.placeholder" [(ngModel)]="i.value" [type]="i.type" dir="auto" [min]="i.min" [max]="i.max" [attr.id]="i.id" class="alert-input">' +
  254. '</div>' +
  255. '</div>' +
  256. '</ng-template>' +
  257. '</div>' +
  258. '<div class="alert-button-group" [ngClass]="{\'alert-button-group-vertical\':d.buttons.length>2}">' +
  259. '<button ion-button="alert-button" *ngFor="let b of d.buttons" (click)="btnClick(b)" [ngClass]="b.cssClass">' +
  260. '{{b.text}}' +
  261. '</button>' +
  262. '</div>' +
  263. '</div>',
  264. host: {
  265. 'role': 'dialog',
  266. '[attr.aria-labelledby]': 'hdrId',
  267. '[attr.aria-describedby]': 'descId'
  268. },
  269. encapsulation: ViewEncapsulation.None,
  270. },] },
  271. ];
  272. /** @nocollapse */
  273. AlertCmp.ctorParameters = function () { return [
  274. { type: ViewController, },
  275. { type: ElementRef, },
  276. { type: Config, },
  277. { type: GestureController, },
  278. { type: NavParams, },
  279. { type: Renderer, },
  280. { type: Platform, },
  281. ]; };
  282. AlertCmp.propDecorators = {
  283. 'keyUp': [{ type: HostListener, args: ['body:keyup', ['$event'],] },],
  284. };
  285. return AlertCmp;
  286. }());
  287. export { AlertCmp };
  288. var alertIds = -1;
  289. //# sourceMappingURL=alert-component.js.map