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

/* Theme */
input-group($material)
  input,
  textarea
    color: $material.text.primary

    &:disabled
      color: $material.text.disabled

  &:not(.input-group--error) .input-group__messages
    color: $material.text.secondary

  &.input-group--textarea:not(.input-group--full-width)
    .input-group__input
      border: 2px solid $material.text.secondary

  // This is specific for selects
  &.input-group--dirty
    .input-group__selections__comma
      &:not(.input-group__selections__comma--active)
        color: $material.text.primary

  &:not(.input-group--error)
    label
      color: $material.text.secondary

    &.input-group--disabled
      label,
      .input-group__selections__comma
        color: $material.text.disabled

    .input-group__details:before
      background-color: $material.input-bottom-line

    &:not(.input-group--focused):not(.input-group--disabled)
      .input-group__input
        .input-group__append-icon,
        .input-group__prepend-icon
          &:not(:hover)
            color: $material.text.secondary

      &:not(.input-group--disabled):not(.input-group--overflow):not(.input-group--segmented):not(.input-group--editable):hover
        .input-group__details:before
          background-color: $material.text.primary

        &.input-group--textarea:not(.input-group--full-width)
          .input-group__input
            border-color: $material.text.primary

  &.input-group
    &--overflow, &--segmented, &--editable
      .input-group__input,
      .input-group__details
        &:before
          background-color: $material.dividers

  &.input-group--disabled
    .input-group__input
      .input-group__append-icon,
      .input-group__prepend-icon
        color: $material.text.disabled

    .input-group__details:before
      background-color: transparent
      background-image: linear-gradient(
        to right,
        $material.text.disabled 0,
        $material.text.disabled 33%,
        transparent 0
      )

  .input-group--text-field
    &__prefix, &__suffix
      color: $material.text.secondary

    &.input-group--disabled
      &__prefix, &__suffix
        color: $material.text.disabled

theme(input-group, "input-group")

.input-group
  display: flex
  flex: 1 1
  flex-wrap: wrap
  min-width: 24px
  padding: $input-group-padding
  position: relative
  width: 100%
  outline: none
  transition: box-shadow $primary-transition

/** Label */
.input-group
  label
    display: inline-block
    font-size: $input-font-size
    line-height: $input-height
    height: $input-height
    max-width: 90%
    min-width: 0
    overflow: hidden
    pointer-events: none
    text-align: left
    text-overflow: ellipsis
    transform: translate3d(0, 0, 0)
    transform-origin: top left
    transition: .4s $transition.fast-in-fast-out
    white-space: nowrap
    width: 100%
    z-index: 0

/** Blocks */
.input-group
  &__input
    display: flex
    flex: 1 0 100%
    min-width: 0
    min-height: $input-height

/** Icons */
.input-group
  &__icon-cb
    cursor: pointer

.input-group
  &.input-group--focused,
  &.input-group--error
    .input-group__append-icon,
    .input-group__prepend-icon
      color: inherit

  &.input-group--append-icon, &.input-group--selection-controls
    label
      max-width: 75%

  &.input-group--prepend-icon
    label
      max-width: 75%

  &.input-group--append-icon.input-group--prepend-icon
    label
      max-width: 65%

  .input-group__append-icon
    padding: 0 6px
    transition: .3s $transition.ease-in-out

  .input-group__append-icon,
  .input-group__prepend-icon
    align-self: center
    user-select: none

/** States */
.input-group
  // Single Line
  &.input-group--single-line,
  &.input-group--solo
    label
      transform: translate3d(0,0,0)

    &.input-group--dirty
      label
        display: none

  // Solo
  &.input-group--solo
    background: #fff
    min-height: 46px
    border-radius: $card-border-radius
    padding: 0
    elevation(2)

    label
      top: 8px
      padding-left: 16px
      transform: none !important

    .input-group__input
      align-items: center
      padding: 8px 16px

    .input-group__details
      display: none

  &--disabled
    .input-group__details:before
      background-color: transparent
      background-position: bottom
      background-size: 3px 1px
      background-repeat: repeat-x

  // Focused
  &.input-group--text-field
    &:not(.input-group--single-line):not(.input-group--error)
      &.input-group--focused
        label
          color: inherit

        .input-group__input
          border-color: inherit

    &.input-group--focused
      &:not(.input-group--disabled)
        .input-group__details
          &:after
            transform: scaleX(1)

  // Required
  &--required
    label:after
      content: '*'

    &.input-group--focused
      label:after
        color: $theme.error

  // Error
  &.input-group--error
    label
      animation: shake .6s $transition.swing

    .input-group__messages
      color: inherit

    .input-group__details
      &:before
        background-color: currentColor

/** Details */
.input-group
  .slide-y-transition-leave,
  .slide-y-transition-leave-to
    position: absolute

  &__details
    color: inherit
    display: flex
    padding-top: 4px
    flex: 1 0 100%
    font-size: 12px
    min-height: 26px
    overflow: hidden
    position: relative
    width: 100%

    &:after, &:before
      content: ''
      position: absolute
      left: 0
      transition: .3s $transition.fast-out-slow-in

    &:after
      background-color: currentColor
      color: inherit
      top: 0
      height: 2px
      transform: scaleX(0)
      transform-origin: center center 0
      width: 100%
      z-index: 1

    &:before
      top: 0
      height: 1px
      width: 100%
      z-index: 0

  &--hide-details
    .input-group__details
      min-height: 2px
      padding: 0

  &--async-loading
    .input-group__details
      &:before,
      &:after
        display: none

  /** Async loading indicator */
  .progress-linear
    position: absolute
    top: 0
    left: 0
    margin: 0

/** Hint */
.input-group
  &__hint
    // flex: 1 0
    transition: .3s $transition.fast-in-fast-out

/** Error */
.input-group
  .input-group__error
    color: inherit
    flex: 1 0
    transition: .3s $transition.fast-in-fast-out

/** Types */
.input-group
  &--overflow,
  &--segmented,
  &--editable
    &.input-group--active
      background-color: #fff


keep keep keep keep replace keep keep keep keep

class="list__tile list__tile--link list__tile--active primary--text"

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

exports[`VSelect should be clearable with prop, dirty and multi select 1`] = `

        1

      <input disabled="disabled"
             tabindex="-1"
             class="input-group--select__autocomplete"
             style="display: none;"
      >

                        check_box_outline_blank

                        check_box

                    1

                        check_box_outline_blank

                    2

      clear

`;

exports[`VSelect should be clearable with prop, dirty and multi select 2`] = `

      <input disabled="disabled"
             tabindex="-1"
             class="input-group--select__autocomplete"
             style="display: none;"
      >

                        check_box

                        check_box_outline_blank

                    1

                        check_box_outline_blank

                    2

      arrow_drop_down

`;

exports[`VSelect should be clearable with prop, dirty and single select 1`] = `

        1

      <input disabled="disabled"
             tabindex="-1"
             class="input-group--select__autocomplete"
             style="display: none;"
      >

                    1

                    2

      clear

`;

exports[`VSelect should be clearable with prop, dirty and single select 2`] = `

      <input disabled="disabled"
             tabindex="-1"
             class="input-group--select__autocomplete"
             style="display: none;"
      >

                    1

                    2

      arrow_drop_down

`;


import { test } from '~util/testing'
import { VRadioGroup, VRadio } from '~components/VRadioGroup'

test('VRadio.vue', ({ mount }) => {
  it('should advise about v-radio-group being necessary', () => {
    mount(VRadio, {
      provide: {
        name: () => 'name',
        registerChild: () => {},
        unregisterChild: () => {},
        isMandatory: () => false,
      }
    })

    expect('immediate parent of v-radio-group').toHaveBeenTipped()
  })

  // TODO: Enable test when there's a way to test $parent.$vnode.tag
  it.skip('should not advise about v-radio-group being necessary in VRadioGroup', () => {
    const wrapper = mount(VRadioGroup, {
      slots: {
        default: [VRadio]
      }
    })
    // no expectation other than a lack of tip
  })

  it('should render role and aria-checked attributes on input group', () => {
    const wrapper = mount(VRadio, {
      data: {
        isActive: false
      },
      provide: {
        name: () => 'name',
        registerChild: () => {},
        unregisterChild: () => {},
        isMandatory: () => false
      }
    })

    let inputGroup = wrapper.find('.input-group')[0]
    expect(inputGroup.getAttribute('role')).toBe('radio')
    expect(inputGroup.getAttribute('aria-checked')).toBe('false')

    wrapper.setData({ 'isActive': true })
    inputGroup = wrapper.find('.input-group')[0]
    expect(inputGroup.getAttribute('aria-checked')).toBe('true')
    expect(wrapper.html()).toMatchSnapshot()

    expect('immediate parent of v-radio-group').toHaveBeenTipped()
  })

  it('should render aria-label attribute with label value on input group', () => {
    const wrapper = mount(VRadio, {
      propsData: {
        label: 'Test'
      },
      attrs: {},
      provide: {
        name: () => 'name',
        registerChild: () => {},
        unregisterChild: () => {},
        isMandatory: () => false
      }
    })

    const inputGroup = wrapper.find('.input-group')[0]
    expect(inputGroup.getAttribute('aria-label')).toBe('Test')
    expect(wrapper.html()).toMatchSnapshot()

    expect('immediate parent of v-radio-group').toHaveBeenTipped()
  })

  it('should not render aria-label attribute with no label value on input group', () => {
    const wrapper = mount(VRadio, {
      propsData: {
        label: null
      },
      provide: {
        name: () => 'name',
        registerChild: () => {},
        unregisterChild: () => {},
        isMandatory: () => false
      }
    })

    const inputGroup = wrapper.find('.input-group')[0]
    expect(inputGroup.element.getAttribute('aria-label')).toBeFalsy()
    expect(wrapper.html()).toMatchSnapshot()

    expect('immediate parent of v-radio-group').toHaveBeenTipped()
  })

  it('should render proper input name', () => {
    const wrapper = mount(VRadio, {
      provide: {
        name: () => 'name',
        registerChild: () => {},
        unregisterChild: () => {},
        isMandatory: () => false
      }
    })

    const input = wrapper.find('input')[0]
    expect(input.getAttribute('name')).toBe('name')
    expect(wrapper.html()).toMatchSnapshot()

    expect('immediate parent of v-radio-group').toHaveBeenTipped()
  })

  it('should register and unregister', () => {
    const register = jest.fn()
    const unregister = jest.fn()

    const wrapper = mount(VRadio, {
      provide: {
        name: () => 'name',
        registerChild: register,
        unregisterChild: unregister,
        isMandatory: () => false
      }
    })

    expect(register).toHaveBeenCalled()
    wrapper.destroy()
    expect(unregister).toHaveBeenCalled()

    expect('immediate parent of v-radio-group').toHaveBeenTipped()
  })
})


/**
 * Bootable
 * @mixin
 *
 * Used to add lazy content functionality to components
 * Looks for change in "isActive" to automatically boot
 * Otherwise can be set manually
 */
export default {
  data: () => ({
    isBooted: false
  }),

  props: {
    lazy: Boolean
  },

  watch: {
    isActive () {
      this.isBooted = true
    }
  },

  methods: {
    showLazyContent (content) {
      return (this.isBooted || !this.lazy)
        ? content
        : null
    }
  }
}


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

exports[`VBtnToggle.vue should allow deselecting a value when mandatory prop is used with multiple prop 1`] = `

  <button type="button"
          class="btn btn--flat"
          data-ripple="true"
  >

        add

  </button>
  <button type="button"
          class="btn btn--flat btn--active"
          data-ripple="true"
          data-selected="true"
  >

        add

  </button>
  <button type="button"
          class="btn btn--flat btn--active"
          data-ripple="true"
          data-selected="true"
  >

        add

  </button>

`;

