perf(app): tap-click is a ES

we can finally remove the slot!
This commit is contained in:
Manu Mtz.-Almeida
2018-08-01 16:24:18 +02:00
parent 49cac8beec
commit b0ac5badc2
11 changed files with 3890 additions and 5535 deletions

View File

@ -44,7 +44,6 @@ ion-app.is-device {
// Misc
// --------------------------------------------------
ion-tap-click,
ion-route,
ion-route-redirect,
ion-router,

View File

@ -13,6 +13,8 @@ import { createThemedClasses } from '../../utils/theme';
})
export class App {
private isDevice = false;
mode!: Mode;
@Element() el!: HTMLElement;
@ -21,13 +23,17 @@ export class App {
@Prop({ context: 'config' }) config!: Config;
@Prop({ context: 'queue' }) queue!: QueueApi;
componentWillLoad() {
this.isDevice = this.config.getBoolean('isDevice', isDevice(this.win));
}
componentDidLoad() {
importTapClick(this.win, this.isDevice);
importInputShims(this.win, this.config);
importStatusTap(this.win, this.config, this.queue);
importStatusTap(this.win, this.isDevice, this.queue);
}
hostData() {
const device = this.config.getBoolean('isDevice', isDevice(this.win));
const hybrid = isHybrid(this.win);
const statusbarPadding = this.config.get('statusbarPadding', hybrid);
@ -35,25 +41,23 @@ export class App {
class: {
...createThemedClasses(this.mode, 'app'),
'is-device': device,
'is-device': this.isDevice,
'is-hydrid': hybrid,
'statusbar-padding': statusbarPadding
}
};
}
}
render() {
return [
<ion-tap-click></ion-tap-click>,
<slot></slot>
];
async function importStatusTap(win: Window, device: boolean, queue: QueueApi) {
if (device) {
(await import('../../utils/status-tap')).startStatusTap(win, queue);
}
}
async function importStatusTap(win: Window, config: Config, queue: QueueApi) {
const device = config.getBoolean('isDevice', isDevice(win));
async function importTapClick(win: Window, device: boolean) {
if (device) {
(await import('../../utils/status-tap')).startStatusTap(win, queue);
(await import('../../utils/tap-click')).startTapClick(win.document);
}
}

View File

@ -1,12 +0,0 @@
# ion-tap-click
TabClick is an internal component with no public API.
<!-- Auto Generated Below -->
----------------------------------------------
*Built with [StencilJS](https://stenciljs.com/)*

View File

@ -1,164 +0,0 @@
import { Component, Element, EventListenerEnable, Listen, Prop } from '@stencil/core';
import { now, pointerCoord } from '../../utils/helpers';
/** @hidden */
@Component({
tag: 'ion-tap-click',
})
export class TapClick {
private lastTouch = -MOUSE_WAIT * 10;
private lastActivated = 0;
private cancelled = false;
private activatableEle?: HTMLElement;
private activeDefer: any;
private clearDefers = new WeakMap<HTMLElement, any>();
@Prop({ context: 'isServer' }) isServer!: boolean;
@Prop({ context: 'enableListener' }) enableListener!: EventListenerEnable;
@Element() el!: HTMLElement;
@Listen('body:click', { passive: false, capture: true })
onBodyClick(ev: Event) {
if (this.cancelled) {
ev.preventDefault();
ev.stopPropagation();
}
}
// Touch Events
@Listen('document:touchstart', { passive: true, capture: true })
onTouchStart(ev: TouchEvent) {
this.lastTouch = now(ev);
this.pointerDown(ev);
}
@Listen('document:touchcancel', { passive: false, capture: true })
@Listen('document:touchend', { passive: false, capture: true })
onTouchEnd(ev: TouchEvent) {
this.lastTouch = now(ev);
this.pointerUp(ev);
}
@Listen('document:mousedown', { passive: true, capture: true })
onMouseDown(ev: MouseEvent) {
const t = now(ev) - MOUSE_WAIT;
if (this.lastTouch < t) {
this.pointerDown(ev);
}
}
@Listen('document:mouseup', { passive: false, capture: true })
onMouseUp(ev: TouchEvent) {
const t = now(ev) - MOUSE_WAIT;
if (this.lastTouch < t) {
this.pointerUp(ev);
}
}
@Listen('body:ionScrollStart')
@Listen('body:ionGestureCaptured')
cancelActive() {
clearTimeout(this.activeDefer);
if (this.activatableEle) {
this.removeActivated(false);
this.activatableEle = undefined;
}
this.cancelled = true;
}
private pointerDown(ev: any) {
if (this.activatableEle) {
return;
}
this.cancelled = false;
this.setActivatedElement(getActivatableTarget(ev.target), ev);
}
private pointerUp(ev: UIEvent) {
this.setActivatedElement(undefined, ev);
if (this.cancelled && ev.cancelable) {
ev.preventDefault();
}
}
private setActivatedElement(el: HTMLElement | undefined, ev: UIEvent) {
// do nothing
const activatableEle = this.activatableEle;
if (el && el === activatableEle) {
return;
}
clearTimeout(this.activeDefer);
this.activeDefer = undefined;
const { x, y } = pointerCoord(ev);
// unactivate selected
if (activatableEle) {
if (this.clearDefers.has(activatableEle)) {
throw new Error('internal error');
}
if (!activatableEle.classList.contains(ACTIVATED)) {
this.addActivated(activatableEle, x, y);
}
this.removeActivated(true);
}
// activate
if (el) {
const deferId = this.clearDefers.get(el);
if (deferId) {
clearTimeout(deferId);
this.clearDefers.delete(el);
}
el.classList.remove(ACTIVATED);
this.activeDefer = setTimeout(() => {
this.addActivated(el, x, y);
this.activeDefer = undefined;
}, ADD_ACTIVATED_DEFERS);
}
this.activatableEle = el;
}
private addActivated(el: HTMLElement, x: number, y: number) {
this.lastActivated = Date.now();
el.classList.add(ACTIVATED);
const event = new CustomEvent('ionActivated', {
bubbles: false,
detail: { x, y }
});
el.dispatchEvent(event);
}
private removeActivated(smooth: boolean) {
const activatableEle = this.activatableEle;
if (!activatableEle) {
return;
}
const time = CLEAR_STATE_DEFERS - Date.now() + this.lastActivated;
if (smooth && time > 0) {
const deferId = setTimeout(() => {
activatableEle.classList.remove(ACTIVATED);
this.clearDefers.delete(activatableEle);
}, CLEAR_STATE_DEFERS);
this.clearDefers.set(activatableEle, deferId);
} else {
activatableEle.classList.remove(ACTIVATED);
}
}
}
function getActivatableTarget(el: HTMLElement): any {
return el.closest(':not([tappable]) > a, :not([tappable]) > button, [tappable]');
}
const ACTIVATED = 'activated';
const ADD_ACTIVATED_DEFERS = 200;
const CLEAR_STATE_DEFERS = 200;
const MOUSE_WAIT = 2500;

View File

@ -1,75 +0,0 @@
<!DOCTYPE html>
<html dir="ltr">
<head>
<meta charset="UTF-8">
<title>Button Effect - Basic</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>
<link rel="stylesheet" type="text/css" href="/css/ionic.min.css">
<style>
.my-block {
position: relative;
background: blue;
color: white;
width: 300px;
height: 100px;
margin: 1rem;
}
</style>
</head>
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Button Effect - Basic</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding no-bounce>
<p>
<ion-button size="small">Small</ion-button>
</p>
<p>
<ion-button size="large">Large</ion-button>
</p>
<p>
<ion-button size="large" fill="outline">Large</ion-button>
</p>
<p>
<ion-button size="large" fill="clear">Large</ion-button>
</p>
<div class="my-block">
<ion-ripple-effect></ion-ripple-effect>
This is just a div + effect behind
<ion-button onclick="buttonClicked()">Nested button</ion-button>
</div>
<div class="my-block">
This is just a div + effect on top
<ion-button onclick="buttonClicked()">Nested button</ion-button>
<ion-ripple-effect></ion-ripple-effect>
</div>
<div class="my-block">
This is just a div + effect
<ion-ripple-effect></ion-ripple-effect>
</div>
</ion-content>
</ion-app>
<script>
function blockClicked() {
console.log('block clicked');
return true;
}
function buttonClicked() {
console.log('button clicked');
}
</script>
</body>
</html>

View File

@ -1,82 +0,0 @@
<!DOCTYPE html>
<html dir="ltr">
<head>
<meta charset="UTF-8">
<title>Button Effect</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>
<link rel="stylesheet" type="text/css" href="/css/ionic.min.css">
<style>
.my-block {
position: relative;
background: blue;
color: white;
width: 300px;
height: 100px;
margin: 1rem;
}
</style>
</head>
<body>
<ion-app>
<ion-header translucent>
<ion-toolbar>
<ion-title>Button Effect</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding no-bounce fullscreen>
<p>
<ion-button size="small">Small</ion-button>
</p>
<p>
<ion-button size="large">Large</ion-button>
</p>
<p>
<ion-button size="large" fill="outline">Large</ion-button>
</p>
<p>
<ion-button size="large" fill="clear">Large</ion-button>
</p>
<div class="my-block">
<ion-ripple-effect></ion-ripple-effect>
This is just a div + effect behind
<ion-button onclick="buttonClicked()">Nested button</ion-button>
</div>
<div class="my-block">
This is just a div + effect on top
<ion-button onclick="buttonClicked()">Nested button</ion-button>
<ion-ripple-effect></ion-ripple-effect>
</div>
<div class="my-block">
This is just a div + effect
<ion-ripple-effect></ion-ripple-effect>
</div>
</ion-content>
</ion-app>
<style>
ion-toolbar {
--background: white;
}
</style>
<script>
function blockClicked() {
console.log('block clicked');
return true;
}
function buttonClicked() {
console.log('button clicked');
}
</script>
</body>
</html>