'click:prepend-inner': (e: MouseEvent) => true,

'click:append-inner': (e: MouseEvent) => true,

'click:control': (e: MouseEvent) => true,

'update:focused': (focused: boolean) => true,

keep keep keep replace replace keep keep keep

// Styles
import './VInput.sass'

// Components
import { VIcon } from '@/components/VIcon'
import { VMessages } from '@/components/VMessages'

// Composables
import { makeDensityProps, useDensity } from '@/composables/density'
import { makeValidationProps, useValidation } from '@/composables/validation'

// Utilities
import { computed } from 'vue'
import { genericComponent, getUid, pick, propsFactory, useRender } from '@/util'

// Types
import type { ComputedRef, ExtractPropTypes, PropType, Ref } from 'vue'
import type { MakeSlots } from '@/util'

export interface VInputSlot {
  id: ComputedRef<string>
  isDirty: ComputedRef<boolean>
  isDisabled: ComputedRef<boolean>
  isReadonly: ComputedRef<boolean>
  isPristine: Ref<boolean>
  isValid: ComputedRef<boolean | null>
  isValidating: Ref<boolean>
  reset: () => void
  resetValidation: () => void
  validate: () => void
}

export const makeVInputProps = propsFactory({
  id: String,
  appendIcon: String,
  prependIcon: String,
  hideDetails: [Boolean, String] as PropType<boolean | 'auto'>,
  messages: {
    type: [Array, String] as PropType<string | string[]>,
    default: () => ([]),
  },
  direction: {
    type: String as PropType<'horizontal' | 'vertical'>,
    default: 'horizontal',
    validator: (v: any) => ['horizontal', 'vertical'].includes(v),
  },

  ...makeDensityProps(),
  ...makeValidationProps(),
})

export type VInputSlots = MakeSlots<{
  default: [VInputSlot]
  prepend: [VInputSlot]
  append: [VInputSlot]
  details: [VInputSlot]
}>

export const VInput = genericComponent<new <T>() => {
  $slots: VInputSlots
}>()({
  name: 'VInput',

  props: {
    ...makeVInputProps(),
  },

  emits: {
    'click:prepend': (e: MouseEvent) => true,
    'click:append': (e: MouseEvent) => true,
    'update:modelValue': (val: any) => true,
  },

  setup (props, { slots, emit }) {
    const { densityClasses } = useDensity(props)
    const {
      errorMessages,
      isDirty,
      isDisabled,
      isReadonly,
      isPristine,
      isValid,
      isValidating,
      reset,
      resetValidation,
      validate,
      validationClasses,
    } = useValidation(props)

    const uid = getUid()
    const id = computed(() => props.id || `input-${uid}`)

    const slotProps = computed<VInputSlot>(() => ({
      id,
      isDirty,
      isDisabled,
      isReadonly,
      isPristine,
      isValid,
      isValidating,
      reset,
      resetValidation,
      validate,
    }))

    useRender(() => {
      const hasPrepend = !!(slots.prepend || props.prependIcon)
      const hasAppend = !!(slots.append || props.appendIcon)
      const hasMessages = !!(
        props.messages?.length ||
        errorMessages.value.length
      )
      const hasDetails = !props.hideDetails || (
        props.hideDetails === 'auto' &&
        hasMessages
      )

      return (
        <div class={[
          'v-input',
          `v-input--${props.direction}`,
          densityClasses.value,
          validationClasses.value,
        ]}
        >
          { hasPrepend && (

              { slots?.prepend?.(slotProps.value) }

              { props.prependIcon && (
                <VIcon
                  onClick={ (e: MouseEvent) => emit('click:prepend', e) }
                  icon={ props.prependIcon }
                />
              ) }

          ) }

          { slots.default && (

              { slots.default?.(slotProps.value) }

          ) }

          { hasAppend && (

              { slots?.append?.(slotProps.value) }

              { props.appendIcon && (
                <VIcon
                  onClick={ (e: MouseEvent) => emit('click:append', e) }
                  icon={ props.appendIcon }
                />
              ) }

          ) }

          { hasDetails && (

              <VMessages
                active={ hasMessages }
                messages={ errorMessages.value.length > 0
                  ? errorMessages.value
                  : props.messages
                }
                v-slots={{ message: slots.message }}
              />

              { slots.details?.(slotProps.value) }

          ) }

      )
    })

    return {
      reset,
      resetValidation,
      validate,
    }
  },
})

export type VInput = InstanceType<typeof VInput>

export function filterInputProps (props: ExtractPropTypes<ReturnType<typeof makeVInputProps>>) {
  return pick(props, Object.keys(VInput.props) as any)
}


<template>
  <div>
    <v-card
      class="d-flex flex-wrap-reverse"
      color="grey lighten-2"
      flat
      tile
    >
      <v-card
        v-for="n in 20"
        :key="n"
        class="pa-2"
        outlined
        tile
      >
        Flex item {{ n }}
      </v-card>
    </v-card>
  </div>
</template>


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

import './VBottomSheet.sass'

// Extensions
import VDialog from '../VDialog/VDialog'

/* @vue/component */
export default VDialog.extend({
  name: 'v-bottom-sheet',

  props: {
    inset: Boolean,
    maxWidth: {
      type: [String, Number],
      default: 'auto',
    },
    transition: {
      type: String,
      default: 'bottom-sheet-transition',
    },
  },

  computed: {
    classes (): object {
      return {
        ...VDialog.options.computed.classes.call(this),
        'v-bottom-sheet': true,
        'v-bottom-sheet--inset': this.inset,
      }
    },
  },
})


const { DataEvents, DataProps, DataDefaultScopedSlotProps } = require('./v-data')
const { DataFooterPageTextScopedProps } = require('./v-data-footer')

const DataIteratorProps = DataProps.concat([
  { name: 'value', source: 'v-data-iterator' },
  { name: 'itemKey', source: 'v-data-iterator' },
  { name: 'singleSelect', source: 'v-data-iterator' },
  { name: 'expanded', source: 'v-data-iterator' },
  { name: 'singleExpand', source: 'v-data-iterator' },
  { name: 'loading', source: 'v-data-iterator' },
  { name: 'loadingText', source: 'v-data-iterator' },
  { name: 'noResultsText', source: 'v-data-iterator' },
  { name: 'noDataText', source: 'v-data-iterator' },
  { name: 'hideDefaultFooter', source: 'v-data-iterator' },
  { name: 'footerProps', source: 'v-data-iterator' },
])

const DataIteratorEvents = DataEvents.concat([
  { name: 'input', source: 'v-data-iterator', value: 'any[]' },
  { name: 'update:expanded', source: 'v-data-iterator', value: 'any[]' },
  { name: 'item-selected', source: 'v-data-iterator', value: '{ item: any, value: boolean }' },
  { name: 'item-expanded', source: 'v-data-iterator', value: '{ item: any, value: boolean }' },
  { name: 'toggle-select-all', source: 'v-data-iterator', value: '{ items: any[], value: boolean }' },
])

const DataIteratorSlots = [
  { name: 'default', source: 'v-data-iterator' },
  { name: 'loading', source: 'v-data-iterator' },
  { name: 'no-data', source: 'v-data-iterator' },
  { name: 'no-results', source: 'v-data-iterator' },
]

const DataIteratorItemScopedProps = {
  expand: '(v: boolean) => void',
  index: 'number',
  item: 'any',
  isExpanded: 'boolean',
  isMobile: 'boolean',
  isSelected: 'boolean',
  select: '(v: boolean) => void',
}

const DataIteratorScopedSlots = [
  {
    name: 'default',
    props: {
      ...DataDefaultScopedSlotProps,
      isSelected: '(item: any) => boolean',
      select: '(item: any, value: boolean) => void',
      isExpanded: '(item: any) => boolean',
      expand: '(item: any, value: boolean) => void',
    },
    source: 'data-iterator',
  },
  {
    name: 'footer',
    props: DataDefaultScopedSlotProps,
    source: 'data-iterator',
  },
  {
    name: 'footer.page-text',
    props: DataFooterPageTextScopedProps,
    source: 'data-iterator',
  },
  {
    name: 'header',
    props: DataDefaultScopedSlotProps,
    source: 'data-iterator',
  },
  {
    name: 'item',
    props: DataIteratorItemScopedProps,
    source: 'data-iterator',
  },
]

module.exports = {
  'v-data-iterator': {
    props: DataIteratorProps,
    slots: [
      ...DataIteratorSlots,
      ...DataIteratorScopedSlots,
    ],
    events: DataIteratorEvents,
  },
  DataIteratorProps,
  DataIteratorEvents,
  DataIteratorSlots,
  DataIteratorScopedSlots,
  DataIteratorItemScopedProps,
}


keep keep keep keep keep replace keep keep keep

// Styles
import './VField.sass'

// Components
import { VExpandXTransition } from '@/components/transitions'
import { VIcon } from '@/components/VIcon'
import { VFieldLabel } from './VFieldLabel'

// Composables
import { LoaderSlot, makeLoaderProps, useLoader } from '@/composables/loader'
import { makeThemeProps, provideTheme } from '@/composables/theme'
import { useBackgroundColor, useTextColor } from '@/composables/color'
import { makeFocusProps, useFocus } from '@/composables/focus'

// Utilities
import { computed, ref, toRef, watch } from 'vue'
import {
  convertToUnit,
  genericComponent,
  getUid,
  nullifyTransforms,
  pick,
  propsFactory,
  standardEasing,
  useRender,
} from '@/util'

// Types
import type { VInputSlot } from '@/components/VInput/VInput'
import type { LoaderSlotProps } from '@/composables/loader'
import type { PropType, Ref } from 'vue'
import type { MakeSlots } from '@/util'

const allowedVariants = ['underlined', 'outlined', 'filled', 'contained', 'plain'] as const
type Variant = typeof allowedVariants[number]

export interface DefaultInputSlot {
  isActive: Ref<boolean>
  isFocused: Ref<boolean>
  controlRef: Ref<HTMLElement | undefined>
  focus: () => void
  blur: () => void
}

export interface VFieldSlot extends DefaultInputSlot {
  props: Record<string, unknown>
}

