Skip to content

Commit db9fa7e

Browse files
manucorporatadamdbradley
authored andcommitted
fix(item): improve open/close logic, update demos
1 parent 0660cb6 commit db9fa7e

File tree

6 files changed

+162
-130
lines changed

6 files changed

+162
-130
lines changed

demos/item-sliding/index.ts

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,95 @@
11
import {Component} from '@angular/core';
2-
import {ionicBootstrap} from 'ionic-angular';
2+
import {ionicBootstrap, ItemSliding, Toast, NavController} from 'ionic-angular';
33

44

55
@Component({
66
templateUrl: 'main.html'
77
})
8-
class ApiDemoApp {}
8+
class InitialPage {
9+
chats: any[];
10+
logins: any[];
11+
12+
constructor(private nav: NavController) {
13+
this.chats = [
14+
{
15+
img: './avatar-cher.png',
16+
name: 'Cher',
17+
message: 'Ugh. As if.',
18+
time: '9:38 pm'
19+
}, {
20+
img: './avatar-dionne.png',
21+
name: 'Dionne',
22+
message: 'Mr. Hall was way harsh.',
23+
time: '8:59 pm'
24+
}, {
25+
img: './avatar-murray.png',
26+
name: 'Murray',
27+
message: 'Excuse me, "Ms. Dione."',
28+
time: 'Wed'
29+
}];
30+
31+
this.logins = [
32+
{
33+
icon: 'logo-twitter',
34+
name: 'Twitter',
35+
username: 'admin',
36+
}, {
37+
icon: 'logo-github',
38+
name: 'GitHub',
39+
username: 'admin37',
40+
}, {
41+
icon: 'logo-instagram',
42+
name: 'Instagram',
43+
username: 'imanadmin',
44+
}, {
45+
icon: 'logo-codepen',
46+
name: 'Codepen',
47+
username: 'administrator',
48+
}];
49+
}
50+
51+
more(item: ItemSliding) {
52+
console.log('More');
53+
item.close();
54+
}
55+
56+
delete(item: ItemSliding) {
57+
console.log('Delete');
58+
item.close();
59+
}
60+
61+
mute(item: ItemSliding) {
62+
console.log('Mute');
63+
item.close();
64+
}
65+
66+
archive(item: ItemSliding) {
67+
console.log('Archive');
68+
item.close();
69+
}
70+
71+
download(item: ItemSliding) {
72+
item.setClass('downloading', true);
73+
setTimeout(() => {
74+
const toast = Toast.create({
75+
message: 'Item was downloaded!'
76+
});
77+
this.nav.present(toast);
78+
item.setClass('downloading', false);
79+
item.close();
80+
81+
// Wait 2s to close toast
82+
setTimeout(() => toast.dismiss(), 2000);
83+
}, 1500);
84+
}
85+
}
86+
87+
88+
@Component({
89+
template: '<ion-nav [root]="root"></ion-nav>'
90+
})
91+
class ApiDemoApp {
92+
root = InitialPage;
93+
}
994

1095
ionicBootstrap(ApiDemoApp);

demos/item-sliding/main.html

Lines changed: 38 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -8,83 +8,37 @@
88
Chats
99
</ion-list-header>
1010

11-
<ion-item-sliding>
11+
<ion-item-sliding *ngFor="let chat of chats; let ref = index" [ref]="ref" #item>
1212
<ion-item>
1313
<ion-avatar item-left>
14-
<img src="./avatar-cher.png">
14+
<img [src]="chat.img">
1515
</ion-avatar>
16-
<h2>Cher</h2>
17-
<p>Ugh. As if.</p>
16+
<h2>{{chat.name}}</h2>
17+
<p>{{chat.message}}</p>
1818
<ion-note item-right>
19-
9:38 pm
19+
{{chat.time}}
2020
</ion-note>
2121
</ion-item>
22-
<ion-item-options>
23-
<button secondary>
24-
<ion-icon name="menu"></ion-icon>
25-
More
26-
</button>
27-
<button dark>
28-
<ion-icon name="volume-off"></ion-icon>
29-
Mute
30-
</button>
31-
<button danger>
32-
<ion-icon name="trash"></ion-icon>
33-
Delete
34-
</button>
35-
</ion-item-options>
36-
</ion-item-sliding>
3722

