Compare commits

...

74 Commits

Author SHA1 Message Date
Benjamin Canac
b830f63c89 chore(release): v2.21.1 2025-03-08 13:08:41 +01:00
renovate[bot]
71dac5e5b0 chore(deps): update devdependency eslint to ^9.22.0 (dev) (#3488)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-08 12:30:03 +01:00
Benjamin Canac
7b81bfa1ae docs(installation): use pkg.pr.new 2025-03-08 12:20:28 +01:00
Benjamin Canac
bf1c9e7c94 chore(github): use pkg.pr.new 2025-03-08 12:03:10 +01:00
Corey Shuman
23d9b51a58 fix(Table): revert #2600 to fix excessive column data slot re-renders (#3375) 2025-03-08 11:53:46 +01:00
renovate[bot]
2e6ba71e89 chore(deps): update nuxt framework to ^3.16.0 (dev) (#3483)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-07 18:49:41 +01:00
renovate[bot]
ea4007c62d chore(deps): update all non-major dependencies (dev) (#3472)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-07 14:42:35 +01:00
renovate[bot]
69d6997210 chore(deps): update all non-major dependencies (dev) (#3451)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 19:47:07 +01:00
renovate[bot]
6565472570 chore(deps): update dependency ohash to v2 (dev) (#3453)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
Co-authored-by: Sébastien Chopin <atinux@gmail.com>
2025-03-05 15:39:34 +01:00
renovate[bot]
ee408e522e chore(deps): lock file maintenance (dev) (#3434)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 12:05:17 +01:00
renovate[bot]
461e6173a9 chore(deps): update all non-major dependencies (dev) (#3422)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 11:50:12 +01:00
renovate[bot]
b824f0682e chore(deps): update all non-major dependencies (dev) (#3410)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 15:38:34 +01:00
renovate[bot]
7ce6af4870 chore(deps): lock file maintenance (dev) (#3387)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-26 12:13:06 +01:00
renovate[bot]
b4cc9a5ab4 chore(deps): update all non-major dependencies (dev) (#3366)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-26 11:56:16 +01:00
renovate[bot]
06eceff68b chore(deps): update all non-major dependencies (dev) (#3345)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-19 11:54:43 +01:00
renovate[bot]
40f3e3b486 chore(deps): lock file maintenance (dev) (#3335)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-17 11:00:23 +01:00
renovate[bot]
a5458765dc chore(deps): update all non-major dependencies (dev) (#3325)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-17 10:45:06 +01:00
Benjamin Canac
ac574b239b chore(deps): update happy-dom 2025-02-16 16:25:30 +01:00
Léonard PLOTON
feb716c941 feat(SelectMenu): add inputTargetForm prop to handle input validation (#3107) 2025-02-14 15:30:37 +01:00
Vann
15da5cf71e docs(input): add example for maxlength (#3110) 2025-02-14 15:30:01 +01:00
Benjamin Canac
125a28190b fix(Alert/Notification): allow description ui override
Resolves #2554
2025-02-14 14:25:59 +01:00
renovate[bot]
569fa7619b chore(deps): update pnpm to v10 (dev) (#3232)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-14 12:29:33 +01:00
renovate[bot]
0ff2448655 chore(deps): update all non-major dependencies (dev) (#3295)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-14 11:35:31 +01:00
renovate[bot]
a6c3daa363 chore(deps): update all non-major dependencies (dev) (#3260)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-11 21:20:39 +01:00
Nexos Creator
e16eeee8c1 docs(theming/shortcuts): improve (#3259)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-02-06 21:18:52 +01:00
renovate[bot]
53ac62eae5 chore(deps): update dependency @headlessui/tailwindcss to ^0.2.2 (dev) (#3252)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-06 17:12:02 +01:00
Gerben Mulder
9c36d37b84 feat(Form): add standard schema support (#2880) 2025-02-05 09:27:51 +01:00
renovate[bot]
0462edb84e chore(deps): update devdependency vitest to ^3.0.5 (dev) (#3230)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-04 14:59:35 +01:00
renovate[bot]
91e77bb09c chore(deps): lock file maintenance (dev) (#3228)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-03 10:59:26 +01:00
renovate[bot]
84e35d1a79 chore(deps): update all non-major dependencies (dev) (#3204)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-03 10:41:42 +01:00
renovate[bot]
28f29e98b8 chore(deps): update devdependency @nuxt/eslint-config to v1 (dev) (#3211)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-02 20:51:11 +01:00
renovate[bot]
81d7ca0cd1 chore(deps): update nuxt framework to ^3.15.4 (dev) (#3197)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-29 12:50:03 +01:00
renovate[bot]
89d3766835 chore(deps): lock file maintenance (dev) (#3182)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-27 17:52:23 +01:00
renovate[bot]
9104213d35 chore(deps): update nuxt framework to ^3.15.3 (dev) (#3175)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-27 17:37:59 +01:00
renovate[bot]
d699558e38 chore(deps): update all non-major dependencies (dev) (#3184)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-27 17:19:25 +01:00
Benjamin Canac
7cbc3913d9 chore(renovate): run pnpm dedupe post update 2025-01-25 12:22:45 +01:00
Benjamin Canac
d2ceeadae7 feat(module): add colorMode option
Resolves #3143
2025-01-24 12:30:31 +01:00
renovate[bot]
c7f64b64c7 chore(deps): lock file maintenance (dev) (#3144)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 15:53:54 +01:00
renovate[bot]
72ab47e77d chore(deps): update all non-major dependencies (dev) (#3088)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 14:37:17 +01:00
renovate[bot]
5b187d6fbd chore(deps): update devdependency vitest to v3 (dev) (#3116)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 18:29:36 +01:00
Benjamin Canac
f9e61fc422 chore(deps): dedupe 2025-01-20 18:11:15 +01:00
renovate[bot]
1291e95e1c chore(deps): update nuxt framework to ^3.15.2 (dev) (#3073)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 10:57:02 +01:00
Benjamin Canac
f943203770 docs(deps): update @nuxt/ui-pro 2025-01-14 11:51:14 +01:00
Benjamin Canac
f2d387622a chore(release): v2.21.0 2025-01-14 11:20:58 +01:00
Benjamin Canac
b02dc4d5b7 docs(deps): add missing @iconify-json/lucide 2025-01-14 11:03:53 +01:00
Benjamin Canac
6dddadc370 chore(deps): update @nuxtjs/tailwindcss 2025-01-14 11:01:46 +01:00
renovate[bot]
d89ecce472 chore(deps): lock file maintenance (dev) (#3080)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 11:02:11 +01:00
renovate[bot]
efb74668bd chore(deps): update all non-major dependencies (dev) (#3064)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 10:51:46 +01:00
Benjamin Canac
e065734d58 chore(deps): revert vue-tsc update 2025-01-09 15:45:18 +01:00
renovate[bot]
0c5bea5f11 chore(deps): update all non-major dependencies (dev) (#3038)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-09 15:16:42 +01:00
renovate[bot]
f6d4dd3b88 chore(deps): update devdependency @release-it/conventional-changelog to v10 (dev) (#3045)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-08 16:18:33 +01:00
kyyy
d9d4f1915a fix(Table): remove @select event on checkbox (#3042)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-01-08 11:23:26 +01:00
Benjamin Canac
c70d29702e chore(deps): revert typescript upgrade 2025-01-08 11:03:45 +01:00
Benjamin Canac
a0d8935f64 chore(deps): refresh lock 2025-01-06 17:33:54 +01:00
Benjamin Canac
04aefcf81f chore(deps): update 2025-01-06 17:29:49 +01:00
Benjamin Canac
e68b9795be chore(deps): remove unimport resolution 2025-01-06 17:24:30 +01:00
Benjamin Canac
b8c8718560 chore(deps): update typescript 2025-01-06 17:23:57 +01:00
Benjamin Canac
2a33a8171d chore(deps): dedupe 2025-01-06 17:21:04 +01:00
renovate[bot]
23cfc046e7 chore(deps): update dependency pathe to v2 (dev) (#3016)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-06 12:47:24 +01:00
renovate[bot]
e68cb53ab6 chore(deps): update devdependency release-it to v18 (dev) (#3026)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-06 12:46:44 +01:00
renovate[bot]
109b857472 chore(deps): update nuxt framework to ^3.15.1 (dev) (#3020)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-06 10:24:37 +01:00
Stijn Slats
ea15e21cdc feat(module): handle tailwindMerge config from app.config (#2902)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-12-26 11:11:02 +01:00
renovate[bot]
b7153cd879 chore(deps): update nuxt framework to ^3.15.0 (dev) (#2969)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2024-12-25 14:08:44 +01:00
renovate[bot]
5047d448ed chore(deps): update all non-major dependencies (dev) (#2960)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-23 19:07:37 +01:00
renovate[bot]
a0fee0fa73 chore(deps): update dependency nuxt-og-image to v4 (dev) (#2748)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-23 18:37:13 +01:00
renovate[bot]
b762d29220 chore(deps): lock file maintenance (dev) (#2957)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-23 10:58:54 +01:00
renovate[bot]
98c19be71a chore(deps): update all non-major dependencies (dev) (#2918)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-23 10:46:14 +01:00
Inesh Bose
8cf9f27d53 fix(tailwind): use mjs template (#2945) 2024-12-21 14:33:36 +01:00
Benjamin Canac
c0455c831f chore(deps): remove unbuild explicit dependency 2024-12-18 17:00:50 +01:00
renovate[bot]
0360ea7a3c chore(deps): update dependency tailwindcss to ^3.4.17 (dev) (#2927)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-18 09:50:13 +01:00
renovate[bot]
711539f3ce chore(deps): lock file maintenance (dev) (#2913)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-16 10:56:07 +01:00
renovate[bot]
80d6d89467 chore(deps): update all non-major dependencies (dev) (#2864)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-16 10:21:53 +01:00
kyyy
d573fb636f fix(Table): v-model causing first column missing (#2890) 2024-12-13 14:43:17 +01:00
Benjamin Canac
1d08d319a7 chore(deps): set unimport resolution 2024-12-12 14:45:41 +01:00
56 changed files with 4253 additions and 3882 deletions

View File

@@ -37,16 +37,6 @@ jobs:
node-version: ${{ matrix.node }}
cache: pnpm
- name: Filter changes
uses: dorny/paths-filter@v3
id: changes
with:
filters: |
src:
- 'src/**'
- 'package.json'
- 'pnpm-lock.yaml'
- name: Install dependencies
run: pnpm install
@@ -65,8 +55,5 @@ jobs:
- name: Test
run: pnpm run test run
- name: Release Edge
if: github.event_name == 'push' && steps.changes.outputs.src == 'true'
run: ./scripts/release-edge.sh
env:
NODE_AUTH_TOKEN: ${{secrets.NODE_AUTH_TOKEN}}
- name: Publish
run: pnpx pkg-pr-new publish --compact --no-template --pnpm

View File

@@ -1,5 +1,30 @@
# Changelog
## [2.21.1](https://github.com/nuxt/ui/compare/v2.21.0...v2.21.1) (2025-03-08)
### Features
* **Form:** add standard schema support ([#2880](https://github.com/nuxt/ui/issues/2880)) ([9c36d37](https://github.com/nuxt/ui/commit/9c36d37b847468d1cbd76eea38ac00cbc22549ca))
* **module:** add `colorMode` option ([d2ceead](https://github.com/nuxt/ui/commit/d2ceeadae796254128697d94a3e317234bc2ecda)), closes [#3143](https://github.com/nuxt/ui/issues/3143)
* **SelectMenu:** add inputTargetForm prop to handle input validation ([#3107](https://github.com/nuxt/ui/issues/3107)) ([feb716c](https://github.com/nuxt/ui/commit/feb716c941f1e7315009b53861a4dc0c2f233052))
### Bug Fixes
* **Alert/Notification:** allow description ui override ([125a281](https://github.com/nuxt/ui/commit/125a28190b1a83e2456457e7a4ec618384b2446c)), closes [#2554](https://github.com/nuxt/ui/issues/2554)
* **Table:** revert [#2600](https://github.com/nuxt/ui/issues/2600) to fix excessive column data slot re-renders ([#3375](https://github.com/nuxt/ui/issues/3375)) ([23d9b51](https://github.com/nuxt/ui/commit/23d9b51a5861f5d1f32f68a3141a600655a0598a))
## [2.21.0](https://github.com/nuxt/ui/compare/v2.20.0...v2.21.0) (2025-01-14)
### Features
* **module:** handle `tailwindMerge` config from `app.config` ([#2902](https://github.com/nuxt/ui/issues/2902)) ([ea15e21](https://github.com/nuxt/ui/commit/ea15e21cdcba00e21302415829113e8c6def8a6e))
### Bug Fixes
* **Table:** `v-model` causing first column missing ([#2890](https://github.com/nuxt/ui/issues/2890)) ([d573fb6](https://github.com/nuxt/ui/commit/d573fb636f7f749ce95b93c5fb1ae2a053eeeeb0))
* **Table:** remove `[@select](https://github.com/select)` event on checkbox ([#3042](https://github.com/nuxt/ui/issues/3042)) ([d9d4f19](https://github.com/nuxt/ui/commit/d9d4f1915aac586ae1abf3ebe67ca9aff65b9be0))
* **tailwind:** use mjs template ([#2945](https://github.com/nuxt/ui/issues/2945)) ([8cf9f27](https://github.com/nuxt/ui/commit/8cf9f27d537bad5ffe4e136f52ff71548a451c5f))
## [2.20.0](https://github.com/nuxt/ui/compare/v2.19.2...v2.20.0) (2024-12-09)
### ⚠ BREAKING CHANGES

View File

@@ -30,16 +30,6 @@ Read more on [ui.nuxt.com](https://ui.nuxt.com)
npx nuxi@latest module add ui
```
If you want latest updates, please use `@nuxt/ui-edge` in your `package.json`:
```json
{
"devDependencies": {
"@nuxt/ui": "npm:@nuxt/ui-edge@latest"
}
}
```
## Documentation
Visit https://ui.nuxt.com to explore the documentation.

View File

@@ -0,0 +1,15 @@
<template>
<UInput
v-model="name"
:maxlength="maxLength"
>
<template #trailing>
<span class="text-xs text-gray-500 dark:text-gray-400">{{ name.length }}/{{ maxLength }}</span>
</template>
</UInput>
</template>
<script setup lang="ts">
const name = ref('')
const maxLength = 10
</script>

View File

@@ -243,19 +243,21 @@ export default defineNuxtConfig({
})
```
## Edge
## Continuous Releases
To use the latest updates pushed on the [`dev`](https://github.com/nuxt/ui/tree/dev) branch, you can use `@nuxt/ui-edge`.
Nuxt UI uses [pkg.pr.new](https://github.com/stackblitz-labs/pkg.pr.new) for continuous preview releases, providing developers with instant access to the latest features and bug fixes without waiting for official releases.
Update your `package.json` to the following:
Preview releases are automatically generated for every commit to the `dev` branch and pull requests targeting the `dev` branch. To use it into your project, replace the version in your `package.json` with the commit hash or pull request number.
```diff [package.json]
{
"devDependencies": {
- "@nuxt/ui": "^2.11.0"
+ "@nuxt/ui": "npm:@nuxt/ui-edge@latest"
"dependencies": {
- "@nuxt/ui": "^2.21.0",
+ "@nuxt/ui": "https://pkg.pr.new/@nuxt/ui@bf1c9e7",
}
}
```
Then run `pnpm install`, `yarn install` or `npm install`.
::note
**pkg.pr.new** will automatically comment on PRs with the installation URL, making it easy to test changes.
::

View File

@@ -221,6 +221,52 @@ export default defineAppConfig({
})
```
### Extend Tailwind Merge
Tailwind Merge is a library that allows you to efficiently merge Tailwind CSS classes. It is used by this module to merge the classes from the `ui` prop, the `class` attribute, and the default classes.
::callout{icon="i-heroicons-light-bulb" to="https://github.com/dcastil/tailwind-merge" target="_blank"}
Learn more about Tailwind Merge.
::
By default, Tailwind Merge doesn't handle custom Tailwind CSS configuration like custom colors, spacing, or other utilities you may have defined. You'll need to extend it to handle your custom configuration.
You can extend Tailwind Merge by using the `tailwindMerge` option in your `app.config.ts`:
::code-group
```ts [app.config.ts]
export default defineAppConfig({
ui: {
tailwindMerge: {
extend: {
theme: {
spacing: ['sm', 'md', 'lg', 'xl', '2xl']
}
}
}
}
})
```
```ts [tailwind.config.ts]
import type { Config } from 'tailwindcss'
export default <Partial<Config>>{
theme: {
extend: {
spacing: {
sm: '0.5rem',
md: '1rem',
lg: '1.5rem',
xl: '2rem',
'2xl': '2.5rem'
}
}
}
}
```
::
## Dark mode
All the components are styled with dark mode in mind.
@@ -343,6 +389,12 @@ export default defineAppConfig({
loadingIcon: 'i-octicon-sync-24'
}
},
inputMenu: {
default: {
selectedIcon: 'i-octicon-check-24',
trailingIcon: 'i-octicon-chevron-down-24'
}
},
select: {
default: {
loadingIcon: 'i-octicon-sync-24',
@@ -378,6 +430,9 @@ export default defineAppConfig({
sortButton: {
icon: 'i-octicon-arrow-switch-24'
},
expandButton: {
icon: 'i-octicon-chevron-down-24'
},
loadingState: {
icon: 'i-octicon-sync-24'
},
@@ -411,6 +466,21 @@ export default defineAppConfig({
default: {
divider: 'i-octicon-chevron-right-24'
}
},
carousel: {
default: {
prevButton: {
icon: 'i-octicon-chevron-left-24'
},
nextButton: {
icon: 'i-octicon-chevron-right-24'
}
}
},
toggle: {
default: {
loadingIcon: 'i-octicon-sync-24'
}
}
}
})

View File

@@ -49,18 +49,22 @@ defineShortcuts({
Shortcuts keys are written as the literal keyboard key value. Combinations are made with `_` separator. Chained shortcuts are made with `-` separator.
Modifiers are also available:
- `meta`: acts as `Command` for MacOS and `Control` for others
- `ctrl`: acts as `Control`
- `shift`: acts as `Shift` and is only necessary for alphabetic keys
| Modifier | Description |
|----------|-------------|
| `meta` | Acts as `Command (⌘)` on macOS and `Control (Ctrl)` on Windows/Linux. |
| `ctrl` | Represents the `Control (Ctrl)` key across all operating systems. |
| `shift` | Represents the `Shift` key, only needed for alphabetic keys (e.g., `shift_e`). |
Examples of keys:
- `escape`: will trigger by hitting `Esc`
- `meta_k`: will trigger by hitting `⌘` and `K` at the same time on MacOS, and `Ctrl` and `K` on Windows and Linux
- `ctrl_k`: will trigger by hitting `Ctrl` and `K` at the same time on MacOS, Windows and Linux
- `shift_e`: will trigger by hitting `Shift` and `E` at the same time on MacOS, Windows and Linux
- `?`: will trigger by hitting `?` on some keyboard layouts, or for example `Shift` and `/`, which results in `?` on US Mac keyboards
- `g-d`: will trigger by hitting `g` then `d` with a maximum delay of 800ms by default
- `arrowleft`: will trigger by hitting `` (also: `arrowright`, `arrowup`, `arrowdown`)
| Shortcut Key | Action |
|---------------|--------|
| `escape` | Triggers when `Esc` is pressed |
| `meta_k` | `⌘ + K` on Mac, `Ctrl + K` on Windows/Linux |
| `ctrl_k` | Triggers `Ctrl + K` on all OS |
| `shift_e` | Triggers `Shift + E` on all OS |
| `?` | Triggers `?` (Shift + `/` on US Mac keyboards) |
| `g-d` | Triggers when `g` then `d` are pressed within 800ms |
| `arrowleft` | Triggers when `←` is pressed (also: `arrowright`, `arrowup`, `arrowdown`) |
::callout{icon="i-heroicons-light-bulb"}
For a complete list of available shortcut keys, refer to the [`KeyboardEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values) API docs. Note the `KeyboardEvent.key` has to be written in lowercase.

View File

@@ -173,6 +173,13 @@ baseProps:
---
::
### Limit
Use the `maxlength` prop to limit the length of the Input.
:component-example{component="input-example-max-length"}
## Slots
### `leading`

View File

@@ -3,28 +3,29 @@
"private": true,
"type": "module",
"dependencies": {
"@iconify-json/heroicons": "^1.2.1",
"@iconify-json/simple-icons": "^1.2.14",
"@iconify-json/vscode-icons": "^1.2.3",
"@iconify-json/heroicons": "^1.2.2",
"@iconify-json/lucide": "^1.2.28",
"@iconify-json/simple-icons": "^1.2.27",
"@iconify-json/vscode-icons": "^1.2.16",
"@nuxt/content": "^2.13.4",
"@nuxt/fonts": "^0.10.3",
"@nuxt/image": "^1.8.1",
"@nuxt/image": "^1.9.0",
"@nuxt/ui": "latest",
"@nuxt/ui-pro": "^1.5.0",
"@nuxt/ui-pro": "^1.7.0",
"@nuxtjs/plausible": "^1.2.0",
"@octokit/rest": "^21.0.2",
"@vueuse/nuxt": "^12.0.0",
"@octokit/rest": "^21.1.1",
"@vueuse/nuxt": "^12.8.2",
"date-fns": "^4.1.0",
"joi": "^17.13.3",
"nuxt": "^3.14.1592",
"nuxt": "^3.16.0",
"nuxt-cloudflare-analytics": "^1.0.8",
"nuxt-component-meta": "^0.9.0",
"nuxt-og-image": "^3.1.1",
"prettier": "^3.4.2",
"nuxt-component-meta": "^0.10.0",
"nuxt-og-image": "^4.2.0",
"prettier": "^3.5.3",
"ufo": "^1.5.4",
"v-calendar": "^3.1.2",
"valibot": "^0.42.1",
"yup": "^1.5.0",
"zod": "^3.23.8"
"yup": "^1.6.1",
"zod": "^3.24.2"
}
}

View File

@@ -423,6 +423,7 @@ const { data: module } = await useFetch<{
username: string
}[]
}>('https://api.nuxt.com/modules/ui', {
key: 'stats',
transform: ({ stats, contributors }) => ({ stats, contributors })
})

View File

@@ -41,8 +41,8 @@ if (!page.value) {
throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true })
}
const { data: releases } = await useFetch('/api/releases.json')
const { data: pulls } = await useLazyFetch('/api/pulls.json', { default: () => [] })
const { data: releases } = await useFetch('/api/releases.json', { key: 'releases-list' })
const { data: pulls } = await useLazyFetch('/api/pulls.json', { default: () => [], key: 'pulls-list' })
const dates = computed(() => {
const first = releases.value[releases.value.length - 1]

View File

@@ -1,8 +1,8 @@
{
"name": "@nuxt/ui",
"description": "A UI Library for Modern Web Apps, powered by Vue & Tailwind CSS.",
"version": "2.20.0",
"packageManager": "pnpm@9.15.0",
"version": "2.21.1",
"packageManager": "pnpm@10.6.1",
"repository": "nuxt/ui",
"homepage": "https://ui.nuxt.com",
"type": "module",
@@ -32,57 +32,58 @@
"test": "vitest"
},
"dependencies": {
"@headlessui/tailwindcss": "^0.2.1",
"@headlessui/tailwindcss": "^0.2.2",
"@headlessui/vue": "^1.7.23",
"@iconify-json/heroicons": "^1.2.1",
"@nuxt/icon": "^1.9.1",
"@nuxt/kit": "^3.14.1592",
"@iconify-json/heroicons": "^1.2.2",
"@nuxt/icon": "^1.10.3",
"@nuxt/kit": "^3.16.0",
"@nuxtjs/color-mode": "^3.5.2",
"@nuxtjs/tailwindcss": "^6.12.2",
"@nuxtjs/tailwindcss": "^6.13.1",
"@popperjs/core": "^2.11.8",
"@tailwindcss/aspect-ratio": "^0.4.2",
"@tailwindcss/container-queries": "^0.1.1",
"@tailwindcss/forms": "^0.5.9",
"@tailwindcss/typography": "^0.5.15",
"@vueuse/core": "^12.0.0",
"@vueuse/integrations": "^12.0.0",
"@vueuse/math": "^12.0.0",
"@tailwindcss/forms": "^0.5.10",
"@tailwindcss/typography": "^0.5.16",
"@vueuse/core": "^12.8.2",
"@vueuse/integrations": "^12.8.2",
"@vueuse/math": "^12.8.2",
"defu": "^6.1.4",
"fuse.js": "^7.0.0",
"ohash": "^1.1.4",
"pathe": "^1.1.2",
"fuse.js": "^7.1.0",
"ohash": "^2.0.11",
"pathe": "^2.0.3",
"scule": "^1.3.0",
"tailwind-merge": "^2.5.5",
"tailwindcss": "^3.4.16"
"tailwind-merge": "^2.6.0",
"tailwindcss": "^3.4.17"
},
"devDependencies": {
"@nuxt/eslint-config": "^0.7.2",
"@nuxt/eslint-config": "^1.1.0",
"@nuxt/module-builder": "^0.8.4",
"@nuxt/test-utils": "^3.15.1",
"@release-it/conventional-changelog": "^9.0.3",
"@standard-schema/spec": "^1.0.0",
"@nuxt/test-utils": "^3.17.1",
"@release-it/conventional-changelog": "^10.0.0",
"@vue/test-utils": "^2.4.6",
"eslint": "^9.16.0",
"happy-dom": "^14.12.3",
"eslint": "^9.22.0",
"happy-dom": "^17.1.8",
"joi": "^17.13.3",
"nuxt": "^3.14.1592",
"release-it": "^17.10.0",
"nuxt": "^3.16.0",
"release-it": "^18.1.2",
"superstruct": "^2.0.2",
"typescript": "^5.6.3",
"unbuild": "^2.0.0",
"valibot": "^0.42.1",
"valibot30": "npm:valibot@0.30.0",
"valibot31": "npm:valibot@0.31.0",
"vitest": "^2.1.8",
"vitest": "^3.0.8",
"vitest-environment-nuxt": "^1.0.1",
"vue-tsc": "^2.1.10",
"yup": "^1.5.0",
"zod": "^3.23.8"
"yup": "^1.6.1",
"zod": "^3.24.2"
},
"resolutions": {
"@nuxt/ui": "workspace:*",
"@nuxt/content": "2.13.2",
"@nuxtjs/mdc": "0.9.0",
"chokidar": "3.6.0",
"vue-tsc": "2.1.10",
"typescript": "5.6.3"
}
}

View File

@@ -9,6 +9,6 @@
},
"dependencies": {
"@nuxt/ui": "latest",
"nuxt": "^3.14.1592"
"nuxt": "^3.16.0"
}
}

7518
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,6 @@
"enabled": true
},
"ignoreDeps": [
"happy-dom",
"valibot30",
"valibot31"
],
@@ -24,5 +23,6 @@
}, {
"matchDepTypes": ["resolutions"],
"enabled": false
}]
}],
"postUpdateOptions": ["pnpmDedupe"]
}

View File

@@ -1,36 +0,0 @@
import { promises as fsp } from 'node:fs'
import { resolve } from 'node:path'
import { execSync } from 'node:child_process'
async function loadPackage(dir: string) {
const pkgPath = resolve(dir, 'package.json')
const data = JSON.parse(await fsp.readFile(pkgPath, 'utf-8').catch(() => '{}'))
const save = () => fsp.writeFile(pkgPath, JSON.stringify(data, null, 2) + '\n')
return {
dir,
data,
save
}
}
async function main() {
const pkg = await loadPackage(process.cwd())
const commit = execSync('git rev-parse --short HEAD').toString('utf-8').trim()
const date = Math.round(Date.now() / (1000 * 60))
pkg.data.name = `${pkg.data.name}-edge`
pkg.data.version = `${pkg.data.version}-${date}.${commit}`
pkg.save()
}
main().catch((err) => {
console.error(err)
process.exit(1)
})

View File

@@ -1,19 +0,0 @@
#!/bin/bash
# Restore all git changes
git restore -s@ -SW -- .
# Bump versions to edge
pnpm jiti ./scripts/bump-edge
# Update token
if [[ ! -z ${NODE_AUTH_TOKEN} ]] ; then
echo "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}" >> ~/.npmrc
echo "registry=https://registry.npmjs.org/" >> ~/.npmrc
echo "always-auth=true" >> ~/.npmrc
npm whoami
fi
# Release package
echo "Publishing @nuxt/ui"
npm publish -q --access public

View File

@@ -1,5 +1,6 @@
import { createRequire } from 'node:module'
import { defineNuxtModule, installModule, addComponentsDir, addImportsDir, createResolver, addPlugin } from '@nuxt/kit'
import type { ConfigExtension, DefaultClassGroupIds, DefaultThemeGroupIds } from 'tailwind-merge'
import { name, version } from '../package.json'
import createTemplates from './templates'
import type * as config from './runtime/ui.config'
@@ -20,6 +21,7 @@ type UI = {
gray?: string
colors?: string[]
strategy?: Strategy
tailwindMerge?: ConfigExtension<DefaultClassGroupIds, DefaultThemeGroupIds>
[key: string]: any
} & DeepPartial<typeof config, string | number | boolean>
@@ -41,6 +43,11 @@ export interface ModuleOptions {
*/
global?: boolean
/**
* @default true
*/
colorMode?: boolean
safelistColors?: string[]
/**
* Disables the global css styles added by the module.
@@ -59,6 +66,7 @@ export default defineNuxtModule<ModuleOptions>({
},
defaults: {
prefix: 'U',
colorMode: true,
safelistColors: ['primary'],
disableGlobalStyles: false
},
@@ -81,7 +89,9 @@ export default defineNuxtModule<ModuleOptions>({
// Modules
await installModule('@nuxt/icon')
await installModule('@nuxtjs/color-mode', { classSuffix: '' })
if (options.colorMode) {
await installModule('@nuxtjs/color-mode', { classSuffix: '' })
}
await installTailwind(options, nuxt, resolve)
// Plugins

View File

@@ -18,7 +18,7 @@
:class="[ui.th.base, ui.th.padding, ui.th.color, ui.th.font, ui.th.size, column.key === 'select' && ui.checkbox.padding, column.class]"
:aria-sort="getAriaSort(column)"
>
<slot v-if="!singleSelect && modelValue && (column.key === 'select' || shouldRenderColumnInFirstPlace(index, 'select'))" name="select-header" :indeterminate="indeterminate" :checked="isAllRowChecked" :change="onChange">
<slot v-if="!singleSelect && modelValue && column.key === 'select'" name="select-header" :indeterminate="indeterminate" :checked="isAllRowChecked" :change="onChange">
<UCheckbox
:model-value="isAllRowChecked"
:indeterminate="indeterminate"
@@ -93,19 +93,17 @@
/>
</td>
<td v-for="(column, subIndex) in columns" :key="subIndex" :class="[ui.td.base, ui.td.padding, ui.td.color, ui.td.font, ui.td.size, column?.rowClass, row[column.key]?.class, column.key === 'select' && ui.checkbox.padding]">
<slot v-if="modelValue && (column.key === 'select' || shouldRenderColumnInFirstPlace(subIndex, 'select')) " name="select-data" :checked="isSelected(row)" :change="(ev: boolean) => onChangeCheckbox(ev, row)">
<slot v-if="modelValue && column.key === 'select' " name="select-data" :checked="isSelected(row)" :change="(ev: boolean) => onChangeCheckbox(ev, row)">
<UCheckbox
:model-value="isSelected(row)"
v-bind="ui.default.checkbox"
aria-label="Select row"
@change="onChangeCheckbox($event, row)"
@click.capture.stop="() => onSelect(row)"
/>
</slot>
<slot
v-else
:key="retriggerSlot"
:name="`${column.key}-data`"
:column="column"
:row="row"
@@ -133,18 +131,18 @@
</template>
<script lang="ts">
import { computed, defineComponent, ref, toRaw, toRef, watch } from 'vue'
import { computed, defineComponent, toRaw, toRef } from 'vue'
import type { PropType, AriaAttributes } from 'vue'
import { upperFirst } from 'scule'
import { defu } from 'defu'
import { useVModel } from '@vueuse/core'
import { isEqual } from 'ohash'
import { isEqual } from 'ohash/utils'
import UIcon from '../elements/Icon.vue'
import UButton from '../elements/Button.vue'
import UProgress from '../elements/Progress.vue'
import UCheckbox from '../forms/Checkbox.vue'
import { useUI } from '../../composables/useUI'
import { mergeConfig, get } from '../../utils'
import { get, mergeConfig } from '../../utils'
import type { TableRow, TableColumn, Strategy, Button, ProgressColor, ProgressAnimation, DeepPartial, Expanded } from '../../types/index'
// @ts-expect-error
import appConfig from '#build/app.config'
@@ -274,7 +272,30 @@ export default defineComponent({
setup(props, { emit, attrs: $attrs }) {
const { ui, attrs } = useUI('table', toRef(props, 'ui'), config, toRef(props, 'class'))
const columns = computed(() => props.columns ?? Object.keys(props.rows[0] ?? {}).map(key => ({ key, label: upperFirst(key), sortable: false, class: undefined, sort: defaultSort }) as TableColumn))
const columns = computed(() => {
const defaultColumns = props.columns ?? (
Object.keys(props.rows[0]).map(key => ({
key,
label: upperFirst(key),
sortable: false,
class: undefined,
sort: defaultSort
}))
) as TableColumn[]
const hasColumnSelect = defaultColumns.find(v => v.key === 'select')
if (hasColumnSelect || !props.modelValue) {
return defaultColumns
}
return [{
key: 'select',
sortable: false,
class: undefined,
sort: defaultSort
}, ...defaultColumns]
})
const sort = useVModel(props, 'sort', emit, { passive: true, defaultValue: defu({}, props.sort, { column: null, direction: 'asc' }) })
const expand = useVModel(props, 'expand', emit, {
@@ -285,8 +306,6 @@ export default defineComponent({
})
})
const retriggerSlot = ref(null)
const savedSort = { column: sort.value.column, direction: null }
const rows = computed(() => {
@@ -422,8 +441,7 @@ export default defineComponent({
if (checked) {
selected.value = props.singleSelect ? [row] : [...selected.value, row]
} else {
const index = selected.value.findIndex(item => compare(item, row))
selected.value.splice(index, 1)
selected.value = selected.value.filter(value => !compare(toRaw(value), toRaw(row)))
}
}
@@ -435,13 +453,6 @@ export default defineComponent({
return expand.value?.openedRows ? expand.value.openedRows.some(openedRow => compare(openedRow, row)) : false
}
function shouldRenderColumnInFirstPlace(index: number, key: string) {
if (!props.columns) {
return index === 0
}
return index === 0 && !props.columns.find(col => col.key === key)
}
function toggleOpened(row: TableRow) {
expand.value = {
openedRows: isExpanded(row) ? expand.value.openedRows.filter(v => !compare(v, row)) : props.multipleExpand ? [...expand.value.openedRows, row] : [row],
@@ -469,12 +480,6 @@ export default defineComponent({
return undefined
}
watch(rows, () => {
retriggerSlot.value = new Date()
}, {
deep: true
})
return {
// eslint-disable-next-line vue/no-dupe-keys
ui,
@@ -501,9 +506,7 @@ export default defineComponent({
getRowData,
toggleOpened,
getAriaSort,
isExpanded,
shouldRenderColumnInFirstPlace,
retriggerSlot
isExpanded
}
}
})

View File

@@ -14,7 +14,7 @@
{{ title }}
</slot>
</p>
<div v-if="description || $slots.description" :class="twMerge(ui.description, !title && !$slots.title && 'mt-0 leading-5')">
<div v-if="description || $slots.description" :class="twMerge(ui.description, !title && !$slots.title && ui.descriptionOnly)">
<slot name="description" :description="description">
{{ description }}
</slot>
@@ -42,13 +42,13 @@
<script lang="ts">
import { computed, toRef, defineComponent } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import UIcon from '../elements/Icon.vue'
import UAvatar from '../elements/Avatar.vue'
import UButton from '../elements/Button.vue'
import { useUI } from '../../composables/useUI'
import type { Avatar, Button, AlertColor, AlertVariant, AlertAction, Strategy, DeepPartial } from '../../types/index'
import { mergeConfig } from '../../utils'
import { mergeConfig, twMerge } from '../../utils'
// @ts-expect-error
import appConfig from '#build/app.config'
import { alert } from '#ui/ui.config'

View File

@@ -23,10 +23,10 @@
<script lang="ts">
import { defineComponent, ref, computed, toRef, watch } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import UIcon from '../elements/Icon.vue'
import { useUI } from '../../composables/useUI'
import { mergeConfig } from '../../utils'
import { mergeConfig, twMerge } from '../../utils'
import type { AvatarSize, AvatarChipColor, AvatarChipPosition, Strategy, DeepPartial } from '../../types/index'
// @ts-expect-error
import appConfig from '#build/app.config'

View File

@@ -1,8 +1,8 @@
import { h, cloneVNode, computed, toRef, defineComponent } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import { useUI } from '../../composables/useUI'
import { mergeConfig, getSlotsChildren } from '../../utils'
import { getSlotsChildren, mergeConfig, twMerge } from '../../utils'
import type { AvatarSize, DeepPartial, Strategy } from '../../types/index'
import UAvatar from './Avatar.vue'
// @ts-expect-error

View File

@@ -19,10 +19,10 @@
<script lang="ts">
import { computed, toRef, defineComponent } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import UIcon from '../elements/Icon.vue'
import { useUI } from '../../composables/useUI'
import { mergeConfig } from '../../utils'
import { mergeConfig, twMerge } from '../../utils'
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
import type { BadgeColor, BadgeSize, BadgeVariant, DeepPartial, Strategy } from '../../types/index'
// @ts-expect-error

View File

@@ -19,11 +19,11 @@
<script lang="ts">
import { computed, defineComponent, toRef } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import UIcon from '../elements/Icon.vue'
import ULink from '../elements/Link.vue'
import { useUI } from '../../composables/useUI'
import { mergeConfig, nuxtLinkProps, getNuxtLinkProps } from '../../utils'
import { getNuxtLinkProps, mergeConfig, nuxtLinkProps, twMerge } from '../../utils'
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
import type { ButtonColor, ButtonSize, ButtonVariant, DeepPartial, Strategy } from '../../types/index'
// @ts-expect-error

View File

@@ -1,8 +1,8 @@
import { h, computed, toRef, defineComponent } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import { useUI } from '../../composables/useUI'
import { mergeConfig, getSlotsChildren } from '../../utils'
import { getSlotsChildren, mergeConfig, twMerge } from '../../utils'
import { useProvideButtonGroup } from '../../composables/useButtonGroup'
import type { ButtonSize, DeepPartial, Strategy } from '../../types/index'
// @ts-expect-error

View File

@@ -58,9 +58,8 @@
<script lang="ts">
import { ref, toRef, computed, defineComponent } from 'vue'
import type { PropType } from 'vue'
import { twMerge } from 'tailwind-merge'
import { useScroll, useResizeObserver, useElementSize } from '@vueuse/core'
import { mergeConfig } from '../../utils'
import { mergeConfig, twMerge } from '../../utils'
import UButton from '../elements/Button.vue'
import type { Strategy, Button, DeepPartial } from '../../types/index'
import { useUI } from '../../composables/useUI'

View File

@@ -60,13 +60,13 @@ import { defineComponent, ref, computed, watch, toRef, onMounted, resolveCompone
import type { PropType } from 'vue'
import { Menu as HMenu, MenuButton as HMenuButton, MenuItems as HMenuItems, MenuItem as HMenuItem, provideUseId } from '@headlessui/vue'
import { defu } from 'defu'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import UIcon from '../elements/Icon.vue'
import UAvatar from '../elements/Avatar.vue'
import UKbd from '../elements/Kbd.vue'
import { useUI } from '../../composables/useUI'
import { usePopper } from '../../composables/usePopper'
import { mergeConfig, getNuxtLinkProps } from '../../utils'
import { getNuxtLinkProps, mergeConfig, twMerge } from '../../utils'
import type { DeepPartial, DropdownItem, PopperOptions, Strategy } from '../../types/index'
// @ts-expect-error
import appConfig from '#build/app.config'

View File

@@ -7,9 +7,9 @@
<script lang="ts">
import { toRef, defineComponent, computed } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import { useUI } from '../../composables/useUI'
import { mergeConfig } from '../../utils'
import { mergeConfig, twMerge } from '../../utils'
import type { DeepPartial, KbdSize, Strategy } from '../../types/index'
// @ts-expect-error
import appConfig from '#build/app.config'

View File

@@ -32,7 +32,7 @@
</template>
<script lang="ts">
import { isEqual, diff } from 'ohash'
import { isEqual, diff } from 'ohash/utils'
import { type PropType, defineComponent } from 'vue'
import { nuxtLinkProps } from '../../utils'
@@ -74,14 +74,18 @@ export default defineComponent({
}
},
setup(props) {
function isPartiallyEqual(item1, item2) {
function isPartiallyEqual(item1: any, item2: any) {
const diffedKeys = diff(item1, item2).reduce((filtered, q) => {
if (q.type === 'added') {
filtered.push(q.key)
filtered.add(q.key)
}
return filtered
}, [])
return isEqual(item1, item2, { excludeKeys: key => diffedKeys.includes(key) })
}, new Set<string>())
const item1Filtered = Object.fromEntries(Object.entries(item1).filter(([key]) => !diffedKeys.has(key)))
const item2Filtered = Object.fromEntries(Object.entries(item2).filter(([key]) => !diffedKeys.has(key)))
return isEqual(item1Filtered, item2Filtered)
}
function resolveLinkClass(route, $route, { isActive, isExactActive }: { isActive: boolean, isExactActive: boolean }) {

View File

@@ -3,7 +3,7 @@ import type { ComputedRef, VNode, SlotsType, PropType } from 'vue'
import { twJoin } from 'tailwind-merge'
import UIcon from '../elements/Icon.vue'
import { useUI } from '../../composables/useUI'
import { mergeConfig, getSlotsChildren } from '../../utils'
import { getSlotsChildren, mergeConfig } from '../../utils'
import type { DeepPartial, Strategy, MeterSize } from '../../types/index'
import type Meter from './Meter.vue'
// @ts-expect-error

View File

@@ -32,10 +32,10 @@
<script lang="ts">
import { computed, toRef, defineComponent } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import { useUI } from '../../composables/useUI'
import { useFormGroup } from '../../composables/useFormGroup'
import { mergeConfig } from '../../utils'
import { mergeConfig, twMerge } from '../../utils'
import type { DeepPartial, Strategy } from '../../types/index'
// @ts-expect-error
import appConfig from '#build/app.config'

View File

@@ -13,6 +13,7 @@ import type { ObjectSchema as YupObjectSchema, ValidationError as YupError } fro
import type { BaseSchema as ValibotSchema30, BaseSchemaAsync as ValibotSchemaAsync30 } from 'valibot30'
import type { GenericSchema as ValibotSchema31, GenericSchemaAsync as ValibotSchemaAsync31, SafeParser as ValibotSafeParser31, SafeParserAsync as ValibotSafeParserAsync31 } from 'valibot31'
import type { GenericSchema as ValibotSchema, GenericSchemaAsync as ValibotSchemaAsync, SafeParser as ValibotSafeParser, SafeParserAsync as ValibotSafeParserAsync } from 'valibot'
import type { StandardSchemaV1 } from '@standard-schema/spec'
import type { Struct } from 'superstruct'
import type { FormError, FormEvent, FormEventType, FormSubmitEvent, FormErrorEvent, Form, ValidateReturnSchema } from '../../types/form'
import { useId } from '#imports'
@@ -33,6 +34,7 @@ type Schema = PropType<ZodSchema>
| PropType<ValibotSafeParser31<any, any> | ValibotSafeParserAsync31<any, any>>
| PropType<ValibotSchema | ValibotSchemaAsync>
| PropType<ValibotSafeParser<any, any> | ValibotSafeParserAsync<any, any>> | PropType<Struct<any, any>>
| PropType<StandardSchemaV1>
export default defineComponent({
props: {
@@ -220,6 +222,35 @@ function isZodSchema(schema: any): schema is ZodSchema {
return schema.parse !== undefined
}
export function isStandardSchema(schema: any): schema is StandardSchemaV1 {
return '~standard' in schema
}
export async function validateStandardSchema(
state: any,
schema: StandardSchemaV1
): Promise<ValidateReturnSchema<typeof state>> {
const result = await schema['~standard'].validate(state)
if (!result.issues || result.issues.length === 0) {
const output = ('value' in result ? result.value : null)
return {
errors: null,
result: output
}
}
const errors = result.issues.map(issue => ({
path: issue.path?.map(item => typeof item === 'object' ? item.key : item).join('.') || '',
message: issue.message
}))
return {
errors,
result: null
}
}
async function validateValibotSchema(
state: any,
schema: ValibotSchema30 | ValibotSchemaAsync30 | ValibotSchema31 | ValibotSchemaAsync31 | ValibotSafeParser31<any, any> | ValibotSafeParserAsync31<any, any> | ValibotSchema | ValibotSchemaAsync | ValibotSafeParser<any, any> | ValibotSafeParserAsync<any, any>
@@ -346,7 +377,9 @@ async function validateYupSchema(
}
function parseSchema(state: any, schema: Schema): Promise<ValidateReturnSchema<typeof state>> {
if (isZodSchema(schema)) {
if (isStandardSchema(schema)) {
return validateStandardSchema(state, schema)
} else if (isZodSchema(schema)) {
return validateZodSchema(state, schema)
} else if (isJoiSchema(schema)) {
return validateJoiSchema(state, schema)

View File

@@ -33,12 +33,12 @@
<script lang="ts">
import { ref, computed, toRef, onMounted, defineComponent } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import { defu } from 'defu'
import UIcon from '../elements/Icon.vue'
import { useUI } from '../../composables/useUI'
import { useFormGroup } from '../../composables/useFormGroup'
import { mergeConfig, looseToNumber } from '../../utils'
import { looseToNumber, mergeConfig, twMerge } from '../../utils'
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
import type { InputSize, InputColor, InputVariant, Strategy, DeepPartial } from '../../types/index'
// @ts-expect-error

View File

@@ -103,14 +103,14 @@ import {
} from '@headlessui/vue'
import { computedAsync, useDebounceFn } from '@vueuse/core'
import { defu } from 'defu'
import { twMerge, twJoin } from 'tailwind-merge'
import { isEqual } from 'ohash'
import { twJoin } from 'tailwind-merge'
import { isEqual } from 'ohash/utils'
import UIcon from '../elements/Icon.vue'
import UAvatar from '../elements/Avatar.vue'
import { useUI } from '../../composables/useUI'
import { usePopper } from '../../composables/usePopper'
import { useFormGroup } from '../../composables/useFormGroup'
import { get, mergeConfig } from '../../utils'
import { get, mergeConfig, twMerge } from '../../utils'
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
import type { InputSize, InputColor, InputVariant, PopperOptions, Strategy, DeepPartial } from '../../types/index'
// @ts-expect-error

View File

@@ -31,10 +31,10 @@
<script lang="ts">
import { computed, defineComponent, inject, toRef } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import { useUI } from '../../composables/useUI'
import { useFormGroup } from '../../composables/useFormGroup'
import { mergeConfig } from '../../utils'
import { mergeConfig, twMerge } from '../../utils'
import type { DeepPartial, Strategy } from '../../types/index'
// @ts-expect-error
import appConfig from '#build/app.config'

View File

@@ -34,7 +34,7 @@ import { computed, defineComponent, provide, toRef } from 'vue'
import type { PropType } from 'vue'
import { useUI } from '../../composables/useUI'
import { useFormGroup } from '../../composables/useFormGroup'
import { mergeConfig, get } from '../../utils'
import { get, mergeConfig } from '../../utils'
import type { DeepPartial, Strategy } from '../../types/index'
import URadio from './Radio.vue'
// @ts-expect-error

View File

@@ -22,10 +22,10 @@
<script lang="ts">
import { computed, toRef, defineComponent } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import { useUI } from '../../composables/useUI'
import { useFormGroup } from '../../composables/useFormGroup'
import { mergeConfig } from '../../utils'
import { mergeConfig, twMerge } from '../../utils'
import type { RangeSize, RangeColor, Strategy, DeepPartial } from '../../types/index'
// @ts-expect-error
import appConfig from '#build/app.config'

View File

@@ -55,11 +55,11 @@
<script lang="ts">
import { computed, toRef, defineComponent } from 'vue'
import type { PropType, ComputedRef } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import UIcon from '../elements/Icon.vue'
import { useUI } from '../../composables/useUI'
import { useFormGroup } from '../../composables/useFormGroup'
import { mergeConfig, get } from '../../utils'
import { get, mergeConfig, twMerge } from '../../utils'
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
import type { SelectSize, SelectColor, SelectVariant, Strategy, DeepPartial } from '../../types/index'
// @ts-expect-error

View File

@@ -16,6 +16,7 @@
:value="modelValue"
:required="required"
:class="uiMenu.required"
:form="inputTargetForm"
tabindex="-1"
aria-hidden="true"
>
@@ -139,14 +140,14 @@ import {
} from '@headlessui/vue'
import { computedAsync, useDebounceFn } from '@vueuse/core'
import { defu } from 'defu'
import { twMerge, twJoin } from 'tailwind-merge'
import { isEqual } from 'ohash'
import { twJoin } from 'tailwind-merge'
import { isEqual } from 'ohash/utils'
import UIcon from '../elements/Icon.vue'
import UAvatar from '../elements/Avatar.vue'
import { useUI } from '../../composables/useUI'
import { usePopper } from '../../composables/usePopper'
import { useFormGroup } from '../../composables/useFormGroup'
import { get, mergeConfig } from '../../utils'
import { get, mergeConfig, twMerge } from '../../utils'
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
import type { SelectSize, SelectColor, SelectVariant, PopperOptions, Strategy, DeepPartial } from '../../types/index'
// @ts-expect-error
@@ -314,6 +315,10 @@ export default defineComponent({
type: Array,
default: null
},
inputTargetForm: {
type: String,
default: null
},
popper: {
type: Object as PropType<PopperOptions>,
default: () => ({})

View File

@@ -23,11 +23,11 @@
<script lang="ts">
import { ref, computed, toRef, watch, onMounted, nextTick, defineComponent } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import { defu } from 'defu'
import { useUI } from '../../composables/useUI'
import { useFormGroup } from '../../composables/useFormGroup'
import { mergeConfig, looseToNumber } from '../../utils'
import { looseToNumber, mergeConfig, twMerge } from '../../utils'
import type { TextareaSize, TextareaColor, TextareaVariant, Strategy, DeepPartial } from '../../types/index'
// @ts-expect-error
import appConfig from '#build/app.config'

View File

@@ -33,11 +33,11 @@
import { computed, toRef, defineComponent } from 'vue'
import type { PropType } from 'vue'
import { Switch as HSwitch, provideUseId } from '@headlessui/vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import UIcon from '../elements/Icon.vue'
import { useUI } from '../../composables/useUI'
import { useFormGroup } from '../../composables/useFormGroup'
import { mergeConfig } from '../../utils'
import { mergeConfig, twMerge } from '../../utils'
import type { ToggleSize, ToggleColor, Strategy, DeepPartial } from '../../types/index'
// @ts-expect-error
import appConfig from '#build/app.config'

View File

@@ -19,9 +19,9 @@
<script lang="ts">
import { computed, toRef, defineComponent } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import { useUI } from '../../composables/useUI'
import { mergeConfig } from '../../utils'
import { mergeConfig, twMerge } from '../../utils'
import type { DeepPartial, Strategy } from '../../types/index'
// @ts-expect-error
import appConfig from '#build/app.config'

View File

@@ -7,9 +7,9 @@
<script lang="ts">
import { computed, toRef, defineComponent } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import { useUI } from '../../composables/useUI'
import { mergeConfig } from '../../utils'
import { mergeConfig, twMerge } from '../../utils'
import type { DeepPartial, Strategy } from '../../types/index'
// @ts-expect-error
import appConfig from '#build/app.config'

View File

@@ -21,11 +21,11 @@
<script lang="ts">
import { toRef, computed, defineComponent } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import UIcon from '../elements/Icon.vue'
import UAvatar from '../elements/Avatar.vue'
import { useUI } from '../../composables/useUI'
import { mergeConfig } from '../../utils'
import { mergeConfig, twMerge } from '../../utils'
import type { Avatar, DeepPartial, DividerSize, Strategy } from '../../types/index'
// @ts-expect-error
import appConfig from '#build/app.config'

View File

@@ -5,9 +5,9 @@
<script lang="ts">
import { computed, toRef, defineComponent } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import { useUI } from '../../composables/useUI'
import { mergeConfig } from '../../utils'
import { mergeConfig, twMerge } from '../../utils'
import type { DeepPartial, Strategy } from '../../types/index'
// @ts-expect-error
import appConfig from '#build/app.config'

View File

@@ -36,11 +36,11 @@
<script lang="ts">
import { defineComponent, toRef } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import UIcon from '../elements/Icon.vue'
import ULink from '../elements/Link.vue'
import { useUI } from '../../composables/useUI'
import { mergeConfig, getULinkProps } from '../../utils'
import { getULinkProps, mergeConfig, twMerge } from '../../utils'
import type { BreadcrumbLink, DeepPartial, Strategy } from '../../types/index'
// @ts-expect-error
import appConfig from '#build/app.config'

View File

@@ -54,13 +54,13 @@
<script lang="ts">
import { toRef, defineComponent, computed } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import UIcon from '../elements/Icon.vue'
import UAvatar from '../elements/Avatar.vue'
import UBadge from '../elements/Badge.vue'
import ULink from '../elements/Link.vue'
import { useUI } from '../../composables/useUI'
import { mergeConfig, getULinkProps } from '../../utils'
import { getULinkProps, mergeConfig, twMerge } from '../../utils'
import type { DeepPartial, HorizontalNavigationLink, Strategy } from '../../types/index'
// @ts-expect-error
import appConfig from '#build/app.config'

View File

@@ -55,14 +55,14 @@
<script lang="ts">
import { toRef, defineComponent, computed } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import UIcon from '../elements/Icon.vue'
import UAvatar from '../elements/Avatar.vue'
import UBadge from '../elements/Badge.vue'
import ULink from '../elements/Link.vue'
import UDivider from '../layout/Divider.vue'
import { useUI } from '../../composables/useUI'
import { mergeConfig, getULinkProps } from '../../utils'
import { getULinkProps, mergeConfig, twMerge } from '../../utils'
import type { VerticalNavigationLink, Strategy, DeepPartial } from '../../types/index'
// @ts-expect-error
import appConfig from '#build/app.config'

View File

@@ -18,10 +18,10 @@ import type { PropType, Ref } from 'vue'
import { defu } from 'defu'
import { onClickOutside } from '@vueuse/core'
import type { VirtualElement } from '@popperjs/core'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import { useUI } from '../../composables/useUI'
import { usePopper } from '../../composables/usePopper'
import { mergeConfig } from '../../utils'
import { mergeConfig, twMerge } from '../../utils'
import type { DeepPartial, PopperOptions, Strategy } from '../../types/index'
// @ts-expect-error
import appConfig from '#build/app.config'

View File

@@ -18,7 +18,7 @@
{{ title }}
</slot>
</p>
<div v-if="(description || $slots.description)" :class="twMerge(ui.description, !title && !$slots.title && 'mt-0 leading-5')">
<div v-if="(description || $slots.description)" :class="twMerge(ui.description, !title && !$slots.title && ui.descriptionOnly)">
<slot name="description" :description="description">
{{ description }}
</slot>
@@ -45,13 +45,13 @@
<script lang="ts">
import { ref, computed, toRef, onMounted, onUnmounted, watch, watchEffect, defineComponent } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import UIcon from '../elements/Icon.vue'
import UAvatar from '../elements/Avatar.vue'
import UButton from '../elements/Button.vue'
import { useUI } from '../../composables/useUI'
import { useTimer } from '../../composables/useTimer'
import { mergeConfig } from '../../utils'
import { mergeConfig, twMerge } from '../../utils'
import type { Avatar, Button, NotificationColor, NotificationAction, Strategy, DeepPartial } from '../../types/index'
// @ts-expect-error
import appConfig from '#build/app.config'

View File

@@ -22,10 +22,10 @@
<script lang="ts">
import { computed, toRef, defineComponent } from 'vue'
import type { PropType } from 'vue'
import { twMerge, twJoin } from 'tailwind-merge'
import { twJoin } from 'tailwind-merge'
import { useUI } from '../../composables/useUI'
import { useToast } from '../../composables/useToast'
import { mergeConfig } from '../../utils'
import { mergeConfig, twMerge } from '../../utils'
import type { DeepPartial, Notification, Strategy } from '../../types/index'
import UNotification from './Notification.vue'
import { useState } from '#imports'

View File

@@ -5,6 +5,7 @@ export default {
inner: 'w-0 flex-1',
title: 'text-sm font-medium',
description: 'mt-1 text-sm leading-4 opacity-90',
descriptionOnly: 'mt-0 leading-5',
actions: 'flex items-center gap-2 mt-3 flex-shrink-0',
shadow: '',
rounded: 'rounded-lg',

View File

@@ -6,6 +6,7 @@ export default {
inner: 'w-0 flex-1',
title: 'text-sm font-medium text-gray-900 dark:text-white',
description: 'mt-1 text-sm leading-4 text-gray-500 dark:text-gray-400',
descriptionOnly: 'mt-0 leading-5',
actions: 'flex items-center gap-2 mt-3 flex-shrink-0',
background: 'bg-white dark:bg-gray-900',
shadow: 'shadow-lg',

View File

@@ -1,14 +1,16 @@
import { defu, createDefu } from 'defu'
import { extendTailwindMerge } from 'tailwind-merge'
import type { Strategy } from '../types/index'
// @ts-ignore
import appConfig from '#build/app.config'
const customTwMerge = extendTailwindMerge<string, string>({
export const twMerge = extendTailwindMerge<string, string>(defu({
extend: {
classGroups: {
icons: [(classPart: string) => classPart.startsWith('i-')]
}
}
})
}, appConfig.ui?.tailwindMerge))
const defuTwMerge = createDefu((obj, key, value, namespace) => {
if (namespace === 'default' || namespace.startsWith('default.')) {
@@ -28,7 +30,7 @@ const defuTwMerge = createDefu((obj, key, value, namespace) => {
}
if (typeof obj[key] === 'string' && typeof value === 'string' && obj[key] && value) {
// @ts-ignore
obj[key] = customTwMerge(obj[key], value)
obj[key] = twMerge(obj[key], value)
return true
}
})

View File

@@ -30,21 +30,26 @@ export default function installTailwind(
// 2. add config template
const configTemplate = addTemplate({
filename: 'nuxtui-tailwind.config.cjs',
filename: 'nuxtui-tailwind.config.mjs',
write: true,
getContents: ({ nuxt }) => `
const { defaultExtractor: createDefaultExtractor } = require('tailwindcss/lib/lib/defaultExtractor.js')
const { customSafelistExtractor, generateSafelist } = require(${JSON.stringify(resolve(runtimeDir, 'utils', 'colors'))})
import { defaultExtractor as createDefaultExtractor } from "tailwindcss/lib/lib/defaultExtractor.js";
import { customSafelistExtractor, generateSafelist } from ${JSON.stringify(resolve(runtimeDir, 'utils', 'colors'))};
import formsPlugin from "@tailwindcss/forms";
import aspectRatio from "@tailwindcss/aspect-ratio";
import typography from "@tailwindcss/typography";
import containerQueries from "@tailwindcss/container-queries";
import headlessUi from "@headlessui/tailwindcss";
const defaultExtractor = createDefaultExtractor({ tailwindConfig: { separator: ':' } })
const defaultExtractor = createDefaultExtractor({ tailwindConfig: { separator: ':' } });
module.exports = {
export default {
plugins: [
require('@tailwindcss/forms')({ strategy: 'class' }),
require('@tailwindcss/aspect-ratio'),
require('@tailwindcss/typography'),
require('@tailwindcss/container-queries'),
require('@headlessui/tailwindcss')
formsPlugin({ strategy: 'class' }),
aspectRatio,
typography,
containerQueries,
headlessUi
],
content: {
files: [