keep keep replace keep keep keep keep keep

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

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

// Extensions
import VSelect from '../VSelect/VSelect'
import VTextField from '../VTextField/VTextField'

// Utils
import { keyCodes } from '../../util/helpers'

export default {
  name: 'v-autocomplete',

  extends: VSelect,

  data: vm => ({
    attrsInput: null,
    lazySearch: vm.searchInput,
    lazyValue: vm.value != null
      ? vm.value
      : vm.multi || vm.tags ? [] : undefined
  }),

  props: {
    browserAutocomplete: {
      type: String,
      default: 'off'
    },
    combobox: Boolean,
    delimiters: Array,
    filter: {
      type: Function,
      default: (item, queryText, itemText) => {
        const hasValue = val => val != null ? val : ''

        const text = hasValue(itemText)
        const query = hasValue(queryText)

        return text.toString()
          .toLowerCase()
          .indexOf(query.toString().toLowerCase()) > -1
      }
    },
    noFilter: Boolean,
    offsetY: {
      type: Boolean,
      default: true
    },
    searchInput: {
      default: null
    },
    tags: Boolean,
    transition: {
      type: [Boolean, String],
      default: false
    }
  },

  computed: {
    classes () {
      return Object.assign({}, VSelect.computed.classes.call(this), {
        'v-autocomplete': true,
        'v-autocomplete--is-selecting-index': this.selectedIndex > -1
      })
    },
    computedItems () {
      return this.filteredItems
    },
    /**
     * The range of the current input text
     *
     * @return {Number}
     */
    currentRange () {
      if (this.selectedItem == null) return 0

      return this.getText(this.selectedItem).toString().length
    },
    filteredItems () {
      const items = this.filterDuplicates(this.cachedItems.concat(this.items))

      if (!this.isSearching || this.noFilter) return items

      return items.filter(i => this.filter(i, this.internalSearch, this.getText(i)))
    },
    hasSlot () {
      return VSelect.computed.hasSlot.call(this) || this.tags
    },
    internalSearch: {
      get () {
        return this.lazySearch
      },
      set (val) {
        this.lazySearch = val

        this.$emit('update:searchInput', val)
      }
    },
    isAnyValueAllowed () {
      return this.combobox || this.tags
    },
    isDirty () {
      return this.searchIsDirty || this.selectedItems.length > 0
    },
    isMulti () {
      return this.tags || VSelect.computed.isMulti.call(this)
    },
    isSearching () {
      if (this.isMulti) return this.searchIsDirty

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

      const filtered = this.filteredItems.length > 0

      if (this.isAnyValueAllowed) {
        return filtered
      }

      return !this.hideNoData || filtered
    },
    menuProps () {
      return Object.assign(VSelect.computed.menuProps.call(this), {
        contentClass: 'v-autocomplete__content',
        value: this.menuCanShow && this.isMenuActive
      })
    },
    searchIsDirty () {
      return this.internalSearch != null &&
        this.internalSearch !== ''
    },
    selectedItem () {
      if (this.isMulti) return null

      return this.selectedItems.find(i => {
        return this.valueComparator(this.getValue(i), this.getValue(this.internalValue))
      })
    }
  },

  watch: {
    filteredItems () {
      this.setMenuIndex(-1)
    },
    isFocused (val) {
      if (val) {
        this.$refs.input &&
          this.$refs.input.select()
      }
    },
    isMenuActive (val) {
      if (val || !this.hasSlot) return

      this.lazySearch = null
    },
    searchInput (val) {
      this.lazySearch = val
    },
    internalSearch (val) {
      if (
        val &&
        this.tags &&
        this.delimiters
      ) {
        const delimiter = this.delimiters.find(d => val.endsWith(d))
        if (delimiter == null) return

        this.internalSearch = val.slice(0, val.length - delimiter.length)
        this.updateTags()
      }
    }
  },

  created () {
    this.setSearch()
  },

  methods: {
    activateMenu () {
      if (this.menuCanShow) {
        this.isMenuActive = true
      }
    },
    changeSelectedIndex (keyCode) {
      // 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.internalValue = this.isMulti ? [] : undefined
        } else {
          this.selectItem(currentItem)
        }

        this.selectedIndex = newIndex
      }
    },
    genInput () {
      const input = VTextField.methods.genInput.call(this)

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

      return input
    },
    genList () {
      const list = VSelect.methods.genList.call(this)

      list.componentOptions.propsData.items = this.filteredItems
      list.componentOptions.propsData.noFilter = (
        this.noFilter ||
        !this.isSearching ||
        !this.filteredItems.length
      )
      list.componentOptions.propsData.searchInput = this.internalSearch

      return list
    },
    genSelections () {
      return this.hasSlot || this.isMulti
        ? VSelect.methods.genSelections.call(this)
        : []
    },
    onBlur (e) {
      this.updateSelf()
      VSelect.methods.onBlur.call(this, e)
    },
    onClick (e) {
      if (this.isDisabled) return

      if (e.target !== this.$refs.input) this.onFocus()
      this.activateMenu()
    },
    onEnterDown () {
      this.onBlur()
    },
    onEscDown (e) {
      e.preventDefault()
      this.isMenuActive = false
    },
    onInput (e) {
      // If typing and menu is not currently active
      if (e.target.value) {
        this.activateMenu()
        if (!this.isAnyValueAllowed) this.setMenuIndex(0)
      }

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

      // If enter, space, up, or down is pressed, open menu
      if (!this.isMenuActive &&
        [keyCodes.enter, keyCodes.space, keyCodes.up, keyCodes.down].includes(keyCode)
      ) this.activateMenu()

      // This should do something different
      if (e.keyCode === keyCodes.enter) return this.onEnterDown()

      // If escape deactivate the menu
      if (keyCode === keyCodes.esc) return this.onEscDown(e)

      // If tab - select item or close menu
      if (keyCode === keyCodes.tab) return this.onTabDown(e)

      if (this.$refs.input.selectionStart === 0) this.updateSelf()
      if (!this.hideSelections) this.changeSelectedIndex(keyCode)
    },
    onTabDown (e) {
      const menuIndex = this.getMenuIndex()

      // When adding tags, if searching and
      // there is not a filtered options,
      // add the value to the tags list
      if (this.tags &&
        this.internalSearch &&
        menuIndex === -1
      ) {
        e.preventDefault()
        e.stopPropagation()

        return this.updateTags()
      // An item that is selected by
      // menu-index should toggled
      } else if (this.isMenuActive && menuIndex > -1) {
        // Reset the list index if searching
        this.internalSearch &&
          this.$nextTick(() => setTimeout(() => this.setMenuIndex(-1), 0))

        e.preventDefault()
        this.selectListTile(menuIndex)
      } else {
        // If we make it here,
        // the user has no selected indexes
        // and is probably tabbing out
        this.isFocused = false
      }

      this.updateSelf()
    },
    selectItem (item) {
      VSelect.methods.selectItem.call(this, item)

      this.setSearch()
    },
    selectListTile (index) {
      if (!this.$refs.menu.tiles[index]) return

      this.$refs.menu.tiles[index].click()
    },
    setSelectedItems () {
      if (this.internalValue == null) {
        this.selectedItems = []
      } else if (this.tags) {
        this.selectedItems = this.internalValue
      } else if (this.combobox) {
        this.selectedItems = [this.internalValue]
      } else {
        VSelect.methods.setSelectedItems.call(this)
      }
    },
    setMenuIndex (index) {
      this.$refs.menu && (this.$refs.menu.listIndex = index)
    },
    setSearch () {
      if (this.hasSlot || this.isMulti) return

      this.lazySearch = this.getText(this.internalValue)
    },
    setValue () {
      this.internalValue = this.internalSearch
      this.$emit('change', this.internalSearch)
    },
    updateAutocomplete () {
      if (!this.searchIsDirty &&
        !this.internalValue
      ) return

      if (this.isMulti) return (this.internalSearch = null)

      if (!this.valueComparator(
        this.internalSearch,
        this.getValue(this.internalValue)
      )) {
        this.internalSearch = this.initialValue
      }
    },
    updateCombobox () {
      if (this.internalSearch !== this.internalValue) this.setValue()
      else this.updateAutocomplete()
    },
    updateSelf () {
      if (this.tags) this.updateTags()
      else if (this.combobox) this.updateCombobox()
      else this.updateAutocomplete()
    },
    // Maybe change to onBlur?
    updateTags () {
      const menuIndex = this.getMenuIndex()

      // If the user is not searching
      // and no menu item is selected
      // do nothing
      if (menuIndex < 0 &&
        !this.searchIsDirty
      ) return

      const index = this.selectedItems.indexOf(this.internalSearch)
      // If it already exists, do nothing
      // this might need to change to bring
      // the duplicated item to the last entered
      if (index > -1) {
        this.internalValue.splice(index, 1)
      }

      // If menu index is greater than 1
      // the selection is handled elsewhere
      // TODO: find out where
      if (menuIndex > -1) return (this.internalSearch = null)

      this.selectItem(this.internalSearch)
      this.internalSearch = null
    }
  }
}


import { test } from '@/test'
import VAutocomplete from '@/components/VAutocomplete'