exports[`VBtnToggle.vue should allow new value when mandatory prop is used 1`] = `

  <button type="button"
          class="btn btn--flat"
          data-ripple="true"
  >

        add

  </button>
  <button type="button"
          class="btn btn--flat btn--active"
          data-ripple="true"
          data-selected="true"
          data-only-child="true"
  >

        add

  </button>
  <button type="button"
          class="btn btn--flat"
          data-ripple="true"
  >

        add

  </button>

`;

exports[`VBtnToggle.vue should allow new value when mandatory prop is used with multiple prop 1`] = `

  <button type="button"
          class="btn btn--flat"
          data-ripple="true"
  >

        add

  </button>
  <button type="button"
          class="btn btn--flat btn--active"
          data-ripple="true"
          data-selected="true"
          data-only-child="true"
  >

        add

  </button>
  <button type="button"
          class="btn btn--flat"
          data-ripple="true"
  >

        add

  </button>

`;

exports[`VBtnToggle.vue should not allow empty value when mandatory prop is used 1`] = `

  <button type="button"
          class="btn btn--flat btn--active"
          data-ripple="true"
          data-selected="true"
          data-only-child="true"
  >

        add

  </button>
  <button type="button"
          class="btn btn--flat"
          data-ripple="true"
  >

        add

  </button>
  <button type="button"
          class="btn btn--flat"
          data-ripple="true"
  >

        add

  </button>

`;

exports[`VBtnToggle.vue should not allow empty value when mandatory prop is used with multiple prop 1`] = `

  <button type="button"
          class="btn btn--flat"
          data-ripple="true"
  >

        add

  </button>
  <button type="button"
          class="btn btn--flat btn--active"
          data-ripple="true"
          data-selected="true"
          data-only-child="true"
  >

        add

  </button>
  <button type="button"
          class="btn btn--flat"
          data-ripple="true"
  >

        add

  </button>

`;

exports[`VBtnToggle.vue should use button value attribute if available 1`] = `

  <button type="button"
          value="left"
          class="btn btn--flat"
          data-ripple="true"
  >

        add

  </button>
  <button type="button"
          value="center"
          class="btn btn--flat btn--active"
          data-ripple="true"
          data-selected="true"
          data-only-child="true"
  >

        add

  </button>
  <button type="button"
          value="right"
          class="btn btn--flat"
          data-ripple="true"
  >

        add

  </button>

`;


keep keep keep keep replace keep keep keep

import Routable from '../../mixins/routable'
import Toggleable from '../../mixins/toggleable'
import Ripple from '../../directives/ripple'

export default {
  name: 'v-list-tile',

  mixins: [Routable, Toggleable],

  directives: {
    Ripple
  },

  inheritAttrs: false,

  props: {
    activeClass: {
      type: String,
      default: 'list__tile--active'
    },
    avatar: Boolean,
    inactive: Boolean,
    tag: String
  },

  computed: {
    classes () {
      return {
        'list__tile': true,
        'list__tile--link': this.isLink && !this.inactive,
        'list__tile--avatar': this.avatar,
        'list__tile--disabled': this.disabled,
        [this.activeClass]: this.isActive
      }
    },
    isLink () {
      return this.href || this.to ||
        (this.$listeners && (this.$listeners.click || this.$listeners['!click']))
    }
  },

  render (h) {
    const isRouteLink = !this.inactive && this.isLink
    const { tag, data } = isRouteLink ? this.generateRouteLink() : {
      tag: this.tag || 'div',
      data: {
        class: this.classes
      }
    }

    data.attrs = Object.assign({}, data.attrs, this.$attrs)

    return h('li', {
      attrs: {
        disabled: this.disabled
      },
      on: {
        ...this.$listeners
      }
    }, [h(tag, data, this.$slots.default)])
  }
}


@import '../bootstrap'

.speed-dial
  position: relative

  &--absolute
    position: absolute

  &--fixed
    position: fixed

  &--top
    &:not(.speed-dial--absolute)
      top: $grid-gutters.lg

    &.speed-dial--absolute
      top: 50%
      transform: translateY(-50%)

  &--bottom
    &:not(.speed-dial--absolute)
      bottom: $grid-gutters.lg

    &.speed-dial--absolute
      bottom: 50%
      transform: translateY(50%)

  &--left
    left: $grid-gutters.lg

  &--right
    right: $grid-gutters.lg

  &--direction
    &-left,
    &-right
      .speed-dial__list
        height: 100%
        top: 0

    &-top,
    &-bottom
      .speed-dial__list
        left: 0
        width: 100%

    &-top
      .speed-dial__list
        flex-direction: column-reverse
        bottom: 100%

    &-right
      .speed-dial__list
        flex-direction: row
        left: 100%

    &-bottom
      .speed-dial__list
        flex-direction: column
        top: 100%

    &-left
      .speed-dial__list
        flex-direction: row-reverse
        right: 100%

/** Elements */
.speed-dial__list
  align-items: center
  display: flex
  justify-content: center
  position: absolute

  .btn
    for n in (1..7)
      &:nth-child({n})
        transition-delay: "%ss" % (n * .05)


export default {
  methods: {
    genTitle () {
      const children = [this.genTime()]

      if (this.format === 'ampm') {
        children.push(this.genAMPM())
      }

      return this.genPickerTitle(children)
    },
    genTime () {
      let hour = this.hour

      if (this.is24hr && hour < 10) {
        hour = `0${hour}`
      }

      return this.$createElement('div', {
        'class': 'picker--time__title'
      }, [
        this.$createElement('span', {
          'class': { active: this.selectingHour },
          on: {
            click: () => (this.selectingHour = true)
          }
        }, hour),
        this.$createElement('span', {
          'class': { active: !this.selectingHour },
          on: {
            click: () => (this.selectingHour = false)
          }
        }, `:${this.minute}`)
      ])
    },
    genAMPM () {
      return this.$createElement('div', [
        this.genPeriod('am'),
        this.genPeriod('pm')
      ])
    },
    genPeriod (period) {
      return this.$createElement('span', {
        'class': { active: this.period === period },
        on: { click: () => (this.period = period) }
      }, period.toUpperCase())
    }
  }
}


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

textfield($material)
  &.input-group--text-field-box
    .input-group__input
      background: $material.buttons.focused-alt

  input::placeholder,
  textarea::placeholder
    color: $material.text.disabled

  &:not(.input-group--error)
    .input-group__counter
      color: $material.text.secondary

theme(textfield, "input-group--text-field")

/** Label */
.input-group--text-field label
  position: absolute
  top: $input-group-text-field-label-top
  left: 0

/** Input */
.input-group--text-field
  input,
  textarea
    font-size: $input-font-size

    &::placeholder
      color: inherit
      transition: .3s $transition.swing

.input-group--text-field input
  box-shadow: none
  flex: 1
  height: $input-height
  margin: 0
  min-width: 0
  overflow: hidden
  text-overflow: ellipsis
  white-space: nowrap

  &:focus
    outline: none

  &:disabled
    pointer-events: none

/** Textarea */
.input-group--text-field
  textarea
    flex: 1 1

    &:focus
      outline: none

.input-group--text-field.input-group--textarea
  label
    top: 13px

  .input-group__input
    border-radius: 2px
    transition: .4s $transition.fast-in-fast-out

  textarea
    font-size: $input-font-size
    transition: .3s $transition.swing

  &:not(.input-group--full-width)
    label
      top: 30px
      left: 15px

    .input-group__input
      padding: 30px 0 0 13px

  .input-group__details
    &:before,
    &:after
      opacity: 0

/** Counter */
.input-group--text-field
  .input-group__counter
    margin-left: auto

    &--error
      color: inherit

/** Input States */
.input-group--text-field
  &.input-group--placeholder.input-group--dirty
    input::placeholder,
    textarea::placeholder
      opacity: 0

/** Types */
.input-group--text-field
  // Icons
  &.input-group--prepend-icon
    .input-group__prepend-icon
      align-items: center
      display: flex
      justify-content: flex-start
      min-width: 40px
      transition: .3s $transition.fast-in-fast-out

    .input-group__messages
      margin-right: auto
      padding-left: 40px

    .input-group__details
      &:before, &:after
        margin-left: 40px
        max-width: calc(100% - 40px)

    label
      left: 40px

  &:not(.input-group--single-line)
    &.input-group--focused,
    &.input-group--placeholder
      label
        opacity: 1

      &:not(.input-group--textarea)
        label
          transform: translate3d(0, -18px, 0) scale(.75)

      &:not(.input-group--full-width).input-group--textarea
        label
          transform: translate3d(0, -8px, 0) scale(.75)

      &.input-group--text-field-box
        label
          transform: translate3d(0, -10px, 0) scale(.75)

  &.input-group--dirty
    &.input-group--select,
    &:not(.input-group--textarea)
      label
        transform: translate3d(0, -18px, 0) scale(.75)

    &:not(.input-group--full-width).input-group--textarea
      label
        transform: translate3d(0, -8px, 0) scale(.75)

  // Multi Line
  &.input-group--multi-line
    textarea
      padding-top: 4px

  // Full Width
  &.input-group--full-width
    padding: 16px

    label
      margin-left: $grid-gutters.lg

    .input-group__details:before, .input-group__details:after
      display: none

/** Prefix/Suffix */
.input-group--prefix
  &:not(.input-group--focused):not(.input-group--dirty)
    label
      left: 16px

.input-group--prefix,
.input-group--suffix
  .input-group--text-field__prefix,
  .input-group--text-field__suffix
    align-items: center
    display: inline-flex
    font-size: 16px
    margin-top: 1px

  .input-group--text-field__prefix
    margin-right: 3px

  .input-group--text-field__suffix
    margin-left: 3px

/** Box */
.input-group--text-field-box
  input, textarea
    cursor: pointer

  label
    left: 16px

  .input-group__input
    align-items: flex-end
    border-radius: 4px 4px 0 0
    cursor: pointer
    min-height: 56px

  .input-group__details
    padding: 8px 16px 0

    &:before,
    &:after
      height: 2px
      border-bottom-left-radius: 4px
      border-bottom-right-radius: 4px

  &.input-group--multi-line
    textarea
      padding-left: 24px
      padding-right: 24px

  &:not(.input-group--textarea)
    &.input-group--multi-line
      .input-group__input
        padding-top: 26px

      label
        top: 26px

    &:not(.input-group--multi-line)
      .input-group__input
        padding-left: 16px
        padding-right: 16px

      label
        top: 32px

    &:not(.input-group--single-line)
      &.input-group--focused,
      &.input-group--dirty
        label
          transform: translate3d(0, -10px, 0) scale(0.75)
  // Icons
  &.input-group--prepend-icon
    .input-group__details
      &:before, &:after
        margin-left: 56px
        max-width: calc(100% - 56px)

    label
      left: 56px


$list-tile-font-size := $headings.subheading.size //16px - the mdc scss uses subheading to configure like this

$list-tile-font-weight := $headings.subheading.weight //400

$list-action-text-font-size := 12px //this is for the stacked actions


@import '../bootstrap'

.container
  flex: 1 1 100%
  margin: auto
  padding: $grid-gutters.lg
  width: 100%

  for size, width in $container-max-widths
    @media only screen and (min-width: width)
      max-width: (width * 0.9375) // Credit to bootstrap

  @media $display-breakpoints.xs-only
    padding: $grid-gutters.xl

  &.fluid
    max-width: 100%

  &.fill-height
    align-items: center
    display: flex

    .layout
      height: 100%
      flex: 1 1 auto

  &.grid-list
    for $size, $gutter in $grid-gutters
      &-{$size}
        padding: $gutter

        .layout
          .flex
            padding: ($gutter / 2)

        .layout:only-child
          margin: -($gutter / 2)

        .layout:not(:only-child)
          margin: auto -($gutter / 2)

        *:not(:only-child)
          .layout:first-child
            margin-top: -($gutter / 2)

          .layout:last-child
            margin-bottom: -($gutter / 2)

.layout
  display: flex
  flex: 1 1 auto
  flex-wrap: nowrap

  &.row
    flex-direction: row

    &.reverse
      flex-direction: row-reverse

  &.column
    flex-direction: column

    &.reverse
      flex-direction: column-reverse

  &.wrap
    flex-wrap: wrap

