Compare commits

...

37 Commits

Author SHA1 Message Date
Benjamin Canac
4c96909020 chore(release): v3.0.0 2025-03-12 15:33:38 +01:00
Benjamin Canac
06dee66722 chore(package): update release command 2025-03-12 15:04:12 +01:00
Benjamin Canac
8494f50d98 docs: rename dev to v2 2025-03-12 15:04:05 +01:00
Benjamin Canac
cc7bdf9247 chore(release): v3.0.0-beta.4 2025-03-12 14:15:31 +01:00
renovate[bot]
8cbf6d2d58 chore(deps): update all non-major dependencies (v3) (#3521)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-12 14:10:05 +01:00
renovate[bot]
881f977fe4 chore(deps): update tailwindcss to ^4.0.13 (v3) (#3526)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-12 13:55:27 +01:00
Romain Hamel
6e03d9c6ef feat(Form): global errors (#3482)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-03-11 14:50:36 +01:00
Benjamin Canac
5ecd2271ca fix(vue): prevent calling useHead in colors 2025-03-11 10:36:13 +01:00
Benjamin Canac
556ebb0b36 playground-vue: add logo 2025-03-11 09:59:36 +01:00
Benjamin Canac
3a56e3cf45 chore(deps): update @nuxt/ui-pro 2025-03-11 09:59:36 +01:00
renovate[bot]
34dfe7d4b3 chore(deps): update all non-major dependencies (v3) (#3518)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-11 09:59:17 +01:00
GU Yiling
54468ca0f2 docs(figma): improve figma to code section (#3520) 2025-03-11 09:58:13 +01:00
Alex
a9c8eb3f60 feat(useLocale): handle generic messages (#3100)
Co-authored-by: hywax <me@hywax.space>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-03-10 18:37:28 +01:00
Eugen Istoc
04fc367568 docs(migration): fix typo (#3516) 2025-03-10 15:49:22 +01:00
Eugen Istoc
2b4e88d727 docs(migration): describe useToast and useOverlay (#3509)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-03-10 14:08:57 +01:00
Benjamin Canac
c713f3015d chore(deps): add vue / vue-router as dependencies 2025-03-10 12:42:56 +01:00
Benjamin Canac
0a603e41f9 chore(Pagination): remove vue-router import 2025-03-10 12:34:40 +01:00
renovate[bot]
8329fedd1a chore(deps): lock file maintenance (v3) (#3508)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 11:53:10 +01:00
renovate[bot]
e289db874d chore(deps): update all non-major dependencies (v3) (#3502)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 11:24:02 +01:00
Benjamin Canac
cbc5675e04 chore(vitest): improve config to ignore docs .c12 2025-03-10 11:14:41 +01:00
Benjamin Canac
66686d2d2a chore(deps): remove happy-dom resolution 2025-03-10 11:13:56 +01:00
Benjamin Canac
e67906aa0b chore(renovate): ignore vaul-vue 2025-03-10 10:59:42 +01:00
renovate[bot]
20fb8252f7 chore(deps): update vueuse monorepo to v13 (v3) (major) (#3512)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 10:47:48 +01:00
Iván Máximiliano, Lo Giudice
97c8098d4a fix(Form): input blur validation on submit (#3504) 2025-03-09 13:56:03 +01:00
Benjamin Canac
fbc3200ec5 chore(deps): update @nuxt/ui-pro 2025-03-09 13:00:33 +01:00
Benjamin Canac
243f981ff4 docs(app): add missing cache-key on mdc 2025-03-09 12:30:49 +01:00
renovate[bot]
9979a1c818 chore(deps): update nuxt framework to ^3.16.0 (v3) (#3484)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
Co-authored-by: Pooya Parsa <pooya@pi0.io>
2025-03-09 12:28:54 +01:00
renovate[bot]
619bf0d7c9 chore(deps): update all non-major dependencies (v3) (#3497)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-08 23:13:20 +01:00
Benjamin Canac
21dbf01888 fix(Button): missing import
Related to nuxt/ui#3417
2025-03-08 22:48:53 +01:00
renovate[bot]
a208dedaea chore(deps): update dependency tailwind-variants to v1 (v3) (#3499)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-03-08 19:40:23 +01:00
Benjamin Canac
6120a15a99 chore(LinkBase): update types for nuxt@3.16 2025-03-08 15:42:25 +01:00
renovate[bot]
b1552e447d chore(deps): update all non-major dependencies (v3) (#3489)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-08 12:48:04 +01:00
Benjamin Canac
36ec141c16 docs(installation): improve continuous releases section 2025-03-08 12:21:17 +01:00
Benjamin Canac
7940f5c0aa chore(templates): add tsdoc on ui app config key 2025-03-07 18:33:48 +01:00
Iván Máximiliano, Lo Giudice
cfe9b2ecf3 feat(Input/Textarea): allow null value in model (#3415) 2025-03-07 18:17:30 +01:00
Benjamin Canac
ed7710a890 docs(deps): update @nuxt/ui-pro 2025-03-07 18:08:29 +01:00
Benjamin Canac
83725ac048 chore(release): v3.0.0-beta.3 2025-03-07 15:38:35 +01:00
82 changed files with 2213 additions and 1996 deletions

View File

@@ -1,5 +1,36 @@
# Changelog # Changelog
## [3.0.0](https://github.com/nuxt/ui/compare/v3.0.0-beta.4...v3.0.0) (2025-03-12)
## [3.0.0-beta.4](https://github.com/nuxt/ui/compare/v3.0.0-beta.3...v3.0.0-beta.4) (2025-03-12)
### Features
* **Form:** global errors ([#3482](https://github.com/nuxt/ui/issues/3482)) ([6e03d9c](https://github.com/nuxt/ui/commit/6e03d9c6efc8f4cfc306813e733d7d3e03706323))
* **Input/Textarea:** allow `null` value in model ([#3415](https://github.com/nuxt/ui/issues/3415)) ([cfe9b2e](https://github.com/nuxt/ui/commit/cfe9b2ecf34827bc11a5281a069988ab96030047))
* **useLocale:** handle generic messages ([#3100](https://github.com/nuxt/ui/issues/3100)) ([a9c8eb3](https://github.com/nuxt/ui/commit/a9c8eb3f60a10d1a71632991c9db594716b0fba1))
### Bug Fixes
* **Button:** missing import ([21dbf01](https://github.com/nuxt/ui/commit/21dbf01888a161a9d8ac6eb0d957c1342f6cc30d)), closes [nuxt/ui#3417](https://github.com/nuxt/ui/issues/3417)
* **Form:** input blur validation on submit ([#3504](https://github.com/nuxt/ui/issues/3504)) ([97c8098](https://github.com/nuxt/ui/commit/97c8098d4a35c392719ae179d36aa008d6f8f78a))
* **vue:** prevent calling `useHead` in colors ([5ecd227](https://github.com/nuxt/ui/commit/5ecd2271ca86087cb805548397d75c38763ad412))
## [3.0.0-beta.3](https://github.com/nuxt/ui/compare/v3.0.0-beta.2...v3.0.0-beta.3) (2025-03-07)
### Features
* **Button:** handle `active` state ([bd2d484](https://github.com/nuxt/ui/commit/bd2d4848d246a3d5930f8059913f5a1a0abe29fd)), closes [#3417](https://github.com/nuxt/ui/issues/3417)
* **Table:** add `loading` slot ([99e531d](https://github.com/nuxt/ui/commit/99e531d8dfb7954322b7ab7feda3d8814c6d8d02)), closes [#3444](https://github.com/nuxt/ui/issues/3444)
### Bug Fixes
* **InputMenu/SelectMenu:** proxy `required` in root props ([60b7e2d](https://github.com/nuxt/ui/commit/60b7e2d69e80afa7e221855dcec46479d0ca5c6c))
* **InputMenu:** wrong `required` in multiple mode ([01fa230](https://github.com/nuxt/ui/commit/01fa230eae4b6623c5fd71cc218d114d9f6f0f25)), closes [#2741](https://github.com/nuxt/ui/issues/2741)
* **Pagination:** add missing slots ([a47c5ff](https://github.com/nuxt/ui/commit/a47c5ff46616eafee3158cb9801183965f5f9874)), closes [#3441](https://github.com/nuxt/ui/issues/3441)
* **Pagination:** wrong next link ([e823022](https://github.com/nuxt/ui/commit/e823022b19bb172d2e5fabb9144b4a4286a25a5f)), closes [#3008](https://github.com/nuxt/ui/issues/3008)
* **templates:** prevent overriding existing colors ([ccbd89c](https://github.com/nuxt/ui/commit/ccbd89c908fe8af54c7d723dd12da5b7f3906c8f)), closes [#3426](https://github.com/nuxt/ui/issues/3426)
## [3.0.0-beta.2](https://github.com/nuxt/ui/compare/v3.0.0-beta.1...v3.0.0-beta.2) (2025-02-28) ## [3.0.0-beta.2](https://github.com/nuxt/ui/compare/v3.0.0-beta.1...v3.0.0-beta.2) (2025-02-28)
### Bug Fixes ### Bug Fixes

View File

@@ -14,7 +14,7 @@
We're thrilled to introduce Nuxt UI v3, a significant upgrade to our UI library that delivers extensive improvements and robust new capabilities. This major update harnesses the combined strengths of [Reka UI](https://reka-ui.com/), [Tailwind CSS v4](https://tailwindcss.com/), and [Tailwind Variants](https://www.tailwind-variants.org/) to offer developers an unparalleled set of tools for creating sophisticated, accessible, and highly performant user interfaces. We're thrilled to introduce Nuxt UI v3, a significant upgrade to our UI library that delivers extensive improvements and robust new capabilities. This major update harnesses the combined strengths of [Reka UI](https://reka-ui.com/), [Tailwind CSS v4](https://tailwindcss.com/), and [Tailwind Variants](https://www.tailwind-variants.org/) to offer developers an unparalleled set of tools for creating sophisticated, accessible, and highly performant user interfaces.
> [!NOTE] > [!NOTE]
> You are on the `v3` development branch, check out the [dev branch](https://github.com/nuxt/ui/tree/dev) for Nuxt UI v2. > You are on the `v3` development branch, check out the [v2 branch](https://github.com/nuxt/ui/tree/v2) for Nuxt UI v2.
## Documentation ## Documentation

View File

@@ -275,16 +275,14 @@ faq:
content: As the Figma Pro Kit is a digital product packaged as a zip file, we cannot offer refunds once the purchase is made. content: As the Figma Pro Kit is a digital product packaged as a zip file, we cannot offer refunds once the purchase is made.
- label: Do you have a Figma to Code plugin? - label: Do you have a Figma to Code plugin?
content: > content: >
We recommend the open source [TeamPad Dev](https://github.com/ecomfe/tempad-dev) inspect panel with the [TeamPad Dev Nuxt UI Plugin](https://github.com/Justineo/tempad-dev-plugin-nuxt-ui): We recommend the open source [TemPad Dev](https://github.com/ecomfe/tempad-dev) inspect panel with the [TemPad Dev Nuxt UI Plugin](https://github.com/Justineo/tempad-dev-plugin-nuxt-ui):
1. Install the [TeamPad Dev Chrome Extension](https://chromewebstore.google.com/detail/tempad-dev/lgoeakbaikpkihoiphamaeopmliaimpc) 1. Install the [TemPad Dev Chrome Extension](https://chromewebstore.google.com/detail/tempad-dev/lgoeakbaikpkihoiphamaeopmliaimpc)
2. Open your Figma file with Nuxt UI components (reload the page if you don't see the TeamPad Dev panel) 2. Open your Figma file with Nuxt UI components (reload the page if you don't see the TemPad Dev panel)
3. Install the `@nuxt` in TeamPad Dev's plugins section 3. Install the `@nuxt` (or `@nuxt/pro` for Nuxt UI Pro) in TemPad Dev's plugins section
4. Select any Nuxt UI component and inspect the code it generates 4. Select any Nuxt UI component and inspect the code it generates
![TeamPad Dev Nuxt UI Plugin](/pro/figma/teampad-dev-nuxt-ui-plugin.gif){.w-full .rounded .mb-2 .max-w-[636px]} ![TemPad Dev Nuxt UI Plugin](/pro/figma/teampad-dev-nuxt-ui-plugin.gif){.w-full .rounded .mb-2 .max-w-[636px]}
*Right now, only Nuxt UI components are supported, but the code of the plugin is [open source](https://github.com/Justineo/tempad-dev-plugin-nuxt-ui) and anyone can contribute to it.*

View File

@@ -189,7 +189,7 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
:links="page.design_system.links" :links="page.design_system.links"
orientation="horizontal" orientation="horizontal"
> >
<MDC :value="page.design_system.code" /> <MDC :value="page.design_system.code" cache-key="index-design-system-code" />
</UPageSection> </UPageSection>
<USeparator /> <USeparator />
@@ -201,10 +201,10 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
orientation="horizontal" orientation="horizontal"
> >
<template #description> <template #description>
<MDC :value="page.component_customization.description" /> <MDC :value="page.component_customization.description" cache-key="index-component-customization-description" />
</template> </template>
<MDC :value="page.component_customization.code" /> <MDC :value="page.component_customization.code" cache-key="index-component-customization-code" />
</UPageSection> </UPageSection>
<USeparator /> <USeparator />

View File

@@ -227,28 +227,17 @@ This option adds the `transition-colors` class on components with hover or activ
Nuxt UI v3 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 v3 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.
Preview releases are automatically generated for every commit to the `v3` branch and pull requests targeting the `v3` branch. To use it into your project, use the installation command below by replacing `5385f84` with any commit hash or pull request number. Automatic preview releases are created for all commits and PRs to the `v3` branch. Use them by replacing your package version with the specific commit hash or PR number.
::code-group{sync="pm"} ```diff [package.json]
{
```bash [pnpm] "dependencies": {
pnpm add https://pkg.pr.new/@nuxt/ui@5385f84 - "@nuxt/ui": "^3.0.0-beta.3",
+ "@nuxt/ui": "https://pkg.pr.new/@nuxt/ui@83725ac",
}
}
``` ```
```bash [yarn]
yarn add https://pkg.pr.new/@nuxt/ui@5385f84
```
```bash [npm]
npm install https://pkg.pr.new/@nuxt/ui@5385f84
```
```bash [bun]
bun add https://pkg.pr.new/@nuxt/ui@5385f84
```
::
::note ::note
**pkg.pr.new** will automatically comment on PRs with the installation URL, making it easy to test changes. **pkg.pr.new** will automatically comment on PRs with the installation URL, making it easy to test changes.
:: ::

View File

@@ -315,28 +315,17 @@ This option adds the `transition-colors` class on components with hover or activ
Nuxt UI v3 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 v3 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.
Preview releases are automatically generated for every commit to the `v3` branch and pull requests targeting the `v3` branch. To use it into your project, use the installation command below by replacing `5385f84` with any commit hash or pull request number. Automatic preview releases are created for all commits and PRs to the `v3` branch. Use them by replacing your package version with the specific commit hash or PR number.
::code-group{sync="pm"} ```diff [package.json]
{
```bash [pnpm] "dependencies": {
pnpm add https://pkg.pr.new/@nuxt/ui@5385f84 - "@nuxt/ui": "^3.0.0-beta.3",
+ "@nuxt/ui": "https://pkg.pr.new/@nuxt/ui@83725ac",
}
}
``` ```
```bash [yarn]
yarn add https://pkg.pr.new/@nuxt/ui@5385f84
```
```bash [npm]
npm install https://pkg.pr.new/@nuxt/ui@5385f84
```
```bash [bun]
bun add https://pkg.pr.new/@nuxt/ui@5385f84
```
::
::note ::note
**pkg.pr.new** will automatically comment on PRs with the installation URL, making it easy to test changes. **pkg.pr.new** will automatically comment on PRs with the installation URL, making it easy to test changes.
:: ::

View File

@@ -439,7 +439,10 @@ This change affects the following components: `Modal`, `Popover`, `Slideover`, `
This change affects the following components: `Modal`, `Slideover`. This change affects the following components: `Modal`, `Slideover`.
:: ::
6. The `Toast` component `timeout` prop has been renamed to `duration`:
### Changed composables
1. The `useToast()` composable `timeout` prop has been renamed to `duration`:
```diff ```diff
<script setup lang="ts"> <script setup lang="ts">
@@ -450,6 +453,79 @@ const toast = useToast()
</script> </script>
``` ```
2. The `useModal` and `useSlideover` composables have been removed in favor of a more generic `useOverlay` composable:
Some important differences:
- The `useOverlay` composable is now used to create overlay instances
- Overlays that are opened, can be awaited for their result
- Overlays can no longer be closed using `modal.close()` or `slideover.close()`, rather, they close automatically: either when a `closed` event is fired explicitly from the opened component OR when the overlay closes itself (clicking on backdrop, pressing the ESC key, etc)
- To capture the return value in the parent component you must explictly emit a `closed` event with the desired value
```diff
<script setup lang="ts">
import { ModalExampleComponent } from '#components'
- const modal = useModal()
+ const overlay = useOverlay()
- modal.open(ModalExampleComponent)
+ const modal = overlay.create(ModalExampleComponent)
</script>
```
Props are now passed through a props attribute:
```diff
<script setup lang="ts">
import { ModalExampleComponent } from '#components'
- const modal = useModal()
+ const overlay = useOverlay()
const count = ref(0)
- modal.open(ModalExampleComponent, {
- count: count.value
- })
+ const modal = overlay.create(ModalExampleComponent, {
+ props: {
+ count: count.value
+ }
+ })
</script>
```
Closing a modal is now done through the `closed` event. The `modal.open` method now returns a promise that resolves to the result of the modal whenever the modal is closed:
```diff
<script setup lang="ts">
import { ModalExampleComponent } from '#components'
- const modal = useModal()
+ const overlay = useOverlay()
+ const modal = overlay.create(ModalExampleComponent)
- function openModal() {
- modal.open(ModalExampleComponent, {
- onSuccess() {
- toast.add({ title: 'Success!' })
- }
- })
- }
+ async function openModal() {
+ const result = await modal.open(ModalExampleComponent, {
+ count: count.value
+ })
+
+ if (result) {
+ toast.add({ title: 'Success!' })
+ }
+ }
</script>
```
--- ---
::warning ::warning

View File

@@ -17,6 +17,11 @@ Nuxt UI provides an **App** component that wraps your app to provide global conf
### Locale ### Locale
::module-only
#ui
:::div
Use the `locale` prop with the locale you want to use from `@nuxt/ui/locale`: Use the `locale` prop with the locale you want to use from `@nuxt/ui/locale`:
```vue [app.vue] ```vue [app.vue]
@@ -31,13 +36,42 @@ import { fr } from '@nuxt/ui/locale'
</template> </template>
``` ```
:::
#ui-pro
:::div
Use the `locale` prop with the locale you want to use from `@nuxt/ui-pro/locale`:
```vue [app.vue]
<script setup lang="ts">
import { fr } from '@nuxt/ui-pro/locale'
</script>
<template>
<UApp :locale="fr">
<NuxtPage />
</UApp>
</template>
```
:::
::
### Custom locale ### Custom locale
You also have the option to add your own locale using `defineLocale`: You also have the option to add your own locale using `defineLocale`:
::module-only
#ui
:::div
```vue [app.vue] ```vue [app.vue]
<script setup lang="ts"> <script setup lang="ts">
const locale = defineLocale({ import type { Messages } from '@nuxt/ui'
const locale = defineLocale<Messages>({
name: 'My custom locale', name: 'My custom locale',
code: 'en', code: 'en',
dir: 'ltr', dir: 'ltr',
@@ -54,6 +88,35 @@ const locale = defineLocale({
</template> </template>
``` ```
:::
#ui-pro
:::div
```vue [app.vue]
<script setup lang="ts">
import type { Messages } from '@nuxt/ui-pro'
const locale = defineLocale<Messages>({
name: 'My custom locale',
code: 'en',
dir: 'ltr',
messages: {
// implement pairs
}
})
</script>
<template>
<UApp :locale="locale">
<NuxtPage />
</UApp>
</template>
```
:::
::
::tip ::tip
Look at the `code` parameter, there you need to pass the iso code of the language. Example: Look at the `code` parameter, there you need to pass the iso code of the language. Example:
@@ -116,6 +179,11 @@ export default defineNuxtConfig({
#### Set the `locale` prop using `useI18n` #### Set the `locale` prop using `useI18n`
::module-only
#ui
:::div
```vue [app.vue] ```vue [app.vue]
<script setup lang="ts"> <script setup lang="ts">
import * as locales from '@nuxt/ui/locale' import * as locales from '@nuxt/ui/locale'
@@ -130,6 +198,28 @@ const { locale } = useI18n()
</template> </template>
``` ```
:::
#ui-pro
:::div
```vue [app.vue]
<script setup lang="ts">
import * as locales from '@nuxt/ui-pro/locale'
const { locale } = useI18n()
</script>
<template>
<UApp :locale="locales[locale]">
<NuxtPage />
</UApp>
</template>
```
:::
::
:: ::
### Dynamic direction ### Dynamic direction
@@ -138,6 +228,11 @@ Each locale has a `dir` property which will be used by the `App` component to se
In a multilingual application, you might want to set the `lang` and `dir` attributes on the `<html>` element dynamically based on the user's locale, which you can do with the [useHead](https://nuxt.com/docs/api/composables/use-head) composable: In a multilingual application, you might want to set the `lang` and `dir` attributes on the `<html>` element dynamically based on the user's locale, which you can do with the [useHead](https://nuxt.com/docs/api/composables/use-head) composable:
::module-only
#ui
:::div
```vue [app.vue] ```vue [app.vue]
<script setup lang="ts"> <script setup lang="ts">
import * as locales from '@nuxt/ui/locale' import * as locales from '@nuxt/ui/locale'
@@ -162,6 +257,38 @@ useHead({
</template> </template>
``` ```
:::
#ui-pro
:::div
```vue [app.vue]
<script setup lang="ts">
import * as locales from '@nuxt/ui-pro/locale'
const { locale } = useI18n()
const lang = computed(() => locales[locale.value].code)
const dir = computed(() => locales[locale.value].dir)
useHead({
htmlAttrs: {
lang,
dir
}
})
</script>
<template>
<UApp :locale="locales[locale]">
<NuxtPage />
</UApp>
</template>
```
:::
::
## Supported languages ## Supported languages
:supported-languages :supported-languages

View File

@@ -17,6 +17,11 @@ Nuxt UI provides an **App** component that wraps your app to provide global conf
### Locale ### Locale
::module-only
#ui
:::div
Use the `locale` prop with the locale you want to use from `@nuxt/ui/locale`: Use the `locale` prop with the locale you want to use from `@nuxt/ui/locale`:
```vue [App.vue] ```vue [App.vue]
@@ -31,15 +36,43 @@ import { fr } from '@nuxt/ui/locale'
</template> </template>
``` ```
:::
#ui-pro
:::div
Use the `locale` prop with the locale you want to use from `@nuxt/ui-pro/locale`:
```vue [App.vue]
<script setup lang="ts">
import { fr } from '@nuxt/ui-pro/locale'
</script>
<template>
<UApp :locale="fr">
<RouterView />
</UApp>
</template>
```
:::
::
### Custom locale ### Custom locale
You also have the option to add your locale using `defineLocale`: You also have the option to add your locale using `defineLocale`:
::module-only
#ui
:::div
```vue [App.vue] ```vue [App.vue]
<script setup lang="ts"> <script setup lang="ts">
import { defineLocale } from '@nuxt/ui/composables/defineLocale' import type { Messages } from '@nuxt/ui'
import { defineLocale } from '@nuxt/ui/composables/defineLocale.js'
const locale = defineLocale({ const locale = defineLocale<Messages>({
name: 'My custom locale', name: 'My custom locale',
code: 'en', code: 'en',
dir: 'ltr', dir: 'ltr',
@@ -56,6 +89,36 @@ const locale = defineLocale({
</template> </template>
``` ```
:::
#ui-pro
:::div
```vue [App.vue]
<script setup lang="ts">
import type { Messages } from '@nuxt/ui-pro'
import { defineLocale } from '@nuxt/ui/composables/defineLocale.js'
const locale = defineLocale<Messages>({
name: 'My custom locale',
code: 'en',
dir: 'ltr',
messages: {
// implement pairs
}
})
</script>
<template>
<UApp :locale="locale">
<RouterView />
</UApp>
</template>
```
:::
::
::tip ::tip
Look at the `code` parameter, there you need to pass the iso code of the language. Example: Look at the `code` parameter, there you need to pass the iso code of the language. Example:
@@ -131,6 +194,11 @@ app.mount('#app')
#### Set the `locale` prop using `useI18n` #### Set the `locale` prop using `useI18n`
::module-only
#ui
:::div
```vue [App.vue] ```vue [App.vue]
<script setup lang="ts"> <script setup lang="ts">
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
@@ -146,6 +214,29 @@ const { locale } = useI18n()
</template> </template>
``` ```
:::
#ui-pro
:::div
```vue [App.vue]
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import * as locales from '@nuxt/ui-pro/locale'
const { locale } = useI18n()
</script>
<template>
<UApp :locale="locales[locale]">
<RouterView />
</UApp>
</template>
```
:::
::
:: ::
### Dynamic direction ### Dynamic direction
@@ -154,6 +245,11 @@ Each locale has a `dir` property which will be used by the `App` component to se
In a multilingual application, you might want to set the `lang` and `dir` attributes on the `<html>` element dynamically based on the user's locale, which you can do with the [useHead](https://unhead.unjs.io/usage/composables/use-head) composable: In a multilingual application, you might want to set the `lang` and `dir` attributes on the `<html>` element dynamically based on the user's locale, which you can do with the [useHead](https://unhead.unjs.io/usage/composables/use-head) composable:
::module-only
#ui
:::div
```vue [App.vue] ```vue [App.vue]
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed } from 'vue'
@@ -181,6 +277,41 @@ useHead({
</template> </template>
``` ```
:::
#ui-pro
:::div
```vue [App.vue]
<script setup lang="ts">
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { useHead } from '@unhead/vue'
import * as locales from '@nuxt/ui-pro/locale'
const { locale } = useI18n()
const lang = computed(() => locales[locale.value].code)
const dir = computed(() => locales[locale.value].dir)
useHead({
htmlAttrs: {
lang,
dir
}
})
</script>
<template>
<UApp :locale="locales[locale]">
<RouterView />
</UApp>
</template>
```
:::
::
## Supported languages ## Supported languages
:supported-languages :supported-languages

View File

@@ -55,6 +55,7 @@ export default defineNuxtConfig({
}] }]
}, },
rootAttrs: { rootAttrs: {
// @ts-expect-error - vaul-drawer-wrapper is not typed
'vaul-drawer-wrapper': '', 'vaul-drawer-wrapper': '',
'class': 'bg-(--ui-bg)' 'class': 'bg-(--ui-bg)'
} }

View File

@@ -4,25 +4,25 @@
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@iconify-json/logos": "^1.2.4", "@iconify-json/logos": "^1.2.4",
"@iconify-json/lucide": "^1.2.28", "@iconify-json/lucide": "^1.2.29",
"@iconify-json/simple-icons": "^1.2.27", "@iconify-json/simple-icons": "^1.2.28",
"@iconify-json/vscode-icons": "^1.2.16", "@iconify-json/vscode-icons": "^1.2.16",
"@nuxt/content": "^3.3.0", "@nuxt/content": "^3.3.0",
"@nuxt/image": "^1.9.0", "@nuxt/image": "^1.9.0",
"@nuxt/ui": "latest", "@nuxt/ui": "latest",
"@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@02b7ea0", "@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@a2768ed",
"@nuxthub/core": "^0.8.18", "@nuxthub/core": "^0.8.18",
"@nuxtjs/plausible": "^1.2.0", "@nuxtjs/plausible": "^1.2.0",
"@octokit/rest": "^21.1.1", "@octokit/rest": "^21.1.1",
"@rollup/plugin-yaml": "^4.1.2", "@rollup/plugin-yaml": "^4.1.2",
"@vueuse/nuxt": "^12.8.2", "@vueuse/nuxt": "^13.0.0",
"joi": "^17.13.3", "joi": "^17.13.3",
"motion": "^12.4.10", "motion": "^12.5.0",
"motion-v": "0.11.1", "motion-v": "0.11.3",
"nuxt": "^3.15.4", "nuxt": "^3.16.0",
"nuxt-component-meta": "^0.10.0", "nuxt-component-meta": "^0.10.0",
"nuxt-llms": "^0.1.0", "nuxt-llms": "^0.1.0",
"nuxt-og-image": "^4.2.0", "nuxt-og-image": "^5.0.2",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"shiki-transformer-color-highlight": "^1.0.0", "shiki-transformer-color-highlight": "^1.0.0",
"superstruct": "^2.0.2", "superstruct": "^2.0.2",

View File

@@ -1,8 +1,8 @@
{ {
"name": "@nuxt/ui", "name": "@nuxt/ui",
"description": "A UI Library for Modern Web Apps, powered by Vue & Tailwind CSS.", "description": "A UI Library for Modern Web Apps, powered by Vue & Tailwind CSS.",
"version": "3.0.0-beta.2", "version": "3.0.0",
"packageManager": "pnpm@10.6.1", "packageManager": "pnpm@10.6.2",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/nuxt/ui.git" "url": "git+https://github.com/nuxt/ui.git"
@@ -67,7 +67,7 @@
"dev:build": "nuxi build playground", "dev:build": "nuxi build playground",
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground && nuxi prepare docs && vite build playground-vue", "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground && nuxi prepare docs && vite build playground-vue",
"docs": "DEV=true nuxi dev docs", "docs": "DEV=true nuxi dev docs",
"docs:build": "nuxi build docs", "docs:build": "NODE_OPTIONS='--max-old-space-size=8192' nuxi build docs",
"docs:prepare": "nuxt-component-meta docs", "docs:prepare": "nuxt-component-meta docs",
"lint": "eslint .", "lint": "eslint .",
"lint:fix": "eslint . --fix", "lint:fix": "eslint . --fix",
@@ -75,23 +75,23 @@
"test": "vitest", "test": "vitest",
"test:vue": "vitest -c vitest.vue.config.ts", "test:vue": "vitest -c vitest.vue.config.ts",
"test:vue:build": "vite build playground-vue", "test:vue:build": "vite build playground-vue",
"release": "release-it --preRelease=beta --npm.tag=next" "release": "release-it"
}, },
"dependencies": { "dependencies": {
"@iconify/vue": "^4.3.0", "@iconify/vue": "^4.3.0",
"@internationalized/date": "^3.7.0", "@internationalized/date": "^3.7.0",
"@internationalized/number": "^3.6.0", "@internationalized/number": "^3.6.0",
"@nuxt/fonts": "^0.10.3", "@nuxt/fonts": "^0.11.0",
"@nuxt/icon": "^1.10.3", "@nuxt/icon": "^1.11.0",
"@nuxt/kit": "^3.15.4", "@nuxt/kit": "^3.16.0",
"@nuxt/schema": "^3.15.4", "@nuxt/schema": "^3.16.0",
"@nuxtjs/color-mode": "^3.5.2", "@nuxtjs/color-mode": "^3.5.2",
"@tailwindcss/postcss": "^4.0.12", "@tailwindcss/postcss": "^4.0.13",
"@tailwindcss/vite": "^4.0.12", "@tailwindcss/vite": "^4.0.13",
"@tanstack/vue-table": "^8.21.2", "@tanstack/vue-table": "^8.21.2",
"@unhead/vue": "^1.11.20", "@unhead/vue": "^2.0.0-rc.10",
"@vueuse/core": "^12.8.2", "@vueuse/core": "^13.0.0",
"@vueuse/integrations": "^12.8.2", "@vueuse/integrations": "^13.0.0",
"colortranslator": "^4.1.0", "colortranslator": "^4.1.0",
"consola": "^3.4.0", "consola": "^3.4.0",
"defu": "^6.1.4", "defu": "^6.1.4",
@@ -110,26 +110,28 @@
"pathe": "^2.0.3", "pathe": "^2.0.3",
"reka-ui": "^2.0.2", "reka-ui": "^2.0.2",
"scule": "^1.3.0", "scule": "^1.3.0",
"tailwind-variants": "^0.3.1", "tailwind-variants": "^1.0.0",
"tailwindcss": "^4.0.12", "tailwindcss": "^4.0.13",
"tinyglobby": "^0.2.12", "tinyglobby": "^0.2.12",
"unplugin": "^2.2.0", "unplugin": "^2.2.0",
"unplugin-auto-import": "^19.1.1", "unplugin-auto-import": "^19.1.1",
"unplugin-vue-components": "^28.4.1", "unplugin-vue-components": "^28.4.1",
"vaul-vue": "^0.3.0" "vaul-vue": "^0.3.0",
"vue": "^3.5.13",
"vue-router": "^4.5.0"
}, },
"devDependencies": { "devDependencies": {
"@nuxt/eslint-config": "^1.1.0", "@nuxt/eslint-config": "^1.2.0",
"@nuxt/module-builder": "^0.8.4", "@nuxt/module-builder": "^0.8.4",
"@nuxt/test-utils": "^3.17.1", "@nuxt/test-utils": "^3.17.2",
"@release-it/conventional-changelog": "^10.0.0", "@release-it/conventional-changelog": "^10.0.0",
"@standard-schema/spec": "^1.0.0", "@standard-schema/spec": "^1.0.0",
"@vue/test-utils": "^2.4.6", "@vue/test-utils": "^2.4.6",
"embla-carousel": "^8.5.2", "embla-carousel": "^8.5.2",
"eslint": "^9.21.0", "eslint": "^9.22.0",
"happy-dom": "^17.1.2", "happy-dom": "^17.4.4",
"joi": "^17.13.3", "joi": "^17.13.3",
"nuxt": "^3.15.4", "nuxt": "^3.16.0",
"release-it": "^18.1.2", "release-it": "^18.1.2",
"superstruct": "^2.0.2", "superstruct": "^2.0.2",
"valibot": "^0.42.1", "valibot": "^0.42.1",
@@ -146,12 +148,9 @@
"@nuxt/ui": "workspace:*", "@nuxt/ui": "workspace:*",
"chokidar": "3.6.0", "chokidar": "3.6.0",
"debug": "4.3.7", "debug": "4.3.7",
"happy-dom": "17.1.2", "rollup": "4.34.9",
"rollup": "4.32.1",
"typescript": "5.6.3", "typescript": "5.6.3",
"unimport": "3.14.5",
"unplugin": "^2.2.0", "unplugin": "^2.2.0",
"vue": "3.5.13",
"vue-tsc": "2.2.0" "vue-tsc": "2.2.0"
}, },
"pnpm": { "pnpm": {

View File

@@ -2,8 +2,9 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Nuxt UI ❤️ Vue</title> <title>Nuxt UI - Vue Playground</title>
</head> </head>
<body> <body>
<div id="app" class="isolate"></div> <div id="app" class="isolate"></div>

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
<style>
.st0 { fill: #42B883; }
.st1 { fill: #35495E; }
</style>
<path class="st0" d="M78.8,10L64,35.4L49.2,10H0l64,110l64-110C128,10,78.8,10,78.8,10z" />
<path class="st1" d="M78.8,10L64,35.4L49.2,10H25.6L64,76l38.4-66H78.8z" />
</svg>

After

Width:  |  Height:  |  Size: 316 B

View File

@@ -8,10 +8,10 @@
"generate": "nuxi generate" "generate": "nuxi generate"
}, },
"dependencies": { "dependencies": {
"@iconify-json/lucide": "^1.2.28", "@iconify-json/lucide": "^1.2.29",
"@iconify-json/simple-icons": "^1.2.27", "@iconify-json/simple-icons": "^1.2.28",
"@nuxt/ui": "latest", "@nuxt/ui": "latest",
"@nuxthub/core": "^0.8.18", "@nuxthub/core": "^0.8.18",
"nuxt": "^3.15.4" "nuxt": "^3.16.0"
} }
} }

3418
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,13 +6,13 @@
"enabled": true "enabled": true
}, },
"ignoreDeps": [ "ignoreDeps": [
"happy-dom",
"valibot30", "valibot30",
"valibot31", "valibot31",
"typescript", "typescript",
"vaul-vue",
"vue-tsc" "vue-tsc"
], ],
"baseBranches": ["dev", "v3"], "baseBranches": ["v2", "v3"],
"packageRules": [{ "packageRules": [{
"matchBaseBranches": ["v3"], "matchBaseBranches": ["v3"],
"labels": ["v3"] "labels": ["v3"]

View File

@@ -53,7 +53,7 @@ export default defineNuxtModule<ModuleOptions>({
name: 'ui', name: 'ui',
configKey: 'ui', configKey: 'ui',
compatibility: { compatibility: {
nuxt: '>=3.13.1' nuxt: '>=3.16.0'
}, },
docs: 'https://ui3.nuxt.dev/getting-started/installation/nuxt' docs: 'https://ui3.nuxt.dev/getting-started/installation/nuxt'
}, },

View File

@@ -1,12 +1,11 @@
<script lang="ts"> <script lang="ts">
import type { ConfigProviderProps, TooltipProviderProps } from 'reka-ui' import type { ConfigProviderProps, TooltipProviderProps } from 'reka-ui'
import { localeContextInjectionKey } from '../composables/useLocale' import type { ToasterProps, Locale, Messages } from '../types'
import type { ToasterProps, Locale } from '../types'
export interface AppProps extends Omit<ConfigProviderProps, 'useId' | 'dir' | 'locale'> { export interface AppProps<T extends Messages = Messages> extends Omit<ConfigProviderProps, 'useId' | 'dir' | 'locale'> {
tooltip?: TooltipProviderProps tooltip?: TooltipProviderProps
toaster?: ToasterProps | null toaster?: ToasterProps | null
locale?: Locale locale?: Locale<T>
} }
export interface AppSlots { export interface AppSlots {
@@ -18,14 +17,15 @@ export default {
} }
</script> </script>
<script setup lang="ts"> <script setup lang="ts" generic="T extends Messages = Messages">
import { toRef, useId, provide } from 'vue' import { toRef, useId, provide } from 'vue'
import { ConfigProvider, TooltipProvider, useForwardProps } from 'reka-ui' import { ConfigProvider, TooltipProvider, useForwardProps } from 'reka-ui'
import { reactivePick } from '@vueuse/core' import { reactivePick } from '@vueuse/core'
import { localeContextInjectionKey } from '../composables/useLocale'
import UToaster from './Toaster.vue' import UToaster from './Toaster.vue'
import UOverlayProvider from './OverlayProvider.vue' import UOverlayProvider from './OverlayProvider.vue'
const props = defineProps<AppProps>() const props = defineProps<AppProps<T>>()
defineSlots<AppSlots>() defineSlots<AppSlots>()
const configProviderProps = useForwardProps(reactivePick(props, 'scrollBody')) const configProviderProps = useForwardProps(reactivePick(props, 'scrollBody'))

View File

@@ -60,6 +60,7 @@ import { pickLinkProps } from '../utils/link'
import UIcon from './Icon.vue' import UIcon from './Icon.vue'
import UAvatar from './Avatar.vue' import UAvatar from './Avatar.vue'
import ULink from './Link.vue' import ULink from './Link.vue'
import ULinkBase from './LinkBase.vue'
const props = withDefaults(defineProps<ButtonProps>(), { const props = withDefaults(defineProps<ButtonProps>(), {
active: undefined, active: undefined,

View File

@@ -29,7 +29,7 @@ export interface FormEmits<T extends object> {
} }
export interface FormSlots { export interface FormSlots {
default(props?: {}): any default(props?: { errors: FormError[] }): any
} }
</script> </script>
@@ -69,7 +69,7 @@ onMounted(async () => {
nestedForms.value.set(event.formId, { validate: event.validate }) nestedForms.value.set(event.formId, { validate: event.validate })
} else if (event.type === 'detach') { } else if (event.type === 'detach') {
nestedForms.value.delete(event.formId) nestedForms.value.delete(event.formId)
} else if (props.validateOn?.includes(event.type)) { } else if (props.validateOn?.includes(event.type) && !loading.value) {
if (event.type !== 'input') { if (event.type !== 'input') {
await _validate({ name: event.name, silent: true, nested: false }) await _validate({ name: event.name, silent: true, nested: false })
} else if (event.eager || blurredFields.has(event.name)) { } else if (event.eager || blurredFields.has(event.name)) {
@@ -121,7 +121,7 @@ const blurredFields = new Set<keyof T>()
function resolveErrorIds(errs: FormError[]): FormErrorWithId[] { function resolveErrorIds(errs: FormError[]): FormErrorWithId[] {
return errs.map(err => ({ return errs.map(err => ({
...err, ...err,
id: inputs.value[err.name]?.id id: err?.name ? inputs.value[err.name]?.id : undefined
})) }))
} }
@@ -159,12 +159,12 @@ async function _validate(opts: { name?: keyof T | (keyof T)[], silent?: boolean,
if (names) { if (names) {
const otherErrors = errors.value.filter(error => !names.some((name) => { const otherErrors = errors.value.filter(error => !names.some((name) => {
const pattern = inputs.value?.[name]?.pattern const pattern = inputs.value?.[name]?.pattern
return name === error.name || (pattern && error.name.match(pattern)) return name === error.name || (pattern && error.name?.match(pattern))
})) }))
const pathErrors = (await getErrors()).filter(error => names.some((name) => { const pathErrors = (await getErrors()).filter(error => names.some((name) => {
const pattern = inputs.value?.[name]?.pattern const pattern = inputs.value?.[name]?.pattern
return name === error.name || (pattern && error.name.match(pattern)) return name === error.name || (pattern && error.name?.match(pattern))
})) }))
errors.value = otherErrors.concat(pathErrors) errors.value = otherErrors.concat(pathErrors)
@@ -269,6 +269,6 @@ defineExpose<Form<T>>({
:class="form({ class: props.class })" :class="form({ class: props.class })"
@submit.prevent="onSubmitWrapper" @submit.prevent="onSubmitWrapper"
> >
<slot /> <slot :errors="errors" />
</component> </component>
</template> </template>

View File

@@ -63,7 +63,7 @@ const ui = computed(() => formField({
const formErrors = inject<Ref<FormError[]> | null>('form-errors', null) const formErrors = inject<Ref<FormError[]> | null>('form-errors', null)
const error = computed(() => props.error || formErrors?.value?.find(error => error.name === props.name || (props.errorPattern && error.name.match(props.errorPattern)))?.message) const error = computed(() => props.error || formErrors?.value?.find(error => error.name && (error.name === props.name || (props.errorPattern && error.name.match(props.errorPattern))))?.message)
const id = ref(useId()) const id = ref(useId())
// Copies id's initial value to bind aria-attributes such as aria-describedby. // Copies id's initial value to bind aria-attributes such as aria-describedby.

View File

@@ -82,7 +82,7 @@ const props = withDefaults(defineProps<InputProps>(), {
const emits = defineEmits<InputEmits>() const emits = defineEmits<InputEmits>()
const slots = defineSlots<InputSlots>() const slots = defineSlots<InputSlots>()
const [modelValue, modelModifiers] = defineModel<string | number>() const [modelValue, modelModifiers] = defineModel<string | number | null>()
const { emitFormBlur, emitFormInput, emitFormChange, size: formGroupSize, color, id, name, highlight, disabled, emitFormFocus, ariaAttrs } = useFormField<InputProps>(props, { deferInputValidation: true }) const { emitFormBlur, emitFormInput, emitFormChange, size: formGroupSize, color, id, name, highlight, disabled, emitFormFocus, ariaAttrs } = useFormField<InputProps>(props, { deferInputValidation: true })
const { orientation, size: buttonGroupSize } = useButtonGroup<InputProps>(props) const { orientation, size: buttonGroupSize } = useButtonGroup<InputProps>(props)
@@ -111,15 +111,19 @@ function autoFocus() {
} }
// Custom function to handle the v-model properties // Custom function to handle the v-model properties
function updateInput(value: string) { function updateInput(value: string | null) {
if (modelModifiers.trim) { if (modelModifiers.trim) {
value = value.trim() value = value?.trim() ?? null
} }
if (modelModifiers.number || props.type === 'number') { if (modelModifiers.number || props.type === 'number') {
value = looseToNumber(value) value = looseToNumber(value)
} }
if (modelModifiers.nullify) {
value ||= null
}
modelValue.value = value modelValue.value = value
emitFormInput() emitFormInput()
} }

View File

@@ -1,4 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { LinkProps } from '../types'
export interface LinkBaseProps { export interface LinkBaseProps {
as?: string as?: string
type?: string type?: string
@@ -6,8 +8,8 @@ export interface LinkBaseProps {
onClick?: ((e: MouseEvent) => void | Promise<void>) | Array<((e: MouseEvent) => void | Promise<void>)> onClick?: ((e: MouseEvent) => void | Promise<void>) | Array<((e: MouseEvent) => void | Promise<void>)>
href?: string href?: string
navigate?: (e: MouseEvent) => void navigate?: (e: MouseEvent) => void
rel?: string target?: LinkProps['target']
target?: string rel?: LinkProps['rel']
isExternal?: boolean isExternal?: boolean
} }
</script> </script>

View File

@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { PaginationRootProps, PaginationRootEmits } from 'reka-ui' import type { PaginationRootProps, PaginationRootEmits } from 'reka-ui'
import type { AppConfig } from '@nuxt/schema' import type { AppConfig } from '@nuxt/schema'
import type { RouteLocationRaw } from '#vue-router'
import _appConfig from '#build/app.config' import _appConfig from '#build/app.config'
import theme from '#build/ui/pagination' import theme from '#build/ui/pagination'
import { tv } from '../utils/tv' import { tv } from '../utils/tv'
@@ -78,7 +77,7 @@ export interface PaginationProps extends Partial<Pick<PaginationRootProps, 'defa
* A function to render page controls as links. * A function to render page controls as links.
* @param page The page number to navigate to. * @param page The page number to navigate to.
*/ */
to?: (page: number) => RouteLocationRaw to?: (page: number) => ButtonProps['to']
class?: any class?: any
ui?: Partial<typeof pagination.slots> ui?: Partial<typeof pagination.slots>
} }

View File

@@ -74,7 +74,7 @@ const props = withDefaults(defineProps<TextareaProps>(), {
defineSlots<TextareaSlots>() defineSlots<TextareaSlots>()
const emits = defineEmits<TextareaEmits>() const emits = defineEmits<TextareaEmits>()
const [modelValue, modelModifiers] = defineModel<string | number>() const [modelValue, modelModifiers] = defineModel<string | number | null>()
const { emitFormFocus, emitFormBlur, emitFormInput, emitFormChange, size, color, id, name, highlight, disabled, ariaAttrs } = useFormField<TextareaProps>(props, { deferInputValidation: true }) const { emitFormFocus, emitFormBlur, emitFormInput, emitFormChange, size, color, id, name, highlight, disabled, ariaAttrs } = useFormField<TextareaProps>(props, { deferInputValidation: true })
@@ -94,15 +94,19 @@ function autoFocus() {
} }
// Custom function to handle the v-model properties // Custom function to handle the v-model properties
function updateInput(value: string) { function updateInput(value: string | null) {
if (modelModifiers.trim) { if (modelModifiers.trim) {
value = value.trim() value = value?.trim() ?? null
} }
if (modelModifiers.number) { if (modelModifiers.number) {
value = looseToNumber(value) value = looseToNumber(value)
} }
if (modelModifiers.nullify) {
value ||= null
}
modelValue.value = value modelValue.value = value
emitFormInput() emitFormInput()
} }

View File

@@ -1,13 +1,13 @@
import { defu } from 'defu' import { defu } from 'defu'
import type { Locale, Direction, Messages } from '../types/locale' import type { Locale, Direction } from '../types/locale'
interface DefineLocaleOptions { interface DefineLocaleOptions<M> {
name: string name: string
code: string code: string
dir?: Direction dir?: Direction
messages: Messages messages: M
} }
export function defineLocale(options: DefineLocaleOptions): Locale { export function defineLocale<M>(options: DefineLocaleOptions<M>): Locale<M> {
return defu<DefineLocaleOptions, [{ dir: Direction }]>(options, { dir: 'ltr' }) return defu<DefineLocaleOptions<M>, [{ dir: Direction }]>(options, { dir: 'ltr' })
} }

View File

@@ -1,16 +1,16 @@
import { computed, inject, ref } from 'vue' import { computed, inject, toRef } from 'vue'
import type { InjectionKey, Ref } from 'vue' import type { InjectionKey, Ref } from 'vue'
import type { Locale } from '../types/locale' import { createSharedComposable } from '@vueuse/core'
import type { Locale, Messages } from '../types/locale'
import { buildLocaleContext } from '../utils/locale' import { buildLocaleContext } from '../utils/locale'
import en from '../locale/en' import en from '../locale/en'
import { createSharedComposable } from '@vueuse/core'
export const localeContextInjectionKey: InjectionKey<Ref<Locale | undefined>> = Symbol('nuxt-ui.locale-context') export const localeContextInjectionKey: InjectionKey<Ref<Locale<unknown> | undefined>> = Symbol('nuxt-ui.locale-context')
const _useLocale = (localeOverrides?: Ref<Locale | undefined>) => { const _useLocale = (localeOverrides?: Ref<Locale<Messages> | undefined>) => {
const locale = localeOverrides || inject(localeContextInjectionKey, ref())! const locale = localeOverrides || toRef(inject<Locale<Messages>>(localeContextInjectionKey))
return buildLocaleContext(computed(() => locale.value || en)) return buildLocaleContext<Messages>(computed(() => locale.value || en))
} }
export const useLocale = createSharedComposable(_useLocale) export const useLocale = createSharedComposable(_useLocale)

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'العربية', name: 'العربية',
code: 'ar', code: 'ar',
dir: 'rtl', dir: 'rtl',

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Azərbaycanca', name: 'Azərbaycanca',
code: 'az', code: 'az',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'বাংলা', name: 'বাংলা',
code: 'bn', code: 'bn',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Čeština', name: 'Čeština',
code: 'cs', code: 'cs',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Danish', name: 'Danish',
code: 'da', code: 'da',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Deutsch', name: 'Deutsch',
code: 'de', code: 'de',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Ελληνικά', name: 'Ελληνικά',
code: 'el', code: 'el',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'English', name: 'English',
code: 'en', code: 'en',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Español', name: 'Español',
code: 'es', code: 'es',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Eesti', name: 'Eesti',
code: 'et', code: 'et',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'فارسی', name: 'فارسی',
code: 'fa-IR', code: 'fa-IR',
dir: 'rtl', dir: 'rtl',

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Suomeksi', name: 'Suomeksi',
code: 'fi', code: 'fi',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Français', name: 'Français',
code: 'fr', code: 'fr',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Hebrew', name: 'Hebrew',
code: 'he', code: 'he',
dir: 'rtl', dir: 'rtl',

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Hindi', name: 'Hindi',
code: 'hi', code: 'hi',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Magyar', name: 'Magyar',
code: 'hu', code: 'hu',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Indonesia', name: 'Indonesia',
code: 'id', code: 'id',
messages: { messages: {

View File

@@ -5,13 +5,15 @@ export { default as cs } from './cs'
export { default as da } from './da' export { default as da } from './da'
export { default as de } from './de' export { default as de } from './de'
export { default as el } from './el' export { default as el } from './el'
export { default as et } from './et'
export { default as en } from './en' export { default as en } from './en'
export { default as es } from './es' export { default as es } from './es'
export { default as et } from './et'
export { default as fa_ir } from './fa_ir' export { default as fa_ir } from './fa_ir'
export { default as fi } from './fi' export { default as fi } from './fi'
export { default as fr } from './fr' export { default as fr } from './fr'
export { default as he } from './he'
export { default as hi } from './hi' export { default as hi } from './hi'
export { default as hu } from './hu'
export { default as id } from './id' export { default as id } from './id'
export { default as it } from './it' export { default as it } from './it'
export { default as ja } from './ja' export { default as ja } from './ja'
@@ -31,4 +33,3 @@ export { default as uk } from './uk'
export { default as vi } from './vi' export { default as vi } from './vi'
export { default as zh_cn } from './zh_cn' export { default as zh_cn } from './zh_cn'
export { default as zh_tw } from './zh_tw' export { default as zh_tw } from './zh_tw'
export { default as he } from './he'

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Italiano', name: 'Italiano',
code: 'it', code: 'it',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: '日本語', name: '日本語',
code: 'ja', code: 'ja',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'ភាសាខ្មែរ', name: 'ភាសាខ្មែរ',
code: 'km', code: 'km',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: '한국어', name: '한국어',
code: 'ko', code: 'ko',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Norsk Bokmål', name: 'Norsk Bokmål',
code: 'nb-NO', code: 'nb-NO',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Nederlands', name: 'Nederlands',
code: 'nl', code: 'nl',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Polski', name: 'Polski',
code: 'pl', code: 'pl',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Português', name: 'Português',
code: 'pt', code: 'pt',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Português (Brasil)', name: 'Português (Brasil)',
code: 'pt-BR', code: 'pt-BR',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Русский', name: 'Русский',
code: 'ru', code: 'ru',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Slovenčina', name: 'Slovenčina',
code: 'sk', code: 'sk',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Svenska', name: 'Svenska',
code: 'sv', code: 'sv',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'ไทย', name: 'ไทย',
code: 'th', code: 'th',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Türkçe', name: 'Türkçe',
code: 'tr', code: 'tr',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Українська', name: 'Українська',
code: 'uk', code: 'uk',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: 'Tiếng Việt', name: 'Tiếng Việt',
code: 'vi', code: 'vi',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: '简体中文', name: '简体中文',
code: 'zh-CN', code: 'zh-CN',
messages: { messages: {

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale' import { defineLocale } from '../composables/defineLocale'
export default defineLocale({ export default defineLocale<Messages>({
name: '繁體中文', name: '繁體中文',
code: 'zh-TW', code: 'zh-TW',
messages: { messages: {

View File

@@ -55,5 +55,7 @@ export default defineNuxtPlugin(() => {
}] }]
} }
useHead(headData) if (!nuxtApp.isVue) {
useHead(headData)
}
}) })

View File

@@ -36,7 +36,7 @@ export type FormSchema<T extends object> =
export type FormInputEvents = 'input' | 'blur' | 'change' | 'focus' export type FormInputEvents = 'input' | 'blur' | 'change' | 'focus'
export interface FormError<P extends string = string> { export interface FormError<P extends string = string> {
name: P name?: P
message: string message: string
} }

View File

@@ -50,9 +50,9 @@ export type Messages = {
export type Direction = 'ltr' | 'rtl' export type Direction = 'ltr' | 'rtl'
export type Locale = { export type Locale<M> = {
name: string name: string
code: string code: string
dir: Direction dir: Direction
messages: Messages messages: M
} }

View File

@@ -6,19 +6,19 @@ import { get } from './index'
export type TranslatorOption = Record<string, string | number> export type TranslatorOption = Record<string, string | number>
export type Translator = (path: string, option?: TranslatorOption) => string export type Translator = (path: string, option?: TranslatorOption) => string
export type LocaleContext = { export type LocaleContext<M> = {
locale: Ref<Locale> locale: Ref<Locale<M>>
lang: Ref<string> lang: Ref<string>
dir: Ref<Direction> dir: Ref<Direction>
code: Ref<string> code: Ref<string>
t: Translator t: Translator
} }
export function buildTranslator(locale: MaybeRef<Locale>): Translator { export function buildTranslator<M>(locale: MaybeRef<Locale<M>>): Translator {
return (path, option) => translate(path, option, unref(locale)) return (path, option) => translate(path, option, unref(locale))
} }
export function translate(path: string, option: undefined | TranslatorOption, locale: Locale): string { export function translate<M>(path: string, option: undefined | TranslatorOption, locale: Locale<M>): string {
const prop: string = get(locale, `messages.${path}`, path) const prop: string = get(locale, `messages.${path}`, path)
return prop.replace( return prop.replace(
@@ -27,11 +27,11 @@ export function translate(path: string, option: undefined | TranslatorOption, lo
) )
} }
export function buildLocaleContext(locale: MaybeRef<Locale>): LocaleContext { export function buildLocaleContext<M>(locale: MaybeRef<Locale<M>>): LocaleContext<M> {
const lang = computed(() => unref(locale).name) const lang = computed(() => unref(locale).name)
const code = computed(() => unref(locale).code) const code = computed(() => unref(locale).code)
const dir = computed(() => unref(locale).dir) const dir = computed(() => unref(locale).dir)
const localeRef = isRef(locale) ? locale : ref(locale) const localeRef = isRef(locale) ? locale : ref(locale) as Ref<Locale<M>>
return { return {
lang, lang,

View File

@@ -1,8 +1,9 @@
import { createHead, setHeadInjectionHandler } from '@unhead/vue' import { createHead } from '@unhead/vue/client'
import type { Plugin } from 'vue' import type { Plugin } from 'vue'
export default { export default {
install() { install(app) {
setHeadInjectionHandler(() => createHead()) const head = createHead()
app.use(head)
} }
} satisfies Plugin } satisfies Plugin

View File

@@ -9,6 +9,7 @@ export { useHead } from '@unhead/vue'
export { useRoute, useRouter } from 'vue-router' export { useRoute, useRouter } from 'vue-router'
export { defineShortcuts } from '../composables/defineShortcuts' export { defineShortcuts } from '../composables/defineShortcuts'
export { defineLocale } from '../composables/defineLocale'
export { useLocale } from '../composables/useLocale' export { useLocale } from '../composables/useLocale'
export const useColorMode = () => { export const useColorMode = () => {
@@ -60,6 +61,7 @@ export const useState = <T>(key: string, init: () => T): Ref<T> => {
export function useNuxtApp() { export function useNuxtApp() {
return { return {
isHydrating: true, isHydrating: true,
isVue: true,
payload: { serverRendered: false } payload: { serverRendered: false }
} }
} }

View File

@@ -122,6 +122,10 @@ type AppConfigUI = {
declare module '@nuxt/schema' { declare module '@nuxt/schema' {
interface AppConfigInput { interface AppConfigInput {
/**
* Nuxt UI theme configuration
* @see https://ui3.nuxt.dev/getting-started/theme#customize-theme
*/
ui?: AppConfigUI ui?: AppConfigUI
} }
} }

View File

@@ -52,7 +52,8 @@ describe('Input', () => {
it.each([ it.each([
['with .trim modifier', { props: { modelModifiers: { trim: true } } }, { input: 'input ', expected: 'input' }], ['with .trim modifier', { props: { modelModifiers: { trim: true } } }, { input: 'input ', expected: 'input' }],
['with .number modifier', { props: { modelModifiers: { number: true } } }, { input: '42', expected: 42 }], ['with .number modifier', { props: { modelModifiers: { number: true } } }, { input: '42', expected: 42 }],
['with .lazy modifier', { props: { modelModifiers: { lazy: true } } }, { input: 'input', expected: 'input' }] ['with .lazy modifier', { props: { modelModifiers: { lazy: true } } }, { input: 'input', expected: 'input' }],
['with .nullify modifier', { props: { modelModifiers: { nullify: true } } }, { input: '', expected: null }]
])('%s works', async (_nameOrHtml: string, options: { props?: any, slots?: any }, spec: { input: any, expected: any }) => { ])('%s works', async (_nameOrHtml: string, options: { props?: any, slots?: any }, spec: { input: any, expected: any }) => {
const wrapper = mount(Input, { const wrapper = mount(Input, {
...options ...options

View File

@@ -35,7 +35,8 @@ describe('Textarea', () => {
it.each([ it.each([
['with .trim modifier', { props: { modelModifiers: { trim: true } } }, { input: 'input ', expected: 'input' }], ['with .trim modifier', { props: { modelModifiers: { trim: true } } }, { input: 'input ', expected: 'input' }],
['with .number modifier', { props: { modelModifiers: { number: true } } }, { input: '42', expected: 42 }], ['with .number modifier', { props: { modelModifiers: { number: true } } }, { input: '42', expected: 42 }],
['with .lazy modifier', { props: { modelModifiers: { lazy: true } } }, { input: 'input', expected: 'input' }] ['with .lazy modifier', { props: { modelModifiers: { lazy: true } } }, { input: 'input', expected: 'input' }],
['with .nullify modifier', { props: { modelModifiers: { nullify: true } } }, { input: '', expected: null }]
])('%s works', async (_nameOrHtml: string, options: { props?: any, slots?: any }, spec: { input: any, expected: any }) => { ])('%s works', async (_nameOrHtml: string, options: { props?: any, slots?: any }, spec: { input: any, expected: any }) => {
const wrapper = mount(Textarea, { const wrapper = mount(Textarea, {
...options ...options

View File

@@ -252,13 +252,13 @@ exports[`CommandPalette > renders with defaultValue correctly 1`] = `
exports[`CommandPalette > renders with disabled correctly 1`] = ` exports[`CommandPalette > renders with disabled correctly 1`] = `
"<div dir="ltr" data-disabled="" class="flex flex-col min-h-0 min-w-0 divide-y divide-(--ui-border)"> "<div dir="ltr" data-disabled="" class="flex flex-col min-h-0 min-w-0 divide-y divide-(--ui-border)">
<div class="relative inline-flex items-center [&amp;>input]:h-12"><input type="text" placeholder="Type a command or search..." class="w-full rounded-[calc(var(--ui-radius)*1.5)] border-0 placeholder:text-(--ui-text-dimmed) focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 transition-colors px-3 py-2 text-sm gap-2 text-(--ui-text-highlighted) bg-transparent ps-10" disabled="" autocomplete="off" data-disabled="" aria-disabled="true" value="" aria-activedescendant="reka-listbox-item-v-0-0-1"><span class="absolute inset-y-0 start-0 flex items-center ps-3"><span class="iconify i-lucide:search shrink-0 text-(--ui-text-dimmed) size-5" aria-hidden="true"></span></span> <div class="relative inline-flex items-center [&amp;>input]:h-12"><input type="text" placeholder="Type a command or search..." class="w-full rounded-[calc(var(--ui-radius)*1.5)] border-0 placeholder:text-(--ui-text-dimmed) focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 transition-colors px-3 py-2 text-sm gap-2 text-(--ui-text-highlighted) bg-transparent ps-10" disabled="" autocomplete="off" data-disabled="" aria-disabled="true" value=""><span class="absolute inset-y-0 start-0 flex items-center ps-3"><span class="iconify i-lucide:search shrink-0 text-(--ui-text-dimmed) size-5" aria-hidden="true"></span></span>
<!--v-if--> <!--v-if-->
</div> </div>
<div role="listbox" aria-orientation="vertical" aria-multiselectable="false" data-orientation="vertical" class="relative overflow-hidden flex flex-col"> <div role="listbox" aria-orientation="vertical" aria-multiselectable="false" data-orientation="vertical" class="relative overflow-hidden flex flex-col">
<div class="relative divide-y divide-(--ui-border) scroll-py-1 overflow-y-auto flex-1 focus:outline-none"> <div class="relative divide-y divide-(--ui-border) scroll-py-1 overflow-y-auto flex-1 focus:outline-none">
<div role="group" aria-labelledby="reka-listbox-group-v-0-0-0" class="p-1 isolate"> <div role="group" aria-labelledby="reka-listbox-group-v-0-0-0" class="p-1 isolate">
<!--v-if--><button type="button" id="reka-listbox-item-v-0-0-1" role="option" tabindex="-1" aria-selected="false" data-disabled="" data-state="unchecked" data-reka-collection-item="" class="group relative w-full flex items-center gap-2 px-2 py-1.5 text-sm select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-[calc(var(--ui-radius)*1.5)] data-disabled:cursor-not-allowed data-disabled:opacity-75 text-(--ui-text-highlighted) before:bg-(--ui-bg-elevated)" data-highlighted=""><span class="iconify i-lucide:file-plus shrink-0 size-5 text-(--ui-text)" aria-hidden="true"></span><span class="truncate space-x-1 rtl:space-x-reverse text-(--ui-text-dimmed)"><!--v-if--><span class="text-(--ui-text-highlighted) [&amp;>mark]:text-(--ui-bg) [&amp;>mark]:bg-(--ui-primary)">Add new file</span><span class="text-(--ui-text-dimmed) [&amp;>mark]:text-(--ui-bg) [&amp;>mark]:bg-(--ui-primary)">Create a new file in the current directory or workspace.</span></span><span class="ms-auto inline-flex gap-1.5 items-center"><span class="hidden lg:inline-flex items-center shrink-0 gap-0.5"><kbd class="inline-flex items-center justify-center px-1 rounded-(--ui-radius) font-medium font-sans bg-(--ui-bg) text-(--ui-text-highlighted) ring ring-inset ring-(--ui-border-accented) h-5 min-w-[20px] text-[11px]">⊞</kbd><kbd class="inline-flex items-center justify-center px-1 rounded-(--ui-radius) font-medium font-sans bg-(--ui-bg) text-(--ui-text-highlighted) ring ring-inset ring-(--ui-border-accented) h-5 min-w-[20px] text-[11px]">N</kbd></span> <!--v-if--><button type="button" id="reka-listbox-item-v-0-0-1" role="option" tabindex="-1" aria-selected="false" data-disabled="" data-state="unchecked" data-reka-collection-item="" class="group relative w-full flex items-center gap-2 px-2 py-1.5 text-sm select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-[calc(var(--ui-radius)*1.5)] data-disabled:cursor-not-allowed data-disabled:opacity-75 text-(--ui-text-highlighted) before:bg-(--ui-bg-elevated)"><span class="iconify i-lucide:file-plus shrink-0 size-5 text-(--ui-text)" aria-hidden="true"></span><span class="truncate space-x-1 rtl:space-x-reverse text-(--ui-text-dimmed)"><!--v-if--><span class="text-(--ui-text-highlighted) [&amp;>mark]:text-(--ui-bg) [&amp;>mark]:bg-(--ui-primary)">Add new file</span><span class="text-(--ui-text-dimmed) [&amp;>mark]:text-(--ui-bg) [&amp;>mark]:bg-(--ui-primary)">Create a new file in the current directory or workspace.</span></span><span class="ms-auto inline-flex gap-1.5 items-center"><span class="hidden lg:inline-flex items-center shrink-0 gap-0.5"><kbd class="inline-flex items-center justify-center px-1 rounded-(--ui-radius) font-medium font-sans bg-(--ui-bg) text-(--ui-text-highlighted) ring ring-inset ring-(--ui-border-accented) h-5 min-w-[20px] text-[11px]">⊞</kbd><kbd class="inline-flex items-center justify-center px-1 rounded-(--ui-radius) font-medium font-sans bg-(--ui-bg) text-(--ui-text-highlighted) ring ring-inset ring-(--ui-border-accented) h-5 min-w-[20px] text-[11px]">N</kbd></span>
<!----></span> <!----></span>
</button><button type="button" id="reka-listbox-item-v-0-0-2" role="option" tabindex="-1" aria-selected="false" data-disabled="" data-state="unchecked" data-reka-collection-item="" class="group relative w-full flex items-center gap-2 px-2 py-1.5 text-sm select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-[calc(var(--ui-radius)*1.5)] data-disabled:cursor-not-allowed data-disabled:opacity-75 text-(--ui-text) data-highlighted:text-(--ui-text-highlighted) data-highlighted:before:bg-(--ui-bg-elevated)/50 transition-colors before:transition-colors"><span class="iconify i-lucide:folder-plus shrink-0 size-5 text-(--ui-text-dimmed) group-data-highlighted:text-(--ui-text) transition-colors" aria-hidden="true"></span><span class="truncate space-x-1 rtl:space-x-reverse text-(--ui-text-dimmed)"><!--v-if--><span class="text-(--ui-text-highlighted) [&amp;>mark]:text-(--ui-bg) [&amp;>mark]:bg-(--ui-primary)">Add new folder</span><span class="text-(--ui-text-dimmed) [&amp;>mark]:text-(--ui-bg) [&amp;>mark]:bg-(--ui-primary)">Create a new folder in the current directory or workspace.</span></span><span class="ms-auto inline-flex gap-1.5 items-center"><span class="hidden lg:inline-flex items-center shrink-0 gap-0.5"><kbd class="inline-flex items-center justify-center px-1 rounded-(--ui-radius) font-medium font-sans bg-(--ui-bg) text-(--ui-text-highlighted) ring ring-inset ring-(--ui-border-accented) h-5 min-w-[20px] text-[11px]">⊞</kbd><kbd class="inline-flex items-center justify-center px-1 rounded-(--ui-radius) font-medium font-sans bg-(--ui-bg) text-(--ui-text-highlighted) ring ring-inset ring-(--ui-border-accented) h-5 min-w-[20px] text-[11px]">F</kbd></span> </button><button type="button" id="reka-listbox-item-v-0-0-2" role="option" tabindex="-1" aria-selected="false" data-disabled="" data-state="unchecked" data-reka-collection-item="" class="group relative w-full flex items-center gap-2 px-2 py-1.5 text-sm select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-[calc(var(--ui-radius)*1.5)] data-disabled:cursor-not-allowed data-disabled:opacity-75 text-(--ui-text) data-highlighted:text-(--ui-text-highlighted) data-highlighted:before:bg-(--ui-bg-elevated)/50 transition-colors before:transition-colors"><span class="iconify i-lucide:folder-plus shrink-0 size-5 text-(--ui-text-dimmed) group-data-highlighted:text-(--ui-text) transition-colors" aria-hidden="true"></span><span class="truncate space-x-1 rtl:space-x-reverse text-(--ui-text-dimmed)"><!--v-if--><span class="text-(--ui-text-highlighted) [&amp;>mark]:text-(--ui-bg) [&amp;>mark]:bg-(--ui-primary)">Add new folder</span><span class="text-(--ui-text-dimmed) [&amp;>mark]:text-(--ui-bg) [&amp;>mark]:bg-(--ui-primary)">Create a new folder in the current directory or workspace.</span></span><span class="ms-auto inline-flex gap-1.5 items-center"><span class="hidden lg:inline-flex items-center shrink-0 gap-0.5"><kbd class="inline-flex items-center justify-center px-1 rounded-(--ui-radius) font-medium font-sans bg-(--ui-bg) text-(--ui-text-highlighted) ring ring-inset ring-(--ui-border-accented) h-5 min-w-[20px] text-[11px]">⊞</kbd><kbd class="inline-flex items-center justify-center px-1 rounded-(--ui-radius) font-medium font-sans bg-(--ui-bg) text-(--ui-text-highlighted) ring ring-inset ring-(--ui-border-accented) h-5 min-w-[20px] text-[11px]">F</kbd></span>
<!----></span> <!----></span>

View File

@@ -2,6 +2,6 @@
exports[`Container > renders with as correctly 1`] = `"<article class="max-w-(--ui-container) mx-auto px-4 sm:px-6 lg:px-8"></article>"`; exports[`Container > renders with as correctly 1`] = `"<article class="max-w-(--ui-container) mx-auto px-4 sm:px-6 lg:px-8"></article>"`;
exports[`Container > renders with class correctly 1`] = `"<div class="max-w-(--ui-container) mx-auto px-4 sm:px-6 lg:px-8 max-w-5xl"></div>"`; exports[`Container > renders with class correctly 1`] = `"<div class="mx-auto px-4 sm:px-6 lg:px-8 max-w-5xl"></div>"`;
exports[`Container > renders with default slot correctly 1`] = `"<div class="max-w-(--ui-container) mx-auto px-4 sm:px-6 lg:px-8">Default slot</div>"`; exports[`Container > renders with default slot correctly 1`] = `"<div class="max-w-(--ui-container) mx-auto px-4 sm:px-6 lg:px-8">Default slot</div>"`;

View File

@@ -2,6 +2,6 @@
exports[`Container > renders with as correctly 1`] = `"<article class="max-w-(--ui-container) mx-auto px-4 sm:px-6 lg:px-8"></article>"`; exports[`Container > renders with as correctly 1`] = `"<article class="max-w-(--ui-container) mx-auto px-4 sm:px-6 lg:px-8"></article>"`;
exports[`Container > renders with class correctly 1`] = `"<div class="max-w-(--ui-container) mx-auto px-4 sm:px-6 lg:px-8 max-w-5xl"></div>"`; exports[`Container > renders with class correctly 1`] = `"<div class="mx-auto px-4 sm:px-6 lg:px-8 max-w-5xl"></div>"`;
exports[`Container > renders with default slot correctly 1`] = `"<div class="max-w-(--ui-container) mx-auto px-4 sm:px-6 lg:px-8">Default slot</div>"`; exports[`Container > renders with default slot correctly 1`] = `"<div class="max-w-(--ui-container) mx-auto px-4 sm:px-6 lg:px-8">Default slot</div>"`;

View File

@@ -223,7 +223,7 @@ exports[`InputMenu > renders with defaultValue correctly 1`] = `
`; `;
exports[`InputMenu > renders with disabled correctly 1`] = ` exports[`InputMenu > renders with disabled correctly 1`] = `
"<div dir="ltr" data-disabled="" class="relative inline-flex items-center" style="pointer-events: auto;"><input disabled="" data-disabled="" aria-disabled="true" type="text" aria-expanded="true" aria-controls="" aria-autocomplete="list" role="combobox" autocomplete="false" class="rounded-[calc(var(--ui-radius)*1.5)] transition-colors px-2.5 py-1.5 text-sm gap-1.5 text-(--ui-text-highlighted) bg-(--ui-bg) ring ring-inset ring-(--ui-border-accented) w-full border-0 placeholder:text-(--ui-text-dimmed) focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-(--ui-primary) pe-9" value="" aria-activedescendant="reka-combobox-item-v-0-0-3"> "<div dir="ltr" data-disabled="" class="relative inline-flex items-center" style="pointer-events: auto;"><input disabled="" data-disabled="" aria-disabled="true" type="text" aria-expanded="true" aria-controls="" aria-autocomplete="list" role="combobox" autocomplete="false" class="rounded-[calc(var(--ui-radius)*1.5)] transition-colors px-2.5 py-1.5 text-sm gap-1.5 text-(--ui-text-highlighted) bg-(--ui-bg) ring ring-inset ring-(--ui-border-accented) w-full border-0 placeholder:text-(--ui-text-dimmed) focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-(--ui-primary) pe-9" value="">
<!--v-if--><button disabled="" type="button" tabindex="-1" aria-label="Show popup" aria-haspopup="listbox" aria-expanded="true" aria-controls="" data-state="open" data-disabled="" aria-disabled="true" class="group absolute inset-y-0 end-0 flex items-center disabled:cursor-not-allowed disabled:opacity-75 pe-2.5"><span class="iconify i-lucide:chevron-down shrink-0 text-(--ui-text-dimmed) size-5" aria-hidden="true"></span></button> <!--v-if--><button disabled="" type="button" tabindex="-1" aria-label="Show popup" aria-haspopup="listbox" aria-expanded="true" aria-controls="" data-state="open" data-disabled="" aria-disabled="true" class="group absolute inset-y-0 end-0 flex items-center disabled:cursor-not-allowed disabled:opacity-75 pe-2.5"><span class="iconify i-lucide:chevron-down shrink-0 text-(--ui-text-dimmed) size-5" aria-hidden="true"></span></button>
<!--teleport start--> <!--teleport start-->
<div data-reka-popper-content-wrapper="" style="position: fixed; left: 0px; top: 0px; transform: translate(0, -200%); min-width: max-content;"> <div data-reka-popper-content-wrapper="" style="position: fixed; left: 0px; top: 0px; transform: translate(0, -200%); min-width: max-content;">
@@ -232,7 +232,7 @@ exports[`InputMenu > renders with disabled correctly 1`] = `
<div class="divide-y divide-(--ui-border) scroll-py-1" data-reka-combobox-viewport="" role="presentation" style="position: relative; overflow: auto; flex-grow: 1; flex-shrink: 1; flex-basis: 0%;"> <div class="divide-y divide-(--ui-border) scroll-py-1" data-reka-combobox-viewport="" role="presentation" style="position: relative; overflow: auto; flex-grow: 1; flex-shrink: 1; flex-basis: 0%;">
<!--v-if--> <!--v-if-->
<div role="group" aria-labelledby="" id="reka-combobox-group-v-0-0-1" class="p-1 isolate"> <div role="group" aria-labelledby="" id="reka-combobox-group-v-0-0-1" class="p-1 isolate">
<div id="reka-combobox-item-v-0-0-3" role="option" tabindex="-1" aria-selected="false" disabled="" data-disabled="" data-state="unchecked" class="group relative w-full flex items-center select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-[calc(var(--ui-radius)*1.5)] data-disabled:cursor-not-allowed data-disabled:opacity-75 text-(--ui-text) data-highlighted:text-(--ui-text-highlighted) data-highlighted:before:bg-(--ui-bg-elevated)/50 transition-colors before:transition-colors p-1.5 text-sm gap-1.5" data-reka-collection-item="" data-highlighted=""><span class="iconify i-lucide:circle-help shrink-0 text-(--ui-text-dimmed) group-data-highlighted:text-(--ui-text) transition-colors size-5" aria-hidden="true"></span><span class="truncate">Backlog</span><span class="ms-auto inline-flex gap-1.5 items-center"><!----></span></div> <div id="reka-combobox-item-v-0-0-3" role="option" tabindex="-1" aria-selected="false" disabled="" data-disabled="" data-state="unchecked" class="group relative w-full flex items-center select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-[calc(var(--ui-radius)*1.5)] data-disabled:cursor-not-allowed data-disabled:opacity-75 text-(--ui-text) data-highlighted:text-(--ui-text-highlighted) data-highlighted:before:bg-(--ui-bg-elevated)/50 transition-colors before:transition-colors p-1.5 text-sm gap-1.5" data-reka-collection-item=""><span class="iconify i-lucide:circle-help shrink-0 text-(--ui-text-dimmed) group-data-highlighted:text-(--ui-text) transition-colors size-5" aria-hidden="true"></span><span class="truncate">Backlog</span><span class="ms-auto inline-flex gap-1.5 items-center"><!----></span></div>
<div id="reka-combobox-item-v-0-0-5" role="option" tabindex="-1" aria-selected="false" disabled="" data-disabled="" data-state="unchecked" class="group relative w-full flex items-center select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-[calc(var(--ui-radius)*1.5)] data-disabled:cursor-not-allowed data-disabled:opacity-75 text-(--ui-text) data-highlighted:text-(--ui-text-highlighted) data-highlighted:before:bg-(--ui-bg-elevated)/50 transition-colors before:transition-colors p-1.5 text-sm gap-1.5" data-reka-collection-item=""><span class="iconify i-lucide:circle-plus shrink-0 text-(--ui-text-dimmed) group-data-highlighted:text-(--ui-text) transition-colors size-5" aria-hidden="true"></span><span class="truncate">Todo</span><span class="ms-auto inline-flex gap-1.5 items-center"><!----></span></div> <div id="reka-combobox-item-v-0-0-5" role="option" tabindex="-1" aria-selected="false" disabled="" data-disabled="" data-state="unchecked" class="group relative w-full flex items-center select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-[calc(var(--ui-radius)*1.5)] data-disabled:cursor-not-allowed data-disabled:opacity-75 text-(--ui-text) data-highlighted:text-(--ui-text-highlighted) data-highlighted:before:bg-(--ui-bg-elevated)/50 transition-colors before:transition-colors p-1.5 text-sm gap-1.5" data-reka-collection-item=""><span class="iconify i-lucide:circle-plus shrink-0 text-(--ui-text-dimmed) group-data-highlighted:text-(--ui-text) transition-colors size-5" aria-hidden="true"></span><span class="truncate">Todo</span><span class="ms-auto inline-flex gap-1.5 items-center"><!----></span></div>
<div id="reka-combobox-item-v-0-0-7" role="option" tabindex="-1" aria-selected="false" disabled="" data-disabled="" data-state="unchecked" class="group relative w-full flex items-center select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-[calc(var(--ui-radius)*1.5)] data-disabled:cursor-not-allowed data-disabled:opacity-75 text-(--ui-text) data-highlighted:text-(--ui-text-highlighted) data-highlighted:before:bg-(--ui-bg-elevated)/50 transition-colors before:transition-colors p-1.5 text-sm gap-1.5" data-reka-collection-item=""><span class="iconify i-lucide:circle-arrow-up shrink-0 text-(--ui-text-dimmed) group-data-highlighted:text-(--ui-text) transition-colors size-5" aria-hidden="true"></span><span class="truncate">In Progress</span><span class="ms-auto inline-flex gap-1.5 items-center"><!----></span></div> <div id="reka-combobox-item-v-0-0-7" role="option" tabindex="-1" aria-selected="false" disabled="" data-disabled="" data-state="unchecked" class="group relative w-full flex items-center select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-[calc(var(--ui-radius)*1.5)] data-disabled:cursor-not-allowed data-disabled:opacity-75 text-(--ui-text) data-highlighted:text-(--ui-text-highlighted) data-highlighted:before:bg-(--ui-bg-elevated)/50 transition-colors before:transition-colors p-1.5 text-sm gap-1.5" data-reka-collection-item=""><span class="iconify i-lucide:circle-arrow-up shrink-0 text-(--ui-text-dimmed) group-data-highlighted:text-(--ui-text) transition-colors size-5" aria-hidden="true"></span><span class="truncate">In Progress</span><span class="ms-auto inline-flex gap-1.5 items-center"><!----></span></div>
<div id="reka-combobox-item-v-0-0-9" role="option" tabindex="-1" aria-selected="false" disabled="" data-disabled="" data-state="unchecked" class="group relative w-full flex items-center select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-[calc(var(--ui-radius)*1.5)] data-disabled:cursor-not-allowed data-disabled:opacity-75 text-(--ui-text) data-highlighted:text-(--ui-text-highlighted) data-highlighted:before:bg-(--ui-bg-elevated)/50 transition-colors before:transition-colors p-1.5 text-sm gap-1.5" data-reka-collection-item=""><span class="iconify i-lucide:circle-check shrink-0 text-(--ui-text-dimmed) group-data-highlighted:text-(--ui-text) transition-colors size-5" aria-hidden="true"></span><span class="truncate">Done</span><span class="ms-auto inline-flex gap-1.5 items-center"><!----></span></div> <div id="reka-combobox-item-v-0-0-9" role="option" tabindex="-1" aria-selected="false" disabled="" data-disabled="" data-state="unchecked" class="group relative w-full flex items-center select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-[calc(var(--ui-radius)*1.5)] data-disabled:cursor-not-allowed data-disabled:opacity-75 text-(--ui-text) data-highlighted:text-(--ui-text-highlighted) data-highlighted:before:bg-(--ui-bg-elevated)/50 transition-colors before:transition-colors p-1.5 text-sm gap-1.5" data-reka-collection-item=""><span class="iconify i-lucide:circle-check shrink-0 text-(--ui-text-dimmed) group-data-highlighted:text-(--ui-text) transition-colors size-5" aria-hidden="true"></span><span class="truncate">Done</span><span class="ms-auto inline-flex gap-1.5 items-center"><!----></span></div>

View File

@@ -270,7 +270,7 @@ exports[`SelectMenu > renders with disabled correctly 1`] = `
<div data-reka-popper-content-wrapper="" style="position: fixed; left: 0px; top: 0px; transform: translate(0, -200%); min-width: max-content;"> <div data-reka-popper-content-wrapper="" style="position: fixed; left: 0px; top: 0px; transform: translate(0, -200%); min-width: max-content;">
<div position="popper" id="reka-combobox-content-v-0-0-0" data-state="open" style="display: flex; flex-direction: column; box-sizing: border-box; --reka-combobox-content-transform-origin: var(--reka-popper-transform-origin); --reka-combobox-content-available-width: var(--reka-popper-available-width); --reka-combobox-content-available-height: var(--reka-popper-available-height); --reka-combobox-trigger-width: var(--reka-popper-anchor-width); --reka-combobox-trigger-height: var(--reka-popper-anchor-height); animation: none; outline-color: none; outline-style: none; outline-width: initial;" data-dismissable-layer="" role="listbox" aria-orientation="vertical" aria-multiselectable="false" data-orientation="vertical" class="max-h-60 w-(--reka-popper-anchor-width) bg-(--ui-bg) shadow-lg rounded-[calc(var(--ui-radius)*1.5)] ring ring-(--ui-border) overflow-hidden data-[state=open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in] pointer-events-auto" data-side="bottom" data-align="center"> <div position="popper" id="reka-combobox-content-v-0-0-0" data-state="open" style="display: flex; flex-direction: column; box-sizing: border-box; --reka-combobox-content-transform-origin: var(--reka-popper-transform-origin); --reka-combobox-content-available-width: var(--reka-popper-available-width); --reka-combobox-content-available-height: var(--reka-popper-available-height); --reka-combobox-trigger-width: var(--reka-popper-anchor-width); --reka-combobox-trigger-height: var(--reka-popper-anchor-height); animation: none; outline-color: none; outline-style: none; outline-width: initial;" data-dismissable-layer="" role="listbox" aria-orientation="vertical" aria-multiselectable="false" data-orientation="vertical" class="max-h-60 w-(--reka-popper-anchor-width) bg-(--ui-bg) shadow-lg rounded-[calc(var(--ui-radius)*1.5)] ring ring-(--ui-border) overflow-hidden data-[state=open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in] pointer-events-auto" data-side="bottom" data-align="center">
<div tabindex="-1" class="flex flex-col min-h-0"> <div tabindex="-1" class="flex flex-col min-h-0">
<div class="relative inline-flex items-center border-b border-(--ui-border)"><input type="text" placeholder="Search..." class="w-full rounded-[calc(var(--ui-radius)*1.5)] border-0 placeholder:text-(--ui-text-dimmed) focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 transition-colors px-2.5 py-1.5 text-sm gap-1.5 text-(--ui-text-highlighted) bg-transparent" disabled="" autocomplete="off" data-disabled="" aria-disabled="true" aria-expanded="true" aria-controls="reka-combobox-content-v-0-0-0" aria-autocomplete="list" role="combobox" value="" aria-activedescendant="reka-combobox-item-v-0-0-3"> <div class="relative inline-flex items-center border-b border-(--ui-border)"><input type="text" placeholder="Search..." class="w-full rounded-[calc(var(--ui-radius)*1.5)] border-0 placeholder:text-(--ui-text-dimmed) focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 transition-colors px-2.5 py-1.5 text-sm gap-1.5 text-(--ui-text-highlighted) bg-transparent" disabled="" autocomplete="off" data-disabled="" aria-disabled="true" aria-expanded="true" aria-controls="reka-combobox-content-v-0-0-0" aria-autocomplete="list" role="combobox" value="">
<!--v-if--> <!--v-if-->
<!--v-if--> <!--v-if-->
</div> </div>
@@ -278,7 +278,7 @@ exports[`SelectMenu > renders with disabled correctly 1`] = `
<div class="divide-y divide-(--ui-border) scroll-py-1" data-reka-combobox-viewport="" role="presentation" style="position: relative; overflow: auto; flex-grow: 1; flex-shrink: 1; flex-basis: 0%;"> <div class="divide-y divide-(--ui-border) scroll-py-1" data-reka-combobox-viewport="" role="presentation" style="position: relative; overflow: auto; flex-grow: 1; flex-shrink: 1; flex-basis: 0%;">
<!--v-if--> <!--v-if-->
<div role="group" aria-labelledby="" id="reka-combobox-group-v-0-0-1" class="p-1 isolate"> <div role="group" aria-labelledby="" id="reka-combobox-group-v-0-0-1" class="p-1 isolate">
<div id="reka-combobox-item-v-0-0-3" role="option" tabindex="-1" aria-selected="false" disabled="" data-disabled="" data-state="unchecked" class="group relative w-full flex items-center select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-[calc(var(--ui-radius)*1.5)] data-disabled:cursor-not-allowed data-disabled:opacity-75 text-(--ui-text) data-highlighted:text-(--ui-text-highlighted) data-highlighted:before:bg-(--ui-bg-elevated)/50 transition-colors before:transition-colors p-1.5 text-sm gap-1.5" data-reka-collection-item="" data-highlighted=""><span class="iconify i-lucide:circle-help shrink-0 text-(--ui-text-dimmed) group-data-highlighted:text-(--ui-text) transition-colors size-5" aria-hidden="true"></span><span class="truncate">Backlog</span><span class="ms-auto inline-flex gap-1.5 items-center"><!----></span></div> <div id="reka-combobox-item-v-0-0-3" role="option" tabindex="-1" aria-selected="false" disabled="" data-disabled="" data-state="unchecked" class="group relative w-full flex items-center select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-[calc(var(--ui-radius)*1.5)] data-disabled:cursor-not-allowed data-disabled:opacity-75 text-(--ui-text) data-highlighted:text-(--ui-text-highlighted) data-highlighted:before:bg-(--ui-bg-elevated)/50 transition-colors before:transition-colors p-1.5 text-sm gap-1.5" data-reka-collection-item=""><span class="iconify i-lucide:circle-help shrink-0 text-(--ui-text-dimmed) group-data-highlighted:text-(--ui-text) transition-colors size-5" aria-hidden="true"></span><span class="truncate">Backlog</span><span class="ms-auto inline-flex gap-1.5 items-center"><!----></span></div>
<div id="reka-combobox-item-v-0-0-5" role="option" tabindex="-1" aria-selected="false" disabled="" data-disabled="" data-state="unchecked" class="group relative w-full flex items-center select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-[calc(var(--ui-radius)*1.5)] data-disabled:cursor-not-allowed data-disabled:opacity-75 text-(--ui-text) data-highlighted:text-(--ui-text-highlighted) data-highlighted:before:bg-(--ui-bg-elevated)/50 transition-colors before:transition-colors p-1.5 text-sm gap-1.5" data-reka-collection-item=""><span class="iconify i-lucide:circle-plus shrink-0 text-(--ui-text-dimmed) group-data-highlighted:text-(--ui-text) transition-colors size-5" aria-hidden="true"></span><span class="truncate">Todo</span><span class="ms-auto inline-flex gap-1.5 items-center"><!----></span></div> <div id="reka-combobox-item-v-0-0-5" role="option" tabindex="-1" aria-selected="false" disabled="" data-disabled="" data-state="unchecked" class="group relative w-full flex items-center select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-[calc(var(--ui-radius)*1.5)] data-disabled:cursor-not-allowed data-disabled:opacity-75 text-(--ui-text) data-highlighted:text-(--ui-text-highlighted) data-highlighted:before:bg-(--ui-bg-elevated)/50 transition-colors before:transition-colors p-1.5 text-sm gap-1.5" data-reka-collection-item=""><span class="iconify i-lucide:circle-plus shrink-0 text-(--ui-text-dimmed) group-data-highlighted:text-(--ui-text) transition-colors size-5" aria-hidden="true"></span><span class="truncate">Todo</span><span class="ms-auto inline-flex gap-1.5 items-center"><!----></span></div>
<div id="reka-combobox-item-v-0-0-7" role="option" tabindex="-1" aria-selected="false" disabled="" data-disabled="" data-state="unchecked" class="group relative w-full flex items-center select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-[calc(var(--ui-radius)*1.5)] data-disabled:cursor-not-allowed data-disabled:opacity-75 text-(--ui-text) data-highlighted:text-(--ui-text-highlighted) data-highlighted:before:bg-(--ui-bg-elevated)/50 transition-colors before:transition-colors p-1.5 text-sm gap-1.5" data-reka-collection-item=""><span class="iconify i-lucide:circle-arrow-up shrink-0 text-(--ui-text-dimmed) group-data-highlighted:text-(--ui-text) transition-colors size-5" aria-hidden="true"></span><span class="truncate">In Progress</span><span class="ms-auto inline-flex gap-1.5 items-center"><!----></span></div> <div id="reka-combobox-item-v-0-0-7" role="option" tabindex="-1" aria-selected="false" disabled="" data-disabled="" data-state="unchecked" class="group relative w-full flex items-center select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-[calc(var(--ui-radius)*1.5)] data-disabled:cursor-not-allowed data-disabled:opacity-75 text-(--ui-text) data-highlighted:text-(--ui-text-highlighted) data-highlighted:before:bg-(--ui-bg-elevated)/50 transition-colors before:transition-colors p-1.5 text-sm gap-1.5" data-reka-collection-item=""><span class="iconify i-lucide:circle-arrow-up shrink-0 text-(--ui-text-dimmed) group-data-highlighted:text-(--ui-text) transition-colors size-5" aria-hidden="true"></span><span class="truncate">In Progress</span><span class="ms-auto inline-flex gap-1.5 items-center"><!----></span></div>
<div id="reka-combobox-item-v-0-0-9" role="option" tabindex="-1" aria-selected="false" disabled="" data-disabled="" data-state="unchecked" class="group relative w-full flex items-center select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-[calc(var(--ui-radius)*1.5)] data-disabled:cursor-not-allowed data-disabled:opacity-75 text-(--ui-text) data-highlighted:text-(--ui-text-highlighted) data-highlighted:before:bg-(--ui-bg-elevated)/50 transition-colors before:transition-colors p-1.5 text-sm gap-1.5" data-reka-collection-item=""><span class="iconify i-lucide:circle-check shrink-0 text-(--ui-text-dimmed) group-data-highlighted:text-(--ui-text) transition-colors size-5" aria-hidden="true"></span><span class="truncate">Done</span><span class="ms-auto inline-flex gap-1.5 items-center"><!----></span></div> <div id="reka-combobox-item-v-0-0-9" role="option" tabindex="-1" aria-selected="false" disabled="" data-disabled="" data-state="unchecked" class="group relative w-full flex items-center select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-[calc(var(--ui-radius)*1.5)] data-disabled:cursor-not-allowed data-disabled:opacity-75 text-(--ui-text) data-highlighted:text-(--ui-text-highlighted) data-highlighted:before:bg-(--ui-bg-elevated)/50 transition-colors before:transition-colors p-1.5 text-sm gap-1.5" data-reka-collection-item=""><span class="iconify i-lucide:circle-check shrink-0 text-(--ui-text-dimmed) group-data-highlighted:text-(--ui-text) transition-colors size-5" aria-hidden="true"></span><span class="truncate">Done</span><span class="ms-auto inline-flex gap-1.5 items-center"><!----></span></div>

View File

@@ -33,7 +33,7 @@ exports[`Slider > renders with defaultValue correctly 1`] = `
exports[`Slider > renders with disabled correctly 1`] = ` exports[`Slider > renders with disabled correctly 1`] = `
"<span data-slider-impl="" dir="ltr" data-orientation="horizontal" style="--reka-slider-thumb-transform: translateX(-50%);" class="relative flex items-center select-none touch-none w-full opacity-75 cursor-not-allowed" aria-disabled="true" data-disabled=""><span data-disabled="" data-orientation="horizontal" class="relative bg-(--ui-bg-accented) overflow-hidden rounded-full grow h-[8px]"><span data-disabled="" data-orientation="horizontal" style="left: 0%; right: 100%;" class="absolute rounded-full bg-(--ui-primary) h-full"></span></span> "<span data-slider-impl="" dir="ltr" data-orientation="horizontal" style="--reka-slider-thumb-transform: translateX(-50%);" class="relative flex items-center select-none touch-none w-full opacity-75 cursor-not-allowed" aria-disabled="true" data-disabled=""><span data-disabled="" data-orientation="horizontal" class="relative bg-(--ui-bg-accented) overflow-hidden rounded-full grow h-[8px]"><span data-disabled="" data-orientation="horizontal" style="left: 0%; right: 100%;" class="absolute rounded-full bg-(--ui-primary) h-full"></span></span>
<div class="rounded-full bg-(--ui-bg) ring-2 focus-visible:outline-2 focus-visible:outline-offset-2 ring-(--ui-primary) focus-visible:outline-(--ui-primary)/50 size-4" role="slider" data-disabled="" data-orientation="horizontal" aria-valuemin="0" aria-valuemax="100" aria-orientation="horizontal" style="transform: var(--reka-slider-thumb-transform); position: absolute; left: calc(0% + 0px);" data-reka-collection-item="" aria-valuenow="0"></div> <div class="rounded-full bg-(--ui-bg) ring-2 focus-visible:outline-2 focus-visible:outline-offset-2 ring-(--ui-primary) focus-visible:outline-(--ui-primary)/50 size-4" role="slider" data-disabled="" data-orientation="horizontal" aria-valuemin="0" aria-valuemax="100" aria-orientation="horizontal" style="transform: var(--reka-slider-thumb-transform); position: absolute; left: calc(0% + 0px);" data-reka-collection-item=""></div>
<!----></span>" <!----></span>"
`; `;

View File

@@ -33,7 +33,7 @@ exports[`Slider > renders with defaultValue correctly 1`] = `
exports[`Slider > renders with disabled correctly 1`] = ` exports[`Slider > renders with disabled correctly 1`] = `
"<span data-slider-impl="" dir="ltr" data-orientation="horizontal" style="--reka-slider-thumb-transform: translateX(-50%);" class="relative flex items-center select-none touch-none w-full opacity-75 cursor-not-allowed" aria-disabled="true" data-disabled=""><span data-disabled="" data-orientation="horizontal" class="relative bg-(--ui-bg-accented) overflow-hidden rounded-full grow h-[8px]"><span data-disabled="" data-orientation="horizontal" style="left: 0%; right: 100%;" class="absolute rounded-full bg-(--ui-primary) h-full"></span></span> "<span data-slider-impl="" dir="ltr" data-orientation="horizontal" style="--reka-slider-thumb-transform: translateX(-50%);" class="relative flex items-center select-none touch-none w-full opacity-75 cursor-not-allowed" aria-disabled="true" data-disabled=""><span data-disabled="" data-orientation="horizontal" class="relative bg-(--ui-bg-accented) overflow-hidden rounded-full grow h-[8px]"><span data-disabled="" data-orientation="horizontal" style="left: 0%; right: 100%;" class="absolute rounded-full bg-(--ui-primary) h-full"></span></span>
<div class="rounded-full bg-(--ui-bg) ring-2 focus-visible:outline-2 focus-visible:outline-offset-2 ring-(--ui-primary) focus-visible:outline-(--ui-primary)/50 size-4" role="slider" data-disabled="" data-orientation="horizontal" aria-valuemin="0" aria-valuemax="100" aria-orientation="horizontal" style="transform: var(--reka-slider-thumb-transform); position: absolute; left: calc(0% + 0px);" data-reka-collection-item="" aria-valuenow="0"></div> <div class="rounded-full bg-(--ui-bg) ring-2 focus-visible:outline-2 focus-visible:outline-offset-2 ring-(--ui-primary) focus-visible:outline-(--ui-primary)/50 size-4" role="slider" data-disabled="" data-orientation="horizontal" aria-valuemin="0" aria-valuemax="100" aria-orientation="horizontal" style="transform: var(--reka-slider-thumb-transform); position: absolute; left: calc(0% + 0px);" data-reka-collection-item=""></div>
<!----></span>" <!----></span>"
`; `;

View File

@@ -1,13 +1,12 @@
import { fileURLToPath } from 'node:url' import { fileURLToPath } from 'node:url'
import { defineVitestConfig } from '@nuxt/test-utils/config' import { defineVitestConfig } from '@nuxt/test-utils/config'
import { defaultExclude } from 'vitest/config'
export default defineVitestConfig({ export default defineVitestConfig({
test: { test: {
testTimeout: 1000, testTimeout: 1000,
globals: true, globals: true,
silent: true, silent: true,
exclude: [...defaultExclude, './test/vue/**.spec.ts'], include: ['./test/components/**.spec.ts'],
environment: 'nuxt', environment: 'nuxt',
environmentOptions: { environmentOptions: {
nuxt: { nuxt: {

View File

@@ -9,6 +9,7 @@ const vueComponents = await glob('./src/runtime/vue/components/*.vue', { absolut
export default defineConfig({ export default defineConfig({
test: { test: {
testTimeout: 1000,
environment: 'happy-dom', environment: 'happy-dom',
silent: true, silent: true,
include: ['./test/components/**.spec.ts'], include: ['./test/components/**.spec.ts'],