docs: clean content components

This commit is contained in:
Benjamin Canac
2024-04-17 16:27:27 +02:00
parent 1b941ede36
commit 3cbcc0f09a
184 changed files with 0 additions and 5510 deletions

View File

@@ -1,28 +0,0 @@
<template>
<ClientOnly>
<UButton
:icon="isDark ? 'i-heroicons-moon-20-solid' : 'i-heroicons-sun-20-solid'"
color="gray"
variant="ghost"
aria-label="Theme"
@click="isDark = !isDark"
/>
<template #fallback>
<div class="w-8 h-8" />
</template>
</ClientOnly>
</template>
<script setup lang="ts">
const colorMode = useColorMode()
const isDark = computed({
get () {
return colorMode.value === 'dark'
},
set () {
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
}
})
</script>

View File

@@ -1,294 +0,0 @@
<template>
<div>
<div v-if="propsToSelect.length" class="relative flex border border-gray-200 dark:border-gray-700 rounded-t-md overflow-hidden not-prose">
<div v-for="prop in propsToSelect" :key="prop.name" class="flex flex-col gap-0.5 justify-between py-1.5 font-medium bg-gray-50 dark:bg-gray-800 border-r border-r-gray-200 dark:border-r-gray-700">
<label :for="`prop-${prop.name}`" class="block text-xs px-2.5 font-medium text-gray-400 dark:text-gray-500 -my-px">{{ prop.label }}</label>
<UCheckbox
v-if="prop.type.startsWith('boolean')"
v-model="componentProps[prop.name]"
:name="`prop-${prop.name}`"
tabindex="-1"
class="justify-center"
/>
<USelectMenu
v-else-if="prop.options.length && prop.name !== 'label'"
v-model="componentProps[prop.name]"
:options="prop.options"
:name="`prop-${prop.name}`"
variant="none"
class="inline-flex"
:ui-menu="{ width: 'w-32 !-mt-px', rounded: 'rounded-t-none' }"
select-class="py-0"
tabindex="-1"
:popper="{ strategy: 'fixed', placement: 'bottom-start' }"
/>
<UInput
v-else
:model-value="componentProps[prop.name]"
:type="prop.type === 'number' ? 'number' : 'text'"
:name="`prop-${prop.name}`"
variant="none"
autocomplete="off"
input-class="py-0"
tabindex="-1"
@update:model-value="val => componentProps[prop.name] = prop.type === 'number' ? Number(val) : val"
/>
</div>
</div>
<div class="flex border border-b-0 border-gray-200 dark:border-gray-700 relative not-prose" :class="[{ 'p-4': padding }, propsToSelect.length ? 'border-t-0' : 'rounded-t-md', backgroundClass, extraClass]">
<component :is="name" v-model="vModel" v-bind="fullProps" :class="componentClass">
<ContentSlot v-if="$slots.default" :use="$slots.default" />
<template v-for="slot in Object.keys(slots || {})" :key="slot" #[slot]>
<ContentSlot :name="slot" unwrap="p" />
</template>
</component>
</div>
<ContentRenderer v-if="!previewOnly" :value="ast" class="[&>div>pre]:!rounded-t-none [&>div>pre]:!mt-0" />
</div>
</template>
<script setup lang="ts">
import { transformContent } from '@nuxt/content/transformers'
import { upperFirst, camelCase, kebabCase } from 'scule'
import { useShikiHighlighter } from '~/composables/useShikiHighlighter'
// eslint-disable-next-line vue/no-dupe-keys
const props = defineProps({
slug: {
type: String,
default: null
},
padding: {
type: Boolean,
default: true
},
props: {
type: Object,
default: () => ({})
},
code: {
type: String,
default: null
},
slots: {
type: Object,
default: null
},
baseProps: {
type: Object,
default: () => ({})
},
ui: {
type: Object,
default: () => ({})
},
excludedProps: {
type: Array,
default: () => []
},
options: {
type: Array as PropType<{ name: string; values: string[]; restriction: 'expected' | 'included' | 'excluded' | 'only' }[]>,
default: () => []
},
backgroundClass: {
type: String,
default: 'bg-white dark:bg-gray-900'
},
extraClass: {
type: String,
default: ''
},
previewOnly: {
type: Boolean,
default: false
},
componentClass: {
type: String,
default: ''
},
ignoreVModel: {
type: Boolean,
default: false
}
})
// eslint-disable-next-line vue/no-dupe-keys
const baseProps = reactive({ ...props.baseProps })
const componentProps = reactive({ ...props.props })
const { $prettier } = useNuxtApp()
const appConfig = useAppConfig()
const route = useRoute()
const highlighter = useShikiHighlighter()
let name = props.slug || `U${upperFirst(camelCase(route.params.slug[route.params.slug.length - 1]))}`
// TODO: Remove once merged on `main` branch
if (['AvatarGroup', 'ButtonGroup', 'MeterGroup'].includes(name)) {
name = `U${name}`
}
if (['avatar-group', 'button-group', 'radio'].includes(name)) {
name = `U${upperFirst(camelCase(name))}`
}
const meta = await fetchComponentMeta(name)
// Computed
const fullProps = computed(() => ({ ...componentProps, ...baseProps }))
const vModel = computed({
get: () => baseProps.modelValue,
set: (value) => {
baseProps.modelValue = value
}
})
const generateOptions = (key: string, schema: { kind: string, schema: [], type: string }) => {
let options = []
const optionItem = props?.options?.find(item => item?.name === key) || null
const types = schema?.type?.split('|')?.map(item => item.trim()?.replaceAll('"', '')) || []
const hasIgnoredTypes = types?.every(item => ['string', 'number', 'boolean', 'array', 'object', 'Function'].includes(item))
if (key.toLowerCase().endsWith('color')) {
options = [...appConfig.ui.colors]
}
if (key.toLowerCase() === 'size' && schema?.schema?.length > 0) {
const baseSizeOrder = { 'xs': 1, 'sm': 2, 'md': 3, 'lg': 4, 'xl': 5 }
schema.schema.sort((a: string, b: string) => {
const aBase = a.match(/[a-zA-Z]+/)[0].toLowerCase()
const bBase = b.match(/[a-zA-Z]+/)[0].toLowerCase()
const aNum = parseInt(a.match(/\d+/)?.[0]) || 1
const bNum = parseInt(b.match(/\d+/)?.[0]) || 1
if (aBase === bBase) {
return aBase === 'xs' ? bNum - aNum : aNum - bNum
}
return baseSizeOrder[aBase] - baseSizeOrder[bBase]
})
}
if (schema?.schema?.length > 0 && schema?.kind === 'enum' && !hasIgnoredTypes && optionItem?.restriction !== 'only') {
options = schema.schema.filter(option => typeof option === 'string').map((option: string) => option.replaceAll('"', ''))
}
if (optionItem?.restriction === 'only') {
options = optionItem.values
}
if (optionItem?.restriction === 'expected') {
options = options.filter(item => optionItem.values.includes(item))
}
if (optionItem?.restriction === 'included') {
options = [...options, ...optionItem.values]
}
if (optionItem?.restriction === 'excluded') {
options = options.filter(item => !optionItem.values.includes(item))
}
return options
}
const propsToSelect = computed(() => Object.keys(componentProps).map((key) => {
if (props.excludedProps.includes(key)) {
return null
}
const prop = meta?.meta?.props?.find((prop: any) => prop.name === key)
const schema = prop?.schema || {}
const options = generateOptions(key, schema)
return {
type: prop?.type || 'string',
name: key,
label: key === 'modelValue' ? 'value' : camelCase(key),
options
}
}).filter(Boolean))
// eslint-disable-next-line vue/no-dupe-keys
const code = computed(() => {
let code = `\`\`\`html
<template>
<${name}`
for (const [key, value] of Object.entries(fullProps.value)) {
if (value === 'undefined' || value === null) {
continue
}
if (key === 'modelValue' && props.ignoreVModel) {
continue
}
code += ` ${(typeof value === 'boolean' && (value !== true || key === 'modelValue')) || typeof value === 'object' || typeof value === 'number' ? ':' : ''}${key === 'modelValue' ? 'model-value' : kebabCase(key)}${typeof value === 'boolean' && !!value && key !== 'modelValue' ? '' : `="${typeof value === 'object' ? renderObject(value) : value}"`}`
}
if (props.slots) {
code += `>
${Object.entries(props.slots).map(([key, value]) => `<template #${key}>
${value}
</template>`).join('\n ')}
</${name}>`
} else if (props.code) {
const lineBreaks = (props.code.match(/\n/g) || []).length
if (lineBreaks > 1) {
code += `>
${props.code}</${name}>`
} else {
code += `>${props.code.endsWith('>') ? `${props.code}\n` : props.code}</${name}>`
}
} else {
code += ' />'
}
code += `\n</template>
\`\`\`
`
return code
})
function renderObject (obj: any) {
if (Array.isArray(obj)) {
return `[${obj.map(renderObject).join(', ')}]`
}
if (typeof obj === 'object') {
return `{ ${Object.entries(obj).map(([key, value]) => `${key}: ${renderObject(value)}`).join(', ')} }`
}
if (typeof obj === 'string') {
return `'${obj}'`
}
return obj
}
const { data: ast } = await useAsyncData(
`${name}-ast-${JSON.stringify({ props: componentProps, slots: props.slots, code: props.code })}`,
async () => {
let formatted = ''
try {
formatted = await $prettier.format(code.value) || code.value
} catch (error) {
formatted = code.value
}
return transformContent('content:_markdown.md', formatted, {
markdown: {
highlight: {
highlighter,
theme: {
light: 'material-theme-lighter',
default: 'material-theme',
dark: 'material-theme-palenight'
}
}
}
})
}, { watch: [code] })
</script>

View File