export const makeVFieldProps = propsFactory({
  appendInnerIcon: String,
  bgColor: String,
  clearable: Boolean,
  clearIcon: {
    type: String,
    default: '$clear',
  },
  active: Boolean,
  color: String,
  dirty: Boolean,
  disabled: Boolean,
  error: Boolean,
  label: String,
  persistentClear: Boolean,
  prependInnerIcon: String,
  reverse: Boolean,
  singleLine: Boolean,
  variant: {
    type: String as PropType<Variant>,
    default: 'filled',
    validator: (v: any) => allowedVariants.includes(v),
  },

  ...makeThemeProps(),
  ...makeLoaderProps(),
}, 'v-field')

export type VFieldSlots = MakeSlots<{
  clear: []
  prependInner: [DefaultInputSlot & VInputSlot]
  appendInner: [DefaultInputSlot & VInputSlot]
  label: [DefaultInputSlot & VInputSlot]
  loader: [LoaderSlotProps]
  default: [VFieldSlot]
}>

export const VField = genericComponent<new <T>() => {
  $props: {
    modelValue?: T
    'onUpdate:modelValue'?: (val: T) => any
  }
  $slots: VFieldSlots
}>()({
  name: 'VField',

  inheritAttrs: false,

  props: {
    id: String,

    ...makeFocusProps(),
    ...makeVFieldProps(),
  },

  emits: {
    'click:clear': (e: MouseEvent) => true,
    'click:prepend-inner': (e: MouseEvent) => true,
    'click:append-inner': (e: MouseEvent) => true,
    'click:control': (e: MouseEvent) => true,
    'update:focused': (focused: boolean) => true,
    'update:modelValue': (val: any) => true,
  },

  setup (props, { attrs, emit, slots }) {
    const { themeClasses } = provideTheme(props)
    const { loaderClasses } = useLoader(props)
    const { focusClasses, isFocused, focus, blur } = useFocus(props)

    const isActive = computed(() => props.dirty || props.active)
    const hasLabel = computed(() => !props.singleLine && !!(props.label || slots.label))

    const uid = getUid()
    const id = computed(() => props.id || `input-${uid}`)

    const labelRef = ref<VFieldLabel>()
    const floatingLabelRef = ref<VFieldLabel>()
    const controlRef = ref<HTMLElement>()

    const { backgroundColorClasses, backgroundColorStyles } = useBackgroundColor(toRef(props, 'bgColor'))
    const { textColorClasses, textColorStyles } = useTextColor(computed(() => {
      return (
        isActive.value &&
        isFocused.value &&
        !props.error &&
        !props.disabled
      ) ? props.color : undefined
    }))

    watch(isActive, val => {
      if (hasLabel.value) {
        const el: HTMLElement = labelRef.value!.$el
        const targetEl: HTMLElement = floatingLabelRef.value!.$el
        const rect = nullifyTransforms(el)
        const targetRect = targetEl.getBoundingClientRect()

        const x = targetRect.x - rect.x
        const y = targetRect.y - rect.y - (rect.height / 2 - targetRect.height / 2)

        const targetWidth = targetRect.width / 0.75
        const width = Math.abs(targetWidth - rect.width) > 1
          ? { maxWidth: convertToUnit(targetWidth) }
          : undefined

        const duration = parseFloat(getComputedStyle(el).transitionDuration) * 1000
        const scale = parseFloat(getComputedStyle(targetEl).getPropertyValue('--v-field-label-scale'))

        el.style.visibility = 'visible'
        targetEl.style.visibility = 'hidden'

        el.animate([
          { transform: 'translate(0)' },
          { transform: `translate(${x}px, ${y}px) scale(${scale})`, ...width },
        ], {
          duration,
          easing: standardEasing,
          direction: val ? 'normal' : 'reverse',
        }).finished.then(() => {
          el.style.removeProperty('visibility')
          targetEl.style.removeProperty('visibility')
        })
      }
    }, { flush: 'post' })

    const slotProps = computed<DefaultInputSlot>(() => ({
      isActive,
      isFocused,
      controlRef,
      blur,
      focus,
    }))

    function onClick (e: MouseEvent) {
      if (e.target !== document.activeElement) {
        e.preventDefault()
      }

      emit('click:control', e)
    }

    useRender(() => {
      const isOutlined = props.variant === 'outlined'
      const hasPrepend = (slots.prependInner || props.prependInnerIcon)
      const hasClear = !!(props.clearable || slots.clear)
      const hasAppend = !!(slots.appendInner || props.appendInnerIcon || hasClear)
      const label = slots.label
        ? slots.label({
          label: props.label,
          props: { for: id.value },
        })
        : props.label

      return (
        <div
          class={[
            'v-field',
            {
              'v-field--active': isActive.value,
              'v-field--appended': hasAppend,
              'v-field--disabled': props.disabled,
              'v-field--dirty': props.dirty,
              'v-field--error': props.error,
              'v-field--has-background': !!props.bgColor,
              'v-field--persistent-clear': props.persistentClear,
              'v-field--prepended': hasPrepend,
              'v-field--reverse': props.reverse,
              'v-field--single-line': props.singleLine,
              [`v-field--variant-${props.variant}`]: true,
            },
            themeClasses.value,
            backgroundColorClasses.value,
            focusClasses.value,
            loaderClasses.value,
          ]}
          style={[
            backgroundColorStyles.value,
            textColorStyles.value,
          ]}
          onClick={ onClick }
          { ...attrs }
        >

          <LoaderSlot
            name="v-field"
            active={ props.loading }
            color={ props.error ? 'error' : props.color }
            v-slots={{ default: slots.loader }}
          />

          { hasPrepend && (

              { props.prependInnerIcon && (
                <VIcon
                  onClick={ (e: MouseEvent) => emit('click:prepend-inner', e) }
                  icon={ props.prependInnerIcon }
                />
              ) }

              { slots?.prependInner?.(slotProps.value) }

          ) }

            { ['contained', 'filled'].includes(props.variant) && hasLabel.value && (
              <VFieldLabel
                ref={ floatingLabelRef }
                class={[textColorClasses.value]}
                floating
              >
                { label }
              </VFieldLabel>
            ) }

            <VFieldLabel ref={ labelRef } for={ id.value }>
              { label }
            </VFieldLabel>

            { slots.default?.({
              ...slotProps.value,
              props: {
                id: id.value,
                class: 'v-field__input',
              },
              focus,
              blur,
            } as VFieldSlot) }

          { hasClear && (
            <VExpandXTransition>

                { slots.clear
                  ? slots.clear()
                  : (
                    <VIcon
                      onClick={ (e: MouseEvent) => emit('click:clear', e) }
                      icon={ props.clearIcon }
                    />
                  )
                }

            </VExpandXTransition>
          ) }

          { hasAppend && (

              { slots?.appendInner?.(slotProps.value) }

              { props.appendInnerIcon && (
                <VIcon
                  onClick={ (e: MouseEvent) => emit('click:append-inner', e) }
                  icon={ props.appendInnerIcon }
                />
              ) }

          ) }

            { isOutlined && (
              <>

                { hasLabel.value && (

                    <VFieldLabel ref={ floatingLabelRef } floating>
                      { label }
                    </VFieldLabel>

                ) }

              </>
            ) }

            { ['plain', 'underlined'].includes(props.variant) && hasLabel.value && (
              <VFieldLabel ref={ floatingLabelRef } floating>
                { label }
              </VFieldLabel>
            ) }

      )
    })

    return {
      controlRef,
    }
  },
})

export type VField = InstanceType<typeof VField>

// TODO: this is kinda slow, might be better to implicitly inherit props instead
export function filterFieldProps (attrs: Record<string, unknown>) {
  return pick(attrs, Object.keys(VField.props))
}


// Defaults
$messages-color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity)) !default;
$messages-font-size: 12px !default;
$messages-line-height: $messages-font-size !default;
$messages-min-height: 14px !default;
$messages-transition-duration: 150ms !default;


// Composables
import { provideDefaults } from '@/composables/defaults'

// Utility
import { defineComponent, useRender } from '@/util'

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

  props: {
    color: String,
    density: String,
  },

  setup (props, { slots }) {
    provideDefaults({
      VBtn: {
        color: props.color,
        density: props.density,
        variant: 'text',
      },
    })

    useRender(() => (
      <div class="v-banner-actions">
        { slots?.default?.() }
      </div>
    ))

    return {}
  },
})


node scripts/warn-npm-install.js && yarn run lint --since HEAD && yarn lerna run test --since HEAD

lerna run start --scope vuetifyjs.com --stream

yarn lint && lerna run test && lerna run cy:run && yarn build


onClick={ (e: MouseEvent) => emit('click:append-inner', e) }

keep keep keep keep keep replace keep keep keep keep keep


// Utilities
import { computed, inject, provide, ref, unref } from 'vue'
import { mergeDeep } from '@/util/helpers'

// Types
import type { ComputedRef, InjectionKey, Ref } from 'vue'
import type { MaybeRef } from '@/util'

export interface DefaultsInstance {
  [key: string]: undefined | Record<string, unknown>
  global?: Record<string, unknown>
}

export type DefaultsOptions = Partial<DefaultsInstance>

export const DefaultsSymbol: InjectionKey<Ref<DefaultsInstance>> = Symbol.for('vuetify:defaults')

export function createDefaults (options?: DefaultsInstance): Ref<DefaultsInstance> {
  return ref(options ?? {})
}

export function useDefaults () {
  const defaults = inject(DefaultsSymbol)

  if (!defaults) throw new Error('[Vuetify] Could not find defaults instance')

  return defaults
}

