Skip to content

Commit 0cf6198

Browse files
committed
chore: add formwerk’s useCustomControl to all form components
1 parent ef1af9a commit 0cf6198

File tree

16 files changed

+141
-87
lines changed

16 files changed

+141
-87
lines changed

src/runtime/components/Checkbox.vue

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,10 @@ export interface CheckboxSlots {
5959
</script>
6060

6161
<script setup lang="ts">
62-
import { computed, useId } from 'vue'
62+
import { computed } from 'vue'
6363
import { Primitive, CheckboxRoot, CheckboxIndicator, Label, useForwardProps } from 'reka-ui'
6464
import { reactivePick } from '@vueuse/core'
65+
import { useCustomControl } from '@formwerk/core'
6566
import { useAppConfig } from '#imports'
6667
import { useFormField } from '../composables/useFormField'
6768
import { tv } from '../utils/tv'
@@ -79,8 +80,13 @@ const appConfig = useAppConfig() as Checkbox['AppConfig']
7980
8081
const rootProps = useForwardProps(reactivePick(props, 'required', 'value', 'defaultValue'))
8182
82-
const { id: _id, emitFormChange, emitFormInput, size, color, name, disabled, ariaAttrs } = useFormField<CheckboxProps>(props)
83-
const id = _id.value ?? useId()
83+
const { emitFormChange, emitFormInput, size, color, name, disabled } = useFormField<CheckboxProps>(props)
84+
const { controlProps, field: { isDisabled } } = useCustomControl<boolean>({
85+
name,
86+
disabled,
87+
required: props.required,
88+
controlType: 'UCheckbox'
89+
})
8490
8591
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.checkbox || {}) })({
8692
size: size.value,
@@ -103,13 +109,12 @@ function onUpdate(value: any) {
103109
<!-- eslint-disable vue/no-template-shadow -->
104110
<template>
105111
<Primitive :as="(!variant || variant === 'list') ? as : Label" :class="ui.root({ class: [props.ui?.root, props.class] })">
112+
<pre>{{ name }}</pre>
106113
<div :class="ui.container({ class: props.ui?.container })">
107114
<CheckboxRoot
108-
:id="id"
109-
v-bind="{ ...rootProps, ...$attrs, ...ariaAttrs }"
115+
v-bind="{ ...rootProps, ...$attrs, ...controlProps }"
110116
v-model="modelValue"
111-
:name="name"
112-
:disabled="disabled"
117+
:disabled="isDisabled"
113118
:class="ui.base({ class: props.ui?.base })"
114119
@update:model-value="onUpdate"
115120
>

src/runtime/components/CheckboxGroup.vue

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,10 @@ export interface CheckboxGroupSlots<T extends CheckboxGroupItem[] = CheckboxGrou
7777
</script>
7878

7979
<script setup lang="ts" generic="T extends CheckboxGroupItem[], VK extends GetItemKeys<T> = 'value'">
80-
import { computed, useId } from 'vue'
80+
import { computed } from 'vue'
8181
import { CheckboxGroupRoot, useForwardProps, useForwardPropsEmits } from 'reka-ui'
8282
import { reactivePick } from '@vueuse/core'
83+
import { useCustomControl } from '@formwerk/core'
8384
import { useAppConfig } from '#imports'
8485
import { useFormField } from '../composables/useFormField'
8586
import { get, omit } from '../utils'
@@ -101,8 +102,13 @@ const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', '
101102
const checkboxProps = useForwardProps(reactivePick(props, 'variant', 'indicator', 'icon'))
102103
const getProxySlots = () => omit(slots, ['legend'])
103104
104-
const { emitFormChange, emitFormInput, color, name, size, id: _id, disabled, ariaAttrs } = useFormField<CheckboxGroupProps<T>>(props, { bind: false })
105-
const id = _id.value ?? useId()
105+
const { emitFormChange, emitFormInput, color, name, size, disabled } = useFormField<CheckboxGroupProps<T>>(props, { bind: false })
106+
const { controlProps, controlId } = useCustomControl({
107+
name,
108+
disabled,
109+
required: props.required,
110+
controlType: 'UInputMenu'
111+
})
106112
107113
const ui = computed(() => tv({ extend: theme, ...(appConfig.ui?.checkboxGroup || {}) })({
108114
size: size.value,
@@ -115,15 +121,15 @@ const ui = computed(() => tv({ extend: theme, ...(appConfig.ui?.checkboxGroup ||
115121
function normalizeItem(item: any) {
116122
if (item === null) {
117123
return {
118-
id: `${id}:null`,
124+
id: `${controlId}:null`,
119125
value: undefined,
120126
label: undefined
121127
}
122128
}
123129
124130
if (typeof item === 'string' || typeof item === 'number') {
125131
return {
126-
id: `${id}:${item}`,
132+
id: `${controlId}:${item}`,
127133
value: String(item),
128134
label: String(item)
129135
}
@@ -138,7 +144,7 @@ function normalizeItem(item: any) {
138144
value,
139145
label,
140146
description,
141-
id: `${id}:${value}`
147+
id: `${controlId}:${value}`
142148
}
143149
}
144150
@@ -161,14 +167,14 @@ function onUpdate(value: any) {
161167
<!-- eslint-disable vue/no-template-shadow -->
162168
<template>
163169
<CheckboxGroupRoot
164-
:id="id"
170+
:id="controlId"
165171
v-bind="rootProps"
166172
:name="name"
167173
:disabled="disabled"
168174
:class="ui.root({ class: [props.ui?.root, props.class] })"
169175
@update:model-value="onUpdate"
170176
>
171-
<fieldset :class="ui.fieldset({ class: props.ui?.fieldset })" v-bind="ariaAttrs">
177+
<fieldset :class="ui.fieldset({ class: props.ui?.fieldset })" v-bind="controlProps">
172178
<legend v-if="legend || !!slots.legend" :class="ui.legend({ class: props.ui?.legend })">
173179
<slot name="legend">
174180
{{ legend }}

src/runtime/components/FileUpload.vue

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ export interface FileUploadSlots<M extends boolean = false> {
126126
import { computed, watch } from 'vue'
127127
import { Primitive } from 'reka-ui'
128128
import { createReusableTemplate } from '@vueuse/core'
129+
import { useCustomControl } from '@formwerk/core'
129130
import { useAppConfig, useLocale } from '#imports'
130131
import { useFormField } from '../composables/useFormField'
131132
import { useFileUpload } from '../composables/useFileUpload'
@@ -163,7 +164,13 @@ const { isDragging, open, inputRef, dropzoneRef } = useFileUpload({
163164
dropzone: props.dropzone,
164165
onUpdate
165166
})
166-
const { emitFormInput, emitFormChange, id, name, disabled, ariaAttrs } = useFormField<FileUploadProps>(props)
167+
const { emitFormInput, emitFormChange, size: formFieldSize, highlight, color, name, disabled } = useFormField<FileUploadProps>(props)
168+
const { controlProps } = useCustomControl<File[] | File>({
169+
name,
170+
disabled,
171+
required: props.required,
172+
controlType: 'UFileUpload'
173+
})
167174
168175
const variant = computed(() => props.multiple ? 'area' : props.variant)
169176
const layout = computed(() => props.variant === 'button' && !props.multiple ? 'grid' : props.layout)
@@ -181,14 +188,14 @@ const position = computed(() => {
181188
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.fileUpload || {}) })({
182189
dropzone: props.dropzone,
183190
interactive: props.interactive,
184-
color: props.color,
185-
size: props.size,
191+
color: color.value,
192+
size: formFieldSize.value || props.size,
186193
variant: variant.value,
187194
layout: layout.value,
188195
position: position.value,
189196
multiple: props.multiple,
190-
highlight: props.highlight,
191-
disabled: props.disabled
197+
highlight: highlight.value,
198+
disabled: disabled.value
192199
}))
193200
194201
function createObjectUrl(file: File): string {
@@ -363,15 +370,14 @@ defineExpose({
363370
</slot>
364371

365372
<input
366-
:id="id"
367373
ref="inputRef"
368374
type="file"
369375
:name="name"
370376
:accept="accept"
371377
:multiple="(multiple as boolean)"
372378
:required="required"
373379
:disabled="disabled"
374-
v-bind="{ ...$attrs, ...ariaAttrs }"
380+
v-bind="{ ...$attrs, ...controlProps }"
375381
class="sr-only"
376382
tabindex="-1"
377383
>

src/runtime/components/FormField.vue

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,7 @@ provide(formFieldInjectionKey, computed(() => ({
7979
size: props.size,
8080
eagerValidation: props.eagerValidation,
8181
validateOnInputDelay: props.validateOnInputDelay,
82-
errorPattern: props.errorPattern,
83-
hint: props.hint,
84-
description: props.description,
85-
help: props.help
82+
errorPattern: props.errorPattern
8683
}) as FormFieldInjectedOptions<FormFieldProps>))
8784
</script>
8885

src/runtime/components/InputMenu.vue

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ import { ComboboxRoot, ComboboxArrow, ComboboxAnchor, ComboboxInput, ComboboxTri
178178
import { defu } from 'defu'
179179
import { isEqual } from 'ohash/utils'
180180
import { reactivePick, createReusableTemplate } from '@vueuse/core'
181+
import { useCustomControl } from '@formwerk/core'
181182
import { useAppConfig } from '#imports'
182183
import { useFieldGroup } from '../composables/useFieldGroup'
183184
import { useComponentIcons } from '../composables/useComponentIcons'
@@ -214,7 +215,13 @@ const portalProps = usePortal(toRef(() => props.portal))
214215
const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }) as ComboboxContentProps)
215216
const arrowProps = toRef(() => props.arrow as ComboboxArrowProps)
216217
217-
const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, size: formGroupSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField<InputProps>(props)
218+
const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, size: formGroupSize, color, name, highlight, disabled } = useFormField<InputProps>(props)
219+
const { controlProps } = useCustomControl({
220+
name,
221+
disabled,
222+
required: props.required,
223+
controlType: 'UInputMenu'
224+
})
218225
const { orientation, size: fieldGroupSize } = useFieldGroup<InputProps>(props)
219226
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(toRef(() => defu(props, { trailingIcon: appConfig.ui.icons.chevronDown })))
220227
@@ -457,9 +464,8 @@ defineExpose({
457464

458465
<ComboboxInput v-model="searchTerm" as-child>
459466
<TagsInputInput
460-
:id="id"
461467
ref="inputRef"
462-
v-bind="{ ...$attrs, ...ariaAttrs }"
468+
v-bind="{ ...$attrs, ...controlProps }"
463469
:placeholder="placeholder"
464470
:class="ui.tagsInput({ class: props.ui?.tagsInput })"
465471
@keydown.enter.prevent
@@ -469,10 +475,9 @@ defineExpose({
469475

470476
<ComboboxInput
471477
v-else
472-
:id="id"
473478
ref="inputRef"
474479
:display-value="displayValue"
475-
v-bind="{ ...$attrs, ...ariaAttrs }"
480+
v-bind="{ ...$attrs, ...controlProps }"
476481
:type="type"
477482
:placeholder="placeholder"
478483
:required="required"

src/runtime/components/InputNumber.vue

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export interface InputNumberSlots {
8080
import { onMounted, ref, computed } from 'vue'
8181
import { NumberFieldRoot, NumberFieldInput, NumberFieldDecrement, NumberFieldIncrement, useForwardPropsEmits } from 'reka-ui'
8282
import { reactivePick, useVModel } from '@vueuse/core'
83+
import { useCustomControl } from '@formwerk/core'
8384
import { useAppConfig } from '#imports'
8485
import { useFieldGroup } from '../composables/useFieldGroup'
8586
import { useFormField } from '../composables/useFormField'
@@ -104,7 +105,13 @@ const appConfig = useAppConfig() as InputNumber['AppConfig']
104105
105106
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'defaultValue', 'min', 'max', 'step', 'stepSnapping', 'formatOptions', 'disableWheelChange', 'invertWheelChange', 'readonly'), emits)
106107
107-
const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, id, color, size: formGroupSize, name, highlight, disabled, ariaAttrs } = useFormField<InputNumberProps>(props)
108+
const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, color, size: formGroupSize, name, highlight, disabled } = useFormField<InputNumberProps>(props)
109+
const { controlProps } = useCustomControl({
110+
name,
111+
disabled,
112+
required: props.required,
113+
controlType: 'UInputNumber'
114+
})
108115
const { orientation, size: fieldGroupSize } = useFieldGroup<InputNumberProps>(props)
109116
110117
const locale = computed(() => props.locale || codeLocale.value)
@@ -171,7 +178,7 @@ defineExpose({
171178
@update:model-value="onUpdate"
172179
>
173180
<NumberFieldInput
174-
v-bind="{ ...$attrs, ...ariaAttrs }"
181+
v-bind="{ ...$attrs, ...controlProps }"
175182
ref="inputRef"
176183
:placeholder="placeholder"
177184
:required="required"

src/runtime/components/InputTags.vue

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export interface InputTagsSlots<T extends InputTagItem = InputTagItem> {
6767
import { computed, ref, onMounted, toRaw } from 'vue'
6868
import { TagsInputRoot, TagsInputItem, TagsInputItemText, TagsInputItemDelete, TagsInputInput, useForwardPropsEmits } from 'reka-ui'
6969
import { reactivePick } from '@vueuse/core'
70+
import { useCustomControl } from '@formwerk/core'
7071
import { useAppConfig } from '#imports'
7172
import { useFieldGroup } from '../composables/useFieldGroup'
7273
import { useComponentIcons } from '../composables/useComponentIcons'
@@ -88,7 +89,13 @@ const appConfig = useAppConfig() as InputTags['AppConfig']
8889
8990
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'addOnPaste', 'addOnTab', 'addOnBlur', 'duplicate', 'delimiter', 'max', 'convertValue', 'displayValue', 'required'), emits)
9091
91-
const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, size: formGroupSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField<InputTagsProps>(props)
92+
const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, size: formGroupSize, color, name, highlight, disabled } = useFormField<InputTagsProps>(props)
93+
const { controlProps } = useCustomControl({
94+
name,
95+
disabled,
96+
required: props.required,
97+
controlType: 'UInputTags'
98+
})
9299
const { orientation, size: fieldGroupSize } = useFieldGroup<InputTagsProps>(props)
93100
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(props)
94101
@@ -180,7 +187,7 @@ defineExpose({
180187

181188
<TagsInputInput
182189
ref="inputRef"
183-
v-bind="{ ...$attrs, ...ariaAttrs }"
190+
v-bind="{ ...$attrs, ...controlProps }"
184191
:placeholder="placeholder"
185192
:max-length="maxLength"
186193
:class="ui.input({ class: props.ui?.input })"

src/runtime/components/PinInput.vue

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import type { ComponentPublicInstance } from 'vue'
5252
import { ref, computed, onMounted } from 'vue'
5353
import { PinInputInput, PinInputRoot, useForwardPropsEmits } from 'reka-ui'
5454
import { reactivePick } from '@vueuse/core'
55+
import { useCustomControl } from '@formwerk/core'
5556
import { useAppConfig } from '#imports'
5657
import { useFormField } from '../composables/useFormField'
5758
import { looseToNumber } from '../utils'
@@ -68,7 +69,13 @@ const appConfig = useAppConfig() as PinInput['AppConfig']
6869
6970
const rootProps = useForwardPropsEmits(reactivePick(props, 'disabled', 'id', 'mask', 'name', 'otp', 'required', 'type'), emits)
7071
71-
const { emitFormInput, emitFormFocus, emitFormChange, emitFormBlur, size, color, id, name, highlight, disabled, ariaAttrs } = useFormField<PinInputProps>(props)
72+
const { emitFormInput, emitFormFocus, emitFormChange, emitFormBlur, size, color, name, highlight, disabled } = useFormField<PinInputProps>(props)
73+
const { controlProps, field: { isDisabled } } = useCustomControl({
74+
name,
75+
disabled,
76+
required: props.required,
77+
controlType: 'UPinInput'
78+
})
7279
7380
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.pinInput || {}) })({
7481
color: color.value,
@@ -113,8 +120,7 @@ defineExpose({
113120

114121
<template>
115122
<PinInputRoot
116-
v-bind="{ ...rootProps, ...ariaAttrs }"
117-
:id="id"
123+
v-bind="{ ...rootProps, ...controlProps }"
118124
:name="name"
119125
:placeholder="placeholder"
120126
:model-value="(modelValue as PinInputValue<T>)"
@@ -129,7 +135,7 @@ defineExpose({
129135
:ref="el => (inputsRef[index] = el as ComponentPublicInstance)"
130136
:index="index"
131137
:class="ui.base({ class: props.ui?.base })"
132-
:disabled="disabled"
138+
:disabled="isDisabled"
133139
@blur="onBlur"
134140
@focus="emitFormFocus"
135141
/>

0 commit comments

Comments
 (0)