mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-23 05:58:26 +08:00
4
core/src/components/refresher/refresher-interface.ts
Normal file
4
core/src/components/refresher/refresher-interface.ts
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
export interface RefresherEventDetail {
|
||||
complete(): void;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import { Component, ComponentInterface, Element, Event, EventEmitter, Method, Prop, QueueApi, State, Watch } from '@stencil/core';
|
||||
|
||||
import { Gesture, GestureDetail, Mode } from '../../interface';
|
||||
import { Gesture, GestureDetail, Mode, RefresherEventDetail } from '../../interface';
|
||||
import { createThemedClasses } from '../../utils/theme';
|
||||
|
||||
@Component({
|
||||
@ -20,6 +20,8 @@ export class Refresher implements ComponentInterface {
|
||||
|
||||
mode!: Mode;
|
||||
|
||||
@Element() el!: HTMLElement;
|
||||
|
||||
@Prop({ context: 'queue' }) queue!: QueueApi;
|
||||
|
||||
/**
|
||||
@ -34,8 +36,6 @@ export class Refresher implements ComponentInterface {
|
||||
*/
|
||||
@State() private state: RefresherState = RefresherState.Inactive;
|
||||
|
||||
@Element() el!: HTMLElement;
|
||||
|
||||
/**
|
||||
* The minimum distance the user must pull down until the
|
||||
* refresher will go into the `refreshing` state. Defaults to `60`.
|
||||
@ -77,7 +77,7 @@ export class Refresher implements ComponentInterface {
|
||||
* Updates the refresher state to `refreshing`. The `complete()` method should be
|
||||
* called when the async operation has completed.
|
||||
*/
|
||||
@Event() ionRefresh!: EventEmitter<void>;
|
||||
@Event() ionRefresh!: EventEmitter<RefresherEventDetail>;
|
||||
|
||||
/**
|
||||
* Emitted while the user is pulling down the content and exposing the refresher.
|
||||
@ -307,7 +307,9 @@ export class Refresher implements ComponentInterface {
|
||||
|
||||
// emit "refresh" because it was pulled down far enough
|
||||
// and they let go to begin refreshing
|
||||
this.ionRefresh.emit();
|
||||
this.ionRefresh.emit({
|
||||
complete: this.complete.bind(this)
|
||||
});
|
||||
}
|
||||
|
||||
private close(state: RefresherState, delay: string) {
|
||||
|
@ -1,6 +1,50 @@
|
||||
# ion-reorder-group
|
||||
|
||||
The reorder group is a wrapper component for items with the `ion-reorder` component. For more information, see the [Reorder documentation](../reorder/).
|
||||
The reorder group is a wrapper component for items with the `ion-reorder` component, Check [Reorder documentation](../reorder/) for further information about the anchor component that is used to drag items within the `ion-reorder-group` list.
|
||||
|
||||
Once the user drags an item and drops it in a new position, the `ionItemReorder` event is dispatched. A handler for it must be implemented by the developer to commit changes.
|
||||
|
||||
```js
|
||||
reorderGroup.addEventListener('ionItemReorder', (ev) => {
|
||||
console.log(`Moving item from ${ev.detail.from} to ${ev.detail.to}`);
|
||||
|
||||
this.dataList = reorderArray(this.dataList, ev.detail.from, ev.detail.to);
|
||||
ev.detail.complete();
|
||||
});
|
||||
```
|
||||
|
||||
The event's detail includes all the relevant information about the reorder operation, including the `from` and `to` indexes. The meaning of this indexes are pretty self-explanatory, the item **from** index X, moved **to** the index Y.
|
||||
|
||||
For example, in this list we move the item at index `0` to the index `3`:
|
||||
|
||||
```
|
||||
BEFORE | AFTER
|
||||
0 | 1
|
||||
1 | 2
|
||||
2 | 3
|
||||
3 | 0
|
||||
4 | 4
|
||||
```
|
||||
|
||||
```
|
||||
detail: {
|
||||
from: 0
|
||||
to: 3
|
||||
}
|
||||
```
|
||||
|
||||
Once the data structure has been updated to reflect the reorder change, the `complete()` method must be called.
|
||||
This method finishes the reorder operation and resets all the CSS transforms applied by the `ion-reorder-group` component.
|
||||
|
||||
Fortunately this `complete()` method can optionally accept an array as input and it will return a reordered copy, based in `detail.from` and `detail.to`.
|
||||
|
||||
```ts
|
||||
this.dataList = reorderGroup.complete(this.dataList);
|
||||
```
|
||||
|
||||
This utility is really handy when
|
||||
|
||||
|
||||
|
||||
<!-- Auto Generated Below -->
|
||||
|
||||
@ -14,9 +58,16 @@ The reorder group is a wrapper component for items with the `ion-reorder` compon
|
||||
|
||||
## Events
|
||||
|
||||
| Event | Description |
|
||||
| ---------------- | ----------- |
|
||||
| `ionItemReorder` | |
|
||||
| Event | Description |
|
||||
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `ionItemReorder` | Event that needs to be listen to in order to respond to reorder action. `ion-reorder-group` uses this event to delegate to the user the reordering of data array. The complete() method exposed as |
|
||||
|
||||
|
||||
## Methods
|
||||
|
||||
| Method | Description |
|
||||
| ---------- | ----------- |
|
||||
| `complete` | |
|
||||
|
||||
|
||||
----------------------------------------------
|
||||
|
@ -0,0 +1,6 @@
|
||||
|
||||
export interface ItemReorderDetail {
|
||||
from: number;
|
||||
to: number;
|
||||
complete: (data?: boolean | any[]) => any;
|
||||
}
|
@ -1,8 +1,14 @@
|
||||
import { Component, ComponentInterface, Element, Event, EventEmitter, Prop, QueueApi, State, Watch } from '@stencil/core';
|
||||
import { Component, ComponentInterface, Element, Event, EventEmitter, Method, Prop, QueueApi, State, Watch } from '@stencil/core';
|
||||
|
||||
import { Gesture, GestureDetail } from '../../interface';
|
||||
import { Gesture, GestureDetail, ItemReorderDetail } from '../../interface';
|
||||
import { hapticSelectionChanged, hapticSelectionEnd, hapticSelectionStart } from '../../utils/haptic';
|
||||
|
||||
const enum ReordeGroupState {
|
||||
Idle = 0,
|
||||
Active = 1,
|
||||
Complete = 2
|
||||
}
|
||||
|
||||
@Component({
|
||||
tag: 'ion-reorder-group',
|
||||
styleUrl: 'reorder-group.scss'
|
||||
@ -23,7 +29,7 @@ export class ReorderGroup implements ComponentInterface {
|
||||
private containerTop = 0;
|
||||
private containerBottom = 0;
|
||||
|
||||
@State() activated = false;
|
||||
@State() state = ReordeGroupState.Idle;
|
||||
|
||||
@Element() el!: HTMLElement;
|
||||
|
||||
@ -43,7 +49,14 @@ export class ReorderGroup implements ComponentInterface {
|
||||
delete a.a;
|
||||
}
|
||||
|
||||
@Event() ionItemReorder!: EventEmitter;
|
||||
/**
|
||||
* Event that needs to be listen to in order to respond to reorder action.
|
||||
* `ion-reorder-group` uses this event to delegate to the user the reordering of data array.
|
||||
*
|
||||
*
|
||||
* The complete() method exposed as
|
||||
*/
|
||||
@Event({ bubbles: false }) ionItemReorder!: EventEmitter<ItemReorderDetail>;
|
||||
|
||||
async componentDidLoad() {
|
||||
const contentEl = this.el.closest('ion-content');
|
||||
@ -73,8 +86,13 @@ export class ReorderGroup implements ComponentInterface {
|
||||
this.onEnd();
|
||||
}
|
||||
|
||||
@Method()
|
||||
complete(listOrReorder?: boolean | any[]): Promise<any> {
|
||||
return Promise.resolve(this.completeSync(listOrReorder));
|
||||
}
|
||||
|
||||
private canStart(ev: GestureDetail): boolean {
|
||||
if (this.selectedItemEl) {
|
||||
if (this.selectedItemEl || this.state !== ReordeGroupState.Idle) {
|
||||
return false;
|
||||
}
|
||||
const target = ev.event.target as HTMLElement;
|
||||
@ -111,7 +129,7 @@ export class ReorderGroup implements ComponentInterface {
|
||||
child.$ionIndex = i;
|
||||
}
|
||||
|
||||
const box = this.el.getBoundingClientRect();
|
||||
const box = el.getBoundingClientRect();
|
||||
this.containerTop = box.top;
|
||||
this.containerBottom = box.bottom;
|
||||
|
||||
@ -128,7 +146,7 @@ export class ReorderGroup implements ComponentInterface {
|
||||
|
||||
this.lastToIndex = indexForItem(item);
|
||||
this.selectedItemHeight = item.offsetHeight;
|
||||
this.activated = true;
|
||||
this.state = ReordeGroupState.Active;
|
||||
|
||||
item.classList.add(ITEM_REORDER_SELECTED);
|
||||
|
||||
@ -163,48 +181,62 @@ export class ReorderGroup implements ComponentInterface {
|
||||
}
|
||||
|
||||
private onEnd() {
|
||||
this.activated = false;
|
||||
const selectedItem = this.selectedItemEl;
|
||||
this.state = ReordeGroupState.Complete;
|
||||
if (!selectedItem) {
|
||||
this.state = ReordeGroupState.Idle;
|
||||
return;
|
||||
}
|
||||
|
||||
const children = this.el.children as any;
|
||||
const toIndex = this.lastToIndex;
|
||||
const fromIndex = indexForItem(selectedItem);
|
||||
|
||||
const ref = (fromIndex < toIndex)
|
||||
? children[toIndex + 1]
|
||||
: children[toIndex];
|
||||
|
||||
this.el.insertBefore(selectedItem, ref);
|
||||
|
||||
const len = children.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
children[i].style['transform'] = '';
|
||||
}
|
||||
|
||||
const reorderInactive = () => {
|
||||
if (this.selectedItemEl) {
|
||||
this.selectedItemEl.style.transition = '';
|
||||
this.selectedItemEl.classList.remove(ITEM_REORDER_SELECTED);
|
||||
this.selectedItemEl = undefined;
|
||||
}
|
||||
};
|
||||
if (toIndex === fromIndex) {
|
||||
selectedItem.style.transition = 'transform 200ms ease-in-out';
|
||||
setTimeout(reorderInactive, 200);
|
||||
setTimeout(() => this.completeSync(), 200);
|
||||
} else {
|
||||
reorderInactive();
|
||||
this.ionItemReorder.emit({
|
||||
from: fromIndex,
|
||||
to: toIndex
|
||||
to: toIndex,
|
||||
complete: this.completeSync.bind(this)
|
||||
});
|
||||
}
|
||||
|
||||
hapticSelectionEnd();
|
||||
}
|
||||
|
||||
private completeSync(listOrReorder?: boolean | any[]): any {
|
||||
const selectedItemEl = this.selectedItemEl;
|
||||
if (selectedItemEl && this.state === ReordeGroupState.Complete) {
|
||||
const children = this.el.children as any;
|
||||
const len = children.length;
|
||||
const toIndex = this.lastToIndex;
|
||||
const fromIndex = indexForItem(selectedItemEl);
|
||||
|
||||
if (listOrReorder === true) {
|
||||
const ref = (fromIndex < toIndex)
|
||||
? children[toIndex + 1]
|
||||
: children[toIndex];
|
||||
|
||||
this.el.insertBefore(selectedItemEl, ref);
|
||||
}
|
||||
|
||||
if (Array.isArray(listOrReorder)) {
|
||||
listOrReorder = reorderArray(listOrReorder, fromIndex, toIndex);
|
||||
}
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
children[i].style['transform'] = '';
|
||||
}
|
||||
|
||||
selectedItemEl.style.transition = '';
|
||||
selectedItemEl.classList.remove(ITEM_REORDER_SELECTED);
|
||||
this.selectedItemEl = undefined;
|
||||
this.state = ReordeGroupState.Idle;
|
||||
}
|
||||
return listOrReorder;
|
||||
}
|
||||
|
||||
private itemIndexForTop(deltaY: number): number {
|
||||
const heights = this.cachedHeights;
|
||||
let i = 0;
|
||||
@ -257,7 +289,7 @@ export class ReorderGroup implements ComponentInterface {
|
||||
return {
|
||||
class: {
|
||||
'reorder-enabled': !this.disabled,
|
||||
'reorder-list-active': this.activated,
|
||||
'reorder-list-active': this.state !== ReordeGroupState.Idle,
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -284,3 +316,10 @@ function findReorderItem(node: HTMLElement, container: HTMLElement): HTMLElement
|
||||
const AUTO_SCROLL_MARGIN = 60;
|
||||
const SCROLL_JUMP = 10;
|
||||
const ITEM_REORDER_SELECTED = 'reorder-selected';
|
||||
|
||||
function reorderArray(array: any[], from: number, to: number): any[] {
|
||||
const element = array[from];
|
||||
array.splice(from, 1);
|
||||
array.splice(to, 0, element);
|
||||
return array.slice();
|
||||
}
|
||||
|
@ -114,6 +114,9 @@
|
||||
function toggleEdit() {
|
||||
const reorderGroup = document.getElementById('reorder');
|
||||
reorderGroup.disabled = !reorderGroup.disabled;
|
||||
reorderGroup.addEventListener('ionItemReorder', ({detail}) => {
|
||||
detail.complete(true);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
49
core/src/components/reorder-group/usage/angular.md
Normal file
49
core/src/components/reorder-group/usage/angular.md
Normal file
@ -0,0 +1,49 @@
|
||||
```html
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-reorder-group>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 1
|
||||
</ion-label>
|
||||
<ion-reorder slot="end"></ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 2 (default ion-reorder slot="start")
|
||||
</ion-label>
|
||||
<ion-reorder slot="start"></ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 3 (custom ion-reorder)
|
||||
</ion-label>
|
||||
<ion-reorder slot="end">
|
||||
<ion-icon name="pizza"></ion-icon>
|
||||
</ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 4 (custom ion-reorder slot="start")
|
||||
</ion-label>
|
||||
<ion-reorder slot="start">
|
||||
<ion-icon name="pizza"></ion-icon>
|
||||
</ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-reorder>
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 5 (the whole item can be dragged)
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-reorder>
|
||||
|
||||
</ion-reorder-group>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
```
|
60
core/src/components/reorder-group/usage/javascript.md
Normal file
60
core/src/components/reorder-group/usage/javascript.md
Normal file
@ -0,0 +1,60 @@
|
||||
```html
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-reorder-group disabled="false">
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 1
|
||||
</ion-label>
|
||||
<ion-reorder slot="end"></ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 2 (default ion-reorder slot="start")
|
||||
</ion-label>
|
||||
<ion-reorder slot="start"></ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 3 (custom ion-reorder)
|
||||
</ion-label>
|
||||
<ion-reorder slot="end">
|
||||
<ion-icon name="pizza"></ion-icon>
|
||||
</ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 4 (custom ion-reorder slot="start")
|
||||
</ion-label>
|
||||
<ion-reorder slot="start">
|
||||
<ion-icon name="pizza"></ion-icon>
|
||||
</ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-reorder>
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 5 (the whole item can be dragged)
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-reorder>
|
||||
|
||||
</ion-reorder-group>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
```
|
||||
|
||||
```javascript
|
||||
const reorderGroup = document.querySelector('ion-reorder-group');
|
||||
reorderGroup.addEventListener('ionItemReorder', ({detail}) => {
|
||||
// finishing the reorder, true means ion-reorder-group with reorder the DOM
|
||||
detail.complete(true);
|
||||
|
||||
// or:
|
||||
// reorderGroup.complete(true)
|
||||
});
|
||||
```
|
@ -1,6 +1,19 @@
|
||||
# ion-reorder
|
||||
|
||||
Reorder is a component that allows an item to be dragged to change its order. It can be used within an `ion-reorder-group` to provide a visual drag and drop interface.
|
||||
Reorder is a component that allows an item to be dragged to change its order. It must be used within an `ion-reorder-group` to provide a visual drag and drop interface.
|
||||
|
||||
`ion-reorder` is the anchor users will use to drag and drop items inside the `ion-reorder-group`. It must be added to `ion-item` in order for them to be draggable.
|
||||
|
||||
```html
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item
|
||||
</ion-label>
|
||||
<ion-reorder slot="end"></ion-reorder>
|
||||
</ion-item>
|
||||
```
|
||||
|
||||
The position of the
|
||||
|
||||
|
||||
<!-- Auto Generated Below -->
|
||||
|
@ -1,88 +0,0 @@
|
||||
```html
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-reorder-group>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 1
|
||||
</ion-label>
|
||||
<ion-reorder slot="end"></ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 2
|
||||
</ion-label>
|
||||
<ion-reorder slot="end"></ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 3 (default ion-reorder slot="start")
|
||||
</ion-label>
|
||||
<ion-reorder slot="start"></ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item color="secondary">
|
||||
<ion-label>
|
||||
Item 4 (default ion-reorder slot="start")
|
||||
</ion-label>
|
||||
<ion-reorder slot="start"></ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 5 (custom ion-reorder)
|
||||
</ion-label>
|
||||
<ion-reorder slot="end">
|
||||
<ion-icon name="pizza"></ion-icon>
|
||||
</ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 6 (custom ion-reorder)
|
||||
</ion-label>
|
||||
<ion-reorder slot="end">
|
||||
<ion-icon name="pizza"></ion-icon>
|
||||
</ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 7 (custom ion-reorder slot="start")
|
||||
</ion-label>
|
||||
<ion-reorder slot="start">
|
||||
<ion-icon name="pizza"></ion-icon>
|
||||
</ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-reorder>
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 8 (the whole item can be dragged)
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-reorder>
|
||||
|
||||
<ion-reorder>
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 9 (the whole item can be dragged)
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-reorder>
|
||||
|
||||
<ion-reorder>
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 10 (the whole item can be dragged)
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-reorder>
|
||||
|
||||
</ion-reorder-group>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
```
|
@ -1,88 +0,0 @@
|
||||
```html
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-reorder-group>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 1
|
||||
</ion-label>
|
||||
<ion-reorder slot="end"></ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 2
|
||||
</ion-label>
|
||||
<ion-reorder slot="end"></ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 3 (default ion-reorder slot="start")
|
||||
</ion-label>
|
||||
<ion-reorder slot="start"></ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item color="secondary">
|
||||
<ion-label>
|
||||
Item 4 (default ion-reorder slot="start")
|
||||
</ion-label>
|
||||
<ion-reorder slot="start"></ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 5 (custom ion-reorder)
|
||||
</ion-label>
|
||||
<ion-reorder slot="end">
|
||||
<ion-icon name="pizza"></ion-icon>
|
||||
</ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 6 (custom ion-reorder)
|
||||
</ion-label>
|
||||
<ion-reorder slot="end">
|
||||
<ion-icon name="pizza"></ion-icon>
|
||||
</ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 7 (custom ion-reorder slot="start")
|
||||
</ion-label>
|
||||
<ion-reorder slot="start">
|
||||
<ion-icon name="pizza"></ion-icon>
|
||||
</ion-reorder>
|
||||
</ion-item>
|
||||
|
||||
<ion-reorder>
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 8 (the whole item can be dragged)
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-reorder>
|
||||
|
||||
<ion-reorder>
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 9 (the whole item can be dragged)
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-reorder>
|
||||
|
||||
<ion-reorder>
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
Item 10 (the whole item can be dragged)
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-reorder>
|
||||
|
||||
</ion-reorder-group>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
```
|
Reference in New Issue
Block a user