@@ -1,95 +0,0 @@
<template>
<div class="[&>div>pre]:!rounded-t-none [&>div>pre]:!mt-0">
<div
class="flex border border-gray-200 dark:border-gray-700 relative rounded-t-md"
:class="[{ 'p-4': padding, 'rounded-b-md': !hasCode, 'border-b-0': hasCode, 'not-prose': !prose }, backgroundClass, extraClass]"
>
<template v-if="component">
<iframe v-if="iframe" :src="`/examples/${component}`" v-bind="iframeProps" :class="backgroundClass" class="w-full" />
<component :is="camelName" v-else v-bind="componentProps" :class="componentClass" />
</template>
<ContentSlot v-if="$slots.default" :use="$slots.default" />
</div>
<template v-if="hasCode">
<slot v-if="$slots.code" name="code" />
<ContentRenderer v-else :value="ast" class="[&>div>pre]:!rounded-t-none [&>div>pre]:!mt-0" />
</template>
</div>
</template>
<script setup lang="ts">
import { camelCase } from 'scule'
import { fetchContentExampleCode } from '~/composables/useContentExamplesCode'
import { transformContent } from '@nuxt/content/transformers'
import { useShikiHighlighter } from '~/composables/useShikiHighlighter'
const props = defineProps({
component: {
type: String,
default: null
},
componentClass: {
type: String,
default: ''
},
componentProps: {
type: Object,
default: () => ({})
},
hiddenCode: {
type: Boolean,
default: false
},
padding: {
type: Boolean,
default: true
},
prose: {
type: Boolean,
default: false
},
iframe: {
type: Boolean,
default: false
},
iframeProps: {
type: Object,
default: () => ({})
},
backgroundClass: {
type: String,
default: 'bg-white dark:bg-gray-900'
},
extraClass: {
type: String,
default: ''
}
})
let component = props.component
// TODO: Remove once merged on `main` branch
if (['command-palette-theme-algolia', 'command-palette-theme-raycast', 'vertical-navigation-theme-tailwind', 'pagination-theme-rounded'].includes(component)) {
component = component.replace('-theme', '-example-theme')
}
const instance = getCurrentInstance()
const camelName = camelCase(component)
const data = await fetchContentExampleCode(camelName)
const highlighter = useShikiHighlighter()
const hasCode = computed(() => !props.hiddenCode && (data?.code || instance.slots.code))
const { data: ast } = await useAsyncData(`content-example-${camelName}-ast`, () => transformContent('content:_markdown.md', `\`\`\`vue\n${data?.code ?? ''}\n\`\`\``, {
markdown: {
highlight: {
highlighter,
theme: {
light: 'material-theme-lighter',
default: 'material-theme',
dark: 'material-theme-palenight'
}
}
}
}))
</script>

View File

@@ -1,44 +0,0 @@
<template>
<ContentRenderer :value="ast" />
</template>
<script setup lang="ts">
import { transformContent } from '@nuxt/content/transformers'
import { upperFirst, camelCase } from 'scule'
import json5 from 'json5'
import { useShikiHighlighter } from '~/composables/useShikiHighlighter'
import * as config from '#ui/ui.config'
const props = defineProps({
slug: {
type: String,
default: null
}
})
const route = useRoute()
const highlighter = useShikiHighlighter()
// eslint-disable-next-line vue/no-dupe-keys
const slug = props.slug || route.params.slug[route.params.slug.length - 1]
const camelName = camelCase(slug)
const name = `U${upperFirst(camelName)}`
const preset = config[camelName]
const { data: ast } = await useAsyncData(`${name}-preset`, () => transformContent('content:_markdown.md', `
\`\`\`yml
${json5.stringify(preset, null, 2)}
\`\`\`\
`, {
markdown: {
highlight: {
highlighter,
theme: {
light: 'material-theme-lighter',
default: 'material-theme',
dark: 'material-theme-palenight'
}
}
}
}))
</script>

View File

@@ -1,32 +0,0 @@
<template>
<div>
<FieldGroup>
<ComponentPropsField v-for="prop in meta?.meta?.props" :key="prop.name" :prop="prop" />
</FieldGroup>
</div>
</template>
<script setup lang="ts">
import { upperFirst, camelCase } from 'scule'
const props = defineProps({
slug: {
type: String,
default: null
}
})
const route = useRoute()
let name = props.slug || `U${upperFirst(camelCase(route.params.slug[route.params.slug.length - 1]))}`
// TODO: Remove once merged on `main` branch
if (['AvatarGroup', 'ButtonGroup', 'MeterGroup'].includes(name)) {
name = `U${name}`
}
if (['avatar-group', 'button-group', 'radio'].includes(name)) {
name = `U${upperFirst(camelCase(name))}`
}
const meta = await fetchComponentMeta(name)
</script>

View File

@@ -1,46 +0,0 @@
<template>
<Field v-bind="prop">
<code v-if="prop.default" class="leading-6">{{ prop.default }}</code>
<p v-if="prop.description">
{{ prop.description }}
</p>
<Collapsible v-if="prop.schema?.kind === 'array' && prop.schema?.schema?.filter(schema => schema.kind === 'object').length">
<FieldGroup v-for="schema in prop.schema.schema" :key="schema.name">
<ComponentPropsField v-for="subProp in Object.values(schema.schema)" :key="(subProp as any).name" :prop="subProp" />
</FieldGroup>
</Collapsible>
<Collapsible v-else-if="prop.schema?.kind === 'array' && prop.schema?.schema?.filter(schema => schema.kind === 'array').length">
<FieldGroup v-for="schema in prop.schema.schema" :key="schema.name">
<template v-for="subSchema in schema.schema" :key="subSchema.name">
<ComponentPropsField v-for="subProp in Object.values(subSchema.schema)" :key="(subProp as any).name" :prop="subProp" />
</template>
</FieldGroup>
</Collapsible>
<Collapsible v-else-if="prop.schema?.kind === 'object' && prop.schema.type !== 'Function' && Object.values(prop.schema.schema)?.length">
<FieldGroup>
<ComponentPropsField v-for="subProp in Object.values(prop.schema.schema)" :key="(subProp as any).name" :prop="subProp" />
</FieldGroup>
</Collapsible>
<div v-else-if="prop.schema?.kind === 'enum' && prop.schema.type !== 'boolean' && startsWithCapital(prop.schema.type) && !prop.schema.type.startsWith(prop.schema.schema[0])" class="flex items-center flex-wrap gap-1 -my-1">
<code v-for="value in prop.schema.schema.filter(value => typeof value === 'string')" :key="value" class="whitespace-pre-wrap break-words leading-6">{{ value }}</code>
</div>
</Field>
</template>
<script setup lang="ts">
defineProps({
prop: {
type: Object as PropType<any>,
default: () => ({})
}
})
function startsWithCapital (word) {
if (word.charAt(0).startsWith('"')) {
return false
}
return word.charAt(0) === word.charAt(0).toUpperCase()
}
</script>

View File

@@ -1,24 +0,0 @@
<template>
<div>
<FieldGroup>
<Field v-for="slot in meta?.meta.slots" :key="slot.name" v-bind="slot" />
</FieldGroup>
</div>
</template>
<script setup lang="ts">
import { upperFirst, camelCase } from 'scule'
const props = defineProps({
slug: {
type: String,
default: null
}
})
const route = useRoute()
const name = props.slug || `U${upperFirst(camelCase(route.params.slug[route.params.slug.length - 1]))}`
const meta = await fetchComponentMeta(name)
</script>

View File

@@ -1,33 +0,0 @@
<script setup lang="ts">
const items = [{
label: 'Getting Started',
icon: 'i-heroicons-information-circle',
defaultOpen: true,
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}, {
label: 'Installation',
icon: 'i-heroicons-arrow-down-tray',
disabled: true,
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}, {
label: 'Theming',
icon: 'i-heroicons-eye-dropper',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}, {
label: 'Layouts',
icon: 'i-heroicons-rectangle-group',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}, {
label: 'Components',
icon: 'i-heroicons-square-3-stack-3d',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}, {
label: 'Utilities',
icon: 'i-heroicons-wrench-screwdriver',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}]
</script>
<template>
<UAccordion :items="items" />
</template>

View File

@@ -1,52 +0,0 @@
<script setup lang="ts">
const items = [{
label: 'Getting Started',
icon: 'i-heroicons-information-circle',
defaultOpen: true,
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}, {
label: 'Installation',
icon: 'i-heroicons-arrow-down-tray',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}, {
label: 'Theming',
icon: 'i-heroicons-eye-dropper',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}, {
label: 'Layouts',
icon: 'i-heroicons-rectangle-group',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}, {
label: 'Components',
icon: 'i-heroicons-square-3-stack-3d',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}, {
label: 'Utilities',
icon: 'i-heroicons-wrench-screwdriver',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}]
</script>
<template>
<UAccordion :items="items" :ui="{ wrapper: 'flex flex-col w-full' }">
<template #default="{ item, index, open }">
<UButton color="gray" variant="ghost" class="border-b border-gray-200 dark:border-gray-700" :ui="{ rounded: 'rounded-none', padding: { sm: 'p-3' } }">
<template #leading>
<div class="w-6 h-6 rounded-full bg-primary-500 dark:bg-primary-400 flex items-center justify-center -my-1">
<UIcon :name="item.icon" class="w-4 h-4 text-white dark:text-gray-900" />
</div>
</template>
<span class="truncate">{{ index + 1 }}. {{ item.label }}</span>
<template #trailing>
<UIcon
name="i-heroicons-chevron-right-20-solid"
class="w-5 h-5 ms-auto transform transition-transform duration-200"
:class="[open && 'rotate-90']"
/>
</template>
</UButton>
</template>
</UAccordion>
</template>

View File

