mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 03:00:58 +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 {
|
import {
|
||||||
Tab as IonTab
|
Tab as IonTab
|
||||||
} from './components/tab/tab';
|
} from './components/tab/tab';
|
||||||
@ -2805,6 +2775,7 @@ declare global {
|
|||||||
highlight?: boolean;
|
highlight?: boolean;
|
||||||
layout?: string;
|
layout?: string;
|
||||||
placement?: string;
|
placement?: string;
|
||||||
|
scrollable?: Boolean;
|
||||||
selectedTab?: HTMLIonTabElement;
|
selectedTab?: HTMLIonTabElement;
|
||||||
tabs?: HTMLIonTabElement[];
|
tabs?: HTMLIonTabElement[];
|
||||||
translucent?: boolean;
|
translucent?: boolean;
|
||||||
@ -2838,6 +2809,7 @@ declare global {
|
|||||||
namespace JSXElements {
|
namespace JSXElements {
|
||||||
export interface IonTabsAttributes extends HTMLAttributes {
|
export interface IonTabsAttributes extends HTMLAttributes {
|
||||||
name?: string;
|
name?: string;
|
||||||
|
scrollable?: boolean;
|
||||||
tabbarHidden?: boolean;
|
tabbarHidden?: boolean;
|
||||||
tabbarHighlight?: boolean;
|
tabbarHighlight?: boolean;
|
||||||
tabbarLayout?: string;
|
tabbarLayout?: string;
|
||||||
|
@ -148,7 +148,7 @@ export class Scroll {
|
|||||||
if (easedT < 1) {
|
if (easedT < 1) {
|
||||||
// do not use DomController here
|
// do not use DomController here
|
||||||
// must use nativeRaf in order to fire in the next frame
|
// must use nativeRaf in order to fire in the next frame
|
||||||
this.dom.raf(step);
|
self.dom.raf(step);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
stopScroll = true;
|
stopScroll = true;
|
||||||
|
@ -31,6 +31,12 @@ any
|
|||||||
|
|
||||||
## Events
|
## Events
|
||||||
|
|
||||||
|
#### ionTabButtonDidLoad
|
||||||
|
|
||||||
|
|
||||||
|
#### ionTabButtonDidUnload
|
||||||
|
|
||||||
|
|
||||||
#### ionTabbarClick
|
#### 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({
|
@Component({
|
||||||
@ -6,10 +6,22 @@ import { Component, Event, EventEmitter, Listen, Prop } from '@stencil/core';
|
|||||||
})
|
})
|
||||||
export class TabButton {
|
export class TabButton {
|
||||||
|
|
||||||
|
@Element() el: HTMLElement;
|
||||||
|
|
||||||
@Prop() selected = false;
|
@Prop() selected = false;
|
||||||
@Prop() tab: HTMLIonTabElement;
|
@Prop() tab: HTMLIonTabElement;
|
||||||
|
|
||||||
@Event() ionTabbarClick: EventEmitter;
|
@Event() ionTabbarClick: EventEmitter;
|
||||||
|
@Event() ionTabButtonDidLoad: EventEmitter;
|
||||||
|
@Event() ionTabButtonDidUnload: EventEmitter;
|
||||||
|
|
||||||
|
componentDidLoad() {
|
||||||
|
this.ionTabButtonDidLoad.emit({ button: this });
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUnload() {
|
||||||
|
this.ionTabButtonDidUnload.emit({ button: this });
|
||||||
|
}
|
||||||
|
|
||||||
@Listen('click')
|
@Listen('click')
|
||||||
protected onClick(ev: UIEvent) {
|
protected onClick(ev: UIEvent) {
|
||||||
@ -21,7 +33,7 @@ export class TabButton {
|
|||||||
const selected = this.selected;
|
const selected = this.selected;
|
||||||
const tab = this.tab;
|
const tab = this.tab;
|
||||||
const hasTitle = !!tab.title;
|
const hasTitle = !!tab.title;
|
||||||
const hasIcon = !! tab.icon;
|
const hasIcon = !!tab.icon;
|
||||||
const hasTitleOnly = (hasTitle && !hasIcon);
|
const hasTitleOnly = (hasTitle && !hasIcon);
|
||||||
const hasIconOnly = (hasIcon && !hasTitle);
|
const hasIconOnly = (hasIcon && !hasTitle);
|
||||||
const hasBadge = !!tab.badge;
|
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
|
string
|
||||||
|
|
||||||
|
|
||||||
|
#### scrollable
|
||||||
|
|
||||||
|
any
|
||||||
|
|
||||||
|
|
||||||
#### selectedTab
|
#### selectedTab
|
||||||
|
|
||||||
any
|
any
|
||||||
@ -54,6 +59,11 @@ string
|
|||||||
string
|
string
|
||||||
|
|
||||||
|
|
||||||
|
#### scrollable
|
||||||
|
|
||||||
|
any
|
||||||
|
|
||||||
|
|
||||||
#### selectedTab
|
#### selectedTab
|
||||||
|
|
||||||
any
|
any
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Component, Listen, Prop, State } from '@stencil/core';
|
import {Component, Element, Listen, Prop, PropDidChange, State} from '@stencil/core';
|
||||||
import { createThemedClasses } from '../../utils/theme';
|
import {createThemedClasses} from '../../utils/theme';
|
||||||
|
import {DomController} from "../../index";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
tag: 'ion-tabbar',
|
tag: 'ion-tabbar',
|
||||||
@ -12,11 +12,27 @@ export class Tabbar {
|
|||||||
mode: string;
|
mode: string;
|
||||||
color: string;
|
color: string;
|
||||||
|
|
||||||
|
@Element() el: HTMLElement;
|
||||||
|
|
||||||
|
@State() canScrollLeft: boolean = false;
|
||||||
|
@State() canScrollRight: boolean = false;
|
||||||
|
|
||||||
@State() hidden = false;
|
@State() hidden = false;
|
||||||
|
|
||||||
|
@Prop({ context: 'dom' }) dom: DomController;
|
||||||
@Prop() placement = 'bottom';
|
@Prop() placement = 'bottom';
|
||||||
@Prop() tabs: HTMLIonTabElement[];
|
|
||||||
@Prop() selectedTab: 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() layout: string = 'icon-top';
|
||||||
@Prop() highlight: boolean = false;
|
@Prop() highlight: boolean = false;
|
||||||
@Prop() translucent: 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() {
|
hostData() {
|
||||||
const themedClasses = this.translucent ? createThemedClasses(this.mode, this.color, 'tabbar-translucent') : {};
|
const themedClasses = this.translucent ? createThemedClasses(this.mode, this.color, 'tabbar-translucent') : {};
|
||||||
@ -43,6 +99,7 @@ export class Tabbar {
|
|||||||
const hostClasses = {
|
const hostClasses = {
|
||||||
...themedClasses,
|
...themedClasses,
|
||||||
'tabbar-hidden': this.hidden,
|
'tabbar-hidden': this.hidden,
|
||||||
|
'scrollable': this.scrollable,
|
||||||
[layoutClass]: true,
|
[layoutClass]: true,
|
||||||
[placementClass]: true
|
[placementClass]: true
|
||||||
};
|
};
|
||||||
@ -54,16 +111,89 @@ export class Tabbar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const selectedTab = this.selectedTab;
|
const selectedTab = this.selectedTab,
|
||||||
const dom = this.tabs.map(tab => (
|
ionTabbarHighlight = this.highlight ? <div class='animated tabbar-highlight'/> as HTMLElement : null,
|
||||||
<ion-tab-button
|
tabButtons = this.tabs.map(tab => <ion-tab-button tab={tab} selected={selectedTab === tab}/>);
|
||||||
tab={tab}
|
|
||||||
selected={selectedTab === tab}>
|
|
||||||
</ion-tab-button>
|
if (this.scrollable) {
|
||||||
));
|
return [
|
||||||
if (this.highlight) {
|
<ion-button onClick={() => this.scrollByTab('left')} fill='clear' class={{inactive: !this.canScrollLeft}}>
|
||||||
dom.push(<ion-tab-highlight selectedTab={selectedTab}></ion-tab-highlight>);
|
<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
|
string
|
||||||
|
|
||||||
|
|
||||||
|
#### scrollable
|
||||||
|
|
||||||
|
boolean
|
||||||
|
|
||||||
|
|
||||||
#### tabbarHidden
|
#### tabbarHidden
|
||||||
|
|
||||||
boolean
|
boolean
|
||||||
@ -127,6 +132,11 @@ boolean
|
|||||||
string
|
string
|
||||||
|
|
||||||
|
|
||||||
|
#### scrollable
|
||||||
|
|
||||||
|
boolean
|
||||||
|
|
||||||
|
|
||||||
#### tabbarHidden
|
#### tabbarHidden
|
||||||
|
|
||||||
boolean
|
boolean
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
border-bottom: $tabs-ios-border;
|
border-bottom: $tabs-ios-border;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabbar-ios > ion-tab-highlight {
|
.tabbar-ios .tabbar-highlight {
|
||||||
background: $tabs-ios-tab-color-active;
|
background: $tabs-ios-tab-color-active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
@import "./tabs";
|
@import "./tabs";
|
||||||
@import "./tabs.md.vars";
|
@import "./tabs.md.vars";
|
||||||
|
|
||||||
|
|
||||||
.tabbar-md {
|
.tabbar-md {
|
||||||
height: $tabs-md-tab-height;
|
height: $tabs-md-tab-height;
|
||||||
|
|
||||||
@ -11,7 +10,7 @@
|
|||||||
contain: strict;
|
contain: strict;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabbar-md > ion-tab-highlight {
|
.tabbar-md .tabbar-highlight {
|
||||||
background: $tabs-md-tab-color-active;
|
background: $tabs-md-tab-color-active;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,16 +37,30 @@
|
|||||||
fill: $tabs-md-tab-icon-color;
|
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 {
|
.tabbar-md .tab-selected {
|
||||||
color: $tabs-md-tab-text-color-active;
|
color: $tabs-md-tab-text-color-active;
|
||||||
|
|
||||||
fill: $tabs-md-tab-icon-color-active;
|
fill: $tabs-md-tab-icon-color-active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Material Design Tab Button Text
|
// 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 {
|
.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);
|
@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);
|
@include margin(-2px, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Material Design Tab Button Icon
|
// 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));
|
@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
|
// Material Design Tab with Icon or Title only
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
||||||
@ -139,13 +150,12 @@
|
|||||||
fill: $color-contrast;
|
fill: $color-contrast;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabbar-md-#{$color-name} ion-tab-highlight {
|
.tabbar-md-#{$color-name} .tabbar-highlight {
|
||||||
background: $color-contrast;
|
background: $color-contrast;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Material Design Tabbar Color Generation
|
// Material Design Tabbar Color Generation
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
||||||
|
@ -45,6 +45,7 @@ ion-tabbar {
|
|||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
order: 1;
|
order: 1;
|
||||||
|
|
||||||
@ -165,11 +166,10 @@ ion-tab-button {
|
|||||||
@include position(null, calc(50% - 50px), null, null);
|
@include position(null, calc(50% - 50px), null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Tab Highlight
|
// Tab Highlight
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
||||||
ion-tab-highlight {
|
.tabbar-highlight {
|
||||||
@include position(null, null, 0, 0);
|
@include position(null, null, 0, 0);
|
||||||
@include transform-origin(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;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.placement-bottom > ion-tab-highlight {
|
.placement-bottom .tabbar-highlight {
|
||||||
top: 0;
|
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() translucent: boolean = false;
|
||||||
|
|
||||||
|
@Prop() scrollable: boolean = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @output {any} Emitted when the tab changes.
|
* @output {any} Emitted when the tab changes.
|
||||||
*/
|
*/
|
||||||
@ -102,7 +104,6 @@ export class Tabs {
|
|||||||
}
|
}
|
||||||
selectedTab.selected = true;
|
selectedTab.selected = true;
|
||||||
|
|
||||||
console.log('HEY');
|
|
||||||
// The same selected was selected
|
// The same selected was selected
|
||||||
// we need to set root in the nested ion-nav if it exist
|
// we need to set root in the nested ion-nav if it exist
|
||||||
if (this.selectedTab === selectedTab) {
|
if (this.selectedTab === selectedTab) {
|
||||||
@ -165,7 +166,6 @@ export class Tabs {
|
|||||||
|
|
||||||
@Method()
|
@Method()
|
||||||
getRoutes(): RouterEntries {
|
getRoutes(): RouterEntries {
|
||||||
debugger;
|
|
||||||
const a = this.tabs.map(t => {
|
const a = this.tabs.map(t => {
|
||||||
return {
|
return {
|
||||||
path: t.getPath(),
|
path: t.getPath(),
|
||||||
@ -231,7 +231,8 @@ export class Tabs {
|
|||||||
highlight={this.tabbarHighlight}
|
highlight={this.tabbarHighlight}
|
||||||
placement={this.tabbarPlacement}
|
placement={this.tabbarPlacement}
|
||||||
layout={this.tabbarLayout}
|
layout={this.tabbarLayout}
|
||||||
translucent={this.translucent}>
|
translucent={this.translucent}
|
||||||
|
scrollable={this.scrollable}>
|
||||||
</ion-tabbar>
|
</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-spinner'] },
|
||||||
{ components: ['ion-split-pane'] },
|
{ components: ['ion-split-pane'] },
|
||||||
{ components: ['ion-range', 'ion-range-knob']},
|
{ 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-toggle'] },
|
||||||
{ components: ['ion-nav'] },
|
{ components: ['ion-nav'] },
|
||||||
{ components: ['ion-toast', 'ion-toast-controller'] },
|
{ components: ['ion-toast', 'ion-toast-controller'] },
|
||||||
|
Reference in New Issue
Block a user