export function provideDefaults (
  defaults?: MaybeRef<DefaultsInstance | undefined>,
  options?: {
    reset?: MaybeRef<number | string | undefined>
    root?: MaybeRef<boolean | undefined>
    scoped?: MaybeRef<boolean | undefined>
  }
) {
  const injectedDefaults = useDefaults()
  const providedDefaults = ref(defaults)

  const newDefaults = computed(() => {
    const scoped = unref(options?.scoped)
    const reset = unref(options?.reset)
    const root = unref(options?.root)

    let properties = mergeDeep(providedDefaults.value, { prev: injectedDefaults.value })

    if (scoped) return properties

    if (reset || root) {
      const len = Number(reset || Infinity)

      for (let i = 0; i <= len; i++) {
        if (!properties.prev) break

        properties = properties.prev
      }

      return properties
    }

    return mergeDeep(properties.prev, properties)
  }) as ComputedRef<DefaultsInstance>

  provide(DefaultsSymbol, newDefaults)

  return newDefaults
}


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

import { VLayout } from '@/components'
import { VNavigationDrawer } from '..'

describe('VNavigationDrawer', () => {
  beforeEach(() => {
    cy.viewport(1280, 768)
  })

  it('should open when changed to permanent on mobile', () => {
    cy.viewport(400, 800)

    cy.mount(({ permanent }: any) => (
      <VLayout>
        <VNavigationDrawer permanent={ permanent } />
      </VLayout>
    ))

    cy.get('.v-navigation-drawer').should('have.class', 'v-navigation-drawer--temporary')

    cy.vue().then(wrapper => {
      wrapper.setProps({ permanent: true })
    })

    cy.get('.v-navigation-drawer').should('not.have.class', 'v-navigation-drawer--temporary')
  })

  it('should change width when using rail, expandOnHover, and hovering', () => {
    cy.mount((props: any) => (
      <VLayout>
        <VNavigationDrawer { ...props } />
      </VLayout>
    ))

    cy.vue().then(wrapper => {
      wrapper.setProps({ rail: true, expandOnHover: true })
    })

    cy.get('.v-navigation-drawer').should('have.css', 'width', '72px')

    cy.get('.v-navigation-drawer').trigger('mouseenter')

    cy.get('.v-navigation-drawer').should('have.css', 'width', '256px')

    cy.get('.v-navigation-drawer').trigger('mouseleave')

    cy.get('.v-navigation-drawer').should('have.css', 'width', '72px')
  })

  it('should hide drawer if window resizes below mobile breakpoint', () => {
    cy.mount(() => (
      <VLayout>
        <VNavigationDrawer />
      </VLayout>
    ))

    cy.get('.v-navigation-drawer').should('have.class', 'v-navigation-drawer--active')

    cy.viewport(400, 800)

    cy.get('.v-navigation-drawer').should('not.have.class', 'v-navigation-drawer--active')
  })

  it('should not hide drawer if window resizes below mobile breakpoint and disable-resize-watcher is used', () => {
    cy.mount(() => (
      <VLayout>
        <VNavigationDrawer disableResizeWatcher />
      </VLayout>
    ))

    cy.get('.v-navigation-drawer').should('have.class', 'v-navigation-drawer--active')

    cy.viewport(400, 800)

    cy.get('.v-navigation-drawer').should('have.class', 'v-navigation-drawer--active')
  })

  it('should always show drawer if using permanent', () => {
    cy.mount(() => (
      <VLayout>
        <VNavigationDrawer permanent />
      </VLayout>
    ))

    cy.get('.v-navigation-drawer').should('have.class', 'v-navigation-drawer--active')

    cy.viewport(400, 800)

    cy.get('.v-navigation-drawer').should('have.class', 'v-navigation-drawer--active')
    cy.get('.v-navigation-drawer').should('not.have.class', 'v-navigation-drawer--temporary')
  })

  it('should show temporary drawer', () => {
    cy.mount(({ active }: any) => (
      <VLayout>
        <VNavigationDrawer temporary model-value={ active } />
      </VLayout>
    ))

    cy.get('.v-navigation-drawer').should('have.class', 'v-navigation-drawer--temporary')

    cy.get('.v-navigation-drawer').should('not.have.class', 'v-navigation-drawer--active')

    cy.vue().then(wrapper => wrapper.setProps({ active: true }))

    cy.get('.v-navigation-drawer').should('have.class', 'v-navigation-drawer--active')
  })

  it('should allow custom widths', () => {
    cy.mount(() => (
      <VLayout>
        <VNavigationDrawer width={ 300 } permanent />
      </VLayout>
    ))

    cy.get('.v-navigation-drawer').should('have.css', 'width', '300px')
  })

  it('should position drawer to the right', () => {
    cy.mount(() => (
      <VLayout>
        <VNavigationDrawer position="right" permanent />
      </VLayout>
    ))

    cy.get('.v-navigation-drawer').should('have.css', 'right', '0px')
  })

  it('should position drawer on the bottom', () => {
    cy.mount(() => (
      <VLayout>
        <VNavigationDrawer position="bottom" permanent />
      </VLayout>
    ))

    cy.get('.v-navigation-drawer').should('have.css', 'bottom', '0px')
  })
})


font-size: calc(var(--v-icon-size-multiplier) * #{map.get($icon-sizes, $name)})

keep keep keep add keep keep keep keep keep

// Styles
import './VIcon.sass'

// Composables
import { makeSizeProps, useSize } from '@/composables/size'
import { makeTagProps } from '@/composables/tag'
import { useIcon } from '@/composables/icons'
import { useTextColor } from '@/composables/color'
import { makeThemeProps, provideTheme } from '@/composables/theme'

// Utilities
import { computed, toRef } from 'vue'
import { convertToUnit, defineComponent, flattenFragments, propsFactory } from '@/util'

// Types
import type { IconValue } from '@/composables/icons'
import type { ComputedRef, PropType } from 'vue'

export const makeVIconProps = propsFactory({
  color: String,
  start: Boolean,
  end: Boolean,
  icon: {
    type: [String, Object] as PropType<IconValue>,
  },

  ...makeSizeProps(),
  ...makeTagProps({ tag: 'i' }),
  ...makeThemeProps(),
}, 'v-icon')

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

  props: makeVIconProps(),

  setup (props, { slots }) {
    let slotIcon: ComputedRef<string | undefined> | undefined
    if (slots.default) {
      slotIcon = computed(() => {
        const slot = slots.default?.()
        if (!slot) return

        return flattenFragments(slot).filter(node =>
          node.children && typeof node.children === 'string'
        )[0]?.children as string
      })
    }

    const { themeClasses } = provideTheme(props)
    const { iconData } = useIcon(slotIcon || props)
    const { sizeClasses } = useSize(props)
    const { textColorClasses, textColorStyles } = useTextColor(toRef(props, 'color'))

    return () => {
      return (
        <iconData.value.component
          tag={ props.tag }
          icon={ iconData.value.icon }
          class={[
            'v-icon',
            'notranslate',
            sizeClasses.value,
            textColorClasses.value,
            themeClasses.value,
            {
              'v-icon--start': props.start,
              'v-icon--end': props.end,
            },
          ]}
          style={[
            !sizeClasses.value ? ({
              fontSize: convertToUnit(props.size),
              width: convertToUnit(props.size),
              height: convertToUnit(props.size),
            }) : undefined,
            textColorStyles.value,
          ]}
          aria-hidden="true"
        />
      )
    }
  },
})


@forward './variables'
@use 'sass:map'
@use 'sass:list'
@use '../../styles/settings'
@use '../../styles/tools'
@use './variables' as *

.v-selection-control
  align-items: center
  contain: layout
  display: flex
  flex: 1 0
  grid-area: control
  position: relative
  user-select: none

  .v-label
    height: 100%
    width: 100%

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

  &--error,
  &--disabled
    .v-label
      opacity: 1

  &--error:not(.v-selection-control--disabled)
    .v-label
      color: rgb(var(--v-theme-error))

  &--inline
    display: inline-flex

    .v-label
      width: auto

  @at-root
    @include tools.density('v-selection-control', $selection-control-density) using ($modifier)
      --v-selection-control-size: #{$selection-control-size + $modifier}

.v-selection-control__wrapper
  width: var(--v-selection-control-size)
  height: var(--v-selection-control-size)
  display: inline-flex
  align-items: center
  position: relative
  justify-content: center
  flex: none

