feat(item): initial checkin of item sliding

This commit is contained in:
Brandy Carney
2017-07-17 11:00:07 -04:00
parent aeb8a1ebea
commit 3d5ed7e81f
6 changed files with 1077 additions and 3 deletions

View File

@ -0,0 +1,69 @@
import { Component, h, Ionic, Prop } from '@stencil/core';
import { isRightSide, Side } from '../../utils/util';
/**
* @name ItemOptions
* @description
* The option buttons for an `ion-item-sliding`. These buttons can be placed either on the left or right side.
* You can combine the `(ionSwipe)` event plus the `expandable` directive to create a full swipe action for the item.
*
* @usage
*
* ```html
* <ion-item-sliding>
* <ion-item>
* Item 1
* </ion-item>
* <ion-item-options side="right" (ionSwipe)="saveItem(item)">
* <ion-button expandable (click)="saveItem(item)">
* <ion-icon name="star"></ion-icon>
* </ion-button>
* </ion-item-options>
* </ion-item-sliding>
*```
*/
@Component({
tag: 'ion-item-options'
})
export class ItemOptions {
$el: HTMLElement;
/**
* @input {string} The side the option button should be on. Defaults to `"right"`.
* If you have multiple `ion-item-options`, a side must be provided for each.
*/
@Prop() side: Side = 'right';
/**
* @output {event} Emitted when the item has been fully swiped.
*/
// @Output() ionSwipe: EventEmitter<ItemSliding> = new EventEmitter<ItemSliding>();
/**
* @hidden
*/
isRightSide(): boolean {
const isRTL = document.dir === 'rtl';
return isRightSide(this.side, isRTL, true);
}
/**
* @output {event} Emitted when the item has been fully swiped.
*/
ionSwipe(itemSliding: any) {
Ionic.emit(itemSliding, 'ionSwipe');
}
/**
* @hidden
*/
width(): number {
return this.$el.offsetWidth;
}
render() {
return <slot></slot>;
}
}

View File

@ -0,0 +1,171 @@
@import "../../themes/ionic.globals";
// Item Sliding
// --------------------------------------------------
// The hidden right-side buttons that can be exposed under a list item with dragging.
ion-item-sliding {
position: relative;
display: block;
overflow: hidden;
width: 100%;
}
ion-item-sliding .item {
position: static;
}
ion-item-options {
position: absolute;
z-index: $z-index-item-options;
display: none;
height: 100%;
font-size: 14px;
visibility: hidden;
@include multi-dir() {
// scss-lint:disable PropertySpelling
top: 0;
right: 0;
}
@include ltr() {
justify-content: flex-end;
}
@include rtl() {
justify-content: flex-start;
&:not([side=right]) {
justify-content: flex-end;
// scss-lint:disable PropertySpelling
right: auto;
left: 0;
}
}
}
ion-item-options[side=left] {
@include multi-dir() {
// scss-lint:disable PropertySpelling
right: auto;
left: 0;
}
@include ltr() {
justify-content: flex-start;
}
@include rtl() {
justify-content: flex-end;
}
}
ion-item-options .button {
@include margin(0);
@include padding(0, .7em);
@include border-radius(0);
height: 100%;
box-shadow: none;
box-sizing: content-box;
}
ion-item-options:not([icon-left]) .button:not([icon-only]), // deprecated
ion-item-options:not([icon-start]) .button:not([icon-only]) {
.button-inner {
flex-direction: column;
}
ion-icon {
@include padding(null, 0, .3em, 0);
}
}
ion-item-sliding.active-slide {
@include rtl() {
&.active-options-left ion-item-options:not([side=right]) {
width: 100%;
visibility: visible;
}
}
.item,
.item.activated {
position: relative;
z-index: $z-index-item-options + 1;
opacity: 1;
transition: transform 500ms cubic-bezier(.36, .66, .04, 1);
pointer-events: none;
will-change: transform;
}
ion-item-options {
display: flex;
}
&.active-options-left ion-item-options[side=left],
&.active-options-right ion-item-options:not([side=left]) {
width: 100%;
visibility: visible;
}
}
// Item Expandable Animation
// --------------------------------------------------
button[expandable] {
flex-shrink: 0;
transition-duration: 0;
transition-property: none;
transition-timing-function: cubic-bezier(.65, .05, .36, 1);
}
ion-item-sliding.active-swipe-right button[expandable] {
transition-duration: .6s;
transition-property: padding-left;
@include multi-dir() {
// scss-lint:disable PropertySpelling
padding-left: 90%;
}
@include ltr() {
order: 1;
}
@include rtl() {
order: -1;
}
}
ion-item-sliding.active-swipe-left button[expandable] {
transition-duration: .6s;
transition-property: padding-right;
@include multi-dir() {
// scss-lint:disable PropertySpelling
padding-right: 90%;
}
@include ltr() {
order: -1;
}
@include rtl() {
order: 1;
}
}