@@ -1,70 +0,0 @@
<script setup lang="ts">
const items = [{
label: 'Getting Started',
icon: 'i-heroicons-information-circle',
defaultOpen: true,
slot: 'getting-started'
}, {
label: 'Installation',
icon: 'i-heroicons-arrow-down-tray',
defaultOpen: true,
slot: 'installation'
}, {
label: 'Theming',
icon: 'i-heroicons-eye-dropper',
defaultOpen: true,
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}, {
label: 'Layouts',
icon: 'i-heroicons-rectangle-group',
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}, {
label: 'Components',
icon: 'i-heroicons-square-3-stack-3d',
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}, {
label: 'Utilities',
icon: 'i-heroicons-wrench-screwdriver',
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
}]
</script>
<template>
<UAccordion :items="items">
<template #item="{ item }">
<p class="italic text-gray-900 dark:text-white text-center">
{{ item.description }}
</p>
</template>
<template #getting-started>
<div class="text-gray-900 dark:text-white text-center">
<Logo class="w-auto h-8 mx-auto" />
<p class="text-sm text-gray-500 dark:text-gray-400 mt-2">
Fully styled and customizable components for Nuxt.
</p>
</div>
</template>
<template #installation="{ description }">
<div class="flex flex-col justify-center items-center gap-1 mb-4">
<h3 class="text-xl font-bold text-gray-900 dark:text-white">
Installation
</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">
Install <code>@nuxt/ui</code> dependency to your project:
</p>
<p>
{{ description }}
</p>
</div>
<div class="flex flex-col items-center">
<code>$ npm i @nuxt/ui</code>
<code>$ yarn add @nuxt/ui</code>
<code>$ pnpm add @nuxt/ui</code>
</div>
</template>
</UAccordion>
</template>

View File

@@ -1,20 +0,0 @@
<template>
<UAlert
title="Customize Alert Avatar"
description="Insert custom content into the avatar slot!"
:avatar="{
src: 'https://avatars.githubusercontent.com/u/739984?v=4',
alt: 'Avatar'
}"
>
<template #avatar="{ avatar }">
<UAvatar
v-bind="avatar"
chip-color="primary"
chip-text=""
chip-position="top-right"
/>
</template>
</UAlert>
</template>

View File

@@ -1,12 +0,0 @@
<template>
<UAlert title="Heads <i>up</i>!" icon="i-heroicons-command-line">
<template #title="{ title }">
<!-- eslint-disable-next-line vue/no-v-html -->
<span v-html="title" />
</template>
<template #description>
You can add <b>components</b> to your app using the <u>cli</u>.
</template>
</UAlert>
</template>

View File

@@ -1,10 +0,0 @@
<template>
<UAlert title="Customize Alert Icon" description="Insert custom content into the icon slot!" icon="i-heroicons-command-line">
<template #icon="{ icon }">
<UBadge size="sm">
<UIcon :name="icon" />
</UBadge>
</template>
</UAlert>
</template>

View File

@@ -1,17 +0,0 @@
<script setup lang="ts">
const links = [{
label: 'Home',
icon: 'i-heroicons-home',
to: '/'
}, {
label: 'Navigation',
icon: 'i-heroicons-square-3-stack-3d'
}, {
label: 'Breadcrumb',
icon: 'i-heroicons-link'
}]
</script>
<template>
<UBreadcrumb :links="links" />
</template>

View File

@@ -1,20 +0,0 @@
<script setup lang="ts">
const links = [{
label: 'Home',
to: '/'
}, {
label: 'Navigation'
}, {
label: 'Breadcrumb'
}]
</script>
<template>
<UBreadcrumb :links="links">
<template #default="{ link, isActive, index }">
<UBadge :color="isActive ? 'primary' : 'gray'" class="rounded-full">
{{ index + 1 }}. {{ link.label }}
</UBadge>
</template>
</UBreadcrumb>
</template>

View File

@@ -1,21 +0,0 @@
<script setup lang="ts">
const links = [{
label: 'Home',
icon: 'i-heroicons-home',
to: '/'
}, {
label: 'Navigation',
icon: 'i-heroicons-square-3-stack-3d'
}, {
label: 'Breadcrumb',
icon: 'i-heroicons-link'
}]
</script>
<template>
<UBreadcrumb :links="links" :ui="{ ol: 'gap-x-3', li: 'gap-x-3' }">
<template #divider>
<span class="w-8 h-1 rounded-full bg-gray-300 dark:bg-gray-700" />
</template>
</UBreadcrumb>
</template>

View File

@@ -1,25 +0,0 @@
<script setup lang="ts">
const links = [{
label: 'Home',
to: '/'
}, {
label: 'Navigation'
}, {
label: 'Breadcrumb'
}]
</script>
<template>
<UBreadcrumb :links="links" :divider="null" :ui="{ ol: 'gap-x-3' }">
<template #icon="{ link, index, isActive }">
<UAvatar
:alt="(index + 1 ).toString()"
:ui="{
background: isActive ? 'bg-primary-500 dark:bg-primary-400' : undefined,
placeholder: isActive ? 'text-white dark:text-gray-900' : !!link.to ? 'group-hover:text-gray-700 dark:group-hover:text-gray-200' : ''
}"
size="xs"
/>
</template>
</UBreadcrumb>
</template>

View File

@@ -1,13 +0,0 @@
<template>
<UCard>
<template #header>
<Placeholder class="h-8" />
</template>
<Placeholder class="h-32" />
<template #footer>
<Placeholder class="h-8" />
</template>
</UCard>
</template>

View File

@@ -1,16 +0,0 @@
<script setup lang="ts">
const items = [
'https://picsum.photos/600/800?random=1',
'https://picsum.photos/600/800?random=2',
'https://picsum.photos/600/800?random=3',
'https://picsum.photos/600/800?random=4',
'https://picsum.photos/600/800?random=5',
'https://picsum.photos/600/800?random=6'
]
</script>
<template>
<UCarousel v-slot="{ item }" :items="items">
<img :src="item" width="300" height="400" draggable="false">
</UCarousel>
</template>

View File

@@ -1,16 +0,0 @@
<script setup lang="ts">
const items = [
'https://picsum.photos/1920/1080?random=1',
'https://picsum.photos/1920/1080?random=2',
'https://picsum.photos/1920/1080?random=3',
'https://picsum.photos/1920/1080?random=4',
'https://picsum.photos/1920/1080?random=5',
'https://picsum.photos/1920/1080?random=6'
]
</script>
<template>
<UCarousel v-slot="{ item }" :items="items" :ui="{ item: 'basis-full' }" class="rounded-lg overflow-hidden" arrows>
<img :src="item" class="w-full" draggable="false">
</UCarousel>
</template>

View File

@@ -1,35 +0,0 @@
<script setup lang="ts">
const items = [
'https://picsum.photos/600/800?random=1',
'https://picsum.photos/600/800?random=2',
'https://picsum.photos/600/800?random=3',
'https://picsum.photos/600/800?random=4',
'https://picsum.photos/600/800?random=5',
'https://picsum.photos/600/800?random=6'
]
</script>
<template>
<UCarousel
v-slot="{ item }"
:items="items"
:ui="{
item: 'basis-full',
container: 'rounded-lg'
}"
:prev-button="{
color: 'gray',
icon: 'i-heroicons-arrow-left-20-solid',
class: '-left-12'
}"
:next-button="{
color: 'gray',
icon: 'i-heroicons-arrow-right-20-solid',
class: '-right-12'
}"
arrows
class="w-64 mx-auto"
>
<img :src="item" class="w-full" draggable="false">
</UCarousel>
</template>

View File

@@ -1,37 +0,0 @@
<script setup lang="ts">
const items = [
'https://picsum.photos/1920/1080?random=1',
'https://picsum.photos/1920/1080?random=2',
'https://picsum.photos/1920/1080?random=3',
'https://picsum.photos/1920/1080?random=4',
'https://picsum.photos/1920/1080?random=5',
'https://picsum.photos/1920/1080?random=6'
]
const carouselRef = ref()
onMounted(() => {
setInterval(() => {
if (!carouselRef.value) return
if (carouselRef.value.page === carouselRef.value.pages) {
return carouselRef.value.select(0)
}
carouselRef.value.next()
}, 3000)
})
</script>
<template>
<UCarousel
ref="carouselRef"
v-slot="{ item }"
:items="items"
:ui="{ item: 'basis-full' }"
class="rounded-lg overflow-hidden"
indicators
>
<img :src="item" class="w-full" draggable="false">
</UCarousel>
</template>

View File

@@ -1,16 +0,0 @@
<script setup lang="ts">
const items = [
'https://picsum.photos/1920/1080?random=1',
'https://picsum.photos/1920/1080?random=2',
'https://picsum.photos/1920/1080?random=3',
'https://picsum.photos/1920/1080?random=4',
'https://picsum.photos/1920/1080?random=5',
'https://picsum.photos/1920/1080?random=6'
]
</script>
<template>
<UCarousel v-slot="{ item }" :items="items" :ui="{ item: 'basis-full' }" class="rounded-lg overflow-hidden" indicators>
<img :src="item" class="w-full" draggable="false">
</UCarousel>
</template>

View File

@@ -1,16 +0,0 @@
<script setup lang="ts">
const items = [
'https://picsum.photos/600/600?random=1',
'https://picsum.photos/600/600?random=2',
'https://picsum.photos/600/600?random=3',
'https://picsum.photos/600/600?random=4',
'https://picsum.photos/600/600?random=5',
'https://picsum.photos/600/600?random=6'
]
</script>
<template>
<UCarousel v-slot="{ item }" :items="items" :ui="{ item: 'basis-full md:basis-1/2 lg:basis-1/3' }" indicators class="rounded-lg overflow-hidden">
<img :src="item" class="w-full" draggable="false">
</UCarousel>
</template>

View File

