mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-23 05:58:26 +08:00
feat(reorder): adds ion-reorder-group to core
This commit is contained in:
@ -38,7 +38,7 @@ export class Gesture {
|
||||
@Prop() gestureName: string = '';
|
||||
@Prop() gesturePriority: number = 0;
|
||||
@Prop() maxAngle: number = 40;
|
||||
@Prop() threshold: number = 20;
|
||||
@Prop() threshold: number = 10;
|
||||
@Prop() type: string = 'pan';
|
||||
|
||||
@Prop() canStart: GestureCallback;
|
||||
@ -209,7 +209,7 @@ export class Gesture {
|
||||
const detail = this.detail;
|
||||
this.calcGestureData(ev);
|
||||
if (this.pan.detect(detail.currentX, detail.currentY)) {
|
||||
if (this.pan.isGesture() !== 0) {
|
||||
if (this.pan.isGesture()) {
|
||||
if (!this.tryToCapturePan()) {
|
||||
this.abortGesture();
|
||||
}
|
||||
@ -463,6 +463,7 @@ export interface GestureDetail {
|
||||
deltaX?: number;
|
||||
deltaY?: number;
|
||||
timeStamp?: number;
|
||||
data?: any;
|
||||
}
|
||||
|
||||
|
||||
|
@ -7,13 +7,14 @@ export class PanRecognizer {
|
||||
private dirty: boolean = false;
|
||||
private threshold: number;
|
||||
private maxCosine: number;
|
||||
private isDirX: boolean;
|
||||
|
||||
private angle = 0;
|
||||
private isPan = 0;
|
||||
|
||||
|
||||
constructor(private direction: string, threshold: number, maxAngle: number) {
|
||||
constructor(direction: string, threshold: number, maxAngle: number) {
|
||||
const radians = maxAngle * (Math.PI / 180);
|
||||
this.isDirX = direction === 'x';
|
||||
this.maxCosine = Math.cos(radians);
|
||||
this.threshold = threshold * threshold;
|
||||
}
|
||||
@ -35,13 +36,11 @@ export class PanRecognizer {
|
||||
const deltaY = (y - this.startY);
|
||||
const distance = deltaX * deltaX + deltaY * deltaY;
|
||||
|
||||
if (distance >= this.threshold) {
|
||||
var angle = Math.atan2(deltaY, deltaX);
|
||||
var cosine = (this.direction === 'y')
|
||||
? Math.sin(angle)
|
||||
: Math.cos(angle);
|
||||
|
||||
this.angle = angle;
|
||||
if (distance < this.threshold) {
|
||||
return false;
|
||||
}
|
||||
const hypotenuse = Math.sqrt(distance);
|
||||
const cosine = ((this.isDirX) ? deltaX : deltaY) / hypotenuse;
|
||||
|
||||
if (cosine > this.maxCosine) {
|
||||
this.isPan = 1;
|
||||
@ -57,10 +56,11 @@ export class PanRecognizer {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
isGesture(): boolean {
|
||||
return this.isPan !== 0;
|
||||
}
|
||||
|
||||
isGesture(): number {
|
||||
getDirection(): number {
|
||||
return this.isPan;
|
||||
}
|
||||
}
|
||||
|
@ -97,3 +97,10 @@ ion-input.item {
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
[reorderAnchor] {
|
||||
display: none;
|
||||
|
||||
pointer-events: all !important;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
@ -20,9 +20,6 @@ export class Item {
|
||||
private itemStyles: { [key: string]: CssClassMap } = Object.create(null);
|
||||
private label: any;
|
||||
|
||||
// TODO get reorder from a parent list/group
|
||||
@State() reorder: boolean = false;
|
||||
|
||||
@Element() private el: HTMLElement;
|
||||
|
||||
@Prop() mode: string;
|
||||
@ -131,10 +128,6 @@ export class Item {
|
||||
<slot></slot>
|
||||
</div>
|
||||
<slot name='end'></slot>
|
||||
{ this.reorder
|
||||
? <ion-reorder></ion-reorder>
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
<div class='button-effect'></div>
|
||||
</TagType>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, Method, State } from '@stencil/core';
|
||||
import { Component, Method, Prop, State } from '@stencil/core';
|
||||
|
||||
import { ItemSliding } from '../item-sliding/item-sliding';
|
||||
|
||||
@ -15,9 +15,18 @@ import { ItemSliding } from '../item-sliding/item-sliding';
|
||||
}
|
||||
})
|
||||
export class List {
|
||||
|
||||
@State() openContainer: ItemSliding;
|
||||
@Prop() radioGroup: boolean;
|
||||
|
||||
render() {
|
||||
if (this.radioGroup) {
|
||||
return (
|
||||
<ion-radio-group>
|
||||
<slot></slot>
|
||||
</ion-radio-group>
|
||||
);
|
||||
}
|
||||
return <slot></slot>;
|
||||
}
|
||||
|
||||
|
383
packages/core/src/components/reorder/reorder-group.tsx
Normal file
383
packages/core/src/components/reorder/reorder-group.tsx
Normal file
@ -0,0 +1,383 @@
|
||||
import { Component, Element, Prop, PropDidChange, State } from '@stencil/core';
|
||||
import { GestureDetail } from '../../index';
|
||||
import { 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';
|
||||
|
||||
|
||||
export class ReorderIndexes {
|
||||
constructor(public from: number, public to: number) {}
|
||||
|
||||
applyTo(array: any) {
|
||||
reorderArray(array, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @name ReorderGroup
|
||||
* @description
|
||||
* Item reorder adds the ability to change an item's order in a group.
|
||||
* It can be used within an `ion-list` or `ion-item-group` to provide a
|
||||
* visual drag and drop interface.
|
||||
*
|
||||
* ## Grouping Items
|
||||
*
|
||||
* All reorderable items must be grouped in the same element. If an item
|
||||
* should not be reordered, it shouldn't be included in this group. For
|
||||
* example, the following code works because the items are grouped in the
|
||||
* `<ion-list>`:
|
||||
*
|
||||
* ```html
|
||||
* <ion-list reorder="true">
|
||||
* <ion-item *ngFor="let item of items">{% raw %}{{ item }}{% endraw %}</ion-item>
|
||||
* </ion-list>
|
||||
* ```
|
||||
*
|
||||
* However, the below list includes a header that shouldn't be reordered:
|
||||
*
|
||||
* ```html
|
||||
* <ion-list reorder="true">
|
||||
* <ion-list-header>Header</ion-list-header>
|
||||
* <ion-item *ngFor="let item of items">{% raw %}{{ item }}{% endraw %}</ion-item>
|
||||
* </ion-list>
|
||||
* ```
|
||||
*
|
||||
* In order to mix different sets of items, `ion-item-group` should be used to
|
||||
* group the reorderable items:
|
||||
*
|
||||
* ```html
|
||||
* <ion-list>
|
||||
* <ion-list-header>Header</ion-list-header>
|
||||
* <ion-item-group reorder="true">
|
||||
* <ion-item *ngFor="let item of items">{% raw %}{{ item }}{% endraw %}</ion-item>
|
||||
* </ion-item-group>
|
||||
* </ion-list>
|
||||
* ```
|
||||
*
|
||||
* It's important to note that in this example, the `[reorder]` directive is applied to
|
||||
* the `<ion-item-group>` instead of the `<ion-list>`. This way makes it possible to
|
||||
* mix items that should and shouldn't be reordered.
|
||||
*
|
||||
*
|
||||
* ## Implementing the Reorder Function
|
||||
*
|
||||
* When the item is dragged and dropped into the new position, the `(ionItemReorder)` event is
|
||||
* emitted. This event provides the initial index (from) and the new index (to) of the reordered
|
||||
* item. For example, if the first item is dragged to the fifth position, the event will emit
|
||||
* `{from: 0, to: 4}`. Note that the index starts at zero.
|
||||
*
|
||||
* A function should be called when the event is emitted that handles the reordering of the items.
|
||||
* See [usage](#usage) below for some examples.
|
||||
*
|
||||
*
|
||||
* @usage
|
||||
*
|
||||
* ```html
|
||||
* <ion-list>
|
||||
* <ion-list-header>Header</ion-list-header>
|
||||
* <ion-item-group reorder="true" (ionItemReorder)="reorderItems($event)">
|
||||
* <ion-item *ngFor="let item of items">{% raw %}{{ item }}{% endraw %}</ion-item>
|
||||
* </ion-item-group>
|
||||
* </ion-list>
|
||||
* ```
|
||||
*
|
||||
* ```ts
|
||||
* class MyComponent {
|
||||
* items = [];
|
||||
*
|
||||
* constructor() {
|
||||
* for (let x = 0; x < 5; x++) {
|
||||
* this.items.push(x);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* reorderItems(indexes) {
|
||||
* let element = this.items[indexes.from];
|
||||
* this.items.splice(indexes.from, 1);
|
||||
* this.items.splice(indexes.to, 0, element);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Ionic also provides a helper function called `reorderArray` to
|
||||
* reorder the array of items. This can be used instead:
|
||||
*
|
||||
* ```ts
|
||||
* import { reorderArray } from 'ionic-angular';
|
||||
*
|
||||
* class MyComponent {
|
||||
* items = [];
|
||||
*
|
||||
* constructor() {
|
||||
* for (let x = 0; x < 5; x++) {
|
||||
* this.items.push(x);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* reorderItems(indexes) {
|
||||
* this.items = reorderArray(this.items, indexes);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* Alternatevely you can execute helper function inside template:
|
||||
*
|
||||
* ```html
|
||||
* <ion-list>
|
||||
* <ion-list-header>Header</ion-list-header>
|
||||
* <ion-item-group reorder="true" (ionItemReorder)="$event.applyTo(items)">
|
||||
* <ion-item *ngFor="let item of items">{% raw %}{{ item }}{% endraw %}</ion-item>
|
||||
* </ion-item-group>
|
||||
* </ion-list>
|
||||
* ```
|
||||
*
|
||||
* @demo /docs/demos/src/item-reorder/
|
||||
* @see {@link /docs/components#lists List Component Docs}
|
||||
* @see {@link ../../list/List List API Docs}
|
||||
* @see {@link ../Item Item API Docs}
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-reorder-group',
|
||||
styleUrl: 'reorder.scss'
|
||||
})
|
||||
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;
|
||||
|
||||
@State() _enabled: boolean = false;
|
||||
@State() _iconVisible: boolean = false;
|
||||
@State() _actived: boolean = false;
|
||||
|
||||
@Element() ele: HTMLElement;
|
||||
|
||||
@Prop() enabled: boolean = false;
|
||||
|
||||
/**
|
||||
* @input {string} Which side of the view the ion-reorder should be placed. Default `"end"`.
|
||||
*/
|
||||
@Prop() side: string;
|
||||
|
||||
@PropDidChange('enabled')
|
||||
enabledChanged(enabled: boolean) {
|
||||
if (enabled) {
|
||||
this._enabled = true;
|
||||
Context.dom.raf(() => {
|
||||
this._iconVisible = true;
|
||||
});
|
||||
} else {
|
||||
this._iconVisible = false;
|
||||
setTimeout(() => this._enabled = false, 400);
|
||||
}
|
||||
}
|
||||
|
||||
ionViewDidLoad() {
|
||||
this.containerEle = this.ele.querySelector('ion-gesture') as HTMLElement;
|
||||
}
|
||||
|
||||
ionViewDidUnload() {
|
||||
this.onDragEnd();
|
||||
}
|
||||
|
||||
private canStart(ev: GestureDetail): boolean {
|
||||
if (this.selectedItemEle) {
|
||||
return false;
|
||||
}
|
||||
const target = ev.event.target as HTMLElement;
|
||||
const reorderEle = target.closest('[reorderAnchor]') as HTMLElement;
|
||||
if (!reorderEle) {
|
||||
return false;
|
||||
}
|
||||
const item = findReorderItem(reorderEle, this.containerEle);
|
||||
if (!item) {
|
||||
console.error('reorder node not found');
|
||||
return false;
|
||||
}
|
||||
ev.data = item;
|
||||
return true;
|
||||
}
|
||||
|
||||
private onDragStart(ev: GestureDetail) {
|
||||
const item = this.selectedItemEle = ev.data;
|
||||
const heights = this.cachedHeights;
|
||||
heights.length = 0;
|
||||
const ele = this.containerEle;
|
||||
const children: any = ele.children;
|
||||
if (!children || children.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sum = 0;
|
||||
for (let 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;
|
||||
this.lastToIndex = indexForItem(item);
|
||||
this.selectedItemHeight = item.offsetHeight;
|
||||
|
||||
item.classList.add(ITEM_REORDER_ACTIVE);
|
||||
}
|
||||
|
||||
private onDragMove(ev: GestureDetail) {
|
||||
const selectedItem = this.selectedItemEle;
|
||||
if (!selectedItem) {
|
||||
return;
|
||||
}
|
||||
// ev.event.preventDefault();
|
||||
|
||||
// // 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);
|
||||
}
|
||||
}
|
||||
|
||||
// Update selected item position
|
||||
(selectedItem.style as any)[CSS_PROP.transformProp] = `translateY(${posY}px)`;
|
||||
}
|
||||
|
||||
private onDragEnd() {
|
||||
this._actived = false;
|
||||
const selectedItem = this.selectedItemEle;
|
||||
if (!selectedItem) {
|
||||
return;
|
||||
}
|
||||
// if (ev.event) {
|
||||
// ev.event.preventDefault();
|
||||
// ev.event.stopPropagation();
|
||||
// }
|
||||
|
||||
const toIndex = this.lastToIndex;
|
||||
const fromIndex = indexForItem(selectedItem);
|
||||
|
||||
const ref = (fromIndex < toIndex)
|
||||
? this.containerEle.children[toIndex + 1]
|
||||
: this.containerEle.children[toIndex];
|
||||
|
||||
this.containerEle.insertBefore(this.selectedItemEle, ref);
|
||||
|
||||
const children = this.containerEle.children;
|
||||
const len = children.length;
|
||||
const transform = CSS_PROP.transformProp;
|
||||
for (let i = 0; i < len; i++) {
|
||||
(children[i] as any).style[transform] = '';
|
||||
}
|
||||
|
||||
const reorderInactive = () => {
|
||||
this.selectedItemEle.style.transition = '';
|
||||
this.selectedItemEle.classList.remove(ITEM_REORDER_ACTIVE);
|
||||
this.selectedItemEle = null;
|
||||
};
|
||||
if (toIndex === fromIndex) {
|
||||
selectedItem.style.transition = 'transform 200ms ease-in-out';
|
||||
setTimeout(reorderInactive, 200);
|
||||
} else {
|
||||
reorderInactive();
|
||||
}
|
||||
}
|
||||
|
||||
private itemIndexForDelta(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;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private _reorderMove(fromIndex: number, toIndex: number, itemHeight: number) {
|
||||
/********* DOM WRITE ********* */
|
||||
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 = '';
|
||||
if (i > fromIndex && i <= toIndex) {
|
||||
value = `translateY(${-itemHeight}px)`;
|
||||
} else if (i < fromIndex && i >= toIndex) {
|
||||
value = `translateY(${itemHeight}px)`;
|
||||
}
|
||||
style[transform] = value;
|
||||
}
|
||||
}
|
||||
|
||||
hostData() {
|
||||
return {
|
||||
class: {
|
||||
'reorder-enabled': this._enabled,
|
||||
'reorder-list-active': this._actived,
|
||||
'reorder-visible': this._iconVisible,
|
||||
'reorder-side-start': this.side === 'start'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ion-gesture props={{
|
||||
disableScroll: true,
|
||||
canStart: this.canStart.bind(this),
|
||||
onStart: this.onDragStart.bind(this),
|
||||
onMove: this.onDragMove.bind(this),
|
||||
onEnd: this.onDragEnd.bind(this),
|
||||
enabled: this.enabled,
|
||||
gestureName: 'reorder',
|
||||
gesturePriority: 30,
|
||||
type: 'pan',
|
||||
direction: 'y',
|
||||
threshold: 0,
|
||||
attachTo: 'parent'
|
||||
}}>
|
||||
<slot></slot>
|
||||
</ion-gesture>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
function indexForItem(element: any): number {
|
||||
return element['$ionIndex'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
function findReorderItem(node: HTMLElement, container: HTMLElement): HTMLElement {
|
||||
let nested = 0;
|
||||
let parent;
|
||||
while (node && nested < 6) {
|
||||
parent = node.parentNode as HTMLElement;
|
||||
if (parent === container) {
|
||||
return node;
|
||||
}
|
||||
node = parent;
|
||||
nested++;
|
||||
}
|
||||
return null;
|
||||
}
|
64
packages/core/src/components/reorder/reorder.scss
Normal file
64
packages/core/src/components/reorder/reorder.scss
Normal file
@ -0,0 +1,64 @@
|
||||
@import "../../themes/ionic.globals";
|
||||
|
||||
$reorder-initial-transform: 160% !default;
|
||||
|
||||
// Reorder group general
|
||||
// --------------------------------------------------
|
||||
|
||||
.reorder-enabled [reorderAnchor] {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.reorder-list-active ion-gesture > * {
|
||||
transition: transform 300ms;
|
||||
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.reorder-list-active ion-gesture *:not([reorderAnchor]) {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.reorder-active {
|
||||
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));
|
||||
|
||||
margin-top: auto !important;
|
||||
margin-bottom: auto !important;
|
||||
|
||||
font-size: 1.7em;
|
||||
opacity: .25;
|
||||
|
||||
line-height: 0;
|
||||
|
||||
transition: transform 140ms ease-in;
|
||||
}
|
||||
|
||||
ion-reorder ion-icon {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.reorder-side-start ion-reorder {
|
||||
@include transform(translate3d(-$reorder-initial-transform, 0, 0));
|
||||
|
||||
order: -1;
|
||||
}
|
||||
|
||||
.reorder-visible ion-reorder {
|
||||
@include transform(translate3d(0, 0, 0));
|
||||
}
|
||||
|
20
packages/core/src/components/reorder/reorder.tsx
Normal file
20
packages/core/src/components/reorder/reorder.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { Component } from '@stencil/core';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-reorder',
|
||||
})
|
||||
export class ItemReorder {
|
||||
|
||||
hostData() {
|
||||
return {
|
||||
attrs: {
|
||||
'reorderAnchor': '',
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return <ion-icon name='reorder'></ion-icon>;
|
||||
}
|
||||
}
|
||||
|
87
packages/core/src/components/reorder/test/basic.html
Normal file
87
packages/core/src/components/reorder/test/basic.html
Normal file
@ -0,0 +1,87 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Ionic Reorder</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-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Item Reorder</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button onclick="toggleEdit()">Toggle</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-reorder-group id="reorder">
|
||||
<ion-item>
|
||||
Item 1
|
||||
<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-icon reorderAnchor name="pizza" slot="end"></ion-icon>
|
||||
|
||||
</ion-item>
|
||||
<ion-item>Item 4
|
||||
<ion-icon reorderAnchor name="pizza" slot="end"></ion-icon>
|
||||
|
||||
</ion-item>
|
||||
<ion-item>Item 5
|
||||
<ion-icon reorderAnchor name="pizza" slot="end"></ion-icon>
|
||||
|
||||
</ion-item>
|
||||
<ion-item>Item 6
|
||||
<ion-icon reorderAnchor name="pizza" slot="end"></ion-icon>
|
||||
|
||||
</ion-item>
|
||||
<ion-item>Item 7
|
||||
<ion-icon reorderAnchor name="pizza" slot="end"></ion-icon>
|
||||
|
||||
</ion-item>
|
||||
<ion-item>Item 8
|
||||
<ion-icon reorderAnchor name="pizza" slot="end"></ion-icon>
|
||||
|
||||
</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-item>
|
||||
<ion-item>Item 11
|
||||
<ion-icon reorderAnchor name="pizza" slot="end"></ion-icon>
|
||||
|
||||
</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>
|
||||
</ion-app>
|
||||
|
||||
<script>
|
||||
function toggleEdit() {
|
||||
const list = document.getElementById('reorder');
|
||||
list.enabled = !list.enabled;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -268,3 +268,13 @@ export function hasFocusedTextInput() {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export function reorderArray(array: any[], indexes: {from: number, to: number}): any[] {
|
||||
const element = array[indexes.from];
|
||||
array.splice(indexes.from, 1);
|
||||
array.splice(indexes.to, 0, element);
|
||||
return array;
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ exports.config = {
|
||||
{ components: ['ion-modal', 'ion-modal-controller'] },
|
||||
{ components: ['ion-popover', 'ion-popover-controller'] },
|
||||
{ components: ['ion-radio', 'ion-radio-group'] },
|
||||
{ components: ['ion-reorder', 'ion-reorder-group'] },
|
||||
{ components: ['ion-searchbar'] },
|
||||
{ components: ['ion-segment', 'ion-segment-button'] },
|
||||
{ components: ['ion-select', 'ion-select-option', 'ion-select-popover'] },
|
||||
|
Reference in New Issue
Block a user