fix(ripple-effect): using method invocation instead of events

fixes #15318
This commit is contained in:
Manu Mtz.-Almeida
2018-08-24 22:58:12 +02:00
parent 926758e585
commit 4a45effe8c
16 changed files with 30 additions and 82 deletions

View File

@ -340,7 +340,7 @@ export class InfiniteScroll {
constructor(r: ElementRef) { constructor(r: ElementRef) {
const el = r.nativeElement; const el = r.nativeElement;
proxyMethods(this, el, ['complete', 'waitFor']); proxyMethods(this, el, ['complete']);
proxyInputs(this, el, ['threshold', 'disabled', 'position']); proxyInputs(this, el, ['threshold', 'disabled', 'position']);
proxyOutputs(this, el, ['ionInfinite']); proxyOutputs(this, el, ['ionInfinite']);
} }
@ -640,13 +640,12 @@ export class ReorderGroup {
} }
export declare interface RippleEffect extends Promisify<StencilComponents<'IonRippleEffect'>> {} export declare interface RippleEffect extends Promisify<StencilComponents<'IonRippleEffect'>> {}
@Component({ selector: 'ion-ripple-effect', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: '<ng-content></ng-content>', inputs: ['parent', 'tapClick'] }) @Component({ selector: 'ion-ripple-effect', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: '<ng-content></ng-content>' })
export class RippleEffect { export class RippleEffect {
constructor(r: ElementRef) { constructor(r: ElementRef) {
const el = r.nativeElement; const el = r.nativeElement;
proxyMethods(this, el, ['addRipple']); proxyMethods(this, el, ['addRipple']);
proxyInputs(this, el, ['parent', 'tapClick']);
} }
} }

View File