test('VAutocomplete.js', ({ mount, shallow }) => {
  const app = document.createElement('div')
  app.setAttribute('data-app', true)
  document.body.appendChild(app)

  it('should allow changing of browser autocomplete', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        browserAutocomplete: 'on'
      }
    })

    const input = wrapper.first('input')

    expect(input.element.getAttribute('autocomplete')).toBe('on')

    wrapper.setProps({ browserAutocomplete: 'off' })

    await wrapper.vm.$nextTick()

    expect(input.element.getAttribute('autocomplete')).toBe('off')
  })

  it('should have explicit tabindex passed through when autocomplete', () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        tabindex: 10
      }
    })

    expect(wrapper.vm.$refs.input.tabIndex).toBe(10)
    expect(wrapper.vm.$el.tabIndex).toBe(-1)
  })

  it('should emit search input changes', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
      }
    })

    const input = wrapper.first('input')

    const update = jest.fn()
    wrapper.vm.$on('update:searchInput', update)

    input.element.value = 'test'
    input.trigger('input')

    await wrapper.vm.$nextTick()

    expect(update).toBeCalledWith('test')
  })

  it('should filter autocomplete search results', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: { items: ['foo', 'bar'] }
    })

    wrapper.setData({ internalSearch: 'foo' })

    expect(wrapper.vm.filteredItems).toHaveLength(1)
    expect(wrapper.vm.filteredItems[0]).toBe('foo')
  })

  it('should filter numeric primitives', () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        items: [1, 2]
      }
    })

    wrapper.setData({ internalSearch: 1 })

    expect(wrapper.vm.filteredItems).toHaveLength(1)
    expect(wrapper.vm.filteredItems[0]).toBe(1)
  })

  it('should activate when search changes and not active', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        items: [1, 2, 3, 4],
        multiple: true
      }
    })

    wrapper.vm.isMenuActive = true
    await wrapper.vm.$nextTick()
    wrapper.setData({ internalSearch: 2 })
    await wrapper.vm.$nextTick()

    expect(wrapper.vm.isMenuActive).toBe(true)
  })

  it('should set searchValue to null when deactivated', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        items: [1, 2, 3, 4],
        multiple: true
      }
    })

    const input = wrapper.first('input')

    input.trigger('focus')
    input.element.value = 2
    input.trigger('input')

    expect(wrapper.vm.internalSearch).toBe('2')

    wrapper.setProps({
      multiple: false,
      value: 2
    })

    input.trigger('focus')
    input.element.value = 3
    input.trigger('input')
    input.trigger('blur')

    expect(wrapper.vm.internalSearch).toBe(null)
  })

  it('should render role=combobox correctly when autocomplete', async () => {
    const wrapper = shallow(VAutocomplete)

    expect(wrapper.vm.$el.getAttribute('role')).toBeFalsy()

    const input = wrapper.first('input')
    expect(input.element.getAttribute('role')).toBe('combobox')
  })

  it('should not duplicate items after items update when caching is turned on', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        cacheItems: true,
        returnObject: true,
        itemText: 'text',
        itemValue: 'id',
        items: [],
      }
    })

    wrapper.setProps({ items: [{ id: 1, text: 'A' }] })
    expect(wrapper.vm.computedItems).toHaveLength(1)
    wrapper.setProps({ items: [{ id: 1, text: 'A' }] })
    expect(wrapper.vm.computedItems).toHaveLength(1)
  })

  it('should cache items', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: { cacheItems: true }
    })

    wrapper.setProps({ items: ['bar', 'baz'] })
    expect(wrapper.vm.computedItems).toHaveLength(2)

    wrapper.setProps({ items: ['foo'] })
    expect(wrapper.vm.computedItems).toHaveLength(3)

    wrapper.setProps({ items: ['bar'] })
    expect(wrapper.vm.computedItems).toHaveLength(3)
  })

  it('should cache items passed via prop', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        cacheItems: true,
        items: [1, 2, 3, 4]
      }
    })

    expect(wrapper.vm.computedItems).toHaveLength(4)

    wrapper.setProps({ items: [5] })

    expect(wrapper.vm.computedItems).toHaveLength(5)
  })

  it('should show input when focused and autocomplete', async () => {
    const wrapper = shallow(VAutocomplete)

    const input = wrapper.first('input')

    expect(input.hasStyle('display', 'none'))

    wrapper.trigger('focus')

    expect(input.hasStyle('display', 'block'))
  })

  it('should not filter text with no items', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        items: ['foo', 'bar']
      }
    })

    wrapper.setProps({ searchInput: 'asdf' })

    // Wait for watcher
    await wrapper.vm.$nextTick()

    const tile = wrapper.first('.v-list__tile__title')

    expect(tile.text()).toBe('No data available')
  })

  it('should not display menu when tab focused', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        items: [1 ,2],
        value: 1
      }
    })

    const input = wrapper.first('input')
    input.trigger('focus')
    await wrapper.vm.$nextTick()
    expect(wrapper.vm.isMenuActive).toBe(false)

    wrapper.setProps({
      items: [
        { text: 'Foo', value: 1 },
        { text: 'Bar', value: 2 }
      ]
    })

    input.trigger('blur')
    await wrapper.vm.$nextTick()

    input.trigger('focus')
    await wrapper.vm.$nextTick()

    expect(wrapper.vm.isMenuActive).toBe(false)
  })

  it('should emit custom value on blur', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: { combobox: true }
    })

    const input = wrapper.first('input')

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

    input.trigger('focus')
    await wrapper.vm.$nextTick()

    input.element.value = 'foo'
    input.trigger('input')

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.isMenuActive).toBe(false)

    input.trigger('blur')
    expect(change).toHaveBeenCalledWith('foo')

    input.trigger('keydown.esc')
    expect(wrapper.vm.isMenuActive).toBe(false)

    input.element.value = ''
    input.trigger('input')

    await wrapper.vm.$nextTick()
    expect(wrapper.vm.isMenuActive).toBe(false)
  })

  it('should change selected index', async () => {
    const wrapper = shallow(VAutocomplete, {
      attachToDocument: true,
      propsData: {
        items: ['foo', 'bar', 'fizz'],
        multiple: true,
        value: ['foo', 'bar', 'fizz']
      }
    })

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.selectedIndex).toBe(-1)
    expect(wrapper.vm.selectedItems.length).toBe(3)

    // Right arrow
    wrapper.vm.changeSelectedIndex(39)
    expect(wrapper.vm.selectedIndex).toBe(0)

    wrapper.vm.changeSelectedIndex(39)
    expect(wrapper.vm.selectedIndex).toBe(1)

    wrapper.vm.changeSelectedIndex(39)
    expect(wrapper.vm.selectedIndex).toBe(2)

    // Left arrow
    wrapper.vm.changeSelectedIndex(37)
    expect(wrapper.vm.selectedIndex).toBe(1)

    wrapper.vm.changeSelectedIndex(37)
    expect(wrapper.vm.selectedIndex).toBe(0)

    wrapper.vm.changeSelectedIndex(37)
    expect(wrapper.vm.selectedIndex).toBe(-1)

    wrapper.vm.changeSelectedIndex(37)
    expect(wrapper.vm.selectedIndex).toBe(2)

    wrapper.vm.changeSelectedIndex(37)
    expect(wrapper.vm.selectedIndex).toBe(1)

    // Delete key
    wrapper.vm.changeSelectedIndex(8)
    await wrapper.vm.$nextTick()
    expect(wrapper.vm.selectedIndex).toBe(1)

    wrapper.vm.changeSelectedIndex(37)
    expect(wrapper.vm.selectedIndex).toBe(0)

    wrapper.vm.changeSelectedIndex(8)
    await wrapper.vm.$nextTick()
    expect(wrapper.vm.selectedIndex).toBe(0)

    // Should not change index if search is dirty
    wrapper.setProps({ searchInput: 'foo' })
    wrapper.vm.changeSelectedIndex(8)

    expect(wrapper.vm.selectedIndex).toBe(0)
    expect(wrapper.vm.selectedItems.length).toBe(1)

    wrapper.setProps({ searchInput: undefined })

    // Should not proceed if keyCode doesn't match
    wrapper.vm.changeSelectedIndex(99)
    await wrapper.vm.$nextTick()

    expect(wrapper.vm.selectedIndex).toBe(0)
    expect(wrapper.vm.selectedItems.length).toBe(1)

    wrapper.vm.changeSelectedIndex(8)
    await wrapper.vm.$nextTick()

    expect(wrapper.vm.selectedItems.length).toBe(0)
    expect(wrapper.vm.selectedIndex).toBe(-1)

    // Should not change/error if called with no selection
    wrapper.vm.changeSelectedIndex(8)
    await wrapper.vm.$nextTick()

    expect(wrapper.vm.selectedIndex).toBe(-1)

    wrapper.setProps({ value: ['foo', 'bar', 'fizz'] })

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.selectedItems.length).toBe(3)

    wrapper.vm.selectedIndex = 2

    // Simulating removing items when an index already selected
    wrapper.setProps({ value: ['foo', 'bar'] })

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.selectedIndex).toBe(2)

    // Backspace
    wrapper.vm.changeSelectedIndex(46)
    expect(wrapper.vm.selectedIndex).toBe(-1)
  })

  it('should conditionally show the menu', async () => {
    const wrapper = mount(VAutocomplete, {
      attachToDocument: true,
      propsData: {
        items: ['foo', 'bar', 'fizz']
      }
    })

    const slot = wrapper.first('.v-input__slot')
    const input = wrapper.first('input')

    // Focus input should only focus
    input.trigger('focus')

    expect(wrapper.vm.isFocused).toBe(true)
    expect(wrapper.vm.menuCanShow).toBe(true)
    expect(wrapper.vm.isMenuActive).toBe(false)

    // Clicking input should open menu
    slot.trigger('click')

    expect(wrapper.vm.isMenuActive).toBe(true)
    expect(wrapper.vm.menuCanShow).toBe(true)

    wrapper.setProps({ searchInput: 'foo' })

    expect(wrapper.vm.isMenuActive).toBe(true)
    expect(wrapper.vm.menuCanShow).toBe(true)

    // Should close menu but keep focus
    input.trigger('keydown.esc')

    expect(wrapper.vm.isFocused).toBe(true)
    expect(wrapper.vm.isMenuActive).toBe(false)
    expect(wrapper.vm.menuCanShow).toBe(true)

    // Should not show menu
    wrapper.setProps({
      combobox: true,
      searchInput: 'foobar'
    })

    expect(wrapper.vm.menuCanShow).toBe(false)

    slot.trigger('click')

    expect(wrapper.vm.isMenuActive).toBe(false)

    // TODO: Add expects for tags when impl
  })

  it('should have the correct selected item', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        items: ['foo', 'bar', 'fizz'],
        multiple: true,
        value: ['foo']
      }
    })

    expect(wrapper.vm.selectedItem).toBe(null)

    wrapper.setProps({
      multiple: false,
      value: 'foo'
    })

    expect(wrapper.vm.selectedItem).toBe('foo')
  })

  it('should reset lazySearch', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        chips: true,
        items: ['foo', 'bar', 'fizz'],
        searchInput: 'foo'
      }
    })

    expect(wrapper.vm.lazySearch).toBe('foo')
    expect(wrapper.vm.hasSlot).toBe(true)

    wrapper.setData({ isMenuActive: true })
    await wrapper.vm.$nextTick()
    wrapper.setData({ isMenuActive: false })
    await wrapper.vm.$nextTick()

    expect(wrapper.vm.lazySearch).toBe(null)
  })

  it('should call methods on blur', () => {
    const updateTags = jest.fn()
    const updateCombobox = jest.fn()
    const updateAutocomplete = jest.fn()
    const wrapper = shallow(VAutocomplete, {
      methods: {
        updateAutocomplete,
        updateCombobox,
        updateTags
      }
    })

    wrapper.vm.onEnterDown()

    expect(updateAutocomplete).toHaveBeenCalledTimes(1)

    wrapper.setProps({ combobox: true })

    wrapper.vm.onEnterDown()

    expect(updateCombobox).toHaveBeenCalledTimes(1)

    wrapper.setProps({
      combobox: false,
      tags: true
    })

    wrapper.vm.onEnterDown()

    expect(updateTags).toHaveBeenCalledTimes(1)
  })

  it('should select input text on focus', async () => {
    const wrapper = mount(VAutocomplete)
    const select = jest.fn()
    wrapper.vm.$refs.input.select = select

    const input = wrapper.first('input')
    input.trigger('focus')

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.isFocused).toBe(true)
    expect(select).toHaveBeenCalledTimes(1)

    input.trigger('keydown.tab')

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.isFocused).toBe(false)
    expect(select).toHaveBeenCalledTimes(1)
  })

  it('should not respond to click', () => {
    const onFocus = jest.fn()
    const wrapper = mount(VAutocomplete, {
      propsData: { disabled: true },
      methods: { onFocus }
    })
    const slot = wrapper.first('.v-input__slot')

    slot.trigger('click')

    expect(onFocus).not.toBeCalled()

    wrapper.setProps({ disabled: false, readonly: true })

    slot.trigger('click')

    expect(onFocus).not.toBeCalled()

    wrapper.setProps({ readonly: false })

    slot.trigger('click')

    expect(onFocus).toBeCalled()
  })

  it('should react to tabs', async () => {
    const selectListTile = jest.fn()
    const updateTags = jest.fn()
    const wrapper = mount(VAutocomplete, {
      propsData: {
        items: ['fizz', 'buzz'],
        tags: true
      },
      methods: {
        selectListTile,
        updateTags
      }
    })

    const input = wrapper.first('input')
    const menu = wrapper.first('.v-menu')
    const tile = wrapper.first('.v-list__tile')

    input.trigger('focus')
    input.element.value = 'foo'
    input.trigger('input')
    input.trigger('keydown.tab')

    expect(wrapper.vm.getMenuIndex()).toBe(-1)
    expect(updateTags).toBeCalled()

    wrapper.setProps({ tags: false })

    input.element.value = 'fizz'
    input.trigger('input')
    menu.trigger('keydown.down')

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

    expect(selectListTile).not.toBeCalled()

    input.trigger('keydown.tab')

    expect(selectListTile).toBeCalled()
  })

  it('should select list tile', () => {
    const wrapper = mount(VAutocomplete)
    const click = jest.fn()

    // Would normally be filled with actual tiles
    wrapper.vm.$refs.menu.tiles = [{ click }]

    wrapper.vm.selectListTile(2)

    expect(click).not.toBeCalled()

    wrapper.vm.selectListTile(0)

    expect(click).toBeCalled()
  })

  it('should react to keydown', () => {
    const activateMenu = jest.fn()
    const changeSelectedIndex = jest.fn()
    const onEscDown = jest.fn()
    const onTabDown = jest.fn()
    const wrapper = mount(VAutocomplete, {
      methods: {
        activateMenu,
        changeSelectedIndex,
        onEscDown,
        onTabDown
      }
    })

    const input = wrapper.first('input')

    expect(wrapper.vm.isMenuActive).toBe(false)

    input.trigger('keydown.enter')
    input.trigger('keydown.space')
    input.trigger('keydown.up')
    input.trigger('keydown.down')

    expect(activateMenu).toHaveBeenCalledTimes(4)

    input.trigger('keydown.esc')

    expect(onEscDown).toHaveBeenCalledTimes(1)

    input.trigger('keydown.tab')

    expect(onTabDown).toHaveBeenCalledTimes(1)

    // Skip menu activation
    wrapper.setData({ isMenuActive: true })

    input.element.value = 'foo'
    input.trigger('input')

    wrapper.setProps({ hideSelections: true })

    expect(wrapper.vm.genSelections()).toEqual([])
  })

  // https://github.com/vuetifyjs/vuetify/issues/3793
  it('should reset menu index after selection', async () => {
    const wrapper = shallow(VAutocomplete, {
      propsData: {
        items: ['foo', 'bar'],
        value: 'foo'
      }
    })

    expect(wrapper.vm.isMenuActive).toBe(false)
    const slot = wrapper.first('.v-input__slot')
    const item = wrapper.first('.v-list__tile')
    slot.trigger('click')

    expect(wrapper.vm.isMenuActive).toBe(true)

    expect(wrapper.vm.getMenuIndex()).toBe(-1)
  })

  it('should not remove a disabled item', () => {
    const wrapper = mount(VAutocomplete, {
      propsData: {
        chips: true,
        multiple: true,
        items: [
          { text: 'foo', value: 'foo', disabled: true },
          { text: 'bar', value: 'bar' },
        ],
        value: ['foo', 'bar']
      }
    })

    const chips = wrapper.find('.v-chip')
    const input = wrapper.first('input')

    expect(chips[0].element.classList.contains('v-chip--disabled')).toBe(true)

    input.trigger('focus')
    input.trigger('keydown.left')

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

    input.trigger('keydown.delete')

    expect(wrapper.vm.internalValue).toEqual(['foo'])

    input.trigger('keydown.delete')

    expect(wrapper.vm.internalValue).toEqual(['foo'])
  })

  it('should not filter results', async () => {
    const wrapper = mount(VAutocomplete, {
      propsData: {
        items: ['foo', 'bar']
      }
    })

    const input = wrapper.first('input')
    input.element.value = 'foo'
    input.trigger('input')

    expect(wrapper.vm.filteredItems.length).toBe(1)

    wrapper.setProps({ noFilter: true })

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.filteredItems.length).toBe(2)
  })

  it('should hide menu when no data', async () => {
    const wrapper = mount(VAutocomplete)

    const input = wrapper.first('input')
    input.trigger('focus')
    input.element.value = 'foo'
    input.trigger('input')

    expect(wrapper.vm.menuCanShow).toBe(true)

    wrapper.setProps({ hideNoData: true })

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.menuCanShow).toBe(false)

    wrapper.setProps({ hideNoData: false })

    await wrapper.vm.$nextTick()

    expect(wrapper.vm.menuCanShow).toBe(true)
  })
})


