fix(item): improve open/close logic, update demos

This commit is contained in:
Manu Mtz.-Almeida
2016-06-12 03:07:08 +02:00
committed by Adam Bradley
parent 0660cb6b4a
commit db9fa7ead3
6 changed files with 163 additions and 131 deletions

View File

@ -1,10 +1,95 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {ionicBootstrap} from 'ionic-angular'; import {ionicBootstrap, ItemSliding, Toast, NavController} from 'ionic-angular';
@Component({ @Component({
templateUrl: 'main.html' templateUrl: 'main.html'
}) })
class ApiDemoApp {} class InitialPage {
chats: any[];
logins: any[];
constructor(private nav: NavController) {
this.chats = [
{
img: './avatar-cher.png',
name: 'Cher',
message: 'Ugh. As if.',
time: '9:38 pm'
}, {
img: './avatar-dionne.png',
name: 'Dionne',
message: 'Mr. Hall was way harsh.',
time: '8:59 pm'
}, {
img: './avatar-murray.png',
name: 'Murray',
message: 'Excuse me, "Ms. Dione."',
time: 'Wed'
}];
this.logins = [
{
icon: 'logo-twitter',
name: 'Twitter',
username: 'admin',
}, {
icon: 'logo-github',
name: 'GitHub',
username: 'admin37',
}, {
icon: 'logo-instagram',
name: 'Instagram',
username: 'imanadmin',
}, {
icon: 'logo-codepen',
name: 'Codepen',
username: 'administrator',
}];
}
more(item: ItemSliding) {
console.log('More');
item.close();
}
delete(item: ItemSliding) {
console.log('Delete');
item.close();
}
mute(item: ItemSliding) {
console.log('Mute');
item.close();
}
archive(item: ItemSliding) {
console.log('Archive');
item.close();
}
download(item: ItemSliding) {
item.setClass('downloading', true);
setTimeout(() => {
const toast = Toast.create({
message: 'Item was downloaded!'
});
this.nav.present(toast);
item.setClass('downloading', false);
item.close();
// Wait 2s to close toast
setTimeout(() => toast.dismiss(), 2000);
}, 1500);
}
}
@Component({
template: '<ion-nav [root]="root"></ion-nav>'
})
class ApiDemoApp {
root = InitialPage;
}
ionicBootstrap(ApiDemoApp); ionicBootstrap(ApiDemoApp);

View File

