Merge branch 'master' into list-border-refactor

This commit is contained in:
Brandy Carney
2015-11-16 14:40:17 -05:00
61 changed files with 49 additions and 2847 deletions

View File

@@ -1,135 +0,0 @@
import {FormBuilder, Validators, ControlGroup} from 'angular2/angular2';
import {Log} from 'ionic/util'
import {App, IonicView, NavController} from 'ionic/ionic'
@IonicView({
templateUrl: 'pages/login.html'
})
class LoginPage {
constructor( nav: NavController ) {
this.nav = nav
Log.log('LOGIN PAGE', this)
var fb = new FormBuilder()
this.loginForm = fb.group({
email: ['', Validators.required],
password: ['', Validators.required],
});
}
doLogin(event) {
Log.log('Doing login')
event.preventDefault();
console.log(this.loginForm.value);
}
doSignup(event) {
this.nav.push(SignupPage)
}
}
@IonicView({
templateUrl: 'pages/signup.html'
})
export class SignupPage {
constructor( nav: NavController ) { //, fb: FormBuilder ) {
this.nav = nav
Log.log('SIGNUP PAGE')
var fb = new FormBuilder()
this.signupForm = fb.group({
name: ['', Validators.required],
email: ['', Validators.required],
password: ['', Validators.required],
});
}
doLogin(event) {
this.nav.pop()
}
doSignup(event) {
Log.log('Doing signup')
event.preventDefault();
console.log(this.signupForm.value);
this.nav.push(AppPage)
}
}
@IonicView({
templateUrl: 'pages/app.html'
})
export class AppPage {
constructor( nav: NavController ) { //, fb: FormBuilder ) {
this.nav = nav;
this.streamTab = StreamTab
}
}
@IonicView({
templateUrl: 'pages/tabs/home.html'
})
class StreamTab {
constructor(nav: NavController) {
this.nav = nav;
this.posts = [
{'title': 'Just barked my first bark'},
{'title': 'Went poopy' }
];
}
selectPost(post) {
console.log('Select post', post);
this.nav.push(PostDetail, {
post
}, {
transition: '3dflip'
})
}
}
@IonicView({
templateUrl: 'pages/post/detail.html'
})
class PostDetail {
constructor(nav: NavController) {
this.nav = nav;
this.title = 'Hello'
}
selectItem() {
this.nav.push(PostDetailTab)
}
}
@IonicView({
templateUrl: 'pages/splash.html'
})
class SplashPage {
constructor(nav: NavController) {
this.nav = nav;
window.nav = nav;
}
doLogin() {
this.nav.push(LoginPage);
}
}
@App({
template: '<ion-nav [root]="rootView"></ion-nav>'
})
class IonicApp {
constructor() {
this.rootView = SplashPage;
}
}

View File

@@ -1,6 +0,0 @@
<ion-tabs>
<ion-tab [initial]="streamTab" tab-title="Stream">
</ion-tab>
</ion-tabs>

View File

@@ -1,22 +0,0 @@
<ion-toolbar *header>
<ion-title>
Login
</ion-title>
</ion-toolbar>
<ion-content padding>
<form (submit)="doLogin($event)" [control-group]="loginForm">
<ion-input>
<input control="email" type="email" placeholder="Your email">
</ion-input>
<ion-input>
<input control="password" type="password" placeholder="Your password">
</ion-input>
<button dark block type="submit">Log in</button>
<div>
<button block (click)="doSignup()">Create an account</button>
</div>
</form>
</ion-content>

View File

@@ -1 +0,0 @@
<h1>{{title}}</h1>

View File

@@ -1,18 +0,0 @@
<ion-view nav-title="Signup" style="padding: 20px">
<form (submit)="doSignup($event)" [control-group]="signupForm">
<ion-input>
<input control="name" type="text" placeholder="Your name">
</ion-input>
<ion-input>
<input control="email" type="email" placeholder="Your email">
</ion-input>
<ion-input>
<input control="password" type="password" placeholder="Your password">
</ion-input>
<button dark block type="submit">Create account</button>
<div>
<button block (click)="doLogin()">Log in</button>
</div>
</form>
</ion-view>

View File

@@ -1,4 +0,0 @@
<div>
<h1>BARK PARK</h1>
<button (click)="doLogin()">Log in</button>
</div>

View File

@@ -1,10 +0,0 @@
<ion-view nav-title="Stream">
<ion-content padding>
<h2>Posts</h2>
<ion-list>
<ion-item *for="#post of posts" (click)="selectPost(post)">
{{post.title}}
</ion-item>
</ion-list>
</ion-content>
</ion-view>

View File

@@ -1,30 +0,0 @@
var APIUrl = 'https://hacker-news.firebaseio.com/v0/';
export class HackerNewsClient {
constructor() {
//this.fb = new Firebase(APIUrl);
}
getTopStories(cb) {
console.log('GETTING TOP STORIES');
this.fb.child('topstories').on('value', (snapshot) => {
let items = snapshot.val();
console.log('Fetched', items.length, 'items');
for(var itemID of items) {
this.fb.child("item").child(itemID).on('value', (data) => {
cb(data.val());
//console.log(data.val());
});
}
});
}
}
export var HackerNews = new HackerNewsClient();

View File

@@ -1,94 +0,0 @@
import {Component} from 'angular2/angular2';
import {App, NavController, IonicView} from 'ionic/ionic';
import {HackerNews} from './hn';
import {HNSinglePost} from './pages/single';
@Component({
selector: 'story'
})
@IonicView({
template: '<div class="hn-story"><ng-content></ng-content></div>'
})
export class Story {
constructor() {
}
}
@IonicView({
templateUrl: './pages/top.html'
})
class HNTopStories {
constructor(
nav: NavController
) {
this.nav = nav;
this.stories = [
/*
{
by: "FatalLogic",
descendants: 77,
id: 9444675,
//kids: Array[26]
score: 464,
text: "",
time: 1430116925,
title: "Under Pressure",
type: "story",
url: "http://minusbat.livejournal.com/180556.html"
}*/
];
var APIUrl = 'https://hacker-news.firebaseio.com/v0';
console.log('FIREBASE', window.Firebase);
this.fb = new window.Firebase(APIUrl);
this.fb.child('topstories').limitToFirst(20).once('value', (snapshot) => {
let items = snapshot.val();
console.log('Fetched', items.length, 'items');
for(var itemID of items) {
this.fb.child("item").child(itemID).on('value', (data) => {
console.log('GOT ITEM', data.val());
this.stories.push(data.val());
});
}
});
//doStuffEnd
}
openStory(story) {
console.log('Opening story', story);
this.nav.push(HNSinglePost, story);
}
/*
HackerNews.getTopStories((val) => {
new Promise((resolve, reject) => {
console.log('PROMISES!', val);
this.stories.push(val);
resolve();
});
});
*/
}
@App({
template: '<ion-nav [root]="rootView"></ion-nav>'
})
class IonicApp {
constructor() {
this.rootView = HNTopStories;
}
}

View File

@@ -1,11 +0,0 @@
<ion-nav [initial]="splashPage">
</ion-nav>
<style>
ion-navbar-section {
background-color: #ff6600 !important;
}
.md ion-title {
color: #fff !important;
}
</style>

View File

@@ -1,6 +0,0 @@
<ion-navbar *navbar><ion-title>{{post.title}}</ion-title></ion-navbar>
<ion-content>
<iframe style="width: 100%; height: 100%" src="{{post.url}}">
</iframe>
</ion-content>

View File

@@ -1,18 +0,0 @@
import {IonicView, NavController, NavParams} from 'ionic/ionic';
import {HackerNews} from '../hn'
@IonicView({
templateUrl: 'pages/single.html'
})
export class HNSinglePost {
constructor(
nav: NavController,
params: NavParams
) {
this.nav = nav;
this.post = params;
console.log('SINGLE', params);
}
}

View File

@@ -1,9 +0,0 @@
<ion-navbar *navbar><ion-title>Top</ion-title></ion-navbar>
<ion-content padding>
<ion-list inset>
<ion-item *ng-for="#story of stories" (click)="openStory(story)">{{story.title}}</ion-item>
</ion-list>
</ion-content>

View File

@@ -1,68 +0,0 @@
import {Directive, ElementRef, FormBuilder, Validators, ControlGroup} from 'angular2/angular2';
import {App, NavController, IonicView} from 'ionic/ionic';
@IonicView({
templateUrl: 'pages/app.html'
})
class AppPage {
constructor(nav: NavController) {
this.nav = nav;
}
doLogin() {
}
}
@Directive({
selector: '[parallax]',
inputs: [
'parallax'
]
})
export class ParallaxEffect {
constructor(
elementRef: ElementRef
) {
this.ele = elementRef.nativeElement;
setTimeout(() => {
Object.observe(this, (changes) => {
changes.forEach((change) => {
if(change.name == 'parallax') {
this.parallaxItems();
}
});
});
});
}
parallaxItems() {
let list = this.ele;
console.log('Moving items', this.parallax);
var x = Math.max(0, (1 - this.parallax) * 20);
var y = 0;//Math.max(0, (1 - this.parallax) * 10);
var scale = Math.min(1, (0.9 + 0.1 * this.parallax));
list.style['opacity'] = Math.min(this.parallax, 1);
list.style['transform'] = 'translate3d(' + x + 'px, ' + y + 'px, 0) scale(' + scale + ')';
}
}
/**
* Main app entry point
*/
@App({
directives: [ParallaxEffect],
templateUrl: 'main.html'
})
class IonicApp {
constructor() {
this.rootView = AppPage
this.menuOpenAmount = 0;
}
onMenuOpening(amt) {
this.menuOpenAmount = amt;
}
}