for size, width in $grid-breakpoints
  @media all and (min-width: width)
    for n in (1..$grid-columns)
      .flex.{size}{n}
        flex-basis: (n / $grid-columns * 100)%
        max-width: (n / $grid-columns * 100)%

      .flex.order-{size}{n}
        order: n

    for n in (0..$grid-columns)
      .flex.offset-{size}{n}
        margin-left: (n / $grid-columns * 100)%

.flex,
.child-flex > *
  flex: 1 1 auto

.align
  &-start
    align-items: flex-start

  &-end
    align-items: flex-end

  &-center
    align-items: center

  &-baseline
    align-items: baseline

.align-content
  &-start
    align-content: flex-start

  &-end
    align-content: flex-end

  &-center
    align-content: center

  &-space-between
    align-content: space-between

  &-space-around
    align-content: space-around

.justify
  &-start
    justify-content: flex-start

  &-end
    justify-content: flex-end

  &-center
    justify-content: center

  &-space-around
    justify-content: space-around

  &-space-between
    justify-content: space-between

.spacer
  flex-grow: 1 !important

.scroll-y
  overflow-y: auto

.fill-height
  height: 100%

.hide-overflow
  overflow: hidden !important

.show-overflow
  overflow: visible !important

.no-wrap
  white-space: nowrap

.ellipsis
  white-space: nowrap
  overflow: hidden
  text-overflow: ellipsis

// Display helpers
.d-flex
  display: flex

.d-inline-flex
  display: inline-flex

.d-flex,
.d-inline-flex
  > *
    flex: 1 1 auto

.d-block
  display: block

.d-inline-block
  display: inline-block


require('../../stylus/components/_alerts.styl')

import VIcon from '../VIcon'

import Colorable from '../../mixins/colorable'
import Toggleable from '../../mixins/toggleable'
import Transitionable from '../../mixins/transitionable'

export default {
  name: 'v-alert',

  components: {
    VIcon
  },

  mixins: [Colorable, Toggleable, Transitionable],

  props: {
    dismissible: Boolean,
    icon: String
  },

  data: () => ({
    defaultColor: 'error'
  }),

  computed: {
    classes () {
      return this.addBackgroundColorClassChecks({
        'alert--dismissible': this.dismissible
      })
    }
  },

  render (h) {
    const children = [h('div', this.$slots.default)]

    if (this.icon) {
      children.unshift(h('v-icon', {
        'class': 'alert__icon'
      }, this.icon))
    }

    if (this.dismissible) {
      const close = h('a', {
        'class': 'alert__dismissible',
        domProps: { href: 'javascript:;' },
        on: { click: () => this.$emit('input', false) }
      }, [
        h(VIcon, {
          props: {
            right: true
          }
        }, 'cancel')
      ])

      children.push(close)
    }

    const alert = h('div', {
      staticClass: 'alert',
      'class': this.classes,
      directives: [{
        name: 'show',
        value: this.isActive
      }],
      on: this.$listeners
    }, children)

    if (!this.transition) return alert

    return h('transition', {
      props: {
        name: this.transition,
        origin: this.origin,
        mode: this.mode
      }
    }, [alert])
  }
}


&--variant-filled
border-bottom-left-radius: 0
border-bottom-right-radius: 0
&--variant-plain,
&--variant-underlined
border-radius: 0
padding: 0
--v-field-padding-start: 0px
--v-field-padding-end: 0px
--v-field-padding-top: 6px
--v-field-padding-bottom: 2px
&--variant-outlined,
&--single-line
--v-field-padding-top: 0px
$root: &
@at-root

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

@use 'sass:selector'
@use '../../styles/settings'
@use './variables' as *

/* region BLOCK */
.v-text-field
  input
    color: inherit
    flex: $text-field-input-flex
    transition: $text-field-input-transition
    min-width: 0

    &:focus,
    &:active
      outline: none

    // Remove Firefox red outline
    &:invalid
      box-shadow: none

  .v-field
    cursor: text

  .v-field__input
    @at-root #{selector.append('.v-text-field--prefixed', &)}
      --v-field-padding-start: #{$text-field-input-padding-start}

    @at-root #{selector.append('.v-text-field--suffixed', &)}
      --v-field-padding-end: #{$text-field-input-padding-end}

  .v-input__details
    padding-inline-start: $text-field-details-padding-inline
    padding-inline-end: $text-field-details-padding-inline

  .v-field--has-label
    input
      opacity: 0

  .v-field--active
    input
      opacity: 1

  .v-field--single-line
    input
      transition: none

/* endregion */
/* region ELEMENTS */
.v-text-field
  &__prefix,
  &__suffix
    align-items: center
    color: $text-field-affix-color
    cursor: default
    display: flex
    opacity: 0
    transition: inherit
    white-space: nowrap
    padding-top: calc(var(--v-field-padding-top, 4px) + var(--v-input-padding-top, 0))
    padding-bottom: var(--v-field-padding-bottom, 6px)

    .v-field--active &
      opacity: 1

    .v-field--disabled &
      color: $text-field-disabled-affix-color

  &__prefix
    padding-inline-start: var(--v-field-padding-start)

  &__suffix
    padding-inline-end: var(--v-field-padding-end)

/* endregion */
/* region MODIFIERS */
.v-text-field
  &--flush-details
    .v-input__details
      padding: 0
/* endregion */


Respects boundaries of—and will not overlap with—other `app` components like `v-app-bar`, `v-navigation-drawer`, and `v-footer`.

Positions the snackbar in the center of the screen, (x and y axis).

Apply a custom class to the snackbar content

Gives the snackbar a larger minimum height.

Applies the defined **color** to text and a low opacity background of the same.

Time (in milliseconds) to wait until snackbar is automatically hidden.  Use `-1` to keep open indefinitely (`0` in version < 2.3 ). It is recommended for this number to be between `4000` and `10000`. Changes to this property will reset the timeout.

Sets the component transition. Can be one of the [built in transitions](/styles/transitions) or one your own.

Stacks snackbar content on top of the actions (button).

Used to bind styles to [v-btn](/components/buttons) to match MD2 specification.


<template>
  <v-app>

async load of items

      <v-list :items="loadedItems" :open-strategy="loadItems" :opened="opened" />

custom ctrl + click open strategy

      <v-list :items="listItems" :open-strategy="openStrategyFn" density="compact">
        <template #prepend v-if="useSlot">
          foo
        </template>
      </v-list>
      <v-btn @click="doStuff">click me</v-btn>

router integration

      <v-list v-model:selected="test" v-model:opened="open">
        <v-list-item active-color="primary" to="/" exact title="foo" />
        <v-list-item active-color="primary" to="/page1" title="bar" />
        <v-list-group>
          <template #header="props">
            <v-list-item active-color="primary" v-bind="props" :active="$route.path.startsWith('/nested')" title="header" />
          </template>
          <v-list-item active-color="primary" to="/nested/page1" title="baz" />
          <v-list-item active-color="primary" to="/nested/page2" title="bro" />
        </v-list-group>
      </v-list>

value prop

      <v-list v-model:active="test2" v-model:opened="open2">
        <v-list-item value="/" title="foo" />
        <v-list-item value="/page1" title="bar" />
        <v-list-group>
          <template #header="props">
            <v-list-item v-bind="props" title="header" />
          </template>
          <v-list-item value="/nested/page1" title="baz" />
          <v-list-item value="/nested/page2" title="bro" />
          <v-list-group>
            <template #header="props">
              <v-list-item v-bind="props" title="header 2" />
            </template>
            <v-list-item value="/nested/foo/page1" title="baz 2" />
            <v-list-item value="/nested/foo/page2" title="bro 2" />
          </v-list-group>
        </v-list-group>
      </v-list>
      {{ test2 }}

with checkbox and selected

      <v-list :items="listItems" v-model:selected="selected">
        <template #item="props">
          <v-list-item v-bind="props">
            <template #prepend="{ select, isSelected }">
              <input type="checkbox" :checked="isSelected" @click="select(!isSelected)" />
            </template>
          </v-list-item>
        </template>
      </v-list>
      {{ selected }}

open on programmatic activate

      <v-list v-model:active="openOnSelect" v-model:opened="openStuff" active-strategy="multiple" ref="listRef">
        <v-list-group>
          <template #header="props">
            <v-list-item v-bind="props" title="group 1" />
          </template>
          <v-list-item value="a" title="a" />
          <v-list-item value="b" title="b" />
        </v-list-group>
        <v-list-group>
          <template #header="props">
            <v-list-item v-bind="props" title="group 2" />
          </template>
          <v-list-item value="c" title="c" />
          <v-list-item value="d" title="d" />
        </v-list-group>
        <v-list-group>
          <template #header="props">
            <v-list-item v-bind="props" title="group 3" />
          </template>
          <v-list-item value="e" title="e" />
          <v-list-item value="f" title="f" />
          <v-list-group>
            <template #header="props">
              <v-list-item v-bind="props" title="group 4" />
            </template>
            <v-list-item value="g" title="g" />
            <v-list-item value="h" title="h" />
          </v-list-group>
        </v-list-group>
      </v-list>
      <v-btn @click="activateStuff">activate</v-btn>

select strategies

      <select v-model="selectStrategy">
        <option value="single-leaf">single-leaf</option>
        <option value="leaf">leaf</option>
      </select>
      <v-list :items="listItems" :select-strategy="selectStrategy" />

open strategies

      <select v-model="openStrategy">
        <option value="single">single</option>
        <option value="multiple">multiple</option>
      </select>
      <v-list :items="listItems" :open-strategy="openStrategy" />

router view

      <router-view />

  </v-app>
</template>


---
nav: Custom Overlays
meta:
  title: Overlay component
  description: The overlay component makes it easy to create a scrim over components or your entire application.
  keywords: overlays, vuetify overlay component, vue overlay component
related:
  - /components/dialogs/
  - /components/menus/
  - /components/tooltips/
---

# Overlays

`v-overlay` is the base for components that float over the rest of the page, such as `v-menu` and `v-dialog`. It can also be used on its own and comes with everything you need to create a custom popover component.

<entry />

## Usage

In its simplest form, the `v-overlay` component will add a dimmed layer over your application.

<example file="v-overlay/usage" />

## API

<api-inline />

## Activator

Overlays can be opened with v-model, or by clicking or hovering on an activator element. An activator is mandatory for the connected locationLocation strategy. The activator element (if present) will also be used by some transitions to slide or scale from the activator's location instead of the middle of the screen.

Related props:

- `activator`
- `activatorProps`
- `openOnClick`
- `openOnHover`
- `openOnFocus`
- `closeDelay`
- `openDelay`

### Activator prop

The simplest way of providing an activator. Can be a CSS selector to pass to `document.querySelector()`, a component instance, or a HTMLElement. The string `"parent"` is also accepted to automatically bind to the parent element.

```html
<v-overlay activator="#id" />
<v-overlay activator=".class" />
<v-overlay :activator="elementRef" />
<v-btn>
  <v-overlay activator="parent" />
</v-btn>
```

### Activator slot

For more manual control, the slot can be used instead. `props` is an object containing all the relevant ARIA attributes and event handlers, and must be applied to the target element with `v-bind` for the component to work correctly.

```html
<v-overlay>
  <template #activator="{ isActive, props }">
    <v-btn v-bind="props">Overlay is {{ isActive ? 'open' : 'closed' }}</v-btn>
  </template>
</v-overlay>
```

## Location Strategies

### Static (default)

`location-strategy="static"`

Overlay content is absolutely positioned to the center of its container by default.

### Connected

`location-strategy="connected"`

The connected strategy is used by [v-menu](/components/menus) and [v-tooltip](/components/tooltips) to attach the overlay content to an activator element.

`location` selects a point on the activator, and `origin` a point on the overlay content. The content element will be positioned so the two points overlap.

<example file="v-overlay/connected-playground" />

## Scroll Strategies

### Block (default)

`scroll-strategy="block"`

