1
// Styles
2 1
import './VBtn.sass'
3

4
// Extensions
5 1
import VSheet from '../VSheet'
6

7
// Components
8 1
import VProgressCircular from '../VProgressCircular'
9

10
// Mixins
11 1
import { factory as GroupableFactory } from '../../mixins/groupable'
12 1
import { factory as ToggleableFactory } from '../../mixins/toggleable'
13 1
import Positionable from '../../mixins/positionable'
14 1
import Routable from '../../mixins/routable'
15 1
import Sizeable from '../../mixins/sizeable'
16

17
// Utilities
18 1
import mixins, { ExtractVue } from '../../util/mixins'
19 1
import { breaking } from '../../util/console'
20

21
// Types
22
import { VNode } from 'vue'
23
import { PropValidator, PropType } from 'vue/types/options'
24
import { RippleOptions } from '../../directives/ripple'
25

26 1
const baseMixins = mixins(
27
  VSheet,
28
  Routable,
29
  Positionable,
30
  Sizeable,
31
  GroupableFactory('btnToggle'),
32
  ToggleableFactory('inputValue')
33
  /* @vue/component */
34
)
35
interface options extends ExtractVue<typeof baseMixins> {
36
  $el: HTMLElement
37
}
38

39 1
export default baseMixins.extend<options>().extend({
40
  name: 'v-btn',
41

42
  props: {
43
    activeClass: {
44
      type: String,
45 1
      default (): string | undefined {
46 1
        if (!this.btnToggle) return ''
47

48 1
        return this.btnToggle.activeClass
49
      },
50
    } as any as PropValidator<string>,
51
    block: Boolean,
52
    depressed: Boolean,
53
    fab: Boolean,
54
    icon: Boolean,
55
    loading: Boolean,
56
    outlined: Boolean,
57
    retainFocusOnClick: Boolean,
58
    rounded: Boolean,
59
    tag: {
60
      type: String,
61
      default: 'button',
62
    },
63
    text: Boolean,
64
    tile: Boolean,
65
    type: {
66
      type: String,
67
      default: 'button',
68
    },
69
    value: null as any as PropType<any>,
70
  },
71

72 1
  data: () => ({
73
    proxyClass: 'v-btn--active',
74
  }),
75

76
  computed: {
77 1
    classes (): any {
78 1
      return {
79
        'v-btn': true,
80
        ...Routable.options.computed.classes.call(this),
81
        'v-btn--absolute': this.absolute,
82
        'v-btn--block': this.block,
83
        'v-btn--bottom': this.bottom,
84
        'v-btn--contained': this.contained,
85 1
        'v-btn--depressed': (this.depressed) || this.outlined,
86
        'v-btn--disabled': this.disabled,
87
        'v-btn--fab': this.fab,
88
        'v-btn--fixed': this.fixed,
89
        'v-btn--flat': this.isFlat,
90
        'v-btn--icon': this.icon,
91
        'v-btn--left': this.left,
92
        'v-btn--loading': this.loading,
93
        'v-btn--outlined': this.outlined,
94
        'v-btn--right': this.right,
95
        'v-btn--round': this.isRound,
96
        'v-btn--rounded': this.rounded,
97
        'v-btn--router': this.to,
98
        'v-btn--text': this.text,
99
        'v-btn--tile': this.tile,
100
        'v-btn--top': this.top,
101
        ...this.themeClasses,
102
        ...this.groupClasses,
103
        ...this.elevationClasses,
104
        ...this.sizeableClasses,
105
      }
106
    },
107 1
    contained (): boolean {
108 1
      return Boolean(
109 1
        !this.isFlat &&
110 1
        !this.depressed &&
111
        // Contained class only adds elevation
112
        // is not needed if user provides value
113 1
        !this.elevation
114
      )
115
    },
116 1
    computedRipple (): RippleOptions | boolean {
117 1
      const defaultRipple = this.icon || this.fab ? { circle: true } : true
118 1
      if (this.disabled) return false
119 1
      else return this.ripple ?? defaultRipple
120
    },
121 1
    isFlat (): boolean {
122 1
      return Boolean(
123 1
        this.icon ||
124 1
        this.text ||
125 1
        this.outlined
126
      )
127
    },
128 1
    isRound (): boolean {
129 1
      return Boolean(
130 1
        this.icon ||
131 1
        this.fab
132
      )
133
    },
134 1
    styles (): object {
135 1
      return {
136
        ...this.measurableStyles,
137
      }
138
    },
139
  },
140

141 1
  created () {
142 1
    const breakingProps = [
143
      ['flat', 'text'],
144
      ['outline', 'outlined'],
145
      ['round', 'rounded'],
146
    ]
147

148
    /* istanbul ignore next */
149
    breakingProps.forEach(([original, replacement]) => {
150
      if (this.$attrs.hasOwnProperty(original)) breaking(original, replacement, this)
151
    })
152
  },
153

154
  methods: {
155 1
    click (e: MouseEvent): void {
156
      // TODO: Remove this in v3
157 1
      !this.retainFocusOnClick && !this.fab && e.detail && this.$el.blur()
158 1
      this.$emit('click', e)
159

160 1
      this.btnToggle && this.toggle()
161
    },
162 1
    genContent (): VNode {
163 1
      return this.$createElement('span', {
164
        staticClass: 'v-btn__content',
165
      }, this.$slots.default)
166
    },
167 1
    genLoader (): VNode {
168 1
      return this.$createElement('span', {
169
        class: 'v-btn__loader',
170 1
      }, this.$slots.loader || [this.$createElement(VProgressCircular, {
171
        props: {
172
          indeterminate: true,
173
          size: 23,
174
          width: 2,
175
        },
176
      })])
177
    },
178
  },
179

180 1
  render (h): VNode {
181 1
    const children = [
182
      this.genContent(),
183 1
      this.loading && this.genLoader(),
184
    ]
185 1
    const setColor = !this.isFlat ? this.setBackgroundColor : this.setTextColor
186 1
    const { tag, data } = this.generateRouteLink()
187

188 1
    if (tag === 'button') {
189 1
      data.attrs!.type = this.type
190 1
      data.attrs!.disabled = this.disabled
191
    }
192 1
    data.attrs!.value = ['string', 'number'].includes(typeof this.value)
193 1
      ? this.value
194 1
      : JSON.stringify(this.value)
195

196 1
    return h(tag, this.disabled ? data : setColor(this.color, data), children)
197
  },
198
})

Read our documentation on viewing source code .

Loading