recreate tabs

This commit is contained in:
Marcos Moura 2016-12-03 17:45:59 -02:00
parent f27d56fc09
commit 8eb59ab163
6 changed files with 140 additions and 566 deletions

View file

@ -1,6 +1,6 @@
<template>
<page-content page-title="Components - Tabs">
<!-- <docs-component>
<docs-component>
<div slot="description">
<p>Tabs enable content organization at a high level, such as switching between views, data sets, or functional aspects of an app.</p>
<p>The following classes can be applied to change the color palette:</p>
@ -123,7 +123,7 @@
<div slot="example">
<example-box card-title="Default">
<div slot="demo">
<md-tabs>
<md-tabs :md-dynamic-height="false">
<md-tab id="movies" md-label="Movies">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.</p>
</md-tab>
@ -167,19 +167,23 @@
</div>
</example-box>
<example-box card-title="Fixed with only icons">
<example-box card-title="Fixed">
<div slot="demo">
<md-tabs md-fixed>
<md-tab md-icon="phone">
<md-tabs :md-dynamic-height="false" md-fixed>
<md-tab id="movies" md-label="Movies">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.</p>
</md-tab>
<md-tab md-icon="favorite">
<md-tab id="music" md-label="Music">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.</p>
</md-tab>
<md-tab md-icon="near_me">
<md-tab id="books" md-label="Books">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas.</p>
</md-tab>
<md-tab id="pictures" md-label="Pictures">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas.</p>
</md-tab>
</md-tabs>
@ -188,16 +192,20 @@
<div slot="code">
<code-block lang="xml">
&lt;md-tabs md-fixed&gt;
&lt;md-tab md-icon=&quot;phone&quot;&gt;
&lt;md-tab id=&quot;movies&quot; md-label=&quot;Movies&quot;&gt;
&lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.&lt;/p&gt;
&lt;/md-tab&gt;
&lt;md-tab md-icon=&quot;favorite&quot;&gt;
&lt;md-tab id=&quot;music&quot; md-label=&quot;Music&quot;&gt;
&lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.&lt;/p&gt;
&lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.&lt;/p&gt;
&lt;/md-tab&gt;
&lt;md-tab md-icon=&quot;near_me&quot;&gt;
&lt;md-tab id=&quot;books&quot; md-label=&quot;Books&quot;&gt;
&lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas.&lt;/p&gt;
&lt;/md-tab&gt;
&lt;md-tab id=&quot;pictures&quot; md-label=&quot;Pictures&quot;&gt;
&lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas.&lt;/p&gt;
&lt;/md-tab&gt;
&lt;/md-tabs&gt;
@ -207,7 +215,7 @@
<example-box card-title="Centered with Text and Icon">
<div slot="demo">
<md-tabs md-centered>
<md-tabs :md-dynamic-height="false" md-centered>
<md-tab md-label="Movies" md-icon="ondemand_video">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.</p>
</md-tab>
@ -251,23 +259,19 @@
</div>
</example-box>
<example-box card-title="Aligned to the right">
<example-box card-title="Aligned to the right with only icons">
<div slot="demo">
<md-tabs md-right>
<md-tab id="movies" md-label="Movies">
<md-tabs :md-dynamic-height="false" md-right>
<md-tab md-icon="phone">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.</p>
</md-tab>
<md-tab id="music" md-label="Music">
<md-tab md-icon="favorite">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.</p>
</md-tab>
<md-tab id="books" md-label="Books">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas.</p>
</md-tab>
<md-tab id="pictures" md-label="Pictures">
<md-tab md-icon="near_me">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas.</p>
</md-tab>
</md-tabs>
@ -276,233 +280,23 @@
<div slot="code">
<code-block lang="xml">
&lt;md-tabs md-right&gt;
&lt;md-tab id=&quot;movies&quot; md-label=&quot;Movies&quot;&gt;
&lt;md-tab md-icon=&quot;phone&quot;&gt;
&lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.&lt;/p&gt;
&lt;/md-tab&gt;
&lt;md-tab id=&quot;music&quot; md-label=&quot;Music&quot;&gt;
&lt;md-tab md-icon=&quot;favorite&quot;&gt;
&lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.&lt;/p&gt;
&lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.&lt;/p&gt;
&lt;/md-tab&gt;
&lt;md-tab id=&quot;books&quot; md-label=&quot;Books&quot;&gt;
&lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas.&lt;/p&gt;
&lt;/md-tab&gt;
&lt;md-tab id=&quot;pictures&quot; md-label=&quot;Pictures&quot;&gt;
&lt;md-tab md-icon=&quot;near_me&quot;&gt;
&lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas.&lt;/p&gt;
&lt;/md-tab&gt;
&lt;/md-tabs&gt;
</code-block>
</div>
</example-box>
<example-box card-title="Playground">
<div slot="demo">
<div class="playground">
<md-subheader>Tabs Attributes</md-subheader>
<md-checkbox id="fixed" v-model="playground.fixed">Fixed</md-checkbox>
<md-checkbox id="centered" v-model="playground.centered">Centered</md-checkbox>
<md-input-container>
<label for="shadow">Shadow</label>
<md-input type="number" id="shadow" v-model="playground.shadow" min="0" max="24"></md-input>
</md-input-container>
<md-subheader>Theme</md-subheader>
<md-radio v-model="playground.theme" id="theme1" name="theme" md-value="default">Default</md-radio>
<md-radio v-model="playground.theme" id="theme2" name="theme" md-value="green">Green</md-radio>
<md-radio v-model="playground.theme" id="theme3" name="theme" md-value="cyan">Cyan</md-radio>
<md-radio v-model="playground.theme" id="theme4" name="theme" md-value="brown">Brown</md-radio>
<md-subheader>Colors</md-subheader>
<md-radio v-model="playground.color" id="color1" name="color" md-value="0">Default</md-radio>
<md-radio v-model="playground.color" id="color2" name="color" md-value="1">Accent</md-radio>
<md-radio v-model="playground.color" id="color3" name="color" md-value="2">Warn</md-radio>
<md-radio v-model="playground.color" id="color4" name="color" md-value="3">Transparent</md-radio>
<md-subheader>Second Tab</md-subheader>
<md-checkbox id="disabled" v-model="playground.tabs[1].disabled">Disabled</md-checkbox>
<md-checkbox id="active" v-model="playground.tabs[1].active">Active</md-checkbox>
</div>
<md-tabs
:class="{
'md-accent': playground.color === '1',
'md-warn': playground.color === '2',
'md-transparent': playground.color === '3'
}"
:md-fixed="playground.fixed"
:md-centered="playground.centered"
:md-elevation="playground.shadow"
v-md-theme="playground.theme">
<md-tab v-for="(tab, index) in playground.tabs" :md-label="tab.label" :md-icon="tab.icon" :md-disabled="tab.disabled" :md-active="tab.active">
<md-input-container>
<label :for="'label' + index">Label</label>
<md-input type="text" :id="'label' + index" v-model="tab.label"></md-input>
</md-input-container>
<md-input-container>
<label :for="'icon' + index">Icon</label>
<md-input type="text" :id="'icon' + index" v-model="tab.icon"></md-input>
</md-input-container>
</md-tab>
</md-tabs>
</div>
<div slot="code">
<code-block lang="xml">
&lt;div class=&quot;playground&quot;&gt;
&lt;md-subheader&gt;Tabs Attributes&lt;/md-subheader&gt;
&lt;md-checkbox id=&quot;fixed&quot; v-model=&quot;playground.fixed&quot;&gt;Fixed&lt;/md-checkbox&gt;
&lt;md-checkbox id=&quot;centered&quot; v-model=&quot;playground.centered&quot;&gt;Centered&lt;/md-checkbox&gt;
&lt;md-input-container&gt;
&lt;label for=&quot;shadow&quot;&gt;Shadow&lt;/label&gt;
&lt;md-input type=&quot;number&quot; id=&quot;shadow&quot; v-model=&quot;playground.shadow&quot; min=&quot;0&quot; max=&quot;24&quot;&gt;&lt;/md-input&gt;
&lt;/md-input-container&gt;
&lt;md-subheader&gt;Theme&lt;/md-subheader&gt;
&lt;md-radio v-model=&quot;playground.theme&quot; id=&quot;theme1&quot; name=&quot;theme&quot; md-value=&quot;default&quot;&gt;Default&lt;/md-radio&gt;
&lt;md-radio v-model=&quot;playground.theme&quot; id=&quot;theme2&quot; name=&quot;theme&quot; md-value=&quot;green&quot;&gt;Green&lt;/md-radio&gt;
&lt;md-radio v-model=&quot;playground.theme&quot; id=&quot;theme3&quot; name=&quot;theme&quot; md-value=&quot;cyan&quot;&gt;Cyan&lt;/md-radio&gt;
&lt;md-radio v-model=&quot;playground.theme&quot; id=&quot;theme4&quot; name=&quot;theme&quot; md-value=&quot;brown&quot;&gt;Brown&lt;/md-radio&gt;
&lt;md-subheader&gt;Colors&lt;/md-subheader&gt;
&lt;md-radio v-model=&quot;playground.color&quot; id=&quot;color1&quot; name=&quot;color&quot; md-value=&quot;0&quot;&gt;Default&lt;/md-radio&gt;
&lt;md-radio v-model=&quot;playground.color&quot; id=&quot;color2&quot; name=&quot;color&quot; md-value=&quot;1&quot;&gt;Accent&lt;/md-radio&gt;
&lt;md-radio v-model=&quot;playground.color&quot; id=&quot;color3&quot; name=&quot;color&quot; md-value=&quot;2&quot;&gt;Warn&lt;/md-radio&gt;
&lt;md-radio v-model=&quot;playground.color&quot; id=&quot;color4&quot; name=&quot;color&quot; md-value=&quot;3&quot;&gt;Transparent&lt;/md-radio&gt;
&lt;md-subheader&gt;Second Tab&lt;/md-subheader&gt;
&lt;md-checkbox id=&quot;disabled&quot; v-model=&quot;playground.tabs[1].disabled&quot;&gt;Disabled&lt;/md-checkbox&gt;
&lt;md-checkbox id=&quot;active&quot; v-model=&quot;playground.tabs[1].active&quot;&gt;Active&lt;/md-checkbox&gt;
&lt;/div&gt;
&lt;md-tabs
:class=&quot;{
&#039;md-accent&#039;: playground.color === &#039;1&#039;,
&#039;md-warn&#039;: playground.color === &#039;2&#039;,
&#039;md-transparent&#039;: playground.color === &#039;3&#039;
}&quot;
:md-fixed=&quot;playground.fixed&quot;
:md-centered=&quot;playground.centered&quot;
:md-elevation=&quot;playground.shadow&quot;
v-md-theme=&quot;playground.theme&quot;&gt;
&lt;md-tab v-for=&quot;(tab, index) in playground.tabs&quot; :md-label=&quot;tab.label&quot; :md-icon=&quot;tab.icon&quot; :md-disabled=&quot;tab.disabled&quot; :md-active=&quot;tab.active&quot;&gt;
&lt;md-input-container&gt;
&lt;label :for=&quot;&#039;label&#039; + index&quot;&gt;Label&lt;/label&gt;
&lt;md-input type=&quot;text&quot; :id=&quot;&#039;label&#039; + index&quot; v-model=&quot;tab.label&quot;&gt;&lt;/md-input&gt;
&lt;/md-input-container&gt;
&lt;md-input-container&gt;
&lt;label :for=&quot;&#039;icon&#039; + index&quot;&gt;Icon&lt;/label&gt;
&lt;md-input type=&quot;text&quot; :id=&quot;&#039;icon&#039; + index&quot; v-model=&quot;tab.icon&quot;&gt;&lt;/md-input&gt;
&lt;/md-input-container&gt;
&lt;/md-tab&gt;
&lt;/md-tabs&gt;
</code-block>
<code-block lang="javascript">
export default {
data() {
return {
playground: {
color: '3',
fixed: true,
centered: false,
shadow: 0,
theme: 'default',
tabs: [
{
label: 'Phone',
icon: 'phone',
active: false,
disabled: false
},
{
label: 'Favorites',
icon: 'favorite',
active: false,
disabled: false
},
{
label: 'Near me',
icon: 'near_me',
active: true,
disabled: false
}
]
}
};
}
};
</code-block>
</div>
</example-box>
</div>
</docs-component> -->
<md-tabs>
<md-tab id="tab-1" md-label="Test 1">
<p>Optio, assumenda placeat laboriosam incidunt obcaecati nisi, ipsa earum rem cumque nesciunt iste animi recusandae! Nostrum vero quod consequuntur enim quo cum.</p>
</md-tab>
<md-tab id="tab-2" md-label="Test 2">
<p>Animi architecto blanditiis nihil aliquam nemo aperiam quo asperiores quam suscipit quae labore in, qui odit beatae assumenda, accusamus, ex sapiente fugit?</p>
</md-tab>
<md-tab id="tab-3" md-label="Test 3">
<p>Excepturi, blanditiis in a non ipsa. Praesentium atque maxime officia amet porro veritatis, vitae quisquam ex magni nisi sed. Asperiores, vel aut.</p>
</md-tab>
</md-tabs>
</docs-component>
</page-content>
</template>
<style lang="scss" scoped>
.playground {
padding: 0 16px 16px;
.md-subheader {
padding: 16px 0 0;
}
}
div.examples .demo-example .demo-example-body {
padding: 0;
}
</style>
<script>
export default {
data() {
return {
playground: {
color: '3',
fixed: true,
centered: false,
shadow: 0,
theme: 'default',
tabs: [
{
label: 'Phone',
icon: 'phone',
active: false,
disabled: false
},
{
label: 'Favorites',
icon: 'favorite',
active: false,
disabled: false
},
{
label: 'Near me',
icon: 'near_me',
active: true,
disabled: false
}
]
}
};
}
};
</script>