Scrolling is blocked while the overlay is active, and the scrollbar is hidden. If `contained` is also set, scrolling will only be blocked up to the overlay's [`offsetParent`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetParent).

<example file="v-overlay/scroll-block" />

### Close

`scroll-strategy="close"`

Scrolling when the overlay is active will de-activate it.

<example file="v-overlay/scroll-close" />

### Reposition

`scroll-strategy="reposition"`

When using the `connected` location strategy, this scroll strategy will reposition the overlay element to always respect the activator location.

<example file="v-overlay/scroll-reposition" />

### None

`scroll-strategy="none"`

No scroll strategy is used.

<example file="v-overlay/scroll-none" />

## Examples

### Props

#### Contained

A **contained** overlay is positioned absolutely and contained inside its parent element.

<example file="v-overlay/prop-contained" />

### Misc

#### Advanced

Using the [v-hover](/components/hover), we are able to add a nice scrim over the information card with additional actions the user can take.

<example file="v-overlay/misc-advanced" />

#### Loader

Using the `v-overlay` as a background, add a progress component to easily create a custom loader.

<example file="v-overlay/misc-loader" />

<backmatter />


@include tools.density('v-input', $input-density) using ($modifier)

--v-field-padding-bottom: #{16px + $modifier * .5}

--v-input-control-height: #{$field-control-height + $modifier}

--v-field-padding-bottom: #{$field-control-padding-bottom + $modifier * .5}


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

exports[`VItemGroup should render with a specified tag when the tag prop is provided with a value 1`] = `
<button class="v-item-group v-theme--light">
</button>
`;


@use '../../styles/settings';

// VLabel
$label-color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity)) !default;
$label-disabled-color: rgba(var(--v-theme-on-surface), var(--v-disabled-opacity)) !default;
$label-display: inline-flex !default;
$label-error-color: rgb(var(--v-theme-error)) !default;
$label-font-size: 1rem !default;
$label-letter-spacing: .009375em !default;


@use '../../styles/tools'
@use './variables' as *

.v-sheet
  display: block

  @include tools.border($sheet-border...)
  @include tools.elevation($sheet-elevation)
  @include tools.position($sheet-positions)
  @include tools.rounded($sheet-border-radius)
  @include tools.theme($sheet-theme...)

  &--rounded
    @include tools.rounded($sheet-rounded-border-radius)


&--variant-outlined,
&--single-line,
&--no-label
--v-field-padding-top: 0px

@use 'sass:map'
@use 'sass:selector'
@use '../../styles/settings'
@use '../../styles/tools'
@use './variables' as *
@use '../VInput/variables' as *

/* region INPUT */
.v-field
  display: grid
  grid-template-areas: "prepend-inner field clear append-inner"
  grid-template-columns: min-content minmax(0, 1fr) min-content min-content
  font-size: $field-font-size
  letter-spacing: $field-letter-spacing
  max-width: $field-max-width
  border-radius: $field-border-radius
  contain: layout
  flex: 1 0
  grid-area: control
  position: relative

  &--disabled
    opacity: var(--v-disabled-opacity)
    pointer-events: none

  --v-field-padding-start: #{$field-control-padding-start}
  --v-field-padding-end: #{$field-control-padding-end}
  --v-field-padding-top: #{$field-control-padding-top}
  --v-field-padding-bottom: #{$field-control-padding-bottom}

/* endregion */
/* region MODIFIERS */
.v-field
  &--prepended
    padding-inline-start: $field-control-affixed-padding

  &--appended
    padding-inline-end: $field-control-affixed-padding

  &--variant-solo
    background: $field-control-solo-background
    border-color: transparent
    color: $field-control-solo-color

    @include tools.elevation($field-control-solo-elevation)

  &--variant-filled
    border-bottom-left-radius: 0
    border-bottom-right-radius: 0

  &--variant-plain,
  &--variant-underlined
    border-radius: 0
    padding: 0
    --v-field-padding-start: 0px
    --v-field-padding-end: 0px
    --v-field-padding-top: 6px
    --v-field-padding-bottom: 2px

  &--variant-outlined,
  &--single-line
    --v-field-padding-top: 0px
    $root: &

    @at-root
      @include tools.density('v-input', $input-density) using ($modifier)
        @at-root #{selector.nest(&, $root)}
          --v-field-padding-bottom: #{16px + $modifier * .5}

  &--variant-solo,
  &--variant-filled
    $root: &

    @at-root
      @include tools.density('v-input', $input-density) using ($modifier)
        @at-root #{selector.nest(&, $root)}
          --v-input-control-height: #{$field-control-height + $modifier}
          --v-field-padding-bottom: #{$field-control-padding-bottom + $modifier * .5}

  &--variant-plain,
  &--variant-underlined
    $root: &

    @at-root
      @include tools.density('v-input', $input-density) using ($modifier)
        @at-root #{selector.nest(&, $root)}
          --v-input-control-height: #{$field-control-underlined-height + $modifier}

  // These are separate so they can override the default variant styles
  &--prepended
    --v-field-padding-start: #{$field-control-affixed-inner-padding}

  &--appended
    --v-field-padding-end: #{$field-control-affixed-inner-padding}

/* endregion */
/* region ELEMENTS */
.v-field__input
  color: inherit
  display: flex
  flex-wrap: wrap
  letter-spacing: $field-letter-spacing
  opacity: $field-input-opacity
  min-height: var(--v-input-control-height, $input-control-height)
  padding-inline-start: var(--v-field-padding-start)
  padding-inline-end: var(--v-field-padding-end)
  padding-top: calc(var(--v-field-padding-top, $field-control-padding-top) + var(--v-input-padding-top, 0))
  padding-bottom: var(--v-field-padding-bottom, $field-control-padding-bottom)
  width: 100%

  input
    letter-spacing: inherit

  @at-root
    & input::placeholder,
    input#{&}::placeholder,
    textarea#{&}::placeholder
      color: currentColor
      opacity: var(--v-disabled-opacity)

  &:focus,
  &:active
    outline: none

  // Remove Firefox red outline
  &:invalid
    box-shadow: none

.v-field__field
  flex: 1 0
  grid-area: field
  position: relative
  align-items: flex-start
  display: flex

/* endregion */
/* region AFFIXES */
.v-field__prepend-inner
  grid-area: prepend-inner
  padding-inline-end: var(--v-field-padding-after)

.v-field__clearable
  grid-area: clear

.v-field__append-inner
  grid-area: append-inner
  padding-inline-start: var(--v-field-padding-after)

.v-field__prepend-inner,
.v-field__append-inner,
.v-field__clearable
  align-items: flex-start
  display: flex
  padding-top: var(--v-input-padding-top, $field-control-padding-top)

.v-field__prepend-inner,
.v-field__append-inner
  .v-field--focused &
    opacity: 1

.v-field__prepend-inner,
.v-field__append-inner,
.v-field__clearable
  > .v-icon
    opacity: var(--v-medium-emphasis-opacity)

  .v-field--disabled &,
  .v-field--error &
    > .v-icon
      opacity: 1

  .v-field--error:not(.v-field--disabled) &
    > .v-icon
      color: rgb(var(--v-theme-error))

.v-field__clearable
  cursor: pointer
  opacity: 0
  margin-inline-start: $field-clearable-margin
  margin-inline-end: $field-clearable-margin
  transition: $field-transition-timing
  transition-property: opacity, transform, width

  .v-field--focused &,
  .v-field--persistent-clear &
    opacity: 1

  @media (hover: hover)
    .v-field:hover &
      opacity: 1

/* endregion */
/* region LABEL */
.v-label.v-field-label
  contain: layout paint
  margin-inline-start: var(--v-field-padding-start)
  margin-inline-end: var(--v-field-padding-end)
  max-width: calc(100% - var(--v-field-padding-start) - var(--v-field-padding-end))
  pointer-events: none
  position: absolute
  top: var(--v-input-padding-top, $field-control-padding-top)
  transform-origin: left center
  transition: $field-transition-timing
  transition-property: opacity, transform

  .v-field--active &
    visibility: hidden

  .v-field--disabled &,
  .v-field--focused &,
  .v-field--error &
    opacity: 1

  .v-field--error:not(.v-field--disabled) &
    color: rgb(var(--v-theme-error))

  &--floating
    --v-field-label-scale: #{$field-label-floating-scale}em
    font-size: var(--v-field-label-scale)
    visibility: hidden
    max-width: 100%

    .v-field.v-field--active &
      visibility: visible

    .v-field--variant-solo &,
    .v-field--variant-filled &
      $root: &

      @at-root
        @include tools.density('v-input', $input-density) using ($modifier)
          @at-root #{selector.nest(&, $root)}
            top: 7px + $modifier * .25

    .v-field--variant-plain &,
    .v-field--variant-underlined &
      transform: translateY(-16px)
      margin: 0

    .v-field--variant-outlined &
      transform: translateY(-50%)
      transform-origin: center
      position: static
      margin: 0 4px

/* endregion */
/* region OUTLINE */
.v-field__outline
  --v-field-border-width: #{$field-border-width}
  --v-field-border-opacity: #{$field-outline-opacity}
  align-items: stretch
  contain: layout
  display: flex
  height: 100%
  left: 0
  pointer-events: none
  position: absolute
  right: 0
  width: 100%

  @media (hover: hover)
    .v-field:hover &
      --v-field-border-opacity: var(--v-high-emphasis-opacity)

  .v-field--error:not(.v-field--disabled) &
    color: rgb(var(--v-theme-error))

  .v-field.v-field--focused &,
  .v-input.v-input--error &
    --v-field-border-opacity: 1

  .v-field--variant-outlined.v-field--focused &
    --v-field-border-width: #{$field-focused-border-width}

  .v-field--variant-filled &,
  .v-field--variant-underlined &
    &::before
      border-style: solid
      border-width: 0 0 var(--v-field-border-width)
      opacity: var(--v-field-border-opacity)
      transition: opacity $field-subtle-transition-timing
      @include tools.absolute(true)

  .v-field--variant-filled &,
  .v-field--variant-underlined &
    &::after
      border-color: currentColor
      border-style: solid
      border-width: 0 0 $field-focused-border-width
      transform: scaleX(0)
      transition: transform $field-transition-timing
      @include tools.absolute(true)

      @at-root #{selector.append('.v-field--focused', &)}
        transform: scaleX(1)

  .v-field--variant-outlined &
    border-radius: inherit

    &__start,
    &__notch::before,
    &__notch::after,
    &__end
      border: 0 solid currentColor
      opacity: var(--v-field-border-opacity)
      transition: opacity $field-subtle-transition-timing

    &__start
      flex: 0 0 $field-control-affixed-padding
      border-top-width: var(--v-field-border-width)
      border-bottom-width: var(--v-field-border-width)
      border-inline-start-width: var(--v-field-border-width)

      @include tools.ltr()
        border-radius: $field-border-radius 0 0 $field-border-radius

      @include tools.rtl()
        border-radius: 0 $field-border-radius $field-border-radius 0

    &__notch
      flex: none
      position: relative
      border-width: 0 0 var(--v-field-border-width)

      &::before,
      &::after
        opacity: var(--v-field-border-opacity)
        transition: opacity $field-subtle-transition-timing

        @include tools.absolute(true)

      &::before
        border-width: var(--v-field-border-width) 0 0

      &::after
        bottom: 0
        border-width: 0 0 var(--v-field-border-width)

      @at-root #{selector.append('.v-field--active', &)}
        &::before
          opacity: 0

    &__end
      flex: 1
      border-top-width: var(--v-field-border-width)
      border-bottom-width: var(--v-field-border-width)
      border-inline-end-width: var(--v-field-border-width)

      @include tools.ltr()
        border-radius: 0 $field-border-radius $field-border-radius 0

      @include tools.rtl()
        border-radius: $field-border-radius 0 0 $field-border-radius
/* endregion */
/* region LOADER */
.v-field__loader
  bottom: 0
  left: 0
  position: absolute
  right: 0
  width: 100%

