Compare commits

...

1 Commits

Author SHA1 Message Date
Romain Hamel
d099492285 feat(FormField): add inline variant 2025-06-14 18:52:55 +02:00
3 changed files with 37 additions and 12 deletions

View File

@@ -16,7 +16,7 @@ const feedbacks = [
<div class="flex flex-col items-center gap-4"> <div class="flex flex-col items-center gap-4">
<div class="flex flex-col gap-4 ms-[-38px]"> <div class="flex flex-col gap-4 ms-[-38px]">
<div v-for="(feedback, count) in feedbacks" :key="count" class="flex items-center"> <div v-for="(feedback, count) in feedbacks" :key="count" class="flex items-center">
<UFormField v-bind="feedback" label="Email" name="email"> <UFormField v-bind="feedback" label="Email" name="email" variant="inline">
<UInput placeholder="john@lennon.com" /> <UInput placeholder="john@lennon.com" />
</UFormField> </UFormField>
</div> </div>
@@ -41,6 +41,8 @@ const feedbacks = [
:size="size" :size="size"
label="Email" label="Email"
description="This is a description" description="This is a description"
hint="This is a hint"
help="This is a help"
name="email" name="email"
> >
<UInput placeholder="john@lennon.com" /> <UInput placeholder="john@lennon.com" />

View File

@@ -2,6 +2,7 @@
import type { AppConfig } from '@nuxt/schema' import type { AppConfig } from '@nuxt/schema'
import theme from '#build/ui/form-field' import theme from '#build/ui/form-field'
import type { ComponentConfig } from '../types/utils' import type { ComponentConfig } from '../types/utils'
import { createReusableTemplate } from '@vueuse/core'
type FormField = ComponentConfig<typeof theme, AppConfig, 'formField'> type FormField = ComponentConfig<typeof theme, AppConfig, 'formField'>
@@ -20,6 +21,8 @@ export interface FormFieldProps {
help?: string help?: string
error?: string | boolean error?: string | boolean
hint?: string hint?: string
variant?: 'default' | 'inline'
/** /**
* @defaultValue 'md' * @defaultValue 'md'
*/ */
@@ -61,6 +64,7 @@ const appConfig = useAppConfig() as FormField['AppConfig']
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.formField || {}) })({ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.formField || {}) })({
size: props.size, size: props.size,
variant: props.variant,
required: props.required required: props.required
})) }))
@@ -87,9 +91,28 @@ provide(formFieldInjectionKey, computed(() => ({
help: props.help, help: props.help,
ariaId ariaId
}) as FormFieldInjectedOptions<FormFieldProps>)) }) as FormFieldInjectedOptions<FormFieldProps>))
const [DefineHintTemplate, ReuseHintTemplate] = createReusableTemplate()
const [DefineDescriptionTemplate, ReuseDescriptionTemplate] = createReusableTemplate()
</script> </script>
<template> <template>
<DefineHintTemplate>
<span v-if="(hint || !!slots.hint)" :id="`${ariaId}-hint`" :class="ui.hint({ class: props.ui?.hint })">
<slot name="hint" :hint="hint">
{{ hint }}
</slot>
</span>
</DefineHintTemplate>
<DefineDescriptionTemplate>
<p v-if="description || !!slots.description" :id="`${ariaId}-description`" :class="ui.description({ class: props.ui?.description })">
<slot name="description" :description="description">
{{ description }}
</slot>
</p>
</DefineDescriptionTemplate>
<Primitive :as="as" :class="ui.root({ class: [props.ui?.root, props.class] })"> <Primitive :as="as" :class="ui.root({ class: [props.ui?.root, props.class] })">
<div :class="ui.wrapper({ class: props.ui?.wrapper })"> <div :class="ui.wrapper({ class: props.ui?.wrapper })">
<div v-if="label || !!slots.label" :class="ui.labelWrapper({ class: props.ui?.labelWrapper })"> <div v-if="label || !!slots.label" :class="ui.labelWrapper({ class: props.ui?.labelWrapper })">
@@ -98,20 +121,12 @@ provide(formFieldInjectionKey, computed(() => ({
{{ label }} {{ label }}
</slot> </slot>
</Label> </Label>
<span v-if="hint || !!slots.hint" :id="`${ariaId}-hint`" :class="ui.hint({ class: props.ui?.hint })"> <ReuseHintTemplate v-if="variant !== 'inline'" />
<slot name="hint" :hint="hint">
{{ hint }}
</slot>
</span>
</div> </div>
<p v-if="description || !!slots.description" :id="`${ariaId}-description`" :class="ui.description({ class: props.ui?.description })">
<slot name="description" :description="description">
{{ description }}
</slot>
</p>
</div> </div>
<ReuseDescriptionTemplate v-if="variant !== 'inline'" />
<div :class="[(label || !!slots.label || description || !!slots.description) && ui.container({ class: props.ui?.container })]"> <div :class="[(label || !!slots.label || description || !!slots.description) && ui.container({ class: props.ui?.container })]">
<slot :error="error" /> <slot :error="error" />

View File

@@ -18,6 +18,14 @@ export default {
lg: { root: 'text-sm' }, lg: { root: 'text-sm' },
xl: { root: 'text-base' } xl: { root: 'text-base' }
}, },
variant: {
inline: {
root: 'inline-flex',
label: 'mt-1.5 mx-2',
container: 'mt-0'
}
},
required: { required: {
true: { true: {
label: `after:content-['*'] after:ms-0.5 after:text-error` label: `after:content-['*'] after:ms-0.5 after:text-error`