.v-selection-control__input
  width: var(--v-selection-control-size)
  height: var(--v-selection-control-size)
  align-items: center
  display: flex
  flex: none
  justify-content: center
  position: relative
  border-radius: 50%

  input
    cursor: pointer
    position: absolute
    left: 0
    top: 0
    width: 100%
    height: 100%
    opacity: 0

  &::before
    @include tools.absolute(true)
    border-radius: 100%
    background-color: currentColor
    opacity: 0
    pointer-events: none

  &:hover::before
    opacity: calc(#{map.get(settings.$states, 'hover')} * var(--v-theme-overlay-multiplier))

  > .v-icon
    opacity: var(--v-medium-emphasis-opacity)

  .v-selection-control--disabled &,
  .v-selection-control--dirty &,
  .v-selection-control--error &
    > .v-icon
      opacity: 1

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

  .v-selection-control--focus-visible &::before
    opacity: calc(#{map.get(settings.$states, 'focus')} * var(--v-theme-overlay-multiplier))


<template>
  <div class="d-flex flex-column justify-space-between align-center">
    <v-select
      v-model="aspectRatio"
      :items="aspectRatios"
    ></v-select>
    <v-slider
      v-model="width"
      class="align-self-stretch"
      min="200"
      max="500"
      step="1"
    ></v-slider>

    <v-img
      :aspect-ratio="aspectRatio"
      :width="width"
      src="https://cdn.vuetifyjs.com/images/parallax/material.jpg"
      cover
    ></v-img>
  </div>
</template>

<script>
  export default {
    data: () => ({
      width: 300,
      aspectRatio: 16 / 9,
      aspectRatios: [
        {
          title: '16/9',
          value: 16 / 9,
        },
        {
          title: '4/3',
          value: 4 / 3,
        },
        {
          title: '1/1',
          value: 1,
        },
      ],
    }),
  }
</script>


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

exports[`VDatePickerTitle.ts should render component and match snapshot 1`] = `

    1234

      2005-11-01

`;

exports[`VDatePickerTitle.ts should render component when selecting year and match snapshot 1`] = `

    1234

      2005-11-01

`;

exports[`VDatePickerTitle.ts should render disabled component and match snapshot 1`] = `

    1234

      2005-11-01

`;

exports[`VDatePickerTitle.ts should render readonly component and match snapshot 1`] = `

    1234

      2005-11-01

`;

exports[`VDatePickerTitle.ts should render year icon 1`] = `

  1234

    year

`;


let slotIcon: ComputedRef<string | undefined> | undefined

keep keep keep replace keep keep keep keep keep


<template>
  <v-row justify="center">
    <v-date-picker
      v-model="date"
      readonly
    ></v-date-picker>
  </v-row>
</template>

<script>
  export default {
    data () {
      return {
        date: (new Date(Date.now() - (new Date()).getTimezoneOffset() * 60000)).toISOString().substr(0, 10),
      }
    },
  }
</script>


import { createSimpleFunctional } from '@/util'

export const VListItemTitle = createSimpleFunctional('v-list-item-title')


import type { LAB, XYZ } from '../colorUtils'

const delta = 0.20689655172413793 // 6÷29

const cielabForwardTransform = (t: number): number => (
  t > delta ** 3
    ? Math.cbrt(t)
    : (t / (3 * delta ** 2)) + 4 / 29
)

const cielabReverseTransform = (t: number): number => (
  t > delta
    ? t ** 3
    : (3 * delta ** 2) * (t - 4 / 29)
)

export function fromXYZ (xyz: XYZ): LAB {
  const transform = cielabForwardTransform
  const transformedY = transform(xyz[1])

  return [
    116 * transformedY - 16,
    500 * (transform(xyz[0] / 0.95047) - transformedY),
    200 * (transformedY - transform(xyz[2] / 1.08883)),
  ]
}

export function toXYZ (lab: LAB): XYZ {
  const transform = cielabReverseTransform
  const Ln = (lab[0] + 16) / 116
  return [
    transform(Ln + lab[1] / 500) * 0.95047,
    transform(Ln),
    transform(Ln - lab[2] / 200) * 1.08883,
  ]
}


<template>
  <v-carousel
    cycle
    height="400"
    hide-delimiter-background
    show-arrows="hover"
  >
    <v-carousel-item
      v-for="(slide, i) in slides"
      :key="i"
    >
      <v-sheet
        :color="colors[i]"
        height="100%"
      >

            {{ slide }} Slide

      </v-sheet>
    </v-carousel-item>
  </v-carousel>
</template>


module.exports = {
  'v-timeline-item': {
    slots: [
      {
        name: 'default',
        props: undefined,
      },
      {
        name: 'icon',
        props: {
          icon: 'string',
          iconColor: 'string',
        },
      },
      {
        name: 'opposite',
        props: undefined,
      },
    ],
  },
}


keep keep keep keep replace replace keep keep keep

// Styles
import './VInput.sass'

// Components
import { VIcon } from '@/components/VIcon'
import { VMessages } from '@/components/VMessages'

// Composables
import { makeDensityProps, useDensity } from '@/composables/density'
import { makeValidationProps, useValidation } from '@/composables/validation'

// Utilities
import { computed } from 'vue'
import { genericComponent, getUid, pick, propsFactory, useRender } from '@/util'

// Types
import type { ComputedRef, ExtractPropTypes, PropType, Ref } from 'vue'
import type { MakeSlots } from '@/util'

export interface VInputSlot {
  id: ComputedRef<string>
  isDirty: ComputedRef<boolean>
  isDisabled: ComputedRef<boolean>
  isReadonly: ComputedRef<boolean>
  isPristine: Ref<boolean>
  isValid: ComputedRef<boolean | null>
  isValidating: Ref<boolean>
  reset: () => void
  resetValidation: () => void
  validate: () => void
}

export const makeVInputProps = propsFactory({
  id: String,
  appendIcon: String,
  prependIcon: String,
  hideDetails: [Boolean, String] as PropType<boolean | 'auto'>,
  messages: {
    type: [Array, String] as PropType<string | string[]>,
    default: () => ([]),
  },
  direction: {
    type: String as PropType<'horizontal' | 'vertical'>,
    default: 'horizontal',
    validator: (v: any) => ['horizontal', 'vertical'].includes(v),
  },

  ...makeDensityProps(),
  ...makeValidationProps(),
})

export type VInputSlots = MakeSlots<{
  default: [VInputSlot]
  prepend: [VInputSlot]
  append: [VInputSlot]
  details: [VInputSlot]
}>

export const VInput = genericComponent<new <T>() => {
  $slots: VInputSlots
}>()({
  name: 'VInput',

  props: {
    ...makeVInputProps(),
  },

  emits: {
    'click:prepend': (e: MouseEvent) => true,
    'click:append': (e: MouseEvent) => true,
    'update:modelValue': (val: any) => true,
  },

  setup (props, { slots, emit }) {
    const { densityClasses } = useDensity(props)
    const {
      errorMessages,
      isDirty,
      isDisabled,
      isReadonly,
      isPristine,
      isValid,
      isValidating,
      reset,
      resetValidation,
      validate,
      validationClasses,
    } = useValidation(props)

    const uid = getUid()
    const id = computed(() => props.id || `input-${uid}`)

    const slotProps = computed<VInputSlot>(() => ({
      id,
      isDirty,
      isDisabled,
      isReadonly,
      isPristine,
      isValid,
      isValidating,
      reset,
      resetValidation,
      validate,
    }))

    useRender(() => {
      const hasPrepend = !!(slots.prepend || props.prependIcon)
      const hasAppend = !!(slots.append || props.appendIcon)
      const hasMessages = !!(
        props.messages?.length ||
        errorMessages.value.length
      )
      const hasDetails = !props.hideDetails || (
        props.hideDetails === 'auto' &&
        hasMessages
      )

      return (
        <div class={[
          'v-input',
          `v-input--${props.direction}`,
          densityClasses.value,
          validationClasses.value,
        ]}
        >
          { hasPrepend && (

              { slots?.prepend?.(slotProps.value) }

              { props.prependIcon && (
                <VIcon
                  onClick={ (e: MouseEvent) => emit('click:prepend', e) }
                  icon={ props.prependIcon }
                />
              ) }

          ) }

          { slots.default && (

              { slots.default?.(slotProps.value) }

          ) }

          { hasAppend && (

              { slots?.append?.(slotProps.value) }

              { props.appendIcon && (
                <VIcon
                  onClick={ (e: MouseEvent) => emit('click:append', e) }
                  icon={ props.appendIcon }
                />
              ) }

          ) }

          { hasDetails && (

              <VMessages
                active={ hasMessages }
                messages={ errorMessages.value.length > 0
                  ? errorMessages.value
                  : props.messages
                }
                v-slots={{ message: slots.message }}
              />

              { slots.details?.(slotProps.value) }

          ) }

      )
    })

    return {
      reset,
      resetValidation,
      validate,
    }
  },
})

export type VInput = InstanceType<typeof VInput>

export function filterInputProps (props: ExtractPropTypes<ReturnType<typeof makeVInputProps>>) {
  return pick(props, Object.keys(VInput.props) as any)
}


// Composables
import { makeRouterProps, useLink } from '@/composables/router'
import { makeTagProps } from '@/composables/tag'
import { useTextColor } from '@/composables/color'

// Utilities
import { computed } from 'vue'
import { defineComponent, useRender } from '@/util'

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

  props: {
    active: Boolean,
    activeClass: String,
    activeColor: String,
    color: String,
    disabled: Boolean,
    text: String,

    ...makeRouterProps(),
    ...makeTagProps({ tag: 'li' }),
  },

  setup (props, { slots, attrs }) {
    const link = useLink(props, attrs)
    const isActive = computed(() => props.active || link.isExactActive?.value)
    const color = computed(() => isActive.value ? props.activeColor : props.color)

    const { textColorClasses, textColorStyles } = useTextColor(color)

    useRender(() => {
      const Tag = link.isLink.value ? 'a' : props.tag
      const hasText = !!(slots.default || props.text)

      return (
        <Tag
          class={[
            'v-breadcrumbs-item',
            {
              'v-breadcrumbs-item--active': isActive.value,
              'v-breadcrumbs-item--disabled': props.disabled,
              'v-breadcrumbs-item--link': link.isLink.value,
              [`${props.activeClass}`]: isActive.value && props.activeClass,
            },
            textColorClasses.value,
          ]}
          style={[
            textColorStyles.value,
          ]}
          href={ link.href.value }
          aria-current={ isActive.value ? 'page' : undefined }
          onClick={ link.navigate }
          v-slots={{
            default: hasText ? () => slots.default?.() ?? props.text : undefined,
          }}
        />
      )
    })

    return {}
  },
})

export type VBreadcrumbsItem = InstanceType<typeof VBreadcrumbsItem>


<template>
  <v-text-field>
    <template v-slot:append>
      <v-icon color="red">
        mdi-plus
      </v-icon>
    </template>
    <template v-slot:prepend>
      <v-icon color="green">
        mdi-minus
      </v-icon>
    </template>
  </v-text-field>
</template>


// Globals
import { IN_BROWSER } from '@/util/globals'

export function copyElementContent (el: HTMLElement) {
  if (!IN_BROWSER) return

  el.setAttribute('contenteditable', 'true')
  el.focus()

  document.execCommand('selectAll', false, undefined)
  document.execCommand('copy')

  el.removeAttribute('contenteditable')
}

export function getBranch () {
  const branch = IN_BROWSER
    ? window.location.hostname.split('.')[0]
    : 'master'

  return ['master', 'dev', 'next'].includes(branch) ? branch : 'master'
}

export const wait = (timeout: number) => {
  return new Promise(resolve => setTimeout(resolve, timeout))
}

export async function waitForReadystate () {
  if (
    !IN_BROWSER ||
    document.readyState === 'interactive'
  ) return

  await new Promise(resolve => {
    const cb = () => {
      window.requestAnimationFrame(resolve)
      window.removeEventListener('DOMContentLoaded', cb)
    }

    window.addEventListener('DOMContentLoaded', cb)
  })
}

