tabindex: this.disabled ? -1 : undefined,

tabindex: this.disabled ? -1 : this.$attrs.tabindex,

// Styles
import '../../stylus/components/_text-fields.styl'

// Extensions
import VInput from '../VInput'

// Components
import VCounter from '../VCounter'
import VLabel from '../VLabel'

// Mixins
import Maskable from '../../mixins/maskable'

// Directives
import Ripple from '../../directives/ripple'

// Utilities
import {
  keyCodes
} from '../../util/helpers'
import { deprecate } from '../../util/console'

const dirtyTypes = ['color', 'file', 'time', 'date', 'datetime-local', 'week', 'month']

/* @vue/component */
export default {
  name: 'v-text-field',

  directives: { Ripple },

  extends: VInput,

  mixins: [Maskable],

  inheritAttrs: false,

  props: {
    appendOuterIcon: String,
    /** @deprecated */
    appendOuterIconCb: Function,
    autofocus: Boolean,
    box: Boolean,
    browserAutocomplete: String,
    clearable: Boolean,
    clearIcon: {
      type: String,
      default: '$vuetify.icons.clear'
    },
    clearIconCb: Function,
    color: {
      type: String,
      default: 'primary'
    },
    counter: [Boolean, Number, String],
    flat: Boolean,
    fullWidth: Boolean,
    label: String,
    outline: Boolean,
    placeholder: String,
    prefix: String,
    prependInnerIcon: String,
    /** @deprecated */
    prependInnerIconCb: Function,
    reverse: Boolean,
    singleLine: Boolean,
    solo: Boolean,
    soloInverted: Boolean,
    suffix: String,
    textarea: Boolean, // TODO: Deprecate
    type: {
      type: String,
      default: 'text'
    }
  },

  data: () => ({
    badInput: false,
    initialValue: null,
    internalChange: false,
    isClearing: false
  }),

  computed: {
    classes () {
      return {
        'v-text-field': true,
        'v-text-field--full-width': this.fullWidth,
        'v-text-field--prefix': this.prefix,
        'v-text-field--single-line': this.isSingle,
        'v-text-field--solo': this.isSolo,
        'v-text-field--solo-inverted': this.soloInverted,
        'v-text-field--solo-flat': this.flat,
        'v-text-field--box': this.box,
        'v-text-field--enclosed': this.isEnclosed,
        'v-text-field--reverse': this.reverse,
        'v-text-field--outline': this.hasOutline
      }
    },
    counterValue () {
      return (this.internalValue || '').toString().length
    },
    directivesInput () {
      return []
    },
    // TODO: Deprecate
    hasOutline () {
      return this.outline || this.textarea
    },
    internalValue: {
      get () {
        return this.lazyValue
      },
      set (val) {
        if (this.mask) {
          this.lazyValue = this.unmaskText(this.maskText(this.unmaskText(val)))
          this.setSelectionRange()
        } else {
          this.lazyValue = val
          this.$emit('input', this.lazyValue)
        }
      }
    },
    isDirty () {
      return (this.lazyValue != null &&
        this.lazyValue.toString().length > 0) ||
        this.badInput
    },
    isEnclosed () {
      return (
        this.box ||
        this.isSolo ||
        this.hasOutline ||
        this.fullWidth
      )
    },
    isLabelActive () {
      return this.isDirty || dirtyTypes.includes(this.type)
    },
    isSingle () {
      return this.isSolo || this.singleLine
    },
    isSolo () {
      return this.solo || this.soloInverted
    },
    labelPosition () {
      const offset = (this.prefix && !this.labelValue) ? 16 : 0

      return (!this.$vuetify.rtl !== !this.reverse) ? {
        left: 'auto',
        right: offset
      } : {
        left: offset,
        right: 'auto'
      }
    },
    showLabel () {
      return this.hasLabel && (!this.isSingle || (!this.isLabelActive && !this.placeholder))
    },
    labelValue () {
      return !this.isSingle &&
        Boolean(this.isFocused || this.isLabelActive || this.placeholder)
    }
  },

  watch: {
    isFocused (val) {
      // Sets validationState from validatable
      this.hasColor = val

      if (val) {
        this.initialValue = this.lazyValue
      } else if (this.initialValue !== this.lazyValue) {
        this.$emit('change', this.lazyValue)
      }
    },
    value (val) {
      if (this.mask && !this.internalChange) {
        const masked = this.maskText(this.unmaskText(val))
        this.lazyValue = this.unmaskText(masked)

        // Emit when the externally set value was modified internally
        String(val) !== this.lazyValue && this.$nextTick(() => {
          this.$refs.input.value = masked
          this.$emit('input', this.lazyValue)
        })
      } else this.lazyValue = val
    }
  },

  mounted () {
    this.autofocus && this.onFocus()
  },

  methods: {
    /** @public */
    focus () {
      this.onFocus()
    },
    /** @public */
    blur () {
      this.onBlur()
    },
    clearableCallback () {
      this.internalValue = null
      this.$nextTick(() => this.$refs.input.focus())
    },
    genAppendSlot () {
      const slot = []

      if (this.$slots['append-outer']) {
        slot.push(this.$slots['append-outer'])
      } else if (this.appendOuterIcon) {
        slot.push(this.genIcon('appendOuter'))
      }

      return this.genSlot('append', 'outer', slot)
    },
    genPrependInnerSlot () {
      const slot = []

      if (this.$slots['prepend-inner']) {
        slot.push(this.$slots['prepend-inner'])
      } else if (this.prependInnerIcon) {
        slot.push(this.genIcon('prependInner'))
      }

      return this.genSlot('prepend', 'inner', slot)
    },
    genIconSlot () {
      const slot = []

      if (this.$slots['append']) {
        slot.push(this.$slots['append'])
      } else if (this.appendIcon) {
        slot.push(this.genIcon('append'))
      }

      return this.genSlot('append', 'inner', slot)
    },
    genInputSlot () {
      const input = VInput.methods.genInputSlot.call(this)

      const prepend = this.genPrependInnerSlot()
      prepend && input.children.unshift(prepend)

      return input
    },
    genClearIcon () {
      if (!this.clearable) return null

      const icon = !this.isDirty
        ? false
        : 'clear'

      if (this.clearIconCb) deprecate(':clear-icon-cb', '@click:clear', this)

      return this.genSlot('append', 'inner', [
        this.genIcon(
          icon,
          (!this.$listeners['click:clear'] && this.clearIconCb) || this.clearableCallback,
          false
        )
      ])
    },
    genCounter () {
      if (this.counter === false || this.counter == null) return null

      const max = this.counter === true ? this.$attrs.maxlength : this.counter

      return this.$createElement(VCounter, {
        props: {
          dark: this.dark,
          light: this.light,
          max,
          value: this.counterValue
        }
      })
    },
    genDefaultSlot () {
      return [
        this.genTextFieldSlot(),
        this.genClearIcon(),
        this.genIconSlot()
      ]
    },
    genLabel () {
      if (!this.showLabel) return null

      const data = {
        props: {
          absolute: true,
          color: this.validationState,
          dark: this.dark,
          disabled: this.disabled,
          focused: !this.isSingle && (this.isFocused || !!this.validationState),
          left: this.labelPosition.left,
          light: this.light,
          right: this.labelPosition.right,
          value: this.labelValue
        }
      }

      if (this.$attrs.id) data.props.for = this.$attrs.id

      return this.$createElement(VLabel, data, this.$slots.label || this.label)
    },
    genInput () {
      const listeners = Object.assign({}, this.$listeners)
      delete listeners['change'] // Change should not be bound externally

      const data = {
        style: {},
        domProps: {
          value: this.maskText(this.lazyValue)
        },
        attrs: {
          ...this.$attrs,
          autofocus: this.autofocus,
          disabled: this.disabled,
          readonly: this.readonly,
          tabindex: this.tabindex,
          type: this.type,
          'aria-label': (!this.$attrs || !this.$attrs.id) && this.label // Label `for` will be set if we have an id
        },
        on: Object.assign(listeners, {
          blur: this.onBlur,
          input: this.onInput,
          focus: this.onFocus,
          keydown: this.onKeyDown
        }),
        ref: 'input'
      }

      if (this.placeholder) data.attrs.placeholder = this.placeholder
      if (this.mask) data.attrs.maxlength = this.masked.length
      if (this.browserAutocomplete) data.attrs.autocomplete = this.browserAutocomplete

      return this.$createElement('input', data)
    },
    genMessages () {
      if (this.hideDetails) return null

      return this.$createElement('div', {
        staticClass: 'v-text-field__details'
      }, [
        VInput.methods.genMessages.call(this),
        this.genCounter()
      ])
    },
    genTextFieldSlot () {
      return this.$createElement('div', {
        staticClass: 'v-text-field__slot'
      }, [
        this.genLabel(),
        this.prefix ? this.genAffix('prefix') : null,
        this.genInput(),
        this.suffix ? this.genAffix('suffix') : null
      ])
    },
    genAffix (type) {
      return this.$createElement('div', {
        'class': `v-text-field__${type}`,
        ref: type
      }, this[type])
    },
    onBlur (e) {
      this.isFocused = false
      // Reset internalChange state
      // to allow external change
      // to persist
      this.internalChange = false

      this.$emit('blur', e)
    },
    onClick () {
      if (this.isFocused || this.disabled) return

      this.$refs.input.focus()
    },
    onFocus (e) {
      if (!this.$refs.input) return

      if (document.activeElement !== this.$refs.input) {
        return this.$refs.input.focus()
      }

      if (!this.isFocused) {
        this.isFocused = true
        this.$emit('focus', e)
      }
    },
    onInput (e) {
      this.internalChange = true
      this.mask && this.resetSelections(e.target)
      this.internalValue = e.target.value
      this.badInput = e.target.validity && e.target.validity.badInput
    },
    onKeyDown (e) {
      this.internalChange = true

      if (e.keyCode === keyCodes.enter) this.$emit('change', this.internalValue)

      this.$emit('keydown', e)
    },
    onMouseDown (e) {
      // Prevent input from being blurred
      if (e.target !== this.$refs.input) {
        e.preventDefault()
        e.stopPropagation()
      }

      VInput.methods.onMouseDown.call(this, e)
    },
    onMouseUp (e) {
      // Default click handler is on slot,
      // Mouse events are to enable specific
      // input types when clicked
      if (
        (this.isSolo || this.hasOutline) &&
        document.activeElement !== this.$refs.input
      ) {
        this.$refs.input.focus()
      }

      VInput.methods.onMouseUp.call(this, e)
    }
  }
}


// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`VTooltip.js should render component and match snapshot 1`] = `

      content

      activator

`;

exports[`VTooltip.js should render component and match snapshot 2`] = `

      content

      activator

`;

exports[`VTooltip.js should render component with custom lazy and match snapshot 1`] = `

      activator

`;

exports[`VTooltip.js should render component with value=true and match snapshot 1`] = `

      content

      activator

`;

exports[`VTooltip.js should render component with zIndex prop and match snapshot 1`] = `

`;


// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`VTabs should match active tab 1`] = `

`;

exports[`VTabs should render generic elements in the tab container 1`] = `

          foobar

`;

exports[`VTabs should show tabs arrows 1`] = `