View file

@ -1,70 +0,0 @@
<template>
<div class="md-tab" :id="tabId" ref="tab">
<slot></slot>
</div>
</template>
<script>
export default {
props: {
id: [String, Number],
mdLabel: [String, Number],
mdIcon: String,
mdActive: Boolean,
mdDisabled: Boolean
},
data() {
let id;
if (!this.id) {
id = 'tab-' + Math.random().toString(36).substr(2, 10);
}
return {
tabId: this.id || id
};
},
watch: {
mdActive() {
this.updateTabData();
},
mdDisabled() {
this.updateTabData();
},
mdIcon() {
this.updateTabData();
},
mdLabel() {
this.updateTabData();
}
},
methods: {
updateTabData() {
this.$parent.updateTabData({
id: this.tabId,
label: this.mdLabel,
icon: this.mdIcon,
active: this.mdActive,
disabled: this.mdDisabled,
ref: this.$refs.tab
});
}
},
mounted() {
if (!this.$parent.$el.classList.contains('md-tabs')) {
this.$destroy();
throw new Error('You should wrap the md-tab in a md-tabs');
}
this.$parent.registerTab({
id: this.tabId,
label: this.mdLabel,
icon: this.mdIcon,
active: this.mdActive,
disabled: this.mdDisabled,
ref: this.$refs.tab
});
}
};
</script>