View File

@ -0,0 +1,521 @@
import { Component, h, Ionic, State } from '@stencil/core';
import { GestureDetail, HostElement } from '../../utils/interfaces';
import { swipeShouldReset } from '../../utils/util';
// import { ItemOptions } from './item-options';
const SWIPE_MARGIN = 30;
const ELASTIC_FACTOR = 0.55;
const ITEM_SIDE_FLAG_NONE = 0;
const ITEM_SIDE_FLAG_LEFT = 1 << 0;
const ITEM_SIDE_FLAG_RIGHT = 1 << 1;
const ITEM_SIDE_FLAG_BOTH = ITEM_SIDE_FLAG_LEFT | ITEM_SIDE_FLAG_RIGHT;
const enum SlidingState {
Disabled = 1 << 1,
Enabled = 1 << 2,
Right = 1 << 3,
Left = 1 << 4,
SwipeRight = 1 << 5,
SwipeLeft = 1 << 6,
}
/**
* @name ItemSliding
* @description
* A sliding item is a list item that can be swiped to reveal buttons. It requires
* an [Item](../Item) component as a child and a [List](../../list/List) component as
* a parent. All buttons to reveal can be placed in the `<ion-item-options>` element.
*
* @usage
* ```html
* <ion-list>
* <ion-item-sliding #item>
* <ion-item>
* Item
* </ion-item>
* <ion-item-options side="left">
* <ion-button (click)="favorite(item)">Favorite</ion-button>
* <ion-button color="danger" (click)="share(item)">Share</ion-button>
* </ion-item-options>
*
* <ion-item-options side="right">
* <ion-button (click)="unread(item)">Unread</ion-button>
* </ion-item-options>
* </ion-item-sliding>
* </ion-list>
* ```
*
* ### Swipe Direction
* By default, the buttons are revealed when the sliding item is swiped from right to left,
* so the buttons are placed in the right side. But it's also possible to reveal them
* in the right side (sliding from left to right) by setting the `side` attribute
* on the `ion-item-options` element. Up to 2 `ion-item-options` can used at the same time
* in order to reveal two different sets of buttons depending the swipping direction.
*
* ```html
* <ion-item-options side="right">
* <ion-button (click)="archive(item)">
* <ion-icon name="archive"></ion-icon>
* Archive
* </ion-button>
* </ion-item-options>
*
* <ion-item-options side="left">
* <ion-button (click)="archive(item)">
* <ion-icon name="archive"></ion-icon>
* Archive
* </ion-button>
* </ion-item-options>
* ```
*
* ### Listening for events (ionDrag) and (ionSwipe)
* It's possible to know the current relative position of the sliding item by subscribing
* to the (ionDrag)` event.
*
* ```html
* <ion-item-sliding (ionDrag)="logDrag($event)">
* <ion-item>Item</ion-item>
* <ion-item-options>
* <ion-button>Favorite</ion-button>
* </ion-item-options>
* </ion-item-sliding>
* ```
*
* ### Button Layout
* If an icon is placed with text in the option button, by default it will
* display the icon on top of the text. This can be changed to display the icon
* to the left of the text by setting `icon-start` as an attribute on the
* `<ion-item-options>` element.
*
* ```html
* <ion-item-options icon-start>
* <ion-button (click)="archive(item)">
* <ion-icon name="archive"></ion-icon>
* Archive
* </ion-button>
* </ion-item-options>
*
* ```
*
* ### Expandable Options
*
* Options can be expanded to take up the full width of the item if you swipe past
* a certain point. This can be combined with the `ionSwipe` event to call methods
* on the class.
*
* ```html
*
* <ion-item-sliding (ionSwipe)="delete(item)">
* <ion-item>Item</ion-item>
* <ion-item-options>
* <ion-button expandable (click)="delete(item)">Delete</ion-button>
* </ion-item-options>
* </ion-item-sliding>
* ```
*
* We can call `delete` by either clicking the button, or by doing a full swipe on the item.
*
* @demo /docs/demos/src/item-sliding/
* @see {@link /docs/components#lists List Component Docs}
* @see {@link ../Item Item API Docs}
* @see {@link ../../list/List List API Docs}
*/
@Component({
tag: 'ion-item-sliding',
styleUrl: 'item-sliding.scss',
// TODO REMOVE
styleUrls: {
ios: 'item-sliding.scss',
md: 'item-sliding.scss',
wp: 'item-sliding.scss'
}
})
export class ItemSliding {
$el: HTMLElement;
item: HostElement;
openAmount: number = 0;
startX: number = 0;
optsWidthRightSide: number = 0;
optsWidthLeftSide: number = 0;
sides: number;
tmr: number = null;
// TODO file with item sliding interfaces & item options implement
// leftOptions: ItemOptions;
// rightOptions: ItemOptions;
leftOptions: any;
rightOptions: any;
optsDirty: boolean = true;
@State() state: SlidingState = SlidingState.Disabled;
preSelectedContainer: ItemSliding = null;
selectedContainer: ItemSliding = null;
openContainer: ItemSliding = null;
firstCoordX: number;
firstTimestamp: number;
/**
* @output {event} Emitted when the sliding position changes.
* It reports the relative position.
*
* ```ts
* onDrag(slidingItem) {
* let percent = slidingItem.getSlidingPercent();
* if (percent > 0) {
* // positive
* console.log('right side');
* } else {
* // negative
* console.log('left side');
* }
* if (Math.abs(percent) > 1) {
* console.log('overscroll');
* }
* }
* ```
*
*/
ionDrag() {
Ionic.emit(this, 'ionDrag');
}
ionViewDidLoad() {
const options = this.$el.querySelectorAll('ion-item-options') as NodeListOf<HostElement>;
let sides = 0;
// Reset left and right options in case they were removed
this.leftOptions = this.rightOptions = null;
for (var i = 0; i < options.length; i++) {
let option = options[i].$instance;
if (option.isRightSide()) {
this.rightOptions = option;
sides |= ITEM_SIDE_FLAG_RIGHT;
} else {
this.leftOptions = option;
sides |= ITEM_SIDE_FLAG_LEFT;
}
}
this.optsDirty = true;
this.sides = sides;
this.item = this.$el.querySelector('ion-item') as HostElement;
}
canStart(gesture: GestureDetail): boolean {
if (this.selectedContainer) {
return false;
}
// Get swiped sliding container
let container = this;
// Close open container if it is not the selected one.
if (container !== this.openContainer) {
this.closeOpened();
}
this.preSelectedContainer = container;
this.firstCoordX = gesture.currentX;
this.firstTimestamp = Date.now();
return true;
}
onDragStart(gesture: GestureDetail) {
this.selectedContainer = this.openContainer = this.preSelectedContainer;
this.selectedContainer.startSliding(gesture.currentX);
}
onDragMove(gesture: GestureDetail) {
this.selectedContainer && this.selectedContainer.moveSliding(gesture.currentX);
}
onDragEnd(gesture: GestureDetail) {
this.selectedContainer.endSliding(gesture.velocityX);
this.selectedContainer = null;
this.preSelectedContainer = null;
}
closeOpened(): boolean {
this.selectedContainer = null;
if (this.openContainer) {
this.openContainer.close();
this.openContainer = null;
return true;
}
return false;
}
/**
* @hidden
*/
getOpenAmount(): number {
return this.openAmount;
}
/**
* @hidden
*/
getSlidingPercent(): number {
const openAmount = this.openAmount;
if (openAmount > 0) {
return openAmount / this.optsWidthRightSide;
} else if (openAmount < 0) {
return openAmount / this.optsWidthLeftSide;
} else {
return 0;
}
}
/**
* @hidden
*/
startSliding(startX: number) {
if (this.tmr) {
clearTimeout(this.tmr);
this.tmr = null;
}
if (this.openAmount === 0) {
this.optsDirty = true;
this.setState(SlidingState.Enabled);
}
this.startX = startX + this.openAmount;
this.item.style.transition = 'none';
}
/**
* @hidden
*/
moveSliding(x: number): number {
if (this.optsDirty) {
this.calculateOptsWidth();
return 0;
}
let openAmount = (this.startX - x);
switch (this.sides) {
case ITEM_SIDE_FLAG_RIGHT: openAmount = Math.max(0, openAmount); break;
case ITEM_SIDE_FLAG_LEFT: openAmount = Math.min(0, openAmount); break;
case ITEM_SIDE_FLAG_BOTH: break;
case ITEM_SIDE_FLAG_NONE: return 0;
default: console.warn('invalid ItemSideFlags value', this.sides); break;
}
if (openAmount > this.optsWidthRightSide) {
var optsWidth = this.optsWidthRightSide;
openAmount = optsWidth + (openAmount - optsWidth) * ELASTIC_FACTOR;
} else if (openAmount < -this.optsWidthLeftSide) {
var optsWidth = -this.optsWidthLeftSide;
openAmount = optsWidth + (openAmount - optsWidth) * ELASTIC_FACTOR;
}
this.setOpenAmount(openAmount, false);
return openAmount;
}
/**
* @hidden
*/
endSliding(velocity: number): number {
let restingPoint = (this.openAmount > 0)
? this.optsWidthRightSide
: -this.optsWidthLeftSide;
// Check if the drag didn't clear the buttons mid-point
// and we aren't moving fast enough to swipe open
const isResetDirection = (this.openAmount > 0) === !(velocity < 0);
const isMovingFast = Math.abs(velocity) > 0.3;
const isOnCloseZone = Math.abs(this.openAmount) < Math.abs(restingPoint / 2);
if (swipeShouldReset(isResetDirection, isMovingFast, isOnCloseZone)) {
restingPoint = 0;
}
this.setOpenAmount(restingPoint, true);
this.fireSwipeEvent();
return restingPoint;
}
/**
* @hidden
* Emit the ionSwipe event on the child options
*/
fireSwipeEvent() {
if (this.state & SlidingState.SwipeRight) {
this.rightOptions.ionSwipe(this);
} else if (this.state & SlidingState.SwipeLeft) {
this.leftOptions.ionSwipe(this);
}
}
/**
* @hidden
*/
calculateOptsWidth() {
if (!this.optsDirty) {
return;
}
this.optsWidthRightSide = 0;
if (this.rightOptions) {
this.optsWidthRightSide = this.rightOptions.width();
this.optsWidthRightSide == 0 && console.warn('optsWidthRightSide should not be zero');
}
this.optsWidthLeftSide = 0;
if (this.leftOptions) {
this.optsWidthLeftSide = this.leftOptions.width();
this.optsWidthLeftSide == 0 && console.warn('optsWidthLeftSide should not be zero');
}
this.optsDirty = false;
}
setOpenAmount(openAmount: number, isFinal: boolean) {
if (this.tmr) {
clearTimeout(this.tmr);
this.tmr = null;
}
this.openAmount = openAmount;
if (isFinal) {
this.item.style.transition = '';
} else {
if (openAmount > 0) {
var state = (openAmount >= (this.optsWidthRightSide + SWIPE_MARGIN))
? SlidingState.Right | SlidingState.SwipeRight
: SlidingState.Right;
this.setState(state);
} else if (openAmount < 0) {
var state = (openAmount <= (-this.optsWidthLeftSide - SWIPE_MARGIN))
? SlidingState.Left | SlidingState.SwipeLeft
: SlidingState.Left;
this.setState(state);
}
}
if (openAmount === 0) {
this.setState(SlidingState.Disabled);
this.tmr = setTimeout(() => {
this.tmr = null;
this.setState(SlidingState.Disabled);
}, 600);
this.item.style.transform = '';
return;
}
this.item.style.transform = `translate3d(${-openAmount}px,0,0)`;
this.ionDrag();
}
private setState(state: SlidingState) {
console.log('setState',
this.state + '\n',
'active-slide', (this.state !== SlidingState.Disabled),
'active-options-right', !!(this.state & SlidingState.Right),
'active-options-left', !!(this.state & SlidingState.Left),
'active-swipe-right', !!(this.state & SlidingState.SwipeRight),
'active-swipe-left', !!(this.state & SlidingState.SwipeLeft)
);
if (state === this.state) {
return;
}
this.state = state;
}
/**
* Close the sliding item. Items can also be closed from the [List](../../list/List).
*
* The sliding item can be closed by grabbing a reference to `ItemSliding`. In the
* below example, the template reference variable `slidingItem` is placed on the element
* and passed to the `share` method.
*
* ```html
* <ion-list>
* <ion-item-sliding #slidingItem>
* <ion-item>
* Item
* </ion-item>
* <ion-item-options>
* <ion-button (click)="share(slidingItem)">Share</ion-button>
* </ion-item-options>
* </ion-item-sliding>
* </ion-list>
* ```
*
* ```ts
* import { Component } from '@angular/core';
* import { ItemSliding } from 'ionic-angular';
*
* @Component({...})
* export class MyClass {
* constructor() { }
*
* share(slidingItem: ItemSliding) {
* slidingItem.close();
* }
* }
* ```
*/
close() {
this.setOpenAmount(0, true);
}
hostData() {
console.log('hostData',
this.state + '\n',
'active-slide', (this.state !== SlidingState.Disabled),
'active-options-right', !!(this.state & SlidingState.Right),
'active-options-left', !!(this.state & SlidingState.Left),
'active-swipe-right', !!(this.state & SlidingState.SwipeRight),
'active-swipe-left', !!(this.state & SlidingState.SwipeLeft)
);
return {
class: {
'item-wrapper': true,
'active-slide': (this.state !== SlidingState.Disabled),
'active-options-right': !!(this.state & SlidingState.Right),
'active-options-left': !!(this.state & SlidingState.Left),
'active-swipe-right': !!(this.state & SlidingState.SwipeRight),
'active-swipe-left': !!(this.state & SlidingState.SwipeLeft)
}
}
}
render() {
return (
<ion-gesture props={{
'canStart': this.canStart.bind(this),
'onStart': this.onDragStart.bind(this),
'onMove': this.onDragMove.bind(this),
'onEnd': this.onDragEnd.bind(this),
'gestureName': 'item-swipe',
'gesturePriority': -10,
'type': 'pan',
'direction': 'x',
'maxAngle': 20,
'threshold': 5,
'attachTo': 'parent'
}}>
<slot></slot>
</ion-gesture>
);
}
}