`;


@import '../bootstrap'
@import '../theme'

/* Themes */
v-card($material)
  background-color: $material.cards
  color: $material.text.primary

theme(v-card, "v-card")

.v-card
  display: block
  border-radius: $card-border-radius
  min-width: 0
  position: relative
  text-decoration: none
  elevation(1)

  > *:first-child:not(.v-btn):not(.v-chip)
    border-top-left-radius: inherit
    border-top-right-radius: inherit

  > *:last-child:not(.v-btn):not(.v-chip)
    border-bottom-left-radius: inherit
    border-bottom-right-radius: inherit

  &--raised
    elevation(3)

  &--tile
    border-radius: 0

  &--flat
    elevation(0)

  &--hover
    cursor: pointer
    transition: all .4s cubic-bezier(.25, .8, .25, 1)
    transition-property: box-shadow

    &:hover
      elevation(8)

  &__title
    align-items: center
    display: flex
    flex-wrap: wrap
    padding: 16px

    &--primary
      padding-top: 24px

  &__text
    padding: 16px
    width: 100%

  &__media
    display: flex
    overflow: hidden
    position: relative

    img
      width: 100%

    &__background
      border-radius: inherit
      position: absolute
      left: 0
      top: 0
      width: 100%
      height: 100%

    &__content
      display: flex
      flex: 1 1 auto
      position: relative

  &__actions
    align-items: center
    display: flex
    padding: 8px

    > *,
    .v-btn
      margin: 0

    .v-btn + .v-btn
      margin-left: 8px


keep keep keep keep replace keep keep keep keep keep

// Styles
import '../../stylus/components/_sliders.styl'

// Components
import { VScaleTransition } from '../transitions'

// Extensions
import VInput from '../VInput'

// Directives
import ClickOutside from '../../directives/click-outside'

// Utilities
import {
  addOnceEventListener,
  convertToUnit,
  createRange,
  keyCodes,
  deepEqual
} from '../../util/helpers'
import { consoleWarn } from '../../util/console'

/* @vue/component */
export default {
  name: 'v-slider',

  directives: { ClickOutside },

  extends: VInput,

  props: {
    alwaysDirty: Boolean,
    inverseLabel: Boolean,
    label: String,
    min: {
      type: [Number, String],
      default: 0
    },
    max: {
      type: [Number, String],
      default: 100
    },
    range: Boolean,
    step: {
      type: [Number, String],
      default: 1
    },
    ticks: {
      type: [Boolean, String],
      default: false,
      validator: v => typeof v === 'boolean' || v === 'always'
    },
    tickLabels: {
      type: Array,
      default: () => ([])
    },
    tickSize: {
      type: [Number, String],
      default: 1
    },
    thumbColor: {
      type: String,
      default: null
    },
    thumbLabel: {
      type: [Boolean, String],
      default: null,
      validator: v => typeof v === 'boolean' || v === 'always'
    },
    thumbSize: {
      type: [Number, String],
      default: 32
    },
    trackColor: {
      type: String,
      default: null
    },
    value: [Number, String]
  },

  data: vm => ({
    app: {},
    defaultColor: 'primary',
    isActive: false,
    keyPressed: 0,
    lazyValue: typeof vm.value !== 'undefined' ? vm.value : Number(vm.min),
    oldValue: null
  }),

  computed: {
    classes () {
      return {
        'v-input--slider': true,
        'v-input--slider--ticks': this.showTicks,
        'v-input--slider--inverse-label': this.inverseLabel,
        'v-input--slider--ticks-labels': this.tickLabels.length > 0,
        'v-input--slider--thumb-label': this.thumbLabel ||
          this.$scopedSlots.thumbLabel
      }
    },
    showTicks () {
      return this.tickLabels.length > 0 ||
        (!this.disabled && this.stepNumeric && !!this.ticks)
    },
    showThumbLabel () {
      return !this.disabled && (
        !!this.thumbLabel ||
        this.thumbLabel === '' ||
        this.$scopedSlots['thumb-label']
      )
    },
    computedColor () {
      if (this.disabled) return null
      return this.validationState || this.color || this.defaultColor
    },
    computedTrackColor () {
      return this.disabled ? null : (this.trackColor || null)
    },
    computedThumbColor () {
      if (this.disabled || !this.isDirty) return null
      return this.validationState || this.thumbColor || this.color || this.defaultColor
    },
    internalValue: {
      get () {
        return this.lazyValue
      },
      set (val) {
        const { min, max } = this

        // Round value to ensure the
        // entire slider range can
        // be selected with step
        const value = this.roundValue(Math.min(Math.max(val, min), max))

        if (value === this.lazyValue) return

        this.lazyValue = value

        this.$emit('input', value)
        this.validate()
      }
    },
    stepNumeric () {
      return this.step > 0 ? parseFloat(this.step) : 0
    },
    trackFillStyles () {
      let left = this.$vuetify.rtl ? 'auto' : 0
      let right = this.$vuetify.rtl ? 0 : 'auto'
      let width = `${this.inputWidth}%`

      if (this.disabled) width = `calc(${this.inputWidth}% - 8px)`

      return {
        transition: this.trackTransition,
        left,
        right,
        width
      }
    },
    trackPadding () {
      return (
        this.isActive ||
        this.inputWidth > 0 ||
        this.disabled
      ) ? 0 : 7
    },
    trackStyles () {
      const trackPadding = this.disabled ? `calc(${this.inputWidth}% + 8px)` : `${this.trackPadding}px`
      let left = this.$vuetify.rtl ? 'auto' : trackPadding
      let right = this.$vuetify.rtl ? trackPadding : 'auto'
      let width = this.disabled
        ? `calc(${100 - this.inputWidth}% - 8px)`
        : '100%'

      return {
        transition: this.trackTransition,
        left,
        right,
        width
      }
    },
    tickStyles () {
      const size = Number(this.tickSize)

      return {
        'border-width': `${size}px`,
        'border-radius': size > 1 ? '50%' : null,
        transform: size > 1 ? `translateX(-${size}px) translateY(-${size - 1}px)` : null
      }
    },
    trackTransition () {
      return this.keyPressed >= 2 ? 'none' : ''
    },
    numTicks () {
      return Math.ceil((this.max - this.min) / this.stepNumeric)
    },
    inputWidth () {
      return (this.roundValue(this.internalValue) - this.min) / (this.max - this.min) * 100
    },
    isDirty () {
      return this.internalValue > this.min ||
        this.alwaysDirty
    }
  },

  watch: {
    min (val) {
      val > this.internalValue && this.$emit('input', parseFloat(val))
    },
    max (val) {
      val < this.internalValue && this.$emit('input', parseFloat(val))
    },
    value (val) {
      this.internalValue = val
    }
  },

  mounted () {
    // Without a v-app, iOS does not work with body selectors
    this.app = document.querySelector('[data-app]') ||
      consoleWarn('Missing v-app or a non-body wrapping element with the [data-app] attribute', this)
  },

  methods: {
    genDefaultSlot () {
      const children = [this.genLabel()]
      const slider = this.genSlider()

      this.inverseLabel
        ? children.unshift(slider)
        : children.push(slider)

      return children
    },
    genListeners () {
      return Object.assign({}, {
        blur: this.onBlur,
        click: this.onSliderClick,
        focus: this.onFocus,
        keydown: this.onKeyDown,
        keyup: this.onKeyUp
      })
    },
    genInput () {
      return this.$createElement('input', {
        attrs: {
          'aria-label': this.label,
          name: this.name,
          role: 'slider',
          tabindex: this.disabled ? -1 : undefined,
          type: 'range',
          value: this.internalValue,
          readonly: true,
          'aria-readonly': String(this.readonly)
        },
        on: this.genListeners(),
        ref: 'input'
      })
    },
    genSlider () {
      return this.$createElement('div', {
        staticClass: 'v-slider',
        'class': {
          'v-slider--is-active': this.isActive
        },
        directives: [{
          name: 'click-outside',
          value: this.onBlur
        }]
      }, this.genChildren())
    },
    genChildren () {
      return [
        this.genInput(),
        this.genTrackContainer(),
        this.genSteps(),
        this.genThumbContainer(
          this.internalValue,
          this.inputWidth,
          this.isFocused || this.isActive,
          this.onThumbMouseDown
        )
      ]
    },
    genSteps () {
      if (!this.step || !this.showTicks) return null

      const ticks = createRange(this.numTicks + 1).map(i => {
        const children = []

        if (this.tickLabels[i]) {
          children.push(this.$createElement('span', this.tickLabels[i]))
        }

        return this.$createElement('span', {
          key: i,
          staticClass: 'v-slider__ticks',
          class: {
            'v-slider__ticks--always-show': this.ticks === 'always' ||
              this.tickLabels.length > 0
          },
          style: {
            ...this.tickStyles,
            left: `${i * (100 / this.numTicks)}%`
          }
        }, children)
      })

      return this.$createElement('div', {
        staticClass: 'v-slider__ticks-container'
      }, ticks)
    },
    genThumb () {
      return this.$createElement('div', {
        staticClass: 'v-slider__thumb',
        'class': this.addBackgroundColorClassChecks({}, this.computedThumbColor)
      })
    },
    genThumbContainer (value, valueWidth, isActive, onDrag) {
      const children = [this.genThumb()]

      const thumbLabelContent = this.getLabel(value)
      this.showThumbLabel && children.push(this.genThumbLabel(thumbLabelContent))

      return this.$createElement('div', {
        staticClass: 'v-slider__thumb-container',
        'class': this.addTextColorClassChecks({
          'v-slider__thumb-container--is-active': isActive,
          'v-slider__thumb-container--show-label': this.showThumbLabel
        }, this.computedThumbColor),
        style: {
          transition: this.trackTransition,
          left: `${this.$vuetify.rtl ? 100 - valueWidth : valueWidth}%`
        },
        on: {
          touchstart: onDrag,
          mousedown: onDrag
        }
      }, children)
    },
    genThumbLabel (content) {
      const size = convertToUnit(this.thumbSize)

      return this.$createElement(VScaleTransition, {
        props: { origin: 'bottom center' }
      }, [
        this.$createElement('div', {
          staticClass: 'v-slider__thumb-label__container',
          directives: [
            {
              name: 'show',
              value: this.isFocused || this.isActive || this.thumbLabel === 'always'
            }
          ]
        }, [
          this.$createElement('div', {
            staticClass: 'v-slider__thumb-label',
            'class': this.addBackgroundColorClassChecks({}, this.computedThumbColor),
            style: {
              height: size,
              width: size
            }
          }, [content])
        ])
      ])
    },
    genTrackContainer () {
      const children = [
        this.$createElement('div', {
          staticClass: 'v-slider__track',
          'class': this.addBackgroundColorClassChecks({}, this.computedTrackColor),
          style: this.trackStyles
        }),
        this.$createElement('div', {
          staticClass: 'v-slider__track-fill',
          'class': this.addBackgroundColorClassChecks(),
          style: this.trackFillStyles
        })
      ]

      return this.$createElement('div', {
        staticClass: 'v-slider__track__container',
        ref: 'track'
      }, children)
    },
    getLabel (value) {
      return this.$scopedSlots['thumb-label']
        ? this.$scopedSlots['thumb-label']({ value })
        : this.$createElement('span', value)
    },
    onBlur (e) {
      if (this.keyPressed === 2) return

      this.isActive = false
      this.isFocused = false
      this.$emit('blur', e)
    },
    onFocus (e) {
      this.isFocused = true
      this.$emit('focus', e)
    },
    onThumbMouseDown (e) {
      this.oldValue = this.internalValue
      this.keyPressed = 2
      const options = { passive: true }
      this.isActive = true
      this.isFocused = false

      if ('touches' in e) {
        this.app.addEventListener('touchmove', this.onMouseMove, options)
        addOnceEventListener(this.app, 'touchend', this.onMouseUp)
      } else {
        this.app.addEventListener('mousemove', this.onMouseMove, options)
        addOnceEventListener(this.app, 'mouseup', this.onMouseUp)
      }

      this.$emit('start', this.internalValue)
    },
    onMouseUp () {
      this.keyPressed = 0
      const options = { passive: true }
      this.isActive = false
      this.isFocused = false
      this.app.removeEventListener('touchmove', this.onMouseMove, options)
      this.app.removeEventListener('mousemove', this.onMouseMove, options)

      this.$emit('end', this.internalValue)
      if (!deepEqual(this.oldValue, this.internalValue)) {
        this.$emit('change', this.internalValue)
      }
    },
    onMouseMove (e) {
      const { value, isInsideTrack } = this.parseMouseMove(e)

      if (isInsideTrack) {
        this.setInternalValue(value)
      }
    },
    onKeyDown (e) {
      if (this.disabled || this.readonly) return

      const value = this.parseKeyDown(e)

      if (value == null) return

      this.setInternalValue(value)
      this.$emit('change', value)
    },
    onKeyUp () {
      this.keyPressed = 0
    },
    onSliderClick (e) {
      this.isFocused = true
      this.onMouseMove(e)
      this.$emit('change', this.internalValue)
    },
    parseMouseMove (e) {
      const {
        left: offsetLeft,
        width: trackWidth
      } = this.$refs.track.getBoundingClientRect()
      const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX
      // It is possible for left to be NaN, force to number
      let left = Math.min(Math.max((clientX - offsetLeft) / trackWidth, 0), 1) || 0

      if (this.$vuetify.rtl) left = 1 - left

      const isInsideTrack = clientX >= offsetLeft - 8 && clientX <= offsetLeft + trackWidth + 8
      const value = parseFloat(this.min) + left * (this.max - this.min)

      return { value, isInsideTrack }
    },
    parseKeyDown (e, value = this.internalValue) {
      if (this.disabled) return

      const { pageup, pagedown, end, home, left, right, down, up } = keyCodes

      if (![pageup, pagedown, end, home, left, right, down, up].includes(e.keyCode)) return

      e.preventDefault()
      const step = this.stepNumeric || 1
      const steps = (this.max - this.min) / step
      if ([left, right, down, up].includes(e.keyCode)) {
        this.keyPressed += 1

        const increase = this.$vuetify.rtl ? [left, up] : [right, up]
        let direction = increase.includes(e.keyCode) ? 1 : -1
        const multiplier = e.shiftKey ? 3 : (e.ctrlKey ? 2 : 1)

        value = value + (direction * step * multiplier)
      } else if (e.keyCode === home) {
        value = parseFloat(this.min)
      } else if (e.keyCode === end) {
        value = parseFloat(this.max)
      } else /* if (e.keyCode === keyCodes.pageup || e.keyCode === pagedown) */ {
        // Page up/down
        const direction = e.keyCode === pagedown ? 1 : -1
        value = value - (direction * step * (steps > 100 ? steps / 10 : 10))
      }

      return value
    },
    roundValue (value) {
      if (!this.stepNumeric) return value
      // Format input value using the same number
      // of decimals places as in the step prop
      const trimmedStep = this.step.toString().trim()
      const decimals = trimmedStep.indexOf('.') > -1
        ? (trimmedStep.length - trimmedStep.indexOf('.') - 1)
        : 0

      const newValue = Math.round(value / this.stepNumeric) * this.stepNumeric

      return parseFloat(Math.min(newValue, this.max).toFixed(decimals))
    },
    setInternalValue (value) {
      this.internalValue = value
    }
  }
}


import Vue from 'vue'
import { test } from '@/test'
import VDatePicker from '@/components/VDatePicker'
import VMenu from '@/components/VMenu'

test('VDatePicker.js', ({ mount, compileToFunctions }) => {
  it('should emit input event on year click (reactive picker)', async () => {
    const wrapper = mount(VDatePicker, {
      propsData: {
        value: '2013-05',
        type: 'month',
        reactive: true
      },
      data: {
        activePicker: 'YEAR'
      }
    })

    const input = jest.fn()
    wrapper.vm.$on('input', input);

    const change = jest.fn()
    wrapper.vm.$on('change', input);

    wrapper.find('.v-date-picker-years li.active + li')[0].trigger('click')
    expect(input).toBeCalledWith('2012-05')
    expect(change).not.toBeCalled()
  })

  it('should not emit input event on year click if month is not allowed', async () => {
    const cb = jest.fn()
    const wrapper = mount(VDatePicker, {
      propsData: {
        value: '2013-05',
        type: 'month',
        allowedDates: () => false
      },
      data: {
        activePicker: 'YEAR'
      }
    })

    wrapper.vm.$on('input', cb);
    wrapper.find('.v-date-picker-years li.active + li')[0].trigger('click')
    expect(cb).not.toBeCalled()
  })

  it('should emit input event on month click', async () => {
    const cb = jest.fn()
    const wrapper = mount(VDatePicker, {
      propsData: {
        value: '2013-05',
        type: 'month'
      }
    })

    wrapper.vm.$on('input', cb);
    wrapper.find('.v-date-picker-table--month button')[0].trigger('click')
    expect(cb).toBeCalledWith('2013-01')
  })

  it('should be scrollable', async () => {
    const wrapper = mount(VDatePicker, {
      propsData: {
        value: '2013-05',
        type: 'month',
        scrollable: true
      }
    })

    wrapper.find('.v-date-picker-table--month')[0].trigger('wheel')
    await wrapper.vm.$nextTick()
    expect(wrapper.vm.tableDate).toBe('2014')
  })

  it('should match snapshot with pick-month prop', () => {
    const wrapper = mount(VDatePicker, {
      propsData: {
        value: '2013-05-07',
        type: 'month'
      }
    })

    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should match snapshot with allowed dates as array', () => {
    const wrapper = mount(VDatePicker, {
      propsData: {
        value: '2013-05',
        type: 'month',
        allowedDates: value => ['2013-01', '2013-03', '2013-05', '2013-07'].includes(value)
      }
    })

    expect(wrapper.find('.v-date-picker-table--month tbody')[0].html()).toMatchSnapshot()
  })

  it('should match snapshot with month formatting functions', () => {
    const wrapper = mount(VDatePicker, {
      propsData: {
        value: '2005-11-01',
        type: 'month',
        monthFormat: date => `(${date.split('-')[1]})`
      }
    })

    expect(wrapper.find('.v-date-picker-table--month tbody')[0].html()).toMatchSnapshot()
  })

  it('should match snapshot with colored picker', () => {
    const wrapper = mount(VDatePicker, {
      propsData: {
        type: 'month',
        value: '2005-11-01',
        color: 'primary',
        headerColor: 'orange darken-1'
      }
    })

    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should match snapshot with colored picker', () => {
    const wrapper = mount(VDatePicker, {
      propsData: {
        type: 'month',
        value: '2005-11-01',
        color: 'orange darken-1'
      }
    })

    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should match change month when clicked on header arrow buttons', () => {
    const wrapper = mount(VDatePicker, {
      propsData: {
        value: '2005-11',
        type: 'month'
      }
    })

    const [leftButton, rightButton] = wrapper.find('.v-date-picker-header button')

    leftButton.trigger('click')
    expect(wrapper.vm.tableDate).toBe('2004')

    rightButton.trigger('click')
    expect(wrapper.vm.tableDate).toBe('2006')
  })

  it('should match change active picker when clicked on month button', () => {
    const wrapper = mount(VDatePicker, {
      propsData: {
        value: '2005-11-01',
        type: 'month'
      }
    })

    const button = wrapper.find('.v-date-picker-header strong')[0]

    button.trigger('click')
    expect(wrapper.vm.activePicker).toBe('YEAR')
  })

  it('should select year', async () => {
    const wrapper = mount(VDatePicker, {
      data: {
        activePicker: 'YEAR'
      },
      propsData: {
        type: 'month',
        value: '2005-11'
      }
    })

    wrapper.find('.v-date-picker-years li.active + li')[0].trigger('click')
    expect(wrapper.vm.activePicker).toBe('MONTH')
    expect(wrapper.vm.tableDate).toBe('2004')
  })

  it('should set the table date when value has changed', () => {
    const wrapper = mount(VDatePicker, {
      propsData: {
        value: null,
        type: 'month'
      }
    })

    wrapper.setProps({ value: '2005-11' })
    expect(wrapper.vm.tableDate).toBe('2005')
  })

  it('should use prev and next icons', () => {
    const wrapper = mount(VDatePicker, {
      propsData: {
        type: 'month',
        prevIcon: 'block',
        nextIcon: 'check'
      }
    })

    const icons = wrapper.find('.v-date-picker-header .v-icon')
    expect(icons[0].element.textContent).toBe('block')
    expect(icons[1].element.textContent).toBe('check')
  })
})


// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`VDataIterator.js should match a snapshot - footer slot 1`] = `

      footer

        Items per page:

                        5

                      <input readonly="readonly"
                             tabindex="0"
                             type="text"
                             autocomplete="on"
                             aria-readonly="false"
                      >

                          arrow_drop_down

                              5

                              10

                              25

                              All

          1-3 of 3

        <button disabled="disabled"
                type="button"
                class="v-btn v-btn--disabled v-btn--flat v-btn--icon"
                aria-label="Previous page"
        >

              chevron_left

        </button>
        <button disabled="disabled"
                type="button"
                class="v-btn v-btn--disabled v-btn--flat v-btn--icon"
                aria-label="Next page"
        >

              chevron_right

        </button>

