Skip to content

Commit

Permalink
docs(form): update (#2167)
Browse files Browse the repository at this point in the history
Co-authored-by: Benjamin Canac <[email protected]>
  • Loading branch information
romhml and benjamincanac authored Sep 12, 2024
1 parent 1667e5a commit 319fce1
Show file tree
Hide file tree
Showing 13 changed files with 698 additions and 11 deletions.
58 changes: 54 additions & 4 deletions docs/app/components/content/ComponentExample.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,26 @@ const props = withDefaults(defineProps<{
* @defaultValue true
*/
preview?: boolean
/**
* Whether to show the source code
* @defaultValue true
*/
source?: boolean
/**
* A list of variable props to link to the component.
*/
options?: Array<{
name: string
label: string
items: any[]
default: any
multiple: boolean
}>
}>(), {
preview: true
preview: true,
source: true
})
const { $prettier } = useNuxtApp()
Expand Down Expand Up @@ -71,16 +89,48 @@ const { data: ast } = await useAsyncData(`component-example-${camelName}`, async
return parseMarkdown(formatted)
}, { watch: [code] })
const optionsValues = ref(props.options?.reduce((acc, option) => {
if (option.name) {
acc[option.name] = option.default
}
return acc
}, {}))
</script>

<template>
<div class="my-5">
<div v-if="preview">
<div class="flex border border-b-0 border-gray-300 dark:border-gray-700 relative p-4 rounded-t-md z-[1]" :class="[props.class]">
<component :is="camelName" v-bind="componentProps" />
<div class="border border-gray-300 dark:border-gray-700 relative z-[1]" :class="[props.class, { 'border-b-0 rounded-t-md': props.source, 'rounded-md': !props.source }]">
<div v-if="props.options?.length" class="flex gap-4 p-4 border-b border-gray-300 dark:border-gray-700">
<UFormField
v-for="option in props.options"
:key="option.name"
:label="option.label"
:name="option.name"
size="sm"
class="inline-flex ring ring-gray-300 dark:ring-gray-700 rounded"
:ui="{
wrapper: 'bg-gray-50 dark:bg-gray-800/50 rounded-l flex border-r border-gray-300 dark:border-gray-700',
label: 'text-gray-500 dark:text-gray-400 px-2 py-1.5',
container: 'mt-0'
}"
>
<USelectMenu
v-model="optionsValues[option.name]"
:items="option.items"
class="rounded rounded-l-none w-40"
multiple
:ui="{ itemLeadingChip: 'size-2' }"
/>
</UFormField>
</div>
<div class="p-4">
<component :is="camelName" v-bind="{ ...componentProps, ...optionsValues }" />
</div>
</div>
</div>

<MDCRenderer v-if="ast" :body="ast.body" :data="ast.data" class="[&_pre]:!rounded-t-none [&_div.my-5]:!mt-0" />
<MDCRenderer v-if="ast && props.source" :body="ast.body" :data="ast.data" class="[&_pre]:!rounded-t-none [&_div.my-5]:!mt-0" />
</div>
</template>
37 changes: 37 additions & 0 deletions docs/app/components/content/examples/form/FormExampleBasic.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<script setup lang="ts">
import type { FormError, FormSubmitEvent } from '#ui/types'
const state = reactive({
email: undefined,
password: undefined
})
const validate = (state: any): FormError[] => {
const errors = []
if (!state.email) errors.push({ name: 'email', message: 'Required' })
if (!state.password) errors.push({ name: 'password', message: 'Required' })
return errors
}
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<any>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'green' })
console.log(event.data)
}
</script>

<template>
<UForm :validate="validate" :state="state" class="space-y-4" @submit="onSubmit">
<UFormField label="Email" name="email">
<UInput v-model="state.email" />
</UFormField>

<UFormField label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormField>

<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
115 changes: 115 additions & 0 deletions docs/app/components/content/examples/form/FormExampleElements.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<script setup lang="ts">
import { z } from 'zod'
import type { FormSubmitEvent, Form } from '@nuxt/ui'
const schema = z.object({
input: z.string().min(10),
inputMenu: z.any().refine(option => option?.value === 'option-2', {
message: 'Select Option 2'
}),
inputMenuMultiple: z.any().refine(values => !!values?.find((option: any) => option.value === 'option-2'), {
message: 'Include Option 2'
}),
textarea: z.string().min(10),
select: z.string().refine(value => value === 'option-2', {
message: 'Select Option 2'
}),
selectMenu: z.any().refine(option => option?.value === 'option-2', {
message: 'Select Option 2'
}),
selectMenuMultiple: z.any().refine(values => !!values?.find((option: any) => option.value === 'option-2'), {
message: 'Include Option 2'
}),
switch: z.boolean().refine(value => value === true, {
message: 'Toggle me'
}),
checkbox: z.boolean().refine(value => value === true, {
message: 'Check me'
}),
radioGroup: z.string().refine(value => value === 'option-2', {
message: 'Select Option 2'
}),
slider: z.number().max(20, { message: 'Must be less than 20' })
})
type Schema = z.output<typeof schema>
const state = reactive<Partial<Schema>>({})
const form = ref<Form<Schema>>()
const items = [
{ label: 'Option 1', value: 'option-1' },
{ label: 'Option 2', value: 'option-2' },
{ label: 'Option 3', value: 'option-3' }
]
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<any>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'green' })
console.log(event.data)
}
</script>

