mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-22 13:32:54 +08:00
fix(sliding-item): finish it
This commit is contained in:
6
packages/core/src/components.d.ts
vendored
6
packages/core/src/components.d.ts
vendored
@ -1222,6 +1222,7 @@ declare global {
|
||||
|
||||
isRightSide?: any,
|
||||
width?: any,
|
||||
fireSwipeEvent?: any,
|
||||
side?: string
|
||||
}
|
||||
}
|
||||
@ -1252,7 +1253,10 @@ declare global {
|
||||
mode?: string,
|
||||
color?: string,
|
||||
|
||||
close?: any
|
||||
getOpenAmount?: any,
|
||||
getSlidingPercent?: any,
|
||||
close?: any,
|
||||
closeOpened?: any
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
import { Component, Prop } from '@stencil/core';
|
||||
import { createThemedClasses } from '../../utils/theme';
|
||||
|
||||
|
||||
/**
|
||||
* @name ItemOption
|
||||
@ -10,7 +8,10 @@ import { createThemedClasses } from '../../utils/theme';
|
||||
* action for the item.
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-item-option'
|
||||
tag: 'ion-item-option',
|
||||
host: {
|
||||
theme: 'item-option'
|
||||
}
|
||||
})
|
||||
export class ItemOption {
|
||||
mode: string;
|
||||
@ -35,18 +36,14 @@ export class ItemOption {
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const themedClasses = createThemedClasses(this.mode, this.color, 'item-option-button');
|
||||
|
||||
const TagType = this.href ? 'a' : 'button';
|
||||
|
||||
return (
|
||||
<TagType class={themedClasses} onClick={this.clickedOptionButton.bind(this)} disabled={this.disabled}>
|
||||
return [
|
||||
<TagType class='item-option-button' onClick={this.clickedOptionButton.bind(this)} disabled={this.disabled}></TagType>,
|
||||
<span class='button-inner'>
|
||||
<slot></slot>
|
||||
</span>
|
||||
<div class='button-effect'></div>
|
||||
</TagType>
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -40,15 +40,21 @@ export class ItemOptions {
|
||||
*/
|
||||
@Event() ionSwipe: EventEmitter;
|
||||
|
||||
@Method() isRightSide() {
|
||||
const isRTL = document.dir === 'rtl';
|
||||
return isRightSide(this.side, isRTL, true);
|
||||
@Method()
|
||||
isRightSide() {
|
||||
return isRightSide(this.side, true);
|
||||
}
|
||||
|
||||
@Method() width(): number {
|
||||
@Method()
|
||||
width(): number {
|
||||
return this.el.offsetWidth;
|
||||
}
|
||||
|
||||
@Method()
|
||||
fireSwipeEvent(value: any) {
|
||||
this.ionSwipe.emit(value);
|
||||
}
|
||||
|
||||
protected render() {
|
||||
return <slot></slot>;
|
||||
}
|
||||
|
@ -29,13 +29,13 @@ $item-ios-sliding-button-icon-color: color-contrast($colors-ios, $item-i
|
||||
border-bottom: $hairlines-width solid $list-ios-border-color;
|
||||
}
|
||||
|
||||
.item-option-button-ios {
|
||||
.item-option-ios {
|
||||
font-size: $item-ios-sliding-button-font-size;
|
||||
color: $item-ios-sliding-button-text-color;
|
||||
background-color: $item-ios-sliding-button-background-color;
|
||||
}
|
||||
|
||||
.item-option-button-ios .icon {
|
||||
.item-option-ios .icon {
|
||||
fill: $item-ios-sliding-button-icon-color;
|
||||
}
|
||||
|
||||
@ -53,12 +53,12 @@ $item-ios-sliding-button-icon-color: color-contrast($colors-ios, $item-i
|
||||
|
||||
@each $color-name, $color-base, $color-contrast in get-colors($colors-ios) {
|
||||
|
||||
.item-option-button-ios-#{$color-name} {
|
||||
.item-option-ios-#{$color-name} {
|
||||
color: $color-contrast;
|
||||
background-color: $color-base;
|
||||
}
|
||||
|
||||
.item-option-button-ios-#{$color-name} .icon {
|
||||
.item-option-ios-#{$color-name} .icon {
|
||||
fill: $color-contrast;
|
||||
}
|
||||
|
||||
|
@ -29,13 +29,13 @@ $item-md-sliding-button-icon-color: color-contrast($colors-md, $item-md
|
||||
border-bottom: 1px solid $list-md-border-color;
|
||||
}
|
||||
|
||||
.item-option-button-md {
|
||||
.item-option-md {
|
||||
font-size: $item-md-sliding-button-font-size;
|
||||
color: $item-md-sliding-button-text-color;
|
||||
background-color: $item-md-sliding-button-background-color;
|
||||
}
|
||||
|
||||
.item-option-button-md .icon {
|
||||
.item-option-md .icon {
|
||||
fill: $item-md-sliding-button-icon-color;
|
||||
}
|
||||
|
||||
@ -52,12 +52,12 @@ $item-md-sliding-button-icon-color: color-contrast($colors-md, $item-md
|
||||
|
||||
@each $color-name, $color-base, $color-contrast in get-colors($colors-md) {
|
||||
|
||||
.item-option-button-md-#{$color-name} {
|
||||
.item-option-md-#{$color-name} {
|
||||
color: $color-contrast;
|
||||
background-color: $color-base;
|
||||
}
|
||||
|
||||
.item-option-button-md-#{$color-name} .icon {
|
||||
.item-option-md-#{$color-name} .icon {
|
||||
fill: $color-contrast;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
// Item Sliding
|
||||
// --------------------------------------------------
|
||||
// The hidden buttons that can be exposed under a list item by dragging
|
||||
// The hidden right-side buttons that can be exposed under a list item with dragging.
|
||||
|
||||
ion-item-sliding {
|
||||
position: relative;
|
||||
@ -12,10 +12,6 @@ ion-item-sliding {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
ion-item-sliding ion-item {
|
||||
position: static;
|
||||
}
|
||||
|
||||
ion-item-options {
|
||||
position: absolute;
|
||||
z-index: $z-index-item-options;
|
||||
@ -66,29 +62,31 @@ ion-item-options[side=left] {
|
||||
}
|
||||
}
|
||||
|
||||
.item-option-button {
|
||||
@include margin(0);
|
||||
@include border-radius(0);
|
||||
|
||||
ion-item-option {
|
||||
@include padding(0, .7em);
|
||||
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
|
||||
height: 100%;
|
||||
min-width: 6rem;
|
||||
}
|
||||
|
||||
.item-option-button {
|
||||
@include position(0, 0, 0, 0);
|
||||
@include margin(0);
|
||||
@include padding(0);
|
||||
@include border-radius(0);
|
||||
|
||||
position: absolute;
|
||||
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
|
||||
box-sizing: border-box;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.item-option-button::before {
|
||||
@include margin(0, auto);
|
||||
}
|
||||
|
||||
ion-item-options:not([icon-start]) .item-option-button:not([icon-only]) {
|
||||
ion-item-options:not([icon-start]) ion-item-option:not([icon-only]) {
|
||||
.button-inner {
|
||||
flex-direction: column;
|
||||
}
|
||||
@ -131,7 +129,7 @@ ion-item-sliding.active-slide {
|
||||
// Item Expandable Animation
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-item-option[expandable] .item-option-button {
|
||||
ion-item-option[expandable] {
|
||||
flex-shrink: 0;
|
||||
|
||||
transition-duration: 0;
|
||||
@ -139,7 +137,7 @@ ion-item-option[expandable] .item-option-button {
|
||||
transition-timing-function: cubic-bezier(.65, .05, .36, 1);
|
||||
}
|
||||
|
||||
ion-item-sliding.active-swipe-right ion-item-option[expandable] .item-option-button {
|
||||
ion-item-sliding.active-swipe-right ion-item-option[expandable] {
|
||||
transition-duration: .6s;
|
||||
transition-property: padding-left;
|
||||
|
||||
@ -157,7 +155,7 @@ ion-item-sliding.active-swipe-right ion-item-option[expandable] .item-option-but
|
||||
}
|
||||
}
|
||||
|
||||
ion-item-sliding.active-swipe-left ion-item-option[expandable] .item-option-button {
|
||||
ion-item-sliding.active-swipe-left ion-item-option[expandable] {
|
||||
transition-duration: .6s;
|
||||
transition-property: padding-right;
|
||||
|
||||
|
@ -8,11 +8,12 @@ import { ItemOptions } from './item-options';
|
||||
const SWIPE_MARGIN = 30;
|
||||
const ELASTIC_FACTOR = 0.55;
|
||||
|
||||
const ITEM_SIDE_FLAG_NONE = 0;
|
||||
const ITEM_SIDE_FLAG_LEFT = 1 << 0;
|
||||
const ITEM_SIDE_FLAG_RIGHT = 1 << 1;
|
||||
const ITEM_SIDE_FLAG_BOTH = ITEM_SIDE_FLAG_LEFT | ITEM_SIDE_FLAG_RIGHT;
|
||||
|
||||
const enum ItemSide {
|
||||
None = 0,
|
||||
Left = 1 << 0,
|
||||
Right = 1 << 1,
|
||||
Both = Left | Right
|
||||
}
|
||||
|
||||
const enum SlidingState {
|
||||
Disabled = 1 << 1,
|
||||
@ -135,30 +136,23 @@ const enum SlidingState {
|
||||
}
|
||||
})
|
||||
export class ItemSliding {
|
||||
@Element() private el: HTMLElement;
|
||||
|
||||
private item: HTMLIonItemElement;
|
||||
private list: HTMLIonListElement;
|
||||
|
||||
private openAmount: number = 0;
|
||||
private startX: number = 0;
|
||||
private optsWidthRightSide: number = 0;
|
||||
private optsWidthLeftSide: number = 0;
|
||||
private sides: number;
|
||||
private openAmount = 0;
|
||||
private initialOpenAmount = 0;
|
||||
private optsWidthRightSide = 0;
|
||||
private optsWidthLeftSide = 0;
|
||||
private sides: ItemSide;
|
||||
private tmr: any = null;
|
||||
|
||||
private leftOptions: ItemOptions;
|
||||
private rightOptions: ItemOptions;
|
||||
|
||||
private optsDirty: boolean = true;
|
||||
private gestureOptions: any;
|
||||
|
||||
@Element() private el: HTMLElement;
|
||||
@State() state: SlidingState = SlidingState.Disabled;
|
||||
|
||||
private preSelectedContainer: ItemSliding = null;
|
||||
private selectedContainer: ItemSliding = null;
|
||||
openContainer: ItemSliding = null;
|
||||
private firstCoordX: number;
|
||||
private firstTimestamp: number;
|
||||
|
||||
/**
|
||||
* @output {event} Emitted when the sliding position changes.
|
||||
@ -183,92 +177,39 @@ export class ItemSliding {
|
||||
*/
|
||||
@Event() ionDrag: EventEmitter;
|
||||
|
||||
constructor() {
|
||||
this.gestureOptions = {
|
||||
'canStart': this.canStart.bind(this),
|
||||
'onStart': this.onDragStart.bind(this),
|
||||
'onMove': this.onDragMove.bind(this),
|
||||
'onEnd': this.onDragEnd.bind(this),
|
||||
'gestureName': 'item-swipe',
|
||||
'gesturePriority': -10,
|
||||
'type': 'pan',
|
||||
'direction': 'x',
|
||||
'maxAngle': 20,
|
||||
'threshold': 5,
|
||||
'attachTo': 'parent'
|
||||
};
|
||||
}
|
||||
|
||||
protected ionViewDidLoad() {
|
||||
const options = this.el.querySelectorAll('ion-item-options');
|
||||
|
||||
let sides = 0;
|
||||
|
||||
// Reset left and right options in case they were removed
|
||||
this.leftOptions = this.rightOptions = null;
|
||||
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
let option = options[i];
|
||||
|
||||
if (option.isRightSide()) {
|
||||
this.rightOptions = option;
|
||||
sides |= ITEM_SIDE_FLAG_RIGHT;
|
||||
} else {
|
||||
this.leftOptions = option;
|
||||
sides |= ITEM_SIDE_FLAG_LEFT;
|
||||
}
|
||||
}
|
||||
this.optsDirty = true;
|
||||
this.sides = sides;
|
||||
|
||||
this.item = this.el.querySelector('ion-item');
|
||||
|
||||
// Get the parent list to close open containers
|
||||
this.list = this.el.closest('ion-list') as HTMLIonListElement;
|
||||
|
||||
this.updateOptions();
|
||||
}
|
||||
|
||||
canStart(gesture: GestureDetail): boolean {
|
||||
if (this.selectedContainer) {
|
||||
return false;
|
||||
}
|
||||
// Get swiped sliding container
|
||||
let container = this;
|
||||
|
||||
// Close open container if it is not the selected one.
|
||||
if (this.list && container !== this.list.getOpenedItem()) {
|
||||
this.closeOpened();
|
||||
protected ionViewDidUnLoad() {
|
||||
this.item = this.list = this.leftOptions = this.rightOptions = null;
|
||||
}
|
||||
|
||||
this.preSelectedContainer = container;
|
||||
this.firstCoordX = gesture.currentX;
|
||||
this.firstTimestamp = Date.now();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
onDragStart(gesture: GestureDetail) {
|
||||
this.selectedContainer = this.preSelectedContainer;
|
||||
this.list.setOpenedItem(this.selectedContainer);
|
||||
this.selectedContainer.startSliding(gesture.currentX);
|
||||
}
|
||||
|
||||
onDragMove(gesture: GestureDetail) {
|
||||
this.selectedContainer && this.selectedContainer.moveSliding(gesture.currentX);
|
||||
}
|
||||
|
||||
onDragEnd(gesture: GestureDetail) {
|
||||
let coordX = gesture.currentX;
|
||||
let deltaX = (coordX - this.firstCoordX);
|
||||
let deltaT = (Date.now() - this.firstTimestamp);
|
||||
this.selectedContainer.endSliding(deltaX / deltaT);
|
||||
this.selectedContainer = null;
|
||||
this.preSelectedContainer = null;
|
||||
}
|
||||
|
||||
closeOpened(): boolean {
|
||||
this.selectedContainer = null;
|
||||
|
||||
if (this.list.getOpenedItem()) {
|
||||
this.list.closeSlidingItems();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
@Method()
|
||||
getOpenAmount(): number {
|
||||
return this.openAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
@Method()
|
||||
getSlidingPercent(): number {
|
||||
const openAmount = this.openAmount;
|
||||
if (openAmount > 0) {
|
||||
@ -280,156 +221,6 @@ export class ItemSliding {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
startSliding(startX: number) {
|
||||
if (this.tmr) {
|
||||
clearTimeout(this.tmr);
|
||||
this.tmr = null;
|
||||
}
|
||||
if (this.openAmount === 0) {
|
||||
this.optsDirty = true;
|
||||
this.setState(SlidingState.Enabled);
|
||||
}
|
||||
this.startX = startX + this.openAmount;
|
||||
this.item.style.transition = 'none';
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
moveSliding(x: number): number {
|
||||
if (this.optsDirty) {
|
||||
this.calculateOptsWidth();
|
||||
return 0;
|
||||
}
|
||||
|
||||
let openAmount = (this.startX - x);
|
||||
|
||||
switch (this.sides) {
|
||||
case ITEM_SIDE_FLAG_RIGHT: openAmount = Math.max(0, openAmount); break;
|
||||
case ITEM_SIDE_FLAG_LEFT: openAmount = Math.min(0, openAmount); break;
|
||||
case ITEM_SIDE_FLAG_BOTH: break;
|
||||
case ITEM_SIDE_FLAG_NONE: return 0;
|
||||
default: console.warn('invalid ItemSideFlags value', this.sides); break;
|
||||
}
|
||||
|
||||
let optsWidth;
|
||||
if (openAmount > this.optsWidthRightSide) {
|
||||
optsWidth = this.optsWidthRightSide;
|
||||
openAmount = optsWidth + (openAmount - optsWidth) * ELASTIC_FACTOR;
|
||||
|
||||
} else if (openAmount < -this.optsWidthLeftSide) {
|
||||
optsWidth = -this.optsWidthLeftSide;
|
||||
openAmount = optsWidth + (openAmount - optsWidth) * ELASTIC_FACTOR;
|
||||
}
|
||||
|
||||
this.setOpenAmount(openAmount, false);
|
||||
return openAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
endSliding(velocity: number): number {
|
||||
let restingPoint = (this.openAmount > 0)
|
||||
? this.optsWidthRightSide
|
||||
: -this.optsWidthLeftSide;
|
||||
|
||||
// Check if the drag didn't clear the buttons mid-point
|
||||
// and we aren't moving fast enough to swipe open
|
||||
const isResetDirection = (this.openAmount > 0) === !(velocity < 0);
|
||||
const isMovingFast = Math.abs(velocity) > 0.3;
|
||||
const isOnCloseZone = Math.abs(this.openAmount) < Math.abs(restingPoint / 2);
|
||||
if (swipeShouldReset(isResetDirection, isMovingFast, isOnCloseZone)) {
|
||||
restingPoint = 0;
|
||||
}
|
||||
|
||||
this.setOpenAmount(restingPoint, true);
|
||||
this.fireSwipeEvent();
|
||||
return restingPoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* Emit the ionSwipe event on the child options
|
||||
*/
|
||||
fireSwipeEvent() {
|
||||
if (this.state & SlidingState.SwipeRight) {
|
||||
this.rightOptions.ionSwipe.emit(this);
|
||||
} else if (this.state & SlidingState.SwipeLeft) {
|
||||
this.leftOptions.ionSwipe.emit(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
calculateOptsWidth() {
|
||||
if (!this.optsDirty) {
|
||||
return;
|
||||
}
|
||||
this.optsWidthRightSide = 0;
|
||||
if (this.rightOptions) {
|
||||
this.optsWidthRightSide = this.rightOptions.width();
|
||||
this.optsWidthRightSide === 0 && console.warn('optsWidthRightSide should not be zero');
|
||||
}
|
||||
|
||||
this.optsWidthLeftSide = 0;
|
||||
if (this.leftOptions) {
|
||||
this.optsWidthLeftSide = this.leftOptions.width();
|
||||
this.optsWidthLeftSide === 0 && console.warn('optsWidthLeftSide should not be zero');
|
||||
}
|
||||
this.optsDirty = false;
|
||||
}
|
||||
|
||||
setOpenAmount(openAmount: number, isFinal: boolean) {
|
||||
if (this.tmr) {
|
||||
clearTimeout(this.tmr);
|
||||
this.tmr = null;
|
||||
}
|
||||
this.openAmount = openAmount;
|
||||
|
||||
if (isFinal) {
|
||||
this.item.style.transition = '';
|
||||
|
||||
} else {
|
||||
var state;
|
||||
if (openAmount > 0) {
|
||||
state = (openAmount >= (this.optsWidthRightSide + SWIPE_MARGIN))
|
||||
? SlidingState.Right | SlidingState.SwipeRight
|
||||
: SlidingState.Right;
|
||||
|
||||
this.setState(state);
|
||||
|
||||
} else if (openAmount < 0) {
|
||||
state = (openAmount <= (-this.optsWidthLeftSide - SWIPE_MARGIN))
|
||||
? SlidingState.Left | SlidingState.SwipeLeft
|
||||
: SlidingState.Left;
|
||||
|
||||
this.setState(state);
|
||||
}
|
||||
}
|
||||
if (openAmount === 0) {
|
||||
this.tmr = setTimeout(() => {
|
||||
this.setState(SlidingState.Disabled);
|
||||
this.tmr = null;
|
||||
}, 600);
|
||||
this.item.style.transform = '';
|
||||
return;
|
||||
}
|
||||
|
||||
this.item.style.transform = `translate3d(${-openAmount}px,0,0)`;
|
||||
this.ionDrag.emit();
|
||||
}
|
||||
|
||||
private setState(state: SlidingState) {
|
||||
if (state === this.state) {
|
||||
return;
|
||||
}
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the sliding item. Items can also be closed from the [List](../../list/List).
|
||||
@ -470,7 +261,160 @@ export class ItemSliding {
|
||||
this.setOpenAmount(0, true);
|
||||
}
|
||||
|
||||
hostData() {
|
||||
@Method()
|
||||
closeOpened(): boolean {
|
||||
return this.list && this.list.closeSlidingItems();
|
||||
}
|
||||
|
||||
private updateOptions() {
|
||||
const options = this.el.querySelectorAll('ion-item-options');
|
||||
|
||||
let sides = 0;
|
||||
|
||||
// Reset left and right options in case they were removed
|
||||
this.leftOptions = this.rightOptions = null;
|
||||
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
let option = options.item(i);
|
||||
|
||||
if (option.isRightSide()) {
|
||||
this.rightOptions = option;
|
||||
sides |= ItemSide.Right;
|
||||
} else {
|
||||
this.leftOptions = option;
|
||||
sides |= ItemSide.Left;
|
||||
}
|
||||
}
|
||||
this.optsDirty = true;
|
||||
this.sides = sides;
|
||||
}
|
||||
|
||||
private canStart(): boolean {
|
||||
const selected = this.list && this.list.getOpenedItem();
|
||||
if (selected && selected !== this) {
|
||||
this.closeOpened();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private onDragStart() {
|
||||
this.list && this.list.setOpenedItem(this);
|
||||
|
||||
if (this.tmr) {
|
||||
clearTimeout(this.tmr);
|
||||
this.tmr = null;
|
||||
}
|
||||
if (this.openAmount === 0) {
|
||||
this.optsDirty = true;
|
||||
this.state = SlidingState.Enabled;
|
||||
}
|
||||
this.initialOpenAmount = this.openAmount;
|
||||
this.item.style.transition = 'none';
|
||||
}
|
||||
|
||||
private onDragMove(gesture: GestureDetail) {
|
||||
if (this.optsDirty) {
|
||||
this.calculateOptsWidth();
|
||||
}
|
||||
let openAmount = this.initialOpenAmount - gesture.deltaX;
|
||||
|
||||
switch (this.sides) {
|
||||
case ItemSide.Right: openAmount = Math.max(0, openAmount); break;
|
||||
case ItemSide.Left: openAmount = Math.min(0, openAmount); break;
|
||||
case ItemSide.Both: break;
|
||||
case ItemSide.None: return;
|
||||
default: console.warn('invalid ItemSideFlags value', this.sides); break;
|
||||
}
|
||||
|
||||
let optsWidth;
|
||||
if (openAmount > this.optsWidthRightSide) {
|
||||
optsWidth = this.optsWidthRightSide;
|
||||
openAmount = optsWidth + (openAmount - optsWidth) * ELASTIC_FACTOR;
|
||||
|
||||
} else if (openAmount < -this.optsWidthLeftSide) {
|
||||
optsWidth = -this.optsWidthLeftSide;
|
||||
openAmount = optsWidth + (openAmount - optsWidth) * ELASTIC_FACTOR;
|
||||
}
|
||||
|
||||
this.setOpenAmount(openAmount, false);
|
||||
}
|
||||
|
||||
private onDragEnd(gesture: GestureDetail) {
|
||||
const velocity = gesture.velocityX;
|
||||
|
||||
let restingPoint = (this.openAmount > 0)
|
||||
? this.optsWidthRightSide
|
||||
: -this.optsWidthLeftSide;
|
||||
|
||||
// Check if the drag didn't clear the buttons mid-point
|
||||
// and we aren't moving fast enough to swipe open
|
||||
const isResetDirection = (this.openAmount > 0) === !(velocity < 0);
|
||||
const isMovingFast = Math.abs(velocity) > 0.3;
|
||||
const isOnCloseZone = Math.abs(this.openAmount) < Math.abs(restingPoint / 2);
|
||||
if (swipeShouldReset(isResetDirection, isMovingFast, isOnCloseZone)) {
|
||||
restingPoint = 0;
|
||||
}
|
||||
|
||||
this.setOpenAmount(restingPoint, true);
|
||||
|
||||
if (this.state & SlidingState.SwipeRight) {
|
||||
this.rightOptions.fireSwipeEvent(this);
|
||||
} else if (this.state & SlidingState.SwipeLeft) {
|
||||
this.leftOptions.fireSwipeEvent(this);
|
||||
}
|
||||
}
|
||||
|
||||
private calculateOptsWidth() {
|
||||
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 setOpenAmount(openAmount: number, isFinal: boolean) {
|
||||
if (this.tmr) {
|
||||
clearTimeout(this.tmr);
|
||||
this.tmr = null;
|
||||
}
|
||||
const style = this.item.style;
|
||||
this.openAmount = openAmount;
|
||||
|
||||
if (isFinal) {
|
||||
style.transition = '';
|
||||
|
||||
} else if (openAmount > 0) {
|
||||
this.state = (openAmount >= (this.optsWidthRightSide + SWIPE_MARGIN))
|
||||
? SlidingState.Right | SlidingState.SwipeRight
|
||||
: SlidingState.Right;
|
||||
|
||||
} else if (openAmount < 0) {
|
||||
this.state = (openAmount <= (-this.optsWidthLeftSide - SWIPE_MARGIN))
|
||||
? SlidingState.Left | SlidingState.SwipeLeft
|
||||
: SlidingState.Left;
|
||||
}
|
||||
|
||||
if (openAmount === 0) {
|
||||
this.tmr = setTimeout(() => {
|
||||
this.state = SlidingState.Disabled;
|
||||
this.tmr = null;
|
||||
}, 600);
|
||||
this.list && this.list.setOpenedItem(null);
|
||||
style.transform = '';
|
||||
return;
|
||||
}
|
||||
|
||||
style.transform = `translate3d(${-openAmount}px,0,0)`;
|
||||
this.ionDrag.emit(this);
|
||||
}
|
||||
|
||||
protected hostData() {
|
||||
return {
|
||||
class: {
|
||||
'item-wrapper': true,
|
||||
@ -485,19 +429,7 @@ export class ItemSliding {
|
||||
|
||||
protected render() {
|
||||
return (
|
||||
<ion-gesture {...{
|
||||
'canStart': this.canStart.bind(this),
|
||||
'onStart': this.onDragStart.bind(this),
|
||||
'onMove': this.onDragMove.bind(this),
|
||||
'onEnd': this.onDragEnd.bind(this),
|
||||
'gestureName': 'item-swipe',
|
||||
'gesturePriority': -10,
|
||||
'type': 'pan',
|
||||
'direction': 'x',
|
||||
'maxAngle': 20,
|
||||
'threshold': 5,
|
||||
'attachTo': 'parent'
|
||||
}}>
|
||||
<ion-gesture {...this.gestureOptions}>
|
||||
<slot></slot>
|
||||
</ion-gesture>
|
||||
);
|
||||
|
@ -29,13 +29,13 @@ $item-wp-sliding-button-icon-color: color-contrast($colors-wp, $item-wp
|
||||
border-bottom: 1px solid $list-wp-border-color;
|
||||
}
|
||||
|
||||
.item-option-button-wp {
|
||||
.item-option-wp {
|
||||
font-size: $item-wp-sliding-button-font-size;
|
||||
color: $item-wp-sliding-button-text-color;
|
||||
background-color: $item-wp-sliding-button-background-color;
|
||||
}
|
||||
|
||||
.item-option-button-wp .icon {
|
||||
.item-option-wp .icon {
|
||||
fill: $item-wp-sliding-button-icon-color;
|
||||
}
|
||||
|
||||
@ -52,12 +52,12 @@ $item-wp-sliding-button-icon-color: color-contrast($colors-wp, $item-wp
|
||||
|
||||
@each $color-name, $color-base, $color-contrast in get-colors($colors-wp) {
|
||||
|
||||
.item-option-button-wp-#{$color-name} {
|
||||
item-option-wp-#{$color-name} {
|
||||
color: $color-contrast;
|
||||
background-color: $color-base;
|
||||
}
|
||||
|
||||
.item-option-button-wp-#{$color-name} .icon {
|
||||
item-option-wp-#{$color-name} .icon {
|
||||
fill: $color-contrast;
|
||||
}
|
||||
|
||||
|
@ -143,13 +143,13 @@
|
||||
<p>I think I figured out how to get more Mountain Dew</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options side="left" (ionSwipe)="unread($event)" class="sliding-enabled">
|
||||
<ion-item-options side="left" class="sliding-enabled">
|
||||
<ion-item-option color="secondary" expandable onclick="unread('item2')">
|
||||
<ion-icon name="ios-checkmark"></ion-icon>Unread
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
|
||||
<ion-item-options side="right" (ionSwipe)="del('item2')" class="sliding-enabled">
|
||||
<ion-item-options side="right" class="sliding-enabled">
|
||||
<ion-item-option color="primary" onclick="archive('item2')">
|
||||
<ion-icon name="mail"></ion-icon>Archive
|
||||
</ion-item-option>
|
||||
@ -166,13 +166,13 @@
|
||||
<p>I think I figured out how to get more Mountain Dew</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options side="left" icon-start (ionSwipe)="unread($event)" class="sliding-enabled">
|
||||
<ion-item-options side="left" icon-start class="sliding-enabled">
|
||||
<ion-item-option color="secondary" expandable onclick="unread('item3')">
|
||||
<ion-icon name="ios-checkmark"></ion-icon>Unread
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
|
||||
<ion-item-options icon-start (ionSwipe)="del('item3')">
|
||||
<ion-item-options icon-start>
|
||||
<ion-item-option color="primary" onclick="archive('item3')">
|
||||
<ion-icon name="mail"></ion-icon>Archive
|
||||
</ion-item-option>
|
||||
@ -190,7 +190,7 @@
|
||||
One Line w/ Icon, div only text
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options icon-start (ionSwipe)="archive($event)">
|
||||
<ion-item-options icon-start>
|
||||
<ion-item-option color="primary" onclick="archive('item4')" expandable class="sliding-enabled">
|
||||
<ion-icon name="archive"></ion-icon>Archive
|
||||
</ion-item-option>
|
||||
@ -249,7 +249,7 @@
|
||||
<p>Paragraph text.</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options (ionSwipe)="download('item8')">
|
||||
<ion-item-options>
|
||||
<ion-item-option color="primary" onclick="archive('item8')">
|
||||
<ion-icon name="archive"></ion-icon>Archive
|
||||
</ion-item-option>
|
||||
@ -379,6 +379,9 @@
|
||||
function reload() {
|
||||
window.location.reload();
|
||||
}
|
||||
document.addEventListener('ionSwipe', (ev)=> console.log('SWIPE!!', ev.detail));
|
||||
document.addEventListener('ionDrag', (ev)=> console.log('DRAG!!', ev.detail.getOpenAmount()));
|
||||
|
||||
</script>
|
||||
<style>
|
||||
img {
|
||||
|
@ -28,9 +28,13 @@ export class List {
|
||||
}
|
||||
|
||||
@Method()
|
||||
closeSlidingItems() {
|
||||
this.openedItem && this.openedItem.close();
|
||||
closeSlidingItems(): boolean {
|
||||
if (this.openedItem) {
|
||||
this.openedItem.close();
|
||||
this.openedItem = null;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
|
@ -85,7 +85,7 @@ export class MenuController {
|
||||
* time. If there are multiple menus on the same side, then enabling one menu
|
||||
* will also automatically disable all the others that are on the same side.
|
||||
* @param {string} [menuId] Optionally get the menu by its id, or side.
|
||||
* @return {Menu} Returns the instance of the menu, which is useful for chaining.
|
||||
* @return {HTMLIonMenuElement} Returns the instance of the menu, which is useful for chaining.
|
||||
*/
|
||||
@Method()
|
||||
enable(shouldEnable: boolean, menuId?: string): HTMLIonMenuElement {
|
||||
@ -100,7 +100,7 @@ export class MenuController {
|
||||
* Used to enable or disable the ability to swipe open the menu.
|
||||
* @param {boolean} shouldEnable True if it should be swipe-able, false if not.
|
||||
* @param {string} [menuId] Optionally get the menu by its id, or side.
|
||||
* @return {Menu} Returns the instance of the menu, which is useful for chaining.
|
||||
* @return {HTMLIonMenuElement} Returns the instance of the menu, which is useful for chaining.
|
||||
*/
|
||||
@Method()
|
||||
swipeEnable(shouldEnable: boolean, menuId?: string): HTMLIonMenuElement {
|
||||
@ -145,7 +145,7 @@ export class MenuController {
|
||||
* provided, then it'll try to find the menu using the menu's `id`
|
||||
* property. If a menu is not found then it'll return `null`.
|
||||
* @param {string} [menuId] Optionally get the menu by its id, or side.
|
||||
* @return {Menu} Returns the instance of the menu if found, otherwise `null`.
|
||||
* @return {HTMLIonMenuElement} Returns the instance of the menu if found, otherwise `null`.
|
||||
*/
|
||||
@Method()
|
||||
get(menuId?: string): HTMLIonMenuElement {
|
||||
@ -188,7 +188,7 @@ export class MenuController {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Array<Menu>} Returns an array of all menu instances.
|
||||
* @return {Array<HTMLIonMenuElement>} Returns an array of all menu instances.
|
||||
*/
|
||||
@Method()
|
||||
getMenus(): HTMLIonMenuElement[] {
|
||||
|
@ -82,8 +82,7 @@ export class Menu {
|
||||
@Prop() side: Side = 'start';
|
||||
@PropDidChange('side')
|
||||
sideChanged() {
|
||||
const isRTL = false;
|
||||
this.isRightSide = isRightSide(this.side, isRTL);
|
||||
this.isRightSide = isRightSide(this.side);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -175,7 +175,8 @@ export function checkEdgeSide(posX: number, isRightSide: boolean, maxEdgeStart:
|
||||
* @param isRTL whether the application dir is rtl
|
||||
* @param defaultRight whether the default side is right
|
||||
*/
|
||||
export function isRightSide(side: Side, isRTL: boolean, defaultRight: boolean = false): boolean {
|
||||
export function isRightSide(side: Side, defaultRight: boolean = false): boolean {
|
||||
const isRTL = document.dir === 'rtl';
|
||||
switch (side) {
|
||||
case 'right': return true;
|
||||
case 'left': return false;
|
||||
|
Reference in New Issue
Block a user