`;

exports[`VDataIterator.js should match a snapshot - hideActions and no footer slot 1`] = `

`;

exports[`VDataIterator.js should match a snapshot - no data 1`] = `

      No data available

        Items per page:

                        5

                      <input readonly="readonly"
                             tabindex="0"
                             type="text"
                             autocomplete="on"
                             aria-readonly="false"
                      >

                          arrow_drop_down

                              5

                              10

                              25

                              All

          –

        <button disabled="disabled"
                type="button"
                class="v-btn v-btn--disabled v-btn--flat v-btn--icon"
                aria-label="Previous page"
        >

              chevron_left

        </button>
        <button disabled="disabled"
                type="button"
                class="v-btn v-btn--disabled v-btn--flat v-btn--icon"
                aria-label="Next page"
        >

              chevron_right

        </button>

`;

exports[`VDataIterator.js should match a snapshot - no matching records 1`] = `

      No matching records found

        Items per page:

                        5

                      <input readonly="readonly"
                             tabindex="0"
                             type="text"
                             autocomplete="on"
                             aria-readonly="false"
                      >

                          arrow_drop_down

                              5

                              10

                              25

                              All

          –

        <button disabled="disabled"
                type="button"
                class="v-btn v-btn--disabled v-btn--flat v-btn--icon"
                aria-label="Previous page"
        >

              chevron_left

        </button>
        <button disabled="disabled"
                type="button"
                class="v-btn v-btn--disabled v-btn--flat v-btn--icon"
                aria-label="Next page"
        >

              chevron_right

        </button>

`;

exports[`VDataIterator.js should match a snapshot - with data 1`] = `

      b

      c

      a

        Items per page:

                        5

                      <input readonly="readonly"
                             tabindex="0"
                             type="text"
                             autocomplete="on"
                             aria-readonly="false"
                      >

                          arrow_drop_down

                              5

                              10

                              25

                              All

          1-3 of 3

        <button disabled="disabled"
                type="button"
                class="v-btn v-btn--disabled v-btn--flat v-btn--icon"
                aria-label="Previous page"
        >

              chevron_left

        </button>
        <button disabled="disabled"
                type="button"
                class="v-btn v-btn--disabled v-btn--flat v-btn--icon"
                aria-label="Next page"
        >

              chevron_right

        </button>

`;

exports[`VDataIterator.js should match a snapshot - with data 2`] = `

    1-3 of 3

  <button disabled="disabled"
          type="button"
          class="v-btn v-btn--disabled v-btn--flat v-btn--icon"
          aria-label="Previous page"
  >

        chevron_right

  </button>
  <button disabled="disabled"
          type="button"
          class="v-btn v-btn--disabled v-btn--flat v-btn--icon"
          aria-label="Next page"
  >

        chevron_left

  </button>