View File

@@ -1,83 +0,0 @@
<ion-view>
<ion-menu id="menu" side="left" [content]="content" (opening)="onMenuOpening($event, amt)">
<ion-list inset [parallax]="menuOpenAmount">
<ion-item>
Search
</ion-item>
<ion-item>
Browse
</ion-item>
<ion-item>
Activity
</ion-item>
<ion-item>
Radio
</ion-item>
<ion-item>
Your Music
</ion-item>
<ion-item>
Settings
</ion-item>
</ion-list>
</ion-menu>
<ion-nav #content [root]="rootView"></ion-nav>
<style>
ion-view {
background-color: black !important;
}
.toolbar-container {
background-color: #202020 !important;
}
#menu {
background: url('http://ionic-io-assets.s3.amazonaws.com/menu.jpg');
background-size: cover;
}
.mode-ios .list .item {
background: transparent;
min-height: 55px;
}
.mode-ios .list .item ion-item-content {
background: transparent;
color: rgba(255,255,255,0.8);
padding: 10px;
}
.mode-ios .toolbar-ios ion-title {
color: white;
font-size: 14px;
font-weight: normal;
letter-spacing: 2px;
}
#menu ion-item .item-label {
font-size: 22px;
}
ion-content {
background: transparent !important;
}
.mode-ios #menu ion-item-content:after {
background: none;
}
.mode-ios .item:first-of-type:before {
background: none !important;
}
.mode-ios .item:last-of-type:after {
background: none !important;
}
.mode-ios .toolbar-container-ios:after {
background: none;
}
ion-nav {
background: black;
}
.nav-item {
background: white;
}
</style>
</ion-view>

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

View File

@@ -1,22 +0,0 @@
<ion-toolbar *header>
<ion-title>YOUR MUSIC</ion-title>
</ion-toolbar>
<ion-content>
<ion-list>
<ion-item>
Playlight
</ion-item>
<ion-item>
Songs
</ion-item>
<ion-item>
Albums
</ion-item>
<ion-item>
Artists
</ion-item>
</ion-list>
</ion-content>

View File

@@ -1,56 +0,0 @@
import {ElementRef, Component, Directive} from 'angular2/angular2';
import {FormBuilder, Control, ControlGroup, Validators} from 'angular2/angular2';
import {App, Modal, Animation, Content} from 'ionic/ionic';
import {NavController, NavParams, IonicView} from 'ionic/ionic';
import {dom} from 'ionic/util';
@Directive({
selector: '[parallax]',
inputs: [
'parallax',
'counter'
]
})
export class ParallaxEffect {
constructor(
content: Content,
elementRef: ElementRef
) {
this.ele = elementRef.nativeElement;
this.scroller = this.ele.querySelector('scroll-content');
this.scroller.addEventListener('scroll', (e) => {
//this.counter.innerHTML = e.target.scrollTop;
dom.raf(() => {
this.parallax.style[dom.CSS.transform] = 'translateY(' + -Math.min(300, (e.target.scrollTop / 4)) + 'px) scale(1)';
if(e.target.scrollTop < 0) {
this.parallax.style[dom.CSS.transform] = 'translateY(0) scale(' + (1 + Math.abs(e.target.scrollTop / 500)) + ')';
}
})
});
setTimeout(() => {
console.log('Watching', this.target, this.counter);
})
}
}
@App({
templateUrl: 'main.html',
directives: [ParallaxEffect]
})
class IonicApp {
constructor() {
this.items = [];
for(let i = 0; i < 100; i++) {
this.items.push({
title: 'Item ' + i
})
}
}
}

View File

@@ -1,53 +0,0 @@
<ion-view>
<div id="profile" #profile>
<div id="face">
</div>
</div>
<ion-content [parallax]="profile"><!-- [counter]="counter">-->
<ion-list inset>
<ion-item *ng-for="#item of items">
{{item.title}}
</ion-item>
</ion-list>
</ion-content>
<!--<div id="counter" #counter></div>-->
</ion-view>
<style>
#counter {
position: fixed;
bottom: 15px;
right: 15px;
background: black;
color: #fff;
font-size: 11px;
padding: 5px;
border-radius: 5px;
}
#profile {
position: absolute;
top: 0;
height: 300px;
width: 100%;
background: url('http://ionic-io-assets.s3.amazonaws.com/demos/bus.jpg') no-repeat transparent;
background-size: cover;
}
#face {
background: url('http://ionic-io-assets.s3.amazonaws.com/demos/face.jpg') no-repeat transparent;
background-size: cover;
background-position: 50%;
width: 96px;
height: 96px;
border-radius: 96px;
border: 2px solid #fff;
box-shadow: 0px 0px 4px rgba(0,0,0,0.4);
margin: 100px auto 0 auto;
}
ion-content {
background: transparent !important;
}
scroll-content {
padding-top: 300px;
}
</style>

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 519 KiB

View File

@@ -1,105 +0,0 @@
import {App, IonicApp} from 'ionic/ionic';
import {ButtonPage} from './pages/button'
import {NavPage} from './pages/nav'
import {PullToRefreshPage} from './pages/pull-to-refresh'
import {ListPage} from './pages/list'
import {ListGroupPage} from './pages/list-group'
import {CardPage} from './pages/card'
import {FormPage} from './pages/form'
import {SegmentPage} from './pages/segment'
import {SearchBarPage} from './pages/searchbar'
import {TableSearchPage} from './pages/table-search'
import {IconsPage} from './pages/ionicons'
import {TabsPage} from './pages/tabs'
import {MenuPage} from './pages/menu'
import {AnimationPage} from './pages/animation'
import {SlidePage} from './pages/slides'
import {ActionSheetPage} from './pages/action-sheet'
import {ModalPage} from './pages/modal'
@App({
templateUrl: 'main.html',
routes: [
{
path: '/nav',
component: NavPage,
root: true
}, {
path: '/buttons',
component: ButtonPage
}, {
path: '/lists',
component: ListPage
}, {
path: '/list-groups',
component: ListGroupPage
}, {
path: '/modal',
component: ModalPage
}, {
path: '/ptr',
component: PullToRefreshPage
}, {
path: '/cards',
component: CardPage
}, {
path: '/forms',
component: FormPage
}, {
path: '/segments',
component: SegmentPage
}, {
path: '/table-search',
component: TableSearchPage
}, {
path: '/icons',
component: IconsPage
}, {
path: '/menu',
component: MenuPage
}, {
path: '/animation',
component: AnimationPage
}, {
path: '/slides',
component: SlidePage
}, {
path: '/action-sheet',
component: ActionSheetPage
}
]
})
class MyApp {
constructor(app: IonicApp) {
this.app = app;
this.components = [
{ title: 'Navigation', component: NavPage },
{ title: 'Tabs', component: TabsPage },
{ title: 'Buttons', component: ButtonPage },
{ title: 'Lists', component: ListPage },
{ title: 'List Groups', component: ListGroupPage },
{ title: 'Modal', component: ModalPage },
{ title: 'Pull to Refresh', component: PullToRefreshPage },
{ title: 'Cards', component: CardPage },
{ title: 'Forms', component: FormPage },
{ title: 'Segments', component: SegmentPage },
{ title: 'Search Bar', component: SearchBarPage },
{ title: 'Table Search', component: TableSearchPage },
{ title: 'Icons', component: IconsPage },
{ title: 'Menu', component: MenuPage },
{ title: 'Animation', component: AnimationPage },
{ title: 'Slides', component: SlidePage},
{ title: 'Action Sheet', component: ActionSheetPage },
];
}
openPage(menu, component) {
menu.close();
let nav = this.app.getComponent('myNav');
nav.setViews([component.component]);
}
}

View File

@@ -1,22 +0,0 @@
<ion-menu #menu [content]="content" id="mainMenu">
<ion-toolbar><ion-title>Ionic 2.0</ion-title></ion-toolbar>
<ion-content>
<ion-list>
<ion-item *ng-for="#c of components" (click)="openPage(menu, c)">
<span>{{c.title}}</span>
</ion-item>
</ion-list>
</ion-content>
</ion-menu>
<ion-nav #content swipe-back-enabled="false"></ion-nav>
<style>
my-modal {
display: block;
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
}
</style>

View File

