mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-23 14:01:20 +08:00
Merge branch 'master' of github.com:driftyco/ionic
This commit is contained in:
@ -1,7 +1,7 @@
|
|||||||
import { Component, Renderer, ElementRef, HostListener, ViewEncapsulation } from '@angular/core';
|
import { Component, Renderer, ElementRef, HostListener, ViewEncapsulation } from '@angular/core';
|
||||||
|
|
||||||
import { Config } from '../../config/config';
|
import { Config } from '../../config/config';
|
||||||
import { Form } from '../../util/form';
|
import { focusOutActiveElement } from '../../util/dom';
|
||||||
import { Key } from '../../util/key';
|
import { Key } from '../../util/key';
|
||||||
import { NavParams } from '../../navigation/nav-params';
|
import { NavParams } from '../../navigation/nav-params';
|
||||||
import { ViewController } from '../../navigation/view-controller';
|
import { ViewController } from '../../navigation/view-controller';
|
||||||
@ -58,16 +58,15 @@ export class ActionSheetCmp {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _viewCtrl: ViewController,
|
private _viewCtrl: ViewController,
|
||||||
private _config: Config,
|
config: Config,
|
||||||
private _elementRef: ElementRef,
|
private _elementRef: ElementRef,
|
||||||
private _form: Form,
|
|
||||||
gestureCtrl: GestureController,
|
gestureCtrl: GestureController,
|
||||||
params: NavParams,
|
params: NavParams,
|
||||||
renderer: Renderer
|
renderer: Renderer
|
||||||
) {
|
) {
|
||||||
this.gestureBlocker = gestureCtrl.createBlocker(BLOCK_ALL);
|
this.gestureBlocker = gestureCtrl.createBlocker(BLOCK_ALL);
|
||||||
this.d = params.data;
|
this.d = params.data;
|
||||||
this.mode = _config.get('mode');
|
this.mode = config.get('mode');
|
||||||
renderer.setElementClass(_elementRef.nativeElement, `action-sheet-${this.mode}`, true);
|
renderer.setElementClass(_elementRef.nativeElement, `action-sheet-${this.mode}`, true);
|
||||||
|
|
||||||
if (this.d.cssClass) {
|
if (this.d.cssClass) {
|
||||||
@ -123,7 +122,7 @@ export class ActionSheetCmp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ionViewDidEnter() {
|
ionViewDidEnter() {
|
||||||
this._form.focusOut();
|
focusOutActiveElement();
|
||||||
|
|
||||||
let focusableEle = this._elementRef.nativeElement.querySelector('button');
|
let focusableEle = this._elementRef.nativeElement.querySelector('button');
|
||||||
if (focusableEle) {
|
if (focusableEle) {
|
||||||
@ -142,7 +141,7 @@ export class ActionSheetCmp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
click(button: any, dismissDelay?: number) {
|
click(button: any) {
|
||||||
if (! this.enabled ) {
|
if (! this.enabled ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -158,16 +157,14 @@ export class ActionSheetCmp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (shouldDismiss) {
|
if (shouldDismiss) {
|
||||||
setTimeout(() => {
|
this.dismiss(button.role);
|
||||||
this.dismiss(button.role);
|
|
||||||
}, dismissDelay || this._config.get('pageTransitionDelay'));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bdClick() {
|
bdClick() {
|
||||||
if (this.enabled && this.d.enableBackdropDismiss) {
|
if (this.enabled && this.d.enableBackdropDismiss) {
|
||||||
if (this.d.cancelButton) {
|
if (this.d.cancelButton) {
|
||||||
this.click(this.d.cancelButton, 1);
|
this.click(this.d.cancelButton);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this.dismiss('backdrop');
|
this.dismiss('backdrop');
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
import { Component, ElementRef, HostListener, Renderer, ViewEncapsulation } from '@angular/core';
|
import { Component, ElementRef, HostListener, Renderer, ViewEncapsulation } from '@angular/core';
|
||||||
|
|
||||||
import { Config } from '../../config/config';
|
import { Config } from '../../config/config';
|
||||||
|
import { focusOutActiveElement, NON_TEXT_INPUT_REGEX } from '../../util/dom';
|
||||||
|
import { GestureController, BlockerDelegate, BLOCK_ALL } from '../../gestures/gesture-controller';
|
||||||
import { isPresent, assert } from '../../util/util';
|
import { isPresent, assert } from '../../util/util';
|
||||||
import { Key } from '../../util/key';
|
import { Key } from '../../util/key';
|
||||||
import { NavParams } from '../../navigation/nav-params';
|
import { NavParams } from '../../navigation/nav-params';
|
||||||
|
import { Platform } from '../../platform/platform';
|
||||||
import { ViewController } from '../../navigation/view-controller';
|
import { ViewController } from '../../navigation/view-controller';
|
||||||
import { GestureController, BlockerDelegate, BLOCK_ALL } from '../../gestures/gesture-controller';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@ -91,21 +94,22 @@ export class AlertCmp {
|
|||||||
constructor(
|
constructor(
|
||||||
public _viewCtrl: ViewController,
|
public _viewCtrl: ViewController,
|
||||||
public _elementRef: ElementRef,
|
public _elementRef: ElementRef,
|
||||||
public _config: Config,
|
config: Config,
|
||||||
gestureCtrl: GestureController,
|
gestureCtrl: GestureController,
|
||||||
params: NavParams,
|
params: NavParams,
|
||||||
renderer: Renderer
|
private _renderer: Renderer,
|
||||||
|
private _platform: Platform
|
||||||
) {
|
) {
|
||||||
// gesture blocker is used to disable gestures dynamically
|
// gesture blocker is used to disable gestures dynamically
|
||||||
this.gestureBlocker = gestureCtrl.createBlocker(BLOCK_ALL);
|
this.gestureBlocker = gestureCtrl.createBlocker(BLOCK_ALL);
|
||||||
this.d = params.data;
|
this.d = params.data;
|
||||||
this.mode = _config.get('mode');
|
this.mode = config.get('mode');
|
||||||
renderer.setElementClass(_elementRef.nativeElement, `alert-${this.mode}`, true);
|
_renderer.setElementClass(_elementRef.nativeElement, `alert-${this.mode}`, true);
|
||||||
|
|
||||||
if (this.d.cssClass) {
|
if (this.d.cssClass) {
|
||||||
this.d.cssClass.split(' ').forEach(cssClass => {
|
this.d.cssClass.split(' ').forEach(cssClass => {
|
||||||
// Make sure the class isn't whitespace, otherwise it throws exceptions
|
// Make sure the class isn't whitespace, otherwise it throws exceptions
|
||||||
if (cssClass.trim() !== '') renderer.setElementClass(_elementRef.nativeElement, cssClass, true);
|
if (cssClass.trim() !== '') _renderer.setElementClass(_elementRef.nativeElement, cssClass, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +135,7 @@ export class AlertCmp {
|
|||||||
|
|
||||||
ionViewDidLoad() {
|
ionViewDidLoad() {
|
||||||
// normalize the data
|
// normalize the data
|
||||||
let data = this.d;
|
const data = this.d;
|
||||||
|
|
||||||
data.buttons = data.buttons.map(button => {
|
data.buttons = data.buttons.map(button => {
|
||||||
if (typeof button === 'string') {
|
if (typeof button === 'string') {
|
||||||
@ -149,7 +153,7 @@ export class AlertCmp {
|
|||||||
label: input.label,
|
label: input.label,
|
||||||
checked: !!input.checked,
|
checked: !!input.checked,
|
||||||
disabled: !!input.disabled,
|
disabled: !!input.disabled,
|
||||||
id: 'alert-input-' + this.id + '-' + index,
|
id: `alert-input-${this.id}-${index}`,
|
||||||
handler: isPresent(input.handler) ? input.handler : null,
|
handler: isPresent(input.handler) ? input.handler : null,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -157,7 +161,7 @@ export class AlertCmp {
|
|||||||
|
|
||||||
// An alert can be created with several different inputs. Radios,
|
// An alert can be created with several different inputs. Radios,
|
||||||
// checkboxes and inputs are all accepted, but they cannot be mixed.
|
// checkboxes and inputs are all accepted, but they cannot be mixed.
|
||||||
let inputTypes: any[] = [];
|
const inputTypes: string[] = [];
|
||||||
data.inputs.forEach(input => {
|
data.inputs.forEach(input => {
|
||||||
if (inputTypes.indexOf(input.type) < 0) {
|
if (inputTypes.indexOf(input.type) < 0) {
|
||||||
inputTypes.push(input.type);
|
inputTypes.push(input.type);
|
||||||
@ -165,15 +169,24 @@ export class AlertCmp {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (inputTypes.length > 1 && (inputTypes.indexOf('checkbox') > -1 || inputTypes.indexOf('radio') > -1)) {
|
if (inputTypes.length > 1 && (inputTypes.indexOf('checkbox') > -1 || inputTypes.indexOf('radio') > -1)) {
|
||||||
console.warn('Alert cannot mix input types: ' + (inputTypes.join('/')) + '. Please see alert docs for more info.');
|
console.warn(`Alert cannot mix input types: ${(inputTypes.join('/'))}. Please see alert docs for more info.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.inputType = inputTypes.length ? inputTypes[0] : null;
|
this.inputType = inputTypes.length ? inputTypes[0] : null;
|
||||||
|
|
||||||
let checkedInput = this.d.inputs.find(input => input.checked);
|
const checkedInput = this.d.inputs.find(input => input.checked);
|
||||||
if (checkedInput) {
|
if (checkedInput) {
|
||||||
this.activeId = checkedInput.id;
|
this.activeId = checkedInput.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasTextInput = (this.d.inputs.length && this.d.inputs.some(i => !(NON_TEXT_INPUT_REGEX.test(i.type))));
|
||||||
|
if (hasTextInput && this._platform.is('mobile')) {
|
||||||
|
// this alert has a text input and it's on a mobile device so we should align
|
||||||
|
// the alert up high because we need to leave space for the virtual keboard
|
||||||
|
// this also helps prevent the layout getting all messed up from
|
||||||
|
// the browser trying to scroll the input into a safe area
|
||||||
|
this._renderer.setElementClass(this._elementRef.nativeElement, 'alert-top', true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ionViewWillEnter() {
|
ionViewWillEnter() {
|
||||||
@ -185,12 +198,14 @@ export class AlertCmp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ionViewDidEnter() {
|
ionViewDidEnter() {
|
||||||
let activeElement: any = document.activeElement;
|
// focus out of the active element
|
||||||
if (document.activeElement) {
|
focusOutActiveElement();
|
||||||
activeElement.blur();
|
|
||||||
}
|
|
||||||
|
|
||||||
let focusableEle = this._elementRef.nativeElement.querySelector('input,button');
|
// set focus on the first input or button in the alert
|
||||||
|
// note that this does not always work and bring up the keyboard on
|
||||||
|
// devices since the focus command must come from the user's touch event
|
||||||
|
// and ionViewDidEnter is not in the same callstack as the touch event :(
|
||||||
|
const focusableEle = this._elementRef.nativeElement.querySelector('input,button');
|
||||||
if (focusableEle) {
|
if (focusableEle) {
|
||||||
focusableEle.focus();
|
focusableEle.focus();
|
||||||
}
|
}
|
||||||
@ -206,19 +221,19 @@ export class AlertCmp {
|
|||||||
// this can happen when the button has focus and used the enter
|
// this can happen when the button has focus and used the enter
|
||||||
// key to click the button. However, both the click handler and
|
// key to click the button. However, both the click handler and
|
||||||
// this keyup event will fire, so only allow one of them to go.
|
// this keyup event will fire, so only allow one of them to go.
|
||||||
console.debug('alert, enter button');
|
console.debug(`alert, enter button`);
|
||||||
let button = this.d.buttons[this.d.buttons.length - 1];
|
let button = this.d.buttons[this.d.buttons.length - 1];
|
||||||
this.btnClick(button);
|
this.btnClick(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (ev.keyCode === Key.ESCAPE) {
|
} else if (ev.keyCode === Key.ESCAPE) {
|
||||||
console.debug('alert, escape button');
|
console.debug(`alert, escape button`);
|
||||||
this.bdClick();
|
this.bdClick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
btnClick(button: any, dismissDelay?: number) {
|
btnClick(button: any) {
|
||||||
if (!this.enabled) {
|
if (!this.enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -238,9 +253,8 @@ export class AlertCmp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (shouldDismiss) {
|
if (shouldDismiss) {
|
||||||
setTimeout(() => {
|
this.dismiss(button.role);
|
||||||
this.dismiss(button.role);
|
focusOutActiveElement();
|
||||||
}, dismissDelay || this._config.get('pageTransitionDelay'));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,7 +285,7 @@ export class AlertCmp {
|
|||||||
if (this.enabled && this.d.enableBackdropDismiss) {
|
if (this.enabled && this.d.enableBackdropDismiss) {
|
||||||
let cancelBtn = this.d.buttons.find(b => b.role === 'cancel');
|
let cancelBtn = this.d.buttons.find(b => b.role === 'cancel');
|
||||||
if (cancelBtn) {
|
if (cancelBtn) {
|
||||||
this.btnClick(cancelBtn, 1);
|
this.btnClick(cancelBtn);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this.dismiss('backdrop');
|
this.dismiss('backdrop');
|
||||||
@ -280,14 +294,15 @@ export class AlertCmp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dismiss(role: any): Promise<any> {
|
dismiss(role: any): Promise<any> {
|
||||||
|
focusOutActiveElement();
|
||||||
return this._viewCtrl.dismiss(this.getValues(), role);
|
return this._viewCtrl.dismiss(this.getValues(), role);
|
||||||
}
|
}
|
||||||
|
|
||||||
getValues() {
|
getValues(): any {
|
||||||
if (this.inputType === 'radio') {
|
if (this.inputType === 'radio') {
|
||||||
// this is an alert with radio buttons (single value select)
|
// this is an alert with radio buttons (single value select)
|
||||||
// return the one value which is checked, otherwise undefined
|
// return the one value which is checked, otherwise undefined
|
||||||
let checkedInput = this.d.inputs.find(i => i.checked);
|
const checkedInput = this.d.inputs.find(i => i.checked);
|
||||||
return checkedInput ? checkedInput.value : undefined;
|
return checkedInput ? checkedInput.value : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,7 +314,7 @@ export class AlertCmp {
|
|||||||
|
|
||||||
// this is an alert with text inputs
|
// this is an alert with text inputs
|
||||||
// return an object of all the values with the input name as the key
|
// return an object of all the values with the input name as the key
|
||||||
let values: {[k: string]: string} = {};
|
const values: {[k: string]: string} = {};
|
||||||
this.d.inputs.forEach(i => {
|
this.d.inputs.forEach(i => {
|
||||||
values[i.name] = i.value;
|
values[i.name] = i.value;
|
||||||
});
|
});
|
||||||
|
@ -24,6 +24,12 @@ ion-alert {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ion-alert.alert-top {
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
padding-top: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
ion-alert input {
|
ion-alert input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ import { Content } from '../content/content';
|
|||||||
* items = [];
|
* items = [];
|
||||||
*
|
*
|
||||||
* constructor() {
|
* constructor() {
|
||||||
* for (var i = 0; i < 30; i++) {
|
* for (let i = 0; i < 30; i++) {
|
||||||
* this.items.push( this.items.length );
|
* this.items.push( this.items.length );
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
@ -44,7 +44,7 @@ import { Content } from '../content/content';
|
|||||||
* console.log('Begin async operation');
|
* console.log('Begin async operation');
|
||||||
*
|
*
|
||||||
* setTimeout(() => {
|
* setTimeout(() => {
|
||||||
* for (var i = 0; i < 30; i++) {
|
* for (let i = 0; i < 30; i++) {
|
||||||
* this.items.push( this.items.length );
|
* this.items.push( this.items.length );
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
|
@ -461,14 +461,14 @@ export class PickerCmp {
|
|||||||
constructor(
|
constructor(
|
||||||
private _viewCtrl: ViewController,
|
private _viewCtrl: ViewController,
|
||||||
private _elementRef: ElementRef,
|
private _elementRef: ElementRef,
|
||||||
private _config: Config,
|
config: Config,
|
||||||
gestureCtrl: GestureController,
|
gestureCtrl: GestureController,
|
||||||
params: NavParams,
|
params: NavParams,
|
||||||
renderer: Renderer
|
renderer: Renderer
|
||||||
) {
|
) {
|
||||||
this._gestureBlocker = gestureCtrl.createBlocker(BLOCK_ALL);
|
this._gestureBlocker = gestureCtrl.createBlocker(BLOCK_ALL);
|
||||||
this.d = params.data;
|
this.d = params.data;
|
||||||
this.mode = _config.get('mode');
|
this.mode = config.get('mode');
|
||||||
renderer.setElementClass(_elementRef.nativeElement, `picker-${this.mode}`, true);
|
renderer.setElementClass(_elementRef.nativeElement, `picker-${this.mode}`, true);
|
||||||
|
|
||||||
if (this.d.cssClass) {
|
if (this.d.cssClass) {
|
||||||
@ -579,7 +579,7 @@ export class PickerCmp {
|
|||||||
this.enabled = true;
|
this.enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
btnClick(button: any, dismissDelay?: number) {
|
btnClick(button: any) {
|
||||||
if (!this.enabled) {
|
if (!this.enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -599,9 +599,7 @@ export class PickerCmp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (shouldDismiss) {
|
if (shouldDismiss) {
|
||||||
setTimeout(() => {
|
this.dismiss(button.role);
|
||||||
this.dismiss(button.role);
|
|
||||||
}, dismissDelay || this._config.get('pageTransitionDelay'));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +137,7 @@ import { ViewController } from '../../navigation/view-controller';
|
|||||||
*
|
*
|
||||||
*```ts
|
*```ts
|
||||||
* switchTabs() {
|
* switchTabs() {
|
||||||
* this.navCtrl.parent.switch(2);
|
* this.navCtrl.parent.select(2);
|
||||||
* }
|
* }
|
||||||
*```
|
*```
|
||||||
* @demo /docs/v2/demos/src/tabs/
|
* @demo /docs/v2/demos/src/tabs/
|
||||||
|
@ -108,15 +108,14 @@ import { isObject, isDefined, isFunction, isArray } from '../util/util';
|
|||||||
* | `menuType` | `string` | Type of menu to display. Available options: `"overlay"`, `"reveal"`, `"push"`. |
|
* | `menuType` | `string` | Type of menu to display. Available options: `"overlay"`, `"reveal"`, `"push"`. |
|
||||||
* | `modalEnter` | `string` | The name of the transition to use while a modal is presented. |
|
* | `modalEnter` | `string` | The name of the transition to use while a modal is presented. |
|
||||||
* | `modalLeave` | `string` | The name of the transition to use while a modal is dismiss. |
|
* | `modalLeave` | `string` | The name of the transition to use while a modal is dismiss. |
|
||||||
* | `mode` | `string` | The mode to use throughout the application. |
|
* | `mode` | `string` | The mode to use throughout the application. |
|
||||||
* | `pageTransition` | `string` | The name of the transition to use while changing pages. |
|
* | `pageTransition` | `string` | The name of the transition to use while changing pages. |
|
||||||
* | `pageTransitionDelay` | `number` | The delay in milliseconds before the transition starts while changing pages. |
|
|
||||||
* | `pickerEnter` | `string` | The name of the transition to use while a picker is presented. |
|
* | `pickerEnter` | `string` | The name of the transition to use while a picker is presented. |
|
||||||
* | `pickerLeave` | `string` | The name of the transition to use while a picker is dismissed. |
|
* | `pickerLeave` | `string` | The name of the transition to use while a picker is dismissed. |
|
||||||
* | `popoverEnter` | `string` | The name of the transition to use while a popover is presented. |
|
* | `popoverEnter` | `string` | The name of the transition to use while a popover is presented. |
|
||||||
* | `popoverLeave` | `string` | The name of the transition to use while a popover is dismissed. |
|
* | `popoverLeave` | `string` | The name of the transition to use while a popover is dismissed. |
|
||||||
* | `spinner` | `string` | The default spinner to use when a name is not defined. |
|
* | `spinner` | `string` | The default spinner to use when a name is not defined. |
|
||||||
* | `swipeBackEnabled` | `boolean` | Whether native iOS swipe to go back functionality is enabled.
|
* | `swipeBackEnabled` | `boolean` | Whether native iOS swipe to go back functionality is enabled. |
|
||||||
* | `tabsHighlight` | `boolean` | Whether to show a highlight line under the tab when it is selected. |
|
* | `tabsHighlight` | `boolean` | Whether to show a highlight line under the tab when it is selected. |
|
||||||
* | `tabsLayout` | `string` | The layout to use for all tabs. Available options: `"icon-top"`, `"icon-left"`, `"icon-right"`, `"icon-bottom"`, `"icon-hide"`, `"title-hide"`. |
|
* | `tabsLayout` | `string` | The layout to use for all tabs. Available options: `"icon-top"`, `"icon-left"`, `"icon-right"`, `"icon-bottom"`, `"icon-hide"`, `"title-hide"`. |
|
||||||
* | `tabsPlacement` | `string` | The position of the tabs relative to the content. Available options: `"top"`, `"bottom"` |
|
* | `tabsPlacement` | `string` | The position of the tabs relative to the content. Available options: `"top"`, `"bottom"` |
|
||||||
|
@ -24,7 +24,6 @@ export const MODE_IOS: any = {
|
|||||||
modalLeave: 'modal-slide-out',
|
modalLeave: 'modal-slide-out',
|
||||||
|
|
||||||
pageTransition: 'ios-transition',
|
pageTransition: 'ios-transition',
|
||||||
pageTransitionDelay: 16,
|
|
||||||
|
|
||||||
pickerEnter: 'picker-slide-in',
|
pickerEnter: 'picker-slide-in',
|
||||||
pickerLeave: 'picker-slide-out',
|
pickerLeave: 'picker-slide-out',
|
||||||
@ -68,7 +67,6 @@ export const MODE_MD: any = {
|
|||||||
modalLeave: 'modal-md-slide-out',
|
modalLeave: 'modal-md-slide-out',
|
||||||
|
|
||||||
pageTransition: 'md-transition',
|
pageTransition: 'md-transition',
|
||||||
pageTransitionDelay: 64,
|
|
||||||
|
|
||||||
pickerEnter: 'picker-slide-in',
|
pickerEnter: 'picker-slide-in',
|
||||||
pickerLeave: 'picker-slide-out',
|
pickerLeave: 'picker-slide-out',
|
||||||
@ -112,7 +110,6 @@ export const MODE_WP: any = {
|
|||||||
modalLeave: 'modal-md-slide-out',
|
modalLeave: 'modal-md-slide-out',
|
||||||
|
|
||||||
pageTransition: 'wp-transition',
|
pageTransition: 'wp-transition',
|
||||||
pageTransitionDelay: 96,
|
|
||||||
|
|
||||||
pickerEnter: 'picker-slide-in',
|
pickerEnter: 'picker-slide-in',
|
||||||
pickerLeave: 'picker-slide-out',
|
pickerLeave: 'picker-slide-out',
|
||||||
|
@ -278,7 +278,7 @@ export function updateDate(existingData: DateTimeData, newData: any) {
|
|||||||
|
|
||||||
|
|
||||||
export function parseTemplate(template: string): string[] {
|
export function parseTemplate(template: string): string[] {
|
||||||
let formats: string[] = [];
|
const formats: string[] = [];
|
||||||
|
|
||||||
template = template.replace(/[^\w\s]/gi, ' ');
|
template = template.replace(/[^\w\s]/gi, ' ');
|
||||||
|
|
||||||
@ -288,17 +288,19 @@ export function parseTemplate(template: string): string[] {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let words = template.split(' ').filter(w => w.length > 0);
|
const words = template.split(' ').filter(w => w.length > 0);
|
||||||
words.forEach((word, i) => {
|
words.forEach((word, i) => {
|
||||||
FORMAT_KEYS.forEach(format => {
|
FORMAT_KEYS.forEach(format => {
|
||||||
if (word === format.f) {
|
if (word === format.f) {
|
||||||
if (word === FORMAT_A || word === FORMAT_a) {
|
if (word === FORMAT_A || word === FORMAT_a) {
|
||||||
// this format is an am/pm format, so it's an "a" or "A"
|
// this format is an am/pm format, so it's an "a" or "A"
|
||||||
|
console.log(`word: ${word}, words[i - 1]: ${words[i - 1]}`);
|
||||||
|
|
||||||
if ((formats.indexOf(FORMAT_h) < 0 && formats.indexOf(FORMAT_hh) < 0) ||
|
if ((formats.indexOf(FORMAT_h) < 0 && formats.indexOf(FORMAT_hh) < 0) ||
|
||||||
(words[i - 1] !== FORMAT_m && words[i - 1] !== FORMAT_mm)) {
|
VALID_AMPM_PREFIX.indexOf(words[i - 1]) === -1) {
|
||||||
// template does not already have a 12-hour format
|
// template does not already have a 12-hour format
|
||||||
// or this am/pm format doesn't have a minute format immediately before it
|
// or this am/pm format doesn't have a hour, minute, or second format immediately before it
|
||||||
// so do not treat this word "a" or "A" as an am/pm format
|
// so do not treat this word "a" or "A" as the am/pm format
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -515,3 +517,7 @@ const MONTH_SHORT_NAMES = [
|
|||||||
'Nov',
|
'Nov',
|
||||||
'Dec',
|
'Dec',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const VALID_AMPM_PREFIX = [
|
||||||
|
FORMAT_hh, FORMAT_h, FORMAT_mm, FORMAT_m, FORMAT_ss, FORMAT_s
|
||||||
|
];
|
||||||
|
@ -251,9 +251,11 @@ export function isTextInput(ele: any) {
|
|||||||
return !!ele &&
|
return !!ele &&
|
||||||
(ele.tagName === 'TEXTAREA' ||
|
(ele.tagName === 'TEXTAREA' ||
|
||||||
ele.contentEditable === 'true' ||
|
ele.contentEditable === 'true' ||
|
||||||
(ele.tagName === 'INPUT' && !(/^(radio|checkbox|range|file|submit|reset|color|image|button)$/i).test(ele.type)));
|
(ele.tagName === 'INPUT' && !(NON_TEXT_INPUT_REGEX.test(ele.type))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const NON_TEXT_INPUT_REGEX = /^(radio|checkbox|range|file|submit|reset|color|image|button)$/i;
|
||||||
|
|
||||||
export function hasFocusedTextInput() {
|
export function hasFocusedTextInput() {
|
||||||
const ele = <HTMLElement>document.activeElement;
|
const ele = <HTMLElement>document.activeElement;
|
||||||
if (isTextInput(ele)) {
|
if (isTextInput(ele)) {
|
||||||
@ -262,6 +264,11 @@ export function hasFocusedTextInput() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function focusOutActiveElement() {
|
||||||
|
const activeElement = <HTMLElement>document.activeElement;
|
||||||
|
activeElement && activeElement.blur && activeElement.blur();
|
||||||
|
}
|
||||||
|
|
||||||
const skipInputAttrsReg = /^(value|checked|disabled|type|class|style|id|autofocus|autocomplete|autocorrect)$/i;
|
const skipInputAttrsReg = /^(value|checked|disabled|type|class|style|id|autofocus|autocomplete|autocorrect)$/i;
|
||||||
export function copyInputAttributes(srcElement: HTMLElement, destElement: HTMLElement) {
|
export function copyInputAttributes(srcElement: HTMLElement, destElement: HTMLElement) {
|
||||||
// copy attributes from one element to another
|
// copy attributes from one element to another
|
||||||
|
@ -24,11 +24,6 @@ export class Form {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
focusOut() {
|
|
||||||
const activeElement = <HTMLElement>document.activeElement;
|
|
||||||
activeElement && activeElement.blur && activeElement.blur();
|
|
||||||
}
|
|
||||||
|
|
||||||
setAsFocused(input: any) {
|
setAsFocused(input: any) {
|
||||||
this._focused = input;
|
this._focused = input;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { Injectable, NgZone } from '@angular/core';
|
import { Injectable, NgZone } from '@angular/core';
|
||||||
|
|
||||||
import { Config } from '../config/config';
|
import { Config } from '../config/config';
|
||||||
import { Form } from './form';
|
import { focusOutActiveElement, hasFocusedTextInput, nativeRaf, nativeTimeout, zoneRafFrames } from './dom';
|
||||||
import { hasFocusedTextInput, nativeRaf, nativeTimeout, zoneRafFrames } from './dom';
|
|
||||||
import { Key } from './key';
|
import { Key } from './key';
|
||||||
|
|
||||||
|
|
||||||
@ -24,7 +23,7 @@ import { Key } from './key';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class Keyboard {
|
export class Keyboard {
|
||||||
|
|
||||||
constructor(config: Config, private _form: Form, private _zone: NgZone) {
|
constructor(config: Config, private _zone: NgZone) {
|
||||||
_zone.runOutsideAngular(() => {
|
_zone.runOutsideAngular(() => {
|
||||||
this.focusOutline(config.get('focusOutline'), document);
|
this.focusOutline(config.get('focusOutline'), document);
|
||||||
|
|
||||||
@ -33,7 +32,7 @@ export class Keyboard {
|
|||||||
// useful when the virtual keyboard is closed natively
|
// useful when the virtual keyboard is closed natively
|
||||||
// https://github.com/driftyco/ionic-plugin-keyboard
|
// https://github.com/driftyco/ionic-plugin-keyboard
|
||||||
if (hasFocusedTextInput()) {
|
if (hasFocusedTextInput()) {
|
||||||
this._form.focusOut();
|
focusOutActiveElement();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -117,7 +116,7 @@ export class Keyboard {
|
|||||||
nativeRaf(() => {
|
nativeRaf(() => {
|
||||||
if (hasFocusedTextInput()) {
|
if (hasFocusedTextInput()) {
|
||||||
// only focus out when a text input has focus
|
// only focus out when a text input has focus
|
||||||
this._form.focusOut();
|
focusOutActiveElement();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import { App } from '../components/app/app';
|
|||||||
import { IonicApp } from '../components/app/app-root';
|
import { IonicApp } from '../components/app/app-root';
|
||||||
import { Config } from '../config/config';
|
import { Config } from '../config/config';
|
||||||
import { DeepLinker } from '../navigation/deep-linker';
|
import { DeepLinker } from '../navigation/deep-linker';
|
||||||
import { Form } from './form';
|
|
||||||
import { GestureController } from '../gestures/gesture-controller';
|
import { GestureController } from '../gestures/gesture-controller';
|
||||||
import { Keyboard } from './keyboard';
|
import { Keyboard } from './keyboard';
|
||||||
import { Menu } from '../components/menu/menu';
|
import { Menu } from '../components/menu/menu';
|
||||||
@ -231,11 +230,9 @@ export const mockNavController = function(): NavControllerBase {
|
|||||||
|
|
||||||
let app = mockApp(config, platform);
|
let app = mockApp(config, platform);
|
||||||
|
|
||||||
let form = new Form();
|
|
||||||
|
|
||||||
let zone = mockZone();
|
let zone = mockZone();
|
||||||
|
|
||||||
let keyboard = new Keyboard(config, form, zone);
|
let keyboard = new Keyboard(config, zone);
|
||||||
|
|
||||||
let elementRef = mockElementRef();
|
let elementRef = mockElementRef();
|
||||||
|
|
||||||
@ -281,11 +278,9 @@ export const mockNavController = function(): NavControllerBase {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const mockOverlayPortal = function(app: App, config: Config, platform: Platform): OverlayPortal {
|
export const mockOverlayPortal = function(app: App, config: Config, platform: Platform): OverlayPortal {
|
||||||
let form = new Form();
|
|
||||||
|
|
||||||
let zone = mockZone();
|
let zone = mockZone();
|
||||||
|
|
||||||
let keyboard = new Keyboard(config, form, zone);
|
let keyboard = new Keyboard(config, zone);
|
||||||
|
|
||||||
let elementRef = mockElementRef();
|
let elementRef = mockElementRef();
|
||||||
|
|
||||||
@ -323,11 +318,9 @@ export const mockTab = function(parentTabs: Tabs): Tab {
|
|||||||
|
|
||||||
let app = (<any>parentTabs)._app || mockApp(config, platform);
|
let app = (<any>parentTabs)._app || mockApp(config, platform);
|
||||||
|
|
||||||
let form = new Form();
|
|
||||||
|
|
||||||
let zone = mockZone();
|
let zone = mockZone();
|
||||||
|
|
||||||
let keyboard = new Keyboard(config, form, zone);
|
let keyboard = new Keyboard(config, zone);
|
||||||
|
|
||||||
let elementRef = mockElementRef();
|
let elementRef = mockElementRef();
|
||||||
|
|
||||||
|
@ -321,6 +321,13 @@ describe('parseTemplate', () => {
|
|||||||
expect(formats[2]).toEqual('a');
|
expect(formats[2]).toEqual('a');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should allow am/pm when using only 12-hour', () => {
|
||||||
|
var formats = datetime.parseTemplate('hh a');
|
||||||
|
expect(formats.length).toEqual(2);
|
||||||
|
expect(formats[0]).toEqual('hh');
|
||||||
|
expect(formats[1]).toEqual('a');
|
||||||
|
});
|
||||||
|
|
||||||
it('should allow am/pm when using 12-hour', () => {
|
it('should allow am/pm when using 12-hour', () => {
|
||||||
var formats = datetime.parseTemplate('hh:mm a');
|
var formats = datetime.parseTemplate('hh:mm a');
|
||||||
expect(formats.length).toEqual(3);
|
expect(formats.length).toEqual(3);
|
||||||
@ -329,7 +336,7 @@ describe('parseTemplate', () => {
|
|||||||
expect(formats[2]).toEqual('a');
|
expect(formats[2]).toEqual('a');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not add am/pm when not using 24-hour', () => {
|
it('should not add am/pm when using 24-hour', () => {
|
||||||
var formats = datetime.parseTemplate('HH:mm a');
|
var formats = datetime.parseTemplate('HH:mm a');
|
||||||
expect(formats.length).toEqual(2);
|
expect(formats.length).toEqual(2);
|
||||||
expect(formats[0]).toEqual('HH');
|
expect(formats[0]).toEqual('HH');
|
||||||
|
Reference in New Issue
Block a user