// For converting XYZ to sRGB
const srgbForwardMatrix = [
  [3.2406, -1.5372, -0.4986],
  [-0.9689, 1.8758, 0.0415],
  [0.0557, -0.2040, 1.0570]
]

// Forward gamma adjust
const srgbForwardTransform = C => (
  C <= 0.0031308
    ? C * 12.92
    : 1.055 * C ** (1 / 2.4) - 0.055
)

// For converting sRGB to XYZ
const srgbReverseMatrix = [
  [0.4124, 0.3576, 0.1805],
  [0.2126, 0.7152, 0.0722],
  [0.0193, 0.1192, 0.9505]
]

// Reverse gamma adjust
const srgbReverseTransform = C => (
  C <= 0.04045
    ? C / 12.92
    : ((C + 0.055) / 1.055) ** 2.4
)

function clamp (value) {
  return Math.max(0, Math.min(1, value))
}

export function fromXYZ (xyz) {
  const rgb = Array(3)
  const transform = srgbForwardTransform
  const matrix = srgbForwardMatrix

  // Matrix transform, then gamma adjustment
  for (let i = 0; i < 3; ++i) {
    rgb[i] = Math.round(clamp(transform(
      matrix[i][0] * xyz[0] +
      matrix[i][1] * xyz[1] +
      matrix[i][2] * xyz[2]
    )) * 255)
  }

  // Rescale back to [0, 255]
  return (rgb[0] << 16) + (rgb[1] << 8) + (rgb[2] << 0)
}

export function toXYZ (rgb) {
  const xyz = Array(3)
  const transform = srgbReverseTransform
  const matrix = srgbReverseMatrix

  // Rescale from [0, 255] to [0, 1] then adjust sRGB gamma to linear RGB
  const r = transform((rgb >> 16 & 0xff) / 255)
  const g = transform((rgb >> 8 & 0xff) / 255)
  const b = transform((rgb >> 0 & 0xff) / 255)

  // Matrix color space transform
  for (let i = 0; i < 3; ++i) {
    xyz[i] = matrix[i][0] * r + matrix[i][1] * g + matrix[i][2] * b
  }

  return xyz
}


import Ripple from '../directives/ripple'

/** @mixin */
export default {
  name: 'rippleable',

  directives: { Ripple },

  props: {
    ripple: {
      type: [Boolean, Object],
      default: true
    }
  },

  methods: {
    genRipple (data = {}) {
      if (!this.ripple) return null

      data.staticClass = 'v-input--selection-controls__ripple'

      if (this.rippleClasses) data.staticClass += ` ${this.rippleClasses}`

      data.directives = data.directives || []
      data.directives.push({
        name: 'ripple',
        value: this.ripple && !this.disabled && { center: true }
      })
      data.on = Object.assign({
        click: this.onChange
      }, this.$listeners)

      return this.$createElement('div', data)
    },
    onChange () {}
  }
}


keep keep keep keep keep replace replace keep keep keep keep

if (!this.isFocused) {
this.isFocused = true
this.$emit('focus', e)
}

// 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'

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

export default {
  name: 'v-text-field',

  extends: VInput,

  mixins: [Maskable],

  directives: { Ripple },

  inheritAttrs: false,

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

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

  computed: {
    classes () {
      return {
        'v-text-field': true,
        '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--box': this.box,
        'v-text-field--enclosed': this.isEnclosed,
        'v-text-field--reverse': this.reverse,
        'v-text-field--outline': this.hasOutline,
        'elevation-0': this.flat
      }
    },
    directivesInput () {
      return this.box
        ? [{ name: 'ripple', value: true }]
        : []
    },
    // 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.isSolo || this.hasOutline
    },
    isLabelActive () {
      return this.isDirty || dirtyTypes.includes(this.type)
    },
    isSingle () {
      return this.isSolo || this.singleLine
    },
    isSolo () {
      return this.solo || this.soloInverted
    }
  },

  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

      if (this.internalChange) this.internalChange = false
    }
  },

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

  methods: {
    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.$slots['append-outer-icon']) {
        slot.push(this.$slots['append-outer-icon'])
      } else if (this.appendOuterIcon) {
        slot.push(this.genIcon('appendOuter'))
      }

      return this.genSlot('append', 'outer', slot)
    },
    genCounter () {
      if (this.counter === false) return null

      const value = (this.internalValue || '').length
      const max = this.counter === true ? this.$attrs.maxlength : this.counter

      return this.$createElement(VCounter, {
        props: {
          value,
          max
        }
      })
    },
    genDefaultSlot () {
      return [
        this.genTextFieldSlot(),
        this.genIconSlot()
      ]
    },
    genLabel () {
      if (this.isSingle &&
        (this.isDirty || !!this.placeholder)
      ) return null

      const isSingleLine = this.isSingle
      let value = 0
      let left = 'auto'
      let right = 'auto'

      // Create spacing
      if ((this.prefix || this.reverse) &&
        (isSingleLine || !this.isFocused) &&
        !this.isDirty
      ) value = 16

      // Check if RTL
      if (this.$vuetify.rtl) right = value
      else left = value

      // Check if reversed
      if (this.reverse) {
        const direction = right
        right = left
        left = direction
      }

      const data = {
        props: {
          absolute: true,
          color: this.validationState,
          disabled: this.disabled,
          focused: !isSingleLine && (this.isFocused || !!this.validationState),
          left,
          right,
          value: Boolean(!isSingleLine &&
            (this.isFocused || this.isDirty || this.placeholder))
        }
      }

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

      return this.$createElement(VLabel, data, this.$slots.label || this.label)
    },
    genIconSlot () {
      const slot = []

      if (this.$slots['append']) {
        slot.push(this.$slots['append'])
      } else if (this.$slots['append-icon']) {
        slot.push(this.$slots['append-icon'])
      } else if (this.appendIcon) {
        slot.push(this.genIcon('append'))
      } else if (this.clearable && this.isDirty) {
        slot.push(this.genIcon('clear',
          this.clearIconCb || this.clearableCallback
        ))
      }

      return this.genSlot('append', 'inner', slot)
    },
    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,
          required: this.required,
          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 () {
      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()
      }

      this.isFocused = true
      this.$emit('focus', e)
    },
    onInput (e) {
      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)
    },
    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)
    }
  }
}


