mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-27 10:20:42 +01:00
chore(FormGroup): simplify bindings between input and form group p… (#704)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
This commit is contained in:
@@ -80,7 +80,7 @@ async function submit (event: FormSubmitEvent<Schema>) {
|
|||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UFormGroup name="checkbox" label="Checkbox">
|
<UFormGroup name="checkbox" label="Checkbox">
|
||||||
<UCheckbox v-model="state.checkbox" />
|
<UCheckbox v-model="state.checkbox" label="Check me" />
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UFormGroup name="radio" label="Radio">
|
<UFormGroup name="radio" label="Radio">
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div :class="ui.wrapper">
|
<div :class="ui.wrapper">
|
||||||
<div class="flex items-center h-5">
|
<div class="flex items-center h-5">
|
||||||
<input
|
<input
|
||||||
:id="name"
|
:id="inputId"
|
||||||
v-model="toggle"
|
v-model="toggle"
|
||||||
:name="name"
|
:name="name"
|
||||||
:required="required"
|
:required="required"
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="label || $slots.label" class="ms-3 text-sm">
|
<div v-if="label || $slots.label" class="ms-3 text-sm">
|
||||||
<label :for="name" :class="ui.label">
|
<label :for="inputId" :class="ui.label">
|
||||||
<slot name="label">{{ label }}</slot>
|
<slot name="label">{{ label }}</slot>
|
||||||
<span v-if="required" :class="ui.required">*</span>
|
<span v-if="required" :class="ui.required">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -36,6 +36,7 @@ import { twMerge, twJoin } from 'tailwind-merge'
|
|||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useFormGroup } from '../../composables/useFormGroup'
|
import { useFormGroup } from '../../composables/useFormGroup'
|
||||||
import { mergeConfig } from '../../utils'
|
import { mergeConfig } from '../../utils'
|
||||||
|
import { uid } from '../../utils/uid'
|
||||||
import type { Strategy } from '../../types'
|
import type { Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
@@ -47,6 +48,11 @@ const config = mergeConfig<typeof checkbox>(appConfig.ui.strategy, appConfig.ui.
|
|||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: {
|
props: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
// A default value is needed here to bind the label
|
||||||
|
default: () => uid()
|
||||||
|
},
|
||||||
value: {
|
value: {
|
||||||
type: [String, Number, Boolean, Object],
|
type: [String, Number, Boolean, Object],
|
||||||
default: null
|
default: null
|
||||||
@@ -103,8 +109,7 @@ export default defineComponent({
|
|||||||
setup (props, { emit }) {
|
setup (props, { emit }) {
|
||||||
const { ui, attrs } = useUI('checkbox', props.ui, config, { mergeWrapper: true })
|
const { ui, attrs } = useUI('checkbox', props.ui, config, { mergeWrapper: true })
|
||||||
|
|
||||||
const { emitFormChange, formGroup } = useFormGroup()
|
const { emitFormChange, color, name, inputId } = useFormGroup(props)
|
||||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
|
||||||
|
|
||||||
const toggle = computed({
|
const toggle = computed({
|
||||||
get () {
|
get () {
|
||||||
@@ -136,6 +141,9 @@ export default defineComponent({
|
|||||||
ui,
|
ui,
|
||||||
attrs,
|
attrs,
|
||||||
toggle,
|
toggle,
|
||||||
|
inputId,
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
name,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
inputClass,
|
inputClass,
|
||||||
onChange
|
onChange
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import type { ValidationError as JoiError, Schema as JoiSchema } from 'joi'
|
|||||||
import type { ObjectSchema as YupObjectSchema, ValidationError as YupError } from 'yup'
|
import type { ObjectSchema as YupObjectSchema, ValidationError as YupError } from 'yup'
|
||||||
import type { ObjectSchemaAsync as ValibotObjectSchema } from 'valibot'
|
import type { ObjectSchemaAsync as ValibotObjectSchema } from 'valibot'
|
||||||
import type { FormError, FormEvent, FormEventType, FormSubmitEvent, Form } from '../../types/form'
|
import type { FormError, FormEvent, FormEventType, FormSubmitEvent, Form } from '../../types/form'
|
||||||
|
import { uid } from '../../utils/uid'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
@@ -40,8 +41,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
emits: ['submit'],
|
emits: ['submit'],
|
||||||
setup (props, { expose, emit }) {
|
setup (props, { expose, emit }) {
|
||||||
const seed = Math.random().toString(36).substring(7)
|
const bus = useEventBus<FormEvent>(`form-${uid()}`)
|
||||||
const bus = useEventBus<FormEvent>(`form-${seed}`)
|
|
||||||
|
|
||||||
bus.on(async (event) => {
|
bus.on(async (event) => {
|
||||||
if (event.type !== 'submit' && props.validateOn?.includes(event.type)) {
|
if (event.type !== 'submit' && props.validateOn?.includes(event.type)) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="ui.wrapper" v-bind="attrs">
|
<div :class="ui.wrapper" v-bind="attrs">
|
||||||
<div v-if="label" :class="[ui.label.wrapper, size]">
|
<div v-if="label" :class="[ui.label.wrapper, size]">
|
||||||
<label :for="labelFor" :class="[ui.label.base, required ? ui.label.required : '']">{{ label }}</label>
|
<label :for="inputId" :class="[ui.label.base, required ? ui.label.required : '']">{{ label }}</label>
|
||||||
<span v-if="hint" :class="[ui.hint]">{{ hint }}</span>
|
<span v-if="hint" :class="[ui.hint]">{{ hint }}</span>
|
||||||
</div>
|
</div>
|
||||||
<p v-if="description" :class="[ui.description, size]">
|
<p v-if="description" :class="[ui.description, size]">
|
||||||
@@ -28,11 +28,10 @@ import type { FormError, InjectedFormGroupValue, Strategy } from '../../types'
|
|||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
import { formGroup } from '#ui/ui.config'
|
import { formGroup } from '#ui/ui.config'
|
||||||
|
import { uid } from '../../utils/uid'
|
||||||
|
|
||||||
const config = mergeConfig<typeof formGroup>(appConfig.ui.strategy, appConfig.ui.formGroup, formGroup)
|
const config = mergeConfig<typeof formGroup>(appConfig.ui.strategy, appConfig.ui.formGroup, formGroup)
|
||||||
|
|
||||||
let increment = 0
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: {
|
props: {
|
||||||
@@ -88,11 +87,11 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const size = computed(() => ui.value.size[props.size ?? config.default.size])
|
const size = computed(() => ui.value.size[props.size ?? config.default.size])
|
||||||
const labelFor = ref(`${props.name || 'lf'}-${increment = increment < 1000000 ? increment + 1 : 0}`)
|
const inputId = ref(uid())
|
||||||
|
|
||||||
provide<InjectedFormGroupValue>('form-group', {
|
provide<InjectedFormGroupValue>('form-group', {
|
||||||
error,
|
error,
|
||||||
labelFor,
|
inputId,
|
||||||
name: computed(() => props.name),
|
name: computed(() => props.name),
|
||||||
size: computed(() => props.size)
|
size: computed(() => props.size)
|
||||||
})
|
})
|
||||||
@@ -101,7 +100,7 @@ export default defineComponent({
|
|||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
ui,
|
ui,
|
||||||
attrs,
|
attrs,
|
||||||
labelFor,
|
inputId,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
size,
|
size,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="ui.wrapper">
|
<div :class="ui.wrapper">
|
||||||
<input
|
<input
|
||||||
:id="id"
|
:id="inputId"
|
||||||
ref="input"
|
ref="input"
|
||||||
:name="name"
|
:name="name"
|
||||||
:value="modelValue"
|
:value="modelValue"
|
||||||
@@ -119,7 +119,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String as PropType<keyof typeof config.size>,
|
type: String as PropType<keyof typeof config.size>,
|
||||||
default: () => config.default.size,
|
default: null,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(config.size).includes(value)
|
return Object.keys(config.size).includes(value)
|
||||||
}
|
}
|
||||||
@@ -154,10 +154,7 @@ export default defineComponent({
|
|||||||
setup (props, { emit, slots }) {
|
setup (props, { emit, slots }) {
|
||||||
const { ui, attrs } = useUI('input', props.ui, config, { mergeWrapper: true })
|
const { ui, attrs } = useUI('input', props.ui, config, { mergeWrapper: true })
|
||||||
|
|
||||||
const { emitFormBlur, emitFormInput, formGroup } = useFormGroup(props)
|
const { emitFormBlur, emitFormInput, size, color, inputId, name } = useFormGroup(props, config)
|
||||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
|
||||||
const size = computed(() => formGroup?.size?.value ?? props.size)
|
|
||||||
const id = formGroup?.labelFor
|
|
||||||
|
|
||||||
const input = ref<HTMLInputElement | null>(null)
|
const input = ref<HTMLInputElement | null>(null)
|
||||||
|
|
||||||
@@ -261,7 +258,8 @@ export default defineComponent({
|
|||||||
ui,
|
ui,
|
||||||
attrs,
|
attrs,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
id,
|
name,
|
||||||
|
inputId,
|
||||||
input,
|
input,
|
||||||
isLeading,
|
isLeading,
|
||||||
isTrailing,
|
isTrailing,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div :class="ui.wrapper">
|
<div :class="ui.wrapper">
|
||||||
<div class="flex items-center h-5">
|
<div class="flex items-center h-5">
|
||||||
<input
|
<input
|
||||||
:id="`${name}-${value}`"
|
:id="inputId"
|
||||||
v-model="pick"
|
v-model="pick"
|
||||||
:name="name"
|
:name="name"
|
||||||
:required="required"
|
:required="required"
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="label || $slots.label" class="ms-3 text-sm">
|
<div v-if="label || $slots.label" class="ms-3 text-sm">
|
||||||
<label :for="`${name}-${value}`" :class="ui.label">
|
<label :for="inputId" :class="ui.label">
|
||||||
<slot name="label">{{ label }}</slot>
|
<slot name="label">{{ label }}</slot>
|
||||||
<span v-if="required" :class="ui.required">*</span>
|
<span v-if="required" :class="ui.required">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -38,12 +38,18 @@ import type { Strategy } from '../../types'
|
|||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
import { radio } from '#ui/ui.config'
|
import { radio } from '#ui/ui.config'
|
||||||
import colors from '#ui-colors'
|
import colors from '#ui-colors'
|
||||||
|
import { uid } from '../../utils/uid'
|
||||||
|
|
||||||
const config = mergeConfig<typeof radio>(appConfig.ui.strategy, appConfig.ui.radio, radio)
|
const config = mergeConfig<typeof radio>(appConfig.ui.strategy, appConfig.ui.radio, radio)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: {
|
props: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
// A default value is needed here to bind the label
|
||||||
|
default: () => uid()
|
||||||
|
},
|
||||||
value: {
|
value: {
|
||||||
type: [String, Number, Boolean],
|
type: [String, Number, Boolean],
|
||||||
default: null
|
default: null
|
||||||
@@ -92,8 +98,7 @@ export default defineComponent({
|
|||||||
setup (props, { emit }) {
|
setup (props, { emit }) {
|
||||||
const { ui, attrs } = useUI('radio', props.ui, config, { mergeWrapper: true })
|
const { ui, attrs } = useUI('radio', props.ui, config, { mergeWrapper: true })
|
||||||
|
|
||||||
const { emitFormChange, formGroup } = useFormGroup()
|
const { emitFormChange, color, name, inputId } = useFormGroup(props)
|
||||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
|
||||||
|
|
||||||
const pick = computed({
|
const pick = computed({
|
||||||
get () {
|
get () {
|
||||||
@@ -120,9 +125,12 @@ export default defineComponent({
|
|||||||
return {
|
return {
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
ui,
|
ui,
|
||||||
|
inputId,
|
||||||
attrs,
|
attrs,
|
||||||
pick,
|
pick,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
name,
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
inputClass
|
inputClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="wrapperClass">
|
<div :class="wrapperClass">
|
||||||
<input
|
<input
|
||||||
:id="id"
|
:id="inputId"
|
||||||
ref="input"
|
ref="input"
|
||||||
v-model.number="value"
|
v-model.number="value"
|
||||||
:name="name"
|
:name="name"
|
||||||
@@ -67,7 +67,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String as PropType<keyof typeof config.size>,
|
type: String as PropType<keyof typeof config.size>,
|
||||||
default: () => config.default.size,
|
default: null,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(config.size).includes(value)
|
return Object.keys(config.size).includes(value)
|
||||||
}
|
}
|
||||||
@@ -92,10 +92,7 @@ export default defineComponent({
|
|||||||
setup (props, { emit }) {
|
setup (props, { emit }) {
|
||||||
const { ui, attrs, attrsClass } = useUI('range', props.ui, config)
|
const { ui, attrs, attrsClass } = useUI('range', props.ui, config)
|
||||||
|
|
||||||
const { emitFormChange, formGroup } = useFormGroup(props)
|
const { emitFormChange, inputId, color, size, name } = useFormGroup(props, config)
|
||||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
|
||||||
const size = computed(() => formGroup?.size?.value ?? props.size)
|
|
||||||
const id = formGroup?.labelFor
|
|
||||||
|
|
||||||
const value = computed({
|
const value = computed({
|
||||||
get () {
|
get () {
|
||||||
@@ -171,7 +168,8 @@ export default defineComponent({
|
|||||||
ui,
|
ui,
|
||||||
attrs,
|
attrs,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
id,
|
name,
|
||||||
|
inputId,
|
||||||
value,
|
value,
|
||||||
wrapperClass,
|
wrapperClass,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="ui.wrapper">
|
<div :class="ui.wrapper">
|
||||||
<select
|
<select
|
||||||
:id="id"
|
:id="inputId"
|
||||||
:name="name"
|
:name="name"
|
||||||
:value="modelValue"
|
:value="modelValue"
|
||||||
:required="required"
|
:required="required"
|
||||||
@@ -137,7 +137,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String as PropType<keyof typeof config.size>,
|
type: String as PropType<keyof typeof config.size>,
|
||||||
default: () => config.default.size,
|
default: null,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(config.size).includes(value)
|
return Object.keys(config.size).includes(value)
|
||||||
}
|
}
|
||||||
@@ -180,10 +180,7 @@ export default defineComponent({
|
|||||||
setup (props, { emit, slots }) {
|
setup (props, { emit, slots }) {
|
||||||
const { ui, attrs } = useUI('select', props.ui, config, { mergeWrapper: true })
|
const { ui, attrs } = useUI('select', props.ui, config, { mergeWrapper: true })
|
||||||
|
|
||||||
const { emitFormChange, formGroup } = useFormGroup(props)
|
const { emitFormChange, inputId, color, size, name } = useFormGroup(props, config)
|
||||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
|
||||||
const size = computed(() => formGroup?.size?.value ?? props.size)
|
|
||||||
const id = formGroup?.labelFor
|
|
||||||
|
|
||||||
const onInput = (event: InputEvent) => {
|
const onInput = (event: InputEvent) => {
|
||||||
emit('update:modelValue', (event.target as HTMLInputElement).value)
|
emit('update:modelValue', (event.target as HTMLInputElement).value)
|
||||||
@@ -323,7 +320,8 @@ export default defineComponent({
|
|||||||
ui,
|
ui,
|
||||||
attrs,
|
attrs,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
id,
|
name,
|
||||||
|
inputId,
|
||||||
normalizedOptionsWithPlaceholder,
|
normalizedOptionsWithPlaceholder,
|
||||||
normalizedValue,
|
normalizedValue,
|
||||||
isLeading,
|
isLeading,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
class="inline-flex w-full"
|
class="inline-flex w-full"
|
||||||
>
|
>
|
||||||
<slot :open="open" :disabled="disabled" :loading="loading">
|
<slot :open="open" :disabled="disabled" :loading="loading">
|
||||||
<button :id="id" :class="selectClass" :disabled="disabled || loading" type="button" v-bind="attrs">
|
<button :id="inputId" :class="selectClass" :disabled="disabled || loading" type="button" v-bind="attrs">
|
||||||
<span v-if="(isLeading && leadingIconName) || $slots.leading" :class="leadingWrapperIconClass">
|
<span v-if="(isLeading && leadingIconName) || $slots.leading" :class="leadingWrapperIconClass">
|
||||||
<slot name="leading" :disabled="disabled" :loading="loading">
|
<slot name="leading" :disabled="disabled" :loading="loading">
|
||||||
<UIcon :name="leadingIconName" :class="leadingIconClass" />
|
<UIcon :name="leadingIconName" :class="leadingIconClass" />
|
||||||
@@ -254,7 +254,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String as PropType<keyof typeof config.size>,
|
type: String as PropType<keyof typeof config.size>,
|
||||||
default: () => config.default.size,
|
default: null,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(config.size).includes(value)
|
return Object.keys(config.size).includes(value)
|
||||||
}
|
}
|
||||||
@@ -314,10 +314,7 @@ export default defineComponent({
|
|||||||
const popper = computed<PopperOptions>(() => defu({}, props.popper, uiMenu.value.popper as PopperOptions))
|
const popper = computed<PopperOptions>(() => defu({}, props.popper, uiMenu.value.popper as PopperOptions))
|
||||||
|
|
||||||
const [trigger, container] = usePopper(popper.value)
|
const [trigger, container] = usePopper(popper.value)
|
||||||
const { emitFormBlur, emitFormChange, formGroup } = useFormGroup(props)
|
const { emitFormBlur, emitFormChange, inputId, color, size, name } = useFormGroup(props, config)
|
||||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
|
||||||
const size = computed(() => formGroup?.size?.value ?? props.size)
|
|
||||||
const id = formGroup?.labelFor
|
|
||||||
|
|
||||||
const query = ref('')
|
const query = ref('')
|
||||||
const searchInput = ref<ComponentPublicInstance<HTMLElement>>()
|
const searchInput = ref<ComponentPublicInstance<HTMLElement>>()
|
||||||
@@ -446,7 +443,8 @@ export default defineComponent({
|
|||||||
uiMenu,
|
uiMenu,
|
||||||
attrs,
|
attrs,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
id,
|
name,
|
||||||
|
inputId,
|
||||||
trigger,
|
trigger,
|
||||||
container,
|
container,
|
||||||
isLeading,
|
isLeading,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="ui.wrapper">
|
<div :class="ui.wrapper">
|
||||||
<textarea
|
<textarea
|
||||||
:id="id"
|
:id="inputId"
|
||||||
ref="textarea"
|
ref="textarea"
|
||||||
:value="modelValue"
|
:value="modelValue"
|
||||||
:name="name"
|
:name="name"
|
||||||
@@ -82,7 +82,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String as PropType<keyof typeof config.size>,
|
type: String as PropType<keyof typeof config.size>,
|
||||||
default: () => config.default.size,
|
default: null,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(config.size).includes(value)
|
return Object.keys(config.size).includes(value)
|
||||||
}
|
}
|
||||||
@@ -117,10 +117,7 @@ export default defineComponent({
|
|||||||
setup (props, { emit }) {
|
setup (props, { emit }) {
|
||||||
const { ui, attrs } = useUI('textarea', props.ui, config, { mergeWrapper: true })
|
const { ui, attrs } = useUI('textarea', props.ui, config, { mergeWrapper: true })
|
||||||
|
|
||||||
const { emitFormBlur, emitFormInput, formGroup } = useFormGroup(props)
|
const { emitFormBlur, emitFormInput, inputId, color, size, name } = useFormGroup(props, config)
|
||||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
|
||||||
const size = computed(() => formGroup?.size?.value ?? props.size)
|
|
||||||
const id = formGroup?.labelFor
|
|
||||||
|
|
||||||
const textarea = ref<HTMLTextAreaElement | null>(null)
|
const textarea = ref<HTMLTextAreaElement | null>(null)
|
||||||
|
|
||||||
@@ -200,7 +197,8 @@ export default defineComponent({
|
|||||||
ui,
|
ui,
|
||||||
attrs,
|
attrs,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
id,
|
name,
|
||||||
|
inputId,
|
||||||
textarea,
|
textarea,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
textareaClass,
|
textareaClass,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<HSwitch
|
<HSwitch
|
||||||
:id="id"
|
:id="inputId"
|
||||||
v-model="active"
|
v-model="active"
|
||||||
:name="name"
|
:name="name"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
@@ -82,9 +82,7 @@ export default defineComponent({
|
|||||||
setup (props, { emit }) {
|
setup (props, { emit }) {
|
||||||
const { ui, attrs, attrsClass } = useUI('toggle', props.ui, config)
|
const { ui, attrs, attrsClass } = useUI('toggle', props.ui, config)
|
||||||
|
|
||||||
const { emitFormChange, formGroup } = useFormGroup(props)
|
const { emitFormChange, color, inputId, name } = useFormGroup(props)
|
||||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
|
||||||
const id = formGroup?.labelFor
|
|
||||||
|
|
||||||
const active = computed({
|
const active = computed({
|
||||||
get () {
|
get () {
|
||||||
@@ -122,7 +120,8 @@ export default defineComponent({
|
|||||||
ui,
|
ui,
|
||||||
attrs,
|
attrs,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
id,
|
name,
|
||||||
|
inputId,
|
||||||
active,
|
active,
|
||||||
switchClass,
|
switchClass,
|
||||||
onIconClass,
|
onIconClass,
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
import { inject, ref } from 'vue'
|
import { inject, ref, computed } from 'vue'
|
||||||
import { type UseEventBusReturn, useDebounceFn } from '@vueuse/core'
|
import { type UseEventBusReturn, useDebounceFn } from '@vueuse/core'
|
||||||
import type { FormEvent, FormEventType, InjectedFormGroupValue } from '../types/form'
|
import type { FormEvent, FormEventType, InjectedFormGroupValue } from '../types/form'
|
||||||
|
|
||||||
type InputAttrs = {
|
type InputProps = {
|
||||||
id?: string
|
id?: string
|
||||||
|
size?: string
|
||||||
|
color?: string
|
||||||
|
name?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useFormGroup = (inputAttrs?: InputAttrs) => {
|
export const useFormGroup = (inputProps?: InputProps, config?: any) => {
|
||||||
const formBus = inject<UseEventBusReturn<FormEvent, string> | undefined>('form-events', undefined)
|
const formBus = inject<UseEventBusReturn<FormEvent, string> | undefined>('form-events', undefined)
|
||||||
const formGroup = inject<InjectedFormGroupValue>('form-group', undefined)
|
const formGroup = inject<InjectedFormGroupValue>('form-group', undefined)
|
||||||
|
|
||||||
if (formGroup) {
|
if (formGroup) {
|
||||||
formGroup.labelFor.value = inputAttrs?.id ?? formGroup?.labelFor.value
|
// Updates for="..." attribute on label if inputProps.id is provided
|
||||||
|
formGroup.inputId.value = inputProps?.id ?? formGroup?.inputId.value
|
||||||
}
|
}
|
||||||
|
|
||||||
const blurred = ref(false)
|
const blurred = ref(false)
|
||||||
@@ -38,9 +42,12 @@ export const useFormGroup = (inputAttrs?: InputAttrs) => {
|
|||||||
}, 300)
|
}, 300)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
inputId: computed(() => inputProps.id ?? formGroup?.inputId.value),
|
||||||
|
name: computed(() => inputProps?.name ?? formGroup?.name.value),
|
||||||
|
size: computed(() => inputProps?.size ?? formGroup?.size.value ?? config?.default?.size),
|
||||||
|
color: computed(() => formGroup?.error?.value ? 'red' : inputProps?.color),
|
||||||
emitFormBlur,
|
emitFormBlur,
|
||||||
emitFormInput,
|
emitFormInput,
|
||||||
emitFormChange,
|
emitFormChange
|
||||||
formGroup
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
4
src/runtime/types/form.d.ts
vendored
4
src/runtime/types/form.d.ts
vendored
@@ -21,8 +21,8 @@ export interface FormEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface InjectedFormGroupValue {
|
export interface InjectedFormGroupValue {
|
||||||
labelFor: Ref<string>
|
inputId: Ref<string>
|
||||||
name: Ref<string>
|
name: Ref<string>
|
||||||
size: Ref<string>
|
size: Ref<string>
|
||||||
error: Ref<string | boolean>
|
error: Ref<string | boolean>
|
||||||
}
|
}
|
||||||
|
|||||||
5
src/runtime/utils/uid.ts
Normal file
5
src/runtime/utils/uid.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
let _id = 0
|
||||||
|
|
||||||
|
export function uid () {
|
||||||
|
return `nuid-${_id++}`
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user