View File

@ -0,0 +1,309 @@
<!DOCTYPE html>
<html dir="ltr">
<head>
<meta charset="UTF-8">
<title>Ionic Item Sliding</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<script src="/dist/ionic.js"></script>
</head>
<body>
<ion-app>
<ion-content>
<div padding>
<ion-button block onclick="toggleSliding()">Toggle sliding</ion-button>
<ion-button block onclick="changeDynamic()">Change Dynamic Options</ion-button>
<ion-button block onclick="closeOpened()">Close Opened Items</ion-button>
</div>
<ion-list #myList>
<ion-item-sliding>
<ion-item>
<ion-label>
<h2>No Options</h2>
<p>Should not error or swipe without options</p>
</ion-label>
</ion-item>
</ion-item-sliding>
<ion-item-sliding id="item6">
<ion-item>
<ion-label>
One Line, dynamic option and text
</ion-label>
</ion-item>
<ion-item-options *ngIf="showOptions">
<ion-button color="primary">
<ion-icon name="more"></ion-icon>
{{ moreText }}
</ion-button>
<ion-button color="secondary" onclick="archive('item6')">
<ion-icon name="archive"></ion-icon>
{{ archiveText }}
</ion-button>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding id="item6">
<ion-item>
<ion-label>
Two options, one dynamic option and text
</ion-label>
</ion-item>
<ion-item-options side="left">
<ion-button color="primary">
<ion-icon slot="icon-only" name="more"></ion-icon>
</ion-button>
</ion-item-options>
<ion-item-options side="right" *ngIf="showOptions">
<ion-button color="primary">
<ion-icon name="more"></ion-icon>
{{ moreText }}
</ion-button>
<ion-button color="secondary" onclick="archive('item6')">
<ion-icon name="archive"></ion-icon>
{{ archiveText }}
</ion-button>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding id="item1"00>
<ion-item href="#">
<ion-label>
<h2>HubStruck Notifications</h2>
<p>A new message from a repo in your network</p>
<p>Oceanic Next has joined your network</p>
</ion-label>
<ion-note slot="end">
10:45 AM
</ion-note>
</ion-item>
<ion-item-options side="left">
<ion-button onclick="noclose(item100)">
No close
</ion-button>
</ion-item-options>
<ion-item-options side="right">
<ion-button color="danger" onclick="unread('item100')">
<ion-icon slot="icon-only" name="trash"></ion-icon>
</ion-button>
<ion-button onclick="unread('item100')">
<ion-icon slot="icon-only" name="star"></ion-icon>
</ion-button>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding id="item0">
<ion-item text-wrap onclick="didClick(item0)">
<ion-label>
<h2>RIGHT side - no icons</h2>
<p>Hey do you want to go to the game tonight?</p>
</ion-label>
</ion-item>
<ion-item-options *ngIf="slidingEnabled">
<ion-button color="primary" onclick="archive('item0')">Archive</ion-button>
<ion-button color="danger" onclick="del('item0')">Delete</ion-button>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding id="item1">
<ion-item text-wrap detail-push href="#" class="activated">
<ion-label>
<h2>LEFT side - no icons</h2>
<p>I think I figured out how to get more Mountain Dew</p>
</ion-label>
</ion-item>
<ion-item-options side="left" *ngIf="slidingEnabled">
<ion-button color="primary" onclick="archive('item1')">Archive</ion-button>
<ion-button color="danger" onclick="del('item1')">Delete</ion-button>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding id="item2">
<ion-item text-wrap detail-push>
<ion-label>
<h2>RIGHT/LEFT side - icons</h2>
<p>I think I figured out how to get more Mountain Dew</p>
</ion-label>
</ion-item>
<ion-item-options side="left" (ionSwipe)="unread($event)" *ngIf="slidingEnabled">
<ion-button color="secondary" expandable onclick="unread('item2')">
<ion-icon name="ios-checkmark"></ion-icon>Unread
</ion-button>
</ion-item-options>
<ion-item-options side="right" (ionSwipe)="del('item2')" *ngIf="slidingEnabled">
<ion-button color="primary" onclick="archive('item2')">
<ion-icon name="mail"></ion-icon>Archive
</ion-button>
<ion-button color="danger" onclick="del('item2')" expandable>
<ion-icon name="trash"></ion-icon>Delete
</ion-button>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding id="item3">
<ion-item text-wrap detail-push>
<ion-label>
<h2>RIGHT/LEFT side - icons (slot="start")</h2>
<p>I think I figured out how to get more Mountain Dew</p>
</ion-label>
</ion-item>
<ion-item-options side="left" icon-start (ionSwipe)="unread($event)" *ngIf="slidingEnabled">
<ion-button color="secondary" expandable onclick="unread('item3')">
<ion-icon name="ios-checkmark"></ion-icon>Unread
</ion-button>
</ion-item-options>
<ion-item-options icon-start (ionSwipe)="del('item3')">
<ion-button color="primary" onclick="archive('item3')">
<ion-icon name="mail"></ion-icon>Archive
</ion-button>
<ion-button color="danger" onclick="del('item3')" expandable *ngIf="slidingEnabled">
<ion-icon name="trash"></ion-icon>Delete
</ion-button>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding id="item4">
<ion-item>
<ion-icon name="mail" slot="start"></ion-icon>
<ion-label>
One Line w/ Icon, div only text
</ion-label>
</ion-item>
<ion-item-options icon-start (ionSwipe)="archive($event)">
<ion-button color="primary" onclick="archive('item4')" expandable *ngIf="slidingEnabled">
<ion-icon name="archive"></ion-icon>Archive
</ion-button>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding id="item5" *ngIf="slidingEnabled">
<ion-item>
<ion-avatar slot="start">
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAAAAACH5BAAAAAAALAAAAAABAAEAAAICTAEAOw==">
</ion-avatar>
<ion-label>
One Line w/ Avatar, div only text
</ion-label>
</ion-item>
<ion-item-options>
<ion-button color="primary" expandable>
<ion-icon name="more"></ion-icon>More
</ion-button>
<ion-button color="secondary" onclick="archive('item5')">
<ion-icon name="archive"></ion-icon>Archive
</ion-button>
<ion-button color="danger" onclick="del('item5')">
<ion-icon name="trash"></ion-icon>Delete
</ion-button>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding id="item7">
<ion-item>
<ion-label>
One Line, dynamic icon-start option
</ion-label>
</ion-item>
<ion-item-options icon-start>
<ion-button color="primary">
<ion-icon name="more"></ion-icon>
{{ moreText }}
</ion-button>
<ion-button color="secondary" onclick="archive('item7')">
<ion-icon name="archive"></ion-icon>
{{ archiveText }}
</ion-button>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding id="item8">
<ion-item>
<ion-thumbnail slot="start">
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAAAAACH5BAAAAAAALAAAAAABAAEAAAICTAEAOw==">
</ion-thumbnail>
<ion-label>
<h2>DOWNLOAD</h2>
<p>Paragraph text.</p>
</ion-label>
</ion-item>
<ion-item-options (ionSwipe)="download($event)">
<ion-button color="primary" onclick="archive('item8')">
<ion-icon name="archive"></ion-icon>Archive
</ion-button>
<ion-button color="secondary" expandable onclick="download(item8)">
<ion-icon name="download" class="download-hide"></ion-icon>
<div class="download-hide">Download</div>
<ion-spinner id="download-spinner"></ion-spinner>
</ion-button>
</ion-item-options>
</ion-item-sliding>
<ion-item-sliding id="item9">
<ion-item>
<ion-thumbnail slot="start">
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAAAAACH5BAAAAAAALAAAAAABAAEAAAICTAEAOw==">
</ion-thumbnail>
<ion-label>
<h2>ion-item-sliding without options (no sliding)</h2>
<p>Paragraph text.</p>
</ion-label>
</ion-item>
</ion-item-sliding>
<ion-item text-wrap>
<ion-label>
<h2>Normal ion-item (no sliding)</h2>
<p>Paragraph text.</p>
</ion-label>
</ion-item>
<ion-item text-wrap onclick="didClick(item9)">
<ion-label>
<h2>Normal button (no sliding)</h2>
<p>Hey do you want to go to the game tonight?</p>
</ion-label>
</ion-item>
</ion-list>
<script>
var dynamicColor = document.getElementById('dynamicColor');
function unread(item) {
console.log('unread');
}
function archive(item) {
console.log('archive');
}
function del(item) {
console.log('del');
}
function toggleSliding() {
}
function changeDynamic() {
}
function closeOpened() {
}
</script>
</ion-content>
</ion-app>
</body>
</html>