<!--
MAKE SURE TO READ THE CONTRIBUTING GUIDE BEFORE CREATING A PR
https://github.com/vuetifyjs/vuetify/blob/master/.github/CONTRIBUTING.md
-->

<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->

## Motivation and Context
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->

## How Has This Been Tested?
<!--- Please describe how you tested your changes. -->
<!--- Have you created new tests or updated existing ones? -->
<!--- e.g. unit | visually | e2e | none -->

## Markup:
<!--- Paste markup that showcases your contribution --->
<!--- You can use ```vue to style the code -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Improvement/refactoring (non-breaking change that doesn't add any feature but make things better)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] The PR title is no longer than 64 characters.
- [ ] The PR is submitted to the correct branch (`master` for bug fixes, `dev` for new features and breaking changes).
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have created a PR in the [documentation](https://github.com/vuetifyjs/vuetifyjs.com) with the necessary changes.


import { test } from '@/test'
import { consoleWarn, consoleError } from '@/util/console'

test('console.js', () => {
  it('should generate a warning', () => {
    consoleWarn('foo')
    expect('[Vuetify] foo').toHaveBeenTipped()

    consoleWarn('bar', { $options: { name: 'baz' } })
    expect('[Vuetify] bar in "baz"').toHaveBeenTipped()
  })

  it('should generate an error', () => {
    consoleError('foo')
    expect('[Vuetify] foo').toHaveBeenWarned()

    consoleError('bar', { $options: { name: 'baz' } })
    expect('[Vuetify] bar in "baz"').toHaveBeenWarned()
  })
})


function inserted (el, binding) {
  const callback = binding.value
  const options = binding.options || { passive: true }

  window.addEventListener('resize', callback, options)
  el._onResize = {
    callback,
    options
  }

  if (!binding.modifiers || !binding.modifiers.quiet) {
    callback()
  }
}

function unbind (el) {
  const { callback, options } = el._onResize

  window.removeEventListener('resize', callback, options)
  delete el._onResize
}

export default {
  name: 'resize',
  inserted,
  unbind
}


keep keep keep keep replace replace replace replace keep keep keep keep

import OurVue from 'vue'

import application from './mixins/application'
import breakpoint from './mixins/breakpoint'
import theme from './mixins/theme'
import icons from './mixins/icons'
import options from './mixins/options'
import genLang from './mixins/lang'
import goTo from './util/goTo'

// Utils
import { consoleWarn, consoleError } from '../../util/console'

// Types
import { VueConstructor } from 'vue/types'
import { Vuetify as VuetifyPlugin, VuetifyUseOptions } from 'types'

const Vuetify: VuetifyPlugin = {
  install (Vue, opts = {}) {
    if ((this as any).installed) return
    ;(this as any).installed = true

    if (OurVue !== Vue) {
      consoleError('Multiple instances of Vue detected\nSee https://github.com/vuetifyjs/vuetify/issues/4068\n\nIf you\'re seeing "$attrs is readonly", it\'s caused by this')
    }

    if (OurVue !== Vue) {
      consoleError('Multiple instances of Vue detected\nSee https://github.com/vuetifyjs/vuetify/issues/4068\n\nIf you\'re seeing "$attrs is readonly", it\'s caused by this')
    }

    checkVueVersion(Vue)

    const lang = genLang(opts.lang)

    Vue.prototype.$vuetify = new Vue({
      mixins: [
        breakpoint
      ],
      data: {
        application,
        dark: false,
        icons: icons(opts.iconfont, opts.icons),
        lang,
        options: options(opts.options),
        rtl: opts.rtl,
        theme: theme(opts.theme)
      },
      methods: {
        goTo,
        t: lang.t.bind(lang)
      }
    })

    if (opts.directives) {
      Object.values(opts.directives).forEach(directive => {
        Vue.directive(directive.name, directive)
      })
    }

    (function registerComponents (components: VuetifyUseOptions['components']) {
      if (components) {
        for (const key in components) {
          const component = components[key]
          if (!registerComponents(component.$_vuetify_subcomponents)) {
            Vue.component(key, component)
          }
        }
        return true
      }
      return false
    })(opts.components)
  },
  version: __VUETIFY_VERSION__
}

export function checkVueVersion (Vue: VueConstructor, requiredVue?: string) {
  const vueDep = requiredVue || __REQUIRED_VUE__

  const required = vueDep.split('.', 3).map(v => v.replace(/\D/g, '')).map(Number)
  const actual = Vue.version.split('.', 3).map(n => parseInt(n, 10))

  // Simple semver caret range comparison
  const passes =
    actual[0] === required[0] && // major matches
    (actual[1] > required[1] || // minor is greater
      (actual[1] === required[1] && actual[2] >= required[2]) // or minor is eq and patch is >=
    )

  if (!passes) {
    consoleWarn(`Vuetify requires Vue version ${vueDep}`)
  }
}

export default Vuetify


import VAvatar from './VAvatar'

export { VAvatar }
export default VAvatar


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

/** Theme */
selection-control($material)
  &.v-input--is-disabled
    .v-icon
      // needed for helper override
      color: $material.buttons.disabled !important

v-selection-control-rtl()
  .v-input--selection-controls__input
    margin-right: 0
    margin-left: 8px

theme(selection-control, "v-input--selection-controls")
rtl(v-selection-control-rtl, "v-input--selection-controls")

.v-input--selection-controls
  margin-top: $input-top-spacing
  padding-top: 4px

  .v-input__append-outer,
  .v-input__prepend-outer
    margin-top: 0
    margin-bottom: 0

  .v-input__slot
    margin-bottom: 12px

  &__input
    color: inherit
    display: inline-flex
    flex: 0 0 auto
    height: 24px
    position: relative
    margin-right: 8px
    transition: color .2s cubic-bezier(.25,.8,.25,1), transform .3s cubic-bezier(.25,.8,.25,1)
    width: 24px
    user-select: none

    input
      position: absolute
      opacity: 0

    // Sibling selector to avoid targeting v-radio-group's label
    & + .v-label
      user-select: none
      cursor: pointer

  &__ripple
    cursor: pointer
    height: 48px
    position: absolute
    transition: inherit
    width: 48px
    left: -12px
    top: calc(50% - 24px)

    &:before
      border-radius: 50%
      bottom: 0
      content: ''
      position: absolute
      opacity: .2
      left: 0
      right: 0
      top: 0
      transform-origin: center center
      transform: scale(0.2)
      transition: inherit

  &.v-input .v-label
    align-items: center
    display: inline-flex
    top: 0
    height: auto

  &.v-input--is-focused,
  .v-radio--is-focused
    .v-input--selection-controls__ripple:before
      background: currentColor
      transform: scale(.8)


vuetify

cross-env TARGET=development webpack --config build/config.js --progress --hide-modules --watch

cross-env NODE_ENV=development webpack-dev-server --config build/webpack.dev.config.js --hot

concurrently "yarn build:dist --no-progress" "yarn build:es5" -n"dist,es5" --kill-others-on-fail -r

cross-env NODE_ENV=production node build/webpack.dev.config.js

rimraf dist && cross-env NODE_ENV=production webpack --config build/config.js --progress --hide-modules

rimraf es5 es5-temp && tsc -p tsconfig.dist.json && cross-env NODE_ENV=es5 babel es5-temp --out-dir es5 --source-maps -q

node --inspect --debug-brk build/config.js

cross-env NODE_ENV=test node --inspect --inspect-brk ./node_modules/jest/bin/jest.js --no-cache -i --verbose

concurrently "tsc --noEmit --pretty" "tslint -p . -c .tslint.js src/**/*.ts -t stylish" "eslint --ext .js,.ts src" "yarn test:types" --kill-others-on-fail -n "tsc,tslint,eslint,types"

node dev/warn-npm-install.js && concurrently "yarn test" "yarn lint" -n "test,lint" --kill-others-on-fail

Vue.js 2 Semantic Component Framework


(function registerComponents (components: VuetifyUseOptions['components']) {

keep keep keep keep keep replace replace replace keep keep keep keep keep

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

exports[`full import should register all subcomponents 1`] = `
Array [
  "KeepAlive",
  "Transition",
  "TransitionGroup",
  "VAlert",
  "VApp",
  "VAutocomplete",
  "VAvatar",
  "VBadge",
  "VBottomNav",
  "VBottomSheet",
  "VBottomSheetTransition",
  "VBreadcrumbs",
  "VBreadcrumbsItem",
  "VBtn",
  "VBtnToggle",
  "VCard",
  "VCardActions",
  "VCardMedia",
  "VCardText",
  "VCardTitle",
  "VCarousel",
  "VCarouselItem",
  "VCarouselReverseTransition",
  "VCarouselTransition",
  "VCheckbox",
  "VChip",
  "VCombobox",
  "VContainer",
  "VContent",
  "VCounter",
  "VDataIterator",
  "VDataTable",
  "VDatePicker",
  "VDatePickerDateTable",
  "VDatePickerHeader",
  "VDatePickerMonthTable",
  "VDatePickerTitle",
  "VDatePickerYears",
  "VDialog",
  "VDialogBottomTransition",
  "VDialogTransition",
  "VDivider",
  "VEditDialog",
  "VExpandTransition",
  "VExpansionPanel",
  "VExpansionPanelContent",
  "VFabTransition",
  "VFadeTransition",
  "VFlex",
  "VFooter",
  "VForm",
  "VHover",
  "VIcon",
  "VImg",
  "VInput",
  "VJumbotron",
  "VLabel",
  "VLayout",
  "VList",
  "VListGroup",
  "VListTile",
  "VListTileAction",
  "VListTileActionText",
  "VListTileAvatar",
  "VListTileContent",
  "VListTileSubTitle",
  "VListTileTitle",
  "VMenu",
  "VMenuTransition",
  "VMessages",
  "VNavigationDrawer",
  "VOverflowBtn",
  "VPagination",
  "VParallax",
  "VPicker",
  "VProgressCircular",
  "VProgressLinear",
  "VRadio",
  "VRadioGroup",
  "VRangeSlider",
  "VRating",
  "VResponsive",
  "VRowExpandTransition",
  "VScaleTransition",
  "VScrollXReverseTransition",
  "VScrollXTransition",
  "VScrollYReverseTransition",
  "VScrollYTransition",
  "VSelect",
  "VSlideXReverseTransition",
  "VSlideXTransition",
  "VSlideYReverseTransition",
  "VSlideYTransition",
  "VSlider",
  "VSnackbar",
  "VSpacer",
  "VSpeedDial",
  "VStepper",
  "VStepperContent",
  "VStepperHeader",
  "VStepperItems",
  "VStepperStep",
  "VSubheader",
  "VSwitch",
  "VSystemBar",
  "VTab",
  "VTabItem",
  "VTabReverseTransition",
  "VTabTransition",
  "VTableOverflow",
  "VTabs",
  "VTabsItems",
  "VTabsSlider",
  "VTextField",
  "VTextarea",
  "VTimePicker",
  "VTimePickerClock",
  "VTimePickerTitle",
  "VToolbar",
  "VToolbarItems",
  "VToolbarSideIcon",
  "VToolbarTitle",
  "VTooltip",
]
`;


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

// Extensions
import VSelect from '../VSelect/VSelect'
import VAutocomplete from '../VAutocomplete/VAutocomplete'

// Utils
import { keyCodes } from '../../util/helpers'

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

  extends: VAutocomplete,

  props: {
    delimiters: {
      type: Array,
      default: () => ([])
    },
    returnObject: {
      type: Boolean,
      default: true
    }
  },

  data: () => ({
    editingIndex: -1
  }),

  computed: {
    counterValue () {
      return this.multiple
        ? this.selectedItems.length
        : (this.internalSearch || '').toString().length
    },
    hasSlot () {
      return VSelect.computed.hasSlot.call(this) || this.multiple
    },
    isAnyValueAllowed () {
      return true
    },
    menuCanShow () {
      if (!this.isFocused) return false

      return (this.displayedItemsCount > 0) ||
        (!!this.$slots['no-data'] && !this.hideNoData)
    }
  },

  methods: {
    onFilteredItemsChanged () {
      // nop
    },
    onInternalSearchChanged (val) {
      if (
        val &&
        this.multiple &&
        this.delimiters
      ) {
        const delimiter = this.delimiters.find(d => val.endsWith(d))
        if (delimiter == null) return

        this.internalSearch = val.slice(0, val.length - delimiter.length)
        this.updateTags()
      }

      this.updateMenuDimensions()
    },
    genChipSelection (item, index) {
      const chip = VSelect.methods.genChipSelection.call(this, item, index)

      // Allow user to update an existing value
      if (this.multiple) {
        chip.componentOptions.listeners.dblclick = () => {
          this.editingIndex = index
          this.internalSearch = this.getText(item)
          this.selectedIndex = -1
        }
      }

      return chip
    },
    onChipInput (item) {
      VSelect.methods.onChipInput.call(this, item)

      this.editingIndex = -1
    },
    // Requires a manual definition
    // to overwrite removal in v-autocomplete
    onEnterDown () {
      this.updateSelf()
      VSelect.methods.onEnterDown.call(this)
    },
    onKeyDown (e) {
      const keyCode = e.keyCode

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

      // If user is at selection index of 0
      // create a new tag
      if (this.multiple &&
        keyCode === keyCodes.left &&
        this.$refs.input.selectionStart === 0
      ) {
        this.updateSelf()
      }

      // 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) {
      // When adding tags, if searching and
      // there is not a filtered options,
      // add the value to the tags list
      if (this.multiple &&
        this.internalSearch &&
        this.getMenuIndex() === -1
      ) {
        e.preventDefault()
        e.stopPropagation()

        return this.updateTags()
      }

      VAutocomplete.methods.onTabDown.call(this, e)
    },
    selectItem (item) {
      // Currently only supports items:<string[]>
      if (this.editingIndex > -1) {
        this.updateEditing()
      } else {
        VSelect.methods.selectItem.call(this, item)
      }

      this.setSearch()
    },
    setSelectedItems () {
      if (this.internalValue == null ||
        this.internalValue === ''
      ) {
        this.selectedItems = []
      } else {
        this.selectedItems = this.multiple ? this.internalValue : [this.internalValue]
      }
    },
    updateEditing () {
      const value = this.internalValue.slice()
      value[this.editingIndex] = this.internalSearch

      this.internalValue = value

      this.editingIndex = -1
    },
    updateCombobox () {
      const isUsingSlot = Boolean(this.$scopedSlots.selection) || this.hasChips

      // If search is not dirty and is
      // using slot, do nothing
      if (isUsingSlot && !this.searchIsDirty) return

      // The internal search is not matching
      // the internal value, update the input
      if (this.internalSearch !== this.getText(this.internalValue)) this.setValue()

      // Reset search if using slot
      // to avoid a double input
      if (isUsingSlot) this.internalSearch = undefined
    },
    updateSelf () {
      this.multiple ? this.updateTags() : this.updateCombobox()
    },
    updateTags () {
      const menuIndex = this.getMenuIndex()

      // If the user is not searching
      // and no menu item is selected
      // do nothing
      if (menuIndex < 0 &&
        !this.searchIsDirty
      ) return

      if (this.editingIndex > -1) {
        return this.updateEditing()
      }

      const index = this.selectedItems.indexOf(this.internalSearch)
      // If it already exists, do nothing
      // this might need to change to bring
      // the duplicated item to the last entered
      if (index > -1) {
        this.internalValue.splice(index, 1)
      }

      // If menu index is greater than 1
      // the selection is handled elsewhere
      // TODO: find out where
      if (menuIndex > -1) return (this.internalSearch = null)

      this.selectItem(this.internalSearch)
      this.internalSearch = null
    }
  }
}


---
name: Other
about: Questions, documentation, or anything else
---

<!--
The issue list of this repo is EXCLUSIVELY for bug reports and feature requests.
For general questions, please join Discord. You can also check Reddit or Stackoverflow.
Anything to do with the documentation should be reported on the vuetifyjs.com repo.

Discord: https://community.vuetifyjs.com
Reddit: https://www.reddit.com/r/vuetifyjs/
Stackoverflow: https://stackoverflow.com/questions/tagged/vuetify.js
Documentation issues: https://github.com/vuetifyjs/vuetifyjs.com/issues
-->


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

// Components
import VIcon from '../VIcon'
import VLabel from '../VLabel'
import VMessages from '../VMessages'

// Mixins
import Colorable from '../../mixins/colorable'
import Loadable from '../../mixins/loadable'
import Themeable from '../../mixins/themeable'
import Validatable from '../../mixins/validatable'

// Utilities
import {
  convertToUnit,
  kebabCase
} from '../../util/helpers'
import { deprecate } from '../../util/console'

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

  mixins: [
    Colorable,
    Loadable,
    Themeable,
    Validatable
  ],

  props: {
    appendIcon: String,
    /** @deprecated */
    appendIconCb: Function,
    backgroundColor: {
      type: String,
      default: ''
    },
    disabled: Boolean,
    height: [Number, String],
    hideDetails: Boolean,
    hint: String,
    label: String,
    persistentHint: Boolean,
    prependIcon: String,
    /** @deprecated */
    prependIconCb: Function,
    readonly: Boolean,
    value: { required: false }
  },

  data: vm => ({
    lazyValue: vm.value,
    isFocused: false
  }),

  computed: {
    classesInput () {
      return {
        ...this.classes,
        'v-input--has-state': this.hasState,
        'v-input--hide-details': this.hideDetails,
        'v-input--is-label-active': this.isLabelActive,
        'v-input--is-dirty': this.isDirty,
        'v-input--is-disabled': this.disabled,
        'v-input--is-focused': this.isFocused,
        'v-input--is-loading': this.loading !== false,
        'v-input--is-readonly': this.readonly,
        ...this.themeClasses
      }
    },
    directivesInput () {
      return []
    },
    hasHint () {
      return !this.hasMessages &&
        this.hint &&
        (this.persistentHint || this.isFocused)
    },
    hasLabel () {
      return Boolean(this.$slots.label || this.label)
    },
    // Proxy for `lazyValue`
    // This allows an input
    // to function without
    // a provided model
    internalValue: {
      get () {
        return this.lazyValue
      },
      set (val) {
        this.lazyValue = val
        this.$emit(this.$_modelEvent, val)
      }
    },
    isDirty () {
      return !!this.lazyValue
    },
    isDisabled () {
      return Boolean(this.disabled || this.readonly)
    },
    isLabelActive () {
      return this.isDirty
    }
  },

  watch: {
    value (val) {
      this.lazyValue = val
    }
  },

  beforeCreate () {
    // v-radio-group needs to emit a different event
    // https://github.com/vuetifyjs/vuetify/issues/4752
    this.$_modelEvent = (this.$options.model && this.$options.model.event) || 'input'
  },

  methods: {
    genContent () {
      return [
        this.genPrependSlot(),
        this.genControl(),
        this.genAppendSlot()
      ]
    },
    genControl () {
      return this.$createElement('div', {
        staticClass: 'v-input__control'
      }, [
        this.genInputSlot(),
        this.genMessages()
      ])
    },
    genDefaultSlot () {
      return [
        this.genLabel(),
        this.$slots.default
      ]
    },
    // TODO: remove shouldDeprecate (2.0), used for clearIcon
    genIcon (type, cb, shouldDeprecate = true) {
      const icon = this[`${type}Icon`]
      const eventName = `click:${kebabCase(type)}`
      cb = cb || this[`${type}IconCb`]

      if (shouldDeprecate && type && cb) {
        deprecate(`:${type}-icon-cb`, `@${eventName}`, this)
      }

      const data = {
        props: {
          color: this.validationState,
          dark: this.dark,
          disabled: this.disabled,
          light: this.light
        },
        on: !(this.$listeners[eventName] || cb)
          ? null
          : {
            click: e => {
              e.preventDefault()
              e.stopPropagation()

              this.$emit(eventName, e)
              cb && cb(e)
            },
            // Container has mouseup event that will
            // trigger menu open if enclosed
            mouseup: e => {
              e.preventDefault()
              e.stopPropagation()
            }
          }
      }

      return this.$createElement('div', {
        staticClass: `v-input__icon v-input__icon--${kebabCase(type)}`,
        key: `${type}${icon}`
      }, [
        this.$createElement(
          VIcon,
          data,
          icon
        )
      ])
    },
    genInputSlot () {
      return this.$createElement('div', this.setBackgroundColor(this.backgroundColor, {
        staticClass: 'v-input__slot',
        style: { height: convertToUnit(this.height) },
        directives: this.directivesInput,
        on: {
          click: this.onClick,
          mousedown: this.onMouseDown,
          mouseup: this.onMouseUp
        },
        ref: 'input-slot'
      }), [
        this.genDefaultSlot(),
        this.genProgress()
      ])
    },
    genLabel () {
      if (!this.hasLabel) return null

      return this.$createElement(VLabel, {
        props: {
          color: this.validationState,
          dark: this.dark,
          focused: this.hasState,
          for: this.$attrs.id,
          light: this.light
        }
      }, this.$slots.label || this.label)
    },
    genMessages () {
      if (this.hideDetails) return null

      const messages = this.hasHint
        ? [this.hint]
        : this.validations

      return this.$createElement(VMessages, {
        props: {
          color: this.hasHint ? '' : this.validationState,
          dark: this.dark,
          light: this.light,
          value: (this.hasMessages || this.hasHint) ? messages : []
        }
      })
    },
    genSlot (type, location, slot) {
      if (!slot.length) return null

      const ref = `${type}-${location}`

      return this.$createElement('div', {
        staticClass: `v-input__${ref}`,
        ref
      }, slot)
    },
    genPrependSlot () {
      const slot = []

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

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

      // Append icon for text field was really
      // an appended inner icon, v-text-field
      // will overwrite this method in order to obtain
      // backwards compat
      if (this.$slots['append']) {
        slot.push(this.$slots['append'])
      } else if (this.appendIcon) {
        slot.push(this.genIcon('append'))
      }

      return this.genSlot('append', 'outer', slot)
    },
    onClick (e) {
      this.$emit('click', e)
    },
    onMouseDown (e) {
      this.$emit('mousedown', e)
    },
    onMouseUp (e) {
      this.$emit('mouseup', e)
    }
  },

  render (h) {
    return h('div', this.setTextColor(this.validationState, {
      staticClass: 'v-input',
      attrs: this.attrsInput,
      'class': this.classesInput
    }), this.genContent())
  }
}


import { VueConstructor, DirectiveOptions } from 'vue/types'

import ClickOutside from './click-outside'

import Vue from 'vue'
import Vuetify, { checkVueVersion } from '@/components/Vuetify'
import { test } from '@/test'

test('Vuetify.install.js', () => {
  it('should register components and directives', async () => {
    const { component, directive, use } = Vue

    Vue.component = jest.fn()
    Vue.directive = jest.fn()
    Vue.use = jest.fn()

    Vuetify.installed = false
    Vuetify.install(Vue, {
      components: {
        OneComponent: {},
        ComponentPack: { $_vuetify_subcomponents: { HisChild: {} } }
      },
      directives: {
        directive: {
          name: 'foobarbaz'
        }
      }
    })

    expect(Vue.use.mock.calls).toEqual([])
    expect(Vue.directive.mock.calls).toEqual([
      ['foobarbaz', { name: 'foobarbaz' }]
    ])
    expect(Vue.component.mock.calls).toEqual([
      ['OneComponent', {}],
      ['HisChild', {}]
    ])

    Vue.use = jest.fn()
    Vuetify.install(Vue, {
      components: {
        component: 'component'
      }
    })
    expect(Vue.use).not.toBeCalled()

    Object.assign(Vue, { component, directive, use })
  })

  describe('should warn about an unsupported version of Vue', () => {
    it('older version', () => {
      checkVueVersion({ version: '2.5.0' }, '^2.5.10')
      expect('Vuetify requires Vue version ^2.5.10').toHaveBeenTipped()
    })

    it('newer version', () => {
      checkVueVersion({ version: '2.5.12' }, '^2.5.10')
    })

    it('newer, prerelease version', () => {
      checkVueVersion({ version: '2.5.17-beta.0' }, '^2.5.10')
    })
  })
})


// Mixins
import {
  provide as RegistrableProvide
} from '../../mixins/registrable'

// Directives
import Touch from '../../directives/touch'

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

  directives: { Touch },

  mixins: [RegistrableProvide('tabs')],

  inject: {
    registerItems: {
      default: null
    },
    tabProxy: {
      default: null
    },
    unregisterItems: {
      default: null
    }
  },

  props: {
    cycle: Boolean,
    touchless: Boolean,
    value: [Number, String]
  },

  data () {
    return {
      items: [],
      lazyValue: this.value,
      reverse: false
    }
  },

  computed: {
    activeIndex () {
      return this.items.findIndex((item, index) => {
        return item.id === this.lazyValue ||
          index === this.lazyValue
      })
    },
    activeItem () {
      if (!this.items.length) return undefined

      return this.items[this.activeIndex]
    },
    inputValue: {
      get () {
        return this.lazyValue
      },
      set (val) {
        this.lazyValue = val

        if (this.tabProxy) this.tabProxy(val)
        else this.$emit('input', val)
      }
    }
  },

  watch: {
    activeIndex (current, previous) {
      this.reverse = current < previous
      this.updateItems()
    },
    value (val) {
      this.lazyValue = val
    }
  },

  mounted () {
    this.registerItems && this.registerItems(this.changeModel)
  },

  beforeDestroy () {
    this.unregisterItems && this.unregisterItems()
  },

  methods: {
    changeModel (val) {
      this.inputValue = val
    },
    next (cycle) {
      let nextIndex = this.activeIndex + 1

      if (!this.items[nextIndex]) {
        if (!cycle) return
        nextIndex = 0
      }

      this.inputValue = this.items[nextIndex].id || nextIndex
    },
    prev (cycle) {
      let prevIndex = this.activeIndex - 1

      if (!this.items[prevIndex]) {
        if (!cycle) return
        prevIndex = this.items.length - 1
      }

      this.inputValue = this.items[prevIndex].id || prevIndex
    },
    onSwipe (action) {
      this[action](this.cycle)
    },
    register (item) {
      this.items.push(item)
    },
    unregister (item) {
      this.items = this.items.filter(i => i !== item)
    },
    updateItems () {
      for (let index = this.items.length; --index >= 0;) {
        this.items[index].toggle(this.activeIndex === index, this.reverse, this.isBooted)
      }
      this.isBooted = true
    }
  },

  render (h) {
    const data = {
      staticClass: 'v-tabs__items',
      directives: []
    }

    !this.touchless && data.directives.push({
      name: 'touch',
      value: {
        left: () => this.onSwipe('next'),
        right: () => this.onSwipe('prev')
      }
    })

    return h('div', data, this.$slots.default)
  }
}


import VBreadcrumbs from './VBreadcrumbs'
import VBreadcrumbsItem from './VBreadcrumbsItem'

export { VBreadcrumbs, VBreadcrumbsItem }

export default {
  $_vuetify_subcomponents: {
    VBreadcrumbs,
    VBreadcrumbsItem
  }
}


import './stylus/app.styl'
import VuetifyComponent from './components/Vuetify'
import * as components from './components'
import * as directives from './directives'
import { VueConstructor } from 'vue'
import { Vuetify as VuetifyPlugin, VuetifyUseOptions } from 'types'

const Vuetify: VuetifyPlugin = {
  install (Vue: VueConstructor, args?: VuetifyUseOptions): void {
    Vue.use(VuetifyComponent, {
      components,
      directives,
      ...args
    })
  },
  version: __VUETIFY_VERSION__
}

if (typeof window !== 'undefined' && window.Vue) {
  window.Vue.use(Vuetify)
}

export default Vuetify


keep keep keep replace replace replace replace replace replace replace replace

import { VueConstructor, DirectiveOptions } from 'vue/types'
import ClickOutside from './click-outside'
import Resize from './resize'
import Ripple from './ripple'
import Scroll from './scroll'
import Touch from './touch'

export {
  ClickOutside,
  Ripple,
  Resize,
  Scroll,
  Touch
}

export default function install (Vue: VueConstructor) {
  Vue.directive('click-outside', ClickOutside as any as DirectiveOptions)
  Vue.directive('ripple', Ripple as any as DirectiveOptions)
  Vue.directive('resize', Resize as any as DirectiveOptions)
  Vue.directive('scroll', Scroll as any as DirectiveOptions)
  Vue.directive('touch', Touch as any as DirectiveOptions)
}


import '../../stylus/components/_jumbotrons.styl'

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

// Utils
import { deprecate } from '../../util/console'

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

  mixins: [
    Colorable,
    Routable,
    Themeable
  ],

  props: {
    gradient: String,
    height: {
      type: [Number, String],
      default: '400px'
    },
    src: String,
    tag: {
      type: String,
      default: 'div'
    }
  },

  computed: {
    backgroundStyles () {
      const styles = {}

      if (this.gradient) {
        styles.background = `linear-gradient(${this.gradient})`
      }

      return styles
    },
    classes () {
      return this.themeClasses
    },
    styles () {
      return {
        height: this.height
      }
    }
  },

  mounted () {
    deprecate('v-jumbotron', this.src ? 'v-img' : 'v-responsive', this)
  },

  methods: {
    genBackground () {
      return this.$createElement('div', this.setBackgroundColor(this.color, {
        staticClass: 'v-jumbotron__background',
        style: this.backgroundStyles
      }))
    },
    genContent () {
      return this.$createElement('div', {
        staticClass: 'v-jumbotron__content'
      }, this.$slots.default)
    },
    genImage () {
      if (!this.src) return null
      if (this.$slots.img) return this.$slots.img({ src: this.src })

      return this.$createElement('img', {
        staticClass: 'v-jumbotron__image',
        attrs: { src: this.src }
      })
    },
    genWrapper () {
      return this.$createElement('div', {
        staticClass: 'v-jumbotron__wrapper'
      }, [
        this.genImage(),
        this.genBackground(),
        this.genContent()
      ])
    }
  },

  render (h) {
    const { tag, data } = this.generateRouteLink(this.classes)
    data.staticClass = 'v-jumbotron'
    data.style = this.styles

    return h(tag, data, [this.genWrapper()])
  }
}


import VDatePicker from './VDatePicker'
import VDatePickerTitle from './VDatePickerTitle'
import VDatePickerHeader from './VDatePickerHeader'
import VDatePickerDateTable from './VDatePickerDateTable'
import VDatePickerMonthTable from './VDatePickerMonthTable'
import VDatePickerYears from './VDatePickerYears'

export {
  VDatePicker,
  VDatePickerTitle,
  VDatePickerHeader,
  VDatePickerDateTable,
  VDatePickerMonthTable,
  VDatePickerYears
}

export default {
  $_vuetify_subcomponents: {
    VDatePicker,
    VDatePickerTitle,
    VDatePickerHeader,
    VDatePickerDateTable,
    VDatePickerMonthTable,
    VDatePickerYears
  }
}


/* @vue/component */
export default {
  methods: {
    genTFoot () {
      if (!this.$slots.footer) {
        return null
      }

      const footer = this.$slots.footer
      const row = this.hasTag(footer, 'td') ? this.genTR(footer) : footer

      return this.$createElement('tfoot', [row])
    },
    genActionsFooter () {
      if (this.hideActions) {
        return null
      }

      return this.$createElement('div', {
        'class': this.classes
      }, this.genActions())
    }
  }
}


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

exports[`full import should register all subcomponents 1`] = `

