mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-15 12:39:35 +01:00
Compare commits
313 Commits
chore/cont
...
feat/3880
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6fe4dc716 | ||
|
|
203a36ae11 | ||
|
|
ec569e427b | ||
|
|
1d052ec565 | ||
|
|
1ba8a55bcb | ||
|
|
63730d684b | ||
|
|
9ab184cc24 | ||
|
|
ad0e4ddbf4 | ||
|
|
6a93556aed | ||
|
|
c3ec805a9a | ||
|
|
c025fbf81a | ||
|
|
c82828d889 | ||
|
|
7be03e0f51 | ||
|
|
9debce737c | ||
|
|
772631cde9 | ||
|
|
d7aefa53b2 | ||
|
|
8922c7388e | ||
|
|
c355cacd43 | ||
|
|
a0e71d9e29 | ||
|
|
127e06ae83 | ||
|
|
09c1ed8bf4 | ||
|
|
a05102fab3 | ||
|
|
48077c12e8 | ||
|
|
09caf44d0d | ||
|
|
15482aae76 | ||
|
|
f903ec396f | ||
|
|
b00e07f13d | ||
|
|
5c573b37b6 | ||
|
|
f62c5ec20c | ||
|
|
b96a1ccbab | ||
|
|
801375d4fb | ||
|
|
4ce654076c | ||
|
|
fb9e7bb856 | ||
|
|
69a7b957d5 | ||
|
|
3b67d54833 | ||
|
|
df8f20232f | ||
|
|
347694b4b5 | ||
|
|
021880328b | ||
|
|
9c1f423555 | ||
|
|
6cb737e038 | ||
|
|
231b82fe4c | ||
|
|
57a5037b13 | ||
|
|
752e2b69bd | ||
|
|
6237663a01 | ||
|
|
44cfa00e4d | ||
|
|
8cbbab9a6b | ||
|
|
2d51e20939 | ||
|
|
268e29b041 | ||
|
|
b3e885211a | ||
|
|
deae9eca84 | ||
|
|
b0364b96b7 | ||
|
|
ba3c6e8788 | ||
|
|
01da3cbf31 | ||
|
|
595fc64515 | ||
|
|
81569713e9 | ||
|
|
1a8feb751e | ||
|
|
1d281e915a | ||
|
|
c3adc381c9 | ||
|
|
edca3bcb74 | ||
|
|
8f32ee3d24 | ||
|
|
9172bb7dc2 | ||
|
|
32dae2e002 | ||
|
|
be41aed1f3 | ||
|
|
bf678412ca | ||
|
|
a999600e9f | ||
|
|
04f12adc5b | ||
|
|
abfd0ede03 | ||
|
|
2fa8db64dd | ||
|
|
52f1963833 | ||
|
|
9a83c9c7f4 | ||
|
|
f2510cb342 | ||
|
|
4dd56c8111 | ||
|
|
6e3ec6a077 | ||
|
|
59c26ec123 | ||
|
|
67ef866a40 | ||
|
|
5170cfd7eb | ||
|
|
9bcf1ad92f | ||
|
|
7a2bd4e617 | ||
|
|
8781a07909 | ||
|
|
2492526d7c | ||
|
|
54bb2282c5 | ||
|
|
2a2495a652 | ||
|
|
f17b15ed1e | ||
|
|
66355ba301 | ||
|
|
4873b3a043 | ||
|
|
0d4baf7851 | ||
|
|
04333cd8cb | ||
|
|
cb3522ed18 | ||
|
|
1a4de49c16 | ||
|
|
3eb7812f2d | ||
|
|
080aed7225 | ||
|
|
d77fd6102a | ||
|
|
0f558fc0d0 | ||
|
|
1841e13b32 | ||
|
|
4e7c1c9c30 | ||
|
|
4b3dd48778 | ||
|
|
a9e8ea9231 | ||
|
|
4dd9344ff9 | ||
|
|
180c150e0f | ||
|
|
145cae798c | ||
|
|
9aea54267a | ||
|
|
9400552491 | ||
|
|
89753fc337 | ||
|
|
f8a6bd3bf6 | ||
|
|
228d4c9835 | ||
|
|
150b334b1d | ||
|
|
bf56e15a2e | ||
|
|
09151df170 | ||
|
|
22b917a0f7 | ||
|
|
43cbb94ee2 | ||
|
|
3bf5acb683 | ||
|
|
d37315cc83 | ||
|
|
326bb9a31e | ||
|
|
4157260a02 | ||
|
|
03b20fdb26 | ||
|
|
004c93bfa2 | ||
|
|
18eb5e6b97 | ||
|
|
42f7f94521 | ||
|
|
ea0c459306 | ||
|
|
546df572fc | ||
|
|
37abcc6a5b | ||
|
|
accf69046c | ||
|
|
80177679f2 | ||
|
|
536b7afcc1 | ||
|
|
483e473e3f | ||
|
|
5835eb5f0f | ||
|
|
ca507c6a0d | ||
|
|
03ac395164 | ||
|
|
f761369888 | ||
|
|
7df7ee336a | ||
|
|
2ee1c5ac2e | ||
|
|
62bc7b25a2 | ||
|
|
66f6c7743c | ||
|
|
dec2b9fd6a | ||
|
|
4604da0f16 | ||
|
|
a9d693095b | ||
|
|
cddcb95ed4 | ||
|
|
967968e02e | ||
|
|
f8e560525f | ||
|
|
8216b59d4f | ||
|
|
44ea02c0d6 | ||
|
|
f95abf8d1d | ||
|
|
dcf34a7ac2 | ||
|
|
2ba94db09e | ||
|
|
d9e9fea35e | ||
|
|
dae9f0b863 | ||
|
|
0a72024361 | ||
|
|
41087d4c95 | ||
|
|
6aab62ec30 | ||
|
|
742a37201e | ||
|
|
473513c246 | ||
|
|
fe4e1f859d | ||
|
|
3243fb88f7 | ||
|
|
43d281f6d1 | ||
|
|
405304775e | ||
|
|
0559beb365 | ||
|
|
56fc757244 | ||
|
|
9cf9f25f44 | ||
|
|
02363994d6 | ||
|
|
f2682fd2ae | ||
|
|
0634a756a4 | ||
|
|
44f536fd00 | ||
|
|
d0be59946b | ||
|
|
1e2a10b4bd | ||
|
|
3c78e2fd98 | ||
|
|
6887e33aae | ||
|
|
28e869e8aa | ||
|
|
d86956e1d5 | ||
|
|
23e4f0ec4d | ||
|
|
c00f6e8cdf | ||
|
|
d29e1481f2 | ||
|
|
79aa161c6d | ||
|
|
94ea75f441 | ||
|
|
0c368c8ab8 | ||
|
|
c5796c4f82 | ||
|
|
204953b780 | ||
|
|
2e4c3082a1 | ||
|
|
f2fd778c0a | ||
|
|
d79da9d7b6 | ||
|
|
a4429eee09 | ||
|
|
0905b2b3d5 | ||
|
|
c7fba2e0eb | ||
|
|
4167f04205 | ||
|
|
276268d311 | ||
|
|
717e35f098 | ||
|
|
459a0410ab | ||
|
|
b9adc83e78 | ||
|
|
d7a4d029b7 | ||
|
|
3c8d6cd01d | ||
|
|
67da90a2f6 | ||
|
|
894e8a61b6 | ||
|
|
1b6ab271ea | ||
|
|
0dc4678c68 | ||
|
|
e86dc79e51 | ||
|
|
35997377a6 | ||
|
|
12303a87be | ||
|
|
f84ccddcd6 | ||
|
|
869c0708bd | ||
|
|
c63d2f380a | ||
|
|
92632e969e | ||
|
|
f6d7994a55 | ||
|
|
f738f68f76 | ||
|
|
17d6803329 | ||
|
|
732a67aa88 | ||
|
|
bdf129fc38 | ||
|
|
d140acc608 | ||
|
|
cc20a26f07 | ||
|
|
983c6382d1 | ||
|
|
37eabc89bd | ||
|
|
a57844e416 | ||
|
|
2be60cddfe | ||
|
|
09b4699aea | ||
|
|
46c2987ebf | ||
|
|
f244d15b96 | ||
|
|
aaa60c0798 | ||
|
|
5467d71cc2 | ||
|
|
941a54e5e3 | ||
|
|
655f98ffed | ||
|
|
999a0f8467 | ||
|
|
2739939c46 | ||
|
|
2a241c87c3 | ||
|
|
e6e510b848 | ||
|
|
a655da1394 | ||
|
|
3a71256d59 | ||
|
|
404359a6ca | ||
|
|
1e4e9c4708 | ||
|
|
6f07f6bd6e | ||
|
|
4c1093bde4 | ||
|
|
7d51a9e479 | ||
|
|
04bdbcfc6e | ||
|
|
58aa296425 | ||
|
|
d3df3bb929 | ||
|
|
4863775e17 | ||
|
|
7551a85ad2 | ||
|
|
c2bcb8e264 | ||
|
|
88124b85c5 | ||
|
|
9f539c9545 | ||
|
|
41d4ffe5b5 | ||
|
|
d8a0bbe710 | ||
|
|
06e5689da8 | ||
|
|
67d19f582a | ||
|
|
650bc40253 | ||
|
|
2f0f317a12 | ||
|
|
d2a26939ad | ||
|
|
b7a8146898 | ||
|
|
e81464a43e | ||
|
|
4b3d2a7b00 | ||
|
|
6f6fa6ae4a | ||
|
|
c7bb6d4c4b | ||
|
|
c23f85fe33 | ||
|
|
591d59fe89 | ||
|
|
caa3bf9c7e | ||
|
|
2731011bb7 | ||
|
|
aebf0b3dca | ||
|
|
6651987dc6 | ||
|
|
61aabd72e4 | ||
|
|
8dfdd63ce3 | ||
|
|
e6369a6746 | ||
|
|
a4f3f6d531 | ||
|
|
c5bdec0f64 | ||
|
|
87a7828931 | ||
|
|
501468960b | ||
|
|
13cd6be679 | ||
|
|
e421ea57ec | ||
|
|
799d7ae422 | ||
|
|
be9b1f659a | ||
|
|
39e29fccf1 | ||
|
|
195773ec7d | ||
|
|
d7710795df | ||
|
|
53f8b34bef | ||
|
|
df83ab355e | ||
|
|
e5df026993 | ||
|
|
6131871a0d | ||
|
|
9543bce787 | ||
|
|
5e345a8a73 | ||
|
|
8acf3c51db | ||
|
|
f3b8b17dc5 | ||
|
|
bc0a296f9d | ||
|
|
ac4c1946ec | ||
|
|
82b5f322eb | ||
|
|
fb6cf708a6 | ||
|
|
8ab1f75e47 | ||
|
|
e9b19349ba | ||
|
|
b2b206e3f4 | ||
|
|
b779064129 | ||
|
|
94155258e2 | ||
|
|
c63a6dd133 | ||
|
|
79833035de | ||
|
|
79e7c7b729 | ||
|
|
bc06185282 | ||
|
|
6e27304d8c | ||
|
|
b4f8ac7ff7 | ||
|
|
a2fa1accaa | ||
|
|
f486423381 | ||
|
|
975331a7b1 | ||
|
|
75e4792f7f | ||
|
|
4a969a8b9e | ||
|
|
664a8c7524 | ||
|
|
fbcc3139a3 | ||
|
|
1a46394668 | ||
|
|
9ca213bd33 | ||
|
|
3484832822 | ||
|
|
80dfa88ea4 | ||
|
|
d710141a1b | ||
|
|
d3e2a3f33a | ||
|
|
9c3d53a02d | ||
|
|
22edfd708a | ||
|
|
e25aa78050 | ||
|
|
122e8ac8f4 | ||
|
|
5fc6312ab1 | ||
|
|
83f0a24704 | ||
|
|
13c299533f | ||
|
|
db11db6ff1 |
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1 @@
|
||||
* @benjamincanac
|
||||
6
.github/ISSUE_TEMPLATE/bug-report-v3.yml
vendored
6
.github/ISSUE_TEMPLATE/bug-report-v3.yml
vendored
@@ -5,12 +5,12 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Before reporting a bug, please make sure that you have read through our [v3 documentation](https://ui.nuxt.com/) and existing [issues](https://github.com/nuxt/ui/issues?q=is%3Aissue%20is%3Aopen%20sort%3Aupdated-desc%20label%3Av3).
|
||||
Before reporting a bug, please make sure that you have read through our [documentation](https://ui.nuxt.com/) and existing [issues](https://github.com/nuxt/ui/issues?q=is%3Aissue%20is%3Aopen%20sort%3Aupdated-desc%20label%3Av3).
|
||||
- type: textarea
|
||||
id: env
|
||||
attributes:
|
||||
label: Environment
|
||||
description: You can use `npx nuxi info` to fill this section
|
||||
description: You can use `npx nuxt info` to fill this section
|
||||
placeholder: |
|
||||
- Operating System: `Darwin`
|
||||
- Node Version: `v18.16.0`
|
||||
@@ -44,7 +44,7 @@ body:
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Reproduction
|
||||
description: Please provide a reproduction link using the Nuxt template https://codesandbox.io/p/devbox/nuxt-ui3-n3sxks or the Vue template https://codesandbox.io/p/devbox/nuxt-ui3-vue-4h5gqn. A minimal [reproduction is required](https://antfu.me/posts/why-reproductions-are-required) unless you are absolutely sure that the issue is obvious and the provided information is enough to understand the problem. If a report is vague (e.g. just a generic error message) and has no reproduction, it will receive a "needs reproduction" label. If no reproduction is provided we might close it.
|
||||
description: Please provide a reproduction link using the Nuxt template https://codesandbox.io/p/devbox/nuxt-ui3-n3sxks or the Vue template https://codesandbox.io/p/devbox/nuxt-ui3-vue-4h5gqn. A minimal [reproduction is required](https://antfu.me/posts/why-reproductions-are-required) unless you are absolutely sure that the issue is obvious and the provided information is enough to understand the problem. If a report is vague (e.g. just a generic error message) and has no reproduction, it will receive a "needs reproduction" label. If no reproduction is provided, it will be closed automatically after a while.
|
||||
placeholder: https://github.com/my/reproduction
|
||||
validations:
|
||||
required: true
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -10,7 +10,7 @@ body:
|
||||
id: env
|
||||
attributes:
|
||||
label: Environment
|
||||
description: You can use `npx nuxi info` to fill this section
|
||||
description: You can use `npx nuxt info` to fill this section
|
||||
placeholder: |
|
||||
- Operating System: `Darwin`
|
||||
- Node Version: `v18.16.0`
|
||||
|
||||
@@ -5,7 +5,7 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Before requesting a feature, please make sure that you have read through our [v3 documentation](https://ui.nuxt.com/) and existing [issues](https://github.com/nuxt/ui/issues?q=is%3Aissue%20is%3Aopen%20sort%3Aupdated-desc%20label%3Av3).
|
||||
Before requesting a feature, please make sure that you have read through our [documentation](https://ui.nuxt.com/) and existing [issues](https://github.com/nuxt/ui/issues?q=is%3Aissue%20is%3Aopen%20sort%3Aupdated-desc%20label%3Av3).
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/question-v3.yml
vendored
2
.github/ISSUE_TEMPLATE/question-v3.yml
vendored
@@ -5,7 +5,7 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Before asking a question, please make sure that you have read through our [v3 documentation](https://ui.nuxt.com/) and existing [issues](https://github.com/nuxt/ui/issues?q=is%3Aissue%20is%3Aopen%20sort%3Aupdated-desc%20label%3Av3).
|
||||
Before asking a question, please make sure that you have read through our [documentation](https://ui.nuxt.com/) and existing [issues](https://github.com/nuxt/ui/issues?q=is%3Aissue%20is%3Aopen%20sort%3Aupdated-desc%20label%3Av3).
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
|
||||
30
.github/reproduire/needs-reproduction.md
vendored
Normal file
30
.github/reproduire/needs-reproduction.md
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
Would you be able to provide a [reproduction](https://nuxt.com/docs/community/reporting-bugs/#create-a-minimal-reproduction)? 🙏
|
||||
|
||||
<details>
|
||||
<summary>More info</summary>
|
||||
|
||||
### Why do I need to provide a reproduction?
|
||||
|
||||
Reproductions make it possible for us to triage and fix issues quickly with a relatively small team. It helps us discover the source of the problem, and also can reveal assumptions you or we might be making.
|
||||
|
||||
### What will happen?
|
||||
|
||||
If you've provided a reproduction, we'll remove the label and try to reproduce the issue. If we can, we'll mark it as a bug and prioritise it based on its severity and how many people we think it might affect.
|
||||
|
||||
If `needs reproduction` labeled issues don't receive any substantial activity (e.g., new comments featuring a reproduction link), they will be closed automatically after a while. That's not because we don't care! At any point, feel free to comment with a reproduction and we'll reopen it.
|
||||
|
||||
### How can I create a reproduction?
|
||||
|
||||
We have templates to create a minimal reproduction:
|
||||
|
||||
* **Nuxt**: https://codesandbox.io/p/devbox/nuxt-ui3-n3sxks
|
||||
* **Vue**: https://codesandbox.io/p/devbox/nuxt-ui3-vue-4h5gqn
|
||||
|
||||
Please ensure that the reproduction is as **minimal** as possible. See more details [in our guide](https://nuxt.com/docs/community/reporting-bugs/#create-a-minimal-reproduction).
|
||||
|
||||
You might also find these other articles interesting and/or helpful:
|
||||
|
||||
- [The Importance of Reproductions](https://antfu.me/posts/why-reproductions-are-required)
|
||||
- [How to Generate a Minimal, Complete, and Verifiable Example](https://stackoverflow.com/help/mcve)
|
||||
|
||||
</details>
|
||||
14
.github/workflows/docs.yml
vendored
14
.github/workflows/docs.yml
vendored
@@ -6,10 +6,6 @@ jobs:
|
||||
deploy:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
environment:
|
||||
name: ${{ github.ref == 'refs/heads/v3' && 'production' || 'preview' }}
|
||||
url: ${{ steps.deploy.outputs.deployment-url }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
@@ -40,14 +36,10 @@ jobs:
|
||||
- name: Prepare build
|
||||
run: pnpm run dev:prepare
|
||||
|
||||
- name: Build application
|
||||
run: pnpm run docs:build
|
||||
- name: Deploy to NuxtHub
|
||||
uses: nuxt-hub/action@v2
|
||||
env:
|
||||
NODE_OPTIONS: '--max-old-space-size=8192'
|
||||
|
||||
- name: Deploy to NuxtHub
|
||||
uses: nuxt-hub/action@v1
|
||||
id: deploy
|
||||
with:
|
||||
project-key: ui-7eg3
|
||||
directory: docs/dist
|
||||
directory: docs
|
||||
|
||||
53
.github/workflows/module.yml
vendored
53
.github/workflows/module.yml
vendored
@@ -69,6 +69,53 @@ jobs:
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: pnpx pkg-pr-new publish --compact --no-template --pnpm
|
||||
|
||||
playground:
|
||||
needs: build
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./playground
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest] # macos-latest, windows-latest
|
||||
node: [22]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Store commit SHA
|
||||
run: |
|
||||
echo "COMMIT_SHA=$(echo ${{ github.event.pull_request.head.sha || github.sha }} | cut -c1-7)" >> $GITHUB_ENV
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: pnpm
|
||||
|
||||
- name: Install latest nuxt/ui
|
||||
run: pnpm install https://pkg.pr.new/@nuxt/ui@${{ env.COMMIT_SHA }} --lockfile-only
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --ignore-workspace
|
||||
|
||||
- name: Prepare
|
||||
run: pnpm nuxt prepare
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm run typecheck
|
||||
|
||||
starter-nuxt:
|
||||
needs: build
|
||||
|
||||
@@ -91,7 +138,7 @@ jobs:
|
||||
|
||||
- name: Store commit SHA
|
||||
run: |
|
||||
echo "COMMIT_SHA=$(echo ${{ github.workflow_sha }} | cut -c1-7)" >> $GITHUB_ENV
|
||||
echo "COMMIT_SHA=$(echo ${{ github.event.pull_request.head.sha || github.sha }} | cut -c1-7)" >> $GITHUB_ENV
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
@@ -136,7 +183,7 @@ jobs:
|
||||
|
||||
- name: Store commit SHA
|
||||
run: |
|
||||
echo "COMMIT_SHA=$(echo ${{ github.workflow_sha }} | cut -c1-7)" >> $GITHUB_ENV
|
||||
echo "COMMIT_SHA=$(echo ${{ github.event.pull_request.head.sha || github.sha }} | cut -c1-7)" >> $GITHUB_ENV
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
@@ -188,7 +235,7 @@ jobs:
|
||||
|
||||
- name: Store commit SHA
|
||||
run: |
|
||||
echo "COMMIT_SHA=$(echo ${{ github.workflow_sha }} | cut -c1-7)" >> $GITHUB_ENV
|
||||
echo "COMMIT_SHA=$(echo ${{ github.event.pull_request.head.sha || github.sha }} | cut -c1-7)" >> $GITHUB_ENV
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
16
.github/workflows/playground.yml
vendored
16
.github/workflows/playground.yml
vendored
@@ -9,10 +9,6 @@ jobs:
|
||||
deploy:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
environment:
|
||||
name: ${{ github.ref == 'refs/heads/v3' && 'production' || 'preview' }}
|
||||
url: ${{ steps.deploy.outputs.deployment-url }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
@@ -40,14 +36,10 @@ jobs:
|
||||
- name: Prepare build
|
||||
run: pnpm run dev:prepare
|
||||
|
||||
- name: Build application
|
||||
run: pnpm run dev:build
|
||||
env:
|
||||
NITRO_PRESET: cloudflare-pages
|
||||
|
||||
- name: Deploy to NuxtHub
|
||||
uses: nuxt-hub/action@v1
|
||||
id: deploy
|
||||
uses: nuxt-hub/action@v2
|
||||
env:
|
||||
NODE_OPTIONS: '--max-old-space-size=8192'
|
||||
with:
|
||||
project-key: ui3-playground-pb9b
|
||||
directory: playground/dist
|
||||
directory: playground
|
||||
|
||||
54
.github/workflows/release.yml
vendored
Normal file
54
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
name: release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v3*'
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest] # macos-latest, windows-latest
|
||||
node: [22]
|
||||
|
||||
env:
|
||||
NUXT_GITHUB_TOKEN: ${{ secrets.NUXT_GITHUB_TOKEN }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: pnpm
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Prepare
|
||||
run: pnpm run dev:prepare
|
||||
|
||||
- name: Lint
|
||||
run: pnpm run lint
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm run typecheck
|
||||
|
||||
- name: Test
|
||||
run: pnpm run test run
|
||||
|
||||
- name: Test (vue)
|
||||
run: pnpm run test:vue run
|
||||
|
||||
- name: Publish
|
||||
run: ./scripts/release.sh
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
|
||||
27
.github/workflows/reproduction.yml
vendored
Normal file
27
.github/workflows/reproduction.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: reproduction
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
|
||||
jobs:
|
||||
reproduction:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
issues: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
days-before-stale: -1 # Issues and PR will never be flagged stale automatically.
|
||||
stale-issue-label: 'needs reproduction' # Label that flags an issue as stale.
|
||||
only-labels: 'needs reproduction' # Only process these issues
|
||||
days-before-issue-close: 7
|
||||
ignore-updates: true
|
||||
remove-stale-when-updated: false
|
||||
close-issue-message: This issue was closed because it was open for 7 days without a reproduction.
|
||||
close-issue-label: closed-by-bot
|
||||
operations-per-run: 300 #default 30
|
||||
17
.github/workflows/reproduire.yml
vendored
Normal file
17
.github/workflows/reproduire.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: reproduire
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
reproduire:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: Hebilicious/reproduire@4b686ae9cbb72dad60f001d278b6e3b2ce40a9ac # v0.0.9-mp
|
||||
with:
|
||||
label: needs reproduction
|
||||
32
.github/workflows/stale.yml
vendored
32
.github/workflows/stale.yml
vendored
@@ -1,6 +1,7 @@
|
||||
name: stale
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
|
||||
@@ -9,17 +10,28 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
issues: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
- uses: actions/stale@4c023f01d613e60293d8004f251a18bfb9bbd71d
|
||||
with:
|
||||
days-before-stale: -1 # Issues and PR will never be flagged stale automatically.
|
||||
stale-issue-label: 'needs reproduction' # Label that flags an issue as stale.
|
||||
only-labels: 'needs reproduction' # Only process these issues
|
||||
days-before-issue-close: 7
|
||||
ignore-updates: true
|
||||
remove-stale-when-updated: false
|
||||
close-issue-message: This issue was closed because it was open for 7 days without a reproduction.
|
||||
close-issue-label: closed-by-bot
|
||||
operations-per-run: 300 #default 30
|
||||
days-before-pr-stale: -1
|
||||
days-before-stale: 60
|
||||
days-before-close: 7
|
||||
stale-issue-label: 'stale'
|
||||
close-issue-label: 'closed-by-bot'
|
||||
close-issue-message: |
|
||||
Hi! 👋
|
||||
|
||||
This issue has been automatically **closed** due to prolonged inactivity.
|
||||
|
||||
We're a small team and can't address every report, but we appreciate your feedback and contributions.
|
||||
|
||||
If this issue is still relevant with the latest version of Nuxt UI, please feel free to reopen or create a new issue with updated details.
|
||||
|
||||
Thank you for your understanding and support!
|
||||
|
||||
— Nuxt UI Team
|
||||
exempt-issue-labels: 'feature,announcement'
|
||||
operations-per-run: 300
|
||||
@@ -3,6 +3,9 @@
|
||||
"commitMessage": "chore(release): v${version}",
|
||||
"tagName": "v${version}"
|
||||
},
|
||||
"npm": {
|
||||
"publish": false
|
||||
},
|
||||
"github": {
|
||||
"release": true,
|
||||
"releaseName": "v${version}",
|
||||
|
||||
213
CHANGELOG.md
213
CHANGELOG.md
@@ -1,5 +1,218 @@
|
||||
# Changelog
|
||||
|
||||
## [3.2.0](https://github.com/nuxt/ui/compare/v3.1.3...v3.2.0) (2025-06-25)
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **useOverlay:** correct spelling of `unmount` function (#4051)
|
||||
|
||||
### Features
|
||||
|
||||
* **Avatar:** add `chip` prop ([#4224](https://github.com/nuxt/ui/issues/4224)) ([03ac395](https://github.com/nuxt/ui/commit/03ac395164c02c964361c68743268b1bc90aae59))
|
||||
* **Carousel:** allow customization of active dot color ([#4229](https://github.com/nuxt/ui/issues/4229)) ([2ee1c5a](https://github.com/nuxt/ui/commit/2ee1c5ac2e20ab9ce2f4037a8e8c64e561b0428b))
|
||||
* **CommandPalette:** handle `children` in items ([#4226](https://github.com/nuxt/ui/issues/4226)) ([59c26ec](https://github.com/nuxt/ui/commit/59c26ec1230375a24fbaf8a630a696ae854700c7))
|
||||
* **extendLocale:** new composable ([0f558fc](https://github.com/nuxt/ui/commit/0f558fc0d014d51549222accfc50286d1770d1aa)), closes [#3729](https://github.com/nuxt/ui/issues/3729)
|
||||
* **Form:** expose loading state to default slot ([#4247](https://github.com/nuxt/ui/issues/4247)) ([ea0c459](https://github.com/nuxt/ui/commit/ea0c459306be585bacaaf5b433114d072550c824))
|
||||
* **InputTags:** new component ([#4261](https://github.com/nuxt/ui/issues/4261)) ([54bb228](https://github.com/nuxt/ui/commit/54bb2282c58d3bf5a7dde4cdee687c68efd934a0))
|
||||
* **locale:** add Luxembourgish language ([#4264](https://github.com/nuxt/ui/issues/4264)) ([43cbb94](https://github.com/nuxt/ui/commit/43cbb94ee25106b414fc8fe979fa65ebaa9ccc76))
|
||||
* **Modal/Slideover:** add `actions` slot ([#4358](https://github.com/nuxt/ui/issues/4358)) ([8156971](https://github.com/nuxt/ui/commit/81569713e9da9d5531ecdf4614660b84c686fa81))
|
||||
* **Modal/Slideover:** add `close` method in slots ([#4219](https://github.com/nuxt/ui/issues/4219)) ([5835eb5](https://github.com/nuxt/ui/commit/5835eb5f0f835b5f03646dec78f85b2f556a109b))
|
||||
* **Select/SelectMenu/Tabs:** expose trigger refs ([7a2bd4e](https://github.com/nuxt/ui/commit/7a2bd4e6179373902ba6f285903ea896fd1d378f)), closes [#4292](https://github.com/nuxt/ui/issues/4292)
|
||||
* **Select/SelectMenu:** handle dynamic `autofocus` ([1a4de49](https://github.com/nuxt/ui/commit/1a4de49c1665c9ef65279315be0393d6272447b9)), closes [#4324](https://github.com/nuxt/ui/issues/4324)
|
||||
* **Table:** add `body-top` / `body-bottom` slots ([#4354](https://github.com/nuxt/ui/issues/4354)) ([595fc64](https://github.com/nuxt/ui/commit/595fc64515613fe82c3a56fc5518f2e3fcce6e19))
|
||||
* **Timeline:** add `reverse` prop ([#4316](https://github.com/nuxt/ui/issues/4316)) ([5170cfd](https://github.com/nuxt/ui/commit/5170cfd7eb44a25c64673cf12979f9ca1049695f))
|
||||
* **Timeline:** new component ([#4215](https://github.com/nuxt/ui/issues/4215)) ([8017767](https://github.com/nuxt/ui/commit/80177679f2aa0d7f0e39e639a02d527a06e6172c))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Card/Drawer/Modal:** prevent scrollbars overflow ([#4368](https://github.com/nuxt/ui/issues/4368)) ([c3adc38](https://github.com/nuxt/ui/commit/c3adc381c90dad7152e27fc303ee678efc7c4c94))
|
||||
* **components:** remove default `md` size on buttons ([#4357](https://github.com/nuxt/ui/issues/4357)) ([be41aed](https://github.com/nuxt/ui/commit/be41aed1f3d3476801e1840dbb8766926bc93c05))
|
||||
* **defineShortcuts:** allow `meta_-` shortcut ([#4321](https://github.com/nuxt/ui/issues/4321)) ([4e7c1c9](https://github.com/nuxt/ui/commit/4e7c1c9c305b45dd76d4c238e70a6aeedae78c8b))
|
||||
* **Form:** conditionally type form data via `transform` prop ([#4188](https://github.com/nuxt/ui/issues/4188)) ([37abcc6](https://github.com/nuxt/ui/commit/37abcc6a5b0a678be626673af5067956657a50d6))
|
||||
* **Form:** expose reactive fields ([#4386](https://github.com/nuxt/ui/issues/4386)) ([1a8feb7](https://github.com/nuxt/ui/commit/1a8feb751e6827c414ef82fe9fb259ba7dcc7e08))
|
||||
* **InputMenu/SelectMenu:** dynamic `empty` size ([ba3c6e8](https://github.com/nuxt/ui/commit/ba3c6e8788ed75d86d4406749797da52d7816b84)), closes [#4377](https://github.com/nuxt/ui/issues/4377)
|
||||
* **InputTags:** extend emits interface ([8781a07](https://github.com/nuxt/ui/commit/8781a079096def0d3bae5b8d896db0df6ce37e23))
|
||||
* **Modal/Slideover:** don't emit `close:prevent` on `closeAutoFocus` ([150b334](https://github.com/nuxt/ui/commit/150b334b1d242c6dc132193e23359c03e6f35666))
|
||||
* **NavigationMenu:** nested accordion context at every level ([#4363](https://github.com/nuxt/ui/issues/4363)) ([2fa8db6](https://github.com/nuxt/ui/commit/2fa8db64ddf4c92a19e73774143518d87d001b72))
|
||||
* **NavigationMenu:** set content `max-height` in `horizontal` orientation ([62bc7b2](https://github.com/nuxt/ui/commit/62bc7b25a2d205d8dffb47a109196f91ff3e823a)), closes [#4208](https://github.com/nuxt/ui/issues/4208)
|
||||
* **Pagination:** match default button `size` ([#4350](https://github.com/nuxt/ui/issues/4350)) ([4dd56c8](https://github.com/nuxt/ui/commit/4dd56c8111e5a224105b82d541b7742b46abb34a))
|
||||
* **Select/SelectMenu:** display falsy values ([7df7ee3](https://github.com/nuxt/ui/commit/7df7ee336a925d7ee07f866551dad9350785c9fc))
|
||||
* **Select/SelectMenu:** prevent empty string display when multiple ([483e473](https://github.com/nuxt/ui/commit/483e473e3f5681cc97c3766ea47283dc95f76345))
|
||||
* **SelectMenu:** dynamic input size ([b0364b9](https://github.com/nuxt/ui/commit/b0364b96b73b9e543781a35962c03b5a983352c4))
|
||||
* **Table:** use `tr` as separator ([#4083](https://github.com/nuxt/ui/issues/4083)) ([edca3bc](https://github.com/nuxt/ui/commit/edca3bcb743c7eb63e6abbaa801d3858342a8777))
|
||||
* **Toast:** calc height on next tick ([3bf5acb](https://github.com/nuxt/ui/commit/3bf5acb683f0ad09735b2417d265d6fcfd901b11)), closes [#4265](https://github.com/nuxt/ui/issues/4265)
|
||||
* **Toaster:** smoother visibility transition for stacked toasts ([#4367](https://github.com/nuxt/ui/issues/4367)) ([abfd0ed](https://github.com/nuxt/ui/commit/abfd0ede036fa2953f9abc841d77ac71bbd3bba9))
|
||||
* **useOverlay:** correct spelling of `unmount` function ([#4051](https://github.com/nuxt/ui/issues/4051)) ([546df57](https://github.com/nuxt/ui/commit/546df572fca60325315bed17c9be3367052fb7a9))
|
||||
* **useOverlay:** set props to original props when `defaultOpen` is set ([#4308](https://github.com/nuxt/ui/issues/4308)) ([66355ba](https://github.com/nuxt/ui/commit/66355ba301d569b9f44527bafc5f8f09bcda63c0))
|
||||
* **useOverlay:** use original props when not provided to `open` ([#4269](https://github.com/nuxt/ui/issues/4269)) ([bf56e15](https://github.com/nuxt/ui/commit/bf56e15a2eed7d51199d5641649a822e91ca41ba))
|
||||
|
||||
## [3.1.3](https://github.com/nuxt/ui/compare/v3.1.2...v3.1.3) (2025-05-26)
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **NavigationMenu:** revert new `collapsible` field
|
||||
|
||||
### Features
|
||||
|
||||
* **locale:** add Kyrgyz language ([#4189](https://github.com/nuxt/ui/issues/4189)) ([4053047](https://github.com/nuxt/ui/commit/405304775e4b2b4e8b37a2364f3e5ee34b46036e))
|
||||
* **locale:** add Lithuanian language ([#4171](https://github.com/nuxt/ui/issues/4171)) ([d86956e](https://github.com/nuxt/ui/commit/d86956e1d57482b3e98eef2d34bff13544284b0b))
|
||||
* **locale:** add Malay language ([#4160](https://github.com/nuxt/ui/issues/4160)) ([c00f6e8](https://github.com/nuxt/ui/commit/c00f6e8cdfd88eeba58812b78d94a2326c13f164))
|
||||
* **locale:** add Mongolian language ([#4214](https://github.com/nuxt/ui/issues/4214)) ([44ea02c](https://github.com/nuxt/ui/commit/44ea02c0d64322ef0cfda63b234369c00d3d0180))
|
||||
* **Modal/Slideover:** add `after:enter` event ([#4187](https://github.com/nuxt/ui/issues/4187)) ([d9e9fea](https://github.com/nuxt/ui/commit/d9e9fea35e4b22d68324c9e85b3aa221a7987d0f))
|
||||
* **NavigationMenu:** add `tooltip` and `popover` props ([f2682fd](https://github.com/nuxt/ui/commit/f2682fd2ae8abb7807977727fc22ef34cb5752e5)), closes [#4186](https://github.com/nuxt/ui/issues/4186)
|
||||
* **NavigationMenu:** add `trigger` type in items ([9cf9f25](https://github.com/nuxt/ui/commit/9cf9f25f4424447691e03e9034155d1541badd43))
|
||||
* **NavigationMenu:** handle `vertical` orientation with Accordion instead of Collapsible ([1e2a10b](https://github.com/nuxt/ui/commit/1e2a10b4bdebaef12316ac60f98a956dad21c1ec)), closes [#4072](https://github.com/nuxt/ui/issues/4072) [#3911](https://github.com/nuxt/ui/issues/3911)
|
||||
* **Popover:** add `anchor` slot ([#4119](https://github.com/nuxt/ui/issues/4119)) ([473513c](https://github.com/nuxt/ui/commit/473513c2460d4329d7d2e0a0ea69bf1310a072d1))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **CheckboxGroup/RadioGroup:** variant `table` borders in RTL mode ([#4192](https://github.com/nuxt/ui/issues/4192)) ([43d281f](https://github.com/nuxt/ui/commit/43d281f6d1d8b0017ed61d929c5e311fb5b03447))
|
||||
* **CommandPalette:** add `presentation` role to viewport ([2ba94db](https://github.com/nuxt/ui/commit/2ba94db09e1ba86020d5d289f1ca1e24ef706299))
|
||||
* **ContextMenu/DropdownMenu:** wrap groups in a viewport ([dcf34a7](https://github.com/nuxt/ui/commit/dcf34a7ac236b96b1302ec2eae155b8f2d3784ef)), closes [#3315](https://github.com/nuxt/ui/issues/3315)
|
||||
* **Drawer:** improve title & description accessibility ([41087d4](https://github.com/nuxt/ui/commit/41087d4c9569eb00c04bd748e055cd151c2f762c)), closes [#4199](https://github.com/nuxt/ui/issues/4199)
|
||||
* **icons:** update `loading` icon ([#4163](https://github.com/nuxt/ui/issues/4163)) ([fe4e1f8](https://github.com/nuxt/ui/commit/fe4e1f859d42aa3c32bb7b75302e84a280abe525))
|
||||
* **Input/Textarea:** define model modifiers types ([#4195](https://github.com/nuxt/ui/issues/4195)) ([3243fb8](https://github.com/nuxt/ui/commit/3243fb88f71c5475824bfdc4d7c4f303b2d6790b))
|
||||
* **InputMenu/Select/SelectMenu:** manual viewport to display scrollbars ([f95abf8](https://github.com/nuxt/ui/commit/f95abf8d1d7b9149e400d7dc6f96f93f5154da7a)), closes [#4069](https://github.com/nuxt/ui/issues/4069)
|
||||
* **NavigationMenu:** incorrect hover when disabled and active ([d0be599](https://github.com/nuxt/ui/commit/d0be59946bfe30c79a6f75476385ab8538aa51b8))
|
||||
* **NavigationMenu:** only display `tooltip` when collapsed ([44f536f](https://github.com/nuxt/ui/commit/44f536fd0034facb3550d910fae71d4f9442ed19))
|
||||
* **NavigationMenu:** remove `font-medium` in popover children ([0236399](https://github.com/nuxt/ui/commit/02363994d66d3c2d11b9913f31167fa25f5c5de2))
|
||||
* **NavigationMenu:** revert new `collapsible` field ([3c78e2f](https://github.com/nuxt/ui/commit/3c78e2fd983f19b5cec65b4a94a8a8b14e548e5e))
|
||||
* **Textarea:** missing imports ([#4207](https://github.com/nuxt/ui/issues/4207)) ([6aab62e](https://github.com/nuxt/ui/commit/6aab62ec30e266c5f0da0cd24aefbb7c53f447ac))
|
||||
* **theme:** define `old-neutral` color as static ([#4193](https://github.com/nuxt/ui/issues/4193)) ([dae9f0b](https://github.com/nuxt/ui/commit/dae9f0b8631b3b9fb60ef47753f7aded0c36c4a2))
|
||||
* **Tooltip:** increase padding for consistency ([0634a75](https://github.com/nuxt/ui/commit/0634a756a496f5131841abafd218ae7e4aaa61e5))
|
||||
|
||||
## [3.1.2](https://github.com/nuxt/ui/compare/v3.1.1...v3.1.2) (2025-05-15)
|
||||
|
||||
### Features
|
||||
|
||||
* **Badge:** add `square` prop ([#4008](https://github.com/nuxt/ui/issues/4008)) ([894e8a6](https://github.com/nuxt/ui/commit/894e8a61b6fea3618fc863bd77678385e9d021c2))
|
||||
* **CheckboxGroup:** add `table` variant ([#3997](https://github.com/nuxt/ui/issues/3997)) ([1b6ab27](https://github.com/nuxt/ui/commit/1b6ab271ea3875a7c77ffe9367c7c341083dd53c))
|
||||
* **components:** add `ui` field in items ([#4060](https://github.com/nuxt/ui/issues/4060)) ([b9adc83](https://github.com/nuxt/ui/commit/b9adc83e787db02507e6e7bb1aabc684eccc197b))
|
||||
* **InputNumber:** add `increment-disabled` / `decrement-disabled` props ([#4141](https://github.com/nuxt/ui/issues/4141)) ([c7fba2e](https://github.com/nuxt/ui/commit/c7fba2e0ebfb7153f3bfb727165d653bbd3dbe54))
|
||||
* **locale:** add Slovenian language ([#4140](https://github.com/nuxt/ui/issues/4140)) ([e86dc79](https://github.com/nuxt/ui/commit/e86dc79e51b2773a77ada5f12d4f0964fbc83354))
|
||||
* **NavigationMenu:** add `collapsible` field in items ([2be60cd](https://github.com/nuxt/ui/commit/2be60cddfe10fd1e2466900fd53e21ee0c877227)), closes [#3353](https://github.com/nuxt/ui/issues/3353) [#3911](https://github.com/nuxt/ui/issues/3911)
|
||||
* **NavigationMenu:** handle `tooltip` in items ([46c2987](https://github.com/nuxt/ui/commit/46c2987ebfd30b2b071a96a745b7270e852e96de)), closes [#4050](https://github.com/nuxt/ui/issues/4050)
|
||||
* **Slider:** handle `tooltip` around thumbs ([d140acc](https://github.com/nuxt/ui/commit/d140acc608c6ae11c0a0531fe443588776ea7807)), closes [#1469](https://github.com/nuxt/ui/issues/1469)
|
||||
* **Toast:** add `progress` prop to hide progress bar ([#4125](https://github.com/nuxt/ui/issues/4125)) ([92632e9](https://github.com/nuxt/ui/commit/92632e969eaa11521a166e50e346753929b7f523))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Badge/Button:** handle zero value in label correctly ([#4108](https://github.com/nuxt/ui/issues/4108)) ([f244d15](https://github.com/nuxt/ui/commit/f244d15b96d97cd8ba34ba9c18f23965e17e3cef))
|
||||
* **ButtonGroup:** add `z-index` on focused element ([204953b](https://github.com/nuxt/ui/commit/204953b780bde08dbfde230fc8887674449227b7))
|
||||
* **Calendar:** wrong color for today date with `neutral` color ([7d51a9e](https://github.com/nuxt/ui/commit/7d51a9e479cb6105ea37759c5cd67ff9f7702c49)), closes [#4084](https://github.com/nuxt/ui/issues/4084) [#3629](https://github.com/nuxt/ui/issues/3629)
|
||||
* **Checkbox/RadioGroup:** render correct element without `variant` ([f2fd778](https://github.com/nuxt/ui/commit/f2fd778c0a604f2d65aec9f3fe2d54b6d4e8c3a2)), closes [#3998](https://github.com/nuxt/ui/issues/3998)
|
||||
* **CheckboxGroup:** relative `UCheckbox` import ([7551a85](https://github.com/nuxt/ui/commit/7551a85ad2d92b59e2909396affb862403d5b27a)), closes [#4090](https://github.com/nuxt/ui/issues/4090)
|
||||
* **ColorPicker:** make thumb touch draggable ([#4101](https://github.com/nuxt/ui/issues/4101)) ([cc20a26](https://github.com/nuxt/ui/commit/cc20a26f07268d19119ab4c7c254033143bb63f4))
|
||||
* **components:** `class` should have priority over `ui` prop ([e6e510b](https://github.com/nuxt/ui/commit/e6e510b848d995a286a51d50a120d67483e11232))
|
||||
* **FormField:** block form field injection after use ([#4150](https://github.com/nuxt/ui/issues/4150)) ([d79da9d](https://github.com/nuxt/ui/commit/d79da9d7b60c9972af64acd8e6eef4ae7d6bc3eb))
|
||||
* **FormField:** use `div` for `error` and `help` slots ([459a041](https://github.com/nuxt/ui/commit/459a0410ab729fde60865e84632b36903465f57e))
|
||||
* **inertia:** link always render as anchor tag ([#3989](https://github.com/nuxt/ui/issues/3989)) ([e81464a](https://github.com/nuxt/ui/commit/e81464a43ede4e63ce3dc92429bbfef48614f731))
|
||||
* **inertia:** make `useAppConfig` reactive ([12303a8](https://github.com/nuxt/ui/commit/12303a87be62dae84ef774e3a9795deb0ac90cc7))
|
||||
* **Input/Textarea:** handle generic types ([3c8d6cd](https://github.com/nuxt/ui/commit/3c8d6cd01dfafed5844c376f52adbdda0c814420)), closes [nuxt/ui-pro#887](https://github.com/nuxt/ui-pro/issues/887)
|
||||
* **InputNumber:** handle inside button group ([2e4c308](https://github.com/nuxt/ui/commit/2e4c3082a1e66fa597086dc3431fec37fa29ef62)), closes [#4155](https://github.com/nuxt/ui/issues/4155)
|
||||
* **Link:** consistent behavior between nuxt, vue and inertia ([#4134](https://github.com/nuxt/ui/issues/4134)) ([67da90a](https://github.com/nuxt/ui/commit/67da90a2f638124f640c4271d3376c5ff3fab6a1))
|
||||
* **module:** configure `@nuxt/fonts` with default weights ([276268d](https://github.com/nuxt/ui/commit/276268d311f57715cec47bc600a0ccc3d3885682))
|
||||
* **NavigationMenu:** arrow position conflict ([#4137](https://github.com/nuxt/ui/issues/4137)) ([0dc4678](https://github.com/nuxt/ui/commit/0dc4678c68e4b500be49c38336dc75b73843e38d))
|
||||
* **Select:** support more primitive types in `value` field ([#4105](https://github.com/nuxt/ui/issues/4105)) ([09b4699](https://github.com/nuxt/ui/commit/09b4699aeadaa195ea081509f8e237bb2c346238))
|
||||
* **Slider:** handle generic types ([d7a4d02](https://github.com/nuxt/ui/commit/d7a4d029b77d2dfa0b8efcd2755d482fa5e31fd3))
|
||||
* **Stepper:** use `div` tag for `title` & `description` ([a57844e](https://github.com/nuxt/ui/commit/a57844e41676c13ed1af861424961b88cee7b4da)), closes [#4096](https://github.com/nuxt/ui/issues/4096)
|
||||
* **Tabs:** prevent trigger truncate without parent width ([06e5689](https://github.com/nuxt/ui/commit/06e5689da80b36205d0548d5d6b58510938e4a6e)), closes [#4056](https://github.com/nuxt/ui/issues/4056)
|
||||
* **Tabs:** set `focus:outline-none` with `link` variant ([999a0f8](https://github.com/nuxt/ui/commit/999a0f84671fad20fa3dc50c6774af2e0200b32e))
|
||||
* **templates:** dont write unused variants in theme files ([d3df3bb](https://github.com/nuxt/ui/commit/d3df3bb929fe6732f27b182d1664213884a662ec))
|
||||
* **Toaster:** allow `base` slot override ([c63d2f3](https://github.com/nuxt/ui/commit/c63d2f380aac16f1d1e812516df3dca7fa7c8034))
|
||||
* **vue:** make `useAppConfig` reactive ([869c070](https://github.com/nuxt/ui/commit/869c0708bd351c7be44e5e430c348b19dd316db9)), closes [#3952](https://github.com/nuxt/ui/issues/3952)
|
||||
|
||||
## [3.1.1](https://github.com/nuxt/ui/compare/v3.1.0...v3.1.1) (2025-05-02)
|
||||
|
||||
### Features
|
||||
|
||||
* **useOverlay:** add `closeAll` method ([#3984](https://github.com/nuxt/ui/issues/3984)) ([ac4c194](https://github.com/nuxt/ui/commit/ac4c1946ec399aec59b4bce9d538e3ff67868abf))
|
||||
* **useOverlay:** add `isOpen` method to check overlay state ([#4041](https://github.com/nuxt/ui/issues/4041)) ([a4f3f6d](https://github.com/nuxt/ui/commit/a4f3f6d531f9c0281f99085a6688d296f8f13f2f))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Calendar:** add `place-items-center` to grid row ([#4034](https://github.com/nuxt/ui/issues/4034)) ([8dfdd63](https://github.com/nuxt/ui/commit/8dfdd63ce3b3a0e904f7c013c774cf9aaf46b240))
|
||||
* **defineShortcuts:** bring back `meta` to `ctrl` convert on non macos platforms ([f3b8b17](https://github.com/nuxt/ui/commit/f3b8b17dc5f43936ef7ffb11c1ed7f9a5f94d0bb)), closes [#3869](https://github.com/nuxt/ui/issues/3869) [#3318](https://github.com/nuxt/ui/issues/3318)
|
||||
* **module:** support `nuxt-nightly` ([#3996](https://github.com/nuxt/ui/issues/3996)) ([bc0a296](https://github.com/nuxt/ui/commit/bc0a296f9d68ca72cd991b11cd3489b63c7b13db))
|
||||
* **NavigationMenu:** remove `sm:w-auto` from content slot ([aebf0b3](https://github.com/nuxt/ui/commit/aebf0b3dca50c51c093cb6abf16c4fd995fc1b39)), closes [#3987](https://github.com/nuxt/ui/issues/3987)
|
||||
* **RadioGroup:** improve items `value` field type ([#3995](https://github.com/nuxt/ui/issues/3995)) ([195773e](https://github.com/nuxt/ui/commit/195773ec7dac12ccc3a0a67867751e8ca634cc04))
|
||||
* **templates:** put back args to watch in dev ([#4033](https://github.com/nuxt/ui/issues/4033)) ([c5bdec0](https://github.com/nuxt/ui/commit/c5bdec0f64963ef602975270a09a1ee795cdacf9))
|
||||
* **theme:** add missing `border-bg` / `divide-bg` utilities ([82b5f32](https://github.com/nuxt/ui/commit/82b5f322ebd8a08e63588122bd4ef567dcb8ba8c))
|
||||
* **theme:** add missing `ring-offset-*` utilities ([#3992](https://github.com/nuxt/ui/issues/3992)) ([e5df026](https://github.com/nuxt/ui/commit/e5df0269935be59df759fe0e1378acb2b0d9014a))
|
||||
* **theme:** define default shades for named tailwindcss colors ([8acf3c5](https://github.com/nuxt/ui/commit/8acf3c51db6c2f9443d04be6ba7d9f062c5cf8ab)), closes [#3977](https://github.com/nuxt/ui/issues/3977)
|
||||
* **theme:** improve app config types for `ui` object ([591d59f](https://github.com/nuxt/ui/commit/591d59fe89f1d9bf016c121bf9160f73fe0a290d)), closes [#3579](https://github.com/nuxt/ui/issues/3579)
|
||||
* **theme:** use `[@theme](https://github.com/theme) inline` to properly reference css variables ([6131871](https://github.com/nuxt/ui/commit/6131871a0d124c5942d60dc5dff20981e8542e51)), closes [#4018](https://github.com/nuxt/ui/issues/4018)
|
||||
* **useOverlay:** improve types and docs ([#4012](https://github.com/nuxt/ui/issues/4012)) ([39e29fc](https://github.com/nuxt/ui/commit/39e29fccf1840c723a13237d65002501b2829b70))
|
||||
|
||||
## [3.1.0](https://github.com/nuxt/ui/compare/v3.0.2...v3.1.0) (2025-04-24)
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **OverlayProvider:** return an overlay instance from `.open()` (#3829)
|
||||
|
||||
### Features
|
||||
|
||||
* **App:** add global `portal` prop ([#3688](https://github.com/nuxt/ui/issues/3688)) ([29fa462](https://github.com/nuxt/ui/commit/29fa46276d6bf69b5b87880c476c6f778c2820bf))
|
||||
* **Carousel:** add `select` event ([#3678](https://github.com/nuxt/ui/issues/3678)) ([22edfd7](https://github.com/nuxt/ui/commit/22edfd708ae3eeadbd4ff6c830cdfd5632948286))
|
||||
* **CheckboxGroup:** new component ([#3862](https://github.com/nuxt/ui/issues/3862)) ([9c3d53a](https://github.com/nuxt/ui/commit/9c3d53a02d6254f6b5c90e5fed826b8aefcdb042))
|
||||
* **components:** add new `content-top` and `content-bottom` slots ([#3886](https://github.com/nuxt/ui/issues/3886)) ([1a46394](https://github.com/nuxt/ui/commit/1a463946681e152aa18372118d0fef4a7d8055a5))
|
||||
* **Form:** add `attach` prop to opt-out of nested form attachement ([#3939](https://github.com/nuxt/ui/issues/3939)) ([1a0d7a3](https://github.com/nuxt/ui/commit/1a0d7a3103cf7591b019ef3ad685e2f3786ef6f2))
|
||||
* **Form:** export loading state ([#3861](https://github.com/nuxt/ui/issues/3861)) ([fdee252](https://github.com/nuxt/ui/commit/fdee2522bb9d8361ff3e9fdd4aa2350be8e49b05))
|
||||
* **InputMenu/SelectMenu:** handle `resetSearchTermOnSelect` ([cea881a](https://github.com/nuxt/ui/commit/cea881abdc139b39df89b503cf2ab872f4246c8f)), closes [#3782](https://github.com/nuxt/ui/issues/3782)
|
||||
* **InputNumber:** add support for `stepSnapping` & `disableWheelChange` props ([#3731](https://github.com/nuxt/ui/issues/3731)) ([f5e6284](https://github.com/nuxt/ui/commit/f5e62849c9313063396ab0e3a9b7d22d98ef69bc))
|
||||
* **locale:** add Bulgarian language ([#3783](https://github.com/nuxt/ui/issues/3783)) ([a0c9731](https://github.com/nuxt/ui/commit/a0c9731f634020e76aa98a9a68d673591d35e8c9))
|
||||
* **locale:** add Kazakh language ([#3875](https://github.com/nuxt/ui/issues/3875)) ([43153c4](https://github.com/nuxt/ui/commit/43153c4e91034b728059e7a9bed05888e48f8890))
|
||||
* **locale:** add Tajik language ([#3850](https://github.com/nuxt/ui/issues/3850)) ([f42a79b](https://github.com/nuxt/ui/commit/f42a79b5efe8dc65430a83799ebb0ee737773820))
|
||||
* **locale:** add Uyghur language ([#3878](https://github.com/nuxt/ui/issues/3878)) ([b7fc69b](https://github.com/nuxt/ui/commit/b7fc69baa718ff65b3988d0fa9f143306fa8fac4))
|
||||
* **Modal/Popover/Slideover:** add `close:prevent` event ([#3958](https://github.com/nuxt/ui/issues/3958)) ([f486423](https://github.com/nuxt/ui/commit/f4864233812eac0ed37e0a2d076a95c285a22c01))
|
||||
* **module:** define default color shades ([#3916](https://github.com/nuxt/ui/issues/3916)) ([7ac7aa9](https://github.com/nuxt/ui/commit/7ac7aa9ba73b6aca1bc29b0de2e95c60b2700135))
|
||||
* **module:** define neutral utilities ([#3629](https://github.com/nuxt/ui/issues/3629)) ([d49e0da](https://github.com/nuxt/ui/commit/d49e0dadeea2a58e05e60b2c461b29ce1d334d2b))
|
||||
* **module:** dynamic `rounded-*` utilities ([#3906](https://github.com/nuxt/ui/issues/3906)) ([f9737c8](https://github.com/nuxt/ui/commit/f9737c8f401bf8bc5307674fad6defe2aeeeb907))
|
||||
* **OverlayProvider:** return an overlay instance from `.open()` ([#3829](https://github.com/nuxt/ui/issues/3829)) ([f3098df](https://github.com/nuxt/ui/commit/f3098df84a3b7f58f7ccc1233bc8b45eab99ee10))
|
||||
* **PinInput:** add `autofocus` / `autofocus-delay` props ([0456670](https://github.com/nuxt/ui/commit/0456670dac1153340220603c8c116e3b71f72ae7)), closes [#3717](https://github.com/nuxt/ui/issues/3717)
|
||||
* **RadioGroup:** add `card` and `table` variants ([#3178](https://github.com/nuxt/ui/issues/3178)) ([4d138ad](https://github.com/nuxt/ui/commit/4d138ad6719a074f5f994006d12745ca05bec9c4))
|
||||
* **Select:** handle `onSelect` field in items ([8640831](https://github.com/nuxt/ui/commit/864083156a79dfb5d0be868658b7f9fc77570178))
|
||||
* **Table:** conditionally apply classes to `tr` and `td` ([#3866](https://github.com/nuxt/ui/issues/3866)) ([80dfa88](https://github.com/nuxt/ui/commit/80dfa88ea442571ee1dc673317cc7baa8cacd8a3))
|
||||
* **Tabs:** add `list-leading` and `list-trailing` slots ([#3837](https://github.com/nuxt/ui/issues/3837)) ([3447a06](https://github.com/nuxt/ui/commit/3447a062b636a469089d6e9bdcfcb3dce9063ee5))
|
||||
* **Textarea:** add `autoresize-delay` prop ([06414d3](https://github.com/nuxt/ui/commit/06414d344b151ad6e1a3225a9f5f1f76d58d319c)), closes [#3730](https://github.com/nuxt/ui/issues/3730)
|
||||
* **Textarea:** add `icon`, `loading`, etc. props to match Input ([cb193f1](https://github.com/nuxt/ui/commit/cb193f1d25b5c73ca03dcf10864800350dd1c290))
|
||||
* **Textarea:** add `resize-none` class with `autoresize` prop ([ffafd81](https://github.com/nuxt/ui/commit/ffafd81e1ed25074430668c792e5e1c6afc22bd0))
|
||||
* **unplugin:** routing support for inertia ([#3845](https://github.com/nuxt/ui/issues/3845)) ([d059efc](https://github.com/nuxt/ui/commit/d059efca258da7ae5116e829189a492824ac1d87))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Accordion:** use `div` instead of `h3` for header tag ([75e4792](https://github.com/nuxt/ui/commit/75e4792f7f00c55229253289c4f806f2b6fc9854)), closes [#3963](https://github.com/nuxt/ui/issues/3963)
|
||||
* **Alert/Toast:** display actions when using slots ([5086363](https://github.com/nuxt/ui/commit/50863635d653c8083772046ddc5b828fba7047d0)), closes [#3950](https://github.com/nuxt/ui/issues/3950)
|
||||
* **Carousel:** move arrows inside container on mobile ([d339dcb](https://github.com/nuxt/ui/commit/d339dcbfb8fe244bd198d247d8448e3ef856dfef)), closes [#3813](https://github.com/nuxt/ui/issues/3813)
|
||||
* **CheckboxGroup:** proxy slots & `ui` prop ([bc06185](https://github.com/nuxt/ui/commit/bc061852822edd2dfb832a46dd6388123ec5771e))
|
||||
* **CommandPalette:** consistent alignement with other components ([d25265c](https://github.com/nuxt/ui/commit/d25265c8b7d34e01af8827d9af5eccb98bf30e9e))
|
||||
* **CommandPalette:** increase input font size to avoid zoom ([d227a10](https://github.com/nuxt/ui/commit/d227a105d8d409ea0753153afaecf639ddb80fed))
|
||||
* **CommandPalette:** prevent hover background on disabled items ([ba534f1](https://github.com/nuxt/ui/commit/ba534f18b94383c97b2654d892ee4b8b024b3fab))
|
||||
* **components:** refactor types after `@nuxt/module-builder` upgrade ([#3855](https://github.com/nuxt/ui/issues/3855)) ([39c861a](https://github.com/nuxt/ui/commit/39c861a64bbd452256ebd1a14a257b94c35855d4))
|
||||
* **components:** respect `transform-origin` in popper content ([#3919](https://github.com/nuxt/ui/issues/3919)) ([01d8dc7](https://github.com/nuxt/ui/commit/01d8dc72adb0b32ad68bb4a98bf24b17f435a89c))
|
||||
* **ContextMenu/DropdownMenu:** handle RTL mode ([#3744](https://github.com/nuxt/ui/issues/3744)) ([1ae5cc0](https://github.com/nuxt/ui/commit/1ae5cc09cb2eca6b6f53eb04db9dcc731b696cae))
|
||||
* **ContextMenuContent/DropdownMenuContent:** remove unwanted `any` ([#3741](https://github.com/nuxt/ui/issues/3741)) ([97274f1](https://github.com/nuxt/ui/commit/97274f15b8bfe457e7e206f81b32e3febf0f875d))
|
||||
* **Form:** input and output type inference ([#3938](https://github.com/nuxt/ui/issues/3938)) ([f429498](https://github.com/nuxt/ui/commit/f42949820be9be9fca41abc653dc12c033e1eeec))
|
||||
* **Form:** loses focus on submit ([#3796](https://github.com/nuxt/ui/issues/3796)) ([8e78eb1](https://github.com/nuxt/ui/commit/8e78eb15c85beef1c814206c4a192d4eb00a7e86))
|
||||
* **InputMenu/Select/SelectMenu:** add `min-w-fit` to `content` slot ([#3922](https://github.com/nuxt/ui/issues/3922)) ([f6b3761](https://github.com/nuxt/ui/commit/f6b376110c8bee2c41ae3137bb972aad402ebff1))
|
||||
* **InputMenu/SelectMenu:** correctly call `onSelect` events ([#3735](https://github.com/nuxt/ui/issues/3735)) ([f25fed5](https://github.com/nuxt/ui/commit/f25fed58e988b304e79cdb536d544d257395cf89))
|
||||
* **InputMenu/SelectMenu:** prevent `disabled` items to be selected ([8435a0f](https://github.com/nuxt/ui/commit/8435a0fe1622eb5b6863b6e4751c9d2d1be36db9)), closes [#3474](https://github.com/nuxt/ui/issues/3474)
|
||||
* **InputMenu/SelectMenu:** remove `valueKey` string case ([9ca213b](https://github.com/nuxt/ui/commit/9ca213bd3340492d7503a34bd142e1f79a697050)), closes [#3949](https://github.com/nuxt/ui/issues/3949) [#3331](https://github.com/nuxt/ui/issues/3331)
|
||||
* **InputMenu/SelectMenu:** support arbitrary `value` ([#3779](https://github.com/nuxt/ui/issues/3779)) ([52a97e2](https://github.com/nuxt/ui/commit/52a97e2df7903f91e3134931eb0d6bd4c528f71f))
|
||||
* **InputMenu:** emit `change` on multiple item removal ([9d2fed1](https://github.com/nuxt/ui/commit/9d2fed125013e3bbfbf9435678729cd05254a5e8)), closes [#3756](https://github.com/nuxt/ui/issues/3756)
|
||||
* **Link:** proxy `download` property ([#3879](https://github.com/nuxt/ui/issues/3879)) ([47cdc2e](https://github.com/nuxt/ui/commit/47cdc2e1d8cd9803ebc954ccae110d62b9a08779))
|
||||
* **NavigationMenu:** add `sm:w-auto` content slot ([abe0859](https://github.com/nuxt/ui/commit/abe0859691e06564f68335bd82dcd121e976408e)), closes [#3788](https://github.com/nuxt/ui/issues/3788)
|
||||
* **Skeleton:** improve accessibility ([#3613](https://github.com/nuxt/ui/issues/3613)) ([3484832](https://github.com/nuxt/ui/commit/3484832822015a224ce6fbeae5132018875557e6))
|
||||
* **Stepper:** ui prop override on `icon` and `content` slots ([1d45980](https://github.com/nuxt/ui/commit/1d459803dc052a16b8966ee89c71646bf6ef1c16)), closes [#3785](https://github.com/nuxt/ui/issues/3785)
|
||||
* **Table:** improve `data` reactivity ([#3967](https://github.com/nuxt/ui/issues/3967)) ([6e27304](https://github.com/nuxt/ui/commit/6e27304d8ca459a04667bac404084264a8cf58fd))
|
||||
* **Table:** pass header `colspan` to `th` ([#3926](https://github.com/nuxt/ui/issues/3926)) ([122e8ac](https://github.com/nuxt/ui/commit/122e8ac8f41ba093cd350c3ce642263263f77296))
|
||||
* **Tree:** simplify reusable template types ([#3836](https://github.com/nuxt/ui/issues/3836)) ([3deed4c](https://github.com/nuxt/ui/commit/3deed4c271cad4adc2a4c47d5dd02e95a14ce11a))
|
||||
* **types:** allow color identifiers with dashes ([#3896](https://github.com/nuxt/ui/issues/3896)) ([e5a1e26](https://github.com/nuxt/ui/commit/e5a1e26f9db763b54caed4ca313f44d1b5fe269d))
|
||||
* **types:** handle `ClassValue` in `ui` prop ([eea1415](https://github.com/nuxt/ui/commit/eea14155aa612649bc969d806ec5df4295945c70)), closes [#3860](https://github.com/nuxt/ui/issues/3860)
|
||||
* **types:** improve dynamic slots ([#3857](https://github.com/nuxt/ui/issues/3857)) ([8dd9d08](https://github.com/nuxt/ui/commit/8dd9d08209e47a7d9a5654db4fb936b4cbcfc021))
|
||||
* **usePortal:** adjust portal target resolution logic ([#3954](https://github.com/nuxt/ui/issues/3954)) ([db11db6](https://github.com/nuxt/ui/commit/db11db6ff1ce4b27a66aaa03f07870ba36426181))
|
||||
* **vite:** vitest skipping nuxt imports transformations ([#3925](https://github.com/nuxt/ui/issues/3925)) ([c31bffa](https://github.com/nuxt/ui/commit/c31bffad1b8afeda584bca8c73bb7f790eb12a9f))
|
||||
|
||||
## [3.0.2](https://github.com/nuxt/ui/compare/v3.0.1...v3.0.2) (2025-03-28)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -16,6 +16,10 @@ Nuxt UI harnesses the combined strengths of [Reka UI](https://reka-ui.com/), [Ta
|
||||
> [!NOTE]
|
||||
> You are on the `v3` development branch, check out the [v2 branch](https://github.com/nuxt/ui/tree/v2) for Nuxt UI v2.
|
||||
|
||||
> [!TIP]
|
||||
> **Looking for more components ?**
|
||||
> Check out [Nuxt UI Pro](https://ui.nuxt.com/pro), a collection of premium Vue components, composables, and utilities built on top of Nuxt UI for faster and more powerful app development.
|
||||
|
||||
## Documentation
|
||||
|
||||
Visit https://ui.nuxt.com to explore the documentation.
|
||||
|
||||
@@ -11,7 +11,7 @@ export default defineBuildConfig({
|
||||
delimiters: ['', ''],
|
||||
values: {
|
||||
// Used in development to import directly from theme
|
||||
'const isUiDev = true': 'const isUiDev = false'
|
||||
'process.argv.includes(\'--uiDev\')': 'false'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -31,8 +31,9 @@ const component = ({ name, primitive, pro, prose, content }) => {
|
||||
? `
|
||||
<script lang="ts">
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
${pro ? `import type { ComponentConfig } from '@nuxt/ui'` : ''}
|
||||
import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
${!pro ? `import type { ComponentConfig } from '../types/utils'` : ''}
|
||||
|
||||
type ${upperName} = ComponentConfig<typeof theme, AppConfig, ${upperName}${pro ? `, '${key}'` : ''}>
|
||||
|
||||
@@ -62,11 +63,11 @@ defineSlots<${upperName}Slots>()
|
||||
|
||||
const appConfig = useAppConfig() as ${upperName}['AppConfig']
|
||||
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.${camelName} || {}) })())
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.${pro ? 'uiPro' : 'ui'}?.${camelName} || {}) })())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive :as="as" :class="ui.root({ class: [props.class, props.ui?.root] })">
|
||||
<Primitive :as="as" :class="ui.root({ class: [props.ui?.root, props.class] })">
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
@@ -75,8 +76,9 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.${camelName}
|
||||
<script lang="ts">
|
||||
import type { ${upperName}RootProps, ${upperName}RootEmits } from 'reka-ui'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
${pro ? `import type { ComponentConfig } from '@nuxt/ui'` : ''}
|
||||
import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
${!pro ? `import type { ComponentConfig } from '../types/utils'` : ''}
|
||||
|
||||
type ${upperName} = ComponentConfig<typeof theme, AppConfig, ${upperName}${pro ? `, '${key}'` : ''}>
|
||||
|
||||
@@ -105,11 +107,11 @@ const appConfig = useAppConfig() as ${upperName}['AppConfig']
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props), emits)
|
||||
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.${camelName} || {}) })())
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.${pro ? 'uiPro' : 'ui'}?.${camelName} || {}) })())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<${upperName}Root v-bind="rootProps" :class="ui.root({ class: [props.class, props.ui?.root] })" />
|
||||
<${upperName}Root v-bind="rootProps" :class="ui.root({ class: [props.ui?.root, props.class] })" />
|
||||
</template>
|
||||
`
|
||||
}
|
||||
@@ -145,7 +147,8 @@ const test = ({ name, prose, content }) => {
|
||||
? undefined
|
||||
: `
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import ${upperName}, { type ${upperName}Props, type ${upperName}Slots } from '../../${content ? '../' : ''}src/runtime/components/${content ? 'content/' : ''}${upperName}.vue'
|
||||
import ${upperName} from '../../${content ? '../' : ''}src/runtime/components/${content ? 'content/' : ''}${upperName}.vue'
|
||||
import type { ${upperName}Props, ${upperName}Slots } from '../../${content ? '../' : ''}src/runtime/components/${content ? 'content/' : ''}${upperName}.vue'
|
||||
import ComponentRender from '../${content ? '../' : ''}component-render'
|
||||
|
||||
describe('${upperName}', () => {
|
||||
@@ -186,6 +189,7 @@ links:${primitive
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/${pro ? 'ui-pro' : 'ui'}/tree/v3/src/runtime/components/${upperName}.vue
|
||||
navigation.badge: Soon
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -53,7 +53,7 @@ provide('navigation', mappedNavigation)
|
||||
<NuxtLoadingIndicator color="var(--ui-primary)" :height="2" />
|
||||
|
||||
<template v-if="!route.path.startsWith('/examples')">
|
||||
<Banner />
|
||||
<!-- <Banner /> -->
|
||||
|
||||
<Header :links="links" />
|
||||
</template>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<UBanner
|
||||
id="ui3-launch"
|
||||
title="Nuxt UI v3 is officially released!"
|
||||
icon="i-lucide-rocket"
|
||||
:actions="[
|
||||
{
|
||||
@@ -10,9 +11,5 @@
|
||||
}
|
||||
]"
|
||||
close
|
||||
>
|
||||
<template #title>
|
||||
<span class="font-semibold">Nuxt UI v3</span> is officially released.
|
||||
</template>
|
||||
</UBanner>
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -20,7 +20,7 @@ watch(framework, () => {
|
||||
color="neutral"
|
||||
:ui="{
|
||||
indicator: 'bg-default',
|
||||
trigger: 'px-1 data-[state=active]:text-highlighted'
|
||||
trigger: 'px-1 data-[state=active]:text-highlighted w-full'
|
||||
}"
|
||||
size="xs"
|
||||
@update:model-value="(framework = $event as string)"
|
||||
|
||||
@@ -22,9 +22,7 @@ onMounted(() => {
|
||||
|
||||
const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
|
||||
|
||||
const githubLink = computed(() => {
|
||||
return `https://github.com/nuxt/${value.value}`
|
||||
})
|
||||
const githubLink = computed(() => `https://github.com/nuxt/${value.value}`)
|
||||
|
||||
const desktopLinks = computed(() => props.links.map(({ icon, ...link }) => link))
|
||||
const mobileLinks = computed(() => [
|
||||
@@ -36,6 +34,16 @@ const mobileLinks = computed(() => [
|
||||
target: '_blank'
|
||||
}
|
||||
])
|
||||
|
||||
const items = computed(() => {
|
||||
const ui2 = { label: 'v2.22.0', to: 'https://ui2.nuxt.com' }
|
||||
const uiPro1 = { label: 'v1.8.0', to: 'https://ui2.nuxt.com/pro' }
|
||||
|
||||
return [
|
||||
{ label: `v${config.version}`, active: true, color: 'primary' as const, checked: true, type: 'checkbox' as const },
|
||||
route.path === '/' ? ui2 : route.path.startsWith('/pro') ? uiPro1 : module.value === 'ui-pro' ? uiPro1 : ui2
|
||||
]
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -53,7 +61,7 @@ const mobileLinks = computed(() => [
|
||||
<UDropdownMenu
|
||||
v-slot="{ open }"
|
||||
:modal="false"
|
||||
:items="[{ label: `v${config.version}`, active: true, color: 'primary', checked: true, type: 'checkbox' }, { label: module === 'ui-pro' ? 'v1.7.1' : 'v2.21.1', to: module === 'ui-pro' ? 'https://ui2.nuxt.com/pro' : 'https://ui2.nuxt.com' }]"
|
||||
:items="items"
|
||||
:ui="{ content: 'w-(--reka-dropdown-menu-trigger-width) min-w-0' }"
|
||||
size="xs"
|
||||
>
|
||||
|
||||
@@ -20,7 +20,7 @@ watch(module, () => {
|
||||
color="neutral"
|
||||
:ui="{
|
||||
indicator: 'bg-default',
|
||||
trigger: 'px-1 data-[state=active]:text-highlighted'
|
||||
trigger: 'px-1 data-[state=active]:text-highlighted w-full'
|
||||
}"
|
||||
size="xs"
|
||||
@update:model-value="(module = $event as string)"
|
||||
|
||||
77
docs/app/components/PageHeaderLinks.vue
Normal file
77
docs/app/components/PageHeaderLinks.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<script setup lang="ts">
|
||||
const route = useRoute()
|
||||
const toast = useToast()
|
||||
const { copy, copied } = useClipboard()
|
||||
const site = useSiteConfig()
|
||||
|
||||
const mdPath = computed(() => `${site.url}/raw${route.path}.md`)
|
||||
|
||||
const items = [
|
||||
{
|
||||
label: 'Copy Markdown link',
|
||||
icon: 'i-lucide-link',
|
||||
onSelect() {
|
||||
copy(mdPath.value)
|
||||
toast.add({
|
||||
title: 'Copied to clipboard',
|
||||
icon: 'i-lucide-check-circle'
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'View as Markdown',
|
||||
icon: 'i-simple-icons:markdown',
|
||||
target: '_blank',
|
||||
to: `/raw${route.path}.md`
|
||||
},
|
||||
{
|
||||
label: 'Open in ChatGPT',
|
||||
icon: 'i-simple-icons:openai',
|
||||
target: '_blank',
|
||||
to: `https://chatgpt.com/?hints=search&q=${encodeURIComponent(`Read ${mdPath.value} so I can ask questions about it.`)}`
|
||||
},
|
||||
{
|
||||
label: 'Open in Claude',
|
||||
icon: 'i-simple-icons:anthropic',
|
||||
target: '_blank',
|
||||
to: `https://claude.ai/new?q=${encodeURIComponent(`Read ${mdPath.value} so I can ask questions about it.`)}`
|
||||
}
|
||||
]
|
||||
|
||||
async function copyPage() {
|
||||
copy(await $fetch<string>(`/raw${route.path}.md`))
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UButtonGroup>
|
||||
<UButton
|
||||
label="Copy page"
|
||||
:icon="copied ? 'i-lucide-copy-check' : 'i-lucide-copy'"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
:ui="{
|
||||
leadingIcon: [copied ? 'text-primary' : 'text-neutral', 'size-3.5']
|
||||
}"
|
||||
@click="copyPage"
|
||||
/>
|
||||
<UDropdownMenu
|
||||
:items="items"
|
||||
:content="{
|
||||
align: 'end',
|
||||
side: 'bottom',
|
||||
sideOffset: 8
|
||||
}"
|
||||
:ui="{
|
||||
content: 'w-48'
|
||||
}"
|
||||
>
|
||||
<UButton
|
||||
icon="i-lucide-chevron-down"
|
||||
size="sm"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
/>
|
||||
</UDropdownMenu>
|
||||
</UButtonGroup>
|
||||
</template>
|
||||
@@ -153,7 +153,8 @@ const options = computed(() => {
|
||||
const items = propItems.length
|
||||
? propItems.map((item: any) => ({
|
||||
value: item,
|
||||
label: String(item)
|
||||
label: String(item),
|
||||
chip: key.toLowerCase().endsWith('color') ? { color: item } : undefined
|
||||
}))
|
||||
: prop?.type === 'boolean' || prop?.type === 'boolean | undefined'
|
||||
? [{ value: true, label: 'true' }, { value: false, label: 'false' }]
|
||||
|
||||
@@ -38,7 +38,7 @@ const schemaProps = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ProseCollapsible v-if="schemaProps?.length" class="mt-1">
|
||||
<ProseCollapsible v-if="schemaProps?.length" class="mt-1 mb-0">
|
||||
<ProseUl>
|
||||
<ProseLi v-for="schemaProp in schemaProps" :key="schemaProp.name">
|
||||
<HighlightInlineType :type="`${schemaProp.name}${schemaProp.required === false ? '?' : ''}: ${schemaProp.type}`" />
|
||||
|
||||
@@ -25,7 +25,11 @@ function getEmojiFlag(locale: string): string {
|
||||
kk: 'kz', // Kazakh -> Kazakhstan
|
||||
km: 'kh', // Khmer -> Cambodia
|
||||
ko: 'kr', // Korean -> South Korea
|
||||
ky: 'kg', // Kyrgyz -> Kyrgyzstan
|
||||
lb: 'lu', // Luxembourgish -> Luxembourg
|
||||
ms: 'my', // Malay -> Malaysia
|
||||
nb: 'no', // Norwegian Bokmål -> Norway
|
||||
sl: 'si', // Slovenian -> Slovenia
|
||||
sv: 'se', // Swedish -> Sweden
|
||||
uk: 'ua', // Ukrainian -> Ukraine
|
||||
ur: 'pk', // Urdu -> Pakistan
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import { CalendarDate } from '@internationalized/date'
|
||||
|
||||
const date = shallowRef(new CalendarDate(2025, 4, 2))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-4">
|
||||
<UCalendar v-model="date" :month-controls="false" :year-controls="false" />
|
||||
|
||||
<div class="flex justify-between gap-4">
|
||||
<UButton color="neutral" variant="outline" @click="date = date.subtract({ months: 1 })">
|
||||
Prev
|
||||
</UButton>
|
||||
|
||||
<UButton color="neutral" variant="outline" @click="date = date.add({ months: 1 })">
|
||||
Next
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,58 @@
|
||||
<script setup lang="ts">
|
||||
const items = [
|
||||
'https://picsum.photos/640/640?random=1',
|
||||
'https://picsum.photos/640/640?random=2',
|
||||
'https://picsum.photos/640/640?random=3',
|
||||
'https://picsum.photos/640/640?random=4',
|
||||
'https://picsum.photos/640/640?random=5',
|
||||
'https://picsum.photos/640/640?random=6'
|
||||
]
|
||||
|
||||
const carousel = useTemplateRef('carousel')
|
||||
const activeIndex = ref(0)
|
||||
|
||||
function onClickPrev() {
|
||||
activeIndex.value--
|
||||
}
|
||||
function onClickNext() {
|
||||
activeIndex.value++
|
||||
}
|
||||
function onSelect(index: number) {
|
||||
activeIndex.value = index
|
||||
}
|
||||
|
||||
function select(index: number) {
|
||||
activeIndex.value = index
|
||||
|
||||
carousel.value?.emblaApi?.scrollTo(index)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex-1 w-full">
|
||||
<UCarousel
|
||||
ref="carousel"
|
||||
v-slot="{ item }"
|
||||
arrows
|
||||
:items="items"
|
||||
:prev="{ onClick: onClickPrev }"
|
||||
:next="{ onClick: onClickNext }"
|
||||
class="w-full max-w-xs mx-auto"
|
||||
@select="onSelect"
|
||||
>
|
||||
<img :src="item" width="320" height="320" class="rounded-lg">
|
||||
</UCarousel>
|
||||
|
||||
<div class="flex gap-1 justify-between pt-4 max-w-xs mx-auto">
|
||||
<div
|
||||
v-for="(item, index) in items"
|
||||
:key="index"
|
||||
class="size-11 opacity-25 hover:opacity-100 transition-opacity"
|
||||
:class="{ 'opacity-100': activeIndex === index }"
|
||||
@click="select(index)"
|
||||
>
|
||||
<img :src="item" width="44" height="44" class="rounded-lg">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -83,7 +83,7 @@ const groups = [
|
||||
</template>
|
||||
|
||||
<template #billing-label="{ item }">
|
||||
{{ item.label }}
|
||||
<span class="font-medium text-primary">{{ item.label }}</span>
|
||||
|
||||
<UBadge variant="subtle" size="sm">
|
||||
50% off
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
<script setup lang="ts">
|
||||
const groups = [
|
||||
{
|
||||
id: 'actions',
|
||||
items: [
|
||||
{
|
||||
label: 'Add new file',
|
||||
suffix: 'Create a new file in the current directory',
|
||||
icon: 'i-lucide-file-plus',
|
||||
kbds: ['meta', 'N']
|
||||
},
|
||||
{
|
||||
label: 'Add new folder',
|
||||
suffix: 'Create a new folder in the current directory',
|
||||
icon: 'i-lucide-folder-plus',
|
||||
kbds: ['meta', 'F']
|
||||
},
|
||||
{
|
||||
label: 'Search files',
|
||||
suffix: 'Search across all files in the project',
|
||||
icon: 'i-lucide-search',
|
||||
kbds: ['meta', 'P']
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
suffix: 'Open application settings',
|
||||
icon: 'i-lucide-settings',
|
||||
kbds: ['meta', ',']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'recent',
|
||||
label: 'Recent',
|
||||
items: [
|
||||
{
|
||||
label: 'project.vue',
|
||||
suffix: 'components/',
|
||||
icon: 'i-vscode-icons-file-type-vue'
|
||||
},
|
||||
{
|
||||
label: 'readme.md',
|
||||
suffix: 'docs/',
|
||||
icon: 'i-vscode-icons-file-type-markdown'
|
||||
},
|
||||
{
|
||||
label: 'package.json',
|
||||
suffix: 'root/',
|
||||
icon: 'i-vscode-icons-file-type-node'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCommandPalette :groups="groups" class="flex-1 h-80">
|
||||
<template #footer>
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<UIcon name="i-simple-icons-nuxtdotjs" class="size-5 text-dimmed ml-1" />
|
||||
<div class="flex items-center gap-1">
|
||||
<UButton color="neutral" variant="ghost" label="Open Command" class="text-dimmed" size="xs">
|
||||
<template #trailing>
|
||||
<UKbd value="enter" />
|
||||
</template>
|
||||
</UButton>
|
||||
<USeparator orientation="vertical" class="h-4" />
|
||||
<UButton color="neutral" variant="ghost" label="Actions" class="text-dimmed" size="xs">
|
||||
<template #trailing>
|
||||
<UKbd value="meta" />
|
||||
<UKbd value="k" />
|
||||
</template>
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</UCommandPalette>
|
||||
</template>
|
||||
@@ -1,9 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { refDebounced } from '@vueuse/core'
|
||||
|
||||
const searchTerm = ref('')
|
||||
const searchTermDebounced = refDebounced(searchTerm, 200)
|
||||
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
key: 'command-palette-users',
|
||||
params: { q: searchTermDebounced },
|
||||
transform: (data: { id: number, name: string, email: string }[]) => {
|
||||
return data?.map(user => ({ id: user.id, label: user.name, suffix: user.email, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || []
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
<script setup lang="ts">
|
||||
const toast = useToast()
|
||||
|
||||
const groups = [{
|
||||
id: 'actions',
|
||||
label: 'Actions',
|
||||
items: [{
|
||||
label: 'Create new',
|
||||
icon: 'i-lucide-plus',
|
||||
children: [{
|
||||
label: 'New file',
|
||||
icon: 'i-lucide-file-plus',
|
||||
suffix: 'Create a new file in the current directory',
|
||||
onSelect(e: Event) {
|
||||
e.preventDefault()
|
||||
toast.add({ title: 'New file created!' })
|
||||
},
|
||||
kbds: ['meta', 'N']
|
||||
}, {
|
||||
label: 'New folder',
|
||||
icon: 'i-lucide-folder-plus',
|
||||
suffix: 'Create a new folder in the current directory',
|
||||
onSelect(e: Event) {
|
||||
e.preventDefault()
|
||||
toast.add({ title: 'New folder created!' })
|
||||
},
|
||||
kbds: ['meta', 'F']
|
||||
}, {
|
||||
label: 'New project',
|
||||
icon: 'i-lucide-folder-git',
|
||||
suffix: 'Create a new project from a template',
|
||||
onSelect(e: Event) {
|
||||
e.preventDefault()
|
||||
toast.add({ title: 'New project created!' })
|
||||
},
|
||||
kbds: ['meta', 'P']
|
||||
}]
|
||||
}, {
|
||||
label: 'Share',
|
||||
icon: 'i-lucide-share',
|
||||
children: [{
|
||||
label: 'Copy link',
|
||||
icon: 'i-lucide-link',
|
||||
suffix: 'Copy a link to the current item',
|
||||
onSelect(e: Event) {
|
||||
e.preventDefault()
|
||||
toast.add({ title: 'Link copied to clipboard!' })
|
||||
},
|
||||
kbds: ['meta', 'L']
|
||||
}, {
|
||||
label: 'Share via email',
|
||||
icon: 'i-lucide-mail',
|
||||
suffix: 'Share the current item via email',
|
||||
onSelect(e: Event) {
|
||||
e.preventDefault()
|
||||
toast.add({ title: 'Share via email dialog opened!' })
|
||||
}
|
||||
}, {
|
||||
label: 'Share on social',
|
||||
icon: 'i-lucide-share-2',
|
||||
suffix: 'Share the current item on social media',
|
||||
children: [{
|
||||
label: 'Twitter',
|
||||
icon: 'i-simple-icons-twitter',
|
||||
onSelect(e: Event) {
|
||||
e.preventDefault()
|
||||
toast.add({ title: 'Shared on Twitter!' })
|
||||
}
|
||||
}, {
|
||||
label: 'LinkedIn',
|
||||
icon: 'i-simple-icons-linkedin',
|
||||
onSelect(e: Event) {
|
||||
e.preventDefault()
|
||||
toast.add({ title: 'Shared on LinkedIn!' })
|
||||
}
|
||||
}, {
|
||||
label: 'Facebook',
|
||||
icon: 'i-simple-icons-facebook',
|
||||
onSelect(e: Event) {
|
||||
e.preventDefault()
|
||||
toast.add({ title: 'Shared on Facebook!' })
|
||||
}
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
label: 'Settings',
|
||||
icon: 'i-lucide-settings',
|
||||
children: [{
|
||||
label: 'General',
|
||||
icon: 'i-lucide-sliders',
|
||||
suffix: 'Configure general settings',
|
||||
onSelect(e: Event) {
|
||||
e.preventDefault()
|
||||
toast.add({ title: 'General settings opened!' })
|
||||
}
|
||||
}, {
|
||||
label: 'Appearance',
|
||||
icon: 'i-lucide-palette',
|
||||
suffix: 'Customize the appearance',
|
||||
onSelect(e: Event) {
|
||||
e.preventDefault()
|
||||
toast.add({ title: 'Appearance settings opened!' })
|
||||
}
|
||||
}, {
|
||||
label: 'Security',
|
||||
icon: 'i-lucide-shield',
|
||||
suffix: 'Manage security settings',
|
||||
onSelect(e: Event) {
|
||||
e.preventDefault()
|
||||
toast.add({ title: 'Security settings opened!' })
|
||||
}
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCommandPalette :groups="groups" class="flex-1" />
|
||||
</template>
|
||||
@@ -28,7 +28,7 @@ const items = [
|
||||
</template>
|
||||
|
||||
<template #refresh-trailing>
|
||||
<UIcon v-if="loading" name="i-lucide-refresh-cw" class="shrink-0 size-5 text-primary animate-spin" />
|
||||
<UIcon v-if="loading" name="i-lucide-loader-circle" class="shrink-0 size-5 text-primary animate-spin" />
|
||||
</template>
|
||||
</UContextMenu>
|
||||
</template>
|
||||
|
||||
@@ -3,7 +3,7 @@ const open = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UDrawer v-model:open="open" :dismissible="false" :ui="{ header: 'flex items-center justify-between' }">
|
||||
<UDrawer v-model:open="open" :dismissible="false" :handle="false" :ui="{ header: 'flex items-center justify-between' }">
|
||||
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
|
||||
|
||||
<template #header>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
const open = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UDrawer
|
||||
v-model:open="open"
|
||||
:dismissible="false"
|
||||
:overlay="false"
|
||||
:handle="false"
|
||||
:modal="false"
|
||||
:ui="{ header: 'flex items-center justify-between' }"
|
||||
>
|
||||
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
|
||||
|
||||
<template #header>
|
||||
<h2 class="text-highlighted font-semibold">
|
||||
Drawer non-dismissible
|
||||
</h2>
|
||||
|
||||
<UButton color="neutral" variant="ghost" icon="i-lucide-x" @click="open = false" />
|
||||
</template>
|
||||
|
||||
<template #body>
|
||||
<Placeholder class="h-48" />
|
||||
</template>
|
||||
</UDrawer>
|
||||
</template>
|
||||
@@ -15,6 +15,9 @@ const schema = z.object({
|
||||
select: z.string().refine(value => value === 'option-2', {
|
||||
message: 'Select Option 2'
|
||||
}),
|
||||
selectMultiple: z.array(z.string()).refine(values => values.includes('option-2'), {
|
||||
message: 'Include Option 2'
|
||||
}),
|
||||
selectMenu: z.any().refine(option => option?.value === 'option-2', {
|
||||
message: 'Select Option 2'
|
||||
}),
|
||||
@@ -30,6 +33,9 @@ const schema = z.object({
|
||||
radioGroup: z.string().refine(value => value === 'option-2', {
|
||||
message: 'Select Option 2'
|
||||
}),
|
||||
checkboxGroup: z.any().refine(values => !!values?.find((option: any) => option === 'option-2'), {
|
||||
message: 'Include Option 2'
|
||||
}),
|
||||
slider: z.number().max(20, { message: 'Must be less than 20' }),
|
||||
pin: z.string().regex(/^\d$/).array().length(5)
|
||||
})
|
||||
@@ -78,6 +84,10 @@ async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||
<USelect v-model="state.select" :items="items" class="w-full" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField name="selectMultiple" label="Select (Multiple)">
|
||||
<USelect v-model="state.selectMultiple" multiple :items="items" class="w-full" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField name="selectMenu" label="Select Menu">
|
||||
<USelectMenu v-model="state.selectMenu" :items="items" class="w-full" />
|
||||
</UFormField>
|
||||
@@ -101,11 +111,14 @@ async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||
<UFormField label="Textarea" name="textarea">
|
||||
<UTextarea v-model="state.textarea" class="w-full" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField name="radioGroup">
|
||||
<URadioGroup v-model="state.radioGroup" legend="Radio group" :items="items" />
|
||||
</UFormField>
|
||||
|
||||
<div class="flex gap-4">
|
||||
<UFormField name="radioGroup">
|
||||
<URadioGroup v-model="state.radioGroup" legend="Radio group" :items="items" />
|
||||
</UFormField>
|
||||
<UFormField name="checkboxGroup">
|
||||
<UCheckboxGroup v-model="state.checkboxGroup" legend="Checkbox group" :items="items" />
|
||||
</UFormField>
|
||||
</div>
|
||||
<UFormField name="pin" label="Pin Input" :error-pattern="/(pin)\..*/">
|
||||
<UPinInput v-model="state.pin" />
|
||||
</UFormField>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { object, string, nonempty, refine, type Infer } from 'superstruct'
|
||||
import { object, string, nonempty, refine } from 'superstruct'
|
||||
import type { Infer } from 'superstruct'
|
||||
import type { FormSubmitEvent } from '@nuxt/ui'
|
||||
|
||||
const schema = object({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { object, string, type InferType } from 'yup'
|
||||
import { object, string } from 'yup'
|
||||
import type { InferType } from 'yup'
|
||||
import type { FormSubmitEvent } from '@nuxt/ui'
|
||||
|
||||
const schema = object({
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { refDebounced } from '@vueuse/core'
|
||||
import type { AvatarProps } from '@nuxt/ui'
|
||||
|
||||
const searchTerm = ref('')
|
||||
const searchTermDebounced = refDebounced(searchTerm, 200)
|
||||
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
key: 'typicode-users',
|
||||
params: { q: searchTermDebounced },
|
||||
transform: (data: { id: number, name: string }[]) => {
|
||||
return data?.map(user => ({
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
const tags = ref(['Vue'])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UFormField label="Tags" required>
|
||||
<UInputTags v-model="tags" placeholder="Enter tags..." />
|
||||
</UFormField>
|
||||
</template>
|
||||
@@ -10,7 +10,7 @@ const domain = ref(domains[0])
|
||||
v-model="value"
|
||||
placeholder="nuxt"
|
||||
:ui="{
|
||||
base: 'pl-[57px]',
|
||||
base: 'pl-14.5',
|
||||
leading: 'pointer-events-none'
|
||||
}"
|
||||
>
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
const value = ref('npx nuxi module add ui')
|
||||
const copied = ref(false)
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
|
||||
function copy() {
|
||||
navigator.clipboard.writeText(value.value)
|
||||
copied.value = true
|
||||
const value = ref('npx nuxt module add ui')
|
||||
|
||||
setTimeout(() => {
|
||||
copied.value = false
|
||||
}, 2000)
|
||||
}
|
||||
const { copy, copied } = useClipboard()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -25,7 +19,7 @@ function copy() {
|
||||
size="sm"
|
||||
:icon="copied ? 'i-lucide-copy-check' : 'i-lucide-copy'"
|
||||
aria-label="Copy to clipboard"
|
||||
@click="copy"
|
||||
@click="copy(value)"
|
||||
/>
|
||||
</UTooltip>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { vMaska } from 'maska/vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-2">
|
||||
<UInput v-maska="'#### #### #### ####'" placeholder="4242 4242 4242 4242" icon="i-lucide-credit-card" />
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<UInput v-maska="'##/##'" placeholder="MM/YY" icon="i-lucide-calendar" />
|
||||
<UInput v-maska="'###'" placeholder="CVC" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -10,8 +10,8 @@ const open = ref(false)
|
||||
<Placeholder class="h-48" />
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<UButton label="Cancel" color="neutral" variant="outline" @click="open = false" />
|
||||
<template #footer="{ close }">
|
||||
<UButton label="Cancel" color="neutral" variant="outline" @click="close" />
|
||||
<UButton label="Submit" color="neutral" />
|
||||
</template>
|
||||
</UModal>
|
||||
|
||||
@@ -13,7 +13,9 @@ const modal = overlay.create(LazyModalExample, {
|
||||
})
|
||||
|
||||
async function open() {
|
||||
const shouldIncrement = await modal.open()
|
||||
const instance = modal.open()
|
||||
|
||||
const shouldIncrement = await instance.result
|
||||
|
||||
if (shouldIncrement) {
|
||||
count.value++
|
||||
|
||||
@@ -65,6 +65,7 @@ const items = [
|
||||
class="w-full justify-center"
|
||||
:ui="{
|
||||
viewport: 'sm:w-(--reka-navigation-menu-viewport-width)',
|
||||
content: 'sm:w-auto',
|
||||
childList: 'sm:w-96',
|
||||
childLinkDescription: 'text-balance line-clamp-2'
|
||||
}"
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<script lang="ts" setup>
|
||||
const open = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UPopover
|
||||
v-model:open="open"
|
||||
:dismissible="false"
|
||||
:ui="{ content: 'w-(--reka-popper-anchor-width) p-4' }"
|
||||
>
|
||||
<template #anchor>
|
||||
<UInput placeholder="Focus to open" @focus="open = true" @blur="open = false" />
|
||||
</template>
|
||||
|
||||
<template #content>
|
||||
<Placeholder class="w-full aspect-square" />
|
||||
</template>
|
||||
</UPopover>
|
||||
</template>
|
||||
@@ -0,0 +1,43 @@
|
||||
<script setup lang="ts">
|
||||
const open = ref(false)
|
||||
const anchor = ref({ x: 0, y: 0 })
|
||||
|
||||
const reference = computed(() => ({
|
||||
getBoundingClientRect: () =>
|
||||
({
|
||||
width: 0,
|
||||
height: 0,
|
||||
left: anchor.value.x,
|
||||
right: anchor.value.x,
|
||||
top: anchor.value.y,
|
||||
bottom: anchor.value.y,
|
||||
...anchor.value
|
||||
} as DOMRect)
|
||||
}))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UPopover
|
||||
:open="open"
|
||||
:reference="reference"
|
||||
:content="{ side: 'top', sideOffset: 16, updatePositionStrategy: 'always' }"
|
||||
>
|
||||
<div
|
||||
class="flex items-center justify-center rounded-md border border-dashed border-accented text-sm aspect-video w-72"
|
||||
@pointerenter="open = true"
|
||||
@pointerleave="open = false"
|
||||
@pointermove="(ev) => {
|
||||
anchor.x = ev.clientX
|
||||
anchor.y = ev.clientY
|
||||
}"
|
||||
>
|
||||
Hover me
|
||||
</div>
|
||||
|
||||
<template #content>
|
||||
<div class="p-4">
|
||||
{{ anchor.x.toFixed(0) }} - {{ anchor.y.toFixed(0) }}
|
||||
</div>
|
||||
</template>
|
||||
</UPopover>
|
||||
</template>
|
||||
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { refDebounced } from '@vueuse/core'
|
||||
import type { AvatarProps } from '@nuxt/ui'
|
||||
|
||||
const searchTerm = ref('')
|
||||
const searchTermDebounced = refDebounced(searchTerm, 200)
|
||||
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
key: 'typicode-users',
|
||||
params: { q: searchTermDebounced },
|
||||
transform: (data: { id: number, name: string }[]) => {
|
||||
return data?.map(user => ({
|
||||
|
||||
@@ -10,8 +10,8 @@ const open = ref(false)
|
||||
<Placeholder class="h-full" />
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<UButton label="Cancel" color="neutral" variant="outline" @click="open = false" />
|
||||
<template #footer="{ close }">
|
||||
<UButton label="Cancel" color="neutral" variant="outline" @click="close" />
|
||||
<UButton label="Submit" color="neutral" />
|
||||
</template>
|
||||
</USlideover>
|
||||
|
||||
@@ -13,7 +13,9 @@ const slideover = overlay.create(LazySlideoverExample, {
|
||||
})
|
||||
|
||||
async function open() {
|
||||
const shouldIncrement = await slideover.open()
|
||||
const instance = slideover.open()
|
||||
|
||||
const shouldIncrement = await instance.result
|
||||
|
||||
if (shouldIncrement) {
|
||||
count.value++
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
<script setup lang="ts">
|
||||
import { h, resolveComponent } from 'vue'
|
||||
import type { TableColumn, TableRow } from '@nuxt/ui'
|
||||
|
||||
const UBadge = resolveComponent('UBadge')
|
||||
|
||||
type Payment = {
|
||||
id: string
|
||||
date: string
|
||||
status: 'paid' | 'failed' | 'refunded'
|
||||
email: string
|
||||
amount: number
|
||||
}
|
||||
|
||||
const data = ref<Payment[]>([{
|
||||
id: '4600',
|
||||
date: '2024-03-11T15:30:00',
|
||||
status: 'paid',
|
||||
email: 'james.anderson@example.com',
|
||||
amount: 594
|
||||
}, {
|
||||
id: '4599',
|
||||
date: '2024-03-11T10:10:00',
|
||||
status: 'failed',
|
||||
email: 'mia.white@example.com',
|
||||
amount: 276
|
||||
}, {
|
||||
id: '4598',
|
||||
date: '2024-03-11T08:50:00',
|
||||
status: 'refunded',
|
||||
email: 'william.brown@example.com',
|
||||
amount: 315
|
||||
}, {
|
||||
id: '4597',
|
||||
date: '2024-03-10T19:45:00',
|
||||
status: 'paid',
|
||||
email: 'emma.davis@example.com',
|
||||
amount: 529
|
||||
}, {
|
||||
id: '4596',
|
||||
date: '2024-03-10T15:55:00',
|
||||
status: 'paid',
|
||||
email: 'ethan.harris@example.com',
|
||||
amount: 639
|
||||
}])
|
||||
|
||||
const columns: TableColumn<Payment>[] = [{
|
||||
accessorKey: 'id',
|
||||
header: '#',
|
||||
cell: ({ row }) => `#${row.getValue('id')}`
|
||||
}, {
|
||||
accessorKey: 'date',
|
||||
header: 'Date',
|
||||
cell: ({ row }) => {
|
||||
return new Date(row.getValue('date')).toLocaleString('en-US', {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false
|
||||
})
|
||||
}
|
||||
}, {
|
||||
accessorKey: 'status',
|
||||
header: 'Status',
|
||||
cell: ({ row }) => {
|
||||
const color = ({
|
||||
paid: 'success' as const,
|
||||
failed: 'error' as const,
|
||||
refunded: 'neutral' as const
|
||||
})[row.getValue('status') as string]
|
||||
|
||||
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
|
||||
}
|
||||
}, {
|
||||
accessorKey: 'email',
|
||||
header: 'Email'
|
||||
}, {
|
||||
accessorKey: 'amount',
|
||||
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
||||
footer: ({ column }) => {
|
||||
const total = column.getFacetedRowModel().rows.reduce((acc: number, row: TableRow<Payment>) => acc + Number.parseFloat(row.getValue('amount')), 0)
|
||||
|
||||
const formatted = new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'EUR'
|
||||
}).format(total)
|
||||
|
||||
return h('div', { class: 'text-right font-medium' }, `Total: ${formatted}`)
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
const amount = Number.parseFloat(row.getValue('amount'))
|
||||
|
||||
const formatted = new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'EUR'
|
||||
}).format(amount)
|
||||
|
||||
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||
}
|
||||
}]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UTable :data="data" :columns="columns" class="flex-1" />
|
||||
</template>
|
||||
@@ -0,0 +1,82 @@
|
||||
<script setup lang="ts">
|
||||
import type { TableColumn } from '@nuxt/ui'
|
||||
import { useSortable } from '@vueuse/integrations/useSortable.mjs'
|
||||
|
||||
type Payment = {
|
||||
id: string
|
||||
date: string
|
||||
email: string
|
||||
amount: number
|
||||
}
|
||||
|
||||
const data = ref<Payment[]>([{
|
||||
id: '4600',
|
||||
date: '2024-03-11T15:30:00',
|
||||
email: 'james.anderson@example.com',
|
||||
amount: 594
|
||||
}, {
|
||||
id: '4599',
|
||||
date: '2024-03-11T10:10:00',
|
||||
email: 'mia.white@example.com',
|
||||
amount: 276
|
||||
}, {
|
||||
id: '4598',
|
||||
date: '2024-03-11T08:50:00',
|
||||
email: 'william.brown@example.com',
|
||||
amount: 315
|
||||
}, {
|
||||
id: '4597',
|
||||
date: '2024-03-10T19:45:00',
|
||||
email: 'emma.davis@example.com',
|
||||
amount: 529
|
||||
}])
|
||||
|
||||
const columns: TableColumn<Payment>[] = [{
|
||||
accessorKey: 'id',
|
||||
header: '#',
|
||||
cell: ({ row }) => `#${row.getValue('id')}`
|
||||
}, {
|
||||
accessorKey: 'date',
|
||||
header: 'Date',
|
||||
cell: ({ row }) => {
|
||||
return new Date(row.getValue('date')).toLocaleString('en-US', {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false
|
||||
})
|
||||
}
|
||||
}, {
|
||||
accessorKey: 'email',
|
||||
header: 'Email'
|
||||
}, {
|
||||
accessorKey: 'amount',
|
||||
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
||||
cell: ({ row }) => {
|
||||
const amount = Number.parseFloat(row.getValue('amount'))
|
||||
const formatted = new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'EUR'
|
||||
}).format(amount)
|
||||
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||
}
|
||||
}]
|
||||
|
||||
useSortable('.my-table-tbody', data, {
|
||||
animation: 150
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<UTable
|
||||
ref="table"
|
||||
:data="data"
|
||||
:columns="columns"
|
||||
:ui="{
|
||||
tbody: 'my-table-tbody'
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -2,6 +2,7 @@
|
||||
import { h, resolveComponent } from 'vue'
|
||||
import { upperFirst } from 'scule'
|
||||
import type { TableColumn } from '@nuxt/ui'
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
|
||||
const UButton = resolveComponent('UButton')
|
||||
const UCheckbox = resolveComponent('UCheckbox')
|
||||
@@ -9,6 +10,7 @@ const UBadge = resolveComponent('UBadge')
|
||||
const UDropdownMenu = resolveComponent('UDropdownMenu')
|
||||
|
||||
const toast = useToast()
|
||||
const { copy } = useClipboard()
|
||||
|
||||
type Payment = {
|
||||
id: string
|
||||
@@ -220,7 +222,7 @@ const columns: TableColumn<Payment>[] = [{
|
||||
}, {
|
||||
label: 'Copy payment ID',
|
||||
onSelect() {
|
||||
navigator.clipboard.writeText(row.original.id)
|
||||
copy(row.original.id)
|
||||
|
||||
toast.add({
|
||||
title: 'Payment ID copied to clipboard!',
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
<script setup lang="ts">
|
||||
import { h, resolveComponent } from 'vue'
|
||||
import type { TableColumn } from '@nuxt/ui'
|
||||
import { getGroupedRowModel } from '@tanstack/vue-table'
|
||||
import type { GroupingOptions } from '@tanstack/vue-table'
|
||||
|
||||
const UBadge = resolveComponent('UBadge')
|
||||
|
||||
type Account = {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
|
||||
type PaymentStatus = 'paid' | 'failed' | 'refunded'
|
||||
|
||||
type Payment = {
|
||||
id: string
|
||||
date: string
|
||||
status: PaymentStatus
|
||||
email: string
|
||||
amount: number
|
||||
account: Account
|
||||
}
|
||||
|
||||
const getColorByStatus = (status: PaymentStatus) => {
|
||||
return {
|
||||
paid: 'success',
|
||||
failed: 'error',
|
||||
refunded: 'neutral'
|
||||
}[status]
|
||||
}
|
||||
|
||||
const data = ref<Payment[]>([
|
||||
{
|
||||
id: '4600',
|
||||
date: '2024-03-11T15:30:00',
|
||||
status: 'paid',
|
||||
email: 'james.anderson@example.com',
|
||||
amount: 594,
|
||||
account: {
|
||||
id: '1',
|
||||
name: 'Account 1'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '4599',
|
||||
date: '2024-03-11T10:10:00',
|
||||
status: 'failed',
|
||||
email: 'mia.white@example.com',
|
||||
amount: 276,
|
||||
account: {
|
||||
id: '2',
|
||||
name: 'Account 2'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '4598',
|
||||
date: '2024-03-11T08:50:00',
|
||||
status: 'refunded',
|
||||
email: 'william.brown@example.com',
|
||||
amount: 315,
|
||||
account: {
|
||||
id: '1',
|
||||
name: 'Account 1'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '4597',
|
||||
date: '2024-03-10T19:45:00',
|
||||
status: 'paid',
|
||||
email: 'emma.davis@example.com',
|
||||
amount: 529,
|
||||
account: {
|
||||
id: '2',
|
||||
name: 'Account 2'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '4596',
|
||||
date: '2024-03-10T15:55:00',
|
||||
status: 'paid',
|
||||
email: 'ethan.harris@example.com',
|
||||
amount: 639,
|
||||
account: {
|
||||
id: '1',
|
||||
name: 'Account 1'
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
const columns: TableColumn<Payment>[] = [
|
||||
{
|
||||
id: 'title',
|
||||
header: 'Item'
|
||||
},
|
||||
{
|
||||
id: 'account_id',
|
||||
accessorKey: 'account.id'
|
||||
},
|
||||
{
|
||||
accessorKey: 'id',
|
||||
header: '#',
|
||||
cell: ({ row }) =>
|
||||
row.getIsGrouped()
|
||||
? `${row.getValue('id')} records`
|
||||
: `#${row.getValue('id')}`,
|
||||
aggregationFn: 'count'
|
||||
},
|
||||
{
|
||||
accessorKey: 'date',
|
||||
header: 'Date',
|
||||
cell: ({ row }) => {
|
||||
return new Date(row.getValue('date')).toLocaleString('en-US', {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false
|
||||
})
|
||||
},
|
||||
aggregationFn: 'max'
|
||||
},
|
||||
{
|
||||
accessorKey: 'status',
|
||||
header: 'Status'
|
||||
},
|
||||
{
|
||||
accessorKey: 'email',
|
||||
header: 'Email',
|
||||
meta: {
|
||||
class: {
|
||||
td: 'w-full'
|
||||
}
|
||||
},
|
||||
cell: ({ row }) =>
|
||||
row.getIsGrouped()
|
||||
? `${row.getValue('email')} customers`
|
||||
: row.getValue('email'),
|
||||
aggregationFn: 'uniqueCount'
|
||||
},
|
||||
{
|
||||
accessorKey: 'amount',
|
||||
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
||||
cell: ({ row }) => {
|
||||
const amount = Number.parseFloat(row.getValue('amount'))
|
||||
|
||||
const formatted = new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'EUR'
|
||||
}).format(amount)
|
||||
|
||||
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||
},
|
||||
aggregationFn: 'sum'
|
||||
}
|
||||
]
|
||||
|
||||
const grouping_options = ref<GroupingOptions>({
|
||||
groupedColumnMode: 'remove',
|
||||
getGroupedRowModel: getGroupedRowModel()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UTable
|
||||
:data="data"
|
||||
:columns="columns"
|
||||
:grouping="['account_id', 'status']"
|
||||
:grouping-options="grouping_options"
|
||||
:ui="{
|
||||
root: 'min-w-full',
|
||||
td: 'empty:p-0' // helps with the colspaned row added for expand slot
|
||||
}"
|
||||
>
|
||||
<template #title-cell="{ row }">
|
||||
<div v-if="row.getIsGrouped()" class="flex items-center">
|
||||
<span
|
||||
class="inline-block"
|
||||
:style="{ width: `calc(${row.depth} * 1rem)` }"
|
||||
/>
|
||||
|
||||
<UButton
|
||||
variant="outline"
|
||||
color="neutral"
|
||||
class="mr-2"
|
||||
size="xs"
|
||||
:icon="row.getIsExpanded() ? 'i-lucide-minus' : 'i-lucide-plus'"
|
||||
@click="row.toggleExpanded()"
|
||||
/>
|
||||
<strong v-if="row.groupingColumnId === 'account_id'">{{
|
||||
row.original.account.name
|
||||
}}</strong>
|
||||
<UBadge
|
||||
v-else-if="row.groupingColumnId === 'status'"
|
||||
:color="getColorByStatus(row.original.status)"
|
||||
class="capitalize"
|
||||
variant="subtle"
|
||||
>
|
||||
{{ row.original.status }}
|
||||
</UBadge>
|
||||
</div>
|
||||
</template>
|
||||
</UTable>
|
||||
</template>
|
||||
@@ -0,0 +1,88 @@
|
||||
<script setup lang="ts">
|
||||
import type { TableColumn } from '@nuxt/ui'
|
||||
import { useInfiniteScroll } from '@vueuse/core'
|
||||
|
||||
const UAvatar = resolveComponent('UAvatar')
|
||||
|
||||
type User = {
|
||||
id: number
|
||||
firstName: string
|
||||
username: string
|
||||
email: string
|
||||
image: string
|
||||
}
|
||||
|
||||
type UserResponse = {
|
||||
users: User[]
|
||||
total: number
|
||||
skip: number
|
||||
limit: number
|
||||
}
|
||||
|
||||
const skip = ref(0)
|
||||
|
||||
const { data, status, execute } = await useFetch('https://dummyjson.com/users?limit=10&select=firstName,username,email,image', {
|
||||
key: 'table-users-infinite-scroll',
|
||||
params: { skip },
|
||||
transform: (data?: UserResponse) => {
|
||||
return data?.users
|
||||
},
|
||||
lazy: true,
|
||||
immediate: false
|
||||
})
|
||||
|
||||
const columns: TableColumn<User>[] = [{
|
||||
accessorKey: 'id',
|
||||
header: 'ID'
|
||||
}, {
|
||||
accessorKey: 'image',
|
||||
header: 'Avatar',
|
||||
cell: ({ row }) => h(UAvatar, { src: row.original.image })
|
||||
}, {
|
||||
accessorKey: 'firstName',
|
||||
header: 'First name'
|
||||
}, {
|
||||
accessorKey: 'email',
|
||||
header: 'Email'
|
||||
}, {
|
||||
accessorKey: 'username',
|
||||
header: 'Username'
|
||||
}]
|
||||
|
||||
const users = ref<User[]>([])
|
||||
|
||||
watch(data, () => {
|
||||
users.value = [
|
||||
...users.value,
|
||||
...(data.value || [])
|
||||
]
|
||||
})
|
||||
|
||||
execute()
|
||||
|
||||
const table = useTemplateRef<ComponentPublicInstance>('table')
|
||||
|
||||
onMounted(() => {
|
||||
useInfiniteScroll(table.value?.$el, () => {
|
||||
skip.value += 10
|
||||
}, {
|
||||
distance: 200,
|
||||
canLoadMore: () => {
|
||||
return status.value !== 'pending'
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<UTable
|
||||
ref="table"
|
||||
:data="users"
|
||||
:columns="columns"
|
||||
:loading="status === 'pending'"
|
||||
sticky
|
||||
class="flex-1 h-80"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -2,12 +2,14 @@
|
||||
import { h, resolveComponent } from 'vue'
|
||||
import type { TableColumn } from '@nuxt/ui'
|
||||
import type { Row } from '@tanstack/vue-table'
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
|
||||
const UButton = resolveComponent('UButton')
|
||||
const UBadge = resolveComponent('UBadge')
|
||||
const UDropdownMenu = resolveComponent('UDropdownMenu')
|
||||
|
||||
const toast = useToast()
|
||||
const { copy } = useClipboard()
|
||||
|
||||
type Payment = {
|
||||
id: string
|
||||
@@ -119,7 +121,7 @@ function getRowItems(row: Row<Payment>) {
|
||||
}, {
|
||||
label: 'Copy payment ID',
|
||||
onSelect() {
|
||||
navigator.clipboard.writeText(row.original.id)
|
||||
copy(row.original.id)
|
||||
|
||||
toast.add({
|
||||
title: 'Payment ID copied to clipboard!',
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
<script setup lang="ts">
|
||||
import { h, resolveComponent } from 'vue'
|
||||
import type { ContextMenuItem, TableColumn, TableRow } from '@nuxt/ui'
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
|
||||
const UBadge = resolveComponent('UBadge')
|
||||
const UCheckbox = resolveComponent('UCheckbox')
|
||||
|
||||
const toast = useToast()
|
||||
const { copy } = useClipboard()
|
||||
|
||||
type Payment = {
|
||||
id: string
|
||||
date: string
|
||||
status: 'paid' | 'failed' | 'refunded'
|
||||
email: string
|
||||
amount: number
|
||||
}
|
||||
|
||||
const data = ref<Payment[]>([{
|
||||
id: '4600',
|
||||
date: '2024-03-11T15:30:00',
|
||||
status: 'paid',
|
||||
email: 'james.anderson@example.com',
|
||||
amount: 594
|
||||
}, {
|
||||
id: '4599',
|
||||
date: '2024-03-11T10:10:00',
|
||||
status: 'failed',
|
||||
email: 'mia.white@example.com',
|
||||
amount: 276
|
||||
}, {
|
||||
id: '4598',
|
||||
date: '2024-03-11T08:50:00',
|
||||
status: 'refunded',
|
||||
email: 'william.brown@example.com',
|
||||
amount: 315
|
||||
}, {
|
||||
id: '4597',
|
||||
date: '2024-03-10T19:45:00',
|
||||
status: 'paid',
|
||||
email: 'emma.davis@example.com',
|
||||
amount: 529
|
||||
}, {
|
||||
id: '4596',
|
||||
date: '2024-03-10T15:55:00',
|
||||
status: 'paid',
|
||||
email: 'ethan.harris@example.com',
|
||||
amount: 639
|
||||
}])
|
||||
|
||||
const columns: TableColumn<Payment>[] = [{
|
||||
id: 'select',
|
||||
header: ({ table }) => h(UCheckbox, {
|
||||
'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(),
|
||||
'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value),
|
||||
'aria-label': 'Select all'
|
||||
}),
|
||||
cell: ({ row }) => h(UCheckbox, {
|
||||
'modelValue': row.getIsSelected(),
|
||||
'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value),
|
||||
'aria-label': 'Select row'
|
||||
})
|
||||
}, {
|
||||
accessorKey: 'id',
|
||||
header: '#',
|
||||
cell: ({ row }) => `#${row.getValue('id')}`
|
||||
}, {
|
||||
accessorKey: 'date',
|
||||
header: 'Date',
|
||||
cell: ({ row }) => {
|
||||
return new Date(row.getValue('date')).toLocaleString('en-US', {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false
|
||||
})
|
||||
}
|
||||
}, {
|
||||
accessorKey: 'status',
|
||||
header: 'Status',
|
||||
cell: ({ row }) => {
|
||||
const color = ({
|
||||
paid: 'success' as const,
|
||||
failed: 'error' as const,
|
||||
refunded: 'neutral' as const
|
||||
})[row.getValue('status') as string]
|
||||
|
||||
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
|
||||
}
|
||||
}, {
|
||||
accessorKey: 'email',
|
||||
header: 'Email'
|
||||
}, {
|
||||
accessorKey: 'amount',
|
||||
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
||||
cell: ({ row }) => {
|
||||
const amount = Number.parseFloat(row.getValue('amount'))
|
||||
|
||||
const formatted = new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'EUR'
|
||||
}).format(amount)
|
||||
|
||||
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||
}
|
||||
}]
|
||||
|
||||
const items = ref<ContextMenuItem[]>([])
|
||||
|
||||
function getRowItems(row: TableRow<Payment>) {
|
||||
return [{
|
||||
type: 'label' as const,
|
||||
label: 'Actions'
|
||||
}, {
|
||||
label: 'Copy payment ID',
|
||||
onSelect() {
|
||||
copy(row.original.id)
|
||||
|
||||
toast.add({
|
||||
title: 'Payment ID copied to clipboard!',
|
||||
color: 'success',
|
||||
icon: 'i-lucide-circle-check'
|
||||
})
|
||||
}
|
||||
}, {
|
||||
label: row.getIsExpanded() ? 'Collapse' : 'Expand',
|
||||
onSelect() {
|
||||
row.toggleExpanded()
|
||||
}
|
||||
}, {
|
||||
type: 'separator' as const
|
||||
}, {
|
||||
label: 'View customer'
|
||||
}, {
|
||||
label: 'View payment details'
|
||||
}]
|
||||
}
|
||||
|
||||
function onContextmenu(_e: Event, row: TableRow<Payment>) {
|
||||
items.value = getRowItems(row)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UContextMenu :items="items">
|
||||
<UTable
|
||||
:data="data"
|
||||
:columns="columns"
|
||||
class="flex-1"
|
||||
@contextmenu="onContextmenu"
|
||||
>
|
||||
<template #expanded="{ row }">
|
||||
<pre>{{ row.original }}</pre>
|
||||
</template>
|
||||
</UTable>
|
||||
</UContextMenu>
|
||||
</template>
|
||||
@@ -0,0 +1,157 @@
|
||||
<script setup lang="ts">
|
||||
import { h, resolveComponent } from 'vue'
|
||||
import type { TableColumn, TableRow } from '@nuxt/ui'
|
||||
|
||||
const UBadge = resolveComponent('UBadge')
|
||||
const UCheckbox = resolveComponent('UCheckbox')
|
||||
|
||||
type Payment = {
|
||||
id: string
|
||||
date: string
|
||||
status: 'paid' | 'failed' | 'refunded'
|
||||
email: string
|
||||
amount: number
|
||||
}
|
||||
|
||||
const data = ref<Payment[]>([{
|
||||
id: '4600',
|
||||
date: '2024-03-11T15:30:00',
|
||||
status: 'paid',
|
||||
email: 'james.anderson@example.com',
|
||||
amount: 594
|
||||
}, {
|
||||
id: '4599',
|
||||
date: '2024-03-11T10:10:00',
|
||||
status: 'failed',
|
||||
email: 'mia.white@example.com',
|
||||
amount: 276
|
||||
}, {
|
||||
id: '4598',
|
||||
date: '2024-03-11T08:50:00',
|
||||
status: 'refunded',
|
||||
email: 'william.brown@example.com',
|
||||
amount: 315
|
||||
}, {
|
||||
id: '4597',
|
||||
date: '2024-03-10T19:45:00',
|
||||
status: 'paid',
|
||||
email: 'emma.davis@example.com',
|
||||
amount: 529
|
||||
}, {
|
||||
id: '4596',
|
||||
date: '2024-03-10T15:55:00',
|
||||
status: 'paid',
|
||||
email: 'ethan.harris@example.com',
|
||||
amount: 639
|
||||
}])
|
||||
|
||||
const columns: TableColumn<Payment>[] = [{
|
||||
id: 'select',
|
||||
header: ({ table }) => h(UCheckbox, {
|
||||
'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(),
|
||||
'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value),
|
||||
'aria-label': 'Select all'
|
||||
}),
|
||||
cell: ({ row }) => h(UCheckbox, {
|
||||
'modelValue': row.getIsSelected(),
|
||||
'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value),
|
||||
'aria-label': 'Select row'
|
||||
})
|
||||
}, {
|
||||
accessorKey: 'id',
|
||||
header: '#',
|
||||
cell: ({ row }) => `#${row.getValue('id')}`
|
||||
}, {
|
||||
accessorKey: 'date',
|
||||
header: 'Date',
|
||||
cell: ({ row }) => {
|
||||
return new Date(row.getValue('date')).toLocaleString('en-US', {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false
|
||||
})
|
||||
}
|
||||
}, {
|
||||
accessorKey: 'status',
|
||||
header: 'Status',
|
||||
cell: ({ row }) => {
|
||||
const color = ({
|
||||
paid: 'success' as const,
|
||||
failed: 'error' as const,
|
||||
refunded: 'neutral' as const
|
||||
})[row.getValue('status') as string]
|
||||
|
||||
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
|
||||
}
|
||||
}, {
|
||||
accessorKey: 'email',
|
||||
header: 'Email'
|
||||
}, {
|
||||
accessorKey: 'amount',
|
||||
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
||||
cell: ({ row }) => {
|
||||
const amount = Number.parseFloat(row.getValue('amount'))
|
||||
|
||||
const formatted = new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'EUR'
|
||||
}).format(amount)
|
||||
|
||||
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||
}
|
||||
}]
|
||||
|
||||
const anchor = ref({ x: 0, y: 0 })
|
||||
|
||||
const reference = computed(() => ({
|
||||
getBoundingClientRect: () =>
|
||||
({
|
||||
width: 0,
|
||||
height: 0,
|
||||
left: anchor.value.x,
|
||||
right: anchor.value.x,
|
||||
top: anchor.value.y,
|
||||
bottom: anchor.value.y,
|
||||
...anchor.value
|
||||
} as DOMRect)
|
||||
}))
|
||||
|
||||
const open = ref(false)
|
||||
const openDebounced = refDebounced(open, 10)
|
||||
const selectedRow = ref<TableRow<Payment> | null>(null)
|
||||
|
||||
function onHover(_e: Event, row: TableRow<Payment> | null) {
|
||||
selectedRow.value = row
|
||||
|
||||
open.value = !!row
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex w-full flex-1 gap-1">
|
||||
<UTable
|
||||
:data="data"
|
||||
:columns="columns"
|
||||
class="flex-1"
|
||||
@pointermove="(ev: PointerEvent) => {
|
||||
anchor.x = ev.clientX
|
||||
anchor.y = ev.clientY
|
||||
}"
|
||||
@hover="onHover"
|
||||
/>
|
||||
|
||||
<UPopover
|
||||
:content="{ side: 'top', sideOffset: 16, updatePositionStrategy: 'always' }"
|
||||
:open="openDebounced"
|
||||
:reference="reference"
|
||||
>
|
||||
<template #content>
|
||||
<div class="p-4">
|
||||
{{ selectedRow?.original?.id }}
|
||||
</div>
|
||||
</template>
|
||||
</UPopover>
|
||||
</div>
|
||||
</template>
|
||||
@@ -112,7 +112,7 @@ function onSelect(row: TableRow<Payment>, e?: Event) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class=" flex w-full flex-1 gap-1">
|
||||
<div class="flex w-full flex-1 gap-1">
|
||||
<div class="flex-1">
|
||||
<UTable
|
||||
ref="table"
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { TableColumn, DropdownMenuItem } from '@nuxt/ui'
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
|
||||
interface User {
|
||||
id: number
|
||||
@@ -10,6 +11,7 @@ interface User {
|
||||
}
|
||||
|
||||
const toast = useToast()
|
||||
const { copy } = useClipboard()
|
||||
|
||||
const data = ref<User[]>([{
|
||||
id: 1,
|
||||
@@ -71,7 +73,8 @@ function getDropdownActions(user: User): DropdownMenuItem[][] {
|
||||
label: 'Copy user Id',
|
||||
icon: 'i-lucide-copy',
|
||||
onSelect: () => {
|
||||
navigator.clipboard.writeText(user.id.toString())
|
||||
copy(user.id.toString())
|
||||
|
||||
toast.add({
|
||||
title: 'User ID copied to clipboard!',
|
||||
color: 'success',
|
||||
|
||||
@@ -26,7 +26,7 @@ const state = reactive({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UTabs :items="items" variant="link" class="gap-4 w-full" :ui="{ trigger: 'flex-1' }">
|
||||
<UTabs :items="items" variant="link" class="gap-4 w-full" :ui="{ trigger: 'grow' }">
|
||||
<template #account="{ item }">
|
||||
<p class="text-muted mb-4">
|
||||
{{ item.description }}
|
||||
|
||||
@@ -1,22 +1,32 @@
|
||||
<script setup lang="ts">
|
||||
import type { TabsItem } from '@nuxt/ui'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const items: TabsItem[] = [
|
||||
{
|
||||
label: 'Account'
|
||||
label: 'Account',
|
||||
value: 'account'
|
||||
},
|
||||
{
|
||||
label: 'Password'
|
||||
label: 'Password',
|
||||
value: 'password'
|
||||
}
|
||||
]
|
||||
|
||||
const active = ref('0')
|
||||
|
||||
// Note: This is for demonstration purposes only. Don't do this at home.
|
||||
onMounted(() => {
|
||||
setInterval(() => {
|
||||
active.value = String((Number(active.value) + 1) % items.length)
|
||||
}, 2000)
|
||||
const active = computed({
|
||||
get() {
|
||||
return (route.query.tab as string) || 'account'
|
||||
},
|
||||
set(tab) {
|
||||
// Hash is specified here to prevent the page from scrolling to the top
|
||||
router.push({
|
||||
path: '/components/tabs',
|
||||
query: { tab },
|
||||
hash: '#control-active-item'
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
import type { TimelineItem } from '@nuxt/ui'
|
||||
|
||||
const items: TimelineItem[] = [{
|
||||
date: 'Mar 15, 2025',
|
||||
title: 'Project Kickoff',
|
||||
icon: 'i-lucide-rocket',
|
||||
value: 'kickoff'
|
||||
}, {
|
||||
date: 'Mar 22, 2025',
|
||||
title: 'Design Phase',
|
||||
icon: 'i-lucide-palette',
|
||||
value: 'design'
|
||||
}, {
|
||||
date: 'Mar 29, 2025',
|
||||
title: 'Development Sprint',
|
||||
icon: 'i-lucide-code',
|
||||
value: 'development'
|
||||
}, {
|
||||
date: 'Apr 5, 2025',
|
||||
title: 'Testing & Deployment',
|
||||
icon: 'i-lucide-check-circle',
|
||||
value: 'deployment'
|
||||
}]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UTimeline
|
||||
:items="items"
|
||||
:ui="{ item: 'even:flex-row-reverse even:-translate-x-[calc(100%-2rem)] even:text-right' }"
|
||||
:default-value="2"
|
||||
class="translate-x-[calc(50%-1rem)]"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,52 @@
|
||||
<script setup lang="ts">
|
||||
import type { TimelineItem } from '@nuxt/ui'
|
||||
|
||||
const items = [{
|
||||
date: 'Mar 15, 2025',
|
||||
title: 'Project Kickoff',
|
||||
subtitle: 'Project Initiation',
|
||||
description: 'Kicked off the project with team alignment. Set up project milestones and allocated resources.',
|
||||
icon: 'i-lucide-rocket',
|
||||
value: 'kickoff'
|
||||
}, {
|
||||
date: 'Mar 22, 2025',
|
||||
title: 'Design Phase',
|
||||
description: 'User research and design workshops. Created wireframes and prototypes for user testing.',
|
||||
icon: 'i-lucide-palette',
|
||||
value: 'design'
|
||||
}, {
|
||||
date: 'Mar 29, 2025',
|
||||
title: 'Development Sprint',
|
||||
description: 'Frontend and backend development. Implemented core features and integrated with APIs.',
|
||||
icon: 'i-lucide-code',
|
||||
value: 'development',
|
||||
slot: 'development' as const,
|
||||
developers: [
|
||||
{
|
||||
src: 'https://github.com/J-Michalek.png'
|
||||
}, {
|
||||
src: 'https://github.com/benjamincanac.png'
|
||||
}
|
||||
]
|
||||
}, {
|
||||
date: 'Apr 5, 2025',
|
||||
title: 'Testing & Deployment',
|
||||
description: 'QA testing and performance optimization. Deployed the application to production.',
|
||||
icon: 'i-lucide-check-circle',
|
||||
value: 'deployment'
|
||||
}] satisfies TimelineItem[]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UTimeline :items="items" :default-value="2" class="w-96">
|
||||
<template #development-title="{ item }">
|
||||
<div class="flex items-center gap-1">
|
||||
<span>{{ item.title }}</span>
|
||||
|
||||
<UAvatarGroup size="2xs">
|
||||
<UAvatar v-for="(developer, index) of item.developers" :key="index" v-bind="developer" />
|
||||
</UAvatarGroup>
|
||||
</div>
|
||||
</template>
|
||||
</UTimeline>
|
||||
</template>
|
||||
@@ -0,0 +1,42 @@
|
||||
<script setup lang="ts">
|
||||
import type { TimelineItem } from '@nuxt/ui'
|
||||
|
||||
const items: TimelineItem[] = [{
|
||||
date: 'Mar 15, 2025',
|
||||
title: 'Project Kickoff',
|
||||
description: 'Kicked off the project with team alignment. Set up project milestones and allocated resources.',
|
||||
icon: 'i-lucide-rocket',
|
||||
value: 'kickoff'
|
||||
}, {
|
||||
date: 'Mar 22, 2025',
|
||||
title: 'Design Phase',
|
||||
description: 'User research and design workshops. Created wireframes and prototypes for user testing.',
|
||||
icon: 'i-lucide-palette',
|
||||
value: 'design'
|
||||
}, {
|
||||
date: 'Mar 29, 2025',
|
||||
title: 'Development Sprint',
|
||||
description: 'Frontend and backend development. Implemented core features and integrated with APIs.',
|
||||
icon: 'i-lucide-code',
|
||||
value: 'development'
|
||||
}, {
|
||||
date: 'Apr 5, 2025',
|
||||
title: 'Testing & Deployment',
|
||||
description: 'QA testing and performance optimization. Deployed the application to production.',
|
||||
icon: 'i-lucide-check-circle',
|
||||
value: 'deployment'
|
||||
}]
|
||||
|
||||
const active = ref(0)
|
||||
|
||||
// Note: This is for demonstration purposes only. Don't do this at home.
|
||||
onMounted(() => {
|
||||
setInterval(() => {
|
||||
active.value = (active.value + 1) % items.length
|
||||
}, 2000)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UTimeline v-model="active" :items="items" class="w-96" />
|
||||
</template>
|
||||
@@ -0,0 +1,60 @@
|
||||
<script lang="ts" setup>
|
||||
import type { TimelineItem } from '@nuxt/ui'
|
||||
import { useTimeAgo } from '@vueuse/core'
|
||||
|
||||
const items = [{
|
||||
username: 'J-Michalek',
|
||||
date: '2025-05-24T14:58:55Z',
|
||||
action: 'opened this',
|
||||
avatar: {
|
||||
src: 'https://github.com/J-Michalek.png'
|
||||
}
|
||||
}, {
|
||||
username: 'J-Michalek',
|
||||
date: '2025-05-26T19:30:14+02:00',
|
||||
action: 'marked this pull request as ready for review',
|
||||
icon: 'i-lucide-check-circle'
|
||||
}, {
|
||||
username: 'benjamincanac',
|
||||
date: '2025-05-27T11:01:20Z',
|
||||
action: 'commented on this',
|
||||
description: 'I\'ve made a few changes, let me know what you think! Basically I updated the design, removed unnecessary divs, used Avatar component for the indicator since it supports icon already.',
|
||||
avatar: {
|
||||
src: 'https://github.com/benjamincanac.png'
|
||||
}
|
||||
}, {
|
||||
username: 'J-Michalek',
|
||||
date: '2025-05-27T11:01:20Z',
|
||||
action: 'commented on this',
|
||||
description: 'Looks great! Good job on cleaning it up.',
|
||||
avatar: {
|
||||
src: 'https://github.com/J-Michalek.png'
|
||||
}
|
||||
}, {
|
||||
username: 'benjamincanac',
|
||||
date: '2025-05-27T11:01:20Z',
|
||||
action: 'merged this',
|
||||
icon: 'i-lucide-git-merge'
|
||||
}] satisfies TimelineItem[]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UTimeline
|
||||
:items="items"
|
||||
size="xs"
|
||||
class="w-96"
|
||||
:ui="{
|
||||
date: 'float-end ms-1',
|
||||
description: 'px-3 py-2 ring ring-default mt-2 rounded-md text-default'
|
||||
}"
|
||||
>
|
||||
<template #title="{ item }">
|
||||
<span>{{ item.username }}</span>
|
||||
<span class="font-normal text-muted"> {{ item.action }}</span>
|
||||
</template>
|
||||
|
||||
<template #date="{ item }">
|
||||
{{ useTimeAgo(new Date(item.date)) }}
|
||||
</template>
|
||||
</UTimeline>
|
||||
</template>
|
||||
@@ -0,0 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
const toast = useToast()
|
||||
|
||||
function showToast() {
|
||||
toast.add({
|
||||
title: 'Uh oh! Something went wrong.',
|
||||
description: 'There was a problem with your request.',
|
||||
icon: 'i-lucide-wifi',
|
||||
progress: false
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UButton label="Show toast" color="neutral" variant="outline" @click="showToast" />
|
||||
</template>
|
||||
@@ -0,0 +1,41 @@
|
||||
<script setup lang="ts">
|
||||
const open = ref(false)
|
||||
const anchor = ref({ x: 0, y: 0 })
|
||||
|
||||
const reference = computed(() => ({
|
||||
getBoundingClientRect: () =>
|
||||
({
|
||||
width: 0,
|
||||
height: 0,
|
||||
left: anchor.value.x,
|
||||
right: anchor.value.x,
|
||||
top: anchor.value.y,
|
||||
bottom: anchor.value.y,
|
||||
...anchor.value
|
||||
} as DOMRect)
|
||||
}))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UTooltip
|
||||
:open="open"
|
||||
:reference="reference"
|
||||
:content="{ side: 'top', sideOffset: 16, updatePositionStrategy: 'always' }"
|
||||
>
|
||||
<div
|
||||
class="flex items-center justify-center rounded-md border border-dashed border-accented text-sm aspect-video w-72"
|
||||
@pointerenter="open = true"
|
||||
@pointerleave="open = false"
|
||||
@pointermove="(ev) => {
|
||||
anchor.x = ev.clientX
|
||||
anchor.y = ev.clientY
|
||||
}"
|
||||
>
|
||||
Hover me
|
||||
</div>
|
||||
|
||||
<template #content>
|
||||
{{ anchor.x.toFixed(0) }} - {{ anchor.y.toFixed(0) }}
|
||||
</template>
|
||||
</UTooltip>
|
||||
</template>
|
||||
@@ -98,7 +98,7 @@ export function useLinks() {
|
||||
label: 'Raycast Extension',
|
||||
description: 'Access Nuxt UI components without leaving your editor.',
|
||||
icon: 'i-simple-icons-raycast',
|
||||
to: 'https://www.raycast.com/HugoRCD/nuxt-ui',
|
||||
to: 'https://www.raycast.com/HugoRCD/nuxt',
|
||||
target: '_blank'
|
||||
}, {
|
||||
label: 'Figma to Code',
|
||||
|
||||
@@ -59,7 +59,7 @@ provide('navigation', mappedNavigation)
|
||||
<UApp>
|
||||
<NuxtLoadingIndicator color="#FFF" />
|
||||
|
||||
<Banner />
|
||||
<!-- <Banner /> -->
|
||||
|
||||
<Header :links="links" />
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
import { kebabCase } from 'scule'
|
||||
import type { ContentNavigationItem } from '@nuxt/content'
|
||||
import type { PageLink } from '@nuxt/ui-pro'
|
||||
import { findPageBreadcrumb, mapContentNavigation } from '@nuxt/ui-pro/utils/content'
|
||||
import { mapContentNavigation } from '@nuxt/ui-pro/utils/content'
|
||||
import { findPageBreadcrumb } from '@nuxt/content/utils'
|
||||
|
||||
const route = useRoute()
|
||||
const { framework, module } = useSharedData()
|
||||
@@ -37,7 +38,7 @@ const { data: surround } = await useAsyncData(`${kebabCase(route.path)}-surround
|
||||
|
||||
const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
|
||||
|
||||
const breadcrumb = computed(() => mapContentNavigation(findPageBreadcrumb(navigation?.value, page.value)).map(({ icon, ...link }) => link))
|
||||
const breadcrumb = computed(() => mapContentNavigation(findPageBreadcrumb(navigation?.value, page.value?.path, { indexAsChild: true })).map(({ icon, ...link }) => link))
|
||||
|
||||
if (!import.meta.prerender) {
|
||||
// Redirect to the correct framework version if the page is not the current framework
|
||||
@@ -65,13 +66,17 @@ if (!import.meta.prerender) {
|
||||
})
|
||||
}
|
||||
|
||||
const type = page.value?.path.includes('components') ? 'Vue Component ' : page.value?.path.includes('composables') ? 'Vue Composable ' : ''
|
||||
const title = page.value?.navigation?.title ? page.value.navigation.title : page.value?.title
|
||||
const prefix = page.value?.path.includes('components') || page.value?.path.includes('composables') ? 'Vue ' : ''
|
||||
const suffix = page.value?.path.includes('components') ? 'Component ' : page.value?.path.includes('composables') ? 'Composable ' : ''
|
||||
const description = page.value?.description
|
||||
|
||||
useSeoMeta({
|
||||
titleTemplate: `%s ${type}- Nuxt UI ${page.value.module === 'ui-pro' ? 'Pro' : ''} ${page.value.framework === 'vue' ? ' for Vue' : ''}`,
|
||||
title: page.value.navigation?.title ? page.value.navigation.title : page.value.title,
|
||||
ogTitle: `${page.value.navigation?.title ? page.value.navigation.title : page.value.title} ${type}- Nuxt UI ${page.value.module === 'ui-pro' ? 'Pro' : ''} ${page.value.framework === 'vue' ? ' for Vue' : ''}`,
|
||||
description: page.value.description,
|
||||
ogDescription: page.value.description
|
||||
titleTemplate: `${prefix}%s ${suffix}- Nuxt UI ${page.value?.module === 'ui-pro' ? 'Pro' : ''} ${page.value?.framework === 'vue' ? ' for Vue' : ''}`,
|
||||
title,
|
||||
ogTitle: `${prefix}${title} ${suffix}- Nuxt UI ${page.value?.module === 'ui-pro' ? 'Pro' : ''} ${page.value?.framework === 'vue' ? ' for Vue' : ''}`,
|
||||
description,
|
||||
ogDescription: description
|
||||
})
|
||||
|
||||
if (route.path.startsWith('/components')) {
|
||||
@@ -137,7 +142,7 @@ const communityLinks = computed(() => [{
|
||||
<MDC v-if="page.description" :value="page.description" unwrap="p" :cache-key="`${kebabCase(route.path)}-description`" />
|
||||
</template>
|
||||
|
||||
<template v-if="page.links?.length" #links>
|
||||
<template #links>
|
||||
<UButton
|
||||
v-for="link in page.links"
|
||||
:key="link.label"
|
||||
@@ -150,6 +155,7 @@ const communityLinks = computed(() => [{
|
||||
<UAvatar v-bind="link.avatar" size="2xs" :alt="`${link.label} avatar`" />
|
||||
</template>
|
||||
</UButton>
|
||||
<PageHeaderLinks />
|
||||
</template>
|
||||
</UPageHeader>
|
||||
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
const route = useRoute()
|
||||
const colorMode = useColorMode()
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const name = route.params.slug?.[0]
|
||||
|
||||
if (route.query.theme) {
|
||||
colorMode.preference = route.query.theme === 'light' ? 'light' : 'dark'
|
||||
}
|
||||
if (route.query.neutral) {
|
||||
appConfig.ui.colors.neutral = route.query.neutral as string
|
||||
}
|
||||
if (route.query.primary) {
|
||||
appConfig.ui.colors.primary = route.query.primary as string
|
||||
}
|
||||
|
||||
const width = computed(() => route.query.width && Number.parseInt(route.query.width as string) > 0 ? `${Number.parseInt(route.query.width as string) - 2}px` : '864px')
|
||||
</script>
|
||||
|
||||
|
||||
@@ -24,32 +24,41 @@ onMounted(async () => {
|
||||
const nuxtWordPosition = document.querySelector('#nuxt')?.getBoundingClientRect()
|
||||
const initialScrollX = window.scrollX
|
||||
const initialScrollY = window.scrollY
|
||||
if (figmaWordPosition && nuxtWordPosition) {
|
||||
animate('#cursor1', { left: Math.round(Math.random() * window.outerWidth), top: Math.round(Math.random() * window.outerHeight) }, { duration: 0.1 })
|
||||
.then(() => animate('#cursor1', { opacity: 1 }, { duration: 0.3 }))
|
||||
.then(() => {
|
||||
return animate('#cursor1', {
|
||||
left: Math.round(figmaWordPosition.left + initialScrollX + figmaWordPosition.width / 2),
|
||||
top: Math.round(figmaWordPosition.top + initialScrollY - figmaWordPosition.height / 4)
|
||||
}, { duration: 1.5, delay: 0.2, ease: 'easeInOut' })
|
||||
})
|
||||
.then(() => animate('#cursor1', { scale: 0.8 }, { duration: 0.1, ease: 'easeOut' }))
|
||||
.then(() => animate('#cursor1', { scale: 1 }, { duration: 0.1, ease: 'easeOut' }))
|
||||
.then(() => animate('#figma', { color: 'var(--ui-info)' }, { duration: 0.3, ease: 'easeOut' }))
|
||||
.then(() => animate('#cursor1', { left: Math.round(figmaWordPosition.left + initialScrollX + figmaWordPosition.width), top: Math.round(figmaWordPosition.top + initialScrollY) }, { duration: 0.6, ease: 'easeInOut' }))
|
||||
|
||||
animate('#cursor2', { left: Math.round(Math.random() * window.outerWidth), top: Math.round(Math.random() * window.outerHeight) }, { duration: 0.1, delay: 0.6 })
|
||||
.then(() => animate('#cursor2', { opacity: 1 }, { duration: 0.3 }))
|
||||
.then(() => {
|
||||
return animate('#cursor2', {
|
||||
left: Math.round(nuxtWordPosition.left + initialScrollX + nuxtWordPosition.width / 2),
|
||||
top: Math.round(nuxtWordPosition.top + initialScrollY - nuxtWordPosition.height / 4)
|
||||
}, { duration: 1.5, delay: 0.2, ease: 'easeInOut' })
|
||||
})
|
||||
.then(() => animate('#cursor2', { scale: 0.8 }, { duration: 0.1, ease: 'easeOut' }))
|
||||
.then(() => animate('#cursor2', { scale: 1 }, { duration: 0.1, ease: 'easeOut' }))
|
||||
.then(() => animate('#nuxt', { color: 'var(--ui-success)' }, { duration: 0.3, ease: 'easeOut' }))
|
||||
.then(() => animate('#cursor2', { left: Math.round(nuxtWordPosition.left + initialScrollX + nuxtWordPosition.width), top: Math.round(nuxtWordPosition.top + initialScrollY) }, { duration: 0.6, ease: 'easeInOut' }))
|
||||
if (figmaWordPosition && nuxtWordPosition) {
|
||||
const cursor1Sequence = async () => {
|
||||
await animate('#cursor1', { left: Math.round(Math.random() * window.outerWidth), top: Math.round(Math.random() * window.outerHeight) }, { duration: 0.1 }).finished
|
||||
await animate('#cursor1', { opacity: 1 }, { duration: 0.3 }).finished
|
||||
await animate('#cursor1', {
|
||||
left: Math.round(figmaWordPosition.left + initialScrollX + figmaWordPosition.width / 2),
|
||||
top: Math.round(figmaWordPosition.top + initialScrollY - figmaWordPosition.height / 4)
|
||||
}, { duration: 1.5, delay: 0.2, ease: 'easeInOut' }).finished
|
||||
await animate('#cursor1', { scale: 0.8 }, { duration: 0.1, ease: 'easeOut' }).finished
|
||||
await animate('#cursor1', { scale: 1 }, { duration: 0.1, ease: 'easeOut' }).finished
|
||||
await animate('#figma', { color: 'var(--ui-info)' }, { duration: 0.3, ease: 'easeOut' }).finished
|
||||
await animate('#cursor1', {
|
||||
left: Math.round(figmaWordPosition.left + initialScrollX + figmaWordPosition.width),
|
||||
top: Math.round(figmaWordPosition.top + initialScrollY)
|
||||
}, { duration: 0.6, ease: 'easeInOut' }).finished
|
||||
}
|
||||
|
||||
const cursor2Sequence = async () => {
|
||||
await animate('#cursor2', { left: Math.round(Math.random() * window.outerWidth), top: Math.round(Math.random() * window.outerHeight) }, { duration: 0.1, delay: 0.6 }).finished
|
||||
await animate('#cursor2', { opacity: 1 }, { duration: 0.3 }).finished
|
||||
await animate('#cursor2', {
|
||||
left: Math.round(nuxtWordPosition.left + initialScrollX + nuxtWordPosition.width / 2),
|
||||
top: Math.round(nuxtWordPosition.top + initialScrollY - nuxtWordPosition.height / 4)
|
||||
}, { duration: 1.5, delay: 0.2, ease: 'easeInOut' }).finished
|
||||
await animate('#cursor2', { scale: 0.8 }, { duration: 0.1, ease: 'easeOut' }).finished
|
||||
await animate('#cursor2', { scale: 1 }, { duration: 0.1, ease: 'easeOut' }).finished
|
||||
await animate('#nuxt', { color: 'var(--ui-success)' }, { duration: 0.3, ease: 'easeOut' }).finished
|
||||
await animate('#cursor2', {
|
||||
left: Math.round(nuxtWordPosition.left + initialScrollX + nuxtWordPosition.width),
|
||||
top: Math.round(nuxtWordPosition.top + initialScrollY)
|
||||
}, { duration: 0.6, ease: 'easeInOut' }).finished
|
||||
}
|
||||
|
||||
await Promise.all([cursor1Sequence(), cursor2Sequence()])
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -239,7 +239,7 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
|
||||
<li>
|
||||
<NuxtLink to="https://github.com/nuxt/ui/graphs/contributors" target="_blank" class="min-w-0">
|
||||
<p class="text-4xl font-semibold text-highlighted truncate">
|
||||
175+
|
||||
200+
|
||||
</p>
|
||||
<p class="text-muted text-sm truncate">Contributors</p>
|
||||
</NuxtLink>
|
||||
|
||||
@@ -16,6 +16,32 @@ links:
|
||||
variant: outline
|
||||
trailingIcon: i-lucide-arrow-right
|
||||
templates:
|
||||
- title: 'Portfolio'
|
||||
description: "A sleek, modern portfolio template to showcase your work, skills, blog posts, speaking engagements, and provide contact information. Which can customized easily from the `content/` directory."
|
||||
icon: i-lucide-user
|
||||
thumbnail:
|
||||
dark: https://assets.hub.nuxt.com/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL3BvcnRmb2xpby10ZW1wbGF0ZS5udXh0LmRldiIsImlhdCI6MTc0NTkzNDczMX0.XDWnQoyVy3XVtKQD6PLQ8RFUwr4yr1QnVwPxRrjCrro.jpg?theme=dark
|
||||
light: https://assets.hub.nuxt.com/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL3BvcnRmb2xpby10ZW1wbGF0ZS5udXh0LmRldiIsImlhdCI6MTc0NTkzNDczMX0.XDWnQoyVy3XVtKQD6PLQ8RFUwr4yr1QnVwPxRrjCrro.jpg?theme=light
|
||||
features:
|
||||
- title: Sections for Projects, Blog, Speaking & About
|
||||
icon: i-lucide-layout-list
|
||||
- title: Easily editable content via Markdown & YAML
|
||||
icon: i-simple-icons-markdown
|
||||
- title: Fully responsive design
|
||||
icon: i-lucide-smartphone
|
||||
links:
|
||||
- label: Preview
|
||||
to: https://portfolio-template.nuxt.dev
|
||||
target: _blank
|
||||
leadingIcon: i-logos-nuxt-icon
|
||||
trailingIcon: i-lucide-arrow-up-right
|
||||
color: neutral
|
||||
- label: Nuxt Template
|
||||
to: https://github.com/nuxt-ui-pro/portfolio
|
||||
target: _blank
|
||||
icon: i-simple-icons-github
|
||||
color: neutral
|
||||
variant: outline
|
||||
- title: 'Chat'
|
||||
description: "An AI chatbot template designed to help you build your own chatbot with Nuxt UI Pro components and deployed on [NuxtHub](https://hub.nuxt.com)."
|
||||
icon: i-lucide-message-circle
|
||||
|
||||
@@ -67,17 +67,6 @@ defineOgImageComponent('Docs', {
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center -mb-[36px]">
|
||||
<UButton
|
||||
label="Submit your project"
|
||||
trailing-icon="i-lucide-plus"
|
||||
color="neutral"
|
||||
size="lg"
|
||||
to="https://github.com/nuxt/ui/edit/v3/docs/content/showcase.yml"
|
||||
target="_blank"
|
||||
/>
|
||||
</div>
|
||||
</UPageHero>
|
||||
</UMain>
|
||||
</template>
|
||||
|
||||
@@ -48,13 +48,14 @@ const icons = {
|
||||
</UPageHero>
|
||||
|
||||
<UPageSection :ui="{ container: '!pt-0' }">
|
||||
<UPageGrid class="xl:grid-cols-4">
|
||||
<UPageGrid class="xl:grid-cols-5">
|
||||
<UPageCard
|
||||
v-for="(user, index) in module?.team"
|
||||
:key="index"
|
||||
:title="user.name"
|
||||
:description="[user.pronouns, user.location].filter(Boolean).join(' ・ ')"
|
||||
:ui="{
|
||||
wrapper: 'items-center',
|
||||
container: 'gap-y-4 lg:p-8',
|
||||
leading: 'flex justify-center',
|
||||
title: 'text-center',
|
||||
@@ -123,6 +124,7 @@ const icons = {
|
||||
:key="contributor.username"
|
||||
:title="contributor.username"
|
||||
:ui="{
|
||||
wrapper: 'items-center',
|
||||
container: 'gap-y-2',
|
||||
leading: 'flex justify-center',
|
||||
title: 'text-center',
|
||||
|
||||
@@ -16,12 +16,12 @@ function handleMessage(message) {
|
||||
async function handleFormatMessage(message) {
|
||||
if (!globalThis.prettier) {
|
||||
await Promise.all([
|
||||
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/standalone.js'),
|
||||
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/babel.js'),
|
||||
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/estree.js'),
|
||||
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/html.js'),
|
||||
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/markdown.js'),
|
||||
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/typescript.js')
|
||||
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/standalone.js'),
|
||||
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/plugins/babel.js'),
|
||||
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/plugins/estree.js'),
|
||||
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/plugins/html.js'),
|
||||
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/plugins/markdown.js'),
|
||||
import('https://cdn.jsdelivr.net/npm/prettier@3.6.2/plugins/typescript.js')
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ You can play with Nuxt UI components as well as your app components directly fro
|
||||
Install the module to your Nuxt application with one command:
|
||||
|
||||
```bash [Terminal]
|
||||
npx nuxi module add compodium
|
||||
npx nuxt module add compodium
|
||||
```
|
||||
::
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ Start your project using the [nuxt/starter#ui](https://github.com/nuxt/starter/t
|
||||
Create a new project locally by running the following command:
|
||||
|
||||
```bash [Terminal]
|
||||
npx nuxi init -t ui <my-app>
|
||||
npm create nuxt@latest -- -t ui
|
||||
```
|
||||
|
||||
::note
|
||||
|
||||
@@ -78,6 +78,22 @@ components.d.ts
|
||||
|
||||
::
|
||||
|
||||
::tip
|
||||
Internally, Nuxt UI relies on custom alias to resolve the theme types. If you're using TypeScript, you should add an alias to your `tsconfig` to enable auto-completion in your `vite.config.ts`.
|
||||
|
||||
```json [tsconfig.node.json]
|
||||
{
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"#build/ui": [
|
||||
"./node_modules/@nuxt/ui/.nuxt/ui"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
::
|
||||
|
||||
#### Use the Nuxt UI Vue plugin in your `main.ts`
|
||||
|
||||
```ts [main.ts]{3,14}
|
||||
@@ -99,6 +115,10 @@ app.use(ui)
|
||||
app.mount('#app')
|
||||
```
|
||||
|
||||
::note{to="#inertia"}
|
||||
If you're using [Inertia.js](https://inertiajs.com/), you can skip the `vue-router` setup as Inertia provides its own routing system.
|
||||
::
|
||||
|
||||
#### Import Tailwind CSS and Nuxt UI in your CSS
|
||||
|
||||
```css [assets/main.css]
|
||||
@@ -175,7 +195,7 @@ Start your project using the [nuxtlabs/nuxt-ui-vue-starter](https://github.com/n
|
||||
Create a new project locally by running the following command:
|
||||
|
||||
```bash [Terminal]
|
||||
npx nuxi init -t github:nuxtlabs/nuxt-ui-vue-starter <my-app>
|
||||
npm create nuxt@latest -- -t github:nuxtlabs/nuxt-ui-vue-starter
|
||||
```
|
||||
|
||||
::note
|
||||
@@ -313,6 +333,29 @@ export default defineConfig({
|
||||
This option adds the `transition-colors` class on components with hover or active states.
|
||||
::
|
||||
|
||||
### `inertia`
|
||||
|
||||
Use the `inertia` option to enable compatibility with [Inertia.js](https://inertiajs.com/).
|
||||
|
||||
```ts [vite.config.ts]
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import ui from '@nuxt/ui/vite'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
ui({
|
||||
inertia: true
|
||||
})
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
::note
|
||||
When using this option, `vue-router` is not required as Inertia.js provides its own routing system. The components that would normally use `RouterLink` will automatically use Inertia's `InertiaLink` component instead.
|
||||
::
|
||||
|
||||
## Continuous Releases
|
||||
|
||||
Nuxt UI uses [pkg.pr.new](https://github.com/stackblitz-labs/pkg.pr.new) for continuous preview releases, providing developers with instant access to the latest features and bug fixes without waiting for official releases.
|
||||
|
||||
@@ -504,7 +504,7 @@ const count = ref(0)
|
||||
</script>
|
||||
```
|
||||
|
||||
Closing a modal is now done through the `close` event. The `modal.open` method now returns a promise that resolves to the result of the modal whenever the modal is close:
|
||||
Closing a modal is now done through the `close` event. The `modal.open` method now returns an instance that can be used to await for the result of the modal whenever the modal is closed:
|
||||
|
||||
```diff
|
||||
<script setup lang="ts">
|
||||
@@ -523,10 +523,12 @@ import { ModalExampleComponent } from '#components'
|
||||
- })
|
||||
- }
|
||||
+ async function openModal() {
|
||||
+ const result = await modal.open(ModalExampleComponent, {
|
||||
+ const instance = modal.open(ModalExampleComponent, {
|
||||
+ count: count.value
|
||||
+ })
|
||||
+
|
||||
+ const result = await instance.result
|
||||
+
|
||||
+ if (result) {
|
||||
+ toast.add({ title: 'Success!' })
|
||||
+ }
|
||||
|
||||
@@ -229,6 +229,10 @@ export default defineConfig({
|
||||
|
||||
::
|
||||
|
||||
::caution
|
||||
When configuring your theme colors, you must use either color names from the [default Tailwind palette](https://tailwindcss.com/docs/colors) (like 'blue', 'green', etc.) or reference custom colors that you've previously defined in your [CSS file](#theme).
|
||||
::
|
||||
|
||||
### Extend colors
|
||||
|
||||
::framework-only
|
||||
@@ -727,15 +731,22 @@ This is how the `@theme` is generated for each design token:
|
||||
--border-color-muted: var(--ui-border-muted);
|
||||
--border-color-accented: var(--ui-border-accented);
|
||||
--border-color-inverted: var(--ui-border-inverted);
|
||||
--border-color-bg: var(--ui-bg);
|
||||
--ring-color-default: var(--ui-border);
|
||||
--ring-color-muted: var(--ui-border-muted);
|
||||
--ring-color-accented: var(--ui-border-accented);
|
||||
--ring-color-inverted: var(--ui-border-inverted);
|
||||
--ring-color-bg: var(--ui-bg);
|
||||
--ring-offset-color-default: var(--ui-border);
|
||||
--ring-offset-color-muted: var(--ui-border-muted);
|
||||
--ring-offset-color-accented: var(--ui-border-accented);
|
||||
--ring-offset-color-inverted: var(--ui-border-inverted);
|
||||
--ring-offset-color-bg: var(--ui-bg);
|
||||
--divide-color-default: var(--ui-border);
|
||||
--divide-color-muted: var(--ui-border-muted);
|
||||
--divide-color-accented: var(--ui-border-accented);
|
||||
--divide-color-inverted: var(--ui-border-inverted);
|
||||
--divide-color-bg: var(--ui-bg);
|
||||
--outline-color-default: var(--ui-border);
|
||||
--outline-color-inverted: var(--ui-border-inverted);
|
||||
--stroke-color-default: var(--ui-border);
|
||||
@@ -966,7 +977,7 @@ export default {
|
||||
|
||||
```vue [src/runtime/components/Card.vue]
|
||||
<template>
|
||||
<div :class="ui.root({ class: [props.class, props.ui?.root] })">
|
||||
<div :class="ui.root({ class: [props.ui?.root, props.class] })">
|
||||
<div :class="ui.header({ class: props.ui?.header })">
|
||||
<slot name="header" />
|
||||
</div>
|
||||
|
||||
@@ -32,6 +32,12 @@ props:
|
||||
You can use any name from the <https://icones.js.org> collection.
|
||||
::
|
||||
|
||||
::warning
|
||||
When using collections with a dash (`-`), you need to separate the icon name from the collection name with a colon (`:`) as `@iconify/vue` does not handle this case like `@nuxt/icon`. For example, instead of `i-simple-icons-github` you need to write `i-simple-icons:github` or `simple-icons:github`.
|
||||
|
||||
Learn more about the [Iconify naming convention](https://iconify.design/docs/icon-components/vue/#icon).
|
||||
::
|
||||
|
||||
### Component Props
|
||||
|
||||
Some components also have an `icon` prop to display an icon, like the [Button](/components/button) for example:
|
||||
|
||||
@@ -60,7 +60,7 @@ import { fr } from '@nuxt/ui-pro/locale'
|
||||
|
||||
### Custom locale
|
||||
|
||||
You also have the option to add your own locale using `defineLocale`:
|
||||
You can create your own locale using the `defineLocale` composable:
|
||||
|
||||
::module-only
|
||||
|
||||
@@ -125,6 +125,65 @@ Look at the `code` parameter, there you need to pass the iso code of the languag
|
||||
|
||||
::
|
||||
|
||||
### Extend locale :badge{label="New" class="align-text-top"}
|
||||
|
||||
You can customize an existing locale by overriding its `messages` or `code` using the `extendLocale` composable:
|
||||
|
||||
::module-only
|
||||
|
||||
#ui
|
||||
:::div
|
||||
|
||||
```vue [app.vue]
|
||||
<script setup lang="ts">
|
||||
import { en } from '@nuxt/ui/locale'
|
||||
|
||||
const locale = extendLocale(en, {
|
||||
code: 'en-GB',
|
||||
messages: {
|
||||
commandPalette: {
|
||||
placeholder: 'Search a component...'
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UApp :locale="locale">
|
||||
<NuxtPage />
|
||||
</UApp>
|
||||
</template>
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
#ui-pro
|
||||
:::div
|
||||
|
||||
```vue [app.vue]
|
||||
<script setup lang="ts">
|
||||
import { en } from '@nuxt/ui-pro/locale'
|
||||
|
||||
const locale = extendLocale(en, {
|
||||
code: 'en-GB',
|
||||
messages: {
|
||||
commandPalette: {
|
||||
placeholder: 'Search a component...'
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UApp :locale="locale">
|
||||
<NuxtPage />
|
||||
</UApp>
|
||||
</template>
|
||||
```
|
||||
|
||||
:::
|
||||
::
|
||||
|
||||
### Dynamic locale
|
||||
|
||||
To dynamically switch between languages, you can use the [Nuxt I18n](https://i18n.nuxtjs.org/) module.
|
||||
|
||||
@@ -60,7 +60,7 @@ import { fr } from '@nuxt/ui-pro/locale'
|
||||
|
||||
### Custom locale
|
||||
|
||||
You also have the option to add your locale using `defineLocale`:
|
||||
You can create your own locale using the `defineLocale` composable:
|
||||
|
||||
::module-only
|
||||
|
||||
@@ -127,6 +127,67 @@ Look at the `code` parameter, there you need to pass the iso code of the languag
|
||||
|
||||
::
|
||||
|
||||
### Extend locale :badge{label="New" class="align-text-top"}
|
||||
|
||||
You can customize an existing locale by overriding its `messages` or `code` using the `extendLocale` composable:
|
||||
|
||||
::module-only
|
||||
|
||||
#ui
|
||||
:::div
|
||||
|
||||
```vue [App.vue]
|
||||
<script setup lang="ts">
|
||||
import { en } from '@nuxt/ui/locale'
|
||||
import { extendLocale } from '@nuxt/ui/composables/defineLocale.js'
|
||||
|
||||
const locale = extendLocale(en, {
|
||||
code: 'en-GB',
|
||||
messages: {
|
||||
commandPalette: {
|
||||
placeholder: 'Search a component...'
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UApp :locale="locale">
|
||||
<RouterView />
|
||||
</UApp>
|
||||
</template>
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
#ui-pro
|
||||
:::div
|
||||
|
||||
```vue [App.vue]
|
||||
<script setup lang="ts">
|
||||
import { en } from '@nuxt/ui-pro/locale'
|
||||
import { extendLocale } from '@nuxt/ui/composables/defineLocale.js'
|
||||
|
||||
const locale = extendLocale(en, {
|
||||
code: 'en-GB',
|
||||
messages: {
|
||||
commandPalette: {
|
||||
placeholder: 'Search a component...'
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UApp :locale="locale">
|
||||
<RouterView />
|
||||
</UApp>
|
||||
</template>
|
||||
```
|
||||
|
||||
:::
|
||||
::
|
||||
|
||||
### Dynamic locale
|
||||
|
||||
To dynamically switch between languages, you can use the [Vue I18n](https://vue-i18n.intlify.dev/) plugin.
|
||||
|
||||
@@ -16,7 +16,7 @@ Here's an overview of the key directories and files in the Nuxt UI project struc
|
||||
|
||||
### Documentation
|
||||
|
||||
The documentation lives in the `docs` folder as a Nuxt app using `@nuxt/content` v3 to generate pages from Markdown files. See the [Content v3 Docs](https://content3.nuxt.dev/docs/getting-started) for details on how it works. Here's a breakdown of its structure:
|
||||
The documentation lives in the `docs` folder as a Nuxt app using `@nuxt/content` v3 to generate pages from Markdown files. See the [Nuxt Content documentation](https://content.nuxt.com/docs/getting-started) for details on how it works. Here's a breakdown of its structure:
|
||||
|
||||
```bash
|
||||
├── app/
|
||||
@@ -225,7 +225,7 @@ pnpm run test:vue # for Vue
|
||||
```
|
||||
|
||||
::tip
|
||||
If you have to update the snapshots, press `u` when running the tests.
|
||||
If you have to update the snapshots, press `u` after the tests have finished running.
|
||||
::
|
||||
|
||||
### Commit Conventions
|
||||
|
||||
@@ -19,6 +19,7 @@ defineShortcuts({
|
||||
</script>
|
||||
```
|
||||
|
||||
- Shortcuts are automatically adjusted for non-macOS platforms, converting `meta` to `ctrl`.
|
||||
- The composable uses VueUse's [`useEventListener`](https://vueuse.org/core/useEventListener/) to handle keydown events.
|
||||
- For a complete list of available shortcut keys, refer to the [`KeyboardEvent.key`](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values) API documentation. Note that the key should be written in lowercase.
|
||||
|
||||
@@ -46,7 +47,7 @@ Shortcuts are defined using the following format:
|
||||
|
||||
#### Modifiers
|
||||
|
||||
- `meta`: Represents `⌘ Command` on macOS and `⊞ Windows` on Windows
|
||||
- `meta`: Represents `⌘ Command` on macOS and `Ctrl` on other platforms
|
||||
- `ctrl`: Represents `Ctrl` on all platforms
|
||||
- `shift`: Used for alphabetic keys when Shift is required
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: useOverlay
|
||||
description: "A composable to programmatically control overlays."
|
||||
description: 'A composable to programmatically control overlays.'
|
||||
---
|
||||
|
||||
## Usage
|
||||
@@ -9,9 +9,11 @@ Use the auto-imported `useOverlay` composable to programmatically control [Modal
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { LazyModalExample } from '#components'
|
||||
|
||||
const overlay = useOverlay()
|
||||
|
||||
const modal = overlay.create(MyModal)
|
||||
const modal = overlay.create(LazyModalExample)
|
||||
|
||||
async function openModal() {
|
||||
modal.open()
|
||||
@@ -22,71 +24,80 @@ async function openModal() {
|
||||
- The `useOverlay` composable is created using `createSharedComposable`, ensuring that the same overlay state is shared across your entire application.
|
||||
|
||||
::note
|
||||
In order to return a value from the overlay, the `overlay.open()` can be awaited. In order for this to work, however, the **overlay component must emit a `close` event**. See example below for details.
|
||||
In order to return a value from the overlay, the `overlay.open().instance.result` can be awaited. In order for this to work, however, the **overlay component must emit a `close` event**. See example below for details.
|
||||
::
|
||||
|
||||
## API
|
||||
|
||||
### `create(component: T, options: OverlayOptions): OverlayInstance`
|
||||
|
||||
Creates an overlay, and returns its instance
|
||||
Create an overlay, and return a factory instance.
|
||||
|
||||
- Parameters:
|
||||
- `component`: The overlay component
|
||||
- `options` The overlay options
|
||||
- `defaultOpen?: boolean` Opens the overlay immediately after being created `default: false`
|
||||
- `component`: The overlay component.
|
||||
- `options`:
|
||||
- `defaultOpen?: boolean` Open the overlay immediately after being created. Defaults to `false`.
|
||||
- `props?: ComponentProps`: An optional object of props to pass to the rendered component.
|
||||
- `destroyOnClose?: boolean` Removes the overlay from memory when closed `default: false`
|
||||
- `destroyOnClose?: boolean` Removes the overlay from memory when closed. Defaults to `false`.
|
||||
|
||||
### `open(id: symbol, props?: ComponentProps<T>): Promise<any>`
|
||||
### `open(id: symbol, props?: ComponentProps<T>): OpenedOverlay<T>`
|
||||
|
||||
Opens the overlay using its `id`
|
||||
Open an overlay by its `id`.
|
||||
|
||||
- Parameters:
|
||||
- `id`: The identifier of the overlay
|
||||
- `id`: The identifier of the overlay.
|
||||
- `props`: An optional object of props to pass to the rendered component.
|
||||
|
||||
### `close(id: symbol, value?: any): void`
|
||||
|
||||
Close an overlay using its `id`
|
||||
Close an overlay by its `id`.
|
||||
|
||||
- Parameters:
|
||||
- `id`: The identifier of the overlay
|
||||
- `value`: A value to resolve the overlay promise with
|
||||
- `id`: The identifier of the overlay.
|
||||
- `value`: A value to resolve the overlay promise with.
|
||||
|
||||
### `patch(id: symbol, props: ComponentProps<T>): void`
|
||||
|
||||
Update an overlay using its `id`
|
||||
Update an overlay by its `id`.
|
||||
|
||||
- Parameters:
|
||||
- `id`: The identifier of the overlay
|
||||
- `id`: The identifier of the overlay.
|
||||
- `props`: An object of props to update on the rendered component.
|
||||
|
||||
### `unmount(id: symbol): void`
|
||||
|
||||
Removes the overlay from the DOM using its `id`
|
||||
Remove an overlay from the DOM by its `id`.
|
||||
|
||||
- Parameters:
|
||||
- `id`: The identifier of the overlay
|
||||
- `id`: The identifier of the overlay.
|
||||
|
||||
### `isOpen(id: symbol): boolean`
|
||||
|
||||
Check if an overlay is open using its `id`.
|
||||
|
||||
- Parameters:
|
||||
- `id`: The identifier of the overlay.
|
||||
|
||||
### `overlays: Overlay[]`
|
||||
|
||||
In-memory list of overlays that were created
|
||||
In-memory list of all overlays that were created.
|
||||
|
||||
## Overlay Instance API
|
||||
## Instance API
|
||||
|
||||
### `open(props?: ComponentProps<T>): Promise<any>`
|
||||
### `open(props?: ComponentProps<T>): Promise<OpenedOverlay<T>>`
|
||||
|
||||
Opens the overlay
|
||||
Open the overlay.
|
||||
|
||||
- Parameters:
|
||||
- `props`: An optional object of props to pass to the rendered component.
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { LazyModalExample } from '#components'
|
||||
|
||||
const overlay = useOverlay()
|
||||
|
||||
const modal = overlay.create(MyModalContent)
|
||||
const modal = overlay.create(LazyModalExample)
|
||||
|
||||
function openModal() {
|
||||
modal.open({
|
||||
@@ -98,23 +109,25 @@ function openModal() {
|
||||
|
||||
### `close(value?: any): void`
|
||||
|
||||
Close the overlay
|
||||
Close the overlay.
|
||||
|
||||
- Parameters:
|
||||
- `value`: A value to resolve the overlay promise with
|
||||
- `value`: A value to resolve the overlay promise with.
|
||||
|
||||
### `patch(props: ComponentProps<T>)`
|
||||
|
||||
Updates the props of the overlay.
|
||||
Update the props of the overlay.
|
||||
|
||||
- Parameters:
|
||||
- `props`: An object of props to update on the rendered component.
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { LazyModalExample } from '#components'
|
||||
|
||||
const overlay = useOverlay()
|
||||
|
||||
const modal = overlay.create(MyModal, {
|
||||
const modal = overlay.create(LazyModalExample, {
|
||||
title: 'Welcome'
|
||||
})
|
||||
|
||||
@@ -134,33 +147,58 @@ Here's a complete example of how to use the `useOverlay` composable:
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { ModalA, ModalB, SlideoverA } from '#components'
|
||||
|
||||
const overlay = useOverlay()
|
||||
|
||||
// Create with default props
|
||||
const modalA = overlay.create(ModalA, { title: 'Welcome' })
|
||||
const modalB = overlay.create(modalB)
|
||||
const modalB = overlay.create(ModalB)
|
||||
|
||||
const slideoverA = overlay.create(SlideoverA)
|
||||
|
||||
const openModalA = () => {
|
||||
// Open Modal A, but override the title prop
|
||||
// Open modalA, but override the title prop
|
||||
modalA.open({ title: 'Hello' })
|
||||
}
|
||||
|
||||
const openModalB = async () => {
|
||||
// Open modalB, and wait for its result
|
||||
const input = await modalB.open()
|
||||
const modalBInstance = modalB.open()
|
||||
|
||||
// Pass the result from modalB to the slideover, and open it.
|
||||
const input = await modalBInstance.result
|
||||
|
||||
// Pass the result from modalB to the slideover, and open it
|
||||
slideoverA.open({ input })
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<button @click="openModal">Open Modal</button>
|
||||
</div>
|
||||
<button @click="openModalA">Open Modal</button>
|
||||
</template>
|
||||
```
|
||||
|
||||
In this example, we're using the `useOverlay` composable to control multiple modals and slideovers.
|
||||
|
||||
## Caveats
|
||||
|
||||
### Provide / Inject
|
||||
|
||||
When opening overlays programmatically (e.g. modals, slideovers, etc), the overlay component can only access injected values from the component containing `UApp` (typically `app.vue` or layout components). This is because overlays are mounted outside of the page context by the `UApp` component.
|
||||
|
||||
As such, using `provide()` in pages or parent components isn't supported directly. To pass provided values to overlays, the recommended approach is to use props instead:
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { LazyModalExample } from '#components'
|
||||
|
||||
const providedValue = inject('valueProvidedInPage')
|
||||
|
||||
const modal = overlay.create(LazyModalExample, {
|
||||
props: {
|
||||
providedValue,
|
||||
otherData: someValue
|
||||
}
|
||||
})
|
||||
</script>
|
||||
```
|
||||
@@ -23,6 +23,8 @@ Use the `items` prop as an array of objects with the following properties:
|
||||
- `value?: string`{lang="ts-type"}
|
||||
- `disabled?: boolean`{lang="ts-type"}
|
||||
- [`slot?: string`{lang="ts-type"}](#with-custom-slot)
|
||||
- `class?: any`{lang="ts-type"}
|
||||
- `ui?: { item?: ClassNameValue, header?: ClassNameValue, trigger?: ClassNameValue, leadingIcon?: ClassNameValue, label?: ClassNameValue, trailingIcon?: ClassNameValue, content?: ClassNameValue, body?: ClassNameValue }`{lang="ts-type"}
|
||||
|
||||
::component-code
|
||||
---
|
||||
@@ -247,6 +249,16 @@ You can also pass the `value` of one of the items if provided.
|
||||
When `type="multiple"`, ensure to pass an array to the `default-value` prop or the `v-model` directive.
|
||||
::
|
||||
|
||||
### With drag and drop
|
||||
|
||||
Use the [`useSortable`](https://vueuse.org/integrations/useSortable/) composable from [`@vueuse/integrations`](https://vueuse.org/integrations/README.html) to enable drag and drop functionality on the Accordion. This integration wraps [Sortable.js](https://sortablejs.github.io/Sortable/) to provide a seamless drag and drop experience.
|
||||
|
||||
::component-example
|
||||
---
|
||||
name: 'accordion-drag-and-drop-example'
|
||||
---
|
||||
::
|
||||
|
||||
### With body slot
|
||||
|
||||
Use the `#body` slot to customize the body of each item.
|
||||
@@ -292,18 +304,6 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
### With drag and drop
|
||||
|
||||
Use the [`useSortable`](https://vueuse.org/integrations/useSortable/) composable from [`@vueuse/integrations`](https://vueuse.org/integrations/README.html) to enable drag and drop functionality on the accordion. This integration wraps [Sortable.js](https://sortablejs.github.io/Sortable/) to provide a seamless drag and drop experience.
|
||||
|
||||
The `useSortable` composable accepts various options, see the [Usage](https://vueuse.org/integrations/useSortable/#usage) for more examples.
|
||||
|
||||
::component-example
|
||||
---
|
||||
name: 'accordion-drag-and-drop-example'
|
||||
---
|
||||
::
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
@@ -16,8 +16,9 @@ Use the `items` prop as an array of objects with the following properties:
|
||||
- `label?: string`{lang="ts-type"}
|
||||
- `icon?: string`{lang="ts-type"}
|
||||
- `avatar?: AvatarProps`{lang="ts-type"}
|
||||
- `class?: any`{lang="ts-type"}
|
||||
- [`slot?: string`{lang="ts-type"}](#with-custom-slot)
|
||||
- `class?: any`{lang="ts-type"}
|
||||
- `ui?: { item?: ClassNameValue, link?: ClassNameValue, linkLeadingIcon?: ClassNameValue, linkLeadingAvatar?: ClassNameValue, linkLabel?: ClassNameValue, separator?: ClassNameValue, separatorIcon?: ClassNameValue }`{lang="ts-type"}
|
||||
|
||||
You can pass any property from the [Link](/components/link#props) component such as `to`, `target`, etc.
|
||||
|
||||
|
||||
@@ -258,13 +258,13 @@ This also works with the [Form](/components/form) component.
|
||||
|
||||
### Loading Icon
|
||||
|
||||
Use the `loading-icon` prop to customize the loading icon. Defaults to `i-lucide-refresh-cw`.
|
||||
Use the `loading-icon` prop to customize the loading icon. Defaults to `i-lucide-loader-circle`.
|
||||
|
||||
::component-code
|
||||
---
|
||||
props:
|
||||
loading: true
|
||||
loadingIcon: 'i-lucide-repeat-2'
|
||||
loadingIcon: 'i-lucide-loader'
|
||||
slots:
|
||||
default: Button
|
||||
---
|
||||
|
||||
@@ -223,6 +223,16 @@ name: 'calendar-other-system-example'
|
||||
You can check all the available calendars on `@internationalized/date` docs.
|
||||
::
|
||||
|
||||
### With external controls
|
||||
|
||||
You can control the calendar with external controls by manipulating the date passed in the `v-model`.
|
||||
|
||||
::component-example
|
||||
---
|
||||
name: 'calendar-external-controls-example'
|
||||
---
|
||||
::
|
||||
|
||||
### As a DatePicker
|
||||
|
||||
Use a [Button](/components/button) and a [Popover](/components/popover) component to create a date picker.
|
||||
|
||||
@@ -27,6 +27,11 @@ class: 'p-8'
|
||||
---
|
||||
::
|
||||
|
||||
You can also pass an array of objects with the following properties:
|
||||
|
||||
- `class?: any`{lang="ts-type"}
|
||||
- `ui?: { item?: ClassNameValue }`{lang="ts-type"}
|
||||
|
||||
You can control how many items are visible by using the [`basis`](https://tailwindcss.com/docs/flex-basis) / [`width`](https://tailwindcss.com/docs/width) utility classes on the `item`:
|
||||
|
||||
::component-example
|
||||
@@ -229,6 +234,19 @@ class: 'p-8 px-16'
|
||||
---
|
||||
::
|
||||
|
||||
## Examples
|
||||
|
||||
### With thumbnails
|
||||
|
||||
You can use the [`emblaApi`](#expose) function [scrollTo](https://www.embla-carousel.com/api/methods/#scrollto) to display thumbnails under the carousel that allows you to navigate to a specific slide.
|
||||
|
||||
::component-example
|
||||
---
|
||||
name: 'carousel-thumbnails-example'
|
||||
class: 'p-8 px-16'
|
||||
---
|
||||
::
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
@@ -239,6 +257,10 @@ class: 'p-8 px-16'
|
||||
|
||||
:component-slots
|
||||
|
||||
### Emits
|
||||
|
||||
:component-emits
|
||||
|
||||
### Expose
|
||||
|
||||
You can access the typed component instance using [`useTemplateRef`](https://vuejs.org/api/composition-api-helpers.html#usetemplateref).
|
||||
|
||||
354
docs/content/3.components/checkbox-group.md
Normal file
354
docs/content/3.components/checkbox-group.md
Normal file
@@ -0,0 +1,354 @@
|
||||
---
|
||||
title: CheckboxGroup
|
||||
description: A set of checklist buttons to select multiple option from a list.
|
||||
category: form
|
||||
links:
|
||||
- label: CheckboxGroup
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/checkbox#group-root
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/CheckboxGroup.vue
|
||||
---
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
Use the `v-model` directive to control the value of the CheckboxGroup or the `default-value` prop to set the initial value when you do not need to control its state.
|
||||
|
||||
### Items
|
||||
|
||||
Use the `items` prop as an array of strings or numbers:
|
||||
|
||||
::component-code
|
||||
---
|
||||
prettier: true
|
||||
ignore:
|
||||
- modelValue
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
- modelValue
|
||||
externalTypes:
|
||||
- CheckboxGroupItem[]
|
||||
- CheckboxGroupValue[]
|
||||
props:
|
||||
modelValue:
|
||||
- 'System'
|
||||
items:
|
||||
- 'System'
|
||||
- 'Light'
|
||||
- 'Dark'
|
||||
---
|
||||
::
|
||||
|
||||
You can also pass an array of objects with the following properties:
|
||||
|
||||
- `label?: string`{lang="ts-type"}
|
||||
- `description?: string`{lang="ts-type"}
|
||||
- [`value?: string`{lang="ts-type"}](#value-key)
|
||||
- `disabled?: boolean`{lang="ts-type"}
|
||||
- `class?: any`{lang="ts-type"}
|
||||
- `ui?: { item?: ClassNameValue, container?: ClassNameValue, base?: ClassNameValue, 'indicator'?: ClassNameValue, icon?: ClassNameValue, wrapper?: ClassNameValue, label?: ClassNameValue, description?: ClassNameValue }`{lang="ts-type"}
|
||||
|
||||
::component-code
|
||||
---
|
||||
ignore:
|
||||
- modelValue
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
- modelValue
|
||||
externalTypes:
|
||||
- CheckboxGroupItem[]
|
||||
- CheckboxGroupValue[]
|
||||
props:
|
||||
modelValue:
|
||||
- 'system'
|
||||
items:
|
||||
- label: 'System'
|
||||
description: 'This is the first option.'
|
||||
value: 'system'
|
||||
- label: 'Light'
|
||||
description: 'This is the second option.'
|
||||
value: 'light'
|
||||
- label: 'Dark'
|
||||
description: 'This is the third option.'
|
||||
value: 'dark'
|
||||
---
|
||||
::
|
||||
|
||||
::caution
|
||||
When using objects, you need to reference the `value` property of the object in the `v-model` directive or the `default-value` prop.
|
||||
::
|
||||
|
||||
### Value Key
|
||||
|
||||
You can change the property that is used to set the value by using the `value-key` prop. Defaults to `value`.
|
||||
|
||||
::component-code
|
||||
---
|
||||
ignore:
|
||||
- modelValue
|
||||
- items
|
||||
- valueKey
|
||||
external:
|
||||
- items
|
||||
- modelValue
|
||||
externalTypes:
|
||||
- CheckboxGroupItem[]
|
||||
- CheckboxGroupValue[]
|
||||
props:
|
||||
modelValue:
|
||||
- 'light'
|
||||
valueKey: 'id'
|
||||
items:
|
||||
- label: 'System'
|
||||
description: 'This is the first option.'
|
||||
id: 'system'
|
||||
- label: 'Light'
|
||||
description: 'This is the second option.'
|
||||
id: 'light'
|
||||
- label: 'Dark'
|
||||
description: 'This is the third option.'
|
||||
id: 'dark'
|
||||
---
|
||||
::
|
||||
|
||||
### Legend
|
||||
|
||||
Use the `legend` prop to set the legend of the CheckboxGroup.
|
||||
|
||||
::component-code
|
||||
---
|
||||
prettier: true
|
||||
ignore:
|
||||
- defaultValue
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- CheckboxGroupItem[]
|
||||
props:
|
||||
legend: 'Theme'
|
||||
defaultValue:
|
||||
- 'System'
|
||||
items:
|
||||
- 'System'
|
||||
- 'Light'
|
||||
- 'Dark'
|
||||
---
|
||||
::
|
||||
|
||||
### Color
|
||||
|
||||
Use the `color` prop to change the color of the CheckboxGroup.
|
||||
|
||||
::component-code
|
||||
---
|
||||
prettier: true
|
||||
ignore:
|
||||
- defaultValue
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- CheckboxGroupItem[]
|
||||
items:
|
||||
color:
|
||||
- primary
|
||||
- secondary
|
||||
- success
|
||||
- info
|
||||
- warning
|
||||
- error
|
||||
- neutral
|
||||
props:
|
||||
color: neutral
|
||||
defaultValue:
|
||||
- 'System'
|
||||
items:
|
||||
- 'System'
|
||||
- 'Light'
|
||||
- 'Dark'
|
||||
---
|
||||
::
|
||||
|
||||
### Variant
|
||||
|
||||
Use the `variant` prop to change the variant of the CheckboxGroup.
|
||||
|
||||
::component-code
|
||||
---
|
||||
prettier: true
|
||||
ignore:
|
||||
- defaultValue
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- CheckboxGroupItem[]
|
||||
items:
|
||||
color:
|
||||
- primary
|
||||
- secondary
|
||||
- success
|
||||
- info
|
||||
- warning
|
||||
- error
|
||||
- neutral
|
||||
variant:
|
||||
- list
|
||||
- card
|
||||
- table
|
||||
props:
|
||||
color: 'primary'
|
||||
variant: 'card'
|
||||
defaultValue:
|
||||
- 'System'
|
||||
items:
|
||||
- 'System'
|
||||
- 'Light'
|
||||
- 'Dark'
|
||||
---
|
||||
::
|
||||
|
||||
### Size
|
||||
|
||||
Use the `size` prop to change the size of the CheckboxGroup.
|
||||
|
||||
::component-code
|
||||
---
|
||||
prettier: true
|
||||
ignore:
|
||||
- defaultValue
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- CheckboxGroupItem[]
|
||||
items:
|
||||
variant:
|
||||
- list
|
||||
- card
|
||||
- table
|
||||
props:
|
||||
size: 'xl'
|
||||
variant: 'list'
|
||||
defaultValue:
|
||||
- 'System'
|
||||
items:
|
||||
- 'System'
|
||||
- 'Light'
|
||||
- 'Dark'
|
||||
---
|
||||
::
|
||||
|
||||
### Orientation
|
||||
|
||||
Use the `orientation` prop to change the orientation of the CheckboxGroup. Defaults to `vertical`.
|
||||
|
||||
::component-code
|
||||
---
|
||||
prettier: true
|
||||
ignore:
|
||||
- defaultValue
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- CheckboxGroupItem[]
|
||||
items:
|
||||
variant:
|
||||
- list
|
||||
- card
|
||||
- table
|
||||
props:
|
||||
orientation: 'horizontal'
|
||||
variant: 'list'
|
||||
defaultValue:
|
||||
- 'System'
|
||||
items:
|
||||
- 'System'
|
||||
- 'Light'
|
||||
- 'Dark'
|
||||
---
|
||||
::
|
||||
|
||||
### Indicator
|
||||
|
||||
Use the `indicator` prop to change the position or hide the indicator. Defaults to `start`.
|
||||
|
||||
::component-code
|
||||
---
|
||||
prettier: true
|
||||
ignore:
|
||||
- defaultValue
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- CheckboxGroupItem[]
|
||||
items:
|
||||
indicator:
|
||||
- start
|
||||
- end
|
||||
- hidden
|
||||
variant:
|
||||
- list
|
||||
- card
|
||||
- table
|
||||
props:
|
||||
indicator: 'end'
|
||||
variant: 'card'
|
||||
defaultValue:
|
||||
- 'System'
|
||||
items:
|
||||
- 'System'
|
||||
- 'Light'
|
||||
- 'Dark'
|
||||
---
|
||||
::
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` prop to disable the CheckboxGroup.
|
||||
|
||||
::component-code
|
||||
---
|
||||
prettier: true
|
||||
ignore:
|
||||
- defaultValue
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- CheckboxGroupItem[]
|
||||
props:
|
||||
disabled: true
|
||||
defaultValue:
|
||||
- 'System'
|
||||
items:
|
||||
- 'System'
|
||||
- 'Light'
|
||||
- 'Dark'
|
||||
---
|
||||
::
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
:component-props
|
||||
|
||||
### Slots
|
||||
|
||||
:component-slots
|
||||
|
||||
### Emits
|
||||
|
||||
:component-emits
|
||||
|
||||
## Theme
|
||||
|
||||
:component-theme
|
||||
@@ -156,6 +156,23 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
### Variant
|
||||
|
||||
Use the `variant` prop to change the variant of the Checkbox.
|
||||
|
||||
::component-code
|
||||
---
|
||||
ignore:
|
||||
- label
|
||||
- defaultValue
|
||||
props:
|
||||
color: 'primary'
|
||||
variant: 'card'
|
||||
defaultValue: true
|
||||
label: Check me
|
||||
---
|
||||
::
|
||||
|
||||
### Size
|
||||
|
||||
Use the `size` prop to change the size of the Checkbox.
|
||||
@@ -167,6 +184,24 @@ ignore:
|
||||
- defaultValue
|
||||
props:
|
||||
size: xl
|
||||
variant: list
|
||||
defaultValue: true
|
||||
label: Check me
|
||||
---
|
||||
::
|
||||
|
||||
### Indicator
|
||||
|
||||
Use the `indicator` prop to change the position or hide the indicator. Defaults to `start`.
|
||||
|
||||
::component-code
|
||||
---
|
||||
ignore:
|
||||
- label
|
||||
- defaultValue
|
||||
props:
|
||||
indicator: 'end'
|
||||
variant: 'card'
|
||||
defaultValue: true
|
||||
label: Check me
|
||||
---
|
||||
|
||||
@@ -7,9 +7,9 @@ links:
|
||||
icon: i-custom-fuse-js
|
||||
to: https://fusejs.io/
|
||||
target: _blank
|
||||
- label: Combobox
|
||||
- label: Listbox
|
||||
icon: i-custom-reka-ui
|
||||
to: https://reka-ui.com/docs/components/combobox
|
||||
to: https://reka-ui.com/docs/components/listbox
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/CommandPalette.vue
|
||||
@@ -52,7 +52,11 @@ Each group contains an `items` array of objects that define the commands. Each i
|
||||
- `loading?: boolean`{lang="ts-type"}
|
||||
- `disabled?: boolean`{lang="ts-type"}
|
||||
- [`slot?: string`{lang="ts-type"}](#with-custom-slot)
|
||||
- `placeholder?: string`{lang="ts-type"}
|
||||
- `children?: CommandPaletteItem[]`{lang="ts-type"}
|
||||
- `onSelect?(e?: Event): void`{lang="ts-type"}
|
||||
- `class?: any`{lang="ts-type"}
|
||||
- `ui?: { item?: ClassNameValue, itemLeadingIcon?: ClassNameValue, itemLeadingAvatarSize?: ClassNameValue, itemLeadingAvatar?: ClassNameValue, itemLeadingChipSize?: ClassNameValue, itemLeadingChip?: ClassNameValue, itemLabel?: ClassNameValue, itemLabelPrefix?: ClassNameValue, itemLabelBase?: ClassNameValue, itemLabelSuffix?: ClassNameValue, itemTrailing?: ClassNameValue, itemTrailingKbds?: ClassNameValue, itemTrailingKbdsSize?: ClassNameValue, itemTrailingHighlightedIcon?: ClassNameValue, itemTrailingIcon?: ClassNameValue }`{lang="ts-type"}
|
||||
|
||||
You can pass any property from the [Link](/components/link#props) component such as `to`, `target`, etc.
|
||||
|
||||
@@ -108,6 +112,10 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
::tip{to="#with-children-in-items"}
|
||||
Each item can take a `children` array of objects with the following properties to create submenus:
|
||||
::
|
||||
|
||||
### Multiple
|
||||
|
||||
Use the `multiple` prop to allow multiple selections.
|
||||
@@ -244,6 +252,128 @@ You can customize this icon globally in your `vite.config.ts` under `ui.icons.se
|
||||
:::
|
||||
::
|
||||
|
||||
### Selected Icon
|
||||
|
||||
Use the `selected-icon` prop to customize the selected item [Icon](/components/icon). Defaults to `i-lucide-check`.
|
||||
|
||||
::component-code
|
||||
---
|
||||
collapse: true
|
||||
hide:
|
||||
- autofocus
|
||||
ignore:
|
||||
- groups
|
||||
- modelValue
|
||||
- multiple
|
||||
- class
|
||||
external:
|
||||
- groups
|
||||
- modelValue
|
||||
class: '!p-0'
|
||||
props:
|
||||
multiple: true
|
||||
autofocus: false
|
||||
modelValue:
|
||||
- label: 'Benjamin Canac'
|
||||
suffix: 'benjamincanac'
|
||||
avatar:
|
||||
src: 'https://github.com/benjamincanac.png'
|
||||
selectedIcon: 'i-lucide-circle-check'
|
||||
groups:
|
||||
- id: 'users'
|
||||
label: 'Users'
|
||||
items:
|
||||
- label: 'Benjamin Canac'
|
||||
suffix: 'benjamincanac'
|
||||
avatar:
|
||||
src: 'https://github.com/benjamincanac.png'
|
||||
- label: 'Sylvain Marroufin'
|
||||
suffix: 'smarroufin'
|
||||
avatar:
|
||||
src: 'https://github.com/smarroufin.png'
|
||||
- label: 'Sébastien Chopin'
|
||||
suffix: 'atinux'
|
||||
avatar:
|
||||
src: 'https://github.com/atinux.png'
|
||||
- label: 'Romain Hamel'
|
||||
suffix: 'romhml'
|
||||
avatar:
|
||||
src: 'https://github.com/romhml.png'
|
||||
- label: 'Haytham A. Salama'
|
||||
suffix: 'Haythamasalama'
|
||||
avatar:
|
||||
src: 'https://github.com/Haythamasalama.png'
|
||||
- label: 'Daniel Roe'
|
||||
suffix: 'danielroe'
|
||||
avatar:
|
||||
src: 'https://github.com/danielroe.png'
|
||||
- label: 'Neil Richter'
|
||||
suffix: 'noook'
|
||||
avatar:
|
||||
src: 'https://github.com/noook.png'
|
||||
class: 'flex-1'
|
||||
---
|
||||
::
|
||||
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.check` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.check` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Trailing Icon :badge{label="New" class="align-text-top"}
|
||||
|
||||
Use the `trailing-icon` prop to customize the trailing [Icon](/components/icon) when an item has children. Defaults to `i-lucide-chevron-right`.
|
||||
|
||||
::component-code
|
||||
---
|
||||
collapse: true
|
||||
prettier: true
|
||||
hide:
|
||||
- autofocus
|
||||
ignore:
|
||||
- groups
|
||||
- class
|
||||
external:
|
||||
- groups
|
||||
class: '!p-0'
|
||||
props:
|
||||
autofocus: false
|
||||
trailingIcon: 'i-lucide-arrow-right'
|
||||
groups:
|
||||
- id: 'actions'
|
||||
items:
|
||||
- label: 'Share'
|
||||
icon: 'i-lucide-share'
|
||||
children:
|
||||
- label: 'Email'
|
||||
icon: 'i-lucide-mail'
|
||||
- label: 'Copy'
|
||||
icon: 'i-lucide-copy'
|
||||
- label: 'Link'
|
||||
icon: 'i-lucide-link'
|
||||
class: 'flex-1'
|
||||
---
|
||||
::
|
||||
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.chevronRight` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.chevronRight` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Loading
|
||||
|
||||
Use the `loading` prop to show a loading icon on the CommandPalette.
|
||||
@@ -277,7 +407,7 @@ props:
|
||||
|
||||
### Loading Icon
|
||||
|
||||
Use the `loading-icon` prop to customize the loading icon. Defaults to `i-lucide-refresh-cw`.
|
||||
Use the `loading-icon` prop to customize the loading icon. Defaults to `i-lucide-loader-circle`.
|
||||
|
||||
::component-code
|
||||
---
|
||||
@@ -293,7 +423,7 @@ class: '!p-0'
|
||||
props:
|
||||
autofocus: false
|
||||
loading: true
|
||||
loadingIcon: 'i-lucide-repeat-2'
|
||||
loadingIcon: 'i-lucide-loader'
|
||||
groups:
|
||||
- id: 'apps'
|
||||
items:
|
||||
@@ -319,37 +449,6 @@ You can customize this icon globally in your `vite.config.ts` under `ui.icons.lo
|
||||
:::
|
||||
::
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` prop to disable the CommandPalette.
|
||||
|
||||
::component-code
|
||||
---
|
||||
collapse: true
|
||||
hide:
|
||||
- autofocus
|
||||
ignore:
|
||||
- groups
|
||||
- class
|
||||
external:
|
||||
- groups
|
||||
class: '!p-0'
|
||||
props:
|
||||
autofocus: false
|
||||
disabled: true
|
||||
groups:
|
||||
- id: 'apps'
|
||||
items:
|
||||
- label: 'Calendar'
|
||||
icon: 'i-lucide-calendar'
|
||||
- label: 'Music'
|
||||
icon: 'i-lucide-music'
|
||||
- label: 'Maps'
|
||||
icon: 'i-lucide-map'
|
||||
class: 'flex-1'
|
||||
---
|
||||
::
|
||||
|
||||
### Close
|
||||
|
||||
Use the `close` prop to display a [Button](/components/button) to dismiss the CommandPalette.
|
||||
@@ -466,6 +565,124 @@ You can customize this icon globally in your `vite.config.ts` under `ui.icons.cl
|
||||
:::
|
||||
::
|
||||
|
||||
### Back :badge{label="New" class="align-text-top"}
|
||||
|
||||
Use the `back` prop to customize or hide the back button (with `false` value) displayed when navigating into a submenu.
|
||||
|
||||
You can pass any property from the [Button](/components/button) component to customize it.
|
||||
|
||||
::component-code
|
||||
---
|
||||
collapse: true
|
||||
prettier: true
|
||||
hide:
|
||||
- autofocus
|
||||
ignore:
|
||||
- back.color
|
||||
- groups
|
||||
- class
|
||||
external:
|
||||
- groups
|
||||
class: '!p-0'
|
||||
props:
|
||||
autofocus: false
|
||||
back:
|
||||
color: primary
|
||||
groups:
|
||||
- id: 'actions'
|
||||
items:
|
||||
- label: 'Share'
|
||||
icon: 'i-lucide-share'
|
||||
children:
|
||||
- label: 'Email'
|
||||
icon: 'i-lucide-mail'
|
||||
- label: 'Copy'
|
||||
icon: 'i-lucide-copy'
|
||||
- label: 'Link'
|
||||
icon: 'i-lucide-link'
|
||||
class: 'flex-1'
|
||||
---
|
||||
::
|
||||
|
||||
### Back Icon :badge{label="New" class="align-text-top"}
|
||||
|
||||
Use the `back-icon` prop to customize the back button [Icon](/components/icon). Defaults to `i-lucide-arrow-left`.
|
||||
|
||||
::component-code
|
||||
---
|
||||
collapse: true
|
||||
hide:
|
||||
- autofocus
|
||||
ignore:
|
||||
- class
|
||||
- groups
|
||||
- back
|
||||
external:
|
||||
- groups
|
||||
class: '!p-0'
|
||||
props:
|
||||
autofocus: false
|
||||
back: true
|
||||
backIcon: 'i-lucide-house'
|
||||
groups:
|
||||
- id: 'actions'
|
||||
items:
|
||||
- label: 'Share'
|
||||
icon: 'i-lucide-share'
|
||||
children:
|
||||
- label: 'Email'
|
||||
icon: 'i-lucide-mail'
|
||||
- label: 'Copy'
|
||||
icon: 'i-lucide-copy'
|
||||
- label: 'Link'
|
||||
icon: 'i-lucide-link'
|
||||
class: 'flex-1'
|
||||
---
|
||||
::
|
||||
|
||||
::framework-only
|
||||
#nuxt
|
||||
:::tip{to="/getting-started/icons/nuxt#theme"}
|
||||
You can customize this icon globally in your `app.config.ts` under `ui.icons.arrowLeft` key.
|
||||
:::
|
||||
|
||||
#vue
|
||||
:::tip{to="/getting-started/icons/vue#theme"}
|
||||
You can customize this icon globally in your `vite.config.ts` under `ui.icons.arrowLeft` key.
|
||||
:::
|
||||
::
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` prop to disable the CommandPalette.
|
||||
|
||||
::component-code
|
||||
---
|
||||
collapse: true
|
||||
hide:
|
||||
- autofocus
|
||||
ignore:
|
||||
- groups
|
||||
- class
|
||||
external:
|
||||
- groups
|
||||
class: '!p-0'
|
||||
props:
|
||||
autofocus: false
|
||||
disabled: true
|
||||
groups:
|
||||
- id: 'apps'
|
||||
items:
|
||||
- label: 'Calendar'
|
||||
icon: 'i-lucide-calendar'
|
||||
- label: 'Music'
|
||||
icon: 'i-lucide-music'
|
||||
- label: 'Maps'
|
||||
icon: 'i-lucide-map'
|
||||
class: 'flex-1'
|
||||
---
|
||||
::
|
||||
|
||||
## Examples
|
||||
|
||||
### Control selected item(s)
|
||||
@@ -500,6 +717,28 @@ props:
|
||||
This example uses the `@update:model-value` event to reset the search term when an item is selected.
|
||||
::
|
||||
|
||||
### With children in items :badge{label="New" class="align-text-top"}
|
||||
|
||||
You can create hierarchical menus by using the `children` property in items. When an item has children, it will automatically display a chevron icon and enable navigation into a submenu.
|
||||
|
||||
::component-example
|
||||
---
|
||||
collapse: true
|
||||
prettier: true
|
||||
name: 'command-palette-items-children-example'
|
||||
class: '!p-0'
|
||||
props:
|
||||
autofocus: false
|
||||
---
|
||||
::
|
||||
|
||||
::note
|
||||
When navigating into a submenu:
|
||||
- The search term is reset
|
||||
- A back button appears in the input
|
||||
- You can go back to the previous group by pressing the :kbd{value="backspace"} key
|
||||
::
|
||||
|
||||
### With fetched items
|
||||
|
||||
You can fetch items from an API and use them in the CommandPalette.
|
||||
@@ -638,6 +877,20 @@ props:
|
||||
This can be useful when using the CommandPalette inside a [`Modal`](/components/modal) for example.
|
||||
::
|
||||
|
||||
### With footer slot :badge{label="Soon" class="align-text-top"}
|
||||
|
||||
Use the `#footer` slot to add custom content at the bottom of the CommandPalette, such as keyboard shortcuts help or additional actions.
|
||||
|
||||
::component-example
|
||||
---
|
||||
collapse: true
|
||||
name: 'command-palette-footer-slot-example'
|
||||
class: '!p-0'
|
||||
props:
|
||||
autofocus: false
|
||||
---
|
||||
::
|
||||
|
||||
### With custom slot
|
||||
|
||||
Use the `slot` property to customize a specific item or group.
|
||||
@@ -656,6 +909,7 @@ You will have access to the following slots:
|
||||
|
||||
::component-example
|
||||
---
|
||||
collapse: true
|
||||
name: 'command-palette-custom-slot-example'
|
||||
class: '!p-0'
|
||||
props:
|
||||
|
||||
@@ -28,11 +28,12 @@ Use the `items` prop as an array of objects with the following properties:
|
||||
- [`color?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral"`{lang="ts-type"}](#with-color-items)
|
||||
- [`checked?: boolean`{lang="ts-type"}](#with-checkbox-items)
|
||||
- `disabled?: boolean`{lang="ts-type"}
|
||||
- `class?: any`{lang="ts-type"}
|
||||
- [`slot?: string`{lang="ts-type"}](#with-custom-slot)
|
||||
- `onSelect?(e: Event): void`{lang="ts-type"}
|
||||
- [`onUpdateChecked?(checked: boolean): void`{lang="ts-type"}](#with-checkbox-items)
|
||||
- `children?: ContextMenuItem[] | ContextMenuItem[][]`{lang="ts-type"}
|
||||
- `class?: any`{lang="ts-type"}
|
||||
- `ui?: { item?: ClassNameValue, label?: ClassNameValue, separator?: ClassNameValue, itemLeadingIcon?: ClassNameValue, itemLeadingAvatarSize?: ClassNameValue, itemLeadingAvatar?: ClassNameValue, itemLabel?: ClassNameValue, itemLabelExternalIcon?: ClassNameValue, itemTrailing?: ClassNameValue, itemTrailingIcon?: ClassNameValue, itemTrailingKbds?: ClassNameValue, itemTrailingKbdsSize?: ClassNameValue }`{lang="ts-type"}
|
||||
|
||||
You can pass any property from the [Link](/components/link#props) component such as `to`, `target`, etc.
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user