diff --git a/.gitignore b/.gitignore index e03cb50..b45f898 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,171 @@ plugins/ *.swp .DS_Store Thumbs.db -.editorconfig \ No newline at end of file +.editorconfig +/.idea + + +# Created by https://www.gitignore.io/api/osx ,osx,jetbrains,webstorm,node + +### OSX ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon +# Thumbnails +._* +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + + +### JetBrains ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/workspace.xml +.idea/tasks.xml + +# Sensitive or high-churn files: +.idea/dataSources/ +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +### JetBrains Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + + +### WebStorm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: + +# Sensitive or high-churn files: + +# Gradle: + +# Mongo Explorer plugin: + +## File-based project format: + +## Plugin-specific files: + +# IntelliJ + +# mpeltonen/sbt-idea plugin + +# JIRA plugin + +# Crashlytics plugin (for Android Studio and IntelliJ) + +### WebStorm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + + +### Node ### +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules +jspm_packages + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + + +# End of https://www.gitignore.io/api/osx ,osx,jetbrains,webstorm,node \ No newline at end of file diff --git a/demo/config.xml b/demo/config.xml index fba2861..e98b5d2 100644 --- a/demo/config.xml +++ b/demo/config.xml @@ -1,10 +1,11 @@ - - demo + + ionic-pullup-demo An awesome Ionic/Cordova app. Ionic Framework Team + @@ -25,7 +26,14 @@ + + + + + + + \ No newline at end of file diff --git a/demo/ionic-pullup/.gitignore b/demo/ionic-pullup/.gitignore new file mode 100644 index 0000000..b45f898 --- /dev/null +++ b/demo/ionic-pullup/.gitignore @@ -0,0 +1,178 @@ +# Specifies files to intentionally ignore when using Git +# http://git-scm.com/docs/gitignore + +node_modules/ +www/build/ +platforms/ +plugins/ +*.swp +.DS_Store +Thumbs.db +.editorconfig +/.idea + + +# Created by https://www.gitignore.io/api/osx ,osx,jetbrains,webstorm,node + +### OSX ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon +# Thumbnails +._* +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + + +### JetBrains ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/workspace.xml +.idea/tasks.xml + +# Sensitive or high-churn files: +.idea/dataSources/ +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +### JetBrains Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + + +### WebStorm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: + +# Sensitive or high-churn files: + +# Gradle: + +# Mongo Explorer plugin: + +## File-based project format: + +## Plugin-specific files: + +# IntelliJ + +# mpeltonen/sbt-idea plugin + +# JIRA plugin + +# Crashlytics plugin (for Android Studio and IntelliJ) + +### WebStorm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + + +### Node ### +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules +jspm_packages + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + + +# End of https://www.gitignore.io/api/osx ,osx,jetbrains,webstorm,node \ No newline at end of file diff --git a/demo/ionic-pullup/README.md b/demo/ionic-pullup/README.md new file mode 100644 index 0000000..afd98cd --- /dev/null +++ b/demo/ionic-pullup/README.md @@ -0,0 +1,7 @@ +# ionic-pullup for Ionic 2 and Angular 2 + +A pull-up footer component for Ionic. The pull-up footer is a UI component built on top of Ionic's footer and offers an efficient way to hide/reveal information. The footer can fully expand to cover the content area of the screen or can be configured to expand to a maximum height. +The component will compute the available screen height providing for header and tabs. + +*See project [website](http://arielfaur.github.io/ionic-pullup)* + diff --git a/demo/ionic-pullup/index.d.ts b/demo/ionic-pullup/index.d.ts new file mode 100644 index 0000000..8df7196 --- /dev/null +++ b/demo/ionic-pullup/index.d.ts @@ -0,0 +1,3 @@ +export {IonPullUpComponent, IonPullUpFooterState} from './src/ion-pullup'; +export {IonPullUpTabComponent} from './src/ion-pullup-tab'; +export {IonPullUpModule} from './src/ion-pullup.module'; diff --git a/demo/ionic-pullup/index.js b/demo/ionic-pullup/index.js new file mode 100644 index 0000000..8a1796a --- /dev/null +++ b/demo/ionic-pullup/index.js @@ -0,0 +1,4 @@ +export { IonPullUpComponent, IonPullUpFooterState } from './src/ion-pullup'; +export { IonPullUpTabComponent } from './src/ion-pullup-tab'; +export { IonPullUpModule } from './src/ion-pullup.module'; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/demo/ionic-pullup/index.js.map b/demo/ionic-pullup/index.js.map new file mode 100644 index 0000000..002da3d --- /dev/null +++ b/demo/ionic-pullup/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,SAAQ,kBAAkB,EAAE,oBAAoB,QAAO,kBAAkB,CAAC;AAC1E,SAAQ,qBAAqB,QAAO,sBAAsB,CAAC;AAC3D,SAAQ,eAAe,QAAO,yBAAyB,CAAC"} \ No newline at end of file diff --git a/demo/ionic-pullup/index.ts b/demo/ionic-pullup/index.ts new file mode 100644 index 0000000..8df7196 --- /dev/null +++ b/demo/ionic-pullup/index.ts @@ -0,0 +1,3 @@ +export {IonPullUpComponent, IonPullUpFooterState} from './src/ion-pullup'; +export {IonPullUpTabComponent} from './src/ion-pullup-tab'; +export {IonPullUpModule} from './src/ion-pullup.module'; diff --git a/demo/src/app/ionic-pullup/ion-pullup-tab.ts b/demo/ionic-pullup/ion-pullup-tab.ts similarity index 100% rename from demo/src/app/ionic-pullup/ion-pullup-tab.ts rename to demo/ionic-pullup/ion-pullup-tab.ts diff --git a/demo/ionic-pullup/ion-pullup.module.ts b/demo/ionic-pullup/ion-pullup.module.ts new file mode 100644 index 0000000..072c2f9 --- /dev/null +++ b/demo/ionic-pullup/ion-pullup.module.ts @@ -0,0 +1,27 @@ +import {NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {FormsModule} from '@angular/forms'; +import {IonicModule} from 'ionic-angular'; + +import {IonPullUpComponent} from './ion-pullup'; +import {IonPullUpTabComponent} from './ion-pullup-tab'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + IonicModule + ], + declarations: [ + IonPullUpComponent, + IonPullUpTabComponent + ], + exports: [ + IonPullUpComponent, + IonPullUpTabComponent + ], + providers: [], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class IonPullUpModule { +} \ No newline at end of file diff --git a/src/ionic-pullup/ion-pullup.scss b/demo/ionic-pullup/ion-pullup.scss similarity index 90% rename from src/ionic-pullup/ion-pullup.scss rename to demo/ionic-pullup/ion-pullup.scss index a332fe8..7e544a7 100644 --- a/src/ionic-pullup/ion-pullup.scss +++ b/demo/ionic-pullup/ion-pullup.scss @@ -13,15 +13,15 @@ $tabShadow: #666; // shadow color background: $background; color: $foreground; border-radius: $radius $radius 0 0; - margin: 0 auto; + margin: 0 auto; } @mixin pullup-tab-shadow($shadowColor) { - box-shadow: 0px -2px 2px $shadowColor; + box-shadow: 0px -2px 2px $shadowColor; } -ion-pullup { - background : color($colors, primary, base); +ion-pullup { + background : color($colors, primary, base); ion-footer { &::before { @@ -39,12 +39,12 @@ ion-pullup { ion-content { position:absolute; - + .ios & { @import 'node_modules/ionic-angular/components/toolbar/toolbar.ios'; margin-top: calc(#{$toolbar-ios-height} + #{$tabHeight}); - scroll-content { + .scroll-content { margin-bottom: calc(#{$toolbar-ios-height} + #{$tabHeight}); } } @@ -52,7 +52,7 @@ ion-pullup { @import 'node_modules/ionic-angular/components/toolbar/toolbar.md'; margin-top: calc(#{$toolbar-md-height} + #{$tabHeight}); - scroll-content { + .scroll-content { margin-bottom: calc(#{$toolbar-md-height} + #{$tabHeight}); } } @@ -60,12 +60,12 @@ ion-pullup { ion-toolbar { @include pullup-tab-shadow($tabShadow); - + .toolbar-background { .ios & { border-top: none; } } } - } + } } \ No newline at end of file diff --git a/demo/ionic-pullup/ion-pullup.ts b/demo/ionic-pullup/ion-pullup.ts new file mode 100644 index 0000000..16a46a3 --- /dev/null +++ b/demo/ionic-pullup/ion-pullup.ts @@ -0,0 +1,285 @@ +/* + ionic-pullup v2 for Ionic/Angular 2 + + Copyright 2016 Ariel Faur (https://github.com/arielfaur) + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { + Attribute, + ChangeDetectionStrategy, + Component, + Directive, + DoCheck, + SimpleChange, + OnChanges, + EventEmitter, + ElementRef, + Renderer, + ViewChild, + ContentChild, + Output, + Input, + Injectable, + Inject, + Optional +} from '@angular/core'; +import {Gesture} from 'ionic-angular/gestures/gesture'; +import {Toolbar, Footer} from 'ionic-angular/components/toolbar/toolbar'; +import {Platform} from 'ionic-angular'; + +interface FooterMetadata { + height: number; + posY: number; + lastPosY: number; + defaultHeight?: number; +} + +interface ViewMetadata { + tabs?: Element; + tabsHeight?: number; + hasBottomTabs?: boolean; + header?: Element; + headerHeight?: number; +} + +interface FooterTab { + x?: number; + y?: number; + upperLeftRadius?: number; + upperRightRadius?: number; + backgroundColor?: string; + color?: string; + content?: string; +} + +export enum IonPullUpFooterState { + Collapsed = 0, + Expanded = 1, + Minimized = 2 +} + +export enum IonPullUpFooterBehavior { + Hide, + Expand +} + +@Component({ + selector: 'ion-pullup', + changeDetection: ChangeDetectionStrategy.OnPush, + template: ` + + + + ` +}) +export class IonPullUpComponent { + @Input() state: IonPullUpFooterState; + @Output() stateChange: EventEmitter = new EventEmitter(); + + @Input() initialState: IonPullUpFooterState; // TODO implemment + @Input() defaultBehavior: IonPullUpFooterBehavior; // TODO implemment + @Input() maxHeight: number; + + @Output() onExpand = new EventEmitter(); + @Output() onCollapse = new EventEmitter(); + @Output() onMinimize = new EventEmitter(); + + @ContentChild(Toolbar) childToolbar; + @ViewChild('footer') childFooter; + + protected _footerMeta: FooterMetadata; + protected _currentViewMeta: ViewMetadata; + protected _oldState: IonPullUpFooterState; + + constructor(private platform: Platform, private el: ElementRef, private renderer: Renderer) { + this._footerMeta = { + height: 0, + posY: 0, + lastPosY: 0 + } + this._currentViewMeta = {}; + + // sets initial state + this.initialState = this.initialState || IonPullUpFooterState.Collapsed; + this.defaultBehavior = this.defaultBehavior || IonPullUpFooterBehavior.Expand; + this.maxHeight = this.maxHeight || 0; + } + + ngOnInit() { + console.debug('ionic-pullup => Initializing footer...'); + + window.addEventListener("orientationchange", () => { + console.debug('ionic-pullup => Changed orientation => updating'); + this.updateUI(); + }); + this.platform.resume.subscribe(() => { + console.debug('ionic-pullup => Resumed from background => updating'); + this.updateUI(); + }); + } + + ngAfterContentInit() { + this.computeDefaults(); + + let barGesture = new Gesture(this.childToolbar.getNativeElement()); + barGesture.listen(); + barGesture.on('tap', e => { + this.onTap(e); + }); + barGesture.on('pan panstart panend', e => { + this.onDrag(e); + }); + + this.state = IonPullUpFooterState.Collapsed; + + this.updateUI(true); // need to indicate whether it's first run to avoid emitting events twice due to change detection + + } + + public get expandedHeight(): number { + return window.innerHeight - this._currentViewMeta.headerHeight; + } + + computeDefaults() { + this._footerMeta.defaultHeight = this.childFooter.nativeElement.offsetHeight; + + + // TODO: still need to test with tabs template (not convinced it is a valid use case...) + this._currentViewMeta.tabs = this.el.nativeElement.closest('ion-tabs'); + this._currentViewMeta.tabsHeight = this._currentViewMeta.tabs ? ( this._currentViewMeta.tabs.querySelector('.tabbar')).offsetHeight : 0 + console.debug(this._currentViewMeta.tabsHeight ? 'ionic-pullup => Tabs detected' : 'ionic.pullup => View has no tabs'); + //this._currentViewMeta.hasBottomTabs = this._currentViewMeta.tabs && this._currentViewMeta.tabs.classList.contains('tabs-bottom'); + + + this._currentViewMeta.header = document.querySelector('ion-navbar.toolbar'); + this._currentViewMeta.headerHeight = this._currentViewMeta.header ? (this._currentViewMeta.header).offsetHeight : 0; + } + + computeHeights(isInit: boolean = false) { + this._footerMeta.height = this.maxHeight > 0 ? this.maxHeight : this.expandedHeight; + + this.renderer.setElementStyle(this.childFooter.nativeElement, 'height', this._footerMeta.height + 'px'); + + // TODO: implement minimize mode + //this.renderer.setElementStyle(this.el.nativeElement, 'min-height', this._footerMeta.height + 'px'); + //if (this.initialState == IonPullUpFooterState.Minimized) { + // this.minimize() + //} else { + this.collapse(isInit); + //} + } + + updateUI(isInit: boolean = false) { + setTimeout(() => { + this.computeHeights(isInit); + }, 300); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', 'none'); // avoids flickering when changing orientation + } + + expand() { + this._footerMeta.lastPosY = 0; + this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, 0, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, 0, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', '300ms ease-in-out'); + + this.onExpand.emit(null); + } + + collapse(isInit: boolean = false) { + this._footerMeta.lastPosY = this._footerMeta.height - this._footerMeta.defaultHeight - this._currentViewMeta.tabsHeight; + this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); + + if (!isInit) this.onCollapse.emit(null); + } + + minimize() { + this._footerMeta.lastPosY = this._footerMeta.height; + this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); + + this.onMinimize.emit(null); + } + + + onTap(e: any) { + e.preventDefault(); + + if (this.state == IonPullUpFooterState.Collapsed) { + if (this.defaultBehavior == IonPullUpFooterBehavior.Hide) + this.state = IonPullUpFooterState.Minimized; + else + this.state = IonPullUpFooterState.Expanded; + } else { + if (this.state == IonPullUpFooterState.Minimized) { + if (this.defaultBehavior == IonPullUpFooterBehavior.Hide) + this.state = IonPullUpFooterState.Collapsed; + else + this.state = IonPullUpFooterState.Expanded; + } else { + // footer is expanded + this.state = this.initialState == IonPullUpFooterState.Minimized ? IonPullUpFooterState.Minimized : IonPullUpFooterState.Collapsed; + } + } + } + + + onDrag(e: any) { + e.preventDefault(); + + switch (e.type) { + case 'panstart': + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', 'none'); + break; + case 'pan': + this._footerMeta.posY = Math.round(e.deltaY) + this._footerMeta.lastPosY; + if (this._footerMeta.posY < 0 || this._footerMeta.posY > this._footerMeta.height) return; + this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, ' + this._footerMeta.posY + 'px, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, ' + this._footerMeta.posY + 'px, 0)'); + break; + case 'panend': + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', '300ms ease-in-out'); + + if (this._footerMeta.lastPosY > this._footerMeta.posY) { + this.state = IonPullUpFooterState.Expanded; + } + else if (this._footerMeta.lastPosY < this._footerMeta.posY) { + this.state = (this.initialState == IonPullUpFooterState.Minimized) ? IonPullUpFooterState.Minimized : IonPullUpFooterState.Collapsed; + } + + break; + } + } + + ngDoCheck() { + if (!Object.is(this.state, this._oldState)) { + switch (this.state) { + case IonPullUpFooterState.Collapsed: + this.collapse(); + break; + case IonPullUpFooterState.Expanded: + this.expand(); + break; + case IonPullUpFooterState.Minimized: + this.minimize(); + break; + } + this._oldState = this.state; + + // TODO: fix hack due to BUG (https://github.com/angular/angular/issues/6005) + window.setTimeout(() => { + this.stateChange.emit(this.state); + }) + } + } + +} \ No newline at end of file diff --git a/demo/ionic-pullup/package.json b/demo/ionic-pullup/package.json new file mode 100644 index 0000000..db035ff --- /dev/null +++ b/demo/ionic-pullup/package.json @@ -0,0 +1,38 @@ +{ + "name": "ionic-pullup", + "description": "ionic-pullup for Angular 2 and Ionic 2", + "version": "2.1.0", + "license": "MIT", + "main": "index.js", + "typings": "index.d.ts", + "repository": { + "type": "git", + "url": "https://github.com/arielfaur/ionic-pullup.git" + }, + "dependencies": { + "@angular/common": "2.1.1", + "@angular/compiler": "2.1.1", + "@angular/compiler-cli": "2.1.1", + "@angular/core": "2.1.1", + "@angular/forms": "2.1.1", + "@angular/http": "2.1.1", + "@angular/platform-browser": "2.1.1", + "@angular/platform-browser-dynamic": "2.1.1", + "@angular/platform-server": "2.1.1", + "@ionic/storage": "1.1.6", + "ionic-angular": "2.0.0-rc.2", + "ionic-native": "2.2.3", + "ionicons": "3.0.0", + "rxjs": "5.0.0-beta.12", + "zone.js": "0.6.21" + }, + "devDependencies": { + "@ionic/app-scripts": "0.0.44", + "@types/core-js": "^0.9.35", + "typescript": "2.0.6" + }, + "scripts": { + "build": "npm install && tsc -p tsconfig.json", + "release": "npm run build && npm publish" + } +} diff --git a/demo/ionic-pullup/src/ion-pullup-tab.js b/demo/ionic-pullup/src/ion-pullup-tab.js new file mode 100644 index 0000000..e1ab3d8 --- /dev/null +++ b/demo/ionic-pullup/src/ion-pullup-tab.js @@ -0,0 +1,70 @@ +/* +ionic-pullup v2 for Ionic/Angular 2 + +Copyright 2016 Ariel Faur (https://github.com/arielfaur) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +import { Component, ElementRef, Renderer, Input } from '@angular/core'; +import { Gesture } from 'ionic-angular/gestures/gesture'; +import { IonPullUpComponent, IonPullUpFooterState } from './ion-pullup'; +export var IonPullUpTabComponent = (function () { + function IonPullUpTabComponent(el, renderer) { + this.el = el; + this.renderer = renderer; + } + Object.defineProperty(IonPullUpTabComponent.prototype, "IsExpanded", { + get: function () { + return this.footer.state == IonPullUpFooterState.Expanded; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(IonPullUpTabComponent.prototype, "IsCollapsed", { + get: function () { + return this.footer.state == IonPullUpFooterState.Collapsed; + }, + enumerable: true, + configurable: true + }); + IonPullUpTabComponent.prototype.ngOnInit = function () { + var _this = this; + var tabGesture = new Gesture(this.el.nativeElement); + tabGesture.listen(); + tabGesture.on('tap', function (e) { + _this.footer && _this.footer.onTap(e); + }); + tabGesture.on('pan panstart panend', function (e) { + _this.footer && _this.footer.onDrag(e); + }); + }; + __decorate([ + Input(), + __metadata('design:type', IonPullUpComponent) + ], IonPullUpTabComponent.prototype, "footer", void 0); + IonPullUpTabComponent = __decorate([ + Component({ + selector: 'ion-pullup-tab', + template: '' + }), + __metadata('design:paramtypes', [ElementRef, Renderer]) + ], IonPullUpTabComponent); + return IonPullUpTabComponent; +}()); +//# sourceMappingURL=ion-pullup-tab.js.map \ No newline at end of file diff --git a/demo/ionic-pullup/src/ion-pullup-tab.js.map b/demo/ionic-pullup/src/ion-pullup-tab.js.map new file mode 100644 index 0000000..9ad40ef --- /dev/null +++ b/demo/ionic-pullup/src/ion-pullup-tab.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ion-pullup-tab.js","sourceRoot":"","sources":["ion-pullup-tab.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;EAaE;;;;;;;;;;OAEK,EAAY,SAAS,EAA6D,UAAU,EAAE,QAAQ,EAAwB,KAAK,EAAoD,MAAM,eAAe;OAC5M,EAAC,OAAO,EAAC,MAAM,gCAAgC;OAE/C,EAAC,kBAAkB,EAAE,oBAAoB,EAAC,MAAM,cAAc;AAMrE;IAGE,+BAAoB,EAAc,EAAU,QAAkB;QAA1C,OAAE,GAAF,EAAE,CAAY;QAAU,aAAQ,GAAR,QAAQ,CAAU;IAE9D,CAAC;IAED,sBAAW,6CAAU;aAArB;YACE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,oBAAoB,CAAC,QAAQ,CAAC;QAC5D,CAAC;;;OAAA;IAED,sBAAW,8CAAW;aAAtB;YACE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,oBAAoB,CAAC,SAAS,CAAC;QAC7D,CAAC;;;OAAA;IAED,wCAAQ,GAAR;QAAA,iBASC;QARC,IAAI,UAAU,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC;QACpD,UAAU,CAAC,MAAM,EAAE,CAAC;QACpB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,UAAA,CAAC;YACpB,KAAI,CAAC,MAAM,IAAI,KAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;QACH,UAAU,CAAC,EAAE,CAAC,qBAAqB,EAAE,UAAA,CAAC;YACpC,KAAI,CAAC,MAAM,IAAI,KAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC;IAvBD;QAAC,KAAK,EAAE;;yDAAA;IALV;QAAC,SAAS,CAAC;YACP,QAAQ,EAAE,gBAAgB;YAC1B,QAAQ,EAAE,2BAA2B;SACxC,CAAC;;6BAAA;IA2BF,4BAAC;AAAD,CAAC,AA1BD,IA0BC"} \ No newline at end of file diff --git a/src/ionic-pullup/ion-pullup-tab.ts b/demo/ionic-pullup/src/ion-pullup-tab.ts similarity index 100% rename from src/ionic-pullup/ion-pullup-tab.ts rename to demo/ionic-pullup/src/ion-pullup-tab.ts diff --git a/demo/ionic-pullup/src/ion-pullup.js b/demo/ionic-pullup/src/ion-pullup.js new file mode 100644 index 0000000..c471d52 --- /dev/null +++ b/demo/ionic-pullup/src/ion-pullup.js @@ -0,0 +1,261 @@ +/* + ionic-pullup v2 for Ionic/Angular 2 + + Copyright 2016 Ariel Faur (https://github.com/arielfaur) + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +import { ChangeDetectionStrategy, Component, EventEmitter, ElementRef, Renderer, ViewChild, ContentChild, Output, Input } from '@angular/core'; +import { Gesture } from 'ionic-angular/gestures/gesture'; +import { Toolbar } from 'ionic-angular/components/toolbar/toolbar'; +import { Platform } from 'ionic-angular'; +export var IonPullUpFooterState; +(function (IonPullUpFooterState) { + IonPullUpFooterState[IonPullUpFooterState["Collapsed"] = 0] = "Collapsed"; + IonPullUpFooterState[IonPullUpFooterState["Expanded"] = 1] = "Expanded"; + IonPullUpFooterState[IonPullUpFooterState["Minimized"] = 2] = "Minimized"; +})(IonPullUpFooterState || (IonPullUpFooterState = {})); +export var IonPullUpFooterBehavior; +(function (IonPullUpFooterBehavior) { + IonPullUpFooterBehavior[IonPullUpFooterBehavior["Hide"] = 0] = "Hide"; + IonPullUpFooterBehavior[IonPullUpFooterBehavior["Expand"] = 1] = "Expand"; +})(IonPullUpFooterBehavior || (IonPullUpFooterBehavior = {})); +export var IonPullUpComponent = (function () { + function IonPullUpComponent(platform, el, renderer) { + this.platform = platform; + this.el = el; + this.renderer = renderer; + this.stateChange = new EventEmitter(); + this.onExpand = new EventEmitter(); + this.onCollapse = new EventEmitter(); + this.onMinimize = new EventEmitter(); + this._footerMeta = { + height: 0, + posY: 0, + lastPosY: 0 + }; + this._currentViewMeta = {}; + // sets initial state + this.initialState = this.initialState || IonPullUpFooterState.Collapsed; + this.defaultBehavior = this.defaultBehavior || IonPullUpFooterBehavior.Expand; + this.maxHeight = this.maxHeight || 0; + } + IonPullUpComponent.prototype.ngOnInit = function () { + var _this = this; + console.debug('ionic-pullup => Initializing footer...'); + window.addEventListener("orientationchange", function () { + console.debug('ionic-pullup => Changed orientation => updating'); + _this.updateUI(); + }); + this.platform.resume.subscribe(function () { + console.debug('ionic-pullup => Resumed from background => updating'); + _this.updateUI(); + }); + }; + IonPullUpComponent.prototype.ngAfterContentInit = function () { + var _this = this; + this.computeDefaults(); + var barGesture = new Gesture(this.childToolbar.getNativeElement()); + barGesture.listen(); + barGesture.on('tap', function (e) { + _this.onTap(e); + }); + barGesture.on('pan panstart panend', function (e) { + _this.onDrag(e); + }); + this.state = IonPullUpFooterState.Collapsed; + this.updateUI(true); // need to indicate whether it's first run to avoid emitting events twice due to change detection + }; + Object.defineProperty(IonPullUpComponent.prototype, "expandedHeight", { + get: function () { + return window.innerHeight - this._currentViewMeta.headerHeight; + }, + enumerable: true, + configurable: true + }); + IonPullUpComponent.prototype.computeDefaults = function () { + this._footerMeta.defaultHeight = this.childFooter.nativeElement.offsetHeight; + // TODO: still need to test with tabs template (not convinced it is a valid use case...) + this._currentViewMeta.tabs = this.el.nativeElement.closest('ion-tabs'); + this._currentViewMeta.tabsHeight = this._currentViewMeta.tabs ? this._currentViewMeta.tabs.querySelector('.tabbar').offsetHeight : 0; + console.debug(this._currentViewMeta.tabsHeight ? 'ionic-pullup => Tabs detected' : 'ionic.pullup => View has no tabs'); + //this._currentViewMeta.hasBottomTabs = this._currentViewMeta.tabs && this._currentViewMeta.tabs.classList.contains('tabs-bottom'); + this._currentViewMeta.header = document.querySelector('ion-navbar.toolbar'); + this._currentViewMeta.headerHeight = this._currentViewMeta.header ? this._currentViewMeta.header.offsetHeight : 0; + }; + IonPullUpComponent.prototype.computeHeights = function (isInit) { + if (isInit === void 0) { isInit = false; } + this._footerMeta.height = this.maxHeight > 0 ? this.maxHeight : this.expandedHeight; + this.renderer.setElementStyle(this.childFooter.nativeElement, 'height', this._footerMeta.height + 'px'); + // TODO: implement minimize mode + //this.renderer.setElementStyle(this.el.nativeElement, 'min-height', this._footerMeta.height + 'px'); + //if (this.initialState == IonPullUpFooterState.Minimized) { + // this.minimize() + //} else { + this.collapse(isInit); + //} + }; + IonPullUpComponent.prototype.updateUI = function (isInit) { + var _this = this; + if (isInit === void 0) { isInit = false; } + setTimeout(function () { + _this.computeHeights(isInit); + }, 300); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', 'none'); // avoids flickering when changing orientation + }; + IonPullUpComponent.prototype.expand = function () { + this._footerMeta.lastPosY = 0; + this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, 0, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, 0, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', '300ms ease-in-out'); + this.onExpand.emit(null); + }; + IonPullUpComponent.prototype.collapse = function (isInit) { + if (isInit === void 0) { isInit = false; } + this._footerMeta.lastPosY = this._footerMeta.height - this._footerMeta.defaultHeight - this._currentViewMeta.tabsHeight; + this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); + if (!isInit) + this.onCollapse.emit(null); + }; + IonPullUpComponent.prototype.minimize = function () { + this._footerMeta.lastPosY = this._footerMeta.height; + this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); + this.onMinimize.emit(null); + }; + IonPullUpComponent.prototype.onTap = function (e) { + e.preventDefault(); + if (this.state == IonPullUpFooterState.Collapsed) { + if (this.defaultBehavior == IonPullUpFooterBehavior.Hide) + this.state = IonPullUpFooterState.Minimized; + else + this.state = IonPullUpFooterState.Expanded; + } + else { + if (this.state == IonPullUpFooterState.Minimized) { + if (this.defaultBehavior == IonPullUpFooterBehavior.Hide) + this.state = IonPullUpFooterState.Collapsed; + else + this.state = IonPullUpFooterState.Expanded; + } + else { + // footer is expanded + this.state = this.initialState == IonPullUpFooterState.Minimized ? IonPullUpFooterState.Minimized : IonPullUpFooterState.Collapsed; + } + } + }; + IonPullUpComponent.prototype.onDrag = function (e) { + e.preventDefault(); + switch (e.type) { + case 'panstart': + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', 'none'); + break; + case 'pan': + this._footerMeta.posY = Math.round(e.deltaY) + this._footerMeta.lastPosY; + if (this._footerMeta.posY < 0 || this._footerMeta.posY > this._footerMeta.height) + return; + this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, ' + this._footerMeta.posY + 'px, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, ' + this._footerMeta.posY + 'px, 0)'); + break; + case 'panend': + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', '300ms ease-in-out'); + if (this._footerMeta.lastPosY > this._footerMeta.posY) { + this.state = IonPullUpFooterState.Expanded; + } + else if (this._footerMeta.lastPosY < this._footerMeta.posY) { + this.state = (this.initialState == IonPullUpFooterState.Minimized) ? IonPullUpFooterState.Minimized : IonPullUpFooterState.Collapsed; + } + break; + } + }; + IonPullUpComponent.prototype.ngDoCheck = function () { + var _this = this; + if (!Object.is(this.state, this._oldState)) { + switch (this.state) { + case IonPullUpFooterState.Collapsed: + this.collapse(); + break; + case IonPullUpFooterState.Expanded: + this.expand(); + break; + case IonPullUpFooterState.Minimized: + this.minimize(); + break; + } + this._oldState = this.state; + // TODO: fix hack due to BUG (https://github.com/angular/angular/issues/6005) + window.setTimeout(function () { + _this.stateChange.emit(_this.state); + }); + } + }; + __decorate([ + Input(), + __metadata('design:type', Number) + ], IonPullUpComponent.prototype, "state", void 0); + __decorate([ + Output(), + __metadata('design:type', EventEmitter) + ], IonPullUpComponent.prototype, "stateChange", void 0); + __decorate([ + Input(), + __metadata('design:type', Number) + ], IonPullUpComponent.prototype, "initialState", void 0); + __decorate([ + // TODO implemment + Input(), + __metadata('design:type', Number) + ], IonPullUpComponent.prototype, "defaultBehavior", void 0); + __decorate([ + // TODO implemment + Input(), + __metadata('design:type', Number) + ], IonPullUpComponent.prototype, "maxHeight", void 0); + __decorate([ + Output(), + __metadata('design:type', Object) + ], IonPullUpComponent.prototype, "onExpand", void 0); + __decorate([ + Output(), + __metadata('design:type', Object) + ], IonPullUpComponent.prototype, "onCollapse", void 0); + __decorate([ + Output(), + __metadata('design:type', Object) + ], IonPullUpComponent.prototype, "onMinimize", void 0); + __decorate([ + ContentChild(Toolbar), + __metadata('design:type', Object) + ], IonPullUpComponent.prototype, "childToolbar", void 0); + __decorate([ + ViewChild('footer'), + __metadata('design:type', Object) + ], IonPullUpComponent.prototype, "childFooter", void 0); + IonPullUpComponent = __decorate([ + Component({ + selector: 'ion-pullup', + changeDetection: ChangeDetectionStrategy.OnPush, + template: "\n \n \n \n " + }), + __metadata('design:paramtypes', [Platform, ElementRef, Renderer]) + ], IonPullUpComponent); + return IonPullUpComponent; +}()); +//# sourceMappingURL=ion-pullup.js.map \ No newline at end of file diff --git a/demo/ionic-pullup/src/ion-pullup.js.map b/demo/ionic-pullup/src/ion-pullup.js.map new file mode 100644 index 0000000..f64a09b --- /dev/null +++ b/demo/ionic-pullup/src/ion-pullup.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ion-pullup.js","sourceRoot":"","sources":["ion-pullup.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/demo/ionic-pullup/src/ion-pullup.module.js b/demo/ionic-pullup/src/ion-pullup.module.js new file mode 100644 index 0000000..d7f8943 --- /dev/null +++ b/demo/ionic-pullup/src/ion-pullup.module.js @@ -0,0 +1,41 @@ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { IonicModule } from 'ionic-angular'; +import { IonPullUpComponent } from './ion-pullup'; +import { IonPullUpTabComponent } from './ion-pullup-tab'; +export var IonPullUpModule = (function () { + function IonPullUpModule() { + } + IonPullUpModule = __decorate([ + NgModule({ + imports: [ + CommonModule, + FormsModule, + IonicModule + ], + declarations: [ + IonPullUpComponent, + IonPullUpTabComponent + ], + exports: [ + IonPullUpComponent, + IonPullUpTabComponent + ], + providers: [], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + }), + __metadata('design:paramtypes', []) + ], IonPullUpModule); + return IonPullUpModule; +}()); +//# sourceMappingURL=ion-pullup.module.js.map \ No newline at end of file diff --git a/demo/ionic-pullup/src/ion-pullup.module.js.map b/demo/ionic-pullup/src/ion-pullup.module.js.map new file mode 100644 index 0000000..2ca5a61 --- /dev/null +++ b/demo/ionic-pullup/src/ion-pullup.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ion-pullup.module.js","sourceRoot":"","sources":["ion-pullup.module.ts"],"names":[],"mappings":";;;;;;;;;OAAO,EAAC,QAAQ,EAAE,sBAAsB,EAAC,MAAM,eAAe;OACvD,EAAC,YAAY,EAAC,MAAM,iBAAiB;OACrC,EAAC,WAAW,EAAC,MAAM,gBAAgB;OACnC,EAAC,WAAW,EAAC,MAAM,eAAe;OAElC,EAAC,kBAAkB,EAAC,MAAM,cAAc;OACxC,EAAC,qBAAqB,EAAC,MAAM,kBAAkB;AAmBtD;IAAA;IACA,CAAC;IAlBD;QAAC,QAAQ,CAAC;YACN,OAAO,EAAE;gBACL,YAAY;gBACZ,WAAW;gBACX,WAAW;aACd;YACD,YAAY,EAAE;gBACV,kBAAkB;gBAClB,qBAAqB;aACxB;YACD,OAAO,EAAE;gBACL,kBAAkB;gBAClB,qBAAqB;aACxB;YACD,SAAS,EAAE,EAAE;YACb,OAAO,EAAE,CAAC,sBAAsB,CAAC;SACpC,CAAC;;uBAAA;IAEF,sBAAC;AAAD,CAAC,AADD,IACC"} \ No newline at end of file diff --git a/demo/ionic-pullup/src/ion-pullup.module.ts b/demo/ionic-pullup/src/ion-pullup.module.ts new file mode 100644 index 0000000..072c2f9 --- /dev/null +++ b/demo/ionic-pullup/src/ion-pullup.module.ts @@ -0,0 +1,27 @@ +import {NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {FormsModule} from '@angular/forms'; +import {IonicModule} from 'ionic-angular'; + +import {IonPullUpComponent} from './ion-pullup'; +import {IonPullUpTabComponent} from './ion-pullup-tab'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + IonicModule + ], + declarations: [ + IonPullUpComponent, + IonPullUpTabComponent + ], + exports: [ + IonPullUpComponent, + IonPullUpTabComponent + ], + providers: [], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class IonPullUpModule { +} \ No newline at end of file diff --git a/demo/src/app/ionic-pullup/ion-pullup.scss b/demo/ionic-pullup/src/ion-pullup.scss similarity index 90% rename from demo/src/app/ionic-pullup/ion-pullup.scss rename to demo/ionic-pullup/src/ion-pullup.scss index a332fe8..7e544a7 100644 --- a/demo/src/app/ionic-pullup/ion-pullup.scss +++ b/demo/ionic-pullup/src/ion-pullup.scss @@ -13,15 +13,15 @@ $tabShadow: #666; // shadow color background: $background; color: $foreground; border-radius: $radius $radius 0 0; - margin: 0 auto; + margin: 0 auto; } @mixin pullup-tab-shadow($shadowColor) { - box-shadow: 0px -2px 2px $shadowColor; + box-shadow: 0px -2px 2px $shadowColor; } -ion-pullup { - background : color($colors, primary, base); +ion-pullup { + background : color($colors, primary, base); ion-footer { &::before { @@ -39,12 +39,12 @@ ion-pullup { ion-content { position:absolute; - + .ios & { @import 'node_modules/ionic-angular/components/toolbar/toolbar.ios'; margin-top: calc(#{$toolbar-ios-height} + #{$tabHeight}); - scroll-content { + .scroll-content { margin-bottom: calc(#{$toolbar-ios-height} + #{$tabHeight}); } } @@ -52,7 +52,7 @@ ion-pullup { @import 'node_modules/ionic-angular/components/toolbar/toolbar.md'; margin-top: calc(#{$toolbar-md-height} + #{$tabHeight}); - scroll-content { + .scroll-content { margin-bottom: calc(#{$toolbar-md-height} + #{$tabHeight}); } } @@ -60,12 +60,12 @@ ion-pullup { ion-toolbar { @include pullup-tab-shadow($tabShadow); - + .toolbar-background { .ios & { border-top: none; } } } - } + } } \ No newline at end of file diff --git a/demo/ionic-pullup/src/ion-pullup.ts b/demo/ionic-pullup/src/ion-pullup.ts new file mode 100644 index 0000000..16a46a3 --- /dev/null +++ b/demo/ionic-pullup/src/ion-pullup.ts @@ -0,0 +1,285 @@ +/* + ionic-pullup v2 for Ionic/Angular 2 + + Copyright 2016 Ariel Faur (https://github.com/arielfaur) + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { + Attribute, + ChangeDetectionStrategy, + Component, + Directive, + DoCheck, + SimpleChange, + OnChanges, + EventEmitter, + ElementRef, + Renderer, + ViewChild, + ContentChild, + Output, + Input, + Injectable, + Inject, + Optional +} from '@angular/core'; +import {Gesture} from 'ionic-angular/gestures/gesture'; +import {Toolbar, Footer} from 'ionic-angular/components/toolbar/toolbar'; +import {Platform} from 'ionic-angular'; + +interface FooterMetadata { + height: number; + posY: number; + lastPosY: number; + defaultHeight?: number; +} + +interface ViewMetadata { + tabs?: Element; + tabsHeight?: number; + hasBottomTabs?: boolean; + header?: Element; + headerHeight?: number; +} + +interface FooterTab { + x?: number; + y?: number; + upperLeftRadius?: number; + upperRightRadius?: number; + backgroundColor?: string; + color?: string; + content?: string; +} + +export enum IonPullUpFooterState { + Collapsed = 0, + Expanded = 1, + Minimized = 2 +} + +export enum IonPullUpFooterBehavior { + Hide, + Expand +} + +@Component({ + selector: 'ion-pullup', + changeDetection: ChangeDetectionStrategy.OnPush, + template: ` + + + + ` +}) +export class IonPullUpComponent { + @Input() state: IonPullUpFooterState; + @Output() stateChange: EventEmitter = new EventEmitter(); + + @Input() initialState: IonPullUpFooterState; // TODO implemment + @Input() defaultBehavior: IonPullUpFooterBehavior; // TODO implemment + @Input() maxHeight: number; + + @Output() onExpand = new EventEmitter(); + @Output() onCollapse = new EventEmitter(); + @Output() onMinimize = new EventEmitter(); + + @ContentChild(Toolbar) childToolbar; + @ViewChild('footer') childFooter; + + protected _footerMeta: FooterMetadata; + protected _currentViewMeta: ViewMetadata; + protected _oldState: IonPullUpFooterState; + + constructor(private platform: Platform, private el: ElementRef, private renderer: Renderer) { + this._footerMeta = { + height: 0, + posY: 0, + lastPosY: 0 + } + this._currentViewMeta = {}; + + // sets initial state + this.initialState = this.initialState || IonPullUpFooterState.Collapsed; + this.defaultBehavior = this.defaultBehavior || IonPullUpFooterBehavior.Expand; + this.maxHeight = this.maxHeight || 0; + } + + ngOnInit() { + console.debug('ionic-pullup => Initializing footer...'); + + window.addEventListener("orientationchange", () => { + console.debug('ionic-pullup => Changed orientation => updating'); + this.updateUI(); + }); + this.platform.resume.subscribe(() => { + console.debug('ionic-pullup => Resumed from background => updating'); + this.updateUI(); + }); + } + + ngAfterContentInit() { + this.computeDefaults(); + + let barGesture = new Gesture(this.childToolbar.getNativeElement()); + barGesture.listen(); + barGesture.on('tap', e => { + this.onTap(e); + }); + barGesture.on('pan panstart panend', e => { + this.onDrag(e); + }); + + this.state = IonPullUpFooterState.Collapsed; + + this.updateUI(true); // need to indicate whether it's first run to avoid emitting events twice due to change detection + + } + + public get expandedHeight(): number { + return window.innerHeight - this._currentViewMeta.headerHeight; + } + + computeDefaults() { + this._footerMeta.defaultHeight = this.childFooter.nativeElement.offsetHeight; + + + // TODO: still need to test with tabs template (not convinced it is a valid use case...) + this._currentViewMeta.tabs = this.el.nativeElement.closest('ion-tabs'); + this._currentViewMeta.tabsHeight = this._currentViewMeta.tabs ? ( this._currentViewMeta.tabs.querySelector('.tabbar')).offsetHeight : 0 + console.debug(this._currentViewMeta.tabsHeight ? 'ionic-pullup => Tabs detected' : 'ionic.pullup => View has no tabs'); + //this._currentViewMeta.hasBottomTabs = this._currentViewMeta.tabs && this._currentViewMeta.tabs.classList.contains('tabs-bottom'); + + + this._currentViewMeta.header = document.querySelector('ion-navbar.toolbar'); + this._currentViewMeta.headerHeight = this._currentViewMeta.header ? (this._currentViewMeta.header).offsetHeight : 0; + } + + computeHeights(isInit: boolean = false) { + this._footerMeta.height = this.maxHeight > 0 ? this.maxHeight : this.expandedHeight; + + this.renderer.setElementStyle(this.childFooter.nativeElement, 'height', this._footerMeta.height + 'px'); + + // TODO: implement minimize mode + //this.renderer.setElementStyle(this.el.nativeElement, 'min-height', this._footerMeta.height + 'px'); + //if (this.initialState == IonPullUpFooterState.Minimized) { + // this.minimize() + //} else { + this.collapse(isInit); + //} + } + + updateUI(isInit: boolean = false) { + setTimeout(() => { + this.computeHeights(isInit); + }, 300); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', 'none'); // avoids flickering when changing orientation + } + + expand() { + this._footerMeta.lastPosY = 0; + this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, 0, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, 0, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', '300ms ease-in-out'); + + this.onExpand.emit(null); + } + + collapse(isInit: boolean = false) { + this._footerMeta.lastPosY = this._footerMeta.height - this._footerMeta.defaultHeight - this._currentViewMeta.tabsHeight; + this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); + + if (!isInit) this.onCollapse.emit(null); + } + + minimize() { + this._footerMeta.lastPosY = this._footerMeta.height; + this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); + + this.onMinimize.emit(null); + } + + + onTap(e: any) { + e.preventDefault(); + + if (this.state == IonPullUpFooterState.Collapsed) { + if (this.defaultBehavior == IonPullUpFooterBehavior.Hide) + this.state = IonPullUpFooterState.Minimized; + else + this.state = IonPullUpFooterState.Expanded; + } else { + if (this.state == IonPullUpFooterState.Minimized) { + if (this.defaultBehavior == IonPullUpFooterBehavior.Hide) + this.state = IonPullUpFooterState.Collapsed; + else + this.state = IonPullUpFooterState.Expanded; + } else { + // footer is expanded + this.state = this.initialState == IonPullUpFooterState.Minimized ? IonPullUpFooterState.Minimized : IonPullUpFooterState.Collapsed; + } + } + } + + + onDrag(e: any) { + e.preventDefault(); + + switch (e.type) { + case 'panstart': + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', 'none'); + break; + case 'pan': + this._footerMeta.posY = Math.round(e.deltaY) + this._footerMeta.lastPosY; + if (this._footerMeta.posY < 0 || this._footerMeta.posY > this._footerMeta.height) return; + this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, ' + this._footerMeta.posY + 'px, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, ' + this._footerMeta.posY + 'px, 0)'); + break; + case 'panend': + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', '300ms ease-in-out'); + + if (this._footerMeta.lastPosY > this._footerMeta.posY) { + this.state = IonPullUpFooterState.Expanded; + } + else if (this._footerMeta.lastPosY < this._footerMeta.posY) { + this.state = (this.initialState == IonPullUpFooterState.Minimized) ? IonPullUpFooterState.Minimized : IonPullUpFooterState.Collapsed; + } + + break; + } + } + + ngDoCheck() { + if (!Object.is(this.state, this._oldState)) { + switch (this.state) { + case IonPullUpFooterState.Collapsed: + this.collapse(); + break; + case IonPullUpFooterState.Expanded: + this.expand(); + break; + case IonPullUpFooterState.Minimized: + this.minimize(); + break; + } + this._oldState = this.state; + + // TODO: fix hack due to BUG (https://github.com/angular/angular/issues/6005) + window.setTimeout(() => { + this.stateChange.emit(this.state); + }) + } + } + +} \ No newline at end of file diff --git a/demo/ionic-pullup/tsconfig.json b/demo/ionic-pullup/tsconfig.json new file mode 100644 index 0000000..3a47c85 --- /dev/null +++ b/demo/ionic-pullup/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "es2015", + "moduleResolution": "node", + "sourceMap": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true + }, + "files": [ + "index.ts" + ] +} diff --git a/demo/ionic-pullup/tslint.json b/demo/ionic-pullup/tslint.json new file mode 100644 index 0000000..a75b2c9 --- /dev/null +++ b/demo/ionic-pullup/tslint.json @@ -0,0 +1,6 @@ +{ + "extends": "tslint-ionic-rules", + "rules" : { + + } +} \ No newline at end of file diff --git a/demo/ionic.config.json b/demo/ionic.config.json index 84073d5..68b5f01 100644 --- a/demo/ionic.config.json +++ b/demo/ionic.config.json @@ -1,5 +1,5 @@ { - "name": "demo", + "name": "ionic-pullup-demo", "app_id": "", "v2": true, "typescript": true diff --git a/demo/package.json b/demo/package.json index 2d69546..20f298c 100644 --- a/demo/package.json +++ b/demo/package.json @@ -4,38 +4,49 @@ "homepage": "http://ionicframework.com/", "private": true, "scripts": { + "clean": "ionic-app-scripts clean", + "build": "ionic-app-scripts build", "ionic:build": "ionic-app-scripts build", "ionic:serve": "ionic-app-scripts serve" }, "dependencies": { - "@angular/common": "2.1.1", - "@angular/compiler": "2.1.1", - "@angular/compiler-cli": "2.1.1", - "@angular/core": "2.1.1", - "@angular/forms": "2.1.1", - "@angular/http": "2.1.1", - "@angular/platform-browser": "2.1.1", - "@angular/platform-browser-dynamic": "2.1.1", - "@angular/platform-server": "2.1.1", - "@ionic/storage": "1.1.6", - "ionic-angular": "2.0.0-rc.2", - "ionic-native": "2.2.3", + "@angular/common": "2.2.1", + "@angular/compiler": "2.2.1", + "@angular/compiler-cli": "2.2.1", + "@angular/core": "2.2.1", + "@angular/forms": "2.2.1", + "@angular/http": "2.2.1", + "@angular/platform-browser": "2.2.1", + "@angular/platform-browser-dynamic": "2.2.1", + "@angular/platform-server": "2.2.1", + "@ionic/storage": "1.1.7", + "ionic-angular": "2.0.0", + "ionic-native": "2.4.1", "ionicons": "3.0.0", "rxjs": "5.0.0-beta.12", - "zone.js": "0.6.21" + "zone.js": "0.6.26", + "sw-toolbox": "3.4.0", + "ionic-pullup": "file:./ionic-pullup" }, "devDependencies": { - "@ionic/app-scripts": "0.0.44", - "typescript": "2.0.6" + "@ionic/app-scripts": "1.0.0", + "typescript": "2.0.9" }, "cordovaPlugins": [ "cordova-plugin-whitelist", + "cordova-plugin-statusbar", "cordova-plugin-console", "cordova-plugin-device", - "cordova-plugin-statusbar", "cordova-plugin-splashscreen", "ionic-plugin-keyboard" ], - "cordovaPlatforms": [], - "description": "demo: An Ionic project" + "cordovaPlatforms": [ + "ios", + { + "platform": "ios", + "version": "", + "locator": "ios" + } + ], + "description": "ionic-pullup-demo: An Ionic project" } diff --git a/demo/resources/android/icon/drawable-hdpi-icon.png b/demo/resources/android/icon/drawable-hdpi-icon.png deleted file mode 100644 index b910093..0000000 Binary files a/demo/resources/android/icon/drawable-hdpi-icon.png and /dev/null differ diff --git a/demo/resources/android/icon/drawable-ldpi-icon.png b/demo/resources/android/icon/drawable-ldpi-icon.png deleted file mode 100644 index 16cd5db..0000000 Binary files a/demo/resources/android/icon/drawable-ldpi-icon.png and /dev/null differ diff --git a/demo/resources/android/icon/drawable-mdpi-icon.png b/demo/resources/android/icon/drawable-mdpi-icon.png deleted file mode 100644 index 64a6cbc..0000000 Binary files a/demo/resources/android/icon/drawable-mdpi-icon.png and /dev/null differ diff --git a/demo/resources/android/icon/drawable-xhdpi-icon.png b/demo/resources/android/icon/drawable-xhdpi-icon.png deleted file mode 100644 index 1605f69..0000000 Binary files a/demo/resources/android/icon/drawable-xhdpi-icon.png and /dev/null differ diff --git a/demo/resources/android/icon/drawable-xxhdpi-icon.png b/demo/resources/android/icon/drawable-xxhdpi-icon.png deleted file mode 100644 index 56fb29e..0000000 Binary files a/demo/resources/android/icon/drawable-xxhdpi-icon.png and /dev/null differ diff --git a/demo/resources/android/icon/drawable-xxxhdpi-icon.png b/demo/resources/android/icon/drawable-xxxhdpi-icon.png deleted file mode 100644 index e4a9152..0000000 Binary files a/demo/resources/android/icon/drawable-xxxhdpi-icon.png and /dev/null differ diff --git a/demo/resources/ios/icon/icon-40.png b/demo/resources/ios/icon/icon-40.png deleted file mode 100644 index 76cc53c..0000000 Binary files a/demo/resources/ios/icon/icon-40.png and /dev/null differ diff --git a/demo/resources/ios/icon/icon-40@2x.png b/demo/resources/ios/icon/icon-40@2x.png deleted file mode 100644 index 64b4906..0000000 Binary files a/demo/resources/ios/icon/icon-40@2x.png and /dev/null differ diff --git a/demo/resources/ios/icon/icon-50.png b/demo/resources/ios/icon/icon-50.png deleted file mode 100644 index 8bd51df..0000000 Binary files a/demo/resources/ios/icon/icon-50.png and /dev/null differ diff --git a/demo/resources/ios/icon/icon-50@2x.png b/demo/resources/ios/icon/icon-50@2x.png deleted file mode 100644 index 2676f8f..0000000 Binary files a/demo/resources/ios/icon/icon-50@2x.png and /dev/null differ diff --git a/demo/resources/ios/icon/icon-60.png b/demo/resources/ios/icon/icon-60.png deleted file mode 100644 index 11f3912..0000000 Binary files a/demo/resources/ios/icon/icon-60.png and /dev/null differ diff --git a/demo/resources/ios/icon/icon-60@2x.png b/demo/resources/ios/icon/icon-60@2x.png deleted file mode 100644 index b521048..0000000 Binary files a/demo/resources/ios/icon/icon-60@2x.png and /dev/null differ diff --git a/demo/resources/ios/icon/icon-60@3x.png b/demo/resources/ios/icon/icon-60@3x.png deleted file mode 100644 index dbc8303..0000000 Binary files a/demo/resources/ios/icon/icon-60@3x.png and /dev/null differ diff --git a/demo/resources/ios/icon/icon-72.png b/demo/resources/ios/icon/icon-72.png deleted file mode 100644 index 4e5a827..0000000 Binary files a/demo/resources/ios/icon/icon-72.png and /dev/null differ diff --git a/demo/resources/ios/icon/icon-72@2x.png b/demo/resources/ios/icon/icon-72@2x.png deleted file mode 100644 index 56fb29e..0000000 Binary files a/demo/resources/ios/icon/icon-72@2x.png and /dev/null differ diff --git a/demo/resources/ios/icon/icon-76.png b/demo/resources/ios/icon/icon-76.png deleted file mode 100644 index e66a90e..0000000 Binary files a/demo/resources/ios/icon/icon-76.png and /dev/null differ diff --git a/demo/resources/ios/icon/icon-76@2x.png b/demo/resources/ios/icon/icon-76@2x.png deleted file mode 100644 index 3f5c942..0000000 Binary files a/demo/resources/ios/icon/icon-76@2x.png and /dev/null differ diff --git a/demo/resources/ios/icon/icon-small.png b/demo/resources/ios/icon/icon-small.png deleted file mode 100644 index de3146d..0000000 Binary files a/demo/resources/ios/icon/icon-small.png and /dev/null differ diff --git a/demo/resources/ios/icon/icon-small@2x.png b/demo/resources/ios/icon/icon-small@2x.png deleted file mode 100644 index 916a02e..0000000 Binary files a/demo/resources/ios/icon/icon-small@2x.png and /dev/null differ diff --git a/demo/resources/ios/icon/icon-small@3x.png b/demo/resources/ios/icon/icon-small@3x.png deleted file mode 100644 index 0efa99d..0000000 Binary files a/demo/resources/ios/icon/icon-small@3x.png and /dev/null differ diff --git a/demo/resources/ios/icon/icon.png b/demo/resources/ios/icon/icon.png deleted file mode 100644 index 89f8c00..0000000 Binary files a/demo/resources/ios/icon/icon.png and /dev/null differ diff --git a/demo/resources/ios/icon/icon@2x.png b/demo/resources/ios/icon/icon@2x.png deleted file mode 100644 index a6687a1..0000000 Binary files a/demo/resources/ios/icon/icon@2x.png and /dev/null differ diff --git a/demo/src/app/app.component.ts b/demo/src/app/app.component.ts index 218886a..9310530 100644 --- a/demo/src/app/app.component.ts +++ b/demo/src/app/app.component.ts @@ -2,14 +2,14 @@ import { Component } from '@angular/core'; import { Platform } from 'ionic-angular'; import { StatusBar, Splashscreen } from 'ionic-native'; -import { TabsPage } from '../pages/tabs/tabs'; +import { HomePage } from '../pages/home/home'; @Component({ - template: `` + templateUrl: 'app.html' }) export class MyApp { - rootPage = TabsPage; + rootPage = HomePage; constructor(platform: Platform) { platform.ready().then(() => { diff --git a/demo/src/app/app.html b/demo/src/app/app.html new file mode 100644 index 0000000..7b88c96 --- /dev/null +++ b/demo/src/app/app.html @@ -0,0 +1 @@ + diff --git a/demo/src/app/app.module.ts b/demo/src/app/app.module.ts index b6d0df5..9ddbf51 100644 --- a/demo/src/app/app.module.ts +++ b/demo/src/app/app.module.ts @@ -1,34 +1,25 @@ -import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { IonicApp, IonicModule } from 'ionic-angular'; +import { NgModule, ErrorHandler } from '@angular/core'; +import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular'; import { MyApp } from './app.component'; -import { AboutPage } from '../pages/about/about'; -import { ContactPage } from '../pages/contact/contact'; import { HomePage } from '../pages/home/home'; -import { TabsPage } from '../pages/tabs/tabs'; -import {IonPullupModule} from './ionic-pullup/ion-pullup.module'; +import {IonPullUpComponent, IonPullUpTabComponent} from 'ionic-pullup'; @NgModule({ declarations: [ MyApp, - AboutPage, - ContactPage, HomePage, - TabsPage + IonPullUpComponent, + IonPullUpTabComponent ], imports: [ - IonicModule.forRoot(MyApp), - IonPullupModule + IonicModule.forRoot(MyApp) ], bootstrap: [IonicApp], entryComponents: [ MyApp, - AboutPage, - ContactPage, - HomePage, - TabsPage + HomePage ], - providers: [], - schemas: [ CUSTOM_ELEMENTS_SCHEMA ] + providers: [{provide: ErrorHandler, useClass: IonicErrorHandler}] }) export class AppModule {} diff --git a/demo/src/app/app.scss b/demo/src/app/app.scss index a967d6f..acd3be3 100644 --- a/demo/src/app/app.scss +++ b/demo/src/app/app.scss @@ -14,3 +14,8 @@ // To declare rules for a specific mode, create a child rule // for the .md, .ios, or .wp mode classes. The mode class is // automatically applied to the element in the app. + +// Ionic Pullup +// http://arielfaur.github.io/ionic-pullup/ +// @import "../../node_modules/ionic-pullup/src/ion-pullup.scss"; + @import "../../ionic-pullup/src/ion-pullup.scss"; \ No newline at end of file diff --git a/demo/src/app/ionic-pullup/ion-pullup.module.ts b/demo/src/app/ionic-pullup/ion-pullup.module.ts deleted file mode 100644 index 28af041..0000000 --- a/demo/src/app/ionic-pullup/ion-pullup.module.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; -import { IonicModule } from 'ionic-angular'; - -import {IonPullUpComponent} from './ion-pullup'; -import {IonPullUpTabComponent} from './ion-pullup-tab'; - -@NgModule({ - imports: [ CommonModule, FormsModule, IonicModule ], - declarations: [ - IonPullUpComponent, - IonPullUpTabComponent - ], - exports: [ - IonPullUpComponent, - IonPullUpTabComponent - ], - providers: [], - schemas: [ CUSTOM_ELEMENTS_SCHEMA ] -}) -export class IonPullupModule { } - -export * from './ion-pullup'; -export * from './ion-pullup-tab'; \ No newline at end of file diff --git a/demo/src/app/ionic-pullup/ion-pullup.ts b/demo/src/app/ionic-pullup/ion-pullup.ts deleted file mode 100644 index 4e8f507..0000000 --- a/demo/src/app/ionic-pullup/ion-pullup.ts +++ /dev/null @@ -1,267 +0,0 @@ -/* -ionic-pullup v2 for Ionic/Angular 2 - -Copyright 2016 Ariel Faur (https://github.com/arielfaur) -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { Attribute, ChangeDetectionStrategy, Component, Directive, DoCheck, SimpleChange, OnChanges, EventEmitter, ElementRef, Renderer, ViewChild, ContentChild, Output, Input, Injectable, Inject, Optional } from '@angular/core'; -import { Gesture } from 'ionic-angular/gestures/gesture'; -import { Toolbar, Footer } from 'ionic-angular/components/toolbar/toolbar'; -import { Platform } from 'ionic-angular'; - -interface FooterMetadata { - height: number; - posY: number; - lastPosY: number; - defaultHeight?: number; -} - -interface ViewMetadata { - tabs?: Element; - tabsHeight?: number; - hasBottomTabs?: boolean; - header?: Element; - headerHeight?: number; -} - -interface FooterTab { - x?: number; - y?: number; - upperLeftRadius?: number; - upperRightRadius?: number; - backgroundColor?: string; - color?: string; - content?: string; -} - -export enum IonPullUpFooterState { - Collapsed = 0, - Expanded = 1, - Minimized = 2 -} - -export enum IonPullUpFooterBehavior { - Hide, - Expand -} - -@Component({ - selector: 'ion-pullup', - changeDetection: ChangeDetectionStrategy.OnPush, - template: ` - - - - ` -}) -export class IonPullUpComponent { - @Input() state: IonPullUpFooterState; - @Output() stateChange: EventEmitter = new EventEmitter(); - - @Input() initialState: IonPullUpFooterState; // TODO implemment - @Input() defaultBehavior: IonPullUpFooterBehavior; // TODO implemment - @Input() maxHeight: number; - - @Output() onExpand = new EventEmitter(); - @Output() onCollapse = new EventEmitter(); - @Output() onMinimize = new EventEmitter(); - - @ContentChild(Toolbar) childToolbar; - @ViewChild('footer') childFooter; - - protected _footerMeta: FooterMetadata; - protected _currentViewMeta: ViewMetadata; - protected _oldState: IonPullUpFooterState; - - constructor(private platform: Platform, private el: ElementRef, private renderer: Renderer) { - this._footerMeta = { - height: 0, - posY: 0, - lastPosY: 0 - } - this._currentViewMeta = {}; - - // sets initial state - this.initialState = this.initialState || IonPullUpFooterState.Collapsed; - this.defaultBehavior = this.defaultBehavior || IonPullUpFooterBehavior.Expand; - this.maxHeight = this.maxHeight || 0; - } - - ngOnInit() { - console.debug('ionic-pullup => Initializing footer...'); - - window.addEventListener("orientationchange", () => { - console.debug('ionic-pullup => Changed orientation => updating'); - this.updateUI(); - }); - this.platform.resume.subscribe(() => { - console.debug('ionic-pullup => Resumed from background => updating'); - this.updateUI(); - }); - } - - ngAfterContentInit() { - this.computeDefaults(); - - let barGesture = new Gesture(this.childToolbar.getNativeElement()); - barGesture.listen(); - barGesture.on('tap', e => { - this.onTap(e); - }); - barGesture.on('pan panstart panend', e => { - this.onDrag(e); - }); - - this.state = IonPullUpFooterState.Collapsed; - - this.updateUI(true); // need to indicate whether it's first run to avoid emitting events twice due to change detection - - } - - public get expandedHeight() : number { - return window.innerHeight - this._currentViewMeta.headerHeight; - } - - computeDefaults() { - this._footerMeta.defaultHeight = this.childFooter.nativeElement.offsetHeight; - - - // TODO: still need to test with tabs template (not convinced it is a valid use case...) - this._currentViewMeta.tabs = this.el.nativeElement.closest('ion-tabs'); - this._currentViewMeta.tabsHeight = this._currentViewMeta.tabs ? ( this._currentViewMeta.tabs.querySelector('.tabbar')).offsetHeight : 0 - console.debug (this._currentViewMeta.tabsHeight ? 'ionic-pullup => Tabs detected' : 'ionic.pullup => View has no tabs'); - //this._currentViewMeta.hasBottomTabs = this._currentViewMeta.tabs && this._currentViewMeta.tabs.classList.contains('tabs-bottom'); - - - this._currentViewMeta.header = document.querySelector('ion-navbar.toolbar'); - this._currentViewMeta.headerHeight = this._currentViewMeta.header ? (this._currentViewMeta.header).offsetHeight : 0; - } - - computeHeights(isInit: boolean = false) { - this._footerMeta.height = this.maxHeight > 0 ? this.maxHeight : this.expandedHeight; - - this.renderer.setElementStyle(this.childFooter.nativeElement, 'height', this._footerMeta.height + 'px'); - - // TODO: implement minimize mode - //this.renderer.setElementStyle(this.el.nativeElement, 'min-height', this._footerMeta.height + 'px'); - //if (this.initialState == IonPullUpFooterState.Minimized) { - // this.minimize() - //} else { - this.collapse(isInit); - //} - } - - updateUI(isInit: boolean = false) { - setTimeout(() => { - this.computeHeights(isInit); - }, 300); - this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', 'none'); // avoids flickering when changing orientation - } - - expand() { - this._footerMeta.lastPosY = 0; - this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, 0, 0)'); - this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, 0, 0)'); - this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', '300ms ease-in-out'); - - this.onExpand.emit(null); - } - - collapse(isInit: boolean = false) { - this._footerMeta.lastPosY = this._footerMeta.height - this._footerMeta.defaultHeight - this._currentViewMeta.tabsHeight; - this.renderer.setElementStyle( this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); - this.renderer.setElementStyle( this.childFooter.nativeElement, 'transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); - - if (!isInit) this.onCollapse.emit(null); - } - - minimize() { - this._footerMeta.lastPosY = this._footerMeta.height; - this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); - this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); - - this.onMinimize.emit(null); - } - - - onTap(e: any) { - e.preventDefault(); - - if (this.state == IonPullUpFooterState.Collapsed) { - if (this.defaultBehavior == IonPullUpFooterBehavior.Hide) - this.state = IonPullUpFooterState.Minimized; - else - this.state = IonPullUpFooterState.Expanded; - } else { - if (this.state == IonPullUpFooterState.Minimized) { - if (this.defaultBehavior == IonPullUpFooterBehavior.Hide) - this.state = IonPullUpFooterState.Collapsed; - else - this.state = IonPullUpFooterState.Expanded; - } else { - // footer is expanded - this.state = this.initialState == IonPullUpFooterState.Minimized ? IonPullUpFooterState.Minimized : IonPullUpFooterState.Collapsed; - } - } - } - - - onDrag(e: any) { - e.preventDefault(); - - switch(e.type) { - case 'panstart': - this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', 'none'); - break; - case 'pan': - this._footerMeta.posY = Math.round(e.deltaY) + this._footerMeta.lastPosY; - if (this._footerMeta.posY < 0 || this._footerMeta.posY > this._footerMeta.height) return; - this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, ' + this._footerMeta.posY + 'px, 0)'); - this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, ' + this._footerMeta.posY + 'px, 0)'); - break; - case 'panend': - this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', '300ms ease-in-out'); - - if (this._footerMeta.lastPosY > this._footerMeta.posY) { - this.state = IonPullUpFooterState.Expanded; - } - else if (this._footerMeta.lastPosY < this._footerMeta.posY) { - this.state = (this.initialState == IonPullUpFooterState.Minimized) ? IonPullUpFooterState.Minimized : IonPullUpFooterState.Collapsed; - } - - break; - } - } - - ngDoCheck() { - if (!Object.is(this.state, this._oldState)) { - switch (this.state) { - case IonPullUpFooterState.Collapsed: - this.collapse(); - break; - case IonPullUpFooterState.Expanded: - this.expand(); - break; - case IonPullUpFooterState.Minimized: - this.minimize(); - break; - } - this._oldState = this.state; - - // TODO: fix hack due to BUG (https://github.com/angular/angular/issues/6005) - window.setTimeout(() => { - this.stateChange.emit(this.state); - }) - } - } - -} \ No newline at end of file diff --git a/demo/src/app/main.prod.ts b/demo/src/app/main.prod.ts deleted file mode 100644 index ec61e04..0000000 --- a/demo/src/app/main.prod.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { platformBrowser } from '@angular/platform-browser'; -import { enableProdMode } from '@angular/core'; - -import { AppModuleNgFactory } from './app.module.ngfactory'; - -enableProdMode(); -platformBrowser().bootstrapModuleFactory(AppModuleNgFactory); diff --git a/demo/src/app/main.dev.ts b/demo/src/app/main.ts similarity index 100% rename from demo/src/app/main.dev.ts rename to demo/src/app/main.ts diff --git a/demo/src/assets/icon/favicon.ico b/demo/src/assets/icon/favicon.ico deleted file mode 100644 index d76fa29..0000000 Binary files a/demo/src/assets/icon/favicon.ico and /dev/null differ diff --git a/demo/src/pages/about/about.html b/demo/src/pages/about/about.html deleted file mode 100644 index 2763c93..0000000 --- a/demo/src/pages/about/about.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - About - - - - - - - diff --git a/demo/src/pages/about/about.scss b/demo/src/pages/about/about.scss deleted file mode 100644 index 639956d..0000000 --- a/demo/src/pages/about/about.scss +++ /dev/null @@ -1,3 +0,0 @@ -page-about { - -} diff --git a/demo/src/pages/about/about.ts b/demo/src/pages/about/about.ts deleted file mode 100644 index 1b4c5ad..0000000 --- a/demo/src/pages/about/about.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Component } from '@angular/core'; - -import { NavController } from 'ionic-angular'; - -@Component({ - selector: 'page-about', - templateUrl: 'about.html' -}) -export class AboutPage { - - constructor(public navCtrl: NavController) { - - } - -} diff --git a/demo/src/pages/contact/contact.html b/demo/src/pages/contact/contact.html deleted file mode 100644 index c6ce324..0000000 --- a/demo/src/pages/contact/contact.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - Contact - - - - - - - Follow us on Twitter - - - @ionicframework - - - diff --git a/demo/src/pages/contact/contact.scss b/demo/src/pages/contact/contact.scss deleted file mode 100644 index 13d4440..0000000 --- a/demo/src/pages/contact/contact.scss +++ /dev/null @@ -1,3 +0,0 @@ -page-contact { - -} diff --git a/demo/src/pages/contact/contact.ts b/demo/src/pages/contact/contact.ts deleted file mode 100644 index a344720..0000000 --- a/demo/src/pages/contact/contact.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Component } from '@angular/core'; - -import { NavController } from 'ionic-angular'; - -@Component({ - selector: 'page-contact', - templateUrl: 'contact.html' -}) -export class ContactPage { - - constructor(public navCtrl: NavController) { - - } - -} diff --git a/demo/src/pages/home/home.html b/demo/src/pages/home/home.html index c81dc27..b70f9e1 100644 --- a/demo/src/pages/home/home.html +++ b/demo/src/pages/home/home.html @@ -1,25 +1,32 @@ - Home + + Ionic Blank + - -