@@ -1,60 +0,0 @@
import {IonicApp, IonicView, NavController, ActionSheet} from 'ionic/ionic';
import {SinkPage} from '../sink-page';
@IonicView({
template: `
<ion-navbar *navbar><ion-nav-items primary><button icon (click)="toggleMenu()"><i class="icon ion-navicon"></i></button></ion-nav-items><ion-title>Action Sheet</ion-title></ion-navbar>
<ion-content padding>
<h2>Action Sheet</h2>
<p>
The Action Sheet, similar to Action Sheet's on iOS, is a slide-up prompt
that displays several options for the user to choose from before an action is performed.
</p>
<p>
Action Sheet's are great for prompting for dangerous actions (like deleting a photo album),
or showing a "context menu" with multiple actions the user can perform on something.
</p>
<button (click)="openMenu()">Open Menu</button>
</ion-content>
`
})
export class ActionSheetPage extends SinkPage {
constructor(app: IonicApp, nav: NavController, actionSheet: ActionSheet) {
super(app);
this.nav = nav;
this.actionSheet = actionSheet;
}
openMenu() {
console.log('Opening ActionSheet')
this.actionSheet.open({
buttons: [
{ text: 'Share This' },
{ text: 'Move' }
],
destructiveText: 'Delete',
titleText: 'Modify your album',
cancelText: 'Cancel',
cancel: function() {
// add cancel code..
console.log('Canceled');
},
destructiveButtonClicked: () => {
console.log('Destructive clicked');
},
buttonClicked: function(index) {
console.log('Button clicked', index);
if(index == 1) { return false; }
return true;
}
}).then(actionSheetRef => {
this.actionSheetRef = actionSheetRef;
})
}
}

View File

@@ -1,133 +0,0 @@
import {IonicView, Animation, IonicApp} from 'ionic/ionic';
import {SinkPage} from '../sink-page';
let opacity = 0.2;
let rotateZ = '180deg';
let translateX = '100px';
let scale = 0.6;
@IonicView({
template: `
<ion-navbar *navbar><ion-nav-items primary><button icon (click)="toggleMenu()"><i class="icon ion-navicon"></i></button></ion-nav-items><ion-title>Animation</ion-title></ion-navbar>
<style>
.ball-container {
position: absolute;
top: 300px;
left: 20px;
border: 1px solid gray;
width: 300px;
height: 51px;
}
.ball {
position: absolute;
width: 50px;
height: 50px;
background: blue;
}
</style>
<ion-content padding>
<h2>Animation</h2>
<p>
Ionic comes with a powerful Animation library based on the emerging Web Animation API. Trigger
animations based on user actions, step (or "scrub") through during a drag gesture, or add
realistic physics effects to your app. The Animation API is a major improvement over CSS animations.
</p>
<p>
<button (click)="play($event)">Play</button>
<button (click)="pause($event)">Pause</button>
</p>
<div class="ball-container">
<div class="ball"></div>
</div>
<div style="position: absolute; top: 400px; left: 20px;">
<div class="red square" style="position:absolute; width:100px; height:100px; background:red; top: 0; left: 0;"></div>
<div class="green square" style="position:absolute; width:100px; height:100px; background:green; top: 0; left: 100px;"></div>
<div class="blue square" style="position:absolute; width:100px; height:100px; background:blue; top: 0; left: 200px;"></div>
</div>
<div style="position: absolute; top: 500px; left: 20px;">
<div class="yellow square2" style="position:absolute; width:100px; height:100px; background:yellow; top: 0; left: 0;"></div>
<div class="purple square2" style="position:absolute; width:100px; height:100px; background:purple; top: 0; left: 100px;"></div>
<div class="maroon square2" style="position:absolute; width:100px; height:100px; background:maroon; top: 0; left: 200px;"></div>
</div>
<p>
<input type="range" (input)="progress($event)" value="0" min="0" step="0.001" max="1" style="width:200px; margin-left: 100px">
</p>
</ion-content>
`
})
export class AnimationPage extends SinkPage {
constructor(app: IonicApp) {
super(app);
this.animation = new Animation();
this.animation
.duration(2000)
.easing('spring', { damping: 6, elasticity: 10 });
var ball = new Animation( document.querySelector('.ball') );
ball
.from('translateX', '0px')
.to('translateX', '250px')
this.animation.add(ball);
var row1 = new Animation( document.querySelectorAll('.square') );
row1
.from('opacity', 0.8)
.to('opacity', 0.2)
this.animation.add(row1);
var row2 = new Animation( document.querySelectorAll('.square2') );
row2
.from('rotate', '0deg')
.from('scale', '1')
.to('rotate', '90deg')
.to('scale', '0.5')
.before.addClass('added-before-play')
.after.addClass('added-after-finish')
this.animation.add(row1, row2);
this.animation.onReady(animation => {
console.log('onReady', animation);
});
this.animation.onPlay(animation => {
console.log('onPlay', animation);
});
this.animation.onFinish(animation => {
console.log('onFinish', animation);
});
}
play() {
this.animation.play();
}
pause() {
this.animation.pause();
}
progress(ev) {
this.animation.progress( parseFloat(ev.srcElement.value) );
}
}

View File

@@ -1,35 +0,0 @@
import {IonicApp, IonicView} from 'ionic/ionic';
import {SinkPage} from '../sink-page';
@IonicView({
template: `
<ion-navbar *navbar><ion-nav-items primary><button icon (click)="toggleMenu()"><i class="icon ion-navicon"></i></button></ion-nav-items><ion-title>Menu</ion-title></ion-navbar>
<ion-content padding>
<h2>Menu</h2>
<p>
Menus slide or swipe in to show more information.
</p>
<p>
Try it! Just swipe from the left edge of the screen to the right to expose
the app menu, or tap the button to toggle the menu:
</p>
<p>
<div class="height: 50px; background-color: E05780; width: 5px; margin-left: -15px"></div>
</p>
<p>
<button (click)="toggleMenu()">Toggle</button>
</p>
</ion-content>
`
})
export class MenuPage extends SinkPage {
constructor(app: IonicApp) {
super(app);
}
openMenu() {
}
}

View File

@@ -1,73 +0,0 @@
import {IonicApp, IonicView, NavController} from 'ionic/ionic';
import {SinkPage} from '../sink-page';
@IonicView({
template: `
<ion-navbar *navbar><ion-nav-items primary><button icon (click)="toggleMenu()"><i class="icon ion-navicon"></i></button></ion-nav-items><ion-title>Buttons</ion-title></ion-navbar>
<ion-content padding>
<h2>Buttons</h2>
<p>
The faithful button. Not only our favorite place to click, but a true friend.
</p>
<p>
Buttons come in lots of different colors:
</p>
<p>
<div (click)="onButtonClick($event)">
<button>Primary</button>
<button secondary>Secondary</button>
<button danger>Danger</button>
<button light>Light</button>
<button dark>Dark</button>
</div>
</p>
<p>
various shapes:
</p>
<p>
<div (click)="onButtonClick($event)">
<button small>small</button>
<button dark>Medium</button>
<button secondary large>LARGE</button>
</div>
</p>
<p>
and with different embellishments:
</p>
<p>
<div (click)="onButtonClick($event)">
<button outline>Outline</button>
<button dark clear>Clear</button>
<button danger block>Block</button>
</div>
</p>
</p>
<div *ng-if="clicked">
<b>CLICKED</b>
</div>
</p>
</ion-content>
`
})
export class ButtonPage extends SinkPage {
constructor(app: IonicApp, nav: NavController) {
super(app);
this.nav = nav;
}
onButtonClick(event) {
console.log('On button click', event);
clearTimeout(this.clickTimeout);
this.clicked = true;
this.clickTimeout = setTimeout(() => {
this.clicked = false;
}, 500);
}
}

View File

@@ -1,40 +0,0 @@
import {IonicApp, IonicView} from 'ionic/ionic';
import {SinkPage} from '../sink-page';
@IonicView({
template: `
<ion-navbar *navbar><ion-nav-items primary><button icon (click)="toggleMenu()"><i class="icon ion-navicon"></i></button></ion-nav-items><ion-title>Cards</ion-title></ion-navbar>
<ion-content padding>
<h2>Cards</h2>
<p>
Cards are an emerging UI concept where a bit of content is displayed
like it would be on an index card or a piece of paper.
</p>
<p>
Cards are great for displaying contextual information in a small space,
like a Tweet, todays weather report, or a photo.
</p>
<ion-card>
<ion-card-header>
Card Header
</ion-card-header>
<div>
<img src="http://i.imgur.com/7BEPcGo.jpg">
</div>
</ion-card>
</ion-content>
`
})
export class CardPage extends SinkPage {
constructor(app: IonicApp) {
super(app);
}
}

View File

@@ -1,50 +0,0 @@
import {FormBuilder, Validators, ControlGroup} from 'angular2/angular2';
import {IonicApp, IonicView} from 'ionic/ionic';
import {SinkPage} from '../sink-page';
@IonicView({
template: `
<ion-navbar *navbar><ion-nav-items primary><button icon (click)="toggleMenu()"><i class="icon ion-navicon"></i></button></ion-nav-items><ion-title>Form</ion-title></ion-navbar>
<ion-content padding>
<h2>Forms</h2>
<p>
Forms help you gather important information from the user, like
login information or content to send to your server.
</p>
<p>
Ionic comes with a variety of useful from controls, like
text inputs, text areas, toggle switches, and sliders.
</p>
<form (submit)="doSubmit($event)" [ng-form-model]="form">
<ion-input>
<input ng-control="email" type="email" placeholder="Your email">
</ion-input>
<ion-input>
<input control="password" type="password" placeholder="Your password">
</ion-input>
<button block type="submit">Submit</button>
</form>
</ion-content>
`
})
export class FormPage extends SinkPage {
constructor(app: IonicApp) {
super(app);
var fb = new FormBuilder()
this.form = fb.group({
email: ['', Validators.required],
password: ['', Validators.required],
});
}
doSubmit(event) {
console.log('Submitted:', this.form.value);
event.preventDefault();
}
}