@@ -1,16 +0,0 @@
<script setup lang="ts">
const items = [
'https://picsum.photos/600/600?random=1',
'https://picsum.photos/600/600?random=2',
'https://picsum.photos/600/600?random=3',
'https://picsum.photos/600/600?random=4',
'https://picsum.photos/600/600?random=5',
'https://picsum.photos/600/600?random=6'
]
</script>
<template>
<UCarousel v-slot="{ item }" :items="items" :ui="{ item: 'basis-full md:basis-1/2 lg:basis-1/3' }">
<img :src="item" class="w-full" draggable="false">
</UCarousel>
</template>

View File

@@ -1,16 +0,0 @@
<script setup lang="ts">
const items = [
'https://picsum.photos/600/800?random=1',
'https://picsum.photos/600/800?random=2',
'https://picsum.photos/600/800?random=3',
'https://picsum.photos/600/800?random=4',
'https://picsum.photos/600/800?random=5',
'https://picsum.photos/600/800?random=6'
]
</script>
<template>
<UCarousel v-slot="{ item }" :items="items" :ui="{ item: 'basis-full' }" class="w-64 mx-auto rounded-lg overflow-hidden">
<img :src="item" class="w-full" draggable="false">
</UCarousel>
</template>

View File

@@ -1,16 +0,0 @@
<script setup lang="ts">
const items = [
'https://picsum.photos/1920/1080?random=1',
'https://picsum.photos/1920/1080?random=2',
'https://picsum.photos/1920/1080?random=3',
'https://picsum.photos/1920/1080?random=4',
'https://picsum.photos/1920/1080?random=5',
'https://picsum.photos/1920/1080?random=6'
]
</script>
<template>
<UCarousel v-slot="{ item }" :items="items" :ui="{ item: 'basis-full' }" class="rounded-lg overflow-hidden">
<img :src="item" class="w-full" draggable="false">
</UCarousel>
</template>

View File

@@ -1,31 +0,0 @@
<script setup lang="ts">
const items = [{
name: 'Sébastien Chopin',
to: 'https://github.com/Atinux',
avatar: { src: 'https://ipx.nuxt.com/f_auto,s_192x192/gh_avatar/atinux' }
}, {
name: 'Pooya Parsa',
to: 'https://github.com/pi0',
avatar: { src: 'https://ipx.nuxt.com/f_auto,s_192x192/gh_avatar/pi0' }
}, {
name: 'Daniel Roe',
to: 'https://github.com/danielroe',
avatar: { src: 'https://ipx.nuxt.com/f_auto,s_192x192/gh_avatar/danielroe' }
}, {
name: 'Anthony Fu',
to: 'https://github.com/antfu',
avatar: { src: 'https://ipx.nuxt.com/f_auto,s_192x192/gh_avatar/antfu' }
}]
</script>
<template>
<UCarousel v-slot="{ item, index }" :items="items" :ui="{ item: 'w-full' }">
<div class="text-center mx-auto">
<img :src="item.avatar.src" :alt="item.name" class="rounded-full w-48 h-48 mb-2" draggable="false">
<p class="font-semibold">
{{ index + 1 }}. {{ item.name }}
</p>
</div>
</UCarousel>
</template>

View File

@@ -1,33 +0,0 @@
<script setup lang="ts">
const items = [
'https://picsum.photos/600/800?random=1',
'https://picsum.photos/600/800?random=2',
'https://picsum.photos/600/800?random=3',
'https://picsum.photos/600/800?random=4',
'https://picsum.photos/600/800?random=5',
'https://picsum.photos/600/800?random=6'
]
</script>
<template>
<UCarousel
:items="items"
:ui="{
item: 'basis-full',
container: 'rounded-lg',
indicators: {
wrapper: 'relative bottom-0 mt-4'
}
}"
indicators
class="w-64 mx-auto"
>
<template #default="{ item }">
<img :src="item" class="w-full" draggable="false">
</template>
<template #indicator="{ onClick, page, active }">
<UButton :label="String(page)" :variant="active ? 'solid' : 'outline'" size="2xs" class="rounded-full min-w-6 justify-center" @click="onClick(page)" />
</template>
</UCarousel>
</template>

View File

@@ -1,38 +0,0 @@
<script setup lang="ts">
const items = [
'https://picsum.photos/600/800?random=1',
'https://picsum.photos/600/800?random=2',
'https://picsum.photos/600/800?random=3',
'https://picsum.photos/600/800?random=4',
'https://picsum.photos/600/800?random=5',
'https://picsum.photos/600/800?random=6'
]
</script>
<template>
<UCarousel
:items="items"
:ui="{
item: 'basis-full',
container: 'rounded-lg'
}"
arrows
class="w-64 mx-auto"
>
<template #default="{ item }">
<img :src="item" class="w-full" draggable="false">
</template>
<template #prev="{ onClick, disabled }">
<button :disabled="disabled" @click="onClick">
Prev
</button>
</template>
<template #next="{ onClick, disabled }">
<button :disabled="disabled" @click="onClick">
Next
</button>
</template>
</UCarousel>
</template>

View File

@@ -1,16 +0,0 @@
<script setup lang="ts">
const items = [
'https://picsum.photos/400/600?random=1',
'https://picsum.photos/400/600?random=2',
'https://picsum.photos/400/600?random=3',
'https://picsum.photos/400/600?random=4',
'https://picsum.photos/400/600?random=5',
'https://picsum.photos/400/600?random=6'
]
</script>
<template>
<UCarousel v-slot="{ item }" :items="items" :ui="{ item: 'snap-end' }">
<img :src="item" width="200" height="300" draggable="false">
</UCarousel>
</template>

View File

@@ -1,16 +0,0 @@
<script setup lang="ts">
const items = [
'https://picsum.photos/400/600?random=1',
'https://picsum.photos/400/600?random=2',
'https://picsum.photos/400/600?random=3',
'https://picsum.photos/400/600?random=4',
'https://picsum.photos/400/600?random=5',
'https://picsum.photos/400/600?random=6'
]
</script>
<template>
<UCarousel v-slot="{ item }" :items="items" :ui="{ item: 'snap-start' }">
<img :src="item" width="200" height="300" draggable="false">
</UCarousel>
</template>

View File

@@ -1,7 +0,0 @@
<script setup lang="ts">
const selected = ref(true)
</script>
<template>
<UCheckbox v-model="selected" name="notifications" label="Notifications" />
</template>

View File

@@ -1,19 +0,0 @@
<template>
<UChip size="md" position="bottom-right" inset :ui="{ base: '-mx-2 rounded-none ring-0', background: '' }">
<UAvatar
src="https://avatars.githubusercontent.com/u/739984?v=4"
alt="Avatar"
size="lg"
/>
<template #content>
<UAvatar
src="https://avatars.githubusercontent.com/in/80442?v=4"
alt="Avatar"
size="xs"
:ui="{ rounded: 'rounded-md' }"
class="shadow-md"
/>
</template>
</UChip>
</template>

View File

@@ -1,19 +0,0 @@
<script setup lang="ts">
const items = [{
name: 'messages',
icon: 'i-heroicons-chat-bubble-oval-left',
count: 3
}, {
name: 'notifications',
icon: 'i-heroicons-bell',
count: 0
}]
</script>
<template>
<div class="flex gap-3">
<UChip v-for="{ name, icon, count } in items" :key="name" :show="count > 0">
<UButton :icon="icon" color="gray" />
</UChip>
</div>
</template>

View File

@@ -1,19 +0,0 @@
<script setup lang="ts">
const groups = [{
key: 'users',
label: q => q && `Users matching “${q}”...`,
search: async (q) => {
if (!q) {
return []
}
const users = await $fetch<any[]>('https://jsonplaceholder.typicode.com/users', { params: { q } })
return users.map(user => ({ id: user.id, label: user.name, suffix: user.email }))
}
}]
</script>
<template>
<UCommandPalette :groups="groups" :autoselect="false" />
</template>

View File

@@ -1,27 +0,0 @@
<script setup lang="ts">
const people = [
{ id: 1, label: 'Wade Cooper' },
{ id: 2, label: 'Arlene Mccoy' },
{ id: 3, label: 'Devon Webb' },
{ id: 4, label: 'Tom Cook' },
{ id: 5, label: 'Tanya Fox' },
{ id: 6, label: 'Hellen Schmidt' },
{ id: 7, label: 'Caroline Schultz' },
{ id: 8, label: 'Mason Heaney' },
{ id: 9, label: 'Claudie Smitham' },
{ id: 10, label: 'Emil Schaefer' }
]
const selected = ref([people[3]])
</script>
<template>
<UCommandPalette
v-model="selected"
multiple
nullable
:autoselect="false"
:groups="[{ key: 'people', commands: people }]"
:fuse="{ resultLimit: 6, fuseOptions: { threshold: 0.1 } }"
/>
</template>

View File

@@ -1,10 +0,0 @@
<template>
<UCommandPalette>
<template #empty-state>
<div class="flex flex-col items-center justify-center py-6 gap-3">
<span class="italic text-sm">Nothing here!</span>
<UButton label="Add item" />
</div>
</template>
</UCommandPalette>
</template>

View File

@@ -1,30 +0,0 @@
<script setup lang="ts">
const people = [
{ id: 1, label: 'Wade Cooper', child: true },
{ id: 2, label: 'Arlene Mccoy' },
{ id: 3, label: 'Devon Webb', child: true },
{ id: 4, label: 'Tom Cook' },
{ id: 5, label: 'Tanya Fox', child: true },
{ id: 6, label: 'Hellen Schmidt' },
{ id: 7, label: 'Caroline Schultz', child: true },
{ id: 8, label: 'Mason Heaney' },
{ id: 9, label: 'Claudie Smitham', child: true },
{ id: 10, label: 'Emil Schaefer' }
]
const groups = [{
key: 'users',
commands: people,
filter: (q, commands) => {
if (!q) {
return commands?.filter(command => !command.child)
}
return commands
}
}]
</script>
<template>
<UCommandPalette :groups="groups" :autoselect="false" />
</template>

View File

