UNPKG

@minix-iot/ui

Version:

基于Vue的移动端轻量级UI组件库

214 lines (207 loc) 6.99 kB
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> ) } }