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

// Components
// import VOverflowBtn from '../VOverflowBtn'

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

describe.skip('VOverflowBtn.js', () => {
  type Instance = ExtractVue<typeof VOverflowBtn>
  let mountFunction: (options?: object) => Wrapper<Instance>

  beforeEach(() => {
    document.body.setAttribute('data-app', 'true')

    mountFunction = (options = {}) => {
      return mount(VOverflowBtn, {
        ...options,
        // https://github.com/vuejs/vue-test-utils/issues/1130
        sync: false,
        mocks: {
          $vuetify: {
            lang: {
              t: (val: string) => val,
            },
            theme: {
              dark: false,
            },
          },
        },
      })
    }
  })

  const warning = 'items must contain both a text and callback property'

  it.skip('segmented - should warn when item has no callback', async () => {
    const items = [
      { text: 'Hello' },
      { text: 'Hello' },
    ]

    const wrapper = mountFunction({
      propsData: {
        segmented: true,
        items,
      },
    })

    await wrapper.vm.$nextTick()

    // The error only happens
    // when generating the button
    // which only happens when
    // we have a matching model
    wrapper.setProps({
      items: [items[1]],
      value: 'Hello',
    })

    await wrapper.vm.$nextTick()

    expect(warning).toHaveBeenTipped()
  })

  it('should use default autocomplete selections', async () => {
    const wrapper = mountFunction({
      propsData: {
        items: ['foo'],
        multiple: true,
        value: ['foo'],
      },
    })

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

    wrapper.setProps({
      items: [{ text: 'foo', value: 'foo', callback: () => { } }],
      multiple: false,
      segmented: true,
      value: 'foo',
    })

    await wrapper.vm.$nextTick()

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

  it('should invoke item callback', () => {
    const callback = jest.fn()
    const wrapper = mountFunction({
      propsData: {
        items: [{
          text: 'foo',
          value: 'bar',
          callback,
        }],
        segmented: true,
        value: 'bar',
      },
    })

    const btn = wrapper.find('.v-btn')

    btn.trigger('click')

    expect(callback).toHaveBeenCalled()
  })

  it('should show label with persistentPlaceholder property set to true', async () => {
    const wrapper = mountFunction({
      propsData: {
        items: ['foo'],
        label: 'Some label',
        persistentPlaceholder: true,
      },
    })

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

    wrapper.find('input').trigger('click')

    await wrapper.vm.$nextTick()

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


keep keep keep add keep keep keep keep keep

// Styles
import './VSelectionControl.sass'

// Components
import { VIcon } from '@/components/VIcon'
import { VLabel } from '@/components/VLabel'
import { makeSelectionControlGroupProps, VSelectionControlGroupSymbol } from '@/components/VSelectionControlGroup/VSelectionControlGroup'

// Composables
import { useTextColor } from '@/composables/color'
import { makeComponentProps } from '@/composables/component'
import { useDensity } from '@/composables/density'
import { useProxiedModel } from '@/composables/proxiedModel'

// Directives
import { Ripple } from '@/directives/ripple'

// Utilities
import { computed, inject, nextTick, ref, shallowRef } from 'vue'
import {
  filterInputAttrs,
  genericComponent,
  getUid,
  propsFactory,
  SUPPORTS_FOCUS_VISIBLE,
  useRender,
  wrapInArray,
} from '@/util'

// Types
import type { CSSProperties, ExtractPropTypes, Ref, WritableComputedRef } from 'vue'
import type { GenericProps } from '@/util'

export type SelectionControlSlot = {
  model: WritableComputedRef<any>
  textColorClasses: Ref<string[]>
  textColorStyles: Ref<CSSProperties>
  props: {
    onBlur: (e: Event) => void
    onFocus: (e: FocusEvent) => void
    id: string
  }
}

export type VSelectionControlSlots = {
  default: never
  label: { label: string | undefined, props: Record<string, unknown> }
  input: SelectionControlSlot
}

export const makeSelectionControlProps = propsFactory({
  label: String,
  trueValue: null,
  falseValue: null,
  value: null,

  ...makeComponentProps(),
  ...makeSelectionControlGroupProps(),
}, 'v-selection-control')

export function useSelectionControl (
  props: ExtractPropTypes<ReturnType<typeof makeSelectionControlProps>> & {
    'onUpdate:modelValue': ((val: any) => void) | undefined
  }
) {
  const group = inject(VSelectionControlGroupSymbol, undefined)
  const { densityClasses } = useDensity(props)
  const modelValue = useProxiedModel(props, 'modelValue')
  const trueValue = computed(() => (
    props.trueValue !== undefined ? props.trueValue
    : props.value !== undefined ? props.value
    : true
  ))
  const falseValue = computed(() => props.falseValue !== undefined ? props.falseValue : false)
  const isMultiple = computed(() => (
    !!props.multiple ||
    (props.multiple == null && Array.isArray(modelValue.value))
  ))
  const model = computed({
    get () {
      const val = group ? group.modelValue.value : modelValue.value

      return isMultiple.value
        ? val.some((v: any) => props.valueComparator(v, trueValue.value))
        : props.valueComparator(val, trueValue.value)
    },
    set (val: boolean) {
      if (props.readonly) return

      const currentValue = val ? trueValue.value : falseValue.value

      let newVal = currentValue

      if (isMultiple.value) {
        newVal = val
          ? [...wrapInArray(modelValue.value), currentValue]
          : wrapInArray(modelValue.value).filter((item: any) => !props.valueComparator(item, trueValue.value))
      }

      if (group) {
        group.modelValue.value = newVal
      } else {
        modelValue.value = newVal
      }
    },
  })
  const { textColorClasses, textColorStyles } = useTextColor(computed(() => {
    return (
      model.value &&
      !props.error &&
      !props.disabled
    ) ? props.color : undefined
  }))
  const icon = computed(() => model.value ? props.trueIcon : props.falseIcon)

  return {
    group,
    densityClasses,
    trueValue,
    falseValue,
    model,
    textColorClasses,
    textColorStyles,
    icon,
  }
}

export const VSelectionControl = genericComponent<new <T>(
  props: {
    modelValue?: T
    'onUpdate:modelValue'?: (val: T) => any
  },
  slots: VSelectionControlSlots,
) => GenericProps<typeof props, typeof slots>>()({
  name: 'VSelectionControl',

  directives: { Ripple },

  inheritAttrs: false,

  props: makeSelectionControlProps(),

  emits: {
    'update:modelValue': (val: any) => true,
  },

  setup (props, { attrs, slots }) {
    const {
      group,
      densityClasses,
      icon,
      model,
      textColorClasses,
      textColorStyles,
      trueValue,
    } = useSelectionControl(props)
    const uid = getUid()
    const id = computed(() => props.id || `input-${uid}`)
    const isFocused = shallowRef(false)
    const isFocusVisible = shallowRef(false)
    const input = ref<HTMLInputElement>()

    group?.onForceUpdate(() => {
      if (input.value) {
        input.value.checked = model.value
      }
    })

    function onFocus (e: FocusEvent) {
      isFocused.value = true
      if (
        !SUPPORTS_FOCUS_VISIBLE ||
        (SUPPORTS_FOCUS_VISIBLE && (e.target as HTMLElement).matches(':focus-visible'))
      ) {
        isFocusVisible.value = true
      }
    }

    function onBlur () {
      isFocused.value = false
      isFocusVisible.value = false
    }

    function onInput (e: Event) {
      if (props.readonly && group) {
        nextTick(() => group.forceUpdate())
      }
      model.value = (e.target as HTMLInputElement).checked
    }

    useRender(() => {
      const label = slots.label
        ? slots.label({
          label: props.label,
          props: { for: id.value },
        })
        : props.label
      const [rootAttrs, inputAttrs] = filterInputAttrs(attrs)

      return (
        <div
          class={[
            'v-selection-control',
            {
              'v-selection-control--dirty': model.value,
              'v-selection-control--disabled': props.disabled,
              'v-selection-control--error': props.error,
              'v-selection-control--focused': isFocused.value,
              'v-selection-control--focus-visible': isFocusVisible.value,
              'v-selection-control--inline': props.inline,
            },
            densityClasses.value,
            props.class,
          ]}
          { ...rootAttrs }
          style={ props.style }
        >
          <div
            class={[
              'v-selection-control__wrapper',
              textColorClasses.value,
            ]}
            style={ textColorStyles.value }
          >
            { slots.default?.() }

            <div
              class={[
                'v-selection-control__input',
              ]}
              v-ripple={ props.ripple && [
                !props.disabled && !props.readonly,
                null,
                ['center', 'circle'],
              ]}
            >
              { icon.value && <VIcon key="icon" icon={ icon.value } /> }

              <input
                ref={ input }
                checked={ model.value }
                disabled={ props.disabled }
                id={ id.value }
                onBlur={ onBlur }
                onFocus={ onFocus }
                onInput={ onInput }
                aria-disabled={ props.readonly }
                type={ props.type }
                value={ trueValue.value }
                name={ props.name }
                aria-checked={ props.type === 'checkbox' ? model.value : undefined }
                { ...inputAttrs }
              />

              { slots.input?.({
                model,
                textColorClasses,
                textColorStyles,
                props: {
                  onFocus,
                  onBlur,
                  id: id.value,
                },
              } as SelectionControlSlot)}
            </div>
          </div>

          { label && (
            <VLabel for={ id.value } clickable>
              { label }
            </VLabel>
          )}
        </div>
      )
    })

    return {
      isFocused,
      input,
    }
  },
})

export type VSelectionControl = InstanceType<typeof VSelectionControl>


// Utilities
import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { refElement } from '@/util'

// Types
import type { ComponentPublicInstance } from 'vue'

export interface MutationOptions {
  attr?: boolean
  char?: boolean
  child?: boolean
  sub?: boolean
  once?: boolean
  immediate?: boolean
}

export function useMutationObserver (
  handler?: MutationCallback,
  options?: MutationOptions,
) {
  const mutationRef = ref<ComponentPublicInstance | HTMLElement>()
  const { once, immediate, ...optionKeys } = options || {}
  const defaultValue = !Object.keys(optionKeys).length

  const observer = new MutationObserver((
    mutations: MutationRecord[],
    observer: MutationObserver
  ) => {
    handler?.(mutations, observer)

    if (options?.once) observer.disconnect()
  })

  onMounted(() => {
    if (!options?.immediate) return

    handler?.([], observer)
  })

  onBeforeUnmount(() => {
    observer.disconnect()
  })

  watch(mutationRef, (newValue, oldValue) => {
    if (oldValue) observer.disconnect()

    const el = refElement(newValue)

    if (!el) return

    observer.observe(el, {
      attributes: options?.attr ?? defaultValue,
      characterData: options?.char ?? defaultValue,
      childList: options?.child ?? defaultValue,
      subtree: options?.sub ?? defaultValue,
    })
  }, {
    flush: 'post',
  })

  return { mutationRef }
}


<div class="d-flex flex-column flex-grow-1">

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

'Default input': <VSelect items={ items } />,

Disabled: <VSelect items={ items } disabled />,

Affixes: <VSelect items={ items } prefix="prefix" suffix="suffix" />,

'Prepend/append': <VSelect items={ items } prependIcon="$vuetify" appendIcon="$vuetify" />,

'Prepend/append inner': <VSelect items={ items } prependInnerIcon="$vuetify" appendInnerIcon="$vuetify" />,

Placeholder: <VSelect items={ items } placeholder="placeholder" persistentPlaceholder />,

/// <reference types="../../../../types/cypress" />

// Components
import { VSelect } from '../VSelect'
import { VForm } from '@/components/VForm'
import { VListItem } from '@/components/VList'

// Utilities
import { cloneVNode, ref } from 'vue'
import { generate } from '../../../../cypress/templates'
import { keyValues } from '@/util'

const variants = ['underlined', 'outlined', 'filled', 'solo', 'plain'] as const
const densities = ['default', 'comfortable', 'compact'] as const
const items = ['California', 'Colorado', 'Florida', 'Georgia', 'Texas', 'Wyoming'] as const

const stories = Object.fromEntries(Object.entries({
  'Default input': <VSelect label="label" items={ items } />,
  Disabled: <VSelect label="label" items={ items } disabled />,
  Affixes: <VSelect label="label" items={ items } prefix="prefix" suffix="suffix" />,
  'Prepend/append': <VSelect label="label" items={ items } prependIcon="$vuetify" appendIcon="$vuetify" />,
  'Prepend/append inner': <VSelect label="label" items={ items } prependInnerIcon="$vuetify" appendInnerIcon="$vuetify" />,
  Placeholder: <VSelect label="label" items={ items } placeholder="placeholder" persistentPlaceholder />,
}).map(([k, v]) => [k, (

    { variants.map(variant => (
      densities.map(density => (

          { cloneVNode(v, { variant, density }) }
          { cloneVNode(v, { variant, density, modelValue: ['California'] }) }
          { cloneVNode(v, { variant, density, chips: true, modelValue: ['California'] }) }
          <VSelect
            variant={ variant }
            density={ density }
            modelValue={['California']}
            { ...v.props }
          >{{
            selection: ({ item }) => {
              return item.title
            },
          }}
          </VSelect>

      ))
    )).flat()}

)]))

describe('VSelect', () => {
  it('should render selection slot', () => {
    const items = [
      { title: 'a' },
      { title: 'b' },
      { title: 'c' },
    ]
    let model: { title: string }[] = [{ title: 'b' }]

    cy.mount(() => (
      <VSelect
        multiple
        returnObject
        items={ items }
        modelValue={ model }
        onUpdate:modelValue={ val => model = val }
      >
        {{
          selection: ({ item, index }) => {
            return item.raw.title.toUpperCase()
          },
        }}
      </VSelect>
    ))
      .get('.v-select__selection').eq(0).invoke('text').should('equal', 'B')
  })

  it('should render prepend-item slot', () => {
    cy.mount(() => (
      <VSelect menu items={['Item #1', 'Item #2']}>
        {{
          'prepend-item': () => (
            <VListItem title="Foo"></VListItem>
          ),
        }}
      </VSelect>
    ))
      .get('.v-list-item').eq(0).invoke('text').should('equal', 'Foo')
  })

  it('should render append-item slot', () => {
    cy.mount(() => (
      <VSelect menu items={['Item #1', 'Item #2']}>
        {{
          'append-item': () => (
            <VListItem title="Foo"></VListItem>
          ),
        }}
      </VSelect>
    ))
      .get('.v-list-item').last().invoke('text').should('equal', 'Foo')
  })

  it('should close only first chip', () => {
    const items = ['Item 1', 'Item 2', 'Item 3', 'Item 4']

    const selectedItems = ['Item 1', 'Item 2', 'Item 3']

    cy.mount(() => (
      <VSelect
        items={ items }
        modelValue={ selectedItems }
        chips
        closableChips
        multiple
      />
    ))
      .get('.v-chip__close')
      .eq(0)
      .click()
      .get('input')
      .get('.v-chip')
      .should('have.length', 2)
  })

  describe('prefilled data', () => {
    it('should work with array of strings when using multiple', () => {
      const items = ref(['California', 'Colorado', 'Florida'])

      const selectedItems = ref(['California', 'Colorado'])

      cy.mount(() => (
        <VSelect v-model={ selectedItems.value } items={ items.value } multiple chips closableChips />
      ))

      cy.get('.v-select').click()

      cy.get('.v-list-item--active').should('have.length', 2)
      cy.get('.v-list-item input').eq(2).click().should(() => {
        expect(selectedItems.value).to.deep.equal(['California', 'Colorado', 'Florida'])
      })

      cy
        .get('.v-chip__close')
        .eq(0)
        .click()
        .get('.v-chip')
        .should('have.length', 2)
        .should(() => expect(selectedItems.value).to.deep.equal(['Colorado', 'Florida']))
    })

    it('should work with objects when using multiple', () => {
      const items = ref([
        {
          title: 'Item 1',
          value: 'item1',
        },
        {
          title: 'Item 2',
          value: 'item2',
        },
        {
          title: 'Item 3',
          value: 'item3',
        },
      ])

      const selectedItems = ref(
        [
          {
            title: 'Item 1',
            value: 'item1',
          },
          {
            title: 'Item 2',
            value: 'item2',
          },
        ]
      )

      cy.mount(() => (
        <VSelect
          v-model={ selectedItems.value }
          items={ items.value }
          multiple
          chips
          closableChips
          returnObject
        />
      ))

      cy.get('.v-select').click()

      cy.get('.v-list-item--active').should('have.length', 2)
      cy.get('.v-list-item input').eq(2).click().should(() => {
        expect(selectedItems.value).to.deep.equal([
          {
            title: 'Item 1',
            value: 'item1',
          },
          {
            title: 'Item 2',
            value: 'item2',
          },
          {
            title: 'Item 3',
            value: 'item3',
          },
        ])
      })

      cy
        .get('.v-chip__close')
        .eq(0)
        .click()
        .get('.v-chip')
        .should('have.length', 2)
        .should(() => expect(selectedItems.value).to.deep.equal([
          {
            title: 'Item 2',
            value: 'item2',
          },
          {
            title: 'Item 3',
            value: 'item3',
          },
        ]))
    })
  })

  it('should not be clickable when in readonly', () => {
    const items = ['Item 1', 'Item 2', 'Item 3', 'Item 4']

    const selectedItems = 'Item 1'

    cy.mount(() => (
      <VSelect
        items={ items }
        modelValue={ selectedItems }
        readonly
      />
    ))

    cy.get('.v-select')
      .click()
      .get('.v-list-item').should('have.length', 0)
      .get('.v-select--active-menu').should('have.length', 0)

    cy
      .get('.v-select input')
      .focus()
      .type('{downarrow}', { force: true })
      .get('.v-list-item').should('have.length', 0)
      .get('.v-select--active-menu').should('have.length', 0)
  })

  it('should not be clickable when in readonly form', () => {
    const items = ['Item 1', 'Item 2', 'Item 3', 'Item 4']

    const selectedItems = 'Item 1'

    cy.mount(() => (
      <VForm readonly>
        <VSelect
          items={ items }
          modelValue={ selectedItems }
          readonly
        />
      </VForm>
    ))

    cy.get('.v-select')
      .click()
      .get('.v-list-item').should('have.length', 0)
      .get('.v-select--active-menu').should('have.length', 0)

    cy
      .get('.v-select input')
      .focus()
      .type('{downarrow}', { force: true })
      .get('.v-list-item').should('have.length', 0)
      .get('.v-select--active-menu').should('have.length', 0)
  })

  it('should conditionally show placeholder', () => {
    cy.mount(props => (
      <VSelect placeholder="Placeholder" { ...props } />
    ))
      .get('.v-select input')
      .should('have.attr', 'placeholder', 'Placeholder')
      .setProps({ label: 'Label' })
      .get('.v-select input')
      .should('not.have.attr', 'placeholder')
      .get('.v-select input')
      .focus()
      .should('have.attr', 'placeholder', 'Placeholder')
      .blur()
      .setProps({ persistentPlaceholder: true })
      .get('.v-select input')
      .should('have.attr', 'placeholder', 'Placeholder')
      .setProps({ modelValue: 'Foobar' })
      .get('.v-select input')
      .should('not.have.attr', 'placeholder')
      .setProps({ multiple: true, modelValue: ['Foobar'] })
      .get('.v-select input')
      .should('not.have.attr', 'placeholder')
  })

  // https://github.com/vuetifyjs/vuetify/issues/16210
  it('should return item object as the argument of item-title function', () => {
    const items = [
      { id: 1, name: 'a' },
      { id: 2, name: 'b' },
    ]

    const selectedItems = ref(null)

    function itemTitleFunc (item: any) {
      return 'Item: ' + JSON.stringify(item)
    }

    const itemTitleFuncSpy = cy.spy(itemTitleFunc).as('itemTitleFunc')

    cy.mount(() => (
      <VSelect
        items={ items }
        modelValue={ selectedItems }
        item-title={ itemTitleFuncSpy }
        item-value="id"
      />
    ))

    cy.get('.v-select').click()

    cy.get('.v-list-item').eq(0).click({ waitForAnimations: false }).should(() => {
      expect(selectedItems.value).to.deep.equal(1)
    })

    cy.get('@itemTitleFunc')
      .should('have.been.calledWith', { id: 1, name: 'a' })

    cy.get('.v-select__selection-text').should('have.text', `Item: {"id":1,"name":"a"}`)
  })

  describe('hide-selected', () => {
    it('should hide selected item(s)', () => {
      const items = ref(['Item 1',
        'Item 2',
        'Item 3',
        'Item 4',
      ])

      const selectedItems = ref([
        'Item 1',
        'Item 2',
      ])

      cy.mount(() => (
        <VSelect v-model={ selectedItems.value } items={ items.value } multiple hideSelected />
      ))

      cy.get('.v-select').click()

      cy.get('.v-overlay__content .v-list-item').should('have.length', 2)
      cy.get('.v-overlay__content .v-list-item .v-list-item-title').eq(0).should('have.text', 'Item 3')
      cy.get('.v-overlay__content .v-list-item .v-list-item-title').eq(1).should('have.text', 'Item 4')
    })
  })

  // https://github.com/vuetifyjs/vuetify/issues/16055
  it('should select item after typing its first few letters', () => {
    const items = ref(['aaa', 'foo', 'faa'])

    const selectedItems = ref(undefined)

    cy.mount(() => (
      <VSelect
        v-model={ selectedItems.value }
        items={ items.value }
      />
    ))

    cy.get('.v-select')
      .click()
      .get('.v-select input')
      .focus()
      .type('f', { force: true })
      .get('.v-list-item').should('have.length', 3)
      .then(_ => {
        expect(selectedItems.value).equal('foo')
      })
  })

  it('should keep TextField focused while selecting items from open menu', () => {
    cy.mount(() => (
      <VSelect
        items={['California', 'Colorado', 'Florida', 'Georgia', 'Texas', 'Wyoming']}
      />
    ))

    cy.get('.v-select')
      .click()

    cy.get('.v-list')
      .trigger('keydown', { key: keyValues.down, waitForAnimations: false })
      .trigger('keydown', { key: keyValues.down, waitForAnimations: false })
      .trigger('keydown', { key: keyValues.down, waitForAnimations: false })

    cy.get('.v-field').should('have.class', 'v-field--focused')
  })

  it('should not open menu when closing a chip', () => {
    cy
      .mount(() => (
        <VSelect
          chips
          closable-chips
          items={['foo', 'bar']}
          label="Select"
          modelValue={['foo', 'bar']}
          multiple
        />
      ))
      .get('.v-select')
      .should('not.have.class', 'v-select--active-menu')
      .get('.v-chip__close').eq(1)
      .click()
      .get('.v-select')
      .should('not.have.class', 'v-select--active-menu')
      .get('.v-chip__close')
      .click()
      .get('.v-select')
      .should('not.have.class', 'v-select--active-menu')
      .click()
      .should('have.class', 'v-select--active-menu')
      .trigger('keydown', { key: keyValues.esc })
      .should('not.have.class', 'v-select--active-menu')
  })

  describe('Showcase', () => {
    generate({ stories })
  })
})


/// <reference types="../../../../types/cypress" />

// Components
import { VToolbar } from '..'
import { VBtn } from '@/components/VBtn'

// Utilities
const colors = ['success', 'info', 'warning', 'error', 'invalid']

describe('VToolbar', () => {
  function mountFunction (content: JSX.Element) {
    return cy.mount(() => content)
  }

  it('supports the title prop', () => {
    mountFunction((
      <VToolbar title="foo" />
    ))
      .get('.v-toolbar-title').contains('foo')
  })

  it('supports default color props', () => {
    mountFunction((
      <>
        { colors.map(color => (
          <VToolbar color={ color } title={ color } />
        ))}
      </>
    ))
      .get('.v-toolbar')
      .should('have.length', colors.length)
      .then(subjects => {
        Array.from(subjects).forEach((subject, idx) => {
          expect(subject).to.contain(colors[idx])
        })
      })
  })

  it('aligns prepend / append slot content center', () => {
    const wrapper = mountFunction((
      <VToolbar>
        <slot name="prepend">
          <VBtn>Prepend</VBtn>
        </slot>

        <slot name="append">
          <VBtn>Append</VBtn>
        </slot>
      </VToolbar>
    ))

    wrapper.get('[name="prepend"] > .v-btn').should('have.length', 1)
    wrapper.get('[name="append"] > .v-btn').should('have.length', 1)
  })
})


<template>
  <v-row justify="space-around">
    <v-date-picker
      v-model="date1"
      :show-current="false"
    ></v-date-picker>
    <v-date-picker
      v-model="date2"
      show-current="2013-07-13"
    ></v-date-picker>
  </v-row>
</template>

<script>
  export default {
    data () {
      return {
        date1: (new Date(Date.now() - (new Date()).getTimezoneOffset() * 60000)).toISOString().substr(0, 10),
        date2: '2013-07-29',
      }
    },
  }
</script>


<template>
  <v-app id="inspire">
    <v-app-bar
      class="px-3"
      color="white"
      flat
      density="compact"
    >
      <v-avatar
        color="grey-darken-1"
        size="32"
      ></v-avatar>

      <v-spacer></v-spacer>

      <v-tabs
        centered
        color="grey-darken-2"
      >
        <v-tab
          v-for="link in links"
          :key="link"
        >
          {{ link }}
        </v-tab>
      </v-tabs>
      <v-spacer></v-spacer>

      <v-avatar
        class="hidden-sm-and-down"
        color="grey-darken-1"
        size="32"
      ></v-avatar>
    </v-app-bar>

    <v-main class="bg-grey-lighten-3">
      <v-container>
        <v-row>
          <v-col
            cols="12"
            sm="2"
          >
            <v-sheet
              rounded="lg"
              min-height="268"
            >
              <!--  -->
            </v-sheet>
          </v-col>

          <v-col
            cols="12"
            sm="8"
          >
            <v-sheet
              min-height="70vh"
              rounded="lg"
            >
              <!--  -->
            </v-sheet>
          </v-col>

          <v-col
            cols="12"
            sm="2"
          >
            <v-sheet
              rounded="lg"
              min-height="268"
            >
              <!--  -->
            </v-sheet>
          </v-col>
        </v-row>
      </v-container>
    </v-main>
  </v-app>
</template>

<script>
  export default {
    data: () => ({
      links: [
        'Dashboard',
        'Messages',
        'Profile',
        'Updates',
      ],
    }),
  }
</script>


<div class="d-flex align-start" style="gap: 0.4rem; height: 100px;">

keep keep keep replace replace replace keep keep keep keep

{ cloneVNode(v, { variant, density, label: `${variant} ${density}` }) }

{ cloneVNode(v, { variant, density, label: `with value`, modelValue: ['California'] }) }

{ cloneVNode(v, { variant, density, label: `chips`, chips: true, modelValue: ['California'] }) }

/// <reference types="../../../../types/cypress" />

// Components
import { VAutocomplete } from '../VAutocomplete'
import { VForm } from '@/components/VForm'

// Utilities
import { cloneVNode, ref } from 'vue'
import { generate } from '../../../../cypress/templates'
import { keyValues } from '@/util'

const variants = ['underlined', 'outlined', 'filled', 'solo', 'plain'] as const
const densities = ['default', 'comfortable', 'compact'] as const
const items = ['California', 'Colorado', 'Florida', 'Georgia', 'Texas', 'Wyoming'] as const

const stories = Object.fromEntries(Object.entries({
  'Default input': <VAutocomplete label="label" />,
  Disabled: <VAutocomplete label="label" items={ items } disabled />,
  Affixes: <VAutocomplete label="label" items={ items } prefix="prefix" suffix="suffix" />,
  'Prepend/append': <VAutocomplete label="label" items={ items } prependIcon="$vuetify" appendIcon="$vuetify" />,
  'Prepend/append inner': <VAutocomplete label="label" items={ items } prependInnerIcon="$vuetify" appendInnerIcon="$vuetify" />,
  Placeholder: <VAutocomplete label="label" items={ items } placeholder="placeholder" persistentPlaceholder />,
}).map(([k, v]) => [k, (

    { variants.map(variant => (
      densities.map(density => (

          { cloneVNode(v, { variant, density }) }
          { cloneVNode(v, { variant, density, modelValue: ['California'] }) }
          { cloneVNode(v, { variant, density, chips: true, modelValue: ['California'] }) }
          <VAutocomplete
            variant={ variant }
            density={ density }
            modelValue={['California']}
            { ...v.props }
          >{{
            selection: ({ item }) => {
              return item.title
            },
          }}
          </VAutocomplete>

      ))
    )).flat()}

)]))

describe('VAutocomplete', () => {
  it('should close only first chip', () => {
    const items = ['Item 1', 'Item 2', 'Item 3', 'Item 4']

    const selectedItems = ['Item 1', 'Item 2', 'Item 3']

    cy.mount(() => (
      <VAutocomplete
        items={ items }
        modelValue={ selectedItems }
        chips
        closableChips
        multiple
      />
    ))

    cy.get('.v-chip__close').eq(0).click()
    cy.get('input').should('exist')
    cy.get('.v-chip').should('have.length', 2)
  })

  it('should have selected chip with array of strings', () => {
    const items = ref(['California', 'Colorado', 'Florida'])

    const selectedItems = ref(['California', 'Colorado'])

    cy.mount(() => (
      <VAutocomplete
        v-model={ selectedItems.value }
        items={ items.value }
        chips
        multiple
        closableChips
      />
    ))

    cy.get('.mdi-menu-down').click()

    cy.get('.v-list-item--active').should('have.length', 2)
    cy.get('.v-list-item--active input').eq(0).click()
    cy.then(() => {
      expect(selectedItems.value).to.deep.equal(['Colorado'])
    })

    cy.get('.v-list-item--active').should('have.length', 1)

    cy.get('.v-chip__close').eq(0).click()
    cy.get('.v-chip')
      .should('have.length', 0)
      .should(() => expect(selectedItems.value).to.be.empty)
  })

  it('should have selected chip with return-object', () => {
    const items = ref([
      {
        title: 'Item 1',
        value: 'item1',
      },
      {
        title: 'Item 2',
        value: 'item2',
      },
    ])

    const selectedItems = ref([
      {
        title: 'Item 1',
        value: 'item1',
      },
    ])

    cy.mount(() => (
      <VAutocomplete
        v-model={ selectedItems.value }
        items={ items.value }
        returnObject
        chips
        multiple
      />
    ))

    cy.get('.mdi-menu-down').click()

    cy.get('.v-list-item--active').should('have.length', 1)
    cy.get('.v-list-item--active input').click()
    cy.then(() => {
      expect(selectedItems.value).to.be.empty
    })
    cy.get('.v-list-item--active').should('have.length', 0)
  })

  it('should not be clickable when in readonly', () => {
    const items = ['Item 1', 'Item 2', 'Item 3', 'Item 4']

    const selectedItems = 'Item 1'

    cy.mount(() => (
      <VAutocomplete
        items={ items }
        modelValue={ selectedItems }
        readonly
      />
    ))

    cy.get('.v-autocomplete').click()
    cy.get('.v-list-item').should('have.length', 0)
    cy.get('.v-select--active-menu').should('have.length', 0)

    cy.get('.v-autocomplete input').as('input')
      .focus()
    cy.get('@input').type('{downarrow}', { force: true })
    cy.get('.v-list-item').should('have.length', 0)
    cy.get('.v-select--active-menu').should('have.length', 0)
  })

  it('should not be clickable when in readonly form', () => {
    const items = ['Item 1', 'Item 2', 'Item 3', 'Item 4']

    const selectedItems = 'Item 1'

    cy.mount(() => (
      <VForm readonly>
        <VAutocomplete
          items={ items }
          modelValue={ selectedItems }
          readonly
        />
      </VForm>
    ))

    cy.get('.v-autocomplete').click()
    cy.get('.v-list-item').should('have.length', 0)
    cy.get('.v-select--active-menu').should('have.length', 0)

    cy.get('.v-autocomplete input').as('input')
      .focus()
    cy.get('@input').type('{downarrow}', { force: true })
    cy.get('.v-list-item').should('have.length', 0)
    cy.get('.v-select--active-menu').should('have.length', 0)
  })

  it('should be empty when delete the selected option', () => {
    const items = ref([
      { title: 'Item 1', value: 'Item 1' },
      { title: 'Item 2', value: 'Item 2' },
    ])

    const selectedItems = ref(null)

    cy.mount(() => (
      <VAutocomplete
        v-model={ selectedItems.value }
        items={ items.value }
        returnObject
      />
    ))

    cy.get('.v-autocomplete').click()
    cy.get('.v-list-item').should('have.length', 2)
    cy.get('.v-list-item').contains('Item 1').click()

    cy.get('.v-field__input').clear()
    cy.get('body').click('bottomLeft')
    cy.get('.v-field__input').should('not.include.text', 'Item 1')
  })

  // https://github.com/vuetifyjs/vuetify/issues/16210
  it('should return item object as the argument of item-title function', () => {
    const items = [
      { id: 1, name: 'a' },
      { id: 2, name: 'b' },
    ]

    const selectedItems = ref(null)

    function itemTitleFunc (item: any) {
      return 'Item: ' + JSON.stringify(item)
    }

    const itemTitleFuncSpy = cy.spy(itemTitleFunc).as('itemTitleFunc')

    cy.mount(() => (
      <VAutocomplete
        items={ items }
        modelValue={ selectedItems }
        item-title={ itemTitleFuncSpy }
        item-value="id"
      />
    ))

    cy.get('.v-autocomplete').click()

    cy.get('.v-list-item').eq(0).click({ waitForAnimations: false }).should(() => {
      expect(selectedItems.value).to.deep.equal(1)
    })

    cy.get('@itemTitleFunc')
      .should('have.been.calledWith', { id: 1, name: 'a' })

    cy.get('.v-autocomplete__selection-text').should('have.text', `Item: {"id":1,"name":"a"}`)
  })

  describe('hide-selected', () => {
    it('should hide selected item(s)', () => {
      const items = ['Item 1', 'Item 2', 'Item 3', 'Item 4']

      const selectedItems = ['Item 1', 'Item 2']

      cy.mount(() => (
        <VAutocomplete
          items={ items }
          modelValue={ selectedItems }
          hideSelected
          multiple
        />
      ))

      cy.get('.mdi-menu-down').click()

      cy.get('.v-overlay__content .v-list-item').should('have.length', 2)
      cy.get('.v-overlay__content .v-list-item .v-list-item-title').eq(0).should('have.text', 'Item 3')
      cy.get('.v-overlay__content .v-list-item .v-list-item-title').eq(1).should('have.text', 'Item 4')
    })
  })

  // https://github.com/vuetifyjs/vuetify/issues/16055
  it('should not replicate html select hotkeys in v-autocomplete', () => {
    const items = ref(['aaa', 'foo', 'faa'])

    const selectedItems = ref(undefined)

    cy.mount(() => (
      <VAutocomplete
        v-model={ selectedItems.value }
        items={ items.value }
      />
    ))

    cy.get('.v-autocomplete')
      .click()
      .get('.v-autocomplete input')
      .focus()
      .type('f', { force: true })
      .get('.v-list-item').should('have.length', 2)
      .then(_ => {
        expect(selectedItems.value).equal(undefined)
      })
  })

  it('should conditionally show placeholder', () => {
    cy.mount(props => (
      <VAutocomplete placeholder="Placeholder" { ...props } />
    ))
      .get('.v-autocomplete input')
      .should('have.attr', 'placeholder', 'Placeholder')
      .setProps({ label: 'Label' })
      .get('.v-autocomplete input')
      .should('not.be.visible')
      .get('.v-autocomplete input')
      .focus()
      .should('have.attr', 'placeholder', 'Placeholder')
      .should('be.visible')
      .blur()
      .setProps({ persistentPlaceholder: true })
      .get('.v-autocomplete input')
      .should('have.attr', 'placeholder', 'Placeholder')
      .should('be.visible')
      .setProps({ modelValue: 'Foobar' })
      .get('.v-autocomplete input')
      .should('not.have.attr', 'placeholder')
      .setProps({ multiple: true, modelValue: ['Foobar'] })
      .get('.v-autocomplete input')
      .should('not.have.attr', 'placeholder')
  })

  it('should keep TextField focused while selecting items from open menu', () => {
    cy.mount(() => (
      <VAutocomplete
        multiple
        items={['California', 'Colorado', 'Florida', 'Georgia', 'Texas', 'Wyoming']}
      />
    ))

    cy.get('.v-autocomplete')
      .click()

    cy.get('.v-list')
      .trigger('keydown', { key: keyValues.down, waitForAnimations: false })
      .trigger('keydown', { key: keyValues.down, waitForAnimations: false })
      .trigger('keydown', { key: keyValues.down, waitForAnimations: false })

    cy.get('.v-field').should('have.class', 'v-field--focused')
  })

  it('should not open menu when closing a chip', () => {
    cy
      .mount(() => (
        <VAutocomplete
          chips
          closable-chips
          items={['foo', 'bar']}
          label="Autocomplete"
          modelValue={['foo', 'bar']}
          multiple
        />
      ))
      .get('.v-autocomplete')
      .should('not.have.class', 'v-autocomplete--active-menu')
      .get('.v-chip__close').eq(1)
      .click()
      .get('.v-autocomplete')
      .should('not.have.class', 'v-autocomplete--active-menu')
      .get('.v-chip__close')
      .click()
      .get('.v-autocomplete')
      .should('not.have.class', 'v-autocomplete--active-menu')
      .click()
      .should('have.class', 'v-autocomplete--active-menu')
      .trigger('keydown', { key: keyValues.esc })
      .should('not.have.class', 'v-autocomplete--active-menu')
  })

  it('should auto-select-first item when pressing enter', () => {
    cy
      .mount(() => (
        <VAutocomplete
          items={['California', 'Colorado', 'Florida', 'Georgia', 'Texas', 'Wyoming']}
          multiple
          autoSelectFirst
        />
      ))
      .get('.v-autocomplete')
      .click()
      .get('.v-list-item')
      .should('have.length', 6)
      .get('.v-autocomplete input')
      .type('Cal')
      .get('.v-list-item').eq(0)
      .should('have.class', 'v-list-item--active')
      .get('.v-autocomplete input')
      .trigger('keydown', { key: keyValues.enter, waitForAnimations: false })
      .get('.v-list-item')
      .should('have.length', 6)
  })

  describe('Showcase', () => {
    generate({ stories })
  })
})


<template>

    <v-stepper alt-labels>
      <v-stepper-header>
        <v-stepper-step step="1">
          Ad unit details
        </v-stepper-step>

        <v-divider></v-divider>

        <v-stepper-step step="2">
          Ad sizes
        </v-stepper-step>

        <v-divider></v-divider>

        <v-stepper-step step="3">
          Ad templates
        </v-stepper-step>
      </v-stepper-header>
    </v-stepper>

    <v-stepper
      alt-labels
      class="mt-12"
    >
      <v-stepper-header>
        <v-stepper-step step="1">
          Ad unit details
        </v-stepper-step>

        <v-divider></v-divider>

        <v-stepper-step step="2">
          Ad sizes
          Optional
        </v-stepper-step>

        <v-divider></v-divider>

        <v-stepper-step step="3">
          Ad templates
        </v-stepper-step>
      </v-stepper-header>
    </v-stepper>

</template>


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

// Theme
.v-table
  @include tools.theme($table-theme...)

  .v-table-divider
    border-right: $table-border

  .v-table__wrapper
    > table
      > thead
        > tr
          > th
            border-bottom: $table-border
            color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity))

      > tbody
        > tr
          &:not(:last-child)
            > td,
            > th
              border-bottom: $table-border

      > tfoot
        > tr
            > td,
            > th
              border-top: $table-border

  &.v-table--hover
    > .v-table__wrapper
      > table
        > tbody
          > tr
            &:hover td
              background: $table-hover-color

  &.v-table--fixed-header
    > .v-table__wrapper
      > table
        > thead
          > tr
            > th
              background: $table-background
              box-shadow: inset 0 -1px 0 $table-border-color
              z-index: 1

  &.v-table--fixed-footer
    > tfoot
      > tr
        > th,
        > td
          background: $table-background
          box-shadow: inset 0 1px 0 $table-border-color

// Block
.v-table
  --v-table-header-height: #{$table-header-height}

  border-radius: inherit
  // Do not inherit line-height
  line-height: $table-line-height
  max-width: 100%

  > .v-table__wrapper
    > table
      width: 100%
      border-spacing: 0

      > tbody,
      > thead,
      > tfoot
        > tr
          > td,
          > th
            padding: $table-column-padding
            transition: height settings.$standard-easing

          > th
            font-weight: $table-header-font-weight
            user-select: none
            text-align: start

  @at-root
    @include tools.density('v-table', $table-density) using ($modifier)
      > .v-table__wrapper
        > table
          > tbody,
          > thead,
          > tfoot
            > tr
              > th
                height: calc(var(--v-table-header-height) + $modifier)

              > td
                height: calc($table-row-height + $modifier)

// Elements
.v-table__wrapper
  border-radius: inherit
  overflow: auto

// Modifiers
.v-table--has-top
  > .v-table__wrapper
    > table
      > tbody
        > tr
          &:first-child
            &:hover
              > td
                &:first-child
                  border-top-left-radius: 0

                &:last-child
                  border-top-right-radius: 0

.v-table--has-bottom
  > .v-table__wrapper
    > table
      > tbody
        > tr
          &:last-child
            &:hover
              > td
                &:first-child
                  border-bottom-left-radius: 0

                &:last-child
                  border-bottom-right-radius: 0

.v-table--fixed-height
  > .v-table__wrapper
    overflow-y: auto

.v-table--fixed-header
  > .v-table__wrapper
    > table
      > thead
        > tr
          > th
            border-bottom: 0px !important
            position: sticky
            top: 0

.v-table--fixed-footer
  > .v-table__wrapper
    > table
      > tfoot
        > tr
          > td,
          > th
              border-top: 0px !important
              position: sticky
              bottom: 0


import VSpeedDial from './VSpeedDial'

export { VSpeedDial }
export default VSpeedDial


/// <reference types="../../../../types/cypress" />

// Components
import { VCombobox } from '../VCombobox'
import { VForm } from '@/components/VForm'

// Utilities
import { cloneVNode, ref } from 'vue'
import { generate } from '../../../../cypress/templates'
import { keyValues } from '@/util'

const variants = ['underlined', 'outlined', 'filled', 'solo', 'plain'] as const
const densities = ['default', 'comfortable', 'compact'] as const
const items = ['California', 'Colorado', 'Florida', 'Georgia', 'Texas', 'Wyoming'] as const

const stories = Object.fromEntries(Object.entries({
  'Default input': <VCombobox label="label" />,
  Disabled: <VCombobox label="label" items={ items } disabled />,
  Affixes: <VCombobox label="label" items={ items } prefix="prefix" suffix="suffix" />,
  'Prepend/append': <VCombobox label="label" items={ items } prependIcon="$vuetify" appendIcon="$vuetify" />,
  'Prepend/append inner': <VCombobox label="label" items={ items } prependInnerIcon="$vuetify" appendInnerIcon="$vuetify" />,
  Placeholder: <VCombobox label="label" items={ items } placeholder="placeholder" persistentPlaceholder />,
}).map(([k, v]) => [k, (

    { variants.map(variant => (
      densities.map(density => (

          { cloneVNode(v, { variant, density }) }
          { cloneVNode(v, { variant, density, modelValue: ['California'] }) }
          { cloneVNode(v, { variant, density, chips: true, modelValue: ['California'] }) }
          <VCombobox
            variant={ variant }
            density={ density }
            modelValue={['California']}
            { ...v.props }
          >{{
            selection: ({ item }) => {
              return item.title
            },
          }}
          </VCombobox>

      ))
    )).flat()}

)]))

describe('VCombobox', () => {
  describe('closableChips', () => {
    it('should close only first chip', () => {
      const items = [
        'Item 1',
        'Item 2',
        'Item 3',
        'Item 4',
      ]

      const selectedItems = [
        'Item 1',
        'Item 2',
        'Item 3',
      ]

      cy.mount(() => (
        <VCombobox items={ items } modelValue={ selectedItems } multiple closableChips chips />
      ))
        .get('.v-chip__close').eq(0)
        .click()
      cy.get('input').should('exist')
      cy.get('.v-chip')
        .should('have.length', 2)
    })
  })

  describe('complex objects', () => {
    it('single', () => {
      const items = [
        { title: 'Item 1', value: 'item1' },
        { title: 'Item 2', value: 'item2' },
        { title: 'Item 3', value: 'item3' },
        { title: 'Item 4', value: 'item4' },
      ]
      const model = ref()
      const search = ref()
      const updateModel = cy.stub().as('model').callsFake(val => model.value = val)
      const updateSearch = cy.stub().as('search').callsFake(val => search.value = val)

      cy.mount(() => (
        <VCombobox
          modelValue={ model.value }
          search={ search.value }
          onUpdate:modelValue={ updateModel }
          onUpdate:search={ updateSearch }
          items={ items }
        />
      ))
        .get('input')
        .click()
      cy.get('.v-list-item').eq(0)
        .click({ waitForAnimations: false })
      cy.should(() => {
        expect(model.value).to.deep.equal(items[0])
        expect(search.value).to.deep.equal(items[0].title)
      })
      cy.get('input')
        .should('have.value', items[0].title)
        .blur()
      cy.get('.v-combobox__selection')
        .should('contain', items[0].title)

      cy.get('input').click()
      cy.get('input').clear()
      cy.get('input').type('Item 2')
      cy.should(() => {
        expect(model.value).to.equal('Item 2')
        expect(search.value).to.equal('Item 2')
      })
      cy.get('input')
        .should('have.value', 'Item 2')
        .blur()
      cy.get('.v-combobox__selection')
        .should('contain', 'Item 2')

      cy.get('input').click()
      cy.get('input').clear()
      cy.get('input').type('item3')
      cy.should(() => {
        expect(model.value).to.equal('item3')
        expect(search.value).to.equal('item3')
      })
      cy.get('input')
        .should('have.value', 'item3')
        .blur()
      cy.get('.v-combobox__selection')
        .should('contain', 'item3')
    })

    it('multiple', () => {
      const items = [
        { title: 'Item 1', value: 'item1' },
        { title: 'Item 2', value: 'item2' },
        { title: 'Item 3', value: 'item3' },
        { title: 'Item 4', value: 'item4' },
      ]
      const model = ref<(string | typeof items[number])[]>([])
      const search = ref()
      const updateModel = cy.stub().as('model').callsFake(val => model.value = val)
      const updateSearch = cy.stub().as('search').callsFake(val => search.value = val)

      cy.mount(() => (
        <VCombobox
          modelValue={ model.value }
          search={ search.value }
          onUpdate:modelValue={ updateModel }
          onUpdate:search={ updateSearch }
          multiple
          items={ items }
        />
      ))
        .get('.v-field input')
        .click()
      cy.get('.v-list-item').eq(0)
        .click({ waitForAnimations: false })
      cy.then(() => {
        expect(model.value).to.deep.equal([items[0]])
        expect(search.value).to.be.undefined
      })
      cy.get('.v-field input').as('input')
        .should('have.value', '')
      cy.get('.v-combobox__selection')
        .should('contain', items[0].title)

      cy.get('@input').click()
      cy.get('@input').type('Item 2')
      cy.get('@input').blur()
      cy.should(() => {
        expect(model.value).to.deep.equal([items[0], 'Item 2'])
        expect(search.value).to.equal('')
      })
      cy.get('@input').should('have.value', '')
      cy.get('.v-combobox__selection')
        .should('contain', 'Item 2')

      cy.get('@input').click()
      cy.get('@input').type('item3')
      cy.get('@input').blur()
      cy.should(() => {
        expect(model.value).to.deep.equal([items[0], 'Item 2', 'item3'])
        expect(search.value).to.equal('')
      })
      cy.get('@input').should('have.value', '')
      cy.get('.v-combobox__selection')
        .should('contain', 'item3')
    })
  })

  describe('search', () => {
    it('should filter items', () => {
      const items = [
        'Item 1',
        'Item 1a',
        'Item 2',
        'Item 2a',
      ]

      cy.mount(() => (
        <VCombobox items={ items } />
      ))
        .get('input')
        .type('Item')
      cy.get('.v-list-item')
        .should('have.length', 4)
      cy.get('input').clear()
      cy.get('input').type('Item 1')
      cy.get('.v-list-item')
        .should('have.length', 2)
      cy.get('input').clear()
      cy.get('input').type('Item 3')
      cy.get('input').should('have.length', 1)
    })

    it('should filter items when using multiple', () => {
      const items = [
        'Item 1',
        'Item 1a',
        'Item 2',
        'Item 2a',
      ]

      cy.mount(() => (
        <VCombobox items={ items } multiple />
      ))
        .get('input')
        .type('Item')
      cy.get('.v-list-item')
        .should('have.length', 4)
      cy.get('input:first-child').as('input')
        .clear()
      cy.get('@input').type('Item 1')
      cy.get('.v-list-item')
        .should('have.length', 2)
      cy.get('@input').clear()
      cy.get('@input').type('Item 3')
      cy.get('.v-list-item')
        .should('have.length', 0)
    })

    it('should filter with custom item shape', () => {
      const items = [
        {
          id: 1,
          name: 'Test1',
        },
        {
          id: 2,
          name: 'Antonsen PK',
        },
      ]

      cy.mount(() => (
        <VCombobox
          items={ items }
          item-value="id"
          item-title="name"
        />
      ))
        .get('input')
        .type('test')
      cy.get('.v-list-item')
        .should('have.length', 1)
        .eq(0)
        .should('have.text', 'Test1')
      cy.get('input').clear()
      cy.get('input').type('antonsen')
      cy.get('.v-list-item')
        .should('have.length', 1)
        .eq(0)
        .should('have.text', 'Antonsen PK')
    })
  })

  describe('prefilled data', () => {
    it('should work with array of strings when using multiple', () => {
      const items = ref(['California', 'Colorado', 'Florida'])

      const selectedItems = ref(['California', 'Colorado'])

      cy.mount(() => (
        <VCombobox v-model={ selectedItems.value } items={ items.value } multiple chips closableChips />
      ))

      cy.get('.v-combobox input').click()

      cy.get('.v-list-item--active').should('have.length', 2)
      cy.get('input').get('.v-chip').should('have.length', 2)

      cy.get('.v-chip__close')
        .eq(0)
        .click()
      cy.get('input').should('exist')
      cy.get('.v-chip')
        .should('have.length', 1)
      cy.should(() => expect(selectedItems.value).to.deep.equal(['Colorado']))
    })

    it('should work with objects when using multiple', () => {
      const items = ref([
        {
          title: 'Item 1',
          value: 'item1',
        },
        {
          title: 'Item 2',
          value: 'item2',
        },
        {
          title: 'Item 3',
          value: 'item3',
        },
      ])

      const selectedItems = ref(
        [
          {
            title: 'Item 1',
            value: 'item1',
          },
          {
            title: 'Item 2',
            value: 'item2',
          },
        ]
      )

      cy.mount(() => (
        <VCombobox
          v-model={ selectedItems.value }
          items={ items.value }
          multiple
          chips
          closableChips
          returnObject
        />
      ))

      cy.get('.v-combobox input').click()

      cy.get('.v-list-item--active').should('have.length', 2)
      cy.get('input').get('.v-chip').should('have.length', 2)

      cy.get('.v-chip__close')
        .eq(0)
        .click()
      cy.get('input').should('exist')
      cy.get('.v-chip')
        .should('have.length', 1)
      cy.should(() => expect(selectedItems.value).to.deep.equal([{
        title: 'Item 2',
        value: 'item2',
      }]))
    })
  })

  describe('readonly', () => {
    it('should not be clickable when in readonly', () => {
      const items = ['Item 1', 'Item 2', 'Item 3', 'Item 4']

      const selectedItems = 'Item 1'

      cy.mount(() => (
        <VCombobox
          items={ items }
          modelValue={ selectedItems }
          readonly
        />
      ))

      cy.get('.v-combobox')
        .click()
      cy.get('.v-list-item').should('have.length', 0)
        .get('.v-select--active-menu').should('have.length', 0)

      cy.get('.v-combobox input').as('input')
        .focus()
      cy.get('@input').type('{downarrow}', { force: true })
      cy.get('.v-list-item').should('have.length', 0)
        .get('.v-select--active-menu').should('have.length', 0)
    })

    it('should not be clickable when in readonly form', () => {
      const items = ['Item 1', 'Item 2', 'Item 3', 'Item 4']

      const selectedItems = 'Item 1'

      cy.mount(() => (
        <VForm readonly>
          <VCombobox
            items={ items }
            modelValue={ selectedItems }
            readonly
          />
        </VForm>
      ))

      cy.get('.v-combobox')
        .click()
      cy.get('.v-list-item').should('have.length', 0)
        .get('.v-select--active-menu').should('have.length', 0)

      cy.get('.v-combobox input').as('input')
        .focus()
      cy.get('@input').type('{downarrow}', { force: true })
      cy.get('.v-list-item').should('have.length', 0)
        .get('.v-select--active-menu').should('have.length', 0)
    })
  })

  describe('hide-selected', () => {
    it('should hide selected item(s)', () => {
      const items = [
        'Item 1',
        'Item 2',
        'Item 3',
        'Item 4',
      ]

      const selectedItems = [
        'Item 1',
        'Item 2',
      ]

      cy.mount(() => (
        <VCombobox items={ items } modelValue={ selectedItems } multiple hideSelected />
      ))

      cy.get('.v-combobox input').click()

      cy.get('.v-overlay__content .v-list-item').should('have.length', 2)
      cy.get('.v-overlay__content .v-list-item .v-list-item-title').eq(0).should('have.text', 'Item 3')
      cy.get('.v-overlay__content .v-list-item .v-list-item-title').eq(1).should('have.text', 'Item 4')
    })
  })

  // https://github.com/vuetifyjs/vuetify/issues/17120
  it('should display 0 when selected', () => {
    const items = [0, 1, 2, 3, 4]

    const selectedItems = ref(undefined)

    cy.mount(() => (
      <VCombobox
        items={ items }
        v-model={ selectedItems.value }
      />
    ))
      .get('.v-field input')
      .click()

    cy.get('.v-list-item').eq(0)
      .click({ waitForAnimations: false })

    cy.get('.v-combobox input')
      .should('have.value', '0')
  })

  it('should conditionally show placeholder', () => {
    cy.mount(props => (
      <VCombobox placeholder="Placeholder" { ...props } />
    ))
      .get('.v-combobox input')
      .should('have.attr', 'placeholder', 'Placeholder')
      .setProps({ label: 'Label' })
      .get('.v-combobox input')
      .should('not.be.visible')
      .get('.v-combobox input')
      .focus()
      .should('have.attr', 'placeholder', 'Placeholder')
      .should('be.visible')
      .blur()
      .setProps({ persistentPlaceholder: true })
      .get('.v-combobox input')
      .should('have.attr', 'placeholder', 'Placeholder')
      .should('be.visible')
      .setProps({ modelValue: 'Foobar' })
      .get('.v-combobox input')
      .should('not.have.attr', 'placeholder')
      .setProps({ multiple: true, modelValue: ['Foobar'] })
      .get('.v-combobox input')
      .should('not.have.attr', 'placeholder')
  })

  it('should keep TextField focused while selecting items from open menu', () => {
    cy.mount(() => (
      <VCombobox
        multiple
        items={['California', 'Colorado', 'Florida', 'Georgia', 'Texas', 'Wyoming']}
      />
    ))

    cy.get('.v-combobox')
      .click()

    cy.get('.v-list')
      .trigger('keydown', { key: keyValues.down, waitForAnimations: false })
      .trigger('keydown', { key: keyValues.down, waitForAnimations: false })
      .trigger('keydown', { key: keyValues.down, waitForAnimations: false })

    cy.get('.v-field').should('have.class', 'v-field--focused')
  })

  it('should not open menu when closing a chip', () => {
    cy
      .mount(() => (
        <VCombobox
          chips
          closable-chips
          items={['foo', 'bar']}
          label="Select"
          modelValue={['foo', 'bar']}
          multiple
        />
      ))
      .get('.v-combobox')
      .should('not.have.class', 'v-combobox--active-menu')
      .get('.v-chip__close').eq(1)
      .click()
      .get('.v-combobox')
      .should('not.have.class', 'v-combobox--active-menu')
      .get('.v-chip__close')
      .click()
      .get('.v-combobox')
      .should('not.have.class', 'v-combobox--active-menu')
      .click()
      .should('have.class', 'v-combobox--active-menu')
      .trigger('keydown', { key: keyValues.esc })
      .should('not.have.class', 'v-combobox--active-menu')
  })

  it('should auto-select-first item when pressing enter', () => {
    cy
      .mount(() => (
        <VCombobox
          items={['California', 'Colorado', 'Florida', 'Georgia', 'Texas', 'Wyoming']}
          multiple
          autoSelectFirst
        />
      ))
      .get('.v-combobox')
      .click()
      .get('.v-list-item')
      .should('have.length', 6)
      .get('.v-combobox input')
      .type('Cal')
      .get('.v-list-item').eq(0)
      .should('have.class', 'v-list-item--active')
      .get('.v-combobox input')
      .trigger('keydown', { key: keyValues.enter, waitForAnimations: false })
      .get('.v-list-item')
      .should('have.length', 6)
  })

  describe('Showcase', () => {
    generate({ stories })
  })
})


<template>
  <v-row justify="center">
    <v-dialog
      v-model="dialog"
      width="800"
    >
      <template v-slot:activator="{ props }">
        <v-btn
          color="primary"
          v-bind="props"
        >
          Open Dialog
        </v-btn>
      </template>
      <v-card>
        <v-card-title>
          Use Google's location service?
        </v-card-title>
        <v-card-text>
          Lorem ipsum dolor sit amet, semper quis, sapien id natoque elit. Nostra urna at, magna at neque sed sed ante imperdiet, dolor mauris cursus velit, velit non, sem nec. Volutpat sem ridiculus placerat leo, augue in, duis erat proin condimentum in a eget, sed fermentum sed vestibulum varius ac, vestibulum volutpat orci ut elit eget tortor. Ultrices nascetur nulla gravida ante arcu. Pharetra rhoncus morbi ipsum, nunc tempor debitis, ipsum pellentesque, vitae id quam ut mauris dui tempor, aptent non. Quisque turpis. Phasellus quis lectus luctus orci eget rhoncus. Amet donec vestibulum mattis commodo, nulla aliquet, nibh praesent, elementum nulla. Sit lacus pharetra tempus magna neque pellentesque, nulla vel erat.
          Justo ex quisque nulla accusamus venenatis, sed quis. Nibh phasellus gravida metus in, fusce aenean ut erat commodo eros. Ut turpis, dui integer, nonummy pede placeat nec in sit leo. Faucibus porttitor illo taciti odio, amet viverra scelerisque quis quis et tortor, curabitur morbi a. Enim tempor at, rutrum elit condimentum, amet rutrum vitae tempor torquent nunc. Praesent vestibulum integer maxime felis. Neque aenean quia vitae nostra, tempus elit enim id dui, at egestas pulvinar. Integer libero vestibulum, quis blandit scelerisque mattis fermentum nulla, tortor donec vestibulum dolor amet eget, elit nullam. Aliquam leo phasellus aliquam curabitur metus a, nulla justo mattis duis interdum vel, mollis vitae et id, vestibulum erat ridiculus sit pulvinar justo sed. Vehicula convallis, et nulla wisi, amet vestibulum risus, quam ac egestas.
          Et vitae, nulla gravida erat scelerisque nullam nunc pellentesque, a dictumst cras augue, purus imperdiet non. Varius montes cursus varius vel tortor, nec leo a qui, magni cras, velit vel consectetuer lobortis vel. Nibh erat et wisi felis leo porttitor, sapien nibh sapien pede mi, sed eget porttitor, repellendus arcu ac quis. Luctus vulputate aut est sem magna, placerat accumsan nunc vestibulum ipsum ac auctor, maecenas lorem in ut nec mauris tortor, doloribus varius sem tortor vestibulum mollis, eleifend tortor felis tempus lacus eu eu. Eleifend vel eu, nullam maecenas mauris nec nunc euismod, tortor porta ridiculus potenti, massa tristique nam magna, et wisi placerat et erat ante. Eget pede erat in facilisis, fermentum venenatis sodales. Ac tortor sociis et non animi tristique, rhoncus malesuada, ut arcu volutpat scelerisque sollicitudin, elit curabitur dui pede purus dolor, integer aenean risus taciti nulla eleifend accumsan. At pulvinar diam parturient, interdum mi velit aliquet et a. Arcu at ac placerat eget justo semper, purus sociis curabitur mi ipsum consequat ut, mollis vestibulum, est ante ornare lacus sem. Neque magna mauris, commodo quisque, praesent semper suscipit lobortis nam. Justo malesuada cursus ac nunc litora nunc. Tellus ac, in lobortis nunc, montes lectus purus fermentum.
          Ac sit wisi. Sodales aliquam, sed vestibulum nullam arcu sit risus arcu, id luctus vitae lorem nibh, integer nec nullam class cursus mi, purus arcu lectus. Vel ante suscipit volutpat potenti mattis sed, wisi eu placerat aliquam erat, lectus morbi lobortis at assumenda. Consequat neque purus ipsum voluptas odio, netus vestibulum ut nec, suspendisse pellentesque nec enim in. Wisi dictum sed semper a, ipsum erat tellus habitasse est, erat sem ornare, vitae quisque ultricies. Dui sed blandit. Tempor et faucibus justo sed luctus, nec vitae vitae. Nunc nibh pede, ipsum vestibulum aenean leo ante ultricies, nam cras quis sed penatibus amet. In mauris a. Integer metus mauris tortor, et rutrum vestibulum ultricies, ut phasellus in ullamcorper ut mollit, eu justo. Cursus pretium venenatis.
          Cras pellentesque vel sodales accumsan aenean. Feugiat metus sit nec in aliquet amet, porttitor pretium vulputate massa. Consequat ipsum luctus quisque adipiscing libero. Wisi sollicitudin. Eget vitae ac lobortis, lorem natoque vestibulum et, aliquet faucibus at morbi nibh, vel condimentum. Massa unde orci sed id sed, odio donec congue nec praesent amet. Hymenaeos velit lacus, quis vivamus libero tempus duis, eu nisi eu, ipsum at accumsan pede justo morbi donec, massa et libero sit risus neque tortor. Ut sed sed etiam hendrerit dapibus, quis metus suspendisse nibh.
          Fringilla tempor felis augue magna. Cum arcu a, id vitae. Pellentesque pharetra in cras sociis adipiscing est. Nibh nec mattis at maecenas, nisl orci aliquam nulla justo egestas venenatis, elementum duis vel porta eros, massa vitae, eligendi imperdiet amet. Nec neque luctus suscipit, justo sem praesent, ut nisl quisque, volutpat torquent wisi tellus aliquam reprehenderit, curabitur cras at quis massa porttitor mauris. Eros sed ultrices. Amet dignissim justo urna feugiat mauris litora, etiam accumsan, lobortis a orci suspendisse. Semper ac mauris, varius bibendum pretium, orci urna nunc ullamcorper auctor, saepe sem integer quam, at feugiat egestas duis. Urna ligula ante. Leo elementum nonummy. Sagittis mauris est in ipsum, nulla amet non justo, proin id potenti platea posuere sit ut, nunc sit erat bibendum. Nibh id auctor, ab nulla vivamus ultrices, posuere morbi nunc tellus gravida vivamus.
          Mauris nec, facilisi quam fermentum, ut mauris integer, orci tellus tempus diam ut in pellentesque. Wisi faucibus tempor et odio leo diam, eleifend quis integer curabitur sit scelerisque ac, mauris consequat luctus quam penatibus fringilla dis, vitae lacus in, est eu ac tempus. Consectetuer amet ipsum amet dui, sed blandit id sed. Tellus integer, dignissim id pede sodales quis, felis dolorem id mauris orci, orci tempus ut. Nullam hymenaeos. Curabitur in a, tortor ut praesent placerat tincidunt interdum, ac dignissim metus nonummy hendrerit wisi, etiam ut.
          Semper praesent integer fusce, tortor suspendisse, augue ligula orci ante asperiores ullamcorper. In sit per mi sed sed, mi vestibulum mus nam, morbi mauris neque vitae aliquam proin senectus. Ac amet arcu mollis ante congue elementum, inceptos eget optio quam pellentesque quis lobortis, sollicitudin sed vestibulum sollicitudin, lectus parturient nullam, leo orci ligula ultrices. At tincidunt enim, suspendisse est sit sem ac. Amet tellus molestie est purus magna augue, non etiam et in wisi id. Non commodo, metus lorem facilisi lobortis ac velit, montes neque sed risus consectetuer fringilla dolor. Quam justo et integer aliquam, cursus nulla enim orci, nam cursus adipiscing, integer torquent non, fringilla per maecenas. Libero ipsum sed tellus purus et. Duis molestie placerat erat donec ut. Dolor enim erat massa faucibus ultrices in, ante ultricies orci lacus, libero consectetuer mauris magna feugiat neque dapibus, donec pretium et. Aptent dui, aliquam et et amet nostra ligula.
          Augue curabitur duis dui volutpat, tempus sed ut pede donec. Interdum luctus, lectus nulla aenean elit, id sit magna, vulputate ultrices pellentesque vel id fermentum morbi. Tortor et. Adipiscing augue lorem cum non lacus, rutrum sodales laoreet duis tortor, modi placerat facilisis et malesuada eros ipsum, vehicula tempus. Ac vivamus amet non aliquam venenatis lectus, sociosqu adipiscing consequat nec arcu odio. Blandit orci nec nec, posuere in pretium, enim ut, consectetuer nullam urna, risus vel. Nullam odio vehicula massa sed, etiam sociis mauris, lacus ullamcorper, libero imperdiet non sodales placerat justo vehicula. Nec morbi imperdiet. Fermentum sem libero iaculis bibendum et eros, eget maecenas non nunc, ad pellentesque. Ut nec diam elementum interdum. Elementum vitae tellus lacus vitae, ipsum phasellus, corporis vehicula in ac sed massa vivamus, rutrum elit, ultricies metus volutpat.
          Semper wisi et, sollicitudin nunc vestibulum, cursus accumsan nunc pede tempus mi ipsum, ligula sed. Non condimentum ac dolor sit. Mollis eu aliquam, vel mattis mollis massa ut dolor ante, tempus lacinia arcu. Urna vestibulum lorem, nulla fermentum, iaculis ut congue ac vivamus. Nam libero orci, pulvinar nulla, enim pellentesque consectetuer leo, feugiat rhoncus rhoncus vel. Magna sociosqu donec, dictum cursus ullamcorper viverra. Ultricies quis orci lorem, suspendisse ut vestibulum integer, purus sed lorem pulvinar habitasse turpis.
          +
        </v-card-text>
        <v-card-actions>
          <v-spacer></v-spacer>
          <v-btn
            color="green-darken-1"
            variant="text"
            @click="dialog = false"
          >
            Disagree
          </v-btn>
          <v-btn
            color="green-darken-1"
            variant="text"
            @click="dialog = false"
          >
            Agree
          </v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>
  </v-row>
</template>


<template>
  <v-container fluid>
    <v-sparkline
      :fill="fill"
      :gradient="selectedGradient"
      :line-width="width"
      :padding="padding"
      :smooth="radius || false"
      :value="value"
      auto-draw
    ></v-sparkline>

    <v-divider></v-divider>

    <v-row>
      <v-col
        cols="12"
        md="6"
      >
        <v-row
          class="fill-height"
          align="center"
        >
          <v-item-group
            v-model="selectedGradient"
            mandatory
          >
            <v-row>
              <v-item
                v-for="(gradient, i) in gradients"
                :key="i"
                v-slot="{ active, toggle }"
                :value="gradient"
              >
                <v-card
                  :style="{
                    background: gradient.length > 1
                      ? `linear-gradient(0deg, ${gradient})`
                      : gradient[0],
                    border: '2px solid',
                    borderColor: active ? '#222' : 'white'
                  }"
                  width="30"
                  height="30"
                  class="me-2"
                  @click="toggle"
                ></v-card>
              </v-item>
            </v-row>
          </v-item-group>
        </v-row>
      </v-col>

      <v-col
        cols="12"
        md="6"
      >
        <v-slider
          v-model="width"
          label="Width"
          min="0.1"
          max="10"
          step="0.1"
          thumb-label
        ></v-slider>
      </v-col>

      <v-col cols="6">
        <v-row
          class="fill-height"
          align="center"
        >
          <v-switch
            v-model="fill"
            label="Filled"
          ></v-switch>
        </v-row>
      </v-col>

      <v-col
        cols="12"
        md="6"
      >
        <v-slider
          v-model="radius"
          label="Radius"
          min="0"
          max="25"
          thumb-label
        ></v-slider>
      </v-col>

      <v-col
        cols="12"
        md="6"
        offset-md="6"
      >
        <v-slider
          v-model="padding"
          label="Padding"
          min="0"
          max="25"
          thumb-label
        ></v-slider>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
  const gradients = [
    ['#222'],
    ['#42b3f4'],
    ['red', 'orange', 'yellow'],
    ['purple', 'violet'],
    ['#00c6ff', '#F0F', '#FF0'],
    ['#f72047', '#ffd200', '#1feaea'],
  ]

  export default {
    data: () => ({
      fill: true,
      selectedGradient: gradients[4],
      gradients,
      padding: 8,
      radius: 10,
      value: [0, 2, 5, 9, 5, 10, 3, 5, 0, 0, 1, 8, 2, 9, 0],
      width: 2,
    }),
  }
</script>


project_id_env: CROWDIN_PROJECT_ID
api_token_env: CROWDIN_PERSONAL_TOKEN
project_identifier: vuetify
preserve_hierarchy: true
files:
  - source: packages/api-generator/src/locale/en/**/*.json
    translation: packages/api-generator/src/locale/%locale%/**/%original_file_name%
  - source: packages/docs/src/pages/en/**/*.md
    translation: packages/docs/src/pages/%locale%/**/%original_file_name%
  - source: packages/docs/src/i18n/messages/en.json
    translation: packages/docs/src/i18n/messages/%locale%.json


'Default input': <VTextField label="label" />,

Disabled: <VTextField label="label" disabled />,

Affixes: <VTextField label="label" prefix="prefix" suffix="suffix" />,

'Prepend/append': <VTextField label="label" prependIcon="$vuetify" appendIcon="$vuetify" />,

'Prepend/append inner': <VTextField label="label" prependInnerIcon="$vuetify" appendInnerIcon="$vuetify" />,

Placeholder: <VTextField label="label" placeholder="placeholder" persistentPlaceholder />,

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

Affixes: <VTextField prefix="prefix" suffix="suffix" />,

'Prepend/append': <VTextField prependIcon="$vuetify" appendIcon="$vuetify" />,

'Prepend/append inner': <VTextField prependInnerIcon="$vuetify" appendInnerIcon="$vuetify" />,

Placeholder: <VTextField placeholder="placeholder" persistentPlaceholder />,


const variants = ['underlined', 'outlined', 'filled', 'solo', 'plain'] as const

const densities = ['default', 'comfortable', 'compact'] as const

const stories = Object.fromEntries(Object.entries({

---
nav: Selection
meta:
  title: Data table - Selection
  description: The data table component is used for displaying tabular data in a way that is easy for users to scan. It includes sorting, searching, pagination and selection.
  keywords: data tables, vuetify data table component, vue data table component
related:
  - /components/data-tables/basics/
  - /components/tables/
---

# Data table - Selection

## Item value

For the selection feature to work, the data table must be able to differentiate each row in the data set. This is done using the **item-value** prop. It designates a property on the item that should contain a unique value. By default the property it looks for is `id`.

You can also supply a function, if for example the unique value needs to be a composite of several properties. The function receives each item as its first argument.

<example file="v-data-table/prop-item-value" />

## Selected values

The current selection of the data-table can be accessed through the **v-model** prop. The array will consist of the unique values found in the property you set using the **item-value** prop (or the value returned by the function you passed). You can use **return-object** prop if you want the array to consist of the actual objects instead.

<example file="v-data-table/prop-return-object" />

<entry />

## Examples

### Selectable rows

Use the **item-selectable** prop to designate a property on your items that controls if the item should be selectable or not.

<example file="v-data-table/prop-item-selectable" />

### Select strategies

Data-tables support three different select strategies.

|Strategy|Description|
|-|-|
|`'single'`|Only a single row can be selected. The select all checkbox in the header is not shown|
|`'page'`|Multiple rows can be selected. Clicking on the select all checkbox in the header selects all (selectable) rows on the current page|
|`'all'`|Multiple rows can be selected. Clicking on the select all checkbox in the header selects all (selectable) rows in the entire data set|

<example file="v-data-table/prop-select-strategy" />


<template>
  <v-layout style="height: 128px;" class="border rounded">
    <div class="mx-auto my-4">
      <v-btn
        color="deep-purple"
        variant="outlined"
        @click="active = !active"
      >
        Toggle Navigation
      </v-btn>
    </div>

    <v-bottom-navigation
      :active="active"
      color="indigo"
    >
      <v-btn>
        <v-icon>mdi-history</v-icon>

        Recents
      </v-btn>

      <v-btn>
        <v-icon>mdi-heart</v-icon>

        Favorites
      </v-btn>

      <v-btn>
        <v-icon>mdi-map-marker</v-icon>

        Nearby
      </v-btn>
    </v-bottom-navigation>

  </v-layout>
</template>

<script>
  export default {
    data: () => ({ active: true }),
  }
</script>


<template>
  <usage-example
    v-model="model"
    :code="code"
    :options="options"
    :name="name"
  >
    <div>
      <v-radio-group v-bind="props">
        <v-radio label="Radio 1" value="1"></v-radio>
        <v-radio label="Radio 2" value="2"></v-radio>
        <v-radio label="Radio 3" value="3"></v-radio>
      </v-radio-group>
    </div>

    <template v-slot:configuration>
      <v-checkbox v-model="label" label="Radio group label"></v-checkbox>
    </template>
  </usage-example>
</template>

<script setup>
  // Utilities
  import { computed, ref } from 'vue'
  import { propsToString } from '@/util/helpers'

  const name = 'v-radio-group'
  const model = ref('default')
  const options = ['inline']
  const label = ref(false)
  const props = computed(() => {
    return {
      inline: model.value === 'inline' || undefined,
      label: label.value ? 'Radio group label' : undefined,
    }
  })

  const slots = computed(() => {
    return `
  <v-radio label="Radio 1" value="1"></v-radio>
  <v-radio label="Radio 2" value="2"></v-radio>
  <v-radio label="Radio 3" value="3"></v-radio>
`
  })

  const code = computed(() => {
    return `<${name}${propsToString(props.value)}>${slots.value}</${name}>`
  })
</script>


{ cloneVNode(v, { variant, density, modelValue: 'Value' }) }

keep keep keep replace replace keep keep keep keep

{ cloneVNode(v, { variant, density, label: `with value`, modelValue: 'Value' }) }

/// <reference types="../../../../types/cypress" />

import { CenteredGrid, generate } from '@/../cypress/templates'

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

// Utilities
import { cloneVNode, ref } from 'vue'

const oneMBFile = new File([new ArrayBuffer(1021576)], '1MB file')
const twoMBFile = new File([new ArrayBuffer(2021152)], '2MB file')

const variants = ['underlined', 'outlined', 'filled', 'solo', 'plain'] as const
const densities = ['default', 'comfortable', 'compact'] as const
const items = ['California', 'Colorado', 'Florida', 'Georgia', 'Texas', 'Wyoming'] as const

const stories = Object.fromEntries(Object.entries({
  'Default input': <VFileInput label="label" />,
  Disabled: <VFileInput label="label" items={ items } disabled />,
  Affixes: <VFileInput label="label" items={ items } prefix="prefix" suffix="suffix" />,
  'Prepend/append': <VFileInput label="label" items={ items } prependIcon="$vuetify" appendIcon="$vuetify" />,
  'Prepend/append inner': <VFileInput label="label" items={ items } prependInnerIcon="$vuetify" appendInnerIcon="$vuetify" />,
  Placeholder: <VFileInput label="label" items={ items } placeholder="placeholder" persistentPlaceholder />,
}).map(([k, v]) => [k, (

    { variants.map(variant => (
      densities.map(density => (

          { cloneVNode(v, { variant, density }) }
          { cloneVNode(v, { variant, density, modelValue: [oneMBFile, twoMBFile] }) }
          { cloneVNode(v, { variant, density, chips: true, modelValue: [oneMBFile, twoMBFile] }) }
          <VFileInput
            variant={ variant }
            density={ density }
            modelValue={[oneMBFile, twoMBFile]}
            { ...v.props }
          >{{
            selection: ({ fileNames }) => {
              return fileNames.map(f => f)
            },
          }}
          </VFileInput>

      ))
    )).flat()}

)]))

describe('VFileInput', () => {
  it('should add file', () => {
    cy.mount(() => (
      <CenteredGrid width="400px">
        <VFileInput label="foo" counter />
      </CenteredGrid>
    ))
      .get('.v-file-input input')
      .attachFile('text.txt')
      .get('.v-file-input .v-field__input')
      .should('have.text', 'text.txt')
  })

  it('should show number of files', () => {
    cy.mount(() => (
      <CenteredGrid width="400px">
        <VFileInput label="foo" modelValue={[oneMBFile, twoMBFile]} multiple counter />
      </CenteredGrid>
    ))
      .get('.v-file-input .v-input__details')
      .should('have.text', '2 files')
  })

  it('should show size of files', () => {
    cy.mount(() => (
      <CenteredGrid width="400px">
        <VFileInput label="foo" modelValue={[oneMBFile, twoMBFile]} multiple show-size />
      </CenteredGrid>
    ))
      .get('.v-file-input .v-field__input')
      .should('have.text', '1MB file (1.0 MB), 2MB file (2.0 MB)')
  })

  it('should show total size of files in counter', () => {
    cy.mount(() => (
      <CenteredGrid width="400px">
        <VFileInput label="foo" modelValue={[oneMBFile, twoMBFile]} multiple counter show-size />
      </CenteredGrid>
    ))
      .get('.v-file-input .v-input__details')
      .should('have.text', '2 files (3.0 MB in total)')
  })

  it('should clear input', () => {
    const model = ref([oneMBFile, twoMBFile])
    cy.mount(() => (
      <CenteredGrid width="400px">
        <VFileInput label="foo" v-model={ model.value } />
      </CenteredGrid>
    ))
      .get('.v-field__clearable > .v-icon')
      .click()
    cy.get('.v-input input')
      .should('have.value', '')
  })

  it('should support removing clearable icon', () => {
    cy.mount(() => (
      <CenteredGrid width="400px">
        <VFileInput label="foo" modelValue={[oneMBFile, twoMBFile]} clearable={ false } />
      </CenteredGrid>
    ))
      .get('.v-field__append-inner > .v-btn')
      .should('not.exist')
  })

  it('should be disabled', () => {
    cy.mount(() => (
      <CenteredGrid width="400px">
        <VFileInput label="foo" modelValue={[oneMBFile, twoMBFile]} disabled />
      </CenteredGrid>
    ))
      .get('.v-file-input')
      .should('have.class', 'v-input--disabled')
      .get('.v-file-input input')
      .should('have.attr', 'disabled')
  })

  it('should support no prepend icon', () => {
    cy.mount(() => (
      <CenteredGrid width="400px">
        <VFileInput label="foo" modelValue={[oneMBFile, twoMBFile]} prependIcon="" />
      </CenteredGrid>
    ))
      .get('.v-file-input .v-input__prepend')
      .should('not.exist')
  })

  it('should support chips', () => {
    cy.mount(() => (
      <CenteredGrid width="400px">
        <VFileInput label="foo" modelValue={[oneMBFile, twoMBFile]} chips />
      </CenteredGrid>
    ))
      .get('.v-file-input .v-chip')
      .should('have.length', 2)
  })

  // https://github.com/vuetifyjs/vuetify/issues/8167
  it('should not emit change event when input is blurred', () => {
    const change = cy.spy().as('change')
    const update = cy.spy().as('update')
    cy.mount(() => (
      <VFileInput label="foo" />
    ), {
      props: {
        onChange: change,
        'onUpdate:modelValue': update,
      },
    })
      .get('.v-file-input input').as('input')
      .focus()
    cy.get('@input').attachFile('text.txt')
    cy.get('@input').blur()
    cy.then(() => {
      expect(change).to.be.calledOnce
      expect(update).to.be.calledOnce
    })
  })

  it('should put extra attributes on input', () => {
    cy.mount(() => (
      <CenteredGrid width="400px">
        <VFileInput label="foo" accept="image/*" />
      </CenteredGrid>
    ))
      .get('.v-file-input input')
      .should('have.attr', 'accept', 'image/*')
  })

  /**
   * https://github.com/vuetifyjs/vuetify/issues/16486
   */
  it('should reset the underlying HTMLInput when model is controlled input', () => {
    function TestWrapper () {
      const files = ref<File[]>([])
      const onReset = () => {
        files.value = []
      }
      return (
        <CenteredGrid width="400px">
          <VFileInput modelValue={ files.value } />
          <button onClick={ onReset }>Reset Model Value</button>
        </CenteredGrid>
      )
    }

    cy.mount(() => (
      <TestWrapper />
    ))
      .get('.v-file-input input').as('input')
      .should($res => {
        const input = $res[0] as HTMLInputElement
        expect(input.files).to.have.length(0)
      })
    // add file
    cy.get('@input').attachFile('text.txt')
      .should($res => {
        const input = $res[0] as HTMLInputElement
        expect(input.files).to.have.length(1)
      })
    // reset input from wrapper/parent component
    cy.get('button').click()
    cy.get('@input')
      .should($res => {
        const input = $res[0] as HTMLInputElement
        expect(input.files).to.have.length(0)
      })
      // add same file again
      .attachFile('text.txt')
      .should($res => {
        const input = $res[0] as HTMLInputElement
        expect(input.files).to.have.length(1)
      })
    // reset input from wrapper/parent component
    cy.get('button').click()
    cy.get('@input')
      .should($res => {
        const input = $res[0] as HTMLInputElement
        expect(input.files).to.have.length(0)
      })
  })

  describe('Showcase', () => {
    generate({ stories })
  })
})


// Imports
import { cleanupOutdatedCaches, matchPrecache, precacheAndRoute } from 'workbox-precaching'
import { registerRoute, setCatchHandler, setDefaultHandler } from 'workbox-routing'
import { NetworkFirst, NetworkOnly } from 'workbox-strategies'

const MANIFEST = self.__WB_MANIFEST

cleanupOutdatedCaches()
precacheAndRoute(MANIFEST)

const networkOnly = new NetworkOnly()
const networkFirst = new NetworkFirst()

registerRoute(
  ({ url }) => ['https://tag.researchnow.com', 'https://srv.carbonads.net', 'https://pixel.adsafeprotected.com', 'https://cdn4.buysellads.net', 'https://ad.doubleclick.net'].includes(url.origin),
  networkOnly
)

registerRoute(
  ({ request }) => request.destination !== 'document',
  networkFirst
)

registerRoute(
  ({ url, request }) => url.origin === self.location.origin && request.destination === 'document',
  async options => {
    const { url } = options
    const fallback = await getFallbackDocument(url)
    if (fallback) {
      console.log(`[SW] serving fallback for ${url.pathname}`)
    }

    return fallback ?? networkOnly.handle(options)
  }
)

setDefaultHandler(networkFirst)

setCatchHandler(async ({ url, request }) => {
  if (
    url.origin === self.location.origin &&
    request.destination === 'document'
  ) {
    return getFallbackDocument(url)
  }

  return Response.error()
})

let previousManifest
self.addEventListener('message', async event => {
  if (event.data === 'sw:update' || event.data?.type === 'SKIP_WAITING') {
    console.log('[SW] Skip waiting')
    self.skipWaiting()
  } else if (event.data?.type === 'GET_MANIFEST') {
    event.ports[0].postMessage(MANIFEST)
  } else if (event.data?.type === 'SET_MANIFEST') {
    previousManifest = event.data.manifest
  } else if (event.data?.type === 'CLEAN_CACHE') {
    await cleanCache(event.data.manifest)
  } else console.log(event)

  event.ports[0].postMessage({ type: 'DONE' })
})

self.addEventListener('activate', event => {
  previousManifest && event.waitUntil(cleanCache(previousManifest))
  event.waitUntil(self.clients.claim())
})

function getFallbackDocument (url) {
  return matchPrecache(url.pathname.startsWith('/eo-UY/') ? '_crowdin.html' : '/_fallback.html')
}

async function cleanCache (manifest) {
  const precache = await openCache('precache')

  const responses = await Promise.all(
    manifest.map(entry => precache.match(entry.url + (entry.revision ? `?__WB_REVISION__=${entry.revision}` : '')))
  )

  // Date of earliest entry in the old manifest
  const date = Array.from(
    new Set(responses.map(getDate))
  ).sort()[0]

  let n = 0
  for (const cache of [precache, await openCache('runtime')]) {
    for (const req of await cache.keys()) {
      const res = await cache.match(req)
      if (res && getDate(res) < date) {
        ++n
        await cache.delete(req)
      }
    }
  }
  console.log(`[SW] Cleared ${n} old items from cache`)
}

async function openCache (name) {
  const precache = (await caches.keys()).find(k => k.includes(self.registration.scope) && k.includes(`-${name}-`))

  if (!precache) return

  return caches.open(precache)
}

function getDate (response) {
  const date = new Date(Object.fromEntries(response.headers).date)
  date.setMinutes(0, 0, 0)
  return date.getTime()
}


// Directives
import Touch from '../'

// Utilities
import { describe, expect, it } from '@jest/globals'
import { mount } from '@vue/test-utils'
import { nextTick } from 'vue'
import { touch } from '@/../test'

// Types
import type { TouchValue } from '../'

describe('v-touch', () => {
  const mountFunction = (value: TouchValue): Element => {
    const wrapper = mount({
      directives: { Touch },
      props: {
        value: Object,
      },
      template: '
',
    }, { props: { value } })

    return wrapper.element
  }

  it('should call directive handlers', () => {
    const down = jest.fn()
    touch(mountFunction({ down })).start(0, 0).end(0, 20)
    expect(down).toHaveBeenCalled()

    const up = jest.fn()
    touch(mountFunction({ up })).start(0, 0).end(0, -20)
    expect(up).toHaveBeenCalled()

    const left = jest.fn()
    touch(mountFunction({ left })).start(0, 0).end(-20, 0)
    expect(left).toHaveBeenCalled()

    const right = jest.fn()
    touch(mountFunction({ right })).start(0, 0).end(20, 0)
    expect(right).toHaveBeenCalled()

    const start = jest.fn()
    touch(mountFunction({ start })).start(0, 0)
    expect(start).toHaveBeenCalled()

    const move = jest.fn()
    touch(mountFunction({ move })).move(0, 0)
    expect(move).toHaveBeenCalled()

    const end = jest.fn()
    touch(mountFunction({ end })).end(0, 0)
    expect(end).toHaveBeenCalled()
  })

  it('should call directive handlers if not straight down/up/right/left', async () => {
    const nope = jest.fn()
    const down = jest.fn()
    touch(mountFunction({ down, right: nope })).start(0, 0).end(5, 20)
    expect(nope).not.toHaveBeenCalled()
    expect(down).toHaveBeenCalled()
  })

  it('should not call directive handlers if distance is too small', async () => {
    const down = jest.fn()
    touch(mountFunction({ down })).start(0, 0).end(0, 10)
    expect(down).not.toHaveBeenCalled()

    const up = jest.fn()
    touch(mountFunction({ up })).start(0, 0).end(0, -10)
    expect(up).not.toHaveBeenCalled()

    const left = jest.fn()
    touch(mountFunction({ left })).start(0, 0).end(-10, 0)
    expect(left).not.toHaveBeenCalled()

    const right = jest.fn()
    touch(mountFunction({ right })).start(0, 0).end(10, 0)
    expect(right).not.toHaveBeenCalled()
  })

  it('should unmount', async () => {
    const start = jest.fn()
    const wrapper = mount({
      directives: { Touch },
      props: {
        value: Object,
        bound: Boolean,
      },
      template: '

',
    }, {
      props: {
        value: { start },
        bound: true,
      },
    })
    const el = wrapper.element

    await nextTick()
    await wrapper.setProps({ bound: false })

    touch(el).start(0, 0)
    expect(start.mock.calls).toHaveLength(0)
  })
})


export { VBanner } from './VBanner'
export { VBannerActions } from './VBannerActions'
export { VBannerText } from './VBannerText'


const changedFiles = await showCommit(cwd, "--name-only");

describe("with spurious -- arguments", () => {

it("ignores the extra arguments with cheesy parseConfiguration()", async () => {

const cwd = await initFixture("lifecycle");

await lernaVersion(cwd)("--yes", "--", "--loglevel", "ignored", "--blah");

const logMessages = loggingOutput("warn");

expect(logMessages).toContain("Arguments after -- are no longer passed to subprocess executions.");

"use strict";

const log = require("npmlog");
const semver = require("semver");

/**
 * @see https://github.com/yargs/yargs/blob/master/docs/advanced.md#providing-a-command-module
 */
exports.command = "version [bump]";

exports.describe = "Bump version of packages changed since the last release.";

exports.builder = (yargs, composed) => {
  const opts = {
    "allow-branch": {
      describe: "Specify which branches to allow versioning from.",
      type: "array",
    },
    amend: {
      describe: "Amend the existing commit, instead of generating a new one.",
      type: "boolean",
    },
    "conventional-commits": {
      describe: "Use conventional-changelog to determine version bump and generate CHANGELOG.",
      type: "boolean",
    },
    "conventional-graduate": {
      describe: "Version currently prereleased packages to a non-prerelease version.",
      // type must remain ambiguous because it is overloaded (boolean _or_ string _or_ array)
    },
    "conventional-prerelease": {
      describe: "Version changed packages as prereleases when using --conventional-commits.",
      // type must remain ambiguous because it is overloaded (boolean _or_ string _or_ array)
    },
    "changelog-preset": {
      describe: "Custom conventional-changelog preset.",
      type: "string",
      requiresArg: true,
      defaultDescription: "angular",
    },
    exact: {
      describe: "Specify cross-dependency version numbers exactly rather than with a caret (^).",
      type: "boolean",
    },
    "force-publish": {
      describe: "Always include targeted packages in versioning operations, skipping default logic.",
      // type must remain ambiguous because it is overloaded (boolean _or_ string _or_ array)
    },
    "git-remote": {
      describe: "Push git changes to the specified remote.",
      type: "string",
      requiresArg: true,
      defaultDescription: "origin",
    },
    "create-release": {
      describe: "Create an official GitHub or GitLab release for every version.",
      type: "string",
      choices: ["gitlab", "github"],
    },
    "ignore-changes": {
      describe: [
        "Ignore changes in files matched by glob(s) when detecting changed packages.",
        "Pass --no-ignore-changes to completely disable.",
      ].join("\n"),
      type: "array",
    },
    "ignore-scripts": {
      describe: "Disable all lifecycle scripts",
      type: "boolean",
    },
    "include-merged-tags": {
      describe: "Also include tags from merged branches",
      type: "boolean",
    },
    m: {
      describe: "Use a custom commit message when creating the version commit.",
      alias: "message",
      type: "string",
      requiresArg: true,
    },
    "no-changelog": {
      describe: "Do not generate CHANGELOG.md files when using --conventional-commits.",
      type: "boolean",
    },
    changelog: {
      // proxy for --no-changelog
      hidden: true,
      type: "boolean",
    },
    "no-commit-hooks": {
      describe: "Do not run git commit hooks when committing version changes.",
      type: "boolean",
    },
    "commit-hooks": {
      // proxy for --no-commit-hooks
      hidden: true,
      type: "boolean",
    },
    "no-git-tag-version": {
      describe: "Do not commit or tag version changes.",
      type: "boolean",
    },
    "git-tag-version": {
      // proxy for --no-git-tag-version
      hidden: true,
      type: "boolean",
    },
    "no-push": {
      describe: "Do not push tagged commit to git remote.",
      type: "boolean",
    },
    push: {
      // proxy for --no-push
      hidden: true,
      type: "boolean",
    },
    // preid is copied into ../publish/command because a whitelist for one option isn't worth it
    preid: {
      describe: "Specify the prerelease identifier when versioning a prerelease",
      type: "string",
      requiresArg: true,
      defaultDescription: "alpha",
    },
    "sign-git-commit": {
      describe: "Pass the `--gpg-sign` flag to `git commit`.",
      type: "boolean",
    },
    "sign-git-tag": {
      describe: "Pass the `--sign` flag to `git tag`.",
      type: "boolean",
    },
    "tag-version-prefix": {
      describe: "Customize the tag prefix. To remove entirely, pass an empty string.",
      type: "string",
      requiresArg: true,
      defaultDescription: "v",
    },
    y: {
      describe: "Skip all confirmation prompts.",
      alias: "yes",
      type: "boolean",
    },
  };

  if (composed) {
    // hide options from composed command's help output
    Object.keys(opts).forEach(key => {
      opts[key].hidden = true;
    });

    // set argv.composed for wrapped execution logic
    yargs.default("composed", composed).hide("composed");
  } else {
    exports.addBumpPositional(yargs);
  }

  yargs.options(opts);

  if (!composed) {
    // hide options from composed command's help output
    yargs.group(Object.keys(opts), "Command Options:");
  }

  return yargs
    .option("ignore", {
      // TODO: remove in next major release
      // NOT the same as filter-options --ignore
      hidden: true,
      conflicts: "ignore-changes",
      type: "array",
    })
    .option("cd-version", {
      // TODO: remove in next major release
      hidden: true,
      conflicts: "bump",
      type: "string",
      requiresArg: true,
    })
    .option("repo-version", {
      // TODO: remove in next major release
      hidden: true,
      conflicts: "bump",
      type: "string",
      requiresArg: true,
    })
    .option("skip-git", {
      // TODO: remove in next major release
      hidden: true,
      type: "boolean",
    })
    .option("github-release", {
      // TODO: remove in next major release
      hidden: true,
      type: "boolean",
    })
    .check(argv => {
      /* eslint-disable no-param-reassign */
      if (argv.ignore) {
        argv.ignoreChanges = argv.ignore;
        delete argv.ignore;
        log.warn("deprecated", "--ignore has been renamed --ignore-changes");
      }

      if (argv.cdVersion && !argv.bump) {
        argv.bump = argv.cdVersion;
        delete argv.cdVersion;
        delete argv["cd-version"];
        log.warn("deprecated", "--cd-version has been replaced by positional [bump]");
      }

      if (argv.repoVersion && !argv.bump) {
        argv.bump = argv.repoVersion;
        delete argv.repoVersion;
        delete argv["repo-version"];
        log.warn("deprecated", "--repo-version has been replaced by positional [bump]");
      }

      if (argv.skipGit) {
        argv.gitTagVersion = false;
        argv["git-tag-version"] = false;
        argv.push = false;
        delete argv.skipGit;
        delete argv["skip-git"];
        log.warn("deprecated", "--skip-git has been replaced by --no-git-tag-version --no-push");
      }

      if (argv.githubRelease) {
        argv.createRelease = "github";
        delete argv.githubRelease;
        log.warn("deprecated", "--github-release has been replaced by --create-release=github");
      }
      /* eslint-enable no-param-reassign */

      return argv;
    });
};

exports.handler = function handler(argv) {
  return require(".")(argv);
};

exports.addBumpPositional = function addBumpPositional(yargs, additionalKeywords = []) {
  const semverKeywords = ["major", "minor", "patch", "premajor", "preminor", "prepatch", "prerelease"].concat(
    additionalKeywords
  );
  const bumpOptionList = `'${semverKeywords.slice(0, -1).join("', '")}', or '${
    semverKeywords[semverKeywords.length - 1]
  }'.`;

  yargs.positional("bump", {
    describe: `Increment version(s) by explicit version _or_ semver keyword,\n${bumpOptionList}`,
    type: "string",
    coerce: choice => {
      if (!semver.valid(choice) && semverKeywords.indexOf(choice) === -1) {
        throw new Error(`bump must be an explicit version string _or_ one of: ${bumpOptionList}`);
      }

      return choice;
    },
  });
};