exports[`full import should register all directives 1`] = `


export default {
  dataIterator: {
    rowsPerPageText: 'Itens por página:',
    rowsPerPageAll: 'Todos',
    pageText: '{0}-{1} de {2}',
    noResultsText: 'Nenhum dado encontrado',
    nextPage: 'Próxima página',
    prevPage: 'Página anterior'
  },
  dataTable: {
    rowsPerPageText: 'Linhas por página:'
  },
  noDataText: 'Não há dados disponíveis'
}


# <type>: (If applied, this commit will...) <subject> (Max 50 char)
# |<----  Using a Maximum Of 50 Characters  ---->| Hard limit to 72 -->|

# Explain why this change is being made
# |<----   Try To Limit Each Line to a Maximum Of 72 Characters   ---->|

# Provide links to any relevant issues, articles, commits, or other
# pull requests
# Example: See #23, fixes #58

# --- COMMIT END ---
# <type> can be
#    feat     (new feature)
#    fix      (bug fix)
#    refactor (refactoring production code)
#    style    (formatting, missing semi colons, etc; no code change)
#    test     (adding or refactoring tests; no production code change)
#    chore    (updating npm scripts etc; no production code change)
# --------------------
# Remember to
#    Capitalize the subject line
#    Use the imperative mood in the subject line
#    Do not end the subject line with a period
#    Separate subject from body with a blank line (comments don't count)
#    Use the body to explain what and why vs. how
#    Can use multiple lines with "-" for bullet points in body
#
# If you can't summarize your changes in a single line, they should
# probably be split into multiple commits
# --------------------
# For more information about this template, check out
# https://gist.github.com/adeekshith/cd4c95a064977cdc6c50


.v-autocomplete
  &.v-input,
  .v-menu__activator,
  .v-menu__activator *
    cursor: text

  input
    align-self: center

  &--is-selecting-index
    input
      opacity: 0

  // When a single select, does not have
  // selections padding
  &.v-text-field--enclosed:not(.v-text-field--solo)
    .v-select__slot > input
      margin-top: 24px

  &:not(.v-input--is-disabled).v-select.v-text-field
    input
      pointer-events: auto

  &__content.v-menu__content
    border-radius: 0

    .v-card
      border-radius: 0


const registeredComponents = Object.keys(Vue.options.components).sort()

it('should register all subcomponents', () => {

it('should register all names in PascalCase', () => {

keep keep keep keep add keep keep keep keep keep

const registeredDirectives = Object.keys(Vue.options.directives).sort()


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

/** Theme */
v-input--switch($material)
  &__thumb
    color: $material.selection-controls.thumb.inactive

  &__track
    color: $material.selection-controls.track.inactive

  &.v-input--is-disabled
    .v-input--switch__thumb
      color: $material.selection-controls.thumb.disabled !important

    .v-input--switch__track
      color: $material.selection-controls.track.disabled !important

v-input--switch-rtl()
  .v-input--selection-controls__ripple
    left: auto
    right: -14px

  &.v-input--is-dirty
    .v-input--selection-controls__ripple,
    .v-input--switch__thumb
      transform: translate(-16px, 0)

theme(v-input--switch, "v-input--switch")
rtl(v-input--switch-rtl, "v-input--switch")

.v-input--switch
  &__track,
  &__thumb
    background-color: currentColor
    pointer-events: none
    transition: inherit

  &__track
    border-radius: 8px
    height: 14px
    left: 2px
    opacity: .6
    position: absolute
    right: 2px
    top: calc(50% - 7px)

  &__thumb
    border-radius: 50%
    top: calc(50% - 10px)
    height: 20px
    position: relative
    width: 20px
    elevation(4)

  .v-input--selection-controls__input
    width: 38px

  .v-input--selection-controls__ripple
    left: -14px
    top: calc(50% - 24px)

  &.v-input--is-dirty
    .v-input--selection-controls__ripple,
    .v-input--switch__thumb
      transform: translate(16px, 0)


.v-ripple
  &__container
    color: inherit
    border-radius: inherit
    position: absolute
    width: 100%
    height: 100%
    left: 0
    top: 0
    overflow: hidden
    z-index: 0
    pointer-events: none
    contain: strict

  &__animation
    color: inherit
    position: absolute
    top: 0
    left: 0
    border-radius: 50%
    background: currentColor
    opacity: 0
    transition: $ripple-animation-transition
    pointer-events: none
    overflow: hidden
    will-change: transform, opacity

    &--enter
      transition: none

    &--visible
      opacity: $ripple-animation-visible-opacity


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

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

    <button type="button"
            class="v-btn v-btn--icon theme--dark"
    >

          chevron_left

    </button>

    <button type="button"
            class="v-btn v-btn--icon theme--dark"
    >

          chevron_right

    </button>

`;