38-
<ion-item-sliding>
39-
<ion-item>
40-
<ion-avatar item-left>
41-
<img src="./avatar-dionne.png">
42-
</ion-avatar>
43-
<h2>Dionne</h2>
44-
<p>Mr. Hall was way harsh.</p>
45-
<ion-note item-right>
46-
8:59 pm
47-
</ion-note>
48-
</ion-item>
4923
<ion-item-options>
50-
<button secondary>
24+
<button secondary (click)="more(item)">
5125
<ion-icon name="menu"></ion-icon>
5226
More
5327
</button>
54-
<button dark>
28+
<button dark (click)="mute(item)">
5529
<ion-icon name="volume-off"></ion-icon>
5630
Mute
5731
</button>
58-
<button danger>
32+
<button danger (click)="delete(item)">
5933
<ion-icon name="trash"></ion-icon>
6034
Delete
6135
</button>
6236
</ion-item-options>
63-
</ion-item-sliding>
6437

65-
<ion-item-sliding>
66-
<ion-item>
67-
<ion-avatar item-left>
68-
<img src="./avatar-murray.png">
69-
</ion-avatar>
70-
<h2>Murray</h2>
71-
<p>Excuse me, "Ms. Dione."</p>
72-
<ion-note item-right>
73-
Wed
74-
</ion-note>
75-
</ion-item>
76-
<ion-item-options>
77-
<button secondary>
78-
<ion-icon name="menu"></ion-icon>
79-
More
80-
</button>
81-
<button dark>
82-
<ion-icon name="volume-off"></ion-icon>
83-
Mute
84-
</button>
85-
<button danger>
86-
<ion-icon name="trash"></ion-icon>
87-
Delete
38+
<ion-item-options side="left">
39+
<button primary expandable (click)="archive(item)">
40+
<ion-icon name="archive"></ion-icon>
41+
Archive
8842
</button>
8943
</ion-item-options>
9044
</ion-item-sliding>
@@ -95,66 +49,26 @@ <h2>Murray</h2>
9549
Logins
9650
</ion-list-header>
9751

98-
<ion-item-sliding>
52+
<ion-item-sliding *ngFor="let login of logins" #item>
9953
<ion-item>
100-
<ion-icon name="logo-twitter" item-left></ion-icon>
101-
<h2>Twitter</h2>
102-
<p>admin</p>
54+
<ion-icon [name]="login.icon" item-left></ion-icon>
55+
<h2>{{login.name}}</h2>
56+
<p>{{login.username}}</p>
10357
</ion-item>
104-
<ion-item-options>
105-
<button>
106-
edit
107-
</button>
108-
<button danger>
109-
<ion-icon name="trash"></ion-icon>
110-
</button>
111-
</ion-item-options>
112-
</ion-item-sliding>
113-
114-
<ion-item-sliding>
115-
<ion-item>
116-
<ion-icon name="logo-github" item-left></ion-icon>
117-
<h2>GitHub</h2>
118-
<p>admin37</p>
119-
</ion-item>
120-
<ion-item-options>
121-
<button>
122-
edit
123-
</button>
124-
<button danger>
125-
<ion-icon name="trash"></ion-icon>
126-
</button>
127-
</ion-item-options>
128-
</ion-item-sliding>
129-
130-
<ion-item-sliding>
131-
<ion-item>
132-
<ion-icon name="logo-instagram" item-left></ion-icon>
133-
<h2>Instagram</h2>
134-
<p>imanadmin</p>
135-
</ion-item>
136-
<ion-item-options>
137-
<button>
138-
edit
139-
</button>
58+
<ion-item-options side="left">
14059
<button danger>
14160
<ion-icon name="trash"></ion-icon>
14261
</button>
14362
</ion-item-options>
144-
</ion-item-sliding>
145-
146-
<ion-item-sliding>
147-
<ion-item>
148-
<ion-icon name="logo-codepen" item-left dark></ion-icon>
149-
<h2>Codepen</h2>
150-
<p>administrator</p>
151-
</ion-item>
152-
<ion-item-options>
153-
<button>
154-
edit
63+
<ion-item-options (ionSwipe)="download(item)">
64+
<button dark (click)="more(item)">
65+
<ion-icon name="volume-off"></ion-icon>
66+
Mute
15567
</button>
156-
<button danger>
157-
<ion-icon name="trash"></ion-icon>
68+
<button light expandable (click)="download(item)">
69+
<ion-icon name="download" class="download-hide"></ion-icon>
70+
<div class="download-hide">Download</div>
71+
<ion-spinner id="download-spinner"></ion-spinner>
15872
</button>
15973
</ion-item-options>
16074
</ion-item-sliding>
@@ -164,6 +78,19 @@ <h2>Codepen</h2>
16478
</ion-content>
16579