@ -8,83 +8,37 @@
Chats Chats
</ion-list-header> </ion-list-header>
<ion-item-sliding> <ion-item-sliding *ngFor="let chat of chats; let ref = index" [ref]="ref" #item>
<ion-item> <ion-item>
<ion-avatar item-left> <ion-avatar item-left>
<img src="./avatar-cher.png"> <img [src]="chat.img">
</ion-avatar> </ion-avatar>
<h2>Cher</h2> <h2>{{chat.name}}</h2>
<p>Ugh. As if.</p> <p>{{chat.message}}</p>
<ion-note item-right> <ion-note item-right>
9:38 pm {{chat.time}}
</ion-note> </ion-note>
</ion-item> </ion-item>
<ion-item-options> <ion-item-options>
<button secondary> <button secondary (click)="more(item)">
<ion-icon name="menu"></ion-icon> <ion-icon name="menu"></ion-icon>
More More
</button> </button>
<button dark> <button dark (click)="mute(item)">
<ion-icon name="volume-off"></ion-icon> <ion-icon name="volume-off"></ion-icon>
Mute Mute
</button> </button>
<button danger> <button danger (click)="delete(item)">
<ion-icon name="trash"></ion-icon> <ion-icon name="trash"></ion-icon>
Delete Delete
</button> </button>
</ion-item-options> </ion-item-options>
</ion-item-sliding>
<ion-item-sliding> <ion-item-options side="left">
<ion-item> <button primary expandable (click)="archive(item)">
<ion-avatar item-left> <ion-icon name="archive"></ion-icon>
<img src="./avatar-dionne.png"> Archive
</ion-avatar>
<h2>Dionne</h2>
<p>Mr. Hall was way harsh.</p>
<ion-note item-right>
8:59 pm
</ion-note>
</ion-item>
<ion-item-options>
<button secondary>
<ion-icon name="menu"></ion-icon>
More
</button>
<button dark>
<ion-icon name="volume-off"></ion-icon>
Mute
</button>
<button danger>
<ion-icon name="trash"></ion-icon>
Delete
</button>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding>
<ion-item>
<ion-avatar item-left>
<img src="./avatar-murray.png">
</ion-avatar>
<h2>Murray</h2>
<p>Excuse me, "Ms. Dione."</p>
<ion-note item-right>
Wed
</ion-note>
</ion-item>
<ion-item-options>
<button secondary>
<ion-icon name="menu"></ion-icon>
More
</button>
<button dark>
<ion-icon name="volume-off"></ion-icon>
Mute
</button>
<button danger>
<ion-icon name="trash"></ion-icon>
Delete
</button> </button>
</ion-item-options> </ion-item-options>
</ion-item-sliding> </ion-item-sliding>
@ -95,66 +49,26 @@
Logins Logins
</ion-list-header> </ion-list-header>
<ion-item-sliding> <ion-item-sliding *ngFor="let login of logins" #item>
<ion-item> <ion-item>
<ion-icon name="logo-twitter" item-left></ion-icon> <ion-icon [name]="login.icon" item-left></ion-icon>
<h2>Twitter</h2> <h2>{{login.name}}</h2>
<p>admin</p> <p>{{login.username}}</p>
</ion-item> </ion-item>
<ion-item-options> <ion-item-options side="left">
<button>
edit
</button>
<button danger> <button danger>
<ion-icon name="trash"></ion-icon> <ion-icon name="trash"></ion-icon>
</button> </button>
</ion-item-options> </ion-item-options>
</ion-item-sliding> <ion-item-options (ionSwipe)="download(item)">
<button dark (click)="more(item)">
<ion-item-sliding> <ion-icon name="volume-off"></ion-icon>
<ion-item> Mute
<ion-icon name="logo-github" item-left></ion-icon>
<h2>GitHub</h2>
<p>admin37</p>
</ion-item>
<ion-item-options>
<button>
edit
</button> </button>
<button danger> <button light expandable (click)="download(item)">
<ion-icon name="trash"></ion-icon> <ion-icon name="download" class="download-hide"></ion-icon>
</button> <div class="download-hide">Download</div>
</ion-item-options> <ion-spinner id="download-spinner"></ion-spinner>
</ion-item-sliding>
<ion-item-sliding>
<ion-item>
<ion-icon name="logo-instagram" item-left></ion-icon>
<h2>Instagram</h2>
<p>imanadmin</p>
</ion-item>
<ion-item-options>
<button>
edit
</button>
<button danger>
<ion-icon name="trash"></ion-icon>
</button>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding>
<ion-item>
<ion-icon name="logo-codepen" item-left dark></ion-icon>
<h2>Codepen</h2>
<p>administrator</p>
</ion-item>
<ion-item-options>
<button>
edit
</button>
<button danger>
<ion-icon name="trash"></ion-icon>
</button> </button>
</ion-item-options> </ion-item-options>
</ion-item-sliding> </ion-item-sliding>
@ -164,6 +78,19 @@
</ion-content> </ion-content>
<style> <style>
#download-spinner {
display: none;
}
svg circle {
stroke: white;
}
.downloading #download-spinner {
display: block;
}
.downloading .download-hide {
display: none;
}
.chat-sliding-demo ion-note { .chat-sliding-demo ion-note {
font-size: 13px; font-size: 13px;
margin-top: -8px; margin-top: -8px;

View File

@ -87,10 +87,10 @@ ion-item-sliding.active-slide {
} }
} }
// Item Swipeable Animation // Item Expandable Animation
// -------------------------------------------------- // --------------------------------------------------
button[swipeable] { .button-expandable {
flex-shrink: 0; flex-shrink: 0;
transition-duration: 0; transition-duration: 0;
@ -105,7 +105,7 @@ ion-item-sliding.active-swipe-left {
} }
} }
ion-item-sliding.active-swipe-right button[swipeable] { ion-item-sliding.active-swipe-right .button-expandable {
order: 1; order: 1;
padding-left: 90%; padding-left: 90%;
@ -114,7 +114,7 @@ ion-item-sliding.active-swipe-right button[swipeable] {
transition-property: padding-left; transition-property: padding-left;
} }
ion-item-sliding.active-swipe-left button[swipeable] { ion-item-sliding.active-swipe-left .button-expandable {
order: -1; order: -1;
padding-right: 90%; padding-right: 90%;

View File

@ -269,10 +269,10 @@ export class ItemSliding {
// Check if the drag didn't clear the buttons mid-point // Check if the drag didn't clear the buttons mid-point
// and we aren't moving fast enough to swipe open // and we aren't moving fast enough to swipe open
let isOnResetZone = Math.abs(this._openAmount) < Math.abs(restingPoint / 2); let isCloseDirection = (this._openAmount > 0) === !(velocity < 0);
let isMovingSlow = Math.abs(velocity) < 0.3; let isMovingFast = Math.abs(velocity) > 0.3;
let isDirection = (this._openAmount > 0) === (velocity > 0); let isOnCloseZone = Math.abs(this._openAmount) < Math.abs(restingPoint / 2);
if (isOnResetZone && (isMovingSlow || isDirection)) { if (shouldClose(isCloseDirection, isMovingFast, isOnCloseZone)) {
restingPoint = 0; restingPoint = 0;
} }
@ -402,3 +402,23 @@ export class ItemSliding {
} }
} }
function shouldClose(isCloseDirection: boolean, isMovingFast: boolean, isOnCloseZone: boolean): boolean {
// The logic required to know when the sliding item should close (openAmount=0)
// depends on three booleans (isCloseDirection, isMovingFast, isOnCloseZone)
// and it ended up being too complicated to be written manually without errors
// so the truth table is attached below: (0=false, 1=true)
// isCloseDirection | isMovingFast | isOnCloseZone || shouldClose
// 0 | 0 | 0 || 0
// 0 | 0 | 1 || 1
// 0 | 1 | 0 || 0
// 0 | 1 | 1 || 0
// 1 | 0 | 0 || 0
// 1 | 0 | 1 || 1
// 1 | 1 | 0 || 1
// 1 | 1 | 1 || 1
// The resulting expression was generated by resolving the K-map (Karnaugh map):
let shouldClose = (!isMovingFast && isOnCloseZone) || (isCloseDirection && isMovingFast);
return shouldClose;
}

View File

@ -32,7 +32,7 @@
</ion-item> </ion-item>
<ion-item-options side="left" (ionSwipe)="del($event)" *ngIf="slidingEnabled"> <ion-item-options side="left" (ionSwipe)="del($event)" *ngIf="slidingEnabled">
<button primary (click)="archive(item1)">Archive</button> <button primary (click)="archive(item1)">Archive</button>
<button danger (click)="del(item1)" swipeable>Delete</button> <button danger (click)="del(item1)">Delete</button>
</ion-item-options> </ion-item-options>
</ion-item-sliding> </ion-item-sliding>
@ -43,7 +43,7 @@
<p>I think I figured out how to get more Mountain Dew</p> <p>I think I figured out how to get more Mountain Dew</p>
</ion-item> </ion-item>
<ion-item-options side="left" (ionSwipe)="unread($event)" *ngIf="slidingEnabled"> <ion-item-options side="left" (ionSwipe)="unread($event)" *ngIf="slidingEnabled">
<button secondary swipeable (click)="unread(item2)"> <button secondary expandable (click)="unread(item2)">
<ion-icon name="ios-checkmark"></ion-icon>Unread <ion-icon name="ios-checkmark"></ion-icon>Unread
</button> </button>
</ion-item-options> </ion-item-options>
@ -52,7 +52,7 @@
<button primary (click)="archive(item2)"> <button primary (click)="archive(item2)">
<ion-icon name="mail"></ion-icon>Archive <ion-icon name="mail"></ion-icon>Archive
</button> </button>
<button danger (click)="del(item2)" swipeable> <button danger (click)="del(item2)" expandable>
<ion-icon name="trash"></ion-icon>Delete <ion-icon name="trash"></ion-icon>Delete
</button> </button>
</ion-item-options> </ion-item-options>
@ -65,7 +65,7 @@
<p>I think I figured out how to get more Mountain Dew</p> <p>I think I figured out how to get more Mountain Dew</p>
</ion-item> </ion-item>
<ion-item-options side="left" icon-left (ionSwipe)="unread($event)" *ngIf="slidingEnabled"> <ion-item-options side="left" icon-left (ionSwipe)="unread($event)" *ngIf="slidingEnabled">
<button secondary swipeable (click)="unread(item3)"> <button secondary expandable (click)="unread(item3)">
<ion-icon name="ios-checkmark"></ion-icon>Unread <ion-icon name="ios-checkmark"></ion-icon>Unread
</button> </button>
</ion-item-options> </ion-item-options>
@ -74,7 +74,7 @@
<button primary (click)="archive(item3)"> <button primary (click)="archive(item3)">
<ion-icon name="mail"></ion-icon>Archive <ion-icon name="mail"></ion-icon>Archive
</button> </button>
<button danger (click)="del(item3)" swipeable *ngIf="slidingEnabled"> <button danger (click)="del(item3)" expandable *ngIf="slidingEnabled">
<ion-icon name="trash"></ion-icon>Delete <ion-icon name="trash"></ion-icon>Delete
</button> </button>
</ion-item-options> </ion-item-options>
@ -87,7 +87,7 @@
One Line w/ Icon, div only text One Line w/ Icon, div only text
</ion-item> </ion-item>
<ion-item-options icon-left (ionSwipe)="archive($event)"> <ion-item-options icon-left (ionSwipe)="archive($event)">
<button primary (click)="archive(item4)" swipeable *ngIf="slidingEnabled"> <button primary (click)="archive(item4)" expandable *ngIf="slidingEnabled">
<ion-icon name="archive"></ion-icon>Archive <ion-icon name="archive"></ion-icon>Archive
</button> </button>
</ion-item-options> </ion-item-options>
@ -102,7 +102,7 @@
One Line w/ Avatar, div only text One Line w/ Avatar, div only text
</ion-item> </ion-item>
<ion-item-options> <ion-item-options>
<button primary swipeable> <button primary expandable>
<ion-icon name="more"></ion-icon>More <ion-icon name="more"></ion-icon>More
</button> </button>
<button secondary (click)="archive(item5)"> <button secondary (click)="archive(item5)">
@ -158,7 +158,7 @@
<button primary (click)="archive(item8)"> <button primary (click)="archive(item8)">
<ion-icon name="archive"></ion-icon>Archive <ion-icon name="archive"></ion-icon>Archive
</button> </button>
<button secondary swipeable (click)="download(item8)"> <button secondary expandable (click)="download(item8)">
<ion-icon name="download" class="download-hide"></ion-icon> <ion-icon name="download" class="download-hide"></ion-icon>
<div class="download-hide">Download</div> <div class="download-hide">Download</div>
<ion-spinner id="download-spinner"></ion-spinner> <ion-spinner id="download-spinner"></ion-spinner>

View File

@ -366,7 +366,7 @@ export class Menu extends Ion {
/** /**
* @private * @private
*/ */
bdClick(ev) { bdClick(ev: Event) {
console.debug('backdrop clicked'); console.debug('backdrop clicked');
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();