Welcome to Ionic!

+ + +

Welcome to your first Ionic app!

+ +

+ This starter project is our way of helping you get a functional app running in record time. +

- This starter project comes with simple tabs-based layout for apps - that are going to primarily use a Tabbed UI. + Follow along on the tutorial section of the Ionic docs!

- Take a look at the src/pages/ directory to add or change tabs, - update any existing page or create new pages. +

+
+ - + Footer @@ -78,4 +85,4 @@

Welcome to Ionic!

-
\ No newline at end of file + diff --git a/demo/src/pages/home/home.ts b/demo/src/pages/home/home.ts index d23a173..ba2b36b 100644 --- a/demo/src/pages/home/home.ts +++ b/demo/src/pages/home/home.ts @@ -1,15 +1,14 @@ import { Component } from '@angular/core'; - import { NavController } from 'ionic-angular'; -import {IonPullUpComponent, IonPullUpFooterState} from '../../app/ionic-pullup/ion-pullup'; -import {IonPullUpTabComponent} from '../../app/ionic-pullup/ion-pullup-tab'; +import {IonPullUpFooterState} from 'ionic-pullup'; @Component({ selector: 'page-home', templateUrl: 'home.html' }) export class HomePage { + footerState: IonPullUpFooterState; constructor(public navCtrl: NavController) { diff --git a/demo/src/pages/tabs/tabs.html b/demo/src/pages/tabs/tabs.html deleted file mode 100644 index fa3921a..0000000 --- a/demo/src/pages/tabs/tabs.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/demo/src/pages/tabs/tabs.ts b/demo/src/pages/tabs/tabs.ts deleted file mode 100644 index a95cdf4..0000000 --- a/demo/src/pages/tabs/tabs.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Component } from '@angular/core'; - -import { HomePage } from '../home/home'; -import { AboutPage } from '../about/about'; -import { ContactPage } from '../contact/contact'; - -@Component({ - templateUrl: 'tabs.html' -}) -export class TabsPage { - // this tells the tabs component which Pages - // should be each tab's root Page - tab1Root: any = HomePage; - tab2Root: any = AboutPage; - tab3Root: any = ContactPage; - - constructor() { - - } -} diff --git a/demo/src/service-worker.js b/demo/src/service-worker.js index 0928a8f..6679781 100644 --- a/demo/src/service-worker.js +++ b/demo/src/service-worker.js @@ -1,80 +1,30 @@ -// tick this to make the cache invalidate and update -const CACHE_VERSION = 1; -const CURRENT_CACHES = { - 'read-through': 'read-through-cache-v' + CACHE_VERSION -}; - -self.addEventListener('activate', (event) => { - // Delete all caches that aren't named in CURRENT_CACHES. - // While there is only one cache in this example, the same logic will handle the case where - // there are multiple versioned caches. - const expectedCacheNames = Object.keys(CURRENT_CACHES).map((key) => { - return CURRENT_CACHES[key]; - }); - - event.waitUntil( - caches.keys().then((cacheNames) => { - return Promise.all( - cacheNames.map((cacheName) => { - if (expectedCacheNames.indexOf(cacheName) === -1) { - // If this cache name isn't present in the array of "expected" cache names, then delete it. - console.log('Deleting out of date cache:', cacheName); - return caches.delete(cacheName); - } - }) - ); - }) - ); -}); - -// This sample illustrates an aggressive approach to caching, in which every valid response is -// cached and every request is first checked against the cache. -// This may not be an appropriate approach if your web application makes requests for -// arbitrary URLs as part of its normal operation (e.g. a RSS client or a news aggregator), -// as the cache could end up containing large responses that might not end up ever being accessed. -// Other approaches, like selectively caching based on response headers or only caching -// responses served from a specific domain, might be more appropriate for those use cases. -self.addEventListener('fetch', (event) => { +/** + * Check out https://googlechrome.github.io/sw-toolbox/docs/master/index.html for + * more info on how to use sw-toolbox to custom configure your service worker. + */ - event.respondWith( - caches.open(CURRENT_CACHES['read-through']).then((cache) => { - return cache.match(event.request).then((response) => { - if (response) { - // If there is an entry in the cache for event.request, then response will be defined - // and we can just return it. - return response; - } +'use strict'; +importScripts('./build/sw-toolbox.js'); - // Otherwise, if there is no entry in the cache for event.request, response will be - // undefined, and we need to fetch() the resource. - console.log(' No response for %s found in cache. ' + - 'About to fetch from network...', event.request.url); - - // We call .clone() on the request since we might use it in the call to cache.put() later on. - // Both fetch() and cache.put() "consume" the request, so we need to make a copy. - // (see https://fetch.spec.whatwg.org/#dom-request-clone) - return fetch(event.request.clone()).then((response) => { - - // Optional: add in extra conditions here, e.g. response.type == 'basic' to only cache - // responses from the same domain. See https://fetch.spec.whatwg.org/#concept-response-type - if (response.status < 400 && response.type === 'basic') { - // We need to call .clone() on the response object to save a copy of it to the cache. - // (https://fetch.spec.whatwg.org/#dom-request-clone) - cache.put(event.request, response.clone()); - } - - // Return the original response object, which will be used to fulfill the resource request. - return response; - }); - }).catch((error) => { - // This catch() will handle exceptions that arise from the match() or fetch() operations. - // Note that a HTTP error response (e.g. 404) will NOT trigger an exception. - // It will return a normal response object that has the appropriate error code set. - console.error(' Read-through caching failed:', error); +self.toolbox.options.cache = { + name: 'ionic-cache' +}; - throw error; - }); - }) - ); -}); \ No newline at end of file +// pre-cache our key assets +self.toolbox.precache( + [ + './build/main.js', + './build/main.css', + './build/polyfills.js', + 'index.html', + 'manifest.json' + ] +); + +// dynamically cache any other local assets +self.toolbox.router.any('/*', self.toolbox.cacheFirst); + +// for any other requests go to the network, cache, +// and then only use that cached resource if your user goes offline +self.toolbox.router.default = self.toolbox.networkFirst; \ No newline at end of file diff --git a/demo/src/theme/variables.scss b/demo/src/theme/variables.scss index 49e7c6b..12c82d5 100644 --- a/demo/src/theme/variables.scss +++ b/demo/src/theme/variables.scss @@ -1,5 +1,7 @@ // Ionic Variables and Theming. For more info, please see: // http://ionicframework.com/docs/v2/theming/ +$font-path: "../assets/fonts"; + @import "ionic.globals"; @@ -10,8 +12,7 @@ // To view all the possible Ionic variables, see: // http://ionicframework.com/docs/v2/theming/overriding-ionic-variables/ -$text-color: #000; -$background-color: #fff; + // Named Color Variables @@ -67,5 +68,11 @@ $colors: ( // The premium icon font for Ionic. For more info, please see: // http://ionicframework.com/docs/v2/ionicons/ -$ionicons-font-path: "../assets/fonts"; -@import "ionicons"; +@import "ionic.ionicons"; + + +// Fonts +// -------------------------------------------------- + +@import "roboto"; +@import "noto-sans"; diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..8df7196 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,3 @@ +export {IonPullUpComponent, IonPullUpFooterState} from './src/ion-pullup'; +export {IonPullUpTabComponent} from './src/ion-pullup-tab'; +export {IonPullUpModule} from './src/ion-pullup.module'; diff --git a/index.js b/index.js new file mode 100644 index 0000000..8a1796a --- /dev/null +++ b/index.js @@ -0,0 +1,4 @@ +export { IonPullUpComponent, IonPullUpFooterState } from './src/ion-pullup'; +export { IonPullUpTabComponent } from './src/ion-pullup-tab'; +export { IonPullUpModule } from './src/ion-pullup.module'; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/index.js.map b/index.js.map new file mode 100644 index 0000000..002da3d --- /dev/null +++ b/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,SAAQ,kBAAkB,EAAE,oBAAoB,QAAO,kBAAkB,CAAC;AAC1E,SAAQ,qBAAqB,QAAO,sBAAsB,CAAC;AAC3D,SAAQ,eAAe,QAAO,yBAAyB,CAAC"} \ No newline at end of file diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..8df7196 --- /dev/null +++ b/index.ts @@ -0,0 +1,3 @@ +export {IonPullUpComponent, IonPullUpFooterState} from './src/ion-pullup'; +export {IonPullUpTabComponent} from './src/ion-pullup-tab'; +export {IonPullUpModule} from './src/ion-pullup.module'; diff --git a/package.json b/package.json index 6f4c687..db035ff 100644 --- a/package.json +++ b/package.json @@ -1,26 +1,38 @@ { - "dependencies": { - "@angular/common": "2.1.1", - "@angular/compiler": "2.1.1", - "@angular/compiler-cli": "2.1.1", - "@angular/core": "2.1.1", - "@angular/forms": "2.1.1", - "@angular/http": "2.1.1", - "@angular/platform-browser": "2.1.1", - "@angular/platform-browser-dynamic": "2.1.1", - "@angular/platform-server": "2.1.1", - "@ionic/storage": "1.1.6", - "ionic-angular": "2.0.0-rc.2", - "ionic-native": "2.2.3", - "ionicons": "3.0.0", - "rxjs": "5.0.0-beta.12", - "zone.js": "0.6.21" - }, - "devDependencies": { - "@ionic/app-scripts": "0.0.44", - "typescript": "2.0.6" - }, - "name": "ionic-pullup", - "description": "ionic-pullup for Angular 2 and Ionic 2", - "version": "2.1.0" + "name": "ionic-pullup", + "description": "ionic-pullup for Angular 2 and Ionic 2", + "version": "2.1.0", + "license": "MIT", + "main": "index.js", + "typings": "index.d.ts", + "repository": { + "type": "git", + "url": "https://github.com/arielfaur/ionic-pullup.git" + }, + "dependencies": { + "@angular/common": "2.1.1", + "@angular/compiler": "2.1.1", + "@angular/compiler-cli": "2.1.1", + "@angular/core": "2.1.1", + "@angular/forms": "2.1.1", + "@angular/http": "2.1.1", + "@angular/platform-browser": "2.1.1", + "@angular/platform-browser-dynamic": "2.1.1", + "@angular/platform-server": "2.1.1", + "@ionic/storage": "1.1.6", + "ionic-angular": "2.0.0-rc.2", + "ionic-native": "2.2.3", + "ionicons": "3.0.0", + "rxjs": "5.0.0-beta.12", + "zone.js": "0.6.21" + }, + "devDependencies": { + "@ionic/app-scripts": "0.0.44", + "@types/core-js": "^0.9.35", + "typescript": "2.0.6" + }, + "scripts": { + "build": "npm install && tsc -p tsconfig.json", + "release": "npm run build && npm publish" + } } diff --git a/src/ion-pullup-tab.js b/src/ion-pullup-tab.js new file mode 100644 index 0000000..e1ab3d8 --- /dev/null +++ b/src/ion-pullup-tab.js @@ -0,0 +1,70 @@ +/* +ionic-pullup v2 for Ionic/Angular 2 + +Copyright 2016 Ariel Faur (https://github.com/arielfaur) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +import { Component, ElementRef, Renderer, Input } from '@angular/core'; +import { Gesture } from 'ionic-angular/gestures/gesture'; +import { IonPullUpComponent, IonPullUpFooterState } from './ion-pullup'; +export var IonPullUpTabComponent = (function () { + function IonPullUpTabComponent(el, renderer) { + this.el = el; + this.renderer = renderer; + } + Object.defineProperty(IonPullUpTabComponent.prototype, "IsExpanded", { + get: function () { + return this.footer.state == IonPullUpFooterState.Expanded; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(IonPullUpTabComponent.prototype, "IsCollapsed", { + get: function () { + return this.footer.state == IonPullUpFooterState.Collapsed; + }, + enumerable: true, + configurable: true + }); + IonPullUpTabComponent.prototype.ngOnInit = function () { + var _this = this; + var tabGesture = new Gesture(this.el.nativeElement); + tabGesture.listen(); + tabGesture.on('tap', function (e) { + _this.footer && _this.footer.onTap(e); + }); + tabGesture.on('pan panstart panend', function (e) { + _this.footer && _this.footer.onDrag(e); + }); + }; + __decorate([ + Input(), + __metadata('design:type', IonPullUpComponent) + ], IonPullUpTabComponent.prototype, "footer", void 0); + IonPullUpTabComponent = __decorate([ + Component({ + selector: 'ion-pullup-tab', + template: '' + }), + __metadata('design:paramtypes', [ElementRef, Renderer]) + ], IonPullUpTabComponent); + return IonPullUpTabComponent; +}()); +//# sourceMappingURL=ion-pullup-tab.js.map \ No newline at end of file diff --git a/src/ion-pullup-tab.js.map b/src/ion-pullup-tab.js.map new file mode 100644 index 0000000..9ad40ef --- /dev/null +++ b/src/ion-pullup-tab.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ion-pullup-tab.js","sourceRoot":"","sources":["ion-pullup-tab.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;EAaE;;;;;;;;;;OAEK,EAAY,SAAS,EAA6D,UAAU,EAAE,QAAQ,EAAwB,KAAK,EAAoD,MAAM,eAAe;OAC5M,EAAC,OAAO,EAAC,MAAM,gCAAgC;OAE/C,EAAC,kBAAkB,EAAE,oBAAoB,EAAC,MAAM,cAAc;AAMrE;IAGE,+BAAoB,EAAc,EAAU,QAAkB;QAA1C,OAAE,GAAF,EAAE,CAAY;QAAU,aAAQ,GAAR,QAAQ,CAAU;IAE9D,CAAC;IAED,sBAAW,6CAAU;aAArB;YACE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,oBAAoB,CAAC,QAAQ,CAAC;QAC5D,CAAC;;;OAAA;IAED,sBAAW,8CAAW;aAAtB;YACE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,oBAAoB,CAAC,SAAS,CAAC;QAC7D,CAAC;;;OAAA;IAED,wCAAQ,GAAR;QAAA,iBASC;QARC,IAAI,UAAU,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC;QACpD,UAAU,CAAC,MAAM,EAAE,CAAC;QACpB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,UAAA,CAAC;YACpB,KAAI,CAAC,MAAM,IAAI,KAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;QACH,UAAU,CAAC,EAAE,CAAC,qBAAqB,EAAE,UAAA,CAAC;YACpC,KAAI,CAAC,MAAM,IAAI,KAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC;IAvBD;QAAC,KAAK,EAAE;;yDAAA;IALV;QAAC,SAAS,CAAC;YACP,QAAQ,EAAE,gBAAgB;YAC1B,QAAQ,EAAE,2BAA2B;SACxC,CAAC;;6BAAA;IA2BF,4BAAC;AAAD,CAAC,AA1BD,IA0BC"} \ No newline at end of file diff --git a/src/ion-pullup-tab.ts b/src/ion-pullup-tab.ts new file mode 100644 index 0000000..aefd7e3 --- /dev/null +++ b/src/ion-pullup-tab.ts @@ -0,0 +1,51 @@ +/* +ionic-pullup v2 for Ionic/Angular 2 + +Copyright 2016 Ariel Faur (https://github.com/arielfaur) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import {Attribute, Component, Directive, DoCheck, SimpleChange, OnChanges, EventEmitter, ElementRef, Renderer, ContentChild, Output, Input, Injectable, Inject, Optional, Pipe, PipeTransform} from '@angular/core'; +import {Gesture} from 'ionic-angular/gestures/gesture'; +import {Toolbar} from 'ionic-angular/components/toolbar/toolbar'; +import {IonPullUpComponent, IonPullUpFooterState} from './ion-pullup'; + +@Component({ + selector: 'ion-pullup-tab', + template: '' +}) +export class IonPullUpTabComponent { + @Input() footer: IonPullUpComponent; + + constructor(private el: ElementRef, private renderer: Renderer) { + + } + + public get IsExpanded(): boolean { + return this.footer.state == IonPullUpFooterState.Expanded; + } + + public get IsCollapsed(): boolean { + return this.footer.state == IonPullUpFooterState.Collapsed; + } + + ngOnInit() { + let tabGesture = new Gesture(this.el.nativeElement); + tabGesture.listen(); + tabGesture.on('tap', e => { + this.footer && this.footer.onTap(e); + }); + tabGesture.on('pan panstart panend', e => { + this.footer && this.footer.onDrag(e); + }); + } + +} \ No newline at end of file diff --git a/src/ion-pullup.js b/src/ion-pullup.js new file mode 100644 index 0000000..c471d52 --- /dev/null +++ b/src/ion-pullup.js @@ -0,0 +1,261 @@ +/* + ionic-pullup v2 for Ionic/Angular 2 + + Copyright 2016 Ariel Faur (https://github.com/arielfaur) + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +import { ChangeDetectionStrategy, Component, EventEmitter, ElementRef, Renderer, ViewChild, ContentChild, Output, Input } from '@angular/core'; +import { Gesture } from 'ionic-angular/gestures/gesture'; +import { Toolbar } from 'ionic-angular/components/toolbar/toolbar'; +import { Platform } from 'ionic-angular'; +export var IonPullUpFooterState; +(function (IonPullUpFooterState) { + IonPullUpFooterState[IonPullUpFooterState["Collapsed"] = 0] = "Collapsed"; + IonPullUpFooterState[IonPullUpFooterState["Expanded"] = 1] = "Expanded"; + IonPullUpFooterState[IonPullUpFooterState["Minimized"] = 2] = "Minimized"; +})(IonPullUpFooterState || (IonPullUpFooterState = {})); +export var IonPullUpFooterBehavior; +(function (IonPullUpFooterBehavior) { + IonPullUpFooterBehavior[IonPullUpFooterBehavior["Hide"] = 0] = "Hide"; + IonPullUpFooterBehavior[IonPullUpFooterBehavior["Expand"] = 1] = "Expand"; +})(IonPullUpFooterBehavior || (IonPullUpFooterBehavior = {})); +export var IonPullUpComponent = (function () { + function IonPullUpComponent(platform, el, renderer) { + this.platform = platform; + this.el = el; + this.renderer = renderer; + this.stateChange = new EventEmitter(); + this.onExpand = new EventEmitter(); + this.onCollapse = new EventEmitter(); + this.onMinimize = new EventEmitter(); + this._footerMeta = { + height: 0, + posY: 0, + lastPosY: 0 + }; + this._currentViewMeta = {}; + // sets initial state + this.initialState = this.initialState || IonPullUpFooterState.Collapsed; + this.defaultBehavior = this.defaultBehavior || IonPullUpFooterBehavior.Expand; + this.maxHeight = this.maxHeight || 0; + } + IonPullUpComponent.prototype.ngOnInit = function () { + var _this = this; + console.debug('ionic-pullup => Initializing footer...'); + window.addEventListener("orientationchange", function () { + console.debug('ionic-pullup => Changed orientation => updating'); + _this.updateUI(); + }); + this.platform.resume.subscribe(function () { + console.debug('ionic-pullup => Resumed from background => updating'); + _this.updateUI(); + }); + }; + IonPullUpComponent.prototype.ngAfterContentInit = function () { + var _this = this; + this.computeDefaults(); + var barGesture = new Gesture(this.childToolbar.getNativeElement()); + barGesture.listen(); + barGesture.on('tap', function (e) { + _this.onTap(e); + }); + barGesture.on('pan panstart panend', function (e) { + _this.onDrag(e); + }); + this.state = IonPullUpFooterState.Collapsed; + this.updateUI(true); // need to indicate whether it's first run to avoid emitting events twice due to change detection + }; + Object.defineProperty(IonPullUpComponent.prototype, "expandedHeight", { + get: function () { + return window.innerHeight - this._currentViewMeta.headerHeight; + }, + enumerable: true, + configurable: true + }); + IonPullUpComponent.prototype.computeDefaults = function () { + this._footerMeta.defaultHeight = this.childFooter.nativeElement.offsetHeight; + // TODO: still need to test with tabs template (not convinced it is a valid use case...) + this._currentViewMeta.tabs = this.el.nativeElement.closest('ion-tabs'); + this._currentViewMeta.tabsHeight = this._currentViewMeta.tabs ? this._currentViewMeta.tabs.querySelector('.tabbar').offsetHeight : 0; + console.debug(this._currentViewMeta.tabsHeight ? 'ionic-pullup => Tabs detected' : 'ionic.pullup => View has no tabs'); + //this._currentViewMeta.hasBottomTabs = this._currentViewMeta.tabs && this._currentViewMeta.tabs.classList.contains('tabs-bottom'); + this._currentViewMeta.header = document.querySelector('ion-navbar.toolbar'); + this._currentViewMeta.headerHeight = this._currentViewMeta.header ? this._currentViewMeta.header.offsetHeight : 0; + }; + IonPullUpComponent.prototype.computeHeights = function (isInit) { + if (isInit === void 0) { isInit = false; } + this._footerMeta.height = this.maxHeight > 0 ? this.maxHeight : this.expandedHeight; + this.renderer.setElementStyle(this.childFooter.nativeElement, 'height', this._footerMeta.height + 'px'); + // TODO: implement minimize mode + //this.renderer.setElementStyle(this.el.nativeElement, 'min-height', this._footerMeta.height + 'px'); + //if (this.initialState == IonPullUpFooterState.Minimized) { + // this.minimize() + //} else { + this.collapse(isInit); + //} + }; + IonPullUpComponent.prototype.updateUI = function (isInit) { + var _this = this; + if (isInit === void 0) { isInit = false; } + setTimeout(function () { + _this.computeHeights(isInit); + }, 300); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', 'none'); // avoids flickering when changing orientation + }; + IonPullUpComponent.prototype.expand = function () { + this._footerMeta.lastPosY = 0; + this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, 0, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, 0, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', '300ms ease-in-out'); + this.onExpand.emit(null); + }; + IonPullUpComponent.prototype.collapse = function (isInit) { + if (isInit === void 0) { isInit = false; } + this._footerMeta.lastPosY = this._footerMeta.height - this._footerMeta.defaultHeight - this._currentViewMeta.tabsHeight; + this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); + if (!isInit) + this.onCollapse.emit(null); + }; + IonPullUpComponent.prototype.minimize = function () { + this._footerMeta.lastPosY = this._footerMeta.height; + this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); + this.onMinimize.emit(null); + }; + IonPullUpComponent.prototype.onTap = function (e) { + e.preventDefault(); + if (this.state == IonPullUpFooterState.Collapsed) { + if (this.defaultBehavior == IonPullUpFooterBehavior.Hide) + this.state = IonPullUpFooterState.Minimized; + else + this.state = IonPullUpFooterState.Expanded; + } + else { + if (this.state == IonPullUpFooterState.Minimized) { + if (this.defaultBehavior == IonPullUpFooterBehavior.Hide) + this.state = IonPullUpFooterState.Collapsed; + else + this.state = IonPullUpFooterState.Expanded; + } + else { + // footer is expanded + this.state = this.initialState == IonPullUpFooterState.Minimized ? IonPullUpFooterState.Minimized : IonPullUpFooterState.Collapsed; + } + } + }; + IonPullUpComponent.prototype.onDrag = function (e) { + e.preventDefault(); + switch (e.type) { + case 'panstart': + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', 'none'); + break; + case 'pan': + this._footerMeta.posY = Math.round(e.deltaY) + this._footerMeta.lastPosY; + if (this._footerMeta.posY < 0 || this._footerMeta.posY > this._footerMeta.height) + return; + this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, ' + this._footerMeta.posY + 'px, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, ' + this._footerMeta.posY + 'px, 0)'); + break; + case 'panend': + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', '300ms ease-in-out'); + if (this._footerMeta.lastPosY > this._footerMeta.posY) { + this.state = IonPullUpFooterState.Expanded; + } + else if (this._footerMeta.lastPosY < this._footerMeta.posY) { + this.state = (this.initialState == IonPullUpFooterState.Minimized) ? IonPullUpFooterState.Minimized : IonPullUpFooterState.Collapsed; + } + break; + } + }; + IonPullUpComponent.prototype.ngDoCheck = function () { + var _this = this; + if (!Object.is(this.state, this._oldState)) { + switch (this.state) { + case IonPullUpFooterState.Collapsed: + this.collapse(); + break; + case IonPullUpFooterState.Expanded: + this.expand(); + break; + case IonPullUpFooterState.Minimized: + this.minimize(); + break; + } + this._oldState = this.state; + // TODO: fix hack due to BUG (https://github.com/angular/angular/issues/6005) + window.setTimeout(function () { + _this.stateChange.emit(_this.state); + }); + } + }; + __decorate([ + Input(), + __metadata('design:type', Number) + ], IonPullUpComponent.prototype, "state", void 0); + __decorate([ + Output(), + __metadata('design:type', EventEmitter) + ], IonPullUpComponent.prototype, "stateChange", void 0); + __decorate([ + Input(), + __metadata('design:type', Number) + ], IonPullUpComponent.prototype, "initialState", void 0); + __decorate([ + // TODO implemment + Input(), + __metadata('design:type', Number) + ], IonPullUpComponent.prototype, "defaultBehavior", void 0); + __decorate([ + // TODO implemment + Input(), + __metadata('design:type', Number) + ], IonPullUpComponent.prototype, "maxHeight", void 0); + __decorate([ + Output(), + __metadata('design:type', Object) + ], IonPullUpComponent.prototype, "onExpand", void 0); + __decorate([ + Output(), + __metadata('design:type', Object) + ], IonPullUpComponent.prototype, "onCollapse", void 0); + __decorate([ + Output(), + __metadata('design:type', Object) + ], IonPullUpComponent.prototype, "onMinimize", void 0); + __decorate([ + ContentChild(Toolbar), + __metadata('design:type', Object) + ], IonPullUpComponent.prototype, "childToolbar", void 0); + __decorate([ + ViewChild('footer'), + __metadata('design:type', Object) + ], IonPullUpComponent.prototype, "childFooter", void 0); + IonPullUpComponent = __decorate([ + Component({ + selector: 'ion-pullup', + changeDetection: ChangeDetectionStrategy.OnPush, + template: "\n \n \n \n " + }), + __metadata('design:paramtypes', [Platform, ElementRef, Renderer]) + ], IonPullUpComponent); + return IonPullUpComponent; +}()); +//# sourceMappingURL=ion-pullup.js.map \ No newline at end of file diff --git a/src/ion-pullup.js.map b/src/ion-pullup.js.map new file mode 100644 index 0000000..f64a09b --- /dev/null +++ b/src/ion-pullup.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ion-pullup.js","sourceRoot":"","sources":["ion-pullup.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/ion-pullup.module.js b/src/ion-pullup.module.js new file mode 100644 index 0000000..d7f8943 --- /dev/null +++ b/src/ion-pullup.module.js @@ -0,0 +1,41 @@ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { IonicModule } from 'ionic-angular'; +import { IonPullUpComponent } from './ion-pullup'; +import { IonPullUpTabComponent } from './ion-pullup-tab'; +export var IonPullUpModule = (function () { + function IonPullUpModule() { + } + IonPullUpModule = __decorate([ + NgModule({ + imports: [ + CommonModule, + FormsModule, + IonicModule + ], + declarations: [ + IonPullUpComponent, + IonPullUpTabComponent + ], + exports: [ + IonPullUpComponent, + IonPullUpTabComponent + ], + providers: [], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + }), + __metadata('design:paramtypes', []) + ], IonPullUpModule); + return IonPullUpModule; +}()); +//# sourceMappingURL=ion-pullup.module.js.map \ No newline at end of file diff --git a/src/ion-pullup.module.js.map b/src/ion-pullup.module.js.map new file mode 100644 index 0000000..2ca5a61 --- /dev/null +++ b/src/ion-pullup.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ion-pullup.module.js","sourceRoot":"","sources":["ion-pullup.module.ts"],"names":[],"mappings":";;;;;;;;;OAAO,EAAC,QAAQ,EAAE,sBAAsB,EAAC,MAAM,eAAe;OACvD,EAAC,YAAY,EAAC,MAAM,iBAAiB;OACrC,EAAC,WAAW,EAAC,MAAM,gBAAgB;OACnC,EAAC,WAAW,EAAC,MAAM,eAAe;OAElC,EAAC,kBAAkB,EAAC,MAAM,cAAc;OACxC,EAAC,qBAAqB,EAAC,MAAM,kBAAkB;AAmBtD;IAAA;IACA,CAAC;IAlBD;QAAC,QAAQ,CAAC;YACN,OAAO,EAAE;gBACL,YAAY;gBACZ,WAAW;gBACX,WAAW;aACd;YACD,YAAY,EAAE;gBACV,kBAAkB;gBAClB,qBAAqB;aACxB;YACD,OAAO,EAAE;gBACL,kBAAkB;gBAClB,qBAAqB;aACxB;YACD,SAAS,EAAE,EAAE;YACb,OAAO,EAAE,CAAC,sBAAsB,CAAC;SACpC,CAAC;;uBAAA;IAEF,sBAAC;AAAD,CAAC,AADD,IACC"} \ No newline at end of file diff --git a/src/ion-pullup.module.ts b/src/ion-pullup.module.ts new file mode 100644 index 0000000..072c2f9 --- /dev/null +++ b/src/ion-pullup.module.ts @@ -0,0 +1,27 @@ +import {NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {FormsModule} from '@angular/forms'; +import {IonicModule} from 'ionic-angular'; + +import {IonPullUpComponent} from './ion-pullup'; +import {IonPullUpTabComponent} from './ion-pullup-tab'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + IonicModule + ], + declarations: [ + IonPullUpComponent, + IonPullUpTabComponent + ], + exports: [ + IonPullUpComponent, + IonPullUpTabComponent + ], + providers: [], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class IonPullUpModule { +} \ No newline at end of file diff --git a/src/ion-pullup.scss b/src/ion-pullup.scss new file mode 100644 index 0000000..695892d --- /dev/null +++ b/src/ion-pullup.scss @@ -0,0 +1,85 @@ +$tabWidth: 120px !default; // tab width +$tabHeight: 22px !default; // tab height, set to 0px if there is no tab +$tabRadius: 20px !default; // tab radius +$tabBackground: color($colors, primary, base) !default; // tab background color +$tabIconColor: white !default; // icon color within tab +$tabShadow: #666 !default; // shadow color + +@mixin pullup-tab($width, $height, $radius, $background: color($colors, primary, base), $foreground: color($colors, primary, contrast)) { + position: relative; + width: $width; + height: $height; + background: $background; + color: $foreground; + border-radius: $radius $radius 0 0; + margin: 0 auto; +} + +@mixin pullup-tab-shadow($shadowColor) { + box-shadow: 0px -2px 2px $shadowColor; +} + +ion-pullup { + background: color($colors, primary, base); + + ion-footer { + &::before { + content: none; + } + + ion-pullup-tab { + z-index: 1000; + display: flex; + justify-content: center; + + @include pullup-tab($tabWidth, $tabHeight, $tabRadius, $tabBackground, $tabIconColor); + @include pullup-tab-shadow($tabShadow); + } + + ion-content { + position: absolute; + + .ios & { + @import 'node_modules/ionic-angular/components/toolbar/toolbar.ios'; + + @if $tabHeight != 0 { + margin-top: calc(#{$toolbar-ios-height} + #{$tabHeight}); + .scroll-content { + margin-bottom: calc(#{$toolbar-ios-height} + #{$tabHeight}); + } + } @else { + margin-top: $toolbar-ios-height; + .scroll-content { + margin-bottom: $toolbar-ios-height; + } + } + + } + .md & { + @import 'node_modules/ionic-angular/components/toolbar/toolbar.md'; + + @if $tabHeight != 0 { + margin-top: calc(#{$toolbar-md-height} + #{$tabHeight}); + .scroll-content { + margin-bottom: calc(#{$toolbar-md-height} + #{$tabHeight}); + } + } @else { + margin-top: $toolbar-md-height; + .scroll-content { + margin-bottom: $toolbar-md-height; + } + } + } + } + + ion-toolbar { + @include pullup-tab-shadow($tabShadow); + + .toolbar-background { + .ios & { + border-top: none; + } + } + } + } +} \ No newline at end of file diff --git a/src/ion-pullup.ts b/src/ion-pullup.ts new file mode 100644 index 0000000..16a46a3 --- /dev/null +++ b/src/ion-pullup.ts @@ -0,0 +1,285 @@ +/* + ionic-pullup v2 for Ionic/Angular 2 + + Copyright 2016 Ariel Faur (https://github.com/arielfaur) + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { + Attribute, + ChangeDetectionStrategy, + Component, + Directive, + DoCheck, + SimpleChange, + OnChanges, + EventEmitter, + ElementRef, + Renderer, + ViewChild, + ContentChild, + Output, + Input, + Injectable, + Inject, + Optional +} from '@angular/core'; +import {Gesture} from 'ionic-angular/gestures/gesture'; +import {Toolbar, Footer} from 'ionic-angular/components/toolbar/toolbar'; +import {Platform} from 'ionic-angular'; + +interface FooterMetadata { + height: number; + posY: number; + lastPosY: number; + defaultHeight?: number; +} + +interface ViewMetadata { + tabs?: Element; + tabsHeight?: number; + hasBottomTabs?: boolean; + header?: Element; + headerHeight?: number; +} + +interface FooterTab { + x?: number; + y?: number; + upperLeftRadius?: number; + upperRightRadius?: number; + backgroundColor?: string; + color?: string; + content?: string; +} + +export enum IonPullUpFooterState { + Collapsed = 0, + Expanded = 1, + Minimized = 2 +} + +export enum IonPullUpFooterBehavior { + Hide, + Expand +} + +@Component({ + selector: 'ion-pullup', + changeDetection: ChangeDetectionStrategy.OnPush, + template: ` + + + + ` +}) +export class IonPullUpComponent { + @Input() state: IonPullUpFooterState; + @Output() stateChange: EventEmitter = new EventEmitter(); + + @Input() initialState: IonPullUpFooterState; // TODO implemment + @Input() defaultBehavior: IonPullUpFooterBehavior; // TODO implemment + @Input() maxHeight: number; + + @Output() onExpand = new EventEmitter(); + @Output() onCollapse = new EventEmitter(); + @Output() onMinimize = new EventEmitter(); + + @ContentChild(Toolbar) childToolbar; + @ViewChild('footer') childFooter; + + protected _footerMeta: FooterMetadata; + protected _currentViewMeta: ViewMetadata; + protected _oldState: IonPullUpFooterState; + + constructor(private platform: Platform, private el: ElementRef, private renderer: Renderer) { + this._footerMeta = { + height: 0, + posY: 0, + lastPosY: 0 + } + this._currentViewMeta = {}; + + // sets initial state + this.initialState = this.initialState || IonPullUpFooterState.Collapsed; + this.defaultBehavior = this.defaultBehavior || IonPullUpFooterBehavior.Expand; + this.maxHeight = this.maxHeight || 0; + } + + ngOnInit() { + console.debug('ionic-pullup => Initializing footer...'); + + window.addEventListener("orientationchange", () => { + console.debug('ionic-pullup => Changed orientation => updating'); + this.updateUI(); + }); + this.platform.resume.subscribe(() => { + console.debug('ionic-pullup => Resumed from background => updating'); + this.updateUI(); + }); + } + + ngAfterContentInit() { + this.computeDefaults(); + + let barGesture = new Gesture(this.childToolbar.getNativeElement()); + barGesture.listen(); + barGesture.on('tap', e => { + this.onTap(e); + }); + barGesture.on('pan panstart panend', e => { + this.onDrag(e); + }); + + this.state = IonPullUpFooterState.Collapsed; + + this.updateUI(true); // need to indicate whether it's first run to avoid emitting events twice due to change detection + + } + + public get expandedHeight(): number { + return window.innerHeight - this._currentViewMeta.headerHeight; + } + + computeDefaults() { + this._footerMeta.defaultHeight = this.childFooter.nativeElement.offsetHeight; + + + // TODO: still need to test with tabs template (not convinced it is a valid use case...) + this._currentViewMeta.tabs = this.el.nativeElement.closest('ion-tabs'); + this._currentViewMeta.tabsHeight = this._currentViewMeta.tabs ? ( this._currentViewMeta.tabs.querySelector('.tabbar')).offsetHeight : 0 + console.debug(this._currentViewMeta.tabsHeight ? 'ionic-pullup => Tabs detected' : 'ionic.pullup => View has no tabs'); + //this._currentViewMeta.hasBottomTabs = this._currentViewMeta.tabs && this._currentViewMeta.tabs.classList.contains('tabs-bottom'); + + + this._currentViewMeta.header = document.querySelector('ion-navbar.toolbar'); + this._currentViewMeta.headerHeight = this._currentViewMeta.header ? (this._currentViewMeta.header).offsetHeight : 0; + } + + computeHeights(isInit: boolean = false) { + this._footerMeta.height = this.maxHeight > 0 ? this.maxHeight : this.expandedHeight; + + this.renderer.setElementStyle(this.childFooter.nativeElement, 'height', this._footerMeta.height + 'px'); + + // TODO: implement minimize mode + //this.renderer.setElementStyle(this.el.nativeElement, 'min-height', this._footerMeta.height + 'px'); + //if (this.initialState == IonPullUpFooterState.Minimized) { + // this.minimize() + //} else { + this.collapse(isInit); + //} + } + + updateUI(isInit: boolean = false) { + setTimeout(() => { + this.computeHeights(isInit); + }, 300); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', 'none'); // avoids flickering when changing orientation + } + + expand() { + this._footerMeta.lastPosY = 0; + this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, 0, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, 0, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', '300ms ease-in-out'); + + this.onExpand.emit(null); + } + + collapse(isInit: boolean = false) { + this._footerMeta.lastPosY = this._footerMeta.height - this._footerMeta.defaultHeight - this._currentViewMeta.tabsHeight; + this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); + + if (!isInit) this.onCollapse.emit(null); + } + + minimize() { + this._footerMeta.lastPosY = this._footerMeta.height; + this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); + + this.onMinimize.emit(null); + } + + + onTap(e: any) { + e.preventDefault(); + + if (this.state == IonPullUpFooterState.Collapsed) { + if (this.defaultBehavior == IonPullUpFooterBehavior.Hide) + this.state = IonPullUpFooterState.Minimized; + else + this.state = IonPullUpFooterState.Expanded; + } else { + if (this.state == IonPullUpFooterState.Minimized) { + if (this.defaultBehavior == IonPullUpFooterBehavior.Hide) + this.state = IonPullUpFooterState.Collapsed; + else + this.state = IonPullUpFooterState.Expanded; + } else { + // footer is expanded + this.state = this.initialState == IonPullUpFooterState.Minimized ? IonPullUpFooterState.Minimized : IonPullUpFooterState.Collapsed; + } + } + } + + + onDrag(e: any) { + e.preventDefault(); + + switch (e.type) { + case 'panstart': + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', 'none'); + break; + case 'pan': + this._footerMeta.posY = Math.round(e.deltaY) + this._footerMeta.lastPosY; + if (this._footerMeta.posY < 0 || this._footerMeta.posY > this._footerMeta.height) return; + this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, ' + this._footerMeta.posY + 'px, 0)'); + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, ' + this._footerMeta.posY + 'px, 0)'); + break; + case 'panend': + this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', '300ms ease-in-out'); + + if (this._footerMeta.lastPosY > this._footerMeta.posY) { + this.state = IonPullUpFooterState.Expanded; + } + else if (this._footerMeta.lastPosY < this._footerMeta.posY) { + this.state = (this.initialState == IonPullUpFooterState.Minimized) ? IonPullUpFooterState.Minimized : IonPullUpFooterState.Collapsed; + } + + break; + } + } + + ngDoCheck() { + if (!Object.is(this.state, this._oldState)) { + switch (this.state) { + case IonPullUpFooterState.Collapsed: + this.collapse(); + break; + case IonPullUpFooterState.Expanded: + this.expand(); + break; + case IonPullUpFooterState.Minimized: + this.minimize(); + break; + } + this._oldState = this.state; + + // TODO: fix hack due to BUG (https://github.com/angular/angular/issues/6005) + window.setTimeout(() => { + this.stateChange.emit(this.state); + }) + } + } + +} \ No newline at end of file diff --git a/src/ionic-pullup/ion-pullup.module.ts b/src/ionic-pullup/ion-pullup.module.ts deleted file mode 100644 index 28af041..0000000 --- a/src/ionic-pullup/ion-pullup.module.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; -import { IonicModule } from 'ionic-angular'; - -import {IonPullUpComponent} from './ion-pullup'; -import {IonPullUpTabComponent} from './ion-pullup-tab'; - -@NgModule({ - imports: [ CommonModule, FormsModule, IonicModule ], - declarations: [ - IonPullUpComponent, - IonPullUpTabComponent - ], - exports: [ - IonPullUpComponent, - IonPullUpTabComponent - ], - providers: [], - schemas: [ CUSTOM_ELEMENTS_SCHEMA ] -}) -export class IonPullupModule { } - -export * from './ion-pullup'; -export * from './ion-pullup-tab'; \ No newline at end of file diff --git a/src/ionic-pullup/ion-pullup.ts b/src/ionic-pullup/ion-pullup.ts deleted file mode 100644 index 4e8f507..0000000 --- a/src/ionic-pullup/ion-pullup.ts +++ /dev/null @@ -1,267 +0,0 @@ -/* -ionic-pullup v2 for Ionic/Angular 2 - -Copyright 2016 Ariel Faur (https://github.com/arielfaur) -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { Attribute, ChangeDetectionStrategy, Component, Directive, DoCheck, SimpleChange, OnChanges, EventEmitter, ElementRef, Renderer, ViewChild, ContentChild, Output, Input, Injectable, Inject, Optional } from '@angular/core'; -import { Gesture } from 'ionic-angular/gestures/gesture'; -import { Toolbar, Footer } from 'ionic-angular/components/toolbar/toolbar'; -import { Platform } from 'ionic-angular'; - -interface FooterMetadata { - height: number; - posY: number; - lastPosY: number; - defaultHeight?: number; -} - -interface ViewMetadata { - tabs?: Element; - tabsHeight?: number; - hasBottomTabs?: boolean; - header?: Element; - headerHeight?: number; -} - -interface FooterTab { - x?: number; - y?: number; - upperLeftRadius?: number; - upperRightRadius?: number; - backgroundColor?: string; - color?: string; - content?: string; -} - -export enum IonPullUpFooterState { - Collapsed = 0, - Expanded = 1, - Minimized = 2 -} - -export enum IonPullUpFooterBehavior { - Hide, - Expand -} - -@Component({ - selector: 'ion-pullup', - changeDetection: ChangeDetectionStrategy.OnPush, - template: ` - - - - ` -}) -export class IonPullUpComponent { - @Input() state: IonPullUpFooterState; - @Output() stateChange: EventEmitter = new EventEmitter(); - - @Input() initialState: IonPullUpFooterState; // TODO implemment - @Input() defaultBehavior: IonPullUpFooterBehavior; // TODO implemment - @Input() maxHeight: number; - - @Output() onExpand = new EventEmitter(); - @Output() onCollapse = new EventEmitter(); - @Output() onMinimize = new EventEmitter(); - - @ContentChild(Toolbar) childToolbar; - @ViewChild('footer') childFooter; - - protected _footerMeta: FooterMetadata; - protected _currentViewMeta: ViewMetadata; - protected _oldState: IonPullUpFooterState; - - constructor(private platform: Platform, private el: ElementRef, private renderer: Renderer) { - this._footerMeta = { - height: 0, - posY: 0, - lastPosY: 0 - } - this._currentViewMeta = {}; - - // sets initial state - this.initialState = this.initialState || IonPullUpFooterState.Collapsed; - this.defaultBehavior = this.defaultBehavior || IonPullUpFooterBehavior.Expand; - this.maxHeight = this.maxHeight || 0; - } - - ngOnInit() { - console.debug('ionic-pullup => Initializing footer...'); - - window.addEventListener("orientationchange", () => { - console.debug('ionic-pullup => Changed orientation => updating'); - this.updateUI(); - }); - this.platform.resume.subscribe(() => { - console.debug('ionic-pullup => Resumed from background => updating'); - this.updateUI(); - }); - } - - ngAfterContentInit() { - this.computeDefaults(); - - let barGesture = new Gesture(this.childToolbar.getNativeElement()); - barGesture.listen(); - barGesture.on('tap', e => { - this.onTap(e); - }); - barGesture.on('pan panstart panend', e => { - this.onDrag(e); - }); - - this.state = IonPullUpFooterState.Collapsed; - - this.updateUI(true); // need to indicate whether it's first run to avoid emitting events twice due to change detection - - } - - public get expandedHeight() : number { - return window.innerHeight - this._currentViewMeta.headerHeight; - } - - computeDefaults() { - this._footerMeta.defaultHeight = this.childFooter.nativeElement.offsetHeight; - - - // TODO: still need to test with tabs template (not convinced it is a valid use case...) - this._currentViewMeta.tabs = this.el.nativeElement.closest('ion-tabs'); - this._currentViewMeta.tabsHeight = this._currentViewMeta.tabs ? ( this._currentViewMeta.tabs.querySelector('.tabbar')).offsetHeight : 0 - console.debug (this._currentViewMeta.tabsHeight ? 'ionic-pullup => Tabs detected' : 'ionic.pullup => View has no tabs'); - //this._currentViewMeta.hasBottomTabs = this._currentViewMeta.tabs && this._currentViewMeta.tabs.classList.contains('tabs-bottom'); - - - this._currentViewMeta.header = document.querySelector('ion-navbar.toolbar'); - this._currentViewMeta.headerHeight = this._currentViewMeta.header ? (this._currentViewMeta.header).offsetHeight : 0; - } - - computeHeights(isInit: boolean = false) { - this._footerMeta.height = this.maxHeight > 0 ? this.maxHeight : this.expandedHeight; - - this.renderer.setElementStyle(this.childFooter.nativeElement, 'height', this._footerMeta.height + 'px'); - - // TODO: implement minimize mode - //this.renderer.setElementStyle(this.el.nativeElement, 'min-height', this._footerMeta.height + 'px'); - //if (this.initialState == IonPullUpFooterState.Minimized) { - // this.minimize() - //} else { - this.collapse(isInit); - //} - } - - updateUI(isInit: boolean = false) { - setTimeout(() => { - this.computeHeights(isInit); - }, 300); - this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', 'none'); // avoids flickering when changing orientation - } - - expand() { - this._footerMeta.lastPosY = 0; - this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, 0, 0)'); - this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, 0, 0)'); - this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', '300ms ease-in-out'); - - this.onExpand.emit(null); - } - - collapse(isInit: boolean = false) { - this._footerMeta.lastPosY = this._footerMeta.height - this._footerMeta.defaultHeight - this._currentViewMeta.tabsHeight; - this.renderer.setElementStyle( this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); - this.renderer.setElementStyle( this.childFooter.nativeElement, 'transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); - - if (!isInit) this.onCollapse.emit(null); - } - - minimize() { - this._footerMeta.lastPosY = this._footerMeta.height; - this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); - this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, ' + this._footerMeta.lastPosY + 'px, 0)'); - - this.onMinimize.emit(null); - } - - - onTap(e: any) { - e.preventDefault(); - - if (this.state == IonPullUpFooterState.Collapsed) { - if (this.defaultBehavior == IonPullUpFooterBehavior.Hide) - this.state = IonPullUpFooterState.Minimized; - else - this.state = IonPullUpFooterState.Expanded; - } else { - if (this.state == IonPullUpFooterState.Minimized) { - if (this.defaultBehavior == IonPullUpFooterBehavior.Hide) - this.state = IonPullUpFooterState.Collapsed; - else - this.state = IonPullUpFooterState.Expanded; - } else { - // footer is expanded - this.state = this.initialState == IonPullUpFooterState.Minimized ? IonPullUpFooterState.Minimized : IonPullUpFooterState.Collapsed; - } - } - } - - - onDrag(e: any) { - e.preventDefault(); - - switch(e.type) { - case 'panstart': - this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', 'none'); - break; - case 'pan': - this._footerMeta.posY = Math.round(e.deltaY) + this._footerMeta.lastPosY; - if (this._footerMeta.posY < 0 || this._footerMeta.posY > this._footerMeta.height) return; - this.renderer.setElementStyle(this.childFooter.nativeElement, '-webkit-transform', 'translate3d(0, ' + this._footerMeta.posY + 'px, 0)'); - this.renderer.setElementStyle(this.childFooter.nativeElement, 'transform', 'translate3d(0, ' + this._footerMeta.posY + 'px, 0)'); - break; - case 'panend': - this.renderer.setElementStyle(this.childFooter.nativeElement, 'transition', '300ms ease-in-out'); - - if (this._footerMeta.lastPosY > this._footerMeta.posY) { - this.state = IonPullUpFooterState.Expanded; - } - else if (this._footerMeta.lastPosY < this._footerMeta.posY) { - this.state = (this.initialState == IonPullUpFooterState.Minimized) ? IonPullUpFooterState.Minimized : IonPullUpFooterState.Collapsed; - } - - break; - } - } - - ngDoCheck() { - if (!Object.is(this.state, this._oldState)) { - switch (this.state) { - case IonPullUpFooterState.Collapsed: - this.collapse(); - break; - case IonPullUpFooterState.Expanded: - this.expand(); - break; - case IonPullUpFooterState.Minimized: - this.minimize(); - break; - } - this._oldState = this.state; - - // TODO: fix hack due to BUG (https://github.com/angular/angular/issues/6005) - window.setTimeout(() => { - this.stateChange.emit(this.state); - }) - } - } - -} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 260bda1..3a47c85 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,21 +1,13 @@ { "compilerOptions": { "target": "es5", - "module": "commonjs", + "module": "es2015", + "moduleResolution": "node", + "sourceMap": true, "emitDecoratorMetadata": true, "experimentalDecorators": true }, - "filesGlob": [ - "**/*.ts", - "!node_modules/**/*" - ], - "exclude": [ - "node_modules", - "typings/global", - "typings/global.d.ts" - ], - "compileOnSave": false, - "atom": { - "rewriteTsconfig": false - } + "files": [ + "index.ts" + ] } diff --git a/typings.json b/typings.json deleted file mode 100644 index c44aff9..0000000 --- a/typings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "dependencies": {}, - "devDependencies": {}, - "globalDependencies": { - "es6-shim": "registry:dt/es6-shim#0.31.2+20160602141504" - } -}