View File

@@ -1,30 +0,0 @@
import {IonicApp, IonicView} from 'ionic/ionic';
import {SinkPage} from '../sink-page';
@IonicView({
template: `
<ion-navbar *navbar><ion-nav-items primary><button icon (click)="toggleMenu()"><i class="icon ion-navicon"></i></button></ion-nav-items><ion-title>Icons</ion-title></ion-navbar>
<ion-content padding>
<h2>Icons</h2>
<p>
Ionic comes with a totally free (in price and license), icon pack (known as "Ionicons") with over 700
icons for your app.
</p>
<p>
Ionicons is an icon font, and can be used with simple CSS icon classes (recommended), or
with unicode characters.
</p>
<div>
<img src="http://ionic-io-assets.s3.amazonaws.com/v2/ionicons.png" style="max-width: 100%">
</div>
</ion-content>
`
})
export class IconsPage extends SinkPage {
constructor(app: IonicApp) {
super(app);
}
}

View File

@@ -1,45 +0,0 @@
import {IonicApp, IonicView} from 'ionic/ionic';
import {SinkPage} from '../sink-page';
@IonicView({
template: `
<ion-navbar *navbar><ion-nav-items primary><button icon (click)="toggleMenu()"><i class="icon ion-navicon"></i></button></ion-nav-items><ion-title>List Groups</ion-title></ion-navbar>
<ion-content>
<ion-list inset>
<ion-item-group *ng-for="#group of groups">
<ion-item-group-title>{{group.title}}</ion-item-group-title>
<ion-item *ng-for="#item of group.items">
{{item.title}}
</ion-item>
</ion-item-group>
</ion-list>
</ion-content>
`
})
export class ListGroupPage extends SinkPage {
constructor(app: IonicApp) {
super(app)
this.groups = [];
var letters = "abcdefghijklmnopqrstuvwxyz".split('');
for(let i = 0; i < letters.length; i++) {
let group = [];
for(let j = 0; j < 10; j++) {
group.push({
title: letters[i] + j
});
}
this.groups.push({
title: letters[i].toUpperCase(),
items: group
});
}
}
}

View File

@@ -1,44 +0,0 @@
import {IonicApp, IonicView} from 'ionic/ionic';
import {SinkPage} from '../sink-page';
@IonicView({
template: `
<ion-navbar *navbar><ion-nav-items primary><button icon (click)="toggleMenu()"><i class="icon ion-navicon"></i></button></ion-nav-items><ion-title>Lists</ion-title></ion-navbar>
<ion-content padding>
<h2>Lists</h2>
<p>
Lists display data as rows in a table.
</p>
<p>
Lists are great for displaying sets of things like contacts,
songs, and documents.
</p>
<ion-list inset>
<ion-header>
List Header
</ion-header>
<ion-item>
<input control="email" type="email" placeholder="Your email">
</ion-item>
<ion-item>
Item
</ion-item>
<ion-item>
Item
</ion-item>
</ion-list>
</ion-content>
`
})
export class ListPage extends SinkPage {
constructor(app: IonicApp) {
super(app)
}
}

View File

@@ -1,41 +0,0 @@
import {IonicApp, IonicView, Modal} from 'ionic/ionic';
import {SinkPage} from '../sink-page';
@IonicView({
template: `
<ion-navbar *navbar><ion-nav-items primary><button icon (click)="toggleMenu()"><i class="icon ion-navicon"></i></button></ion-nav-items><ion-title>Modal</ion-title></ion-navbar>
<ion-content padding>
<h2>Modal</h2>
<p>
Modals are entire views that slide in off screen.
</p>
<p>
Modals make it easy to open a new "context" for the user, without taking them
out of the current context. For example, clicking the "compose" button
on a mail app might slide up a Compose modal.
</p>
<button (click)="openModal()">Open Modal</button>
</ion-content>
`
})
export class ModalPage extends SinkPage {
constructor(app: IonicApp, modal: Modal) {
super(app);
this.modal = modal;
}
openModal() {
console.log('Opening modal');
this.modal.open(MyModal);
}
}
@IonicView({
template: '<ion-content padding><button (click)="close()" primary>Close Modal</button></ion-content>',
})
export class MyModal {}

View File

@@ -1,44 +0,0 @@
import {IonicView, NavController} from 'ionic/ionic';
@IonicView({
template: `
<ion-navbar *navbar><ion-title>Navigation</ion-title></ion-navbar>
<ion-content padding>
<h2>Navigation</h2>
<p>
Navigation makes your app feel like, well, an app!
</p>
<p>
With the navigation features in Ionic, we can navigate to new pages,
go back in history (including swipe-to-go-back), and control the stack
of pages the user can navigate between.
</p>
<button (click)="push()">Push</button>
</ion-content>
`
})
export class NavPage {
constructor(nav: NavController) {
this.nav = nav;
}
push() {
this.nav.push(NavSecondPage);
}
}
@IonicView({
template: `
<ion-navbar *navbar><ion-title>Second Page</ion-title></ion-navbar>
<ion-content padding>
<button (click)="nav.pop()">Pop</button>
</ion-content>
`
})
export class NavSecondPage {
constructor(nav: NavController) {
this.nav = nav;
}
}

View File

@@ -1,50 +0,0 @@
import {IonicApp, IonicView} from 'ionic/ionic';
import {SinkPage} from '../sink-page';
@IonicView({
template: `
<ion-navbar *navbar><ion-nav-items primary><button icon (click)="toggleMenu()"><i class="icon ion-navicon"></i></button></ion-nav-items><ion-title>Pull to Refresh</ion-title></ion-navbar>
<ion-content>
<ion-refresher (starting)="doStarting()" (refresh)="doRefresh($event, refresher)" (pulling)="doPulling($event, amt)">
</ion-refresher>
<ion-list inset>
<ion-item *ng-for="#item of items">
{{item.title}}
</ion-item>
</ion-list>
</ion-content>
`
})
export class PullToRefreshPage extends SinkPage {
constructor(app: IonicApp) {
super(app);
this.items = [];
for(let i = 90; i < 100; i++) {
this.items.push({
title: i
});
}
this.i = 90;
}
doRefresh(refresher) {
console.log('DOREFRESH', refresher)
this.items.unshift({
title: (--this.i)
});
setTimeout(() => {
refresher.complete();
}, 1500)
}
doStarting() {
console.log('DOSTARTING');
}
doPulling(amt) {
console.log('DOPULLING', amt);
}
}

View File

@@ -1,44 +0,0 @@
import {Component, FormBuilder, Validators, ControlGroup} from 'angular2/angular2';
import {IonicApp, IonicView} from 'ionic/ionic';
import {SinkPage} from '../sink-page';
@Component({
selector: 'ion-view',
appInjector: [FormBuilder]
})
@IonicView({
template: `
<ion-navbar *navbar><ion-nav-items primary><button icon (click)="toggleMenu()"><i class="icon ion-navicon"></i></button></ion-nav-items><ion-title>Search Bar</ion-title></ion-navbar>
<ion-content padding>
<h2>Search Bar</h2>
<p>
The Search Bar is a multi-function search component.
</p>
<p>
The bar can sit standalone as part of a form or header search,
or it can also handle and display a list of search results.
</p>
<form (submit)="doSubmit($event)" [ng-form-model]="form">
<ion-searchbar placeholder="Search" ng-control="searchQuery"></ion-searchbar>
<div>
Query: <b>{{form.controls.searchQuery.value}}</b>
</div>
</form>
</ion-content>
`
})
export class SearchBarPage extends SinkPage {
constructor(app: IonicApp, formBuilder: FormBuilder) {
super(app);
//var fb = new FormBuilder();
this.form = formBuilder.group({
searchQuery: ['', Validators.required]
});
}
}

View File

@@ -1,70 +0,0 @@
import {FormBuilder, Validators, Control, ControlGroup} from 'angular2/angular2';
import {IonicView} from 'ionic/ionic';
@IonicView({
template: `
<ion-navbar *navbar><ion-title>Segment</ion-title></ion-navbar>
<ion-content padding>
<h2>Segment</h2>
<p>
A segment is a radio-style filter bar to let the user toggle between
multiple, exclusive options.
</p>
<p>
Segments are useful for quick filtering, like switching the
the map display between street, hybrid, and satellite.
</p>
<form (submit)="doSubmit($event)">
<div ng-control-group="form">
<ion-segment [ng-form-control]="mapStyle">
<ion-segment-button value="standard" button>
Standard
</ion-segment-button>
<ion-segment-button value="hybrid" button>
Hybrid
</ion-segment-button>
<ion-segment-button value="sat" button>
Satellite
</ion-segment-button>
</ion-segment>
</div>
<button type="submit">Submit</button>
</form>
Map mode: <b>{{form.controls.mapStyle.value}}</b>
<div [switch]="form.controls.mapStyle.value">
<div *switch-when="'standard'">
<h2>Standard</h2>
</div>
<div *switch-when="'hybrid'">
<h2>Hybrid</h2>
</div>
<div *switch-when="'sat'">
<h2>Satellite!!</h2>
</div>
</div>
</ion-content>
`
})
export class SegmentPage {
constructor() {
/*
var fb = new FormBuilder();
this.form = fb.group({
mapStyle: ['hybrid', Validators.required]
});
*/
this.mapStyle = new Control("hybrid", Validators.required);
this.form = new ControlGroup({
"mapStyle": this.mapStyle
});
}
}