@ -3517,19 +3517,8 @@ export namespace Components {
* Adds the ripple effect to the parent element * Adds the ripple effect to the parent element
*/ */
'addRipple': (pageX: number, pageY: number) => void; 'addRipple': (pageX: number, pageY: number) => void;
'parent': HTMLElement | string;
/**
* If true, the ripple effect will listen to any click events and animate
*/
'tapClick': boolean;
}
interface IonRippleEffectAttributes extends StencilHTMLAttributes {
'parent'?: HTMLElement | string;
/**
* If true, the ripple effect will listen to any click events and animate
*/
'tapClick'?: boolean;
} }
interface IonRippleEffectAttributes extends StencilHTMLAttributes {}
interface IonRouteRedirect { interface IonRouteRedirect {
/** /**

View File

@ -333,7 +333,7 @@ export class Alert implements OverlayInterface {
{i.label} {i.label}
</div> </div>
</div> </div>
{ this.mode === 'md' && <ion-ripple-effect tapClick={true}/> } { this.mode === 'md' && <ion-ripple-effect /> }
</button> </button>
))} ))}
</div> </div>
@ -362,7 +362,7 @@ export class Alert implements OverlayInterface {
{i.label} {i.label}
</div> </div>
</div> </div>
{ this.mode === 'md' && <ion-ripple-effect tapClick={true}/> } { this.mode === 'md' && <ion-ripple-effect /> }
</button> </button>
))} ))}
</div> </div>

View File

@ -83,7 +83,7 @@ export class BackButton {
<span class="back-button-inner"> <span class="back-button-inner">
{ backButtonIcon && <ion-icon icon={backButtonIcon} lazy={false}/> } { backButtonIcon && <ion-icon icon={backButtonIcon} lazy={false}/> }
{ this.mode === 'ios' && backButtonText && <span class="button-text">{backButtonText}</span> } { this.mode === 'ios' && backButtonText && <span class="button-text">{backButtonText}</span> }
{ this.mode === 'md' && <ion-ripple-effect tapClick={true} parent={this.el}/> } { this.mode === 'md' && <ion-ripple-effect /> }
</span> </span>
</button> </button>
); );

View File

@ -183,7 +183,7 @@ export class Button {
<slot></slot> <slot></slot>
<slot name="end"></slot> <slot name="end"></slot>
</span> </span>
{ this.mode === 'md' && <ion-ripple-effect tapClick={true} parent={this.el} /> } { this.mode === 'md' && <ion-ripple-effect /> }
</TagType> </TagType>
); );
} }

View File

@ -62,7 +62,7 @@ export class ChipButton {
<span class="chip-button-inner"> <span class="chip-button-inner">
<slot></slot> <slot></slot>
</span> </span>
{ this.mode === 'md' && <ion-ripple-effect tapClick={true}/> } { this.mode === 'md' && <ion-ripple-effect /> }
</TagType> </TagType>
); );
} }

View File

@ -537,7 +537,7 @@ export class Datetime {
aria-disabled={this.disabled ? 'true' : null} aria-disabled={this.disabled ? 'true' : null}
onClick={this.open.bind(this)} onClick={this.open.bind(this)}
class="datetime-cover"> class="datetime-cover">
{ this.mode === 'md' && <ion-ripple-effect tapClick={true}/> } { this.mode === 'md' && <ion-ripple-effect /> }
</button> </button>
]; ];
} }

View File

@ -101,7 +101,7 @@ export class FabButton {
<span class="fab-button-inner"> <span class="fab-button-inner">
<slot></slot> <slot></slot>
</span> </span>
{ this.mode === 'md' && <ion-ripple-effect tapClick={true} parent={this.el}/> } { this.mode === 'md' && <ion-ripple-effect /> }
</TagType> </TagType>
); );
} }

View File

@ -36,7 +36,6 @@ Separating the `ion-infinite-scroll` and `ion-infinite-scroll-content` component
| Method | Description | | Method | Description |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `complete` | Call `complete()` within the `infinite` output event handler when your async operation has completed. For example, the `loading` state is while the app is performing an asynchronous operation, such as receiving more data from an AJAX request to add more items to a data list. Once the data has been received and UI updated, you then call this method to signify that the loading has completed. This method will change the infinite scroll's state from `loading` to `enabled`. | | `complete` | Call `complete()` within the `infinite` output event handler when your async operation has completed. For example, the `loading` state is while the app is performing an asynchronous operation, such as receiving more data from an AJAX request to add more items to a data list. Once the data has been received and UI updated, you then call this method to signify that the loading has completed. This method will change the infinite scroll's state from `loading` to `enabled`. |
| `waitFor` | Pass a promise inside `waitFor()` within the `infinite` output event handler in order to change state of infiniteScroll to "complete" |
---------------------------------------------- ----------------------------------------------

View File

@ -139,7 +139,7 @@ export class Item {
} }
render() { render() {
const { href, detail, mode, win, state, detailIcon, el, routerDirection, type } = this; const { href, detail, mode, win, state, detailIcon, routerDirection, type } = this;
const clickable = this.isClickable(); const clickable = this.isClickable();
const TagType = clickable ? (href ? 'a' : 'button') : 'div'; const TagType = clickable ? (href ? 'a' : 'button') : 'div';
@ -161,7 +161,7 @@ export class Item {
{ showDetail && <ion-icon icon={detailIcon} lazy={false} class="item-detail-icon"></ion-icon> } { showDetail && <ion-icon icon={detailIcon} lazy={false} class="item-detail-icon"></ion-icon> }
</div> </div>
{ state && <div class="item-state"></div> } { state && <div class="item-state"></div> }
{ clickable && mode === 'md' && <ion-ripple-effect tapClick={true} parent={el} /> } { clickable && mode === 'md' && <ion-ripple-effect /> }
</TagType> </TagType>
); );
} }

View File

@ -6,14 +6,6 @@ The ripple effect component adds the [Material Design ink ripple interaction eff
<!-- Auto Generated Below --> <!-- Auto Generated Below -->
## Properties
| Property | Attribute | Description | Type |
| ---------- | ----------- | ---------------------------------------------------------------------- | ----------------------- |
| `parent` | `parent` | | `HTMLElement`, `string` |
| `tapClick` | `tap-click` | If true, the ripple effect will listen to any click events and animate | `boolean` |
## Methods ## Methods
| Method | Description | | Method | Description |

View File

@ -1,6 +1,4 @@
import { Component, Element, EventListenerEnable, Listen, Method, Prop, QueueApi, Watch } from '@stencil/core'; import { Component, Element, Method, Prop, QueueApi } from '@stencil/core';
import { now } from '../../utils/helpers';
@Component({ @Component({
tag: 'ion-ripple-effect', tag: 'ion-ripple-effect',
@ -9,48 +7,11 @@ import { now } from '../../utils/helpers';
}) })
export class RippleEffect { export class RippleEffect {
private lastClick = -10000;
@Element() el!: HTMLElement; @Element() el!: HTMLElement;
@Prop({ context: 'queue' }) queue!: QueueApi; @Prop({ context: 'queue' }) queue!: QueueApi;
@Prop({ context: 'enableListener' }) enableListener!: EventListenerEnable;
@Prop({ context: 'window' }) win!: Window; @Prop({ context: 'window' }) win!: Window;
@Prop() parent?: HTMLElement | string = 'parent';
/** If true, the ripple effect will listen to any click events and animate */
@Prop() tapClick = false;
@Watch('tapClick')
tapClickChanged(tapClick: boolean) {
this.enableListener(this, 'ionActivated', tapClick, this.parent);
this.enableListener(this, 'touchstart', !tapClick);
this.enableListener(this, 'mousedown', !tapClick);
}
@Listen('ionActivated', { enabled: false })
ionActivated(ev: CustomEvent) {
this.addRipple(ev.detail.x, ev.detail.y);
}
@Listen('touchstart', { passive: true, enabled: false })
touchStart(ev: TouchEvent) {
this.lastClick = now(ev);
const touches = ev.touches[0];
this.addRipple(touches.clientX, touches.clientY);
}
@Listen('mousedown', { passive: true, enabled: false })
mouseDown(ev: MouseEvent) {
const timeStamp = now(ev);
if (this.lastClick < (timeStamp - 1000)) {
this.addRipple(ev.pageX, ev.pageY);
}
}
componentDidLoad() {
this.tapClickChanged(this.tapClick);
}
/** /**
* Adds the ripple effect to the parent element * Adds the ripple effect to the parent element
*/ */

View File

@ -75,7 +75,7 @@ export class SegmentButton {
disabled={this.disabled} disabled={this.disabled}
onClick={() => this.checked = true }> onClick={() => this.checked = true }>
<slot></slot> <slot></slot>
{ this.mode === 'md' && <ion-ripple-effect tapClick={true} parent={this.el}/> } { this.mode === 'md' && <ion-ripple-effect /> }
</button> </button>
]; ];
} }

