vuetify
Version:
Vue Material Component Framework
199 lines (180 loc) • 5.08 kB
text/typescript
// Styles
import './VBtn.sass'
// Extensions
import VSheet from '../VSheet'
// Components
import VProgressCircular from '../VProgressCircular'
// Mixins
import { factory as GroupableFactory } from '../../mixins/groupable'
import { factory as ToggleableFactory } from '../../mixins/toggleable'
import Positionable from '../../mixins/positionable'
import Routable from '../../mixins/routable'
import Sizeable from '../../mixins/sizeable'
// Utilities
import mixins, { ExtractVue } from '../../util/mixins'
import { breaking } from '../../util/console'
// Types
import { VNode } from 'vue'
import { PropValidator, PropType } from 'vue/types/options'
import { RippleOptions } from '../../directives/ripple'
const baseMixins = mixins(
VSheet,
Routable,
Positionable,
Sizeable,
GroupableFactory('btnToggle'),
ToggleableFactory('inputValue')
/* @vue/component */
)
interface options extends ExtractVue<typeof baseMixins> {
$el: HTMLElement
}
export default baseMixins.extend<options>().extend({
name: 'v-btn',
props: {
activeClass: {
type: String,
default (): string | undefined {
if (!this.btnToggle) return ''
return this.btnToggle.activeClass
},
} as any as PropValidator<string>,
block: Boolean,
depressed: Boolean,
fab: Boolean,
icon: Boolean,
loading: Boolean,
outlined: Boolean,
retainFocusOnClick: Boolean,
rounded: Boolean,
tag: {
type: String,
default: 'button',
},
text: Boolean,
tile: Boolean,
type: {
type: String,
default: 'button',
},
value: null as any as PropType<any>,
},
data: () => ({
proxyClass: 'v-btn--active',
}),
computed: {
classes (): any {
return {
'v-btn': true,
...Routable.options.computed.classes.call(this),
'v-btn--absolute': this.absolute,
'v-btn--block': this.block,
'v-btn--bottom': this.bottom,
'v-btn--contained': this.contained,
'v-btn--depressed': (this.depressed) || this.outlined,
'v-btn--disabled': this.disabled,
'v-btn--fab': this.fab,
'v-btn--fixed': this.fixed,
'v-btn--flat': this.isFlat,
'v-btn--icon': this.icon,
'v-btn--left': this.left,
'v-btn--loading': this.loading,
'v-btn--outlined': this.outlined,
'v-btn--right': this.right,
'v-btn--round': this.isRound,
'v-btn--rounded': this.rounded,
'v-btn--router': this.to,
'v-btn--text': this.text,
'v-btn--tile': this.tile,
'v-btn--top': this.top,
...this.themeClasses,
...this.groupClasses,
...this.elevationClasses,
...this.sizeableClasses,
}
},
contained (): boolean {
return Boolean(
!this.isFlat &&
!this.depressed &&
// Contained class only adds elevation
// is not needed if user provides value
!this.elevation
)
},
computedRipple (): RippleOptions | boolean {
const defaultRipple = this.icon || this.fab ? { circle: true } : true
if (this.disabled) return false
else return this.ripple ?? defaultRipple
},
isFlat (): boolean {
return Boolean(
this.icon ||
this.text ||
this.outlined
)
},
isRound (): boolean {
return Boolean(
this.icon ||
this.fab
)
},
styles (): object {
return {
...this.measurableStyles,
}
},
},
created () {
const breakingProps = [
['flat', 'text'],
['outline', 'outlined'],
['round', 'rounded'],
]
/* istanbul ignore next */
breakingProps.forEach(([original, replacement]) => {
if (this.$attrs.hasOwnProperty(original)) breaking(original, replacement, this)
})
},
methods: {
click (e: MouseEvent): void {
// TODO: Remove this in v3
!this.retainFocusOnClick && !this.fab && e.detail && this.$el.blur()
this.$emit('click', e)
this.btnToggle && this.toggle()
},
genContent (): VNode {
return this.$createElement('span', {
staticClass: 'v-btn__content',
}, this.$slots.default)
},
genLoader (): VNode {
return this.$createElement('span', {
class: 'v-btn__loader',
}, this.$slots.loader || [this.$createElement(VProgressCircular, {
props: {
indeterminate: true,
size: 23,
width: 2,
},
})])
},
},
render (h): VNode {
const children = [
this.genContent(),
this.loading && this.genLoader(),
]
const setColor = !this.isFlat ? this.setBackgroundColor : this.setTextColor
const { tag, data } = this.generateRouteLink()
if (tag === 'button') {
data.attrs!.type = this.type
data.attrs!.disabled = this.disabled
}
data.attrs!.value = ['string', 'number'].includes(typeof this.value)
? this.value
: JSON.stringify(this.value)
return h(tag, this.disabled ? data : setColor(this.color, data), children)
},
})