`;


import { inject as RegistrableInject } from './registrable'
import { consoleError } from '../util/console'

// Mixins
import Colorable from './colorable'

/* @vue/component */
export default {
  name: 'validatable',

  mixins: [
    Colorable,
    RegistrableInject('form')
  ],

  props: {
    error: Boolean,
    errorCount: {
      type: [Number, String],
      default: 1
    },
    errorMessages: {
      type: [String, Array],
      default: () => []
    },
    messages: {
      type: [String, Array],
      default: () => []
    },
    rules: {
      type: Array,
      default: () => []
    },
    success: Boolean,
    successMessages: {
      type: [String, Array],
      default: () => []
    },
    validateOnBlur: Boolean
  },

  data: () => ({
    errorBucket: [],
    hasColor: false,
    hasFocused: false,
    hasInput: false,
    isResetting: false,
    valid: false
  }),

  computed: {
    hasError () {
      return this.internalErrorMessages.length > 0 ||
        this.errorBucket.length > 0 ||
        this.error
    },
    externalError () {
      return this.internalErrorMessages.length > 0 || this.error
    },
    // TODO: Add logic that allows the user to enable based
    // upon a good validation
    hasSuccess () {
      return this.successMessages.length > 0 ||
        this.success
    },
    hasMessages () {
      return this.validations.length > 0
    },
    hasState () {
      return this.shouldValidate && (this.hasError || this.hasSuccess)
    },
    internalErrorMessages () {
      return this.errorMessages || ''
    },
    shouldValidate () {
      return this.externalError || (!this.isResetting && (
        this.validateOnBlur
          ? this.hasFocused && !this.isFocused
          : (this.hasInput || this.hasFocused)
      ))
    },
    validations () {
      return this.validationTarget.slice(0, this.errorCount)
    },
    validationState () {
      if (this.hasError && this.shouldValidate) return 'error'
      if (this.hasSuccess && this.shouldValidate) return 'success'
      if (this.hasColor) return this.color
      return null
    },
    validationTarget () {
      const target = this.internalErrorMessages.length > 0
        ? this.errorMessages
        : this.successMessages.length > 0
          ? this.successMessages
          : this.messages

      // String
      if (!Array.isArray(target)) {
        return [target]
      // Array with items
      } else if (target.length > 0) {
        return target
      // Currently has validation
      } else if (this.shouldValidate) {
        return this.errorBucket
      } else {
        return []
      }
    }
  },

  watch: {
    rules: {
      handler (newVal, oldVal) {
        // TODO: This handler seems to trigger when input changes, even though
        // rules array stays the same? Solved it like this for now
        if (newVal.length === oldVal.length) return

        this.validate()
      },
      deep: true
    },
    internalValue () {
      // If it's the first time we're setting input,
      // mark it with hasInput
      this.hasInput = true
      this.validateOnBlur || this.$nextTick(this.validate)
    },
    isFocused (val) {
      if (!val) {
        this.hasFocused = true
        this.validateOnBlur && this.validate()
      }
    },
    isResetting () {
      setTimeout(() => {
        this.hasInput = false
        this.hasFocused = false
        this.isResetting = false
      }, 0)
    },
    hasError (val) {
      if (this.shouldValidate) {
        this.$emit('update:error', val)
      }
    }
  },

  beforeMount () {
    this.validate()
  },

  created () {
    this.form && this.form.register(this)
  },

  beforeDestroy () {
    this.form && this.form.unregister(this)
  },

  methods: {
    reset () {
      this.isResetting = true
      this.internalValue = Array.isArray(this.internalValue)
        ? []
        : undefined
    },
    validate (force = false, value = this.internalValue) {
      const errorBucket = []

      if (force) this.hasInput = this.hasFocused = true

      for (let index = 0; index < this.rules.length; index++) {
        const rule = this.rules[index]
        const valid = typeof rule === 'function' ? rule(value) : rule

        if (valid === false || typeof valid === 'string') {
          errorBucket.push(valid)
        } else if (valid !== true) {
          consoleError(`Rules should return a string or boolean, received '${typeof valid}' instead`, this)
        }
      }

      this.errorBucket = errorBucket
      this.valid = errorBucket.length === 0

      return this.valid
    }
  }
}


'aria-label': (!this.$attrs || !this.$attrs.id) && this.label // Label `for` will be set if we have an id


@import '../bootstrap'
@import '../theme'

v-jumbotron($material)
  &__content
    color: $material.text.primary

theme(v-jumbotron, 'v-jumbotron')

.v-jumbotron
  display: block
  top: 0
  transition: .3s $transition.swing
  width: 100%

  &__wrapper
    height: 100%
    overflow: hidden
    position: relative
    transition: inherit
    width: 100%

  &__background
    position: absolute
    top: 0
    left: 0
    right: 0
    bottom: 0
    contain: strict
    transition: inherit

  &__image
    position: absolute
    top: 50%
    left: 50%
    transform: translate(-50%, -50%)
    min-width: 100%
    will-change: transform
    transition: inherit

  &__content
    height: 100%
    position: relative
    transition: inherit


import Vue from 'vue'
import { mount, shallow } from 'avoriaz'
import toHaveBeenWarnedInit from '@/test/util/to-have-been-warned'
import Vuetify from '@/components/Vuetify'
import { compileToFunctions } from 'vue-template-compiler'

export function test(name, cb) {
  toHaveBeenWarnedInit()

  Vuetify.install(Vue)

/*
  const app = document.createElement('div')
  app.setAttribute('data-app', true)
  document.body.appendChild(app)
*/

  rafPolyfill(window)

  // Very naive polyfill for performance.now()
  window.performance = { now: () => (new Date()).getTime() }

  describe(name, () => cb({
    functionalContext,
    mount (component, options) {
      if (component.options) {
        component = component.options
      }
      return mount(component, options)
    },
    shallow,
    compileToFunctions
  }))
}

test.skip = describe.skip

export function functionalContext(context = {}, children = []) {
  if (!Array.isArray(children)) children = [children]
  return {
    context: Object.assign({
      data: {},
      props: {}
    }, context),
    children
  }
}

//requestAnimationFrame polyfill | Milos Djakonovic ( @Miloshio ) | MIT | https://github.com/milosdjakonovic/requestAnimationFrame-polyfill
export function rafPolyfill(w) {
  /**
   *
   * How many times should polyfill call
   * update callback? By canon, it should
   * be 60 times per second, so that ideal
   * framerate 60fps could be reached.
   *
   * However, even native implementations
   * of requestAnimationFrame often cannot
   * do 60fps, but, unlike any polyfill,
   * they can synchronise achievable fps
   * rate with screen refresh rate.
   *
   * So, leave this value 1000/60 unless
   * you target specific browser on spec
   * ific device that is going to work
   * better with custom value. I think
   * that this is the longest comment I've
   * written on single variable so far.
  **/
  var FRAME_RATE_INTERVAL = 1000/60,

  /**
   * All queued callbacks in given cycle.
  **/
  allCallbacks = [],

  executeAllScheduled = false,

  shouldCheckCancelRaf = false,

  /**
   * Callbacks queued for cancellation.
  **/
  callbacksForCancellation = [],

  /**
   * Should callback be cancelled?
   * @param cb - callback
  **/
  isToBeCancelled = function(cb){
    for(var i=0;i<callbacksForCancellation.length;i++){
      if(callbacksForCancellation[i] === cb ){
        callbacksForCancellation.splice(i,1);
        return true;
      }
    }
  },

  /**
   *
   * Executes all (surprise) callbacks in
   * and removes them from callback queue.
   *
  **/
  executeAll = function(){
    executeAllScheduled = false;
    var _allCallbacks = allCallbacks;
    allCallbacks = [];
    for(var i=0;i<_allCallbacks.length;i++){
      if(shouldCheckCancelRaf===true){
        if (isToBeCancelled(_allCallbacks[i])){
          shouldCheckCancelRaf = false;
          return;
        }
      }
      _allCallbacks[i].apply(w, [ new Date().getTime() ] );
    }
  },

  /**
   *
   * requestAnimationFrame polyfill
   * @param callback - callback to be queued & executed | executed
   * @return callback
   *
  **/
  raf = function(callback){
    allCallbacks.push(callback);
    if(executeAllScheduled===false){
      w.setTimeout(executeAll, FRAME_RATE_INTERVAL);
      executeAllScheduled = true;
    }
    return callback;
  },

  /**
   *
   * Cancels raf.
  **/
  cancelRaf = function(callback){
    callbacksForCancellation.push(callback);
    shouldCheckCancelRaf = true;
  },

  //https://gist.github.com/paulirish/1579671
  vendors = ['ms', 'moz', 'webkit', 'o'];

    for(var x = 0; x < vendors.length && !w.requestAnimationFrame; ++x) {
        w.requestAnimationFrame = w[vendors[x]+'RequestAnimationFrame'];
        w.cancelAnimationFrame = w[vendors[x]+'CancelAnimationFrame']
        || w[vendors[x]+'CancelRequestAnimationFrame'];
    }

  if (!w.requestAnimationFrame) w.requestAnimationFrame = raf;
  if (!w.cancelAnimationFrame)  w.cancelAnimationFrame  = cancelRaf;
}

export function touch(element) {
  const createTrigger = eventName => (clientX, clientY) => {
    const touches = [{ clientX, clientY }]
    element.trigger(eventName, ({ touches, changedTouches: touches }))
    return touch(element)
  }

  return {
    start: createTrigger('touchstart'),
    move: createTrigger('touchmove'),
    end: createTrigger('touchend')
  }
}

export const resizeWindow = (width = global.innerWidth, height = global.innerHeight) => {
  global.innerWidth = width
  global.innerHeight = height
  global.dispatchEvent(new Event('resize'))
  return new Promise(resolve => setTimeout(resolve, 200))
}


import '../../stylus/components/_pickers.styl'
import '../../stylus/components/_cards.styl'

// Mixins
import Colorable from '../../mixins/colorable'
import Themeable from '../../mixins/themeable'

/* @vue/component */
export default {
  name: 'v-picker',

  mixins: [Colorable, Themeable],

  props: {
    fullWidth: Boolean,
    landscape: Boolean,
    transition: {
      type: String,
      default: 'fade-transition'
    },
    width: {
      type: [Number, String],
      default: 290,
      validator: value => parseInt(value, 10) > 0
    }
  },

  data () {
    return {
      defaultColor: 'primary'
    }
  },

  computed: {
    computedTitleColor () {
      const darkTheme = this.dark || (!this.light && this.$vuetify.dark)
      const defaultTitleColor = darkTheme ? null : this.computedColor
      return this.color || defaultTitleColor
    }
  },

  methods: {
    genTitle () {
      return this.$createElement('div', {
        staticClass: 'v-picker__title',
        'class': this.addBackgroundColorClassChecks({
          'v-picker__title--landscape': this.landscape
        }, this.computedTitleColor)
      }, this.$slots.title)
    },
    genBodyTransition () {
      return this.$createElement('transition', {
        props: {
          name: this.transition
        }
      }, this.$slots.default)
    },
    genBody () {
      return this.$createElement('div', {
        staticClass: 'v-picker__body',
        'class': this.themeClasses,
        style: this.fullWidth ? undefined : {
          width: this.width + 'px'
        }
      }, [
        this.genBodyTransition()
      ])
    },
    genActions () {
      return this.$createElement('div', {
        staticClass: 'v-picker__actions v-card__actions'
      }, this.$slots.actions)
    }
  },

  render (h) {
    return h('div', {
      staticClass: 'v-picker v-card',
      'class': {
        'v-picker--landscape': this.landscape,
        ...this.themeClasses
      }
    }, [
      this.$slots.title ? this.genTitle() : null,
      this.genBody(),
      this.$slots.actions ? this.genActions() : null
    ])
  }
}


keep keep keep keep replace replace replace replace keep keep keep

@mixin list-shaped($size)
  .v-list-item,
  .v-list-item:before
    border-bottom-right-radius: #{$size * 0.6666666666666667}
    border-top-right-radius: #{$size * 0.6666666666666667}


module.exports = {
  'v-time-picker': {
    events: [
      {
        name: 'input',
        value: 'string'
      },
      {
        name: 'change',
        value: 'string'
      },
      {
        name: 'click:hour',
        value: 'string'
      },
      {
        name: 'click:minute',
        value: 'string'
      },
      {
        name: 'click:second',
        value: 'string'
      }
    ]
  }
}


// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`VStepperStep.ts should accept a custom color 1`] = `