@@ -1,46 +0,0 @@
<script setup lang="ts">
const router = useRouter()
const toast = useToast()
const commandPaletteRef = ref()
const users = [
{ id: 'benjamincanac', label: 'benjamincanac', href: 'https://github.com/benjamincanac', target: '_blank', avatar: { src: 'https://ipx.nuxt.com/s_16x16/gh_avatar/benjamincanac', srcset: 'https://ipx.nuxt.com/s_32x32/gh_avatar/benjamincanac 2x', loading: 'lazy' } },
{ id: 'Atinux', label: 'Atinux', href: 'https://github.com/Atinux', target: '_blank', avatar: { src: 'https://ipx.nuxt.com/s_16x16/gh_avatar/Atinux', srcset: 'https://ipx.nuxt.com/s_32x32/gh_avatar/Atinux 2x', loading: 'lazy' } },
{ id: 'smarroufin', label: 'smarroufin', href: 'https://github.com/smarroufin', target: '_blank', avatar: { src: 'https://ipx.nuxt.com/s_16x16/gh_avatar/smarroufin', srcset: 'https://ipx.nuxt.com/s_32x32/gh_avatar/smarroufin 2x', loading: 'lazy' } }
]
const actions = [
{ id: 'new-file', label: 'Add new file', icon: 'i-heroicons-document-plus', click: () => toast.add({ title: 'New file added!' }), shortcuts: ['⌘', 'N'] },
{ id: 'new-folder', label: 'Add new folder', icon: 'i-heroicons-folder-plus', click: () => toast.add({ title: 'New folder added!' }), shortcuts: ['⌘', 'F'] },
{ id: 'hashtag', label: 'Add hashtag', icon: 'i-heroicons-hashtag', click: () => toast.add({ title: 'Hashtag added!' }), shortcuts: ['⌘', 'H'] },
{ id: 'label', label: 'Add label', icon: 'i-heroicons-tag', click: () => toast.add({ title: 'Label added!' }), shortcuts: ['⌘', 'L'] }
]
const groups = computed(() =>
[commandPaletteRef.value?.query ? {
key: 'users',
commands: users
} : {
key: 'recent',
label: 'Recent searches',
commands: users.slice(0, 1)
}, {
key: 'actions',
commands: actions
}].filter(Boolean))
function onSelect (option) {
if (option.click) {
option.click()
} else if (option.to) {
router.push(option.to)
} else if (option.href) {
window.open(option.href, '_blank')
}
}
</script>
<template>
<UCommandPalette ref="commandPaletteRef" :groups="groups" :autoselect="false" @update:model-value="onSelect" />
</template>

View File

@@ -1,33 +0,0 @@
<script setup lang="ts">
const isOpen = ref(false)
const people = [
{ id: 1, label: 'Wade Cooper' },
{ id: 2, label: 'Arlene Mccoy' },
{ id: 3, label: 'Devon Webb' },
{ id: 4, label: 'Tom Cook' },
{ id: 5, label: 'Tanya Fox' },
{ id: 6, label: 'Hellen Schmidt' },
{ id: 7, label: 'Caroline Schultz' },
{ id: 8, label: 'Mason Heaney' },
{ id: 9, label: 'Claudie Smitham' },
{ id: 10, label: 'Emil Schaefer' }
]
const selected = ref([])
</script>
<template>
<div>
<UButton label="Open" @click="isOpen = true" />
<UModal v-model="isOpen">
<UCommandPalette
v-model="selected"
multiple
nullable
:groups="[{ key: 'people', commands: people }]"
/>
</UModal>
</div>
</template>

View File

@@ -1,78 +0,0 @@
<script setup lang="ts">
import type { NavItem, ParsedContent } from '@nuxt/content/dist/runtime/types'
import type { Button } from '#ui/types'
const commandPaletteRef = ref()
const navigation = inject<Ref<NavItem[]>>('navigation')
const files = inject<Ref<ParsedContent[]>>('files')
const groups = computed(() => navigation.value.map(item => ({
key: item._path,
label: item.label,
commands: files.value.filter(file => file._path.startsWith(item._path)).map(file => ({
id: file._id,
icon: 'i-heroicons-document',
title: file.navigation?.title || file.title,
category: item.title,
to: file._path
}))
})))
const closeButton = computed(() => commandPaletteRef.value?.query ? ({ icon: 'i-heroicons-x-mark', color: 'black', variant: 'ghost', size: 'lg', padded: false }) : null)
const emptyState = computed(() => commandPaletteRef.value?.query ? ({ icon: 'i-heroicons-magnifying-glass', queryLabel: 'No results' }) : ({ icon: '', label: 'No recent searches' }))
const ui = {
wrapper: 'flex flex-col flex-1 min-h-0 bg-gray-50 dark:bg-gray-800',
input: {
wrapper: 'relative flex items-center mx-3 py-3',
base: 'w-full rounded border-2 border-primary-500 placeholder-gray-400 dark:placeholder-gray-500 focus:border-primary-500 focus:outline-none focus:ring-0 bg-white dark:bg-gray-900',
padding: 'px-4',
height: 'h-14',
size: 'text-lg',
icon: {
base: 'pointer-events-none absolute left-3 text-primary-500 dark:text-primary-400',
size: 'h-6 w-6'
}
},
group: {
wrapper: 'p-3 relative',
label: '-mx-3 px-3 -mt-4 mb-2 py-1 text-sm font-semibold text-primary-500 dark:text-primary-400 font-semibold sticky top-0 bg-gray-50 dark:bg-gray-800 z-10',
container: 'space-y-1',
command: {
base: 'flex justify-between select-none items-center rounded px-2 py-4 gap-2 relative font-medium text-sm group shadow',
active: 'bg-primary-500 dark:bg-primary-400 text-white',
inactive: 'bg-white dark:bg-gray-900',
label: 'flex flex-col min-w-0',
suffix: 'text-xs',
icon: {
base: 'flex-shrink-0 w-6 h-6',
active: 'text-white',
inactive: 'text-gray-400 dark:text-gray-500'
}
}
},
emptyState: {
wrapper: 'flex flex-col items-center justify-center flex-1 py-9',
label: 'text-sm text-center text-gray-500 dark:text-gray-400',
queryLabel: 'text-lg text-center text-gray-900 dark:text-white',
icon: 'w-12 h-12 mx-auto text-gray-400 dark:text-gray-500 mb-4'
}
}
</script>
<template>
<UCommandPalette
ref="commandPaletteRef"
:groups="groups"
:ui="ui"
:close-button="(closeButton as Button)"
:empty-state="(emptyState as any)"
:autoselect="false"
command-attribute="title"
:fuse="{
fuseOptions: { keys: ['title', 'category'] },
}"
placeholder="Search docs"
/>
</template>

View File

@@ -1,64 +0,0 @@
<script setup lang="ts">
const commandPaletteRef = ref()
const suggestions = [
{ id: 'linear', label: 'Linear', icon: 'i-simple-icons-linear' },
{ id: 'figma', label: 'Figma', icon: 'i-simple-icons-figma' },
{ id: 'slack', label: 'Slack', icon: 'i-simple-icons-slack' },
{ id: 'youtube', label: 'YouTube', icon: 'i-simple-icons-youtube' },
{ id: 'github', label: 'GitHub', icon: 'i-simple-icons-github' }
]
const commands = [
{ id: 'clipboard-history', label: 'Clipboard History', icon: 'i-heroicons-clipboard', click: () => alert('New file') },
{ id: 'import-extension', label: 'Import Extension', icon: 'i-heroicons-wrench-screwdriver', click: () => alert('New folder') },
{ id: 'manage-extensions', label: 'Manage Extensions', icon: 'i-heroicons-wrench-screwdriver', click: () => alert('Add hashtag') }
]
const groups = [{
key: 'suggestions',
label: 'Suggestions',
inactive: 'Application',
commands: suggestions
}, {
key: 'commands',
label: 'Commands',
inactive: 'Command',
commands
}]
const ui = {
wrapper: 'flex flex-col flex-1 min-h-0 divide-y divide-gray-200 dark:divide-gray-700 bg-gray-50 dark:bg-gray-800',
container: 'relative flex-1 overflow-y-auto divide-y divide-gray-200 dark:divide-gray-700 scroll-py-2',
input: {
base: 'w-full h-14 px-4 placeholder-gray-400 dark:placeholder-gray-500 bg-transparent border-0 text-gray-900 dark:text-white focus:ring-0 focus:outline-none'
},
group: {
label: 'px-2 my-2 text-xs font-semibold text-gray-500 dark:text-gray-400',
command: {
base: 'flex justify-between select-none cursor-default items-center rounded-md px-2 py-2 gap-2 relative',
active: 'bg-gray-200 dark:bg-gray-700/50 text-gray-900 dark:text-white',
container: 'flex items-center gap-3 min-w-0',
icon: {
base: 'flex-shrink-0 w-5 h-5',
active: 'text-gray-900 dark:text-white',
inactive: 'text-gray-400 dark:text-gray-500'
},
avatar: {
size: '2xs'
}
}
}
}
</script>
<template>
<UCommandPalette
ref="commandPaletteRef"
:groups="groups"
icon=""
:ui="ui"
:autoselect="false"
placeholder="Search for apps and commands"
/>
</template>

View File

@@ -1,5 +0,0 @@
<template>
<UContainer>
<Placeholder class="h-32" />
</UContainer>
</template>

View File

@@ -1,35 +0,0 @@
<script setup lang="ts">
const { x, y } = useMouse()
const { y: windowY } = useWindowScroll()
const isOpen = ref(false)
const virtualElement = ref({ getBoundingClientRect: () => ({}) })
function onContextMenu () {
const top = unref(y) - unref(windowY)
const left = unref(x)
virtualElement.value.getBoundingClientRect = () => ({
width: 0,
height: 0,
top,
left
})
isOpen.value = true
}
</script>
<template>
<div class="w-full" @contextmenu.prevent="onContextMenu">
<Placeholder class="h-96 select-none w-full flex items-center justify-center">
Right click here
</Placeholder>
<UContextMenu v-model="isOpen" :virtual-element="virtualElement">
<div class="p-4">
Menu
</div>
</UContextMenu>
</div>
</template>

