mirror of
https://github.com/Hopiu/vue-material.git
synced 2026-04-24 08:34:50 +00:00
start creation of tabs
This commit is contained in:
parent
1aa10cd9a7
commit
f27d56fc09
12 changed files with 855 additions and 176 deletions
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
11
package.json
11
package.json
|
|
@ -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"
|
||||
]
|
||||
]]
|
||||
]
|
||||
}
|
||||
|
|
|
|||
395
src/components/mdTable/mdTable.old.scss
Normal file
395
src/components/mdTable/mdTable.old.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
70
src/components/mdTabs/mdTab.old.vue
Normal file
70
src/components/mdTabs/mdTab.old.vue
Normal 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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
211
src/components/mdTabs/mdTabs.old.vue
Normal file
211
src/components/mdTabs/mdTabs.old.vue
Normal 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>
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
13
src/core/utils/debounce.js
Normal file
13
src/core/utils/debounce.js
Normal 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;
|
||||
16
src/core/utils/throttle.js
Normal file
16
src/core/utils/throttle.js
Normal 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;
|
||||
5
src/core/utils/uniqueId.js
Normal file
5
src/core/utils/uniqueId.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
const uniqueId = () => {
|
||||
return Math.random().toString(36).slice(4);
|
||||
};
|
||||
|
||||
export default uniqueId;
|
||||
Loading…
Reference in a new issue