mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-17 10:41:13 +08:00
feat(tabs): scrollable tabs
This commit is contained in:

committed by
Adam Bradley

parent
a881eb1b82
commit
ec2bf8bfbe
32
packages/core/src/components.d.ts
vendored
32
packages/core/src/components.d.ts
vendored
@ -2707,36 +2707,6 @@ declare global {
|
||||
}
|
||||
|
||||
|
||||
import {
|
||||
TabHighlight as IonTabHighlight
|
||||
} from './components/tab-highlight/tab-highlight';
|
||||
|
||||
declare global {
|
||||
interface HTMLIonTabHighlightElement extends IonTabHighlight, HTMLElement {
|
||||
}
|
||||
var HTMLIonTabHighlightElement: {
|
||||
prototype: HTMLIonTabHighlightElement;
|
||||
new (): HTMLIonTabHighlightElement;
|
||||
};
|
||||
interface HTMLElementTagNameMap {
|
||||
"ion-tab-highlight": HTMLIonTabHighlightElement;
|
||||
}
|
||||
interface ElementTagNameMap {
|
||||
"ion-tab-highlight": HTMLIonTabHighlightElement;
|
||||
}
|
||||
namespace JSX {
|
||||
interface IntrinsicElements {
|
||||
"ion-tab-highlight": JSXElements.IonTabHighlightAttributes;
|
||||
}
|
||||
}
|
||||
namespace JSXElements {
|
||||
export interface IonTabHighlightAttributes extends HTMLAttributes {
|
||||
selectedTab?: HTMLIonTabElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
import {
|
||||
Tab as IonTab
|
||||
} from './components/tab/tab';
|
||||
@ -2805,6 +2775,7 @@ declare global {
|
||||
highlight?: boolean;
|
||||
layout?: string;
|
||||
placement?: string;
|
||||
scrollable?: Boolean;
|
||||
selectedTab?: HTMLIonTabElement;
|
||||
tabs?: HTMLIonTabElement[];
|
||||
translucent?: boolean;
|
||||
@ -2838,6 +2809,7 @@ declare global {
|
||||
namespace JSXElements {
|
||||
export interface IonTabsAttributes extends HTMLAttributes {
|
||||
name?: string;
|
||||
scrollable?: boolean;
|
||||
tabbarHidden?: boolean;
|
||||
tabbarHighlight?: boolean;
|
||||
tabbarLayout?: string;
|
||||
|
@ -148,7 +148,7 @@ export class Scroll {
|
||||
if (easedT < 1) {
|
||||
// do not use DomController here
|
||||
// must use nativeRaf in order to fire in the next frame
|
||||
this.dom.raf(step);
|
||||
self.dom.raf(step);
|
||||
|
||||
} else {
|
||||
stopScroll = true;
|
||||
|
@ -31,6 +31,12 @@ any
|
||||
|
||||
## Events
|
||||
|
||||
#### ionTabButtonDidLoad
|
||||
|
||||
|
||||
#### ionTabButtonDidUnload
|
||||
|
||||
|
||||
#### ionTabbarClick
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, Event, EventEmitter, Listen, Prop } from '@stencil/core';
|
||||
import {Component, Element, Event, EventEmitter, Listen, Prop} from '@stencil/core';
|
||||
|
||||
|
||||
@Component({
|
||||
@ -6,10 +6,22 @@ import { Component, Event, EventEmitter, Listen, Prop } from '@stencil/core';
|
||||
})
|
||||
export class TabButton {
|
||||
|
||||
@Element() el: HTMLElement;
|
||||
|
||||
@Prop() selected = false;
|
||||
@Prop() tab: HTMLIonTabElement;
|
||||
|
||||
@Event() ionTabbarClick: EventEmitter;
|
||||
@Event() ionTabButtonDidLoad: EventEmitter;
|
||||
@Event() ionTabButtonDidUnload: EventEmitter;
|
||||
|
||||
componentDidLoad() {
|
||||
this.ionTabButtonDidLoad.emit({ button: this });
|
||||
}
|
||||
|
||||
componentDidUnload() {
|
||||
this.ionTabButtonDidUnload.emit({ button: this });
|
||||
}
|
||||
|
||||
@Listen('click')
|
||||
protected onClick(ev: UIEvent) {
|
||||
@ -21,7 +33,7 @@ export class TabButton {
|
||||
const selected = this.selected;
|
||||
const tab = this.tab;
|
||||
const hasTitle = !!tab.title;
|
||||
const hasIcon = !! tab.icon;
|
||||
const hasIcon = !!tab.icon;
|
||||
const hasTitleOnly = (hasTitle && !hasIcon);
|
||||
const hasIconOnly = (hasIcon && !hasTitle);
|
||||
const hasBadge = !!tab.badge;
|
||||
|
@ -1,25 +0,0 @@
|
||||
# ion-tab-highlight
|
||||
|
||||
|
||||
|
||||
<!-- Auto Generated Below -->
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
#### selectedTab
|
||||
|
||||
any
|
||||
|
||||
|
||||
## Attributes
|
||||
|
||||
#### selectedTab
|
||||
|
||||
any
|
||||
|
||||
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
*Built by [StencilJS](https://stenciljs.com/)*
|
@ -1,64 +0,0 @@
|
||||
import { Component, Element, Listen, Prop, PropDidChange, State } from '@stencil/core';
|
||||
import { DomController } from '../../index';
|
||||
import { getParentElement } from '../../utils/helpers';
|
||||
|
||||
|
||||
@Component({
|
||||
tag: 'ion-tab-highlight'
|
||||
})
|
||||
export class TabHighlight {
|
||||
|
||||
@Element() private el: HTMLElement;
|
||||
|
||||
@State() animated = false;
|
||||
@State() transform = '';
|
||||
|
||||
@Prop({ context: 'dom' }) dom: DomController;
|
||||
|
||||
@Prop() selectedTab: HTMLIonTabElement;
|
||||
@PropDidChange('selectedTab')
|
||||
selectedTabChanged() {
|
||||
this.updateTransform();
|
||||
}
|
||||
|
||||
@Listen('window:resize')
|
||||
onResize() {
|
||||
this.updateTransform();
|
||||
}
|
||||
|
||||
componentDidLoad() {
|
||||
this.updateTransform();
|
||||
}
|
||||
|
||||
protected updateTransform() {
|
||||
this.dom.read(() => {
|
||||
const btn = this.getSelectedButton();
|
||||
this.transform = (btn)
|
||||
? `translate3d(${btn.offsetLeft}px,0,0) scaleX(${btn.offsetWidth})`
|
||||
: '';
|
||||
if (!this.animated) {
|
||||
setTimeout(() => this.animated = true, 80);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private getSelectedButton(): HTMLIonTabButtonElement {
|
||||
const parent = getParentElement(this.el) as HTMLElement;
|
||||
if (!parent) {
|
||||
return null;
|
||||
}
|
||||
return Array.from(parent.querySelectorAll('ion-tab-button'))
|
||||
.find(btn => btn.selected);
|
||||
}
|
||||
|
||||
hostData() {
|
||||
return {
|
||||
style: {
|
||||
'transform': this.transform
|
||||
},
|
||||
class: {
|
||||
'animated': this.animated,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -22,6 +22,11 @@ string
|
||||
string
|
||||
|
||||
|
||||
#### scrollable
|
||||
|
||||
any
|
||||
|
||||
|
||||
#### selectedTab
|
||||
|
||||
any
|
||||
@ -54,6 +59,11 @@ string
|
||||
string
|
||||
|
||||
|
||||
#### scrollable
|
||||
|
||||
any
|
||||
|
||||
|
||||
#### selectedTab
|
||||
|
||||
any
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Component, Listen, Prop, State } from '@stencil/core';
|
||||
import { createThemedClasses } from '../../utils/theme';
|
||||
|
||||
import {Component, Element, Listen, Prop, PropDidChange, State} from '@stencil/core';
|
||||
import {createThemedClasses} from '../../utils/theme';
|
||||
import {DomController} from "../../index";
|
||||
|
||||
@Component({
|
||||
tag: 'ion-tabbar',
|
||||
@ -12,11 +12,27 @@ export class Tabbar {
|
||||
mode: string;
|
||||
color: string;
|
||||
|
||||
@Element() el: HTMLElement;
|
||||
|
||||
@State() canScrollLeft: boolean = false;
|
||||
@State() canScrollRight: boolean = false;
|
||||
|
||||
@State() hidden = false;
|
||||
|
||||
@Prop({ context: 'dom' }) dom: DomController;
|
||||
@Prop() placement = 'bottom';
|
||||
@Prop() tabs: HTMLIonTabElement[];
|
||||
@Prop() selectedTab: HTMLIonTabElement;
|
||||
@Prop() scrollable:Boolean;
|
||||
@Prop() tabs: HTMLIonTabElement[];
|
||||
|
||||
private scrollEl: HTMLIonScrollElement;
|
||||
|
||||
@PropDidChange('selectedTab')
|
||||
selectedTabChanged() {
|
||||
this.scrollable && this.scrollToSelectedButton();
|
||||
this.highlight && this.updateHighlight();
|
||||
}
|
||||
|
||||
@Prop() layout: string = 'icon-top';
|
||||
@Prop() highlight: boolean = false;
|
||||
@Prop() translucent: boolean = false;
|
||||
@ -33,6 +49,46 @@ export class Tabbar {
|
||||
}
|
||||
}
|
||||
|
||||
@Listen('window:resize')
|
||||
onResize() {
|
||||
this.highlight && this.updateHighlight();
|
||||
}
|
||||
|
||||
@Listen('ionTabButtonDidLoad')
|
||||
@Listen('ionTabButtonDidUnload')
|
||||
onTabButtonLoad() {
|
||||
this.scrollable && this.updateBoundaries();
|
||||
this.highlight && this.updateHighlight()
|
||||
}
|
||||
|
||||
protected analyzeTabs() {
|
||||
const tabs: HTMLIonTabButtonElement[] = Array.from(document.querySelectorAll('ion-tab-button')),
|
||||
scrollLeft: number = this.scrollEl.scrollLeft,
|
||||
tabsWidth: number = this.scrollEl.clientWidth;
|
||||
let previous: {tab: HTMLIonTabButtonElement, amount: number},
|
||||
next: {tab: HTMLIonTabButtonElement, amount: number};
|
||||
|
||||
tabs.forEach((tab: HTMLIonTabButtonElement) => {
|
||||
const left: number = tab.offsetLeft,
|
||||
right: number = left + tab.offsetWidth;
|
||||
|
||||
if (left < scrollLeft) {
|
||||
previous = {tab, amount: left};
|
||||
}
|
||||
|
||||
if (!next && right > (tabsWidth + scrollLeft)) {
|
||||
let amount = right - tabsWidth;
|
||||
next = {tab, amount};
|
||||
}
|
||||
});
|
||||
|
||||
return {previous, next};
|
||||
}
|
||||
|
||||
private getSelectedButton(): HTMLIonTabButtonElement {
|
||||
return Array.from(this.el.querySelectorAll('ion-tab-button'))
|
||||
.find(btn => btn.selected);
|
||||
}
|
||||
|
||||
hostData() {
|
||||
const themedClasses = this.translucent ? createThemedClasses(this.mode, this.color, 'tabbar-translucent') : {};
|
||||
@ -43,6 +99,7 @@ export class Tabbar {
|
||||
const hostClasses = {
|
||||
...themedClasses,
|
||||
'tabbar-hidden': this.hidden,
|
||||
'scrollable': this.scrollable,
|
||||
[layoutClass]: true,
|
||||
[placementClass]: true
|
||||
};
|
||||
@ -54,16 +111,89 @@ export class Tabbar {
|
||||
}
|
||||
|
||||
render() {
|
||||
const selectedTab = this.selectedTab;
|
||||
const dom = this.tabs.map(tab => (
|
||||
<ion-tab-button
|
||||
tab={tab}
|
||||
selected={selectedTab === tab}>
|
||||
</ion-tab-button>
|
||||
));
|
||||
if (this.highlight) {
|
||||
dom.push(<ion-tab-highlight selectedTab={selectedTab}></ion-tab-highlight>);
|
||||
const selectedTab = this.selectedTab,
|
||||
ionTabbarHighlight = this.highlight ? <div class='animated tabbar-highlight'/> as HTMLElement : null,
|
||||
tabButtons = this.tabs.map(tab => <ion-tab-button tab={tab} selected={selectedTab === tab}/>);
|
||||
|
||||
|
||||
if (this.scrollable) {
|
||||
return [
|
||||
<ion-button onClick={() => this.scrollByTab('left')} fill='clear' class={{inactive: !this.canScrollLeft}}>
|
||||
<ion-icon name='arrow-dropleft'/>
|
||||
</ion-button>,
|
||||
<ion-scroll
|
||||
ref={(scrollEl: HTMLIonScrollElement) => {
|
||||
this.scrollEl = scrollEl;
|
||||
}}>
|
||||
{tabButtons}
|
||||
{ionTabbarHighlight}
|
||||
</ion-scroll>,
|
||||
<ion-button onClick={() => this.scrollByTab('right')} fill='clear' class={{inactive: !this.canScrollRight}}>
|
||||
<ion-icon name='arrow-dropright'/>
|
||||
</ion-button>
|
||||
]
|
||||
} else {
|
||||
return [
|
||||
...tabButtons,
|
||||
ionTabbarHighlight
|
||||
]
|
||||
}
|
||||
return dom;
|
||||
}
|
||||
|
||||
protected scrollToSelectedButton() {
|
||||
this.dom.read(() => {
|
||||
const activeTabButton: HTMLIonTabButtonElement = this.getSelectedButton();
|
||||
|
||||
if (activeTabButton) {
|
||||
const scrollLeft: number = this.scrollEl.scrollLeft,
|
||||
tabsWidth: number = this.scrollEl.clientWidth,
|
||||
left: number = activeTabButton.offsetLeft,
|
||||
right: number = left + activeTabButton.offsetWidth;
|
||||
|
||||
let amount;
|
||||
|
||||
if (right > (tabsWidth + scrollLeft)) {
|
||||
amount = right - tabsWidth;
|
||||
} else if (left < scrollLeft) {
|
||||
amount = left;
|
||||
}
|
||||
|
||||
if (amount !== undefined) {
|
||||
this.scrollEl.scrollToPoint(amount, 0, 250).then(() => {
|
||||
this.updateBoundaries();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
scrollByTab(direction: 'left' | 'right') {
|
||||
this.dom.read(() => {
|
||||
const {previous, next} = this.analyzeTabs(),
|
||||
info = direction === 'right' ? next : previous,
|
||||
amount = info && info.amount;
|
||||
|
||||
if (info) {
|
||||
this.scrollEl.scrollToPoint(amount, 0, 250).then(() => {
|
||||
this.updateBoundaries();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateBoundaries() {
|
||||
this.canScrollLeft = this.scrollEl.scrollLeft != 0;
|
||||
this.canScrollRight = this.scrollEl.scrollLeft < (this.scrollEl.scrollWidth - this.scrollEl.offsetWidth);
|
||||
}
|
||||
|
||||
updateHighlight() {
|
||||
this.dom.read(() => {
|
||||
const btn = this.getSelectedButton(),
|
||||
ionTabbarHighlight:HTMLElement = this.highlight && this.el.querySelector('div.tabbar-highlight') as HTMLElement;
|
||||
|
||||
if (btn && ionTabbarHighlight) {
|
||||
ionTabbarHighlight.style.transform = `translate3d(${btn.offsetLeft}px,0,0) scaleX(${btn.offsetWidth})`;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -95,6 +95,11 @@ components to switch to `TabsRoot3`:
|
||||
string
|
||||
|
||||
|
||||
#### scrollable
|
||||
|
||||
boolean
|
||||
|
||||
|
||||
#### tabbarHidden
|
||||
|
||||
boolean
|
||||
@ -127,6 +132,11 @@ boolean
|
||||
string
|
||||
|
||||
|
||||
#### scrollable
|
||||
|
||||
boolean
|
||||
|
||||
|
||||
#### tabbarHidden
|
||||
|
||||
boolean
|
||||
|
@ -20,7 +20,7 @@
|
||||
border-bottom: $tabs-ios-border;
|
||||
}
|
||||
|
||||
.tabbar-ios > ion-tab-highlight {
|
||||
.tabbar-ios .tabbar-highlight {
|
||||
background: $tabs-ios-tab-color-active;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
@import "./tabs";
|
||||
@import "./tabs.md.vars";
|
||||
|
||||
|
||||
.tabbar-md {
|
||||
height: $tabs-md-tab-height;
|
||||
|
||||
@ -11,7 +10,7 @@
|
||||
contain: strict;
|
||||
}
|
||||
|
||||
.tabbar-md > ion-tab-highlight {
|
||||
.tabbar-md .tabbar-highlight {
|
||||
background: $tabs-md-tab-color-active;
|
||||
}
|
||||
|
||||
@ -38,16 +37,30 @@
|
||||
fill: $tabs-md-tab-icon-color;
|
||||
}
|
||||
|
||||
.tabbar-md.scrollable ion-scroll {
|
||||
max-width: 650px;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
.tabbar-md.scrollable ion-tab-button {
|
||||
overflow: hidden;
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
|
||||
.tabbar-md .tab-selected {
|
||||
color: $tabs-md-tab-text-color-active;
|
||||
|
||||
fill: $tabs-md-tab-icon-color-active;
|
||||
}
|
||||
|
||||
|
||||
// Material Design Tab Button Text
|
||||
// --------------------------------------------------
|
||||
|
||||
.tabbar-md.placement-top .tab-selected .tab-button-icon,
|
||||
.tabbar-md.placement-top .tab-selected .tab-button-text {
|
||||
transform: inherit;
|
||||
}
|
||||
|
||||
.tabbar-md .tab-button-text {
|
||||
@include margin($tabs-md-tab-text-margin-top, $tabs-md-tab-text-margin-end, $tabs-md-tab-text-margin-bottom, $tabs-md-tab-text-margin-start);
|
||||
|
||||
@ -74,7 +87,6 @@
|
||||
@include margin(-2px, null, null, null);
|
||||
}
|
||||
|
||||
|
||||
// Material Design Tab Button Icon
|
||||
// --------------------------------------------------
|
||||
|
||||
@ -109,7 +121,6 @@
|
||||
@include transform(translate3d($tabs-md-tab-icon-left-transform-x-active, $tabs-md-tab-icon-left-transform-y-active, $tabs-md-tab-icon-left-transform-z-active));
|
||||
}
|
||||
|
||||
|
||||
// Material Design Tab with Icon or Title only
|
||||
// --------------------------------------------------
|
||||
|
||||
@ -139,13 +150,12 @@
|
||||
fill: $color-contrast;
|
||||
}
|
||||
|
||||
.tabbar-md-#{$color-name} ion-tab-highlight {
|
||||
.tabbar-md-#{$color-name} .tabbar-highlight {
|
||||
background: $color-contrast;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Material Design Tabbar Color Generation
|
||||
// --------------------------------------------------
|
||||
|
||||
|
@ -45,6 +45,7 @@ ion-tabbar {
|
||||
display: flex;
|
||||
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
order: 1;
|
||||
|
||||
@ -165,11 +166,10 @@ ion-tab-button {
|
||||
@include position(null, calc(50% - 50px), null, null);
|
||||
}
|
||||
|
||||
|
||||
// Tab Highlight
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-tab-highlight {
|
||||
.tabbar-highlight {
|
||||
@include position(null, null, 0, 0);
|
||||
@include transform-origin(0, 0);
|
||||
|
||||
@ -191,11 +191,27 @@ ion-tab-highlight {
|
||||
}
|
||||
}
|
||||
|
||||
.placement-top > ion-tab-highlight {
|
||||
.placement-top .tabbar-highlight {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.placement-bottom > ion-tab-highlight {
|
||||
.placement-bottom .tabbar-highlight {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
// Overflow Scrolling
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-tabbar.scrollable ion-scroll {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
ion-tabbar.scrollable .scroll-inner {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
ion-tabbar.scrollable ion-button.inactive {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
@ -55,6 +55,8 @@ export class Tabs {
|
||||
*/
|
||||
@Prop() translucent: boolean = false;
|
||||
|
||||
@Prop() scrollable: boolean = false;
|
||||
|
||||
/**
|
||||
* @output {any} Emitted when the tab changes.
|
||||
*/
|
||||
@ -102,7 +104,6 @@ export class Tabs {
|
||||
}
|
||||
selectedTab.selected = true;
|
||||
|
||||
console.log('HEY');
|
||||
// The same selected was selected
|
||||
// we need to set root in the nested ion-nav if it exist
|
||||
if (this.selectedTab === selectedTab) {
|
||||
@ -165,7 +166,6 @@ export class Tabs {
|
||||
|
||||
@Method()
|
||||
getRoutes(): RouterEntries {
|
||||
debugger;
|
||||
const a = this.tabs.map(t => {
|
||||
return {
|
||||
path: t.getPath(),
|
||||
@ -231,7 +231,8 @@ export class Tabs {
|
||||
highlight={this.tabbarHighlight}
|
||||
placement={this.tabbarPlacement}
|
||||
layout={this.tabbarLayout}
|
||||
translucent={this.translucent}>
|
||||
translucent={this.translucent}
|
||||
scrollable={this.scrollable}>
|
||||
</ion-tabbar>
|
||||
);
|
||||
}
|
||||
|
34
packages/core/src/components/tabs/test/scroll/index.html
Normal file
34
packages/core/src/components/tabs/test/scroll/index.html
Normal file
@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Tab - Scroll</title>
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||||
<script src="/dist/ionic.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<ion-app>
|
||||
<ion-tabs scrollable="true" tabbar-placement="top" tabbar-highlight="true" class="tabbar-md-light">
|
||||
<ion-tab title="Plain List" icon="star"></ion-tab>
|
||||
<ion-tab title="Schedule Long Text" icon="globe"></ion-tab>
|
||||
<ion-tab title="Stopwatch" icon="logo-facebook"></ion-tab>
|
||||
<ion-tab title="Messages" icon="chatboxes"></ion-tab>
|
||||
<ion-tab title="Messages 2" icon="chatboxes"></ion-tab>
|
||||
<ion-tab title="Messages 3" icon="chatboxes"></ion-tab>
|
||||
<ion-tab title="Messages 4" icon="chatboxes"></ion-tab>
|
||||
<ion-tab title="Messages 5" icon="chatboxes"></ion-tab>
|
||||
<ion-tab title="Messages 6" icon="chatboxes"></ion-tab>
|
||||
<ion-tab title="Messages 7" icon="chatboxes"></ion-tab>
|
||||
<ion-tab title="Messages 8" icon="chatboxes"></ion-tab>
|
||||
<ion-tab title="Messages 9" icon="chatboxes"></ion-tab>
|
||||
<ion-tab title="Messages 10" icon="chatboxes"></ion-tab>
|
||||
<ion-tab title="Messages 11" icon="chatboxes"></ion-tab>
|
||||
<ion-tab title="Messages 12" icon="chatboxes"></ion-tab>
|
||||
</ion-tabs>
|
||||
|
||||
<ion-nav-controller></ion-nav-controller>
|
||||
</ion-app>
|
||||
</body>
|
||||
</html>
|
@ -36,7 +36,7 @@ exports.config = {
|
||||
{ components: ['ion-spinner'] },
|
||||
{ components: ['ion-split-pane'] },
|
||||
{ components: ['ion-range', 'ion-range-knob']},
|
||||
{ components: ['ion-tabs', 'ion-tab', 'ion-tabbar', 'ion-tab-button', 'ion-tab-highlight'] },
|
||||
{ components: ['ion-tabs', 'ion-tab', 'ion-tabbar', 'ion-tab-button'] },
|
||||
{ components: ['ion-toggle'] },
|
||||
{ components: ['ion-nav'] },
|
||||
{ components: ['ion-toast', 'ion-toast-controller'] },
|
||||
|
Reference in New Issue
Block a user