View File

@@ -1,35 +0,0 @@
<script setup lang="ts">
const { x, y } = useMouse()
const { y: windowY } = useWindowScroll()
const isOpen = ref(false)
const virtualElement = ref({ getBoundingClientRect: () => ({}) })
function onContextMenu () {
const top = unref(y) - unref(windowY)
const left = unref(x)
virtualElement.value.getBoundingClientRect = () => ({
width: 0,
height: 0,
top,
left
})
isOpen.value = true
}
</script>
<template>
<div class="w-full" @contextmenu.prevent="onContextMenu">
<Placeholder class="h-96 select-none w-full flex items-center justify-center">
Right click here
</Placeholder>
<UContextMenu v-model="isOpen" :virtual-element="virtualElement" :popper="{ arrow: true, placement: 'right' }">
<div class="p-4">
Menu
</div>
</UContextMenu>
</div>
</template>

View File

@@ -1,35 +0,0 @@
<script setup lang="ts">
const { x, y } = useMouse()
const { y: windowY } = useWindowScroll()
const isOpen = ref(false)
const virtualElement = ref({ getBoundingClientRect: () => ({}) })
function onContextMenu () {
const top = unref(y) - unref(windowY)
const left = unref(x)
virtualElement.value.getBoundingClientRect = () => ({
width: 0,
height: 0,
top,
left
})
isOpen.value = true
}
</script>
<template>
<div class="w-full" @contextmenu.prevent="onContextMenu">
<Placeholder class="h-96 select-none w-full flex items-center justify-center">
Right click here
</Placeholder>
<UContextMenu v-model="isOpen" :virtual-element="virtualElement" :popper="{ offset: 0 }">
<div class="p-4">
Menu
</div>
</UContextMenu>
</div>
</template>

View File

@@ -1,35 +0,0 @@
<script setup lang="ts">
const { x, y } = useMouse()
const { y: windowY } = useWindowScroll()
const isOpen = ref(false)
const virtualElement = ref({ getBoundingClientRect: () => ({}) })
function onContextMenu () {
const top = unref(y) - unref(windowY)
const left = unref(x)
virtualElement.value.getBoundingClientRect = () => ({
width: 0,
height: 0,
top,
left
})
isOpen.value = true
}
</script>
<template>
<div class="w-full" @contextmenu.prevent="onContextMenu">
<Placeholder class="h-96 select-none w-full flex items-center justify-center">
Right click here
</Placeholder>
<UContextMenu v-model="isOpen" :virtual-element="virtualElement" :popper="{ placement: 'right-start' }">
<div class="p-4">
Menu
</div>
</UContextMenu>
</div>
</template>

View File

@@ -1,15 +0,0 @@
<script setup lang="ts">
import { format } from 'date-fns'
const date = ref(new Date())
</script>
<template>
<UPopover :popper="{ placement: 'bottom-start' }">
<UButton icon="i-heroicons-calendar-days-20-solid" :label="format(date, 'd MMM, yyy')" />
<template #panel="{ close }">
<DatePicker v-model="date" is-required @close="close" />
</template>
</UPopover>
</template>

View File

@@ -1,49 +0,0 @@
<script setup lang="ts">
import { sub, format, isSameDay, type Duration } from 'date-fns'
const ranges = [
{ label: 'Last 7 days', duration: { days: 7 } },
{ label: 'Last 14 days', duration: { days: 14 } },
{ label: 'Last 30 days', duration: { days: 30 } },
{ label: 'Last 3 months', duration: { months: 3 } },
{ label: 'Last 6 months', duration: { months: 6 } },
{ label: 'Last year', duration: { years: 1 } }
]
const selected = ref({ start: sub(new Date(), { days: 14 }), end: new Date() })
function isRangeSelected (duration: Duration) {
return isSameDay(selected.value.start, sub(new Date(), duration)) && isSameDay(selected.value.end, new Date())
}
function selectRange (duration: Duration) {
selected.value = { start: sub(new Date(), duration), end: new Date() }
}
</script>
<template>
<UPopover :popper="{ placement: 'bottom-start' }">
<UButton icon="i-heroicons-calendar-days-20-solid">
{{ format(selected.start, 'd MMM, yyy') }} - {{ format(selected.end, 'd MMM, yyy') }}
</UButton>
<template #panel="{ close }">
<div class="flex items-center sm:divide-x divide-gray-200 dark:divide-gray-800">
<div class="hidden sm:flex flex-col py-4">
<UButton
v-for="(range, index) in ranges"
:key="index"
:label="range.label"
color="gray"
variant="ghost"
class="rounded-none px-6"
:class="[isRangeSelected(range.duration) ? 'bg-gray-100 dark:bg-gray-800' : 'hover:bg-gray-50 dark:hover:bg-gray-800/50']"
truncate
@click="selectRange(range.duration)"
/>
</div>
<DatePicker v-model="selected" @close="close" />
</div>
</template>
</UPopover>
</template>

View File

@@ -1,5 +0,0 @@
<template>
<UDivider>
<Logo class="w-28 h-6" />
</UDivider>
</template>

View File

@@ -1,47 +0,0 @@
<script setup lang="ts">
const form = reactive({ email: 'mail@example.com', password: 'password' })
</script>
<template>
<div class="w-full flex flex-col gap-y-4">
<UCard :ui="{ body: { base: 'grid grid-cols-3' } }">
<div class="space-y-4">
<UFormGroup label="Email" name="email">
<UInput v-model="form.email" />
</UFormGroup>
<UFormGroup label="Password" name="password">
<UInput v-model="form.password" type="password" />
</UFormGroup>
<UButton label="Login" color="gray" block />
</div>
<UDivider label="OR" orientation="vertical" />
<div class="space-y-4 flex flex-col justify-center">
<UButton color="black" label="Login with GitHub" icon="i-simple-icons-github" block />
<UButton color="black" label="Login with Google" icon="i-simple-icons-google" block />
</div>
</UCard>
<UCard>
<div class="space-y-4">
<UFormGroup label="Email" name="email">
<UInput v-model="form.email" />
</UFormGroup>
<UFormGroup label="Password" name="password">
<UInput v-model="form.password" type="password" />
</UFormGroup>
<UButton label="Login" color="gray" block />
<UDivider label="OR" />
<UButton color="black" label="Login with GitHub" icon="i-simple-icons-github" block />
<UButton color="black" label="Login with Google" icon="i-simple-icons-google" block />
</div>
</UCard>
</div>
</template>

View File

@@ -1,16 +0,0 @@
<script setup lang="ts">
const items = [
[{
label: 'Profile',
avatar: {
src: 'https://avatars.githubusercontent.com/u/739984?v=4'
}
}]
]
</script>
<template>
<UDropdown :items="items" :popper="{ arrow: true }">
<UButton color="white" label="Options" trailing-icon="i-heroicons-chevron-down-20-solid" />
</UDropdown>
</template>

View File

@@ -1,38 +0,0 @@
<script setup lang="ts">
const items = [
[{
label: 'Profile',
avatar: {
src: 'https://avatars.githubusercontent.com/u/739984?v=4'
}
}], [{
label: 'Edit',
icon: 'i-heroicons-pencil-square-20-solid',
shortcuts: ['E'],
click: () => {
console.log('Edit')
}
}, {
label: 'Duplicate',
icon: 'i-heroicons-document-duplicate-20-solid',
shortcuts: ['D'],
disabled: true
}], [{
label: 'Archive',
icon: 'i-heroicons-archive-box-20-solid'
}, {
label: 'Move',
icon: 'i-heroicons-arrow-right-circle-20-solid'
}], [{
label: 'Delete',
icon: 'i-heroicons-trash-20-solid',
shortcuts: ['⌘', 'D']
}]
]
</script>
<template>
<UDropdown :items="items" :popper="{ placement: 'bottom-start' }">
<UButton color="white" label="Options" trailing-icon="i-heroicons-chevron-down-20-solid" />
</UDropdown>
</template>

View File

@@ -1,16 +0,0 @@
<script setup lang="ts">
const items = [
[{
label: 'Profile',
avatar: {
src: 'https://avatars.githubusercontent.com/u/739984?v=4'
}
}]
]
</script>
<template>
<UDropdown :items="items" mode="hover" :popper="{ placement: 'bottom-start' }">
<UButton color="white" label="Options" trailing-icon="i-heroicons-chevron-down-20-solid" />
</UDropdown>
</template>

View File

@@ -1,16 +0,0 @@
<script setup lang="ts">
const items = [
[{
label: 'Profile',
avatar: {
src: 'https://avatars.githubusercontent.com/u/739984?v=4'
}
}]
]
</script>
<template>
<UDropdown :items="items" :popper="{ offsetDistance: 0, placement: 'right-start' }">
<UButton color="white" label="Options" trailing-icon="i-heroicons-chevron-down-20-solid" />
</UDropdown>
</template>

View File

@@ -1,22 +0,0 @@
<script setup lang="ts">
const items = [
[{
label: 'Profile',
avatar: {
src: 'https://avatars.githubusercontent.com/u/739984?v=4'
}
}]
]
const open = ref(true)
defineShortcuts({
o: () => open.value = !open.value
})
</script>
<template>
<UDropdown v-model:open="open" :items="items" :popper="{ placement: 'bottom-start' }">
<UButton color="white" label="Options" trailing-icon="i-heroicons-chevron-down-20-solid" />
</UDropdown>
</template>

View File

@@ -1,16 +0,0 @@
<script setup lang="ts">
const items = [
[{
label: 'Profile',
avatar: {
src: 'https://avatars.githubusercontent.com/u/739984?v=4'
}
}]
]
</script>
<template>
<UDropdown :items="items" :popper="{ placement: 'right-start' }">
<UButton color="white" label="Options" trailing-icon="i-heroicons-chevron-down-20-solid" />
</UDropdown>
</template>

