Skip to content

Commit b74ec6e

Browse files
edimitchelCopilotbenjamincanac
authored
feat(FormField): add orientation prop (#5632)
Co-authored-by: Copilot <[email protected]> Co-authored-by: Benjamin Canac <[email protected]>
1 parent 49daf8d commit b74ec6e

File tree

9 files changed

+301
-201
lines changed

9 files changed

+301
-201
lines changed

docs/content/docs/2.components/form-field.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,30 @@ slots:
170170
:u-input{placeholder="Enter your email" class="w-full"}
171171
::
172172

173+
### Orientation :badge{label="Soon" class="align-text-top"}
174+
175+
Use the `orientation` prop to change the layout of the FormField. Defaults to `vertical`.
176+
177+
::component-code
178+
---
179+
prettier: true
180+
ignore:
181+
- label
182+
- class
183+
props:
184+
orientation: horizontal
185+
label: Email
186+
help: Please enter a valid email address.
187+
class: w-72
188+
slots:
189+
default: |
190+
191+
<UInput placeholder="Enter your email" class="w-full" />
192+
---
193+
194+
:u-input{placeholder="Enter your email" class="w-full"}
195+
::
196+
173197
## API
174198

175199
### Props

playgrounds/nuxt/app/pages/components/form-field.vue

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
import theme from '#build/ui/form-field'
33
44
const sizes = Object.keys(theme.variants.size)
5+
const orientations = Object.keys(theme.variants.orientation)
56
67
const attrs = reactive({
7-
size: [theme.defaultVariants.size]
8+
size: [theme.defaultVariants.size],
9+
orientation: [theme.defaultVariants.orientation]
810
})
911
1012
const feedbacks = [
@@ -20,11 +22,12 @@ const feedbacks = [
2022
<template>
2123
<Navbar>
2224
<USelect v-model="attrs.size" :items="sizes" placeholder="Size" multiple />
25+
<USelect v-model="attrs.orientation" :items="orientations" placeholder="Orientation" multiple />
2326
</Navbar>
2427

2528
<Matrix v-slot="props" :attrs="attrs">
2629
<template v-for="(feedback, index) in feedbacks" :key="index">
27-
<UFormField label="Email" name="email" v-bind="{ ...feedback, ...props }">
30+
<UFormField label="Email" name="email" v-bind="{ ...feedback, ...props }" class="data-[orientation=horizontal]:w-full">
2831
<UInput placeholder="[email protected]" />
2932
</UFormField>
3033
</template>

src/runtime/components/FormField.vue

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ export interface FormFieldProps {
3333
* @defaultValue `300`
3434
*/
3535
validateOnInputDelay?: number
36+
/**
37+
* The orientation of the form field.
38+
* @defaultValue 'vertical'
39+
*/
40+
orientation?: FormField['variants']['orientation']
3641
class?: any
3742
ui?: FormField['slots']
3843
}
@@ -64,7 +69,8 @@ const appConfig = useAppConfig() as FormField['AppConfig']
6469
6570
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.formField || {}) })({
6671
size: props.size,
67-
required: props.required
72+
required: props.required,
73+
orientation: props.orientation
6874
}))
6975
7076
const formErrors = inject<Ref<FormError[]> | null>(formErrorsInjectionKey, null)
@@ -100,7 +106,7 @@ provide(formFieldInjectionKey, computed(() => ({
100106
</script>
101107

102108
<template>
103-
<Primitive :as="as" data-slot="root" :class="ui.root({ class: [props.ui?.root, props.class] })">
109+
<Primitive :as="as" :data-orientation="orientation" data-slot="root" :class="ui.root({ class: [props.ui?.root, props.class] })">
104110
<div data-slot="wrapper" :class="ui.wrapper({ class: props.ui?.wrapper })">
105111
<div v-if="label || !!slots.label" data-slot="labelWrapper" :class="ui.labelWrapper({ class: props.ui?.labelWrapper })">
106112
<Label :for="id" data-slot="label" :class="ui.label({ class: props.ui?.label })">

src/theme/form-field.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ export default {
22
slots: {
33
root: '',
44
wrapper: '',
5-
labelWrapper: 'flex content-center items-center justify-between',
5+
labelWrapper: 'flex content-center items-center justify-between gap-1',
66
label: 'block font-medium text-default',
7-
container: 'mt-1 relative',
7+
container: 'relative',
88
description: 'text-muted',
99
error: 'mt-2 text-error',
1010
hint: 'text-muted',
@@ -22,9 +22,18 @@ export default {
2222
true: {
2323
label: `after:content-['*'] after:ms-0.5 after:text-error`
2424
}
25+
},
26+
orientation: {
27+
vertical: {
28+
container: 'mt-1'
29+
},
30+
horizontal: {
31+
root: 'flex justify-between place-items-baseline gap-2'
32+
}
2533
}
2634
},
2735
defaultVariants: {
28-
size: 'md'
36+
size: 'md',
37+
orientation: 'vertical'
2938
}
3039
}

test/components/FormField.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ const FormFieldWrapper = defineComponent({
6161

6262
describe('FormField', () => {
6363
const sizes = Object.keys(theme.variants.size) as any
64+
const orientations = Object.keys(theme.variants.orientation) as any
6465

6566
it.each([
6667
// Props
@@ -70,6 +71,7 @@ describe('FormField', () => {
7071
['with error', { props: { error: 'Username is already taken' } }],
7172
['with hint', { props: { hint: 'Use letters, numbers, and special characters' } }],
7273
...sizes.map((size: string) => [`with size ${size}`, { props: { label: 'Username', description: 'Enter your username', size } }]),
74+
...orientations.map((orientation: string) => [`with orientation ${orientation}`, { props: { label: 'Username', description: 'Enter your username', orientation } }]),
7375
['with as', { props: { as: 'section' } }],
7476
['with class', { props: { class: 'relative' } }],
7577
['with ui', { props: { ui: { label: 'text-highlighted' } } }],

0 commit comments

Comments
 (0)