`;

exports[`VStepperStep.ts should accept a custom css color 1`] = `

`;


module.exports = {
  'v-stepper-header': {
    slots: ['default']
  }
}


keep keep keep replace replace replace replace keep keep keep

@import '../../styles/styles.sass'
@import './_variables.scss'
@import './_mixins.sass'

// Theme
.v-list
  &.primary,
  &.secondary,
  &.accent,
  &.success,
  &.error,
  &.warning,
  &.info
    > .v-list-item
      color: map-deep-get($material-dark, 'text', 'primary')

+theme(v-list) using ($material)
  background: map-get($material, 'cards')
  color: map-deep-get($material, 'text','primary')

  .v-list--disabled
    color: map-deep-get($material, 'text', 'disabled')

  .v-list-group--active:before,
  .v-list-group--active:after
    background: map-get($material, 'dividers')

// Block
.v-list
  padding: 8px 0

// Modifier
.v-list--disabled
  pointer-events: none

.v-list--flat
  .v-list-item:before
    display: none

.v-list--dense
  .v-subheader
    font-size: $list-dense-subheader-font-size
    height: $list-dense-subheader-height
    padding: 0 8px

.v-list--nav
  padding-left: 8px
  padding-right: 8px

  .v-list-item:not(:last-child):not(:only-child)
    margin-bottom: 8px

  .v-list-item,
  .v-list-item:before
    border-radius: 4px

  &.v-list--dense .v-list-item,
  .v-list-item--dense
    &:not(:last-child):not(:only-child)
      margin-bottom: 4px

.v-list--shaped
  padding-right: 8px
  +list-shaped($list-item-min-height)

  &.v-list--two-line
    +list-shaped($list-item-two-line-min-height)

  &.v-list--three-line
    +list-shaped($list-item-three-line-min-height)

  +rtl()
    padding-left: 8px
    padding-right: 0

.v-list--subheader
  padding-top: 0


@import '../../styles/styles.sass'
@import './_variables.scss'
@import './_mixins.sass'

// Element
.v-list-group
  .v-list-item__icon.v-list-group__header__append-icon
    min-width: 48px
    justify-content: flex-end
    margin-left: 0

.v-list-group--sub-group
  align-items: center
  display: flex
  flex-wrap: wrap

.v-list-group__header
  align-items: center
  cursor: pointer
  display: flex
  flex: 1 1 auto
  outline: none
  padding: 0 16px
  position: relative
  user-select: none

  > .v-list-item
    padding: 0

.v-list-group__items
  flex: 1 1 auto

.v-list-group--active > .v-list-group__header
  > .v-list-group__header__append-icon .v-icon
    transform: rotate(-180deg)

  &.v-list-group__header--sub-group
    > .v-list-group__header__prepend-icon .v-icon
      transform: rotate(-180deg)

  .v-list-item,
  .v-list-item__content,
  .v-list-group__header__prepend-icon .v-icon
    color: inherit

.v-list-group--sub-group
  .v-list-item__action,
  .v-list-item__avatar,
  .v-list-item__icon
    &:first-child
      margin-right: 16px

  .v-list-group__header
    padding-left: 32px

  .v-list-group__items
    .v-list-item
      padding-left: 40px

  &.v-list-group--active
    .v-list-item__icon.v-list-group__header__prepend-icon .v-icon
      transform: rotate(-180deg)

.v-list-group--no-action
  > .v-list-group__items > div > .v-list-item
    padding-left: 72px

  &.v-list-group--sub-group
    > .v-list-group__items > div > .v-list-item
      padding-left: 88px

.v-list--dense
  .v-list-group--sub-group .v-list-group__header
    padding-left: 24px

  .v-list-group--no-action
    > .v-list-group__items > div > .v-list-item
      padding-left: 64px

    &.v-list-group--sub-group
      > .v-list-group__items > div > .v-list-item
        padding-left: 80px


<template>
  <div>
    <doc-heading>Generic.Pages.usage</doc-heading>
    <doc-text>{{ `${namespace}.${page}.examples.${internalValue.file}.desc` }}</doc-text>
    <doc-example
      :id="`usage-${-1}`"
      :value="internalValue"
    />
  </div>
</template>

<script>
  // Utilities
  import kebabCase from 'lodash/kebabCase'
  import {
    mapGetters
  } from 'vuex'

  export default {
    props: {
      value: {
        type: [Object, String],
        default: undefined
      }
    },

    computed: {
      ...mapGetters('documentation', [
        'namespace',
        'page'
      ]),
      internalValue () {
        return this.value === Object(this.value)
          ? this.value
          : { file: this.value }
      }
    },

    methods: { kebabCase }
  }
</script>


Указывает, какой элемент DOM должен отделить этот компонент. Используйте либо селектор CSS, либо ссылку на объект для элемента.

Применяет пользовательский класс к отдельному элементу. Это полезно, потому что содержимое перемещается в конец приложения и не является таргетингом по классам, переданным непосредственно в компонент.


.v-list--nav
padding-left: 8px
padding-right: 8px
.v-list-item,
.v-list-item:before
border-radius: 4px

// Styles
import './VList.sass'
import VListGroup from './VListGroup'

// Components
import VSheet from '../VSheet/VSheet'

// Types
import { VNode } from 'vue'

type VListGroupInstance = InstanceType<typeof VListGroup>

/* @vue/component */
export default VSheet.extend({
  name: 'v-list',

  provide (): object {
    return {
      list: this
    }
  },

  props: {
    dense: Boolean,
    disabled: Boolean,
    expand: Boolean,
    flat: Boolean,
    nav: Boolean,
    shaped: Boolean,
    subheader: Boolean,
    threeLine: Boolean,
    tile: {
      type: Boolean,
      default: true
    },
    twoLine: Boolean
  },

  data: () => ({
    groups: [] as VListGroupInstance[]
  }),

  computed: {
    classes (): object {
      return {
        ...VSheet.options.computed.classes.call(this),
        'v-list--dense': this.dense,
        'v-list--disabled': this.disabled,
        'v-list--flat': this.flat,
        'v-list--nav': this.nav,
        'v-list--shaped': this.shaped,
        'v-list--subheader': this.subheader,
        'v-list--two-line': this.twoLine,
        'v-list--three-line': this.threeLine
      }
    }
  },

  methods: {
    register (content: VListGroupInstance) {
      this.groups.push(content)
    },
    unregister (content: VListGroupInstance) {
      const index = this.groups.findIndex(g => g._uid === content._uid)

      if (index > -1) this.groups.splice(index, 1)
    },
    listClick (uid: number) {
      if (this.expand) return

      for (const group of this.groups) {
        group.toggle(uid)
      }
    }
  },

  render (h): VNode {
    const data = {
      staticClass: 'v-list',
      class: this.classes,
      attrs: {
        role: 'list'
      }
    }

    return h('div', this.setBackgroundColor(this.color, data), [this.$slots.default])
  }
})


Vue.js Material Component Framework

Vuetify.js is a reusable semantic component framework for Vue.js. It aims to provide clean, semantic and reusable components.

vue, vue 2, vue js, vue.js, material design, vue components, material design components, vuetify, vuetify.js

404

Quick Start

Get started with Vue and Vuetify in no time.

quick start, vuetify templates, get started

Starter Templates

Understand the layout and structure of your application. Check out examples.

Why Vuetify?

Why you should choose Vuetify over other Vue frameworks.

why vuetify, why vue, why choose vuetify

Frequently asked questions

Stuck on a problem? Check these frequently asked questions.

Sponsors and backers

Help support Vuetify.js by backing the development of new features

Contributing

Help develop Vuetify with new features and functionality.

contribute, contributing, feature request

Roadmap

The upcoming planned features and new functionality coming to Vuetify. New vue components, new directives, and much much more!.

Transitions

Make an amazing application with smooth built in animations.

Scrolling

Scroll to a target location on the page.

Material color palette

Easily apply any color from the material design palette.

Application theming

Setup your custom application theme by changing a few variables.

Application typography

View the various typography styles. From headings to captions, with various weights and sizes.

Content styles

Vuetify has custom styling for default html markup.

Application layouts

Select from a multitude of official material design layouts.

pre-defined layouts, layouts, application layout, material design layouts

Free / Premium Themes

Vuetify offers numerous pre-build starter and premium themes. Kickstart your next application today, no design skills needed.

vuetify themes, pre-built material themes, premium themes

Spacing helpers

Spacing helper classes allow you to apply margin or padding to any element.

Alignment helpers

Alignment helpers assist in modifying your application layout on varying viewport sizes.

Display helpers

Display helper classes allow you to control when elements should display based upon viewport.

Elevation helpers

Elevation helper classes allow you to control relative depth, or distance, between two surfaces along the z-axis.

Sandbox

Play around with the available configuring options for your next application.

Alert Component

The v-alert component is used to convey information to the user. Designed to stand out, the alerts come in four contextual styles, success, info, warning, and error.

Avatar Component

The v-avatar component is used to control the size and border radius of an image.

Badges

Badge component for the Vuetify framework.

Breadcrumbs Component

The v-breadcrumbs component is a navigational helper for pages. It can accept a Material Icons icon or characters as a divider.

Bottom Navigation Component

The v-bottom-navigation component is primarily for mobile use and acts as the primary navigation.

bottom nav, material nav, nav, bottom navigation, navigation

Bottom sheets

The v-bottom-sheets component is used for elevating content above other elements in a dialog style fashion

bottom sheets, bottom dialog, bottom modal

Cards

Card component for Vuetify framework.

Button Component

Button component for Vuetify Framework.

Floating Action Button Component

Floating Action Button component for Vuetify Framework.

vuetify, buttons, fab, floating button, floating action button

Carousels Component

Carousel component for Vuetify Framework.

Chip Component

Chip component for Vuetify Framework.

Dividers Component

Divider component for Vuetify Framework.

Data iterator Component

The v-data-iterator component is used for displaying data, and shares a majority of its functionality with the v-data-table component. Features include sorting, searching, pagination, and selection.

Data tables Component

The v-data-table component is used for displaying tabular data. Features include sorting, searching, pagination, inline-editing, header tooltips, and row selection.

Dialogs Component

A dialog informs a user about a specific task and may contain critical information.

Expansion panel Component

Expansion panel component for Vuetify framework.

Footer Component

Footer component for Vuetify Framework

Form Component

Form component for Vuetify Framework

Layout grid system

Vuetify supports the material design 12 point grid for laying out your application.

Grid List Component

Grid list component for Vuetify Framework

Icon Component

Icon component for Vuetify Framework

List Component

List component for Vuetify Framework

Menu Component

Menu component for Vuetify Framework

Navigation drawer

Navigation drawer component for Vuetify Framework

Pagination Component

The v-pagination component is used to separate long sets of data so that it is easier for a user to consume information.

Parallax Component

The v-parallax component creates a 3d effect that makes an image appear to scroll slower than the window.

Date Picker Component

Date Picker component for the Vuetify framework.

Time Picker Component

Time Picker component for the Vuetify framework.

Progress

Progress components for the Vuetify framework.

Slider Component

The v-slider component is a better visualization of the number input. It is used for gathering numerical user data.

Snackbar & toasts Component

Snackbar component for Vuetify Framework

Stepper Component

Stepper component for Vuetify Framework

Selection controls Input Components

Selection controls input components for Vuetify Framework

Selects Input Components

Selects input components for Vuetify Framework

Subheaders

Subheaders are used for separating lists of content.

Tabs

Tabs component for the Vuetify framework.

Text fields

Text field component for the Vuetify framework.

Toolbars

Toolbars component for the Vuetify framework.

Tooltip

Tooltip components for the Vuetify framework.

Resizing

Resize directive for the Vuetify framework.

resize, resize directive, window resize directive

Ripple

Ripple directive for the Vuetify framework.

Scrolling

Scroll directive for the Vuetify framework.

scroll, scroll directive, window scroll directive

Touch Support

Touch directive for the Vuetify framework.

touch, touch directive, mobile touch directive

Baseline layout example

Baseline layout for the Vuetify.js framework

Baseline Flipped layout example

Baseline Flipped layout for the Vuetify.js framework

Centered layout example

Centered layout for the Vuetify.js framework

Complex layout example

Complex layout for the Vuetify.js framework

Dark layout example

Dark layout for the Vuetify.js framework

Google Contacts layout example

Google Contacts layout for the Vuetify.js framework

Google Keep layout example

Google Keep layout for the Vuetify.js framework

Google Youtube layout example

Google Youtube layout for the Vuetify.js framework

Layout Sandbox

Layout Sandbox for the Vuetify.js framework

template, pre-made, vuetify layout, sandbox

A La Carte

Use only the components you need. Single component importing allows you to select what you want and leave the rest.

a la carte, a-la-carte, vuetify single import, vuetify import, component importing

Server side rendering

Server side rendering guide for the Vuetify framework.

Material design theme generator

Create a custom Material Design theme for your next application.

material theme, theme generator, material theme generator, vuetify theme generator


// Styles
import './VAutocomplete.sass'

// Extensions
import VSelect, { defaultMenuProps as VSelectMenuProps } from '../VSelect/VSelect'
import VTextField from '../VTextField/VTextField'

// Components
import VMenu from '../VMenu/VMenu'

// Utilities
import { keyCodes } from '../../util/helpers'
import mixins, { ExtractVue } from '../../util/mixins'

const defaultMenuProps = {
  ...VSelectMenuProps,
  offsetY: true,
  offsetOverflow: true,
  transition: false
}

// Types
const baseMixins = mixins(VSelect)

interface options extends ExtractVue<typeof baseMixins> {
  $refs: {
    menu: InstanceType<typeof VMenu>
    input: HTMLInputElement
    prefix: HTMLElement
    suffix: HTMLElement
  }
}

/* @vue/component */
export default baseMixins.extend<options>().extend({
  name: 'v-autocomplete',

  props: {
    allowOverflow: {
      type: Boolean,
      default: true
    },
    browserAutocomplete: {
      type: String,
      default: 'off'
    },
    filter: {
      type: Function,
      default: (item: any, queryText: string, itemText: string) => {
        return itemText.toLocaleLowerCase().indexOf(queryText.toLocaleLowerCase()) > -1
      }
    },
    hideNoData: Boolean,
    noFilter: Boolean,
    searchInput: {
      default: undefined
    },
    menuProps: {
      type: VSelect.options.props.menuProps.type,
      default: () => defaultMenuProps
    },
    autoSelectFirst: {
      type: Boolean,
      default: false
    }
  },

  data () {
    return {
      attrsInput: null,
      lazySearch: this.searchInput as null | undefined
    }
  },

  computed: {
    classes (): object {
      return Object.assign({}, VSelect.options.computed.classes.call(this), {
        'v-autocomplete': true,
        'v-autocomplete--is-selecting-index': this.selectedIndex > -1
      })
    },
    computedItems () {
      return this.filteredItems
    },
    selectedValues () {
      return this.selectedItems.map(item => this.getValue(item))
    },
    hasDisplayedItems () {
      return this.hideSelected
        ? this.filteredItems.some(item => !this.hasItem(item))
        : this.filteredItems.length > 0
    },
    currentRange () {
      if (this.selectedItem == null) return 0

      return this.getText(this.selectedItem).toString().length
    },
    filteredItems () {
      if (!this.isSearching || this.noFilter || this.internalSearch == null) return this.allItems

      return this.allItems.filter(item => this.filter(item, this.internalSearch.toString(), this.getText(item).toString()))
    },
    internalSearch: {
      get () {
        return this.lazySearch
      },
      set (val: any) {
        this.lazySearch = val

        this.$emit('update:searchInput', val)
      }
    },
    isAnyValueAllowed () {
      return false
    },
    isDirty () {
      return this.searchIsDirty || this.selectedItems.length > 0
    },
    isSearching () {
      if (this.multiple) return this.searchIsDirty

      return (
        this.searchIsDirty &&
        this.internalSearch !== this.getText(this.selectedItem)
      )
    },
    menuCanShow () {
      if (!this.isFocused) return false

      return this.hasDisplayedItems || !this.hideNoData
    },
    $_menuProps () {
      const props = VSelect.options.computed.$_menuProps.call(this)
      props.contentClass = `v-autocomplete__content ${props.contentClass || ''}`.trim()
      return {
        ...defaultMenuProps,
        ...props
      }
    },
    searchIsDirty () {
      return this.internalSearch != null &&
        this.internalSearch !== ''
    },
    selectedItem () {
      if (this.multiple) return null

      return this.selectedItems.find(i => {
        return this.valueComparator(this.getValue(i), this.getValue(this.internalValue))
      })
    },
    listData () {
      const data = VSelect.options.computed.listData.call(this) as any

      Object.assign(data.props, {
        items: this.virtualizedItems,
        noFilter: (
          this.noFilter ||
          !this.isSearching ||
          !this.filteredItems.length
        ),
        searchInput: this.internalSearch
      })

      return data
    }
  },

  watch: {
    filteredItems: 'onFilteredItemsChanged',
    internalValue: 'setSearch',
    isFocused (val) {
      if (val) {
        this.$refs.input &&
          this.$refs.input.select()
      } else {
        this.updateSelf()
      }
    },
    isMenuActive (val) {
      if (val || !this.hasSlot) return

      this.lazySearch = null
    },
    items (val, oldVal) {
      // If we are focused, the menu
      // is not active, hide no data is enabled,
      // and items change
      // User is probably async loading
      // items, try to activate the menu
      if (
        !(oldVal && oldVal.length) &&
        this.hideNoData &&
        this.isFocused &&
        !this.isMenuActive &&
        val.length
      ) this.activateMenu()
    },
    searchInput (val) {
      this.lazySearch = val
    },
    internalSearch: 'onInternalSearchChanged',
    itemText: 'updateSelf'
  },

  created () {
    this.setSearch()
  },

  methods: {
    onFilteredItemsChanged (val: never[]) {
      this.setMenuIndex(-1)

      this.$nextTick(() => {
        this.setMenuIndex(val.length > 0 && (val.length === 1 || this.autoSelectFirst) ? 0 : -1)
      })
    },
    onInternalSearchChanged () {
      this.updateMenuDimensions()
    },
    updateMenuDimensions () {
      if (this.isMenuActive &&
        this.$refs.menu
      ) {
        // Type from menuable is not making it through
        (this.$refs.menu as any).updateDimensions()
      }
    },
    changeSelectedIndex (keyCode: number) {
      // Do not allow changing of selectedIndex
      // when search is dirty
      if (this.searchIsDirty) return

      if (![
        keyCodes.backspace,
        keyCodes.left,
        keyCodes.right,
        keyCodes.delete
      ].includes(keyCode)) return

      const indexes = this.selectedItems.length - 1

      if (keyCode === keyCodes.left) {
        this.selectedIndex = this.selectedIndex === -1
          ? indexes
          : this.selectedIndex - 1
      } else if (keyCode === keyCodes.right) {
        this.selectedIndex = this.selectedIndex >= indexes
          ? -1
          : this.selectedIndex + 1
      } else if (this.selectedIndex === -1) {
        this.selectedIndex = indexes
        return
      }

      const currentItem = this.selectedItems[this.selectedIndex]

      if ([
        keyCodes.backspace,
        keyCodes.delete
      ].includes(keyCode) &&
        !this.getDisabled(currentItem)
      ) {
        const newIndex = this.selectedIndex === indexes
          ? this.selectedIndex - 1
          : this.selectedItems[this.selectedIndex + 1]
            ? this.selectedIndex
            : -1

        if (newIndex === -1) {
          this.setValue(this.multiple ? [] : undefined)
        } else {
          this.selectItem(currentItem)
        }

        this.selectedIndex = newIndex
      }
    },
    clearableCallback () {
      this.internalSearch = undefined

      VSelect.options.methods.clearableCallback.call(this)
    },
    genInput () {
      const input = VTextField.options.methods.genInput.call(this)

      input.data = input.data || {}
      input.data.attrs = input.data.attrs || {}
      input.data.domProps = input.data.domProps || {}

      input.data.attrs.role = 'combobox'
      input.data.domProps.value = this.internalSearch

      return input
    },
    genSelections () {
      return this.hasSlot || this.multiple
        ? VSelect.options.methods.genSelections.call(this)
        : []
    },
    onClick () {
      if (this.isDisabled) return

      this.selectedIndex > -1
        ? (this.selectedIndex = -1)
        : this.onFocus()

      this.activateMenu()
    },
    onEnterDown () {
      // Avoid invoking this method
      // will cause updateSelf to
      // be called emptying search
    },
    onInput (e: Event) {
      if (
        this.selectedIndex > -1 ||
        !e.target
      ) return

      const target = e.target as HTMLInputElement
      const value = target.value

      // If typing and menu is not currently active
      if (target.value) {
        this.activateMenu()
        if (!this.isAnyValueAllowed) this.setMenuIndex(0)
      }

      this.mask && this.resetSelections(target)
      this.internalSearch = value
      this.badInput = target.validity && target.validity.badInput
    },
    onKeyDown (e: KeyboardEvent) {
      const keyCode = e.keyCode

      VSelect.options.methods.onKeyDown.call(this, e)

      // The ordering is important here
      // allows new value to be updated
      // and then moves the index to the
      // proper location
      this.changeSelectedIndex(keyCode)
    },
    onTabDown (e: KeyboardEvent) {
      VSelect.options.methods.onTabDown.call(this, e)
      this.updateSelf()
    },
    setSelectedItems () {
      VSelect.options.methods.setSelectedItems.call(this)

      // #4273 Don't replace if searching
      // #4403 Don't replace if focused
      if (!this.isFocused) this.setSearch()
    },
    setSearch () {
      // Wait for nextTick so selectedItem
      // has had time to update
      this.$nextTick(() => {
        this.internalSearch = (
          this.multiple &&
          this.internalSearch &&
          this.isMenuActive
        )
          ? this.internalSearch
          : (
            !this.selectedItems.length ||
            this.multiple ||
            this.hasSlot
          )
            ? null
            : this.getText(this.selectedItem)
      })
    },
    updateSelf () {
      this.updateAutocomplete()
    },
    updateAutocomplete () {
      if (!this.searchIsDirty &&
        !this.internalValue
      ) return

      if (!this.valueComparator(
        this.internalSearch,
        this.getValue(this.internalValue)
      )) {
        this.setSearch()
      }
    },
    hasItem (item: any) {
      return this.selectedValues.indexOf(this.getValue(item)) > -1
    }
  }
})


`v-alert`组件被用来向用户传达重要信息，它有4个变种，**成功（success）**，**信息（info）**，**警告（warning）**和**错误（error）**。它们被分配了可以修改的表示不同操作的默认图标

使用`v-model`可以控制提示框的状态。如果您不想分配一个v-model，只显示警报，也可以简单地分配属性值。

您可以轻松分配一个自定义图标或把他们全部删除。

您可以把自定义过渡应用于提示框的显示/隐藏

轮廓提示框从当前应用的颜色继承他们的边界。

指定**成功（success）**，**信息（info）**，**警告（warning*）*或者**错误（error）**提示框，使用上下文颜色和预定义的图标。


padding-left: 8px
padding-right: 0
.v-list--subheader
padding-top: 0


import VRangeSlider from '../VRangeSlider'
import {
  mount,
  MountOptions,
  Wrapper
} from '@vue/test-utils'

describe('VRangeSlider.ts', () => {
  type Instance = InstanceType<typeof VRangeSlider>
  let mountFunction: (options?: MountOptions<Instance>) => Wrapper<Instance>

  let el
  beforeEach(() => {
    el = document.createElement('div')
    el.setAttribute('data-app', 'true')
    document.body.appendChild(el)

    mountFunction = (options?: MountOptions<Instance>) => {
      return mount(VRangeSlider, {
        ...options,
        mocks: {
          $vuetify: {
            rtl: false,
            theme: {
              dark: false
            }
          }
        }
      })
    }
  })
  afterEach(() => {
    document.body.removeChild(el)
  })

  it('should provide a default value if non provided', async () => {
    const wrapper = mountFunction()

    expect(wrapper.vm.lazyValue).toEqual([0, 0])
  })

  it('should round values and swap order if needed', () => {
    const wrapper = mountFunction({
      propsData: {
        value: [0, 0]
      }
    })

    expect(wrapper.vm.lazyValue).toEqual([0, 0])

    const input = jest.fn()
    wrapper.vm.$on('input', input)

    wrapper.vm.internalValue = [1.01, 2.99]

    expect(input).toHaveBeenCalledWith([1, 3])

    wrapper.vm.internalValue = [4.5, 2.99]

    expect(input).toHaveBeenCalledWith([3, 5])

    wrapper.setData({ activeThumb: 1 })

    expect(wrapper.vm.activeThumb).toBe(1)

    wrapper.vm.internalValue = [5, 1.1]

    expect(input).toHaveBeenCalledWith([1, 5])
    expect(wrapper.vm.activeThumb).toBe(0)

    wrapper.setProps({ value: [1, 5] })
    wrapper.vm.internalValue = [1, 5]

    expect(input).not.toHaveBeenCalledWith()
  })

  it('should change value on key down', () => {
    const setInternalValue = jest.fn()
    const wrapper = mountFunction({
      methods: { setInternalValue }
    })
    const input = wrapper.find('.v-slider__thumb-container')

    expect(wrapper.vm.activeThumb).toBeNull()
    input.trigger('focus')
    expect(wrapper.vm.activeThumb).toBe(0)
    input.trigger('keydown.up')

    expect(setInternalValue).toHaveBeenCalledTimes(1)

    wrapper.setData({ activeThumb: null })
    expect(wrapper.vm.activeThumb).toBeNull()

    input.trigger('keydown.esc')

    expect(setInternalValue).toHaveBeenCalledTimes(1)
  })

  it('should return index of closest value', () => {
    const wrapper = mountFunction()

    expect(wrapper.vm.getIndexOfClosestValue([0, 25], 23)).toBe(1)
    expect(wrapper.vm.getIndexOfClosestValue([0, 25], 5)).toBe(0)
    expect(wrapper.vm.getIndexOfClosestValue([25, 28], 32)).toBe(1)
    expect(wrapper.vm.getIndexOfClosestValue([25, 28], 23)).toBe(0)
  })

  it('should call on drag', async () => {
    const wrapper = mountFunction()

    expect(wrapper.vm.isActive).toBeFalsy()
    expect(wrapper.vm.activeThumb).toBeNull()

    const container = wrapper.find('.v-slider__thumb-container')

    container.trigger('mousedown')

    expect(wrapper.vm.isActive).toBeTruthy()
    expect(wrapper.vm.activeThumb).toBe(0)
  })

  it('should set internal value', () => {
    const wrapper = mountFunction()

    expect(wrapper.vm.internalValue).toEqual([0, 0])

    wrapper.vm.setInternalValue(2)

    expect(wrapper.vm.internalValue).toEqual([0, 0])

    wrapper.setData({ activeThumb: 0 })
    wrapper.vm.setInternalValue(1)

    expect(wrapper.vm.internalValue).toEqual([0, 1])

    wrapper.vm.setInternalValue(5)

    expect(wrapper.vm.internalValue).toEqual([0, 5])

    wrapper.setProps({ value: [5, 10] })

    expect(wrapper.vm.internalValue).toEqual([5, 10])

    wrapper.vm.setInternalValue(100)

    expect(wrapper.vm.internalValue).toEqual([5, 100])

    wrapper.setData({ activeThumb: 1 })
    wrapper.vm.setInternalValue(25)

    expect(wrapper.vm.internalValue).toEqual([5, 25])
  })

  it('should return the proper styles', () => {
    const wrapper = mountFunction()

    let styles = wrapper.vm.trackFillStyles

    expect(styles.left).toBe('0%')
    expect(styles.right).toBe('auto')
    expect(styles.width).toBe('0%')
    wrapper.vm.$vuetify.rtl = true

    styles = wrapper.vm.trackFillStyles
    expect(styles.left).toBe('auto')
    expect(styles.right).toBe('0%')
    expect(styles.width).toBe('0%')

    wrapper.vm.$vuetify.rtl = undefined
  })

  it('should render a vertical slider', async () => {
    const wrapper = mountFunction({
      propsData: {
        vertical: true
      }
    })

    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should render disabled slider', async () => {
    const wrapper = mountFunction({
      propsData: {
        disabled: true
      }
    })

    expect(wrapper.html()).toMatchSnapshot()
  })
})


<template>
  <v-container>
    <v-layout column>
      <main-header>Switches</main-header>

      <core-title>Simple</core-title>
      <core-section>
        <v-layout justify-space-around>
          <v-switch v-model="model1" />
        </v-layout>
      </core-section>

      <core-title>Disabled</core-title>
      <core-section>
        <v-layout justify-space-around>
          <v-switch v-model="model21" disabled />
          <v-switch v-model="model22" disabled />
        </v-layout>
      </core-section>

      <core-title>Colors</core-title>
      <core-section>
        <v-layout justify-space-around>
          <v-switch v-model="model31" color="cyan" />
          <v-switch v-model="model32" color="red lighten-2" />
          <v-switch v-model="model33" color="warning" />
        </v-layout>
      </core-section>

      <core-title>Flat</core-title>
      <core-section>
        <v-layout justify-space-around>
          <v-switch v-model="model4" flat />
        </v-layout>
      </core-section>

      <core-title>Loading</core-title>
      <core-section>
        <v-layout justify-space-around>
          <v-switch v-model="model51" loading color="cyan" />
          <v-switch v-model="model52" disabled loading color="red lighten-2" />
          <v-switch v-model="model53" disabled loading color="warning" />
        </v-layout>
      </core-section>

      <core-title>Inset</core-title>
      <core-section>
        <v-layout justify-space-around>
          <v-switch v-model="model61" color="red" inset />
          <v-switch v-model="model62" color="blue" inset />
        </v-layout>
      </core-section>
    </v-layout>
  </v-container>
</template>

<script>
  export default {
    name: '',

    data: () => ({
      model1: false,
      model21: false,
      model22: true,
      model31: true,
      model32: true,
      model33: true,
      model4: false,
      model51: true,
      model52: false,
      model53: true,
      model61: true,
      model62: false
    })
  }
</script>


import Vue from 'vue'
import { test } from '@/test'
import VDataTable from '@/components/VDataTable/VDataTable'

test.skip('VDataTable.vue', ({ mount, compileToFunctions }) => {
  function dataTableTestData () {
    return {
      propsData: {
        pagination: {
          descending: false,
          sortBy: 'col1',
          rowsPerPage: 1,
          page: 1
        },
        headers: [
          { text: 'First Column', value: 'col1', class: 'a-string' },
          { text: 'Second Column', value: 'col2', sortable: false },
          { text: 'Third Column', value: 'col3', class: ['an', 'array'] }
        ],
        items: [
          { other: 1, col1: 'foo', col2: 'a', col3: 1 },
          { other: 2, col1: null, col2: 'b', col3: 2 },
          { other: 3, col1: undefined, col2: 'c', col3: 3 }
        ]
      }
    }
  }

  function dataTableNestedTestData () {
    return {
      propsData: {
        value: [{ nested: { value: { id: 1, name: 'bar' } } }],
        itemKey: 'nested.value.id',
        headers: [
          { text: 'ID', value: 'nested.value.id' },
          { text: 'Name', value: 'nested.value.name' },
        ],
        items: [
          { nested: { value: { id: 0, name: 'foo' } } },
          { nested: { value: { id: 1, name: 'bar' } } },
          { nested: { value: { id: 2, name: 'baz' } } }
        ]
      }
    }
  }

  function dataTableTestDataFilter () {
    return {
      propsData: {
        headers: [
          { text: 'First Column', value: 'first' },
          { text: 'Second Column', value: 'second.first' },
          { text: 'Third Column', value: 'third.first.second' }
        ],
        items: [
          { other: 1, first: 'foo', second: { first: 'bar' }, third: { first: { second: 'baz', third: 'outside' } }, fourth: 'outside' }
        ]
      }
    }
  }

  // TODO: This doesn't actually test anything
  it.skip('should be able to filter null and undefined values', async () => {
    const data = dataTableTestData()
    const pagination = data.propsData.pagination
    const wrapper = mount(VDataTable, data)

    pagination.descending = true

    expect(wrapper.vm.$props.pagination.descending).toBe(true)
    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should match a snapshot - no matching results', () => {
    const data = dataTableTestData()
    data.propsData.search = "asdf"
    const wrapper = mount(VDataTable, data)

    expect(wrapper.html()).toMatchSnapshot()

    const content = wrapper.find('table.v-datatable tbody > tr > td')[0]
    expect(content.element.textContent).toBe('No matching records found')

    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should match a snapshot - no data', () => {
    const data = dataTableTestData()
    data.propsData.items = []
    const wrapper = mount(VDataTable, data)

    expect(wrapper.html()).toMatchSnapshot()

    const content = wrapper.find('table.v-datatable tbody > tr > td')[0]
    expect(content.element.textContent).toBe('No data available')

    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should match a snapshot - with data', () => {
    const data = dataTableTestData()
    data.propsData.pagination.rowsPerPage = 3

    const vm = new Vue()
    const items = props => vm.$createElement('td', [props.item.col2])
    const component = Vue.component('test', {
      render (h) {
        return h(VDataTable, {
          props: {
            ...data.propsData
          },
          scopedSlots: {
            items
          }
        })
      }
    })

    const wrapper = mount(component)

    expect(wrapper.html()).toMatchSnapshot()
    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should match a snapshot with single rows-per-page-items', () => {
    const data = dataTableTestData()
    data.propsData.rowsPerPageItems = [1]
    const wrapper = mount(VDataTable, data)

    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should match display no-data-text when no data', () => {
    const data = dataTableTestData()
    data.propsData.items = []
    data.propsData.noDataText = 'foo'
    const wrapper = mount(VDataTable, data)

    expect(wrapper.find('tbody td')[0].html()).toMatchSnapshot()
    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should match display no-results-text when no results', () => {
    const data = dataTableTestData()
    data.propsData.noResultsText = 'bar'
    data.propsData.search = "no such item"
    const wrapper = mount(VDataTable, data)

    expect(wrapper.find('tbody tr td')[0].html()).toMatchSnapshot()
    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should render aria-sort attribute on column headers', async () => {
    const data = dataTableTestData()
    const wrapper = mount(VDataTable, data)

    const headers = wrapper.find('thead:first-of-type > tr:first-of-type > th')

    expect(
      headers.map(h => h.getAttribute('aria-sort'))
    ).toEqual(['ascending', 'none', 'none'])

    wrapper.setProps({
      pagination: {
        sortBy: 'col3',
        descending: false
      }
    })

    expect(
      headers.map(h => h.getAttribute('aria-sort'))
    ).toEqual(['none', 'none', 'ascending'])

    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should match not allow a null sort', async () => {
    const data = {
      propsData: {
        mustSort: true,
        headers: [
          { text: 'First Column', value: 'col1' },
          { text: 'Second Column', value: 'col2', sortable: false },
          { text: 'Third Column', value: 'col3' }
        ],
        items: [
          { other: 1, col1: 'foo', col2: 'a', col3: 1 },
          { other: 2, col1: null, col2: 'b', col3: 2 },
          { other: 3, col1: undefined, col2: 'c', col3: 3 }
        ]
      }
    }

    const wrapper = mount(VDataTable, data)

    expect(wrapper.vm.defaultPagination.descending).toBe(false)
    wrapper.vm.sort(0)
    await wrapper.vm.$nextTick()
    expect(wrapper.vm.defaultPagination.descending).toBe(false)
    wrapper.vm.sort(0)
    await wrapper.vm.$nextTick()
    expect(wrapper.vm.defaultPagination.descending).toBe(true)
    wrapper.vm.sort(0)
    await wrapper.vm.$nextTick()
    expect(wrapper.vm.defaultPagination.descending).toBe(false)

    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should render a progress with headers slot', () => {
    const vm = new Vue()
    const wrapper = mount(Vue.component('test', {
      render (h) {
        return h(VDataTable, {
          props: {
            items: []
          },
          scopedSlots: {
            headers: props => vm.$createElement('tr')
          }
        })
      }
    }))

    expect(wrapper.find('.v-datatable__progress')).toHaveLength(1)
    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should only filter on data specified in headers', async () => {
    const wrapper = mount(VDataTable, dataTableTestDataFilter())

    expect(wrapper.instance().filteredItems).toHaveLength(1)
    wrapper.setProps({
      search: 'outside'
    })
    expect(wrapper.instance().filteredItems).toHaveLength(0)
    wrapper.setProps({
      search: 'baz'
    })
    expect(wrapper.instance().filteredItems).toHaveLength(1)

    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should not filter items if search is empty', async () => {
    const data = dataTableTestDataFilter()
    data.propsData.search = '    '
    const wrapper = mount(VDataTable, data)

    expect(wrapper.instance().filteredItems).toHaveLength(data.propsData.items.length)

    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should allow custom tr when using no-data slot', async () => {
    const wrapper = mount(Vue.component('test', {
      render (h) {
        return h(VDataTable, {
          props: {
            items: []
          },
        }, [h('tr', { slot: 'no-data', class: 'custom-class' })])
      }
    }))

    expect(wrapper.find('table tbody tr.custom-class').length).toBe(1)
    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should allow custom td when using no-results slot', async () => {
    const wrapper = mount(Vue.component('test', {
      render (h) {
        return h(VDataTable, {
          props: {
            items: [{}],
            search: 'foo'
          },
        }, [h('td', { slot: 'no-results', class: 'custom-class' })])
      }
    }))

    expect(wrapper.find('table tbody tr td.custom-class').length).toBe(1)
    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should render tr and td when using no-results slot', async () => {
    const wrapper = mount(Vue.component('test', {
      render (h) {
        return h(VDataTable, {
          props: {
            items: [{}],
            search: 'foo'
          },
        }, [h('div', { slot: 'no-results', class: 'custom-class' })])
      }
    }))

    expect(wrapper.find('table tbody tr td div.custom-class').length).toBe(1)
    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should initialize everyItem state', async () => {
    const data = dataTableTestData()
    data.propsData.value = data.propsData.items
    const wrapper = mount(VDataTable, data)

    expect(wrapper.vm.everyItem).toBe(true);
    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should update everyItem state', async () => {
    const data = dataTableTestData()
    data.propsData.itemKey = 'other';
    const wrapper = mount(VDataTable, data)

    expect(wrapper.vm.everyItem).toBe(false);
    wrapper.vm.value.push(wrapper.vm.items[0]);
    expect(wrapper.vm.everyItem).toBe(false);

    wrapper.vm.value.push(wrapper.vm.items[1]);
    wrapper.vm.value.push(wrapper.vm.items[2]);
    expect(wrapper.vm.everyItem).toBe(true);
    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should render correct colspan when using headers-length prop', async () => {
    const data = dataTableTestData()
    data.propsData.headersLength = 11
    const wrapper = mount(VDataTable, data)

    expect(wrapper.find('tr.v-datatable__progress th')[0].getAttribute('colspan')).toBe('11')
    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should not emit pagination event after load (#3585)', async () => {
    const data = dataTableTestDataFilter()
    data.propsData.totalItems = 0
    data.propsData.search = ''
    data.propsData.pagination = {}

    const wrapper = mount(VDataTable, data)
    const pagination = jest.fn()
    wrapper.vm.$on('update:pagination', pagination)

    await wrapper.vm.$nextTick()

    expect(pagination).toHaveBeenCalledTimes(0)

    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should emit correct totalItems property in pagination when searching (#3511)', async () => {
    const data = {
      propsData: {
        pagination: {
          rowsPerPage: 5,
          descending: false,
          page: 1,
          sortBy: 'calories'
        },
        headers: [{ text: 'Calories', value: 'calories' }],
        items: [{ calories: 19 }, { calories: 19 }, { calories: 152 }]
      }
    }

    const wrapper = mount(VDataTable, data)
    const pagination = jest.fn()
    wrapper.vm.$on('update:pagination', pagination)

    await wrapper.vm.$nextTick()

    wrapper.setProps({ search: '9' })

    await wrapper.vm.$nextTick()

    expect(wrapper.instance().filteredItems).toHaveLength(2)
    expect(pagination).toHaveBeenCalledWith(Object.assign({}, data.propsData.pagination, { totalItems: 2 }))

    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should not reset page when total-items prop changes (#3766)', async () => {
    const data = {
      propsData: {
        pagination: {
          rowsPerPage: 1,
          descending: false,
          page: 2,
          sortBy: 'calories'
        },
        totalItems: 3,
        headers: [{ text: 'Calories', value: 'calories' }],
        items: [{ calories: 19 }, { calories: 19 }, { calories: 152 }]
      }
    }

    const wrapper = mount(VDataTable, data)

    await wrapper.vm.$nextTick()

    expect(wrapper.instance().computedPagination.page).toBe(2)

    wrapper.setProps({ totalItems: 10 })

    await wrapper.vm.$nextTick()

    expect(wrapper.instance().computedPagination.page).toBe(2)

    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should allow selection using nested values', async () => {
    const data = dataTableNestedTestData()
    const wrapper = mount(VDataTable, data)

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.selected.hasOwnProperty(1)).toBe(true)
    expect(wrapper.vm.selected[1]).toBe(true)

    wrapper.setProps({ value: [{ nested: { value: { id: 2, name: 'baz' } } }] })
    await wrapper.vm.$nextTick()

    expect(wrapper.vm.selected.hasOwnProperty(1)).toBe(false)
    expect(wrapper.vm.selected.hasOwnProperty(2)).toBe(true)
    expect(wrapper.vm.selected[2]).toBe(true)

    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })

  it('should support setting item-key using nested values', async () => {
    const data = dataTableNestedTestData()

    const vm = new Vue()
    const component = Vue.component('test', {
      render (h) {
        return h(VDataTable, {
          props: data.propsData,
          scopedSlots: {
            items: props => {
              return [vm.$createElement('td', [props.item.nested.value.name])]
            }
          }
        })
      }
    })
    const wrapper = mount(component)

    const table = wrapper.vm.$children[0]
    table.genTR = jest.fn()
    table.genTBody()

    expect(table.genTR.mock.calls.every(([ node, attrs ], i) => {
      return (attrs.key === data.propsData.items[i].nested.value.id)
    })).toBe(true)

    expect('Unable to locate target [data-app]').toHaveBeenTipped()
  })
})


disabled: Boolean,
expand: Boolean,
flat: Boolean,
nav: Boolean,
shaped: Boolean,
subheader: Boolean,
threeLine: Boolean,


module.exports = {
  'v-toolbar': {
    slots: ['default', 'extension'],
    scopedSlots: [{
      name: 'img',
      props: {
        props: '{ height: string, src: string | srcObject }'
      }
    }]
  }
}


<template>
  <v-layout
    id="patrons"
    align-center
    tag="section"
    wrap
  >
    <v-flex>
      <v-container
        fluid
        grid-list-md
        pa-0
      >
        <supporter-group
          v-if="tier.includes(0)"
          :group="supporters.special"
          :title="!hideTitles ? 'Premiere Sponsor' : undefined"
          :class="classes"
          :large="!compact"
          :small="compact"
        />

        <supporter-group
          v-if="tier.includes(1)"
          :group="supporters.diamond"
          :title="!hideTitles ? 'Diamond' : undefined"
          :class="classes"
          :large="!compact"
          :small="compact"
        />

        <supporter-group
          v-if="tier.includes(2)"
          :group="supporters.palladium"
          :title="!hideTitles ? 'Palladium' : undefined"
          :class="classes"
        />

        <supporter-group
          v-if="tier.includes(3)"
          :group="supporters.gold"
          :title="!hideTitles ? 'Gold' : undefined"
          :class="classes"
          small
        />

        <supporter-group
          v-if="tier.includes(4)"
          :group="supporters.affiliates"
          :title="!hideTitles ? 'Affiliate' : undefined"
          :class="classes"
          small
        />

        <supporter-group
          v-if="tier.includes(5)"
          :group="supporters.service"
          :title="!hideTitles ? 'Service' : undefined"
          small
        />
      </v-container>
    </v-flex>
  </v-layout>
</template>

<script>
  // Utilities
  import {
    mapMutations,
    mapState
  } from 'vuex'

  export default {
    components: {
      SupporterGroup: () => import('@/components/supporters/SupporterGroup')
    },

    props: {
      compact: {
        type: Boolean,
        default: false
      },
      dense: {
        type: Boolean,
        default: false
      },
      hideTitles: {
        type: Boolean,
        default: false
      },
      tier: {
        type: Array,
        default: () => ([0, 1, 2, 3, 4, 5])
      }
    },

    computed: {
      ...mapState('app', ['supporters']),
      classes () {
        return {
          'mb-0': this.dense,
          'mb-5': !this.dense
        }
      }
    },

    async created () {
      // if (this.$ssrContext || Object.keys(this.supporters).length) return

      const supporters = require('@/data/api/supporters.json')

      // const supporters = await fetch('https://cdn.vuetifyjs.com/supporters.json', {
      //   headers: {
      //     'Access-Control-Allow-Origin': '*'
      //   }
      // }).then(res => res.json())

      if (supporters) this.setSupporters(supporters)
    },

    methods: {
      ...mapMutations('app', ['setSupporters'])
    }
  }
</script>


`v-footer`用于显示用户可能希望从站点内的所有页面访问到的公共信息。

页脚组件只是一个基本的容器。

使用 footer 组件制作一个包含一些页面链接的公司页脚。

使用 footer 组件制作一个靛蓝背景以及包含一些社交媒体图标按钮的页脚。

使用 footer 组件制作一个蓝绿色顶部以及几个行列链接的页脚。


keep keep keep keep add keep keep keep keep keep


Nome

Descrição