View File

@@ -1,47 +0,0 @@
<script setup lang="ts">
const items = [
[{
label: 'ben@example.com',
slot: 'account',
disabled: true
}], [{
label: 'Settings',
icon: 'i-heroicons-cog-8-tooth'
}], [{
label: 'Documentation',
icon: 'i-heroicons-book-open'
}, {
label: 'Changelog',
icon: 'i-heroicons-megaphone'
}, {
label: 'Status',
icon: 'i-heroicons-signal'
}], [{
label: 'Sign out',
icon: 'i-heroicons-arrow-left-on-rectangle'
}]
]
</script>
<template>
<UDropdown :items="items" :ui="{ item: { disabled: 'cursor-text select-text' } }" :popper="{ placement: 'bottom-start' }">
<UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" />
<template #account="{ item }">
<div class="text-left">
<p>
Signed in as
</p>
<p class="truncate font-medium text-gray-900 dark:text-white">
{{ item.label }}
</p>
</div>
</template>
<template #item="{ item }">
<span class="truncate">{{ item.label }}</span>
<UIcon :name="item.icon" class="flex-shrink-0 h-4 w-4 text-gray-400 dark:text-gray-500 ms-auto" />
</template>
</UDropdown>
</template>

View File

@@ -1,36 +0,0 @@
<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({ path: 'email', message: 'Required' })
if (!state.password) errors.push({ path: 'password', message: 'Required' })
return errors
}
async function onSubmit (event: FormSubmitEvent<any>) {
// Do something with data
console.log(event.data)
}
</script>
<template>
<UForm :validate="validate" :state="state" class="space-y-4" @submit="onSubmit">
<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>

View File

@@ -1,114 +0,0 @@
<script setup lang="ts">
import { z } from 'zod'
import type { FormSubmitEvent } from '#ui/types'
const options = [
{ label: 'Option 1', value: 'option-1' },
{ label: 'Option 2', value: 'option-2' },
{ label: 'Option 3', value: 'option-3' }
]
const state = reactive({
input: undefined,
inputMenu: undefined,
textarea: undefined,
select: undefined,
selectMenu: undefined,
checkbox: undefined,
toggle: undefined,
radio: undefined,
radioGroup: undefined,
switch: undefined,
range: undefined
})
const schema = z.object({
input: z.string().min(10),
inputMenu: z.any().refine(option => option?.value === 'option-2', {
message: 'Select 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'
}),
toggle: z.boolean().refine(value => value === true, {
message: 'Toggle me'
}),
checkbox: z.boolean().refine(value => value === true, {
message: 'Check me'
}),
radio: z.string().refine(value => value === 'option-2', {
message: 'Select Option 2'
}),
radioGroup: z.string().refine(value => value === 'option-2', {
message: 'Select Option 2'
}),
range: z.number().max(20, { message: 'Must be less than 20' })
})
type Schema = z.infer<typeof schema>
const form = ref()
async function onSubmit (event: FormSubmitEvent<Schema>) {
// Do something with event.data
console.log(event.data)
}
</script>
<template>
<UForm ref="form" :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
<UFormGroup name="input" label="Input">
<UInput v-model="state.input" />
</UFormGroup>
<UFormGroup name="inputMenu" label="Input Menu">
<UInputMenu v-model="state.inputMenu" :options="options" />
</UFormGroup>
<UFormGroup name="textarea" label="Textarea">
<UTextarea v-model="state.textarea" />
</UFormGroup>
<UFormGroup name="select" label="Select">
<USelect v-model="state.select" placeholder="Select..." :options="options" />
</UFormGroup>
<UFormGroup name="selectMenu" label="Select Menu">
<USelectMenu v-model="state.selectMenu" placeholder="Select..." :options="options" />
</UFormGroup>
<UFormGroup name="toggle" label="Toggle">
<UToggle v-model="state.toggle" />
</UFormGroup>
<UFormGroup name="checkbox" label="Checkbox">
<UCheckbox v-model="state.checkbox" label="Check me" />
</UFormGroup>
<UFormGroup name="radioGroup" label="Radio Group">
<URadioGroup v-model="state.radioGroup" :options="options" />
</UFormGroup>
<UFormGroup name="radio" label="Radio">
<URadio v-for="option in options" :key="option.value" v-model="state.radio" v-bind="option">
{{ option.label }}
</URadio>
</UFormGroup>
<UFormGroup name="range" label="Range">
<URange v-model="state.range" />
</UFormGroup>
<UButton type="submit">
Submit
</UButton>
<UButton variant="outline" class="ml-2" @click="form.clear()">
Clear
</UButton>
</UForm>
</template>

View File

@@ -1,37 +0,0 @@
<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
})
async function onSubmit (event: FormSubmitEvent<any>) {
// Do something with event.data
console.log(event.data)
}
</script>
<template>
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
<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>

View File

@@ -1,42 +0,0 @@
<script setup lang="ts">
import type { FormError, FormErrorEvent, FormSubmitEvent } from '#ui/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 onSubmit (event: FormSubmitEvent<any>) {
// Do something with data
console.log(event.data)
}
async function onError (event: FormErrorEvent) {
const element = document.getElementById(event.errors[0].id)
element?.focus()
element?.scrollIntoView({ behavior: 'smooth', block: 'center' })
}
</script>
<template>
<UForm :validate="validate" :state="state" class="space-y-4" @submit="onSubmit" @error="onError">
<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>

View File

@@ -1,37 +0,0 @@
<script setup lang="ts">
import { string, objectAsync, email, minLength, type Input } from 'valibot'
import type { FormSubmitEvent } from '#ui/types'
const schema = objectAsync({
email: string([email('Invalid email')]),
password: string([minLength(8, 'Must be at least 8 characters')])
})
type Schema = Input<typeof schema>
const state = reactive({
email: '',
password: ''
})
async function onSubmit (event: FormSubmitEvent<Schema>) {
// Do something with event.data
console.log(event.data)
}
</script>
<template>
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
<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>

View File

@@ -1,39 +0,0 @@
<script setup lang="ts">
import { object, string, type InferType } from 'yup'
import type { FormSubmitEvent } from '#ui/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 onSubmit (event: FormSubmitEvent<Schema>) {
// Do something with event.data
console.log(event.data)
}
</script>
<template>
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
<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>

View File

@@ -1,37 +0,0 @@
<script setup lang="ts">
import { z } from 'zod'
import type { FormSubmitEvent } from '#ui/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 onSubmit (event: FormSubmitEvent<Schema>) {
// Do something with data
console.log(event.data)
}
</script>
<template>
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
<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>

View File

@@ -1,19 +0,0 @@
<template>
<UForm :schema="schema" :state="state" class="space-y-4">
<UFormGroup label="Username" name="username" eager-validation>
<UInput v-model="state.username" placeholder="Choose Username" />
</UFormGroup>
</UForm>
</template>
<script setup lang="ts">
import { z } from 'zod'
const schema = z.object({
username: z.string().min(10, 'Must be at least 10 characters')
})
const state = reactive({
username: undefined
})
</script>

View File

@@ -1,9 +0,0 @@
<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' : undefined" />
</UFormGroup>
</template>
<script setup lang="ts">
const email = ref('')
</script>

View File

@@ -1,17 +0,0 @@
<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 }">
<span :class="[error ? 'text-red-500 dark:text-red-400' : 'text-primary-500 dark:text-primary-400']">
{{ error ? error : 'Your email is valid' }}
</span>
</template>
</UFormGroup>
</template>
<script setup lang="ts">
const email = ref('')
</script>

View File

@@ -1,27 +0,0 @@
<script setup lang="ts">
const route = useRoute()
const links = [{
label: 'Profile',
avatar: {
src: 'https://avatars.githubusercontent.com/u/739984?v=4'
},
badge: 100
}, {
label: 'Installation',
icon: 'i-heroicons-home',
to: '/getting-started/installation'
}, {
label: 'Horizontal Navigation',
icon: 'i-heroicons-chart-bar',
to: `${route.path.startsWith('/dev') ? '/dev' : ''}/components/horizontal-navigation`
}, {
label: 'Command Palette',
icon: 'i-heroicons-command-line',
to: '/components/command-palette'
}]
</script>
<template>
<UHorizontalNavigation :links="links" class="border-b border-gray-200 dark:border-gray-800" />
</template>

View File

@@ -1,22 +0,0 @@
<script setup lang="ts">
const route = useRoute()
const links = [{
label: 'Horizontal Navigation',
to: `${route.path.startsWith('/dev') ? '/dev' : ''}/components/horizontal-navigation`
}, {
label: 'Command Palette',
to: '/components/command-palette'
}, {
label: 'Table',
to: '/components/table'
}]
</script>
<template>
<UHorizontalNavigation :links="links">
<template #default="{ link }">
<span class="group-hover:text-primary relative">{{ link.label }}</span>
</template>
</UHorizontalNavigation>
</template>

View File

@@ -1,29 +0,0 @@
<script setup lang="ts">
const route = useRoute()
const links = [
[{
label: 'Installation',
icon: 'i-heroicons-home',
to: '/getting-started/installation'
}, {
label: 'Horizontal Navigation',
icon: 'i-heroicons-chart-bar',
to: `${route.path.startsWith('/dev') ? '/dev' : ''}/components/horizontal-navigation`
}, {
label: 'Command Palette',
icon: 'i-heroicons-command-line',
to: '/components/command-palette'
}], [{
label: 'Examples',
icon: 'i-heroicons-light-bulb'
}, {
label: 'Help',
icon: 'i-heroicons-question-mark-circle'
}]
]
</script>
<template>
<UHorizontalNavigation :links="links" class="border-b border-gray-200 dark:border-gray-800" />
</template>

