@minix-iot/ui
Version:
基于Vue的移动端轻量级UI组件库
214 lines (207 loc) • 6.99 kB
JSX
import { Namespace } from "../../utils/namespace";
import { MathExtension } from '../../utils/extensions/math';
import { mixParent } from '../../mixins/relation/parent'
import Badge from '../badge/badge.jsx'
import Icon from '../icon/icon.jsx'
export const namespace =Namespace.Create('tab')
export default {
name: namespace.name,
mixins:[
mixParent(namespace.name)
],
components:{
Badge,
Icon
},
props:{
value:{
type: String | Number,
default: 0
},
type:{
type: String,
default: 'line'
},
position:{
type: String,
default: 'top'
},
duration:{
type: String | Number,
default: 0.36
},
lineColor:{
type: String,
default: '#000000'
},
lineSize:{
type: String | Number,
default: '2'
},
animated:{
type: Boolean,
default: false
},
headerClass:{
type: String
},
titleActiveColor:{
type: String,
default:'#000000'
},
titleActiveBackground:{
type: String,
default:'#000000'
},
titleInactiveBackground:{
type: String,
default: '#F2F2F2'
},
titleInactiveColor:{
type: String,
default:'#8A8A8F'
}
},
model:{
prop:'value',
event:'change'
},
data(){
return {
inited:false,
isLineInited:false
}
},
computed:{
cls(){
return namespace.derive([this.position])
},
headerCls(){
return [namespace.derive('header',[this.position]),this.headerClass]
},
navCls(){
return namespace.derive('nav',[this.position])
},
navStyle(){
const style = {}
if(this.inited){
const nav = this.$refs.nav
const maxOffset =this.isHorizental ? (nav.scrollWidth - nav.offsetWidth): (nav.scrollHeight - nav.offsetHeight)
const el = this.$refs[`nav_button_${this.value}`]
const offset = this.isHorizental ?
(el.offsetLeft + el.offsetWidth / 2 - nav.offsetWidth / 2)
: (el.offsetTop + el.offsetHeight / 2 - nav.offsetHeight / 2)
style.transform = `translate${this.isHorizental ? 'X':'Y'}(${-MathExtension.Clamp(offset, 0, maxOffset)}px)`
}
return style
},
lineCls(){
return namespace.derive('nav-line',[this.position])
},
lineStyle(){
if(this.type !== 'line'){
return
}
const style={
background: this.lineColor,
[this.isHorizental?'height':'width']: `${this.lineSize}px`,
}
if(this.inited){
const el = this.$refs[`nav_button_text_${this.value}`]
style[this.isHorizental?'width':'height'] = `${this.isHorizental ? el.offsetWidth : el.offsetHeight}px`
style.transform = `translate${this.isHorizental?'X':'Y'}(${this.isHorizental ? el.offsetLeft : el.offsetTop}px)`
if(!this.isLineInited){
style.transitionDuration = '0s'
this.isLineInited = true
}
}
return style
},
bodyCls(){
return namespace.derive('body',[this.position])
},
trackCls(){
return namespace.derive('body-track')
},
trackStyle(){
const style = {
transform:`translateX(${-this.value * 100}%)`,
transitionDuration: (!this.inited || !this.animated) ? '0s' : `${this.duration}s`
}
return style
},
buttonIconCls(){
return namespace.derive('nav-button-text--button-icon')
},
isFrontHeader(){
return this.position === 'top' || this.position === 'left'
},
isRearHeader(){
return this.position === 'bottom' || this.position === 'right'
},
isVertical(){
return this.position === 'left' || this.position === 'right'
},
isHorizental(){
return this.position === 'top' || this.position === 'bottom'
}
},
mounted(){
this.$nextTick(()=>this.inited = true)
},
methods:{
onButtonClick(button,index,e){
this.$emit('change', index)
}
},
render(){
const renderNav= ()=> this.children.map((x,index)=>{
const isActive = this.value === index
const buttonCls = namespace.derive('nav-button',[this.position,this.type])
const textCls = namespace.derive('nav-button-text',[this.type=== 'button' ? 'button': 'text', {active:isActive}])
const textStyle = {
color: isActive ? this.titleActiveColor: this.titleInactiveColor
}
if(this.type === 'button'){
textStyle.background = isActive ? this.titleActiveBackground : this.titleInactiveBackground
}
const tag = this.type === 'button'? 'div' : 'span'
const text = <tag ref={`nav_button_text_${index}`} class={textCls} style={textStyle}>
{
x.$scopedSlots.title?.({active: isActive})
||[
x.icon && <Icon class={this.buttonIconCls} name={x.icon} size={x.iconSize} color={textStyle.color} />,
x.title
]
}
</tag>
const content = (x.dot || x.badge ) ?
<Badge dot={x.dot} content={x.badge} small>{text}</Badge>
: text
return (
<div ref={`nav_button_${index}`} class={buttonCls} onClick={e=>this.onButtonClick(x,index,e)}>
{content}
</div>
)
})
const renderHeader = ()=>(
<div ref="header" class={this.headerCls}>
<div ref="nav" class={this.navCls} style={this.navStyle}>
{renderNav()}
{this.type === 'line' && <div class={this.lineCls} style={this.lineStyle}/>}
</div>
</div>
)
return (
<div class={this.cls}>
{this.isFrontHeader && renderHeader()}
<div class={this.bodyCls}>
<div class={this.trackCls} style={this.trackStyle}>
{this.$slots.default}
</div>
</div>
{this.isRearHeader && renderHeader()}
</div>
)
}
}