Compare commits

...

34 Commits

Author SHA1 Message Date
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
81 changed files with 2208 additions and 1993 deletions

View File

@@ -1,5 +1,34 @@
# Changelog
## [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)
### Bug Fixes

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.
- label: Do you have a Figma to Code plugin?
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
![TeamPad 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.*
![TemPad Dev Nuxt UI Plugin](/pro/figma/teampad-dev-nuxt-ui-plugin.gif){.w-full .rounded .mb-2 .max-w-[636px]}

View File

@@ -189,7 +189,7 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
:links="page.design_system.links"
orientation="horizontal"
>
<MDC :value="page.design_system.code" />
<MDC :value="page.design_system.code" cache-key="index-design-system-code" />
</UPageSection>
<USeparator />
@@ -201,10 +201,10 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
orientation="horizontal"
>
<template #description>
<MDC :value="page.component_customization.description" />
<MDC :value="page.component_customization.description" cache-key="index-component-customization-description" />
</template>
<MDC :value="page.component_customization.code" />
<MDC :value="page.component_customization.code" cache-key="index-component-customization-code" />
</UPageSection>
<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.
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"}
```bash [pnpm]
pnpm add https://pkg.pr.new/@nuxt/ui@5385f84
```diff [package.json]
{
"dependencies": {
- "@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
**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.
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"}
```bash [pnpm]
pnpm add https://pkg.pr.new/@nuxt/ui@5385f84
```diff [package.json]
{
"dependencies": {
- "@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
**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`.
::
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
<script setup lang="ts">
@@ -450,6 +453,79 @@ const toast = useToast()
</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

View File

@@ -17,6 +17,11 @@ Nuxt UI provides an **App** component that wraps your app to provide global conf
### Locale
::module-only
#ui
:::div
Use the `locale` prop with the locale you want to use from `@nuxt/ui/locale`:
```vue [app.vue]
@@ -31,13 +36,42 @@ import { fr } from '@nuxt/ui/locale'
</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
You also have the option to add your own locale using `defineLocale`:
::module-only
#ui
:::div
```vue [app.vue]
<script setup lang="ts">
const locale = defineLocale({
import type { Messages } from '@nuxt/ui'
const locale = defineLocale<Messages>({
name: 'My custom locale',
code: 'en',
dir: 'ltr',
@@ -54,6 +88,35 @@ const locale = defineLocale({
</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
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`
::module-only
#ui
:::div
```vue [app.vue]
<script setup lang="ts">
import * as locales from '@nuxt/ui/locale'
@@ -130,6 +198,28 @@ const { locale } = useI18n()
</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
@@ -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:
::module-only
#ui
:::div
```vue [app.vue]
<script setup lang="ts">
import * as locales from '@nuxt/ui/locale'
@@ -162,6 +257,38 @@ useHead({
</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

View File

@@ -17,6 +17,11 @@ Nuxt UI provides an **App** component that wraps your app to provide global conf
### Locale
::module-only
#ui
:::div
Use the `locale` prop with the locale you want to use from `@nuxt/ui/locale`:
```vue [App.vue]
@@ -31,15 +36,43 @@ import { fr } from '@nuxt/ui/locale'
</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
You also have the option to add your locale using `defineLocale`:
::module-only
#ui
:::div
```vue [App.vue]
<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',
code: 'en',
dir: 'ltr',
@@ -56,6 +89,36 @@ const locale = defineLocale({
</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
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`
::module-only
#ui
:::div
```vue [App.vue]
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
@@ -146,6 +214,29 @@ const { locale } = useI18n()
</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
@@ -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:
::module-only
#ui
:::div
```vue [App.vue]
<script setup lang="ts">
import { computed } from 'vue'
@@ -181,6 +277,41 @@ useHead({
</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

View File

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

View File

@@ -4,25 +4,25 @@
"type": "module",
"dependencies": {
"@iconify-json/logos": "^1.2.4",
"@iconify-json/lucide": "^1.2.28",
"@iconify-json/simple-icons": "^1.2.27",
"@iconify-json/lucide": "^1.2.29",
"@iconify-json/simple-icons": "^1.2.28",
"@iconify-json/vscode-icons": "^1.2.16",
"@nuxt/content": "^3.3.0",
"@nuxt/image": "^1.9.0",
"@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",
"@nuxtjs/plausible": "^1.2.0",
"@octokit/rest": "^21.1.1",
"@rollup/plugin-yaml": "^4.1.2",
"@vueuse/nuxt": "^12.8.2",
"@vueuse/nuxt": "^13.0.0",
"joi": "^17.13.3",
"motion": "^12.4.10",
"motion-v": "0.11.1",
"nuxt": "^3.15.4",
"motion": "^12.5.0",
"motion-v": "0.11.3",
"nuxt": "^3.16.0",
"nuxt-component-meta": "^0.10.0",
"nuxt-llms": "^0.1.0",
"nuxt-og-image": "^4.2.0",
"nuxt-og-image": "^5.0.2",
"prettier": "^3.5.3",
"shiki-transformer-color-highlight": "^1.0.0",
"superstruct": "^2.0.2",

View File

@@ -1,8 +1,8 @@
{
"name": "@nuxt/ui",
"description": "A UI Library for Modern Web Apps, powered by Vue & Tailwind CSS.",
"version": "3.0.0-beta.2",
"packageManager": "pnpm@10.6.1",
"version": "3.0.0-beta.4",
"packageManager": "pnpm@10.6.2",
"repository": {
"type": "git",
"url": "git+https://github.com/nuxt/ui.git"
@@ -67,7 +67,7 @@
"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",
"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",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
@@ -81,17 +81,17 @@
"@iconify/vue": "^4.3.0",
"@internationalized/date": "^3.7.0",
"@internationalized/number": "^3.6.0",
"@nuxt/fonts": "^0.10.3",
"@nuxt/icon": "^1.10.3",
"@nuxt/kit": "^3.15.4",
"@nuxt/schema": "^3.15.4",
"@nuxt/fonts": "^0.11.0",
"@nuxt/icon": "^1.11.0",
"@nuxt/kit": "^3.16.0",
"@nuxt/schema": "^3.16.0",
"@nuxtjs/color-mode": "^3.5.2",
"@tailwindcss/postcss": "^4.0.12",
"@tailwindcss/vite": "^4.0.12",
"@tailwindcss/postcss": "^4.0.13",
"@tailwindcss/vite": "^4.0.13",
"@tanstack/vue-table": "^8.21.2",
"@unhead/vue": "^1.11.20",
"@vueuse/core": "^12.8.2",
"@vueuse/integrations": "^12.8.2",
"@unhead/vue": "^2.0.0-rc.10",
"@vueuse/core": "^13.0.0",
"@vueuse/integrations": "^13.0.0",
"colortranslator": "^4.1.0",
"consola": "^3.4.0",
"defu": "^6.1.4",
@@ -110,26 +110,28 @@
"pathe": "^2.0.3",
"reka-ui": "^2.0.2",
"scule": "^1.3.0",
"tailwind-variants": "^0.3.1",
"tailwindcss": "^4.0.12",
"tailwind-variants": "^1.0.0",
"tailwindcss": "^4.0.13",
"tinyglobby": "^0.2.12",
"unplugin": "^2.2.0",
"unplugin-auto-import": "^19.1.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": {
"@nuxt/eslint-config": "^1.1.0",
"@nuxt/eslint-config": "^1.2.0",
"@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",
"@standard-schema/spec": "^1.0.0",
"@vue/test-utils": "^2.4.6",
"embla-carousel": "^8.5.2",
"eslint": "^9.21.0",
"happy-dom": "^17.1.2",
"eslint": "^9.22.0",
"happy-dom": "^17.4.4",
"joi": "^17.13.3",
"nuxt": "^3.15.4",
"nuxt": "^3.16.0",
"release-it": "^18.1.2",
"superstruct": "^2.0.2",
"valibot": "^0.42.1",
@@ -146,12 +148,9 @@
"@nuxt/ui": "workspace:*",
"chokidar": "3.6.0",
"debug": "4.3.7",
"happy-dom": "17.1.2",
"rollup": "4.32.1",
"rollup": "4.34.9",
"typescript": "5.6.3",
"unimport": "3.14.5",
"unplugin": "^2.2.0",
"vue": "3.5.13",
"vue-tsc": "2.2.0"
},
"pnpm": {

View File

@@ -2,8 +2,9 @@
<html lang="en">
<head>
<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" />
<title>Nuxt UI ❤️ Vue</title>
<title>Nuxt UI - Vue Playground</title>
</head>
<body>
<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"
},
"dependencies": {
"@iconify-json/lucide": "^1.2.28",
"@iconify-json/simple-icons": "^1.2.27",
"@iconify-json/lucide": "^1.2.29",
"@iconify-json/simple-icons": "^1.2.28",
"@nuxt/ui": "latest",
"@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,10 +6,10 @@
"enabled": true
},
"ignoreDeps": [
"happy-dom",
"valibot30",
"valibot31",
"typescript",
"vaul-vue",
"vue-tsc"
],
"baseBranches": ["dev", "v3"],

View File

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

View File

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

View File

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

View File

@@ -29,7 +29,7 @@ export interface FormEmits<T extends object> {
}
export interface FormSlots {
default(props?: {}): any
default(props?: { errors: FormError[] }): any
}
</script>
@@ -69,7 +69,7 @@ onMounted(async () => {
nestedForms.value.set(event.formId, { validate: event.validate })
} else if (event.type === 'detach') {
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') {
await _validate({ name: event.name, silent: true, nested: false })
} else if (event.eager || blurredFields.has(event.name)) {
@@ -121,7 +121,7 @@ const blurredFields = new Set<keyof T>()
function resolveErrorIds(errs: FormError[]): FormErrorWithId[] {
return errs.map(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) {
const otherErrors = errors.value.filter(error => !names.some((name) => {
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 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)
@@ -269,6 +269,6 @@ defineExpose<Form<T>>({
:class="form({ class: props.class })"
@submit.prevent="onSubmitWrapper"
>
<slot />
<slot :errors="errors" />
</component>
</template>

View File

@@ -63,7 +63,7 @@ const ui = computed(() => formField({
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())
// 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 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 { orientation, size: buttonGroupSize } = useButtonGroup<InputProps>(props)
@@ -111,15 +111,19 @@ function autoFocus() {
}
// Custom function to handle the v-model properties
function updateInput(value: string) {
function updateInput(value: string | null) {
if (modelModifiers.trim) {
value = value.trim()
value = value?.trim() ?? null
}
if (modelModifiers.number || props.type === 'number') {
value = looseToNumber(value)
}
if (modelModifiers.nullify) {
value ||= null
}
modelValue.value = value
emitFormInput()
}

View File

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

View File

@@ -1,7 +1,6 @@
<script lang="ts">
import type { PaginationRootProps, PaginationRootEmits } from 'reka-ui'
import type { AppConfig } from '@nuxt/schema'
import type { RouteLocationRaw } from '#vue-router'
import _appConfig from '#build/app.config'
import theme from '#build/ui/pagination'
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.
* @param page The page number to navigate to.
*/
to?: (page: number) => RouteLocationRaw
to?: (page: number) => ButtonProps['to']
class?: any
ui?: Partial<typeof pagination.slots>
}

View File

@@ -74,7 +74,7 @@ const props = withDefaults(defineProps<TextareaProps>(), {
defineSlots<TextareaSlots>()
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 })
@@ -94,15 +94,19 @@ function autoFocus() {
}
// Custom function to handle the v-model properties
function updateInput(value: string) {
function updateInput(value: string | null) {
if (modelModifiers.trim) {
value = value.trim()
value = value?.trim() ?? null
}
if (modelModifiers.number) {
value = looseToNumber(value)
}
if (modelModifiers.nullify) {
value ||= null
}
modelValue.value = value
emitFormInput()
}

View File

@@ -1,13 +1,13 @@
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
code: string
dir?: Direction
messages: Messages
messages: M
}
export function defineLocale(options: DefineLocaleOptions): Locale {
return defu<DefineLocaleOptions, [{ dir: Direction }]>(options, { dir: 'ltr' })
export function defineLocale<M>(options: DefineLocaleOptions<M>): Locale<M> {
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 { Locale } from '../types/locale'
import { createSharedComposable } from '@vueuse/core'
import type { Locale, Messages } from '../types/locale'
import { buildLocaleContext } from '../utils/locale'
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 locale = localeOverrides || inject(localeContextInjectionKey, ref())!
const _useLocale = (localeOverrides?: Ref<Locale<Messages> | undefined>) => {
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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,13 +5,15 @@ export { default as cs } from './cs'
export { default as da } from './da'
export { default as de } from './de'
export { default as el } from './el'
export { default as et } from './et'
export { default as en } from './en'
export { default as es } from './es'
export { default as et } from './et'
export { default as fa_ir } from './fa_ir'
export { default as fi } from './fi'
export { default as fr } from './fr'
export { default as he } from './he'
export { default as hi } from './hi'
export { default as hu } from './hu'
export { default as id } from './id'
export { default as it } from './it'
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 zh_cn } from './zh_cn'
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'
export default defineLocale({
export default defineLocale<Messages>({
name: 'Italiano',
code: 'it',
messages: {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale'
export default defineLocale({
export default defineLocale<Messages>({
name: '繁體中文',
code: 'zh-TW',
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 interface FormError<P extends string = string> {
name: P
name?: P
message: string
}

View File

@@ -50,9 +50,9 @@ export type Messages = {
export type Direction = 'ltr' | 'rtl'
export type Locale = {
export type Locale<M> = {
name: string
code: string
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 Translator = (path: string, option?: TranslatorOption) => string
export type LocaleContext = {
locale: Ref<Locale>
export type LocaleContext<M> = {
locale: Ref<Locale<M>>
lang: Ref<string>
dir: Ref<Direction>
code: Ref<string>
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))
}
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)
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 code = computed(() => unref(locale).code)
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 {
lang,

View File

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

View File

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

View File

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

View File

@@ -52,7 +52,8 @@ describe('Input', () => {
it.each([
['with .trim modifier', { props: { modelModifiers: { trim: true } } }, { input: 'input ', expected: 'input' }],
['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 }) => {
const wrapper = mount(Input, {
...options

View File

@@ -35,7 +35,8 @@ describe('Textarea', () => {
it.each([
['with .trim modifier', { props: { modelModifiers: { trim: true } } }, { input: 'input ', expected: 'input' }],
['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 }) => {
const wrapper = mount(Textarea, {
...options

View File

@@ -252,13 +252,13 @@ exports[`CommandPalette > renders with defaultValue 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 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-->
</div>
<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 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>
</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>

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 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>"`;

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 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>"`;

View File

@@ -223,7 +223,7 @@ exports[`InputMenu > renders with defaultValue 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>
<!--teleport start-->
<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%;">
<!--v-if-->
<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-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>

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 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 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-->
</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%;">
<!--v-if-->
<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-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>

View File

@@ -33,7 +33,7 @@ exports[`Slider > renders with defaultValue 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>
<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>"
`;

View File

@@ -33,7 +33,7 @@ exports[`Slider > renders with defaultValue 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>
<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>"
`;

View File

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

View File

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