<template>
<UForm ref="form" :state="state" :schema="schema" @submit="onSubmit">
<div class="grid grid-cols-3 gap-4">
<UFormField label="Input" name="input">
<UInput v-model="state.input" placeholder="[email protected]" class="w-40" />
</UFormField>

<div class="flex flex-col gap-4">
<UFormField name="switch">
<USwitch v-model="state.switch" label="Switch me" />
</UFormField>

<UFormField name="checkbox">
<UCheckbox v-model="state.checkbox" label="Check me" />
</UFormField>
</div>

<UFormField name="slider" label="Slider">
<USlider v-model="state.slider" />
</UFormField>

<UFormField name="select" label="Select">
<USelect v-model="state.select" :items="items" />
</UFormField>

<UFormField name="selectMenu" label="Select Menu">
<USelectMenu v-model="state.selectMenu" :items="items" />
</UFormField>

<UFormField name="selectMenuMultiple" label="Select Menu (Multiple)">
<USelectMenu v-model="state.selectMenuMultiple" multiple :items="items" />
</UFormField>

<UFormField name="inputMenu" label="Input Menu">
<UInputMenu v-model="state.inputMenu" :items="items" />
</UFormField>

<UFormField name="inputMenuMultiple" label="Input Menu (Multiple)">
<UInputMenu v-model="state.inputMenuMultiple" multiple :items="items" />
</UFormField>

<span />

<UFormField label="Textarea" name="textarea">
<UTextarea v-model="state.textarea" />
</UFormField>

<UFormField name="radioGroup">
<URadioGroup v-model="state.radioGroup" legend="Radio group" :items="items" />
</UFormField>
</div>

<div class="flex gap-2 mt-8">
<UButton color="gray" type="submit" :disabled="form?.disabled">
Submit
</UButton>

<UButton color="gray" variant="outline" :disabled="form?.disabled" @click="form?.clear()">
Clear
</UButton>
</div>
</UForm>
</template>
38 changes: 38 additions & 0 deletions docs/app/components/content/examples/form/FormExampleJoi.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<script setup lang="ts">
import Joi from 'joi'
import type { FormSubmitEvent } from '#ui/types'
const schema = Joi.object({
email: Joi.string().required(),
password: Joi.string()
.min(8)
.required()
})
const state = reactive({
email: undefined,
password: undefined
})
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<any>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'green' })
console.log(event.data)
}
</script>

<template>
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
<UFormField label="Email" name="email">
<UInput v-model="state.email" />
</UFormField>

<UFormField label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormField>

<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
54 changes: 54 additions & 0 deletions docs/app/components/content/examples/form/FormExampleNested.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<script setup lang="ts">
import { z } from 'zod'
import type { FormSubmitEvent } from '@nuxt/ui'
const schema = z.object({
name: z.string().min(2),
news: z.boolean()
})
type Schema = z.output<typeof schema>
const nestedSchema = z.object({
email: z.string().email()
})
type NestedSchema = z.output<typeof nestedSchema>
const state = reactive<Partial<Schema & NestedSchema>>({ })
const toast = useToast()
async function onSubmit(event: FormSubmitEvent<any>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'green' })
console.log(event.data)
}
</script>

<template>
<UForm
:state="state"
:schema="schema"
class="gap-4 flex flex-col w-60"
@submit="(event) => onSubmit(event)"
>
<UFormField label="Name" name="name">
<UInput v-model="state.name" placeholder="John Lennon" />
</UFormField>

<div>
<UCheckbox v-model="state.news" name="news" label="Register to our newsletter" />
</div>

<UForm v-if="state.news" :state="state" :schema="nestedSchema">
<UFormField label="Email" name="email">
<UInput v-model="state.email" placeholder="[email protected]" />
</UFormField>
</UForm>

<div>
<UButton type="submit">
Submit
</UButton>
</div>
</UForm>
</template>
Loading

0 comments on commit 319fce1

Please sign in to comment.