View file

@ -1,5 +1,5 @@
<template>
<div class="md-tab" :id="tabId">
<div class="md-tab" :id="tabId" :style="styles">
<slot></slot>
</div>
</template>
@ -19,9 +19,33 @@
data() {
return {
mounted: false,
tabId: this.id || 'tab-' + uniqueId()
tabId: this.id || 'tab-' + uniqueId(),
width: '0px',
left: '0px'
};
},
watch: {
mdActive() {
this.updateTabData();
},
mdDisabled() {
this.updateTabData();
},
mdIcon() {
this.updateTabData();
},
mdLabel() {
this.updateTabData();
}
},
computed: {
styles() {
return {
width: this.width,
left: this.left
};
}
},
methods: {
getTabData() {
return {
@ -29,8 +53,12 @@
label: this.mdLabel,
icon: this.mdIcon,
active: this.mdActive,
disabled: this.mdDisabled
disabled: this.mdDisabled,
ref: this
};
},
updateTabData() {
this.parentTabs.updateTab(this.getTabData());
}
},
mounted() {

View file

@ -1,211 +0,0 @@
<template>
<div class="md-tabs" :class="tabClasses">
<md-whiteframe :md-elevation="elevation || 0">
<div class="md-tabs-navigation" :class="navigationClasses">
<button
v-for="header in tabs"
:key="header.id"
type="button"
class="md-tab-header"
:class="getHeaderClass(header)"
:disabled="header.disabled"
@click="changeTab(header.id)"
v-md-ink-ripple="header.disabled"
ref="tabHeader">
<div class="md-tab-header-container">
<md-icon v-if="header.icon">{{ header.icon }}</md-icon>
<span v-if="header.label">{{ header.label }}</span>
</div>
</button>
<span class="md-tab-indicator" :class="indicatorClass" ref="indicator"></span>
</div>
</md-whiteframe>
<div class="md-tabs-content" ref="tabContent">
<div class="md-tabs-wrapper" ref="tabWrapper">
<slot></slot>
</div>
</div>
</div>
</template>
<style lang="scss" src="./mdTabs.scss"></style>
<script>
export default {
props: {
mdFixed: Boolean,
mdCentered: Boolean,
mdRight: Boolean,
mdDynamicHeight: {
type: Boolean,
default: true
},
mdElevation: [String, Number]
},
data() {
return {
hasIcons: false,
hasLabel: false,
elevation: this.mdElevation,
activeTab: '',
activeTabNumber: 0,
tabs: {}
};
},
watch: {
mdFixed() {
let transitionCounter = 0;
let transitionInterval = window.setInterval(() => {
transitionCounter++;
window.requestAnimationFrame(() => {
this.calculateIndicatorPos(true);
});
if (transitionCounter > 200) {
window.clearInterval(transitionInterval);
}
}, 10);
this.recalculateAllTabsPos();
},
mdCentered() {
this.recalculateAllTabsPos();
},
mdElevation() {
this.elevation = this.mdElevation;
}
},
computed: {
tabClasses() {
return {
'md-no-transition': !this.mdDynamicHeight
};
},
navigationClasses() {
return {
'md-fixed': this.mdFixed,
'md-right': !this.mdCentered && this.mdRight,
'md-centered': this.mdCentered || this.mdFixed,
'md-has-icon': this.hasIcons,
'md-has-label': this.hasLabel
};
},
indicatorClass() {
let toLeft = this.lastIndicatorNumber > this.activeTabNumber;
this.lastIndicatorNumber = this.activeTabNumber;
return {
'md-to-right': !toLeft,
'md-to-left': toLeft
};
}
},
methods: {
getHeaderClass(header) {
return {
'md-active': this.activeTab === header.id,
'md-disabled': header.disabled
};
},
calculateIndicatorPos(recalculate) {
let indicator = this.$refs.indicator;
let tabsWidth = this.$el.offsetWidth;
if (recalculate) {
indicator.classList.add('md-transition-off');
}
this.$nextTick(() => {
let activeTab = this.$refs.tabHeader[this.activeTabNumber];
let left = activeTab.offsetLeft;
let right = tabsWidth - left - activeTab.offsetWidth;
indicator.style.left = left + 'px';
indicator.style.right = right + 'px';
if (recalculate) {
window.setTimeout(() => {
indicator.classList.remove('md-transition-off');
}, 100);
}
});
},
calculateTabPos(ref, index) {
this.$refs.tabWrapper.style.transform = 'translate3D(' + -this.$refs.tabContent.offsetWidth * this.activeTabNumber + 'px, 0, 0)';
ref.style.width = this.$refs.tabContent.offsetWidth + 'px';
ref.style.left = this.$refs.tabContent.offsetWidth * index + 'px';
},
setVisibleTab(ref) {
this.$refs.tabContent.style.height = ref.offsetHeight + 'px';
ref.classList.add('md-active');
},
changeTab(tabId) {
let idList = Object.keys(this.tabs);
let id = tabId || idList[0];
let index = idList.indexOf(id);
this.tabs[this.activeTab || id].ref.classList.remove('md-active');
this.activeTab = id;
this.activeTabNumber = index;
this.$emit('change', index);
window.setTimeout(() => {
this.calculateIndicatorPos();
this.calculateTabPos(this.tabs[id].ref, index);
this.setVisibleTab(this.tabs[id].ref);
});
},
handleTabData(data) {
let idList = Object.keys(this.tabs);
let index = idList.indexOf(data.id);
this.hasIcons = !!data.icon;
this.hasLabel = !!data.label;
if (!data.disabled && data.active) {
this.changeTab(data.id);
} else {
this.changeTab(idList[index + 1]);
}
},
registerTab(data) {
this.tabs[data.id] = data;
this.handleTabData(data);
},
updateTabData(data) {
this.tabs[data.id] = data;
this.handleTabData(data);
this.recalculateAllTabsPos();
this.$forceUpdate();
},
recalculateAllTabsPos(transitionOff) {
if (typeof transitionOff === 'undefined') {
transitionOff = true;
}
window.requestAnimationFrame(() => {
this.calculateIndicatorPos(!transitionOff);
Object.keys(this.tabs).forEach((tab, index) => {
this.calculateTabPos(this.tabs[tab].ref, index);
});
});
}
},
mounted() {
if (!this.activeTab) {
this.changeTab();
}
window.addEventListener('resize', this.recalculateAllTabsPos);
},
beforeDestroy() {
window.removeEventListener('resize', this.recalculateAllTabsPos);
}
};
</script>

View file

@ -9,6 +9,16 @@ $tab-max-width: 264px;
flex-flow: column;
position: relative;
&.md-transition-off * {
transition: none !important;
}
&.md-dynamic-height {
.md-tabs-content {
transition: height $swift-ease-out-duration $swift-ease-out-timing-function;
}
}
.md-tabs-navigation {
height: 48px;
min-height: 48px;
@ -82,6 +92,10 @@ $tab-max-width: 264px;
left: 0;
transform: translate3D(0, 0, 0);
&.md-transition-off {
transition: none !important;
}
&.md-to-right {
transition: $swift-ease-out,
left .3s $swift-ease-in-out-timing-function,
@ -95,19 +109,14 @@ $tab-max-width: 264px;
}
}
.md-transition-off {
transition: none !important;
}
.md-tabs-content {
width: 100%;
//height: 0;
height: 0;
position: relative;
overflow: hidden;
transition: height $swift-ease-out-duration $swift-ease-out-timing-function;
}
/*.md-tabs-wrapper {
.md-tabs-wrapper {
width: 9999em;
position: absolute;
top: 0;
@ -116,28 +125,13 @@ $tab-max-width: 264px;
left: 0;
transform: translate3d(0, 0, 0);
transition: transform $swift-ease-out-duration $swift-ease-out-timing-function;
}*/
}
.md-tab {
padding: 16px;
/* position: absolute;
position: absolute;
top: 0;
left: 0;
right: 0; */
pointer-events: none;
//transform: translate3d(0, -100%, 0);
transition: transform 0s $swift-ease-out-duration;
&.md-active {
transform: translate3d(0, 0, 0);
pointer-events: auto;
transition: none;
}
}
}
.md-tabs.md-no-transition {
.md-tabs-content {
transition: none !important;
right: 0;
}
}

