diff --git a/CHANGELOG.md b/CHANGELOG.md index 56025ef19e..9db9661b12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,71 @@ + +# [2.2.0](https://github.com/driftyco/ionic/compare/v2.1.0...v2.2.0) (2017-03-08) + +### Updating to 2.2.0 + +1. Update your `package.json` to match the following dependencies, remove the existing `node_modules` directory, and then run `npm install`: + + ``` + "dependencies": { + "@angular/common": "2.4.8", + "@angular/compiler": "2.4.8", + "@angular/compiler-cli": "2.4.8", + "@angular/core": "2.4.8", + "@angular/forms": "2.4.8", + "@angular/http": "2.4.8", + "@angular/platform-browser": "2.4.8", + "@angular/platform-browser-dynamic": "2.4.8", + "@angular/platform-server": "2.4.8", + "@ionic/storage": "2.0.0", + "ionic-angular": "2.2.0", + "ionic-native": "2.4.1", + "ionicons": "3.0.0", + "rxjs": "5.0.1", + "sw-toolbox": "3.4.0", + "zone.js": "0.7.2" + }, + "devDependencies": { + "@ionic/app-scripts": "1.1.4", + "typescript": "2.0.9" + } + ``` + Note: If you are using `ionic-storage`, you need to update it to `2.0.0` or you will run into an error similar to this: `Error: Can't resolve all parameters for Storage: (?, ?).`. For more information, see the [Storage Documentation](https://ionicframework.com/docs/v2/storage/). + +### What's new + +#### Split Pane +As part of our initiative to improve desktop support we have introduced a new component called [Split Pane](http://ionicframework.com/docs/v2/api/components/split-pane/SplitPane/). Split Pane makes it possible to easily create multi-view layouts. It allows elements, such as a menu or another navigation pane, to be displayed on large viewports. Split Pane can be used to achieve a layout similar to the Gmail (Android) or Mail (Apple) applications. + +#### Angular 2.4.8 +Ionic has been updated to depend on Angular 2.4.8, which is the latest version that we have tested and confirmed to be compatible with Ionic. This means that updating to the 2.2.0 release of Ionic will automatically work with all of the performance updates, bug fixes and features in Angular 2.4.8! + +### Ionic Storage + +We recently released the 2.0.0 version of `ionic-storage`. If you are using Ionic Storage in your application, you need to update to this version of `ionic-storage`. Attempting to use an older version of `ionic-storage` with Ionic 2.2.0 will cause errors. You can read about how to update to `ionic-storage` 2.0.0 [here](https://github.com/driftyco/ionic-storage/releases/tag/v2.0.0). + + +### Bug Fixes + +* **components:** clean up event listeners to stop memory leaks ([8d9f374](https://github.com/driftyco/ionic/commit/8d9f374)), closes [#10459](https://github.com/driftyco/ionic/issues/10459) [#10416](https://github.com/driftyco/ionic/issues/10416) [#10286](https://github.com/driftyco/ionic/issues/10286) +* **infinite-scroll:** use icon color from Sass var and add var for text color ([7b97fb7](https://github.com/driftyco/ionic/commit/7b97fb7)), closes [#10574](https://github.com/driftyco/ionic/issues/10574) +* **menu:** disable the menus when they should be ([dc53c8e](https://github.com/driftyco/ionic/commit/dc53c8e)) +* **menu:** don't hide menuToggle outside navbar ([e56bad9](https://github.com/driftyco/ionic/commit/e56bad9)) +* **radio:** calculate radio-inner width/height with border width ([#10495](https://github.com/driftyco/ionic/issues/10495)) ([176aa23](https://github.com/driftyco/ionic/commit/176aa23)) +* **refresher:** don't destroy events manager ([9308694](https://github.com/driftyco/ionic/commit/9308694)), ([1dd8883](https://github.com/driftyco/ionic/commit/1dd8883)), closes [#10652](https://github.com/driftyco/ionic/issues/10652) +* **refresher:** use refresher icon color from Sass var ([116ae38](https://github.com/driftyco/ionic/commit/116ae38)), closes [#10479](https://github.com/driftyco/ionic/issues/10479) +* **tabs:** emit ionChange after the tab is selected ([ac1a886](https://github.com/driftyco/ionic/commit/ac1a886)), closes [#10538](https://github.com/driftyco/ionic/issues/10538) +* **tabs:** catch the rejected promise with popToRoot ([7385158](https://github.com/driftyco/ionic/commit/7385158)) +* **view-controller:** set navigation so dimiss() will work synchronously. ([61a5317](https://github.com/driftyco/ionic/commit/61a5317)), closes [#10654](https://github.com/driftyco/ionic/issues/10654) + + +### Features + +* **alert:** add ability to set the mode on alert ([f577e54](https://github.com/driftyco/ionic/commit/f577e54)) +* **split-pane:** split pane support for ion-nav and ion-menu ([#10343](https://github.com/driftyco/ionic/issues/10343)) ([9e4c3a6](https://github.com/driftyco/ionic/commit/9e4c3a6)) +* **typography:** add text-wrap attribute for all elements ([2c2b87b](https://github.com/driftyco/ionic/commit/2c2b87b)), closes [#7051](https://github.com/driftyco/ionic/issues/7051) + + + # [2.1.0](https://github.com/driftyco/ionic/compare/v2.0.1...v2.1.0) (2017-02-23) diff --git a/package.json b/package.json index 63a5cf27da..f0a0940e80 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "ionic2", - "version": "2.1.0", + "version": "2.2.0", "description": "A powerful framework for building mobile and progressive web apps with JavaScript and Angular 2", "keywords": [ "ionic", diff --git a/scripts/npm/README.md b/scripts/npm/README.md index 3310457539..36e6202a03 100644 --- a/scripts/npm/README.md +++ b/scripts/npm/README.md @@ -1,10 +1,10 @@ -## Ionic Framework 2.x +## Ionic Framework -The official npm package for [Ionic 2](http://ionicframework.com/), complete with pre-built ES5 bundles, TypeScript definitions, Sass files, CommonJS ES5 files, and more. +The official npm package for [Ionic](http://ionicframework.com/), complete with pre-built ES5 bundles, TypeScript definitions, Sass files, CommonJS ES5 files, and more. -To get started with Ionic 2, please read the [Installation Guide](http://ionicframework.com/docs/v2/getting-started/installation/). +To get started with Ionic, please read the [Installation Guide](http://ionicframework.com/docs/v2/intro/installation/). -[Ionic 2 Documentation](http://ionicframework.com/docs/v2/) +[Ionic Documentation](http://ionicframework.com/docs/) ### Source files @@ -22,7 +22,6 @@ Minified and unminified CommonJS and System.register module format bundles, as w ### Installation and More -To use Ionic 2, we recommend installing and utilizing the [Ionic CLI](http://ionicframework.com/docs/v2/getting-started/installation/) which will help you create pre-configured Ionic apps. - -For full instructions on using Ionic 2, please visit the [Ionic 2 Documentation](http://ionicframework.com/docs/v2/) +To use Ionic, we recommend installing and utilizing the [Ionic CLI](http://ionicframework.com/docs/v2/intro/installation/) which will help you create pre-configured Ionic apps. +For full instructions on using Ionic, please visit the [Ionic Documentation](http://ionicframework.com/docs/) diff --git a/src/components/action-sheet/action-sheet.ts b/src/components/action-sheet/action-sheet.ts index f9ef7e7c5f..39a0ccf2a5 100644 --- a/src/components/action-sheet/action-sheet.ts +++ b/src/components/action-sheet/action-sheet.ts @@ -33,7 +33,7 @@ export class ActionSheet extends ViewController { /** * @hidden */ - getTransitionName(direction: string) { + getTransitionName(direction: string): string { let key = 'actionSheet' + (direction === 'back' ? 'Leave' : 'Enter'); return this._nav && this._nav.config.get(key); } @@ -41,22 +41,25 @@ export class ActionSheet extends ViewController { /** * @param {string} title Action sheet title */ - setTitle(title: string) { + setTitle(title: string): ActionSheet { this.data.title = title; + return this; } /** * @param {string} subTitle Action sheet subtitle */ - setSubTitle(subTitle: string) { + setSubTitle(subTitle: string): ActionSheet { this.data.subTitle = subTitle; + return this; } /** * @param {object} button Action sheet button */ - addButton(button: any) { + addButton(button: any): ActionSheet { this.data.buttons.push(button); + return this; } /** @@ -65,7 +68,7 @@ export class ActionSheet extends ViewController { * @param {NavOptions} [opts={}] Nav options to go with this transition. * @returns {Promise} Returns a promise which is resolved when the transition has completed. */ - present(navOptions: NavOptions = {}) { + present(navOptions: NavOptions = {}): Promise { navOptions.minClickBlockDuration = navOptions.minClickBlockDuration || 400; return this._app.present(this, navOptions); } diff --git a/src/components/action-sheet/test/basic/pages/page-one/page-one.ts b/src/components/action-sheet/test/basic/pages/page-one/page-one.ts index 012bb7c6e1..3ec51bad28 100644 --- a/src/components/action-sheet/test/basic/pages/page-one/page-one.ts +++ b/src/components/action-sheet/test/basic/pages/page-one/page-one.ts @@ -1,6 +1,8 @@ import { Component } from '@angular/core'; import { ActionSheetController, AlertController, IonicPage, ModalController, Platform } from '../../../../../..'; +import { ModalPage } from '../modal-page/modal-page'; + @IonicPage() @Component({ templateUrl: 'page-one.html' @@ -13,59 +15,55 @@ export class PageOne { presentActionSheet1() { this.result = ''; - let actionSheet = this.actionSheetCtrl.create({ - title: 'Albums', - buttons: [ - { - text: 'Delete', - role: 'destructive', - icon: 'trash', - handler: () => { - console.log('Delete clicked'); - this.result = 'Deleted'; - } - }, - { - text: 'Share', - icon: 'share', - handler: () => { - console.log('Share clicked'); - this.result = 'Shared'; - } - }, - { - text: 'Play (open modal)', - icon: 'arrow-dropright-circle', - handler: () => { - this.result = 'Play (open modal)'; - let modal = this.modalCtrl.create('modal-page'); - modal.present(); - - // returning false does not allow the actionsheet to be closed - return false; - } - }, - { - text: 'Favorite', - icon: !this.plt.is('ios') ? 'heart' : null, - handler: () => { - console.log('Favorite clicked'); - this.result = 'Favorited'; - } - }, - { - text: 'Cancel', - role: 'cancel', // will always sort to be on the bottom - icon: !this.plt.is('ios') ? 'close' : null, - handler: () => { - console.log('Cancel clicked'); - this.result = 'Canceled'; - } + this.actionSheetCtrl.create() + .setTitle('Albums') + .addButton({ + text: 'Delete', + role: 'destructive', + icon: 'trash', + handler: () => { + console.log('Delete clicked'); + this.result = 'Deleted'; } - ] - }); + }) + .addButton({ + text: 'Share', + icon: 'share', + handler: () => { + console.log('Share clicked'); + this.result = 'Shared'; + } + }) + .addButton({ + text: 'Play (open modal)', + icon: 'arrow-dropright-circle', + handler: () => { + this.result = 'Play (open modal)'; + let modal = this.modalCtrl.create(ModalPage); + modal.present(); - actionSheet.present(); + // returning false does not allow the actionsheet to be closed + return false; + } + }) + .addButton({ + text: 'Favorite', + icon: !this.plt.is('ios') ? 'heart' : null, + handler: () => { + console.log('Favorite clicked'); + this.result = 'Favorited'; + } + }) + .addButton({ + text: 'Cancel', + role: 'cancel', // will always sort to be on the bottom + icon: !this.plt.is('ios') ? 'close' : null, + handler: () => { + console.log('Cancel clicked'); + this.result = 'Canceled'; + } + }) + .present(); } presentActionSheet2() { diff --git a/src/components/alert/alert-component.ts b/src/components/alert/alert-component.ts index 1cdd6c9bba..1d70af5a91 100644 --- a/src/components/alert/alert-component.ts +++ b/src/components/alert/alert-component.ts @@ -9,6 +9,7 @@ import { NavParams } from '../../navigation/nav-params'; import { NavOptions } from '../../navigation/nav-util'; import { Platform } from '../../platform/platform'; import { ViewController } from '../../navigation/view-controller'; +import { AlertInputOptions, AlertOptions, AlertButton } from './alert-options'; /** @@ -39,7 +40,7 @@ import { ViewController } from '../../navigation/view-controller'; '' + '
' + - '' }) @@ -63,7 +79,8 @@ export class AppComponent { declarations: [ AppComponent, E2EPage1, - E2EPage2 + E2EPage2, + MyContent ], imports: [ BrowserModule, @@ -73,7 +90,8 @@ export class AppComponent { entryComponents: [ AppComponent, E2EPage1, - E2EPage2 + E2EPage2, + MyContent ] }) export class AppModule {} diff --git a/src/components/infinite-scroll/test/basic/main.html b/src/components/infinite-scroll/test/basic/main.html index c287b7a60d..264b8552f4 100644 --- a/src/components/infinite-scroll/test/basic/main.html +++ b/src/components/infinite-scroll/test/basic/main.html @@ -1,33 +1,21 @@ - - - Infinite Scroll - +

