<template> <div class="form-group tw-block" :class="formGroupClass"> <label :for="id" class="tw-text-gray-500 tw-font-medium tw-pt-1">{{ label }}</label> <div class="tw-flex tw-flex-col"> <slot v-bind="controlAttributes" /> <div v-if="hasErrors" :id="errorsId" class="invalid-feedback tw-order-first"> <template v-for="(error, index) in errorList" :key="index"> <p class="last:tw-mb-0">{{ t(`error.${error.code}`) }}</p> </template> </div> </div> </div> </template> <script lang="ts" setup> import { computed, inject } from 'vue' import { useId } from '@/util' import { useI18n } from '@/i18n' type Error = { code: string } const props = defineProps<{ label: string customControl?: boolean errors?: undefined | Error[] }>() const { t } = useI18n() const formGroupClass = inject('formGroupClass', undefined) const id = useId('form-group-control') const errorsId = useId('form-group-errors') const errorList = computed<Error[]>(() => props.errors ?? []) const hasErrors = computed(() => errorList.value.length > 0) const controlAttributes = computed(() => ({ class: [ 'tw-order-first', { 'is-invalid': hasErrors.value, 'form-control': !props.customControl }, ], 'aria-describedby': hasErrors.value ? errorsId.value : '', id: id.value, })) </script> <script lang="ts"> export default { compatConfig: { MODE: 3, }, } </script>