View File

@ -500,7 +500,7 @@ export class Select {
onBlur={this.onBlur.bind(this)} onBlur={this.onBlur.bind(this)}
class="select-cover"> class="select-cover">
<slot></slot> <slot></slot>
{ this.mode === 'md' && <ion-ripple-effect tapClick={true}/> } { this.mode === 'md' && <ion-ripple-effect /> }
</button> </button>
]; ];
} }

View File

@ -130,13 +130,12 @@ export class Tabbar {
if (!tab.disabled) { if (!tab.disabled) {
this.ionTabbarClick.emit(tab); this.ionTabbarClick.emit(tab);
} }
ev.stopPropagation();
ev.preventDefault(); ev.preventDefault();
}}> }}>
{ icon && <ion-icon class="tab-btn-icon" icon={icon} lazy={false}></ion-icon> } { icon && <ion-icon class="tab-btn-icon" icon={icon} lazy={false}></ion-icon> }
{ label && <span class="tab-btn-text">{label}</span> } { label && <span class="tab-btn-text">{label}</span> }
{ badge && <ion-badge class="tab-btn-badge" color={badgeColor}>{badge}</ion-badge> } { badge && <ion-badge class="tab-btn-badge" color={badgeColor}>{badge}</ion-badge> }
{ this.mode === 'md' && <ion-ripple-effect tapClick={true}></ion-ripple-effect> } { this.mode === 'md' && <ion-ripple-effect /> }
</a> </a>
); );
} }

View File

@ -109,11 +109,10 @@ export function startTapClick(doc: Document) {
lastActivated = Date.now(); lastActivated = Date.now();
el.classList.add(ACTIVATED); el.classList.add(ACTIVATED);
const event = new CustomEvent('ionActivated', { const rippleEffect = getRippleEffect(el);
bubbles: false, if (rippleEffect && rippleEffect.addRipple) {
detail: { x, y } rippleEffect.addRipple(x, y);
}); }
el.dispatchEvent(event);
} }
function removeActivated(smooth: boolean) { function removeActivated(smooth: boolean) {
@ -159,6 +158,16 @@ function getActivatableTarget(ev: any): any {
} }
} }
function getRippleEffect(el: HTMLElement) {
if (el.shadowRoot) {
const ripple = el.shadowRoot.querySelector('ion-ripple-effect');
if (ripple) {
return ripple;
}
}
return el.querySelector('ion-ripple-effect');
}
const ACTIVATED = 'activated'; const ACTIVATED = 'activated';
const ADD_ACTIVATED_DEFERS = 200; const ADD_ACTIVATED_DEFERS = 200;
const CLEAR_STATE_DEFERS = 200; const CLEAR_STATE_DEFERS = 200;