/* endregion */
/* region OVERLAY */
.v-field--variant-filled
  .v-field__overlay
    background-color: currentColor
    border-radius: inherit
    opacity: $field-overlay-filled-opacity
    pointer-events: none
    transition: opacity $field-subtle-transition-timing
    @include tools.absolute()

  &.v-field--has-background .v-field__overlay
    opacity: 0

  @media (hover: hover)
    &:hover .v-field__overlay
      opacity: calc(#{$field-overlay-filled-opacity + map.get(settings.$states, 'hover')} * var(--v-theme-overlay-multiplier))

  &.v-field--focused .v-field__overlay
    opacity: calc(#{$field-overlay-filled-opacity + map.get(settings.$states, 'focus')} * var(--v-theme-overlay-multiplier))

/* endregion */
/* region MODIFIERS */
.v-field--reverse
  .v-field__input
    @include tools.ltr()
      text-align: right

    @include tools.rtl()
      text-align: left

.v-field--variant-filled,
.v-field--variant-underlined
  .v-input--disabled &
    .v-field__outline::before
      border-image: repeating-linear-gradient(to right, $field-disabled-color 0px, $field-disabled-color 2px, transparent 2px, transparent 4px) 1 repeat

.v-field--loading
  .v-field__outline::after,
  .v-field__outline::before
    opacity: 0

/* endregion */


module.exports = {
  'v-list-item-action': {
    slots: [
      {
        name: 'default',
        props: undefined,
      },
    ],
  },
}


<template>
  <v-card
    dark
    flat
  >
    <v-toolbar
      flat
      height="72"
    >
      <v-switch
        v-model="$vuetify.theme.dark"
        hint="This toggles the global state of the Vuetify theme"
        inset
        label="Vuetify Theme Dark"
        persistent-hint
      ></v-switch>
    </v-toolbar>

    <v-card-text>
      <v-list>
        <v-subheader>I inherit dark from my parent</v-subheader>

        <v-list-item
          v-for="item in items"
          :key="item"
        >
          <v-list-item-title v-text="item"></v-list-item-title>
        </v-list-item>
      </v-list>

      <v-divider class="mb-y"></v-divider>

      <v-theme-provider root>
        <v-list rounded="b">
          <v-subheader>
            I inherit from the root

             $vuetify.theme.dark
          </v-subheader>

          <v-list-item
            v-for="item in items"
            :key="item"
          >
            <v-list-item-title v-text="item"></v-list-item-title>
          </v-list-item>
        </v-list>
      </v-theme-provider>
    </v-card-text>
  </v-card>
</template>


<template>
  <div class="text-center">
    <v-menu
      v-model="menu"
      :close-on-content-click="false"
      location="end"
    >
      <template v-slot:activator="{ props }">
        <v-btn
          color="indigo"
          v-bind="props"
        >
          Menu as Popover
        </v-btn>
      </template>

      <v-card min-width="300">
        <v-list>
          <v-list-item
            prepend-avatar="https://cdn.vuetifyjs.com/images/john.jpg"
            title="John Leider"
            subtitle="Founder of Vuetify"
          >
            <template v-slot:append>
              <v-btn
                variant="text"
                :class="fav ? 'text-red' : ''"
                icon="mdi-heart"
                @click="fav = !fav"
              ></v-btn>
            </template>
          </v-list-item>
        </v-list>

        <v-divider></v-divider>

        <v-list>
          <v-list-item>
            <v-switch
              v-model="message"
              color="purple"
              label="Enable messages"
              hide-details
            ></v-switch>
          </v-list-item>

          <v-list-item>
            <v-switch
              v-model="hints"
              color="purple"
              label="Enable hints"
              hide-details
            ></v-switch>
          </v-list-item>
        </v-list>

        <v-card-actions>
          <v-spacer></v-spacer>

          <v-btn
            text
            @click="menu = false"
          >
            Cancel
          </v-btn>
          <v-btn
            color="primary"
            text
            @click="menu = false"
          >
            Save
          </v-btn>
        </v-card-actions>
      </v-card>
    </v-menu>
  </div>
</template>

<script>
  export default {
    data: () => ({
      fav: true,
      menu: false,
      message: false,
      hints: true,
    }),
  }
</script>


--v-input-control-height: #{$field-control-height + $modifier}

--v-field-padding-bottom: #{$field-control-padding-bottom + $modifier * .5}

keep keep keep keep keep replace replace keep keep keep

--v-field-padding-bottom: #{16px + $modifier * .5}


<template>
  <v-container fluid>
    <v-checkbox
      v-model="checkbox1"
      :label="`Checkbox 1: ${checkbox1.toString()}`"
    ></v-checkbox>
    <v-checkbox
      v-model="checkbox2"
      :label="`Checkbox 2: ${checkbox2.toString()}`"
    ></v-checkbox>
  </v-container>
</template>

<script>
  export default {
    data () {
      return {
        checkbox1: true,
        checkbox2: false,
      }
    },
  }
</script>


// @ts-nocheck
/* eslint-disable */

// import VCalendarDaily from '../VCalendarDaily'
import {
  mount,
  Wrapper,
  MountOptions,
} from '@vue/test-utils'
// import { ExtractVue } from '../../../util/mixins'

describe.skip('VCalendarDaily', () => {
  type Instance = ExtractVue<typeof VCalendarDaily>
  let mountFunction: (options?: MountOptions<Instance>) => Wrapper<Instance>
  beforeEach(() => {
    mountFunction = (options?: MountOptions<Instance>) => {
      return mount(VCalendarDaily, {
        ...options,
        mocks: {
          $vuetify: {
            lang: {
              current: 'en-US',
            },
          },
        },
      })
    }
  })

  it('should render component and have v-calendar-daily class', async () => {
    const wrapper = mountFunction({
      propsData: {
        start: '2019-01-29',
        end: '2019-02-04',
      },
    })

    expect(wrapper.classes('v-calendar-daily')).toBeTruthy()
    expect(wrapper.html()).toMatchSnapshot()
  })

  it('should compute scrollPush on init', async () => {
    const wrapper = mountFunction({
      propsData: {
        start: '2019-01-29',
        end: '2019-02-04',
      },
    })

    jest.spyOn(wrapper.vm, 'getScrollPush').mockImplementation(_ => 123)

    expect(wrapper.vm.scrollPush).toBe(0)
    expect(wrapper.vm.getScrollPush).not.toHaveBeenCalled()
    await wrapper.vm.$nextTick()
    expect(wrapper.vm.getScrollPush).toHaveBeenCalled()
    expect(wrapper.vm.scrollPush).toBe(123)
  })

  it('should compute scrollPush properly', async () => {
    const wrapper = mountFunction({
      propsData: {
        start: '2019-01-29',
        end: '2019-02-04',
      },
    })

    expect(wrapper.vm.getScrollPush()).toBe(0)

    Object.defineProperty(wrapper.vm.$refs.scrollArea, 'offsetWidth', { value: 100 })
    Object.defineProperty(wrapper.vm.$refs.pane, 'offsetWidth', { value: 25 })

    expect(wrapper.vm.getScrollPush()).toBe(75)
  })

  it('should render correctly with intervalMinutes prop', async () => {
    const wrapper = mountFunction({
      propsData: {
        start: '2019-01-29',
        end: '2019-02-04',
        intervalMinutes: 40,
      },
    })

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

  it('should render correctly with maxDays prop', async () => {
    const wrapper = mountFunction({
      propsData: {
        start: '2019-01-29',
        end: '2019-02-04',
        maxDays: 5,
      },
    })

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

  // TODO: Re-enable once test can be done without breaking travis
  it.skip('should render correctly without shortIntervals prop', async () => {
    const wrapper = mountFunction({
      propsData: {
        start: '2019-01-29',
        end: '2019-02-04',
        shortIntervals: false,
      },
    })

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

  it('should render correctly with intervalHeight prop', async () => {
    const wrapper = mountFunction({
      propsData: {
        start: '2019-01-29',
        end: '2019-02-04',
        intervalHeight: 70,
      },
    })

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

  it('should render correctly with firstInterval prop', async () => {
    const wrapper = mountFunction({
      propsData: {
        start: '2019-01-29',
        end: '2019-02-04',
        firstInterval: 2,
      },
    })

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

  it('should render correctly with intervalCount prop', async () => {
    const wrapper = mountFunction({
      propsData: {
        start: '2019-01-29',
        end: '2019-02-04',
        intervalCount: 12,
      },
    })

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

  it('should use custom interval formatter and render correctly', async () => {
    const wrapper = mountFunction({
      propsData: {
        start: '2019-01-29',
        end: '2019-02-04',
        intervalFormat: jest.fn(x => `test: ${x.date} ${x.time}`),
      },
    })

    expect(wrapper.html()).toMatchSnapshot()
    expect(wrapper.vm.intervalFormat).toHaveBeenCalled()
  })

  it('should use custom interval style function and render correctly', async () => {
    const wrapper = mountFunction({
      propsData: {
        start: '2019-01-29',
        end: '2019-02-04',
        intervalStyle: jest.fn(x => ({
          opacity: x.hour / 24,
        })),
      },
    })

    expect(wrapper.html()).toMatchSnapshot()
    expect(wrapper.vm.intervalStyle).toHaveBeenCalled()
  })

  it('should use custom showIntervalLabel function and render correctly', async () => {
    const wrapper = mountFunction({
      propsData: {
        start: '2019-01-29',
        end: '2019-02-04',
        showIntervalLabel: jest.fn(x => (x.hour % 2 === 0)),
      },
    })

    expect(wrapper.html()).toMatchSnapshot()
    expect(wrapper.vm.showIntervalLabel).toHaveBeenCalled()
  })
})


### Migrating from v1.5.x to v2.0.x

Version 2 contains non backwards compatible breaking changes. This includes previously deprecated functionality from v1.x.x. These breaking changes are noted in the console for the corresponding components.

The existing grid is still operational and has an [eslint plugin](https://github.com/vuetifyjs/eslint-plugin-vuetify) to help with migration. This plugin can also be used to help upgrade to the [**new grid**](https://vuetifyjs.com/components/grids).

### Bootstrap
Vuetify must now be instantiated and passed to the initial Vue instance. This is similar to how **vue-router** and **vuex** are bootstrapped.

#### Vue-CLI 3 Vuetify plugin install
```js
// v1.5
// src/plugins/vuetify.js

import Vue from 'vue'
import Vuetify from 'vuetify/lib'
import 'vuetify/src/stylus/app.styl'

Vue.use(Vuetify, {
  iconfont: 'md',
})

// src/main.js
import Vue from 'vue'
import './plugins/vuetify'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')
```

```js
// v2.0
// src/plugins/vuetify.js

import Vue from 'vue';
import Vuetify from 'vuetify/lib';

Vue.use(Vuetify);

export default new Vuetify({
  icons: {
    iconfont: 'mdi',
  },
});

// src/main.js
import Vue from 'vue'
import App from './App.vue'
import vuetify from './plugins/vuetify';

Vue.config.productionTip = false

new Vue({
  vuetify,
  render: h => h(App)
}).$mount('#app')
```

#### Full Install
```js
// v1.5

import Vue from 'vue'
import Vuetify from 'vuetify'
import 'vuetify/src/stylus/main.styl'

const opts = { ... }
Vue.use(Vuetify, opts)

new Vue(...).$mount('#app')
```

```js
// v2.0

import Vue from 'vue'
import Vuetify from 'vuetify'
import 'vuetify/dist/vuetify.min.css'

const opts = { ... }
Vue.use(Vuetify)

new Vue({
  vuetify: new Vuetify(opts)
}).$mount('#app')
```

#### A-La-Carte Install (vuetify-loader)
```js
// v1.5

import Vue from 'vue'
import Vuetify from 'vuetify/lib'
import 'vuetify/src/stylus/main.styl'

const opts = { ... }
Vue.use(Vuetify, opts)

new Vue(...).$mount('#app')
```

```js
// v2.0

import Vue from 'vue'
import Vuetify from 'vuetify/lib'

const opts = { ... }
Vue.use(Vuetify)

new Vue({
  vuetify: new Vuetify(opts)
}).$mount('#app')
```

### Framework
The following components are now **lazy** by default. This means they will not render their content until they are explicitly activated. This drastically improves performance but may not be wanted depending upon your application's needs _(i.e. For SEO purposes)_. To return to the previous behavior, use the **eager** prop.
  * `v-badge`
  * `v-menu`
  * `v-tooltip`
  * `v-dialog`
  * `v-bottom-sheet`

`vuetify/lib` is now compiled to **es6**. This means supporting IE requires [`transpileDependencies`](https://cli.vuejs.org/config/#transpiledependencies) or similar to be used, along with `@babel/polyfill`. Transpile dependencies are automatically added when using vue-cli-3. If you have an older version, you can simple add 'vuetify' to the list.

### Theme
Now supports dark/light theme variants. The _dark_ property has been moved into the theme property. Will dynamically change when toggling **$vuetify.theme.dark**. If only using one variant, you only need to define its colors.

```js
// v1.5

const opts = {
  dark: true,
  theme: {
    primary: '...',
    ...
  }
}
```

```js
// v2.0

const opts = {
  theme: {
    dark: true,
    themes: {
      light: {
        primary: '...',
        ...
      },
      dark: {
        primary: '...',
        ...
      }
    }
  }
}
```

In order to disable the theme style sheet creation, you must use the **disable** property of the theme object.

```js
// v1.5

const opts = {
  theme: false
}
```

```js
// v2.0
const opts = {
  theme: { disable: true }
}
```

### Icons
Icon and iconfont declaration is now scoped under the **icons** property.

```js
// v1.5

const opts = {
  iconfont: 'fa4',
  icons: { ... }
}
```

```js
// v2.0

const opts = {
  icons: {
    iconfont: 'fa4',
    values: { ... }
  }
}
```

* Now defaults to use [mdi](https://materialdesignicons.com/) icons. For information on how to install please navigate [here](https://vuetifyjs.com/features/icon-fonts/#material-design-icons)
* Is now located under the **icons** property of the Vuetify options

If you want to use a custom iconfont, you must set it up in the initial Vuetify options now.

```js
// v1.5

import Vue from 'vue'
import Vuetify from 'vuetify'

Vue.use(Vuetify, {
  iconfont: 'fa4'
})
```

```js
// v2.0

import Vue from 'vue'
import Vuetify from 'vuetify'

Vue.use(Vuetify, {
  icons: {
    iconfont: 'fa4'
  }
})
```

### Goto (scrolling helper)
Import location has changed. Must be explicitly bootstrapped with the Vuetify instance to use in **vue-router** scroll-behavior. Example of how to do this [here](https://github.com/vuetifyjs/vuetify/blob/next/packages/docs/src/vuetify/index.js#L33). Reference documentation for scroll-behavior usage [here](https://next.vuetifyjs.com/en/framework/scroll#using-with-router).

```js
// v1.5

import goTo from 'vuetify/es5/components/Vuetify/goTo'
```

```js
// v2.0

import goTo from 'vuetify/lib/services/goto'
```

### Lang
The _translator_ function **t** is now nested under the lang property.

```js
// v1.5

this.$vuetify.t(...)
```

```js
// v2.0

this.$vuetify.lang.t(...)
```

### Grid
The grid has been rebuilt modeled after bootstrap. The existing grid still works and needs some slight modifications. [Kael](https://github.com/KaelWD) has created an eslint plugin to help with this process.
* [eslint-plugin-vuetify](https://github.com/vuetifyjs/eslint-plugin-vuetify) **to fix most of these for you**
* Spacing helpers have changed to represent the number of 4px intervals from 0-12 (0-48px)
    * eg. px-7 is 7 * 4 = 28px
    * 3 → 4
    * 4 → 6
    * 5 → 12
* Most "breakpointed" and "non-breakpointed" helpers have been normalised, eg. `.text-xs-center` is now `text-center` as it applies to all screen widths unless overridden
* Children of `.d-flex` no longer have extra flex rules applied. This can be done manually with `.flex-grow-1`
* Helper classes changed:
  * `.fluid` → `.container--fluid`
  * `.scroll-y` → `.overflow-y-auto`
  * `.hide-overflow` → `.overflow-hidden`
  * `.show-overflow` → `.overflow-visible`
  * `.no-wrap` → `.text-no-wrap`
  * `.ellipsis` → `.text-truncate`
  * `.left` → `.float-left`
  * `.right` → `.float-right`
* `<v-layout row>` should not be used as `.row` is now part of the new grid instead (#7956)

Use the following regex to update spacing classes:
```
find: ([\s"][mp][axytblr])-5
replace: $1-12

find: ([\s"][mp][axytblr])-4
replace: $1-6

find: ([\s"][mp][axytblr])-3
replace: $1-4
```
For examples on how the v2 grid compares to v1.5, check out this [github gist](https://gist.github.com/johnleider/207f63c9d30fb77042a0bb0258c5ab32).
### Styles
The main framework styles are now imported automatically.

```js
// v1.5
// src/plugins/vuetify.js

import 'vuetify/src/styles/main.sass' // can be removed
```

Must install the **sass** package

```bash
yarn add sass -D
// OR
npm install sass -D
```

**Do not install _node-sass_**, it is not the correct library.

### Typography
The root font-size (per MD2 specification) is now _16px_.
* The following typography classes have been replaced:
  * subheading → subtitle-1

### Event names
All event names has been changed from camelCase to kebab-case:
- `update:searchInput` → `update:search-input`
- `update:inputValue` → `update:input-value`
- `update:miniVariant` → `update:mini-variant`
- `update:pickerDate` → `update:picker-date`
- `update:selectingYear` → `update:selecting-year`
- `tableDate` → `update:table-date`
- `update:returnValue` → `update:return-value`

### Activators
* Components with activators, `v-tooltip`, `v-menu`, `v-dialog`, `v-list-group` and `v-bottom-sheet` must now be bound using the new [v-slot](https://vuejs.org/v2/guide/components-slots.html#Named-Slots) syntax
  * Requires Vue@2.6
* We understand this is considerably more verbose than the v1.5 counterpart. We are still exploring ways to support the new **v-slot** in a more concise manner
  * You can find more information on the official Vue documentation for [Destructuring Slot Props](https://vuejs.org/v2/guide/components-slots.html#Destructuring-Slot-Props)
  * You can find more information on the official Vue documentation for [v-slot](https://vuejs.org/v2/guide/components-slots.html#Named-Slots)
* The upside to this change is it is easier to support nested activators and provide proper a11y support

#### Regular activator
```vue
<!-- v1.5 -->

<v-dialog>
  <v-btn slot="activator">...</v-btn>
</v-dialog>
```

```vue
<!-- v2.0 -->

<v-dialog>
  <template v-slot:activator="{ on }">
    <v-btn v-on="on">...</v-btn>
  </template>
</v-dialog>
```

#### Nested activator
```vue
<!-- v2.0 -->

<v-menu>
  <template v-slot:activator="{ on: menu }">
    <v-tooltip bottom>
      <template v-slot:activator="{ on: tooltip }">
        <v-btn
          color="primary"
          dark
          v-on="{ ...tooltip, ...menu }"
        >
          Dropdown w/ Tooltip
        </v-btn>
      </template>
      <span>Im A ToolTip</span>
    </v-tooltip>
  </template>
</v-menu>
```

### Unit tests
Testing with Vuetify is now similar to that of **vue-router** and **vuex**.

```js
// setup.js

import Vue from 'vue'
import Vuetify from 'vuetify'

Vue.use(Vuetify)
```

```js
// Component.spec.js

import { createLocalVue, mount } from '@vue/test-utils'
import Vuetify from 'vuetify'
import Component from 'path/to/my/component'

const localVue = createLocalVue()

describe('Component.vue', () => {
  let vuetify

  beforeEach(() => {
    vuetify = new Vuetify(...)
  })

  it('should...', () => {
    const wrapper = mount(Component, {
      localVue,
      vuetify
    })
  })
})
```

### Form Input Validation
All form inputs default to white when using the **dark** prop unless the application is _explicitly_ set to **dark** mode.

### Removed Component Properties
These are previous deprecations from earlier versions that have now been removed:

* `<v-text-field textarea>` will no longer render `<v-textarea>`
* `<v-select autocomplete>` will no longer render `<v-autocomplete>`
* `<v-select combobox>` will no longer render `<v-combobox>`
* `<v-select overflow>` will no longer render `<v-overflow-btn>`
* `<v-select segmented>` will no longer render `<v-overflow-btn segmented>`
* `<v-select editable>` will no longer render `<v-overflow-btn editable>`

### Individual Components
These are the changes required for existing components.

#### `v-app`
* Component classes have been prepended with **v-**. eg `.application` → `.v-application`
* The dark and light prop no longer have an effect on application theme variants

```vue
<!-- v1.5 src/App.vue -->

<template>
  <v-app dark>
    ...
  </v-app>
</template>
```

```js
// v2.0 src/plugins/vuetify.js

import Vue from 'vue'
import Vuetify from 'vuetify'

Vue.use(Vuetify)

export default new Vuetify({
  theme: { dark: true }
})
```

#### `v-alert`
* Alerts are visible by default

```vue
<!-- v1.5 -->

<template>
  <v-alert :value="true">
    ...
  </v-alert>
</template>
```

```vue
<!-- v2.0 -->

<template>
  <v-alert>
    ...
  </v-alert>
</template>
```

#### `v-carousel`
* The **cycle** prop is no longer implicit, must be defined in order to have screens switch

#### `v-btn`
* The **flat** prop is now **text**
* The **round** prop is now **rounded**
* No longer has explicit margin

#### `v-chip`
* **value** no longer controls visibility, use **active**
* **input** event emitted when clicking
* The **close** event is now **click:close**
* **@input** listener is now **@active.sync**

```vue
<!-- v1.5 -->

<v-chip :value="chip" @input="chip = $event">...</v-chip>
<v-chip v-model="chip">...</v-chip>
```

```vue
<!-- v2.0 -->

<v-chip :active="chip" @update:active="chip = $event">...</v-chip>
<v-chip :active.sync="active">...</v-chip>
```

#### `v-bottom-nav`
* Renamed from `v-bottom-nav` to `v-bottom-navigation`

#### `v-bottom-navigation`
* The **color** prop is now **background-color**
* The **color** prop now affects the active `<v-btn>` color

#### `v-bottom-sheet-transition`
* Component has been removed

_Developer notes: Was never explicitly listed in API_

#### `v-btn`
* The **flat** prop is now **text**
* The **round** prop is now **rounded**
* No longer has explicit margin

#### `v-card-media`
* Component has been removed

#### `v-carousel`
* The **cycle** prop is no longer implicit, must be defined in order to have screens switch

#### `v-chip`
* The **value** prop is now **active**
* **value** no longer controls visibility. **input** event emitted when clicking
* The **selected** prop is now **input-value** or **v-model**
* The **close** event is now **click:close**

#### `v-data-iterator` & `v-data-table`
Data table (and iterator) have been rewritten from the ground up to be both easier to use and to allow for more flexibilty in more advanced use cases. This has resulted in a number of breaking changes. Some of these are shared between both components while some are unique to each.

#### Shared
* **disable-initial-sort** has been removed. Neither component initially sorts data anymore. Use **sort-by** (or **options**) prop to sort
* **filter** prop has been removed. Instead use **custom-filter**. This was done in an effort to make custom filtering less confusing
  * The signature for **custom-filter** has changed from `(items: object[], search: string, filter: Filter): object[]` to `(value: any, search: string, item: any) => boolean`
* **pagination** prop has been removed. Instead use the separate props such as **page**, **sort-by**, etc. If you want to provide a single object you can use the new **options** prop instead. **NOTE**: The **options** prop has a different object structure than **pagination**. Check API docs for details
* **total-items** prop has been renamed to **server-items-length**
* **hide-actions** prop has been renamed to **hide-default-footer**. Also it no longer changes the visible items per page
* Props related to the default footer have been move to the **footer-props** prop. These are:
  * **prev-icon**
  * **next-icon**
  * **rows-per-page-items** → **items-per-page-options**
  * **rows-per-page-text** → **items-per-page-text**
* The **expand** prop has been removed

#### `v-data-iterator`
* The **content-tag**, **content-props**, **content-class** props have been removed. Instead simply use the default scoped slot to implement your intended markup.

#### `v-data-table`
* **items** slot has been renamed to **item**
* **headers** slot renamed to **header**
* **item** slot (and **header**) now require you to define a `<tr>` element. Previously this was optional
* **expand** slot renamed to **expanded-item**. It no longer includes an expansion transition, and the slot is inside the `<tr>` element so that you can define your own `<td>` columns. To get back to a similar look as in 1.5, you will need a `<td>` with *colspan* equal to the number of columns in your header
* **hide-header** has been renamed to **hide-default-header**
* **select-all** has been renamed to **show-select**. This will also render a checkbox on each item row as long as you are not defining a slot that overrides the internal row rendering (such as **item** or **body**)
* Props related to the default header have been moved to the **header-props** prop. These are:
  * **sort-icon**

#### `v-expansion-panel` et al
* Many components have been renamed and props moved
  * `v-expansion-panel` → `v-expansion-panels`
  * `v-expansion-panel-content` → `v-expansion-panel`
* New components
  * `v-expansion-panel-header`
  * `v-expansion-panel-content`

```vue
<!-- v1.5 -->

<v-expansion-panel>
  <v-expansion-panel-content
    v-for="(item,i) in 5"
    :key="i"
  >
    <template v-slot:header>Item</template>
    <v-card>
      ...
    </v-card>
  </v-expansion-panel-content>
</v-expansion-panel>
```

```vue
<!-- v2.0 -->

<v-expansion-panels>
  <v-expansion-panel
    v-for="(item,i) in 5"
    :key="i"
  >
    <v-expansion-panel-header>
      Item
    </v-expansion-panel-header>
    <v-expansion-panel-content>
      <v-card>
        ...
      </v-card>
    </v-expansion-panel-content>
  </v-expansion-panel>
</v-expansion-panels>
```

#### `v-footer`
* Now has explicit padding to match other similar MD components. Can be removed with the **padless** prop or a helper class, `class="pa-0"`

#### `v-jumbotron`
* Component has been removed

#### `v-list` et al
* Many components have been renamed
  * `v-list-tile` → `v-list-item`
  * `v-list-tile-action` → `v-list-item-action`
  * `v-list-tile-avatar` → `v-list-item-avatar`
  * `v-list-tile-content` → `v-list-item-content`
  * `v-list-tile-title` → `v-list-item-title`
  * `v-list-tile-sub-title` → `v-list-item-subtitle`
  * The **avatar** prop has been removed

#### `v-list-group`
* Can no longer use `v-list-item`s in the activator slot
  * listeners are passed through to the internal `v-list-item` for activators
  * use `v-list-item-content`/`v-list-item-title` etc instead

#### `v-navigation-drawer`
* Default width has been changed from 300px to 256px. You can adjust it using **width** prop.

#### `v-select`, `v-autocomplete`, `v-combobox`, `v-overflow-btn`
* Now passes **attributes** and **listeners** to **item** slot for proper a11y support (split from tile to match other implementations).

```vue
<!-- v1.5 -->

<v-select>
  <template v-slot:item="{ item, tile }">
    <v-list-tile v-bind="tile">
       ...
    </v-list-tile>
  </template>
</v-select>
```

```vue
<!-- v2.0 -->

<v-select>
  <template v-slot:item="{ item, attrs, on }">
    <v-list-item v-bind="attrs" v-on="on">
       ...
    </v-list-item>
  </template>
</v-select>
```

The item scoped slot value of `{ tile }` is now` { attrs, on }`. is now bound similar to the `v-menu` activator slot.

#### `v-select`
* No longer has a default **autocomplete** of on

#### `v-speed-dial`
* Icons no longer have inferred swapping for activator through css
* The activator slot will provide a model in the future

```vue
<!-- v1.5 -->

<v-speed-dial>
  <template v-slot:activator>
    <v-btn
      dark
      fab
    >
      <v-icon>account_circle</v-icon>
      <v-icon>close</v-icon>
   </v-btn>
  </template>
</v-speed-dial>
```

```vue
<!-- v2.0 -->

<v-speed-dial v-model="fab">
  <template v-slot:activator>
    <v-btn
      dark
      fab
    >
      <v-icon v-if="fab">account_circle</v-icon>
      <v-icon v-else>close</v-icon>
    </v-btn>
  </template>
</v-speed-dial>
```

#### `v-tabs`
* The **color** prop is now **background-color**. Color now affects the default text and slider color
* Various class names have been changed throughout
  * **v-tab__div** removed, use **v-tab**
  * **v-tab__item** → **v-tab**
  * **v-tabs__slider** → **v-tabs-slider**
  * **v-tabs__bar** → **v-tabs-bar**

#### `v-tabs-items`
* No longer implicitly inherits the `v-tabs` model when nested. Must have **:value** or **v-model** explicitly bound.

```vue
<!-- v1.5 -->

<v-tabs v-model="tabs">
  ...
  <v-tabs-items>
    ...
  </v-tabs-items>
</vtabs>
```

```vue
<!-- v2.0 -->

<v-tabs v-model="tabs">
  ...
  <v-tabs-items v-model="tabs">
    ...
  </v-tabs-items>
</vtabs>
```

_Developer notes: The tabs-items component does not have to be provided and is only necessary for custom implementations._

#### `v-text-field`
* The **mask** prop and functionality has been removed. Instead you can use 3rd party libraries such as `vue-the-mask`.

#### `v-text-field`, `v-select`, `v-textarea`, `v-autocomplete`, `v-combobox`
* The **box** prop is now **filled**

```vue
<!-- v1.5 -->

<v-text-field box></v-text-field>
```

```vue
<!-- v2.0 -->

<v-text-field filled></v-text-field>
```

#### `v-text-field`, `v-select`, `v-textarea`, `v-autocomplete`, `v-combobox`, `v-btn`, `v-alert`, `v-chip`
* The **outline** prop is now **outlined**

```vue
<!-- v1.5 -->

<v-btn outline></v-text-field>
<v-autocomplete outline></v-autocomplete>
<v-alert outline></v-alert>
```

```vue
<!-- v2.0 -->

<v-btn outlined></v-text-field>
<v-autocomplete outlined></v-autocomplete>
<v-alert outlined></v-alert>
```

#### `v-toolbar`
* All existing scrolling techniques and `app` functionality has been deprecated and moved to `v-app-bar`

<h2 id="i-need-help">🆘 I need help!</h2>

If you are stuck and need help, don't fret! We have a very large and dedicated community that is able to provide help 24/7. Come to the [#release-migration](https://discord.gg/QHWSAbA) channel.


border-radius: 0
padding: 0
&.v-field
--v-field-padding-start: 0px
--v-field-padding-end: 0px
--v-field-padding-top: 6px
--v-field-padding-bottom: 2px

@use 'sass:math'
@use 'sass:selector'
@use '../../styles/settings'
@use '../../styles/tools'
@use './variables' as *

.v-textarea
  .v-field__field
    --v-input-control-height: var(--v-textarea-control-height)

  .v-field__input
    $a: calc((var(--v-field-padding-top, 0) + var(--v-input-padding-top, 0)) - 6px)
    $b: calc(var(--v-field-padding-top, 0) + var(--v-input-padding-top, 0) + 4px)

    flex: 1 1 auto
    outline: none
    -webkit-mask-image: linear-gradient(to bottom, transparent, transparent $a, black $b)
    mask-image: linear-gradient(to bottom, transparent, transparent $a, black $b)

  &--auto-grow
    .v-field__input
      overflow: hidden

  &--no-resize
    .v-field__input
      resize: none

  &__sizer
    visibility: hidden
    position: absolute
    top: 0
    left: 0
    height: 0 !important
    min-height: 0 !important
    pointer-events: none

  .v-field--has-label
    textarea
      opacity: 0

  .v-field--active
    textarea
      opacity: 1

  textarea
    flex: 1
    min-width: 0
    transition: .15s opacity settings.$standard-easing

    &:focus,
    &:active
      outline: none

    // Remove Firefox red outline
    &:invalid
      box-shadow: none


/* eslint-disable sonarjs/no-identical-functions */
/// <reference types="../../../../types/cypress" />

// Components
import { VTimeline, VTimelineItem } from '../'

// Utilities
import { ref } from 'vue'

// Types
import type {
  TimelineAlign,
  TimelineSide,
  TimelineTruncateLine,
} from '../VTimeline'

describe('VTimeline', () => {
  describe('vertical', () => {
    it('should render', () => {
      cy.mount(() => (
        <VTimeline>
          <VTimelineItem key="1">
            {{
              default: () => 'Content',
              opposite: () => 'Opposite',
            }}
          </VTimelineItem>
          <VTimelineItem key="2">
            {{
              default: () =>
Content
,
              opposite: () => 'Opposite',
            }}
          </VTimelineItem>
        </VTimeline>
      ))

      cy.get('.v-timeline').should('have.class', 'v-timeline--vertical')
    })

    it('should support truncate-line', () => {
      const truncateLine = ref<TimelineTruncateLine>('none')
      cy.mount(() => (
        <VTimeline truncateLine={truncateLine.value}>
          <VTimelineItem key="1">
            {{
              default: () => 'Content',
              opposite: () => 'Opposite',
            }}
          </VTimelineItem>
          <VTimelineItem key="2">
            {{
              default: () =>
Content
,
              opposite: () => 'Opposite',
            }}
          </VTimelineItem>
        </VTimeline>
      ))

      cy.get('.v-timeline')
        .should('not.have.class', 'v-timeline--truncate-line-start')
        .should('not.have.class', 'v-timeline--truncate-line-end')
        .then(() => {
          truncateLine.value = 'start'
        })
        .get('.v-timeline')
        .should('have.class', 'v-timeline--truncate-line-start')
        .then(() => {
          truncateLine.value = 'end'
        })
        .get('.v-timeline')
        .should('have.class', 'v-timeline--truncate-line-end')
        .then(() => {
          truncateLine.value = 'both'
        })
        .get('.v-timeline')
        .should('have.class', 'v-timeline--truncate-line-end')
        .should('have.class', 'v-timeline--truncate-line-start')
    })

    it('should support align', () => {
      const align = ref<TimelineAlign>('center')

      cy.mount(() => (
        <VTimeline align={align.value}>
          <VTimelineItem key="1">
            {{
              default: () => (

Title

Subtitle

              ),
              opposite: () => 'Opposite',
            }}
          </VTimelineItem>
          <VTimelineItem key="2">
            {{
              default: () => (

Title

Subtitle

              ),
              opposite: () => 'Opposite',
            }}
          </VTimelineItem>
        </VTimeline>
      ))

      cy.get('.v-timeline')
        .should('have.class', 'v-timeline--align-center')
        .then(() => align.value = 'start')
        .get('.v-timeline')
        .should('have.class', 'v-timeline--align-start')
    })

    it('should support side', () => {
      const side = ref<TimelineSide>('start')

      cy.mount(() => (
        <VTimeline side={side.value}>
          <VTimelineItem key="1">
            {{
              default: () => (

Title

Subtitle

              ),
              opposite: () => 'Opposite',
            }}
          </VTimelineItem>
          <VTimelineItem key="2">
            {{
              default: () => (

Title

Subtitle

              ),
              opposite: () => 'Opposite',
            }}
          </VTimelineItem>
        </VTimeline>
      ))

      cy.get('.v-timeline')
        .should('have.class', 'v-timeline--side-start')
        .then(() => side.value = 'end')
        .get('.v-timeline')
        .should('have.class', 'v-timeline--side-end')
    })

    it('should support truncate-line and align', () => {
      cy.mount(() => (
        <VTimeline truncateLine="start" align="start">
          <VTimelineItem key="1">
            {{
              default: () => (

Title

Subtitle

              ),
              opposite: () => 'Opposite',
            }}
          </VTimelineItem>
          <VTimelineItem key="2">
            {{
              default: () => (

Title

Subtitle

              ),
              opposite: () => 'Opposite',
            }}
          </VTimelineItem>
        </VTimeline>
      ))

      cy.get('.v-timeline')
        .should('have.class', 'v-timeline--truncate-line-start')
        .should('have.class', 'v-timeline--align-start')
    })
  })

  describe('horizontal', () => {
    it('should render', () => {
      cy.mount(() => (
        <VTimeline direction="horizontal">
          <VTimelineItem key="1">
            {{
              default: () => 'Content',
              opposite: () => 'Opposite',
            }}
          </VTimelineItem>
          <VTimelineItem key="2">
            {{
              default: () =>
Content
,
              opposite: () => 'Opposite',
            }}
          </VTimelineItem>
        </VTimeline>
      ))

      cy.get('.v-timeline').should('have.class', 'v-timeline--horizontal')
    })

    it('should support truncate-line', () => {
      const truncateLine = ref<TimelineTruncateLine>('none')
      cy.mount(() => (
        <VTimeline direction="horizontal" truncateLine={truncateLine.value}>
          <VTimelineItem key="1">
            {{
              default: () => 'Content',
              opposite: () => 'Opposite',
            }}
          </VTimelineItem>
          <VTimelineItem key="2">
            {{
              default: () =>
Content
,
              opposite: () => 'Opposite',
            }}
          </VTimelineItem>
        </VTimeline>
      ))

      cy.get('.v-timeline')
        .should('not.have.class', 'v-timeline--truncate-line-start')
        .should('not.have.class', 'v-timeline--truncate-line-end')
        .then(() => {
          truncateLine.value = 'start'
        })
        .get('.v-timeline')
        .should('have.class', 'v-timeline--truncate-line-start')
        .then(() => {
          truncateLine.value = 'end'
        })
        .get('.v-timeline')
        .should('have.class', 'v-timeline--truncate-line-end')
        .then(() => {
          truncateLine.value = 'both'
        })
        .get('.v-timeline')
        .should('have.class', 'v-timeline--truncate-line-end')
        .should('have.class', 'v-timeline--truncate-line-start')
    })

    it('should support align', () => {
      const align = ref<TimelineAlign>('center')

      cy.mount(() => (
        <VTimeline direction="horizontal" align={align.value}>
          <VTimelineItem key="1">
            {{
              default: () => (

Title

Subtitle

              ),
              opposite: () => 'Opposite',
            }}
          </VTimelineItem>
          <VTimelineItem key="2">
            {{
              default: () => (

Title

Subtitle

              ),
              opposite: () => 'Opposite',
            }}
          </VTimelineItem>
        </VTimeline>
      ))

      cy.get('.v-timeline')
        .should('have.class', 'v-timeline--align-center')
        .then(() => align.value = 'start')
        .get('.v-timeline')
        .should('have.class', 'v-timeline--align-start')
    })

    it('should support side', () => {
      const side = ref<TimelineSide>('start')

      cy.mount(() => (
        <VTimeline direction="horizontal" side={side.value}>
          <VTimelineItem key="1">
            {{
              default: () => (

Title

Subtitle

              ),
              opposite: () => 'Opposite',
            }}
          </VTimelineItem>
          <VTimelineItem key="2">
            {{
              default: () => (

Title

Subtitle

              ),
              opposite: () => 'Opposite',
            }}
          </VTimelineItem>
        </VTimeline>
      ))

      cy.get('.v-timeline')
        .should('have.class', 'v-timeline--side-start')
        .then(() => side.value = 'end')
        .get('.v-timeline')
        .should('have.class', 'v-timeline--side-end')
    })
  })
})


// Components
import { VTimelineDivider } from './VTimelineDivider'

// Composables
import { IconValue } from '@/composables/icons'
import { makeElevationProps } from '@/composables/elevation'
import { makeRoundedProps } from '@/composables/rounded'
import { makeSizeProps } from '@/composables/size'
import { makeTagProps } from '@/composables/tag'

// Utilities
import type { PropType } from 'vue'
import { ref, watch } from 'vue'
import { convertToUnit, defineComponent, useRender } from '@/util'
import { makeDimensionProps, useDimension } from '@/composables/dimensions'

export const VTimelineItem = defineComponent({
  name: 'VTimelineItem',

  props: {
    density: String as PropType<'default' | 'compact'>,
    dotColor: String,
    fillDot: Boolean,
    hideDot: Boolean,
    hideOpposite: {
      type: Boolean,
      default: undefined,
    },
    icon: IconValue,
    iconColor: String,
    lineInset: [Number, String],

    ...makeRoundedProps(),
    ...makeElevationProps(),
    ...makeSizeProps(),
    ...makeTagProps(),
    ...makeDimensionProps(),
  },

  setup (props, { slots }) {
    const { dimensionStyles } = useDimension(props)

    const dotSize = ref(0)
    const dotRef = ref<VTimelineDivider>()
    watch(dotRef, newValue => {
      if (!newValue) return
      dotSize.value = newValue.$el.querySelector('.v-timeline-divider__dot')?.getBoundingClientRect().width ?? 0
    }, {
      flush: 'post',
    })

    useRender(() => (
      <div
        class={[
          'v-timeline-item',
          {
            'v-timeline-item--fill-dot': props.fillDot,
          },
        ]}
        style={{
          '--v-timeline-dot-size': convertToUnit(dotSize.value),
          '--v-timeline-line-inset': props.lineInset ? `calc(var(--v-timeline-dot-size) / 2 + ${convertToUnit(props.lineInset)})` : convertToUnit(0),
        }}
      >
        <div
          class="v-timeline-item__body"
          style={ dimensionStyles.value }
        >
          { slots.default?.() }
        </div>

        <VTimelineDivider
          ref={ dotRef }
          hideDot={ props.hideDot }
          icon={ props.icon }
          iconColor={ props.iconColor }
          size={ props.size }
          elevation={ props.elevation }
          dotColor={ props.dotColor }
          fillDot={ props.fillDot }
          rounded={ props.rounded }
          v-slots={{ default: slots.icon }}
        />

        { props.density !== 'compact' && (
          <div class="v-timeline-item__opposite">
            { !props.hideOpposite && slots.opposite?.() }
          </div>
        ) }
      </div>
    ))

    return {}
  },
})


<template>
  <v-timeline align="start">
    <v-timeline-item
      v-for="(year, i) in years"
      :key="i"
      :dot-color="year.color"
      size="small"
    >
      <template v-slot:opposite>
        <div
          :class="`pt-1 headline font-weight-bold text-${year.color}`"
          v-text="year.year"
        >

      </template>

        <h2 :class="`mt-n1 headline font-weight-light mb-4 text-${year.color}`">
          Lorem ipsum

          Lorem ipsum dolor sit amet, no nam oblique veritus. Commune scaevola imperdiet nec ut, sed euismod convenire principes at. Est et nobis iisque percipit, an vim zril disputando voluptatibus, vix an salutandi sententiae.

    </v-timeline-item>
  </v-timeline>
</template>


keep keep keep replace keep keep keep keep keep


---
nav: Mutation observer
meta:
  title: Mutation observer directive
  description: The mutation observer directive utilizes the Mutation observer API. It allows you to invoke a callback when targetted elements are updated.
  keywords: mutate, vuetify mutate directive, mutation observer directive, mutation observer
related:
  - /components/sheets/
  - /components/images/
  - /directives/intersect/
---

# Mutation observer

The `v-mutate` directive utilizes the [Mutation Observer API](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver). It provides an easy to use interface for detecting when elements are updated.

<entry />

## Usage

`v-mutate` is a simple interface for the [Mutation Observer API](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) that is implemented with [Vue directives](https://v3.vuejs.org/api/directives.html). There are two main ways to alter `v-mutate`'s options; with directive modifiers using period notation, or with a custom options object. The following table contains information on the available directive modifiers:

| Modifier     | Default      | Description |
| ------------ | -----------  | ----------- |
| `.attr`      | `true`       | The [attr](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit/attributes) modifier monitors target node's attribute changes                                                       |
| `.char`      | `true`       | The [char](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit/characterData) modifier monitors changes to target node's character data (and, its descendants if `.sub` is `true`)       |
| `.child`     | `true`       | The [child](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit/childList) modifier monitors for the addition or removal of child nodes (and, its descendants if `.sub` is `true`) |
| `.sub`       | `true`       | The [sub](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit/subtree) modifier extends all monitoring to the entire subtree of target node                                        |
| `.once`      | `undefined`  | The [once](#once) modifier invokes the user provided callback one time and disconnects the observer                                                                                |
| `.immediate` | `undefined`  | The [immediate](#immediate) modifier invokes the user provided callback on _mounted_ and does not effect `.once`                                                                                |

By default, `v-mutate` enables `.attr`, `.char`, `.child`, and `.sub` unless you manually apply one; in which case the undefined options are set to false:

```html
<template>
  <div>
    <!-- attr, char, child, and sub are true -->
    <div v-mutate="..." />

    <!-- child is true, attr, char, and child are false -->
    <div v-mutate.child="...">
  </div>
</template>
```

In addition to the _modifier_ syntax, the `v-mutate` directive is configurable via a custom object containing a **handler** and **options** key. The following example demonstrates how both processes achieve the same result:

```html
<template>
  <div>
    <div v-mutate="{
      handler: onMutate,
        modifiers: {
          child: true,
          sub: true,
        }
      }"
    />

    <!-- is the same as -->

    <div v-mutate.child.sub="onMutate" />
  </div>
</template>

<script>
  export default {
    methods: {
      onMutate () {}
    },
  }
</script>
```

<alert type="warning">

  When using custom options, it's recommended to use the `.sub` modifier. This extends mutation monitoring to all descendants of the target element.

</alert>

### Once

There may be times where your callback should only fire once. In these scenarios, use the **once** option to disconnect the observer after the first detected mutation. In the next example, we bind data value _content_ to 2 separate [v-card](/components/cards/) components and an `input`; then track the number of mutations that occur as we type:

```html
<template>
  <div>
    <input type="text" v-model="content">

    <v-card v-mutate="onMutate">{{ content }}</v-card>

    <v-card v-mutate.once="onMutate">{{ content }}</v-card>
  </div>
</template>

<script>
  export default {
    data: () => ({
      content: 'Foo',
      mutations: 0,
    }),

    mounted () {
      this.content = 'Bar'

      console.log(mutations) // 2

      setTimeout(() => {
        this.content = 'Foobar'

        console.log(mutations) // 3
      }, 200)
    },

    methods: {
      onMutate (mutations, observer) {
        mutations++
      },
    },
  }
</script>
```

When the value of content changes, both cards immediately call _onMutate_ and iterate the value of the _mutations_ data value. Because the second card is using the **once** modifier, it automatically unbinds its observer after the first change to _content_.

### Immediate

Unlike the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver), the provided callback is **not** immediately invoked when a Mutation Observer is created. Vuetify normalizes this behavior with the **immediate** option. In the following example, the `v-mutate` directive invokes the _onMutate_ method when the element is initially mounted in the DOM **and** with every mutation; based upon the provided options.

```html
<template>
  <div v-mutate.immediate="onMutate">...</div>
</template>

<script>
  export default {
    data: () => ({ mutations: 0 }),

    mounted () {
      console.log(mutations) // 1
    },

    methods: {
      onMutate (mutations, observer) {
        mutations++
      },
    },
  }
</script>
```

<alert type="info">

  The **immediate** callback is not counted as a mutation and does not trigger the observer to disconnect when using **once**.

</alert>

## API

<api-inline />

## Examples

<example file="v-mutate/usage" />

<example file="v-mutate/option-modifiers" />

<backmatter />