diff --git a/src/components/item/item-reorder-gesture.ts b/src/components/item/item-reorder-gesture.ts index 13d534ea12..c017bf3af0 100644 --- a/src/components/item/item-reorder-gesture.ts +++ b/src/components/item/item-reorder-gesture.ts @@ -13,12 +13,15 @@ const ITEM_REORDER_ACTIVE = 'reorder-active'; */ export class ItemReorderGesture { private selectedItem: Item = null; + private selectedItemEle: HTMLElement = null; + private selectedItemHeight: number; + private offset: Coordinates; private lastToIndex: number; private lastYcoord: number; + private lastScrollPosition: number; private emptyZone: boolean; - private itemHeight: number; private windowHeight: number; private events: UIEventManager = new UIEventManager(false); @@ -26,9 +29,9 @@ export class ItemReorderGesture { constructor(public list: List) { let element = this.list.getNativeElement(); this.events.pointerEvents(element, - (ev: any) => this.onDragStart(ev), - (ev: any) => this.onDragMove(ev), - (ev: any) => this.onDragEnd(ev)); + this.onDragStart.bind(this), + this.onDragMove.bind(this), + this.onDragEnd.bind(this)); } private onDragStart(ev: any): boolean { @@ -37,67 +40,69 @@ export class ItemReorderGesture { return false; } - let item = itemEle['$ionComponent']; + let item = itemEle['$ionComponent']; if (!item) { console.error('item does not contain ion component'); return false; } ev.preventDefault(); - // Preparing state - this.offset = pointerCoord(ev); - this.offset.y += this.list.scrollContent(0); + // Preparing state this.selectedItem = item; - this.itemHeight = item.height(); + this.selectedItemEle = item.getNativeElement(); + this.selectedItemHeight = item.height(); this.lastToIndex = item.index; + this.lastYcoord = -100; + this.windowHeight = window.innerHeight - AUTO_SCROLL_MARGIN; + this.lastScrollPosition = this.list.scrollContent(0); + + this.offset = pointerCoord(ev); + this.offset.y += this.lastScrollPosition; + item.setCssClass(ITEM_REORDER_ACTIVE, true); + this.list.reorderStart(); return true; } private onDragMove(ev: any) { - if (!this.selectedItem) { + let selectedItem = this.selectedItemEle; + if (!selectedItem) { return; } ev.preventDefault(); // Get coordinate - var coord = pointerCoord(ev); + let coord = pointerCoord(ev); + let posY = coord.y; // Scroll if we reach the scroll margins - let scrollPosition = this.scroll(coord); - + let scrollPosition = this.scroll(posY); + + // Only perform hit test if we moved at least 30px from previous position + 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; + this.lastToIndex = toIndex; + this.lastYcoord = posY; + this.emptyZone = false; + this.list.reorderMove(fromIndex, toIndex, this.selectedItemHeight); + } + } else { + this.emptyZone = true; + } + } + // Update selected item position - let ydiff = Math.round(coord.y - this.offset.y + scrollPosition); - this.selectedItem.setCssStyle(CSS.transform, `translateY(${ydiff}px)`); - - // Only perform hit test if we moved at least 30px from previous position - if (Math.abs(coord.y - this.lastYcoord) < 30) { - return; - } - - // Hit test - let overItem = this.itemForCoord(coord); - if (!overItem) { - this.emptyZone = true; - return; - } - - // Move surrounding items if needed - let toIndex = overItem.index; - if (toIndex !== this.lastToIndex || this.emptyZone) { - let fromIndex = this.selectedItem.index; - this.lastToIndex = overItem.index; - this.lastYcoord = coord.y; - this.emptyZone = false; - nativeRaf(() => { - this.list.reorderMove(fromIndex, toIndex, this.itemHeight); - }); - } + let ydiff = Math.round(posY - this.offset.y + scrollPosition); + selectedItem.style[CSS.transform] = `translateY(${ydiff}px)`; } - private onDragEnd(ev: any) { - if (!this.selectedItem) { + private onDragEnd() { + if (!this.selectedItemEle) { return; } @@ -106,17 +111,17 @@ export class ItemReorderGesture { let fromIndex = this.selectedItem.index; this.selectedItem.setCssClass(ITEM_REORDER_ACTIVE, false); this.selectedItem = null; + this.selectedItemEle = null; this.list.reorderEmit(fromIndex, toIndex); }); } private itemForCoord(coord: Coordinates): Item { - let element = document.elementFromPoint(this.offset.x - 100, coord.y); + let element = document.elementFromPoint(this.offset.x - 100, coord.y); if (!element) { return null; } - element = closest(element, 'ion-item', true); - if (!element) { + if (element.nodeName !== 'ION-ITEM') { return null; } let item = (element)['$ionComponent']; @@ -127,20 +132,22 @@ export class ItemReorderGesture { return item; } - private scroll(coord: Coordinates): number { - let scrollDiff = 0; - if (coord.y < AUTO_SCROLL_MARGIN) { - scrollDiff = -SCROLL_JUMP; - } else if (coord.y > this.windowHeight) { - scrollDiff = SCROLL_JUMP; + private scroll(posY: number): number { + if (posY < AUTO_SCROLL_MARGIN) { + this.lastScrollPosition = this.list.scrollContent(-SCROLL_JUMP); + } else if (posY > this.windowHeight) { + this.lastScrollPosition = this.list.scrollContent(SCROLL_JUMP); } - return this.list.scrollContent(scrollDiff); + return this.lastScrollPosition; } + + /** * @private */ destroy() { + this.onDragEnd(); this.events.unlistenAll(); this.events = null; this.list = null; diff --git a/src/components/item/item-reorder.scss b/src/components/item/item-reorder.scss index 360aa19865..f278117a80 100644 --- a/src/components/item/item-reorder.scss +++ b/src/components/item/item-reorder.scss @@ -24,7 +24,7 @@ ion-reorder { .reorder-enabled { - ion-item { + .item { will-change: transform; } @@ -33,7 +33,14 @@ ion-reorder { } } -ion-item.reorder-active { +.reorder-active { + .item-inner { + pointer-events: none; + } +} + + +.item.reorder-active { z-index: 4; box-shadow: 0 0 10px rgba(0, 0, 0, .5); @@ -41,8 +48,4 @@ ion-item.reorder-active { transition: none; pointer-events: none; - - ion-reorder { - pointer-events: none; - } } diff --git a/src/components/item/item.ts b/src/components/item/item.ts index 49d9aaf510..364bff05e8 100644 --- a/src/components/item/item.ts +++ b/src/components/item/item.ts @@ -403,6 +403,13 @@ export class Item { }); } + /** + * @private + */ + getNativeElement() { + return this._elementRef.nativeElement; + } + /** * @private */ diff --git a/src/components/list/list.ts b/src/components/list/list.ts index d680326db1..e3023c2587 100644 --- a/src/components/list/list.ts +++ b/src/components/list/list.ts @@ -38,7 +38,7 @@ export class List extends Ion { @Output() ionItemReorder: EventEmitter<{ from: number, to: number }> = new EventEmitter<{ from: number, to: number }>(); - constructor(elementRef: ElementRef, private _zone: NgZone, @Optional() private _content: Content) { + constructor(elementRef: ElementRef, private _rendered: Renderer, private _zone: NgZone, @Optional() private _content: Content) { super(elementRef); } @@ -107,6 +107,14 @@ export class List extends Ion { this._slidingGesture && this._slidingGesture.closeOpened(); } + setCssClass(classname: string, add: boolean) { + this._rendered.setElementClass(this.getNativeElement(), classname, add); + } + + reorderStart() { + this.setCssClass('reorder-active', true); + } + /** * @private */ @@ -139,6 +147,7 @@ export class List extends Ion { 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 = ''; } @@ -155,7 +164,13 @@ export class List extends Ion { 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) {