docs(ComponentExample): automatically read code (#789)

This commit is contained in:
KeJun
2023-10-09 16:44:47 +08:00
committed by GitHub
parent cf93d968af
commit fe348b48c6
35 changed files with 387 additions and 2925 deletions

View File

@@ -10,21 +10,7 @@ links:
Use a `v-model` to make the Input reactive.
::component-example
#default
:input-example
#code
```vue
<script setup>
const value = ref('')
</script>
<template>
<UInput v-model="value" />
</template>
```
::
:component-example{component="input-example"}
### Style
@@ -119,8 +105,8 @@ options:
- name: color
restriction: included
values:
- white
- gray
- white
- gray
excludedProps:
- icon
---
@@ -197,32 +183,7 @@ baseProps:
You can for example create a clearable Input by injecting a [Button](/elements/button) in the `trailing` slot that displays when some text is entered.
::component-example
#default
:input-example-clearable
#code
```vue
<template>
<UInput v-model="q" name="q" placeholder="Search..." icon="i-heroicons-magnifying-glass-20-solid" :ui="{ icon: { trailing: { pointer: '' } } }">
<template #trailing>
<UButton
v-show="q !== ''"
color="gray"
variant="link"
icon="i-heroicons-x-mark-20-solid"
:padded="false"
@click="q = ''"
/>
</template>
</UInput>
</template>
<script setup lang="ts">
const q = ref('')
</script>
```
::
:component-example{component="input-example-clearable"}
::callout{icon="i-heroicons-exclamation-triangle-20-solid"}
As leading and trailing icons are wrapped around a `pointer-events-none` class, if you inject a clickable element in the slot, you need to remove this class to make it clickable by adding `:ui="{ icon: { trailing: { pointer: '' } } }"` to the Input.

View File

@@ -17,54 +17,7 @@ The Form component requires the `validate` and `state` props for form validation
- `message` - the error message to display.
- `path` - the path to the form element matching the `name`.
::component-example
#default
:form-example-basic{class="space-y-4 w-60"}
#code
```vue
<script setup lang="ts">
import type { FormError, FormSubmitEvent } from '@nuxt/ui/dist/runtime/types'
const state = reactive({
email: undefined,
password: undefined
})
const validate = (state: any): FormError[] => {
const errors = []
if (!state.email) errors.push({ path: 'email', message: 'Required' })
if (!state.password) errors.push({ path: 'password', message: 'Required' })
return errors
}
async function submit (event: FormSubmitEvent<any>) {
// Do something with data
console.log(event.data)
}
</script>
<template>
<UForm
:validate="validate"
:state="state"
@submit="submit"
>
<UFormGroup label="Email" name="email">
<UInput v-model="state.email" />
</UFormGroup>
<UFormGroup label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormGroup>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
```
::
:component-example{component="form-example-basic" :componentProps='{"class": "space-y-4 w-60"}'}
## Schema
@@ -72,213 +25,20 @@ You can provide a schema from [Yup](#yup), [Zod](#zod) or [Joi](#joi), [Valibot]
### Yup
::component-example
#default
:form-example-yup{class="space-y-4 w-60"}
#code
```vue
<script setup lang="ts">
import { object, string, InferType } from 'yup'
import type { FormSubmitEvent } from '@nuxt/ui/dist/runtime/types'
const schema = object({
email: string().email('Invalid email').required('Required'),
password: string()
.min(8, 'Must be at least 8 characters')
.required('Required')
})
type Schema = InferType<typeof schema>
const state = reactive({
email: undefined,
password: undefined
})
async function submit (event: FormSubmitEvent<Schema>) {
// Do something with event.data
console.log(event.data)
}
</script>
<template>
<UForm
:schema="schema"
:state="state"
@submit="submit"
>
<UFormGroup label="Email" name="email">
<UInput v-model="state.email" />
</UFormGroup>
<UFormGroup label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormGroup>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
```
::
:component-example{component="form-example-yup" :componentProps='{"class": "space-y-4 w-60"}'}
### Zod
::component-example
#default
:form-example-zod{class="space-y-4 w-60"}
#code
```vue
<script setup lang="ts">
import { z } from 'zod'
import type { FormSubmitEvent } from '@nuxt/ui/dist/runtime/types'
const schema = z.object({
email: z.string().email('Invalid email'),
password: z.string().min(8, 'Must be at least 8 characters')
})
type Schema = z.output<typeof schema>
const state = reactive({
email: undefined,
password: undefined
})
async function submit (event: FormSubmitEvent<Schema>) {
// Do something with data
console.log(event.data)
}
</script>
<template>
<UForm
:schema="schema"
:state="state"
@submit="submit"
>
<UFormGroup label="Email" name="email">
<UInput v-model="state.email" />
</UFormGroup>
<UFormGroup label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormGroup>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
```
::
:component-example{component="form-example-zod" :componentProps='{"class": "space-y-4 w-60"}'}
### Joi
::component-example
#default
:form-example-joi{class="space-y-4 w-60"}
:component-example{component="form-example-joi" :componentProps='{"class": "space-y-4 w-60"}'}
#code
```vue
<script setup lang="ts">
import Joi from 'joi'
import type { FormSubmitEvent } from '@nuxt/ui/dist/runtime/types'
const schema = Joi.object({
email: Joi.string().required(),
password: Joi.string()
.min(8)
.required()
})
const state = reactive({
email: undefined,
password: undefined
})
async function submit (event: FormSubmitEvent<any>) {
// Do something with event.data
console.log(event.data)
}
</script>
<template>
<UForm
:schema="schema"
:state="state"
@submit="submit"
>
<UFormGroup label="Email" name="email">
<UInput v-model="state.email" />
</UFormGroup>
<UFormGroup label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormGroup>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
```
::
### Valibot
::component-example
#default
:form-example-valibot{class="space-y-4 w-60"}
#code
```vue
<script setup lang="ts">
import { string, object, email, minLength, Input } from 'valibot'
import type { FormSubmitEvent } from '@nuxt/ui/dist/runtime/types'
const schema = object({
email: string([email('Invalid email')]),
password: string([minLength(8, 'Must be at least 8 characters')])
})
type Schema = Input<typeof schema>
const state = reactive({
email: undefined,
password: undefined
})
async function submit (event: FormSubmitEvent<Schema>) {
// Do something with event.data
console.log(event.data)
}
</script>
<template>
<UForm
:schema="schema"
:state="state"
@submit="submit"
>
<UFormGroup label="Email" name="email">
<UInput v-model="state.email" />
</UFormGroup>
<UFormGroup label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormGroup>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
```
::
:component-example{component="form-example-valibot" :componentProps='{"class": "space-y-4 w-60"}'}
## Other libraries
@@ -372,10 +132,7 @@ async function submit (event: FormSubmitEvent<any>) {
The Form component automatically triggers validation upon `submit`, `input`, `blur` or `change` events. This ensures that any errors are displayed as soon as the user interacts with the form elements. You can control when validation happens this using the `validate-on` prop.
::component-example
#default
:form-example-elements{class="space-y-4 w-60"}
::
:component-example{component="form-example-elements" :componentProps='{"class": "space-y-4 w-60"}' hiddenCode }
::callout{icon="i-simple-icons-github" to="https://github.com/nuxt/ui/blob/dev/docs/components/content/examples/FormExampleElements.vue"}
Take a look at the component!

View File

@@ -10,21 +10,7 @@ links:
Use a `v-model` to make the Textarea reactive.
::component-example
#default
:textarea-example
#code
```vue
<script setup>
const value = ref('')
</script>
<template>
<UTextarea v-model="value" />
</template>
```
::
:component-example{component="textarea-example"}
### Style

View File

@@ -12,55 +12,13 @@ The Select component is a wrapper around the native `<select>` HTML element. For
Use a `v-model` to make the Select reactive alongside the `options` prop to pass an array of strings or objects.
::component-example
#default
:select-example
#code
```vue
<script setup>
const countries = ['United States', 'Canada', 'Mexico']
const country = ref(countries[0])
</script>
<template>
<USelect v-model="country" :options="countries" />
</template>
```
::
:component-example{component="select-example"}
When using objects, you can configure which field will be used for display through the `option-attribute` prop that defaults to `label` and which field will be used for comparison through the `value-attribute` prop that defaults to `value`.
Adding a `disabled` key to the objects will control the disabled state of the option.
::component-example
#default
:select-example-objects
#code
```vue
<script setup>
const countries = [{
name: 'United States',
value: 'US'
}, {
name: 'Canada',
value: 'CA',
disabled: true
}, {
name: 'Mexico',
value: 'MX'
}]
const country = ref('CA')
</script>
<template>
<USelect v-model="country" :options="countries" option-attribute="name" />
</template>
```
::
:component-example{component="select-example-objects"}
### Style
@@ -176,7 +134,7 @@ options:
restriction: included
values:
- white
- gray
- gray
excludedProps:
- icon
---

View File

@@ -16,142 +16,23 @@ The `SelectMenu` component renders by default a [Select](/forms/select) componen
Like the `Select` component, you can use the `options` prop to pass an array of strings or objects.
::component-example
#default
:select-menu-example-basic{class="w-full lg:w-40"}
#code
```vue
<script setup>
const people = ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
const selected = ref(people[0])
</script>
<template>
<USelectMenu v-model="selected" :options="people" />
</template>
```
::
:component-example{component="select-menu-example-basic" :componentProps='{"class": "w-full lg:w-40"}'}
### Multiple
You can use the `multiple` prop to select multiple values.
::component-example
#default
:select-menu-example-multiple{class="w-full lg:w-40"}
#code
```vue
<script setup>
const people = [...]
const selected = ref([])
</script>
<template>
<USelectMenu v-model="selected" :options="people" multiple placeholder="Select people" />
</template>
```
::
:component-example{component="select-menu-example-multiple" :componentProps='{"class": "w-full lg:w-40"}'}
### Objects
You can pass an array of objects to `options` and either compare on the whole object or use the `by` prop to compare on a specific key. You can configure which field will be used to display the label through the `option-attribute` prop that defaults to `label`.
::component-example
#default
:select-menu-example-objects{class="w-full lg:w-40"}
#code
```vue
<script setup>
const people = [{
id: 'benjamincanac',
label: 'benjamincanac',
href: 'https://github.com/benjamincanac',
target: '_blank',
avatar: { src: 'https://avatars.githubusercontent.com/u/739984?v=4' }
},
{
id: 'Atinux',
label: 'Atinux',
href: 'https://github.com/Atinux',
target: '_blank',
avatar: { src: 'https://avatars.githubusercontent.com/u/904724?v=4' }
},
{
id: 'smarroufin',
label: 'smarroufin',
href: 'https://github.com/smarroufin',
target: '_blank',
avatar: { src: 'https://avatars.githubusercontent.com/u/7547335?v=4' }
},
{
id: 'nobody',
label: 'Nobody',
icon: 'i-heroicons-user-circle'
}]
const selected = ref(people[0])
</script>
<template>
<USelectMenu v-model="selected" :options="people">
<template #label>
<UIcon v-if="selected.icon" :name="selected.icon" class="w-4 h-4" />
<UAvatar v-else-if="selected.avatar" v-bind="selected.avatar" size="3xs" />
{{ selected.label }}
</template>
</USelectMenu>
</template>
```
::
:component-example{component="select-menu-example-objects" :componentProps='{"class": "w-full lg:w-40"}'}
If you only want to select a single object property rather than the whole object as value, you can set the `value-attribute` property. This prop defaults to `null`.
::component-example
#default
:select-menu-example-objects-value-attribute{class="w-full lg:w-40"}
#code
```vue
<script setup>
const people = [{
id: 1,
name: 'Wade Cooper'
}, {
id: 2,
name: 'Arlene Mccoy'
}, {
id: 3,
name: 'Devon Webb'
}, {
id: 4,
name: 'Tom Cook'
}]
const selected = ref(people[0].id)
const current = computed(() => people.find(person => person.id === selected.value))
</script>
<template>
<USelectMenu
v-model="selected"
:options="people"
placeholder="Select people"
value-attribute="id"
option-attribute="name"
>
<template #label>
{{ current.name }}
</template>
</USelectMenu>
</template>
```
::
:component-example{component="select-menu-example-objects-value-attribute" :componentProps='{"class": "w-full lg:w-40"}'}
### Icon
@@ -200,33 +81,7 @@ Pass a function to the `searchable` prop to customize the search behavior and fi
Use the `debounce` prop to adjust the delay of the function.
::component-example
#default
:select-menu-example-async-search{class="w-full lg:w-40"}
#code
```vue
<script setup>
const search = async (q) => {
const users = await $fetch('https://jsonplaceholder.typicode.com/users', { params: { q } })
return users.map(user => ({ id: user.id, label: user.name, suffix: user.email })).filter(Boolean)
}
const selected = ref([])
</script>
<template>
<USelectMenu
v-model="selected"
:searchable="search"
placeholder="Search for a user..."
multiple
by="id"
/>
</template>
```
::
:component-example{component="select-menu-example-async-search" :componentProps='{"class": "w-full lg:w-40"}'}
### Create option
@@ -234,99 +89,7 @@ Use the `creatable` prop to enable the creation of new options when the search d
Try to search for something that doesn't exist in the example below.
::component-example
#default
:select-menu-example-creatable{class="w-full lg:w-40"}
#code
```vue
<script setup>
const options = ref([
{ id: 1, name: 'bug', color: 'd73a4a' },
{ id: 2, name: 'documentation', color: '0075ca' },
{ id: 3, name: 'duplicate', color: 'cfd3d7' },
{ id: 4, name: 'enhancement', color: 'a2eeef' },
{ id: 5, name: 'good first issue', color: '7057ff' },
{ id: 6, name: 'help wanted', color: '008672' },
{ id: 7, name: 'invalid', color: 'e4e669' },
{ id: 8, name: 'question', color: 'd876e3' },
{ id: 9, name: 'wontfix', color: 'ffffff' }
])
const selected = ref([])
const labels = computed({
get: () => selected.value,
set: async (labels) => {
const promises = labels.map(async (label) => {
if (label.id) {
return label
}
// In a real app, you would make an API call to create the label
const response = {
name: label.name,
color: generateColorFromString(label.name)
}
options.value.push(response)
return response
})
selected.value = await Promise.all(promises)
}
})
// Look at the component example to see how this is used
function generateColorFromString (str) {
// ...
}
</script>
<template>
<USelectMenu
v-model="labels"
by="id"
name="labels"
:options="options"
option-attribute="name"
multiple
searchable
creatable
>
<template #label>
<template v-if="labels.length">
<span class="flex items-center -space-x-1">
<span v-for="label of labels" :key="label.id" class="flex-shrink-0 w-2 h-2 mt-px rounded-full" :style="{ background: `#${label.color}` }" />
</span>
<span>{{ labels.length }} label{{ labels.length > 1 ? 's' : '' }}</span>
</template>
<template v-else>
<span class="text-gray-500 dark:text-gray-400 truncate">Select labels</span>
</template>
</template>
<template #option="{ option }">
<span
class="flex-shrink-0 w-2 h-2 mt-px rounded-full"
:style="{ background: `#${option.color}` }"
/>
<span class="truncate">{{ option.name }}</span>
</template>
<template #option-create="{ option }">
<span class="flex-shrink-0">New label:</span>
<span
class="flex-shrink-0 w-2 h-2 mt-px rounded-full -mx-1"
:style="{ background: `#${generateColorFromString(option.name)}` }"
/>
<span class="block truncate">{{ option.name }}</span>
</template>
</USelectMenu>
</template>
```
::
:component-example{component="select-menu-example-creatable" :componentProps='{"class": "w-full lg:w-40"}'}
## Slots
@@ -334,125 +97,25 @@ function generateColorFromString (str) {
You can override the `#label` slot and handle the display yourself.
::component-example
#default
:select-menu-example-multiple-slot{class="w-full lg:w-40"}
#code
```vue
<script setup>
const people = [...]
const selected = ref([])
</script>
<template>
<USelectMenu v-model="selected" :options="people" multiple>
<template #label>
<span v-if="selected.length" class="truncate">{{ selected.join(', ') }}</span>
<span v-else>Select people</span>
</template>
</USelectMenu>
</template>
```
::
:component-example{component="select-menu-example-multiple-slot" :componentProps='{"class": "w-full lg:w-40"}'}
### `default`
You can also override the `#default` slot entirely.
::component-example
#default
:select-menu-example-button{class="w-full lg:w-40"}
#code
```vue
<script setup>
const people = [...]
const selected = ref(people[3])
</script>
<template>
<USelectMenu v-slot="{ open }" v-model="selected" :options="people">
<UButton color="gray" class="flex-1 justify-between">
{{ selected }}
<UIcon name="i-heroicons-chevron-right-20-solid" class="w-5 h-5 transition-transform" :class="[open && 'transform rotate-90']" />
</UButton>
</USelectMenu>
</template>
```
::
:component-example{component="select-menu-example-button" :componentProps='{"class": "w-full lg:w-40"}'}
### `option`
Use the `#option` slot to customize the option content. You will have access to the `option`, `active` and `selected` properties in the slot scope.
::component-example
#default
:select-menu-example-option-slot{class="w-full lg:w-40"}
#code
```vue
<script setup>
const people = [
{ name: 'Wade Cooper', online: true },
{ name: 'Arlene Mccoy', online: false },
{ name: 'Devon Webb', online: false },
{ name: 'Tom Cook', online: true },
{ name: 'Tanya Fox', online: false },
{ name: 'Hellen Schmidt', online: true },
{ name: 'Caroline Schultz', online: true },
{ name: 'Mason Heaney', online: false },
{ name: 'Claudie Smitham', online: true },
{ name: 'Emil Schaefer', online: false }
]
const selected = ref(people[3])
</script>
<template>
<USelectMenu v-model="selected" :options="people" option-attribute="name">
<template #label>
<span :class="[selected.online ? 'bg-green-400' : 'bg-gray-200', 'inline-block h-2 w-2 flex-shrink-0 rounded-full']" aria-hidden="true" />
<span class="truncate">{{ selected.name }}</span>
</template>
<template #option="{ option: person }">
<span :class="[person.online ? 'bg-green-400' : 'bg-gray-200', 'inline-block h-2 w-2 flex-shrink-0 rounded-full']" aria-hidden="true" />
<span class="truncate">{{ person.name }}</span>
</template>
</USelectMenu>
</template>
```
::
:component-example{component="select-menu-example-option-slot" :componentProps='{"class": "w-full lg:w-40"}'}
### `option-empty`
Use the `#option-empty` slot to customize the content displayed when the `searchable` prop is `true` and there is no options. You will have access to the `query` property in the slot scope.
::component-example
#default
:select-menu-example-option-empty-slot{class="w-full lg:w-40"}
#code
```vue
<script setup>
const people = ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
const selected = ref(people[0])
</script>
<template>
<USelectMenu v-model="selected" :options="people" searchable>
<template #option-empty="{ query }">
<q>{{ query }}</q> not found
</template>
</USelectMenu>
</template>
```
::
:component-example{component="select-menu-example-option-empty-slot" :componentProps='{"class": "w-full lg:w-40"}'}
### `option-create`

View File

@@ -10,21 +10,7 @@ links:
Use a `v-model` to make the Checkbox reactive.
::component-example
#default
:checkbox-example
#code
```vue
<script setup>
const selected = ref(true)
</script>
<template>
<UCheckbox v-model="selected" name="notifications" label="Notifications" />
</template>
```
::
:component-example{component="checkbox-example"}
### Label

View File

@@ -10,35 +10,7 @@ links:
Use a `v-model` to make the Radio reactive.
::component-example
#default
:radio-example
#code
```vue
<script setup>
const methods = [{
name: 'email',
value: 'email',
label: 'Email'
}, {
name: 'sms',
value: 'sms',
label: 'Phone (SMS)'
}, {
name: 'push',
value: 'push',
label: 'Push notification'
}]
const selected = ref('sms')
</script>
<template>
<URadio v-for="method of methods" :key="method.name" v-model="selected" v-bind="method" />
</template>
```
::
:component-example{component="radio-example"}
### Label

View File

@@ -13,21 +13,7 @@ links:
Use a `v-model` to make the Toggle reactive.
::component-example
#default
:toggle-example
#code
```vue
<script setup>
const selected = ref(false)
</script>
<template>
<UToggle v-model="selected" />
</template>
```
::
### Style

View File

@@ -10,21 +10,7 @@ links:
Use a `v-model` to make the Range reactive.
::component-example
#default
:range-example
#code
```vue
<script setup>
const value = ref(50)
</script>
<template>
<URange v-model="value" />
</template>
```
::
:component-example{component="range-example"}
### Style

View File

@@ -112,23 +112,7 @@ Use the `error` prop to display an error message below the form element.
When used together with the `help` prop, the `error` prop will take precedence.
::component-example
#default
:form-group-error-example
#code
```vue
<template>
<UFormGroup v-slot="{ error }" label="Email" :error="!email && 'You must enter an email'" help="This is a nice email!">
<UInput v-model="email" type="email" placeholder="Enter email" :trailing-icon="error && 'i-heroicons-exclamation-triangle-20-solid'" />
</UFormGroup>
</template>
<script setup lang="ts">
const email = ref('')
</script>
```
::
:component-example{component="form-group-error-example"}
::callout{icon="i-heroicons-light-bulb"}
The `error` prop will automatically set the `color` prop of the form element to `red`.
@@ -274,30 +258,7 @@ props:
Use the `#error` slot to set the custom content for error.
::component-example
#default
:form-group-error-slot-example{class="w-60"}
#code
```vue
<template>
<UFormGroup label="Email" :error="!email && 'You must enter an email'" help="This is a nice email!">
<template #default="{ error }">
<UInput v-model="email" type="email" placeholder="Enter email" :trailing-icon="error ? 'i-heroicons-exclamation-triangle-20-solid' : undefined" />
</template>
<template #error="{ error }">
<UAlert v-if="error" icon="i-heroicons-exclamation-triangle-20-solid" :title="error" color="red" />
<UAlert v-else icon="i-heroicons-check-circle-20-solid" title="Your email is valid" color="green" />
</template>
</UFormGroup>
</template>
<script setup lang="ts">
const email = ref('')
</script>
```
::
:component-example{component="form-group-error-slot-example" :componentProps='{"class": "w-60"}'}
## Props