/** Jaro-Winkler distance between two strings */
// eslint-disable-next-line max-statements
export function distance (s1: string, s2: string) {
  // Exit early if either are empty.
  if (s1.length === 0 || s2.length === 0) {
    return 0
  }

  // Exit early if they're an exact match.
  if (s1 === s2) {
    return 1
  }

  const range = (Math.floor(Math.max(s1.length, s2.length) / 2)) - 1
  const s1Matches = new Array(s1.length)
  const s2Matches = new Array(s2.length)

  let m = 0
  for (let i = 0; i < s1.length; i++) {
    const low = (i >= range) ? i - range : 0
    const high = (i + range <= (s2.length - 1)) ? (i + range) : (s2.length - 1)

    for (let j = low; j <= high; j++) {
      if (s1Matches[i] !== true && s2Matches[j] !== true && s1[i] === s2[j]) {
        ++m
        s1Matches[i] = s2Matches[j] = true
        break
      }
    }
  }

  // Exit early if no matches were found.
  if (m === 0) {
    return 0
  }

  // Count the transpositions.
  let j; let k = 0
  let numTrans = 0

  for (let i = 0; i < s1.length; i++) {
    if (s1Matches[i] === true) {
      for (j = k; j < s2.length; j++) {
        if (s2Matches[j] === true) {
          k = j + 1
          break
        }
      }

      if (s1[i] !== s2[j]) {
        ++numTrans
      }
    }
  }

  let weight = (m / s1.length + m / s2.length + (m - (numTrans / 2)) / m) / 3
  let l = 0
  const p = 0.1

  if (weight > 0.7) {
    while (s1[l] === s2[l] && l < 4) {
      ++l
    }

    weight = weight + l * p * (1 - weight)
  }

  return weight
}

export function getMatchMedia () {
  if (!IN_BROWSER) return

  return window.matchMedia('(prefers-color-scheme: dark)')
}


const { densityClasses } = useDensity(props)

keep keep keep replace keep keep keep keep

// Styles
import './VTextField.sass'

// Components
import { filterInputProps, makeVInputProps, VInput } from '@/components/VInput/VInput'
import { filterFieldProps, makeVFieldProps, VField } from '@/components/VField/VField'
import { VCounter } from '@/components/VCounter'

// Composables
import { useForwardRef } from '@/composables/forwardRef'
import { useProxiedModel } from '@/composables/proxiedModel'

// Directives
import Intersect from '@/directives/intersect'

// Utilities
import { computed, nextTick, ref } from 'vue'
import { filterInputAttrs, genericComponent, useRender } from '@/util'

// Types
import type { PropType } from 'vue'
import type { VInputSlots } from '@/components/VInput/VInput'
import type { VFieldSlots } from '@/components/VField/VField'

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

export const VTextField = genericComponent<new <T>() => {
  $slots: VInputSlots & VFieldSlots
}>()({
  name: 'VTextField',

  directives: { Intersect },

  inheritAttrs: false,

  props: {
    autofocus: Boolean,
    counter: [Boolean, Number, String] as PropType<true | number | string>,
    counterValue: Function as PropType<(value: any) => number>,
    hint: String,
    persistentHint: Boolean,
    prefix: String,
    placeholder: String,
    persistentPlaceholder: Boolean,
    persistentCounter: Boolean,
    suffix: String,
    type: {
      type: String,
      default: 'text',
    },

    ...makeVInputProps(),
    ...makeVFieldProps(),
  },

  emits: {
    'click:append': (e: MouseEvent) => true,
    'click:append-inner': (e: MouseEvent) => true,
    'click:clear': (e: MouseEvent) => true,
    'click:control': (e: MouseEvent) => true,
    'click:input': (e: MouseEvent) => true,
    'click:prepend': (e: MouseEvent) => true,
    'click:prepend-inner': (e: MouseEvent) => true,
    'update:modelValue': (val: string) => true,
  },

  setup (props, { attrs, emit, slots }) {
    const model = useProxiedModel(props, 'modelValue')
    const counterValue = computed(() => {
      return typeof props.counterValue === 'function'
        ? props.counterValue(model.value)
        : (model.value ?? '').toString().length
    })
    const max = computed(() => {
      if (attrs.maxlength) return attrs.maxlength as undefined

      if (
        !props.counter ||
        (typeof props.counter !== 'number' &&
        typeof props.counter !== 'string')
      ) return undefined

      return props.counter
    })

    function onIntersect (
      isIntersecting: boolean,
      entries: IntersectionObserverEntry[]
    ) {
      if (!props.autofocus || !isIntersecting) return

      (entries[0].target as HTMLInputElement)?.focus?.()
    }

    const vInputRef = ref<VInput>()
    const vFieldRef = ref<VInput>()
    const isFocused = ref(false)
    const inputRef = ref<HTMLInputElement>()
    const isActive = computed(() => (
      activeTypes.includes(props.type) ||
      props.persistentPlaceholder ||
      isFocused.value
    ))
    const messages = computed(() => {
      return props.messages.length
        ? props.messages
        : (isFocused.value || props.persistentHint) ? props.hint : ''
    })
    function onFocus () {
      if (inputRef.value !== document.activeElement) {
        inputRef.value?.focus()
      }

      if (!isFocused.value) isFocused.value = true
    }
    function onControlClick (e: MouseEvent) {
      onFocus()

      emit('click:control', e)
    }
    function onClear (e: MouseEvent) {
      e.stopPropagation()

      onFocus()

      nextTick(() => {
        model.value = ''

        emit('click:clear', e)
      })
    }

    useRender(() => {
      const hasCounter = !!(slots.counter || props.counter || props.counterValue)
      const [rootAttrs, inputAttrs] = filterInputAttrs(attrs)
      const [{ modelValue: _, ...inputProps }] = filterInputProps(props)
      const [fieldProps] = filterFieldProps(props)

      return (
        <VInput
          ref={ vInputRef }
          v-model={ model.value }
          class={[
            'v-text-field',
            {
              'v-text-field--persistent-placeholder': props.persistentPlaceholder,
              'v-text-field--prefixed': props.prefix,
              'v-text-field--suffixed': props.suffix,
              'v-text-field--flush-details': ['plain', 'underlined'].includes(props.variant),
            },
          ]}
          onClick:prepend={ (e: MouseEvent) => emit('click:prepend', e) }
          onClick:append={ (e: MouseEvent) => emit('click:append', e) }
          { ...rootAttrs }
          { ...inputProps }
          messages={ messages.value }
        >
          {{
            ...slots,
            default: ({
              isDisabled,
              isDirty,
              isReadonly,
              isValid,
            }) => (
              <VField
                ref={ vFieldRef }
                onMousedown={ (e: MouseEvent) => {
                  if (e.target === inputRef.value) return

                  e.preventDefault()
                }}
                onClick:control={ onControlClick }
                onClick:clear={ onClear }
                onClick:prependInner={ (e: MouseEvent) => emit('click:prepend-inner', e) }
                onClick:appendInner={ (e: MouseEvent) => emit('click:append-inner', e) }
                role="textbox"
                { ...fieldProps }
                active={ isActive.value || isDirty.value }
                dirty={ isDirty.value || props.dirty }
                focused={ isFocused.value }
                error={ isValid.value === false }
              >
                {{
                  ...slots,
                  default: ({
                    props: { class: fieldClass, ...slotProps },
                  }) => {
                    return (
                      <>
                        { props.prefix && (

                            { props.prefix }

                        ) }

 emit('click:input', e) }
                        >
                          { slots.default?.() }

                          <input
                            ref={ inputRef }
                            v-model={ model.value }
                            v-intersect={[{
                              handler: onIntersect,
                            }, null, ['once']]}
                            autofocus={ props.autofocus }
                            readonly={ isReadonly.value }
                            disabled={ isDisabled.value }
                            name={ props.name }
                            placeholder={ props.placeholder }
                            size={ 1 }
                            type={ props.type }
                            onFocus={ onFocus }
                            onBlur={ () => (isFocused.value = false) }
                            { ...slotProps }
                            { ...inputAttrs }
                          />

                        { props.suffix && (

                            { props.suffix }

                        ) }
                      </>
                    )
                  },
                }}
              </VField>
            ),
            details: hasCounter ? () => (
              <>

                <VCounter
                  active={ props.persistentCounter || isFocused.value }
                  value={ counterValue.value }
                  max={ max.value }
                  v-slots={ slots.counter }
                />
              </>
            ) : undefined,
          }}
        </VInput>
      )
    })

    return useForwardRef({}, vInputRef, vFieldRef, inputRef)
  },
})

export type VTextField = InstanceType<typeof VTextField>


<template>
  <v-snackbar
    v-model="pwa.snackbar"
    bottom
    right
    timeout="-1"
    vertical
  >
    <app-markdown :content="$t('pwa.new-content-available')" />

    <template #actions>
      <v-btn
        class="mx-2 text-none font-weight-regular"
        @click="pwa.ignore"
      >
        <i18n-t
          class="white--text"
          keypath="pwa.ignore"
        />
      </v-btn>
      <app-btn
        variant="contained"
        color="primary"
        @click="pwa.update"
      >
        <i18n-t
          class="white--text"
          keypath="pwa.refresh"
        />
      </app-btn>
    </template>
  </v-snackbar>
</template>

<script setup lang="ts">
  import { usePwaStore } from '@/store/pwa'

  const pwa = usePwaStore()
</script>


module.exports = {
  'v-stepper-items': {
    slots: [
      {
        name: 'default',
        props: undefined,
      },
    ],
  },
}


onClick={ (e: MouseEvent) => emit('click:prepend', e) }

// Styles
import './VField.sass'

// Components
import { VExpandXTransition } from '@/components/transitions'
import { VIcon } from '@/components/VIcon'
import { VFieldLabel } from './VFieldLabel'

// Composables
import { LoaderSlot, makeLoaderProps, useLoader } from '@/composables/loader'
import { makeThemeProps, provideTheme } from '@/composables/theme'
import { useBackgroundColor, useTextColor } from '@/composables/color'
import { makeFocusProps, useFocus } from '@/composables/focus'

// Utilities
import { computed, ref, toRef, watch } from 'vue'
import {
  convertToUnit,
  genericComponent,
  getUid,
  nullifyTransforms,
  pick,
  propsFactory,
  standardEasing,
  useRender,
} from '@/util'