View File

@@ -1,58 +0,0 @@
import {IonicApp, IonicView} from 'ionic/ionic';
import {SinkPage} from '../sink-page';
@IonicView({
template: `
<ion-navbar *navbar><ion-nav-items primary><button icon (click)="toggleMenu()"><i class="icon ion-navicon"></i></button></ion-nav-items><ion-title>Slides</ion-title></ion-navbar>
<ion-content padding>
<h2>Slides</h2>
<p>
Slides have a set of pages that can be swiped between.
</p>
<p>
Slides are perfect for making image slideshows,
swipe tutorials, or document viewers.
</p>
<ion-slides style="height: 300px">
<ion-slide style="background-color: #387ef5">
<h2 style="color: #fff">Page 1</h2>
</ion-slide>
<ion-slide style="background-color: rgb(245, 255, 96)">
<h2 style="color: #222;"">Page 2</h2>
</ion-slide>
<ion-slide style="background-color: pink">
<h2>Page 3</h2>
</ion-slide>
<ion-slide style="background-color: red">
<h2>Page 4</h2>
</ion-slide>
<ion-slide style="background-color: rgb(1, 157, 157)">
<h2>Page 5</h2>
</ion-slide>
<ion-pager>
</ion-pager>
</ion-slides>
</ion-content>
<style>
.slide {
display: flex !important;
align-items: center;
justify-content: center;
}
.slide h2 {
color: #fff;
text-transform: uppercase;
letter-spacing: 2px;
font-size: 16px;
}
</style>
`
})
export class SlidePage extends SinkPage {
constructor(app: IonicApp) {
super(app);
}
}

View File

@@ -1,64 +0,0 @@
import {Component, FormBuilder, Validators, ControlGroup} from 'angular2/angular2';
import {IonicView} from 'ionic/ionic';
function randomTitle() {
var items = ['Pizza', 'Pumpkin', 'Apple', 'Bologna', 'Durian', 'Banana', 'Meat pie'];
return items[Math.floor(Math.random() * items.length)];
}
@Component({
selector: 'ion-view',
appInjector: [FormBuilder]
})
@IonicView({
template: `
<ion-navbar *navbar><ion-title>Table Search</ion-title></ion-navbar>
<ion-content>
<form (submit)="doSearch($event)" [control-group]="form">
<ion-searchbar control="searchQuery"></ion-searchbar>
<ion-list #list>
<ion-item *ng-for="#item of getItems()"><!--items | search:form.controls.searchControl.value-->
{{item.title}}
</ion-item>
</ion-list>
</form>
</ion-content>
`
})
export class TableSearchPage {
constructor(formBuilder: FormBuilder) {
console.log('IonicApp Start')
this.form = formBuilder.group({
searchQuery: ['', Validators.required]
});
this.query = 'HELLO';
this.items = [];
for(let i = 0; i < 100; i++) {
this.items.push({
title: randomTitle()
})
}
}
getItems() {
var q = this.form.controls.searchQuery.value;
if(q.trim() == '') {
return this.items;
}
return this.items.filter((v) => {
if(v.title.toLowerCase().indexOf(q.toLowerCase()) >= 0) {
return true;
}
return false;
})
}
}

View File

@@ -1,84 +0,0 @@
import {FormBuilder, Validators, ControlGroup} from 'angular2/angular2';
import {IonicApp, IonicView, NavController} from 'ionic/ionic';
import {SinkPage} from '../sink-page';
@IonicView({
template: '' +
'<ion-navbar *navbar>' +
'<ion-title>Featured</ion-title>' +
'</ion-navbar>' +
'<ion-content padding>' +
'</ion-content>'
})
class FeaturedTabPage {
constructor(nav: NavController) {
this.nav = nav;
}
}
@IonicView({
template: '' +
'<ion-navbar *navbar>' +
'<ion-title>Top</ion-title>' +
'</ion-navbar>' +
'<ion-content padding>' +
'</ion-content>'
})
class TopTabPage {
constructor(nav: NavController) {
this.nav = nav;
}
}
@IonicView({
template: '' +
'<ion-navbar *navbar>' +
'<ion-title>Search</ion-title>' +
'</ion-navbar>' +
'<ion-content padding>' +
'</ion-content>'
})
class SearchTabPage {
constructor(nav: NavController) {
this.nav = nav;
}
}
@IonicView({
template: '' +
'<ion-navbar *navbar>' +
'<ion-title>Updates</ion-title>' +
'</ion-navbar>' +
'<ion-content padding>' +
'</ion-content>'
})
class UpdatesTabPage {
constructor(nav: NavController) {
this.nav = nav;
}
}
@IonicView({
template: `
<ion-navbar *navbar><ion-nav-items primary><button icon (click)="toggleMenu()"><i class="icon ion-navicon"></i></button></ion-nav-items><ion-title>Tabs</ion-title></ion-navbar>
<ion-tabs id="tabs">
<ion-tab tab-title="Featured" tab-icon="star" [root]="featuredTab"></ion-tab>
<ion-tab tab-title="Top Charts" tab-icon="stats" [root]="topTab"></ion-tab>
<ion-tab tab-title="Search" tab-icon="search" [root]="searchTab"></ion-tab>
<ion-tab tab-title="Updates" tab-icon="cloud-download" [root]="updatesTab"></ion-tab>
</ion-tabs>
`
})
export class TabsPage extends SinkPage {
constructor(app: IonicApp) {
super(app);
this.featuredTab = FeaturedTabPage;
this.topTab = TopTabPage;
this.searchTab = SearchTabPage;
this.updatesTab = UpdatesTabPage;
}
}

View File

@@ -1,12 +0,0 @@
/**
* Simple wrapper page for the side menu toggle.
*/
export class SinkPage {
constructor(app: IonicApp) {
this.app = app;
}
toggleMenu() {
let menu = this.app.getComponent('mainMenu');
menu.toggle();
}
}

View File

@@ -1,15 +0,0 @@
<ion-navbar *navbar id="p2toolbar"><ion-title>Post</ion-title></ion-navbar>
<ion-content>
<ion-card>
<div class="card-content">
{{post.text}}
</div>
<img src="{{post.image}}">
</ion-card>
</ion-content>

View File

@@ -1,11 +0,0 @@
<ion-navbar *navbar id="p2toolbar"><ion-title>Feed</ion-title></ion-navbar>
<ion-content>
<div class="card" *ng-for="#post of posts" (click)="postClicked($event, post)">
<div class="card-content">
{{post.text}}
</div>
<img src="{{post.image}}">
</div>
</ion-content>

View File

@@ -1,127 +0,0 @@
import {FormBuilder, Control, ControlGroup, Validators} from 'angular2/angular2';
import {App, IonicView, Animation, Modal, NavController, NavParams} from 'ionic/ionic';
@IonicView({
templateUrl: 'detail.html'
})
export class DetailPage {
constructor(params: NavParams) {
this.post = params.post;
}
}
@IonicView({
templateUrl: 'feed.html'
})
export class FeedPage {
constructor(nav: NavController) {
this.nav = nav;
this.filterForm = new ControlGroup({
filterControl: new Control("")
});
this.posts = [
{
text: 'I tried to keep both arts alive, but the camera won. I found that while the camera does not express the soul, perhaps a photograph can!',
image: 'http://ionic-io-assets.s3.amazonaws.com/images/p.jpg',
day: 5
},
{
text: 'It is good to realize that if love and peace can prevail on earth, and if we can teach our children to honor nature\'s gifts, the joys and beauties of the outdoors will be here forever.',
image: 'http://ionic-io-assets.s3.amazonaws.com/images/p1.png',
day: 6
},
{
text: 'I see humanity now as one vast plant, needing for its highest fulfillment only love, the natural blessings of the great outdoors, and intelligent crossing and selection.',
image: 'http://ionic-io-assets.s3.amazonaws.com/images/p2.png',
day: 7
},
{
text: 'You must not lose faith in humanity. Humanity is an ocean; if a few drops of the ocean are dirty, the ocean does not become dirty.',
image: 'http://ionic-io-assets.s3.amazonaws.com/images/p3.png',
day: 7
},
{
text: 'Keep close to Nature\'s heart... and break clear away, once in awhile, and climb a mountain or spend a week in the woods. Wash your spirit clean.',
image: 'http://ionic-io-assets.s3.amazonaws.com/images/p4.png',
day: 8
}
];
}
postClicked(event, post) {
console.log('Post clicked');
this.nav.push(DetailPage, {
post: post
});
event.preventDefault();
}
}
@App({
templateUrl: 'main.html'
})
class IonicApp {
constructor(modal: Modal) {
this.modal = modal;
this.rootView = FeedPage;
}
openHeart() {
console.log('openHeart');
this.modal.open(HeartModal, {
enterAnimation: 'my-fade-in',
leaveAnimation: 'my-fade-out'
});
}
openGear() {
console.log('openGear');
this.modal.open(SettingsModal, {
enterAnimation: 'my-fade-in',
leaveAnimation: 'my-fade-out'
});
}
}
@IonicView({
template: '<ion-view id="settings-modal"><ion-content padding><button (click)="close()">Close</button></ion-content></ion-view>'
})
export class SettingsModal {}
@IonicView({
template: '<ion-view id="heart-modal"><button icon (click)="close()"><i class="icon ion-close"></i></button><h2>20</h2><p>You\'re pretty awesome</p></ion-view>'
})
export class HeartModal {}
class FadeIn extends Animation {
constructor(element) {
super(element);
this
.easing('ease')
.duration(250)
.fadeIn();
}
}
Animation.register('my-fade-in', FadeIn);
class FadeOut extends Animation {
constructor(element) {
super(element);
this
.easing('ease')
.duration(250)
.fadeOut();
}
}
Animation.register('my-fade-out', FadeOut);