16680
<style>
81+
#download-spinner {
82+
display: none;
83+
}
84+
85+
svg circle {
86+
stroke: white;
87+
}
88+
.downloading #download-spinner {
89+
display: block;
90+
}
91+
.downloading .download-hide {
92+
display: none;
93+
}
16794
.chat-sliding-demo ion-note {
16895
font-size: 13px;
16996
margin-top: -8px;

src/components/item/item-sliding.scss

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,10 @@ ion-item-sliding.active-slide {
8787
}
8888
}
8989

90-
// Item Swipeable Animation
90+
// Item Expandable Animation
9191
// --------------------------------------------------
9292

93-
button[swipeable] {
93+
.button-expandable {
9494
flex-shrink: 0;
9595

9696
transition-duration: 0;
@@ -105,7 +105,7 @@ ion-item-sliding.active-swipe-left {
105105
}
106106
}
107107

108-
ion-item-sliding.active-swipe-right button[swipeable] {
108+
ion-item-sliding.active-swipe-right .button-expandable {
109109
order: 1;
110110

111111
padding-left: 90%;
@@ -114,7 +114,7 @@ ion-item-sliding.active-swipe-right button[swipeable] {
114114
transition-property: padding-left;
115115
}
116116

117-
ion-item-sliding.active-swipe-left button[swipeable] {
117+
ion-item-sliding.active-swipe-left .button-expandable {
118118
order: -1;
119119

120120
padding-right: 90%;

src/components/item/item-sliding.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,10 +269,10 @@ export class ItemSliding {
269269

270270
// Check if the drag didn't clear the buttons mid-point
271271
// and we aren't moving fast enough to swipe open
272-
let isOnResetZone = Math.abs(this._openAmount) < Math.abs(restingPoint / 2);
273-
let isMovingSlow = Math.abs(velocity) < 0.3;
274-
let isDirection = (this._openAmount > 0) === (velocity > 0);
275-
if (isOnResetZone && (isMovingSlow || isDirection)) {
272+
let isCloseDirection = (this._openAmount > 0) === !(velocity < 0);
273+
let isMovingFast = Math.abs(velocity) > 0.3;
274+
let isOnCloseZone = Math.abs(this._openAmount) < Math.abs(restingPoint / 2);
275+
if (shouldClose(isCloseDirection, isMovingFast, isOnCloseZone)) {
276276
restingPoint = 0;
277277
}
278278

@@ -402,3 +402,23 @@ export class ItemSliding {
402402
}
403403

404404
}
405+
406+
function shouldClose(isCloseDirection: boolean, isMovingFast: boolean, isOnCloseZone: boolean): boolean {
407+
// The logic required to know when the sliding item should close (openAmount=0)
408+
// depends on three booleans (isCloseDirection, isMovingFast, isOnCloseZone)
409+
// and it ended up being too complicated to be written manually without errors
410+
// so the truth table is attached below: (0=false, 1=true)
411+
// isCloseDirection | isMovingFast | isOnCloseZone || shouldClose
412+
// 0 | 0 | 0 || 0
413+
// 0 | 0 | 1 || 1
414+
// 0 | 1 | 0 || 0
415+
// 0 | 1 | 1 || 0
416+
// 1 | 0 | 0 || 0
417+
// 1 | 0 | 1 || 1
418+
// 1 | 1 | 0 || 1
419+
// 1 | 1 | 1 || 1
420+
// The resulting expression was generated by resolving the K-map (Karnaugh map):
421+
let shouldClose = (!isMovingFast && isOnCloseZone) || (isCloseDirection && isMovingFast);
422+
return shouldClose;
423+
}
424+

0 commit comments

Comments
 (0)