// Types
import type { VInputSlot } from '@/components/VInput/VInput'
import type { LoaderSlotProps } from '@/composables/loader'
import type { PropType, Ref } from 'vue'
import type { MakeSlots } from '@/util'

const allowedVariants = ['underlined', 'outlined', 'filled', 'contained', 'plain'] as const
type Variant = typeof allowedVariants[number]

export interface DefaultInputSlot {
  isActive: Ref<boolean>
  isFocused: Ref<boolean>
  controlRef: Ref<HTMLElement | undefined>
  focus: () => void
  blur: () => void
}

export interface VFieldSlot extends DefaultInputSlot {
  props: Record<string, unknown>
}

export const makeVFieldProps = propsFactory({
  appendInnerIcon: String,
  bgColor: String,
  clearable: Boolean,
  clearIcon: {
    type: String,
    default: '$clear',
  },
  active: Boolean,
  color: String,
  dirty: Boolean,
  disabled: Boolean,
  error: Boolean,
  label: String,
  persistentClear: Boolean,
  prependInnerIcon: String,
  reverse: Boolean,
  singleLine: Boolean,
  variant: {
    type: String as PropType<Variant>,
    default: 'filled',
    validator: (v: any) => allowedVariants.includes(v),
  },

  ...makeThemeProps(),
  ...makeLoaderProps(),
}, 'v-field')

export type VFieldSlots = MakeSlots<{
  clear: []
  prependInner: [DefaultInputSlot & VInputSlot]
  appendInner: [DefaultInputSlot & VInputSlot]
  label: [DefaultInputSlot & VInputSlot]
  loader: [LoaderSlotProps]
  default: [VFieldSlot]
}>

export const VField = genericComponent<new <T>() => {
  $props: {
    modelValue?: T
    'onUpdate:modelValue'?: (val: T) => any
  }
  $slots: VFieldSlots
}>()({
  name: 'VField',

  inheritAttrs: false,

  props: {
    id: String,

    ...makeFocusProps(),
    ...makeVFieldProps(),
  },

  emits: {
    'click:clear': (e: MouseEvent) => true,
    'click:prepend-inner': (e: MouseEvent) => true,
    'click:append-inner': (e: MouseEvent) => true,
    'click:control': (e: MouseEvent) => true,
    'update:focused': (focused: boolean) => true,
    'update:modelValue': (val: any) => true,
  },

  setup (props, { attrs, emit, slots }) {
    const { themeClasses } = provideTheme(props)
    const { loaderClasses } = useLoader(props)
    const { focusClasses, isFocused, focus, blur } = useFocus(props)

    const isActive = computed(() => props.dirty || props.active)
    const hasLabel = computed(() => !props.singleLine && !!(props.label || slots.label))

    const uid = getUid()
    const id = computed(() => props.id || `input-${uid}`)

    const labelRef = ref<VFieldLabel>()
    const floatingLabelRef = ref<VFieldLabel>()
    const controlRef = ref<HTMLElement>()

    const { backgroundColorClasses, backgroundColorStyles } = useBackgroundColor(toRef(props, 'bgColor'))
    const { textColorClasses, textColorStyles } = useTextColor(computed(() => {
      return (
        isActive.value &&
        isFocused.value &&
        !props.error &&
        !props.disabled
      ) ? props.color : undefined
    }))

    watch(isActive, val => {
      if (hasLabel.value) {
        const el: HTMLElement = labelRef.value!.$el
        const targetEl: HTMLElement = floatingLabelRef.value!.$el
        const rect = nullifyTransforms(el)
        const targetRect = targetEl.getBoundingClientRect()

        const x = targetRect.x - rect.x
        const y = targetRect.y - rect.y - (rect.height / 2 - targetRect.height / 2)

        const targetWidth = targetRect.width / 0.75
        const width = Math.abs(targetWidth - rect.width) > 1
          ? { maxWidth: convertToUnit(targetWidth) }
          : undefined

        const duration = parseFloat(getComputedStyle(el).transitionDuration) * 1000
        const scale = parseFloat(getComputedStyle(targetEl).getPropertyValue('--v-field-label-scale'))

        el.style.visibility = 'visible'
        targetEl.style.visibility = 'hidden'

        el.animate([
          { transform: 'translate(0)' },
          { transform: `translate(${x}px, ${y}px) scale(${scale})`, ...width },
        ], {
          duration,
          easing: standardEasing,
          direction: val ? 'normal' : 'reverse',
        }).finished.then(() => {
          el.style.removeProperty('visibility')
          targetEl.style.removeProperty('visibility')
        })
      }
    }, { flush: 'post' })

    const slotProps = computed<DefaultInputSlot>(() => ({
      isActive,
      isFocused,
      controlRef,
      blur,
      focus,
    }))

    function onClick (e: MouseEvent) {
      if (e.target !== document.activeElement) {
        e.preventDefault()
      }

      emit('click:control', e)
    }

    useRender(() => {
      const isOutlined = props.variant === 'outlined'
      const hasPrepend = (slots.prependInner || props.prependInnerIcon)
      const hasClear = !!(props.clearable || slots.clear)
      const hasAppend = !!(slots.appendInner || props.appendInnerIcon || hasClear)
      const label = slots.label
        ? slots.label({
          label: props.label,
          props: { for: id.value },
        })
        : props.label

      return (
        <div
          class={[
            'v-field',
            {
              'v-field--active': isActive.value,
              'v-field--appended': hasAppend,
              'v-field--disabled': props.disabled,
              'v-field--dirty': props.dirty,
              'v-field--error': props.error,
              'v-field--has-background': !!props.bgColor,
              'v-field--persistent-clear': props.persistentClear,
              'v-field--prepended': hasPrepend,
              'v-field--reverse': props.reverse,
              'v-field--single-line': props.singleLine,
              [`v-field--variant-${props.variant}`]: true,
            },
            themeClasses.value,
            backgroundColorClasses.value,
            focusClasses.value,
            loaderClasses.value,
          ]}
          style={[
            backgroundColorStyles.value,
            textColorStyles.value,
          ]}
          onClick={ onClick }
          { ...attrs }
        >

          <LoaderSlot
            name="v-field"
            active={ props.loading }
            color={ props.error ? 'error' : props.color }
            v-slots={{ default: slots.loader }}
          />

          { hasPrepend && (

              { props.prependInnerIcon && (
                <VIcon
                  onClick={ (e: MouseEvent) => emit('click:prepend-inner', e) }
                  icon={ props.prependInnerIcon }
                />
              ) }

              { slots?.prependInner?.(slotProps.value) }

          ) }

            { ['contained', 'filled'].includes(props.variant) && hasLabel.value && (
              <VFieldLabel
                ref={ floatingLabelRef }
                class={[textColorClasses.value]}
                floating
              >
                { label }
              </VFieldLabel>
            ) }

            <VFieldLabel ref={ labelRef } for={ id.value }>
              { label }
            </VFieldLabel>

            { slots.default?.({
              ...slotProps.value,
              props: {
                id: id.value,
                class: 'v-field__input',
              },
              focus,
              blur,
            } as VFieldSlot) }

          { hasClear && (
            <VExpandXTransition>

                { slots.clear
                  ? slots.clear()
                  : (
                    <VIcon
                      onClick={ (e: MouseEvent) => emit('click:clear', e) }
                      icon={ props.clearIcon }
                    />
                  )
                }

            </VExpandXTransition>
          ) }

          { hasAppend && (

              { slots?.appendInner?.(slotProps.value) }

              { props.appendInnerIcon && (
                <VIcon
                  onClick={ (e: MouseEvent) => emit('click:append-inner', e) }
                  icon={ props.appendInnerIcon }
                />
              ) }

          ) }

            { isOutlined && (
              <>

                { hasLabel.value && (

                    <VFieldLabel ref={ floatingLabelRef } floating>
                      { label }
                    </VFieldLabel>

                ) }

              </>
            ) }

            { ['plain', 'underlined'].includes(props.variant) && hasLabel.value && (
              <VFieldLabel ref={ floatingLabelRef } floating>
                { label }
              </VFieldLabel>
            ) }

      )
    })

    return {
      controlRef,
    }
  },
})

export type VField = InstanceType<typeof VField>

// TODO: this is kinda slow, might be better to implicitly inherit props instead
export function filterFieldProps (attrs: Record<string, unknown>) {
  return pick(attrs, Object.keys(VField.props))
}


<template>
  <v-footer
    class="bg-grey-lighten-1"
  >
    <v-row
      justify="center"
      no-gutters
    >
      <v-btn
        v-for="link in links"
        :key="link"
        color="white"
        variant="text"
        class="mx-2"
        rounded="xl"
      >
        {{ link }}
      </v-btn>
      <v-col
        class="text-center text-white mt-4"
        cols="12"
      >
        {{ new Date().getFullYear() }} — Vuetify
      </v-col>
    </v-row>
  </v-footer>
</template>


<template>
  <v-container fluid>
    <v-textarea
      autocomplete="email"
      label="Email"
    ></v-textarea>
  </v-container>
</template>


<template>
  <v-card
    class="mx-auto"
    max-width="400"
  >
    <v-card-title class="white--text orange darken-4">
      User Directory

      <v-spacer></v-spacer>

      <v-btn
        color="white"
        class="text--primary"
        fab
        small
      >
        <v-icon>mdi-plus</v-icon>
      </v-btn>
    </v-card-title>

    <v-card-text class="pt-4">
      Lorem ipsum dolor sit, amet consectetur adipisicing elit. Quasi nobis a at voluptates culpa optio amet! Inventore deserunt voluptatem maxime a veniam placeat, eos impedit nulla quos? Officiis, aperiam ducimus.
    </v-card-text>

    <v-divider></v-divider>

    <v-virtual-scroll
      :items="items"
      :item-height="50"
      height="300"
    >
      <template v-slot:default="{ item }">
        <v-list-item>
          <v-list-item-avatar>
            <v-avatar
              :color="item.color"
              size="56"
              class="white--text"
            >
              {{ item.initials }}
            </v-avatar>
          </v-list-item-avatar>

          <v-list-item-content>
            <v-list-item-title>{{ item.fullName }}</v-list-item-title>
          </v-list-item-content>

          <v-list-item-action>
            <v-btn
              depressed
              small
            >
              View User

              <v-icon
                color="orange darken-4"
                end
              >
                mdi-open-in-new
              </v-icon>
            </v-btn>
          </v-list-item-action>
        </v-list-item>
      </template>
    </v-virtual-scroll>
  </v-card>