View file

@ -22,8 +22,8 @@
</div>
</md-whiteframe>
<div class="md-tabs-content" ref="tabContent">
<div class="md-tabs-wrapper" ref="tabWrapper">
<div class="md-tabs-content" ref="tabContent" :style="{ height: contentHeight }">
<div class="md-tabs-wrapper" :style="{ transform: `translate3D(-${contentWidth}, 0, 0)` }">
<slot></slot>
</div>
</div>
@ -33,8 +33,6 @@
<style lang="scss" src="./mdTabs.scss"></style>
<script>
import throttle from '../../core/utils/throttle';
export default {
props: {
mdFixed: Boolean,
@ -53,21 +51,26 @@
tabList: {},
activeTab: null,
activeTabNumber: 0,
transitionControl: null
hasIcons: false,
hasLabel: false,
transitionControl: null,
contentHeight: '0px',
contentWidth: '0px'
}),
computed: {
tabClasses() {
return {
'md-no-transition': !this.mdDynamicHeight
'md-dynamic-height': this.mdDynamicHeight,
'md-transition-off': this.transitionOff
};
},
navigationClasses() {
return {
'md-has-icon': this.hasIcons,
'md-has-label': this.hasLabel,
'md-fixed': this.mdFixed,
'md-right': !this.mdCentered && this.mdRight,
'md-centered': this.mdCentered || this.mdFixed,
'md-has-icon': this.hasIcons,
'md-has-label': this.hasLabel
'md-centered': this.mdCentered || this.mdFixed
};
},
indicatorClasses() {
@ -76,6 +79,7 @@
this.lastIndicatorNumber = this.activeTabNumber;
return {
'md-transition-off': this.transitionOff,
'md-to-right': !toLeft,
'md-to-left': toLeft
};
@ -93,14 +97,30 @@
this.$forceUpdate();
},
unregisterTab(tabData) {
console.log(tabData);
delete this.tabList[tabData.id];
},
updateTab(tabData) {
this.registerTab(tabData);
if (tabData.active) {
if (!tabData.disabled) {
this.setActiveTab(tabData);
} else {
let tabsIds = Object.keys(this.tabList);
let targetIndex = tabsIds.indexOf(tabData.id) + 1;
let target = tabsIds[targetIndex];
if (target) {
this.setActiveTab(this.tabList[target]);
} else {
this.setActiveTab(this.tabList[0]);
}
}
}
},
observeElementChanges() {
this.contentObserver = new MutationObserver(throttle(this.debounceTransition, 50));
this.navigationObserver = new MutationObserver(throttle(this.debounceTransition, 50));
this.contentObserver = new MutationObserver(this.calculateOnWatch);
this.navigationObserver = new MutationObserver(this.calculateOnWatch);
this.contentObserver.observe(this.$refs.tabContent, {
childList: true,
attributes: true,
@ -118,13 +138,6 @@
return idList.indexOf(id);
},
debounceTransition() {
window.clearTimeout(this.transitionControl);
this.transitionControl = window.setTimeout(() => {
this.calculateIndicatorPos();
this.$refs.indicator.classList.remove('md-transition-off');
}, 200);
},
calculateIndicatorPos() {
let tabsWidth = this.$el.offsetWidth;
let activeTab = this.$refs.tabHeader[this.activeTabNumber];
@ -134,13 +147,39 @@
this.$refs.indicator.style.left = left + 'px';
this.$refs.indicator.style.right = right + 'px';
},
calculateTabsWidthAndPosition() {
const width = this.$el.offsetWidth;
this.contentWidth = width * this.activeTabNumber + 'px';
Object.values(this.tabList).forEach((tab, index) => {
tab.ref.width = width + 'px';
tab.ref.left = width * index + 'px';
});
},
calculateContentHeight() {
this.$nextTick(() => {
let height = this.tabList[this.activeTab].ref.$el.offsetHeight;
this.contentHeight = height + 'px';
});
},
calculatePosition() {
window.requestAnimationFrame(() => {
this.calculateIndicatorPos();
this.calculateTabsWidthAndPosition();
this.calculateContentHeight();
});
},
onWindowResize() {
this.$refs.indicator.classList.add('md-transition-off');
debounceTransition() {
window.clearTimeout(this.transitionControl);
this.transitionControl = window.setTimeout(() => {
this.calculatePosition();
this.transitionOff = false;
}, 200);
},
calculateOnWatch() {
this.transitionOff = true;
this.calculatePosition();
this.debounceTransition();
},
@ -155,7 +194,7 @@
mounted() {
this.$nextTick(() => {
this.observeElementChanges();
window.addEventListener('resize', this.onWindowResize);
window.addEventListener('resize', this.calculateOnWatch);
if (!this.activeTab) {
let firstTab = Object.keys(this.tabList)[0];
@ -166,7 +205,7 @@
},
beforeDestroy() {
this.contentObserver.disconnect();
window.removeEventListener('resize', this.onWindowResize);
window.removeEventListener('resize', this.calculateOnWatch);
}
};
</script>