From b083ac2c47b10a4b570e9ad4572d6ed4ebafdbd8 Mon Sep 17 00:00:00 2001 From: Drew Rygh Date: Tue, 5 Jan 2016 11:31:01 -0600 Subject: [PATCH 1/6] docs(demos): fix actionSheet demo --- .../action-sheets/basic/pages.ts | 69 +++++++++++-------- demos/component-docs/index.ts | 2 - 2 files changed, 41 insertions(+), 30 deletions(-) diff --git a/demos/component-docs/action-sheets/basic/pages.ts b/demos/component-docs/action-sheets/basic/pages.ts index 20a7e6da02..e412dd31bf 100644 --- a/demos/component-docs/action-sheets/basic/pages.ts +++ b/demos/component-docs/action-sheets/basic/pages.ts @@ -23,43 +23,57 @@ export class BasicPage { if (this.platform.is('android')) { var androidSheet = { - + title: 'Albums', buttons: [ - { text: 'Share', icon: 'share' }, - { text: 'Play', icon: 'arrow-dropright-circle'}, - { text: 'Favorite', icon: 'ion-md-heart-outline'} + { text: 'Share', + handler: buttonHandler, + icon: 'share' + }, + { text: 'Play', + handler: buttonHandler, + icon: 'arrow-dropright-circle' + }, + { text: 'Favorite', + handler: buttonHandler, + icon: 'ion-md-heart-outline' + }, + { + text: 'Delete', + style: 'destructive', + handler: () => { + console.log('Destructive clicked'); + } + }, + { + text: 'Cancel', + style: 'cancel', + handler: () => { + console.log('Cancel clicked'); + } + } ], - destructiveText: 'Delete', - titleText: 'Albums', - cancelText: 'Cancel', - cancel: function() { - console.log('Canceled'); - }, - destructiveButtonClicked: () => { - console.log('Destructive clicked'); - }, - buttonClicked: function(index) { - console.log('Button clicked', index); - if (index == 1) { return false; } - return true; - } }; } - this.actionSheet = ActionSheet.create(androidSheet || { - title: 'Albums', + let actionSheet = ActionSheet.create( androidSheet || { buttons: [ { text: 'Share', - handler: buttonHandler + handler: () => { + console.log('Share clicked'); + } }, { text: 'Play', - handler: buttonHandler + handler: () => { + console.log('Play clicked'); + } }, { text: 'Favorite', - handler: buttonHandler + handler: () => { + console.log('Favorite clicked'); + } }, { text: 'Delete', @@ -72,18 +86,17 @@ export class BasicPage { text: 'Cancel', style: 'cancel', handler: () => { - console.log('Canceled'); + console.log('Cancel clicked'); } } - ], - + ] }); - this.nav.present(this.actionSheet); + this.nav.present(actionSheet); } onPageWillLeave() { - this.actionSheet && this.actionSheet.dismiss(); + actionSheet && actionSheet.dismiss(); } } diff --git a/demos/component-docs/index.ts b/demos/component-docs/index.ts index 1823f3079c..988788dcc4 100644 --- a/demos/component-docs/index.ts +++ b/demos/component-docs/index.ts @@ -36,8 +36,6 @@ class DemoApp { window.addEventListener('message', (e) => { //TODO(@drewrygh) - e = {}; - e.data = '{}'; zone.run(() => { if (e.data) { var data; From 40e5bdbfd074e93dc7c3e5ff03b266caa869609e Mon Sep 17 00:00:00 2001 From: Drew Rygh Date: Tue, 5 Jan 2016 14:55:37 -0600 Subject: [PATCH 2/6] docs(demos): update alert demo --- .../{popups/popups.ts => alerts/alerts.ts} | 0 demos/component-docs/alerts/basic/pages.ts | 51 +++++++++++++++++++ .../{popups => alerts}/basic/template.html | 4 +- demos/component-docs/helpers.ts | 5 +- demos/component-docs/popups/basic/pages.ts | 50 ------------------ 5 files changed, 56 insertions(+), 54 deletions(-) rename demos/component-docs/{popups/popups.ts => alerts/alerts.ts} (100%) create mode 100644 demos/component-docs/alerts/basic/pages.ts rename demos/component-docs/{popups => alerts}/basic/template.html (61%) delete mode 100644 demos/component-docs/popups/basic/pages.ts diff --git a/demos/component-docs/popups/popups.ts b/demos/component-docs/alerts/alerts.ts similarity index 100% rename from demos/component-docs/popups/popups.ts rename to demos/component-docs/alerts/alerts.ts diff --git a/demos/component-docs/alerts/basic/pages.ts b/demos/component-docs/alerts/basic/pages.ts new file mode 100644 index 0000000000..ed70fd2393 --- /dev/null +++ b/demos/component-docs/alerts/basic/pages.ts @@ -0,0 +1,51 @@ +import {Page, Alert, NavController} from 'ionic/ionic'; +import {AndroidAttribute} from '../../helpers'; +import {forwardRef} from 'angular2/core'; + + +@Page({ + templateUrl: 'alerts/basic/template.html', + directives: [forwardRef(() => AndroidAttribute)] +}) +export class BasicPage { + + constructor(nav: NavController) { + this.nav = nav; + } + + doAlert() { + let alert = Alert.create({ + title: "New Friend!", + subTitle: "Your friend, Obi wan Kenobi, just accepted your friend request!", + buttons: ['Ok'] + }); + this.nav.present(alert); + } + + // doPrompt() { + // this.popup.prompt({ + // title: "New Album", + // template: "Enter a name for this new album you're so keen on adding", + // inputPlaceholder: "Title", + // okText: "Save" + // }); + // } + + // doConfirm() { + // this.popup.confirm({ + // title: "Use this lightsaber?", + // template: "Do you agree to use this lightsaber to do good across the intergalactic galaxy?", + // cancelText: "Disagree", + // okText: "Agree" + // }); + // } + + // onPageWillLeave() { + // let popup = this.popup.get(); + // // only try to close if there is an active popup + // if (popup) { + // popup.close(); + // } + // } + +} diff --git a/demos/component-docs/popups/basic/template.html b/demos/component-docs/alerts/basic/template.html similarity index 61% rename from demos/component-docs/popups/basic/template.html rename to demos/component-docs/alerts/basic/template.html index 771bec071d..9a7f0cc525 100644 --- a/demos/component-docs/popups/basic/template.html +++ b/demos/component-docs/alerts/basic/template.html @@ -5,6 +5,6 @@ - - + diff --git a/demos/component-docs/helpers.ts b/demos/component-docs/helpers.ts index 9ae0db33f4..2e977349f7 100644 --- a/demos/component-docs/helpers.ts +++ b/demos/component-docs/helpers.ts @@ -13,7 +13,7 @@ import * as lists from './lists/lists'; import * as menus from './menus/menus'; import * as modals from './modals/modals'; import * as navigation from './navigation/navigation'; -import * as popups from './popups/popups'; +import * as alerts from './alerts/alerts'; import * as slides from './slides/slides'; import * as tabs from './tabs/tabs'; @@ -72,6 +72,8 @@ export function getPageFor(hash) { return { 'overview': actionSheets.BasicPage, 'action-sheets': actionSheets.BasicPage, + 'alert': alerts.BasicPage, + 'badges': badges.BasicPage, 'buttons': buttons.BasicPage, @@ -127,7 +129,6 @@ export function getPageFor(hash) { 'menus': menus.BasicPage, 'modals': modals.BasicPage, 'navigation': navigation.BasicPage, - 'popups': popups.BasicPage, 'slides': slides.BasicPage, 'tabs': tabs.BasicPage, diff --git a/demos/component-docs/popups/basic/pages.ts b/demos/component-docs/popups/basic/pages.ts deleted file mode 100644 index 54a58ddd46..0000000000 --- a/demos/component-docs/popups/basic/pages.ts +++ /dev/null @@ -1,50 +0,0 @@ -import {Page, Popup} from 'ionic/ionic'; -import {AndroidAttribute} from '../../helpers'; -import {forwardRef} from 'angular2/core'; - - -@Page({ - templateUrl: 'popups/basic/template.html', - directives: [forwardRef(() => AndroidAttribute)] -}) -export class BasicPage { - - constructor(popup: Popup) { - this.popup = popup; - } - - doAlert() { - this.popup.alert({ - title: "New Friend!", - template: "Your friend, Obi wan Kenobi, just accepted your friend request!", - cssClass: 'my-alert' - }); - } - - doPrompt() { - this.popup.prompt({ - title: "New Album", - template: "Enter a name for this new album you're so keen on adding", - inputPlaceholder: "Title", - okText: "Save" - }); - } - - doConfirm() { - this.popup.confirm({ - title: "Use this lightsaber?", - template: "Do you agree to use this lightsaber to do good across the intergalactic galaxy?", - cancelText: "Disagree", - okText: "Agree" - }); - } - - onPageWillLeave() { - let popup = this.popup.get(); - // only try to close if there is an active popup - if (popup) { - popup.close(); - } - } - -} From 94f1e0964e6e2c4883b8bd09aa78178cb0d1802a Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Tue, 5 Jan 2016 15:03:56 -0600 Subject: [PATCH 3/6] refactor(textInput): use ContentChild, code cleanup --- ionic/components/label/label.ts | 49 +- ionic/components/text-input/text-input.ts | 534 +++++++++++----------- 2 files changed, 268 insertions(+), 315 deletions(-) diff --git a/ionic/components/label/label.ts b/ionic/components/label/label.ts index 04f877b8b4..1e85845cf3 100644 --- a/ionic/components/label/label.ts +++ b/ionic/components/label/label.ts @@ -1,8 +1,5 @@ -import {Directive, Optional, ElementRef, Renderer} from 'angular2/core'; +import {Directive, ElementRef, Renderer} from 'angular2/core'; -import {Config} from '../../config/config'; -import {TextInput} from '../text-input/text-input'; -import {pointerCoord, hasPointerMoved} from '../../util/dom'; import {Form} from '../../util/form'; @@ -30,25 +27,16 @@ import {Form} from '../../util/form'; 'id' ], host: { - '[attr.id]': 'id', - '(touchstart)': 'pointerStart($event)', - '(touchend)': 'pointerEnd($event)', - '(mousedown)': 'pointerStart($event)', - '(mouseup)': 'pointerEnd($event)' + '[attr.id]': 'id' } }) export class Label { constructor( - config: Config, - @Optional() container: TextInput, private _form: Form, private _elementRef: ElementRef, private _renderer: Renderer - ) { - this.scrollAssist = config.get('scrollAssist'); - this.container = container; - } + ) {} /** * @private @@ -57,37 +45,6 @@ export class Label { if (!this.id) { this.id = 'lbl-' + this._form.nextId(); } - this.container && this.container.registerLabel(this); - } - - /** - * @private - */ - pointerStart(ev) { - if (this.scrollAssist) { - // remember where the touchstart/mousedown started - this.startCoord = pointerCoord(ev); - } - } - - /** - * @private - */ - pointerEnd(ev) { - if (this.container) { - - // get where the touchend/mouseup ended - let endCoord = pointerCoord(ev); - - // focus this input if the pointer hasn't moved XX pixels - if (!hasPointerMoved(20, this.startCoord, endCoord)) { - ev.preventDefault(); - ev.stopPropagation(); - this.container.initFocus(); - } - - this.startCoord = null; - } } /** diff --git a/ionic/components/text-input/text-input.ts b/ionic/components/text-input/text-input.ts index 35d82fc37e..a930c66402 100644 --- a/ionic/components/text-input/text-input.ts +++ b/ionic/components/text-input/text-input.ts @@ -1,4 +1,4 @@ -import {Component, Directive, Attribute, forwardRef, Host, Optional, ElementRef, Renderer, Input, ContentChild} from 'angular2/core'; +import {Component, Directive, Attribute, forwardRef, Host, Optional, ElementRef, Renderer, Input, Output, EventEmitter, ContentChild, HostListener} from 'angular2/core'; import {NgIf} from 'angular2/common'; import {NavController} from '../nav/nav-controller'; @@ -7,11 +7,131 @@ import {Form} from '../../util/form'; import {Label} from '../label/label'; import {IonicApp} from '../app/app'; import {Content} from '../content/content'; -import * as dom from '../../util/dom'; +import {CSS, hasFocus, pointerCoord, hasPointerMoved} from '../../util/dom'; import {Platform} from '../../platform/platform'; import {Button} from '../button/button'; +/** + * @private + */ +@Directive({ + selector: 'textarea,input[type=text],input[type=password],input[type=number],input[type=search],input[type=email],input[type=url],input[type=tel],input[type=date],input[type=datetime],input[type=datetime-local],input[type=week],input[type=time]', + host: { + 'class': 'text-input' + } +}) +export class TextInputElement { + @Input() value: string; + @Input() ngModel: any; + @Output() valueChange: EventEmitter = new EventEmitter(); + @Output() focusChange: EventEmitter = new EventEmitter(); + + constructor( + @Attribute('type') type: string, + private _elementRef: ElementRef, + private _renderer: Renderer + ) { + this.type = type || 'text'; + } + + ngOnInit() { + if (this.ngModel) { + this.value = this.ngModel; + } else { + this.value = this._elementRef.nativeElement.value; + } + } + + @HostListener('keyup', ['$event']) + _keyup(ev) { + this.valueChange.emit(ev.target.value); + } + + @HostListener('focus') + _focus() { + this.focusChange.emit(true); + } + + @HostListener('blur') + _blur() { + this.focusChange.emit(false); + this.hideFocus(false); + } + + labelledBy(val) { + this._renderer.setElementAttribute(this._elementRef, 'aria-labelledby', val); + } + + setFocus() { + this.element().focus(); + } + + relocate(shouldRelocate, inputRelativeY) { + if (this._relocated !== shouldRelocate) { + + let focusedInputEle = this.element(); + if (shouldRelocate) { + let clonedInputEle = cloneInput(focusedInputEle, 'cloned-input'); + + focusedInputEle.classList.add('hide-focused-input'); + focusedInputEle.style[CSS.transform] = `translate3d(-9999px,${inputRelativeY}px,0)`; + focusedInputEle.parentNode.insertBefore(clonedInputEle, focusedInputEle); + + this.setFocus(); + + } else { + focusedInputEle.classList.remove('hide-focused-input'); + focusedInputEle.style[CSS.transform] = ''; + let clonedInputEle = focusedInputEle.parentNode.querySelector('.cloned-input'); + if (clonedInputEle) { + clonedInputEle.parentNode.removeChild(clonedInputEle); + } + } + + this._relocated = shouldRelocate; + } + } + + hideFocus(shouldHideFocus) { + let focusedInputEle = this.element(); + + if (shouldHideFocus) { + let clonedInputEle = cloneInput(focusedInputEle, 'cloned-hidden'); + + focusedInputEle.classList.add('hide-focused-input'); + focusedInputEle.style[CSS.transform] = 'translate3d(-9999px,0,0)'; + focusedInputEle.parentNode.insertBefore(clonedInputEle, focusedInputEle); + + } else { + focusedInputEle.classList.remove('hide-focused-input'); + focusedInputEle.style[CSS.transform] = ''; + let clonedInputEle = focusedInputEle.parentNode.querySelector('.cloned-hidden'); + if (clonedInputEle) { + clonedInputEle.parentNode.removeChild(clonedInputEle); + } + } + } + + hasFocus() { + return hasFocus(this.element()); + } + + addClass(className) { + this._renderer.setElementClass(this._elementRef, className, true); + } + + hasClass(className) { + this._elementRef.nativeElement.classList.contains(className); + } + + element() { + return this._elementRef.nativeElement; + } + +} + + /** * @name Input * @module ionic @@ -56,49 +176,43 @@ import {Button} from '../button/button'; '(touchend)': 'pointerEnd($event)', '(mouseup)': 'pointerEnd($event)', 'class': 'item', - '[class.ng-untouched]': 'addNgClass("ng-untouched")', - '[class.ng-touched]': 'addNgClass("ng-touched")', - '[class.ng-pristine]': 'addNgClass("ng-pristine")', - '[class.ng-dirty]': 'addNgClass("ng-dirty")', - '[class.ng-valid]': 'addNgClass("ng-valid")', - '[class.ng-invalid]': 'addNgClass("ng-invalid")' + '[class.ng-untouched]': 'hasClass("ng-untouched")', + '[class.ng-touched]': 'hasClass("ng-touched")', + '[class.ng-pristine]': 'hasClass("ng-pristine")', + '[class.ng-dirty]': 'hasClass("ng-dirty")', + '[class.ng-valid]': 'hasClass("ng-valid")', + '[class.ng-invalid]': 'hasClass("ng-invalid")' }, template: '
' + '' + - '' + + '' + '' + '
', directives: [NgIf, forwardRef(() => InputScrollAssist), forwardRef(() => TextInputElement), Button] }) export class TextInput { - @ContentChild(forwardRef(() => TextInputElement)) textInputElement; - /** * @private */ @Input() clearInput: any; - - value: any = ''; + value: string = ''; constructor( - form: Form, - elementRef: ElementRef, config: Config, - renderer: Renderer, - app: IonicApp, - platform: Platform, - @Optional() @Host() scrollView: Content, - @Optional() navCtrl: NavController, + private _form: Form, + private _renderer: Renderer, + private _elementRef: ElementRef, + private _app: IonicApp, + private _platform: Platform, + @Optional() @Host() private _scrollView: Content, + @Optional() private _nav: NavController, @Attribute('floating-label') isFloating: string, @Attribute('stacked-label') isStacked: string, @Attribute('fixed-label') isFixed: string, @Attribute('inset') isInset: string ) { - this.renderer = renderer; - - this.form = form; - form.register(this); + _form.register(this); this.type = 'text'; this.lastTouch = 0; @@ -106,40 +220,44 @@ export class TextInput { // make more gud with pending @Attributes API this.displayType = (isFloating === '' ? 'floating' : (isStacked === '' ? 'stacked' : (isFixed === '' ? 'fixed' : (isInset === '' ? 'inset' : null)))); - this.app = app; - this.elementRef = elementRef; - this.platform = platform; - this.navCtrl = navCtrl; - - this.scrollView = scrollView; - this.scrollAssist = config.get('scrollAssist'); + this._assist = config.get('scrollAssist'); this.keyboardHeight = config.get('keyboardHeight'); } /** * @private - * This function is used to add the Angular css classes associated with inputs in forms */ - addNgClass(className) { - this.input && this.input.elementRef.nativeElement.classList.contains(className); - } + @ContentChild(TextInputElement) + set _setInput(textInputElement) { + if (textInputElement) { + textInputElement.addClass('item-input'); + if (this.displayType) { + textInputElement.addClass(this.displayType + '-input'); + } + this.input = textInputElement; + this.type = textInputElement.type; - /** - * @private - */ - registerInput(textInputElement) { - if (this.displayType) { - textInputElement.addClass(this.displayType + '-input'); + this.hasValue(this.input.value); + textInputElement.valueChange.subscribe(inputValue => { + this.hasValue(inputValue); + }); + + this.focusChange(this.hasFocus()); + textInputElement.focusChange.subscribe(hasFocus => { + this.focusChange(hasFocus); + }); + + } else { + console.error(' or