diff --git a/demos/item-sliding/index.ts b/demos/item-sliding/index.ts
index 2ab466b7872..65c41e1846a 100644
--- a/demos/item-sliding/index.ts
+++ b/demos/item-sliding/index.ts
@@ -1,17 +1,52 @@
-import { Component } from '@angular/core';
+import { Component, ViewEncapsulation } from '@angular/core';
import { ionicBootstrap, ItemSliding, NavController, Toast } from 'ionic-angular';
@Component({
- templateUrl: 'main.html'
+ templateUrl: 'main.html',
+ encapsulation: ViewEncapsulation.None
})
class ApiDemoPage {
chats: any[];
logins: any[];
+ editButton: string = 'Edit';
+ editing: boolean = false;
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'
+ },
+ {
+ 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'
+ },
{
img: './avatar-cher.png',
name: 'Cher',
@@ -49,6 +84,15 @@ class ApiDemoPage {
}];
}
+ toggleEdit() {
+ this.editing = !this.editing;
+ if (this.editing) {
+ this.editButton = 'Done';
+ } else {
+ this.editButton = 'Edit';
+ }
+ }
+
more(item: ItemSliding) {
console.log('More');
item.close();
diff --git a/demos/item-sliding/main.html b/demos/item-sliding/main.html
index 5dcd34ef35d..f0aa037119b 100644
--- a/demos/item-sliding/main.html
+++ b/demos/item-sliding/main.html
@@ -2,19 +2,24 @@
Item Sliding
+
+
+
-
+
-
+
Chats
-
+
+
+
@@ -41,13 +46,14 @@ {{chat.name}}
-
+
+
@@ -87,6 +93,12 @@ {{login.name}}
#download-spinner {
display: none;
}
+
+ div.toolbar-background {
+ background-color: rgba(255, 255, 255, 0.65);
+ -webkit-backdrop-filter: saturate(180%) blur(20px);
+ backdrop-filter: saturate(180%) blur(20px);
+ }
svg circle {
stroke: white;
diff --git a/src/components/item/item-reorder-gesture.ts b/src/components/item/item-reorder-gesture.ts
index c017bf3af04..467d39723d6 100644
--- a/src/components/item/item-reorder-gesture.ts
+++ b/src/components/item/item-reorder-gesture.ts
@@ -1,5 +1,5 @@
import {Item} from './item';
-import {List} from '../list/list';
+import {Reorder} from '../item/item-reorder';
import {UIEventManager} from '../../util/ui-event-manager';
import {closest, Coordinates, pointerCoord, CSS, nativeRaf} from '../../util/dom';
@@ -12,7 +12,6 @@ const ITEM_REORDER_ACTIVE = 'reorder-active';
* @private
*/
export class ItemReorderGesture {
- private selectedItem: Item = null;
private selectedItemEle: HTMLElement = null;
private selectedItemHeight: number;
@@ -26,7 +25,7 @@ export class ItemReorderGesture {
private events: UIEventManager = new UIEventManager(false);
- constructor(public list: List) {
+ constructor(public list: Reorder) {
let element = this.list.getNativeElement();
this.events.pointerEvents(element,
this.onDragStart.bind(this),
@@ -35,24 +34,22 @@ export class ItemReorderGesture {
}
private onDragStart(ev: any): boolean {
- let itemEle = ev.target;
- if (itemEle.nodeName !== 'ION-REORDER') {
+ let reorderElement = ev.target;
+ if (reorderElement.nodeName !== 'ION-REORDER') {
return false;
}
- let item = - itemEle['$ionComponent'];
+ let item = reorderElement['$ionReorderNode'];
if (!item) {
- console.error('item does not contain ion component');
+ console.error('item does not contain ion ionReorderNode');
return false;
}
ev.preventDefault();
// Preparing state
- this.selectedItem = item;
- this.selectedItemEle = item.getNativeElement();
- this.selectedItemHeight = item.height();
- this.lastToIndex = item.index;
- this.lastYcoord = -100;
+ this.selectedItemEle = item;
+ this.selectedItemHeight = item.offsetHeight;
+ this.lastYcoord = this.lastToIndex = -100;
this.windowHeight = window.innerHeight - AUTO_SCROLL_MARGIN;
this.lastScrollPosition = this.list.scrollContent(0);
@@ -60,7 +57,7 @@ export class ItemReorderGesture {
this.offset = pointerCoord(ev);
this.offset.y += this.lastScrollPosition;
- item.setCssClass(ITEM_REORDER_ACTIVE, true);
+ item.classList.add(ITEM_REORDER_ACTIVE);
this.list.reorderStart();
return true;
}
@@ -83,9 +80,9 @@ export class ItemReorderGesture {
if (Math.abs(posY - this.lastYcoord) > 30) {
let overItem = this.itemForCoord(coord);
if (overItem) {
- let toIndex = overItem.index;
- if (toIndex !== this.lastToIndex || this.emptyZone) {
- let fromIndex = this.selectedItem.index;
+ let toIndex = indexForItem(overItem);
+ if (toIndex && (toIndex !== this.lastToIndex || this.emptyZone)) {
+ let fromIndex = indexForItem(this.selectedItemEle);
this.lastToIndex = toIndex;
this.lastYcoord = posY;
this.emptyZone = false;
@@ -96,40 +93,25 @@ export class ItemReorderGesture {
}
}
- // Update selected item position
+ // Update selected item position
let ydiff = Math.round(posY - this.offset.y + scrollPosition);
selectedItem.style[CSS.transform] = `translateY(${ydiff}px)`;
}
-
+
private onDragEnd() {
if (!this.selectedItemEle) {
return;
}
- nativeRaf(() => {
- let toIndex = this.lastToIndex;
- let fromIndex = this.selectedItem.index;
- this.selectedItem.setCssClass(ITEM_REORDER_ACTIVE, false);
- this.selectedItem = null;
- this.selectedItemEle = null;
- this.list.reorderEmit(fromIndex, toIndex);
- });
+ let toIndex = this.lastToIndex;
+ let fromIndex = indexForItem(this.selectedItemEle);
+ this.selectedItemEle.classList.remove(ITEM_REORDER_ACTIVE);
+ this.selectedItemEle = null;
+ this.list.reorderEmit(fromIndex, toIndex);
}
- private itemForCoord(coord: Coordinates): Item {
- let element = document.elementFromPoint(this.offset.x - 100, coord.y);
- if (!element) {
- return null;
- }
- if (element.nodeName !== 'ION-ITEM') {
- return null;
- }
- let item =
- (element)['$ionComponent'];
- if (!item) {
- console.error('item does not have $ionComponent');
- return null;
- }
- return item;
+ private itemForCoord(coord: Coordinates): HTMLElement {
+ return itemForPosition(this.offset.x - 100, coord.y);
}
private scroll(posY: number): number {
@@ -141,8 +123,6 @@ export class ItemReorderGesture {
return this.lastScrollPosition;
}
-
-
/**
* @private
*/
@@ -153,3 +133,25 @@ export class ItemReorderGesture {
this.list = null;
}
}
+
+function itemForPosition(x: number, y: number): HTMLElement {
+ let element = document.elementFromPoint(x, y);
+ if (!element) {
+ return null;
+ }
+ if (element.nodeName !== 'ION-ITEM' && !element.hasAttribute('ion-item')) {
+ return null;
+ }
+ if (indexForItem(element)) {
+ return element;
+ }
+ let parent = element.parentNode;
+ if (indexForItem(parent)) {
+ return parent;
+ }
+ return null;
+}
+
+function indexForItem(element: any): number {
+ return element['$ionIndex'];
+}
diff --git a/src/components/item/item-reorder.scss b/src/components/item/item-reorder.scss
index f278117a807..9cc320761e1 100644
--- a/src/components/item/item-reorder.scss
+++ b/src/components/item/item-reorder.scss
@@ -24,7 +24,9 @@ ion-reorder {
.reorder-enabled {
- .item {
+ .item, .item-wrapper {
+ transition: transform 300ms;
+
will-change: transform;
}
@@ -40,6 +42,7 @@ ion-reorder {
}
+.item-wrapper.reorder-active,
.item.reorder-active {
z-index: 4;
diff --git a/src/components/item/item-reorder.ts b/src/components/item/item-reorder.ts
index fc29d759edc..441def587db 100644
--- a/src/components/item/item-reorder.ts
+++ b/src/components/item/item-reorder.ts
@@ -1,6 +1,171 @@
-import { Component, ElementRef, forwardRef, Inject } from '@angular/core';
+import { Component, Directive, ElementRef, EventEmitter, forwardRef, Input, NgZone, Renderer, Inject, Optional, Output } from '@angular/core';
+import { Content } from '../content/content';
+import { CSS } from '../../util/dom';
import { Item } from './item';
+import { ItemReorderGesture } from '../item/item-reorder-gesture';
+import { isTrueProperty } from '../../util/util';
+
+export interface ReorderIndexes {
+ from: number;
+ to: number;
+}
+
+/**
+ * @private
+ */
+@Directive({
+ selector: '[reorder]',
+ host: {
+ '[class.reorder-enabled]': '_enableReorder',
+ }
+})
+export class Reorder {
+ private _enableReorder: boolean = false;
+ private _reorderGesture: ItemReorderGesture;
+ private _lastToIndex: number = -1;
+ private _element: HTMLElement;
+
+ @Output() ionItemReorder: EventEmitter = new EventEmitter();
+
+ constructor(
+ elementRef: ElementRef,
+ private _rendered: Renderer,
+ private _zone: NgZone,
+ @Optional() private _content: Content) {
+ this._element = elementRef.nativeElement;
+ }
+
+ /**
+ * @private
+ */
+ ngOnDestroy() {
+ this._element = null;
+ this._reorderGesture && this._reorderGesture.destroy();
+ }
+
+
+ @Input()
+ get reorder(): boolean {
+ return this._enableReorder;
+ }
+ set reorder(val: boolean) {
+ this._enableReorder = isTrueProperty(val);
+
+ if (!this._enableReorder) {
+ this._reorderGesture && this._reorderGesture.destroy();
+ this._reorderGesture = null;
+
+ } else if (!this._reorderGesture) {
+ console.debug('enableReorderItems');
+ this._reorderGesture = new ItemReorderGesture(this);
+ }
+ }
+
+ /**
+ * @private
+ */
+ reorderStart() {
+ let children = this._element.children;
+ let len = children.length;
+ this.setCssClass('reorder-active', true);
+ for (let i = 0; i < len; i++) {
+ children[i]['$ionIndex'] = i;
+ }
+ }
+
+ /**
+ * @private
+ */
+ reorderEmit(fromIndex: number, toIndex: number) {
+ this.reorderReset();
+ if (fromIndex !== toIndex) {
+ this._zone.run(() => {
+ this.ionItemReorder.emit({
+ from: fromIndex,
+ to: toIndex,
+ });
+ });
+ }
+ }
+
+ /**
+ * @private
+ */
+ scrollContent(scroll: number) {
+ let scrollTop = this._content.getScrollTop() + scroll;
+ if (scroll !== 0) {
+ this._content.scrollTo(0, scrollTop, 0);
+ }
+ return scrollTop;
+ }
+
+ /**
+ * @private
+ */
+ reorderReset() {
+ let children = this._element.children;
+ let len = children.length;
+
+ this.setCssClass('reorder-active', false);
+ let transform = CSS.transform;
+ for (let i = 0; i < len; i++) {
+ children[i].style[transform] = '';
+ }
+ this._lastToIndex = -1;
+ }
+
+ /**
+ * @private
+ */
+ reorderMove(fromIndex: number, toIndex: number, itemHeight: number) {
+ if (this._lastToIndex === -1) {
+ this._lastToIndex = fromIndex;
+ }
+ let lastToIndex = this._lastToIndex;
+ this._lastToIndex = toIndex;
+
+ // TODO: I think both loops can be merged into a single one
+ // but I had no luck last time I tried
+
+ /********* DOM READ ********** */
+ let children = this._element.children;
+
+ /********* DOM WRITE ********* */
+ let transform = CSS.transform;
+ if (toIndex >= lastToIndex) {
+ for (var i = lastToIndex; i <= toIndex; i++) {
+ if (i !== fromIndex) {
+ children[i].style[transform] = (i > fromIndex)
+ ? `translateY(${-itemHeight}px)` : '';
+ }
+ }
+ }
+
+ if (toIndex <= lastToIndex) {
+ for (var i = toIndex; i <= lastToIndex; i++) {
+ if (i !== fromIndex) {
+ children[i].style[transform] = (i < fromIndex)
+ ? `translateY(${itemHeight}px)` : '';
+ }
+ }
+ }
+ }
+
+ /**
+ * @private
+ */
+ setCssClass(classname: string, add: boolean) {
+ this._rendered.setElementClass(this._element, classname, add);
+ }
+
+ /**
+ * @private
+ */
+ getNativeElement(): HTMLElement {
+ return this._element;
+ }
+}
/**
* @private
@@ -11,8 +176,18 @@ import { Item } from './item';
})
export class ItemReorder {
constructor(
- @Inject(forwardRef(() => Item)) item: Item,
- elementRef: ElementRef) {
- elementRef.nativeElement['$ionComponent'] = item;
+ @Inject(forwardRef(() => Item)) private item: Item,
+ private elementRef: ElementRef) {
+ }
+
+ ngAfterContentInit() {
+ let item = this.item.getNativeElement();
+ if (item.parentNode.nodeName === 'ION-ITEM-SLIDING') {
+ this.elementRef.nativeElement['$ionReorderNode'] = item.parentNode;
+ } else {
+ this.elementRef.nativeElement['$ionReorderNode'] = item;
+ }
}
+
+
}
diff --git a/src/components/item/item-sliding-gesture.ts b/src/components/item/item-sliding-gesture.ts
index 10ac1896167..3d0c5a8e49a 100644
--- a/src/components/item/item-sliding-gesture.ts
+++ b/src/components/item/item-sliding-gesture.ts
@@ -70,38 +70,40 @@ export class ItemSlidingGesture extends DragGesture {
}
onDragEnd(ev: any) {
- if (this.selectedContainer) {
- ev.preventDefault();
-
- let openAmount = this.selectedContainer.endSliding(ev.velocityX);
- this.selectedContainer = null;
-
- // TODO: I am not sure listening for a tap event is the best idea
- // we should try mousedown/touchstart
- if (openAmount === 0) {
- this.openContainer = null;
- this.off('tap', this.onTap);
- this.onTap = null;
- } else if (!this.onTap) {
- this.onTap = (event: any) => this.onTapCallback(event);
- this.on('tap', this.onTap);
- }
+ if (!this.selectedContainer) {
+ return;
}
- }
+ ev.preventDefault();
- closeOpened(): boolean {
- if (this.openContainer) {
- this.openContainer.close();
+ let openAmount = this.selectedContainer.endSliding(ev.velocityX);
+ this.selectedContainer = null;
+
+ // TODO: I am not sure listening for a tap event is the best idea
+ // we should try mousedown/touchstart
+ if (openAmount === 0) {
this.openContainer = null;
- this.selectedContainer = null;
this.off('tap', this.onTap);
this.onTap = null;
- return true;
+ } else if (!this.onTap) {
+ this.onTap = (event: any) => this.onTapCallback(event);
+ this.on('tap', this.onTap);
}
- return false;
+ }
+
+ closeOpened(): boolean {
+ if (!this.openContainer) {
+ return false;
+ }
+ this.openContainer.close();
+ this.openContainer = null;
+ this.selectedContainer = null;
+ this.off('tap', this.onTap);
+ this.onTap = null;
+ return true;
}
unlisten() {
+ this.closeOpened();
super.unlisten();
this.list = null;
}
diff --git a/src/components/item/item-sliding.scss b/src/components/item/item-sliding.scss
index eb8997c0f5b..085bba28cc2 100644
--- a/src/components/item/item-sliding.scss
+++ b/src/components/item/item-sliding.scss
@@ -71,9 +71,11 @@ ion-item-sliding.active-slide {
z-index: $z-index-item-options + 1;
opacity: 1;
- transition: all 300ms cubic-bezier(.36, .66, .04, 1);
+ transition: transform 500ms cubic-bezier(.36, .66, .04, 1);
pointer-events: none;
+
+ will-change: transform;
}
ion-item-options {
diff --git a/src/components/item/item-sliding.ts b/src/components/item/item-sliding.ts
index 1561967e5c5..237606088f4 100644
--- a/src/components/item/item-sliding.ts
+++ b/src/components/item/item-sliding.ts
@@ -1,11 +1,11 @@
-import { ChangeDetectionStrategy, Component, ContentChildren, ContentChild, Directive, ElementRef, EventEmitter, HostBinding, Input, Optional, Output, QueryList, Renderer, ViewEncapsulation } from '@angular/core';
+import { ChangeDetectionStrategy, Component, ContentChildren, ContentChild, Directive, ElementRef, EventEmitter, Input, Optional, Output, QueryList, Renderer, ViewEncapsulation } from '@angular/core';
-import { CSS, nativeRaf, nativeTimeout } from '../../util/dom';
+import { CSS, nativeRaf, nativeTimeout, clearNativeTimeout } from '../../util/dom';
import { Item } from './item';
import { isPresent } from '../../util/util';
import { List } from '../list/list';
-const SWIPE_FACTOR = 1.1;
+const SWIPE_MARGIN = 20;
const ELASTIC_FACTOR = 0.55;
export const enum ItemSideFlags {
@@ -52,8 +52,7 @@ export class ItemOptions {
*/
@Output() ionSwipe: EventEmitter = new EventEmitter();
- constructor(private _elementRef: ElementRef, private _renderer: Renderer) {
- }
+ constructor(private _elementRef: ElementRef, private _renderer: Renderer) {}
/**
* @private
@@ -83,10 +82,13 @@ export class ItemOptions {
}
const enum SlidingState {
- Disabled = 0,
- Enabled = 1,
- Right = 2,
- Left = 3
+ Disabled = 1 << 1,
+ Enabled = 1 << 2,
+ Right = 1 << 3,
+ Left = 1 << 4,
+
+ SwipeRight = 1 << 5,
+ SwipeLeft = 1 << 6,
}
@@ -194,23 +196,15 @@ export class ItemSliding {
private _optsDirty: boolean = true;
private _state: SlidingState = SlidingState.Disabled;
- /**
- * @private
- * */
- slidingPercent: number = 0;
-
- /**
- * @private
- * */
@ContentChild(Item) private item: Item;
-
/**
* @output {event} Expression to evaluate when the sliding position changes.
* It reports the relative position.
*
* ```ts
- * ondrag(percent) {
+ * ondrag(item) {
+ * let percent = item.getSlidingPercent();
* if (percent > 0) {
* // positive
* console.log('right side');
@@ -225,20 +219,16 @@ export class ItemSliding {
* ```
*
*/
- @Output() ionDrag: EventEmitter = new EventEmitter();
+ @Output() ionDrag: EventEmitter = new EventEmitter();
- constructor(@Optional() private _list: List, private _renderer: Renderer, private _elementRef: ElementRef) {
- _list.enableSlidingItems(true);
+ constructor( @Optional() list: List, private _renderer: Renderer, private _elementRef: ElementRef) {
+ list && list.containsSlidingItem(true);
_elementRef.nativeElement.$ionComponent = this;
- _renderer.setElementClass(_elementRef.nativeElement, 'item-wrapper', true);
+ this.setCssClass('item-wrapper', true);
}
-
- /**
- * @private
- */
@ContentChildren(ItemOptions)
- set _itemOptions(itemOptions: QueryList) {
+ private set _itemOptions(itemOptions: QueryList) {
let sides = 0;
for (var item of itemOptions.toArray()) {
var side = item.getSides();
@@ -253,12 +243,33 @@ export class ItemSliding {
this._sides = sides;
}
+ /**
+ * @private
+ */
+ getOpenAmount(): number {
+ return this._openAmount;
+ }
+
+ /**
+ * @private
+ */
+ getSlidingPercent(): number {
+ let openAmount = this._openAmount;
+ if (openAmount > 0) {
+ return openAmount / this._optsWidthRightSide;
+ } else if (openAmount < 0) {
+ return openAmount / this._optsWidthLeftSide;
+ } else {
+ return 0;
+ }
+ }
+
/**
* @private
*/
startSliding(startX: number) {
if (this._timer) {
- clearTimeout(this._timer);
+ clearNativeTimeout(this._timer);
this._timer = null;
}
if (this._openAmount === 0) {
@@ -278,7 +289,7 @@ export class ItemSliding {
return;
}
- let openAmount = this._startX - x;
+ let openAmount = (this._startX - x);
switch (this._sides) {
case ItemSideFlags.Right: openAmount = Math.max(0, openAmount); break;
case ItemSideFlags.Left: openAmount = Math.min(0, openAmount); break;
@@ -316,105 +327,88 @@ export class ItemSliding {
restingPoint = 0;
}
- this.fireSwipeEvent();
this._setOpenAmount(restingPoint, true);
+ this.fireSwipeEvent();
return restingPoint;
}
- /**
- * @private
- * */
- fireSwipeEvent() {
- if (this.slidingPercent > SWIPE_FACTOR) {
+ private fireSwipeEvent() {
+ if (this._state & SlidingState.SwipeRight) {
this._rightOptions.ionSwipe.emit(this);
- } else if (this.slidingPercent < -SWIPE_FACTOR) {
+ } else if (this._state & SlidingState.SwipeLeft) {
this._leftOptions.ionSwipe.emit(this);
}
}
- /**
- * @private
- * */
- calculateOptsWidth() {
+ private calculateOptsWidth() {
nativeRaf(() => {
- if (this._optsDirty) {
- this._optsWidthRightSide = 0;
- if (this._rightOptions) {
- this._optsWidthRightSide = this._rightOptions.width();
- }
-
- this._optsWidthLeftSide = 0;
- if (this._leftOptions) {
- this._optsWidthLeftSide = this._leftOptions.width();
- }
- this._optsDirty = false;
+ if (!this._optsDirty) {
+ return;
+ }
+ this._optsWidthRightSide = 0;
+ if (this._rightOptions) {
+ this._optsWidthRightSide = this._rightOptions.width();
+ }
+
+ this._optsWidthLeftSide = 0;
+ if (this._leftOptions) {
+ this._optsWidthLeftSide = this._leftOptions.width();
}
+ this._optsDirty = false;
});
}
- /**
- * @private
- */
private _setOpenAmount(openAmount: number, isFinal: boolean) {
if (this._timer) {
- clearTimeout(this._timer);
+ clearNativeTimeout(this._timer);
this._timer = null;
}
this._openAmount = openAmount;
+
+ if (isFinal) {
+ this.item.setCssStyle(CSS.transition, '');
- let didEnd = openAmount === 0;
- if (didEnd) {
- // TODO: refactor. there must exist a better way
- // if sliding ended, we wait 400ms until animation finishes
+ } else {
+ if (openAmount > 0) {
+ let state = (openAmount >= (this._optsWidthRightSide + SWIPE_MARGIN))
+ ? SlidingState.Right | SlidingState.SwipeRight
+ : SlidingState.Right;
+
+ this._setState(state);
+
+ } else if (openAmount < 0) {
+ let state = (openAmount <= (-this._optsWidthLeftSide - SWIPE_MARGIN))
+ ? SlidingState.Left | SlidingState.SwipeLeft
+ : SlidingState.Left;
+
+ this._setState(state);
+ }
+ }
+ if (openAmount === 0) {
this._timer = nativeTimeout(() => {
this._setState(SlidingState.Disabled);
this._timer = null;
- }, 400);
- this.slidingPercent = 0;
-
- } else if (openAmount > 0) {
- this._setState(SlidingState.Right);
- this.slidingPercent = openAmount / this._optsWidthRightSide;
- } else if (openAmount < 0) {
- this._setState(SlidingState.Left);
- this.slidingPercent = openAmount / this._optsWidthLeftSide;
- }
- if (!isFinal) {
- this.setClass('active-swipe-right', this.slidingPercent > SWIPE_FACTOR);
- this.setClass('active-swipe-left', this.slidingPercent < -SWIPE_FACTOR);
- } else {
- this.item.setCssStyle(CSS.transition, '');
+ }, 600);
+ this.item.setCssStyle(CSS.transform, '');
+ return;
}
-
- this.ionDrag.emit(this.slidingPercent);
- this.item.setCssStyle(CSS.transform, (didEnd ? '' : 'translate3d(' + -openAmount + 'px,0,0)'));
+
+ this.item.setCssStyle(CSS.transform, `translate3d(${-openAmount}px,0,0)`);
+ this.ionDrag.emit(this);
}
private _setState(state: SlidingState) {
- if (state !== this._state) {
- this._state = state;
- this.setClass('active-slide', state !== SlidingState.Disabled);
- this.setClass('active-options-right', state === SlidingState.Right);
- this.setClass('active-options-left', state === SlidingState.Left);
- if (state === SlidingState.Disabled || state === SlidingState.Enabled) {
- this.setClass('active-swipe-right', false);
- this.setClass('active-swipe-left', false);
- }
+ if (state === this._state) {
+ return;
}
+ this.setCssClass('active-slide', (state !== SlidingState.Disabled));
+ this.setCssClass('active-options-right', !!(state & SlidingState.Right));
+ this.setCssClass('active-options-left', !!(state & SlidingState.Left));
+ this.setCssClass('active-swipe-right', !!(state & SlidingState.SwipeRight));
+ this.setCssClass('active-swipe-left', !!(state & SlidingState.SwipeLeft));
+ this._state = state;
}
- /**
- * @private
- */
- setClass(className: string, add: boolean) {
- this._renderer.setElementClass(this._elementRef.nativeElement, className, add);
- }
- /**
- * @private
- */
- getOpenAmount(): number {
- return this._openAmount;
- }
/**
* Close the sliding item. Items can also be closed from the [List](../../list/List).
@@ -454,24 +448,37 @@ export class ItemSliding {
this._setOpenAmount(0, true);
}
+ /**
+ * @private
+ */
+ setCssClass(cssClass: string, shouldAdd: boolean) {
+ this._renderer.setElementClass(this._elementRef.nativeElement, cssClass, shouldAdd);
+ }
+
+ /**
+ * @private
+ */
+ setCssStyle(property: string, value: string) {
+ this._renderer.setElementStyle(this._elementRef.nativeElement, property, value);
+ }
}
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;
+ // 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;
}
diff --git a/src/components/item/item.ts b/src/components/item/item.ts
index 364bff05e83..5ff175b4719 100644
--- a/src/components/item/item.ts
+++ b/src/components/item/item.ts
@@ -291,11 +291,6 @@ export class Item {
private _label: Label;
private _viewLabel: boolean = true;
- /**
- * @private
- */
- @Input() index: number;
-
/**
* @private
*/
@@ -308,7 +303,6 @@ export class Item {
constructor(form: Form, private _renderer: Renderer, private _elementRef: ElementRef) {
this.id = form.nextId().toString();
- _elementRef.nativeElement['$ionComponent'] = this;
}
/**
@@ -333,20 +327,6 @@ export class Item {
}
}
- /**
- * @private
- */
- setCssClass(cssClass: string, shouldAdd: boolean) {
- this._renderer.setElementClass(this._elementRef.nativeElement, cssClass, shouldAdd);
- }
-
- /**
- * @private
- */
- setCssStyle(property: string, value: string) {
- this._renderer.setElementStyle(this._elementRef.nativeElement, property, value);
- }
-
/**
* @private
*/
@@ -406,15 +386,22 @@ export class Item {
/**
* @private
*/
- getNativeElement() {
- return this._elementRef.nativeElement;
+ setCssClass(cssClass: string, shouldAdd: boolean) {
+ this._renderer.setElementClass(this._elementRef.nativeElement, cssClass, shouldAdd);
+ }
+
+ /**
+ * @private
+ */
+ setCssStyle(property: string, value: string) {
+ this._renderer.setElementStyle(this._elementRef.nativeElement, property, value);
}
/**
* @private
*/
- height(): number {
- return this._elementRef.nativeElement.offsetHeight;
+ getNativeElement(): HTMLElement {
+ return this._elementRef.nativeElement;
}
}
diff --git a/src/components/item/test/reorder/index.ts b/src/components/item/test/reorder/index.ts
index cf5c190cb7e..6e92454c3ed 100644
--- a/src/components/item/test/reorder/index.ts
+++ b/src/components/item/test/reorder/index.ts
@@ -1,5 +1,5 @@
import {Component, ChangeDetectorRef} from '@angular/core';
-import {ionicBootstrap} from '../../../../../src';
+import {ionicBootstrap, reorderArray} from '../../../../../src';
@Component({
@@ -21,9 +21,7 @@ class E2EPage {
}
reorder(indexes: any) {
- let element = this.items[indexes.from];
- this.items.splice(indexes.from, 1);
- this.items.splice(indexes.to, 0, element);
+ this.items = reorderArray(this.items, indexes);
}
}
diff --git a/src/components/item/test/reorder/main.html b/src/components/item/test/reorder/main.html
index b8f19189630..b5fa10fb034 100644
--- a/src/components/item/test/reorder/main.html
+++ b/src/components/item/test/reorder/main.html
@@ -10,8 +10,7 @@
-
{{item}}
diff --git a/src/components/list/list.ts b/src/components/list/list.ts
index e3023c25873..06cdd3d88ad 100644
--- a/src/components/list/list.ts
+++ b/src/components/list/list.ts
@@ -4,8 +4,6 @@ import { Content } from '../content/content';
import { Ion } from '../ion';
import { isTrueProperty } from '../../util/util';
import { ItemSlidingGesture } from '../item/item-sliding-gesture';
-import { ItemReorderGesture } from '../item/item-reorder-gesture';
-import { nativeTimeout } from '../../util/dom';
/**
* The List is a widely used interface element in almost any mobile app,
@@ -25,20 +23,13 @@ import { nativeTimeout } from '../../util/dom';
*/
@Directive({
selector: 'ion-list',
- host: {
- '[class.reorder-enabled]': '_enableReorder',
- }
})
export class List extends Ion {
- private _enableReorder: boolean = false;
- private _enableSliding: boolean = false;
+ private _enableSliding: boolean = true;
+ private _containsSlidingItems: boolean = false;
private _slidingGesture: ItemSlidingGesture;
- private _reorderGesture: ItemReorderGesture;
- private _lastToIndex: number = -1;
-
- @Output() ionItemReorder: EventEmitter<{ from: number, to: number }> = new EventEmitter<{ from: number, to: number }>();
- constructor(elementRef: ElementRef, private _rendered: Renderer, private _zone: NgZone, @Optional() private _content: Content) {
+ constructor(elementRef: ElementRef, private _rendered: Renderer) {
super(elementRef);
}
@@ -47,7 +38,6 @@ export class List extends Ion {
*/
ngOnDestroy() {
this._slidingGesture && this._slidingGesture.destroy();
- this._reorderGesture && this._reorderGesture.destroy();
}
/**
@@ -70,20 +60,38 @@ export class List extends Ion {
* ```
* @param {boolean} shouldEnable whether the item-sliding should be enabled or not
*/
- enableSlidingItems(shouldEnable: boolean) {
- if (this._enableSliding === shouldEnable) {
- return;
- }
+ @Input()
+ get sliding(): boolean {
+ return this._enableSliding;
+ }
+ set sliding(val: boolean) {
+ this._enableSliding = isTrueProperty(val);
+ this._updateSlidingState();
+ }
- this._enableSliding = shouldEnable;
- if (shouldEnable) {
- console.debug('enableSlidingItems');
- nativeTimeout(() => this._slidingGesture = new ItemSlidingGesture(this));
- } else {
+
+ /**
+ * @private
+ */
+ containsSlidingItem(contains: boolean) {
+ this._containsSlidingItems = contains;
+ this._updateSlidingState();
+ }
+
+
+ private _updateSlidingState() {
+ let shouldSlide = this._enableSliding && this._containsSlidingItems;
+ if (!shouldSlide) {
this._slidingGesture && this._slidingGesture.unlisten();
+ this._slidingGesture = null;
+
+ } else if (!this._slidingGesture) {
+ console.debug('enableSlidingItems');
+ this._slidingGesture = new ItemSlidingGesture(this);
}
}
+
/**
* Close the open sliding item.
*
@@ -106,111 +114,6 @@ export class List extends Ion {
closeSlidingItems() {
this._slidingGesture && this._slidingGesture.closeOpened();
}
-
- setCssClass(classname: string, add: boolean) {
- this._rendered.setElementClass(this.getNativeElement(), classname, add);
- }
-
- reorderStart() {
- this.setCssClass('reorder-active', true);
- }
-
- /**
- * @private
- */
- reorderEmit(fromIndex: number, toIndex: number) {
- this.reorderReset();
- if (fromIndex !== toIndex) {
- this._zone.run(() => {
- this.ionItemReorder.emit({
- from: fromIndex,
- to: toIndex,
- });
- });
- }
- }
-
- /**
- * @private
- */
- scrollContent(scroll: number) {
- let scrollTop = this._content.getScrollTop() + scroll;
- if (scroll !== 0) {
- this._content.scrollTo(0, scrollTop, 0);
- }
- return scrollTop;
- }
-
- /**
- * @private
- */
- reorderReset() {
- let children = this.elementRef.nativeElement.children;
- let len = children.length;
- this.setCssClass('reorder-active', false);
- for (let i = 0; i < len; i++) {
- children[i].style.transform = '';
- }
- this._lastToIndex = -1;
- }
-
- /**
- * @private
- */
- reorderMove(fromIndex: number, toIndex: number, itemHeight: number) {
- if (this._lastToIndex === -1) {
- this._lastToIndex = fromIndex;
- }
- let lastToIndex = this._lastToIndex;
- this._lastToIndex = toIndex;
-
- // TODO: I think both loops can be merged into a single one
- // but I had no luck last time I tried
-
- /********* DOM READ ********** */
- let children = this.elementRef.nativeElement.children;
-
- /********* DOM WRITE ********* */
- if (toIndex >= lastToIndex) {
- for (var i = lastToIndex; i <= toIndex; i++) {
- if (i !== fromIndex) {
- children[i].style.transform = (i > fromIndex)
- ? `translateY(${-itemHeight}px)` : '';
- }
- }
- }
-
- if (toIndex <= lastToIndex) {
- for (var i = toIndex; i <= lastToIndex; i++) {
- if (i !== fromIndex) {
- children[i].style.transform = (i < fromIndex)
- ? `translateY(${itemHeight}px)` : '';
- }
- }
- }
- }
-
- @Input()
- get reorder(): boolean {
- return this._enableReorder;
- }
-
- set reorder(val: boolean) {
- let enabled = isTrueProperty(val);
- if (this._enableReorder === enabled) {
- return;
- }
-
- this._enableReorder = enabled;
- if (enabled) {
- console.debug('enableReorderItems');
- nativeTimeout(() => this._reorderGesture = new ItemReorderGesture(this));
-
- } else {
- this._reorderGesture && this._reorderGesture.destroy();
- }
- }
-
}
diff --git a/src/config/directives.ts b/src/config/directives.ts
index fe6e7d632c4..28011b6f406 100644
--- a/src/config/directives.ts
+++ b/src/config/directives.ts
@@ -19,6 +19,7 @@ import { Tabs } from '../components/tabs/tabs';
import { Tab } from '../components/tabs/tab';
import { List, ListHeader } from '../components/list/list';
import { Item, ItemContent } from '../components/item/item';
+import { Reorder } from '../components/item/item-reorder';
import { ItemSliding, ItemOptions } from '../components/item/item-sliding';
import { VirtualScroll } from '../components/virtual-scroll/virtual-scroll';
import { VirtualItem, VirtualHeader, VirtualFooter } from '../components/virtual-scroll/virtual-item';
@@ -145,6 +146,7 @@ export const IONIC_DIRECTIVES: any[] = [
ItemContent,
ItemSliding,
ItemOptions,
+ Reorder,
VirtualScroll,
VirtualItem,
VirtualHeader,
diff --git a/src/index.ts b/src/index.ts
index 5b044f34773..756a083039b 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -19,6 +19,7 @@ export * from './util/click-block';
export * from './util/events';
export * from './util/keyboard';
export * from './util/form';
+export { reorderArray } from './util/util';
export * from './animations/animation';
export * from './transitions/page-transition';
diff --git a/src/util/util.ts b/src/util/util.ts
index 5660d3aa9f6..fc2dbddb4fc 100644
--- a/src/util/util.ts
+++ b/src/util/util.ts
@@ -175,3 +175,11 @@ export function getQuerystring(url: string): any {
}
return queryParams;
}
+
+
+export function reorderArray(array: any[], indexes: {from: number, to: number}): any[] {
+ let element = array[indexes.from];
+ array.splice(indexes.from, 1);
+ array.splice(indexes.to, 0, element);
+ return array;
+}