View File

@@ -1,127 +0,0 @@
<ion-slides bounce="false">
<ion-slide>
<ion-view>
<ion-toolbar id="p1toolbar"><button (click)="openHeart()" class="toolbar-primary-item"><i class="icon ion-ios-heart"></i></button><ion-title>secret</ion-title><button (click)="openGear()" class="toolbar-secondary-item"><i class="icon ion-ios-gear"></i></button></ion-toolbar>
<ion-content>
<ion-list>
<ion-item>
Activity
</ion-item>
<ion-item>
Chat
</ion-item>
</ion-list>
<h2 style="text-align: center; color: #555; font-size: 16px;">Swipe Right <i class="icon ion-ios-arrow-right"></i></h2>
</ion-content>
</ion-view>
</ion-slide>
<ion-slide>
<ion-nav [root]="rootView"></ion-nav>
</ion-view>
</ion-slide>
</ion-slides>
<link href='http://fonts.googleapis.com/css?family=Lato&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
<style>
ion-app {
font-family: 'lato' !important;
}
ion-view {
background-color: #F5F5F5;
}
.mode-ios ion-toolbar {
padding-top: 20px;
}
ion-content {
background-color: #F5F5F5 !important;
}
ion-card {
box-shadow: none !important;
}
ion-card ion-card-content {
font-family: 'lato';
font-size: 16px;
line-height: 25px;
}
ion-card .item {
border: none !important;
}
ion-card ion-card-content {
padding: 25px !important;
}
.item {
font-family: 'lato';
font-size: 16px;
}
ion-item-content {
min-height: 70px !important;
}
ion-toolbar {
text-align: center;
align-items: center;
}
ion-toolbar:after {
background: none !important;
}
ion-toolbar {
letter-spacing: 1px;
}
.mode-ios ion-toolbar button i {
color: #fff !important;
font-size: 24px !important;
}
.mode-ios .item:first-of-type:before,
.mode-ios .item:last-of-type:after,
.mode-ios ion-item-content:after {
background-color: #F5F5F5 !important;
}
#p1toolbar {
background-color: #FFB400;
}
#p1toolbar ion-title {
color: white;
font-family: 'lato';
}
#p2toolbar {
background-color: #F93822;
}
#p2toolbar ion-title {
color: white;
font-family: 'lato';
}
#heart-modal {
background-color: #FFB400 !important;
justify-content: center;
align-items: center;
color: #fff;
}
#heart-modal h2 {
font-size: 36px !important;
}
#heart-modal p {
font-size: 18px;
}
#heart-modal icon {
position: absolute;
top: 13px;
left: 0px;
}
#heart-modal icon i {
font-size: 18px;
color: #fff;
}
</style>

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

View File

@@ -1,8 +0,0 @@
<div id="current-weather" *ng-if="current">
<h5><weather-icon [icon]="current.icon" id="current-icon"></weather-icon> {{current.currently.summary}}</h5>
<h5>
<span id="temp-hi"><i class="icon ion-arrow-up-c"></i> <span>{{highTemp}}</span>&deg;</span>
<span id="temp-lo"><i class="icon ion-arrow-down-c"></i> <span>{{lowTemp}}</span>&deg;</span>
</h5>
<h1 class="current-temp"><span>{{currentTemp}}</span>&deg;</h1>
</div>

View File

@@ -1,36 +0,0 @@
let FLICKR_API_KEY = '504fd7414f6275eb5b657ddbfba80a2c';
let baseUrl = 'https://api.flickr.com/services/rest/';
export class Flickr {
constructor() {
/*
var flickrSearch = $resource(baseUrl, {
method: 'flickr.groups.pools.getPhotos',
group_id: '1463451@N25',
safe_search: 1,
jsoncallback: 'JSON_CALLBACK',
api_key: FLICKR_API_KEY,
format: 'json'
}, {
get: {
method: 'JSONP'
}
});
*/
}
static search(tags, lat, lng) {
// TODO: update to angular2 HTTP service
return new Promise((resolve, reject) => {
Http.get(baseUrl + '?method=flickr.groups.pools.getPhotos&group_id=1463451@N25&safe_search=1&api_key=' + FLICKR_API_KEY + '&jsoncallback=JSON_CALLBACK&format=json&tags=' + tags + '&lat=' + lat + '&lng=' + lng, {
method: 'jsonp'
}).then((val) => {
resolve(val);
}, (err) => {
reject(httpResponse);
})
})
}
}

View File

@@ -1,56 +0,0 @@
export class Geo {
static reverseGeocode(lat, lng) {
return new Promise((resolve, reject) => {
let geocoder = new google.maps.Geocoder();
geocoder.geocode({
'latLng': new google.maps.LatLng(lat, lng)
}, (results, status) => {
if (status == google.maps.GeocoderStatus.OK) {
console.log('Reverse', results);
if(results.length > 1) {
var r = results[1];
var a, types;
var parts = [];
var foundLocality = false;
var foundState = false;
for(var i = 0; i < r.address_components.length; i++) {
a = r.address_components[i];
types = a.types;
for(var j = 0; j < types.length; j++) {
if(!foundLocality && types[j] == 'locality') {
foundLocality = true;
parts.push(a.long_name);
} else if(!foundState && types[j] == 'administrative_area_level_1') {
foundState = true;
parts.push(a.short_name);
}
}
}
console.log('Reverse', parts);
resolve(parts.join(', '));
}
} else {
console.log('reverse fail', results, status);
reject(results);
}
});
});
}
static getLocation() {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition((position) => {
resolve(position);
}, (error) => {
reject(error);
})
});
}
}

View File

