Skip to content

Commit ef1af9a

Browse files
committed
chore: migrate UInput
1 parent 790a3b2 commit ef1af9a

File tree

2 files changed

+27
-33
lines changed

2 files changed

+27
-33
lines changed

src/runtime/components/FormField.vue

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -47,41 +47,31 @@ export interface FormFieldSlots {
4747
</script>
4848

4949
<script setup lang="ts">
50-
import { computed, ref, inject, provide, useId, watch } from 'vue'
51-
import type { Ref } from 'vue'
50+
import { computed, provide } from 'vue'
5251
import { Primitive, Label } from 'reka-ui'
52+
import { useFormField } from '@formwerk/core'
5353
import { useAppConfig } from '#imports'
54-
import { formFieldInjectionKey, inputIdInjectionKey, formErrorsInjectionKey, formInputsInjectionKey } from '../composables/useFormField'
54+
import { formFieldInjectionKey } from '../composables/useFormField'
5555
import { tv } from '../utils/tv'
56-
import type { FormError, FormFieldInjectedOptions } from '../types/form'
56+
import type { FormFieldInjectedOptions } from '../types/form'
5757
5858
const props = defineProps<FormFieldProps>()
5959
const slots = defineSlots<FormFieldSlots>()
6060
6161
const appConfig = useAppConfig() as FormField['AppConfig']
6262
63+
const { labelProps, descriptionProps, errorMessageProps, state: { errorMessage, isTouched } } = useFormField({
64+
path: props.name,
65+
label: props.label,
66+
description: props.description
67+
})
68+
6369
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.formField || {}) })({
6470
size: props.size,
6571
required: props.required
6672
}))
6773
68-
const formErrors = inject<Ref<FormError[]> | null>(formErrorsInjectionKey, null)
69-
70-
const error = computed(() => props.error || formErrors?.value?.find(error => error.name === props.name || (props.errorPattern && error.name?.match(props.errorPattern)))?.message)
71-
72-
const id = ref(useId())
73-
// Copies id's initial value to bind aria-attributes such as aria-describedby.
74-
// This is required for the RadioGroup component which unsets the id value.
75-
const ariaId = id.value
76-
77-
const formInputs = inject(formInputsInjectionKey, undefined)
78-
watch(id, () => {
79-
if (formInputs && props.name) {
80-
formInputs.value[props.name] = { id: id.value, pattern: props.errorPattern }
81-
}
82-
}, { immediate: true })
83-
84-
provide(inputIdInjectionKey, id)
74+
const error = computed(() => props.error || (errorMessage.value && isTouched.value ? errorMessage.value : undefined))
8575
8676
provide(formFieldInjectionKey, computed(() => ({
8777
error: error.value,
@@ -92,28 +82,27 @@ provide(formFieldInjectionKey, computed(() => ({
9282
errorPattern: props.errorPattern,
9383
hint: props.hint,
9484
description: props.description,
95-
help: props.help,
96-
ariaId
85+
help: props.help
9786
}) as FormFieldInjectedOptions<FormFieldProps>))
9887
</script>
9988

10089
<template>
10190
<Primitive :as="as" :class="ui.root({ class: [props.ui?.root, props.class] })">
10291
<div :class="ui.wrapper({ class: props.ui?.wrapper })">
10392
<div v-if="label || !!slots.label" :class="ui.labelWrapper({ class: props.ui?.labelWrapper })">
104-
<Label :for="id" :class="ui.label({ class: props.ui?.label })">
93+
<Label v-bind="labelProps" :class="ui.label({ class: props.ui?.label })">
10594
<slot name="label" :label="label">
10695
{{ label }}
10796
</slot>
10897
</Label>
109-
<span v-if="hint || !!slots.hint" :id="`${ariaId}-hint`" :class="ui.hint({ class: props.ui?.hint })">
98+
<span v-if="hint || !!slots.hint" :id="`${labelProps.id}-hint`" :class="ui.hint({ class: props.ui?.hint })">
11099
<slot name="hint" :hint="hint">
111100
{{ hint }}
112101
</slot>
113102
</span>
114103
</div>
115104

116-
<p v-if="description || !!slots.description" :id="`${ariaId}-description`" :class="ui.description({ class: props.ui?.description })">
105+
<p v-if="description || !!slots.description" :v-bind="descriptionProps" :class="ui.description({ class: props.ui?.description })">
117106
<slot name="description" :description="description">
118107
{{ description }}
119108
</slot>
@@ -123,12 +112,12 @@ provide(formFieldInjectionKey, computed(() => ({
123112
<div :class="[(label || !!slots.label || description || !!slots.description) && ui.container({ class: props.ui?.container })]">
124113
<slot :error="error" />
125114

126-
<div v-if="(typeof error === 'string' && error) || !!slots.error" :id="`${ariaId}-error`" :class="ui.error({ class: props.ui?.error })">
115+
<div v-if="(typeof error === 'string' && error) || !!slots.error" :v-bind="errorMessageProps" :class="ui.error({ class: props.ui?.error })">
127116
<slot name="error" :error="error">
128117
{{ error }}
129118
</slot>
130119
</div>
131-
<div v-else-if="help || !!slots.help" :id="`${ariaId}-help`" :class="ui.help({ class: props.ui?.help })">
120+
<div v-else-if="help || !!slots.help" :id="`${labelProps.id}-help`" :class="ui.help({ class: props.ui?.help })">
132121
<slot name="help" :help="help">
133122
{{ help }}
134123
</slot>

src/runtime/components/Input.vue

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export interface InputSlots {
6565
import { ref, computed, onMounted } from 'vue'
6666
import { Primitive } from 'reka-ui'
6767
import { useVModel } from '@vueuse/core'
68+
import { useCustomControl } from '@formwerk/core'
6869
import { useAppConfig } from '#imports'
6970
import { useFieldGroup } from '../composables/useFieldGroup'
7071
import { useComponentIcons } from '../composables/useComponentIcons'
@@ -88,7 +89,13 @@ const modelValue = useVModel<InputProps<T>, 'modelValue', 'update:modelValue'>(p
8889
8990
const appConfig = useAppConfig() as Input['AppConfig']
9091
91-
const { emitFormBlur, emitFormInput, emitFormChange, size: formGroupSize, color, id, name, highlight, disabled, emitFormFocus, ariaAttrs } = useFormField<InputProps<T>>(props, { deferInputValidation: true })
92+
const { emitFormBlur, emitFormInput, emitFormChange, size: formGroupSize, color, name, highlight, disabled, emitFormFocus } = useFormField<InputProps<T>>(props, { deferInputValidation: true })
93+
const { controlProps, field: { isDisabled } } = useCustomControl({
94+
name,
95+
disabled,
96+
required: props.required,
97+
controlType: 'UInput'
98+
})
9299
const { orientation, size: fieldGroupSize } = useFieldGroup<InputProps<T>>(props)
93100
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(props)
94101
@@ -177,17 +184,15 @@ defineExpose({
177184
<template>
178185
<Primitive :as="as" :class="ui.root({ class: [props.ui?.root, props.class] })">
179186
<input
180-
:id="id"
181187
ref="inputRef"
182188
:type="type"
183189
:value="modelValue"
184-
:name="name"
185190
:placeholder="placeholder"
186191
:class="ui.base({ class: props.ui?.base })"
187-
:disabled="disabled"
192+
:disabled="isDisabled"
188193
:required="required"
189194
:autocomplete="autocomplete"
190-
v-bind="{ ...$attrs, ...ariaAttrs }"
195+
v-bind="{ ...$attrs, ...controlProps }"
191196
@input="onInput"
192197
@blur="onBlur"
193198
@change="onChange"

0 commit comments

Comments
 (0)