exports[`VCarousel.js should render component in RTL mode and match snapshot 1`] = `

    <button type="button"
            class="v-btn v-btn--icon theme--dark"
    >

          chevron_right

    </button>

    <button type="button"
            class="v-btn v-btn--icon theme--dark"
    >

          chevron_left

    </button>

`;

exports[`VCarousel.js should render component with custom duration between image cycles and match snapshot 1`] = `

    <button type="button"
            class="v-btn v-btn--icon theme--dark"
    >

          chevron_left

    </button>

    <button type="button"
            class="v-btn v-btn--icon theme--dark"
    >

          chevron_right

    </button>

`;

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

    <button type="button"
            class="v-btn v-btn--icon theme--dark"
    >

          chevron_left

    </button>

    <button type="button"
            class="v-btn v-btn--icon theme--dark"
    >

          chevron_right

    </button>

    <button type="button"
            class="v-btn v-btn--icon v-btn--small theme--dark v-carousel__controls__item v-carousel__controls__item--active"
    >

          stop

    </button>
    <button type="button"
            class="v-btn v-btn--icon v-btn--small theme--dark v-carousel__controls__item"
    >

          stop

    </button>
    <button type="button"
            class="v-btn v-btn--icon v-btn--small theme--dark v-carousel__controls__item"
    >

          stop

    </button>

