start creation of tabs

This commit is contained in:
Marcos Moura 2016-12-02 03:01:43 -02:00
parent 1aa10cd9a7
commit f27d56fc09
12 changed files with 855 additions and 176 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>
@ -439,7 +439,21 @@
</div>
</example-box>
</div>
</docs-component>
</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>
</page-content>
</template>

View file

@ -88,5 +88,14 @@
"webpack-dev-middleware": "^1.8.4",
"webpack-hot-middleware": "^2.13.2",
"webpack-merge": "^0.17.0"
}
},
"hyperlayout": [
[[
"yarn run dev",
[
"",
"g p"
]
]]
]
}

View file

@ -0,0 +1,395 @@
@import '../../core/stylesheets/variables.scss';
.md-table {
display: flex;
flex-flow: column wrap;
overflow-x: auto;
&.md-transition-off {
.md-table-cell,
.md-checkbox .md-checkbox-container,
.md-checkbox .md-checkbox-container:after {
transition: none !important;
}
}
table {
width: 100%;
border-spacing: 0;
border-collapse: collapse;
overflow: hidden;
}
tbody .md-table-row {
border-top: 1px solid #e0e0e0;
&.md-selected .md-table-cell {
background-color: #f5f5f5;
}
&:hover .md-table-cell {
background-color: #eee;
}
}
.md-table-head {
padding: 0;
position: relative;
color: rgba(#000, .54);
font-size: 12px;
line-height: 16px;
text-align: left;
&:last-child .md-table-head-container .md-table-head-text {
padding-right: 24px;
}
&.md-numeric {
text-align: right;
}
.md-icon {
$size: 16px;
width: $size;
min-width: $size;
height: $size;
min-height: $size;
font-size: $size;
color: rgba(#000, .54);
&:not(.md-sortable-icon) {
margin: 0 4px;
}
&:first-child {
margin-left: 0;
}
&:last-child {
margin-right: 0;
}
}
}
.md-table-head-container {
height: 56px;
padding: 14px 0;
transition: $swift-ease-out;
}
.md-table-head-text {
height: 28px;
padding-right: 32px;
padding-left: 24px;
display: inline-block;
position: relative;
overflow: hidden;
line-height: 28px;
text-overflow: ellipsis;
white-space: nowrap;
}
.md-sortable {
cursor: pointer;
&:first-of-type {
.md-sortable-icon {
left: auto;
right: 10px;
}
}
&:hover,
&.md-sorted {
color: rgba(#000, .87);
.md-sortable-icon {
opacity: 1;
}
}
&.md-sorted {
.md-sortable-icon {
color: rgba(#000, .87);
}
}
&.md-sorted-descending {
.md-sortable-icon {
transform: translateY(-50%) rotate(180deg);
}
}
.md-sortable-icon {
position: absolute;
top: 50%;
left: 2px;
transition: $swift-ease-out;
transform: translateY(-50%);
opacity: 0;
color: rgba(#000, .38);
}
.md-ink-ripple {
color: rgba(#000, .87);
}
}
.md-table-cell {
height: 48px;
position: relative;
transition: $swift-ease-out;
color: rgba(#000, .87);
font-size: 13px;
line-height: 18px;
&:last-child .md-table-cell-container {
padding-right: 24px;
}
&.md-numeric {
text-align: right;
.md-table-cell-container {
justify-content: flex-end;
}
}
&.md-has-action {
.md-table-cell-container {
display: flex;
align-items: center;
justify-content: space-between;
}
}
.md-table-cell-container {
padding: 6px 32px 6px 24px;
}
.md-button {
$size: 36px;
width: $size;
min-width: $size;
height: $size;
min-height: $size;
&:last-child {
margin: 0 -10px 0 0;
}
.md-icon {
$size: 18px;
width: $size;
min-width: $size;
height: $size;
min-height: $size;
margin: 0;
color: rgba(#000, .54);
font-size: $size;
}
}
}
.md-table-selection {
width: 60px;
position: relative;
vertical-align: middle;
+ {
.md-table-cell .md-table-cell-container,
.md-table-head .md-table-head-container .md-table-head-text {
padding-left: 8px;
}
}
.md-table-cell-container {
padding-right: 16px;
padding-left: 24px;
}
.md-checkbox {
margin: 0;
}
.md-checkbox-container {
width: 18px;
height: 18px;
margin-top: 1px;
&:after {
top: -1px;
left: 4px
}
}
}
.md-select {
min-width: 84px;
}
.md-select-value,
.md-option {
font-size: 13px;
}
}
.md-table-edit-trigger {
display: inline-block;
cursor: pointer;
color: rgba(#000, .38);
&.md-edited {
color: rgba(#000, .87);
}
}
.md-table-dialog {
max-height: 0;
margin: 0;
padding: 0 24px 2px;
position: absolute;
top: 0;
right: 0;
left: 24px;
z-index: 60;
overflow: hidden;
pointer-events: none;
border-radius: 2px;
box-shadow: $material-shadow-2dp;
background-color: #fff;
opacity: 0;
transition: $swift-ease-out,
max-height 0s .5s;
transition-duration: .3s;
transform: translate3D(0, -8px, 0);
&.md-active {
max-height: 400px;
pointer-events: auto;
transform: translate3D(#000);
opacity: 1;
transition: $swift-ease-out;
transition-duration: .3s;
}
&.md-large {
padding: 12px 24px 2px;
}
.md-input-container {
margin-top: 0;
margin-bottom: 16px;
&.md-input-placeholder input {
font-size: 13px;
&::-webkit-input-placeholder {
font-size: 13px;
}
}
}
.md-char-counter {
font-size: 13.5px;
color: rgba(#000, .54);
}
.md-button {
min-width: 64px;
}
}
.md-table-card {
overflow: visible;
.md-toolbar {
padding-left: 16px;
background-color: #fff;
}
.md-title {
flex: 1;
font-size: 20px;
}
.md-table-pagination {
height: 56px;
display: flex;
flex: 1;
align-items: center;
justify-content: flex-end;
border-top: 1px solid #e0e0e0;
color: rgba(#000, .54);
font-size: 12px;
.md-table-pagination-previous {
margin-right: 2px;
margin-left: 18px;
}
.md-select {
width: auto;
min-width: 36px;
margin: 0 32px;
&:after {
margin-top: 0;
}
.md-select-value {
padding: 0;
border: none;
font-size: 13px;
}
}
.md-button {
&:not([disabled]) {
color: rgba(#000, .87);
}
&[disabled] {
.md-icon {
color: rgba(#000, .26);
}
}
}
}
}
.md-pagination-select {
&.md-direction-bottom-right {
margin-top: -16px;
}
.md-list-item-holder {
font-size: 13px;
}
}
.md-table-alternate-header {
position: absolute;
top: 0;
right: 0;
left: 0;
z-index: 10;
pointer-events: none;
opacity: 0;
transition: $swift-ease-out;
transition-duration: .3s;
&.md-active {
pointer-events: auto;
opacity: 1;
transform: translate3D(#000);
}
.md-counter {
margin-left: 8px;
flex: 1;
}
}

View file

@ -0,0 +1,70 @@
<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,10 +1,13 @@
<template>
<div class="md-tab" :id="tabId" ref="tab">
<div class="md-tab" :id="tabId">
<slot></slot>
</div>
</template>
<script>
import uniqueId from '../../core/utils/uniqueId';
import getClosestVueParent from '../../core/utils/getClosestVueParent';
export default {
props: {
id: [String, Number],
@ -14,57 +17,40 @@
mdDisabled: Boolean
},
data() {
let id;
if (!this.id) {
id = 'tab-' + Math.random().toString(36).substr(2, 10);
}
return {
tabId: this.id || id
mounted: false,
tabId: this.id || 'tab-' + uniqueId()
};
},
watch: {
mdActive() {
this.updateTabData();
},
mdDisabled() {
this.updateTabData();
},
mdIcon() {
this.updateTabData();
},
mdLabel() {
this.updateTabData();
}
},
methods: {
updateTabData() {
this.$parent.updateTabData({
getTabData() {
return {
id: this.tabId,
label: this.mdLabel,
icon: this.mdIcon,
active: this.mdActive,
disabled: this.mdDisabled,
ref: this.$refs.tab
});
disabled: this.mdDisabled
};
}
},
mounted() {
if (!this.$parent.$el.classList.contains('md-tabs')) {
this.$destroy();
this.parentTabs = getClosestVueParent(this.$parent, 'md-tabs');
throw new Error('You should wrap the md-tab in a md-tabs');
if (!this.parentTabs) {
throw new Error('You must 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
this.$nextTick(() => {
this.mounted = true;
this.parentTabs.registerTab(this.getTabData());
if (this.mdActive) {
this.parentTabs.activeTab = this.tabId;
}
});
},
beforeDestroy() {
this.parentTabs.unregisterTab(this.getTabData());
}
};
</script>

View file

@ -0,0 +1,211 @@
<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

@ -101,13 +101,13 @@ $tab-max-width: 264px;
.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,16 +116,16 @@ $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;
right: 0; */
pointer-events: none;
transform: translate3d(0, -100%, 0);
//transform: translate3d(0, -100%, 0);
transition: transform 0s $swift-ease-out-duration;
&.md-active {

View file

@ -10,7 +10,7 @@
&.md-active,
&:focus {
color: #{'PRIMARY-CONTRAST-0.99999'};
color: #{'PRIMARY-CONTRAST'};
}
&.md-disabled {
@ -56,7 +56,7 @@
&.md-active,
&:focus {
color: #{'ACCENT-CONTRAST-0.99999'};
color: #{'ACCENT-CONTRAST'};
}
&.md-disabled {
@ -79,7 +79,7 @@
&.md-active,
&:focus {
color: #{'WARN-CONTRAST-0.99999'};
color: #{'WARN-CONTRAST'};
}
&.md-disabled {

View file

@ -1,24 +1,24 @@
<template>
<div class="md-tabs" :class="tabClasses">
<md-whiteframe :md-elevation="elevation || 0">
<div class="md-tabs-navigation" :class="navigationClasses">
<md-whiteframe :md-elevation="mdElevation">
<div class="md-tabs-navigation" :class="navigationClasses" ref="tabNavigation">
<button
v-for="header in tabs"
v-for="header in tabList"
: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"
@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>
</div>
</button>
<span class="md-tab-indicator" :class="indicatorClass" ref="indicator"></span>
<span class="md-tab-indicator" :class="indicatorClasses" ref="indicator"></span>
</div>
</md-whiteframe>
@ -33,6 +33,8 @@
<style lang="scss" src="./mdTabs.scss"></style>
<script>
import throttle from '../../core/utils/throttle';
export default {
props: {
mdFixed: Boolean,
@ -42,42 +44,17 @@
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;
mdElevation: {
type: [String, Number],
default: 0
}
},
data: () => ({
tabList: {},
activeTab: null,
activeTabNumber: 0,
transitionControl: null
}),
computed: {
tabClasses() {
return {
@ -93,7 +70,7 @@
'md-has-label': this.hasLabel
};
},
indicatorClass() {
indicatorClasses() {
let toLeft = this.lastIndicatorNumber > this.activeTabNumber;
this.lastIndicatorNumber = this.activeTabNumber;
@ -111,102 +88,85 @@
'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();
registerTab(tabData) {
this.tabList[tabData.id] = tabData;
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);
});
unregisterTab(tabData) {
console.log(tabData);
},
updateTab(tabData) {
this.registerTab(tabData);
},
observeElementChanges() {
this.contentObserver = new MutationObserver(throttle(this.debounceTransition, 50));
this.navigationObserver = new MutationObserver(throttle(this.debounceTransition, 50));
this.contentObserver.observe(this.$refs.tabContent, {
childList: true,
attributes: true,
characterData: true,
subtree: true,
attributeOldValue: true,
characterDataOldValue: true
});
this.navigationObserver.observe(this.$refs.tabNavigation, {
attributes: true
});
},
getTabIndex(id) {
let idList = Object.keys(this.tabList);
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];
let left = activeTab.offsetLeft;
let right = tabsWidth - left - activeTab.offsetWidth;
this.$refs.indicator.style.left = left + 'px';
this.$refs.indicator.style.right = right + 'px';
},
calculatePosition() {
window.requestAnimationFrame(() => {
this.calculateIndicatorPos();
});
},
onWindowResize() {
this.$refs.indicator.classList.add('md-transition-off');
this.calculatePosition();
this.debounceTransition();
},
setActiveTab(tabData) {
this.hasIcons = !!tabData.icon;
this.hasLabel = !!tabData.label;
this.activeTab = tabData.id;
this.activeTabNumber = this.getTabIndex(this.activeTab);
this.calculatePosition();
}
},
mounted() {
if (!this.activeTab) {
this.changeTab();
}
this.$nextTick(() => {
this.observeElementChanges();
window.addEventListener('resize', this.onWindowResize);
if (!this.activeTab) {
let firstTab = Object.keys(this.tabList)[0];
window.addEventListener('resize', this.recalculateAllTabsPos);
this.setActiveTab(this.tabList[firstTab]);
}
});
},
beforeDestroy() {
window.removeEventListener('resize', this.recalculateAllTabsPos);
this.contentObserver.disconnect();
window.removeEventListener('resize', this.onWindowResize);
}
};
</script>

View file

@ -0,0 +1,13 @@
const debounce = (callback, wait) => {
let timeout;
return (...args) => {
window.clearTimeout(timeout);
timeout = window.setTimeout(() => {
timeout = null;
callback.apply(this, args);
}, wait);
};
};
export default debounce;

View file

@ -0,0 +1,16 @@
const debounce = (callback, limit) => {
var wait = false;
return () => {
if (!wait) {
callback.call();
wait = true;
window.setTimeout(() => {
wait = false;
}, limit);
}
};
};
export default debounce;

View file

@ -0,0 +1,5 @@
const uniqueId = () => {
return Math.random().toString(36).slice(4);
};
export default uniqueId;