</template>

<script>
  export default {
    data: () => ({
      colors: ['#2196F3', '#90CAF9', '#64B5F6', '#42A5F5', '#1E88E5', '#1976D2', '#1565C0', '#0D47A1', '#82B1FF', '#448AFF', '#2979FF', '#2962FF'],
      names: ['Oliver', 'Jake', 'Noah', 'James', 'Jack', 'Connor', 'Liam', 'John', 'Harry', 'Callum', 'Mason', 'Robert', 'Jacob', 'Jacob', 'Jacob', 'Michael', 'Charlie', 'Kyle', 'William', 'William', 'Thomas', 'Joe', 'Ethan', 'David', 'George', 'Reece', 'Michael', 'Richard', 'Oscar', 'Rhys', 'Alexander', 'Joseph', 'James', 'Charlie', 'James', 'Charles', 'William', 'Damian', 'Daniel', 'Thomas', 'Amelia', 'Margaret', 'Emma', 'Mary', 'Olivia', 'Samantha', 'Olivia', 'Patricia', 'Isla', 'Bethany'],
      surnames: ['Smith', 'Anderson', 'Clark', 'Wright', 'Mitchell', 'Johnson', 'Thomas', 'Rodriguez', 'Lopez', 'Perez', 'Williams', 'Jackson', 'Lewis', 'Hill', 'Roberts', 'Jones', 'White', 'Lee', 'Scott', 'Turner', 'Brown', 'Harris', 'Walker', 'Green', 'Phillips', 'Davis', 'Martin', 'Hall', 'Adams', 'Campbell', 'Miller', 'Thompson', 'Allen', 'Baker', 'Parker', 'Wilson', 'Garcia', 'Young', 'Gonzalez', 'Evans', 'Moore', 'Martinez', 'Hernandez', 'Nelson', 'Edwards', 'Taylor', 'Robinson', 'King', 'Carter', 'Collins'],
    }),

    computed: {
      items () {
        const namesLength = this.names.length
        const surnamesLength = this.surnames.length
        const colorsLength = this.colors.length

        return Array.from({ length: 10000 }, (k, v) => {
          const name = this.names[this.genRandomIndex(namesLength)]
          const surname = this.surnames[this.genRandomIndex(surnamesLength)]

          return {
            color: this.colors[this.genRandomIndex(colorsLength)],
            fullName: `${name} ${surname}`,
            initials: `${name[0]} ${surname[0]}`,
          }
        })
      },
    },

    methods: {
      genRandomIndex (length) {
        return Math.ceil(Math.random() * (length - 1))
      },
    },
  }
</script>


onClick={ (e: MouseEvent) => emit('click:append', e) }

keep keep keep keep keep replace keep keep keep keep


module.exports = {
  'v-simple-checkbox': {
    events: [
      {
        name: 'input',
        value: 'Event',
      },
    ],
  },
}


import Alert from '@/components/Alert.vue'
import ApiInline from '@/components/api/Inline.vue'
import ApiLinks from '@/components/api/Links.vue'
import ApiSection from '@/components/api/Section.vue'
import ApiTable from '@/components/api/Table.vue'
import AppBtn from '@/components/app/Btn.vue'
import AppCaption from '@/components/app/Caption.vue'
import AppDivider from '@/components/app/Divider.vue'
import AppFigure from '@/components/app/Figure.vue'
import AppHeading from '@/components/app/Heading.vue'
import AppHeadline from '@/components/app/Headline.vue'
import AppImg from '@/components/app/Img.vue'
import AppLink from '@/components/app/Link.vue'
import AppMarkdown from '@/components/app/Markdown.vue'
import AppMarkup from '@/components/app/Markup.vue'
import AppSheet from '@/components/app/Sheet.vue'
import AppTable from '@/components/app/Table.vue'
import AppTitle from '@/components/app/Title.vue'
import Backmatter from '@/components/Backmatter.vue'
import Carbon from '@/components/promoted/Carbon.vue'
import Discovery from '@/components/promoted/Discovery.vue'
import Entry from '@/components/promoted/Entry.vue'
import Example from '@/components/examples/Example.vue'
import Inline from '@/components/promoted/Inline.vue'
import PageComponent from '@/components/PageComponent.vue'
import Promoted from '@/components/promoted/Promoted.vue'
import Random from '@/components/promoted/Random.vue'
import Usage from '@/components/examples/Usage.vue'
import UsageExample from '@/components/examples/UsageExample.vue'
import VueFile from '@/components/examples/VueFile.vue'
import Vuetify from '@/components/promoted/Vuetify.vue'

import type { GlobalComponentsPlugin } from '@/types'

export const useGlobalComponents: GlobalComponentsPlugin = ({ app }) => {
  app.component('AppBtn', AppBtn)
  app.component('AppCaption', AppCaption)
  app.component('AppDivider', AppDivider)
  app.component('AppHeading', AppHeading)
  app.component('AppHeadline', AppHeadline)
  app.component('AppLink', AppLink)
  app.component('AppMarkdown', AppMarkdown)
  app.component('AppMarkup', AppMarkup)
  app.component('AppSheet', AppSheet)
  app.component('AppTable', AppTable)
  app.component('AppTitle', AppTitle)

  // Used by markdown files
  app.component('Alert', Alert)
  app.component('AppFigure', AppFigure)
  app.component('ApiInline', ApiInline)
  app.component('ApiLinks', ApiLinks)
  app.component('ApiSection', ApiSection)
  app.component('ApiTable', ApiTable)
  app.component('AppImg', AppImg)
  app.component('Backmatter', Backmatter)
  app.component('Carbon', Carbon)
  app.component('Discovery', Discovery)
  app.component('Entry', Entry)
  app.component('Example', Example)
  app.component('Inline', Inline)
  app.component('PageComponent', PageComponent)
  app.component('Promoted', Promoted)
  app.component('Random', Random)
  app.component('Usage', Usage)
  app.component('UsageExample', UsageExample)
  app.component('VueFile', VueFile)
  app.component('Vuetify', Vuetify)
}


// Composables
import { provideDefaults } from '@/composables/defaults'

// Utility
import { defineComponent, useRender } from '@/util'

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

  props: {
    color: String,
    density: String,
  },

  setup (props, { slots }) {
    provideDefaults({
      VBtn: {
        color: props.color,
        density: props.density,
        variant: 'text',
      },
    })

    useRender(() => (
      <div class="v-banner-actions">
        { slots?.default?.() }
      </div>
    ))

    return {}
  },
})


keep keep keep keep keep replace replace keep keep keep

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

.v-icon
  --v-icon-size-multiplier: 1
  align-items: center
  display: inline-flex
  font-feature-settings: 'liga'
  height: $icon-size
  justify-content: center
  letter-spacing: $icon-letter-spacing
  line-height: $icon-line-height
  position: relative
  text-indent: $icon-text-indent
  user-select: none
  vertical-align: $icon-vertical-align
  width: $icon-size

  @each $name in settings.$sizes
    &--size-#{$name}
      font-size: calc(var(--v-icon-size-multiplier) * #{map.get($icon-sizes, $name)})

.v-icon__svg
  fill: currentColor
  width: 100%
  height: 100%

.v-icon--start
  margin-inline-end: $icon-margin-start

  .v-btn:not(.v-btn--icon) &
    margin-inline-start: $icon-btn-margin-start
    margin-inline-end: $icon-btn-margin-end

  .v-btn.v-btn--stacked &
    margin-inline-start: 0
    margin-inline-end: 0
    margin-bottom: $icon-btn-stacked-margin

.v-icon--end
  margin-inline-start: $icon-margin-end

  .v-btn:not(.v-btn--icon) &
    margin-inline-start: $icon-btn-margin-end
    margin-inline-end: $icon-btn-margin-start

  .v-btn.v-btn--stacked &
    margin-inline-start: 0
    margin-inline-end: 0
    margin-top: $icon-btn-stacked-margin


<template>
  <v-row
    align="center"
    justify="center"
  >
    <v-img
      class="grey lighten-2"
      v-bind="$attrs"
    ></v-img>
  </v-row>
</template>

<script>
  export default {
    name: 'Usage',

    inheritAttrs: false,

    data: () => ({
      show: true,
      defaults: {
        contain: false,
        'lazy-src': 'https://picsum.photos/id/11/10/6',
        'max-height': 150,
        'max-width': 250,
        src: 'https://picsum.photos/id/11/500/300',
      },
      options: {
        booleans: [
          'contain',
        ],
        sliders: {
          'max-height': [0, 300],
          'max-width': [0, 500],
        },
      },
      tabs: [],
    }),
  }
</script>


export { VTextField } from './VTextField'


// Defaults
$code-border-radius: 3px !default;
$code-font-size: 85% !default;
$code-font-weight: normal !default;
$code-padding: .2em .4em !default;


'update:modelValue': (val: string) => true,

const model = useProxiedModel(props, 'modelValue')

keep keep keep replace replace keep keep keep keep keep

// Styles
import './VIcon.sass'

// Composables
import { makeSizeProps, useSize } from '@/composables/size'
import { makeTagProps } from '@/composables/tag'
import { useIcon } from '@/composables/icons'
import { useTextColor } from '@/composables/color'
import { makeThemeProps, provideTheme } from '@/composables/theme'

// Utilities
import { computed, toRef } from 'vue'
import { convertToUnit, defineComponent, flattenFragments, propsFactory } from '@/util'

// Types
import type { IconValue } from '@/composables/icons'
import type { ComputedRef, PropType } from 'vue'

export const makeVIconProps = propsFactory({
  color: String,
  start: Boolean,
  end: Boolean,
  icon: {
    type: [String, Object] as PropType<IconValue>,
  },

  ...makeSizeProps(),
  ...makeTagProps({ tag: 'i' }),
  ...makeThemeProps(),
}, 'v-icon')

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

  props: makeVIconProps(),

  setup (props, { slots }) {
    let slotIcon: ComputedRef<string | undefined> | undefined
    if (slots.default) {
      slotIcon = computed(() => {
        const slot = slots.default?.()
        if (!slot) return

        return flattenFragments(slot).filter(node =>
          node.children && typeof node.children === 'string'
        )[0]?.children as string
      })
    }

    const { themeClasses } = provideTheme(props)
    const { iconData } = useIcon(slotIcon || props)
    const { sizeClasses } = useSize(props)
    const { textColorClasses, textColorStyles } = useTextColor(toRef(props, 'color'))

    return () => {
      return (
        <iconData.value.component
          tag={ props.tag }
          icon={ iconData.value.icon }
          class={[
            'v-icon',
            'notranslate',
            sizeClasses.value,
            textColorClasses.value,
            themeClasses.value,
            {
              'v-icon--start': props.start,
              'v-icon--end': props.end,
            },
          ]}
          style={[
            !sizeClasses.value ? ({
              fontSize: convertToUnit(props.size),
              width: convertToUnit(props.size),
              height: convertToUnit(props.size),
            }) : undefined,
            textColorStyles.value,
          ]}
          aria-hidden="true"
        />
      )
    }
  },
})