`;

exports[`VCarousel.js should render component with custom next icon and match snapshot 1`] = `

    <button type="button"
            class="v-btn v-btn--icon theme--dark"
    >

          chevron_left

    </button>

    <button type="button"
            class="v-btn v-btn--icon theme--dark"
    >

          stop

    </button>

`;

exports[`VCarousel.js should render component with custom prev icon and match snapshot 1`] = `

    <button type="button"
            class="v-btn v-btn--icon theme--dark"
    >

          stop

    </button>

    <button type="button"
            class="v-btn v-btn--icon theme--dark"
    >

          chevron_right

    </button>

`;

exports[`VCarousel.js should render component with image cycling off and match snapshot 1`] = `

    <button type="button"
            class="v-btn v-btn--icon theme--dark"
    >

          chevron_left

    </button>

    <button type="button"
            class="v-btn v-btn--icon theme--dark"
    >

          chevron_right

    </button>

`;

exports[`VCarousel.js should render component with selected active item 1`] = `

    <button type="button"
            class="v-btn v-btn--icon theme--dark"
    >

          chevron_left

    </button>

    <button type="button"
            class="v-btn v-btn--icon theme--dark"
    >

          chevron_right

    </button>

    <button type="button"
            class="v-btn v-btn--icon v-btn--small theme--dark v-carousel__controls__item"
    >

          fiber_manual_record

    </button>
    <button type="button"
            class="v-btn v-btn--icon v-btn--small theme--dark v-carousel__controls__item v-carousel__controls__item--active"
    >

          fiber_manual_record

    </button>
    <button type="button"
            class="v-btn v-btn--icon v-btn--small theme--dark v-carousel__controls__item"
    >

          fiber_manual_record

    </button>

`;

exports[`VCarousel.js should render component without delimiters 1`] = `

    <button type="button"
            class="v-btn v-btn--icon theme--dark"
    >

          chevron_left

    </button>

    <button type="button"
            class="v-btn v-btn--icon theme--dark"
    >

          chevron_right

    </button>

`;

exports[`VCarousel.js should render component without next icon and match snapshot 1`] = `

    <button type="button"
            class="v-btn v-btn--icon theme--dark"
    >

          chevron_left

    </button>

`;

exports[`VCarousel.js should render component without prev icon and match snapshot 1`] = `

    <button type="button"
            class="v-btn v-btn--icon theme--dark"
    >

          chevron_right

    </button>

`;


it('should register all directives', () => {


.v-rating
  .v-icon
    padding: 0.5rem
    border-radius: 50%
    user-select: none

  &--readonly
    .v-icon
      pointer-events: none


import '../../stylus/components/_date-picker-title.styl'

// Components
import VIcon from '../VIcon'

// Mixins
import PickerButton from '../../mixins/picker-button'

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

  mixins: [PickerButton],

  props: {
    date: {
      type: String,
      default: ''
    },
    selectingYear: Boolean,
    year: {
      type: [Number, String],
      default: ''
    },
    yearIcon: {
      type: String
    },
    value: {
      type: String
    }
  },

  data: () => ({
    isReversing: false
  }),

  computed: {
    computedTransition () {
      return this.isReversing ? 'picker-reverse-transition' : 'picker-transition'
    }
  },

  watch: {
    value (val, prev) {
      this.isReversing = val < prev
    }
  },

  methods: {
    genYearIcon () {
      return this.$createElement(VIcon, {
        props: {
          dark: true
        }
      }, this.yearIcon)
    },
    getYearBtn () {
      return this.genPickerButton('selectingYear', true, [
        this.year,
        this.yearIcon ? this.genYearIcon() : null
      ], 'v-date-picker-title__year')
    },
    genTitleText () {
      return this.$createElement('transition', {
        props: {
          name: this.computedTransition
        }
      }, [
        this.$createElement('div', {
          domProps: { innerHTML: this.date || ' ' },
          key: this.value
        })
      ])
    },
    genTitleDate (title) {
      return this.genPickerButton('selectingYear', false, this.genTitleText(title), 'v-date-picker-title__date')
    }
  },

  render (h) {
    return h('div', {
      staticClass: 'v-date-picker-title'
    }, [
      this.getYearBtn(),
      this.genTitleDate()
    ])
  }
}


import VLabel from './VLabel'

export { VLabel }
export default VLabel


ComponentPack: { $_vuetify_subcomponents: { HisChild: {} } }

keep keep keep keep keep replace keep keep keep keep


html
  font-size: $font-size-root
  overflow-x: hidden
  text-rendering: optimizeLegibility
  -webkit-font-smoothing: antialiased
  -moz-osx-font-smoothing: grayscale
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0)

.application
  font-family: $body-font-family
  line-height: $line-height-root

::-ms-clear
::-ms-reveal
  display: none


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

exports[`VSubheader.js should be dark 1`] = `

