fix(reorder): drop reorderAnchor attribute

This commit is contained in:
Manuel Mtz-Almeida
2017-09-20 19:55:15 +02:00
committed by Manu Mtz.-Almeida
parent d3e79fb462
commit 54412a2a8c
9 changed files with 194 additions and 140 deletions

View File

@ -1,4 +1,4 @@
import { render, flush } from '@stencil/core/testing';
import { flush, render } from '@stencil/core/testing';
import { Button } from '../button';

View File

@ -167,6 +167,9 @@ export class Gesture {
this.positions.push(detail.currentX, detail.currentY, timeStamp);
if (this.pan) {
this.hasStartedPan = true;
if (this.threshold === 0) {
return this.tryToCapturePan();
}
this.pan.start(detail.startX, detail.startY);
}
return true;

View File

@ -97,10 +97,3 @@ ion-input.item {
background: transparent;
cursor: pointer;
}
[reorderAnchor] {
display: none;
pointer-events: all !important;
touch-action: manipulation;
}

View File

@ -1,11 +1,11 @@
import { Component, Element, Prop, PropDidChange, State } from '@stencil/core';
import { GestureDetail } from '../../index';
import { reorderArray } from '../../utils/helpers';
import { clamp, reorderArray } from '../../utils/helpers';
import { CSS_PROP } from '../animation-controller/constants';
// const AUTO_SCROLL_MARGIN = 60;
// const SCROLL_JUMP = 10;
const ITEM_REORDER_ACTIVE = 'reorder-active';
const AUTO_SCROLL_MARGIN = 60;
const SCROLL_JUMP = 10;
const ITEM_REORDER_SELECTED = 'reorder-selected';
export class ReorderIndexes {
@ -147,10 +147,16 @@ export class ReorderGroup {
private selectedItemEle: HTMLElement = null;
private selectedItemHeight: number;
private lastToIndex: number;
private lastYcoord: number;
private topOfList: number;
private cachedHeights: number[] = [];
private containerEle: HTMLElement;
private scrollEle: HTMLElement;
private scrollTop: number;
private scrollBottom: number;
private scrollInitial: number;
private containerTop: number;
private containerBottom: number;
@State() _enabled: boolean = false;
@State() _iconVisible: boolean = false;
@ -178,6 +184,7 @@ export class ReorderGroup {
ionViewDidLoad() {
this.containerEle = this.ele.querySelector('ion-gesture') as HTMLElement;
this.scrollEle = this.ele.closest('ion-scroll') as HTMLElement;
}
ionViewDidUnload() {
@ -189,7 +196,7 @@ export class ReorderGroup {
return false;
}
const target = ev.event.target as HTMLElement;
const reorderEle = target.closest('[reorderAnchor]') as HTMLElement;
const reorderEle = target.closest('ion-reorder') as HTMLElement;
if (!reorderEle) {
return false;
}
@ -213,20 +220,33 @@ export class ReorderGroup {
}
let sum = 0;
for (let i = 0, ilen = children.length; i < ilen; i++) {
for (var i = 0, ilen = children.length; i < ilen; i++) {
var child = children[i];
sum += child.offsetHeight;
heights.push(sum);
child.$ionIndex = i;
}
this.topOfList = item.getBoundingClientRect().top;
this._actived = true;
this.lastYcoord = -100;
const box = this.containerEle.getBoundingClientRect();
this.containerTop = box.top;
this.containerBottom = box.bottom;
if (this.scrollEle) {
var scrollBox = this.scrollEle.getBoundingClientRect();
this.scrollInitial = this.scrollEle.scrollTop;
this.scrollTop = scrollBox.top + AUTO_SCROLL_MARGIN;
this.scrollBottom = scrollBox.bottom - AUTO_SCROLL_MARGIN;
} else {
this.scrollInitial = 0;
this.scrollTop = 0;
this.scrollBottom = 0;
}
this.lastToIndex = indexForItem(item);
this.selectedItemHeight = item.offsetHeight;
this._actived = true;
item.classList.add(ITEM_REORDER_ACTIVE);
item.classList.add(ITEM_REORDER_SELECTED);
}
private onDragMove(ev: GestureDetail) {
@ -234,26 +254,24 @@ export class ReorderGroup {
if (!selectedItem) {
return;
}
// ev.event.preventDefault();
// Scroll if we reach the scroll margins
const scroll = this.autoscroll(ev.currentY);
// // Get coordinate
const posY = ev.deltaY;
// Scroll if we reach the scroll margins
// const 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 toIndex = this.itemIndexForDelta(posY);
if (toIndex !== undefined && (toIndex !== this.lastToIndex)) {
let fromIndex = indexForItem(selectedItem);
this.lastToIndex = toIndex;
this.lastYcoord = posY;
this._reorderMove(fromIndex, toIndex, this.selectedItemHeight);
}
const top = this.containerTop - scroll;
const bottom = this.containerBottom - scroll;
const currentY = clamp(top, ev.currentY, bottom);
const deltaY = scroll + currentY - ev.startY;
const normalizedY = currentY - top;
const toIndex = this.itemIndexForTop(normalizedY);
if (toIndex !== undefined && (toIndex !== this.lastToIndex)) {
let fromIndex = indexForItem(selectedItem);
this.lastToIndex = toIndex;
this._reorderMove(fromIndex, toIndex);
}
// Update selected item position
(selectedItem.style as any)[CSS_PROP.transformProp] = `translateY(${posY}px)`;
(selectedItem.style as any)[CSS_PROP.transformProp] = `translateY(${deltaY}px)`;
}
private onDragEnd() {
@ -285,7 +303,7 @@ export class ReorderGroup {
const reorderInactive = () => {
this.selectedItemEle.style.transition = '';
this.selectedItemEle.classList.remove(ITEM_REORDER_ACTIVE);
this.selectedItemEle.classList.remove(ITEM_REORDER_SELECTED);
this.selectedItemEle = null;
};
if (toIndex === fromIndex) {
@ -296,24 +314,29 @@ export class ReorderGroup {
}
}
private itemIndexForDelta(deltaY: number): number {
private itemIndexForTop(deltaY: number): number {
const heights = this.cachedHeights;
let sum = deltaY + this.topOfList - (this.selectedItemHeight / 2);
for (var i = 0; i < heights.length; i++) {
if (heights[i] > sum) {
return i;
let i = 0;
// TODO: since heights is a sorted array of integers, we can do
// speed up the search using binary search. Remember that linear-search is still
// faster than binary-search for small arrays (<64) due CPU branch misprediction.
for (i = 0; i < heights.length; i++) {
if (heights[i] > deltaY) {
break;
}
}
return null;
return i;
}
private _reorderMove(fromIndex: number, toIndex: number, itemHeight: number) {
/********* DOM WRITE ********* */
/********* DOM WRITE ********* */
private _reorderMove(fromIndex: number, toIndex: number) {
const itemHeight = this.selectedItemHeight;
const children = this.containerEle.children;
const transform = CSS_PROP.transformProp;
for (var i = 0; i < children.length; i++) {
const style = (children[i] as any).style;
let value = '';
var style = (children[i] as any).style;
var value = '';
if (i > fromIndex && i <= toIndex) {
value = `translateY(${-itemHeight}px)`;
} else if (i < fromIndex && i >= toIndex) {
@ -323,6 +346,23 @@ export class ReorderGroup {
}
}
private autoscroll(posY: number): number {
if (!this.scrollEle) {
return 0;
}
let amount = 0;
if (posY < this.scrollTop) {
amount = -SCROLL_JUMP;
} else if (posY > this.scrollBottom) {
amount = SCROLL_JUMP;
}
if (amount !== 0) {
this.scrollEle.scrollBy(0, amount);
}
return this.scrollEle.scrollTop - this.scrollInitial;
}
hostData() {
return {
class: {

View File

@ -4,8 +4,7 @@ $reorder-initial-transform: 160% !default;
// Reorder group general
// --------------------------------------------------
.reorder-enabled [reorderAnchor] {
ion-reorder-group > ion-gesture {
display: block;
}
@ -15,50 +14,68 @@ $reorder-initial-transform: 160% !default;
will-change: transform;
}
.reorder-list-active ion-gesture *:not([reorderAnchor]) {
pointer-events: none;
}
.reorder-active {
.reorder-selected {
position: relative;
z-index: 100;
box-shadow: 0 0 10px rgba(0, 0, 0, .4);
opacity: .8;
transition: none !important;
pointer-events: none;
}
// Reorder icon
// --------------------------------------------------
ion-reorder {
@include transform(translate3d($reorder-initial-transform, 0, 0));
ion-reorder.no-hide {
display: block;
visibility: normal;
}
ion-reorder:not(.no-hide) {
display: none;
pointer-events: all !important;
touch-action: manipulation;
}
.reorder-enabled ion-reorder {
display: block;
cursor: grab;
cursor: -webkit-grab;
}
.reorder-selected,
.reorder-selected ion-reorder {
cursor: grabbing;
cursor: -webkit-grabbing;
}
ion-reorder[slot] {
line-height: 0;
margin-top: auto !important;
margin-bottom: auto !important;
}
font-size: 1.7em;
ion-reorder[slot="start"] {
order: -1;
}
.reorder-icon {
@include transform(translate3d($reorder-initial-transform, 0, 0));
font-size: 1.3em;
opacity: .25;
line-height: 0;
transition: transform 140ms ease-in;
}
ion-reorder ion-icon {
pointer-events: none;
}
ion-reorder[slot="start"] {
ion-reorder[slot="start"] .reorder-icon {
@include transform(translate3d(-$reorder-initial-transform, 0, 0));
order: -1;
}
.reorder-visible ion-reorder {
.reorder-visible ion-reorder .reorder-icon {
@include transform(translate3d(0, 0, 0));
}

View File

@ -1,20 +1,26 @@
import { Component } from '@stencil/core';
import { Component, Element, State } from '@stencil/core';
@Component({
tag: 'ion-reorder',
})
export class ItemReorder {
hostData()  {
return {
attrs: {
'reorderAnchor': '',
}
};
@State() hasContent: boolean = null;
@Element() ele: HTMLElement;
ionViewDidLoad() {
this.hasContent = this.ele.childElementCount > 0;
}
render() {
return <ion-icon name='reorder'></ion-icon>;
// TODO: https://github.com/ionic-team/stencil/issues/171
if (this.hasContent === true) {
return <slot></slot>;
} else if (this.hasContent === false) {
return <ion-icon class='reorder-icon' name='reorder'></ion-icon>;
} else {
return undefined;
}
}
}
}

View File

@ -22,54 +22,49 @@
<ion-content>
<ion-list>
<ion-reorder-group id="reorder">
<ion-item>
Item 1
Item 1 (default ion-reorder)
<ion-reorder slot="end"></ion-reorder>
</ion-item>
<ion-item>Item 2
<ion-reorder slot="end"></ion-reorder>
</ion-item>
<ion-item>Item 3
<ion-reorder slot="start"></ion-reorder>
</ion-item>
<ion-item>Item 4
<ion-icon reorderAnchor name="pizza" slot="end"></ion-icon>
<ion-item>
Item 2 (default ion-reorder)
<ion-reorder slot="end"></ion-reorder>
</ion-item>
<ion-item>Item 5
<ion-icon reorderAnchor name="pizza" slot="end"></ion-icon>
<ion-item>
Item 3 (default ion-reorder slot="start")
<ion-reorder slot="start"></ion-reorder>
</ion-item>
<ion-item>Item 6
<ion-icon reorderAnchor name="pizza" slot="end"></ion-icon>
<ion-item>
Item 4 (custom ion-reorder)
<ion-reorder slot="end"><ion-icon name="pizza"></ion-reorder>
</ion-item>
<ion-item>Item 7
<ion-icon reorderAnchor name="pizza" slot="end"></ion-icon>
<ion-item>
Item 5 (custom ion-reorder)
<ion-reorder slot="end"><ion-icon name="pizza"></ion-reorder>
</ion-item>
<ion-item>Item 8
<ion-icon reorderAnchor name="pizza" slot="end"></ion-icon>
<ion-item>
Item 6 (custom ion-reorder slot="start")
<ion-reorder slot="start"><ion-icon name="pizza"></ion-reorder>
</ion-item>
<ion-item>Item 9
<ion-icon reorderAnchor name="pizza" slot="end"></ion-icon>
</ion-item>
<ion-item>Item 10
<ion-icon reorderAnchor name="pizza" slot="end"></ion-icon>
<ion-reorder class="no-hide">
<ion-item>Item 7 (the whole item can be dragged)</ion-item>
</ion-reorder>
</ion-item>
<ion-item>Item 11
<ion-icon reorderAnchor name="pizza" slot="end"></ion-icon>
<ion-reorder class="no-hide">
<ion-item>Item 8 (the whole item can be dragged)</ion-item>
</ion-reorder>
<ion-reorder class="no-hide">
<ion-item>Item 9 (the whole item can be dragged)</ion-item>
</ion-reorder>
</ion-item>
<ion-item>Item 12
<ion-icon reorderAnchor name="pizza" slot="end"></ion-icon>
</ion-item>
<ion-item>Item 13
<ion-icon reorderAnchor name="pizza" slot="end"></ion-icon>
</ion-item>
</ion-reorder-group>
</ion-list>
</ion-content>