Skip to content

useFormField

This requires the following keys to be installed:

This requires the following types to be installed:

useFormField

ts
import type { GetObjectField } from '@/ui/utils/utils'
import { formFieldInjectionKey, inputIdInjectionKey } from '@/ui/keys/form-field'
import { computed, inject } from 'vue'

interface Props<T> {
  id?: string
  name?: string
  size?: GetObjectField<T, 'size'>
  color?: GetObjectField<T, 'color'>
  highlight?: boolean
  disabled?: boolean
}

export function useFormField<T>(props?: Props<T>, opts?: { bind?: boolean }) {
  const formField = inject(formFieldInjectionKey, undefined)
  const inputId = inject(inputIdInjectionKey, undefined)

  if (formField && inputId) {
    if (opts?.bind === false) {
      // Removes for="..." attribute on label for RadioGroup and alike.
      inputId.value = undefined
    }
    else if (props?.id) {
      // Updates for="..." attribute on label if props.id is provided.
      inputId.value = props?.id
    }
  }

  return {
    id: computed(() => props?.id ?? inputId?.value),
    name: computed(() => props?.name ?? formField?.value.name),
    size: computed(() => props?.size ?? formField?.value.size),
    color: computed(() => formField?.value.error ? 'error' : props?.color),
    highlight: computed(() => formField?.value.error ? true : props?.highlight),
    disabled: computed(() => props?.disabled),
    ariaAttrs: computed(() => {
      if (!formField?.value)
        return

      const descriptiveAttrs = ['error' as const, 'hint' as const, 'description' as const, 'help' as const]
        .filter(type => formField?.value?.[type])
        .map(type => `${formField?.value.ariaId}-${type}`) || []

      return {
        'aria-describedby': descriptiveAttrs.join(' '),
        'aria-invalid': !!formField?.value.error,
      }
    }),
  }
}
ts
import type { GetObjectField } from '@/UI/Utils/utils'
import { formFieldInjectionKey, inputIdInjectionKey } from '@/UI/Keys/form-field'
import { computed, inject } from 'vue'

interface Props<T> {
  id?: string
  name?: string
  size?: GetObjectField<T, 'size'>
  color?: GetObjectField<T, 'color'>
  highlight?: boolean
  disabled?: boolean
}

export function useFormField<T>(props?: Props<T>, opts?: { bind?: boolean }) {
  const formField = inject(formFieldInjectionKey, undefined)
  const inputId = inject(inputIdInjectionKey, undefined)

  if (formField && inputId) {
    if (opts?.bind === false) {
      // Removes for="..." attribute on label for RadioGroup and alike.
      inputId.value = undefined
    }
    else if (props?.id) {
      // Updates for="..." attribute on label if props.id is provided.
      inputId.value = props?.id
    }
  }

  return {
    id: computed(() => props?.id ?? inputId?.value),
    name: computed(() => props?.name ?? formField?.value.name),
    size: computed(() => props?.size ?? formField?.value.size),
    color: computed(() => formField?.value.error ? 'error' : props?.color),
    highlight: computed(() => formField?.value.error ? true : props?.highlight),
    disabled: computed(() => props?.disabled),
    ariaAttrs: computed(() => {
      if (!formField?.value)
        return

      const descriptiveAttrs = ['error' as const, 'hint' as const, 'description' as const, 'help' as const]
        .filter(type => formField?.value?.[type])
        .map(type => `${formField?.value.ariaId}-${type}`) || []

      return {
        'aria-describedby': descriptiveAttrs.join(' '),
        'aria-invalid': !!formField?.value.error,
      }
    }),
  }
}