View File

@ -17,6 +17,7 @@ export class Item {
@Prop() mode: string;
@Prop() color: string;
@Prop() href: string;
@Listen('ionStyle')
itemStyle(ev: UIEvent) {
@ -51,8 +52,11 @@ export class Item {
'item-block': true
};
// TODO add support for button items
const TagType = this.href ? 'a' : 'div';
return (
<div class={themedClasses}>
<TagType class={themedClasses}>
<slot name='start'></slot>
<div class='item-inner'>
<div class='input-wrapper'>
@ -60,7 +64,7 @@ export class Item {
</div>
<slot name='end'></slot>
</div>
</div>
</TagType>
);
// template:

View File

@ -10,7 +10,7 @@ exports.config = {
{ components: ['ion-card', 'ion-card-content', 'ion-card-header', 'ion-card-title'] },
{ components: ['ion-fab', 'ion-fab-button', 'ion-fab-list'] },
{ components: ['ion-gesture', 'ion-scroll'], priority: 'low' },
{ components: ['ion-item', 'ion-item-divider', 'ion-label', 'ion-list', 'ion-list-header', 'ion-skeleton-text'] },
{ components: ['ion-item', 'ion-item-divider', 'ion-item-sliding', 'ion-item-options', 'ion-label', 'ion-list', 'ion-list-header', 'ion-skeleton-text'] },
{ components: ['ion-loading', 'ion-loading-controller'] },
{ components: ['ion-menu'], priority: 'low' },
{ components: ['ion-modal', 'ion-modal-controller'] },