module.exports = {
  'v-toolbar-title': {
    slots: [
      {
        name: 'default',
        props: undefined,
      },
    ],
  },
}


<template>
  <validation-observer
    ref="observer"
    v-slot="{ invalid }"
  >
    <form @submit.prevent="submit">
      <validation-provider
        v-slot="{ errors }"
        name="Name"
        rules="required|max:10"
      >
        <v-text-field
          v-model="name"
          :counter="10"
          :error-messages="errors"
          label="Name"
          required
        ></v-text-field>
      </validation-provider>
      <validation-provider
        v-slot="{ errors }"
        name="phoneNumber"
        :rules="{
          required: true,
          digits: 7,
          regex: '^(71|72|74|76|81|82|84|85|86|87|88|89)\\d{5}$'
        }"
      >
        <v-text-field
          v-model="phoneNumber"
          :counter="7"
          :error-messages="errors"
          label="Phone Number"
          required
        ></v-text-field>
      </validation-provider>
      <validation-provider
        v-slot="{ errors }"
        name="email"
        rules="required|email"
      >
        <v-text-field
          v-model="email"
          :error-messages="errors"
          label="E-mail"
          required
        ></v-text-field>
      </validation-provider>
      <validation-provider
        v-slot="{ errors }"
        name="select"
        rules="required"
      >
        <v-select
          v-model="select"
          :items="items"
          :error-messages="errors"
          label="Select"
          data-vv-name="select"
          required
        ></v-select>
      </validation-provider>
      <validation-provider
        v-slot="{ errors }"
        rules="required"
        name="checkbox"
      >
        <v-checkbox
          v-model="checkbox"
          :error-messages="errors"
          value="1"
          label="Option"
          type="checkbox"
          required
        ></v-checkbox>
      </validation-provider>

      <v-btn
        class="mr-4"
        type="submit"
        :disabled="invalid"
      >
        submit
      </v-btn>
      <v-btn @click="clear">
        clear
      </v-btn>
    </form>
  </validation-observer>
</template>

<script>
  // import { required, digits, email, max, regex } from 'vee-validate/dist/rules'
  // import { extend, ValidationObserver, ValidationProvider, setInteractionMode } from 'vee-validate'

  // setInteractionMode('eager')

  // extend('digits', {
  //   ...digits,
  //   message: '{_field_} needs to be {length} digits. ({_value_})',
  // })

  // extend('required', {
  //   ...required,
  //   message: '{_field_} can not be empty',
  // })

  // extend('max', {
  //   ...max,
  //   message: '{_field_} may not be greater than {length} characters',
  // })

  // extend('regex', {
  //   ...regex,
  //   message: '{_field_} {_value_} does not match {regex}',
  // })

  // extend('email', {
  //   ...email,
  //   message: 'Email must be valid',
  // })

  export default {
    components: {
      // ValidationProvider,
      // ValidationObserver,
    },
    data: () => ({
      name: '',
      phoneNumber: '',
      email: '',
      select: null,
      items: [
        'Item 1',
        'Item 2',
        'Item 3',
        'Item 4',
      ],
      checkbox: null,
    }),

    methods: {
      submit () {
        this.$refs.observer.validate()
      },
      clear () {
        this.name = ''
        this.phoneNumber = ''
        this.email = ''
        this.select = null
        this.checkbox = null
        this.$refs.observer.reset()
      },
    },
  }
</script>

<codepen-resources lang="json">
  {
    "js": ["https://cdn.jsdelivr.net/npm/vee-validate@3.x/dist/vee-validate.js"]
  }
</codepen-resources>


Designate a custom activator when the `activator` slot is not used. String can be any valid querySelector and Object can be any valid Node.

Disables the ability to open the component.


onClick:prepend={ (e: MouseEvent) => emit('click:prepend', e) }

onClick:append={ (e: MouseEvent) => emit('click:append', e) }

// Styles
import './VInput.sass'

// Components
import { VIcon } from '@/components/VIcon'
import { VMessages } from '@/components/VMessages'

// Composables
import { makeDensityProps, useDensity } from '@/composables/density'
import { makeValidationProps, useValidation } from '@/composables/validation'

// Utilities
import { computed } from 'vue'
import { genericComponent, getUid, pick, propsFactory, useRender } from '@/util'

// Types
import type { ComputedRef, ExtractPropTypes, PropType, Ref } from 'vue'
import type { MakeSlots } from '@/util'

export interface VInputSlot {
  id: ComputedRef<string>
  isDirty: ComputedRef<boolean>
  isDisabled: ComputedRef<boolean>
  isReadonly: ComputedRef<boolean>
  isPristine: Ref<boolean>
  isValid: ComputedRef<boolean | null>
  isValidating: Ref<boolean>
  reset: () => void
  resetValidation: () => void
  validate: () => void
}

export const makeVInputProps = propsFactory({
  id: String,
  appendIcon: String,
  prependIcon: String,
  hideDetails: [Boolean, String] as PropType<boolean | 'auto'>,
  messages: {
    type: [Array, String] as PropType<string | string[]>,
    default: () => ([]),
  },
  direction: {
    type: String as PropType<'horizontal' | 'vertical'>,
    default: 'horizontal',
    validator: (v: any) => ['horizontal', 'vertical'].includes(v),
  },

  ...makeDensityProps(),
  ...makeValidationProps(),
})

export type VInputSlots = MakeSlots<{
  default: [VInputSlot]
  prepend: [VInputSlot]
  append: [VInputSlot]
  details: [VInputSlot]
}>

export const VInput = genericComponent<new <T>() => {
  $slots: VInputSlots
}>()({
  name: 'VInput',

  props: {
    ...makeVInputProps(),
  },

  emits: {
    'click:prepend': (e: MouseEvent) => true,
    'click:append': (e: MouseEvent) => true,
    'update:modelValue': (val: any) => true,
  },

  setup (props, { slots, emit }) {
    const { densityClasses } = useDensity(props)
    const {
      errorMessages,
      isDirty,
      isDisabled,
      isReadonly,
      isPristine,
      isValid,
      isValidating,
      reset,
      resetValidation,
      validate,
      validationClasses,
    } = useValidation(props)

    const uid = getUid()
    const id = computed(() => props.id || `input-${uid}`)

    const slotProps = computed<VInputSlot>(() => ({
      id,
      isDirty,
      isDisabled,
      isReadonly,
      isPristine,
      isValid,
      isValidating,
      reset,
      resetValidation,
      validate,
    }))

    useRender(() => {
      const hasPrepend = !!(slots.prepend || props.prependIcon)
      const hasAppend = !!(slots.append || props.appendIcon)
      const hasMessages = !!(
        props.messages?.length ||
        errorMessages.value.length
      )
      const hasDetails = !props.hideDetails || (
        props.hideDetails === 'auto' &&
        hasMessages
      )

      return (
        <div class={[
          'v-input',
          `v-input--${props.direction}`,
          densityClasses.value,
          validationClasses.value,
        ]}
        >
          { hasPrepend && (

              { slots?.prepend?.(slotProps.value) }

              { props.prependIcon && (
                <VIcon
                  onClick={ (e: MouseEvent) => emit('click:prepend', e) }
                  icon={ props.prependIcon }
                />
              ) }

          ) }

          { slots.default && (

              { slots.default?.(slotProps.value) }

          ) }

          { hasAppend && (

              { slots?.append?.(slotProps.value) }

              { props.appendIcon && (
                <VIcon
                  onClick={ (e: MouseEvent) => emit('click:append', e) }
                  icon={ props.appendIcon }
                />
              ) }

          ) }

          { hasDetails && (

              <VMessages
                active={ hasMessages }
                messages={ errorMessages.value.length > 0
                  ? errorMessages.value
                  : props.messages
                }
                v-slots={{ message: slots.message }}
              />

              { slots.details?.(slotProps.value) }

          ) }

      )
    })

    return {
      reset,
      resetValidation,
      validate,
    }
  },
})

export type VInput = InstanceType<typeof VInput>

export function filterInputProps (props: ExtractPropTypes<ReturnType<typeof makeVInputProps>>) {
  return pick(props, Object.keys(VInput.props) as any)
}


Determines whether the chip is visible or not.

Change the default icon used for **close** chips

Text used for *aria-label* on the close button in **close** chips. Can also be customized globally in [Internationalization](/customization/internationalization).

Disables the chip, making it un-selectable

Change the default icon used for **filter** chips

Controls the **active** state of the item. This is typically used to highlight the component.

Removes background and applies border and text color

The value used when a child of a [v-chip-group](/components/chip-groups).

Emitted when component is clicked, toggles chip if contained in a chip group - Will trigger component to ripple when clicked unless the `.native` modifier is used

Emitted when close icon is clicked, sets active to `false`