update Tabs

update Tabs
This commit is contained in:
梁灏 2016-12-06 22:51:06 +08:00
parent 871ed4d810
commit 17f52abf5b
7 changed files with 510 additions and 20 deletions

View file

@ -1,13 +1,51 @@
<template>
<div :class="prefixCls"><slot></slot></div>
</template>
<script>
const prefixCls = 'ivu-tabs-tabpane';
export default {
props: {},
data () {
return {}
name: 'TabPane',
props: {
key: {
type: String
},
label: {
type: String,
default: ''
},
icon: {
type: String
},
disabled: {
type: Boolean,
default: false
}
},
computed: {},
methods: {}
data () {
return {
prefixCls: prefixCls,
show: true
}
},
computed: {
},
methods: {
updateNav () {
this.$parent.updateNav();
}
},
watch: {
label () {
this.updateNav();
},
icon () {
this.updateNav();
},
disabled () {
this.updateNav();
}
}
}
</script>

View file

@ -1,13 +1,200 @@
<template>
<div :class="classes">
<div :class="[prefixCls + '-bar']">
<div :class="[prefixCls + '-nav-container']">
<div :class="[prefixCls + '-nav-wrap']">
<div :class="[prefixCls + '-nav-scroll']">
<div :class="[prefixCls + '-nav']" v-el:nav>
<div :class="barClasses" :style="barStyle"></div>
<div :class="tabCls(item)" v-for="item in navList" @click="handleChange($index)">
<Icon v-if="item.icon !== ''" :type="item.icon"></Icon>
{{ item.label }}
<Icon v-if="closable && type === 'card'" type="ios-close-empty" @click.stop="handleRemove($index)"></Icon>
</div>
</div>
</div>
</div>
</div>
</div>
<div :class="contentClasses" :style="contentStyle"><slot></slot></div>
</div>
</template>
<script>
import Icon from '../icon/icon.vue';
import { oneOf, getStyle } from '../../utils/assist';
const prefixCls = 'ivu-tabs';
export default {
props: {},
data () {
return {}
components: { Icon },
props: {
activeKey: {
type: [String, Number]
},
type: {
validator (value) {
return oneOf(value, ['line', 'card']);
},
default: 'line'
},
size: {
validator (value) {
return oneOf(value, ['small', 'default']);
},
default: 'default'
},
animated: {
type: Boolean,
default: true
},
closable: {
type: Boolean,
default: false
}
},
computed: {},
methods: {}
data () {
return {
prefixCls: prefixCls,
navList: [],
barWidth: 0,
barOffset: 0
}
},
computed: {
classes () {
return [
`${prefixCls}`,
{
[`${prefixCls}-card`]: this.type === 'card',
[`${prefixCls}-mini`]: this.size === 'small' && this.type === 'line',
[`${prefixCls}-no-animation`]: !this.animated
}
]
},
contentClasses () {
return [
`${prefixCls}-content`,
`${prefixCls}-content-animated`
]
},
barClasses () {
return [
`${prefixCls}-ink-bar`,
`${prefixCls}-ink-bar-animated`
]
},
contentStyle () {
const x = this.navList.findIndex((nav, index) => nav.key === this.activeKey);
const p = x === 0 ? '0%' : `-${x}00%`;
let style = {};
if (x > -1) {
style = {
transform: `translateX(${p}) translateZ(0px)`
}
}
return style;
},
barStyle () {
let style = {
display: 'none',
width: `${this.barWidth}px`,
transform: `translate3d(${this.barOffset}px, 0px, 0px)`
};
if (this.type === 'line') style.display = 'block';
return style;
}
},
methods: {
getTabs () {
return this.$children.filter(item => item.$options.name === 'TabPane');
},
updateNav () {
this.navList = [];
this.getTabs().forEach((pane, index) => {
this.navList.push({
label: pane.label,
icon: pane.icon || '',
key: pane.key || index,
disabled: pane.disabled
});
if (!pane.key) pane.key = index;
if (index === 0) {
if (!this.activeKey) this.activeKey = pane.key || index;
}
});
this.updateBar();
},
updateBar () {
this.$nextTick(() => {
const index = this.navList.findIndex((nav, index) => nav.key === this.activeKey);
const prevTabs = this.$els.nav.querySelectorAll(`.${prefixCls}-tab`);
const tab = prevTabs[index];
this.barWidth = parseFloat(getStyle(tab, 'width'));
if (index > 0) {
let offset = 0;
const gutter = this.size === 'small' ? 0 : 16;
for (let i = 0; i < index; i++) {
offset += parseFloat(getStyle(prevTabs[i], 'width')) + gutter;
}
this.barOffset = offset;
} else {
this.barOffset = 0;
}
});
},
tabCls (item) {
return [
`${prefixCls}-tab`,
{
[`${prefixCls}-tab-disabled`]: item.disabled,
[`${prefixCls}-tab-active`]: item.key === this.activeKey
}
]
},
handleChange (index) {
const nav = this.navList[index];
if (nav.disabled) return;
this.activeKey = nav.key;
this.$emit('on-click', nav.key);
},
handleRemove (index) {
const tabs = this.getTabs();
const tab = tabs[index];
tab.$destroy(true);
if (tab.key === this.activeKey) {
const newTabs = this.getTabs();
let activeKey = -1;
if (newTabs.length) {
const leftNoDisabledTabs = tabs.filter((item, itemIndex) => !item.disabled && itemIndex < index);
const rightNoDisabledTabs = tabs.filter((item, itemIndex) => !item.disabled && itemIndex > index);
if (rightNoDisabledTabs.length) {
activeKey = rightNoDisabledTabs[0].key;
} else if (leftNoDisabledTabs.length) {
activeKey = leftNoDisabledTabs[leftNoDisabledTabs.length - 1].key;
} else {
activeKey = newTabs[0].key;
}
}
this.activeKey = activeKey;
}
this.$emit('on-tab-remove', tab.key);
this.updateNav();
}
},
compiled () {
this.updateNav();
},
watch: {
activeKey () {
this.updateBar();
}
}
}
</script>

View file

@ -1 +1,205 @@
@tabs-prefix-cls: ~"@{css-prefix}tabs";
@tabs-prefix-cls: ~"@{css-prefix}tabs";
.@{tabs-prefix-cls} {
box-sizing: border-box;
position: relative;
overflow: hidden;
color: @text-color;
.clearfix;
&-bar {
outline: none;
}
&-ink-bar {
height: 2px;
box-sizing: border-box;
background-color: @primary-color;
position: absolute;
left: 0;
bottom: 1px;
z-index: 1;
transition: transform .3s @ease-in-out;
transform-origin: 0 0;
}
&-bar {
border-bottom: 1px solid @border-color-base;
margin-bottom: 16px;
}
&-nav-container {
margin-bottom: -1px;
line-height: @line-height-base;
font-size: @font-size-base;
box-sizing: border-box;
white-space: nowrap;
overflow: hidden;
position: relative;
.clearfix;
}
&-nav-container-scrolling {
padding-left: 32px;
padding-right: 32px;
}
&-nav-wrap {
overflow: hidden;
margin-bottom: -1px;
}
&-nav-scroll {
overflow: hidden;
white-space: nowrap;
}
&-nav {
padding-left: 0;
margin: 0;
float: left;
list-style: none;
box-sizing: border-box;
position: relative;
transition: transform 0.5s @ease-in-out;
&:before,
&:after {
display: table;
content: " ";
}
&:after {
clear: both;
}
.@{tabs-prefix-cls}-tab-disabled {
pointer-events: none;
cursor: default;
color: #ccc;
}
.@{tabs-prefix-cls}-tab {
display: inline-block;
height: 100%;
padding: 8px 16px;
margin-right: 16px;
box-sizing: border-box;
cursor: pointer;
text-decoration: none;
position: relative;
transition: color .3s @ease-in-out;
&:hover {
color: @link-hover-color;
}
&:active {
color: @link-active-color;
}
.@{css-prefix-iconfont} {
width: 14px;
height: 14px;
margin-right: 8px;
}
}
.@{tabs-prefix-cls}-tab-active {
color: @primary-color;
}
}
&-mini &-nav-container {
font-size: @font-size-base;
}
&-mini &-tab {
margin-right: 0;
padding: 8px 16px;
}
& {
.@{tabs-prefix-cls}-content-animated {
display: flex;
flex-direction: row;
will-change: transform;
transition: transform .3s @ease-in-out;
}
.@{tabs-prefix-cls}-tabpane {
flex-shrink: 0;
width: 100%;
transition: opacity .3s;
opacity: 1;
}
.@{tabs-prefix-cls}-tabpane-inactive {
opacity: 0;
height: 0;
}
}
// card style
&&-card > &-bar &-nav-container {
height: 32px;
}
&&-card > &-bar &-ink-bar {
visibility: hidden;
}
&&-card > &-bar &-tab {
margin: 0;
margin-right: 4px;
height: 31px;
padding: 5px 16px 4px;
border: 1px solid @border-color-base;
border-bottom: 0;
border-radius: @btn-border-radius @btn-border-radius 0 0;
transition: all 0.3s @ease-in-out;
background: @table-thead-bg;
}
&&-card > &-bar &-tab-active {
height: 32px;
padding-bottom: 5px;
background: #fff;
transform: translateZ(0);
border-color: @border-color-base;
color: @primary-color;
}
&&-card > &-bar &-nav-wrap {
margin-bottom: 0;
}
&&-card > &-bar &-tab .@{css-prefix-iconfont}-ios-close-empty {
width: 0;
height: 22px;
font-size: 22px;
margin-right: 0;
color: @legend-color;
text-align: right;
vertical-align: middle;
overflow: hidden;
position: relative;
top: -1px;
transform-origin: 100% 50%;
transition: all 0.3s @ease-in-out;
&:hover {
color: #444;
}
}
&&-card > &-bar &-tab-active .@{css-prefix-iconfont}-ios-close-empty,
&&-card > &-bar &-tab:hover .@{css-prefix-iconfont}-ios-close-empty {
width: 14px;
transform: translateZ(0);
}
}
.@{tabs-prefix-cls}-no-animation{
.@{tabs-prefix-cls}-content {
&-animated {
transform: none!important;
}
> .@{tabs-prefix-cls}-tabpane-inactive {
display: none;
}
}
}