@@ -1,281 +0,0 @@
import {Component, Directive, NgIf, NgFor, NgClass, ElementRef} from 'angular2/angular2';
import {FormBuilder, Control, ControlGroup, Validators, FORM_DIRECTIVES} from 'angular2/angular2';
import {App, IonicView, Animation, Content, Scroll, Modal, NavController, NavParams} from 'ionic/ionic';
import {Geo} from './geo';
import {Weather} from './weather';
import {Flickr} from './flickr';
@IonicView({
template: `<ion-view id="settings-modal">
<ion-toolbar><ion-title>Settings</ion-title></ion-toolbar>
<ion-content padding>
<form (submit)="doSubmit($event)" [ng-form-model]="settingsForm">
<ion-list>
<ion-input ion-item>
<ion-label>Units</ion-label>
<!--
<ion-segment ng-control="units">
<ion-segment-button value="standard" button>
&deg;F
</ion-segment-button>
<ion-segment-button value="standard" button>
&deg;C
</ion-segment-button>
</ion-segment>
-->
</ion-input>
</ion-list>
</form>
</ion-content>
</ion-view>`,
directives: [FORM_DIRECTIVES]
})
export class SettingsModal {
constructor(fb: FormBuilder) {
this.settingsForm = fb.group({
mapStyle: ['hybrid', Validators.required]
});
}
}
let WEATHER_ICONS = {
'partlycloudy': 'ion-ios-partlysunny-outline',
'mostlycloudy': 'ion-ios-partlysunny-outline',
'cloudy': 'ion-ios-cloudy-outline',
'rain': 'ion-ios-rainy-outline',
'tstorms': 'ion-ios-thunderstorm-outline',
'sunny': 'ion-ios-sunny-outline',
'clear-day': 'ion-ios-sunny-outline',
'nt_clear': 'ion-ios-moon-outline',
'clear-night': 'ion-ios-moon-outline'
};
@Component({
selector: 'weather-icon',
inputs: [
'icon'
],
template: '<i class="icon" [ng-class]="weatherIcon"></i>',
directives: [NgClass]
})
export class WeatherIcon {
constructor() {
}
onAllChangesDone(data) {
var icon = this.icon;
if(icon in WEATHER_ICONS) {
this.weatherIcon = WEATHER_ICONS[icon];
} else {
this.weatherIcon = WEATHER_ICONS['cloudy'];
}
}
}
@Component({
selector: 'current-time',
inputs: [
'localtz'
],
template: '<span class="current-time">{{currentTime}}</span>',
})
export class CurrentTime {
constructor() {
}
onInit() {
if(this.localtz) {
this.currentTime = new Date();
//this.currentTime = //$filter('date')(+(new Date), 'h:mm') + $scope.localtz;
}
}
}
@Component({
selector: 'current-weather',
inputs: [
'current'
],
templateUrl: 'current-weather.html',
directives: [NgIf]
})
export class CurrentWeather {
constructor(elementRef: ElementRef) {
this.elementRef = elementRef;
/*
$rootScope.$on('settings.changed', function(settings) {
var units = Settings.get('tempUnits');
if($scope.forecast) {
var forecast = $scope.forecast;
var current = $scope.current;
if(units == 'f') {
$scope.highTemp = forecast.forecastday[0].high.fahrenheit;
$scope.lowTemp = forecast.forecastday[0].low.fahrenheit;
$scope.currentTemp = Math.floor(current.temp_f);
} else {
$scope.highTemp = forecast.forecastday[0].high.celsius;
$scope.lowTemp = forecast.forecastday[0].low.celsius;
$scope.currentTemp = Math.floor(current.temp_c);
}
}
});
*/
// Delay so we are in the DOM and can calculate sizes
}
onInit() {
var windowHeight = window.innerHeight;
var thisHeight = this.elementRef.nativeElement.offsetHeight;
var headerHeight = document.querySelector('#header').offsetHeight;
this.elementRef.nativeElement.style.paddingTop = (windowHeight - 250) + 'px';
/*
document.querySelector('.content')).css('-webkit-overflow-scrolling', 'auto');
$timeout(function() {
angular.element(document.querySelector('.content')).css('-webkit-overflow-scrolling', 'touch');
}, 50);
*/
}
onAllChangesDone() {
var units = 'f';//Settings.get('tempUnits');
let current = this.current;
console.log('ALL CHANGES DONE', current);
if(current && current.currently) {
if(units == 'f') {
this.currentTemp = Math.floor(current.currently.temperature);
} else {
this.currentTemp = Math.floor(current.currently.temperature);
}
if(units == 'f') {
this.highTemp = Math.floor(current.daily.data[0].temperatureMax);
this.lowTemp = Math.floor(current.daily.data[0].temperatureMin);
} else {
this.highTemp = Math.floor(current.daily.data[0].temperatureMax);
this.lowTemp = Math.floor(current.daily.data[0].temperatureMin);
}
}
}
}
@Component({
selector: 'background-cycler',
inputs: [
'image'
],
template: '<div class="bg-image"></div>'
})
export class BackgroundCycler {
constructor(elementRef: ElementRef) {
this.elementRef = elementRef;
this.el = elementRef.nativeElement;
}
onInit() {
this.imageEl = this.el.children[0];
}
onAllChangesDone() {
var item = this.image;
if(item) {
var url = "http://farm"+ item.farm +".static.flickr.com/"+ item.server +"/"+ item.id +"_"+ item.secret + "_z.jpg";
this.imageEl.style.backgroundImage = 'url(' + url + ')';
}
}
}
@App({
templateUrl: 'main.html',
directives: [CurrentWeather, WeatherIcon, BackgroundCycler]
})
class WeatherApp {
constructor(modal: Modal) {
this.modal = modal;
this.currentLocationString = 'Madison, WI';
this.activeBgImageIndex = 0;
}
onInit() {
this.refreshData();
}
showSettings() {
this.modal.open(SettingsModal).then((settingsModal) => {
this.settingsModal = settingsModal;
});
}
getBackgroundImage(lat, lng, locString) {
Flickr.search(locString, lat, lng).then((resp) => {
let photos = resp.photos;
if(photos.photo.length) {
this.bgImages = photos.photo;
this.cycleBgImages();
}
}, (error) => {
console.error('Unable to get Flickr images', error);
});
}
getCurrent(lat, lng, locString) {
Weather.getAtLocation(lat, lng).then((resp) => {
this.current = resp;
// TODO: This should be in a custom pipe
let c, d, h;
for(let i = 0; i < this.current.hourly.data.length; i++) {
c = this.current.hourly.data[i];
let t = c.temperature;
d = new Date(c.time * 1000);
c.temperature = Math.floor(t);
h = d.getHours() % 12;
h = (h == 0 ? 12 : h);
c.time_date = h + ' ' + (d.getHours() < 12 ? 'AM' : 'PM');
}
for(let i = 0; i < this.current.daily.data.length; i++) {
c = this.current.daily.data[i];
let max = c.temperatureMax;
let min = c.temperatureMin;
c.temperatureMax = Math.floor(max);
c.temperatureMin = Math.floor(min);
c.time_date = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][new Date(c.time*1000).getDay()];
}
console.log('GOT CURRENT', this.current);
}, (error) => {
alert('Unable to get current conditions');
console.error(error);
});
}
cycleBgImages() {
setTimeout(() => {
if(this.bgImages) {
this.activeBgImage = this.bgImages[this.activeBgImageIndex++ % this.bgImages.length];
}
});
}
refreshData() {
Geo.getLocation().then((position) => {
let lat = position.coords.latitude;
let lng = position.coords.longitude;
Geo.reverseGeocode(lat, lng).then((locString) => {
this.currentLocationString = locString;
this.getBackgroundImage(lat, lng, locString);
});
this.getCurrent(lat, lng);
}, (error) => {
alert('Unable to get current location: ' + error);
});
}
}

View File

@@ -1,68 +0,0 @@
<link rel="stylesheet" href="style.css">
<ion-view>
<background-cycler class="bg-fade" [image]="activeBgImage"></background-cycler>
<ion-toolbar id="header"><ion-title><span class="city"><i id="city-nav-icon" class="icon ion-navigate"></i> {{currentLocationString}}</span><br><current-time localtz="current.local_tz_short"></current-time></ion-title><button (click)="showSettings()" class="toolbar-secondary-item"><i class="icon ion-ios-gear"></i></button></ion-toolbar>
<ion-content>
<div id="main-content">
<current-weather [current]="current"></current-weather>
<div id="forecast" class="weather-box">
<h4 class="title">Forecast</h4>
<ion-scroll scroll-x="true" id="forecast-scroll">
<div id="hourly-forecast" *ng-if="current">
<div class="hourly-hour" *ng-for="#hour of current.hourly.data">
<div class="time" [inner-html]="hour.time_date"></div><!-- * 1000 | date:'h a'"></div>-->
<weather-icon [icon]="hour.icon"></weather-icon>
<div class="temp">{{hour.temperature}}&deg;</div>
</div>
</div>
</ion-scroll>
<div *ng-if="current">
<div class="row" *ng-for="#day of current.daily.data">
<div class="col"><span [inner-html]="day.time_date"></span></div>
<div class="col"><weather-icon [icon]="day.icon"></weather-icon></div>
<div class="col">
<span class="temp-high">{{day.temperatureMax}}&deg;</span>
<span class="temp-low">{{day.temperatureMin}}&deg;</span>
</div>
</div>
</div>
<div class="clearfix">
<div class="credit">
Powered by <a href="http://forecast.io" target="_blank">forecast.io</a>
</div>
</div>
</div>
</div>
</ion-content>
</ion-view>
<style>
.row {
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -moz-flex;
display: -ms-flexbox;
display: flex;
padding: 5px;
width: 100%; }
.row + .row {
margin-top: -5px;
padding-top: 0; }
.col {
-webkit-box-flex: 1;
-webkit-flex: 1;
-moz-box-flex: 1;
-moz-flex: 1;
-ms-flex: 1;
flex: 1;
display: block;
padding: 5px;
width: 100%; }
</style>

View File