+ InfiniteScroll is enabled: {{enabled}} +

-
+ - - - -

- InfiniteScroll is enabled: {{enabled}} -

- - + - - - - - - - - - -
+ + + + diff --git a/src/components/infinite-scroll/test/infinite-scroll.spec.ts b/src/components/infinite-scroll/test/infinite-scroll.spec.ts index ccf4ca299a..4eb4bb49e8 100644 --- a/src/components/infinite-scroll/test/infinite-scroll.spec.ts +++ b/src/components/infinite-scroll/test/infinite-scroll.spec.ts @@ -1,7 +1,7 @@ import { Content, ScrollEvent } from '../../content/content'; import { DomController } from '../../../platform/dom-controller'; import { InfiniteScroll } from '../infinite-scroll'; -import { mockConfig, mockDomController, mockElementRef, mockPlatform, mockRenderer, mockZone } from '../../../util/mock-providers'; +import { mockConfig, mockDomController, mockElementRef, mockElementRefEle, mockPlatform, mockRenderer, mockZone } from '../../../util/mock-providers'; describe('Infinite Scroll', () => { @@ -91,6 +91,29 @@ describe('Infinite Scroll', () => { }); + describe('position', () => { + + it('should default to bottom', () => { + expect(inf._position).toEqual('bottom'); + }); + + it('should set to top', () => { + inf.position = 'top'; + expect(inf._position).toEqual('top'); + }); + + it('should set to bottom', () => { + inf.position = 'bottom'; + expect(inf._position).toEqual('bottom'); + }); + + it('should not set to anything else', () => { + inf.position = 'derp'; + expect(inf._position).toEqual('bottom'); + }); + + }); + let config = mockConfig(); let inf: InfiniteScroll; @@ -104,8 +127,9 @@ describe('Infinite Scroll', () => { contentElementRef = mockElementRef(); dom = mockDomController(); content = new Content(config, mockPlatform(), dom, contentElementRef, mockRenderer(), null, null, mockZone(), null, null); - content._scrollEle = document.createElement('div'); - content._scrollEle.className = 'scroll-content'; + let ele = document.createElement('div'); + ele.className = 'scroll-content'; + content._scrollContent = mockElementRefEle(ele); infiniteElementRef = mockElementRef(); diff --git a/src/components/infinite-scroll/test/position-top/app/app.component.ts b/src/components/infinite-scroll/test/position-top/app/app.component.ts new file mode 100644 index 0000000000..dcf2f9e7e7 --- /dev/null +++ b/src/components/infinite-scroll/test/position-top/app/app.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +import { RootPage } from '../pages/root-page/root-page'; + +@Component({ + template: '' +}) +export class AppComponent { + root = RootPage; +} diff --git a/src/components/infinite-scroll/test/position-top/app/app.module.ts b/src/components/infinite-scroll/test/position-top/app/app.module.ts new file mode 100644 index 0000000000..d4f0bb7adf --- /dev/null +++ b/src/components/infinite-scroll/test/position-top/app/app.module.ts @@ -0,0 +1,25 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { IonicApp, IonicModule } from '../../../../..'; + +import { AppComponent } from './app.component'; +import { RootPage } from '../pages/root-page/root-page'; +import { PageTwo } from '../pages/page-two/page-two'; + +@NgModule({ + declarations: [ + AppComponent, + RootPage, + PageTwo + ], + imports: [ + BrowserModule, + IonicModule.forRoot(AppComponent) + ], + bootstrap: [IonicApp], + entryComponents: [ + RootPage, + PageTwo + ] +}) +export class AppModule {} diff --git a/src/components/infinite-scroll/test/position-top/app/main.ts b/src/components/infinite-scroll/test/position-top/app/main.ts new file mode 100644 index 0000000000..6af7a5b2ae --- /dev/null +++ b/src/components/infinite-scroll/test/position-top/app/main.ts @@ -0,0 +1,5 @@ +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app.module'; + +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/src/components/infinite-scroll/test/position-top/pages/page-two/page-two.html b/src/components/infinite-scroll/test/position-top/pages/page-two/page-two.html new file mode 100644 index 0000000000..17123ccf50 --- /dev/null +++ b/src/components/infinite-scroll/test/position-top/pages/page-two/page-two.html @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/components/infinite-scroll/test/position-top/pages/page-two/page-two.ts b/src/components/infinite-scroll/test/position-top/pages/page-two/page-two.ts new file mode 100644 index 0000000000..1b1ea0c5a1 --- /dev/null +++ b/src/components/infinite-scroll/test/position-top/pages/page-two/page-two.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; +import { NavController } from '../../../../../..'; + + +@Component({ + templateUrl: 'page-two.html' +}) +export class PageTwo { + constructor(public navCtrl: NavController) {} +} diff --git a/src/components/infinite-scroll/test/position-top/pages/root-page/root-page.html b/src/components/infinite-scroll/test/position-top/pages/root-page/root-page.html new file mode 100644 index 0000000000..d8c27c8acb --- /dev/null +++ b/src/components/infinite-scroll/test/position-top/pages/root-page/root-page.html @@ -0,0 +1,31 @@ + + + + Infinite Scroll + + + + + + + + + + + + + + + + +

+ InfiniteScroll is enabled: {{enabled}} +

+ + + +
diff --git a/src/components/infinite-scroll/test/position-top/pages/root-page/root-page.ts b/src/components/infinite-scroll/test/position-top/pages/root-page/root-page.ts new file mode 100644 index 0000000000..3716a2fd88 --- /dev/null +++ b/src/components/infinite-scroll/test/position-top/pages/root-page/root-page.ts @@ -0,0 +1,62 @@ +import { Component, ViewChild } from '@angular/core'; +import { Content, InfiniteScroll, NavController } from '../../../../../..'; + +import { PageTwo } from '../page-two/page-two'; + +@Component({ + templateUrl: 'root-page.html' +}) +export class RootPage { + @ViewChild(InfiniteScroll) infiniteScroll: InfiniteScroll; + @ViewChild(Content) content: Content; + items: number[] = []; + enabled: boolean = true; + + constructor(public navCtrl: NavController) { + for (var i = 0; i < 30; i++) { + this.items.unshift( this.items.length ); + } + } + + doInfinite(infiniteScroll: InfiniteScroll) { + console.log('Begin async operation'); + + getAsyncData().then(newData => { + for (var i = 0; i < newData.length; i++) { + this.items.unshift( this.items.length ); + } + + console.log('Finished receiving data, async operation complete'); + infiniteScroll.complete(); + + if (this.items.length > 90) { + this.enabled = false; + } + }); + } + + goToPage2() { + this.navCtrl.push(PageTwo); + } + + toggleInfiniteScroll() { + this.enabled = !this.enabled; + } +} + + +function getAsyncData(): Promise { + // async return mock data + return new Promise(resolve => { + + setTimeout(() => { + let data: number[] = []; + for (var i = 0; i < 30; i++) { + data.unshift(i); + } + + resolve(data); + }, 2000); + + }); +} diff --git a/src/components/label/label.scss b/src/components/label/label.scss index f81fa30dcb..6f3f7ebf8b 100644 --- a/src/components/label/label.scss +++ b/src/components/label/label.scss @@ -65,7 +65,3 @@ ion-label[floating] { max-width: 100%; } - -.item-select ion-label[floating] { - transform: translate3d(0, 0, 0) scale(.8); -} diff --git a/src/components/loading/loading.ts b/src/components/loading/loading.ts index 790509cc60..80eafa4fdd 100644 --- a/src/components/loading/loading.ts +++ b/src/components/loading/loading.ts @@ -33,16 +33,49 @@ export class Loading extends ViewController { /** * @hidden */ - getTransitionName(direction: string) { + getTransitionName(direction: string): string { let key = (direction === 'back' ? 'loadingLeave' : 'loadingEnter'); return this._nav && this._nav.config.get(key); } /** - * @param {string} content loading message content + * @param {string} sets the html content for the loading indicator. */ - setContent(content: string) { + setContent(content: string): Loading { this.data.content = content; + return this; + } + + /** + * @param {string} sets the name of the SVG spinner for the loading indicator. + */ + setSpinner(spinner: string): Loading { + this.data.spinner = spinner; + return this; + } + + /** + * @param {string} sets additional classes for custom styles, separated by spaces. + */ + setCssClass(cssClass: string): Loading { + this.data.cssClass = cssClass; + return this; + } + + /** + * @param {string} sets whether to show the backdrop. + */ + setShowBackdrop(showBackdrop: boolean): Loading { + this.data.showBackdrop = showBackdrop; + return this; + } + + /** + * @param {string} how many milliseconds to wait before hiding the indicator. + */ + setDuration(dur: number): Loading { + this.data.duration = dur; + return this; } /** @@ -51,7 +84,7 @@ export class Loading extends ViewController { * @param {NavOptions} [opts={}] Nav options to go with this transition. * @returns {Promise} Returns a promise which is resolved when the transition has completed. */ - present(navOptions: NavOptions = {}) { + present(navOptions: NavOptions = {}): Promise { return this._app.present(this, navOptions, PORTAL_LOADING); } diff --git a/src/components/loading/test/basic/pages/page-one/page-one.ts b/src/components/loading/test/basic/pages/page-one/page-one.ts index 59aaee041d..f32e899150 100644 --- a/src/components/loading/test/basic/pages/page-one/page-one.ts +++ b/src/components/loading/test/basic/pages/page-one/page-one.ts @@ -130,13 +130,11 @@ export class PageOne { } presentLoadingCrescent() { - let loading = this.loadingCtrl.create({ - spinner: 'crescent', - content: 'Please wait...', - duration: 1000 - }); - - loading.present(); + this.loadingCtrl.create() + .setSpinner('crescent') + .setContent('Please wait...') + .setDuration(1000) + .present(); } // Pass the fixed-spinner class so we can turn off @@ -210,10 +208,9 @@ export class PageOne { loading2.present(); }, 1000); - let loading3 = this.loadingCtrl.create({ - spinner: 'hide', - content: 'Loading 3 Please Wait...' - }); + let loading3 = this.loadingCtrl.create() + .setSpinner('hide') + .setContent('Loading 3 Please Wait...'); setTimeout(() => { @@ -259,7 +256,6 @@ export class PageOne { } presentLoadingOpenDismiss() { - // debugger; const loading = this.loadingCtrl.create({ content: 'Loading 1' }); diff --git a/src/components/modal/test/basic/pages/modal-pass-data/modal-pass-data.ts b/src/components/modal/test/basic/pages/modal-pass-data/modal-pass-data.ts index c675f571a4..739d0e3361 100644 --- a/src/components/modal/test/basic/pages/modal-pass-data/modal-pass-data.ts +++ b/src/components/modal/test/basic/pages/modal-pass-data/modal-pass-data.ts @@ -59,11 +59,11 @@ export class ModalPassData { this.called.ionViewCanLeave++; return new Promise((resolve: any, reject: any) => { - let alert = this.alertCtrl.create(); - alert.setTitle('Do you want to submit?'); - alert.addButton({ text: 'Submit', handler: resolve }); - alert.addButton({ text: 'Cancel', role: 'cancel', handler: reject }); - alert.present(); + this.alertCtrl.create() + .setTitle('Do you want to submit?') + .addButton({ text: 'Submit', handler: resolve }) + .addButton({ text: 'Cancel', role: 'cancel', handler: reject }) + .present(); }); } diff --git a/src/components/picker/picker-component.ts b/src/components/picker/picker-component.ts index ed9644f90a..4323427f86 100644 --- a/src/components/picker/picker-component.ts +++ b/src/components/picker/picker-component.ts @@ -91,7 +91,7 @@ export class PickerCmp { if (!isPresent(column.options)) { column.options = []; } - + column.selectedIndex = column.selectedIndex || 0; column.options = column.options.map(inputOpt => { let opt: PickerColumnOption = { text: '', diff --git a/src/components/picker/picker.ts b/src/components/picker/picker.ts index 855c8c85cf..2d6883d511 100644 --- a/src/components/picker/picker.ts +++ b/src/components/picker/picker.ts @@ -62,6 +62,10 @@ export class Picker extends ViewController { return this.data.columns; } + getColumn(name: string): PickerColumn { + return this.getColumns().find(column => column.name === name); + } + refresh() { this._cmp && this._cmp.instance.refresh && this._cmp.instance.refresh(); } diff --git a/src/components/range/range.ts b/src/components/range/range.ts index d77c67e2e4..60fe7e079c 100644 --- a/src/components/range/range.ts +++ b/src/components/range/range.ts @@ -302,11 +302,20 @@ export class Range extends Ion implements AfterViewInit, ControlValueAccessor, O return null; } + /** + * @output {Range} Emitted when the range selector drag starts. + */ + @Output() ionFocus: EventEmitter = new EventEmitter(); + /** * @output {Range} Emitted when the range value changes. */ @Output() ionChange: EventEmitter = new EventEmitter(); + /** + * @output {Range} Emitted when the range selector drag ends. + */ + @Output() ionBlur: EventEmitter = new EventEmitter(); constructor( private _form: Form, @@ -356,6 +365,9 @@ export class Range extends Ion implements AfterViewInit, ControlValueAccessor, O return false; } + // trigger ionFocus event + this.ionFocus.emit(this); + // prevent default so scrolling does not happen ev.preventDefault(); ev.stopPropagation(); @@ -368,7 +380,7 @@ export class Range extends Ion implements AfterViewInit, ControlValueAccessor, O // figure out which knob they started closer to const ratio = clamp(0, (current.x - rect.left) / (rect.width), 1); - this._activeB = (Math.abs(ratio - this._ratioA) > Math.abs(ratio - this._ratioB)); + this._activeB = this._dual && (Math.abs(ratio - this._ratioA) > Math.abs(ratio - this._ratioB)); // update the active knob's position this._update(current, rect, true); @@ -411,6 +423,9 @@ export class Range extends Ion implements AfterViewInit, ControlValueAccessor, O // trigger a haptic end this._haptic.gestureSelectionEnd(); + + // trigger ionBlur event + this.ionBlur.emit(this); } } @@ -428,18 +443,14 @@ export class Range extends Ion implements AfterViewInit, ControlValueAccessor, O // update which knob is pressed this._pressed = isPressed; - + let valChanged = false; if (this._activeB) { // when the pointer down started it was determined // that knob B was the one they were interacting with this._pressedB = isPressed; this._pressedA = false; this._ratioB = ratio; - - if (val === this._valB) { - // hasn't changed - return false; - } + valChanged = val === this._valB; this._valB = val; } else { @@ -447,13 +458,13 @@ export class Range extends Ion implements AfterViewInit, ControlValueAccessor, O this._pressedA = isPressed; this._pressedB = false; this._ratioA = ratio; - - if (val === this._valA) { - // hasn't changed - return false; - } + valChanged = val === this._valA; this._valA = val; } + this._updateBar(); + if (valChanged) { + return false; + } // value has been updated if (this._dual) { @@ -478,8 +489,6 @@ export class Range extends Ion implements AfterViewInit, ControlValueAccessor, O this.ionChange.emit(this); }); - this._updateBar(); - return true; } diff --git a/src/components/refresher/test/refresher.spec.ts b/src/components/refresher/test/refresher.spec.ts index 824a214979..6d787c6de8 100644 --- a/src/components/refresher/test/refresher.spec.ts +++ b/src/components/refresher/test/refresher.spec.ts @@ -1,7 +1,7 @@ import { Refresher } from '../refresher'; import { Content } from '../../content/content'; import { GestureController } from '../../../gestures/gesture-controller'; -import { mockConfig, mockDomController, mockElementRef, mockPlatform, mockRenderer, mockZone } from '../../../util/mock-providers'; +import { mockConfig, mockDomController, mockElementRef, mockElementRefEle, mockPlatform, mockRenderer, mockZone } from '../../../util/mock-providers'; describe('Refresher', () => { @@ -234,8 +234,9 @@ describe('Refresher', () => { contentElementRef = mockElementRef(); dom = mockDomController(); content = new Content(mockConfig(), mockPlatform(), dom, contentElementRef, mockRenderer(), null, null, mockZone(), null, null); - content._scrollEle = document.createElement('div'); - content._scrollEle.className = 'scroll-content'; + let ele = document.createElement('div'); + ele.className = 'scroll-content'; + content._scrollContent = mockElementRefEle(ele); let gestureController = new GestureController(null); @@ -270,8 +271,4 @@ describe('Refresher', () => { }; } - // function getScrollElementStyles() { - // return content._scrollEle.style; - // } - }); diff --git a/src/components/searchbar/searchbar.ts b/src/components/searchbar/searchbar.ts index abadcbc0fe..b62d539b77 100644 --- a/src/components/searchbar/searchbar.ts +++ b/src/components/searchbar/searchbar.ts @@ -289,7 +289,7 @@ export class Searchbar extends Ion { // Get the width of the span then remove it var textWidth = tempSpan.offsetWidth; - tempSpan.remove(); + doc.body.removeChild(tempSpan); // Set the input padding left var inputLeft = 'calc(50% - ' + (textWidth / 2) + 'px)'; diff --git a/src/components/select/select.ts b/src/components/select/select.ts index a05643da9b..129f065447 100644 --- a/src/components/select/select.ts +++ b/src/components/select/select.ts @@ -390,6 +390,21 @@ export class Select extends Ion implements AfterContentInit, ControlValueAccesso /** * @hidden */ + checkHasValue(inputValue: any) { + if (this._item) { + let hasValue: boolean; + if (Array.isArray(inputValue)) { + hasValue = inputValue.length > 0; + } else { + hasValue = !isBlank(inputValue); + } + this._item.setElementClass('input-has-value', hasValue); + } + } + + /** + * @private + */ @ContentChildren(Option) set options(val: QueryList