Merge pull request #450 from igor-ribeiro/tabs-navigation-arrows

Tabs navigation arrows
This commit is contained in:
Pablo Henrique 2017-05-21 17:49:06 -03:00 committed by GitHub
commit 7823c377ee
4 changed files with 128 additions and 25 deletions

View file

@ -52,6 +52,12 @@
<md-table-cell><code>Number</code></md-table-cell>
<md-table-cell>Add a shadow on the navigation with an whiteframe. Default <code>0</code></md-table-cell>
</md-table-row>
<md-table-row>
<md-table-cell>md-navigation</md-table-cell>
<md-table-cell><code>Boolean</code></md-table-cell>
<md-table-cell>Display the navigation arrows for horizontal scroll. Default <code>true</code></md-table-cell>
</md-table-row>
</md-table-body>
</md-table>

View file

@ -29,6 +29,26 @@ $tab-max-width: 264px;
transition: $swift-ease-out;
overflow: hidden;
&.md-has-navigation-scroll {
.md-tab-header-navigation-button.md-left {
order: 1;
}
.md-tabs-navigation-container {
order: 2;
}
.md-tab-header-navigation-button.md-right {
order: 3;
}
.md-tabs-navigation-container {
@include layout-small-and-up {
margin-bottom: -15px;
}
}
}
&.md-has-icon.md-has-label {
min-height: 72px;
@ -60,11 +80,11 @@ $tab-max-width: 264px;
.md-tabs-navigation-container {
display: flex;
overflow-x: scroll;
overflow-x: auto;
}
@include layout-small-and-up {
margin-bottom: -15px;
}
.md-tabs-navigation-scroll-container {
display: flex;
}
.md-tab-header {
@ -126,6 +146,26 @@ $tab-max-width: 264px;
}
}
.md-tab-header-navigation-button {
border: none;
height: 100%;
cursor: pointer;
position: relative;
&.md-left {
left: 0;
}
&.md-right {
right: 0;
}
&.md-disabled {
pointer-events: none;
opacity: .4;
}
}
.md-tabs-content {
width: 100%;
height: 0;

View file

@ -19,6 +19,11 @@
.md-tab-indicator {
background-color: #{'ACCENT-COLOR'};
}
.md-tab-header-navigation-button {
color: #{'PRIMARY-CONTRAST-0.54'};
background-color: #{'PRIMARY-COLOR'};
}
}
&.md-transparent {

View file

@ -1,28 +1,34 @@
<template>
<div class="md-tabs" :class="[themeClass, tabClasses]">
<md-whiteframe md-tag="nav" class="md-tabs-navigation" :md-elevation="mdElevation" :class="navigationClasses" ref="tabNavigation">
<div class="md-tabs-navigation-container" ref="tabsContainer" @scroll="calculateIndicatorPos">
<button
v-for="header in tabList"
:key="header.id"
type="button"
class="md-tab-header"
:class="getHeaderClass(header)"
:disabled="header.disabled"
@click="setActiveTab(header)"
ref="tabHeader">
<md-ink-ripple :md-disabled="header.disabled"></md-ink-ripple>
<div class="md-tab-header-container">
<slot name="header-item" :header="header">
<md-icon v-if="header.icon">{{ header.icon }}</md-icon>
<span v-if="header.label">{{ header.label }}</span>
</slot>
<md-tooltip v-if="header.tooltip" :md-direction="header.tooltipDirection" :md-delay="header.tooltipDelay">{{ header.tooltip }}</md-tooltip>
</div>
</button>
<div class="md-tabs-navigation-container" ref="tabsContainer" @scroll="handleNavigationScroll">
<div class="md-tabs-navigation-scroll-container">
<button
v-for="header in tabList"
:key="header.id"
type="button"
class="md-tab-header"
:class="getHeaderClass(header)"
:disabled="header.disabled"
@click="setActiveTab(header)"
ref="tabHeader">
<md-ink-ripple :md-disabled="header.disabled"></md-ink-ripple>
<div class="md-tab-header-container">
<md-icon v-if="header.icon">{{ header.icon }}</md-icon>
<span v-if="header.label">{{ header.label }}</span>
<md-tooltip v-if="header.tooltip" :md-direction="header.tooltipDirection" :md-delay="header.tooltipDelay">{{ header.tooltip }}</md-tooltip>
</div>
</button>
<span class="md-tab-indicator" :class="indicatorClasses" ref="indicator"></span>
<span class="md-tab-indicator" :class="indicatorClasses" ref="indicator"></span>
</div>
</div>
<button v-if="mdNavigation && hasNavigationScroll" @click="navigationScrollLeft" class="md-tab-header-navigation-button md-left" :class="navigationLeftButtonClasses">
<md-icon>keyboard_arrow_left</md-icon>
</button>
<button v-if="mdNavigation && hasNavigationScroll" @click="navigationScrollRight" class="md-tab-header-navigation-button md-right" :class="navigationRightButtonClasses">
<md-icon>keyboard_arrow_right</md-icon>
</button>
</md-whiteframe>
<div class="md-tabs-content" ref="tabContent" :style="{ height: contentHeight }">
@ -45,6 +51,10 @@
mdFixed: Boolean,
mdCentered: Boolean,
mdRight: Boolean,
mdNavigation: {
type: Boolean,
default: true
},
mdDynamicHeight: {
type: Boolean,
default: true
@ -61,6 +71,9 @@
activeTabNumber: 0,
hasIcons: false,
hasLabel: false,
hasNavigationScroll: false,
isNavigationOnStart: true,
isNavigationOnEnd: false,
transitionControl: null,
transitionOff: false,
contentHeight: '0px',
@ -79,7 +92,8 @@
'md-has-label': this.hasLabel,
'md-fixed': this.mdFixed,
'md-right': !this.mdCentered && this.mdRight,
'md-centered': this.mdCentered || this.mdFixed
'md-centered': this.mdCentered || this.mdFixed,
'md-has-navigation-scroll': this.hasNavigationScroll
};
},
indicatorClasses() {
@ -92,6 +106,16 @@
'md-to-right': !toLeft,
'md-to-left': toLeft
};
},
navigationLeftButtonClasses() {
return {
'md-disabled': this.isNavigationOnStart
};
},
navigationRightButtonClasses() {
return {
'md-disabled': this.isNavigationOnEnd
};
}
},
methods: {
@ -178,6 +202,7 @@
this.calculateIndicatorPos();
this.calculateTabsWidthAndPosition();
this.calculateContentHeight();
this.checkNavigationScroll();
});
},
debounceTransition() {
@ -195,6 +220,23 @@
this.transitionOff = true;
this.calculateOnWatch();
},
calculateScrollPos() {
const { scrollLeft, scrollWidth, clientWidth } = this.$refs.tabsContainer;
this.isNavigationOnStart = scrollLeft < 32;
this.isNavigationOnEnd = scrollWidth - scrollLeft - 32 < clientWidth;
},
handleNavigationScroll() {
window.requestAnimationFrame(() => {
this.calculateIndicatorPos();
this.calculateScrollPos();
});
},
checkNavigationScroll() {
const { scrollWidth, clientWidth } = this.$refs.tabsContainer;
this.hasNavigationScroll = scrollWidth > clientWidth;
},
setActiveTab(tabData) {
this.hasIcons = !!tabData.icon;
this.hasLabel = !!tabData.label;
@ -202,6 +244,16 @@
this.activeTabNumber = this.getTabIndex(this.activeTab);
this.calculatePosition();
this.$emit('change', this.activeTabNumber);
},
navigationScrollLeft() {
const { scrollLeft, clientWidth } = this.$refs.tabsContainer;
this.$refs.tabsContainer.scrollLeft = Math.max(0, scrollLeft - clientWidth);
},
navigationScrollRight() {
const { scrollLeft, clientWidth, scrollWidth } = this.$refs.tabsContainer;
this.$refs.tabsContainer.scrollLeft = Math.min(scrollWidth, scrollLeft + clientWidth);
}
},
mounted() {