@@ -1,251 +0,0 @@
body {
text-shadow: 1px 1px 0px rgba(0,0,0,0.5);
background-color: black;
}
#wrapper {
background-color: black;
}
#scroller {
}
background-cycler {
display: block;
position: fixed;
width: 100%;
height: 100%;
}
.bg-image {
width: 120%;
height: 120%;
background-size: cover;
}
.bg-fade > .ng-enter {
opacity: 0;
-webkit-transition: opacity 2s ease-in-out;
}
.bg-fade > .ng-enter-active {
opacity: 1;
}
.bg-fade > .ng-leave {
opacity: 1;
-webkit-transition: opacity 2s ease-in-out;
}
.bg-fade > .ng-leave-active {
opacity: 0;
}
.wunderground-logo {
width: 100px;
height: 14px;
background: url('../img/wunderground.png') no-repeat transparent;
}
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
.wunderground-logo {
background: url('../img/wunderground@2x.png') no-repeat transparent;
background-size: 100px 14px;
}
}
h1,h2,h3,h4,h5 {
color: #fff;
font-weight: 300;
}
#main-content {
color: #fff;
padding: 10px;
}
.ion-ios-sunny-outline {
color: yellow;
}
.ionic-refresher-content {
color: rgb(255,255,255) !important;
text-shadow: none;
}
/**
* Header
*/
#header {
background-color: transparent !important;
height: 44px;
}
#header > * {
margin-top: 0;
}
#header .title {
font-size: 12px;
line-height: 20px;
margin-top: 4px;
}
#header .title .city {
font-size: 16px;
}
#city-nav-icon {
font-size: 11px;
margin-right: 2px;
}
.scroll-refresher {
overflow: visible;
}
.ionic-refresher-content {
background-color: rgba(0,0,0,0.4);
height: 400px;
bottom: 0;
}
.ionic-refresher-content i {
margin-top: 360px;
}
/**
* Current weather
*/
current-weather {
display: block;
height: 180px;
box-sizing: content-box;
}
current-weather > * {
color: #fff;
}
current-weather .current-temp {
font-size: 100px;
font-weight: 100;
margin: 0;
padding: 0;
line-height: 80px;
}
#current-icon {
font-size: 42px;
vertical-align: middle;
margin-right: 5px;
}
#temp-hi, #temp-lo { display: inline-block; }
#temp-lo {
margin-left: 10px;
}
/**
* Forecast and details
*/
.weather-box {
background-color: rgba(0,0,0,0.2);
padding: 9px;
margin: 10px 0px;
color: rgba(255,255,255,0.9);
}
.weather-box .title {
color: rgba(255,255,255,0.9);
font-weight: normal;
margin: 0;
padding-bottom: 5px;
border-bottom: 1px solid #fff;
}
.weather-box .row {
border-bottom: 1px dotted rgba(255,255,255,0.3);
margin: 0;
padding: 0;
}
.weather-box .col {
text-align: center;
}
.weather-box .col:first-child {
text-align: left;
}
.weather-box .col:last-child {
text-align: right;
}
.weather-box span {
vertical-align: middle;
}
.weather-box .icon {
font-size: 42px;
line-height: 24px;
vertical-align: middle;
}
.temp-high {
display: inline-block;
width: 30px;
text-align: right;
}
.temp-low {
display: inline-block;
width: 30px;
text-align: right;
color: #4a87ee;
}
.no-header {
top: 0 !important;
}
#forecast-scroll {
margin: 10px 0px;
height: 85px;
}
#hourly-forecast {
width: 2210px;
height: 70px;
}
.hourly-hour {
display: inline-block;
margin-right: 10px;
text-align: center;
}
#forecast .credit {
float: right;
margin-top: 4px;
margin-right: 4px;
}
#forecast .credit a {
color: #fff;
}
/**
* Settings modal
*/
#settings-modal {
background-color: rgba(0,0,0,0.8) !important;
}
#settings-modal ion-toolbar {
background-color: rgba(0,0,0,0.8);
color: #fff;
}
#settings-modal .item {
background-color: rgba(0,0,0,0.9);
border: none;
color: #fff;
}
#settings-modal ion-label {
color: #fff;
}
ion-view {
background-color: transparent !important;
}
ion-content {
background-color: transparent !important;
}
#header {
background: transparent;
color: #fff;
}

View File

@@ -1,13 +0,0 @@
let WUNDERGROUND_API_KEY = '1cc2d3de40fa5af0';
let FORECASTIO_KEY = '4cd3c5673825a361eb5ce108103ee84a';
export class Weather {
static getAtLocation(lat, lng) {
// TODO: update to angular2 HTTP service
let url = 'https://api.forecast.io/forecast/' + FORECASTIO_KEY + '/';
return Http.get(url + lat + ',' + lng + '?callback=JSON_CALLBACK', {
method: 'jsonp'
});
}
}

View File

@@ -1,57 +0,0 @@
import {App, IonicView, NavController} from 'ionic/ionic';
@App({
templateUrl: 'main.html'
})
class IonicApp {
constructor() {
this.root = TabsPage;
}
}
@IonicView({
template: '' +
'<ion-navbar *navbar>' +
'<ion-title>Home</ion-title>' +
'</ion-navbar>' +
'<ion-content padding>' +
'home' +
'</ion-content>'
})
class HomeTabPage {
constructor(nav: NavController) {
this.nav = nav;
}
push() {
}
}
@IonicView({
template: '' +
'<ion-navbar *navbar>' +
'<ion-title>Peek</ion-title>' +
'</ion-navbar>' +
'<ion-content padding>' +
'peek' +
'</ion-content>'
})
class PeekTabPage {
constructor(nav: NavController) {
this.nav = nav;
}
push() {
}
}
@IonicView({
templateUrl: 'tabs.html'
})
class TabsPage {
constructor() {
this.homeTab = HomeTabPage;
this.peekTab = PeekTabPage;
}
}

View File

@@ -1,30 +0,0 @@
<ion-nav [root]="root"></ion-nav>
<style>
.md ion-navbar-section {
background-color: #2FD9BB !important;
}
.md .navbar-title {
color: #FDFEFE;
}
#tabs tabbar:before {
position: absolute;
top: 0;
right: auto;
bottom: auto;
left: 0;
z-index: $z-index;
display: block;
width: 100%;
height: 1px;
transform-origin: 50% 0%;
background-color: #00ECC4;
content: '';
}
#tabs .tab-button {
color: #C7C7C7;
}
#tabs .tab-button[aria-selected=true] {
color: #51E3C2;
}
</style>

View File

@@ -1,7 +0,0 @@
<ion-tabs id="tabs">
<ion-tab tab-title="Home" tab-icon="globe" [root]="homeTab"></ion-tab>
<ion-tab tab-title="Peek" tab-icon="glasses" [root]="peekTab"></ion-tab>
<ion-tab tab-title="Me" tab-icon="person"></ion-tab>
<ion-tab tab-title="More" tab-icon="more"></ion-tab>
</ion-tabs>

View File

@@ -73,15 +73,16 @@ export class Content extends Ion {
}
onScrollEnd(callback) {
let timerId;
let timerId, deregister;
function debounce() {
timerId = setTimeout(() => {
deregister();
callback();
}, 250);
}
this.addScrollEventListener(debounce);
deregister = this.addScrollEventListener(debounce);
}
/**

View File

@@ -9,7 +9,7 @@ export class ItemSlidingGesture extends DragGesture {
constructor(list: List, listEle) {
super(listEle, {
direction: 'x',
threshold: 40
threshold: DRAG_THRESHOLD
});
this.data = {};
@@ -24,7 +24,7 @@ export class ItemSlidingGesture extends DragGesture {
if (!isFromOptionButtons(ev.target)) {
let didClose = this.closeOpened();
if (didClose) {
ev.preventDefault();
preventDefault(ev);
}
}
});
@@ -46,7 +46,7 @@ export class ItemSlidingGesture extends DragGesture {
if (this.preventDrag) {
this.closeOpened(ev);
return ev.preventDefault();
return preventDefault(ev);
}
itemContainerEle.classList.add('active-slide');
@@ -60,6 +60,11 @@ export class ItemSlidingGesture extends DragGesture {
}
onDrag(ev) {
if (Math.abs(ev.deltaY) > 30) {
this.preventDrag = true;
return this.closeOpened(ev);
}
let itemContainerEle = getItemConatiner(ev.target);
if (!itemContainerEle || !isActive(itemContainerEle) || this.preventDrag) return;
@@ -142,7 +147,7 @@ export class ItemSlidingGesture extends DragGesture {
clearTimeout(this.get(itemContainerEle).timerId);
if (openAmount > 0) {
if (openAmount) {
this.openItems++;
} else {
@@ -157,7 +162,11 @@ export class ItemSlidingGesture extends DragGesture {
}
slidingEle.style[CSS.transition] = (isFinal ? '' : 'none');
slidingEle.style[CSS.transform] = (openAmount === 0 ? '' : 'translate3d(' + -openAmount + 'px,0,0)');
slidingEle.style[CSS.transform] = (openAmount ? 'translate3d(' + -openAmount + 'px,0,0)' : '');
if (isFinal) {
this.enableScroll(!openAmount);
}
}
getOpenAmount(itemContainerEle) {
@@ -175,12 +184,23 @@ export class ItemSlidingGesture extends DragGesture {
this.data[itemContainerEle.$ionSlide][key] = value;
}
enableScroll(shouldEnable) {
let scrollContentEle = closest(this.listEle, 'scroll-content');
if (scrollContentEle) {
scrollContentEle[shouldEnable ? 'removeEventListener' : 'addEventListener']('touchstart', preventDefault);
}
}
unlisten() {
super.unlisten();
this.listEle = null;
}
}
function preventDefault(ev) {
ev.preventDefault();
}
function getItemConatiner(ele) {
return closest(ele, 'ion-item-sliding', true);
}
@@ -199,3 +219,6 @@ function getOptionsWidth(itemContainerEle) {
function isActive(itemContainerEle) {
return itemContainerEle.classList.contains('active-slide');
}
const DRAG_THRESHOLD = 20;

View File

@@ -16,7 +16,11 @@ class E2EApp {
}
getItems() {
return [0,1];
let items = [];
for (let x = 0; x < 20; x++) {
items.push(x);
}
return items;
}
didClick(ev, item) {
@@ -32,4 +36,8 @@ class E2EApp {
console.log('Delete', ev, item);
item.close();
}
reload() {
window.location.reload();
}
}

View File

@@ -1,4 +1,9 @@
<ion-toolbar><ion-title>Sliding Items</ion-title></ion-toolbar>
<ion-toolbar>
<ion-title>Sliding Items</ion-title>
<ion-nav-items secondary>
<button (click)="reload()">Reload</button>
</ion-nav-items>
</ion-toolbar>
<ion-content>

View File

@@ -29,6 +29,8 @@ export class DragGesture extends Gesture {
this.onDragEnd(ev);
this.dragging = false;
});
this.hammertime.get('pan').set(this._options);
}
onDrag() {}