mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-16 04:58:12 +01:00
Compare commits
50 Commits
v3.0.0-alp
...
docs/prere
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84fc8c7c88 | ||
|
|
1becead74e | ||
|
|
ebaf4c530b | ||
|
|
a7a4824ece | ||
|
|
15ca2f5701 | ||
|
|
08b9e4bff0 | ||
|
|
f49b49fd2c | ||
|
|
a74e8c4444 | ||
|
|
781081132d | ||
|
|
b1550d58ad | ||
|
|
e7b69b7d6f | ||
|
|
9478fc0768 | ||
|
|
2e9aeb5f05 | ||
|
|
86f2b4856c | ||
|
|
ba874c9191 | ||
|
|
ffc81cc950 | ||
|
|
d783387ed3 | ||
|
|
37655377e9 | ||
|
|
7ab88d30b2 | ||
|
|
ccb79b7ee4 | ||
|
|
c9806da6d8 | ||
|
|
3bccb6782a | ||
|
|
5a01a81577 | ||
|
|
3baddfd121 | ||
|
|
a7a1227c93 | ||
|
|
b259ddf271 | ||
|
|
c47ffc1cd5 | ||
|
|
0baa3a06d4 | ||
|
|
b140dbfe63 | ||
|
|
6d917baac0 | ||
|
|
c511c95537 | ||
|
|
de8228e504 | ||
|
|
29d2acf564 | ||
|
|
f5452ba0c5 | ||
|
|
b6a841e975 | ||
|
|
7bf85e9a09 | ||
|
|
9e8d50b2b8 | ||
|
|
b13b9e3ec0 | ||
|
|
126c893635 | ||
|
|
7d8b721bdd | ||
|
|
b120e8d998 | ||
|
|
2c4634a58f | ||
|
|
2cbf83eb84 | ||
|
|
da1b0bac04 | ||
|
|
7cc51d2efa | ||
|
|
c163ed8187 | ||
|
|
a6ecef0f0d | ||
|
|
faec8260a4 | ||
|
|
7a02bfeba6 | ||
|
|
9dd525ca26 |
@@ -1,4 +1,4 @@
|
||||
[](https://ui.nuxt.com)
|
||||
[](https://ui.nuxt.com)
|
||||
|
||||
# Nuxt UI
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
[![License][license-src]][license-href]
|
||||
[![Nuxt][nuxt-src]][nuxt-href]
|
||||
|
||||
We're thrilled to introduce Nuxt UI v3, a significant upgrade to our UI library that delivers extensive improvements and robust new capabilities. This major update harnesses the combined strengths of [Radix Vue](https://www.radix-vue.com/), [Tailwind CSS v4](https://tailwindcss.com/blog/tailwindcss-v4-alpha), and [Tailwind Variants](https://www.tailwind-variants.org/) to offer developers an unparalleled set of tools for creating sophisticated, accessible, and highly performant user interfaces.
|
||||
We're thrilled to introduce Nuxt UI v3, a significant upgrade to our UI library that delivers extensive improvements and robust new capabilities. This major update harnesses the combined strengths of [Radix Vue](https://www.radix-vue.com/), [Tailwind CSS v4](https://tailwindcss.com/docs/v4-beta), and [Tailwind Variants](https://www.tailwind-variants.org/) to offer developers an unparalleled set of tools for creating sophisticated, accessible, and highly performant user interfaces.
|
||||
|
||||
> [!NOTE]
|
||||
> You are on the `v3` development branch, check out the [dev branch](https://github.com/nuxt/ui) for Nuxt UI v2.
|
||||
@@ -105,7 +105,7 @@ Learn more in the [installation guide](https://ui3.nuxt.dev/getting-started/inst
|
||||
|
||||
## License
|
||||
|
||||
Licensed under the [MIT license](https://github.com/nuxt/ui/blob/dev/LICENSE.md).
|
||||
Licensed under the [MIT license](https://github.com/nuxt/ui/blob/v3/LICENSE.md).
|
||||
|
||||
<!-- Badges -->
|
||||
[npm-version-src]: https://img.shields.io/npm/v/@nuxt/ui/next.svg?style=flat&colorA=18181B&colorB=28CF8D
|
||||
@@ -115,7 +115,7 @@ Licensed under the [MIT license](https://github.com/nuxt/ui/blob/dev/LICENSE.md)
|
||||
[npm-downloads-href]: https://npm.chart.dev/@nuxt/ui
|
||||
|
||||
[license-src]: https://img.shields.io/github/license/nuxt/ui.svg?style=flat&colorA=18181B&colorB=28CF8D
|
||||
[license-href]: https://github.com/nuxt/ui/blob/main/LICENSE.md
|
||||
[license-href]: https://github.com/nuxt/ui/blob/v3/LICENSE.md
|
||||
|
||||
[nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt.js
|
||||
[nuxt-href]: https://nuxt.com
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"dependencies": {
|
||||
"@nuxt/ui": "latest",
|
||||
"knitwork": "^1.1.0",
|
||||
"nuxt": "^3.14.159",
|
||||
"nuxt": "^3.14.1592",
|
||||
"prettier": "^3.3.3",
|
||||
"zod": "^3.23.8"
|
||||
}
|
||||
|
||||
@@ -73,24 +73,41 @@ useServerSeoMeta({
|
||||
twitterCard: 'summary_large_image'
|
||||
})
|
||||
|
||||
const updatedNavigation = computed(() => navigation.value?.map(item => ({
|
||||
...item,
|
||||
children: item.children?.map((child: typeof item) => ({
|
||||
...child,
|
||||
...(child.path === '/getting-started/installation' && {
|
||||
title: 'Installation',
|
||||
active: route.path.startsWith('/getting-started/installation'),
|
||||
children: []
|
||||
}),
|
||||
...(child.path === '/getting-started/i18n' && {
|
||||
title: 'I18n',
|
||||
active: route.path.startsWith('/getting-started/i18n'),
|
||||
children: []
|
||||
})
|
||||
})) || []
|
||||
})))
|
||||
const { framework, frameworks } = useSharedData()
|
||||
|
||||
provide('navigation', updatedNavigation)
|
||||
const groups = computed(() => {
|
||||
return [{
|
||||
id: 'framework',
|
||||
label: 'Framework',
|
||||
items: frameworks.value
|
||||
}]
|
||||
})
|
||||
|
||||
function filterFrameworkItems(items: any[]) {
|
||||
return items?.filter(item => !item.framework || item.framework === framework.value)
|
||||
}
|
||||
|
||||
function processNavigationItem(item: any): any {
|
||||
if (item.shadow) {
|
||||
const matchingChild = filterFrameworkItems(item.children)?.[0]
|
||||
return matchingChild
|
||||
? {
|
||||
...matchingChild,
|
||||
title: item.title,
|
||||
children: matchingChild.children ? processNavigationItem(matchingChild) : undefined
|
||||
}
|
||||
: item
|
||||
}
|
||||
|
||||
return {
|
||||
...item,
|
||||
children: item.children?.length ? filterFrameworkItems(item.children)?.map(processNavigationItem) : undefined
|
||||
}
|
||||
}
|
||||
|
||||
const filteredNavigation = computed(() => navigation.value?.map(processNavigationItem))
|
||||
|
||||
provide('navigation', filteredNavigation)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -111,7 +128,13 @@ provide('navigation', updatedNavigation)
|
||||
<Footer />
|
||||
|
||||
<ClientOnly>
|
||||
<LazyUContentSearch v-model:search-term="searchTerm" :files="files" :navigation="navigation" :fuse="{ resultLimit: 42 }" />
|
||||
<LazyUContentSearch
|
||||
v-model:search-term="searchTerm"
|
||||
:files="files"
|
||||
:groups="groups"
|
||||
:navigation="filteredNavigation"
|
||||
:fuse="{ resultLimit: 42 }"
|
||||
/>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
</UApp>
|
||||
@@ -121,7 +144,7 @@ provide('navigation', updatedNavigation)
|
||||
@import "tailwindcss";
|
||||
@import "@nuxt/ui-pro";
|
||||
|
||||
@source "../content/**/*.md";
|
||||
@source "../content";
|
||||
|
||||
@theme {
|
||||
--container-8xl: 90rem;
|
||||
|
||||
10
docs/app/assets/icons/icones-js.svg
Normal file
10
docs/app/assets/icons/icones-js.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 283.46 283.46">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1{fill:#231815;}
|
||||
@media (prefers-color-scheme: dark) { .cls-1{fill:#ffffff;} }
|
||||
</style>
|
||||
</defs>
|
||||
<path class="cls-1" d="M144.89,89.86c-33.46,0-54.44,14.56-66.14,26.76a86,86,0,0,0-23.69,58.94c0,22.64,8.81,43.48,24.8,58.67,15.7,14.92,36.9,23.14,59.68,23.14,23.81,0,46-8.49,62.49-23.91,17-15.9,26.37-37.93,26.37-62C228.4,120.37,185.94,89.86,144.89,89.86Zm.49,153.67a61.49,61.49,0,0,1-46.45-20.4c-12.33-13.76-18.85-32.64-18.85-54.62,0-20.7,6.07-37.67,17.57-49.07,10.11-10,24.39-15.62,40.19-15.74,19,0,35.22,6.56,46.76,19,12.6,13.58,19.27,34,19.27,58.95C203.87,224.39,174.49,243.53,145.38,243.53Z"/>
|
||||
<polygon class="cls-1" points="198.75 74.96 179.45 74.96 142.09 37.83 104.51 74.96 86.14 74.96 138.09 24.25 146.81 24.25 198.75 74.96"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 855 B |
25
docs/app/components/FrameworkSelect.vue
Normal file
25
docs/app/components/FrameworkSelect.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<script setup lang="ts">
|
||||
const { framework, frameworks } = useSharedData()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UDropdownMenu
|
||||
v-slot="{ open }"
|
||||
:modal="false"
|
||||
:items="frameworks"
|
||||
:ui="{ content: 'w-(--radix-dropdown-menu-trigger-width)' }"
|
||||
>
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
v-bind="frameworks.find(f => f.value === framework)"
|
||||
block
|
||||
trailing-icon="i-lucide-chevron-down"
|
||||
:class="[open && 'bg-[var(--ui-bg-elevated)]']"
|
||||
:ui="{
|
||||
trailingIcon: ['transition-transform duration-200', open ? 'rotate-180' : undefined].filter(Boolean).join(' ')
|
||||
}"
|
||||
class="-mx-2 w-[calc(100%+1rem)]"
|
||||
/>
|
||||
</UDropdownMenu>
|
||||
</template>
|
||||
@@ -42,7 +42,7 @@ defineShortcuts({
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
to="https://github.com/nuxt/ui/tree/v3"
|
||||
to="https://github.com/nuxt/ui"
|
||||
target="_blank"
|
||||
icon="i-simple-icons-github"
|
||||
aria-label="GitHub"
|
||||
@@ -55,6 +55,8 @@ defineShortcuts({
|
||||
|
||||
<USeparator type="dashed" class="my-4" />
|
||||
|
||||
<FrameworkSelect class="mb-4" />
|
||||
|
||||
<UContentNavigation :navigation="navigation" highlight />
|
||||
</template>
|
||||
</UHeader>
|
||||
|
||||
@@ -3,9 +3,42 @@
|
||||
import json5 from 'json5'
|
||||
import { upperFirst, camelCase, kebabCase } from 'scule'
|
||||
import { hash } from 'ohash'
|
||||
import { CalendarDate } from '@internationalized/date'
|
||||
import * as theme from '#build/ui'
|
||||
import { get, set } from '#ui/utils'
|
||||
|
||||
interface Cast {
|
||||
get: (args: any) => any
|
||||
template: (args: any) => string
|
||||
}
|
||||
|
||||
type CastDateValue = [number, number, number]
|
||||
|
||||
const castMap: Record<string, Cast> = {
|
||||
'DateValue': {
|
||||
get: (args: CastDateValue) => new CalendarDate(...args),
|
||||
template: (value: CalendarDate) => {
|
||||
return value ? `new CalendarDate(${value.year}, ${value.month}, ${value.day})` : 'null'
|
||||
}
|
||||
},
|
||||
'DateValue[]': {
|
||||
get: (args: CastDateValue[]) => args.map(date => new CalendarDate(...date)),
|
||||
template: (value: CalendarDate[]) => {
|
||||
return value ? `[${value.map(date => `new CalendarDate(${date.year}, ${date.month}, ${date.day})`).join(', ')}]` : '[]'
|
||||
}
|
||||
},
|
||||
'DateRange': {
|
||||
get: (args: { start: CastDateValue, end: CastDateValue }) => ({ start: new CalendarDate(...args.start), end: new CalendarDate(...args.end) }),
|
||||
template: (value: { start: CalendarDate, end: CalendarDate }) => {
|
||||
if (!value.start || !value.end) {
|
||||
return `{ start: null, end: null }`
|
||||
}
|
||||
|
||||
return `{ start: new CalendarDate(${value.start.year}, ${value.start.month}, ${value.start.day}), end: new CalendarDate(${value.end.year}, ${value.end.month}, ${value.end.day}) }`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
/** Override the slug taken from the route */
|
||||
slug?: string
|
||||
@@ -18,6 +51,8 @@ const props = defineProps<{
|
||||
external?: string[]
|
||||
/** List of props to use with `v-model` */
|
||||
model?: string[]
|
||||
/** List of props to cast from code and selection */
|
||||
cast?: { [key: string]: string }
|
||||
/** List of items for each prop */
|
||||
items?: { [key: string]: string[] }
|
||||
props?: { [key: string]: any }
|
||||
@@ -45,7 +80,17 @@ const camelName = camelCase(props.slug ?? route.params.slug?.[route.params.slug.
|
||||
const name = `U${upperFirst(camelName)}`
|
||||
const component = defineAsyncComponent(() => import(`#ui/components/${upperFirst(camelName)}.vue`))
|
||||
|
||||
const componentProps = reactive({ ...(props.props || {}) })
|
||||
const componentProps = reactive({
|
||||
...Object.fromEntries(Object.entries(props.props || {}).map(([key, value]) => {
|
||||
const cast = props.cast?.[key]
|
||||
|
||||
if (cast && !castMap[cast]) {
|
||||
throw new Error(`Unknown cast: ${cast}`)
|
||||
}
|
||||
|
||||
return [key, cast ? castMap[cast]!.get(value) : value]
|
||||
}))
|
||||
})
|
||||
const componentEvents = reactive({
|
||||
...Object.fromEntries((props.model || []).map(key => [`onUpdate:${key}`, (e: any) => setComponentProp(key, e)])),
|
||||
...(componentProps.modelValue ? { [`onUpdate:modelValue`]: (e: any) => setComponentProp('modelValue', e) } : {})
|
||||
@@ -96,7 +141,7 @@ const options = computed(() => {
|
||||
return {
|
||||
name: key,
|
||||
label: key,
|
||||
type: prop?.type,
|
||||
type: props?.cast?.[key] ?? prop?.type,
|
||||
items
|
||||
}
|
||||
})
|
||||
@@ -117,7 +162,10 @@ const code = computed(() => {
|
||||
<script setup lang="ts">
|
||||
`
|
||||
for (const key of props.external) {
|
||||
code += `const ${key === 'modelValue' ? 'value' : key} = ref(${json5.stringify(componentProps[key], null, 2).replace(/,([ |\t\n]+[}|\]])/g, '$1')})
|
||||
const cast = props.cast?.[key]
|
||||
const value = cast ? castMap[cast]!.template(componentProps[key]) : json5.stringify(componentProps[key], null, 2).replace(/,([ |\t\n]+[}|\]])/g, '$1')
|
||||
|
||||
code += `const ${key === 'modelValue' ? 'value' : key} = ref(${value})
|
||||
`
|
||||
}
|
||||
code += `<\/script>
|
||||
|
||||
@@ -35,7 +35,7 @@ const schemaProps = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Collapsible v-if="schemaProps?.length" class="mt-1">
|
||||
<ProseCollapsible v-if="schemaProps?.length" class="mt-1">
|
||||
<ProseUl>
|
||||
<ProseLi v-for="schemaProp in schemaProps" :key="schemaProp.name">
|
||||
<HighlightInlineType :type="`${schemaProp.name}${schemaProp.required === false ? '?' : ''}: ${schemaProp.type}`" />
|
||||
@@ -43,5 +43,5 @@ const schemaProps = computed(() => {
|
||||
<MDC v-if="schemaProp.description" :value="schemaProp.description" class="text-[var(--ui-text-muted)] my-1" />
|
||||
</ProseLi>
|
||||
</ProseUl>
|
||||
</Collapsible>
|
||||
</ProseCollapsible>
|
||||
</template>
|
||||
|
||||
@@ -4,14 +4,19 @@ import { camelCase } from 'scule'
|
||||
import * as theme from '#build/ui'
|
||||
|
||||
const route = useRoute()
|
||||
const { framework } = useSharedData()
|
||||
|
||||
const name = camelCase(route.params.slug?.[route.params.slug.length - 1] ?? '')
|
||||
|
||||
const strippedCompoundVariants = ref(false)
|
||||
|
||||
function stripCompoundVariants(component?: any) {
|
||||
if (component?.compoundVariants) {
|
||||
component.compoundVariants = component.compoundVariants.filter((compoundVariant: any) => {
|
||||
const strippedTheme = computed(() => {
|
||||
const strippedTheme = {
|
||||
...(theme as any)[name]
|
||||
}
|
||||
|
||||
if (strippedTheme?.compoundVariants) {
|
||||
strippedTheme.compoundVariants = strippedTheme.compoundVariants.filter((compoundVariant: any) => {
|
||||
if (compoundVariant.color) {
|
||||
if (!['primary', 'neutral'].includes(compoundVariant.color)) {
|
||||
strippedCompoundVariants.value = true
|
||||
@@ -40,24 +45,43 @@ function stripCompoundVariants(component?: any) {
|
||||
})
|
||||
}
|
||||
|
||||
return component
|
||||
}
|
||||
return strippedTheme
|
||||
})
|
||||
|
||||
const component = computed(() => {
|
||||
return {
|
||||
ui: {
|
||||
[name]: stripCompoundVariants((theme as any)[name])
|
||||
[name]: strippedTheme.value
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const { data: ast } = await useAsyncData(`component-theme-${name}`, async () => {
|
||||
const { data: ast } = await useAsyncData(`component-theme-${name}-${framework.value}`, async () => {
|
||||
const md = `
|
||||
::code-collapse
|
||||
${framework.value === 'nuxt'
|
||||
? `
|
||||
\`\`\`ts [app.config.ts]
|
||||
export default defineAppConfig(${json5.stringify(component.value, null, 2).replace(/,([ |\t\n]+[}|\])])/g, '$1')})
|
||||
\`\`\`\
|
||||
`
|
||||
: `
|
||||
\`\`\`ts [vite.config.ts]
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import ui from '@nuxt/ui/vite'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
ui(${json5.stringify(component.value, null, 2).replace(/,([ |\t\n]+[}|\])])/g, '$1')
|
||||
.split('\n')
|
||||
.map((line, i) => i === 0 ? line : ` ${line}`)
|
||||
.join('\n')})
|
||||
]
|
||||
})
|
||||
\`\`\`
|
||||
`}
|
||||
::
|
||||
|
||||
${strippedCompoundVariants.value
|
||||
@@ -69,7 +93,7 @@ Some colors in \`compoundVariants\` are omitted for readability. Check out the s
|
||||
`
|
||||
|
||||
return parseMarkdown(md)
|
||||
})
|
||||
}, { watch: [framework] })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
7
docs/app/components/content/FrameworkOnly.vue
Normal file
7
docs/app/components/content/FrameworkOnly.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
const { framework } = useSharedData()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<slot :name="framework" />
|
||||
</template>
|
||||
@@ -2,8 +2,13 @@
|
||||
import json5 from 'json5'
|
||||
import icons from '../../../../src/theme/icons'
|
||||
|
||||
const { data: ast } = await useAsyncData(`icons-theme`, async () => {
|
||||
const { framework } = useSharedData()
|
||||
|
||||
const { data: ast } = await useAsyncData(`icons-theme-${framework.value}`, async () => {
|
||||
const md = `
|
||||
::code-collapse
|
||||
${framework.value === 'nuxt'
|
||||
? `
|
||||
\`\`\`ts [app.config.ts]
|
||||
export default defineAppConfig(${json5.stringify({
|
||||
ui: {
|
||||
@@ -11,10 +16,33 @@ export default defineAppConfig(${json5.stringify({
|
||||
}
|
||||
}, null, 2).replace(/,([ |\t\n]+[}|\])])/g, '$1')})
|
||||
\`\`\`\
|
||||
`
|
||||
: `
|
||||
\`\`\`ts [vite.config.ts]
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import ui from '@nuxt/ui/vite'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
ui(${json5.stringify({
|
||||
ui: {
|
||||
icons
|
||||
}
|
||||
}, null, 2).replace(/,([ |\t\n]+[}|\])])/g, '$1')
|
||||
.split('\n')
|
||||
.map((line, i) => i === 0 ? line : ` ${line}`)
|
||||
.join('\n')})
|
||||
]
|
||||
})
|
||||
\`\`\`
|
||||
`}
|
||||
::
|
||||
`
|
||||
|
||||
return parseMarkdown(md)
|
||||
})
|
||||
}, { watch: [framework] })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import * as locales from '@nuxt/ui/locale'
|
||||
import type { Locale } from '@nuxt/ui'
|
||||
import * as locales from '@nuxt/ui/locale'
|
||||
|
||||
type LocaleKey = keyof typeof locales
|
||||
type LocaleComputed = Locale & { flag: string }
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
default?: string
|
||||
@@ -10,8 +11,17 @@ const props = withDefaults(defineProps<{
|
||||
default: 'en'
|
||||
})
|
||||
|
||||
const countries = await $fetch('/api/locales.json')
|
||||
|
||||
const getLocaleKeys = Object.keys(locales) as LocaleKey[]
|
||||
const localesList = getLocaleKeys.map<[LocaleKey, Locale]>(locale => [locale, locales[locale]])
|
||||
const localesList = getLocaleKeys.map<LocaleComputed>((code) => {
|
||||
const locale: Locale = locales[code]
|
||||
|
||||
return {
|
||||
...locale,
|
||||
flag: countries[locale.code] || ''
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!-- eslint-disable vue/singleline-html-element-content-newline -->
|
||||
@@ -20,45 +30,24 @@ const localesList = getLocaleKeys.map<[LocaleKey, Locale]>(locale => [locale, lo
|
||||
<ProseP>
|
||||
By default, the <ProseCode>{{ props.default }}</ProseCode> locale is used.
|
||||
</ProseP>
|
||||
<ProseTable>
|
||||
<ProseThead>
|
||||
<ProseTr>
|
||||
<ProseTh>
|
||||
Language
|
||||
</ProseTh>
|
||||
<ProseTh>
|
||||
Code
|
||||
</ProseTh>
|
||||
<ProseTh>
|
||||
Direction
|
||||
</ProseTh>
|
||||
</ProseTr>
|
||||
</ProseThead>
|
||||
<ProseTbody>
|
||||
<ProseTr v-for="[key, locale] in localesList" :key="key">
|
||||
<ProseTd>
|
||||
{{ locale.name }}
|
||||
</ProseTd>
|
||||
<ProseTd>
|
||||
<ProseCode>
|
||||
{{ locale.code }}
|
||||
</ProseCode>
|
||||
</ProseTd>
|
||||
<ProseTd>
|
||||
<ProseCode>
|
||||
{{ locale.dir }}
|
||||
</ProseCode>
|
||||
</ProseTd>
|
||||
</ProseTr>
|
||||
</ProseTbody>
|
||||
</ProseTable>
|
||||
<Note to="https://github.com/nuxt/ui/tree/v3/src/runtime/locale" target="_blank">
|
||||
<div class="grid gap-6 grid-cols-2 md:grid-cols-3">
|
||||
<div v-for="locale in localesList" :key="locale.code">
|
||||
<div class="flex gap-3 items-center">
|
||||
<UAvatar :text="locale.flag" size="xl" />
|
||||
<div class="text-sm">
|
||||
<div class="font-semibold">{{ locale.name }}</div>
|
||||
<div class="mt-1">Code: <ProseCode class="text-xs">{{ locale.code }}</ProseCode></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ProseNote to="https://github.com/nuxt/ui/tree/v3/src/runtime/locale" target="_blank">
|
||||
If you need additional languages, you can contribute by creating a PR to add a new locale in <ProseCode>src/runtime/locale/</ProseCode>.
|
||||
</Note>
|
||||
<Tip>
|
||||
</ProseNote>
|
||||
<ProseTip>
|
||||
You can use the <ProseCode>nuxt-ui</ProseCode> CLI to create a new locale:
|
||||
|
||||
<ProsePre language="bash">nuxt-ui make locale --code "en" --name "English"</ProsePre>
|
||||
</Tip>
|
||||
</ProseTip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import { CalendarDate, DateFormatter, getLocalTimeZone } from '@internationalized/date'
|
||||
|
||||
const df = new DateFormatter('en-US', {
|
||||
dateStyle: 'medium'
|
||||
})
|
||||
|
||||
const modelValue = shallowRef(new CalendarDate(2022, 1, 10))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UPopover>
|
||||
<UButton color="neutral" variant="subtle" icon="i-lucide-calendar">
|
||||
{{ df.format(modelValue.toDate(getLocalTimeZone())) }}
|
||||
</UButton>
|
||||
|
||||
<template #content>
|
||||
<UCalendar v-model="modelValue" class="p-2" />
|
||||
</template>
|
||||
</UPopover>
|
||||
</template>
|
||||
@@ -0,0 +1,35 @@
|
||||
<script setup lang="ts">
|
||||
import { CalendarDate, DateFormatter, getLocalTimeZone } from '@internationalized/date'
|
||||
|
||||
const df = new DateFormatter('en-US', {
|
||||
dateStyle: 'medium'
|
||||
})
|
||||
|
||||
const modelValue = shallowRef({
|
||||
start: new CalendarDate(2022, 1, 20),
|
||||
end: new CalendarDate(2022, 2, 10)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UPopover>
|
||||
<UButton color="neutral" variant="subtle" icon="i-lucide-calendar">
|
||||
<template v-if="modelValue.start">
|
||||
<template v-if="modelValue.end">
|
||||
{{ df.format(modelValue.start.toDate(getLocalTimeZone())) }} - {{ df.format(modelValue.end.toDate(getLocalTimeZone())) }}
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
{{ df.format(modelValue.start.toDate(getLocalTimeZone())) }}
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
Pick a date
|
||||
</template>
|
||||
</UButton>
|
||||
|
||||
<template #content>
|
||||
<UCalendar v-model="modelValue" class="p-2" :number-of-months="2" range />
|
||||
</template>
|
||||
</UPopover>
|
||||
</template>
|
||||
@@ -0,0 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { CalendarDate } from '@internationalized/date'
|
||||
import type { Matcher } from 'radix-vue/date'
|
||||
|
||||
const modelValue = shallowRef({
|
||||
start: new CalendarDate(2022, 1, 1),
|
||||
end: new CalendarDate(2022, 1, 9)
|
||||
})
|
||||
|
||||
const isDateDisabled: Matcher = (date) => {
|
||||
return date.day >= 10 && date.day <= 16
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCalendar v-model="modelValue" :is-date-disabled="isDateDisabled" range />
|
||||
</template>
|
||||
@@ -0,0 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import { CalendarDate } from '@internationalized/date'
|
||||
|
||||
const modelValue = shallowRef(new CalendarDate(2022, 1, 10))
|
||||
|
||||
function getColorByDate(date: Date) {
|
||||
const isWeekend = date.getDay() % 6 == 0
|
||||
const isDayMeeting = date.getDay() % 3 == 0
|
||||
|
||||
if (isWeekend) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (isDayMeeting) {
|
||||
return 'error'
|
||||
}
|
||||
|
||||
return 'success'
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCalendar v-model="modelValue">
|
||||
<template #day="{ day }">
|
||||
<UChip :show="!!getColorByDate(day.toDate('UTC'))" :color="getColorByDate(day.toDate('UTC'))" size="2xs">
|
||||
{{ day.day }}
|
||||
</UChip>
|
||||
</template>
|
||||
</UCalendar>
|
||||
</template>
|
||||
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { CalendarDate } from '@internationalized/date'
|
||||
|
||||
const modelValue = shallowRef(new CalendarDate(2023, 9, 10))
|
||||
const minDate = new CalendarDate(2023, 9, 1)
|
||||
const maxDate = new CalendarDate(2023, 9, 30)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCalendar v-model="modelValue" :min-value="minDate" :max-value="maxDate" />
|
||||
</template>
|
||||
@@ -0,0 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { CalendarDate } from '@internationalized/date'
|
||||
import type { Matcher } from 'radix-vue/date'
|
||||
|
||||
const modelValue = shallowRef({
|
||||
start: new CalendarDate(2022, 1, 1),
|
||||
end: new CalendarDate(2022, 1, 9)
|
||||
})
|
||||
|
||||
const isDateUnavailable: Matcher = (date) => {
|
||||
return date.day >= 10 && date.day <= 16
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCalendar v-model="modelValue" :is-date-unavailable="isDateUnavailable" range />
|
||||
</template>
|
||||
@@ -32,7 +32,7 @@ const items = computed(() => [{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UContextMenu :items="items" class="w-48">
|
||||
<UContextMenu :items="items" :ui="{ content: 'w-48' }">
|
||||
<div class="flex items-center justify-center rounded-md border border-dashed border-[var(--ui-border-accented)] text-sm aspect-video w-72">
|
||||
Right click here
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,7 @@ const items = [
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UContextMenu :items="items" class="w-48">
|
||||
<UContextMenu :items="items" :ui="{ content: 'w-48' }">
|
||||
<div class="flex items-center justify-center rounded-md border border-dashed border-[var(--ui-border-accented)] text-sm aspect-video w-72">
|
||||
Right click here
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,7 @@ const items = [{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UContextMenu :items="items" class="w-48">
|
||||
<UContextMenu :items="items" :ui="{ content: 'w-48' }">
|
||||
<div class="flex items-center justify-center rounded-md border border-dashed border-[var(--ui-border-accented)] text-sm aspect-video w-72">
|
||||
Right click here
|
||||
</div>
|
||||
|
||||
@@ -40,7 +40,7 @@ const items = computed(() => [{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UDropdownMenu :items="items" :content="{ align: 'start' }" class="w-48">
|
||||
<UDropdownMenu :items="items" :content="{ align: 'start' }" :ui="{ content: 'w-48' }">
|
||||
<UButton label="Open" color="neutral" variant="outline" icon="i-lucide-menu" />
|
||||
</UDropdownMenu>
|
||||
</template>
|
||||
|
||||
@@ -25,7 +25,7 @@ const items = [
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UDropdownMenu :items="items" class="w-48">
|
||||
<UDropdownMenu :items="items" :ui="{ content: 'w-48' }">
|
||||
<UButton label="Open" color="neutral" variant="outline" icon="i-lucide-menu" />
|
||||
|
||||
<template #profile-trailing>
|
||||
|
||||
@@ -13,7 +13,7 @@ const items = [{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UDropdownMenu :items="items" class="w-48">
|
||||
<UDropdownMenu :items="items" :ui="{ content: 'w-48' }">
|
||||
<UButton label="Open" color="neutral" variant="outline" icon="i-lucide-menu" />
|
||||
|
||||
<template #profile-trailing>
|
||||
|
||||
@@ -18,7 +18,7 @@ const items = [{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UDropdownMenu v-model:open="open" :items="items" class="w-48">
|
||||
<UDropdownMenu v-model:open="open" :items="items" :ui="{ content: 'w-48' }">
|
||||
<UButton label="Open" color="neutral" variant="outline" icon="i-lucide-menu" />
|
||||
</UDropdownMenu>
|
||||
</template>
|
||||
|
||||
@@ -56,7 +56,7 @@ defineProps({
|
||||
<h1 class="m-0 text-[75px] font-semibold mb-2 text-white flex items-center">
|
||||
<span>{{ title }}</span>
|
||||
</h1>
|
||||
<p v-if="description" class="text-[32px] text-[#94a3b8] leading-tight">
|
||||
<p v-if="description" class="text-[32px] text-[#94a3b8] leading-tight text-balance">
|
||||
{{ description.slice(0, 200) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
23
docs/app/composables/useSharedData.ts
Normal file
23
docs/app/composables/useSharedData.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { createSharedComposable } from '@vueuse/core'
|
||||
|
||||
function _useSharedData() {
|
||||
const framework = useCookie('nuxt-ui-framework', { default: () => 'nuxt' })
|
||||
const frameworks = computed(() => [{
|
||||
label: 'Nuxt',
|
||||
icon: 'i-logos-nuxt-icon',
|
||||
value: 'nuxt',
|
||||
onSelect: () => framework.value = 'nuxt'
|
||||
}, {
|
||||
label: 'Vue',
|
||||
icon: 'i-logos-vue',
|
||||
value: 'vue',
|
||||
onSelect: () => framework.value = 'vue'
|
||||
}].map(f => ({ ...f, active: framework.value === f.value })))
|
||||
|
||||
return {
|
||||
framework,
|
||||
frameworks
|
||||
}
|
||||
}
|
||||
|
||||
export const useSharedData = createSharedComposable(_useSharedData)
|
||||
@@ -10,6 +10,10 @@ const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
|
||||
<UPage>
|
||||
<template #left>
|
||||
<UPageAside>
|
||||
<template #top>
|
||||
<FrameworkSelect />
|
||||
</template>
|
||||
|
||||
<UContentNavigation :navigation="navigation" highlight />
|
||||
</UPageAside>
|
||||
</template>
|
||||
|
||||
@@ -21,10 +21,32 @@ const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
|
||||
|
||||
const breadcrumb = computed(() => mapContentNavigation(findPageBreadcrumb(navigation?.value, page.value)).map(({ icon, ...link }) => link))
|
||||
|
||||
const { framework } = useSharedData()
|
||||
|
||||
// Redirect to the correct framework version if the page is not the current framework
|
||||
if (!import.meta.prerender) {
|
||||
watch(framework, () => {
|
||||
if (page.value?.navigation?.framework && page.value?.navigation?.framework !== framework.value) {
|
||||
if (route.path.endsWith(`/${page.value?.navigation?.framework}`)) {
|
||||
navigateTo(`${route.path.split('/').slice(0, -1).join('/')}/${framework.value}`)
|
||||
} else {
|
||||
navigateTo(`/getting-started`)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Update the framework if the page has a different framework
|
||||
watch(page, () => {
|
||||
if (page.value?.navigation?.framework && page.value?.navigation?.framework !== framework.value) {
|
||||
framework.value = page.value?.navigation?.framework as string
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
useSeoMeta({
|
||||
titleTemplate: '%s - Nuxt UI v3',
|
||||
title: typeof page.value.navigation === 'object' ? page.value.navigation.title : page.value.title,
|
||||
ogTitle: `${typeof page.value.navigation === 'object' ? page.value.navigation.title : page.value.title} - Nuxt UI v3`,
|
||||
title: typeof page.value.navigation === 'object' && page.value.navigation.title ? page.value.navigation.title : page.value.title,
|
||||
ogTitle: `${typeof page.value.navigation === 'object' && page.value.navigation.title ? page.value.navigation.title : page.value.title} - Nuxt UI v3`,
|
||||
description: page.value.description,
|
||||
ogDescription: page.value.description
|
||||
})
|
||||
@@ -75,21 +97,6 @@ const communityLinks = computed(() => [{
|
||||
</template>
|
||||
|
||||
<template #links>
|
||||
<UDropdownMenu v-if="page.select" v-slot="{ open }" :items="page.select.items" :content="{ align: 'end' }">
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="subtle"
|
||||
v-bind="page.select.items.find((item: any) => item.to === route.path)"
|
||||
block
|
||||
trailing-icon="i-lucide-chevron-down"
|
||||
:class="[open && 'bg-[var(--ui-bg-accented)]/75']"
|
||||
:ui="{
|
||||
trailingIcon: ['transition-transform duration-200', open ? 'rotate-180' : undefined].filter(Boolean).join(' ')
|
||||
}"
|
||||
class="w-[128px]"
|
||||
/>
|
||||
</UDropdownMenu>
|
||||
|
||||
<UButton
|
||||
v-for="link in page.links"
|
||||
:key="link.label"
|
||||
|
||||
@@ -5,6 +5,10 @@ export const collections = {
|
||||
type: 'page',
|
||||
source: '**/*',
|
||||
schema: z.object({
|
||||
navigation: z.object({
|
||||
title: z.string().optional(),
|
||||
framework: z.string().optional()
|
||||
}),
|
||||
links: z.array(z.object({
|
||||
label: z.string(),
|
||||
icon: z.string(),
|
||||
@@ -14,14 +18,7 @@ export const collections = {
|
||||
}).optional(),
|
||||
to: z.string(),
|
||||
target: z.string().optional()
|
||||
})),
|
||||
select: z.object({
|
||||
items: z.array(z.object({
|
||||
label: z.string(),
|
||||
icon: z.string(),
|
||||
to: z.string()
|
||||
}))
|
||||
})
|
||||
}))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -21,15 +21,15 @@ This transition empowers Nuxt UI to become a more comprehensive and flexible UI
|
||||
|
||||
### Tailwind CSS v4
|
||||
|
||||
Nuxt UI v3 integrates the latest Tailwind CSS v4 alpha (announced March 6, 2024), bringing significant improvements:
|
||||
Nuxt UI v3 integrates the latest Tailwind CSS v4 beta (released Nov 21, 2024), bringing significant improvements:
|
||||
|
||||
- **Faster Builds**: Up to 10x faster, especially for larger projects.
|
||||
- **Unified Toolchain**: Built-in features like vendor prefixing, nesting support, and modern CSS transforms.
|
||||
- **CSS-First Configuration**: New `@theme` directive for easy customization without JavaScript.
|
||||
- **Optimized Performance**: Smaller engine footprint and more efficient processing.
|
||||
- **Built for performance**: Full builds in the new engine are up to 5x faster, and incremental builds are over 100x faster — and measured in microseconds.
|
||||
- **Unified toolchain**: Built-in import handling, vendor prefixing, and syntax transforms, with no additional tooling required.
|
||||
- **CSS-first configuration**: A reimagined developer experience where you customize and extend the framework directly in CSS instead of a JavaScript configuration file.
|
||||
- **Designed for the modern web**: Built on native cascade layers, wide-gamut colors, and including first-class support for modern CSS features like container queries, @starting-style, popovers, and more.
|
||||
|
||||
::note
|
||||
For a comprehensive overview of Tailwind CSS v4 alpha features, visit the [official announcement](https://tailwindcss.com/blog/tailwindcss-v4-alpha).
|
||||
For a comprehensive overview of Tailwind CSS v4 beta features, read the [prerelease documentation](https://tailwindcss.com/docs/v4-beta).
|
||||
::
|
||||
|
||||
### Tailwind Variants
|
||||
@@ -71,7 +71,7 @@ You can now use Nuxt UI in any Vue project without Nuxt by adding the Vite and V
|
||||
- **Developer Experience**: Complete TypeScript support with IntelliSense and auto-completion
|
||||
|
||||
::tip{to="/getting-started/installation/vue"}
|
||||
Learn how to install and configure Nuxt UI in a Vue project in the [Vue installation guide](/getting-started/installation/vue).
|
||||
Learn how to install and configure Nuxt UI in a Vue project in the **Vue installation guide**.
|
||||
::
|
||||
|
||||
## Migration
|
||||
@@ -90,6 +90,10 @@ Key points to consider:
|
||||
The transition to v3 involves significant changes, including new component structures, updated theming approaches, and revised TypeScript definitions. We recommend a careful, incremental upgrade process, starting with thorough testing in a development environment.
|
||||
::
|
||||
|
||||
::accordion-item{label="Is Nuxt UI v3 compatible with standalone Vue projects?"}
|
||||
Nuxt UI is now compatible with Vue! You can follow the [installation guide](/getting-started/installation/vue) to get started.
|
||||
::
|
||||
|
||||
::accordion-item{label="What about Nuxt UI Pro?"}
|
||||
We've also rebuilt Nuxt UI Pro from scratch and released a `v3.0.0-alpha.x` package but it only contains the components to build this documentation yet. This will be a free update, so the license you buy now will be valid for v3. We're actively working to finish the rewrite of all Nuxt UI Pro components.
|
||||
::
|
||||
@@ -98,16 +102,12 @@ Key points to consider:
|
||||
Nuxt UI v3 is currently designed to work exclusively with Tailwind CSS. While there's interest in UnoCSS support, implementing it would require significant changes to the theme structure due to differences in class naming conventions. As a result, we don't have plans to add UnoCSS support in v3.
|
||||
::
|
||||
|
||||
::accordion-item{label="Is Nuxt UI v3 compatible with standalone Vue projects?"}
|
||||
We're planning to add Vue support in the near future. For now, Nuxt UI v3 is only available for Nuxt. Track progress on Vue compatibility [in this issue](https://github.com/nuxt/ui/issues/2129).
|
||||
::
|
||||
|
||||
::accordion-item{label="How does Nuxt UI v3 handle accessibility?"}
|
||||
Nuxt UI v3 enhances accessibility through Radix Vue integration. This provides automatic ARIA attributes, keyboard navigation support, intelligent focus management, and screen reader announcements. While offering a strong foundation, proper implementation and testing in your specific use case remains crucial for full accessibility compliance. For more detailed information, refer to [Radix Vue's accessibility documentation](https://www.radix-vue.com/overview/accessibility.html).
|
||||
::
|
||||
|
||||
::accordion-item{label="What is the testing approach for Nuxt UI v3?"}
|
||||
Nuxt UI v3 ensures reliability with 800+ Vitest tests, covering core functionality and accessibility. This robust testing suite supports the library's stability and serves as a reference for developers.
|
||||
Nuxt UI v3 ensures reliability with 1000+ Vitest tests, covering core functionality and accessibility. This robust testing suite supports the library's stability and serves as a reference for developers.
|
||||
::
|
||||
|
||||
::accordion-item{label="Is this version stable and suitable for production use?"}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
shadow: true
|
||||
@@ -1,17 +1,13 @@
|
||||
---
|
||||
title: Install in a Nuxt app
|
||||
navigation.title: Nuxt
|
||||
title: Installation
|
||||
description: 'Learn how to install and configure Nuxt UI in your Nuxt application.'
|
||||
select:
|
||||
items:
|
||||
- label: Nuxt
|
||||
icon: i-logos-nuxt-icon
|
||||
to: /getting-started/installation/nuxt
|
||||
- label: Vue
|
||||
icon: i-logos-vue
|
||||
to: /getting-started/installation/vue
|
||||
navigation.framework: nuxt
|
||||
---
|
||||
|
||||
::callout{to="/getting-started/installation/vue" icon="i-logos-vue" class="hidden"}
|
||||
Looking for the **Vue** version?
|
||||
::
|
||||
|
||||
## Setup
|
||||
|
||||
::steps{level="4"}
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
---
|
||||
navigation.title: Vue
|
||||
title: Install in a Vue app
|
||||
title: Installation
|
||||
description: 'Learn how to install and configure Nuxt UI in your Vue application.'
|
||||
select:
|
||||
items:
|
||||
- label: Nuxt
|
||||
icon: i-logos-nuxt-icon
|
||||
to: /getting-started/installation/nuxt
|
||||
- label: Vue
|
||||
icon: i-logos-vue
|
||||
to: /getting-started/installation/vue
|
||||
navigation.framework: vue
|
||||
---
|
||||
|
||||
::callout{to="/getting-started/installation/nuxt" icon="i-logos-nuxt-icon" class="hidden"}
|
||||
Looking for the **Nuxt** version?
|
||||
::
|
||||
|
||||
## Setup
|
||||
|
||||
::steps{level="4"}
|
||||
@@ -156,10 +152,6 @@ export default defineConfig({
|
||||
|
||||
Use the `ui` option to provide configuration for Nuxt UI.
|
||||
|
||||
::warning
|
||||
In the rest of the docs, there are references to the `app.config.ts` file (which is a Nuxt feature). When using Nuxt UI in a project without Nuxt, this configuration is passed in this `ui` option instead.
|
||||
::
|
||||
|
||||
```ts [vite.config.ts]
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
@@ -5,7 +5,7 @@ description: 'Learn how to customize Nuxt UI components using Tailwind CSS v4, C
|
||||
|
||||
## Tailwind CSS
|
||||
|
||||
Nuxt UI v3 uses Tailwind CSS v4 alpha which doesn't have a documentation yet, let's have a look on how to use it.
|
||||
Nuxt UI v3 uses Tailwind CSS v4 beta, you can read the [prerelease documentation](https://tailwindcss.com/docs/v4-beta) for more information.
|
||||
|
||||
### `@theme`
|
||||
|
||||
@@ -36,8 +36,8 @@ Tailwind CSS v4 takes a CSS-first configuration approach, you now customize your
|
||||
|
||||
The `@theme` directive tells Tailwind to make new utilities and variants available based on these variables. It's the equivalent of the `theme.extend` key in Tailwind CSS v3 `tailwind.config.ts` file.
|
||||
|
||||
::note
|
||||
You can learn more about this on [https://tailwindcss.com/blog/tailwindcss-v4-alpha](https://tailwindcss.com/blog/tailwindcss-v4-alpha#css-first-configuration).
|
||||
::note{to="https://tailwindcss.com/docs/v4-beta#css-first-configuration" target="_blank"}
|
||||
Learn more about Tailwind CSS v4 CSS-first configuration approach.
|
||||
::
|
||||
|
||||
### `@source`
|
||||
@@ -50,11 +50,11 @@ This can be useful when writing Tailwind classes in markdown files with [`@nuxt/
|
||||
@import "tailwindcss";
|
||||
@import "@nuxt/ui";
|
||||
|
||||
@source "../content/**/*.md";
|
||||
@source "../content";
|
||||
```
|
||||
|
||||
::note{to="https://github.com/tailwindlabs/tailwindcss/pull/14078"}
|
||||
You can learn more about the `@source` directive in this pull request.
|
||||
::note{to="https://tailwindcss.com/docs/v4-beta#adding-content-sources"}
|
||||
Learn how to add content sources in Tailwind CSS v4.
|
||||
::
|
||||
|
||||
### `@plugin`
|
||||
@@ -68,8 +68,8 @@ You can use the `@plugin` directive to import Tailwind CSS plugins.
|
||||
@plugin "@tailwindcss/typography";
|
||||
```
|
||||
|
||||
::note{to="https://github.com/tailwindlabs/tailwindcss/pull/14264"}
|
||||
You can learn more about the `@plugin` directive in this pull request.
|
||||
::note{to="https://tailwindcss.com/docs/v4-beta#using-plugins"}
|
||||
Learn more about using plugins in Tailwind CSS v4.
|
||||
::
|
||||
|
||||
## Design system
|
||||
@@ -78,8 +78,14 @@ Nuxt UI extends Tailwind CSS's theming capabilities, providing a flexible design
|
||||
|
||||
### Colors
|
||||
|
||||
::framework-only
|
||||
#nuxt
|
||||
Nuxt UI leverages Nuxt [App Config](https://nuxt.com/docs/guide/directory-structure/app-config#app-config-file) to provide customizable color aliases based on [Tailwind CSS colors](https://tailwindcss.com/docs/customizing-colors#color-palette-reference):
|
||||
|
||||
#vue
|
||||
Nuxt UI leverages Vite config to provide customizable color aliases based on [Tailwind CSS colors](https://tailwindcss.com/docs/customizing-colors#color-palette-reference):
|
||||
::
|
||||
|
||||
| Color | Default | Description |
|
||||
| --- | --- | --- |
|
||||
| `primary`{color="primary"} | `green` | Main brand color, used as the default color for components. |
|
||||
@@ -90,6 +96,8 @@ Nuxt UI leverages Nuxt [App Config](https://nuxt.com/docs/guide/directory-struct
|
||||
| `error`{color="error"} | `red` | Used for form error validation states. |
|
||||
| `neutral` | `slate` | Neutral color for backgrounds, text, etc. |
|
||||
|
||||
::framework-only
|
||||
#nuxt
|
||||
You can configure these color aliases at runtime in your `app.config.ts` file under the `ui.colors` key, allowing for dynamic theme customization without requiring an application rebuild:
|
||||
|
||||
```ts [app.config.ts]
|
||||
@@ -103,6 +111,30 @@ export default defineAppConfig({
|
||||
})
|
||||
```
|
||||
|
||||
#vue
|
||||
You can configure these color aliases at runtime in your `vite.config.ts` file under the `ui.colors` key:
|
||||
|
||||
```ts [vite.config.ts]
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import ui from '@nuxt/ui/vite'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
ui({
|
||||
ui: {
|
||||
colors: {
|
||||
primary: 'blue',
|
||||
neutral: 'zinc'
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
})
|
||||
```
|
||||
::
|
||||
|
||||
::note
|
||||
Try the :prose-icon{name="i-lucide-swatch-book" class="text-[var(--ui-primary)]"} theme picker in the header above to change `primary` and `neutral` colors.
|
||||
::
|
||||
@@ -118,8 +150,10 @@ slots:
|
||||
---
|
||||
::
|
||||
|
||||
::tip
|
||||
You can add you own dynamic color aliases in your `app.config.ts`, you just have to make sure to define them in the [`ui.theme.colors`](/getting-started/installation#themecolors) option in your `nuxt.config.ts` file.
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip
|
||||
You can add you own dynamic color aliases in your `app.config.ts`, you just have to make sure to define them in the [`ui.theme.colors`](/getting-started/installation/nuxt#themecolors) option in your `nuxt.config.ts` file.
|
||||
|
||||
```ts [app.config.ts]
|
||||
export default defineAppConfig({
|
||||
@@ -140,7 +174,34 @@ export default defineNuxtConfig({
|
||||
}
|
||||
})
|
||||
```
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip
|
||||
You can add you own dynamic color aliases in your `vite.config.ts`, you just have to make sure to also define them in the [`theme.colors`](/getting-started/installation/vue#themecolors) option of the `ui` plugin.
|
||||
|
||||
```ts [vite.config.ts]
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import ui from '@nuxt/ui/vite'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
ui({
|
||||
ui: {
|
||||
colors: {
|
||||
tertiary: 'indigo'
|
||||
}
|
||||
},
|
||||
theme: {
|
||||
colors: ['primary', 'secondary', 'tertiary', 'info', 'success', 'warning', 'error']
|
||||
}
|
||||
})
|
||||
]
|
||||
})
|
||||
```
|
||||
:::
|
||||
::
|
||||
|
||||
::warning
|
||||
@@ -490,7 +551,19 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
The `defaultVariants` property specifies the default values for each variant. It determines how a component looks and behaves when no prop is provided. These default values can be customized in your [`app.config.ts`](#appconfigts) to adjust the standard appearance of components throughout your application.
|
||||
The `defaultVariants` property specifies the default values for each variant. It determines how a component looks and behaves when no prop is provided.
|
||||
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip
|
||||
These default values can be customized in your [`app.config.ts`](#config) to adjust the standard appearance of components throughout your application.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip
|
||||
These default values can be customized in your [`vite.config.ts`](#config) to adjust the standard appearance of components throughout your application.
|
||||
:::
|
||||
::
|
||||
|
||||
## Customize theme
|
||||
|
||||
@@ -507,9 +580,11 @@ You can explore the theme for each component in two ways:
|
||||
- Browse the source code directly in the GitHub repository at https://github.com/nuxt/ui/tree/v3/src/theme.
|
||||
::
|
||||
|
||||
### `app.config.ts`
|
||||
### Config
|
||||
|
||||
You can override the theme of components inside your `app.config.ts` by using the exact same structure as the theme object.
|
||||
::framework-only
|
||||
#nuxt
|
||||
You can override the theme of components globally inside your `app.config.ts` by using the exact same structure as the theme object.
|
||||
|
||||
Let's say you want to change the font weight of all your buttons, you can do it like this:
|
||||
|
||||
@@ -525,13 +600,42 @@ export default defineAppConfig({
|
||||
})
|
||||
```
|
||||
|
||||
#vue
|
||||
You can override the theme of components globally inside your `vite.config.ts` by using the exact same structure as the theme object.
|
||||
|
||||
Let's say you want to change the font weight of all your buttons, you can do it like this:
|
||||
|
||||
```ts [vite.config.ts]
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import ui from '@nuxt/ui/vite'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
ui({
|
||||
ui: {
|
||||
button: {
|
||||
slots: {
|
||||
base: 'font-bold'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
})
|
||||
```
|
||||
::
|
||||
|
||||
::note
|
||||
In this example, the `font-bold` class will override the default `font-medium` class on all buttons.
|
||||
::
|
||||
|
||||
### `ui` prop
|
||||
### Props
|
||||
|
||||
You can also override a component's **slots** using the `ui` prop. This has priority over the `app.config.ts` configuration and `variants` resolution.
|
||||
#### `ui` prop
|
||||
|
||||
You can also override a component's **slots** using the `ui` prop. This has priority over the global configuration and `variants` resolution.
|
||||
|
||||
::component-code{slug="button"}
|
||||
---
|
||||
@@ -560,9 +664,9 @@ slots:
|
||||
In this example, the `trailingIcon` slot is overwritten with `size-3` even though the `md` size variant would apply a `size-5` class to it.
|
||||
::
|
||||
|
||||
### `class` prop
|
||||
#### `class` prop
|
||||
|
||||
The `class` prop allows you to override the classes of the `root` or `base` slot. This has priority over the `app.config.ts` configuration and `variants` resolution.
|
||||
The `class` prop allows you to override the classes of the `root` or `base` slot. This has priority over the global configuration and `variants` resolution.
|
||||
|
||||
::component-code{slug="button"}
|
||||
---
|
||||
|
||||
1
docs/content/1.getting-started/4.icons/.navigation.yml
Normal file
1
docs/content/1.getting-started/4.icons/.navigation.yml
Normal file
@@ -0,0 +1 @@
|
||||
shadow: true
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: Icons
|
||||
description: 'Nuxt UI integrates with Nuxt Icon to access over 200,000+ icons from Iconify.'
|
||||
navigation.framework: nuxt
|
||||
links:
|
||||
- label: 'Iconify'
|
||||
to: https://iconify.design/
|
||||
@@ -12,14 +13,14 @@ links:
|
||||
icon: i-simple-icons-github
|
||||
---
|
||||
|
||||
::callout{to="/getting-started/icons/vue" icon="i-logos-vue" class="hidden"}
|
||||
Looking for the **Vue** version?
|
||||
::
|
||||
|
||||
## Usage
|
||||
|
||||
Nuxt UI automatically registers the [`@nuxt/icon`](https://github.com/nuxt/icon) module for you, so there's no additional setup required.
|
||||
|
||||
::note
|
||||
You can use any name from the https://icones.js.org collection.
|
||||
::
|
||||
|
||||
### Icon Component
|
||||
|
||||
You can use the [Icon](/components/icon) component with a `name` prop to display an icon:
|
||||
@@ -32,6 +33,10 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::note
|
||||
You can use any name from the https://icones.js.org collection.
|
||||
::
|
||||
|
||||
### Component Props
|
||||
|
||||
Some components also have an `icon` prop to display an icon, like the [Button](/components/button) for example:
|
||||
55
docs/content/1.getting-started/4.icons/2.vue.md
Normal file
55
docs/content/1.getting-started/4.icons/2.vue.md
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: Icons
|
||||
description: 'Nuxt UI integrates with Iconify to access over 200,000+ icons.'
|
||||
navigation.framework: vue
|
||||
links:
|
||||
- label: 'Iconify'
|
||||
to: https://iconify.design/
|
||||
target: _blank
|
||||
icon: i-simple-icons-iconify
|
||||
---
|
||||
|
||||
::callout{to="/getting-started/icons/nuxt" icon="i-logos-nuxt-icon" class="hidden"}
|
||||
Looking for the **Nuxt** version?
|
||||
::
|
||||
|
||||
## Usage
|
||||
|
||||
### Icon Component
|
||||
|
||||
You can use the [Icon](/components/icon) component with a `name` prop to display an icon:
|
||||
|
||||
::component-code{slug="icon"}
|
||||
---
|
||||
props:
|
||||
name: 'i-lucide-lightbulb'
|
||||
class: 'size-5'
|
||||
---
|
||||
::
|
||||
|
||||
::note
|
||||
You can use any name from the https://icones.js.org collection.
|
||||
::
|
||||
|
||||
### Component Props
|
||||
|
||||
Some components also have an `icon` prop to display an icon, like the [Button](/components/button) for example:
|
||||
|
||||
::component-code{slug="button"}
|
||||
---
|
||||
ignore:
|
||||
- color
|
||||
- variant
|
||||
props:
|
||||
icon: i-lucide-sun
|
||||
variant: subtle
|
||||
slots:
|
||||
default: Button
|
||||
---
|
||||
::
|
||||
|
||||
## Theme
|
||||
|
||||
You can change the default icons used by Nuxt UI components in your `vite.config.ts`:
|
||||
|
||||
:icons-theme
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: Fonts
|
||||
description: 'Nuxt UI integrates with Nuxt Fonts to provide plug-and-play font optimization.'
|
||||
navigation.framework: nuxt
|
||||
links:
|
||||
- label: 'nuxt/fonts'
|
||||
to: https://github.com/nuxt/fonts
|
||||
@@ -10,7 +11,7 @@ links:
|
||||
|
||||
## Usage
|
||||
|
||||
Nuxt UI automatically registers the [`@nuxt/fonts`](https://github.com/nuxt/fonts) module for you, so there's no additional setup required. To use a font in your Nuxt UI application, you can simply declare it in your CSS:
|
||||
Nuxt UI automatically registers the [`@nuxt/fonts`](https://github.com/nuxt/fonts) module for you, so there's no additional setup required. To use a font in your Nuxt UI application, you can simply declare it in your CSS. It will be automatically loaded and optimized for you.
|
||||
|
||||
```css [main.css]
|
||||
@import "tailwindcss";
|
||||
@@ -21,8 +22,12 @@ Nuxt UI automatically registers the [`@nuxt/fonts`](https://github.com/nuxt/font
|
||||
}
|
||||
```
|
||||
|
||||
That's it! Nuxt Fonts will detect this and you should immediately see the web font loaded in your browser.
|
||||
You can disable the `@nuxt/fonts` module with the `ui.fonts` option in your `nuxt.config.ts`:
|
||||
|
||||
::note{to="https://fonts.nuxt.com/advanced" target="_blank"}
|
||||
Read more about how `@nuxt/fonts` work behind the scenes to optimize your fonts.
|
||||
::
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
ui: {
|
||||
fonts: false
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
---
|
||||
title: Color Mode
|
||||
description: 'Nuxt UI integrates with Nuxt Color Mode to allow for easy switching between light and dark themes.'
|
||||
links:
|
||||
- label: 'nuxtjs/color-mode'
|
||||
to: https://github.com/nuxt-modules/color-mode
|
||||
target: _blank
|
||||
icon: i-simple-icons-github
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
Nuxt UI automatically registers the [`@nuxtjs/color-mode`](https://github.com/nuxt-modules/color-mode) module for you, so there's no additional setup required.
|
||||
|
||||
You can disable dark mode by setting the `preference` to `light` instead of `system` in your `nuxt.config.ts`.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
colorMode: {
|
||||
preference: 'light'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
::tip
|
||||
If you're stuck in dark mode even after changing this setting, you might need to remove the `nuxt-color-mode` entry from your browser's local storage.
|
||||
::
|
||||
@@ -0,0 +1 @@
|
||||
shadow: true
|
||||
58
docs/content/1.getting-started/6.color-mode/1.nuxt.md
Normal file
58
docs/content/1.getting-started/6.color-mode/1.nuxt.md
Normal file
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: Color Mode
|
||||
description: 'Nuxt UI integrates with Nuxt Color Mode to allow for easy switching between light and dark themes.'
|
||||
navigation.framework: nuxt
|
||||
links:
|
||||
- label: 'nuxtjs/color-mode'
|
||||
to: https://github.com/nuxt-modules/color-mode
|
||||
target: _blank
|
||||
icon: i-simple-icons-github
|
||||
---
|
||||
|
||||
::callout{to="/getting-started/color-mode/vue" icon="i-logos-vue" class="hidden"}
|
||||
Looking for the **Vue** version?
|
||||
::
|
||||
|
||||
## Usage
|
||||
|
||||
Nuxt UI automatically registers the [`@nuxtjs/color-mode`](https://github.com/nuxt-modules/color-mode) module for you, so there's no additional setup required. You can simply use the `useColorMode` composable to switch between light and dark modes:
|
||||
|
||||
```vue [ColorModeButton.vue]
|
||||
<script setup>
|
||||
const colorMode = useColorMode()
|
||||
|
||||
const isDark = computed({
|
||||
get() {
|
||||
return colorMode.value === 'dark'
|
||||
},
|
||||
set() {
|
||||
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ClientOnly v-if="!colorMode?.forced">
|
||||
<UButton
|
||||
:icon="isDark ? 'i-lucide-moon' : 'i-lucide-sun'"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
@click="isDark = !isDark"
|
||||
/>
|
||||
|
||||
<template #fallback>
|
||||
<div class="size-8" />
|
||||
</template>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
```
|
||||
|
||||
You can disable the `@nuxtjs/color-mode` module with the `ui.colorMode` option in your `nuxt.config.ts`:
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
ui: {
|
||||
colorMode: false
|
||||
}
|
||||
})
|
||||
```
|
||||
47
docs/content/1.getting-started/6.color-mode/2.vue.md
Normal file
47
docs/content/1.getting-started/6.color-mode/2.vue.md
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: Color Mode
|
||||
description: 'Nuxt UI integrates with VueUse to allow for easy switching between light and dark themes.'
|
||||
navigation.framework: vue
|
||||
---
|
||||
|
||||
::callout{to="/getting-started/color-mode/nuxt" icon="i-logos-nuxt-icon" class="hidden"}
|
||||
Looking for the **Nuxt** version?
|
||||
::
|
||||
|
||||
## Usage
|
||||
|
||||
Nuxt UI automatically registers the [useDark](https://vueuse.org/core/useDark) composable as a Vue plugin, so there's no additional setup required. You can simply use it to switch between light and dark modes:
|
||||
|
||||
```vue [ColorModeButton.vue]
|
||||
<script setup>
|
||||
import { useColorMode } from '@vueuse/core'
|
||||
|
||||
const mode = useColorMode()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UButton
|
||||
:icon="mode === 'dark' ? 'i-lucide-moon' : 'i-lucide-sun'"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
@click="mode = mode === 'dark' ? 'light' : 'dark'"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
|
||||
You can disable this plugin with the `colorMode` option in your `vite.config.ts`:
|
||||
|
||||
```ts [vite.config.ts]
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import ui from '@nuxt/ui/vite'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
ui({
|
||||
colorMode: false
|
||||
})
|
||||
]
|
||||
})
|
||||
```
|
||||
@@ -1 +1,2 @@
|
||||
badge: New
|
||||
shadow: true
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
---
|
||||
navigation.title: Nuxt
|
||||
title: Internationalization (i18n) in a Nuxt app
|
||||
title: Internationalization (i18n)
|
||||
description: 'Learn how to internationalize your Nuxt app with multi-directional support (LTR/RTL).'
|
||||
select:
|
||||
items:
|
||||
- label: Nuxt
|
||||
icon: i-logos-nuxt-icon
|
||||
to: /getting-started/i18n/nuxt
|
||||
- label: Vue
|
||||
icon: i-logos-vue
|
||||
to: /getting-started/i18n/vue
|
||||
navigation.framework: nuxt
|
||||
---
|
||||
|
||||
::callout{to="/getting-started/i18n/vue" icon="i-logos-vue" class="hidden"}
|
||||
Looking for the **Vue** version?
|
||||
::
|
||||
|
||||
## Usage
|
||||
|
||||
::note{to="/components/app"}
|
||||
Nuxt UI provides an [App](/components/app) component that wraps your app to provide global configurations.
|
||||
Nuxt UI provides an **App** component that wraps your app to provide global configurations.
|
||||
::
|
||||
|
||||
### Locale
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
---
|
||||
navigation.title: Vue
|
||||
title: Internationalization (i18n) in a Vue app
|
||||
title: Internationalization (i18n)
|
||||
description: 'Learn how to internationalize your Vue app with multi-directional support (LTR/RTL).'
|
||||
select:
|
||||
items:
|
||||
- label: Nuxt
|
||||
icon: i-logos-nuxt-icon
|
||||
to: /getting-started/i18n/nuxt
|
||||
- label: Vue
|
||||
icon: i-logos-vue
|
||||
to: /getting-started/i18n/vue
|
||||
navigation.framework: vue
|
||||
---
|
||||
|
||||
::callout{to="/getting-started/i18n/nuxt" icon="i-logos-nuxt-icon" class="hidden"}
|
||||
Looking for the **Nuxt** version?
|
||||
::
|
||||
|
||||
## Usage
|
||||
|
||||
::note{to="/components/app"}
|
||||
Nuxt UI provides an [App](/components/app) component that wraps your app to provide global configurations.
|
||||
Nuxt UI provides an **App** component that wraps your app to provide global configurations.
|
||||
::
|
||||
|
||||
### Locale
|
||||
|
||||
@@ -27,8 +27,16 @@ Use it as at the root of your app:
|
||||
</template>
|
||||
```
|
||||
|
||||
::tip{to="/getting-started/i18n/nuxt#locale"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/i18n/nuxt#locale"}
|
||||
Learn how to use the `locale` prop to change the locale of your app.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/i18n/vue#locale"}
|
||||
Learn how to use the `locale` prop to change the locale of your app.
|
||||
:::
|
||||
::
|
||||
|
||||
## API
|
||||
|
||||
@@ -168,8 +168,16 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.chevronDown` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.chevronDown` key.
|
||||
:::
|
||||
::
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -178,8 +178,16 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.close` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.close` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Actions
|
||||
|
||||
@@ -67,8 +67,16 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.chevronRight` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.chevronRight` key.
|
||||
:::
|
||||
::
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -197,8 +197,16 @@ slots:
|
||||
Button
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.loading` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.loading` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Disabled
|
||||
|
||||
248
docs/content/3.components/calendar.md
Normal file
248
docs/content/3.components/calendar.md
Normal file
@@ -0,0 +1,248 @@
|
||||
---
|
||||
title: Calendar
|
||||
description: A calendar component for selecting single dates, multiple dates or date ranges.
|
||||
links:
|
||||
- label: Calendar
|
||||
icon: i-custom-radix-vue
|
||||
to: https://www.radix-vue.com/components/calendar.html
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Calendar.vue
|
||||
navigation.badge: New
|
||||
---
|
||||
|
||||
::note
|
||||
This component relies on the [@internationalized/date](https://react-spectrum.adobe.com/internationalized/date/index.html) package which provides objects and functions for representing and manipulating dates and times in a locale-aware manner.
|
||||
::
|
||||
|
||||
## Usage
|
||||
|
||||
Use the `v-model` directive to control the selected date.
|
||||
|
||||
::component-code
|
||||
---
|
||||
cast:
|
||||
modelValue: DateValue
|
||||
ignore:
|
||||
- modelValue
|
||||
external:
|
||||
- modelValue
|
||||
props:
|
||||
modelValue: [2022, 2, 3]
|
||||
---
|
||||
::
|
||||
|
||||
Use the `default-value` prop to set the initial value when you do not need to control its state.
|
||||
|
||||
::component-code
|
||||
---
|
||||
cast:
|
||||
defaultValue: DateValue
|
||||
ignore:
|
||||
- defaultValue
|
||||
external:
|
||||
- defaultValue
|
||||
props:
|
||||
defaultValue: [2022, 2, 6]
|
||||
---
|
||||
::
|
||||
|
||||
### Multiple
|
||||
|
||||
Use the `multiple` prop to allow multiple selections.
|
||||
|
||||
::component-code
|
||||
---
|
||||
prettier: true
|
||||
cast:
|
||||
modelValue: DateValue[]
|
||||
ignore:
|
||||
- multiple
|
||||
- modelValue
|
||||
external:
|
||||
- modelValue
|
||||
props:
|
||||
multiple: true
|
||||
modelValue: [[2022, 2, 4], [2022, 2, 6], [2022, 2, 8]]
|
||||
---
|
||||
::
|
||||
|
||||
### Range
|
||||
|
||||
Use the `range` prop to select a range of dates.
|
||||
|
||||
::component-code
|
||||
---
|
||||
prettier: true
|
||||
cast:
|
||||
modelValue: DateRange
|
||||
ignore:
|
||||
- range
|
||||
- modelValue.start
|
||||
- modelValue.end
|
||||
external:
|
||||
- modelValue
|
||||
props:
|
||||
range: true
|
||||
modelValue:
|
||||
start: [2022, 2, 3]
|
||||
end: [2022, 2, 20]
|
||||
---
|
||||
::
|
||||
|
||||
### Color
|
||||
|
||||
Use the `color` prop to change the color of the calendar.
|
||||
|
||||
::component-code
|
||||
---
|
||||
props:
|
||||
color: neutral
|
||||
---
|
||||
::
|
||||
|
||||
### Size
|
||||
|
||||
Use the `size` prop to change the size of the calendar.
|
||||
|
||||
::component-code
|
||||
---
|
||||
props:
|
||||
size: xl
|
||||
---
|
||||
::
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` prop to disable the calendar.
|
||||
|
||||
::component-code
|
||||
---
|
||||
props:
|
||||
disabled: true
|
||||
---
|
||||
::
|
||||
|
||||
### Number Of Months
|
||||
|
||||
Use the `numberOfMonths` prop to change the number of months in the calendar.
|
||||
|
||||
::component-code
|
||||
---
|
||||
props:
|
||||
numberOfMonths: 3
|
||||
---
|
||||
::
|
||||
|
||||
### Month Controls
|
||||
|
||||
Use the `month-controls` prop to show the month controls. Defaults to `true`.
|
||||
|
||||
::component-code
|
||||
---
|
||||
props:
|
||||
monthControls: false
|
||||
---
|
||||
::
|
||||
|
||||
### Year Controls
|
||||
|
||||
Use the `year-controls` prop to show the year controls. Defaults to `true`.
|
||||
|
||||
::component-code
|
||||
---
|
||||
props:
|
||||
yearControls: false
|
||||
---
|
||||
::
|
||||
|
||||
### Fixed Weeks
|
||||
|
||||
Use the `fixed-weeks` prop to display the calendar with fixed weeks.
|
||||
|
||||
::component-code
|
||||
---
|
||||
props:
|
||||
fixedWeeks: false
|
||||
---
|
||||
::
|
||||
|
||||
## Examples
|
||||
|
||||
### With chip events
|
||||
|
||||
Use the [Chip](/components/chip) component to add events to specific days.
|
||||
|
||||
::component-example
|
||||
---
|
||||
name: 'calendar-events-example'
|
||||
---
|
||||
::
|
||||
|
||||
### With disabled dates
|
||||
|
||||
Use the `is-date-disabled` prop with a function to mark specific dates as disabled.
|
||||
|
||||
::component-example
|
||||
---
|
||||
name: 'calendar-disabled-dates-example'
|
||||
---
|
||||
::
|
||||
|
||||
### With unavailable dates
|
||||
|
||||
Use the `is-date-unavailable` prop with a function to mark specific dates as unavailable.
|
||||
|
||||
::component-example
|
||||
---
|
||||
name: 'calendar-unavailable-dates-example'
|
||||
---
|
||||
::
|
||||
|
||||
### With min/max dates
|
||||
|
||||
Use the `min-value` and `max-value` props to limit the dates.
|
||||
|
||||
::component-example
|
||||
---
|
||||
name: 'calendar-min-max-dates-example'
|
||||
---
|
||||
::
|
||||
|
||||
### As a DatePicker
|
||||
|
||||
Use a [Button](/components/button) and a [Popover](/components/popover) component to create a date picker.
|
||||
|
||||
::component-example
|
||||
---
|
||||
name: 'calendar-date-picker-example'
|
||||
---
|
||||
::
|
||||
|
||||
### As a DateRangePicker
|
||||
|
||||
Use a [Button](/components/button) and a [Popover](/components/popover) component to create a date range picker.
|
||||
|
||||
::component-example
|
||||
---
|
||||
name: 'calendar-date-range-picker-example'
|
||||
---
|
||||
::
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
:component-props
|
||||
|
||||
### Slots
|
||||
|
||||
:component-slots
|
||||
|
||||
### Emits
|
||||
|
||||
:component-emits
|
||||
|
||||
## Theme
|
||||
|
||||
:component-theme
|
||||
@@ -94,8 +94,16 @@ options:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize these icons globally in your `app.config.ts` under `ui.icons.arrowLeft` / `ui.icons.arrowRight` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize these icons globally in your `vite.config.ts` under `ui.icons.arrowLeft` / `ui.icons.arrowRight` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Dots
|
||||
|
||||
@@ -58,8 +58,16 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.minus` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.minus` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Label
|
||||
@@ -115,8 +123,16 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.check` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.check` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Color
|
||||
|
||||
@@ -42,6 +42,7 @@ Each group takes some `items` as an array of objects with the following properti
|
||||
- `avatar?: AvatarProps`{lang="ts-type"}
|
||||
- `chip?: ChipProps`{lang="ts-type"}
|
||||
- `kbds?: string[] | KbdProps[]`{lang="ts-type"}
|
||||
- `active?: boolean`{lang="ts-type"}
|
||||
- `loading?: boolean`{lang="ts-type"}
|
||||
- `disabled?: boolean`{lang="ts-type"}
|
||||
- [`slot?: string`{lang="ts-type"}](#with-custom-slot)
|
||||
@@ -215,8 +216,16 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.search` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.search` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Loading
|
||||
@@ -276,8 +285,16 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.loading` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.loading` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Disabled
|
||||
@@ -403,8 +420,16 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.close` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.close` key.
|
||||
:::
|
||||
::
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -40,7 +40,7 @@ prettier: true
|
||||
collapse: true
|
||||
ignore:
|
||||
- items
|
||||
- class
|
||||
- ui.content
|
||||
external:
|
||||
- items
|
||||
props:
|
||||
@@ -90,7 +90,8 @@ props:
|
||||
- option
|
||||
- meta
|
||||
- j
|
||||
class: 'w-48'
|
||||
ui:
|
||||
content: 'w-48'
|
||||
slots:
|
||||
default: |
|
||||
|
||||
@@ -119,7 +120,7 @@ Use the `size` prop to change the size of the ContextMenu.
|
||||
prettier: true
|
||||
ignore:
|
||||
- items
|
||||
- class
|
||||
- ui.content
|
||||
external:
|
||||
- items
|
||||
props:
|
||||
@@ -131,7 +132,8 @@ props:
|
||||
icon: i-lucide-sun
|
||||
- label: Dark
|
||||
icon: i-lucide-moon
|
||||
class: 'w-48'
|
||||
ui:
|
||||
content: 'w-48'
|
||||
slots:
|
||||
default: |
|
||||
|
||||
@@ -152,7 +154,7 @@ Use the `disabled` prop to disable the ContextMenu.
|
||||
prettier: true
|
||||
ignore:
|
||||
- items
|
||||
- class
|
||||
- ui.content
|
||||
external:
|
||||
- items
|
||||
props:
|
||||
@@ -164,7 +166,8 @@ props:
|
||||
icon: i-lucide-sun
|
||||
- label: Dark
|
||||
icon: i-lucide-moon
|
||||
class: 'w-48'
|
||||
ui:
|
||||
content: 'w-48'
|
||||
slots:
|
||||
default: |
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ prettier: true
|
||||
collapse: true
|
||||
ignore:
|
||||
- items
|
||||
- class
|
||||
- ui.content
|
||||
external:
|
||||
- items
|
||||
props:
|
||||
@@ -91,7 +91,8 @@ props:
|
||||
- shift
|
||||
- meta
|
||||
- q
|
||||
class: 'w-48'
|
||||
ui:
|
||||
content: 'w-48'
|
||||
slots:
|
||||
default: |
|
||||
|
||||
@@ -118,7 +119,7 @@ Use the `content` prop to control how the DropdownMenu content is rendered, like
|
||||
prettier: true
|
||||
ignore:
|
||||
- items
|
||||
- class
|
||||
- ui.content
|
||||
external:
|
||||
- items
|
||||
items:
|
||||
@@ -143,7 +144,8 @@ props:
|
||||
align: start
|
||||
side: bottom
|
||||
sideOffset: 8
|
||||
class: 'w-48'
|
||||
ui:
|
||||
content: 'w-48'
|
||||
slots:
|
||||
default: |
|
||||
|
||||
@@ -163,7 +165,7 @@ prettier: true
|
||||
ignore:
|
||||
- arrow
|
||||
- items
|
||||
- class
|
||||
- ui.content
|
||||
external:
|
||||
- items
|
||||
props:
|
||||
@@ -175,7 +177,8 @@ props:
|
||||
icon: i-lucide-credit-card
|
||||
- label: Settings
|
||||
icon: i-lucide-cog
|
||||
class: 'w-48'
|
||||
ui:
|
||||
content: 'w-48'
|
||||
slots:
|
||||
default: |
|
||||
|
||||
@@ -194,8 +197,8 @@ Use the `size` prop to control the size of the DropdownMenu.
|
||||
prettier: true
|
||||
ignore:
|
||||
- items
|
||||
- class
|
||||
- content.align
|
||||
- ui.content
|
||||
external:
|
||||
- items
|
||||
props:
|
||||
@@ -209,7 +212,8 @@ props:
|
||||
icon: i-lucide-cog
|
||||
content:
|
||||
align: start
|
||||
class: 'w-48'
|
||||
ui:
|
||||
content: 'w-48'
|
||||
slots:
|
||||
default: |
|
||||
|
||||
@@ -236,7 +240,7 @@ Use the `disabled` prop to disable the DropdownMenu.
|
||||
prettier: true
|
||||
ignore:
|
||||
- items
|
||||
- class
|
||||
- ui.content
|
||||
external:
|
||||
- items
|
||||
props:
|
||||
@@ -248,7 +252,8 @@ props:
|
||||
icon: i-lucide-credit-card
|
||||
- label: Settings
|
||||
icon: i-lucide-cog
|
||||
class: 'w-48'
|
||||
ui:
|
||||
content: 'w-48'
|
||||
slots:
|
||||
default: |
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
---
|
||||
description: A wrapper around Nuxt Icon component to display icons.
|
||||
description: A component to display any icon from Iconify.
|
||||
links:
|
||||
- label: Nuxt Icon
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/icon
|
||||
- label: Icônes
|
||||
to: https://icones.js.org/
|
||||
target: _blank
|
||||
icon: i-custom-icones-js
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
You can use any name from the https://icones.js.org collection:
|
||||
Use the `name` prop to display an icon:
|
||||
|
||||
::component-code
|
||||
---
|
||||
@@ -18,6 +19,15 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip
|
||||
It's highly recommended to install the icons collections you need, read more about this in [Icons](/getting-started/icons#collections).
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::caution{to="/getting-started/icons/nuxt#collections"}
|
||||
It's highly recommended to install the icons collections you need, read more about this.
|
||||
:::
|
||||
::
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
:component-props
|
||||
|
||||
@@ -189,8 +189,16 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.close` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.close` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Placeholder
|
||||
@@ -441,8 +449,16 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.chevronDown` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.chevronDown` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Selected Icon
|
||||
@@ -470,8 +486,16 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.check` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.check` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Avatar
|
||||
@@ -550,8 +574,16 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.loading` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.loading` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Disabled
|
||||
@@ -739,7 +771,7 @@ name: 'input-menu-filter-fields-example'
|
||||
---
|
||||
::
|
||||
|
||||
### As a country picker
|
||||
### As a CountryPicker
|
||||
|
||||
This example demonstrates using the InputMenu as a country picker with lazy loading - countries are only fetched when the menu is opened.
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@ links:
|
||||
navigation.badge: New
|
||||
---
|
||||
|
||||
::note
|
||||
This component relies on the [@internationalized/number](https://react-spectrum.adobe.com/internationalized/number/index.html) package which provides utilities for formatting and parsing numbers across locales and numbering systems.
|
||||
::
|
||||
|
||||
## Usage
|
||||
|
||||
Use the `v-model` directive to control the value of the InputNumber.
|
||||
|
||||
@@ -184,8 +184,16 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.loading` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.loading` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Disabled
|
||||
@@ -290,7 +298,7 @@ name: 'input-form-field-example'
|
||||
::
|
||||
|
||||
::tip{to="/components/form"}
|
||||
It also provides validation and error handling when used within a [Form](/components/form) component.
|
||||
It also provides validation and error handling when used within a **Form** component.
|
||||
::
|
||||
|
||||
### Within a ButtonGroup
|
||||
|
||||
@@ -156,8 +156,16 @@ slots:
|
||||
:placeholder{class="h-48"}
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.close` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.close` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Overlay
|
||||
|
||||
@@ -434,8 +434,16 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.chevronDown` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.chevronDown` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Arrow
|
||||
|
||||
@@ -482,8 +482,16 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.chevronDown` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.chevronDown` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Selected Icon
|
||||
@@ -513,8 +521,16 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.check` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.check` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Avatar
|
||||
@@ -599,8 +615,16 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.loading` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.loading` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Disabled
|
||||
@@ -782,7 +806,7 @@ name: 'select-menu-filter-fields-example'
|
||||
---
|
||||
::
|
||||
|
||||
### As a country picker
|
||||
### As a CountryPicker
|
||||
|
||||
This example demonstrates using the SelectMenu as a country picker with lazy loading - countries are only fetched when the menu is opened.
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ props:
|
||||
::
|
||||
|
||||
::note{to="https://www.radix-vue.com/components/select.html#change-the-positioning-mode"}
|
||||
Read more about the `content.position` prop in the [Radix Vue documentation](https://www.radix-vue.com/components/select.html#change-the-positioning-mode).
|
||||
Read more about the `content.position` prop in the **Radix Vue** documentation.
|
||||
::
|
||||
|
||||
<!--
|
||||
@@ -378,8 +378,16 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.chevronDown` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.chevronDown` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Selected Icon
|
||||
@@ -409,8 +417,16 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.check` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.check` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Avatar
|
||||
@@ -495,8 +511,16 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.loading` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.loading` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Disabled
|
||||
|
||||
@@ -156,8 +156,16 @@ slots:
|
||||
:placeholder{class="h-full"}
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.close` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.close` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Side
|
||||
|
||||
@@ -123,8 +123,16 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.loading` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.loading` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Color
|
||||
|
||||
@@ -128,8 +128,16 @@ name: 'toast-close-icon-example'
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="/getting-started/icons#theme"}
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.close` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.close` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Actions
|
||||
|
||||
@@ -17,7 +17,12 @@ export default defineNuxtConfig({
|
||||
'@nuxtjs/plausible',
|
||||
'@vueuse/nuxt',
|
||||
'nuxt-component-meta',
|
||||
'nuxt-og-image'
|
||||
'nuxt-og-image',
|
||||
(_, nuxt) => {
|
||||
nuxt.hook('components:dirs', (dirs) => {
|
||||
dirs.unshift({ path: resolve('./app/components/content/examples'), pathPrefix: false, prefix: '', global: true })
|
||||
})
|
||||
}
|
||||
],
|
||||
|
||||
app: {
|
||||
@@ -32,10 +37,24 @@ export default defineNuxtConfig({
|
||||
},
|
||||
|
||||
content: {
|
||||
database: {
|
||||
type: 'd1',
|
||||
binding: 'DB'
|
||||
},
|
||||
|
||||
build: {
|
||||
markdown: {
|
||||
highlight: {
|
||||
langs: ['bash', 'ts', 'typescript', 'diff', 'vue', 'json', 'yml', 'css', 'mdc']
|
||||
},
|
||||
remarkPlugins: {
|
||||
'remark-mdc': {
|
||||
options: {
|
||||
experimental: {
|
||||
autoUnwrap: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,6 +75,8 @@ export default defineNuxtConfig({
|
||||
routeRules: {
|
||||
'/': { redirect: '/getting-started', prerender: false },
|
||||
'/getting-started/installation': { redirect: '/getting-started/installation/nuxt', prerender: false },
|
||||
'/getting-started/icons': { redirect: '/getting-started/icons/nuxt', prerender: false },
|
||||
'/getting-started/color-mode': { redirect: '/getting-started/color-mode/nuxt', prerender: false },
|
||||
'/getting-started/i18n': { redirect: '/getting-started/i18n/nuxt', prerender: false },
|
||||
'/composables': { redirect: '/composables/define-shortcuts', prerender: false },
|
||||
'/components': { redirect: '/components/app', prerender: false }
|
||||
@@ -70,31 +91,28 @@ export default defineNuxtConfig({
|
||||
nitro: {
|
||||
prerender: {
|
||||
routes: [
|
||||
'/getting-started',
|
||||
'/api/countries.json'
|
||||
// '/getting-started',
|
||||
'/api/countries.json',
|
||||
'/api/locales.json'
|
||||
// '/api/releases.json',
|
||||
// '/api/pulls.json'
|
||||
],
|
||||
crawlLinks: true,
|
||||
autoSubfolderIndex: false
|
||||
]
|
||||
// crawlLinks: true,
|
||||
// autoSubfolderIndex: false
|
||||
// ignore: !process.env.NUXT_GITHUB_TOKEN ? ['/pro'] : []
|
||||
},
|
||||
cloudflare: {
|
||||
pages: {
|
||||
routes: {
|
||||
exclude: [
|
||||
'/components/*',
|
||||
'/getting-started/*',
|
||||
'/composables/*',
|
||||
'/api/*'
|
||||
]
|
||||
exclude: []
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
hub: {
|
||||
cache: true
|
||||
cache: true,
|
||||
database: true
|
||||
},
|
||||
|
||||
componentMeta: {
|
||||
|
||||
@@ -4,21 +4,21 @@
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@iconify-json/logos": "^1.2.3",
|
||||
"@iconify-json/lucide": "^1.2.15",
|
||||
"@iconify-json/simple-icons": "^1.2.11",
|
||||
"@iconify-json/lucide": "^1.2.16",
|
||||
"@iconify-json/simple-icons": "^1.2.13",
|
||||
"@iconify-json/vscode-icons": "^1.2.2",
|
||||
"@nuxt/content": "3.0.0-alpha.6",
|
||||
"@nuxt/content": "3.0.0-alpha.7",
|
||||
"@nuxt/image": "^1.8.1",
|
||||
"@nuxt/ui": "latest",
|
||||
"@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@574082c",
|
||||
"@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@1408077",
|
||||
"@nuxthub/core": "^0.8.7",
|
||||
"@nuxtjs/plausible": "^1.1.1",
|
||||
"@octokit/rest": "^21.0.2",
|
||||
"@vueuse/nuxt": "^11.2.0",
|
||||
"@vueuse/nuxt": "^11.3.0",
|
||||
"joi": "^17.13.3",
|
||||
"nuxt": "^3.14.159",
|
||||
"nuxt": "^3.14.1592",
|
||||
"nuxt-component-meta": "^0.9.0",
|
||||
"nuxt-og-image": "^3.0.8",
|
||||
"nuxt-og-image": "^3.1.1",
|
||||
"prettier": "^3.3.3",
|
||||
"shiki-transformer-color-highlight": "^0.2.0",
|
||||
"superstruct": "^2.0.2",
|
||||
@@ -28,6 +28,6 @@
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"wrangler": "^3.87.0"
|
||||
"wrangler": "^3.90.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
user-agent: *
|
||||
disallow: /dev/*
|
||||
|
||||
505
docs/server/api/locales.json.get.ts
Normal file
505
docs/server/api/locales.json.get.ts
Normal file
@@ -0,0 +1,505 @@
|
||||
const locales: Record<string, string> = {
|
||||
'aa': '🇪🇷',
|
||||
'aa-ER': '🇪🇷',
|
||||
'af': '🇳🇦',
|
||||
'af-NA': '🇳🇦',
|
||||
'af-ZA': '🇿🇦',
|
||||
'am': '🇪🇹',
|
||||
'am-ET': '🇪🇹',
|
||||
'ar': '🇪🇬',
|
||||
'ar-AE': '🇦🇪',
|
||||
'ar-BH': '🇧🇭',
|
||||
'ar-DJ': '🇩🇯',
|
||||
'ar-DZ': '🇩🇿',
|
||||
'ar-EG': '🇪🇬',
|
||||
'ar-ER': '🇪🇷',
|
||||
'ar-IL': '🇮🇱',
|
||||
'ar-IQ': '🇮🇶',
|
||||
'ar-JO': '🇯🇴',
|
||||
'ar-KM': '🇰🇲',
|
||||
'ar-KW': '🇰🇼',
|
||||
'ar-LB': '🇱🇧',
|
||||
'ar-LY': '🇱🇾',
|
||||
'ar-MA': '🇲🇦',
|
||||
'ar-MR': '🇲🇷',
|
||||
'ar-OM': '🇴🇲',
|
||||
'ar-PS': '🇵🇸',
|
||||
'ar-QA': '🇶🇦',
|
||||
'ar-SA': '🇸🇦',
|
||||
'ar-SD': '🇸🇩',
|
||||
'ar-SO': '🇸🇴',
|
||||
'ar-SY': '🇸🇾',
|
||||
'ar-TD': '🇹🇩',
|
||||
'ar-TN': '🇹🇳',
|
||||
'ar-YE': '🇾🇪',
|
||||
'ay': '🇧🇴',
|
||||
'ay-BO': '🇧🇴',
|
||||
'az': '🇦🇿',
|
||||
'az-AZ': '🇦🇿',
|
||||
'be': '🇧🇾',
|
||||
'be-BY': '🇧🇾',
|
||||
'bg': '🇧🇬',
|
||||
'bg-BG': '🇧🇬',
|
||||
'bi': '🇻🇺',
|
||||
'bi-VU': '🇻🇺',
|
||||
'bn': '🇧🇩',
|
||||
'bn-BD': '🇧🇩',
|
||||
'bs': '🇧🇦',
|
||||
'bs-BA': '🇧🇦',
|
||||
'bs-ME': '🇲🇪',
|
||||
'byn': '🇪🇷',
|
||||
'byn-ER': '🇪🇷',
|
||||
'ca': '🇦🇩',
|
||||
'ca-AD': '🇦🇩',
|
||||
'ch': '🇬🇺',
|
||||
'ch-GU': '🇬🇺',
|
||||
'ch-MP': '🇲🇵',
|
||||
'cs': '🇨🇿',
|
||||
'cs-CZ': '🇨🇿',
|
||||
'da': '🇩🇰',
|
||||
'da-DK': '🇩🇰',
|
||||
'de': '🇩🇪',
|
||||
'de-AT': '🇦🇹',
|
||||
'de-BE': '🇧🇪',
|
||||
'de-CH': '🇨🇭',
|
||||
'de-DE': '🇩🇪',
|
||||
'de-LI': '🇱🇮',
|
||||
'de-LU': '🇱🇺',
|
||||
'de-VA': '🇻🇦',
|
||||
'dv': '🇲🇻',
|
||||
'dv-MV': '🇲🇻',
|
||||
'dz': '🇧🇹',
|
||||
'dz-BT': '🇧🇹',
|
||||
'el': '🇬🇷',
|
||||
'el-CY': '🇨🇾',
|
||||
'el-GR': '🇬🇷',
|
||||
'en': '🇬🇧',
|
||||
'en-AG': '🇦🇬',
|
||||
'en-AI': '🇦🇮',
|
||||
'en-AQ': '🇦🇶',
|
||||
'en-AS': '🇦🇸',
|
||||
'en-AU': '🇦🇺',
|
||||
'en-BB': '🇧🇧',
|
||||
'en-BM': '🇧🇲',
|
||||
'en-BS': '🇧🇸',
|
||||
'en-BW': '🇧🇼',
|
||||
'en-BZ': '🇧🇿',
|
||||
'en-CA': '🇨🇦',
|
||||
'en-CC': '🇨🇨',
|
||||
'en-CK': '🇨🇰',
|
||||
'en-CM': '🇨🇲',
|
||||
'en-CW': '🇨🇼',
|
||||
'en-CX': '🇨🇽',
|
||||
'en-DM': '🇩🇲',
|
||||
'en-ER': '🇪🇷',
|
||||
'en-FJ': '🇫🇯',
|
||||
'en-FK': '🇫🇰',
|
||||
'en-FM': '🇫🇲',
|
||||
'en-GB': '🇬🇧',
|
||||
'en-GD': '🇬🇩',
|
||||
'en-GG': '🇬🇬',
|
||||
'en-GH': '🇬🇭',
|
||||
'en-GI': '🇬🇮',
|
||||
'en-GM': '🇬🇲',
|
||||
'en-GS': '🇬🇸',
|
||||
'en-GU': '🇬🇺',
|
||||
'en-GY': '🇬🇾',
|
||||
'en-HK': '🇭🇰',
|
||||
'en-HM': '🇭🇲',
|
||||
'en-IE': '🇮🇪',
|
||||
'en-IM': '🇮🇲',
|
||||
'en-IN': '🇮🇳',
|
||||
'en-IO': '🇮🇴',
|
||||
'en-JE': '🇯🇪',
|
||||
'en-JM': '🇯🇲',
|
||||
'en-KE': '🇰🇪',
|
||||
'en-KI': '🇰🇮',
|
||||
'en-KN': '🇰🇳',
|
||||
'en-KY': '🇰🇾',
|
||||
'en-LC': '🇱🇨',
|
||||
'en-LR': '🇱🇷',
|
||||
'en-LS': '🇱🇸',
|
||||
'en-MF': '🇲🇫',
|
||||
'en-MH': '🇲🇭',
|
||||
'en-MP': '🇲🇵',
|
||||
'en-MS': '🇲🇸',
|
||||
'en-MT': '🇲🇹',
|
||||
'en-MU': '🇲🇺',
|
||||
'en-MW': '🇲🇼',
|
||||
'en-NA': '🇳🇦',
|
||||
'en-NF': '🇳🇫',
|
||||
'en-NG': '🇳🇬',
|
||||
'en-NR': '🇳🇷',
|
||||
'en-NU': '🇳🇺',
|
||||
'en-NZ': '🇳🇿',
|
||||
'en-PG': '🇵🇬',
|
||||
'en-PH': '🇵🇭',
|
||||
'en-PK': '🇵🇰',
|
||||
'en-PN': '🇵🇳',
|
||||
'en-PR': '🇵🇷',
|
||||
'en-PW': '🇵🇼',
|
||||
'en-RW': '🇷🇼',
|
||||
'en-SB': '🇸🇧',
|
||||
'en-SC': '🇸🇨',
|
||||
'en-SD': '🇸🇩',
|
||||
'en-SG': '🇸🇬',
|
||||
'en-SH': '🇸🇭',
|
||||
'en-SL': '🇸🇱',
|
||||
'en-SS': '🇸🇸',
|
||||
'en-SX': '🇸🇽',
|
||||
'en-SZ': '🇸🇿',
|
||||
'en-TC': '🇹🇨',
|
||||
'en-TK': '🇹🇰',
|
||||
'en-TO': '🇹🇴',
|
||||
'en-TT': '🇹🇹',
|
||||
'en-TV': '🇹🇻',
|
||||
'en-TZ': '🇹🇿',
|
||||
'en-UG': '🇺🇬',
|
||||
'en-UM': '🇺🇲',
|
||||
'en-US': '🇺🇸',
|
||||
'en-VC': '🇻🇨',
|
||||
'en-VG': '🇻🇬',
|
||||
'en-VI': '🇻🇮',
|
||||
'en-VU': '🇻🇺',
|
||||
'en-WS': '🇼🇸',
|
||||
'en-ZA': '🇿🇦',
|
||||
'en-ZM': '🇿🇲',
|
||||
'en-ZW': '🇿🇼',
|
||||
'es': '🇪🇸',
|
||||
'es-AR': '🇦🇷',
|
||||
'es-BO': '🇧🇴',
|
||||
'es-BZ': '🇧🇿',
|
||||
'es-CL': '🇨🇱',
|
||||
'es-CO': '🇨🇴',
|
||||
'es-CR': '🇨🇷',
|
||||
'es-CU': '🇨🇺',
|
||||
'es-DO': '🇩🇴',
|
||||
'es-EC': '🇪🇨',
|
||||
'es-EH': '🇪🇭',
|
||||
'es-ES': '🇪🇸',
|
||||
'es-GQ': '🇬🇶',
|
||||
'es-GT': '🇬🇹',
|
||||
'es-GU': '🇬🇺',
|
||||
'es-HN': '🇭🇳',
|
||||
'es-MX': '🇲🇽',
|
||||
'es-NI': '🇳🇮',
|
||||
'es-PA': '🇵🇦',
|
||||
'es-PE': '🇵🇪',
|
||||
'es-PR': '🇵🇷',
|
||||
'es-PY': '🇵🇾',
|
||||
'es-SV': '🇸🇻',
|
||||
'es-UY': '🇺🇾',
|
||||
'es-VE': '🇻🇪',
|
||||
'et': '🇪🇪',
|
||||
'et-EE': '🇪🇪',
|
||||
'fa': '🇮🇷',
|
||||
'fa-IR': '🇮🇷',
|
||||
'fan': '🇬🇶',
|
||||
'fan-GQ': '🇬🇶',
|
||||
'ff': '🇧🇫',
|
||||
'ff-BF': '🇧🇫',
|
||||
'ff-GN': '🇬🇳',
|
||||
'fi': '🇫🇮',
|
||||
'fi-FI': '🇫🇮',
|
||||
'fj': '🇫🇯',
|
||||
'fj-FJ': '🇫🇯',
|
||||
'fo': '🇫🇴',
|
||||
'fo-FO': '🇫🇴',
|
||||
'fr': '🇫🇷',
|
||||
'fr-BE': '🇧🇪',
|
||||
'fr-BF': '🇧🇫',
|
||||
'fr-BI': '🇧🇮',
|
||||
'fr-BJ': '🇧🇯',
|
||||
'fr-BL': '🇧🇱',
|
||||
'fr-CA': '🇨🇦',
|
||||
'fr-CD': '🇨🇩',
|
||||
'fr-CF': '🇨🇫',
|
||||
'fr-CG': '🇨🇬',
|
||||
'fr-CH': '🇨🇭',
|
||||
'fr-CI': '🇨🇮',
|
||||
'fr-CM': '🇨🇲',
|
||||
'fr-DJ': '🇩🇯',
|
||||
'fr-FR': '🇫🇷',
|
||||
'fr-GA': '🇬🇦',
|
||||
'fr-GF': '🇬🇫',
|
||||
'fr-GG': '🇬🇬',
|
||||
'fr-GN': '🇬🇳',
|
||||
'fr-GP': '🇬🇵',
|
||||
'fr-GQ': '🇬🇶',
|
||||
'fr-HT': '🇭🇹',
|
||||
'fr-JE': '🇯🇪',
|
||||
'fr-KM': '🇰🇲',
|
||||
'fr-LB': '🇱🇧',
|
||||
'fr-LU': '🇱🇺',
|
||||
'fr-MC': '🇲🇨',
|
||||
'fr-MF': '🇲🇫',
|
||||
'fr-MG': '🇲🇬',
|
||||
'fr-ML': '🇲🇱',
|
||||
'fr-MQ': '🇲🇶',
|
||||
'fr-NC': '🇳🇨',
|
||||
'fr-NE': '🇳🇪',
|
||||
'fr-PF': '🇵🇫',
|
||||
'fr-PM': '🇵🇲',
|
||||
'fr-RE': '🇷🇪',
|
||||
'fr-RW': '🇷🇼',
|
||||
'fr-SC': '🇸🇨',
|
||||
'fr-SN': '🇸🇳',
|
||||
'fr-TD': '🇹🇩',
|
||||
'fr-TF': '🇹🇫',
|
||||
'fr-TG': '🇹🇬',
|
||||
'fr-VA': '🇻🇦',
|
||||
'fr-VU': '🇻🇺',
|
||||
'fr-WF': '🇼🇫',
|
||||
'fr-YT': '🇾🇹',
|
||||
'ga': '🇮🇪',
|
||||
'ga-IE': '🇮🇪',
|
||||
'gn': '🇦🇷',
|
||||
'gn-AR': '🇦🇷',
|
||||
'gn-PY': '🇵🇾',
|
||||
'gv': '🇮🇲',
|
||||
'gv-IM': '🇮🇲',
|
||||
'he': '🇮🇱',
|
||||
'he-IL': '🇮🇱',
|
||||
'hi': '🇮🇳',
|
||||
'hi-IN': '🇮🇳',
|
||||
'hif': '🇫🇯',
|
||||
'hif-FJ': '🇫🇯',
|
||||
'hr': '🇭🇷',
|
||||
'hr-BA': '🇧🇦',
|
||||
'hr-HR': '🇭🇷',
|
||||
'hr-ME': '🇲🇪',
|
||||
'ht': '🇭🇹',
|
||||
'ht-HT': '🇭🇹',
|
||||
'hu': '🇭🇺',
|
||||
'hu-HU': '🇭🇺',
|
||||
'hy': '🇦🇲',
|
||||
'hy-AM': '🇦🇲',
|
||||
'hy-CY': '🇨🇾',
|
||||
'id': '🇮🇩',
|
||||
'id-ID': '🇮🇩',
|
||||
'is': '🇮🇸',
|
||||
'is-IS': '🇮🇸',
|
||||
'it': '🇮🇹',
|
||||
'it-CH': '🇨🇭',
|
||||
'it-IT': '🇮🇹',
|
||||
'it-SM': '🇸🇲',
|
||||
'it-VA': '🇻🇦',
|
||||
'ja': '🇯🇵',
|
||||
'ja-JP': '🇯🇵',
|
||||
'ka': '🇬🇪',
|
||||
'ka-GE': '🇬🇪',
|
||||
'kg': '🇨🇩',
|
||||
'kg-CD': '🇨🇩',
|
||||
'kk': '🇰🇿',
|
||||
'kk-KZ': '🇰🇿',
|
||||
'kl': '🇬🇱',
|
||||
'kl-GL': '🇬🇱',
|
||||
'km': '🇰🇭',
|
||||
'km-KH': '🇰🇭',
|
||||
'ko': '🇰🇵',
|
||||
'ko-KP': '🇰🇵',
|
||||
'ko-KR': '🇰🇷',
|
||||
'ku': '🇮🇶',
|
||||
'ku-IQ': '🇮🇶',
|
||||
'kun': '🇪🇷',
|
||||
'kun-ER': '🇪🇷',
|
||||
'ky': '🇰🇬',
|
||||
'ky-KG': '🇰🇬',
|
||||
'la': '🇻🇦',
|
||||
'la-VA': '🇻🇦',
|
||||
'lb': '🇱🇺',
|
||||
'lb-LU': '🇱🇺',
|
||||
'ln': '🇨🇩',
|
||||
'ln-CD': '🇨🇩',
|
||||
'ln-CG': '🇨🇬',
|
||||
'lo': '🇱🇦',
|
||||
'lo-LA': '🇱🇦',
|
||||
'lt': '🇱🇹',
|
||||
'lt-LT': '🇱🇹',
|
||||
'lu': '🇨🇩',
|
||||
'lu-CD': '🇨🇩',
|
||||
'lv': '🇱🇻',
|
||||
'lv-LV': '🇱🇻',
|
||||
'mg': '🇲🇬',
|
||||
'mg-MG': '🇲🇬',
|
||||
'mh': '🇲🇭',
|
||||
'mh-MH': '🇲🇭',
|
||||
'mi': '🇳🇿',
|
||||
'mi-NZ': '🇳🇿',
|
||||
'mk': '🇲🇰',
|
||||
'mk-MK': '🇲🇰',
|
||||
'mn': '🇲🇳',
|
||||
'mn-MN': '🇲🇳',
|
||||
'ms': '🇧🇳',
|
||||
'ms-BN': '🇧🇳',
|
||||
'ms-MY': '🇲🇾',
|
||||
'ms-SG': '🇸🇬',
|
||||
'mt': '🇲🇹',
|
||||
'mt-MT': '🇲🇹',
|
||||
'my': '🇲🇲',
|
||||
'my-MM': '🇲🇲',
|
||||
'na': '🇳🇷',
|
||||
'na-NR': '🇳🇷',
|
||||
'nb': '🇧🇻',
|
||||
'nb-BV': '🇧🇻',
|
||||
'nb-NO': '🇳🇴',
|
||||
'nd': '🇿🇼',
|
||||
'nd-ZW': '🇿🇼',
|
||||
'ne': '🇳🇵',
|
||||
'ne-NP': '🇳🇵',
|
||||
'nl': '🇳🇱',
|
||||
'nl-AW': '🇦🇼',
|
||||
'nl-BE': '🇧🇪',
|
||||
'nl-BQ': '🇧🇶',
|
||||
'nl-CW': '🇨🇼',
|
||||
'nl-MF': '🇲🇫',
|
||||
'nl-NL': '🇳🇱',
|
||||
'nl-SR': '🇸🇷',
|
||||
'nl-SX': '🇸🇽',
|
||||
'nn': '🇧🇻',
|
||||
'nn-BV': '🇧🇻',
|
||||
'nn-NO': '🇳🇴',
|
||||
'no': '🇳🇴',
|
||||
'no-BV': '🇧🇻',
|
||||
'no-NO': '🇳🇴',
|
||||
'no-SJ': '🇸🇯',
|
||||
'nr': '🇿🇦',
|
||||
'nr-ZA': '🇿🇦',
|
||||
'nrb': '🇪🇷',
|
||||
'nrb-ER': '🇪🇷',
|
||||
'ny': '🇲🇼',
|
||||
'ny-MW': '🇲🇼',
|
||||
'pa': '🇦🇼',
|
||||
'pa-AW': '🇦🇼',
|
||||
'pa-CW': '🇨🇼',
|
||||
'pl': '🇵🇱',
|
||||
'pl-PL': '🇵🇱',
|
||||
'ps': '🇦🇫',
|
||||
'ps-AF': '🇦🇫',
|
||||
'pt': '🇵🇹',
|
||||
'pt-AO': '🇦🇴',
|
||||
'pt-BR': '🇧🇷',
|
||||
'pt-CV': '🇨🇻',
|
||||
'pt-GQ': '🇬🇶',
|
||||
'pt-GW': '🇬🇼',
|
||||
'pt-MO': '🇲🇴',
|
||||
'pt-MZ': '🇲🇿',
|
||||
'pt-PT': '🇵🇹',
|
||||
'pt-ST': '🇸🇹',
|
||||
'pt-TL': '🇹🇱',
|
||||
'qu': '🇧🇴',
|
||||
'qu-BO': '🇧🇴',
|
||||
'rar': '🇨🇰',
|
||||
'rar-CK': '🇨🇰',
|
||||
'rm': '🇨🇭',
|
||||
'rm-CH': '🇨🇭',
|
||||
'rn': '🇧🇮',
|
||||
'rn-BI': '🇧🇮',
|
||||
'ro': '🇷🇴',
|
||||
'ro-MD': '🇲🇩',
|
||||
'ro-RO': '🇷🇴',
|
||||
'rtm': '🇫🇯',
|
||||
'rtm-FJ': '🇫🇯',
|
||||
'ru': '🇷🇺',
|
||||
'ru-AQ': '🇦🇶',
|
||||
'ru-BY': '🇧🇾',
|
||||
'ru-KG': '🇰🇬',
|
||||
'ru-KZ': '🇰🇿',
|
||||
'ru-RU': '🇷🇺',
|
||||
'ru-TJ': '🇹🇯',
|
||||
'ru-TM': '🇹🇲',
|
||||
'ru-UZ': '🇺🇿',
|
||||
'rw': '🇷🇼',
|
||||
'rw-RW': '🇷🇼',
|
||||
'sg': '🇨🇫',
|
||||
'sg-CF': '🇨🇫',
|
||||
'si': '🇱🇰',
|
||||
'si-LK': '🇱🇰',
|
||||
'sk': '🇸🇰',
|
||||
'sk-CZ': '🇨🇿',
|
||||
'sk-SK': '🇸🇰',
|
||||
'sl': '🇸🇮',
|
||||
'sl-SI': '🇸🇮',
|
||||
'sm': '🇦🇸',
|
||||
'sm-AS': '🇦🇸',
|
||||
'sm-WS': '🇼🇸',
|
||||
'sn': '🇿🇼',
|
||||
'sn-ZW': '🇿🇼',
|
||||
'so': '🇸🇴',
|
||||
'so-SO': '🇸🇴',
|
||||
'sq': '🇦🇱',
|
||||
'sq-AL': '🇦🇱',
|
||||
'sq-ME': '🇲🇪',
|
||||
'sq-XK': '🇽🇰',
|
||||
'sr': '🇧🇦',
|
||||
'sr-BA': '🇧🇦',
|
||||
'sr-ME': '🇲🇪',
|
||||
'sr-RS': '🇷🇸',
|
||||
'sr-XK': '🇽🇰',
|
||||
'ss': '🇸🇿',
|
||||
'ss-SZ': '🇸🇿',
|
||||
'ss-ZA': '🇿🇦',
|
||||
'ssy': '🇪🇷',
|
||||
'ssy-ER': '🇪🇷',
|
||||
'st': '🇱🇸',
|
||||
'st-LS': '🇱🇸',
|
||||
'st-ZA': '🇿🇦',
|
||||
'sv': '🇦🇽',
|
||||
'sv-AX': '🇦🇽',
|
||||
'sv-FI': '🇫🇮',
|
||||
'sv-SE': '🇸🇪',
|
||||
'sw': '🇨🇩',
|
||||
'sw-CD': '🇨🇩',
|
||||
'sw-KE': '🇰🇪',
|
||||
'sw-TZ': '🇹🇿',
|
||||
'sw-UG': '🇺🇬',
|
||||
'ta': '🇸🇬',
|
||||
'ta-LK': '🇱🇰',
|
||||
'ta-SG': '🇸🇬',
|
||||
'tg': '🇹🇯',
|
||||
'tg-TJ': '🇹🇯',
|
||||
'th': '🇹🇭',
|
||||
'th-TH': '🇹🇭',
|
||||
'ti': '🇪🇷',
|
||||
'ti-ER': '🇪🇷',
|
||||
'tig': '🇪🇷',
|
||||
'tig-ER': '🇪🇷',
|
||||
'tk': '🇦🇫',
|
||||
'tk-AF': '🇦🇫',
|
||||
'tk-TM': '🇹🇲',
|
||||
'tn': '🇧🇼',
|
||||
'tn-BW': '🇧🇼',
|
||||
'tn-ZA': '🇿🇦',
|
||||
'to': '🇹🇴',
|
||||
'to-TO': '🇹🇴',
|
||||
'tr': '🇹🇷',
|
||||
'tr-CY': '🇨🇾',
|
||||
'tr-TR': '🇹🇷',
|
||||
'ts': '🇿🇦',
|
||||
'ts-ZA': '🇿🇦',
|
||||
'uk': '🇺🇦',
|
||||
'uk-UA': '🇺🇦',
|
||||
'ur': '🇵🇰',
|
||||
'ur-PK': '🇵🇰',
|
||||
'uz': '🇺🇿',
|
||||
'uz-AF': '🇦🇫',
|
||||
'uz-UZ': '🇺🇿',
|
||||
've': '🇿🇦',
|
||||
've-ZA': '🇿🇦',
|
||||
'vi': '🇻🇳',
|
||||
'vi-VN': '🇻🇳',
|
||||
'xh': '🇿🇦',
|
||||
'xh-ZA': '🇿🇦',
|
||||
'zh': '🇨🇳',
|
||||
'zh-CN': '🇨🇳',
|
||||
'zh-HK': '🇭🇰',
|
||||
'zh-Hans': '🇨🇳',
|
||||
'zh-Hant': '🇨🇳',
|
||||
'zh-MO': '🇲🇴',
|
||||
'zh-SG': '🇸🇬',
|
||||
'zh-TW': '🇹🇼',
|
||||
'zu': '🇿🇦',
|
||||
'zu-ZA': '🇿🇦'
|
||||
}
|
||||
|
||||
export default eventHandler(async () => locales)
|
||||
43
package.json
43
package.json
@@ -2,7 +2,7 @@
|
||||
"name": "@nuxt/ui",
|
||||
"description": "A UI Library for Modern Web Apps, powered by Vue & Tailwind CSS.",
|
||||
"version": "3.0.0-alpha.9",
|
||||
"packageManager": "pnpm@9.13.2",
|
||||
"packageManager": "pnpm@9.14.2",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/nuxt/ui.git"
|
||||
@@ -74,27 +74,28 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify/vue": "^4.1.2",
|
||||
"@internationalized/number": "^3.5.4",
|
||||
"@nuxt/devtools-kit": "^1.6.0",
|
||||
"@internationalized/date": "^3.6.0",
|
||||
"@internationalized/number": "^3.6.0",
|
||||
"@nuxt/devtools-kit": "^1.6.1",
|
||||
"@nuxt/fonts": "^0.10.2",
|
||||
"@nuxt/icon": "^1.8.1",
|
||||
"@nuxt/kit": "^3.14.159",
|
||||
"@nuxt/schema": "^3.14.159",
|
||||
"@nuxt/icon": "^1.8.2",
|
||||
"@nuxt/kit": "^3.14.1592",
|
||||
"@nuxt/schema": "^3.14.1592",
|
||||
"@nuxtjs/color-mode": "^3.5.2",
|
||||
"@tailwindcss/postcss": "4.0.0-alpha.34",
|
||||
"@tailwindcss/vite": "4.0.0-alpha.34",
|
||||
"@tailwindcss/postcss": "4.0.0-beta.2",
|
||||
"@tailwindcss/vite": "4.0.0-beta.2",
|
||||
"@tanstack/vue-table": "^8.20.5",
|
||||
"@unhead/vue": "^1.11.11",
|
||||
"@vueuse/core": "^11.2.0",
|
||||
"@vueuse/integrations": "^11.2.0",
|
||||
"@vueuse/core": "^11.3.0",
|
||||
"@vueuse/integrations": "^11.3.0",
|
||||
"consola": "^3.2.3",
|
||||
"defu": "^6.1.4",
|
||||
"embla-carousel-auto-height": "^8.4.0",
|
||||
"embla-carousel-auto-scroll": "^8.4.0",
|
||||
"embla-carousel-autoplay": "^8.4.0",
|
||||
"embla-carousel-class-names": "^8.4.0",
|
||||
"embla-carousel-fade": "^8.4.0",
|
||||
"embla-carousel-vue": "^8.4.0",
|
||||
"embla-carousel-auto-height": "^8.5.1",
|
||||
"embla-carousel-auto-scroll": "^8.5.1",
|
||||
"embla-carousel-autoplay": "^8.5.1",
|
||||
"embla-carousel-class-names": "^8.5.1",
|
||||
"embla-carousel-fade": "^8.5.1",
|
||||
"embla-carousel-vue": "^8.5.1",
|
||||
"embla-carousel-wheel-gestures": "^8.0.1",
|
||||
"fuse.js": "^7.0.0",
|
||||
"get-port-please": "^3.1.2",
|
||||
@@ -107,7 +108,7 @@
|
||||
"scule": "^1.3.0",
|
||||
"sirv": "^3.0.0",
|
||||
"tailwind-variants": "^0.3.0",
|
||||
"tailwindcss": "4.0.0-alpha.34",
|
||||
"tailwindcss": "4.0.0-beta.2",
|
||||
"tinyglobby": "^0.2.10",
|
||||
"unplugin": "^1.16.0",
|
||||
"unplugin-auto-import": "^0.18.5",
|
||||
@@ -121,12 +122,12 @@
|
||||
"@release-it/conventional-changelog": "^9.0.3",
|
||||
"@standard-schema/spec": "1.0.0-beta.3",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"embla-carousel": "^8.4.0",
|
||||
"embla-carousel": "^8.5.1",
|
||||
"eslint": "^9.15.0",
|
||||
"happy-dom": "^15.7.4",
|
||||
"joi": "^17.13.3",
|
||||
"knitwork": "^1.1.0",
|
||||
"nuxt": "^3.14.159",
|
||||
"nuxt": "^3.14.1592",
|
||||
"nuxt-component-meta": "^0.9.0",
|
||||
"release-it": "^17.10.0",
|
||||
"superstruct": "^2.0.2",
|
||||
@@ -142,8 +143,8 @@
|
||||
},
|
||||
"resolutions": {
|
||||
"@nuxt/ui": "workspace:*",
|
||||
"@nuxt/content": "3.0.0-alpha.5",
|
||||
"happy-dom": "14.12.3",
|
||||
"rollup": "^4.24.0"
|
||||
"rollup": "^4.24.0",
|
||||
"typescript": "5.6.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.0",
|
||||
"typescript": "^5.6.3",
|
||||
"typescript": "^5.7.2",
|
||||
"unplugin-auto-import": "^0.18.5",
|
||||
"unplugin-vue-components": "^0.27.4",
|
||||
"vite": "^5.4.11",
|
||||
|
||||
@@ -84,9 +84,9 @@ defineShortcuts({
|
||||
<UApp :toaster="(appConfig.toaster as any)">
|
||||
<div class="h-screen w-screen overflow-hidden flex min-h-0 bg-[var(--ui-bg)]" vaul-drawer-wrapper>
|
||||
<UNavigationMenu :items="items" orientation="vertical" class="hidden lg:flex border-e border-[var(--ui-border)] overflow-y-auto w-48 p-4" />
|
||||
<UNavigationMenu :items="items" orientation="horizontal" class="lg:hidden border-b border-[var(--ui-border)] overflow-x-auto" />
|
||||
<UNavigationMenu :items="items" orientation="horizontal" class="lg:hidden border-b border-[var(--ui-border)] [&>div]:min-w-min overflow-x-auto" />
|
||||
|
||||
<div class="fixed top-4 right-4 flex items-center gap-2">
|
||||
<div class="fixed top-15 lg:top-3 right-4 flex items-center gap-2">
|
||||
<UButton
|
||||
:icon="mode === 'dark' ? 'i-lucide-moon' : 'i-lucide-sun'"
|
||||
color="neutral"
|
||||
@@ -96,7 +96,7 @@ defineShortcuts({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 flex flex-col items-center justify-around overflow-y-auto w-full py-12 px-4">
|
||||
<div class="flex-1 flex flex-col items-center justify-around overflow-y-auto w-full py-14 px-4">
|
||||
<Suspense>
|
||||
<RouterView />
|
||||
</Suspense>
|
||||
|
||||
@@ -6,8 +6,6 @@ const router = useRouter()
|
||||
const appConfig = useAppConfig()
|
||||
const colorMode = useColorMode()
|
||||
|
||||
defineOptions({ inheritAttrs: false })
|
||||
|
||||
const isDark = computed({
|
||||
get() {
|
||||
return colorMode.value === 'dark'
|
||||
@@ -26,6 +24,7 @@ const components = [
|
||||
'button',
|
||||
'button-group',
|
||||
'card',
|
||||
'calendar',
|
||||
'carousel',
|
||||
'checkbox',
|
||||
'chip',
|
||||
@@ -85,9 +84,9 @@ defineShortcuts({
|
||||
<UApp :toaster="appConfig.toaster">
|
||||
<div class="h-screen w-screen overflow-hidden flex flex-col lg:flex-row min-h-0 bg-[var(--ui-bg)]" vaul-drawer-wrapper>
|
||||
<UNavigationMenu :items="items" orientation="vertical" class="hidden lg:flex border-e border-[var(--ui-border)] overflow-y-auto w-48 p-4" />
|
||||
<UNavigationMenu :items="items" orientation="horizontal" class="lg:hidden border-b border-[var(--ui-border)] overflow-x-auto" />
|
||||
<UNavigationMenu :items="items" orientation="horizontal" class="lg:hidden border-b border-[var(--ui-border)] [&>div]:min-w-min overflow-x-auto" />
|
||||
|
||||
<div class="fixed top-4 right-4 flex items-center gap-2">
|
||||
<div class="fixed top-15 lg:top-3 right-4 flex items-center gap-2">
|
||||
<ClientOnly v-if="!colorMode?.forced">
|
||||
<UButton
|
||||
:icon="isDark ? 'i-lucide-moon' : 'i-lucide-sun'"
|
||||
@@ -103,7 +102,7 @@ defineShortcuts({
|
||||
</ClientOnly>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 flex flex-col items-center justify-around overflow-y-auto w-full py-12 px-4">
|
||||
<div class="flex-1 flex flex-col items-center justify-around overflow-y-auto w-full py-14 px-4">
|
||||
<NuxtPage />
|
||||
</div>
|
||||
|
||||
|
||||
20
playground/app/pages/components/calendar.vue
Normal file
20
playground/app/pages/components/calendar.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import { CalendarDate } from '@internationalized/date'
|
||||
|
||||
const singleValue = shallowRef(new CalendarDate(2022, 1, 10))
|
||||
const multipleValue = shallowRef({
|
||||
start: new CalendarDate(2022, 1, 10),
|
||||
end: new CalendarDate(2022, 1, 20)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex justify-center gap-2">
|
||||
<UCalendar v-model="singleValue" />
|
||||
</div>
|
||||
<div class="flex justify-center gap-2">
|
||||
<UCalendar v-model="multipleValue" range />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -105,13 +105,13 @@ defineShortcuts(extractShortcuts(items.value))
|
||||
<USelect v-model="size" :items="sizes" placeholder="Size" />
|
||||
</div>
|
||||
|
||||
<UContextMenu :items="items" class="min-w-48" :size="size">
|
||||
<UContextMenu :items="items" :ui="{ content: 'w-48' }" :size="size">
|
||||
<div class="flex items-center justify-center rounded-md border border-dashed border-[var(--ui-border-accented)] text-sm aspect-video w-72">
|
||||
Right click here
|
||||
</div>
|
||||
</UContextMenu>
|
||||
|
||||
<UContextMenu :items="itemsWithColor" class="min-w-48" :size="size">
|
||||
<UContextMenu :items="itemsWithColor" :ui="{ content: 'w-48' }" :size="size">
|
||||
<div class="flex items-center justify-center rounded-md border border-dashed border-[var(--ui-border-accented)] text-sm aspect-video w-72">
|
||||
Color right click here
|
||||
</div>
|
||||
|
||||
@@ -140,7 +140,7 @@ defineShortcuts(extractShortcuts(items.value))
|
||||
<div class="flex flex-col items-center gap-8">
|
||||
<USelectMenu v-model="size" :items="sizes" placeholder="Size" />
|
||||
|
||||
<UDropdownMenu :items="items" :size="size" arrow :content="{ side: 'bottom', align: 'start' }" class="min-w-48">
|
||||
<UDropdownMenu :items="items" :size="size" arrow :content="{ side: 'bottom', align: 'start' }" :ui="{ content: 'w-48' }">
|
||||
<UButton label="Open" color="neutral" variant="outline" icon="i-lucide-menu" />
|
||||
|
||||
<template #custom-trailing>
|
||||
@@ -148,7 +148,7 @@ defineShortcuts(extractShortcuts(items.value))
|
||||
</template>
|
||||
</UDropdownMenu>
|
||||
|
||||
<UDropdownMenu :items="itemsWithColor" :size="size" arrow :content="{ side: 'bottom', align: 'start' }" class="min-w-48">
|
||||
<UDropdownMenu :items="itemsWithColor" :size="size" arrow :content="{ side: 'bottom', align: 'start' }" :ui="{ content: 'w-48' }">
|
||||
<UButton label="Color" color="neutral" variant="outline" icon="i-lucide-menu" />
|
||||
|
||||
<template #custom-trailing>
|
||||
|
||||
@@ -276,7 +276,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-full flex flex-col flex-1 gap-4 w-full -my-8">
|
||||
<div class="h-full flex flex-col flex-1 gap-4 w-full">
|
||||
<div class="flex gap-2 items-center">
|
||||
<UInput
|
||||
:model-value="(table?.tableApi?.getColumn('email')?.getFilterValue() as string)"
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
"generate": "nuxi generate"
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify-json/lucide": "^1.2.15",
|
||||
"@iconify-json/simple-icons": "^1.2.11",
|
||||
"@iconify-json/lucide": "^1.2.16",
|
||||
"@iconify-json/simple-icons": "^1.2.13",
|
||||
"@nuxt/ui": "latest",
|
||||
"nuxt": "^3.14.159"
|
||||
"nuxt": "^3.14.1592"
|
||||
}
|
||||
}
|
||||
|
||||
1447
pnpm-lock.yaml
generated
1447
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -13,21 +13,21 @@ export interface ModuleOptions {
|
||||
/**
|
||||
* Prefix for components
|
||||
* @defaultValue `U`
|
||||
* @link https://ui3.nuxt.dev/getting-started/installation#prefix
|
||||
* @link https://ui3.nuxt.dev/getting-started/installation/nuxt#prefix
|
||||
*/
|
||||
prefix?: string
|
||||
|
||||
/**
|
||||
* Enable or disable `@nuxt/fonts` module
|
||||
* @defaultValue `true`
|
||||
* @link https://ui3.nuxt.dev/getting-started/installation#fonts
|
||||
* @link https://ui3.nuxt.dev/getting-started/installation/nuxt#fonts
|
||||
*/
|
||||
fonts?: boolean
|
||||
|
||||
/**
|
||||
* Enable or disable `@nuxtjs/color-mode` module
|
||||
* @defaultValue `true`
|
||||
* @link https://ui3.nuxt.dev/getting-started/installation#colormode
|
||||
* @link https://ui3.nuxt.dev/getting-started/installation/nuxt#colormode
|
||||
*/
|
||||
colorMode?: boolean
|
||||
|
||||
@@ -39,14 +39,14 @@ export interface ModuleOptions {
|
||||
/**
|
||||
* Define the color aliases available for components
|
||||
* @defaultValue `['primary', 'secondary', 'success', 'info', 'warning', 'error']`
|
||||
* @link https://ui3.nuxt.dev/getting-started/installation#themecolors
|
||||
* @link https://ui3.nuxt.dev/getting-started/installation/nuxt#themecolors
|
||||
*/
|
||||
colors?: string[]
|
||||
|
||||
/**
|
||||
* Enable or disable transitions on components
|
||||
* @defaultValue `true`
|
||||
* @link https://ui3.nuxt.dev/getting-started/installation#themetransitions
|
||||
* @link https://ui3.nuxt.dev/getting-started/installation/nuxt#themetransitions
|
||||
*/
|
||||
transitions?: boolean
|
||||
}
|
||||
@@ -70,10 +70,9 @@ export default defineNuxtModule<ModuleOptions>({
|
||||
compatibility: {
|
||||
nuxt: '>=3.13.1'
|
||||
},
|
||||
docs: 'https://ui3.nuxt.dev/getting-started/installation'
|
||||
docs: 'https://ui3.nuxt.dev/getting-started/installation/nuxt'
|
||||
},
|
||||
defaults: defaultOptions,
|
||||
|
||||
async setup(options, nuxt) {
|
||||
const { resolve } = createResolver(import.meta.url)
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ const ui = breadcrumb()
|
||||
</ULink>
|
||||
</li>
|
||||
|
||||
<li v-if="index < items!.length - 1" role="presentation" :class="ui.separator({ class: props.ui?.separator })">
|
||||
<li v-if="index < items!.length - 1" role="presentation" aria-hidden="true" :class="ui.separator({ class: props.ui?.separator })">
|
||||
<slot name="separator">
|
||||
<UIcon :name="separatorIcon" :class="ui.separatorIcon({ class: props.ui?.separatorIcon })" />
|
||||
</slot>
|
||||
|
||||
170
src/runtime/components/Calendar.vue
Normal file
170
src/runtime/components/Calendar.vue
Normal file
@@ -0,0 +1,170 @@
|
||||
<script lang="ts">
|
||||
import { tv, type VariantProps } from 'tailwind-variants'
|
||||
import type { CalendarRootProps, CalendarRootEmits, RangeCalendarRootEmits, DateRange, CalendarCellTriggerProps } from 'radix-vue'
|
||||
import type { DateValue } from '@internationalized/date'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/ui/calendar'
|
||||
|
||||
const appConfig = _appConfig as AppConfig & { ui: { calendar: Partial<typeof theme> } }
|
||||
|
||||
const calendar = tv({ extend: tv(theme), ...(appConfig.ui?.calendar || {}) })
|
||||
|
||||
type CalendarVariants = VariantProps<typeof calendar>
|
||||
|
||||
type CalendarModelValue<R extends boolean = false, M extends boolean = false> = R extends true
|
||||
? DateRange
|
||||
: M extends true
|
||||
? DateValue[]
|
||||
: DateValue
|
||||
|
||||
export interface CalendarProps<R extends boolean, M extends boolean> extends Omit<CalendarRootProps, 'as' | 'asChild' | 'modelValue' | 'defaultValue' | 'dir' | 'locale' | 'calendarLabel' | 'multiple'> {
|
||||
/**
|
||||
* The element or component this component should render as.
|
||||
* @defaultValue 'div'
|
||||
*/
|
||||
as?: any
|
||||
color?: CalendarVariants['color']
|
||||
size?: CalendarVariants['size']
|
||||
/** Whether or not a range of dates can be selected */
|
||||
range?: R & boolean
|
||||
/** Whether or not multiple dates can be selected */
|
||||
multiple?: M & boolean
|
||||
/** Show month controls */
|
||||
monthControls?: boolean
|
||||
/** Show year controls */
|
||||
yearControls?: boolean
|
||||
defaultValue?: CalendarModelValue<R, M>
|
||||
modelValue?: CalendarModelValue<R, M>
|
||||
class?: any
|
||||
ui?: Partial<typeof calendar.slots>
|
||||
}
|
||||
|
||||
export interface CalendarEmits<R extends boolean, M extends boolean> extends Omit<CalendarRootEmits & RangeCalendarRootEmits, 'update:modelValue'> {
|
||||
'update:modelValue': [date: CalendarModelValue<R, M>]
|
||||
}
|
||||
|
||||
export interface CalendarSlots {
|
||||
'heading': (props: { value: string }) => any
|
||||
'day': (props: Pick<CalendarCellTriggerProps, 'day'>) => any
|
||||
'week-day': (props: { day: string }) => any
|
||||
}
|
||||
</script>
|
||||
|
||||
<script setup lang="ts" generic="R extends boolean = false, M extends boolean = false">
|
||||
import { computed } from 'vue'
|
||||
import { useForwardPropsEmits } from 'radix-vue'
|
||||
import { Calendar as SingleCalendar, RangeCalendar } from 'radix-vue/namespaced'
|
||||
import { reactiveOmit } from '@vueuse/core'
|
||||
import { useLocale } from '../composables/useLocale'
|
||||
import UButton from './Button.vue'
|
||||
|
||||
const props = withDefaults(defineProps<CalendarProps<R, M>>(), {
|
||||
fixedWeeks: true,
|
||||
monthControls: true,
|
||||
yearControls: true
|
||||
})
|
||||
const emits = defineEmits<CalendarEmits<R, M>>()
|
||||
defineSlots<CalendarSlots>()
|
||||
|
||||
const { code: locale, dir, t } = useLocale()
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactiveOmit(props, 'range', 'modelValue', 'defaultValue', 'color', 'size', 'monthControls', 'yearControls', 'class', 'ui'), emits)
|
||||
|
||||
const prevYearIcon = computed(() => dir.value === 'rtl' ? appConfig.ui.icons.chevronDoubleRight : appConfig.ui.icons.chevronDoubleLeft)
|
||||
const prevMonthIcon = computed(() => dir.value === 'rtl' ? appConfig.ui.icons.chevronRight : appConfig.ui.icons.chevronLeft)
|
||||
const nextYearIcon = computed(() => dir.value === 'rtl' ? appConfig.ui.icons.chevronDoubleLeft : appConfig.ui.icons.chevronDoubleRight)
|
||||
const nextMonthIcon = computed(() => dir.value === 'rtl' ? appConfig.ui.icons.chevronLeft : appConfig.ui.icons.chevronRight)
|
||||
|
||||
const ui = computed(() => calendar({
|
||||
color: props.color,
|
||||
size: props.size
|
||||
}))
|
||||
|
||||
function paginateYear(date: DateValue, sign: -1 | 1) {
|
||||
if (sign === -1) {
|
||||
return date.subtract({ years: 1 })
|
||||
}
|
||||
|
||||
return date.add({ years: 1 })
|
||||
}
|
||||
|
||||
const Calendar = computed(() => props.range ? RangeCalendar : SingleCalendar)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Calendar.Root
|
||||
v-slot="{ weekDays, grid }"
|
||||
v-bind="rootProps"
|
||||
:model-value="(modelValue as CalendarModelValue<true & false>)"
|
||||
:default-value="(defaultValue as CalendarModelValue<true & false>)"
|
||||
:locale="locale"
|
||||
:dir="dir"
|
||||
:class="ui.root({ class: [props.class, props.ui?.root] })"
|
||||
>
|
||||
<Calendar.Header :class="ui.header({ class: props.ui?.header })">
|
||||
<Calendar.Prev v-if="props.yearControls" :prev-page="(date: DateValue) => paginateYear(date, -1)" :aria-label="t('calendar.prevYear')" as-child>
|
||||
<UButton :icon="prevYearIcon" :size="props.size" color="neutral" variant="ghost" />
|
||||
</Calendar.Prev>
|
||||
<Calendar.Prev v-if="props.monthControls" :aria-label="t('calendar.prevMonth')" as-child>
|
||||
<UButton :icon="prevMonthIcon" :size="props.size" color="neutral" variant="ghost" />
|
||||
</Calendar.Prev>
|
||||
<Calendar.Heading v-slot="{ headingValue }" :class="ui.heading({ class: props.ui?.heading })">
|
||||
<slot name="heading" :value="headingValue">
|
||||
{{ headingValue }}
|
||||
</slot>
|
||||
</Calendar.Heading>
|
||||
<Calendar.Next v-if="props.monthControls" :aria-label="t('calendar.nextMonth')" as-child>
|
||||
<UButton :icon="nextMonthIcon" :size="props.size" color="neutral" variant="ghost" />
|
||||
</Calendar.Next>
|
||||
<Calendar.Next v-if="props.yearControls" :next-page="(date: DateValue) => paginateYear(date, 1)" :aria-label="t('calendar.nextYear')" as-child>
|
||||
<UButton :icon="nextYearIcon" :size="props.size" color="neutral" variant="ghost" />
|
||||
</Calendar.Next>
|
||||
</Calendar.Header>
|
||||
<div :class="ui.body({ class: props.ui?.body })">
|
||||
<Calendar.Grid
|
||||
v-for="month in grid"
|
||||
:key="month.value.toString()"
|
||||
:class="ui.grid({ class: props.ui?.grid })"
|
||||
>
|
||||
<Calendar.GridHead>
|
||||
<Calendar.GridRow :class="ui.gridWeekDaysRow({ class: props.ui?.gridWeekDaysRow })">
|
||||
<Calendar.HeadCell
|
||||
v-for="day in weekDays"
|
||||
:key="day"
|
||||
:class="ui.headCell({ class: props.ui?.headCell })"
|
||||
>
|
||||
<slot name="week-day" :day="day">
|
||||
{{ day }}
|
||||
</slot>
|
||||
</Calendar.HeadCell>
|
||||
</Calendar.GridRow>
|
||||
</Calendar.GridHead>
|
||||
<Calendar.GridBody :class="ui.gridBody({ class: props.ui?.gridBody })">
|
||||
<Calendar.GridRow
|
||||
v-for="(weekDates, index) in month.rows"
|
||||
:key="`weekDate-${index}`"
|
||||
:class="ui.gridRow({ class: props.ui?.gridRow })"
|
||||
>
|
||||
<Calendar.Cell
|
||||
v-for="weekDate in weekDates"
|
||||
:key="weekDate.toString()"
|
||||
:date="weekDate"
|
||||
:class="ui.cell({ class: props.ui?.cell })"
|
||||
>
|
||||
<Calendar.CellTrigger
|
||||
:day="weekDate"
|
||||
:month="month.value"
|
||||
:class="ui.cellTrigger({ class: props.ui?.cellTrigger })"
|
||||
>
|
||||
<slot name="day" :day="weekDate">
|
||||
{{ weekDate.day }}
|
||||
</slot>
|
||||
</Calendar.CellTrigger>
|
||||
</Calendar.Cell>
|
||||
</Calendar.GridRow>
|
||||
</Calendar.GridBody>
|
||||
</Calendar.Grid>
|
||||
</div>
|
||||
</Calendar.Root>
|
||||
</template>
|
||||
@@ -23,6 +23,7 @@ export interface CommandPaletteItem {
|
||||
avatar?: AvatarProps
|
||||
chip?: ChipProps
|
||||
kbds?: KbdProps['value'][] | KbdProps[]
|
||||
active?: boolean
|
||||
loading?: boolean
|
||||
disabled?: boolean
|
||||
slot?: string
|
||||
@@ -276,31 +277,31 @@ const groups = computed(() => {
|
||||
:key="`group-${groupIndex}-${index}`"
|
||||
:value="omit(item, ['matches' as any, 'group' as any, 'onSelect', 'labelHtml', 'suffixHtml'])"
|
||||
:disabled="item.disabled"
|
||||
:class="ui.item({ class: props.ui?.item })"
|
||||
:class="ui.item({ class: props.ui?.item, active: item.active })"
|
||||
@select="item.onSelect"
|
||||
>
|
||||
<slot :name="item.slot || group.slot || 'item'" :item="item" :index="index">
|
||||
<slot :name="item.slot ? `${item.slot}-leading` : group.slot ? `${group.slot}-leading` : `item-leading`" :item="item" :index="index">
|
||||
<UIcon v-if="item.loading" :name="loadingIcon || appConfig.ui.icons.loading" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon, loading: true })" />
|
||||
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon })" />
|
||||
<UAvatar v-else-if="item.avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
|
||||
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon, active: item.active })" />
|
||||
<UAvatar v-else-if="item.avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar, active: item.active })" />
|
||||
<UChip
|
||||
v-else-if="item.chip"
|
||||
:size="((props.ui?.itemLeadingChipSize || ui.itemLeadingChipSize()) as ChipProps['size'])"
|
||||
inset
|
||||
standalone
|
||||
v-bind="item.chip"
|
||||
:class="ui.itemLeadingChip({ class: props.ui?.itemLeadingChip })"
|
||||
:class="ui.itemLeadingChip({ class: props.ui?.itemLeadingChip, active: item.active })"
|
||||
/>
|
||||
</slot>
|
||||
|
||||
<span v-if="item.labelHtml || get(item, props.labelKey as string) || !!slots[item.slot ? `${item.slot}-label` : group.slot ? `${group.slot}-label` : `item-label`]" :class="ui.itemLabel({ class: props.ui?.itemLabel })">
|
||||
<span v-if="item.labelHtml || get(item, props.labelKey as string) || !!slots[item.slot ? `${item.slot}-label` : group.slot ? `${group.slot}-label` : `item-label`]" :class="ui.itemLabel({ class: props.ui?.itemLabel, active: item.active })">
|
||||
<slot :name="item.slot ? `${item.slot}-label` : group.slot ? `${group.slot}-label` : `item-label`" :item="item" :index="index">
|
||||
<span v-if="item.prefix" :class="ui.itemLabelPrefix({ class: props.ui?.itemLabelPrefix })">{{ item.prefix }}</span>
|
||||
|
||||
<span :class="ui.itemLabelBase({ class: props.ui?.itemLabelBase })" v-html="item.labelHtml || get(item, props.labelKey as string)" />
|
||||
<span :class="ui.itemLabelBase({ class: props.ui?.itemLabelBase, active: item.active })" v-html="item.labelHtml || get(item, props.labelKey as string)" />
|
||||
|
||||
<span :class="ui.itemLabelSuffix({ class: props.ui?.itemLabelSuffix })" v-html="item.suffixHtml || item.suffix" />
|
||||
<span :class="ui.itemLabelSuffix({ class: props.ui?.itemLabelSuffix, active: item.active })" v-html="item.suffixHtml || item.suffix" />
|
||||
</slot>
|
||||
</span>
|
||||
|
||||
|
||||
@@ -167,12 +167,12 @@ const ui = computed(() => contextMenu({
|
||||
|
||||
<template>
|
||||
<ContextMenuRoot v-bind="rootProps">
|
||||
<ContextMenuTrigger v-if="!!slots.default" as-child :disabled="disabled">
|
||||
<ContextMenuTrigger v-if="!!slots.default" as-child :disabled="disabled" :class="props.class">
|
||||
<slot />
|
||||
</ContextMenuTrigger>
|
||||
|
||||
<UContextMenuContent
|
||||
:class="ui.content({ class: [props.class, props.ui?.content] })"
|
||||
:class="ui.content({ class: [!slots.default && props.class, props.ui?.content] })"
|
||||
:ui="ui"
|
||||
:ui-override="props.ui"
|
||||
v-bind="contentProps"
|
||||
|
||||
@@ -81,14 +81,14 @@ const ui = computed(() => drawer({
|
||||
|
||||
<template>
|
||||
<DrawerRoot v-bind="rootProps">
|
||||
<DrawerTrigger v-if="!!slots.default" as-child>
|
||||
<DrawerTrigger v-if="!!slots.default" as-child :class="props.class">
|
||||
<slot />
|
||||
</DrawerTrigger>
|
||||
|
||||
<DrawerPortal :disabled="!portal">
|
||||
<DrawerOverlay v-if="overlay" :class="ui.overlay({ class: props.ui?.overlay })" />
|
||||
|
||||
<DrawerContent :class="ui.content({ class: [props.class, props.ui?.content] })" v-bind="contentProps">
|
||||
<DrawerContent :class="ui.content({ class: [!slots.default && props.class, props.ui?.content] })" v-bind="contentProps">
|
||||
<slot name="handle">
|
||||
<div v-if="handle" :class="ui.handle({ class: props.ui?.handle })" />
|
||||
</slot>
|
||||
|
||||
@@ -165,12 +165,12 @@ const ui = computed(() => dropdownMenu({
|
||||
|
||||
<template>
|
||||
<DropdownMenuRoot v-slot="{ open }" v-bind="rootProps">
|
||||
<DropdownMenuTrigger v-if="!!slots.default" as-child :disabled="disabled">
|
||||
<DropdownMenuTrigger v-if="!!slots.default" as-child :class="props.class" :disabled="disabled">
|
||||
<slot :open="open" />
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<UDropdownMenuContent
|
||||
:class="ui.content({ class: [props.class, props.ui?.content] })"
|
||||
:class="ui.content({ class: [!slots.default && props.class, props.ui?.content] })"
|
||||
:ui="ui"
|
||||
:ui-override="props.ui"
|
||||
v-bind="contentProps"
|
||||
|
||||
@@ -104,6 +104,7 @@ function resolveErrorIds(errs: FormError[]): FormErrorWithId[] {
|
||||
}))
|
||||
}
|
||||
|
||||
const parsedValue = ref<T | null>(null)
|
||||
async function getErrors(): Promise<FormErrorWithId[]> {
|
||||
let errs = props.validate ? (await props.validate(props.state)) ?? [] : []
|
||||
|
||||
@@ -112,7 +113,7 @@ async function getErrors(): Promise<FormErrorWithId[]> {
|
||||
if (errors) {
|
||||
errs = errs.concat(errors)
|
||||
} else {
|
||||
Object.assign(props.state, result)
|
||||
parsedValue.value = result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +170,7 @@ async function onSubmitWrapper(payload: Event) {
|
||||
|
||||
try {
|
||||
await _validate({ nested: true })
|
||||
event.data = props.state
|
||||
event.data = props.schema ? parsedValue.value : props.state
|
||||
await props.onSubmit?.(event)
|
||||
} catch (error) {
|
||||
if (!(error instanceof FormValidationException)) {
|
||||
|
||||
@@ -107,7 +107,7 @@ export interface InputMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends Ma
|
||||
/** The controlled value of the Combobox. Can be binded-with with `v-model`. */
|
||||
modelValue?: SelectModelValue<T, V, M>
|
||||
/** Whether multiple options can be selected or not. */
|
||||
multiple?: M
|
||||
multiple?: M & boolean
|
||||
}
|
||||
|
||||
export type InputMenuEmits<T, V, M extends boolean> = Omit<ComboboxRootEmits<T>, 'update:modelValue'> & {
|
||||
@@ -167,11 +167,9 @@ const searchTerm = defineModel<string>('searchTerm', { default: '' })
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
const { t } = useLocale()
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'selectedValue', 'open', 'defaultOpen', 'resetSearchTermOnBlur'), emits)
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'selectedValue', 'open', 'defaultOpen', 'multiple', 'resetSearchTermOnBlur'), emits)
|
||||
const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, position: 'popper' }) as ComboboxContentProps)
|
||||
const arrowProps = toRef(() => props.arrow as ComboboxArrowProps)
|
||||
// This is a hack due to generic boolean casting (see https://github.com/nuxt/ui/issues/2541)
|
||||
const multiple = toRef(() => typeof props.multiple === 'string' ? true : props.multiple)
|
||||
|
||||
const { emitFormBlur, emitFormChange, emitFormInput, size: formGroupSize, color, id, name, highlight, disabled } = useFormField<InputProps>(props)
|
||||
const { orientation, size: buttonGroupSize } = useButtonGroup<InputProps>(props)
|
||||
@@ -189,7 +187,7 @@ const ui = computed(() => inputMenu({
|
||||
highlight: highlight.value,
|
||||
leading: isLeading.value || !!props.avatar || !!slots.leading,
|
||||
trailing: isTrailing.value || !!slots.trailing,
|
||||
multiple: multiple.value,
|
||||
multiple: props.multiple,
|
||||
buttonGroup: orientation.value
|
||||
}))
|
||||
|
||||
@@ -336,7 +334,6 @@ defineExpose({
|
||||
v-model:search-term="searchTerm"
|
||||
:name="name"
|
||||
:disabled="disabled"
|
||||
:multiple="multiple"
|
||||
:display-value="displayValue"
|
||||
:filter-function="() => rootItems"
|
||||
:class="ui.root({ class: [props.class, props.ui?.root] })"
|
||||
|
||||
@@ -121,14 +121,14 @@ const ui = computed(() => modal({
|
||||
|
||||
<template>
|
||||
<DialogRoot v-slot="{ open }" v-bind="rootProps">
|
||||
<DialogTrigger v-if="!!slots.default" as-child>
|
||||
<DialogTrigger v-if="!!slots.default" as-child :class="props.class">
|
||||
<slot :open="open" />
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogPortal :disabled="!portal">
|
||||
<DialogOverlay v-if="overlay" :class="ui.overlay({ class: props.ui?.overlay })" />
|
||||
|
||||
<DialogContent :class="ui.content({ class: [props.class, props.ui?.content] })" v-bind="contentProps" v-on="contentEvents">
|
||||
<DialogContent :class="ui.content({ class: [!slots.default && props.class, props.ui?.content] })" v-bind="contentProps" v-on="contentEvents">
|
||||
<slot name="content">
|
||||
<div v-if="!!slots.header || (title || !!slots.title) || (description || !!slots.description) || (close || !!slots.close)" :class="ui.header({ class: props.ui?.header })">
|
||||
<slot name="header">
|
||||
|
||||
@@ -92,12 +92,12 @@ const Component = computed(() => props.mode === 'hover' ? HoverCard : Popover)
|
||||
|
||||
<template>
|
||||
<Component.Root v-slot="{ open }" v-bind="rootProps">
|
||||
<Component.Trigger v-if="!!slots.default" as-child>
|
||||
<Component.Trigger v-if="!!slots.default" as-child :class="props.class">
|
||||
<slot :open="open" />
|
||||
</Component.Trigger>
|
||||
|
||||
<Component.Portal :disabled="!portal">
|
||||
<Component.Content v-bind="contentProps" :class="ui.content({ class: [props.class, props.ui?.content] })" v-on="contentEvents">
|
||||
<Component.Content v-bind="contentProps" :class="ui.content({ class: [!slots.default && props.class, props.ui?.content] })" v-on="contentEvents">
|
||||
<slot name="content" />
|
||||
|
||||
<Component.Arrow v-if="!!arrow" v-bind="arrowProps" :class="ui.arrow({ class: props.ui?.arrow })" />
|
||||
|
||||
@@ -50,6 +50,7 @@ export type ProgressSlots = {
|
||||
import { computed } from 'vue'
|
||||
import { ProgressIndicator, ProgressRoot, useForwardPropsEmits } from 'radix-vue'
|
||||
import { reactivePick } from '@vueuse/core'
|
||||
import { useLocale } from '../composables/useLocale'
|
||||
|
||||
const props = withDefaults(defineProps<ProgressProps>(), {
|
||||
inverted: false,
|
||||
@@ -59,6 +60,8 @@ const props = withDefaults(defineProps<ProgressProps>(), {
|
||||
const emits = defineEmits<ProgressEmits>()
|
||||
defineSlots<ProgressSlots>()
|
||||
|
||||
const { dir } = useLocale()
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'getValueLabel', 'modelValue'), emits)
|
||||
|
||||
const isIndeterminate = computed(() => rootProps.value.modelValue === null)
|
||||
@@ -93,8 +96,20 @@ const indicatorStyle = computed(() => {
|
||||
return
|
||||
}
|
||||
|
||||
return {
|
||||
transform: `translate${props.orientation === 'vertical' ? 'Y' : 'X'}(${props.inverted ? '' : '-'}${100 - percent.value}%)`
|
||||
if (props.orientation === 'vertical') {
|
||||
return {
|
||||
transform: `translateY(${props.inverted ? '' : '-'}${100 - percent.value}%)`
|
||||
}
|
||||
} else {
|
||||
if (dir.value === 'rtl') {
|
||||
return {
|
||||
transform: `translateX(${props.inverted ? '-' : ''}${100 - percent.value}%)`
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
transform: `translateX(${props.inverted ? '' : '-'}${100 - percent.value}%)`
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ export interface SelectMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends M
|
||||
/** The controlled value of the Combobox. Can be binded-with with `v-model`. */
|
||||
modelValue?: SelectModelValue<T, V, M>
|
||||
/** Whether multiple options can be selected or not. */
|
||||
multiple?: M
|
||||
multiple?: M & boolean
|
||||
}
|
||||
|
||||
export type SelectMenuEmits<T, V, M extends boolean> = Omit<ComboboxRootEmits<T>, 'update:modelValue'> & {
|
||||
@@ -160,12 +160,10 @@ const searchTerm = defineModel<string>('searchTerm', { default: '' })
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const { t } = useLocale()
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'modelValue', 'defaultValue', 'selectedValue', 'open', 'defaultOpen', 'resetSearchTermOnBlur'), emits)
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'modelValue', 'defaultValue', 'selectedValue', 'open', 'defaultOpen', 'multiple', 'resetSearchTermOnBlur'), emits)
|
||||
const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, position: 'popper' }) as ComboboxContentProps)
|
||||
const arrowProps = toRef(() => props.arrow as ComboboxArrowProps)
|
||||
const searchInputProps = toRef(() => defu(props.searchInput, { placeholder: 'Search...', variant: 'none' }) as InputProps)
|
||||
// This is a hack due to generic boolean casting (see https://github.com/nuxt/ui/issues/2541)
|
||||
const multiple = toRef(() => typeof props.multiple === 'string' ? true : props.multiple)
|
||||
|
||||
const [DefineCreateItemTemplate, ReuseCreateItemTemplate] = createReusableTemplate()
|
||||
|
||||
@@ -187,7 +185,7 @@ const ui = computed(() => selectMenu({
|
||||
}))
|
||||
|
||||
function displayValue(value: T | T[]): string {
|
||||
if (multiple.value && Array.isArray(value)) {
|
||||
if (props.multiple && Array.isArray(value)) {
|
||||
return value.map(v => displayValue(v)).filter(Boolean).join(', ')
|
||||
}
|
||||
|
||||
@@ -307,7 +305,6 @@ function onUpdateOpen(value: boolean) {
|
||||
as-child
|
||||
:name="name"
|
||||
:disabled="disabled"
|
||||
:multiple="multiple"
|
||||
:display-value="() => searchTerm"
|
||||
:filter-function="() => rootItems"
|
||||
@update:model-value="onUpdate"
|
||||
|
||||
@@ -120,14 +120,14 @@ const ui = computed(() => slideover({
|
||||
|
||||
<template>
|
||||
<DialogRoot v-slot="{ open }" v-bind="rootProps">
|
||||
<DialogTrigger v-if="!!slots.default" as-child>
|
||||
<DialogTrigger v-if="!!slots.default" as-child :class="props.class">
|
||||
<slot :open="open" />
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogPortal :disabled="!portal">
|
||||
<DialogOverlay v-if="overlay" :class="ui.overlay({ class: props.ui?.overlay })" />
|
||||
|
||||
<DialogContent :data-side="side" :class="ui.content({ class: [props.class, props.ui?.content] })" v-bind="contentProps" v-on="contentEvents">
|
||||
<DialogContent :data-side="side" :class="ui.content({ class: [!slots.default && props.class, props.ui?.content] })" v-bind="contentProps" v-on="contentEvents">
|
||||
<slot name="content">
|
||||
<div v-if="!!slots.header || (title || !!slots.title) || (description || !!slots.description) || (close || !!slots.close)" :class="ui.header({ class: props.ui?.header })">
|
||||
<slot name="header">
|
||||
|
||||
@@ -70,12 +70,12 @@ const ui = computed(() => tooltip({
|
||||
|
||||
<template>
|
||||
<TooltipRoot v-slot="{ open }" v-bind="rootProps">
|
||||
<TooltipTrigger v-if="!!slots.default" as-child>
|
||||
<TooltipTrigger v-if="!!slots.default" as-child :class="props.class">
|
||||
<slot :open="open" />
|
||||
</TooltipTrigger>
|
||||
|
||||
<TooltipPortal :disabled="!portal">
|
||||
<TooltipContent v-bind="contentProps" :class="ui.content({ class: [props.class, props.ui?.content] })">
|
||||
<TooltipContent v-bind="contentProps" :class="ui.content({ class: [!slots.default && props.class, props.ui?.content] })">
|
||||
<slot name="content">
|
||||
<span v-if="text" :class="ui.text({ class: props.ui?.text })">{{ text }}</span>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { computed, inject, ref } from 'vue'
|
||||
import type { InjectionKey, Ref } from 'vue'
|
||||
import type { Locale } from '../types/locale'
|
||||
import { buildLocaleContext } from '../utils/locale'
|
||||
import { en } from '../locale'
|
||||
import en from '../locale/en'
|
||||
import { createSharedComposable } from '@vueuse/core'
|
||||
|
||||
export const localeContextInjectionKey: InjectionKey<Ref<Locale | undefined>> = Symbol('nuxt-ui.locale-context')
|
||||
|
||||
@@ -362,6 +362,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@keyframes carousel-rtl {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
width: 50%
|
||||
}
|
||||
|
||||
0% {
|
||||
transform: translateX(100%)
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(-200%)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@keyframes carousel-vertical {
|
||||
|
||||
0%,
|
||||
@@ -394,6 +412,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes carousel-inverse-rtl {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
width: 50%
|
||||
}
|
||||
|
||||
0% {
|
||||
transform: translateX(-200%)
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(100%)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes carousel-inverse-vertical {
|
||||
|
||||
0%,
|
||||
|
||||
@@ -10,6 +10,12 @@ export default defineLocale({
|
||||
noData: 'لا توجد بيانات',
|
||||
create: 'إنشاء "{label}"'
|
||||
},
|
||||
calendar: {
|
||||
prevYear: 'السنة السابقة',
|
||||
nextYear: 'السنة المقبلة',
|
||||
prevMonth: 'الشهر السابق',
|
||||
nextMonth: 'الشهر المقبل'
|
||||
},
|
||||
inputNumber: {
|
||||
increment: 'زيادة',
|
||||
decrement: 'تقليل'
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user