Compare commits

..

2 Commits

Author SHA1 Message Date
HugoRCD
f6b984e475 up 2025-05-31 23:45:37 +02:00
HugoRCD
ac696433b7 feat(SelectMenu): implement virtualization 2025-05-31 23:26:24 +02:00
355 changed files with 7701 additions and 14904 deletions

View File

@@ -10,7 +10,7 @@ body:
id: env id: env
attributes: attributes:
label: Environment label: Environment
description: You can use `npx nuxt info` to fill this section description: You can use `npx nuxi info` to fill this section
placeholder: | placeholder: |
- Operating System: `Darwin` - Operating System: `Darwin`
- Node Version: `v18.16.0` - Node Version: `v18.16.0`

View File

@@ -10,7 +10,7 @@ body:
id: env id: env
attributes: attributes:
label: Environment label: Environment
description: You can use `npx nuxt info` to fill this section description: You can use `npx nuxi info` to fill this section
placeholder: | placeholder: |
- Operating System: `Darwin` - Operating System: `Darwin`
- Node Version: `v18.16.0` - Node Version: `v18.16.0`

View File

@@ -6,6 +6,10 @@ jobs:
deploy: deploy:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
environment:
name: ${{ github.ref == 'refs/heads/v3' && 'production' || 'preview' }}
url: ${{ steps.deploy.outputs.deployment-url }}
permissions: permissions:
contents: read contents: read
id-token: write id-token: write
@@ -36,10 +40,14 @@ jobs:
- name: Prepare build - name: Prepare build
run: pnpm run dev:prepare run: pnpm run dev:prepare
- name: Deploy to NuxtHub - name: Build application
uses: nuxt-hub/action@v2 run: pnpm run docs:build
env: env:
NODE_OPTIONS: '--max-old-space-size=8192' NODE_OPTIONS: '--max-old-space-size=8192'
- name: Deploy to NuxtHub
uses: nuxt-hub/action@v1
id: deploy
with: with:
project-key: ui-7eg3 project-key: ui-7eg3
directory: docs directory: docs/dist

View File

@@ -93,7 +93,7 @@ jobs:
- name: Store commit SHA - name: Store commit SHA
run: | run: |
echo "COMMIT_SHA=$(echo ${{ github.event.pull_request.head.sha || github.sha }} | cut -c1-7)" >> $GITHUB_ENV echo "COMMIT_SHA=$(echo ${{ github.workflow_sha }} | cut -c1-7)" >> $GITHUB_ENV
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v4 uses: pnpm/action-setup@v4
@@ -111,7 +111,7 @@ jobs:
run: pnpm install --ignore-workspace run: pnpm install --ignore-workspace
- name: Prepare - name: Prepare
run: pnpm nuxt prepare run: pnpm nuxi prepare
- name: Typecheck - name: Typecheck
run: pnpm run typecheck run: pnpm run typecheck
@@ -138,7 +138,7 @@ jobs:
- name: Store commit SHA - name: Store commit SHA
run: | run: |
echo "COMMIT_SHA=$(echo ${{ github.event.pull_request.head.sha || github.sha }} | cut -c1-7)" >> $GITHUB_ENV echo "COMMIT_SHA=$(echo ${{ github.workflow_sha }} | cut -c1-7)" >> $GITHUB_ENV
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v4 uses: pnpm/action-setup@v4
@@ -183,7 +183,7 @@ jobs:
- name: Store commit SHA - name: Store commit SHA
run: | run: |
echo "COMMIT_SHA=$(echo ${{ github.event.pull_request.head.sha || github.sha }} | cut -c1-7)" >> $GITHUB_ENV echo "COMMIT_SHA=$(echo ${{ github.workflow_sha }} | cut -c1-7)" >> $GITHUB_ENV
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v4 uses: pnpm/action-setup@v4
@@ -235,7 +235,7 @@ jobs:
- name: Store commit SHA - name: Store commit SHA
run: | run: |
echo "COMMIT_SHA=$(echo ${{ github.event.pull_request.head.sha || github.sha }} | cut -c1-7)" >> $GITHUB_ENV echo "COMMIT_SHA=$(echo ${{ github.workflow_sha }} | cut -c1-7)" >> $GITHUB_ENV
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v4 uses: pnpm/action-setup@v4

View File

@@ -9,6 +9,10 @@ jobs:
deploy: deploy:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
environment:
name: ${{ github.ref == 'refs/heads/v3' && 'production' || 'preview' }}
url: ${{ steps.deploy.outputs.deployment-url }}
permissions: permissions:
contents: read contents: read
id-token: write id-token: write
@@ -36,10 +40,14 @@ jobs:
- name: Prepare build - name: Prepare build
run: pnpm run dev:prepare run: pnpm run dev:prepare
- name: Deploy to NuxtHub - name: Build application
uses: nuxt-hub/action@v2 run: pnpm run dev:build
env: env:
NODE_OPTIONS: '--max-old-space-size=8192' NITRO_PRESET: cloudflare-pages
- name: Deploy to NuxtHub
uses: nuxt-hub/action@v1
id: deploy
with: with:
project-key: ui3-playground-pb9b project-key: ui3-playground-pb9b
directory: playground directory: playground/dist

View File

@@ -1,27 +0,0 @@
name: reproduction
on:
workflow_dispatch:
schedule:
- cron: '30 1 * * *'
jobs:
reproduction:
runs-on: ubuntu-latest
permissions:
actions: write
issues: write
steps:
- uses: actions/stale@v9
with:
days-before-stale: -1 # Issues and PR will never be flagged stale automatically.
stale-issue-label: 'needs reproduction' # Label that flags an issue as stale.
only-labels: 'needs reproduction' # Only process these issues
days-before-issue-close: 7
ignore-updates: true
remove-stale-when-updated: false
close-issue-message: This issue was closed because it was open for 7 days without a reproduction.
close-issue-label: closed-by-bot
operations-per-run: 300 #default 30

View File

@@ -1,7 +1,6 @@
name: stale name: stale
on: on:
workflow_dispatch:
schedule: schedule:
- cron: '30 1 * * *' - cron: '30 1 * * *'
@@ -10,28 +9,17 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
actions: write
issues: write issues: write
steps: steps:
- uses: actions/stale@4c023f01d613e60293d8004f251a18bfb9bbd71d - uses: actions/stale@v9
with: with:
days-before-pr-stale: -1 days-before-stale: -1 # Issues and PR will never be flagged stale automatically.
days-before-stale: 60 stale-issue-label: 'needs reproduction' # Label that flags an issue as stale.
days-before-close: 7 only-labels: 'needs reproduction' # Only process these issues
stale-issue-label: 'stale' days-before-issue-close: 7
close-issue-label: 'closed-by-bot' ignore-updates: true
close-issue-message: | remove-stale-when-updated: false
Hi! 👋 close-issue-message: This issue was closed because it was open for 7 days without a reproduction.
close-issue-label: closed-by-bot
This issue has been automatically **closed** due to prolonged inactivity. operations-per-run: 300 #default 30
We're a small team and can't address every report, but we appreciate your feedback and contributions.
If this issue is still relevant with the latest version of Nuxt UI, please feel free to reopen or create a new issue with updated details.
Thank you for your understanding and support!
— Nuxt UI Team
exempt-issue-labels: 'feature,announcement,release,reka-ui,upstream'
operations-per-run: 300

View File

@@ -1 +0,0 @@
experimental.normalizeComponentNames=false

View File