View File

@@ -1,7 +0,0 @@
<script setup lang="ts">
const value = ref('')
</script>
<template>
<UInput v-model="value" />
</template>

View File

@@ -1,25 +0,0 @@
<template>
<UInput
v-model="q"
name="q"
placeholder="Search..."
icon="i-heroicons-magnifying-glass-20-solid"
autocomplete="off"
: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>

View File

@@ -1,9 +0,0 @@
<script setup lang="ts">
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>
<UInputMenu v-model="selected" :options="people" />
</template>

View File

@@ -1,13 +0,0 @@
<script setup lang="ts">
const people = []
const selected = ref()
</script>
<template>
<UInputMenu v-model="selected" :options="people">
<template #empty>
No people
</template>
</UInputMenu>
</template>

View File

@@ -1,38 +0,0 @@
<script setup lang="ts">
import type { Avatar } from '#ui/types'
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>
<UInputMenu v-model="selected" :options="people">
<template #leading>
<UIcon v-if="selected.icon" :name="(selected.icon as string)" class="w-5 h-5" />
<UAvatar v-else-if="selected.avatar" v-bind="(selected.avatar as Avatar)" size="2xs" />
</template>
</UInputMenu>
</template>

View File

@@ -1,26 +0,0 @@
<script setup lang="ts">
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)
</script>
<template>
<UInputMenu
v-model="selected"
:options="people"
value-attribute="id"
option-attribute="name"
/>
</template>

View File

@@ -1,13 +0,0 @@
<script setup lang="ts">
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>
<UInputMenu v-model="selected" :options="people" searchable>
<template #option-empty="{ query }">
<q>{{ query }}</q> not found
</template>
</UInputMenu>
</template>

View File

@@ -1,25 +0,0 @@
<script setup lang="ts">
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>
<UInputMenu v-model="selected" :options="people" option-attribute="name">
<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>
</UInputMenu>
</template>

View File

@@ -1,9 +0,0 @@
<script setup lang="ts">
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>
<UInputMenu v-model="selected" :options="people" :popper="{ arrow: true }" />
</template>

View File

@@ -1,9 +0,0 @@
<script setup lang="ts">
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>
<UInputMenu v-model="selected" :options="people" :popper="{ offsetDistance: 0 }" />
</template>

View File

@@ -1,9 +0,0 @@
<script setup lang="ts">
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>
<UInputMenu v-model="selected" :options="people" :popper="{ placement: 'right-start' }" />
</template>

View File

@@ -1,26 +0,0 @@
<script setup lang="ts">
const loading = ref(false)
const selected = ref()
async function search (q: string) {
loading.value = true
const users = await $fetch<any[]>('https://jsonplaceholder.typicode.com/users', { params: { q } })
loading.value = false
return users
}
</script>
<template>
<UInputMenu
v-model="selected"
:search="search"
:loading="loading"
placeholder="Search for a user..."
option-attribute="name"
trailing
by="id"
/>
</template>

View File

@@ -1,28 +0,0 @@
<script setup lang="ts">
const options = [
{ id: 1, name: 'Wade Cooper', colors: ['red', 'yellow'] },
{ id: 2, name: 'Arlene Mccoy', colors: ['blue', 'yellow'] },
{ id: 3, name: 'Devon Webb', colors: ['green', 'blue'] },
{ id: 4, name: 'Tom Cook', colors: ['blue', 'red'] },
{ id: 5, name: 'Tanya Fox', colors: ['green', 'red'] },
{ id: 5, name: 'Hellen Schmidt', colors: ['green', 'yellow'] }
]
const selected = ref(options[1])
</script>
<template>
<UInputMenu
v-model="selected"
:options="options"
placeholder="Select a person"
by="id"
option-attribute="name"
:search-attributes="['name', 'colors']"
>
<template #option="{ option: person }">
<span v-for="color in person.colors" :key="color.id" class="h-2 w-2 rounded-full" :class="`bg-${color}-500 dark:bg-${color}-400`" />
<span class="truncate">{{ person.name }}</span>
</template>
</UInputMenu>
</template>

View File

@@ -1,15 +0,0 @@
<script setup lang="ts">
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()
const query = ref('Wade')
</script>
<template>
<UInputMenu
v-model="selected"
v-model:query="query"
:options="people"
placeholder="Select a person"
/>
</template>

View File

@@ -1,10 +0,0 @@
<script setup lang="ts">
const { metaSymbol } = useShortcuts()
</script>
<template>
<div class="flex items-center gap-0.5">
<UKbd>{{ metaSymbol }}</UKbd>
<UKbd>K</UKbd>
</div>
</template>

View File

@@ -1,17 +0,0 @@
<template>
<UMeterGroup :max="128">
<template #indicator>
<div class="flex gap-1.5 justify-between text-sm">
<p>86GB used</p>
<p class="text-gray-500 dark:text-gray-400">
42GB remaining
</p>
</div>
</template>
<UMeter :value="24" color="gray" label="System" icon="i-heroicons-cog-6-tooth" />
<UMeter :value="8" color="red" label="Apps" icon="i-heroicons-window" />
<UMeter :value="12" color="yellow" label="Documents" icon="i-heroicons-document" />
<UMeter :value="42" color="green" label="Multimedia" icon="i-heroicons-film" />
</UMeterGroup>
</template>

View File

@@ -1,15 +0,0 @@
<script setup lang="ts">
const used = ref(84.2)
const total = 238.42
</script>
<template>
<UMeter :value="used" :max="total">
<template #indicator="{ percent }">
<div class="text-sm text-right">
{{ used }}GB used ({{ Math.round(percent) }}%)
</div>
</template>
</UMeter>
</template>

View File

@@ -1,15 +0,0 @@
<script setup lang="ts">
const used = ref(84.2)
const total = 238.42
</script>
<template>
<UMeter :value="used" :max="total">
<template #label="{ percent }">
<p class="text-sm">
You are using {{ Math.round(used) }}GB ({{ Math.round(100 - percent) }}%) of space
</p>
</template>
</UMeter>
</template>

View File

@@ -1,15 +0,0 @@
<script setup lang="ts">
const isOpen = ref(false)
</script>
<template>
<div>
<UButton label="Open" @click="isOpen = true" />
<UModal v-model="isOpen">
<div class="p-4">
<Placeholder class="h-48" />
</div>
</UModal>
</div>
</template>

View File

@@ -1,23 +0,0 @@
<script setup lang="ts">
const isOpen = ref(false)
</script>
<template>
<div>
<UButton label="Open" @click="isOpen = true" />
<UModal v-model="isOpen">
<UCard :ui="{ ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
<template #header>
<Placeholder class="h-8" />
</template>
<Placeholder class="h-32" />
<template #footer>
<Placeholder class="h-8" />
</template>
</UCard>
</UModal>
</div>
</template>

View File

@@ -1,17 +0,0 @@
<script lang="ts" setup>
defineProps({
count: {
type: Number,
default: 0
}
})
</script>
<template>
<UModal>
<UCard>
<p>This modal was opened programmatically !</p>
<p>Count: {{ count }}</p>
</UCard>
</UModal>
</template>

View File

@@ -1,17 +0,0 @@
<script setup lang="ts">
import { ModalExampleComponent } from '#components'
const modal = useModal()
const count = ref(0)
function openModal () {
count.value += 1
modal.open(ModalExampleComponent, {
count: count.value
})
}
</script>
<template>
<UButton label="Reveal modal" @click="openModal" />
</template>

View File

@@ -1,15 +0,0 @@
<script setup lang="ts">
const isOpen = ref(false)
</script>
<template>
<div>
<UButton label="Open" @click="isOpen = true" />
<UModal v-model="isOpen" :overlay="false">
<div class="p-4">
<Placeholder class="h-48" />
</div>
</UModal>
</div>
</template>

View File

@@ -1,15 +0,0 @@
<script setup lang="ts">
const isOpen = ref(false)
</script>
<template>
<div>
<UButton label="Open" @click="isOpen = true" />
<UModal v-model="isOpen" :transition="false">
<div class="p-4">
<Placeholder class="h-48" />
</div>
</UModal>
</div>
</template>

View File

@@ -1,33 +0,0 @@
<script setup lang="ts">
const isOpen = ref(false)
</script>
<template>
<div>
<UButton label="Open" @click="isOpen = true" />
<UModal v-model="isOpen" fullscreen>
<UCard
:ui="{
base: 'h-full flex flex-col',
rounded: '',
divide: 'divide-y divide-gray-100 dark:divide-gray-800',
body: {
base: 'grow'
}
}"
>
<template #header>
<div class="flex items-center justify-between">
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
Modal
</h3>
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="isOpen = false" />
</div>
</template>
<Placeholder class="h-full" />
</UCard>
</UModal>
</div>
</template>

View File

@@ -1,24 +0,0 @@
<script setup lang="ts">
const isOpen = ref(false)
</script>
<template>
<div>
<UButton label="Open" @click="isOpen = true" />
<UModal v-model="isOpen" prevent-close>
<UCard :ui="{ ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
<template #header>
<div class="flex items-center justify-between">
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
Modal
</h3>
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="isOpen = false" />
</div>
</template>
<Placeholder class="h-32" />
</UCard>
</UModal>
</div>
</template>

View File

@@ -1,15 +0,0 @@
<script setup lang="ts">
const toast = useToast()
const actions = ref([{
label: 'Action 1',
click: () => alert('Action 1 clicked!')
}, {
label: 'Action 2',
click: () => alert('Action 2 clicked!')
}])
</script>
<template>
<UButton label="Show toast" @click="toast.add({ title: 'With actions', actions })" />
</template>

View File

@@ -1,7 +0,0 @@
<script setup lang="ts">
const toast = useToast()
</script>
<template>
<UButton label="Show toast" @click="toast.add({ title: 'Hello world!' })" />
</template>

Some files were not shown because too many files have changed in this diff Show More