From 7601c9fd2b90ca90d3f78df2f559ba16513d307c Mon Sep 17 00:00:00 2001 From: Tim Lancina Date: Fri, 25 Sep 2015 09:04:07 -0600 Subject: [PATCH 1/5] fix(demos): update nav demo to use nav-params --- demos/navigation/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demos/navigation/index.ts b/demos/navigation/index.ts index 632fa32380..8b6cd3ad9c 100644 --- a/demos/navigation/index.ts +++ b/demos/navigation/index.ts @@ -11,7 +11,7 @@ import {NavParams, NavController} from 'ionic/ionic'; '' + '

{{title}}

' + '

' + - '

' + + '

' + '

' + '
' }) From 60f4d65e636bcd217082a78d438abc3305e2cbc7 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Fri, 25 Sep 2015 11:18:16 -0500 Subject: [PATCH 2/5] feat(ripple): better ripple --- ionic/animations/animation.ts | 19 +++++++++++++ ionic/components/tap-click/ripple.ts | 42 +++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/ionic/animations/animation.ts b/ionic/animations/animation.ts index 4a0bd81689..1549353008 100644 --- a/ionic/animations/animation.ts +++ b/ionic/animations/animation.ts @@ -408,6 +408,21 @@ export class Animation { } } + /** + * Get the current time of the first animation + * in the list. To get a specific time of an animation, call + * subAnimationInstance.getCurrentTime() + */ + getCurrentTime() { + if(this._chld.length > 0) { + return this._chld[0].getCurrentTime(); + } + if(this._ani.length > 0) { + return this._ani[0].getCurrentTime(); + } + return 0; + } + progressEnd(shouldComplete, rate=3) { let promises = []; @@ -604,6 +619,10 @@ class Animate { } } + getCurrentTime() { + return this.ani.currentTime; + } + playbackRate(value) { this.rate = value; if (this.ani) { diff --git a/ionic/components/tap-click/ripple.ts b/ionic/components/tap-click/ripple.ts index afdc03ec16..240de2d71a 100644 --- a/ionic/components/tap-click/ripple.ts +++ b/ionic/components/tap-click/ripple.ts @@ -4,6 +4,16 @@ import {Animation} from '../../animations/animation'; export class RippleActivator extends Activator { + static TOUCH_DOWN_ACCEL = 512; + static TOUCH_UP_ACCEL = 1024; + static OPACITY_DECAY_VEL = 1.6; + static OUTER_OPACITY_VEL = 1.2; + + static OPACITY_OUT_DURATION = 750; + + static EXPAND_OUT_PLAYBACK_RATE = 2; + static DOWN_PLAYBACK_RATE = 0.45; + static OPACITY_OUT_PLAYBACK_RATE = 1; constructor(app, config) { super(app, config); @@ -17,6 +27,18 @@ export class RippleActivator extends Activator { // create a new ripple element let r = targetEle.getBoundingClientRect(); + let w = r.width; + let h = r.height; + + let halfW = w/2; + let halfH = h/2; + let outerRadius = Math.sqrt(halfW * halfW + halfH * halfH); + + let radiusDuration = (1000 * Math.sqrt(outerRadius / RippleActivator.TOUCH_DOWN_ACCEL) + 0.5); + let outerDuration = 1000 * (1/RippleActivator.OUTER_OPACITY_VEL); + + //console.log('Rippling', radiusDuration); + let x = Math.max(Math.abs(r.width - pointerX), pointerX) * 2; let y = Math.max(Math.abs(r.height - pointerY), pointerY) * 2; let size = (Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))) - 10; @@ -33,14 +55,16 @@ export class RippleActivator extends Activator { targetEle.appendChild(rippleEle); let ripple = this.ripples[Date.now()] = { ele: rippleEle }; + ripple.outerRadius = outerRadius; + ripple.radiusDuration = radiusDuration; // expand the circle from the users starting point // start slow, and when they let up, then speed up the animation ripple.expand = new Animation(rippleEle, {renderDelay: 0}); ripple.expand .fromTo('scale', '0.001', '1') - .duration(300) - .playbackRate(0.35) + .duration(radiusDuration) + .playbackRate(RippleActivator.DOWN_PLAYBACK_RATE) .onFinish(()=> { // finished expanding ripple.expand && ripple.expand.dispose(); @@ -59,16 +83,26 @@ export class RippleActivator extends Activator { let ripple; for (let rippleId in this.ripples) { ripple = this.ripples[rippleId]; + if(ripple.expand) { + let currentTime = ripple.expand.getCurrentTime(); + + // How much more time do we need to finish the radius animation? + // Math: (radius/second) * ((total_radius_time) - current_time) + let remainingTime = (ripple.outerRadius / ripple.radiusDuration) * + ((ripple.radiusDuration / RippleActivator.DOWN_PLAYBACK_RATE)- (currentTime)); + ripple.expand.remainingTime = remainingTime; + } if (!ripple.fade) { // ripple has not been let up yet // spped up the rate if the animation is still going setTimeout(() => { - ripple.expand && ripple.expand.playbackRate(1); + ripple.expand && ripple.expand.playbackRate(RippleActivator.EXPAND_OUT_PLAYBACK_RATE); ripple.fade = new Animation(ripple.ele); ripple.fade .fadeOut() - .duration(750) + .duration(ripple.epxand && ripple.expand.remaingTime || RippleActivator.OPACITY_OUT_DURATION) + .playbackRate(RippleActivator.OPACITY_OUT_PLAYBACK_RATE) .onFinish(() => { ripple.fade && ripple.fade.dispose(); ripple.fade = null; From c3ea116046ea4badd11f464cea75e5a4314a22cd Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Fri, 25 Sep 2015 11:56:54 -0500 Subject: [PATCH 3/5] feat(i18n): translation service --- demos/i18n/index.ts | 26 ++++++++++++++++++++ demos/i18n/main.html | 12 +++++++++ ionic/components/app/app.ts | 4 +++ ionic/ionic.ts | 2 ++ ionic/translation/translate.ts | 45 ++++++++++++++++++++++++++++++++++ 5 files changed, 89 insertions(+) create mode 100644 demos/i18n/index.ts create mode 100644 demos/i18n/main.html create mode 100644 ionic/translation/translate.ts diff --git a/demos/i18n/index.ts b/demos/i18n/index.ts new file mode 100644 index 0000000000..997da2be39 --- /dev/null +++ b/demos/i18n/index.ts @@ -0,0 +1,26 @@ +import {Component} from 'angular2/angular2'; +import {Control, ControlGroup} from 'angular2/forms'; + +import {IonicApp, App, Http} from 'ionic/ionic'; + +import {Translate} from 'ionic/ionic'; + +@App({ + templateUrl: 'main.html' +}) +class MyApp { + constructor(app: IonicApp, trans: Translate) { + this.app = app; + this.trans = trans; + + this.trans.translations('de', { + 'Location': 'lage' + }); + + console.log(this.trans.translate('Location')); + console.log(this.trans.translate('de', 'Location')); + this.trans.setLanguage('de'); + console.log(this.trans.translate('Location')); + + } +} diff --git a/demos/i18n/main.html b/demos/i18n/main.html new file mode 100644 index 0000000000..1882213767 --- /dev/null +++ b/demos/i18n/main.html @@ -0,0 +1,12 @@ + + Plugins + + + + + + + + diff --git a/ionic/components/app/app.ts b/ionic/components/app/app.ts index def11107a2..56d46155e3 100644 --- a/ionic/components/app/app.ts +++ b/ionic/components/app/app.ts @@ -14,6 +14,7 @@ import {Popup} from '../popup/popup'; import {FocusHolder} from '../form/focus-holder'; import {Events} from '../../util/events'; import {NavRegistry} from '../nav/nav-registry'; +import {Translate} from '../../translation/translate'; /** * @name IonicApp @@ -298,6 +299,8 @@ export function ionicBootstrap(rootComponentType, views, config) { let modal = new Modal(app, config); let popup = new Popup(app, config); let events = new Events(); + let translate = new Translate(); + console.log('Translate', translate); let navRegistry = new NavRegistry(views); // add injectables that will be available to all child components @@ -312,6 +315,7 @@ export function ionicBootstrap(rootComponentType, views, config) { bind(Events).toValue(events), ROUTER_BINDINGS, bind(LocationStrategy).toClass(HashLocationStrategy), + bind(Translate).toValue(translate), bind(NavRegistry).toValue(navRegistry) ]); diff --git a/ionic/ionic.ts b/ionic/ionic.ts index 6ccc5c4203..359ed77413 100644 --- a/ionic/ionic.ts +++ b/ionic/ionic.ts @@ -24,3 +24,5 @@ export * from './transitions/ios-transition' export * from './transitions/md-transition' export * from './platform/plugins' + +export * from './translation/translate' diff --git a/ionic/translation/translate.ts b/ionic/translation/translate.ts new file mode 100644 index 0000000000..61659a6f47 --- /dev/null +++ b/ionic/translation/translate.ts @@ -0,0 +1,45 @@ +import {Injectable} from 'angular2/angular2'; + +@Injectable() +export class Translate { + constructor() { + this._transMap = {}; + } + + translations(lang, map) { + this._transMap[lang] = map; + } + + setLanguage(lang) { + this._language = lang; + } + + getTranslations(lang) { + return this._transMap[lang]; + } + + translate(lang, key) { + // If called with just one param, first is the key + if(typeof key === 'undefined') { + key = lang; + lang = this._language; + } + + // If the language isn't specified, return the string passed. + if(!lang) { + return key; + } + + let map = this.getTranslations(lang); + + if(!map) { + console.warn('I18N: No translation for key', key, 'using language', this._language); + return ''; + } + return this._getTranslation(map, key); + } + + _getTranslation(map, key) { + return map && map[key] || ''; + } +} From 6fd4b5f323802ace683bc4dc8e67a91eac608de1 Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Fri, 25 Sep 2015 12:18:51 -0500 Subject: [PATCH 4/5] feat(translation): Translate pipe --- demos/i18n/index.ts | 9 +++++---- demos/i18n/main.html | 13 +++---------- ionic/ionic.ts | 1 + ionic/translation/translate.ts | 18 +++++++---------- ionic/translation/translate_pipe.ts | 30 +++++++++++++++++++++++++++++ 5 files changed, 46 insertions(+), 25 deletions(-) create mode 100644 ionic/translation/translate_pipe.ts diff --git a/demos/i18n/index.ts b/demos/i18n/index.ts index 997da2be39..bdf66c9b61 100644 --- a/demos/i18n/index.ts +++ b/demos/i18n/index.ts @@ -3,10 +3,11 @@ import {Control, ControlGroup} from 'angular2/forms'; import {IonicApp, App, Http} from 'ionic/ionic'; -import {Translate} from 'ionic/ionic'; +import {Translate, TranslatePipe} from 'ionic/ionic'; @App({ - templateUrl: 'main.html' + templateUrl: 'main.html', + pipes: [TranslatePipe] }) class MyApp { constructor(app: IonicApp, trans: Translate) { @@ -18,8 +19,8 @@ class MyApp { }); console.log(this.trans.translate('Location')); - console.log(this.trans.translate('de', 'Location')); - this.trans.setLanguage('de'); + console.log(this.trans.translate('Location', 'de')); + //this.trans.setLanguage('de'); console.log(this.trans.translate('Location')); } diff --git a/demos/i18n/main.html b/demos/i18n/main.html index 1882213767..d73601f91c 100644 --- a/demos/i18n/main.html +++ b/demos/i18n/main.html @@ -1,12 +1,5 @@ - - Plugins + - - - + Hello! What is your {{ 'Location' | translate }} - - - + diff --git a/ionic/ionic.ts b/ionic/ionic.ts index 359ed77413..c7fa97ad3f 100644 --- a/ionic/ionic.ts +++ b/ionic/ionic.ts @@ -26,3 +26,4 @@ export * from './transitions/md-transition' export * from './platform/plugins' export * from './translation/translate' +export * from './translation/translate_pipe' diff --git a/ionic/translation/translate.ts b/ionic/translation/translate.ts index 61659a6f47..835433e57d 100644 --- a/ionic/translation/translate.ts +++ b/ionic/translation/translate.ts @@ -18,22 +18,18 @@ export class Translate { return this._transMap[lang]; } - translate(lang, key) { - // If called with just one param, first is the key - if(typeof key === 'undefined') { - key = lang; - lang = this._language; - } - - // If the language isn't specified, return the string passed. - if(!lang) { + translate(key, lang) { + // If the language isn't specified and we have no overridden one, return the string passed. + if(!lang && !this._language) { return key; } - let map = this.getTranslations(lang); + let setLanguage = lang || this._language; + + let map = this.getTranslations(setLanguage); if(!map) { - console.warn('I18N: No translation for key', key, 'using language', this._language); + console.warn('I18N: No translation for key', key, 'using language', setLanguage); return ''; } return this._getTranslation(map, key); diff --git a/ionic/translation/translate_pipe.ts b/ionic/translation/translate_pipe.ts new file mode 100644 index 0000000000..34181b6240 --- /dev/null +++ b/ionic/translation/translate_pipe.ts @@ -0,0 +1,30 @@ +import {Injectable, Pipe, PipeTransform} from 'angular2/angular2'; + +import {Translate} from './translate'; + +/** + * The Translate pipe makes it easy to translate strings. + * + * @usage + * Translate using the current language or language set through Translate.setLanguage + * {{ 'Please enter your location' | translate }} + * + * Translate using a specific language + * {{ 'Please enter your location' | translate:"de" }} + */ +@Pipe({name: 'translate'}) +@Injectable() +export class TranslatePipe implements PipeTransform { + constructor(translate: Translate) { + this.translate = translate; + } + transform(value, args) { + let lang; + if(args.length > 0) { + lang = args[0]; + } + return this.translate.translate(value, lang); + } + + supports(obj) { return true; } +} From 5a0584df0ec5cb1ab7c59507b5ee539fe0ceacbf Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Fri, 25 Sep 2015 12:46:14 -0500 Subject: [PATCH 5/5] Ripple tweaks --- ionic/animations/animation.ts | 2 +- ionic/components/tap-click/ripple.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ionic/animations/animation.ts b/ionic/animations/animation.ts index 1549353008..c88febc0eb 100644 --- a/ionic/animations/animation.ts +++ b/ionic/animations/animation.ts @@ -620,7 +620,7 @@ class Animate { } getCurrentTime() { - return this.ani.currentTime; + return this.ani && this.ani.currentTime; } playbackRate(value) { diff --git a/ionic/components/tap-click/ripple.ts b/ionic/components/tap-click/ripple.ts index 240de2d71a..9f770d3b65 100644 --- a/ionic/components/tap-click/ripple.ts +++ b/ionic/components/tap-click/ripple.ts @@ -11,8 +11,8 @@ export class RippleActivator extends Activator { static OPACITY_OUT_DURATION = 750; - static EXPAND_OUT_PLAYBACK_RATE = 2; - static DOWN_PLAYBACK_RATE = 0.45; + static EXPAND_OUT_PLAYBACK_RATE = 3.5; + static DOWN_PLAYBACK_RATE = 0.65; static OPACITY_OUT_PLAYBACK_RATE = 1; constructor(app, config) {