`;

exports[`VSubheader.js should be inset 1`] = `

`;

exports[`VSubheader.js should be light 1`] = `

`;

exports[`VSubheader.js should have custom class 1`] = `

`;


const merge = require('webpack-merge')
const HappyPack = require('happypack')
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')
const { config: baseWebpackConfig, happyThreadPool } = require('./webpack.base.config')

// Helpers
const resolve = file => require('path').resolve(__dirname, file)

module.exports = merge(baseWebpackConfig, {
  entry: {
    app: './src/index.ts'
  },
  output: {
    path: resolve('../dist'),
    publicPath: '/dist/',
    library: 'Vuetify',
    libraryTarget: 'umd',
    libraryExport: 'default',
    // See https://github.com/webpack/webpack/issues/6522
    globalObject: 'typeof self !== \'undefined\' ? self : this'
  },
  externals: {
    vue: {
      commonjs: 'vue',
      commonjs2: 'vue',
      amd: 'vue',
      root: 'Vue'
    }
  },
  module: {
    rules: [
      {
        test: /\.[jt]s$/,
        use: 'happypack/loader?id=scripts',
        exclude: /node_modules/
      }
    ]
  },
  plugins: [
    new ForkTsCheckerWebpackPlugin({
      checkSyntacticErrors: true,
      tsconfig: resolve('../tsconfig.json')
    }),
    new HappyPack({
      id: 'scripts',
      threadPool: happyThreadPool,
      loaders: [
        'babel-loader',
        {
          loader: 'ts-loader',
          options: { happyPackMode: true }
        }
      ]
    })
  ]
})


keep keep keep replace keep keep keep keep keep

<?php

return [ // Ordered by ABC.
    'amazon' => [
        'name' => 'Amazon',
        'developers' => 'https://developer.amazon.com/apps-and-games/services-and-apis',
        'icon' => 'icon-amazon',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'apple' => [
        'name' => 'Apple',
        'developers' => 'https://developer.apple.com/',
        'icon' => 'icon-apple',
        'enabled' => true,
        'sandbox' => false,
        'form' => 'apple.phtml', // Preparation for adding ability to customized OAuth UI forms, currently handled hardcoded.
        'beta' => true,
        'mock' => false,
    ],
    'auth0' => [
        'name' => 'Auth0',
        'developers' => 'https://auth0.com/developers',
        'icon' => 'icon-auth0',
        'enabled' => true,
        'sandbox' => false,
        'form' => 'auth0.phtml',
        'beta' => false,
        'mock' => false,
    ],
    'authentik' => [
        'name' => 'Authentik',
        'developers' => 'https://goauthentik.io/docs/',
        'icon' => 'icon-authentik',
        'enabled' => true,
        'sandbox' => false,
        'form' => 'authentik.phtml',
        'beta' => false,
        'mock' => false,
    ],
    'autodesk' => [
        'name' => 'Autodesk',
        'developers' => 'https://forge.autodesk.com/en/docs/oauth/v2/developers_guide/overview/',
        'icon' => 'icon-autodesk',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'bitbucket' => [
        'name' => 'BitBucket',
        'developers' => 'https://developer.atlassian.com/bitbucket',
        'icon' => 'icon-bitbucket',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'bitly' => [
        'name' => 'Bitly',
        'developers' => 'https://dev.bitly.com/v4_documentation.html',
        'icon' => 'icon-bitly',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false
    ],
    'box' => [
        'name' => 'Box',
        'developers' => 'https://developer.box.com/reference/',
        'icon' => 'icon-box',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false
    ],
    'dailymotion' => [
        'name' => 'Dailymotion',
        'developers' => 'https://developers.dailymotion.com/api/',
        'icon' => 'icon-dailymotion',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false
    ],
    'discord' => [
        'name' => 'Discord',
        'developers' => 'https://discordapp.com/developers/docs/topics/oauth2',
        'icon' => 'icon-discord',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'disqus' => [
        'name' => 'Disqus',
        'developers' => 'https://disqus.com/api/docs/auth/',
        'icon' => 'icon-disqus',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'dropbox' => [
        'name' => 'Dropbox',
        'developers' => 'https://www.dropbox.com/developers/documentation',
        'icon' => 'icon-dropbox',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'etsy' => [
        'name' => 'Etsy',
        'developers' => 'https://developers.etsy.com/',
        'icon' => 'icon-etsy',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'facebook' => [
        'name' => 'Facebook',
        'developers' => 'https://developers.facebook.com/',
        'icon' => 'icon-facebook',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'github' => [
        'name' => 'GitHub',
        'developers' => 'https://developer.github.com/',
        'icon' => 'icon-github',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'gitlab' => [
        'name' => 'GitLab',
        'developers' => 'https://docs.gitlab.com/ee/api/',
        'icon' => 'icon-gitlab',
        'enabled' => true,
        'sandbox' => false,
        'form' => 'gitlab.phtml',
        'beta' => false,
        'mock' => false,
    ],
    'google' => [
        'name' => 'Google',
        'developers' => 'https://support.google.com/googleapi/answer/6158849',
        'icon' => 'icon-google',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'linkedin' => [
        'name' => 'LinkedIn',
        'developers' => 'https://developer.linkedin.com/',
        'icon' => 'icon-linkedin',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'microsoft' => [
        'name' => 'Microsoft',
        'developers' => 'https://developer.microsoft.com/en-us/',
        'icon' => 'icon-windows',
        'enabled' => true,
        'sandbox' => false,
        'form' => 'microsoft.phtml',
        'beta' => false,
        'mock' => false,
    ],
    'notion' => [
        'name' => 'Notion',
        'developers' => 'https://developers.notion.com/docs',
        'icon' => 'icon-notion',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'oidc' => [
        'name' => 'OpenID Connect',
        'developers' => 'https://openid.net/connect/faq/',
        'icon' => 'icon-oidc',
        'enabled' => true,
        'sandbox' => false,
        'form' => 'oidc.phtml',
        'beta' => false,
        'mock' => false,
    ],
    'okta' => [
        'name' => 'Okta',
        'developers' => 'https://developer.okta.com/',
        'icon' => 'icon-okta',
        'enabled' => true,
        'sandbox' => false,
        'form' => 'okta.phtml',
        'beta' => false,
        'mock' => false,
    ],
    'paypal' => [
        'name' => 'PayPal',
        'developers' => 'https://developer.paypal.com/docs/api/overview/',
        'icon' => 'icon-paypal',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false
    ],
    'paypalSandbox' => [
        'name' => 'PayPal',
        'developers' => 'https://developer.paypal.com/docs/api/overview/',
        'icon' => 'icon-paypal',
        'enabled' => true,
        'sandbox' => true,
        'form' => false,
        'beta' => false,
        'mock' => false
    ],
    'podio' => [
        'name' => 'Podio',
        'developers' => 'https://developers.podio.com/doc/oauth-authorization',
        'icon' => 'icon-podio',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'salesforce' => [
        'name' => 'Salesforce',
        'developers' => 'https://developer.salesforce.com/docs/',
        'icon' => 'icon-salesforce',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'slack' => [
        'name' => 'Slack',
        'developers' => 'https://api.slack.com/',
        'icon' => 'icon-slack',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'spotify' => [
        'name' => 'Spotify',
        'developers' => 'https://developer.spotify.com/documentation/general/guides/authorization-guide/',
        'icon' => 'icon-spotify',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'stripe' => [
        'name' => 'Stripe',
        'developers' => 'https://stripe.com/docs/api',
        'icon' => 'icon-stripe',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false
    ],
    'tradeshift' => [
        'name' => 'Tradeshift',
        'developers' => 'https://developers.tradeshift.com/docs/api',
        'icon' => 'icon-tradeshift',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'tradeshiftBox' => [
        'name' => 'Tradeshift',
        'developers' => 'https://developers.tradeshift.com/docs/api',
        'icon' => 'icon-tradeshiftbox',
        'enabled' => true,
        'sandbox' => true,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'twitch' => [
        'name' => 'Twitch',
        'developers' => 'https://dev.twitch.tv/docs/authentication',
        'icon' => 'icon-twitch',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'wordpress' => [
        'name' => 'WordPress',
        'developers' => 'https://developer.wordpress.com/docs/oauth2/',
        'icon' => 'icon-wordpress',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false
    ],
    'yahoo' => [
        'name' => 'Yahoo',
        'developers' => 'https://developer.yahoo.com/oauth2/guide/flows_authcode/',
        'icon' => 'icon-yahoo',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'yammer' => [
        'name' => 'Yammer',
        'developers' => 'https://docs.microsoft.com/en-us/rest/api/yammer/oauth-2/',
        'icon' => 'icon-yammer',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'yandex' => [
        'name' => 'Yandex',
        'developers' => 'https://tech.yandex.com/oauth/',
        'icon' => 'icon-yandex',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    'zoom' => [
        'name' => 'Zoom',
        'developers' => 'https://marketplace.zoom.us/docs/guides/auth/oauth/',
        'icon' => 'icon-zoom',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => false,
    ],
    // 'instagram' => [
    //     'name' => 'Instagram',
    //     'developers' => 'https://www.instagram.com/developer/',
    //     'icon' => 'icon-instagram',
    //     'enabled' => false,
    //     'beta' => false,
    //     'mock' => false,
    // ],
    // 'twitter' => [
    //     'name' => 'twitter',
    //     'developers' => 'https://developer.twitter.com/',
    //     'icon' => 'icon-twitter',
    //     'enabled' => false,
    //     'beta' => false,
    //     'mock' => false,
    // ],

    // Keep Last
    'mock' => [
        'name' => 'Mock',
        'developers' => 'https://appwrite.io',
        'icon' => 'icon-appwrite',
        'enabled' => true,
        'sandbox' => false,
        'form' => false,
        'beta' => false,
        'mock' => true,
    ],
];


<?php

namespace Tests\E2E\Scopes;

trait SideServer
{
    /**
     * @var array
     */
    protected $key = [];

    public function getHeaders(): array
    {
        return [
            'x-appwrite-key' => $this->getProject()['apiKey']
        ];
    }

    /**
     * @return string
     */
    public function getSide()
    {
        return 'server';
    }
}


from appwrite.client import Client
from appwrite.services.teams import Teams

client = Client()

(client
  .set_endpoint('https://[HOSTNAME_OR_IP]/v1') # Your API Endpoint
  .set_project('5df5acd0d48c2') # Your project ID
  .set_key('919c2d18fb5d4...a2ae413da83346ad2') # Your secret API key
)

teams = Teams(client)

result = teams.get('[TEAM_ID]')