mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
perf(app): tap-click is a ES
we can finally remove the slot!
This commit is contained in:
@@ -39,7 +39,6 @@ exports.config.outputTargets = [
|
||||
'ion-menu-controller',
|
||||
'ion-animation-controller',
|
||||
'ion-animation-controller',
|
||||
'ion-gesture-controller',
|
||||
|
||||
// navigation
|
||||
'ion-router',
|
||||
@@ -51,8 +50,6 @@ exports.config.outputTargets = [
|
||||
'ion-tab-button',
|
||||
|
||||
// auxiliar
|
||||
'ion-gesture',
|
||||
'ion-tap-click',
|
||||
'ion-picker-column',
|
||||
'ion-range-knob',
|
||||
'ion-backdrop',
|
||||
|
||||
8884
core/src/components.d.ts
vendored
8884
core/src/components.d.ts
vendored
File diff suppressed because it is too large
Load Diff
@@ -44,7 +44,6 @@ ion-app.is-device {
|
||||
// Misc
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-tap-click,
|
||||
ion-route,
|
||||
ion-route-redirect,
|
||||
ion-router,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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/)*
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
155
core/src/utils/tap-click.ts
Normal file
155
core/src/utils/tap-click.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
|
||||
import { now, pointerCoord } from './helpers';
|
||||
|
||||
export function startTapClick(doc: Document) {
|
||||
let lastTouch = -MOUSE_WAIT * 10;
|
||||
let lastActivated = 0;
|
||||
let cancelled = false;
|
||||
|
||||
let activatableEle: HTMLElement | undefined;
|
||||
let activeDefer: any;
|
||||
|
||||
const clearDefers = new WeakMap<HTMLElement, any>();
|
||||
|
||||
function onBodyClick(ev: Event) {
|
||||
if (cancelled) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
// Touch Events
|
||||
function onTouchStart(ev: TouchEvent) {
|
||||
lastTouch = now(ev);
|
||||
pointerDown(ev);
|
||||
}
|
||||
|
||||
function onTouchEnd(ev: TouchEvent) {
|
||||
lastTouch = now(ev);
|
||||
pointerUp(ev);
|
||||
}
|
||||
|
||||
function onMouseDown(ev: MouseEvent) {
|
||||
const t = now(ev) - MOUSE_WAIT;
|
||||
if (lastTouch < t) {
|
||||
pointerDown(ev);
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseUp(ev: MouseEvent) {
|
||||
const t = now(ev) - MOUSE_WAIT;
|
||||
if (lastTouch < t) {
|
||||
pointerUp(ev);
|
||||
}
|
||||
}
|
||||
|
||||
function cancelActive() {
|
||||
clearTimeout(activeDefer);
|
||||
if (activatableEle) {
|
||||
removeActivated(false);
|
||||
activatableEle = undefined;
|
||||
}
|
||||
cancelled = true;
|
||||
}
|
||||
|
||||
function pointerDown(ev: any) {
|
||||
if (activatableEle) {
|
||||
return;
|
||||
}
|
||||
cancelled = false;
|
||||
setActivatedElement(getActivatableTarget(ev.target), ev);
|
||||
}
|
||||
|
||||
function pointerUp(ev: UIEvent) {
|
||||
setActivatedElement(undefined, ev);
|
||||
if (cancelled && ev.cancelable) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
function setActivatedElement(el: HTMLElement | undefined, ev: UIEvent) {
|
||||
// do nothing
|
||||
if (el && el === activatableEle) {
|
||||
return;
|
||||
}
|
||||
clearTimeout(activeDefer);
|
||||
activeDefer = undefined;
|
||||
|
||||
const { x, y } = pointerCoord(ev);
|
||||
|
||||
// unactivate selected
|
||||
if (activatableEle) {
|
||||
if (clearDefers.has(activatableEle)) {
|
||||
throw new Error('internal error');
|
||||
}
|
||||
if (!activatableEle.classList.contains(ACTIVATED)) {
|
||||
addActivated(activatableEle, x, y);
|
||||
}
|
||||
removeActivated(true);
|
||||
}
|
||||
|
||||
// activate
|
||||
if (el) {
|
||||
const deferId = clearDefers.get(el);
|
||||
if (deferId) {
|
||||
clearTimeout(deferId);
|
||||
clearDefers.delete(el);
|
||||
}
|
||||
|
||||
el.classList.remove(ACTIVATED);
|
||||
activeDefer = setTimeout(() => {
|
||||
addActivated(el, x, y);
|
||||
activeDefer = undefined;
|
||||
}, ADD_ACTIVATED_DEFERS);
|
||||
}
|
||||
activatableEle = el;
|
||||
}
|
||||
|
||||
function addActivated(el: HTMLElement, x: number, y: number) {
|
||||
lastActivated = Date.now();
|
||||
el.classList.add(ACTIVATED);
|
||||
|
||||
const event = new CustomEvent('ionActivated', {
|
||||
bubbles: false,
|
||||
detail: { x, y }
|
||||
});
|
||||
el.dispatchEvent(event);
|
||||
}
|
||||
|
||||
function removeActivated(smooth: boolean) {
|
||||
const active = activatableEle;
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
const time = CLEAR_STATE_DEFERS - Date.now() + lastActivated;
|
||||
if (smooth && time > 0) {
|
||||
const deferId = setTimeout(() => {
|
||||
active.classList.remove(ACTIVATED);
|
||||
clearDefers.delete(active);
|
||||
}, CLEAR_STATE_DEFERS);
|
||||
clearDefers.set(active, deferId);
|
||||
} else {
|
||||
active.classList.remove(ACTIVATED);
|
||||
}
|
||||
}
|
||||
|
||||
doc.body.addEventListener('click', onBodyClick, true);
|
||||
doc.body.addEventListener('ionScrollStart', cancelActive);
|
||||
doc.body.addEventListener('ionGestureCaptured', cancelActive);
|
||||
|
||||
doc.addEventListener('touchstart', onTouchStart, true);
|
||||
doc.addEventListener('touchcancel', onTouchEnd, true);
|
||||
doc.addEventListener('touchend', onTouchEnd, true);
|
||||
|
||||
doc.addEventListener('mousedown', onMouseDown, true);
|
||||
doc.addEventListener('mouseup', onMouseUp, true);
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -51,13 +51,3 @@ export async function openURL(win: Window, url: string | undefined, ev: Event, d
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
export function getParentNode(node: Node) {
|
||||
// this also checks for host elements of shadow root node
|
||||
// if the parent node is a document fragment (shadow root)
|
||||
// then use the "host" property on it
|
||||
// otherwise use the parent node
|
||||
// DOCUMENT_FRAGMENT_NODE nodeType === 11
|
||||
const parentNode: any = node.parentNode;
|
||||
return (parentNode && parentNode.NODE_TYPE === 11 ? parentNode.host : parentNode);
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ export const config: Config = {
|
||||
bundles: [
|
||||
{ components: ['ion-action-sheet', 'ion-action-sheet-controller'] },
|
||||
{ components: ['ion-alert', 'ion-alert-controller'] },
|
||||
{ components: ['ion-anchor', 'ion-back-button', 'ion-nav', 'ion-nav-pop', 'ion-nav-push', 'ion-nav-set-root'] },
|
||||
{ components: ['ion-anchor', 'ion-back-button'] },
|
||||
{ components: ['ion-animation-controller'] },
|
||||
{ components: ['ion-app', 'ion-buttons', 'ion-content', 'ion-footer', 'ion-header', 'ion-scroll', 'ion-tap-click', 'ion-title', 'ion-toolbar'] },
|
||||
{ components: ['ion-app', 'ion-buttons', 'ion-content', 'ion-footer', 'ion-header', 'ion-scroll', 'ion-title', 'ion-toolbar'] },
|
||||
{ components: ['ion-avatar', 'ion-badge', 'ion-thumbnail'] },
|
||||
{ components: ['ion-backdrop'] },
|
||||
{ components: ['ion-button', 'ion-icon'] },
|
||||
@@ -20,13 +20,15 @@ export const config: Config = {
|
||||
{ components: ['ion-grid', 'ion-row', 'ion-col'] },
|
||||
{ components: ['ion-hide-when', 'ion-show-when'] },
|
||||
{ components: ['ion-infinite-scroll', 'ion-infinite-scroll-content'] },
|
||||
{ components: ['ion-input', 'ion-textarea'] },
|
||||
{ components: ['ion-item', 'ion-item-divider', 'ion-item-group', 'ion-label', 'ion-list', 'ion-list-header', 'ion-skeleton-text'] },
|
||||
{ components: ['ion-input'] },
|
||||
{ components: ['ion-textarea'] },
|
||||
{ components: ['ion-item', 'ion-item-divider', 'ion-item-group', 'ion-label', 'ion-list', 'ion-list-header', 'ion-skeleton-text', 'ion-note'] },
|
||||
{ components: ['ion-item-sliding', 'ion-item-options', 'ion-item-option'] },
|
||||
{ components: ['ion-loading', 'ion-loading-controller'] },
|
||||
{ components: ['ion-menu', 'ion-menu-controller', 'ion-menu-toggle', 'ion-menu-button'] },
|
||||
{ components: ['ion-modal', 'ion-modal-controller'] },
|
||||
{ components: ['ion-note', 'ion-img', 'ion-text'] },
|
||||
{ components: ['ion-nav', 'ion-nav-pop', 'ion-nav-push', 'ion-nav-set-root'] },
|
||||
{ components: ['ion-img'] },
|
||||
{ components: ['ion-popover', 'ion-popover-controller'] },
|
||||
{ components: ['ion-radio', 'ion-radio-group'] },
|
||||
{ components: ['ion-range', 'ion-range-knob'] },
|
||||
@@ -41,6 +43,7 @@ export const config: Config = {
|
||||
{ components: ['ion-spinner'] },
|
||||
{ components: ['ion-split-pane'] },
|
||||
{ components: ['ion-tabs', 'ion-tab', 'ion-tabbar', 'ion-tab-button'] },
|
||||
{ components: ['ion-text'] },
|
||||
{ components: ['ion-toast', 'ion-toast-controller'] },
|
||||
{ components: ['ion-toggle'] },
|
||||
{ components: ['ion-virtual-scroll'] },
|
||||
|
||||
Reference in New Issue
Block a user