@@ -1,51 +1,5 @@
# Changelog # Changelog
## [3.2.0](https://github.com/nuxt/ui/compare/v3.1.3...v3.2.0) (2025-06-25)
### ⚠ BREAKING CHANGES
* **useOverlay:** correct spelling of `unmount` function (#4051)
### Features
* **Avatar:** add `chip` prop ([#4224](https://github.com/nuxt/ui/issues/4224)) ([03ac395](https://github.com/nuxt/ui/commit/03ac395164c02c964361c68743268b1bc90aae59))
* **Carousel:** allow customization of active dot color ([#4229](https://github.com/nuxt/ui/issues/4229)) ([2ee1c5a](https://github.com/nuxt/ui/commit/2ee1c5ac2e20ab9ce2f4037a8e8c64e561b0428b))
* **CommandPalette:** handle `children` in items ([#4226](https://github.com/nuxt/ui/issues/4226)) ([59c26ec](https://github.com/nuxt/ui/commit/59c26ec1230375a24fbaf8a630a696ae854700c7))
* **extendLocale:** new composable ([0f558fc](https://github.com/nuxt/ui/commit/0f558fc0d014d51549222accfc50286d1770d1aa)), closes [#3729](https://github.com/nuxt/ui/issues/3729)
* **Form:** expose loading state to default slot ([#4247](https://github.com/nuxt/ui/issues/4247)) ([ea0c459](https://github.com/nuxt/ui/commit/ea0c459306be585bacaaf5b433114d072550c824))
* **InputTags:** new component ([#4261](https://github.com/nuxt/ui/issues/4261)) ([54bb228](https://github.com/nuxt/ui/commit/54bb2282c58d3bf5a7dde4cdee687c68efd934a0))
* **locale:** add Luxembourgish language ([#4264](https://github.com/nuxt/ui/issues/4264)) ([43cbb94](https://github.com/nuxt/ui/commit/43cbb94ee25106b414fc8fe979fa65ebaa9ccc76))
* **Modal/Slideover:** add `actions` slot ([#4358](https://github.com/nuxt/ui/issues/4358)) ([8156971](https://github.com/nuxt/ui/commit/81569713e9da9d5531ecdf4614660b84c686fa81))
* **Modal/Slideover:** add `close` method in slots ([#4219](https://github.com/nuxt/ui/issues/4219)) ([5835eb5](https://github.com/nuxt/ui/commit/5835eb5f0f835b5f03646dec78f85b2f556a109b))
* **Select/SelectMenu/Tabs:** expose trigger refs ([7a2bd4e](https://github.com/nuxt/ui/commit/7a2bd4e6179373902ba6f285903ea896fd1d378f)), closes [#4292](https://github.com/nuxt/ui/issues/4292)
* **Select/SelectMenu:** handle dynamic `autofocus` ([1a4de49](https://github.com/nuxt/ui/commit/1a4de49c1665c9ef65279315be0393d6272447b9)), closes [#4324](https://github.com/nuxt/ui/issues/4324)
* **Table:** add `body-top` / `body-bottom` slots ([#4354](https://github.com/nuxt/ui/issues/4354)) ([595fc64](https://github.com/nuxt/ui/commit/595fc64515613fe82c3a56fc5518f2e3fcce6e19))
* **Timeline:** add `reverse` prop ([#4316](https://github.com/nuxt/ui/issues/4316)) ([5170cfd](https://github.com/nuxt/ui/commit/5170cfd7eb44a25c64673cf12979f9ca1049695f))
* **Timeline:** new component ([#4215](https://github.com/nuxt/ui/issues/4215)) ([8017767](https://github.com/nuxt/ui/commit/80177679f2aa0d7f0e39e639a02d527a06e6172c))
### Bug Fixes
* **Card/Drawer/Modal:** prevent scrollbars overflow ([#4368](https://github.com/nuxt/ui/issues/4368)) ([c3adc38](https://github.com/nuxt/ui/commit/c3adc381c90dad7152e27fc303ee678efc7c4c94))
* **components:** remove default `md` size on buttons ([#4357](https://github.com/nuxt/ui/issues/4357)) ([be41aed](https://github.com/nuxt/ui/commit/be41aed1f3d3476801e1840dbb8766926bc93c05))
* **defineShortcuts:** allow `meta_-` shortcut ([#4321](https://github.com/nuxt/ui/issues/4321)) ([4e7c1c9](https://github.com/nuxt/ui/commit/4e7c1c9c305b45dd76d4c238e70a6aeedae78c8b))
* **Form:** conditionally type form data via `transform` prop ([#4188](https://github.com/nuxt/ui/issues/4188)) ([37abcc6](https://github.com/nuxt/ui/commit/37abcc6a5b0a678be626673af5067956657a50d6))
* **Form:** expose reactive fields ([#4386](https://github.com/nuxt/ui/issues/4386)) ([1a8feb7](https://github.com/nuxt/ui/commit/1a8feb751e6827c414ef82fe9fb259ba7dcc7e08))
* **InputMenu/SelectMenu:** dynamic `empty` size ([ba3c6e8](https://github.com/nuxt/ui/commit/ba3c6e8788ed75d86d4406749797da52d7816b84)), closes [#4377](https://github.com/nuxt/ui/issues/4377)
* **InputTags:** extend emits interface ([8781a07](https://github.com/nuxt/ui/commit/8781a079096def0d3bae5b8d896db0df6ce37e23))
* **Modal/Slideover:** don't emit `close:prevent` on `closeAutoFocus` ([150b334](https://github.com/nuxt/ui/commit/150b334b1d242c6dc132193e23359c03e6f35666))
* **NavigationMenu:** nested accordion context at every level ([#4363](https://github.com/nuxt/ui/issues/4363)) ([2fa8db6](https://github.com/nuxt/ui/commit/2fa8db64ddf4c92a19e73774143518d87d001b72))
* **NavigationMenu:** set content `max-height` in `horizontal` orientation ([62bc7b2](https://github.com/nuxt/ui/commit/62bc7b25a2d205d8dffb47a109196f91ff3e823a)), closes [#4208](https://github.com/nuxt/ui/issues/4208)
* **Pagination:** match default button `size` ([#4350](https://github.com/nuxt/ui/issues/4350)) ([4dd56c8](https://github.com/nuxt/ui/commit/4dd56c8111e5a224105b82d541b7742b46abb34a))
* **Select/SelectMenu:** display falsy values ([7df7ee3](https://github.com/nuxt/ui/commit/7df7ee336a925d7ee07f866551dad9350785c9fc))
* **Select/SelectMenu:** prevent empty string display when multiple ([483e473](https://github.com/nuxt/ui/commit/483e473e3f5681cc97c3766ea47283dc95f76345))
* **SelectMenu:** dynamic input size ([b0364b9](https://github.com/nuxt/ui/commit/b0364b96b73b9e543781a35962c03b5a983352c4))
* **Table:** use `tr` as separator ([#4083](https://github.com/nuxt/ui/issues/4083)) ([edca3bc](https://github.com/nuxt/ui/commit/edca3bcb743c7eb63e6abbaa801d3858342a8777))
* **Toast:** calc height on next tick ([3bf5acb](https://github.com/nuxt/ui/commit/3bf5acb683f0ad09735b2417d265d6fcfd901b11)), closes [#4265](https://github.com/nuxt/ui/issues/4265)
* **Toaster:** smoother visibility transition for stacked toasts ([#4367](https://github.com/nuxt/ui/issues/4367)) ([abfd0ed](https://github.com/nuxt/ui/commit/abfd0ede036fa2953f9abc841d77ac71bbd3bba9))
* **useOverlay:** correct spelling of `unmount` function ([#4051](https://github.com/nuxt/ui/issues/4051)) ([546df57](https://github.com/nuxt/ui/commit/546df572fca60325315bed17c9be3367052fb7a9))
* **useOverlay:** set props to original props when `defaultOpen` is set ([#4308](https://github.com/nuxt/ui/issues/4308)) ([66355ba](https://github.com/nuxt/ui/commit/66355ba301d569b9f44527bafc5f8f09bcda63c0))
* **useOverlay:** use original props when not provided to `open` ([#4269](https://github.com/nuxt/ui/issues/4269)) ([bf56e15](https://github.com/nuxt/ui/commit/bf56e15a2eed7d51199d5641649a822e91ca41ba))
## [3.1.3](https://github.com/nuxt/ui/compare/v3.1.2...v3.1.3) (2025-05-26) ## [3.1.3](https://github.com/nuxt/ui/compare/v3.1.2...v3.1.3) (2025-05-26)
### ⚠ BREAKING CHANGES ### ⚠ BREAKING CHANGES

View File

@@ -31,11 +31,10 @@ const component = ({ name, primitive, pro, prose, content }) => {
? ` ? `
<script lang="ts"> <script lang="ts">
import type { AppConfig } from '@nuxt/schema' import type { AppConfig } from '@nuxt/schema'
${pro ? `import type { ComponentConfig } from '@nuxt/ui'` : ''}
import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}' import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}'
${!pro ? `import type { ComponentConfig } from '../types/utils'` : ''} import type { ComponentConfig } from '../types/utils'
type ${upperName} = ComponentConfig<typeof theme, AppConfig, '${camelName}'${pro ? `, '${key}'` : ''}> type ${upperName} = ComponentConfig<typeof theme, AppConfig, ${upperName}${pro ? `, '${key}'` : ''}>
export interface ${upperName}Props { export interface ${upperName}Props {
/** /**
@@ -63,7 +62,7 @@ defineSlots<${upperName}Slots>()
const appConfig = useAppConfig() as ${upperName}['AppConfig'] const appConfig = useAppConfig() as ${upperName}['AppConfig']
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.${pro ? 'uiPro' : 'ui'}?.${camelName} || {}) })()) const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.${camelName} || {}) })())
</script> </script>
<template> <template>
@@ -76,11 +75,10 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.${pro ? 'uiPro'
<script lang="ts"> <script lang="ts">
import type { ${upperName}RootProps, ${upperName}RootEmits } from 'reka-ui' import type { ${upperName}RootProps, ${upperName}RootEmits } from 'reka-ui'
import type { AppConfig } from '@nuxt/schema' import type { AppConfig } from '@nuxt/schema'
${pro ? `import type { ComponentConfig } from '@nuxt/ui'` : ''}
import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}' import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}'
${!pro ? `import type { ComponentConfig } from '../types/utils'` : ''} import type { ComponentConfig } from '../types/utils'
type ${upperName} = ComponentConfig<typeof theme, AppConfig, '${camelName}'${pro ? `, '${key}'` : ''}> type ${upperName} = ComponentConfig<typeof theme, AppConfig, ${upperName}${pro ? `, '${key}'` : ''}>
export interface ${upperName}Props extends Pick<${upperName}RootProps> { export interface ${upperName}Props extends Pick<${upperName}RootProps> {
class?: any class?: any
@@ -107,7 +105,7 @@ const appConfig = useAppConfig() as ${upperName}['AppConfig']
const rootProps = useForwardPropsEmits(reactivePick(props), emits) const rootProps = useForwardPropsEmits(reactivePick(props), emits)
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.${pro ? 'uiPro' : 'ui'}?.${camelName} || {}) })()) const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.${camelName} || {}) })())
</script> </script>
<template> <template>
@@ -147,8 +145,7 @@ const test = ({ name, prose, content }) => {
? undefined ? undefined
: ` : `
import { describe, it, expect } from 'vitest' import { describe, it, expect } from 'vitest'
import ${upperName} from '../../${content ? '../' : ''}src/runtime/components/${content ? 'content/' : ''}${upperName}.vue' import ${upperName}, { type ${upperName}Props, type ${upperName}Slots } from '../../${content ? '../' : ''}src/runtime/components/${content ? 'content/' : ''}${upperName}.vue'
import type { ${upperName}Props, ${upperName}Slots } from '../../${content ? '../' : ''}src/runtime/components/${content ? 'content/' : ''}${upperName}.vue'
import ComponentRender from '../${content ? '../' : ''}component-render' import ComponentRender from '../${content ? '../' : ''}component-render'
describe('${upperName}', () => { describe('${upperName}', () => {
@@ -189,7 +186,6 @@ links:${primitive
- label: GitHub - label: GitHub
icon: i-simple-icons-github icon: i-simple-icons-github
to: https://github.com/nuxt/${pro ? 'ui-pro' : 'ui'}/tree/v3/src/runtime/components/${upperName}.vue to: https://github.com/nuxt/${pro ? 'ui-pro' : 'ui'}/tree/v3/src/runtime/components/${upperName}.vue
navigation.badge: Soon
--- ---
## Usage ## Usage

View File

@@ -53,7 +53,7 @@ provide('navigation', mappedNavigation)
<NuxtLoadingIndicator color="var(--ui-primary)" :height="2" /> <NuxtLoadingIndicator color="var(--ui-primary)" :height="2" />
<template v-if="!route.path.startsWith('/examples')"> <template v-if="!route.path.startsWith('/examples')">
<Banner /> <!-- <Banner /> -->
<Header :links="links" /> <Header :links="links" />
</template> </template>

View File

@@ -1,19 +1,18 @@
<template> <template>
<UBanner <UBanner
id="nuxtlabs-join-vercel" id="ui3-launch"
title="NuxtLabs is joining Vercel" icon="i-lucide-rocket"
icon="i-simple-icons-vercel" :actions="[
to="https://nuxtlabs.com/?utm_source=nuxt-ui&utm_medium=banner&utm_campaign=nuxtlabs-vercel" {
target="_blank" label: 'Discover Nuxt UI Pro',
to: '/pro/pricing',
trailingIcon: 'i-lucide-arrow-right'
}
]"
close close
:actions="[{ >
label: 'Read the announcement', <template #title>
color: 'neutral', <span class="font-semibold">Nuxt UI v3</span> is officially released.
variant: 'outline', </template>
trailingIcon: 'i-lucide-arrow-right', </UBanner>
to: 'https://nuxtlabs.com/?utm_source=nuxt-ui&utm_medium=banner&utm_campaign=nuxtlabs-vercel',
target: '_blank',
class: 'ring-0'
}]"
/>
</template> </template>

View File

@@ -22,7 +22,9 @@ onMounted(() => {
const navigation = inject<Ref<ContentNavigationItem[]>>('navigation') const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
const githubLink = computed(() => `https://github.com/nuxt/${value.value}`) const githubLink = computed(() => {
return `https://github.com/nuxt/${value.value}`
})
const desktopLinks = computed(() => props.links.map(({ icon, ...link }) => link)) const desktopLinks = computed(() => props.links.map(({ icon, ...link }) => link))
const mobileLinks = computed(() => [ const mobileLinks = computed(() => [
@@ -34,16 +36,6 @@ const mobileLinks = computed(() => [
target: '_blank' target: '_blank'
} }
]) ])
const items = computed(() => {
const ui2 = { label: 'v2.22.0', to: 'https://ui2.nuxt.com' }
const uiPro1 = { label: 'v1.8.0', to: 'https://ui2.nuxt.com/pro' }
return [
{ label: `v${config.version}`, active: true, color: 'primary' as const, checked: true, type: 'checkbox' as const },
route.path === '/' ? ui2 : route.path.startsWith('/pro') ? uiPro1 : module.value === 'ui-pro' ? uiPro1 : ui2
]
})
</script> </script>
<template> <template>
@@ -61,7 +53,7 @@ const items = computed(() => {
<UDropdownMenu <UDropdownMenu
v-slot="{ open }" v-slot="{ open }"
:modal="false" :modal="false"
:items="items" :items="[{ label: `v${config.version}`, active: true, color: 'primary', checked: true, type: 'checkbox' }, { label: module === 'ui-pro' ? 'v1.7.1' : 'v2.21.1', to: module === 'ui-pro' ? 'https://ui2.nuxt.com/pro' : 'https://ui2.nuxt.com' }]"
:ui="{ content: 'w-(--reka-dropdown-menu-trigger-width) min-w-0' }" :ui="{ content: 'w-(--reka-dropdown-menu-trigger-width) min-w-0' }"
size="xs" size="xs"
> >

View File

@@ -1,77 +0,0 @@
<script setup lang="ts">
const route = useRoute()
const toast = useToast()
const { copy, copied } = useClipboard()
const site = useSiteConfig()
const mdPath = computed(() => `${site.url}/raw${route.path}.md`)
const items = [
{
label: 'Copy Markdown link',
icon: 'i-lucide-link',
onSelect() {
copy(mdPath.value)
toast.add({
title: 'Copied to clipboard',
icon: 'i-lucide-check-circle'
})
}
},
{
label: 'View as Markdown',
icon: 'i-simple-icons:markdown',
target: '_blank',
to: `/raw${route.path}.md`
},
{
label: 'Open in ChatGPT',
icon: 'i-simple-icons:openai',
target: '_blank',
to: `https://chatgpt.com/?hints=search&q=${encodeURIComponent(`Read ${mdPath.value} so I can ask questions about it.`)}`
},
{
label: 'Open in Claude',
icon: 'i-simple-icons:anthropic',
target: '_blank',
to: `https://claude.ai/new?q=${encodeURIComponent(`Read ${mdPath.value} so I can ask questions about it.`)}`
}
]
async function copyPage() {
copy(await $fetch<string>(`/raw${route.path}.md`))
}
</script>
<template>
<UButtonGroup>
<UButton
label="Copy page"
:icon="copied ? 'i-lucide-copy-check' : 'i-lucide-copy'"
color="neutral"
variant="outline"
:ui="{
leadingIcon: [copied ? 'text-primary' : 'text-neutral', 'size-3.5']
}"
@click="copyPage"
/>
<UDropdownMenu
:items="items"
:content="{
align: 'end',
side: 'bottom',
sideOffset: 8
}"
:ui="{
content: 'w-48'
}"
>
<UButton
icon="i-lucide-chevron-down"
size="sm"
color="neutral"
variant="outline"
/>
</UDropdownMenu>
</UButtonGroup>
</template>

View File

@@ -34,7 +34,7 @@ const meta = await fetchComponentMeta(name as any)
</ProseCode> </ProseCode>
</ProseTd> </ProseTd>
<ProseTd> <ProseTd>
<HighlightInlineType v-if="slot.type" :type="slot.type.replace(/ui:\s*\{[^}]*\}/g, 'ui: {}')" /> <HighlightInlineType v-if="slot.type" :type="slot.type" />
<MDC v-if="slot.description" :value="slot.description" class="text-toned mt-1" :cache-key="`${kebabCase(route.path)}-${slot.name}-description`" /> <MDC v-if="slot.description" :value="slot.description" class="text-toned mt-1" :cache-key="`${kebabCase(route.path)}-${slot.name}-description`" />
</ProseTd> </ProseTd>

View File

@@ -26,7 +26,6 @@ function getEmojiFlag(locale: string): string {
km: 'kh', // Khmer -> Cambodia km: 'kh', // Khmer -> Cambodia
ko: 'kr', // Korean -> South Korea ko: 'kr', // Korean -> South Korea
ky: 'kg', // Kyrgyz -> Kyrgyzstan ky: 'kg', // Kyrgyz -> Kyrgyzstan
lb: 'lu', // Luxembourgish -> Luxembourg
ms: 'my', // Malay -> Malaysia ms: 'my', // Malay -> Malaysia
nb: 'no', // Norwegian Bokmål -> Norway nb: 'no', // Norwegian Bokmål -> Norway
sl: 'si', // Slovenian -> Slovenia sl: 'si', // Slovenian -> Slovenia

View File

@@ -14,8 +14,8 @@ const items = [
v-slot="{ item }" v-slot="{ item }"
orientation="vertical" orientation="vertical"
:items="items" :items="items"
:ui="{ container: 'h-[336px]' }"
class="w-full max-w-xs mx-auto" class="w-full max-w-xs mx-auto"
:ui="{ container: 'h-[336px]' }"
> >
<img :src="item" width="320" height="320" class="rounded-lg"> <img :src="item" width="320" height="320" class="rounded-lg">
</UCarousel> </UCarousel>

View File

@@ -83,7 +83,7 @@ const groups = [
</template> </template>
<template #billing-label="{ item }"> <template #billing-label="{ item }">
<span class="font-medium text-primary">{{ item.label }}</span> {{ item.label }}
<UBadge variant="subtle" size="sm"> <UBadge variant="subtle" size="sm">
50% off 50% off

View File

@@ -1,78 +0,0 @@
<script setup lang="ts">
const groups = [
{
id: 'actions',
items: [
{
label: 'Add new file',
suffix: 'Create a new file in the current directory',
icon: 'i-lucide-file-plus',
kbds: ['meta', 'N']
},
{
label: 'Add new folder',
suffix: 'Create a new folder in the current directory',
icon: 'i-lucide-folder-plus',
kbds: ['meta', 'F']
},
{
label: 'Search files',
suffix: 'Search across all files in the project',
icon: 'i-lucide-search',
kbds: ['meta', 'P']
},
{
label: 'Settings',
suffix: 'Open application settings',
icon: 'i-lucide-settings',
kbds: ['meta', ',']
}
]
},
{
id: 'recent',
label: 'Recent',
items: [
{
label: 'project.vue',
suffix: 'components/',
icon: 'i-vscode-icons-file-type-vue'
},
{
label: 'readme.md',
suffix: 'docs/',
icon: 'i-vscode-icons-file-type-markdown'
},
{
label: 'package.json',
suffix: 'root/',
icon: 'i-vscode-icons-file-type-node'
}
]
}
]
</script>
<template>
<UCommandPalette :groups="groups" class="flex-1 h-80">
<template #footer>
<div class="flex items-center justify-between gap-2">
<UIcon name="i-simple-icons-nuxtdotjs" class="size-5 text-dimmed ml-1" />
<div class="flex items-center gap-1">
<UButton color="neutral" variant="ghost" label="Open Command" class="text-dimmed" size="xs">
<template #trailing>
<UKbd value="enter" />
</template>
</UButton>
<USeparator orientation="vertical" class="h-4" />
<UButton color="neutral" variant="ghost" label="Actions" class="text-dimmed" size="xs">
<template #trailing>
<UKbd value="meta" />
<UKbd value="k" />
</template>
</UButton>
</div>
</div>
</template>
</UCommandPalette>
</template>

View File

@@ -1,119 +0,0 @@
<script setup lang="ts">
const toast = useToast()
const groups = [{
id: 'actions',
label: 'Actions',
items: [{
label: 'Create new',
icon: 'i-lucide-plus',
children: [{
label: 'New file',
icon: 'i-lucide-file-plus',
suffix: 'Create a new file in the current directory',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'New file created!' })
},
kbds: ['meta', 'N']
}, {
label: 'New folder',
icon: 'i-lucide-folder-plus',
suffix: 'Create a new folder in the current directory',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'New folder created!' })
},
kbds: ['meta', 'F']
}, {
label: 'New project',
icon: 'i-lucide-folder-git',
suffix: 'Create a new project from a template',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'New project created!' })
},
kbds: ['meta', 'P']
}]
}, {
label: 'Share',
icon: 'i-lucide-share',
children: [{
label: 'Copy link',
icon: 'i-lucide-link',
suffix: 'Copy a link to the current item',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'Link copied to clipboard!' })
},
kbds: ['meta', 'L']
}, {
label: 'Share via email',
icon: 'i-lucide-mail',
suffix: 'Share the current item via email',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'Share via email dialog opened!' })
}
}, {
label: 'Share on social',
icon: 'i-lucide-share-2',
suffix: 'Share the current item on social media',
children: [{
label: 'Twitter',
icon: 'i-simple-icons-twitter',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'Shared on Twitter!' })
}
}, {
label: 'LinkedIn',
icon: 'i-simple-icons-linkedin',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'Shared on LinkedIn!' })
}
}, {
label: 'Facebook',
icon: 'i-simple-icons-facebook',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'Shared on Facebook!' })
}
}]
}]
}, {
label: 'Settings',
icon: 'i-lucide-settings',
children: [{
label: 'General',
icon: 'i-lucide-sliders',
suffix: 'Configure general settings',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'General settings opened!' })
}
}, {
label: 'Appearance',
icon: 'i-lucide-palette',
suffix: 'Customize the appearance',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'Appearance settings opened!' })
}
}, {
label: 'Security',
icon: 'i-lucide-shield',
suffix: 'Manage security settings',
onSelect(e: Event) {
e.preventDefault()
toast.add({ title: 'Security settings opened!' })
}
}]
}]
}]
</script>
<template>
<UCommandPalette :groups="groups" class="flex-1" />
</template>

View File

@@ -1,15 +0,0 @@
<template>
<UDrawer :ui="{ content: 'h-full', overlay: 'bg-inverted/30' }">
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #footer>
<UDrawer nested :ui="{ content: 'h-full', overlay: 'bg-inverted/30' }">
<UButton color="neutral" variant="outline" label="Open nested" />
<template #content>
<Placeholder class="flex-1 m-4" />
</template>
</UDrawer>
</template>
</UDrawer>
</template>

View File

@@ -1,6 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { object, string, nonempty, refine } from 'superstruct' import { object, string, nonempty, refine, type Infer } from 'superstruct'
import type { Infer } from 'superstruct'
import type { FormSubmitEvent } from '@nuxt/ui' import type { FormSubmitEvent } from '@nuxt/ui'
const schema = object({ const schema = object({

View File

@@ -1,6 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { object, string } from 'yup' import { object, string, type InferType } from 'yup'
import type { InferType } from 'yup'
import type { FormSubmitEvent } from '@nuxt/ui' import type { FormSubmitEvent } from '@nuxt/ui'
const schema = object({ const schema = object({

View File

@@ -1,31 +0,0 @@
<script setup lang="ts">
const { data: users } = await useFetch('https://jsonplaceholder.typicode.com/users', {
key: 'typicode-users-email',
transform: (data: { id: number, name: string, email: string }[]) => {
return data?.map(user => ({
label: user.name,
value: String(user.id),
email: user.email,
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
}))
},
lazy: true
})
</script>
<template>
<UInputMenu
:items="users"
icon="i-lucide-user"
placeholder="Select user"
:ui="{ content: 'min-w-fit' }"
>
<template #item-label="{ item }">
{{ item.label }}
<span class="text-muted">
{{ item.email }}
</span>
</template>
</UInputMenu>
</template>

View File

@@ -35,7 +35,6 @@ const items = ref([
} }
} }
] satisfies InputMenuItem[]) ] satisfies InputMenuItem[])
const value = ref(items.value[0]) const value = ref(items.value[0])
</script> </script>

View File

@@ -1,9 +0,0 @@
<script setup lang="ts">
const tags = ref(['Vue'])
</script>
<template>
<UFormField label="Tags" required>
<UInputTags v-model="tags" placeholder="Enter tags..." />
</UFormField>
</template>

View File

@@ -1,9 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
import { useClipboard } from '@vueuse/core' const value = ref('npx nuxi module add ui')
const copied = ref(false)
const value = ref('npx nuxt module add ui') function copy() {
navigator.clipboard.writeText(value.value)
copied.value = true
const { copy, copied } = useClipboard() setTimeout(() => {
copied.value = false
}, 2000)
}
</script> </script>
<template> <template>
@@ -19,7 +25,7 @@ const { copy, copied } = useClipboard()
size="sm" size="sm"
:icon="copied ? 'i-lucide-copy-check' : 'i-lucide-copy'" :icon="copied ? 'i-lucide-copy-check' : 'i-lucide-copy'"
aria-label="Copy to clipboard" aria-label="Copy to clipboard"
@click="copy(value)" @click="copy"
/> />
</UTooltip> </UTooltip>
</template> </template>

View File

@@ -1,14 +0,0 @@
<script setup lang="ts">
import { vMaska } from 'maska/vue'
</script>
<template>
<div class="flex flex-col gap-2">
<UInput v-maska="'#### #### #### ####'" placeholder="4242 4242 4242 4242" icon="i-lucide-credit-card" />
<div class="flex items-center gap-2">
<UInput v-maska="'##/##'" placeholder="MM/YY" icon="i-lucide-calendar" />
<UInput v-maska="'###'" placeholder="CVC" />
</div>
</div>
</template>

View File

@@ -40,9 +40,9 @@ const text = computed(() => {
placeholder="Password" placeholder="Password"
:color="color" :color="color"
:type="show ? 'text' : 'password'" :type="show ? 'text' : 'password'"
:ui="{ trailing: 'pe-1' }"
:aria-invalid="score < 4" :aria-invalid="score < 4"
aria-describedby="password-strength" aria-describedby="password-strength"
:ui="{ trailing: 'pe-1' }"
class="w-full" class="w-full"
> >
<template #trailing> <template #trailing>

View File

@@ -24,10 +24,3 @@ const password = ref('')
</template> </template>
</UInput> </UInput>
</template> </template>
<style>
/* Hide the password reveal button in Edge */
::-ms-reveal {
display: none;
}
</style>

View File

@@ -6,12 +6,14 @@ const count = ref(0)
const toast = useToast() const toast = useToast()
const overlay = useOverlay() const overlay = useOverlay()
const modal = overlay.create(LazyModalExample) const modal = overlay.create(LazyModalExample, {
props: {
count: count.value
}
})
async function open() { async function open() {
const instance = modal.open({ const instance = modal.open()
count: count.value
})
const shouldIncrement = await instance.result const shouldIncrement = await instance.result

View File

@@ -62,13 +62,13 @@ const items = [
<template> <template>
<UNavigationMenu <UNavigationMenu
:items="items" :items="items"
class="w-full justify-center"
:ui="{ :ui="{
viewport: 'sm:w-(--reka-navigation-menu-viewport-width)', viewport: 'sm:w-(--reka-navigation-menu-viewport-width)',
content: 'sm:w-auto', content: 'sm:w-auto',
childList: 'sm:w-96', childList: 'sm:w-96',
childLinkDescription: 'text-balance line-clamp-2' childLinkDescription: 'text-balance line-clamp-2'
}" }"
class="w-full justify-center"
> >
<template #docs-content="{ item }"> <template #docs-content="{ item }">
<ul class="grid gap-2 p-4 lg:w-[500px] lg:grid-cols-[minmax(0,.75fr)_minmax(0,1fr)]"> <ul class="grid gap-2 p-4 lg:w-[500px] lg:grid-cols-[minmax(0,.75fr)_minmax(0,1fr)]">

View File

@@ -1,43 +0,0 @@
<script setup lang="ts">
const open = ref(false)
const anchor = ref({ x: 0, y: 0 })
const reference = computed(() => ({
getBoundingClientRect: () =>
({
width: 0,
height: 0,
left: anchor.value.x,
right: anchor.value.x,
top: anchor.value.y,
bottom: anchor.value.y,
...anchor.value
} as DOMRect)
}))
</script>
<template>
<UPopover
:open="open"
:reference="reference"
:content="{ side: 'top', sideOffset: 16, updatePositionStrategy: 'always' }"
>
<div
class="flex items-center justify-center rounded-md border border-dashed border-accented text-sm aspect-video w-72"
@pointerenter="open = true"
@pointerleave="open = false"
@pointermove="(ev) => {
anchor.x = ev.clientX
anchor.y = ev.clientY
}"
>
Hover me
</div>
<template #content>
<div class="p-4">
{{ anchor.x.toFixed(0) }} - {{ anchor.y.toFixed(0) }}
</div>
</template>
</UPopover>
</template>

View File

@@ -1,32 +0,0 @@
<script setup lang="ts">
const { data: users } = await useFetch('https://jsonplaceholder.typicode.com/users', {
key: 'typicode-users-email',
transform: (data: { id: number, name: string, email: string }[]) => {
return data?.map(user => ({
label: user.name,
value: String(user.id),
email: user.email,
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
}))
},
lazy: true
})
</script>
<template>
<USelectMenu
:items="users"
icon="i-lucide-user"
placeholder="Select user"
:ui="{ content: 'min-w-fit' }"
class="w-48"
>
<template #item-label="{ item }">
{{ item.label }}
<span class="text-muted">
{{ item.email }}
</span>
</template>
</USelectMenu>
</template>

View File

@@ -35,7 +35,6 @@ const items = ref([
} }
} }
] satisfies SelectMenuItem[]) ] satisfies SelectMenuItem[])
const value = ref(items.value[0]) const value = ref(items.value[0])
</script> </script>

View File

@@ -24,7 +24,6 @@ const items = ref([
} }
} }
] satisfies SelectMenuItem[]) ] satisfies SelectMenuItem[])
const value = ref(items.value[0]) const value = ref(items.value[0])
</script> </script>

View File

@@ -23,7 +23,6 @@ const items = ref([
icon: 'i-lucide-circle-check' icon: 'i-lucide-circle-check'
} }
] satisfies SelectMenuItem[]) ] satisfies SelectMenuItem[])
const value = ref(items.value[0]) const value = ref(items.value[0])
</script> </script>

View File

@@ -1,35 +0,0 @@
<script setup lang="ts">
const value = ref<string>()
const { data: users } = await useFetch('https://jsonplaceholder.typicode.com/users', {
key: 'typicode-users-email',
transform: (data: { id: number, name: string, email: string }[]) => {
return data?.map(user => ({
label: user.name,
email: user.email,
value: String(user.id),
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
}))
},
lazy: true
})
</script>
<template>
<USelect
v-model="value"
:items="users"
placeholder="Select user"
value-key="value"
:ui="{ content: 'min-w-fit' }"
class="w-48"
>
<template #item-label="{ item }">
{{ item.label }}
<span class="text-muted">
{{ item.email }}
</span>
</template>
</USelect>
</template>

View File

@@ -24,8 +24,8 @@ function getUserAvatar(value: string) {
:loading="status === 'pending'" :loading="status === 'pending'"
icon="i-lucide-user" icon="i-lucide-user"
placeholder="Select user" placeholder="Select user"
value-key="value"
class="w-48" class="w-48"
value-key="value"
> >
<template #leading="{ modelValue, ui }"> <template #leading="{ modelValue, ui }">
<UAvatar <UAvatar

View File

@@ -35,7 +35,6 @@ const items = ref([
} }
} }
] satisfies SelectItem[]) ] satisfies SelectItem[])
const value = ref(items.value[0]?.value) const value = ref(items.value[0]?.value)
const avatar = computed(() => items.value.find(item => item.value === value.value)?.avatar) const avatar = computed(() => items.value.find(item => item.value === value.value)?.avatar)

View File

@@ -23,7 +23,6 @@ const items = ref([
icon: 'i-lucide-circle-check' icon: 'i-lucide-circle-check'
} }
] satisfies SelectItem[]) ] satisfies SelectItem[])
const value = ref(items.value[0]?.value) const value = ref(items.value[0]?.value)
const icon = computed(() => items.value.find(item => item.value === value.value)?.icon) const icon = computed(() => items.value.find(item => item.value === value.value)?.icon)

View File

@@ -6,12 +6,14 @@ const count = ref(0)
const toast = useToast() const toast = useToast()
const overlay = useOverlay() const overlay = useOverlay()
const slideover = overlay.create(LazySlideoverExample) const slideover = overlay.create(LazySlideoverExample, {
props: {
count: count.value
}
})
async function open() { async function open() {
const instance = slideover.open({ const instance = slideover.open()
count: count.value
})
const shouldIncrement = await instance.result const shouldIncrement = await instance.result

View File

@@ -1,106 +0,0 @@
<script setup lang="ts">
import { h, resolveComponent } from 'vue'
import type { TableColumn, TableRow } from '@nuxt/ui'
const UBadge = resolveComponent('UBadge')
type Payment = {
id: string
date: string
status: 'paid' | 'failed' | 'refunded'
email: string
amount: number
}
const data = ref<Payment[]>([{
id: '4600',
date: '2024-03-11T15:30:00',
status: 'paid',
email: 'james.anderson@example.com',
amount: 594
}, {
id: '4599',
date: '2024-03-11T10:10:00',
status: 'failed',
email: 'mia.white@example.com',
amount: 276
}, {
id: '4598',
date: '2024-03-11T08:50:00',
status: 'refunded',
email: 'william.brown@example.com',
amount: 315
}, {
id: '4597',
date: '2024-03-10T19:45:00',
status: 'paid',
email: 'emma.davis@example.com',
amount: 529
}, {
id: '4596',
date: '2024-03-10T15:55:00',
status: 'paid',
email: 'ethan.harris@example.com',
amount: 639
}])
const columns: TableColumn<Payment>[] = [{
accessorKey: 'id',
header: '#',
cell: ({ row }) => `#${row.getValue('id')}`
}, {
accessorKey: 'date',
header: 'Date',
cell: ({ row }) => {
return new Date(row.getValue('date')).toLocaleString('en-US', {
day: 'numeric',
month: 'short',
hour: '2-digit',
minute: '2-digit',
hour12: false
})
}
}, {
accessorKey: 'status',
header: 'Status',
cell: ({ row }) => {
const color = ({
paid: 'success' as const,
failed: 'error' as const,
refunded: 'neutral' as const
})[row.getValue('status') as string]
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
}
}, {
accessorKey: 'email',
header: 'Email'
}, {
accessorKey: 'amount',
header: () => h('div', { class: 'text-right' }, 'Amount'),
footer: ({ column }) => {
const total = column.getFacetedRowModel().rows.reduce((acc: number, row: TableRow<Payment>) => acc + Number.parseFloat(row.getValue('amount')), 0)
const formatted = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'EUR'
}).format(total)
return h('div', { class: 'text-right font-medium' }, `Total: ${formatted}`)
},
cell: ({ row }) => {
const amount = Number.parseFloat(row.getValue('amount'))
const formatted = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'EUR'
}).format(amount)
return h('div', { class: 'text-right font-medium' }, formatted)
}
}]
</script>
<template>
<UTable :data="data" :columns="columns" class="flex-1" />
</template>

View File

@@ -2,7 +2,6 @@
import { h, resolveComponent } from 'vue' import { h, resolveComponent } from 'vue'
import { upperFirst } from 'scule' import { upperFirst } from 'scule'
import type { TableColumn } from '@nuxt/ui' import type { TableColumn } from '@nuxt/ui'
import { useClipboard } from '@vueuse/core'
const UButton = resolveComponent('UButton') const UButton = resolveComponent('UButton')
const UCheckbox = resolveComponent('UCheckbox') const UCheckbox = resolveComponent('UCheckbox')
@@ -10,7 +9,6 @@ const UBadge = resolveComponent('UBadge')
const UDropdownMenu = resolveComponent('UDropdownMenu') const UDropdownMenu = resolveComponent('UDropdownMenu')
const toast = useToast() const toast = useToast()
const { copy } = useClipboard()
type Payment = { type Payment = {
id: string id: string
@@ -222,7 +220,7 @@ const columns: TableColumn<Payment>[] = [{
}, { }, {
label: 'Copy payment ID', label: 'Copy payment ID',
onSelect() { onSelect() {
copy(row.original.id) navigator.clipboard.writeText(row.original.id)
toast.add({ toast.add({
title: 'Payment ID copied to clipboard!', title: 'Payment ID copied to clipboard!',

View File

@@ -1,8 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { h, resolveComponent } from 'vue' import { h, resolveComponent } from 'vue'
import type { TableColumn } from '@nuxt/ui' import type { TableColumn } from '@nuxt/ui'
import { getGroupedRowModel } from '@tanstack/vue-table' import { getGroupedRowModel, type GroupingOptions } from '@tanstack/vue-table'
import type { GroupingOptions } from '@tanstack/vue-table'
const UBadge = resolveComponent('UBadge') const UBadge = resolveComponent('UBadge')

View File

@@ -2,14 +2,12 @@
import { h, resolveComponent } from 'vue' import { h, resolveComponent } from 'vue'
import type { TableColumn } from '@nuxt/ui' import type { TableColumn } from '@nuxt/ui'
import type { Row } from '@tanstack/vue-table' import type { Row } from '@tanstack/vue-table'
import { useClipboard } from '@vueuse/core'
const UButton = resolveComponent('UButton') const UButton = resolveComponent('UButton')
const UBadge = resolveComponent('UBadge') const UBadge = resolveComponent('UBadge')
const UDropdownMenu = resolveComponent('UDropdownMenu') const UDropdownMenu = resolveComponent('UDropdownMenu')
const toast = useToast() const toast = useToast()
const { copy } = useClipboard()
type Payment = { type Payment = {
id: string id: string
@@ -121,7 +119,7 @@ function getRowItems(row: Row<Payment>) {
}, { }, {
label: 'Copy payment ID', label: 'Copy payment ID',
onSelect() { onSelect() {
copy(row.original.id) navigator.clipboard.writeText(row.original.id)
toast.add({ toast.add({
title: 'Payment ID copied to clipboard!', title: 'Payment ID copied to clipboard!',

View File

@@ -1,159 +0,0 @@
<script setup lang="ts">
import { h, resolveComponent } from 'vue'
import type { ContextMenuItem, TableColumn, TableRow } from '@nuxt/ui'
import { useClipboard } from '@vueuse/core'
const UBadge = resolveComponent('UBadge')
const UCheckbox = resolveComponent('UCheckbox')
const toast = useToast()
const { copy } = useClipboard()
type Payment = {
id: string
date: string
status: 'paid' | 'failed' | 'refunded'
email: string
amount: number
}
const data = ref<Payment[]>([{
id: '4600',
date: '2024-03-11T15:30:00',
status: 'paid',
email: 'james.anderson@example.com',
amount: 594
}, {
id: '4599',
date: '2024-03-11T10:10:00',
status: 'failed',
email: 'mia.white@example.com',
amount: 276
}, {
id: '4598',
date: '2024-03-11T08:50:00',
status: 'refunded',
email: 'william.brown@example.com',
amount: 315
}, {
id: '4597',
date: '2024-03-10T19:45:00',
status: 'paid',
email: 'emma.davis@example.com',
amount: 529
}, {
id: '4596',
date: '2024-03-10T15:55:00',
status: 'paid',
email: 'ethan.harris@example.com',
amount: 639
}])
const columns: TableColumn<Payment>[] = [{
id: 'select',
header: ({ table }) => h(UCheckbox, {
'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(),
'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value),
'aria-label': 'Select all'
}),
cell: ({ row }) => h(UCheckbox, {
'modelValue': row.getIsSelected(),
'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value),
'aria-label': 'Select row'
})
}, {
accessorKey: 'id',
header: '#',
cell: ({ row }) => `#${row.getValue('id')}`
}, {
accessorKey: 'date',
header: 'Date',
cell: ({ row }) => {
return new Date(row.getValue('date')).toLocaleString('en-US', {
day: 'numeric',
month: 'short',
hour: '2-digit',
minute: '2-digit',
hour12: false
})
}
}, {
accessorKey: 'status',
header: 'Status',
cell: ({ row }) => {
const color = ({
paid: 'success' as const,
failed: 'error' as const,
refunded: 'neutral' as const
})[row.getValue('status') as string]
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
}
}, {
accessorKey: 'email',
header: 'Email'
}, {
accessorKey: 'amount',
header: () => h('div', { class: 'text-right' }, 'Amount'),
cell: ({ row }) => {
const amount = Number.parseFloat(row.getValue('amount'))
const formatted = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'EUR'
}).format(amount)
return h('div', { class: 'text-right font-medium' }, formatted)
}
}]
const items = ref<ContextMenuItem[]>([])
function getRowItems(row: TableRow<Payment>) {
return [{
type: 'label' as const,
label: 'Actions'
}, {
label: 'Copy payment ID',
onSelect() {
copy(row.original.id)
toast.add({
title: 'Payment ID copied to clipboard!',
color: 'success',
icon: 'i-lucide-circle-check'
})
}
}, {
label: row.getIsExpanded() ? 'Collapse' : 'Expand',
onSelect() {
row.toggleExpanded()
}
}, {
type: 'separator' as const
}, {
label: 'View customer'
}, {
label: 'View payment details'
}]
}
function onContextmenu(_e: Event, row: TableRow<Payment>) {
items.value = getRowItems(row)
}
</script>
<template>
<UContextMenu :items="items">
<UTable
:data="data"
:columns="columns"
class="flex-1"
@contextmenu="onContextmenu"
>
<template #expanded="{ row }">
<pre>{{ row.original }}</pre>
</template>
</UTable>
</UContextMenu>
</template>

View File

@@ -1,157 +0,0 @@
<script setup lang="ts">
import { h, resolveComponent } from 'vue'
import type { TableColumn, TableRow } from '@nuxt/ui'
const UBadge = resolveComponent('UBadge')
const UCheckbox = resolveComponent('UCheckbox')
type Payment = {
id: string
date: string
status: 'paid' | 'failed' | 'refunded'
email: string
amount: number
}
const data = ref<Payment[]>([{
id: '4600',
date: '2024-03-11T15:30:00',
status: 'paid',
email: 'james.anderson@example.com',
amount: 594
}, {
id: '4599',
date: '2024-03-11T10:10:00',
status: 'failed',
email: 'mia.white@example.com',
amount: 276
}, {
id: '4598',
date: '2024-03-11T08:50:00',
status: 'refunded',
email: 'william.brown@example.com',
amount: 315
}, {
id: '4597',
date: '2024-03-10T19:45:00',
status: 'paid',
email: 'emma.davis@example.com',
amount: 529
}, {
id: '4596',
date: '2024-03-10T15:55:00',
status: 'paid',
email: 'ethan.harris@example.com',
amount: 639
}])
const columns: TableColumn<Payment>[] = [{
id: 'select',
header: ({ table }) => h(UCheckbox, {
'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(),
'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value),
'aria-label': 'Select all'
}),
cell: ({ row }) => h(UCheckbox, {
'modelValue': row.getIsSelected(),
'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value),
'aria-label': 'Select row'
})
}, {
accessorKey: 'id',
header: '#',
cell: ({ row }) => `#${row.getValue('id')}`
}, {
accessorKey: 'date',
header: 'Date',
cell: ({ row }) => {
return new Date(row.getValue('date')).toLocaleString('en-US', {
day: 'numeric',
month: 'short',
hour: '2-digit',
minute: '2-digit',
hour12: false
})
}
}, {
accessorKey: 'status',
header: 'Status',
cell: ({ row }) => {
const color = ({
paid: 'success' as const,
failed: 'error' as const,
refunded: 'neutral' as const
})[row.getValue('status') as string]
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
}
}, {
accessorKey: 'email',
header: 'Email'
}, {
accessorKey: 'amount',
header: () => h('div', { class: 'text-right' }, 'Amount'),
cell: ({ row }) => {
const amount = Number.parseFloat(row.getValue('amount'))
const formatted = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'EUR'
}).format(amount)
return h('div', { class: 'text-right font-medium' }, formatted)
}
}]
const anchor = ref({ x: 0, y: 0 })
const reference = computed(() => ({
getBoundingClientRect: () =>
({
width: 0,
height: 0,
left: anchor.value.x,
right: anchor.value.x,
top: anchor.value.y,
bottom: anchor.value.y,
...anchor.value
} as DOMRect)
}))
const open = ref(false)
const openDebounced = refDebounced(open, 10)
const selectedRow = ref<TableRow<Payment> | null>(null)
function onHover(_e: Event, row: TableRow<Payment> | null) {
selectedRow.value = row
open.value = !!row
}
</script>
<template>
<div class="flex w-full flex-1 gap-1">
<UTable
:data="data"
:columns="columns"
class="flex-1"
@pointermove="(ev: PointerEvent) => {
anchor.x = ev.clientX
anchor.y = ev.clientY
}"
@hover="onHover"
/>
<UPopover
:content="{ side: 'top', sideOffset: 16, updatePositionStrategy: 'always' }"
:open="openDebounced"
:reference="reference"
>
<template #content>
<div class="p-4">
{{ selectedRow?.original?.id }}
</div>
</template>
</UPopover>
</div>
</template>

View File

@@ -112,7 +112,7 @@ function onSelect(row: TableRow<Payment>, e?: Event) {
</script> </script>
<template> <template>
<div class="flex w-full flex-1 gap-1"> <div class=" flex w-full flex-1 gap-1">
<div class="flex-1"> <div class="flex-1">
<UTable <UTable
ref="table" ref="table"

View File

@@ -1,6 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import type { TableColumn, DropdownMenuItem } from '@nuxt/ui' import type { TableColumn, DropdownMenuItem } from '@nuxt/ui'
import { useClipboard } from '@vueuse/core'
interface User { interface User {
id: number id: number
@@ -11,7 +10,6 @@ interface User {
} }
const toast = useToast() const toast = useToast()
const { copy } = useClipboard()
const data = ref<User[]>([{ const data = ref<User[]>([{
id: 1, id: 1,
@@ -73,8 +71,7 @@ function getDropdownActions(user: User): DropdownMenuItem[][] {
label: 'Copy user Id', label: 'Copy user Id',
icon: 'i-lucide-copy', icon: 'i-lucide-copy',
onSelect: () => { onSelect: () => {
copy(user.id.toString()) navigator.clipboard.writeText(user.id.toString())
toast.add({ toast.add({
title: 'User ID copied to clipboard!', title: 'User ID copied to clipboard!',
color: 'success', color: 'success',

View File

@@ -26,7 +26,7 @@ const state = reactive({
</script> </script>
<template> <template>
<UTabs :items="items" variant="link" :ui="{ trigger: 'grow' }" class="gap-4 w-full"> <UTabs :items="items" variant="link" class="gap-4 w-full" :ui="{ trigger: 'grow' }">
<template #account="{ item }"> <template #account="{ item }">
<p class="text-muted mb-4"> <p class="text-muted mb-4">
{{ item.description }} {{ item.description }}

View File

@@ -27,8 +27,8 @@ const items: TimelineItem[] = [{
<template> <template>
<UTimeline <UTimeline
:items="items" :items="items"
:default-value="2"
:ui="{ item: 'even:flex-row-reverse even:-translate-x-[calc(100%-2rem)] even:text-right' }" :ui="{ item: 'even:flex-row-reverse even:-translate-x-[calc(100%-2rem)] even:text-right' }"
class="translate-x-[calc(50%-1rem)]" :default-value="2"
class="w-full translate-x-[calc(50%-2rem)]"
/> />
</template> </template>

View File

@@ -42,11 +42,11 @@ const items = [{
<UTimeline <UTimeline
:items="items" :items="items"
size="xs" size="xs"
class="w-96"
:ui="{ :ui="{
date: 'float-end ms-1', date: 'float-end ms-1',
description: 'px-3 py-2 ring ring-default mt-2 rounded-md text-default' description: 'px-3 py-2 ring ring-default mt-2 rounded-md text-default'
}" }"
class="w-96"
> >
<template #title="{ item }"> <template #title="{ item }">
<span>{{ item.username }}</span> <span>{{ item.username }}</span>

View File

@@ -1,16 +0,0 @@
<script setup lang="ts">
const toast = useToast()
function showToast() {
toast.add({
title: 'Uh oh! Something went wrong.',
description: 'There was a problem with your request.',
icon: 'i-lucide-wifi',
progress: false
})
}
</script>
<template>
<UButton label="Show toast" color="neutral" variant="outline" @click="showToast" />
</template>

View File

@@ -7,12 +7,12 @@ const appConfig = useAppConfig()
<UFormField <UFormField
label="toaster.duration" label="toaster.duration"
size="sm" size="sm"
class="inline-flex ring ring-accented rounded-sm"
:ui="{ :ui="{
wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented', wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented',
label: 'text-muted px-2 py-1.5', label: 'text-muted px-2 py-1.5',
container: 'mt-0' container: 'mt-0'
}" }"
class="inline-flex ring ring-accented rounded-sm"
> >
<UInput <UInput
v-model="appConfig.toaster.duration" v-model="appConfig.toaster.duration"

View File

@@ -7,12 +7,12 @@ const appConfig = useAppConfig()
<UFormField <UFormField
label="toaster.expand" label="toaster.expand"
size="sm" size="sm"
class="inline-flex ring ring-accented rounded-sm"
:ui="{ :ui="{
wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented', wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented',
label: 'text-muted px-2 py-1.5', label: 'text-muted px-2 py-1.5',
container: 'mt-0' container: 'mt-0'
}" }"
class="inline-flex ring ring-accented rounded-sm"
> >
<USelectMenu <USelectMenu
v-model="appConfig.toaster.expand" v-model="appConfig.toaster.expand"

View File

@@ -10,12 +10,12 @@ const appConfig = useAppConfig()
<UFormField <UFormField
label="toaster.position" label="toaster.position"
size="sm" size="sm"
class="inline-flex ring ring-accented rounded-sm"
:ui="{ :ui="{
wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented', wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented',
label: 'text-muted px-2 py-1.5', label: 'text-muted px-2 py-1.5',
container: 'mt-0' container: 'mt-0'
}" }"
class="inline-flex ring ring-accented rounded-sm"
> >
<USelectMenu <USelectMenu
v-model="appConfig.toaster.position" v-model="appConfig.toaster.position"

View File

@@ -1,41 +0,0 @@
<script setup lang="ts">
const open = ref(false)
const anchor = ref({ x: 0, y: 0 })
const reference = computed(() => ({
getBoundingClientRect: () =>
({
width: 0,
height: 0,
left: anchor.value.x,
right: anchor.value.x,
top: anchor.value.y,
bottom: anchor.value.y,
...anchor.value
} as DOMRect)
}))
</script>
<template>
<UTooltip
:open="open"
:reference="reference"
:content="{ side: 'top', sideOffset: 16, updatePositionStrategy: 'always' }"
>
<div
class="flex items-center justify-center rounded-md border border-dashed border-accented text-sm aspect-video w-72"
@pointerenter="open = true"
@pointerleave="open = false"
@pointermove="(ev) => {
anchor.x = ev.clientX
anchor.y = ev.clientY
}"
>
Hover me
</div>
<template #content>
{{ anchor.x.toFixed(0) }} - {{ anchor.y.toFixed(0) }}
</template>
</UTooltip>
</template>

View File

@@ -1,5 +1,5 @@
import { onMounted, watch } from 'vue' import { onMounted, watch } from 'vue'
import FaviconSvg from '../../public/icon.svg?raw' import FaviconSvg from 'public/icon.svg?raw'
export function useFaviconFromTheme() { export function useFaviconFromTheme() {
const colorMode = useColorMode() const colorMode = useColorMode()

View File

@@ -59,7 +59,7 @@ provide('navigation', mappedNavigation)
<UApp> <UApp>
<NuxtLoadingIndicator color="#FFF" /> <NuxtLoadingIndicator color="#FFF" />
<Banner /> <!-- <Banner /> -->
<Header :links="links" /> <Header :links="links" />

View File

@@ -2,8 +2,7 @@
import { kebabCase } from 'scule' import { kebabCase } from 'scule'
import type { ContentNavigationItem } from '@nuxt/content' import type { ContentNavigationItem } from '@nuxt/content'
import type { PageLink } from '@nuxt/ui-pro' import type { PageLink } from '@nuxt/ui-pro'
import { mapContentNavigation } from '@nuxt/ui-pro/utils/content' import { findPageBreadcrumb, mapContentNavigation } from '@nuxt/ui-pro/utils/content'
import { findPageBreadcrumb } from '@nuxt/content/utils'
const route = useRoute() const route = useRoute()
const { framework, module } = useSharedData() const { framework, module } = useSharedData()
@@ -38,7 +37,7 @@ const { data: surround } = await useAsyncData(`${kebabCase(route.path)}-surround
const navigation = inject<Ref<ContentNavigationItem[]>>('navigation') const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
const breadcrumb = computed(() => mapContentNavigation(findPageBreadcrumb(navigation?.value, page.value?.path, { indexAsChild: true })).map(({ icon, ...link }) => link)) const breadcrumb = computed(() => mapContentNavigation(findPageBreadcrumb(navigation?.value, page.value)).map(({ icon, ...link }) => link))
if (!import.meta.prerender) { if (!import.meta.prerender) {
// Redirect to the correct framework version if the page is not the current framework // Redirect to the correct framework version if the page is not the current framework
@@ -142,7 +141,7 @@ const communityLinks = computed(() => [{
<MDC v-if="page.description" :value="page.description" unwrap="p" :cache-key="`${kebabCase(route.path)}-description`" /> <MDC v-if="page.description" :value="page.description" unwrap="p" :cache-key="`${kebabCase(route.path)}-description`" />
</template> </template>
<template #links> <template v-if="page.links?.length" #links>
<UButton <UButton
v-for="link in page.links" v-for="link in page.links"
:key="link.label" :key="link.label"
@@ -155,7 +154,6 @@ const communityLinks = computed(() => [{
<UAvatar v-bind="link.avatar" size="2xs" :alt="`${link.label} avatar`" /> <UAvatar v-bind="link.avatar" size="2xs" :alt="`${link.label} avatar`" />
</template> </template>
</UButton> </UButton>
<PageHeaderLinks />
</template> </template>
</UPageHeader> </UPageHeader>

View File

@@ -5,17 +5,6 @@ pricing:
title: Upgrade to Nuxt UI [Pro]{class="text-primary"}. title: Upgrade to Nuxt UI [Pro]{class="text-primary"}.
description: On top of 40+ open source components from Nuxt UI, Pro gives you access to 50+ premium Vue components to create beautiful & responsive Nuxt applications in minutes. It includes all primitives to build landing pages, documentations, blogs, dashboards or entire SaaS products. description: On top of 40+ open source components from Nuxt UI, Pro gives you access to 50+ premium Vue components to create beautiful & responsive Nuxt applications in minutes. It includes all primitives to build landing pages, documentations, blogs, dashboards or entire SaaS products.
freePlan: freePlan:
description: "**NuxtLabs is joining Vercel** :tada: As part of this transition, Nuxt UI is becoming even more accessible.<br><br> **In September, we're launching Nuxt UI v4**: a free, open-source library that unifies Nuxt UI and Nuxt UI Pro, offering 100+ components and a complete free Figma Kit for everyone."
orientation: horizontal
button:
label: Read the announcement
to: 'https://nuxtlabs.com/?utm_source=nuxt-ui&utm_medium=banner&utm_campaign=nuxtlabs-vercel'
target: _blank
color: 'neutral'
trailingIcon: 'i-lucide-arrow-right'
ui:
trailingIcon: 'ms-0'
devPlan:
title: Free in development title: Free in development
description: Try Nuxt UI Pro for free in development, no credit card required. Upgrade when ready to deploy. description: Try Nuxt UI Pro for free in development, no credit card required. Upgrade when ready to deploy.
orientation: horizontal orientation: horizontal
@@ -24,9 +13,6 @@ pricing:
to: '/getting-started/installation/pro/nuxt' to: '/getting-started/installation/pro/nuxt'
color: 'neutral' color: 'neutral'
variant: 'subtle' variant: 'subtle'
trailingIcon: 'i-lucide-arrow-right'
ui:
trailingIcon: 'ms-0'
figma: figma:
title: Figma Kit Pro title: Figma Kit Pro
description: Get all Nuxt UI Pro components in a Figma kit to design your next application before coding. Everything you need, from wire-framing to high-fidelity web integration. description: Get all Nuxt UI Pro components in a Figma kit to design your next application before coding. Everything you need, from wire-framing to high-fidelity web integration.

View File

@@ -34,19 +34,10 @@ useSeoMeta({
<div class="flex flex-col bg-default gap-8 lg:gap-0"> <div class="flex flex-col bg-default gap-8 lg:gap-0">
<UPricingPlan <UPricingPlan
v-bind="page.pricing.freePlan" v-bind="page.pricing.freePlan"
class="lg:rounded-none ring-primary/15 ring-inset -mb-px bg-primary/5 z-[1]" variant="naked"
:ui="{ description: 'mt-0 text-primary' }" class="lg:rounded-none border-x border-default border-t border-b lg:border-b-0"
>
<template #description>
<MDC :value="page.pricing.freePlan.description" unwrap="p" />
</template>
</UPricingPlan>
<UPricingPlan
v-bind="page.pricing.devPlan"
class="lg:rounded-none ring-inset -mb-px"
/> />
<UPricingPlans compact class="-space-x-px"> <UPricingPlans compact>
<UPricingPlan <UPricingPlan
v-for="(plan, index) in page.pricing.plans" v-for="(plan, index) in page.pricing.plans"
:key="index" :key="index"
@@ -56,17 +47,18 @@ useSeoMeta({
:discount="plan.discount" :discount="plan.discount"
:billing-period="plan.billing_period" :billing-period="plan.billing_period"
:billing-cycle="plan.billing_cycle" :billing-cycle="plan.billing_cycle"
:variant="plan.highlight ? 'subtle' : 'outline'" :variant="plan.highlight ? 'soft' : 'outline'"
class="lg:rounded-none ring-inset -mb-px" :class="['lg:rounded-none', { 'border-2 lg:border lg:border-x-0 border-primary lg:border-default': plan.highlight }]"
:features="plan.features" :features="plan.features"
:button="plan.button" :button="plan.button"
/> />
</UPricingPlans> </UPricingPlans>
<UPricingPlan <UPricingPlan
v-bind="page.pricing.figma" v-bind="page.pricing.figma"
variant="naked"
:billing-period="page.pricing.figma.billing_period" :billing-period="page.pricing.figma.billing_period"
:billing-cycle="page.pricing.figma.billing_cycle" :billing-cycle="page.pricing.figma.billing_cycle"
class="lg:rounded-none ring-inset -mb-px" class="lg:rounded-none border lg:border-y-0 border-default"
> >
<template #features> <template #features>
<li v-for="(feature, index) in page.pricing.figma.features" :key="index" class="flex items-center gap-2 min-w-0"> <li v-for="(feature, index) in page.pricing.figma.features" :key="index" class="flex items-center gap-2 min-w-0">

View File

@@ -16,12 +16,12 @@ function handleMessage(message) {
async function handleFormatMessage(message) { async function handleFormatMessage(message) {
if (!globalThis.prettier) { if (!globalThis.prettier) {
await Promise.all([ await Promise.all([
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/standalone.js'), import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/standalone.js'),
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/plugins/babel.js'), import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/babel.js'),
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/plugins/estree.js'), import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/estree.js'),
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/plugins/html.js'), import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/html.js'),
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/plugins/markdown.js'), import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/markdown.js'),
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/plugins/typescript.js') import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/typescript.js')
]) ])
} }

View File

@@ -84,7 +84,7 @@ You can play with Nuxt UI components as well as your app components directly fro
Install the module to your Nuxt application with one command: Install the module to your Nuxt application with one command:
```bash [Terminal] ```bash [Terminal]
npx nuxt module add compodium npx nuxi module add compodium
``` ```
:: ::

View File

@@ -115,7 +115,7 @@ Start your project using the [nuxt/starter#ui](https://github.com/nuxt/starter/t
Create a new project locally by running the following command: Create a new project locally by running the following command:
```bash [Terminal] ```bash [Terminal]
npm create nuxt@latest -- -t ui npx nuxi init -t ui <my-app>
``` ```
::note ::note
@@ -225,27 +225,6 @@ export default defineNuxtConfig({
This option adds the `transition-colors` class on components with hover or active states. This option adds the `transition-colors` class on components with hover or active states.
:: ::
### `theme.defaultVariants` :badge{label="Soon" class="align-text-top"}
Use the `theme.defaultVariants` option to override the default `color` and `size` variants for components.
- Default: `{ color: 'primary', size: 'md' }`{lang="ts-type"}
```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: ['@nuxt/ui'],
css: ['~/assets/css/main.css'],
ui: {
theme: {
defaultVariants: {
color: 'neutral',
size: 'sm'
}
}
}
})
```
## Continuous Releases ## Continuous Releases
Nuxt UI uses [pkg.pr.new](https://github.com/stackblitz-labs/pkg.pr.new) for continuous preview releases, providing developers with instant access to the latest features and bug fixes without waiting for official releases. Nuxt UI uses [pkg.pr.new](https://github.com/stackblitz-labs/pkg.pr.new) for continuous preview releases, providing developers with instant access to the latest features and bug fixes without waiting for official releases.

View File

@@ -78,22 +78,6 @@ components.d.ts
:: ::
::tip
Internally, Nuxt UI relies on custom alias to resolve the theme types. If you're using TypeScript, you should add an alias to your `tsconfig` to enable auto-completion in your `vite.config.ts`.
```json [tsconfig.node.json]
{
"compilerOptions": {
"paths": {
"#build/ui": [
"./node_modules/@nuxt/ui/.nuxt/ui"
]
}
}
}
```
::
#### Use the Nuxt UI Vue plugin in your `main.ts` #### Use the Nuxt UI Vue plugin in your `main.ts`
```ts [main.ts]{3,14} ```ts [main.ts]{3,14}
@@ -183,28 +167,7 @@ It's recommended to install the [Tailwind CSS IntelliSense](https://marketplace.
``` ```
::note{to="/components/app"} ::note{to="/components/app"}
The `App` component sets up global config and is required for **Toast**, **Tooltip** and **programmatic overlays**. The `App` component provides global configurations and is required for **Toast**, **Tooltip** components to work as well as **Programmatic Overlays**.
::
#### Add the `isolate` class to your root container
```html [index.html]{9}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Nuxt UI</title>
</head>
<body>
<div id="app" class="isolate"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
```
::note
This ensures styles are scoped to your app and prevents issues with overlays and stacking contexts.
:: ::
:: ::
@@ -216,7 +179,7 @@ Start your project using the [nuxtlabs/nuxt-ui-vue-starter](https://github.com/n
Create a new project locally by running the following command: Create a new project locally by running the following command:
```bash [Terminal] ```bash [Terminal]
npm create nuxt@latest -- -t github:nuxtlabs/nuxt-ui-vue-starter npx nuxi init -t github:nuxtlabs/nuxt-ui-vue-starter <my-app>
``` ```
::note ::note
@@ -354,32 +317,6 @@ export default defineConfig({
This option adds the `transition-colors` class on components with hover or active states. This option adds the `transition-colors` class on components with hover or active states.
:: ::
### `theme.defaultVariants` :badge{label="Soon" class="align-text-top"}
Use the `theme.defaultVariants` option to override the default `color` and `size` variants for components.
- Default: `{ color: 'primary', size: 'md' }`{lang="ts-type"}
```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({
theme: {
defaultVariants: {
color: 'neutral',
size: 'sm'
}
}
})
]
})
```
### `inertia` ### `inertia`
Use the `inertia` option to enable compatibility with [Inertia.js](https://inertiajs.com/). Use the `inertia` option to enable compatibility with [Inertia.js](https://inertiajs.com/).

View File

@@ -536,33 +536,6 @@ import { ModalExampleComponent } from '#components'
</script> </script>
``` ```
### Changed form validation
- The error object property for targeting form fields has been renamed from `path` to `name`:
```diff
<script setup lang="ts">
const validate = (state: any): FormError[] => {
const errors = []
if (!state.email) {
errors.push({
- path: 'email',
+ name: 'email',
message: 'Required'
})
}
if (!state.password) {
errors.push({
- path: 'password',
+ name: 'password',
message: 'Required'
})
}
return errors
}
</script>
```
--- ---
::warning ::warning

View File

@@ -87,7 +87,7 @@ Read more about this in the `@nuxt/icon` documentation.
You can use local SVG files to create a custom Iconify collection. You can use local SVG files to create a custom Iconify collection.
For example, place your icons' SVG files under a folder of your choice, for example, `./app/assets/icons`: For example, place your icons' SVG files under a folder of your choice, for example, `./assets/icons`:
```bash ```bash
assets/icons assets/icons
@@ -104,7 +104,7 @@ export default defineNuxtConfig({
icon: { icon: {
customCollections: [{ customCollections: [{
prefix: 'custom', prefix: 'custom',
dir: './app/assets/icons' dir: './assets/icons'
}] }]
} }
}) })

View File

@@ -32,12 +32,6 @@ props:
You can use any name from the <https://icones.js.org> collection. You can use any name from the <https://icones.js.org> collection.
:: ::
::warning
When using collections with a dash (`-`), you need to separate the icon name from the collection name with a colon (`:`) as `@iconify/vue` does not handle this case like `@nuxt/icon`. For example, instead of `i-simple-icons-github` you need to write `i-simple-icons:github` or `simple-icons:github`.
Learn more about the [Iconify naming convention](https://iconify.design/docs/icon-components/vue/#icon).
::
### Component Props ### Component Props
Some components also have an `icon` prop to display an icon, like the [Button](/components/button) for example: Some components also have an `icon` prop to display an icon, like the [Button](/components/button) for example:

View File

@@ -60,7 +60,7 @@ import { fr } from '@nuxt/ui-pro/locale'
### Custom locale ### Custom locale
You can create your own locale using the `defineLocale` composable: You also have the option to add your own locale using `defineLocale`:
::module-only ::module-only
@@ -125,65 +125,6 @@ Look at the `code` parameter, there you need to pass the iso code of the languag
:: ::
### Extend locale :badge{label="New" class="align-text-top"}
You can customize an existing locale by overriding its `messages` or `code` using the `extendLocale` composable:
::module-only
#ui
:::div
```vue [app.vue]
<script setup lang="ts">
import { en } from '@nuxt/ui/locale'
const locale = extendLocale(en, {
code: 'en-GB',
messages: {
commandPalette: {
placeholder: 'Search a component...'
}
}
})
</script>
<template>
<UApp :locale="locale">
<NuxtPage />
</UApp>
</template>
```
:::
#ui-pro
:::div
```vue [app.vue]
<script setup lang="ts">
import { en } from '@nuxt/ui-pro/locale'
const locale = extendLocale(en, {
code: 'en-GB',
messages: {
commandPalette: {
placeholder: 'Search a component...'
}
}
})
</script>
<template>
<UApp :locale="locale">
<NuxtPage />
</UApp>
</template>
```
:::
::
### Dynamic locale ### Dynamic locale
To dynamically switch between languages, you can use the [Nuxt I18n](https://i18n.nuxtjs.org/) module. To dynamically switch between languages, you can use the [Nuxt I18n](https://i18n.nuxtjs.org/) module.

View File

@@ -60,7 +60,7 @@ import { fr } from '@nuxt/ui-pro/locale'
### Custom locale ### Custom locale
You can create your own locale using the `defineLocale` composable: You also have the option to add your locale using `defineLocale`:
::module-only ::module-only
@@ -127,67 +127,6 @@ Look at the `code` parameter, there you need to pass the iso code of the languag
:: ::
### Extend locale :badge{label="New" class="align-text-top"}
You can customize an existing locale by overriding its `messages` or `code` using the `extendLocale` composable:
::module-only
#ui
:::div
```vue [App.vue]
<script setup lang="ts">
import { en } from '@nuxt/ui/locale'
import { extendLocale } from '@nuxt/ui/composables/defineLocale.js'
const locale = extendLocale(en, {
code: 'en-GB',
messages: {
commandPalette: {
placeholder: 'Search a component...'
}
}
})
</script>
<template>
<UApp :locale="locale">
<RouterView />
</UApp>
</template>
```
:::
#ui-pro
:::div
```vue [App.vue]
<script setup lang="ts">
import { en } from '@nuxt/ui-pro/locale'
import { extendLocale } from '@nuxt/ui/composables/defineLocale.js'
const locale = extendLocale(en, {
code: 'en-GB',
messages: {
commandPalette: {
placeholder: 'Search a component...'
}
}
})
</script>
<template>
<UApp :locale="locale">
<RouterView />
</UApp>
</template>
```
:::
::
### Dynamic locale ### Dynamic locale
To dynamically switch between languages, you can use the [Vue I18n](https://vue-i18n.intlify.dev/) plugin. To dynamically switch between languages, you can use the [Vue I18n](https://vue-i18n.intlify.dev/) plugin.

View File

@@ -1,6 +1,6 @@
--- ---
title: useOverlay title: useOverlay
description: 'A composable to programmatically control overlays.' description: "A composable to programmatically control overlays."
--- ---
## Usage ## Usage
@@ -9,11 +9,9 @@ Use the auto-imported `useOverlay` composable to programmatically control [Modal
```vue ```vue
<script setup lang="ts"> <script setup lang="ts">
import { LazyModalExample } from '#components'
const overlay = useOverlay() const overlay = useOverlay()
const modal = overlay.create(LazyModalExample) const modal = overlay.create(MyModal)
async function openModal() { async function openModal() {
modal.open() modal.open()
@@ -31,73 +29,71 @@ In order to return a value from the overlay, the `overlay.open().instance.result
### `create(component: T, options: OverlayOptions): OverlayInstance` ### `create(component: T, options: OverlayOptions): OverlayInstance`
Create an overlay, and return a factory instance. Creates an overlay, and returns a factory instance
- Parameters: - Parameters:
- `component`: The overlay component. - `component`: The overlay component
- `options`: - `options` The overlay options
- `defaultOpen?: boolean` Open the overlay immediately after being created. Defaults to `false`. - `defaultOpen?: boolean` Opens the overlay immediately after being created `default: false`
- `props?: ComponentProps`: An optional object of props to pass to the rendered component. - `props?: ComponentProps`: An optional object of props to pass to the rendered component.
- `destroyOnClose?: boolean` Removes the overlay from memory when closed. Defaults to `false`. - `destroyOnClose?: boolean` Removes the overlay from memory when closed `default: false`
### `open(id: symbol, props?: ComponentProps<T>): OpenedOverlay<T>` ### `open(id: symbol, props?: ComponentProps<T>): OpenedOverlay<T>`
Open an overlay by its `id`. Opens the overlay using its `id`
- Parameters: - Parameters:
- `id`: The identifier of the overlay. - `id`: The identifier of the overlay
- `props`: An optional object of props to pass to the rendered component. - `props`: An optional object of props to pass to the rendered component.
### `close(id: symbol, value?: any): void` ### `close(id: symbol, value?: any): void`
Close an overlay by its `id`. Close an overlay using its `id`
- Parameters: - Parameters:
- `id`: The identifier of the overlay. - `id`: The identifier of the overlay
- `value`: A value to resolve the overlay promise with. - `value`: A value to resolve the overlay promise with
### `patch(id: symbol, props: ComponentProps<T>): void` ### `patch(id: symbol, props: ComponentProps<T>): void`
Update an overlay by its `id`. Update an overlay using its `id`
- Parameters: - Parameters:
- `id`: The identifier of the overlay. - `id`: The identifier of the overlay
- `props`: An object of props to update on the rendered component. - `props`: An object of props to update on the rendered component.
### `unmount(id: symbol): void` ### `unmount(id: symbol): void`
Remove an overlay from the DOM by its `id`. Removes the overlay from the DOM using its `id`
- Parameters: - Parameters:
- `id`: The identifier of the overlay. - `id`: The identifier of the overlay
### `isOpen(id: symbol): boolean` ### `isOpen(id: symbol): boolean`
Check if an overlay is open using its `id`. Checks if an overlay its open using its `id`
- Parameters: - Parameters:
- `id`: The identifier of the overlay. - `id`: The identifier of the overlay
### `overlays: Overlay[]` ### `overlays: Overlay[]`
In-memory list of all overlays that were created. In-memory list of overlays that were created
## Instance API ## Overlay Instance API
### `open(props?: ComponentProps<T>): Promise<OpenedOverlay<T>>` ### `open(props?: ComponentProps<T>): Promise<OpenedOverlay<T>>`
Open the overlay. Opens the overlay
- Parameters: - Parameters:
- `props`: An optional object of props to pass to the rendered component. - `props`: An optional object of props to pass to the rendered component.
```vue ```vue
<script setup lang="ts"> <script setup lang="ts">
import { LazyModalExample } from '#components'
const overlay = useOverlay() const overlay = useOverlay()
const modal = overlay.create(LazyModalExample) const modal = overlay.create(MyModalContent)
function openModal() { function openModal() {
modal.open({ modal.open({
@@ -109,25 +105,23 @@ function openModal() {
### `close(value?: any): void` ### `close(value?: any): void`
Close the overlay. Close the overlay
- Parameters: - Parameters:
- `value`: A value to resolve the overlay promise with. - `value`: A value to resolve the overlay promise with
### `patch(props: ComponentProps<T>)` ### `patch(props: ComponentProps<T>)`
Update the props of the overlay. Updates the props of the overlay.
- Parameters: - Parameters:
- `props`: An object of props to update on the rendered component. - `props`: An object of props to update on the rendered component.
```vue ```vue
<script setup lang="ts"> <script setup lang="ts">
import { LazyModalExample } from '#components'
const overlay = useOverlay() const overlay = useOverlay()
const modal = overlay.create(LazyModalExample, { const modal = overlay.create(MyModal, {
title: 'Welcome' title: 'Welcome'
}) })
@@ -147,8 +141,6 @@ Here's a complete example of how to use the `useOverlay` composable:
```vue ```vue
<script setup lang="ts"> <script setup lang="ts">
import { ModalA, ModalB, SlideoverA } from '#components'
const overlay = useOverlay() const overlay = useOverlay()
// Create with default props // Create with default props
@@ -158,7 +150,7 @@ const modalB = overlay.create(ModalB)
const slideoverA = overlay.create(SlideoverA) const slideoverA = overlay.create(SlideoverA)
const openModalA = () => { const openModalA = () => {
// Open modalA, but override the title prop // Open Modal A, but override the title prop
modalA.open({ title: 'Hello' }) modalA.open({ title: 'Hello' })
} }
@@ -168,37 +160,16 @@ const openModalB = async () => {
const input = await modalBInstance.result const input = await modalBInstance.result
// Pass the result from modalB to the slideover, and open it // Pass the result from modalB to the slideover, and open it.
slideoverA.open({ input }) slideoverA.open({ input })
} }
</script> </script>
<template> <template>
<button @click="openModalA">Open Modal</button> <div>
<button @click="openModal">Open Modal</button>
</div>
</template> </template>
``` ```
In this example, we're using the `useOverlay` composable to control multiple modals and slideovers. In this example, we're using the `useOverlay` composable to control multiple modals and slideovers.
## Caveats
### Provide / Inject
When opening overlays programmatically (e.g. modals, slideovers, etc), the overlay component can only access injected values from the component containing `UApp` (typically `app.vue` or layout components). This is because overlays are mounted outside of the page context by the `UApp` component.
As such, using `provide()` in pages or parent components isn't supported directly. To pass provided values to overlays, the recommended approach is to use props instead:
```vue
<script setup lang="ts">
import { LazyModalExample } from '#components'
const providedValue = inject('valueProvidedInPage')
const modal = overlay.create(LazyModalExample, {
props: {
providedValue,
otherData: someValue
}
})
</script>
```

View File

@@ -9,6 +9,7 @@ links:
- label: GitHub - label: GitHub
icon: i-simple-icons-github icon: i-simple-icons-github
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/CheckboxGroup.vue to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/CheckboxGroup.vue
navigation.badge: New
--- ---

View File

@@ -156,7 +156,7 @@ props:
--- ---
:: ::
### Variant ### Variant :badge{label="New" class="align-text-top"}
Use the `variant` prop to change the variant of the Checkbox. Use the `variant` prop to change the variant of the Checkbox.
@@ -190,7 +190,7 @@ props:
--- ---
:: ::
### Indicator ### Indicator :badge{label="New" class="align-text-top"}
Use the `indicator` prop to change the position or hide the indicator. Defaults to `start`. Use the `indicator` prop to change the position or hide the indicator. Defaults to `start`.

View File

@@ -52,11 +52,9 @@ Each group contains an `items` array of objects that define the commands. Each i
- `loading?: boolean`{lang="ts-type"} - `loading?: boolean`{lang="ts-type"}
- `disabled?: boolean`{lang="ts-type"} - `disabled?: boolean`{lang="ts-type"}
- [`slot?: string`{lang="ts-type"}](#with-custom-slot) - [`slot?: string`{lang="ts-type"}](#with-custom-slot)
- `placeholder?: string`{lang="ts-type"}
- `children?: CommandPaletteItem[]`{lang="ts-type"}
- `onSelect?(e?: Event): void`{lang="ts-type"} - `onSelect?(e?: Event): void`{lang="ts-type"}
- `class?: any`{lang="ts-type"} - `class?: any`{lang="ts-type"}
- `ui?: { item?: ClassNameValue, itemLeadingIcon?: ClassNameValue, itemLeadingAvatarSize?: ClassNameValue, itemLeadingAvatar?: ClassNameValue, itemLeadingChipSize?: ClassNameValue, itemLeadingChip?: ClassNameValue, itemLabel?: ClassNameValue, itemLabelPrefix?: ClassNameValue, itemLabelBase?: ClassNameValue, itemLabelSuffix?: ClassNameValue, itemTrailing?: ClassNameValue, itemTrailingKbds?: ClassNameValue, itemTrailingKbdsSize?: ClassNameValue, itemTrailingHighlightedIcon?: ClassNameValue, itemTrailingIcon?: ClassNameValue }`{lang="ts-type"} - `ui?: { item?: ClassNameValue, itemLeadingIcon?: ClassNameValue, itemLeadingAvatarSize?: ClassNameValue, itemLeadingAvatar?: ClassNameValue, itemLeadingChipSize?: ClassNameValue, itemLeadingChip?: ClassNameValue, itemLabel?: ClassNameValue, itemLabelPrefix?: ClassNameValue, itemLabelBase?: ClassNameValue, itemLabelSuffix?: ClassNameValue, itemTrailing?: ClassNameValue, itemTrailingKbds?: ClassNameValue, itemTrailingKbdsSize?: ClassNameValue, itemTrailingHighlightedIcon?: ClassNameValue, itemTrailingIcon?: ClassNameValue,}`{lang="ts-type"}
You can pass any property from the [Link](/components/link#props) component such as `to`, `target`, etc. You can pass any property from the [Link](/components/link#props) component such as `to`, `target`, etc.
@@ -112,10 +110,6 @@ props:
--- ---
:: ::
::tip{to="#with-children-in-items"}
Each item can take a `children` array of objects with the following properties to create submenus:
::
### Multiple ### Multiple
Use the `multiple` prop to allow multiple selections. Use the `multiple` prop to allow multiple selections.
@@ -252,128 +246,6 @@ You can customize this icon globally in your `vite.config.ts` under `ui.icons.se
::: :::
:: ::
### Selected Icon
Use the `selected-icon` prop to customize the selected item [Icon](/components/icon). Defaults to `i-lucide-check`.
::component-code
---
collapse: true
hide:
- autofocus
ignore:
- groups
- modelValue
- multiple
- class
external:
- groups
- modelValue
class: '!p-0'
props:
multiple: true
autofocus: false
modelValue:
- label: 'Benjamin Canac'
suffix: 'benjamincanac'
avatar:
src: 'https://github.com/benjamincanac.png'
selectedIcon: 'i-lucide-circle-check'
groups:
- id: 'users'
label: 'Users'
items:
- label: 'Benjamin Canac'
suffix: 'benjamincanac'
avatar:
src: 'https://github.com/benjamincanac.png'
- label: 'Sylvain Marroufin'
suffix: 'smarroufin'
avatar:
src: 'https://github.com/smarroufin.png'
- label: 'Sébastien Chopin'
suffix: 'atinux'
avatar:
src: 'https://github.com/atinux.png'
- label: 'Romain Hamel'
suffix: 'romhml'
avatar:
src: 'https://github.com/romhml.png'
- label: 'Haytham A. Salama'
suffix: 'Haythamasalama'
avatar:
src: 'https://github.com/Haythamasalama.png'
- label: 'Daniel Roe'
suffix: 'danielroe'
avatar:
src: 'https://github.com/danielroe.png'
- label: 'Neil Richter'
suffix: 'noook'
avatar:
src: 'https://github.com/noook.png'
class: 'flex-1'
---
::
::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.
:::
::
### Trailing Icon :badge{label="New" class="align-text-top"}
Use the `trailing-icon` prop to customize the trailing [Icon](/components/icon) when an item has children. Defaults to `i-lucide-chevron-right`.
::component-code
---
collapse: true
prettier: true
hide:
- autofocus
ignore:
- groups
- class
external:
- groups
class: '!p-0'
props:
autofocus: false
trailingIcon: 'i-lucide-arrow-right'
groups:
- id: 'actions'
items:
- label: 'Share'
icon: 'i-lucide-share'
children:
- label: 'Email'
icon: 'i-lucide-mail'
- label: 'Copy'
icon: 'i-lucide-copy'
- label: 'Link'
icon: 'i-lucide-link'
class: 'flex-1'
---
::
::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.
:::
::
### Loading ### Loading
Use the `loading` prop to show a loading icon on the CommandPalette. Use the `loading` prop to show a loading icon on the CommandPalette.
@@ -449,6 +321,37 @@ You can customize this icon globally in your `vite.config.ts` under `ui.icons.lo
::: :::
:: ::
### Disabled
Use the `disabled` prop to disable the CommandPalette.
::component-code
---
collapse: true
hide:
- autofocus
ignore:
- groups
- class
external:
- groups
class: '!p-0'
props:
autofocus: false
disabled: true
groups:
- id: 'apps'
items:
- label: 'Calendar'
icon: 'i-lucide-calendar'
- label: 'Music'
icon: 'i-lucide-music'
- label: 'Maps'
icon: 'i-lucide-map'
class: 'flex-1'
---
::
### Close ### Close
Use the `close` prop to display a [Button](/components/button) to dismiss the CommandPalette. Use the `close` prop to display a [Button](/components/button) to dismiss the CommandPalette.
@@ -565,124 +468,6 @@ You can customize this icon globally in your `vite.config.ts` under `ui.icons.cl
::: :::
:: ::
### Back :badge{label="New" class="align-text-top"}
Use the `back` prop to customize or hide the back button (with `false` value) displayed when navigating into a submenu.
You can pass any property from the [Button](/components/button) component to customize it.
::component-code
---
collapse: true
prettier: true
hide:
- autofocus
ignore:
- back.color
- groups
- class
external:
- groups
class: '!p-0'
props:
autofocus: false
back:
color: primary
groups:
- id: 'actions'
items:
- label: 'Share'
icon: 'i-lucide-share'
children:
- label: 'Email'
icon: 'i-lucide-mail'
- label: 'Copy'
icon: 'i-lucide-copy'
- label: 'Link'
icon: 'i-lucide-link'
class: 'flex-1'
---
::
### Back Icon :badge{label="New" class="align-text-top"}
Use the `back-icon` prop to customize the back button [Icon](/components/icon). Defaults to `i-lucide-arrow-left`.
::component-code
---
collapse: true
hide:
- autofocus
ignore:
- class
- groups
- back
external:
- groups
class: '!p-0'
props:
autofocus: false
back: true
backIcon: 'i-lucide-house'
groups:
- id: 'actions'
items:
- label: 'Share'
icon: 'i-lucide-share'
children:
- label: 'Email'
icon: 'i-lucide-mail'
- label: 'Copy'
icon: 'i-lucide-copy'
- label: 'Link'
icon: 'i-lucide-link'
class: 'flex-1'
---
::
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.arrowLeft` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.arrowLeft` key.
:::
::
### Disabled
Use the `disabled` prop to disable the CommandPalette.
::component-code
---
collapse: true
hide:
- autofocus
ignore:
- groups
- class
external:
- groups
class: '!p-0'
props:
autofocus: false
disabled: true
groups:
- id: 'apps'
items:
- label: 'Calendar'
icon: 'i-lucide-calendar'
- label: 'Music'
icon: 'i-lucide-music'
- label: 'Maps'
icon: 'i-lucide-map'
class: 'flex-1'
---
::
## Examples ## Examples
### Control selected item(s) ### Control selected item(s)
@@ -717,28 +502,6 @@ props:
This example uses the `@update:model-value` event to reset the search term when an item is selected. This example uses the `@update:model-value` event to reset the search term when an item is selected.
:: ::
### With children in items :badge{label="New" class="align-text-top"}
You can create hierarchical menus by using the `children` property in items. When an item has children, it will automatically display a chevron icon and enable navigation into a submenu.
::component-example
---
collapse: true
prettier: true
name: 'command-palette-items-children-example'
class: '!p-0'
props:
autofocus: false
---
::
::note
When navigating into a submenu:
- The search term is reset
- A back button appears in the input
- You can go back to the previous group by pressing the :kbd{value="backspace"} key
::
### With fetched items ### With fetched items
You can fetch items from an API and use them in the CommandPalette. You can fetch items from an API and use them in the CommandPalette.
@@ -877,20 +640,6 @@ props:
This can be useful when using the CommandPalette inside a [`Modal`](/components/modal) for example. This can be useful when using the CommandPalette inside a [`Modal`](/components/modal) for example.
:: ::
### With footer slot :badge{label="Soon" class="align-text-top"}
Use the `#footer` slot to add custom content at the bottom of the CommandPalette, such as keyboard shortcuts help or additional actions.
::component-example
---
collapse: true
name: 'command-palette-footer-slot-example'
class: '!p-0'
props:
autofocus: false
---
::
### With custom slot ### With custom slot
Use the `slot` property to customize a specific item or group. Use the `slot` property to customize a specific item or group.
@@ -909,7 +658,6 @@ You will have access to the following slots:
::component-example ::component-example
--- ---
collapse: true
name: 'command-palette-custom-slot-example' name: 'command-palette-custom-slot-example'
class: '!p-0' class: '!p-0'
props: props:

View File

@@ -328,17 +328,6 @@ name: 'drawer-responsive-example'
--- ---
:: ::
### Nested drawers :badge{label="Soon" class="align-text-top"}
You can nest drawers within each other by using the `nested` prop.
::component-example
---
prettier: true
name: 'drawer-nested-example'
---
::
### With footer slot ### With footer slot
Use the `#footer` slot to add content after the Drawer's body. Use the `#footer` slot to add content after the Drawer's body.

View File

@@ -135,7 +135,7 @@ props:
### Multiple ### Multiple
Use the `multiple` prop to allow multiple selections, the selected items will be displayed as tags. Use the `multiple` prop to allow multiple selections, the selected items will be displayed as badges.
::component-code ::component-code
--- ---
@@ -166,7 +166,7 @@ Ensure to pass an array to the `default-value` prop or the `v-model` directive.
### Delete Icon ### Delete Icon
With `multiple`, use the `delete-icon` prop to customize the delete [Icon](/components/icon) in the tags. Defaults to `i-lucide-x`. With `multiple`, use the `delete-icon` prop to customize the delete [Icon](/components/icon) in the badges. Defaults to `i-lucide-x`.
::component-code ::component-code
--- ---
@@ -757,33 +757,6 @@ name: 'input-menu-filter-fields-example'
--- ---
:: ::
### With full content width
You can expand the content to the full width of its items by using the `ui.content` key.
::component-example
---
name: 'input-menu-content-width-example'
collapse: true
---
::
::tip
You can also change the content width globally in your `app.config.ts`:
```
export default defineAppConfig({
ui: {
inputMenu: {
slots: {
content: 'min-w-fit'
}
}
}
})
```
::
### As a CountryPicker ### 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. This example demonstrates using the InputMenu as a country picker with lazy loading - countries are only fetched when the menu is opened.
@@ -809,14 +782,6 @@ name: 'input-menu-countries-example'
:component-emits :component-emits
### Expose
When accessing the component via a template ref, you can use the following:
| Name | Type |
| ---- | ---- |
| `inputRef`{lang="ts-type"} | `Ref<InstanceType<typeof ComboboxTrigger> \| null>`{lang="ts-type"} |
## Theme ## Theme
:component-theme :component-theme

View File

@@ -1,6 +1,6 @@
--- ---
title: InputNumber title: InputNumber
description: An input for numerical values with a customizable range. description: Input numerical values with a customizable range.
category: form category: form
links: links:
- label: NumberField - label: NumberField
@@ -287,8 +287,8 @@ name: 'input-number-slots-example'
When accessing the component via a template ref, you can use the following: When accessing the component via a template ref, you can use the following:
| Name | Type | | Name | Type |
| -------------------------- | ----------------------------------------------- | |----------------------------|-------------------------------------------------|
| `inputRef`{lang="ts-type"} | `Ref<InstanceType<typeof NumberFieldInput> \| null>`{lang="ts-type"} | | `inputRef`{lang="ts-type"} | `Ref<HTMLInputElement \| null>`{lang="ts-type"} |
## Theme ## Theme

View File

@@ -1,296 +0,0 @@
---
title: InputTags
description: An input element that displays interactive tags.
category: form
links:
- label: InputTags
icon: i-custom-reka-ui
to: https://reka-ui.com/docs/components/tags-input
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/InputTags.vue
navigation.badge: New
---
## Usage
Use the `v-model` directive to control the value of the InputTags.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: ['Vue']
---
::
Use the `default-value` prop to set the initial value when you do not need to control its state.
::component-code
---
prettier: true
ignore:
- defaultValue
props:
defaultValue: ['Vue']
---
::
### Placeholder
Use the `placeholder` prop to set a placeholder text.
::component-code
---
props:
placeholder: 'Enter tags...'
---
::
### Max Length :badge{label="Soon" class="align-text-top"}
Use the `max-length` prop to set the maximum number of characters allowed in a tag.
::component-code
---
props:
maxLength: 4
---
::
### Color
Use the `color` prop to change the ring color when the InputTags is focused.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: ['Vue']
color: neutral
highlight: true
---
::
::note
The `highlight` prop is used here to show the focus state. It's used internally when a validation error occurs.
::
### Variants
Use the `variant` prop to change the appearance of the InputTags.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: ['Vue']
variant: subtle
color: neutral
highlight: false
---
::
### Sizes
Use the `size` prop to adjust the size of the InputTags.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: ['Vue']
size: xl
---
::
### Icon
Use the `icon` prop to show an [Icon](/components/icon) inside the InputTags.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: ['Vue']
icon: 'i-lucide-search'
size: md
variant: outline
---
::
::note
Use the `leading` and `trailing` props to set the icon position or the `leading-icon` and `trailing-icon` props to set a different icon for each position.
::
### Avatar
Use the `avatar` prop to show an [Avatar](/components/avatar) inside the InputTags.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: ['Vue']
avatar:
src: 'https://github.com/vuejs.png'
size: md
variant: outline
---
::
### Delete Icon
Use the `delete-icon` prop to customize the delete [Icon](/components/icon) in the tags. Defaults to `i-lucide-x`.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: ['Vue']
deleteIcon: 'i-lucide-trash'
---
::
::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.
:::
::
### Loading
Use the `loading` prop to show a loading icon on the InputTags.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: ['Vue']
loading: true
trailing: false
---
::
### Loading Icon
Use the `loading-icon` prop to customize the loading icon. Defaults to `i-lucide-loader-circle`.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: ['Vue']
loading: true
loadingIcon: 'i-lucide-loader'
---
::
::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
Use the `disabled` prop to disable the InputTags.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: ['Vue']
disabled: true
---
::
## Examples
### Within a FormField
You can use the InputTags within a [FormField](/components/form-field) component to display a label, help text, required indicator, etc.
::component-example
---
name: 'input-tags-form-field-example'
---
::
## API
### Props
:component-props
### Slots
:component-slots
### Emits
:component-emits
### Expose
When accessing the component via a template ref, you can use the following:
| Name | Type |
| -------------------------- | ----------------------------------------------- |
| `inputRef`{lang="ts-type"} | `Ref<InstanceType<typeof TagsInputInput> \| null>`{lang="ts-type"} |
## Theme
:component-theme

View File

@@ -278,16 +278,6 @@ name: 'input-kbd-example'
This example uses the `defineShortcuts` composable to focus the Input when the :kbd{value="/"} key is pressed. This example uses the `defineShortcuts` composable to focus the Input when the :kbd{value="/"} key is pressed.
:: ::
### With mask
There's no built-in support for masks, but you can use libraries like [maska](https://github.com/beholdr/maska) to mask the Input.
::component-example
---
name: 'input-mask-example'
---
::
### With floating label ### With floating label
You can use the `#default` slot to add a floating label to the Input. You can use the `#default` slot to add a floating label to the Input.

View File

@@ -62,19 +62,6 @@ items:
--- ---
:: ::
### Color :badge{label="Soon" class="align-text-top"}
Use the `color` prop to change the color of the Kbd.
::component-code
---
props:
color: neutral
slots:
default: K
---
::
### Variant ### Variant
Use the `variant` prop to change the variant of the Kbd. Use the `variant` prop to change the variant of the Kbd.
@@ -82,7 +69,6 @@ Use the `variant` prop to change the variant of the Kbd.
::component-code ::component-code
--- ---
props: props:
color: neutral
variant: solid variant: solid
slots: slots:
default: K default: K

View File

@@ -889,7 +889,7 @@ You can inspect the DOM to see each item's content being rendered.
## Examples ## Examples
### With tooltip in items ### With tooltip in items :badge{label="New" class="align-text-top"}
When orientation is `vertical` and the menu is `collapsed`, you can set the `tooltip` prop to `true` to display a [Tooltip](/components/tooltip) around items with their label but you can also use the `tooltip` property on each item to override the default tooltip. When orientation is `vertical` and the menu is `collapsed`, you can set the `tooltip` prop to `true` to display a [Tooltip](/components/tooltip) around items with their label but you can also use the `tooltip` property on each item to override the default tooltip.
@@ -994,7 +994,7 @@ props:
--- ---
:: ::
### With popover in items ### With popover in items :badge{label="New" class="align-text-top"}
When orientation is `vertical` and the menu is `collapsed`, you can set the `popover` prop to `true` to display a [Popover](/components/popover) around items with their children but you can also use the `popover` property on each item to override the default popover. When orientation is `vertical` and the menu is `collapsed`, you can set the `popover` prop to `true` to display a [Popover](/components/popover) around items with their children but you can also use the `popover` property on each item to override the default popover.

View File

@@ -180,8 +180,6 @@ props:
:component-emits :component-emits
### Expose
When accessing the component via a template ref, you can use the following: When accessing the component via a template ref, you can use the following:
| Name | Type | | Name | Type |

View File

@@ -202,17 +202,7 @@ name: 'popover-command-palette-example'
--- ---
:: ::
### With following cursor :badge{label="Soon" class="align-text-top"} ### With anchor slot :badge{label="New" class="align-text-top"}
You can make the Popover follow the cursor when hovering over an element using the [`reference`](https://reka-ui.com/docs/components/tooltip#trigger) prop:
::component-example
---
name: 'popover-cursor-example'
---
::
### With anchor slot
You can use the `#anchor` slot to position the Popover against a custom element. You can use the `#anchor` slot to position the Popover against a custom element.

View File

@@ -159,7 +159,7 @@ props:
--- ---
:: ::
### Variant ### Variant :badge{label="New" class="align-text-top"}
Use the `variant` prop to change the variant of the RadioGroup. Use the `variant` prop to change the variant of the RadioGroup.
@@ -240,7 +240,7 @@ props:
--- ---
:: ::
### Indicator ### Indicator :badge{label="New" class="align-text-top"}
Use the `indicator` prop to change the position or hide the indicator. Defaults to `start`. Use the `indicator` prop to change the position or hide the indicator. Defaults to `start`.

View File

@@ -790,33 +790,6 @@ name: 'select-menu-filter-fields-example'
--- ---
:: ::
### With full content width
You can expand the content to the full width of its items by using the `ui.content` key.
::component-example
---
name: 'select-menu-content-width-example'
collapse: true
---
::
::tip
You can also change the content width globally in your `app.config.ts`:
```
export default defineAppConfig({
ui: {
selectMenu: {
slots: {
content: 'min-w-fit'
}
}
}
})
```
::
### As a CountryPicker ### 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. This example demonstrates using the SelectMenu as a country picker with lazy loading - countries are only fetched when the menu is opened.
@@ -828,8 +801,6 @@ name: 'select-menu-countries-example'
--- ---
:: ::
## API ## API
### Props ### Props
@@ -844,14 +815,6 @@ name: 'select-menu-countries-example'
:component-emits :component-emits
### Expose
When accessing the component via a template ref, you can use the following:
| Name | Type |
| ---- | ---- |
| `triggerRef`{lang="ts-type"} | `Ref<InstanceType<typeof ComboboxTrigger> \| null>`{lang="ts-type"} |
## Theme ## Theme
:component-theme :component-theme

View File

@@ -695,33 +695,6 @@ collapse: true
--- ---
:: ::
### With full content width
You can expand the content to the full width of its items by using the `ui.content` key.
::component-example
---
name: 'select-content-width-example'
collapse: true
---
::
::tip
You can also change the content width globally in your `app.config.ts`:
```
export default defineAppConfig({
ui: {
select: {
slots: {
content: 'min-w-fit'
}
}
}
})
```
::
## API ## API
### Props ### Props
@@ -736,14 +709,6 @@ export default defineAppConfig({
:component-emits :component-emits
### Expose
When accessing the component via a template ref, you can use the following:
| Name | Type |
| ---- | ---- |
| `triggerRef`{lang="ts-type"} | `Ref<InstanceType<typeof SelectTrigger> \| null>`{lang="ts-type"} |
## Theme ## Theme
:component-theme :component-theme

View File

@@ -136,7 +136,7 @@ props:
--- ---
:: ::
### Tooltip ### Tooltip :badge{label="New" class="align-text-top"}
Use the `tooltip` prop to display a [Tooltip](/components/tooltip) around the Slider thumbs with the current value. You can set it to `true` for default behavior or pass an object to customize it with any property from the [Tooltip](/components/tooltip#props) component. Use the `tooltip` prop to display a [Tooltip](/components/tooltip) around the Slider thumbs with the current value. You can set it to `true` for default behavior or pass an object to customize it with any property from the [Tooltip](/components/tooltip#props) component.

View File

@@ -77,15 +77,11 @@ Use the `columns` prop as an array of [ColumnDef](https://tanstack.com/table/lat
- `accessorKey`: [The key of the row object to use when extracting the value for the column.]{class="text-muted"} - `accessorKey`: [The key of the row object to use when extracting the value for the column.]{class="text-muted"}
- `header`: [The header to display for the column. If a string is passed, it can be used as a default for the column ID. If a function is passed, it will be passed a props object for the header and should return the rendered header value (the exact type depends on the adapter being used).]{class="text-muted"} - `header`: [The header to display for the column. If a string is passed, it can be used as a default for the column ID. If a function is passed, it will be passed a props object for the header and should return the rendered header value (the exact type depends on the adapter being used).]{class="text-muted"}
- `footer`: [The footer to display for the column. Works exactly like header, but is displayed under the table.]{class="text-muted"}
- `cell`: [The cell to display each row for the column. If a function is passed, it will be passed a props object for the cell and should return the rendered cell value (the exact type depends on the adapter being used).]{class="text-muted"} - `cell`: [The cell to display each row for the column. If a function is passed, it will be passed a props object for the cell and should return the rendered cell value (the exact type depends on the adapter being used).]{class="text-muted"}
- `meta`: [Extra properties for the column.]{class="text-muted"} - `meta`: [Extra properties for the column.]{class="text-muted"}
- `class`: - `class`:
- `td`: [The classes to apply to the `td` element.]{class="text-muted"} - `td`: [The classes to apply to the `td` element.]{class="text-muted"}
- `th`: [The classes to apply to the `th` element.]{class="text-muted"} - `th`: [The classes to apply to the `th` element.]{class="text-muted"}
- `style`:
- `td`: [The style to apply to the `td` element.]{class="text-muted"}
- `th`: [The style to apply to the `th` element.]{class="text-muted"}
In order to render components or other HTML elements, you will need to use the Vue [`h` function](https://vuejs.org/api/render-function.html#h) inside the `header` and `cell` props. This is different from other components that use slots but allows for more flexibility. In order to render components or other HTML elements, you will need to use the Vue [`h` function](https://vuejs.org/api/render-function.html#h) inside the `header` and `cell` props. This is different from other components that use slots but allows for more flexibility.
@@ -115,8 +111,6 @@ Use the `meta` prop as an object ([TableMeta](https://tanstack.com/table/latest/
- `class`: - `class`:
- `tr`: [The classes to apply to the `tr` element.]{class="text-muted"} - `tr`: [The classes to apply to the `tr` element.]{class="text-muted"}
- `style`:
- `tr`: [The style to apply to the `tr` element.]{class="text-muted"}
### Loading ### Loading
@@ -167,7 +161,7 @@ props:
### Sticky ### Sticky
Use the `sticky` prop to make the header or footer sticky. Use the `sticky` prop to make the header sticky.
::component-code ::component-code
--- ---
@@ -178,10 +172,6 @@ ignore:
- class - class
external: external:
- data - data
items:
sticky:
- true
- false
props: props:
sticky: true sticky: true
data: data:
@@ -276,8 +266,8 @@ You can group rows based on a given column value and show/hide sub rows via some
#### Important parts: #### Important parts:
* Add `grouping` prop with an array of column ids you want to group by. * Add prop `grouping` to `UTable` component with an array of column ids you want to group by.
* Add `grouping-options` prop. It must include `getGroupedRowModel`, you can import it from `@tanstack/vue-table` or implement your own. * Add prop `grouping-options` to `UTable`. It must include `getGroupedRowModel`, you can import it from `@tanstack/vue-table` or implement your own.
* Expand rows via `row.toggleExpanded()` method on any cell of the row. Keep in mind, it also toggles `#expanded` slot. * Expand rows via `row.toggleExpanded()` method on any cell of the row. Keep in mind, it also toggles `#expanded` slot.
* Use `aggregateFn` on column definition to define how to aggregate the rows. * Use `aggregateFn` on column definition to define how to aggregate the rows.
* `agregatedCell` renderer on column definition only works if there is no `cell` renderer. * `agregatedCell` renderer on column definition only works if there is no `cell` renderer.
@@ -314,86 +304,22 @@ class: '!p-0'
You can use the `row-selection` prop to control the selection state of the rows (can be binded with `v-model`). You can use the `row-selection` prop to control the selection state of the rows (can be binded with `v-model`).
:: ::
### With row select event ### With `@select` event
You can add a `@select` listener to make rows clickable with or without a checkbox column. You can add a `@select` listener to make rows clickable. The handler function receives the `TableRow` instance as the first argument and an optional `Event` as the second argument.
::note ::note
The handler function receives the `TableRow` instance as the first argument and an optional `Event` as the second argument.
::
::component-example
---
prettier: true
collapse: true
name: 'table-row-select-event-example'
highlights:
- 123
- 130
class: '!p-0'
---
::
::tip
You can use this to navigate to a page, open a modal or even to select the row manually. You can use this to navigate to a page, open a modal or even to select the row manually.
:: ::
### With row context menu event :badge{label="Soon" class="align-text-top"}
You can add a `@contextmenu` listener to make rows right clickable and wrap the Table in a [ContextMenu](/components/context-menu) component to display row actions for example.
::note
The handler function receives the `Event` and `TableRow` instance as the first and second arguments respectively.
::
::component-example ::component-example
--- ---
prettier: true prettier: true
collapse: true collapse: true
name: 'table-row-context-menu-event-example' name: 'table-row-selection-event-example'
highlights: highlights:
- 123
- 130 - 130
- 170
class: '!p-0'
---
::
### With row hover event :badge{label="Soon" class="align-text-top"}
You can add a `@hover` listener to make rows hoverable and use a [Popover](/components/popover) or a [Tooltip](/components/tooltip) component to display row details for example.
::note
The handler function receives the `Event` and `TableRow` instance as the first and second arguments respectively.
::
::component-example
---
prettier: true
collapse: true
name: 'table-row-hover-event-example'
highlights:
- 126
- 149
class: '!p-0'
---
::
::note
This example is similar as the Popover [with following cursor example](/components/popover#with-following-cursor) and uses a [`refDebounced`](https://vueuse.org/shared/refDebounced/#refdebounced) to prevent the Popover from opening and closing too quickly when moving the cursor from one row to another.
::
### With column footer :badge{label="Soon" class="align-text-top"}
You can add a `footer` property to the column definition to render a footer for the column.
::component-example
---
prettier: true
collapse: true
name: 'table-column-footer-example'
highlights:
- 94
- 108
class: '!p-0' class: '!p-0'
--- ---
:: ::

View File

@@ -19,13 +19,12 @@ Use the `items` prop as an array of objects with the following properties:
- `label?: string`{lang="ts-type"} - `label?: string`{lang="ts-type"}
- `icon?: string`{lang="ts-type"} - `icon?: string`{lang="ts-type"}
- `avatar?: AvatarProps`{lang="ts-type"} - `avatar?: AvatarProps`{lang="ts-type"}
- `badge?: string | number | BadgeProps`{lang="ts-type"}
- `content?: string`{lang="ts-type"} - `content?: string`{lang="ts-type"}
- `value?: string | number`{lang="ts-type"} - `value?: string | number`{lang="ts-type"}
- `disabled?: boolean`{lang="ts-type"} - `disabled?: boolean`{lang="ts-type"}
- [`slot?: string`{lang="ts-type"}](#with-custom-slot) - [`slot?: string`{lang="ts-type"}](#with-custom-slot)
- `class?: any`{lang="ts-type"} - `class?: any`{lang="ts-type"}
- `ui?: { trigger?: ClassNameValue, leadingIcon?: ClassNameValue, leadingAvatar?: ClassNameValue, leadingAvatarSize?: ClassNameValue, label?: ClassNameValue, trailingBadge?: ClassNameValue, trailingBadgeSize?: ClassNameValue, content?: ClassNameValue }`{lang="ts-type"} - `ui?: { trigger?: ClassNameValue, leadingIcon?: ClassNameValue, leadingAvatar?: ClassNameValue, label?: ClassNameValue, content?: ClassNameValue }`{lang="ts-type"}
::component-code ::component-code
--- ---
@@ -243,14 +242,6 @@ You will have access to the following slots:
:component-emits :component-emits
### Expose
When accessing the component via a template ref, you can use the following:
| Name | Type |
| ---- | ---- |
| `triggersRef`{lang="ts-type"} | `Ref<ComponentPublicInstance[]>`{lang="ts-type"} |
## Theme ## Theme
:component-theme :component-theme

View File

@@ -124,7 +124,7 @@ props:
--- ---
:: ::
### Icon ### Icon :badge{label="New" class="align-text-top"}
Use the `icon` prop to show an [Icon](/components/icon) inside the Textarea. Use the `icon` prop to show an [Icon](/components/icon) inside the Textarea.
@@ -157,7 +157,7 @@ props:
--- ---
:: ::
### Avatar ### Avatar :badge{label="New" class="align-text-top"}
Use the `avatar` prop to show an [Avatar](/components/avatar) inside the Textarea. Use the `avatar` prop to show an [Avatar](/components/avatar) inside the Textarea.
@@ -176,7 +176,7 @@ props:
--- ---
:: ::
### Loading ### Loading :badge{label="New" class="align-text-top"}
Use the `loading` prop to show a loading icon on the Textarea. Use the `loading` prop to show a loading icon on the Textarea.
@@ -192,7 +192,7 @@ props:
--- ---
:: ::
### Loading Icon ### Loading Icon :badge{label="New" class="align-text-top"}
Use the `loading-icon` prop to customize the loading icon. Defaults to `i-lucide-loader-circle`. Use the `loading-icon` prop to customize the loading icon. Defaults to `i-lucide-loader-circle`.

View File

@@ -6,7 +6,7 @@ links:
- label: GitHub - label: GitHub
icon: i-simple-icons-github icon: i-simple-icons-github
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Timeline.vue to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Timeline.vue
navigation.badge: New navigation.badge: Soon
--- ---
## Usage ## Usage
@@ -23,7 +23,7 @@ Use the `items` prop as an array of objects with the following properties:
- `value?: string | number`{lang="ts-type"} - `value?: string | number`{lang="ts-type"}
- [`slot?: string`{lang="ts-type"}](#with-custom-slot) - [`slot?: string`{lang="ts-type"}](#with-custom-slot)
- `class?: any`{lang="ts-type"} - `class?: any`{lang="ts-type"}
- `ui?: { item?: ClassNameValue, container?: ClassNameValue, indicator?: ClassNameValue, separator?: ClassNameValue, wrapper?: ClassNameValue, date?: ClassNameValue, title?: ClassNameValue, description?: ClassNameValue }`{lang="ts-type"} - `ui?: { item?: ClassNameValue, container?: ClassNameValue, indicator?: ClassNameValue, separator?: ClassNameValue, wrapper?: ClassNameValue, separator?: ClassNameValue, date?: ClassNameValue, title?: ClassNameValue, description?: ClassNameValue }`{lang="ts-type"}
::component-code ::component-code
--- ---
@@ -169,47 +169,6 @@ props:
description: 'QA testing and performance optimization.' description: 'QA testing and performance optimization.'
icon: 'i-lucide-check-circle' icon: 'i-lucide-check-circle'
class: 'w-full' class: 'w-full'
class: 'overflow-x-auto'
---
::
### Reverse
Use the reverse prop to reverse the direction of the Timeline.
::component-code
---
ignore:
- items
- class
- defaultValue
external:
- items
externalTypes:
- TimelineItem[]
props:
reverse: true
modelValue: 2
orientation: 'vertical'
items:
- date: 'Mar 15, 2025'
title: 'Project Kickoff'
description: 'Kicked off the project with team alignment.'
icon: 'i-lucide-rocket'
- date: 'Mar 22 2025'
title: 'Design Phase'
description: 'User research and design workshops.'
icon: 'i-lucide-palette'
- date: 'Mar 29 2025'
title: 'Development Sprint'
description: 'Frontend and backend development.'
icon: 'i-lucide-code'
- date: 'Apr 5 2025'
title: 'Testing & Deployment'
description: 'QA testing and performance optimization.'
icon: 'i-lucide-check-circle'
class: 'w-full'
class: 'overflow-x-auto'
--- ---
:: ::

View File

@@ -107,7 +107,7 @@ name: 'toast-color-example'
### Close ### Close
Pass a `close` field to customize or hide the close [Button](/components/button) (with `false` value). Pass a `close` field to customize or hide the close button (with `false` value).
::component-example ::component-example
--- ---
@@ -143,7 +143,7 @@ You can customize this icon globally in your `vite.config.ts` under `ui.icons.cl
### Actions ### Actions
Pass an `actions` field to add some [Button](/components/button) actions to the Toast. Pass an `actions` field to add some [Button](/components/button) actions to the Alert.
::component-example ::component-example
--- ---
@@ -155,23 +155,9 @@ name: 'toast-actions-example'
--- ---
:: ::
### Progress :badge{label="Soon" class="align-text-top"}
Pass a `progress` field to customize or hide the [Progress](/components/progress) bar (with `false` value).
::tip
The Progress bar inherits the Toast color by default, but you can override it using the `progress.color` field.
::
::component-example
---
name: 'toast-progress-example'
---
::
### Orientation ### Orientation
Pass an `orientation` field to the `toast.add` method to change the orientation of the Toast. Use the `orientation` prop to change the orientation of the Toast.
::component-example ::component-example
--- ---

View File

@@ -186,16 +186,6 @@ name: 'tooltip-open-example'
In this example, leveraging [`defineShortcuts`](/composables/define-shortcuts), you can toggle the Tooltip by pressing :kbd{value="O"}. In this example, leveraging [`defineShortcuts`](/composables/define-shortcuts), you can toggle the Tooltip by pressing :kbd{value="O"}.
:: ::
### With following cursor :badge{label="Soon" class="align-text-top"}
You can make the Tooltip follow the cursor when hovering over an element using the [`reference`](https://reka-ui.com/docs/components/tooltip#trigger) prop:
::component-example
---
name: 'tooltip-cursor-example'
---
::
## API ## API
### Props ### Props

View File

@@ -51,5 +51,3 @@ items:
url: https://wiredash.com/ url: https://wiredash.com/
- name: Zielgestalt - name: Zielgestalt
url: https://zielgestalt.de/ url: https://zielgestalt.de/
- name: Arthur Danjou's Porfolio
url: https://arthurdanjou.fr/

View File

@@ -33,7 +33,7 @@ export default defineNuxtModule((_, nuxt) => {
} }
const name = template.name.toLowerCase().replace(/\s/g, '-') const name = template.name.toLowerCase().replace(/\s/g, '-')
const filename = join(nuxt.options.rootDir, 'public/assets/showcase', `${name}.png`) const filename = join(process.cwd(), 'docs/public/assets/showcase', `${name}.png`)
if (existsSync(filename)) { if (existsSync(filename)) {
continue continue

View File

@@ -143,6 +143,10 @@ export default defineNuxtConfig({
'/releases': { redirect: 'https://github.com/nuxt/ui/releases', prerender: false } '/releases': { redirect: 'https://github.com/nuxt/ui/releases', prerender: false }
}, },
future: {
compatibilityVersion: 4
},
compatibilityDate: '2024-07-09', compatibilityDate: '2024-07-09',
nitro: { nitro: {

View File

@@ -2,49 +2,41 @@
"private": true, "private": true,
"name": "@nuxt/ui-docs", "name": "@nuxt/ui-docs",
"type": "module", "type": "module",
"scripts": {
"dev": "nuxt dev",
"build": "nuxt build",
"generate": "nuxt generate",
"typecheck": "nuxt typecheck"
},
"dependencies": { "dependencies": {
"@ai-sdk/vue": "^1.2.12", "@ai-sdk/vue": "^1.2.12",
"@iconify-json/logos": "^1.2.4", "@iconify-json/logos": "^1.2.4",
"@iconify-json/lucide": "^1.2.57", "@iconify-json/lucide": "^1.2.44",
"@iconify-json/simple-icons": "^1.2.44", "@iconify-json/simple-icons": "^1.2.35",
"@iconify-json/vscode-icons": "^1.2.23", "@iconify-json/vscode-icons": "^1.2.21",
"@nuxt/content": "^3.6.3", "@nuxt/content": "^3.5.1",
"@nuxt/image": "^1.10.0", "@nuxt/image": "^1.10.0",
"@nuxt/ui": "workspace:*", "@nuxt/ui": "latest",
"@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@17684e4", "@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@f06b49c",
"@nuxthub/core": "^0.9.0", "@nuxthub/core": "^0.8.27",
"@nuxtjs/plausible": "^1.2.0", "@nuxtjs/plausible": "^1.2.0",
"@octokit/rest": "^22.0.0", "@octokit/rest": "^22.0.0",
"@rollup/plugin-yaml": "^4.1.2", "@rollup/plugin-yaml": "^4.1.2",
"@vueuse/integrations": "^13.5.0", "@vueuse/integrations": "^13.2.0",
"@vueuse/nuxt": "^13.5.0", "@vueuse/nuxt": "^13.2.0",
"ai": "^4.3.19", "ai": "^4.3.16",
"better-sqlite3": "^12.2.0",
"capture-website": "^4.2.0", "capture-website": "^4.2.0",
"joi": "^17.13.3", "joi": "^17.13.3",
"maska": "^3.2.0", "motion-v": "^1.1.1",
"motion-v": "^1.5.0", "nuxt": "^3.17.4",
"nuxt": "^4.0.1", "nuxt-component-meta": "^0.11.0",
"nuxt-component-meta": "^0.12.1", "nuxt-llms": "^0.1.2",
"nuxt-llms": "^0.1.3", "nuxt-og-image": "^5.1.4",
"nuxt-og-image": "^5.1.9", "prettier": "^3.5.3",
"prettier": "^3.6.2",
"shiki-transformer-color-highlight": "^1.0.0", "shiki-transformer-color-highlight": "^1.0.0",
"sortablejs": "^1.15.6", "sortablejs": "^1.15.6",
"superstruct": "^2.0.2", "superstruct": "^2.0.2",
"ufo": "^1.6.1", "ufo": "^1.6.1",
"valibot": "^1.1.0", "valibot": "^1.1.0",
"workers-ai-provider": "^0.7.2", "workers-ai-provider": "^0.5.2",
"yup": "^1.6.1", "yup": "^1.6.1",
"zod": "^4.0.5" "zod": "^3.25.28"
}, },
"devDependencies": { "devDependencies": {
"wrangler": "^4.25.0" "wrangler": "^4.16.1"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

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