mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-15 20:48:12 +01:00
Compare commits
168 Commits
v3.0.0-alp
...
v3.0.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55179ce71c | ||
|
|
e6d91c94d1 | ||
|
|
9026cf2594 | ||
|
|
818ec3893c | ||
|
|
31b701d9df | ||
|
|
bee04adf4c | ||
|
|
057e86cfda | ||
|
|
68ee3f11ca | ||
|
|
69a6e11c52 | ||
|
|
53a4696ed3 | ||
|
|
7c4ffa56ec | ||
|
|
1fc21af918 | ||
|
|
ede20f89c4 | ||
|
|
0955c07edd | ||
|
|
aa8fa5be3a | ||
|
|
cf490c3b77 | ||
|
|
220f659c4c | ||
|
|
f2e8762b48 | ||
|
|
c2e879feac | ||
|
|
3a7c5c2601 | ||
|
|
9368c6a639 | ||
|
|
3cf5535b2f | ||
|
|
cb0081e0a7 | ||
|
|
6e7a400d4e | ||
|
|
fde75087d0 | ||
|
|
7c3ed81309 | ||
|
|
cb44980440 | ||
|
|
6c7c2f02f3 | ||
|
|
a1ebf8da9a | ||
|
|
ed77b69f5d | ||
|
|
dd42e652d1 | ||
|
|
b82af02839 | ||
|
|
e487f2e877 | ||
|
|
7addc2f70d | ||
|
|
0986f5e4b7 | ||
|
|
077a9210db | ||
|
|
9dcf903926 | ||
|
|
0f86b87385 | ||
|
|
c9a2631bc3 | ||
|
|
1518d0024f | ||
|
|
421a193d20 | ||
|
|
dd54abf243 | ||
|
|
c837ca5cc0 | ||
|
|
66a04add91 | ||
|
|
a5153d6c8d | ||
|
|
cc6db6f14b | ||
|
|
1f9abdae61 | ||
|
|
f54f607413 | ||
|
|
08db1c68b3 | ||
|
|
725833c4a9 | ||
|
|
6a6c43378b | ||
|
|
10ab4ce06d | ||
|
|
afd9b8f2b9 | ||
|
|
6aac821e80 | ||
|
|
f4e135d07a | ||
|
|
ef4a3a09c9 | ||
|
|
c738bd9d53 | ||
|
|
aeccf30a92 | ||
|
|
673fef1595 | ||
|
|
0ed13f33b5 | ||
|
|
30c33c7113 | ||
|
|
fd0ecc6d96 | ||
|
|
6863254d89 | ||
|
|
d2e075bb4a | ||
|
|
6f0b7309c9 | ||
|
|
30d9a2653b | ||
|
|
fac52fa933 | ||
|
|
5f77aac368 | ||
|
|
5b62a8c8ca | ||
|
|
04a2f2b7a0 | ||
|
|
ad3dc26e41 | ||
|
|
fd6c1b02ea | ||
|
|
19362a9302 | ||
|
|
4e2b957882 | ||
|
|
83ee05dd0e | ||
|
|
5591854ed4 | ||
|
|
db754740ef | ||
|
|
2c7c41bd04 | ||
|
|
9edb23080d | ||
|
|
fb8e6a6a66 | ||
|
|
183207c2b8 | ||
|
|
237e7a4e0e | ||
|
|
cac41137ae | ||
|
|
eb0756674a | ||
|
|
9cb863bc21 | ||
|
|
9125a2d467 | ||
|
|
0297361319 | ||
|
|
749bf90dd1 | ||
|
|
d283887407 | ||
|
|
d7e46e3637 | ||
|
|
713021f12d | ||
|
|
1aff74e985 | ||
|
|
5b3cda741c | ||
|
|
a61e7656c2 | ||
|
|
85b21381e8 | ||
|
|
44b98bc7c9 | ||
|
|
6c285977bd | ||
|
|
4c39fa8b0f | ||
|
|
e584941df9 | ||
|
|
991e725b1a | ||
|
|
b638521ffa | ||
|
|
2ec978ed0d | ||
|
|
b7f9422091 | ||
|
|
d921b764de | ||
|
|
b83ecc9a6f | ||
|
|
40f1161b04 | ||
|
|
d419f0a9cb | ||
|
|
7bd06ecfd9 | ||
|
|
cac33c87b2 | ||
|
|
5e55e15ddf | ||
|
|
09d453b5cf | ||
|
|
4f5a8ee4f6 | ||
|
|
cfe4e0bd65 | ||
|
|
1932aa2671 | ||
|
|
cc8fa471e9 | ||
|
|
19bfc36976 | ||
|
|
7d754c0015 | ||
|
|
8709717a32 | ||
|
|
32ac575d56 | ||
|
|
c39996c0fe | ||
|
|
235f1cc330 | ||
|
|
f45b39937b | ||
|
|
a70ba04fc3 | ||
|
|
500b4727e8 | ||
|
|
d36576b1fe | ||
|
|
6498f8a0c1 | ||
|
|
96c9246d83 | ||
|
|
e45398f6ce | ||
|
|
a32d2c034a | ||
|
|
f52310d4a1 | ||
|
|
a6ae1dcb1d | ||
|
|
2e954467c4 | ||
|
|
d3317d828e | ||
|
|
2c3032e363 | ||
|
|
dd6bf5694f | ||
|
|
a8b26eb1a8 | ||
|
|
df72003516 | ||
|
|
1f5372baba | ||
|
|
d4efd13307 | ||
|
|
87c0a0f9c1 | ||
|
|
d5339d3ebe | ||
|
|
ff8eefdce7 | ||
|
|
2346f52cbf | ||
|
|
d0a669ee2e | ||
|
|
7e672bd041 | ||
|
|
ed18e74549 | ||
|
|
6f20f243fb | ||
|
|
ce91b5d75a | ||
|
|
d9b14bc325 | ||
|
|
d0ac6f95a4 | ||
|
|
8f76caa7f8 | ||
|
|
336631434c | ||
|
|
319fce136f | ||
|
|
1667e5a655 | ||
|
|
ad77a0e82d | ||
|
|
5076b8cc9e | ||
|
|
2fc6e18179 | ||
|
|
8898a5d675 | ||
|
|
7c2adf2f7f | ||
|
|
523493105e | ||
|
|
26e5ac80b6 | ||
|
|
a83e3821a4 | ||
|
|
ea8a083d84 | ||
|
|
cf92c5f3f0 | ||
|
|
594bc9bb2e | ||
|
|
7e87f05840 | ||
|
|
942f087a67 | ||
|
|
13975da684 |
3
.github/workflows/ci-v3.yml
vendored
3
.github/workflows/ci-v3.yml
vendored
@@ -54,3 +54,6 @@ jobs:
|
||||
|
||||
- name: Build
|
||||
run: pnpm run build
|
||||
|
||||
- name: Publish
|
||||
run: pnpx pkg-pr-new publish --compact --no-template --pnpm
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
"commitMessage": "chore(release): v${version}",
|
||||
"tagName": "v${version}"
|
||||
},
|
||||
"github": {
|
||||
"release": true,
|
||||
"releaseName": "v${version}",
|
||||
"web": true
|
||||
},
|
||||
"hooks": {
|
||||
"before:init": ["pnpm lint", "pnpm typecheck"]
|
||||
},
|
||||
"plugins": {
|
||||
"@release-it/conventional-changelog": {
|
||||
"preset": {
|
||||
|
||||
276
CHANGELOG.md
276
CHANGELOG.md
@@ -1,5 +1,92 @@
|
||||
# Changelog
|
||||
|
||||
## [3.0.0-alpha.6](https://github.com/nuxt/ui/compare/v3.0.0-alpha.5...v3.0.0-alpha.6) (2024-10-09)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **module:** implement design system with CSS variables (#2298)
|
||||
|
||||
### Features
|
||||
|
||||
* **Carousel:** implement component ([#2288](https://github.com/nuxt/ui/issues/2288)) ([68ee3f1](https://github.com/nuxt/ui/commit/68ee3f11ca01b19cf890ef8105ffb87ef9bb3188))
|
||||
* **Form:** add Standard Schema support ([#2303](https://github.com/nuxt/ui/issues/2303)) ([0955c07](https://github.com/nuxt/ui/commit/0955c07edd8ea5b5c39b770804b8e4c6f86d94b0))
|
||||
* **module:** implement `--ui-radius` CSS variable ([#2341](https://github.com/nuxt/ui/issues/2341)) ([057e86c](https://github.com/nuxt/ui/commit/057e86cfda1ef5c7a370c99ef409d22e48772ca7))
|
||||
* **module:** set `disableTransition` option on `@nuxtjs/color-mode` ([b82af02](https://github.com/nuxt/ui/commit/b82af02839b7d75344d9431fabdc42f0ac0681e1))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Accordion:** use `text-left break-words` instead of `truncate` on label ([6c7c2f0](https://github.com/nuxt/ui/commit/6c7c2f02f395747a0c68a499630f502e3f02ded3))
|
||||
* **Alert:** default variant to `solid` for consistency ([3a7c5c2](https://github.com/nuxt/ui/commit/3a7c5c26011bfcffcdf6ac3451adb2af1453b9db))
|
||||
* **Button:** center text with `block` prop ([3cf5535](https://github.com/nuxt/ui/commit/3cf5535b2faa28b557ca55d694abdfa7d7ad0efc)), closes [nuxt/ui#2317](https://github.com/nuxt/ui/issues/2317)
|
||||
* **Carousel:** move embla plugins to `dependencies` ([bee04ad](https://github.com/nuxt/ui/commit/bee04adf4cc4fd6d69e93ad94500f5ef604405e7))
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **module:** implement design system with CSS variables ([#2298](https://github.com/nuxt/ui/issues/2298)) ([9368c6a](https://github.com/nuxt/ui/commit/9368c6a63955a2e6c2f4f900a9b91c61bb2e5a72))
|
||||
|
||||
## [3.0.0-alpha.5](https://github.com/nuxt/ui/compare/v3.0.0-alpha.4...v3.0.0-alpha.5) (2024-10-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **module:** enable `@nuxtjs/color-mode` ([9dcf903](https://github.com/nuxt/ui/commit/9dcf903926046b6e92b4784043e374d2174e4201))
|
||||
* **module:** override `dark` variant with class strategy ([0f86b87](https://github.com/nuxt/ui/commit/0f86b87385375e5bd859e84d21f8b4f06b0a99e0))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Button:** props specified more than once ([66a04ad](https://github.com/nuxt/ui/commit/66a04add91389910e1336bf0be1cfeada3540f76))
|
||||
|
||||
## [3.0.0-alpha.4](https://github.com/nuxt/ui/compare/v3.0.0-alpha.3...v3.0.0-alpha.4) (2024-10-01)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Drawer:** handle `direction` + `handle` props ([5f77aac](https://github.com/nuxt/ui/commit/5f77aac368448c7c45a0f9238d2dc3a5b0de825e))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Accordion:** missing `min-w-0` on trigger ([6c28597](https://github.com/nuxt/ui/commit/6c285977bd175d4866ca601bca47132ebb2d3440))
|
||||
* **build.config:** disable mkdist `addRelativeDeclarationExtensions` option ([f54f607](https://github.com/nuxt/ui/commit/f54f6074131db0f68eab1edcde3a4b2a7ecaba92))
|
||||
* **CommandPalette:** missing `min-w-0` on root ([a61e765](https://github.com/nuxt/ui/commit/a61e7656c25b26409cab77178e67d1cb9ec22dbd))
|
||||
* **Drawer:** improve max-width on mobile ([fac52fa](https://github.com/nuxt/ui/commit/fac52fa933aeb02f0855d20be37c4214efba0ab7))
|
||||
* **InputMenu:** missing `group` on trailing ([2c7c41b](https://github.com/nuxt/ui/commit/2c7c41bd046a961d398bbe8ee4a5945cd1fbaeab))
|
||||
* **README:** npm badge link ([#2271](https://github.com/nuxt/ui/issues/2271)) ([30c33c7](https://github.com/nuxt/ui/commit/30c33c71134ccbea4258949a851eaf8b26213b60))
|
||||
* **templates:** app config colors type ([96c9246](https://github.com/nuxt/ui/commit/96c9246d83b54637ceb2e2dd77542e435690c387))
|
||||
* **Toast:** improve focus styles ([1f9abda](https://github.com/nuxt/ui/commit/1f9abdae614acbfa0be868a599071a601406f0f5))
|
||||
|
||||
|
||||
### Reverts
|
||||
|
||||
* Revert "chore(deps): refresh lock" ([b83ecc9](https://github.com/nuxt/ui/commit/b83ecc9a6f309d37d3f096667143a4ed7700db6d))
|
||||
|
||||
## [3.0.0-alpha.3](https://github.com/nuxt/ui/compare/v3.0.0-alpha.2...v3.0.0-alpha.3) (2024-09-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **module:** move `colors` options into `theme.colors` ([2e95446](https://github.com/nuxt/ui/commit/2e954467c4679d70b68d3155ae34eca300508e38))
|
||||
|
||||
## [3.0.0-alpha.2](https://github.com/nuxt/ui/compare/v3.0.0-alpha.1...v3.0.0-alpha.2) (2024-09-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Button:** loading-auto ([#2198](https://github.com/nuxt/ui/issues/2198)) ([ed18e74](https://github.com/nuxt/ui/commit/ed18e7454986ed104fc73b77e88573b3c1df8566))
|
||||
* **module:** improve options ([5076b8c](https://github.com/nuxt/ui/commit/5076b8cc9e908cf289150c668b1707dc1397dba3))
|
||||
* **module:** install `@nuxt/fonts` by default ([8898a5d](https://github.com/nuxt/ui/commit/8898a5d6758b1047e35bcdf648362c42de387488))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Button:** button link not showing disabled classes ([#2189](https://github.com/nuxt/ui/issues/2189)) ([7c2adf2](https://github.com/nuxt/ui/commit/7c2adf2f7fc88174897cc775c752414a8b84f3a9))
|
||||
* **Button:** duplicate click handlers ([#2213](https://github.com/nuxt/ui/issues/2213)) ([dd6bf56](https://github.com/nuxt/ui/commit/dd6bf5694ff05ed1eeb9df8c42f833f51dbec66e))
|
||||
* **playground:** typecheck ([cf92c5f](https://github.com/nuxt/ui/commit/cf92c5f3f0e0f329844ee60772773a844ea1cc71))
|
||||
|
||||
## [3.0.0-alpha.1](https://github.com/nuxt/ui/compare/v3.0.0-alpha.0...v3.0.0-alpha.1) (2024-09-11)
|
||||
|
||||
|
||||
@@ -267,6 +354,195 @@
|
||||
* **types:** useless import ([5f7872f](https://github.com/nuxt/ui/commit/5f7872f06e81e03443e2d1c27a654cfe32c55fb3))
|
||||
* **useComponentIcons:** reactivity when using `defu` ([45454fa](https://github.com/nuxt/ui/commit/45454fae45b8571a9691284bd6a13a838e8ea1c9))
|
||||
|
||||
## [2.18.6](https://github.com/nuxt/ui/compare/v2.18.5...v2.18.6) (2024-09-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **components:** accept partial config in `ui` prop ([#2235](https://github.com/nuxt/ui/issues/2235)) ([eecf4f7](https://github.com/nuxt/ui/commit/eecf4f7ed8a32a874f00afd7bff2964a1366e0b5))
|
||||
* **Modal/Slideover:** bind transition class to `TransitionChild` for Vue 3.5 ([#2227](https://github.com/nuxt/ui/issues/2227)) ([803c20a](https://github.com/nuxt/ui/commit/803c20ad92e8a31fefd6d300856735b0e9adbdf9))
|
||||
* **SelectMenu:** wrong placeholder color with multiple ([#2218](https://github.com/nuxt/ui/issues/2218)) ([28ad5cf](https://github.com/nuxt/ui/commit/28ad5cf98251c6a8acec8d0bf4f0fd07ff6b7066))
|
||||
* **Table:** colspan with expand ([#2217](https://github.com/nuxt/ui/issues/2217)) ([56118c4](https://github.com/nuxt/ui/commit/56118c4a794f3d763dad7b65e044814cf7ef11cf))
|
||||
* **Tabs:** handle icon `margin` in RTL mode ([#2233](https://github.com/nuxt/ui/issues/2233)) ([ea05414](https://github.com/nuxt/ui/commit/ea05414930fe3f5e6805c8aa25bbe8f746bcc86e))
|
||||
* **useFormField:** optional property access ([#2226](https://github.com/nuxt/ui/issues/2226)) ([0a054a5](https://github.com/nuxt/ui/commit/0a054a52b64b4f774041c40223e18e7e056cfd80))
|
||||
|
||||
## [2.18.5](https://github.com/nuxt/ui/compare/v2.18.4...v2.18.5) (2024-09-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Form:** add errors slot prop ([#2188](https://github.com/nuxt/ui/issues/2188)) ([67c6a74](https://github.com/nuxt/ui/commit/67c6a74ed15db1ee8a40e9c74ecfef0d3c3e374a))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Button:** button link not showing disabled classes ([#2185](https://github.com/nuxt/ui/issues/2185)) ([e8ea84a](https://github.com/nuxt/ui/commit/e8ea84a5736759d953664f8f397a2339c212b294))
|
||||
* **Carousel:** remove trailing space in next button icon ([#2088](https://github.com/nuxt/ui/issues/2088)) ([1282a5f](https://github.com/nuxt/ui/commit/1282a5f6c001aa05597d458800bafcf6b6419634))
|
||||
* **FormGroup:** remove id when used with `RadioGroup` ([#2152](https://github.com/nuxt/ui/issues/2152)) ([7aec42c](https://github.com/nuxt/ui/commit/7aec42ca15aaa0ccc63c520b484cba203fd3232b))
|
||||
* **Input:** avoid binding value when type is `file` ([#2047](https://github.com/nuxt/ui/issues/2047)) ([82313e8](https://github.com/nuxt/ui/commit/82313e862cbf21ae631156af4cd057f1383db634))
|
||||
* **module:** allow CSS variables in tailwind colors ([#2014](https://github.com/nuxt/ui/issues/2014)) ([7f50c70](https://github.com/nuxt/ui/commit/7f50c7031fecb5ab26a6d0f58b576b2fd0860487))
|
||||
* **module:** augment `@nuxt/schema` rather than `nuxt/schema` ([#2171](https://github.com/nuxt/ui/issues/2171)) ([ead904f](https://github.com/nuxt/ui/commit/ead904fd2f2bbb29fd60ccde063bf02daa2cbdbb))
|
||||
* **module:** consider user tailwind `configPath` for module as string ([#2074](https://github.com/nuxt/ui/issues/2074)) ([e4ba4f7](https://github.com/nuxt/ui/commit/e4ba4f7c729f99dde51891636605793864812d30))
|
||||
* **Pagination:** use links on prev and next button ([#2179](https://github.com/nuxt/ui/issues/2179)) ([c850f85](https://github.com/nuxt/ui/commit/c850f85aaa40c7abbe8cc4dc1bd4705bf7677390))
|
||||
* **README:** update license link ([#2154](https://github.com/nuxt/ui/issues/2154)) ([8d79eea](https://github.com/nuxt/ui/commit/8d79eea19b3276b1f1e069d33b98b311e9b91cfd))
|
||||
* **Slideover:** bind `rounded` class to panel ([#2187](https://github.com/nuxt/ui/issues/2187)) ([bf32baa](https://github.com/nuxt/ui/commit/bf32baaab01dc4150622f67b3b4a8d02d21b922c))
|
||||
* **Slideover:** bind `shadow` class to panel ([#2201](https://github.com/nuxt/ui/issues/2201)) ([d22526c](https://github.com/nuxt/ui/commit/d22526c0c10735a92e63b7d086e7b8534a08d768))
|
||||
* **Table:** checkbox can emit the `[@select](https://github.com/select)` event ([#2072](https://github.com/nuxt/ui/issues/2072)) ([b1f691f](https://github.com/nuxt/ui/commit/b1f691f28ca8c94f6b658dcb61eeff06951bd1d0))
|
||||
* **Table:** select all rows reactivity issue ([#2200](https://github.com/nuxt/ui/issues/2200)) ([68124de](https://github.com/nuxt/ui/commit/68124de5106e55cb2987a6ba4ec1120d79b51788))
|
||||
* **Tabs:** recalculate marker if items change ([#2101](https://github.com/nuxt/ui/issues/2101)) ([82c4926](https://github.com/nuxt/ui/commit/82c4926c090ce7fac48022a93b1b05b877bb48dd))
|
||||
* **Textarea:** resolve row count calculation errors caused by scrollbar ([#2040](https://github.com/nuxt/ui/issues/2040)) ([8210936](https://github.com/nuxt/ui/commit/8210936f22fcf6b7eb5b9711e2c29be38956b8d6))
|
||||
|
||||
## [2.18.4](https://github.com/nuxt/ui/compare/v2.18.3...v2.18.4) (2024-08-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Form:** submit event data ([#2012](https://github.com/nuxt/ui/issues/2012)) ([4d61936](https://github.com/nuxt/ui/commit/4d61936e7e90b664846a8f265825612c509511d7))
|
||||
* **module:** handle nested colors from ui config ([#2008](https://github.com/nuxt/ui/issues/2008)) ([1cc7e2a](https://github.com/nuxt/ui/commit/1cc7e2a306e0f3f666b9a588f6ed02e7eabc0272))
|
||||
* **module:** reduce css bundle size by fixing safelist regex ([#2005](https://github.com/nuxt/ui/issues/2005)) ([8ac9ca4](https://github.com/nuxt/ui/commit/8ac9ca49789a9a7281f7a40926e7e9a8068cc395))
|
||||
* **module:** suffix types imports with `/index` ([7e37668](https://github.com/nuxt/ui/commit/7e37668940d06c5aa20b60d9bfd600d50a171014)), closes [#2018](https://github.com/nuxt/ui/issues/2018)
|
||||
* **Tabs:** use `nextTick` before marker calc ([#2020](https://github.com/nuxt/ui/issues/2020)) ([9c04969](https://github.com/nuxt/ui/commit/9c049690227af8aba61a1f7c002b00c5dfeb63ff))
|
||||
* **useFormGroup:** app config default input size ([#2011](https://github.com/nuxt/ui/issues/2011)) ([3485092](https://github.com/nuxt/ui/commit/3485092edb55f9ef2ca038a8c137431866d6c28a))
|
||||
|
||||
## [2.18.3](https://github.com/nuxt/ui/compare/v2.18.2...v2.18.3) (2024-07-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Link:** define `rel` as any ([69f605f](https://github.com/nuxt/ui/commit/69f605fa724454e4be9e4cee9666a5d57f43a129))
|
||||
* **types:** only use `.ts` for index ([93ddf1d](https://github.com/nuxt/ui/commit/93ddf1d60b0ea5f99f564f3d3969c397ad91cc72))
|
||||
|
||||
## [2.18.2](https://github.com/nuxt/ui/compare/v2.18.1...v2.18.2) (2024-07-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Tabs:** add missing `UIcon` import ([4fd1be2](https://github.com/nuxt/ui/commit/4fd1be28922bf39584005c14982e5cd9a7d0c624))
|
||||
|
||||
## [2.18.1](https://github.com/nuxt/ui/compare/v2.18.0...v2.18.1) (2024-07-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **components:** use relative imports ([ea721a3](https://github.com/nuxt/ui/commit/ea721a3705cfbcef3075f8c9c1f4acf359974597))
|
||||
|
||||
## [2.18.0](https://github.com/nuxt/ui/compare/v2.17.0...v2.18.0) (2024-07-25)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **Icon:** migrate from `@egoist/tailwindcss-icons` to new `@nuxt/icon` (#1789)
|
||||
|
||||
### Features
|
||||
|
||||
* **Checkbox/Radio/RadioGroup:** add `help` slot ([c3122f7](https://github.com/nuxt/ui/commit/c3122f776daa6d68f201f22c37e0084aac37ed06)), closes [#1957](https://github.com/nuxt/ui/issues/1957)
|
||||
* **CommandPalette:** handle `static` groups ([#1458](https://github.com/nuxt/ui/issues/1458)) ([b264ad2](https://github.com/nuxt/ui/commit/b264ad2ebdc8d4ee4aab5c994df968025207021f))
|
||||
* **Icon:** migrate from `@egoist/tailwindcss-icons` to new `@nuxt/icon` ([#1789](https://github.com/nuxt/ui/issues/1789)) ([c904604](https://github.com/nuxt/ui/commit/c904604c23987c2535e0e91e9c4fec50477f6b34))
|
||||
* **module:** improve app config types autocomplete ([#1870](https://github.com/nuxt/ui/issues/1870)) ([3f8ea5d](https://github.com/nuxt/ui/commit/3f8ea5dbded7b6836495103739688905ff26fe22))
|
||||
* **RadioGroup:** add `selected` to label slot props ([#1587](https://github.com/nuxt/ui/issues/1587)) ([d18477d](https://github.com/nuxt/ui/commit/d18477def58171d51bdb7d00e31e2807b2e7015b))
|
||||
* **SelectMenu:** add selected to `label` / `leading` / `trailing` slots props ([#1349](https://github.com/nuxt/ui/issues/1349)) ([6b216ca](https://github.com/nuxt/ui/commit/6b216cab1ba3bb69cb317254dfd562ab020c5e92))
|
||||
* **SelectMenu:** handle function in `showCreateOptionWhen` prop ([#1853](https://github.com/nuxt/ui/issues/1853)) ([7e974b5](https://github.com/nuxt/ui/commit/7e974b55d72b8ac0ab42ef722a2d1904c3e4e091))
|
||||
* **Skeleton:** add `as` prop ([#1955](https://github.com/nuxt/ui/issues/1955)) ([bce94db](https://github.com/nuxt/ui/commit/bce94db9fdb2c29a4f2e5981e5dce49a44a4ac8a))
|
||||
* **Table:** expand row ([#1036](https://github.com/nuxt/ui/issues/1036)) ([7155318](https://github.com/nuxt/ui/commit/71553180294c53024c28de9bbebf4ea69f616da7))
|
||||
* **Table:** handle `rowClass` property in `columns` ([#1632](https://github.com/nuxt/ui/issues/1632)) ([748e491](https://github.com/nuxt/ui/commit/748e49175da37b85bd18d62a8455875990866d5b))
|
||||
* **Tabs:** handle `icon` in items ([#1798](https://github.com/nuxt/ui/issues/1798)) ([e8eb394](https://github.com/nuxt/ui/commit/e8eb3941ad4c1c306ccbe9e11d979d5f6c808330))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Accordion:** truncate buttons ([5db18c0](https://github.com/nuxt/ui/commit/5db18c00565f9d2bb9f2768c2de2ab291a55bcae)), closes [#1909](https://github.com/nuxt/ui/issues/1909)
|
||||
* **Alert/Notification:** missing margin on description ([2c55fb6](https://github.com/nuxt/ui/commit/2c55fb63365ee7cc1e993ebd5aa5f83ddadcd26a)), closes [#1959](https://github.com/nuxt/ui/issues/1959)
|
||||
* **Breadcrumb:** use `rotate` on rtl icon ([53003fc](https://github.com/nuxt/ui/commit/53003fcd07d67d13ada0759ff6c5cd3635fba0e3))
|
||||
* **ButtonGroup/FormGroup:** pass default sizes to children ([#1875](https://github.com/nuxt/ui/issues/1875)) ([6b6b03d](https://github.com/nuxt/ui/commit/6b6b03d59f5ab3096b731c59d18a1085d25b5e8e))
|
||||
* **Carousel:** remove `mix-blend-overlay` on indicators ([#1714](https://github.com/nuxt/ui/issues/1714)) ([f74f1df](https://github.com/nuxt/ui/commit/f74f1df6ca5f93e11e542245b611c1aa7c4b8308))
|
||||
* **FormGroup:** don't check for `error` slot so `help` slot can render ([#1888](https://github.com/nuxt/ui/issues/1888)) ([99c52e5](https://github.com/nuxt/ui/commit/99c52e50082d5e99440894c7a077a17510f0de50))
|
||||
* **InputMenu/SelectMenu:** invalid `label` with `value-attribute` and async search ([4d5f250](https://github.com/nuxt/ui/commit/4d5f2509022e4fb74fc268d5479f7cc8f0415040)), closes [#1780](https://github.com/nuxt/ui/issues/1780)
|
||||
* **InputMenu/SelectMenu:** prevent double filter with async search ([e2881d3](https://github.com/nuxt/ui/commit/e2881d3801c54c49d66d41d4f0ba312a7b3ebce7)), closes [#1966](https://github.com/nuxt/ui/issues/1966)
|
||||
* **Link:** allow `ariaLabel` to be picked ([720c44d](https://github.com/nuxt/ui/commit/720c44dd5ee90bb3b30aef32f01ff6eae1397aa4)), closes [#1934](https://github.com/nuxt/ui/issues/1934)
|
||||
* **Progress:** pass down attrs to `<progress>` to improve accessibility ([#1881](https://github.com/nuxt/ui/issues/1881)) ([abd13f1](https://github.com/nuxt/ui/commit/abd13f1f8fd4c8b10069174534c5fdec6c83576e))
|
||||
* **RadioGroup:** allow boolean in `modelValue` prop ([#1913](https://github.com/nuxt/ui/issues/1913)) ([8eca5a0](https://github.com/nuxt/ui/commit/8eca5a0d627e22f42350a060f09c4e44b6de422f))
|
||||
|
||||
## [2.17.0](https://github.com/nuxt/ui/compare/v2.16.0...v2.17.0) (2024-06-13)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Alert:** add `actions` slot ([#1785](https://github.com/nuxt/ui/issues/1785)) ([c8dd71c](https://github.com/nuxt/ui/commit/c8dd71c4f5a5239811b07b50f1dc802101af07d5))
|
||||
* **Form:** update and migrate `valibot` to v0.31.0 ([#1848](https://github.com/nuxt/ui/issues/1848)) ([1d5bd89](https://github.com/nuxt/ui/commit/1d5bd89d5881163fc6dc917e138b9d8304dff6c4))
|
||||
* **Notification:** allow ring customization with `{color}` ([#1830](https://github.com/nuxt/ui/issues/1830)) ([3ebff4d](https://github.com/nuxt/ui/commit/3ebff4d133372e339e2c4c439576e9e192b29cc3))
|
||||
* **Slideover:** handle `top` and `bottom` side ([#1834](https://github.com/nuxt/ui/issues/1834)) ([50ad14f](https://github.com/nuxt/ui/commit/50ad14f9dffe4f76bef888cd10d30b417c75bca5))
|
||||
* **Tabs:** add `content` prop to avoid the render of the HTML markup ([#1831](https://github.com/nuxt/ui/issues/1831)) ([6e2678d](https://github.com/nuxt/ui/commit/6e2678d1d8a498322eb3eff909ccbba55e40a2b7))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Alert/Notification:** use `div` for description ([e8898d1](https://github.com/nuxt/ui/commit/e8898d15a667ba66e78828315e3cc4e92845cd3f)), closes [#1551](https://github.com/nuxt/ui/issues/1551)
|
||||
* **Alert:** base style not applied on icon ([#1859](https://github.com/nuxt/ui/issues/1859)) ([f65aefb](https://github.com/nuxt/ui/commit/f65aefb7067c1c64c1355b5d699129e716ef1281))
|
||||
* **Breadcrumb:** allow `aria-current` to be overrideable ([ebfb835](https://github.com/nuxt/ui/commit/ebfb8350339725c0a6f88c73f16bff01d61538c2)), closes [#1856](https://github.com/nuxt/ui/issues/1856)
|
||||
* **Carousel:** prevent mouse click when dragging ([#1781](https://github.com/nuxt/ui/issues/1781)) ([4f0d00f](https://github.com/nuxt/ui/commit/4f0d00f7a6eebf05adceaf1e7c2869ad91949cf3))
|
||||
* **CommandPalette:** hide `empty-state` when `null` ([249bbd4](https://github.com/nuxt/ui/commit/249bbd49dc8420603e8d561543d237abeb400908)), closes [#1787](https://github.com/nuxt/ui/issues/1787)
|
||||
* **Form:** maintain other errors when using `setErrors` with a path ([#1818](https://github.com/nuxt/ui/issues/1818)) ([06990be](https://github.com/nuxt/ui/commit/06990beabf67f668322b4d3fb2ec93cc4f3bdcd4))
|
||||
* **Input:** hide wrapper when type is `hidden` ([#1797](https://github.com/nuxt/ui/issues/1797)) ([e7c2f78](https://github.com/nuxt/ui/commit/e7c2f7856c05ed96f48c83d64d8e1d3f41ab58fe))
|
||||
* **Link:** typo in `exactHash` type ([581b470](https://github.com/nuxt/ui/commit/581b470cc79c2315bb2d56e02a7c134a7861c616)), closes [#1767](https://github.com/nuxt/ui/issues/1767)
|
||||
* **SelectMenu:** wrong placeholder color when `modelValue` is an empty string ([9b9ccdb](https://github.com/nuxt/ui/commit/9b9ccdb59e98fed096dd18809af646b10de46b9f)), closes [#1862](https://github.com/nuxt/ui/issues/1862)
|
||||
* **Select:** remove defaults for `value` and `text` ([6c124bb](https://github.com/nuxt/ui/commit/6c124bb1ac2fef116161da56a3a8e5f92144ce3a)), closes [#1702](https://github.com/nuxt/ui/issues/1702)
|
||||
|
||||
## [2.16.0](https://github.com/nuxt/ui/compare/v2.15.2...v2.16.0) (2024-05-07)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **Input:** redesign `file` type without absolute positioning (#1712)
|
||||
|
||||
### Features
|
||||
|
||||
* **InputMenu/SelectMenu:** allow lazy search ([#1705](https://github.com/nuxt/ui/issues/1705)) ([7e6ba78](https://github.com/nuxt/ui/commit/7e6ba786816516ab5007a2ff15fc974cfdd796ab))
|
||||
* **module:** HMR support with `@nuxtjs/tailwindcss` ([#1665](https://github.com/nuxt/ui/issues/1665)) ([821e15b](https://github.com/nuxt/ui/commit/821e15b696b03d0f5e20e001d39f86a8b3cec426))
|
||||
* **Table:** allow providing a `<caption>` ([#1680](https://github.com/nuxt/ui/issues/1680)) ([3fca668](https://github.com/nuxt/ui/commit/3fca66857d3616bf24a1b0579c90179a7883869d))
|
||||
* **useToast:** allow clearing all notifications ([#1695](https://github.com/nuxt/ui/issues/1695)) ([82d619b](https://github.com/nuxt/ui/commit/82d619b2a75b9d08f3f5b314d37c30d77d8341e9))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Breadcrumb:** pass `click` event to `ULink` ([5481dab](https://github.com/nuxt/ui/commit/5481dab53dbe0b28188b4a16811f3e8816d93edf))
|
||||
* **Input:** redesign `file` type without absolute positioning ([#1712](https://github.com/nuxt/ui/issues/1712)) ([ed5c74d](https://github.com/nuxt/ui/commit/ed5c74dc17df784485eabc39c83e62ada9210a49))
|
||||
* **Notification:** update timer when timeout prop changes ([#1673](https://github.com/nuxt/ui/issues/1673)) ([cba9ad7](https://github.com/nuxt/ui/commit/cba9ad78db58cb9228bb9c96f0469d43bde2bf3e))
|
||||
* **Slideover:** export and clean types ([#1692](https://github.com/nuxt/ui/issues/1692)) ([bd3fa86](https://github.com/nuxt/ui/commit/bd3fa8658f84fb7bd96d322968462c5eaa987b86))
|
||||
* **Table:** provide `aria-sort` for sortable table headings ([#1675](https://github.com/nuxt/ui/issues/1675)) ([6f60fa9](https://github.com/nuxt/ui/commit/6f60fa9a980020f6a5afc2916e699a7f9a47e8ce))
|
||||
|
||||
## [2.15.2](https://github.com/nuxt/ui/compare/v2.15.1...v2.15.2) (2024-04-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Accordion:** add `unmount` prop to allow lazy mounting for heavy components ([#1590](https://github.com/nuxt/ui/issues/1590)) ([91e5002](https://github.com/nuxt/ui/commit/91e50020507ac66992dfb52b3e0ad1a1ae5614b5))
|
||||
* **Table:** add `checkbox` ui config ([#1409](https://github.com/nuxt/ui/issues/1409)) ([8b54660](https://github.com/nuxt/ui/commit/8b546600dbfbff187d9c5be1b35ea1772e94f83f))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Breadcrumb:** missing `min-w-0` on wrapper to truncate ([9f01145](https://github.com/nuxt/ui/commit/9f01145bc674378371ff34d7110f3235b57d2459)), closes [#1650](https://github.com/nuxt/ui/issues/1650)
|
||||
* **Carousel:** next and prev buttons disabled ([#1619](https://github.com/nuxt/ui/issues/1619)) ([e909884](https://github.com/nuxt/ui/commit/e909884d0327bfd7b4d5551382123f8998beff6a))
|
||||
* **Popover/Dropdown:** prevent unintended closure on touchstart in mobile devices ([#1609](https://github.com/nuxt/ui/issues/1609)) ([2392b4a](https://github.com/nuxt/ui/commit/2392b4aa405430fc22766f130448a7cc5ced9a3a))
|
||||
* **Slideover:** remove dynamic component when closing ([#1615](https://github.com/nuxt/ui/issues/1615)) ([58faa10](https://github.com/nuxt/ui/commit/58faa1053b9be3f627c3fcff1bcaa14850bb9e7f))
|
||||
* **Slideover:** wait for transition to complete to reset state ([#1624](https://github.com/nuxt/ui/issues/1624)) ([07a4d13](https://github.com/nuxt/ui/commit/07a4d13c0fcb05c87fb42e02a3a2d6c5c52ccf09))
|
||||
|
||||
## [2.15.1](https://github.com/nuxt/ui/compare/v2.15.0...v2.15.1) (2024-04-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Avatar:** add `as` prop to use `NuxtImg` underneath ([49b73aa](https://github.com/nuxt/ui/commit/49b73aa024be14a9aa150a2804f4dcb18542fa49)), closes [#1577](https://github.com/nuxt/ui/issues/1577)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Checkbox:** `[@change](https://github.com/change)` event value ([#1580](https://github.com/nuxt/ui/issues/1580)) ([c98d6e3](https://github.com/nuxt/ui/commit/c98d6e31c0e3f46b97957d5cf3de7f9da1f70c58))
|
||||
* **Divider:** add `w-full` only on horizontal wrapper ([#1565](https://github.com/nuxt/ui/issues/1565)) ([bd8b737](https://github.com/nuxt/ui/commit/bd8b737642280e6a83b67f9a27dd7a823a77e963))
|
||||
* **Dropdown:** missing `mouseenter` event on container ([7288953](https://github.com/nuxt/ui/commit/72889535e7e9763e7ebf59498f22c39bf09d6477))
|
||||
* **Input/SelectMenu:** handle `file` type and `change` events ([#1570](https://github.com/nuxt/ui/issues/1570)) ([878f707](https://github.com/nuxt/ui/commit/878f7078a28c5e70a662682d1293db466d518c7d))
|
||||
* **Popover:** missing `mouseenter` event on container ([8517897](https://github.com/nuxt/ui/commit/8517897c34adaa9e3624f867b43106deb59fcbe8)), closes [#1564](https://github.com/nuxt/ui/issues/1564)
|
||||
|
||||
## [2.15.0](https://github.com/nuxt/ui/compare/v2.14.2...v2.15.0) (2024-03-26)
|
||||
|
||||
|
||||
|
||||
91
README.md
91
README.md
@@ -1,97 +1,78 @@
|
||||
[](https://ui.nuxt.com)
|
||||
|
||||
# Nuxt UI
|
||||
# Nuxt UI v3
|
||||
|
||||
[![npm version][npm-version-src]][npm-version-href]
|
||||
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
||||
[![License][license-src]][license-href]
|
||||
[![Nuxt][nuxt-src]][nuxt-href]
|
||||
|
||||
Nuxt UI is a module that provides a set of Vue components and composables built with [Tailwind CSS](https://tailwindcss.com/) and [Headless UI](https://headlessui.dev/) to help you build beautiful and accessible user interfaces.
|
||||
|
||||
Its goal is to provide everything related to UI when building a Nuxt app. This includes components, icons, colors, dark mode but also keyboard shortcuts.
|
||||
|
||||
## Features
|
||||
|
||||
- Built with [Headless UI](https://headlessui.dev/) and [Tailwind CSS](https://tailwindcss.com/)
|
||||
- HMR support through Nuxt App Config
|
||||
- Dark mode support
|
||||
- Support for LTR and RTL languages
|
||||
- Keyboard shortcuts
|
||||
- Bundled icons
|
||||
- Fully typed
|
||||
- [Figma Kit](https://www.figma.com/community/file/1288455405058138934)
|
||||
|
||||
Read more on [ui.nuxt.com](https://ui.nuxt.com)
|
||||
We're thrilled to introduce Nuxt UI v3, a significant upgrade to our UI library that delivers extensive improvements and robust new capabilities. This major update harnesses the combined strengths of [Radix Vue](https://www.radix-vue.com/), [Tailwind CSS v4](https://tailwindcss.com/blog/tailwindcss-v4-alpha), and [Tailwind Variants](https://www.tailwind-variants.org/) to offer developers an unparalleled set of tools for creating sophisticated, accessible, and highly performant user interfaces.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm install @nuxt/ui
|
||||
# yarn
|
||||
yarn add @nuxt/ui
|
||||
# pnpm
|
||||
pnpm add @nuxt/ui
|
||||
# bun
|
||||
bun add @nuxt/ui
|
||||
1. Install the Nuxt UI v3 alpha package:
|
||||
|
||||
```bash [pnpm]
|
||||
pnpm add @nuxt/ui@next
|
||||
```
|
||||
|
||||
Then, register the module in your `nuxt.config.ts`:
|
||||
```bash [yarn]
|
||||
yarn add @nuxt/ui@next
|
||||
```
|
||||
|
||||
```js
|
||||
```bash [npm]
|
||||
npm install @nuxt/ui@next
|
||||
```
|
||||
|
||||
```bash [bun]
|
||||
bun add @nuxt/ui@next
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> Make sure you have `typescript` installed in your dev dependencies.
|
||||
|
||||
2. Register the Nuxt UI module in your `nuxt.config.ts`:
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
modules: [
|
||||
'@nuxt/ui'
|
||||
]
|
||||
modules: ['@nuxt/ui']
|
||||
})
|
||||
```
|
||||
|
||||
If you want latest updates, please use `@nuxt/ui-edge` in your `package.json`:
|
||||
3. Import Tailwind CSS and Nuxt UI in your `app.vue` or [CSS](https://nuxt.com/docs/getting-started/styling#the-css-property):
|
||||
|
||||
```json
|
||||
{
|
||||
"devDependencies": {
|
||||
"@nuxt/ui": "npm:@nuxt/ui-edge@latest"
|
||||
}
|
||||
}
|
||||
```vue [app.vue]
|
||||
<style>
|
||||
@import "tailwindcss";
|
||||
@import "@nuxt/ui";
|
||||
</style>
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Visit https://ui.nuxt.com to explore the documentation.
|
||||
Visit https://ui3.nuxt.dev to explore the documentation.
|
||||
|
||||
## Credits
|
||||
|
||||
- [nuxt/nuxt](https://github.com/nuxt/nuxt)
|
||||
- [nuxt/icon](https://github.com/nuxt/icon)
|
||||
- [nuxt/fonts](https://github.com/nuxt/fonts)
|
||||
- [nuxt-modules/color-mode](https://github.com/nuxt-modules/color-mode)
|
||||
- [nuxt-modules/tailwindcss](https://github.com/nuxt-modules/tailwindcss)
|
||||
- [radix-vue/radix-vue](https://github.com/radix-vue/radix-vue)
|
||||
- [tailwindlabs/tailwindcss](https://github.com/tailwindlabs/tailwindcss)
|
||||
- [tailwindlabs/headlessui](https://github.com/tailwindlabs/headlessui)
|
||||
- [vueuse/vueuse](https://github.com/vueuse/vueuse)
|
||||
- [egoist/tailwindcss-icons](https://github.com/egoist/tailwindcss-icons)
|
||||
|
||||
## Contributing
|
||||
|
||||
Thank you for considering contributing to Nuxt UI. Here are a few ways you can get involved:
|
||||
|
||||
- Reporting Bugs: If you come across any bugs or issues, please check out the reporting bugs guide to learn how to submit a bug report.
|
||||
- Suggestions: Have any thoughts to enhance Nuxt UI? We'd love to hear them! Check out the [contribution guide](https://ui.nuxt.com/getting-started/contributing) to share your suggestions.
|
||||
|
||||
## Local Development
|
||||
|
||||
Follow the docs to [Set up your local development environment](https://ui.nuxt.com/getting-started/contributing#_2-local-development-setup) and contribute.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under the [MIT license](https://github.com/nuxt/ui/blob/dev/LICENSE.md).
|
||||
|
||||
<!-- Badges -->
|
||||
[npm-version-src]: https://img.shields.io/npm/v/@nuxt/ui/latest.svg?style=flat&colorA=18181B&colorB=28CF8D
|
||||
[npm-version-src]: https://img.shields.io/npm/v/@nuxt/ui/next.svg?style=flat&colorA=18181B&colorB=28CF8D
|
||||
[npm-version-href]: https://npmjs.com/package/@nuxt/ui
|
||||
|
||||
[npm-downloads-src]: https://img.shields.io/npm/dm/@nuxt/ui.svg?style=flat&colorA=18181B&colorB=28CF8D
|
||||
[npm-downloads-href]: https://npmjs.com/package/@nuxt/ui
|
||||
[npm-downloads-href]: https://npm.chart.dev/@nuxt/ui
|
||||
|
||||
[license-src]: https://img.shields.io/github/license/nuxt/ui.svg?style=flat&colorA=18181B&colorB=28CF8D
|
||||
[license-href]: https://github.com/nuxt/ui/blob/main/LICENSE.md
|
||||
|
||||
12
build.config.ts
Normal file
12
build.config.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { defineBuildConfig } from 'unbuild'
|
||||
|
||||
export default defineBuildConfig({
|
||||
replace: {
|
||||
'process.env.DEV': 'false'
|
||||
},
|
||||
hooks: {
|
||||
'mkdist:entry:options'(ctx, entry, options) {
|
||||
options.addRelativeDeclarationExtensions = false
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -75,7 +75,7 @@ export default defineCommand({
|
||||
await sortFile(themePath)
|
||||
|
||||
if (!args.prose) {
|
||||
const typesPath = resolve(path, 'src/runtime/types/index.d.ts')
|
||||
const typesPath = resolve(path, 'src/runtime/types/index.ts')
|
||||
await appendFile(typesPath, `export * from '../components/${args.content ? 'content/' : ''}${splitByCase(name).map(p => upperFirst(p)).join('')}.vue'`)
|
||||
await sortFile(typesPath)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ const playground = ({ name, pro }) => {
|
||||
const kebabName = kebabCase(name)
|
||||
|
||||
return {
|
||||
filename: `playground/pages/components/${kebabName}.vue`,
|
||||
filename: `playground/app/pages/components/${kebabName}.vue`,
|
||||
contents: pro
|
||||
? undefined
|
||||
: `
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
export default defineAppConfig({
|
||||
toaster: {
|
||||
position: 'bottom-right' as const,
|
||||
expand: true,
|
||||
duration: 5000
|
||||
},
|
||||
theme: {
|
||||
radius: 0.25
|
||||
},
|
||||
ui: {
|
||||
colors: {
|
||||
primary: 'sky',
|
||||
gray: 'cool'
|
||||
primary: 'green',
|
||||
neutral: 'slate'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { withoutTrailingSlash } from 'ufo'
|
||||
import colors from 'tailwindcss/colors'
|
||||
// import { debounce } from 'perfect-debounce'
|
||||
import type { ContentSearchFile } from '@nuxt/ui-pro'
|
||||
|
||||
const route = useRoute()
|
||||
// const colorMode = useColorMode()
|
||||
const appConfig = useAppConfig()
|
||||
const colorMode = useColorMode()
|
||||
const runtimeConfig = useRuntimeConfig()
|
||||
const { integrity, api } = runtimeConfig.public.content
|
||||
|
||||
const { data: navigation } = await useAsyncData('navigation', () => fetchContentNavigation(), { default: () => [] })
|
||||
const { data: files } = await useLazyFetch<ContentSearchFile[]>(`${api.baseURL}/search${integrity ? '.' + integrity : ''}`, { default: () => [] })
|
||||
const { data: files } = await useLazyFetch<ContentSearchFile[]>(`${api.baseURL}/search${integrity ? '-' + integrity : ''}`, { default: () => [] })
|
||||
|
||||
const searchTerm = ref('')
|
||||
|
||||
@@ -49,17 +51,21 @@ const links = computed(() => {
|
||||
}].filter(Boolean)
|
||||
})
|
||||
|
||||
// const color = computed(() => colorMode.value === 'dark' ? '#18181b' : 'white')
|
||||
const color = computed(() => colorMode.value === 'dark' ? (colors as any)[appConfig.ui.colors.neutral][900] : 'white')
|
||||
const radius = computed(() => `:root { --ui-radius: ${appConfig.theme.radius}rem; }`)
|
||||
|
||||
useHead({
|
||||
meta: [
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
|
||||
// { key: 'theme-color', name: 'theme-color', content: color }
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
||||
{ key: 'theme-color', name: 'theme-color', content: color }
|
||||
],
|
||||
link: [
|
||||
{ rel: 'icon', type: 'image/svg+xml', href: '/icon.svg' },
|
||||
{ rel: 'canonical', href: `https://ui.nuxt.com${withoutTrailingSlash(route.path)}` }
|
||||
],
|
||||
style: [
|
||||
{ innerHTML: radius, id: 'nuxt-ui-radius', tagPriority: -2 }
|
||||
],
|
||||
htmlAttrs: {
|
||||
lang: 'en'
|
||||
}
|
||||
@@ -71,24 +77,29 @@ useServerSeoMeta({
|
||||
})
|
||||
|
||||
provide('navigation', navigation)
|
||||
provide('files', files)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UApp>
|
||||
<NuxtLoadingIndicator />
|
||||
<UApp :toaster="appConfig.toaster">
|
||||
<NuxtLoadingIndicator color="#FFF" />
|
||||
|
||||
<Banner v-if="!route.path.startsWith('/examples')" />
|
||||
<template v-if="!route.path.startsWith('/examples')">
|
||||
<Banner />
|
||||
|
||||
<Header v-if="!route.path.startsWith('/examples')" :links="links" />
|
||||
<Header :links="links" />
|
||||
</template>
|
||||
|
||||
<NuxtLayout>
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
|
||||
<Footer v-if="!route.path.startsWith('/examples')" />
|
||||
<template v-if="!route.path.startsWith('/examples')">
|
||||
<Footer />
|
||||
|
||||
<LazyUContentSearch v-model:search-term="searchTerm" :files="files" :navigation="navigation" :fuse="{ resultLimit: 42 }" />
|
||||
<ClientOnly>
|
||||
<LazyUContentSearch v-model:search-term="searchTerm" :files="files" :navigation="navigation" :fuse="{ resultLimit: 42 }" />
|
||||
</ClientOnly>
|
||||
</template>
|
||||
</UApp>
|
||||
</template>
|
||||
|
||||
@@ -99,6 +110,22 @@ provide('files', files)
|
||||
@source "../content/**/*.md";
|
||||
|
||||
@theme {
|
||||
--font-family-sans: 'Inter', sans-serif;
|
||||
--font-family-sans: 'Public Sans', sans-serif;
|
||||
|
||||
--color-green-50: #EFFDF5;
|
||||
--color-green-100: #D9FBE8;
|
||||
--color-green-200: #B3F5D1;
|
||||
--color-green-300: #75EDAE;
|
||||
--color-green-400: #00DC82;
|
||||
--color-green-500: #00C16A;
|
||||
--color-green-600: #00A155;
|
||||
--color-green-700: #007F45;
|
||||
--color-green-800: #016538;
|
||||
--color-green-900: #0A5331;
|
||||
--color-green-950: #052E16;
|
||||
}
|
||||
|
||||
:root {
|
||||
--ui-container-width: 90rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
1
docs/app/assets/icons/embla-carousel.svg
Normal file
1
docs/app/assets/icons/embla-carousel.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 13 KiB |
@@ -23,15 +23,15 @@ const route = useRoute()
|
||||
|
||||
<UFooter>
|
||||
<template #left>
|
||||
<NuxtLink v-if="route.path.startsWith('/pro')" to="https://ui.nuxt.com/pro/purchase" target="_blank" class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Purchase <span class="text-gray-900 dark:text-white">Nuxt UI Pro</span>
|
||||
<NuxtLink v-if="route.path.startsWith('/pro')" to="https://ui.nuxt.com/pro/purchase" target="_blank" class="text-sm text-[--ui-text-muted]">
|
||||
Purchase <span class="text-[--ui-text-highlighted]">Nuxt UI Pro</span>
|
||||
</NuxtLink>
|
||||
<NuxtLink v-else to="https://github.com/nuxt/ui" target="_blank" class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Published under <span class="text-gray-900 dark:text-white">MIT License</span>
|
||||
<NuxtLink v-else to="https://github.com/nuxt/ui" target="_blank" class="text-sm text-[--ui-text-muted]">
|
||||
Published under <span class="text-[--ui-text-highlighted]">MIT License</span>
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
<!-- <UNavigationMenu :items="items" variant="link" color="gray" /> -->
|
||||
<!-- <UNavigationMenu :items="items" variant="link" color="neutral" /> -->
|
||||
|
||||
<template #right>
|
||||
<UButton
|
||||
@@ -39,7 +39,7 @@ const route = useRoute()
|
||||
icon="i-simple-icons-nuxtdotjs"
|
||||
to="https://nuxt.com"
|
||||
target="_blank"
|
||||
color="gray"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
/>
|
||||
<UButton
|
||||
@@ -47,7 +47,7 @@ const route = useRoute()
|
||||
icon="i-simple-icons-discord"
|
||||
to="https://chat.nuxt.dev"
|
||||
target="_blank"
|
||||
color="gray"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
/>
|
||||
<UButton
|
||||
@@ -55,7 +55,7 @@ const route = useRoute()
|
||||
icon="i-simple-icons-x"
|
||||
to="https://x.com/nuxt_js"
|
||||
target="_blank"
|
||||
color="gray"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
/>
|
||||
<UButton
|
||||
@@ -63,7 +63,7 @@ const route = useRoute()
|
||||
icon="i-simple-icons-github"
|
||||
to="https://github.com/nuxt/ui"
|
||||
target="_blank"
|
||||
color="gray"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import type { NavItem } from '@nuxt/content'
|
||||
import type { NavigationMenuItem } from '@nuxt/ui'
|
||||
|
||||
const props = defineProps<{
|
||||
defineProps<{
|
||||
links: NavigationMenuItem[]
|
||||
}>()
|
||||
|
||||
@@ -10,44 +10,50 @@ const config = useRuntimeConfig().public
|
||||
|
||||
const navigation = inject<Ref<NavItem[]>>('navigation')
|
||||
|
||||
const items = computed(() => props.links.map(({ icon, ...link }) => link))
|
||||
// const items = computed(() => props.links.map(({ icon, ...link }) => link))
|
||||
|
||||
defineShortcuts({
|
||||
meta_g: () => {
|
||||
window.open('https://github.com/nuxt/ui/tree/v3', '_blank')
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UHeader>
|
||||
<UHeader :ui="{ left: 'min-w-0' }">
|
||||
<template #left>
|
||||
<NuxtLink to="/" class="flex items-end gap-2 font-bold text-xl text-gray-900 dark:text-white" aria-label="Nuxt UI">
|
||||
<Logo class="w-auto h-6" />
|
||||
<NuxtLink to="/" class="flex items-end gap-2 font-bold text-xl text-[--ui-text-highlighted] min-w-0" aria-label="Nuxt UI">
|
||||
<Logo class="w-auto h-6 shrink-0" />
|
||||
|
||||
<UBadge :label="`v${config.version}`" variant="subtle" size="sm" class="-mb-[2px] rounded font-semibold" />
|
||||
<UBadge :label="`v${config.version}`" variant="subtle" size="sm" class="-mb-[2px] rounded font-semibold inline-block truncate" />
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
<!-- <UNavigationMenu :items="items" variant="link" /> -->
|
||||
|
||||
<template #right>
|
||||
<ColorPicker />
|
||||
<ThemePicker />
|
||||
|
||||
<UTooltip text="Search" :kbds="['meta', 'K']">
|
||||
<UContentSearchButton />
|
||||
</UTooltip>
|
||||
|
||||
<!-- <UColorModeButton /> -->
|
||||
|
||||
<UButton
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
to="https://github.com/nuxt/ui"
|
||||
target="_blank"
|
||||
icon="i-simple-icons-github"
|
||||
aria-label="GitHub"
|
||||
/>
|
||||
<UTooltip text="Open on GitHub" :kbds="['meta', 'G']">
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
to="https://github.com/nuxt/ui/tree/v3"
|
||||
target="_blank"
|
||||
icon="i-simple-icons-github"
|
||||
aria-label="GitHub"
|
||||
/>
|
||||
</UTooltip>
|
||||
</template>
|
||||
|
||||
<template #content>
|
||||
<UNavigationMenu orientation="vertical" :items="items" class="-ml-2.5" />
|
||||
<!-- <UNavigationMenu orientation="vertical" :items="items" class="-ml-2.5" />
|
||||
|
||||
<USeparator type="dashed" class="my-4" />
|
||||
<USeparator type="dashed" class="my-4" /> -->
|
||||
|
||||
<UContentNavigation :navigation="navigation" />
|
||||
</template>
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
<path d="M726 92H739C742.314 92 745 89.3137 745 86V60H773V92H800V116H773V159C773 169.5 778.057 174 787 174H800V200H783C759.948 200 745 185.071 745 160V116H726V92Z" fill="currentColor" />
|
||||
<path d="M591 92V154C591 168.004 585.742 179.809 578 188C570.258 196.191 559.566 200 545 200C530.434 200 518.742 196.191 511 188C503.389 179.809 498 168.004 498 154V92H514C517.412 92 520.769 92.622 523 95C525.231 97.2459 526 98.5652 526 102V154C526 162.059 526.457 167.037 530 171C533.543 174.831 537.914 176 545 176C552.217 176 555.457 174.831 559 171C562.543 167.037 563 162.059 563 154V102C563 98.5652 563.769 96.378 566 94C567.96 91.9107 570.028 91.9599 573 92C573.411 92.0055 574.586 92 575 92H591Z" fill="currentColor" />
|
||||
<path d="M676 144L710 92H684C680.723 92 677.812 93.1758 676 96L660 120L645 97C643.188 94.1758 639.277 92 636 92H611L645 143L608 200H634C637.25 200 640.182 196.787 642 194L660 167L679 195C680.818 197.787 683.75 200 687 200H713L676 144Z" fill="currentColor" />
|
||||
<path d="M168 200H279C282.542 200 285.932 198.756 289 197C292.068 195.244 295.23 193.041 297 190C298.77 186.959 300.002 183.51 300 179.999C299.998 176.488 298.773 173.04 297 170.001L222 41C220.23 37.96 218.067 35.7552 215 34C211.933 32.2448 207.542 31 204 31C200.458 31 197.067 32.2448 194 34C190.933 35.7552 188.77 37.96 187 41L168 74L130 9.99764C128.228 6.95784 126.068 3.75491 123 2C119.932 0.245087 116.542 0 113 0C109.458 0 106.068 0.245087 103 2C99.9323 3.75491 96.7717 6.95784 95 9.99764L2 170.001C0.226979 173.04 0.00154312 176.488 1.90993e-06 179.999C-0.0015393 183.51 0.229648 186.959 2 190C3.77035 193.04 6.93245 195.244 10 197C13.0675 198.756 16.4578 200 20 200H90C117.737 200 137.925 187.558 152 164L186 105L204 74L259 168H186L168 200ZM89 168H40L113 42L150 105L125.491 147.725C116.144 163.01 105.488 168 89 168Z" fill="var(--color-primary-DEFAULT)" />
|
||||
<path d="M958 60.0001H938C933.524 60.0001 929.926 59.9395 927 63C924.074 65.8905 925 67.5792 925 72V141C925 151.372 923.648 156.899 919 162C914.352 166.931 908.468 169 899 169C889.705 169 882.648 166.931 878 162C873.352 156.899 873 151.372 873 141V72.0001C873 67.5793 872.926 65.8906 870 63.0001C867.074 59.9396 863.476 60.0001 859 60.0001H840V141C840 159.023 845.016 173.458 855 184C865.156 194.542 879.893 200 899 200C918.107 200 932.844 194.542 943 184C953.156 173.458 958 159.023 958 141V60.0001Z" fill="var(--color-primary-DEFAULT)" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1000 60.0233L1020 60V77L1020 128V156.007L1020 181L1020 189.004C1020 192.938 1019.98 194.429 1017 197.001C1014.02 199.725 1009.56 200 1005 200H986.001V181.006L986 130.012V70.0215C986 66.1576 986.016 64.5494 989 62.023C991.819 59.6358 995.437 60.0233 1000 60.0233Z" fill="var(--color-primary-DEFAULT)" />
|
||||
<path d="M168 200H279C282.542 200 285.932 198.756 289 197C292.068 195.244 295.23 193.041 297 190C298.77 186.959 300.002 183.51 300 179.999C299.998 176.488 298.773 173.04 297 170.001L222 41C220.23 37.96 218.067 35.7552 215 34C211.933 32.2448 207.542 31 204 31C200.458 31 197.067 32.2448 194 34C190.933 35.7552 188.77 37.96 187 41L168 74L130 9.99764C128.228 6.95784 126.068 3.75491 123 2C119.932 0.245087 116.542 0 113 0C109.458 0 106.068 0.245087 103 2C99.9323 3.75491 96.7717 6.95784 95 9.99764L2 170.001C0.226979 173.04 0.00154312 176.488 1.90993e-06 179.999C-0.0015393 183.51 0.229648 186.959 2 190C3.77035 193.04 6.93245 195.244 10 197C13.0675 198.756 16.4578 200 20 200H90C117.737 200 137.925 187.558 152 164L186 105L204 74L259 168H186L168 200ZM89 168H40L113 42L150 105L125.491 147.725C116.144 163.01 105.488 168 89 168Z" fill="var(--ui-primary)" />
|
||||
<path d="M958 60.0001H938C933.524 60.0001 929.926 59.9395 927 63C924.074 65.8905 925 67.5792 925 72V141C925 151.372 923.648 156.899 919 162C914.352 166.931 908.468 169 899 169C889.705 169 882.648 166.931 878 162C873.352 156.899 873 151.372 873 141V72.0001C873 67.5793 872.926 65.8906 870 63.0001C867.074 59.9396 863.476 60.0001 859 60.0001H840V141C840 159.023 845.016 173.458 855 184C865.156 194.542 879.893 200 899 200C918.107 200 932.844 194.542 943 184C953.156 173.458 958 159.023 958 141V60.0001Z" fill="var(--ui-primary)" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1000 60.0233L1020 60V77L1020 128V156.007L1020 181L1020 189.004C1020 192.938 1019.98 194.429 1017 197.001C1014.02 199.725 1009.56 200 1005 200H986.001V181.006L986 130.012V70.0215C986 66.1576 986.016 64.5494 989 62.023C991.819 59.6358 995.437 60.0233 1000 60.0233Z" fill="var(--ui-primary)" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
<template>
|
||||
<UPopover mode="hover" :ui="{ content: 'p-2' }">
|
||||
<template #default="{ open }">
|
||||
<UButton
|
||||
icon="i-heroicons-swatch-20-solid"
|
||||
color="gray"
|
||||
:variant="open ? 'soft' : 'ghost'"
|
||||
square
|
||||
aria-label="Color picker"
|
||||
:ui="{ leadingIcon: 'text-primary-500 dark:text-primary-400' }"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #content>
|
||||
<fieldset class="grid grid-cols-5 gap-px">
|
||||
<legend class="text-[11px] font-bold mb-1">
|
||||
Primary
|
||||
</legend>
|
||||
|
||||
<ColorPickerPill v-for="color in primaryColors" :key="color" :color="color" :selected="primary" @select="primary = color" />
|
||||
</fieldset>
|
||||
|
||||
<USeparator class="my-2" type="dashed" />
|
||||
|
||||
<fieldset class="grid grid-cols-5 gap-px">
|
||||
<legend class="text-[11px] font-bold mb-1">
|
||||
Gray
|
||||
</legend>
|
||||
|
||||
<ColorPickerPill v-for="color in grayColors" :key="color" :color="color" :selected="gray" @select="gray = color" />
|
||||
</fieldset>
|
||||
</template>
|
||||
</UPopover>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
// Computed
|
||||
|
||||
const primaryColors = ['red', 'orange', 'amber', 'yellow', 'lime', 'green', 'emerald', 'teal', 'cyan', 'sky', 'blue', 'indigo', 'violet', 'purple', 'fuchsia', 'pink', 'rose']
|
||||
const primary = computed({
|
||||
get() {
|
||||
return appConfig.ui.colors.primary
|
||||
},
|
||||
set(option) {
|
||||
appConfig.ui.colors.primary = option
|
||||
window.localStorage.setItem('nuxt-ui-primary', appConfig.ui.colors.primary)
|
||||
}
|
||||
})
|
||||
|
||||
const grayColors = ['slate', 'cool', 'zinc', 'neutral', 'stone']
|
||||
const gray = computed({
|
||||
get() {
|
||||
return appConfig.ui.colors.gray
|
||||
},
|
||||
set(option) {
|
||||
appConfig.ui.colors.gray = option
|
||||
window.localStorage.setItem('nuxt-ui-gray', appConfig.ui.colors.gray)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -1,24 +0,0 @@
|
||||
<template>
|
||||
<UTooltip :text="color" class="capitalize" :portal="false">
|
||||
<UButton
|
||||
color="gray"
|
||||
square
|
||||
:variant="color === selected ? 'soft' : 'ghost'"
|
||||
@click.stop.prevent="$emit('select')"
|
||||
>
|
||||
<span
|
||||
class="inline-block w-3 h-3 rounded-full"
|
||||
:class="`bg-[--color-light] dark:bg-[--color-dark]`"
|
||||
:style="{
|
||||
'--color-light': `var(--color-${color}-500)`,
|
||||
'--color-dark': `var(--color-${color}-400)`
|
||||
}"
|
||||
/>
|
||||
</UButton>
|
||||
</UTooltip>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps<{ color: string, selected: string }>()
|
||||
defineEmits(['select'])
|
||||
</script>
|
||||
@@ -2,10 +2,13 @@
|
||||
<script setup lang="ts">
|
||||
import json5 from 'json5'
|
||||
import { upperFirst, camelCase, kebabCase } from 'scule'
|
||||
import { hash } from 'ohash'
|
||||
import * as theme from '#build/ui'
|
||||
import { get, set } from '#ui/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
/** Override the slug taken from the route */
|
||||
slug?: string
|
||||
class?: any
|
||||
/** List of props to ignore in selection */
|
||||
ignore?: string[]
|
||||
@@ -13,6 +16,8 @@ const props = defineProps<{
|
||||
hide?: string[]
|
||||
/** List of props to externalize in script setup */
|
||||
external?: string[]
|
||||
/** List of props to use with `v-model` */
|
||||
model?: string[]
|
||||
/** List of items for each prop */
|
||||
items?: { [key: string]: string[] }
|
||||
props?: { [key: string]: any }
|
||||
@@ -32,24 +37,28 @@ const props = defineProps<{
|
||||
const route = useRoute()
|
||||
const { $prettier } = useNuxtApp()
|
||||
|
||||
const camelName = camelCase(route.params.slug[route.params.slug.length - 1])
|
||||
const camelName = camelCase(props.slug ?? route.params.slug?.[route.params.slug.length - 1] ?? '')
|
||||
const name = `U${upperFirst(camelName)}`
|
||||
|
||||
const componentProps = reactive({ ...(props.props || {}) })
|
||||
const componentEvents = reactive({
|
||||
...Object.fromEntries((props.model || []).map(key => [`onUpdate:${key}`, (e: any) => setComponentProp(key, e)])),
|
||||
...(componentProps.modelValue ? { [`onUpdate:modelValue`]: (e: any) => setComponentProp('modelValue', e) } : {})
|
||||
})
|
||||
|
||||
function getComponentProp(name: string) {
|
||||
return get(componentProps, name) || undefined
|
||||
return get(componentProps, name) ?? undefined
|
||||
}
|
||||
|
||||
function setComponentProp(name: string, value: any) {
|
||||
set(componentProps, name, value)
|
||||
}
|
||||
|
||||
const componentTheme = theme[camelName]
|
||||
const componentTheme = (theme as any)[camelName]
|
||||
const meta = await fetchComponentMeta(name as any)
|
||||
|
||||
function mapKeys(obj, parentKey = '') {
|
||||
return Object.entries(obj || {}).flatMap(([key, value]) => {
|
||||
function mapKeys(obj: object, parentKey = ''): any {
|
||||
return Object.entries(obj || {}).flatMap(([key, value]: [string, any]) => {
|
||||
if (typeof value === 'object' && !Array.isArray(value)) {
|
||||
return mapKeys(value, key)
|
||||
}
|
||||
@@ -63,21 +72,21 @@ function mapKeys(obj, parentKey = '') {
|
||||
const options = computed(() => {
|
||||
const keys = mapKeys(props.props || {})
|
||||
|
||||
return keys.map((key) => {
|
||||
return keys.map((key: string) => {
|
||||
const prop = meta?.meta?.props?.find((prop: any) => prop.name === key)
|
||||
const propItems = get(props.items, key, [])
|
||||
const items = propItems.length
|
||||
? propItems.map(item => ({
|
||||
? propItems.map((item: any) => ({
|
||||
value: item,
|
||||
label: item
|
||||
}))
|
||||
: prop?.type === 'boolean'
|
||||
: prop?.type === 'boolean' || prop?.type === 'boolean | undefined'
|
||||
? [{ value: true, label: 'true' }, { value: false, label: 'false' }]
|
||||
: Object.keys(componentTheme?.variants?.[key] || {}).map(variant => ({
|
||||
value: variant,
|
||||
label: variant,
|
||||
chip: key.toLowerCase().endsWith('color') ? { color: variant } : undefined
|
||||
})).filter(variant => key.toLowerCase().endsWith('color') ? !['error'].includes(variant.value) : true)
|
||||
}))
|
||||
|
||||
return {
|
||||
name: key,
|
||||
@@ -119,6 +128,11 @@ const code = computed(() => {
|
||||
continue
|
||||
}
|
||||
|
||||
if (props.model?.includes(key)) {
|
||||
code += ` v-model:${key}="${key}"`
|
||||
continue
|
||||
}
|
||||
|
||||
if (value === undefined || value === null || value === '' || props.hide?.includes(key)) {
|
||||
continue
|
||||
}
|
||||
@@ -177,7 +191,7 @@ const code = computed(() => {
|
||||
return code
|
||||
})
|
||||
|
||||
const { data: ast } = await useAsyncData(`component-code-${name}-${JSON.stringify({ props: componentProps, slots: props.slots })}`, async () => {
|
||||
const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props: componentProps, slots: props.slots })}`, async () => {
|
||||
if (!props.prettier) {
|
||||
return parseMarkdown(code.value)
|
||||
}
|
||||
@@ -200,15 +214,15 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${JSON.stringif
|
||||
<template>
|
||||
<div class="my-5">
|
||||
<div>
|
||||
<div v-if="options.length" class="flex items-center gap-2.5 border border-gray-300 dark:border-gray-700 border-b-0 relative rounded-t-md px-4 py-2.5 overflow-x-auto">
|
||||
<div v-if="options.length" class="flex items-center gap-2.5 border border-[--ui-color-neutral-200] dark:border-[--ui-color-neutral-700] border-b-0 relative rounded-t-[calc(var(--ui-radius)*1.5)] px-4 py-2.5 overflow-x-auto">
|
||||
<template v-for="option in options" :key="option.name">
|
||||
<UFormField
|
||||
:label="option.label"
|
||||
size="sm"
|
||||
class="inline-flex ring ring-gray-300 dark:ring-gray-700 rounded"
|
||||
class="inline-flex ring ring-[--ui-border-accented] rounded"
|
||||
:ui="{
|
||||
wrapper: 'bg-gray-50 dark:bg-gray-800/50 rounded-l flex border-r border-gray-300 dark:border-gray-700',
|
||||
label: 'text-gray-500 dark:text-gray-400 px-2 py-1.5',
|
||||
wrapper: 'bg-[--ui-bg-elevated]/50 rounded-l flex border-r border-[--ui-border-accented]',
|
||||
label: 'text-[--ui-text-muted] px-2 py-1.5',
|
||||
container: 'mt-0'
|
||||
}"
|
||||
>
|
||||
@@ -217,10 +231,10 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${JSON.stringif
|
||||
:model-value="getComponentProp(option.name)"
|
||||
:items="option.items"
|
||||
value-key="value"
|
||||
color="gray"
|
||||
color="neutral"
|
||||
variant="soft"
|
||||
class="rounded rounded-l-none min-w-12"
|
||||
:search="false"
|
||||
:search-input="false"
|
||||
:class="[option.name.toLowerCase().endsWith('color') && 'pl-6']"
|
||||
:ui="{ itemLeadingChip: 'size-2' }"
|
||||
@update:model-value="setComponentProp(option.name, $event)"
|
||||
@@ -240,7 +254,7 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${JSON.stringif
|
||||
v-else
|
||||
:type="option.type?.includes('number') ? 'number' : 'text'"
|
||||
:model-value="getComponentProp(option.name)"
|
||||
color="gray"
|
||||
color="neutral"
|
||||
variant="soft"
|
||||
:ui="{ base: 'rounded rounded-l-none min-w-12' }"
|
||||
@update:model-value="setComponentProp(option.name, $event)"
|
||||
@@ -249,11 +263,11 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${JSON.stringif
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="flex border border-b-0 border-gray-300 dark:border-gray-700 relative p-4 z-[1]" :class="[!options.length && 'rounded-t-md', props.class]">
|
||||
<component :is="name" v-bind="componentProps" @update:model-value="!!componentProps.modelValue && setComponentProp('modelValue', $event)">
|
||||
<div class="flex justify-center border border-b-0 border-[--ui-color-neutral-200] dark:border-[--ui-color-neutral-700] relative p-4 z-[1]" :class="[!options.length && 'rounded-t-[calc(var(--ui-radius)*1.5)]', props.class]">
|
||||
<component :is="name" v-bind="{ ...componentProps, ...componentEvents }">
|
||||
<template v-for="slot in Object.keys(slots || {})" :key="slot" #[slot]>
|
||||
<ContentSlot :name="slot" unwrap="p">
|
||||
{{ slots[slot] }}
|
||||
{{ slots?.[slot] }}
|
||||
</ContentSlot>
|
||||
</template>
|
||||
</component>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { upperFirst, camelCase } from 'scule'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const camelName = camelCase(route.params.slug[route.params.slug.length - 1])
|
||||
const camelName = camelCase(route.params.slug?.[route.params.slug.length - 1] ?? '')
|
||||
const name = `U${upperFirst(camelName)}`
|
||||
|
||||
const meta = await fetchComponentMeta(name as any)
|
||||
@@ -22,7 +22,7 @@ const meta = await fetchComponentMeta(name as any)
|
||||
</ProseTr>
|
||||
</ProseThead>
|
||||
<ProseTbody>
|
||||
<ProseTr v-for="event in meta.meta.events" :key="event.name">
|
||||
<ProseTr v-for="event in (meta?.meta?.events || [])" :key="event.name">
|
||||
<ProseTd>
|
||||
<ProseCodeInline>
|
||||
{{ event.name }}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { camelCase } from 'scule'
|
||||
import { get, set } from '#ui/utils'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
name: string
|
||||
@@ -21,10 +22,33 @@ const props = withDefaults(defineProps<{
|
||||
* @defaultValue true
|
||||
*/
|
||||
preview?: boolean
|
||||
|
||||
/**
|
||||
* Whether to show the source code
|
||||
* @defaultValue true
|
||||
*/
|
||||
source?: boolean
|
||||
|
||||
/**
|
||||
* A list of variable props to link to the component.
|
||||
*/
|
||||
options?: Array<{
|
||||
alias?: string
|
||||
name: string
|
||||
label: string
|
||||
items?: any[]
|
||||
default: any
|
||||
multiple?: boolean
|
||||
}>
|
||||
}>(), {
|
||||
preview: true
|
||||
preview: true,
|
||||
source: true
|
||||
})
|
||||
|
||||
const slots = defineSlots<{
|
||||
options(props?: {}): any
|
||||
}>()
|
||||
|
||||
const { $prettier } = useNuxtApp()
|
||||
|
||||
const camelName = camelCase(props.name)
|
||||
@@ -71,16 +95,83 @@ const { data: ast } = await useAsyncData(`component-example-${camelName}`, async
|
||||
|
||||
return parseMarkdown(formatted)
|
||||
}, { watch: [code] })
|
||||
|
||||
const optionsValues = ref(props.options?.reduce((acc, option) => {
|
||||
if (option.name) {
|
||||
acc[option.alias || option.name] = option.default
|
||||
}
|
||||
if (option.name.toLowerCase().endsWith('color') && option.items?.length) {
|
||||
option.items = option.items.map((item: any) => ({
|
||||
label: item,
|
||||
value: item,
|
||||
chip: { color: item }
|
||||
}))
|
||||
}
|
||||
return acc
|
||||
}, {} as Record<string, any>) || {})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="my-5">
|
||||
<div v-if="preview">
|
||||
<div class="flex border border-b-0 border-gray-300 dark:border-gray-700 relative p-4 rounded-t-md z-[1]" :class="[props.class]">
|
||||
<component :is="camelName" v-bind="componentProps" />
|
||||
<div class="border border-[--ui-color-neutral-200] dark:border-[--ui-color-neutral-700] relative z-[1]" :class="[{ 'border-b-0 rounded-t-[calc(var(--ui-radius)*1.5)]': props.source, 'rounded-[calc(var(--ui-radius)*1.5)]': !props.source }]">
|
||||
<div v-if="props.options?.length || !!slots.options" class="flex gap-4 p-4 border-b border-[--ui-color-neutral-200] dark:border-[--ui-color-neutral-700]">
|
||||
<slot name="options" />
|
||||
|
||||
<UFormField
|
||||
v-for="option in props.options"
|
||||
:key="option.name"
|
||||
:label="option.label"
|
||||
:name="option.name"
|
||||
size="sm"
|
||||
class="inline-flex ring ring-[--ui-border-accented] rounded"
|
||||
:ui="{
|
||||
wrapper: 'bg-[--ui-bg-elevated]/50 rounded-l flex border-r border-[--ui-border-accented]',
|
||||
label: 'text-[--ui-text-muted] px-2 py-1.5',
|
||||
container: 'mt-0'
|
||||
}"
|
||||
>
|
||||
<USelectMenu
|
||||
v-if="option.items?.length"
|
||||
:model-value="get(optionsValues, option.name)"
|
||||
:items="option.items"
|
||||
:search-input="false"
|
||||
:value-key="option.name.toLowerCase().endsWith('color') ? 'value' : undefined"
|
||||
color="neutral"
|
||||
variant="soft"
|
||||
class="rounded rounded-l-none min-w-12"
|
||||
:multiple="option.multiple"
|
||||
:class="[option.name.toLowerCase().endsWith('color') && 'pl-6']"
|
||||
:ui="{ itemLeadingChip: 'size-2' }"
|
||||
@update:model-value="set(optionsValues, option.name, $event)"
|
||||
>
|
||||
<template v-if="option.name.toLowerCase().endsWith('color')" #leading="{ modelValue, ui }">
|
||||
<UChip
|
||||
inset
|
||||
standalone
|
||||
:color="(modelValue as any)"
|
||||
:size="ui.itemLeadingChipSize()"
|
||||
class="size-2"
|
||||
/>
|
||||
</template>
|
||||
</USelectMenu>
|
||||
<UInput
|
||||
v-else
|
||||
:model-value="get(optionsValues, option.name)"
|
||||
color="neutral"
|
||||
variant="soft"
|
||||
:ui="{ base: 'rounded rounded-l-none min-w-12' }"
|
||||
@update:model-value="set(optionsValues, option.name, $event)"
|
||||
/>
|
||||
</UFormField>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center p-4" :class="props.class">
|
||||
<component :is="camelName" v-bind="{ ...componentProps, ...optionsValues }" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<MDCRenderer v-if="ast" :body="ast.body" :data="ast.data" class="[&_pre]:!rounded-t-none [&_div.my-5]:!mt-0" />
|
||||
<MDCRenderer v-if="ast && props.source" :body="ast.body" :data="ast.data" class="[&_pre]:!rounded-t-none [&_div.my-5]:!mt-0" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -9,10 +9,10 @@ const props = defineProps<{
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const camelName = camelCase(route.params.slug[route.params.slug.length - 1])
|
||||
const camelName = camelCase(route.params.slug?.[route.params.slug.length - 1] ?? '')
|
||||
const name = `U${upperFirst(camelName)}`
|
||||
|
||||
const componentTheme = theme[camelName]
|
||||
const componentTheme = (theme as any)[camelName]
|
||||
const meta = await fetchComponentMeta(name as any)
|
||||
|
||||
const metaProps: ComputedRef<ComponentMeta['props']> = computed(() => {
|
||||
@@ -43,6 +43,8 @@ const metaProps: ComputedRef<ComponentMeta['props']> = computed(() => {
|
||||
if (b.name === 'ui') {
|
||||
return -1
|
||||
}
|
||||
|
||||
return 0
|
||||
})
|
||||
})
|
||||
</script>
|
||||
@@ -75,7 +77,7 @@ const metaProps: ComputedRef<ComponentMeta['props']> = computed(() => {
|
||||
<ProseTd>
|
||||
<HighlightInlineType v-if="prop.type" :type="prop.type" />
|
||||
|
||||
<MDC v-if="prop.description" :value="prop.description" class="text-gray-600 dark:text-gray-300 mt-1" />
|
||||
<MDC v-if="prop.description" :value="prop.description" class="text-[--ui-text-toned] mt-1" />
|
||||
|
||||
<ComponentPropsSchema v-if="prop.schema" :prop="prop" :ignore="ignore" />
|
||||
</ProseTd>
|
||||
|
||||
@@ -6,7 +6,7 @@ const props = defineProps<{
|
||||
ignore?: string[]
|
||||
}>()
|
||||
|
||||
function getSchemaProps(schema: PropertyMeta['schema']) {
|
||||
function getSchemaProps(schema: PropertyMeta['schema']): any {
|
||||
if (!schema || typeof schema === 'string' || !schema.schema) {
|
||||
return []
|
||||
}
|
||||
@@ -15,12 +15,12 @@ function getSchemaProps(schema: PropertyMeta['schema']) {
|
||||
return Object.values(schema.schema).filter(prop => !props.ignore?.includes(prop.name))
|
||||
}
|
||||
|
||||
return (Array.isArray(schema.schema) ? schema.schema : Object.values(schema.schema)).flatMap(getSchemaProps)
|
||||
return (Array.isArray(schema.schema) ? schema.schema : Object.values(schema.schema)).flatMap(getSchemaProps as any)
|
||||
}
|
||||
|
||||
const schemaProps = computed(() => {
|
||||
return getSchemaProps(props.prop.schema).map((prop) => {
|
||||
const defaultValue = prop.default ?? prop.tags?.find(tag => tag.name === 'defaultValue')?.text
|
||||
return getSchemaProps(props.prop.schema).map((prop: any) => {
|
||||
const defaultValue = prop.default ?? prop.tags?.find((tag: any) => tag.name === 'defaultValue')?.text
|
||||
let description = prop.description
|
||||
if (defaultValue) {
|
||||
description = description ? `${description} Defaults to \`${defaultValue}\`{lang="ts-type"}.` : `Defaults to \`${defaultValue}\`{lang="ts-type"}.`
|
||||
@@ -40,7 +40,7 @@ const schemaProps = computed(() => {
|
||||
<ProseLi v-for="schemaProp in schemaProps" :key="schemaProp.name">
|
||||
<HighlightInlineType :type="`${schemaProp.name}${schemaProp.required === false ? '?' : ''}: ${schemaProp.type}`" />
|
||||
|
||||
<MDC v-if="schemaProp.description" :value="schemaProp.description" class="text-gray-500 dark:text-gray-400 my-1" />
|
||||
<MDC v-if="schemaProp.description" :value="schemaProp.description" class="text-[--ui-text-muted] my-1" />
|
||||
</ProseLi>
|
||||
</ProseUl>
|
||||
</Collapsible>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { upperFirst, camelCase } from 'scule'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const camelName = camelCase(route.params.slug[route.params.slug.length - 1])
|
||||
const camelName = camelCase(route.params.slug?.[route.params.slug.length - 1] ?? '')
|
||||
const name = `U${upperFirst(camelName)}`
|
||||
|
||||
const meta = await fetchComponentMeta(name as any)
|
||||
@@ -22,7 +22,7 @@ const meta = await fetchComponentMeta(name as any)
|
||||
</ProseTr>
|
||||
</ProseThead>
|
||||
<ProseTbody>
|
||||
<ProseTr v-for="slot in meta.meta.slots" :key="slot.name">
|
||||
<ProseTr v-for="slot in (meta?.meta?.slots || [])" :key="slot.name">
|
||||
<ProseTd>
|
||||
<ProseCodeInline>
|
||||
{{ slot.name }}
|
||||
@@ -31,7 +31,7 @@ const meta = await fetchComponentMeta(name as any)
|
||||
<ProseTd>
|
||||
<HighlightInlineType v-if="slot.type" :type="slot.type" />
|
||||
|
||||
<MDC v-if="slot.description" :value="slot.description" class="text-gray-600 dark:text-gray-300 mt-1" />
|
||||
<MDC v-if="slot.description" :value="slot.description" class="text-[--ui-text-toned] mt-1" />
|
||||
</ProseTd>
|
||||
</ProseTr>
|
||||
</ProseTbody>
|
||||
|
||||
@@ -5,15 +5,15 @@ import * as theme from '#build/ui'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const name = camelCase(route.params.slug[route.params.slug.length - 1])
|
||||
const name = camelCase(route.params.slug?.[route.params.slug.length - 1] ?? '')
|
||||
|
||||
const strippedCompoundVariants = ref(false)
|
||||
|
||||
function stripCompoundVariants(component) {
|
||||
if (component.compoundVariants) {
|
||||
function stripCompoundVariants(component?: any) {
|
||||
if (component?.compoundVariants) {
|
||||
component.compoundVariants = component.compoundVariants.filter((compoundVariant: any) => {
|
||||
if (compoundVariant.color) {
|
||||
if (!['primary', 'gray'].includes(compoundVariant.color)) {
|
||||
if (!['primary', 'neutral'].includes(compoundVariant.color)) {
|
||||
strippedCompoundVariants.value = true
|
||||
|
||||
return false
|
||||
@@ -21,7 +21,7 @@ function stripCompoundVariants(component) {
|
||||
}
|
||||
|
||||
if (compoundVariant.highlightColor) {
|
||||
if (!['primary', 'gray'].includes(compoundVariant.highlightColor)) {
|
||||
if (!['primary', 'neutral'].includes(compoundVariant.highlightColor)) {
|
||||
strippedCompoundVariants.value = true
|
||||
|
||||
return false
|
||||
@@ -38,7 +38,7 @@ function stripCompoundVariants(component) {
|
||||
const component = computed(() => {
|
||||
return {
|
||||
ui: {
|
||||
[name]: stripCompoundVariants(theme[name])
|
||||
[name]: stripCompoundVariants((theme as any)[name])
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -53,11 +53,11 @@ export default defineAppConfig(${json5.stringify(component.value, null, 2).repla
|
||||
::
|
||||
|
||||
${strippedCompoundVariants.value
|
||||
? `
|
||||
? `
|
||||
::callout{icon="i-simple-icons-github" to="https://github.com/nuxt/ui/blob/v3/src/theme/${name}.ts"}
|
||||
Some colors in \`compoundVariants\` are omitted for readability. Check out the source code on GitHub.
|
||||
::`
|
||||
: ''}
|
||||
: ''}
|
||||
`
|
||||
|
||||
return parseMarkdown(md)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { murmurHash } from 'ohash'
|
||||
|
||||
const props = defineProps<{
|
||||
type: string
|
||||
}>()
|
||||
@@ -21,7 +23,7 @@ const type = computed(() => {
|
||||
return type
|
||||
})
|
||||
|
||||
const { data: ast } = await useAsyncData(`hightlight-inline-code-${type.value}`, () => parseMarkdown(`\`${type.value}\`{lang="ts-type"}`))
|
||||
const { data: ast } = await useAsyncData(`hightlight-inline-code-${murmurHash(type.value)}`, () => parseMarkdown(`\`${type.value}\`{lang="ts-type"}`))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import json5 from 'json5'
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const icons = computed(() => {
|
||||
return {
|
||||
ui: {
|
||||
icons: appConfig.ui.icons
|
||||
}
|
||||
}
|
||||
})
|
||||
import icons from '../../../../src/theme/icons'
|
||||
|
||||
const { data: ast } = await useAsyncData(`icons-theme`, async () => {
|
||||
const md = `
|
||||
\`\`\`ts [app.config.ts]
|
||||
export default defineAppConfig(${json5.stringify(icons.value, null, 2).replace(/,([ |\t\n]+[}|\])])/g, '$1')})
|
||||
export default defineAppConfig(${json5.stringify({
|
||||
ui: {
|
||||
icons
|
||||
}
|
||||
}, null, 2).replace(/,([ |\t\n]+[}|\])])/g, '$1')})
|
||||
\`\`\`\
|
||||
`
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="relative overflow-hidden rounded border border-dashed border-gray-400 dark:border-gray-500 opacity-75 px-4 flex items-center justify-center">
|
||||
<svg class="absolute inset-0 h-full w-full stroke-gray-900/10 dark:stroke-white/10" fill="none">
|
||||
<div class="relative overflow-hidden rounded-[--ui-radius] border border-dashed border-[--ui-border-accented] opacity-75 px-4 flex items-center justify-center">
|
||||
<svg class="absolute inset-0 h-full w-full stroke-[--ui-border-inverted]/10" fill="none">
|
||||
<defs>
|
||||
<pattern
|
||||
id="pattern-5c1e4f0e-62d5-498b-8ff0-cf77bb448c8e"
|
||||
|
||||
@@ -18,7 +18,7 @@ const items = [
|
||||
<template>
|
||||
<UAccordion :items="items">
|
||||
<template #content="{ item }">
|
||||
<p class="pb-3.5 text-sm text-gray-500 dark:text-gray-400">
|
||||
<p class="pb-3.5 text-sm text-[--ui-text-muted]">
|
||||
This is the {{ item.label }} panel.
|
||||
</p>
|
||||
</template>
|
||||
|
||||
@@ -9,7 +9,7 @@ const items = [
|
||||
label: 'Colors',
|
||||
icon: 'i-heroicons-swatch',
|
||||
slot: 'colors',
|
||||
content: 'Choose a primary and a gray color from your Tailwind CSS theme.'
|
||||
content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
|
||||
},
|
||||
{
|
||||
label: 'Components',
|
||||
@@ -22,7 +22,7 @@ const items = [
|
||||
<template>
|
||||
<UAccordion :items="items">
|
||||
<template #colors="{ item }">
|
||||
<p class="text-sm pb-3.5 text-primary-500 dark:text-primary-400">
|
||||
<p class="text-sm pb-3.5 text-[--ui-primary]">
|
||||
{{ item.content }}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
@@ -8,7 +8,7 @@ const items = [
|
||||
{
|
||||
label: 'Colors',
|
||||
icon: 'i-heroicons-swatch',
|
||||
content: 'Choose a primary and a gray color from your Tailwind CSS theme.'
|
||||
content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
|
||||
},
|
||||
{
|
||||
label: 'Components',
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
<template>
|
||||
<UAvatarGroup>
|
||||
<UChip inset color="green">
|
||||
<UChip inset color="success">
|
||||
<UAvatar
|
||||
src="https://avatars.githubusercontent.com/u/739984?v=4"
|
||||
alt="Benjamin Canac"
|
||||
/>
|
||||
</UChip>
|
||||
|
||||
<UChip inset color="amber">
|
||||
<UChip inset color="warning">
|
||||
<UAvatar
|
||||
src="https://avatars.githubusercontent.com/u/25613751?v=4"
|
||||
alt="Romain Hamel"
|
||||
/>
|
||||
</UChip>
|
||||
|
||||
<UChip inset color="red">
|
||||
<UChip inset color="error">
|
||||
<UAvatar
|
||||
src="https://avatars.githubusercontent.com/u/19751938?v=4"
|
||||
alt="Neil Richter"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<ULink
|
||||
to="https://github.com/benjamincanac"
|
||||
target="_blank"
|
||||
class="hover:ring-primary-500 dark:hover:ring-primary-400 transition"
|
||||
class="hover:ring-[--ui-primary] transition"
|
||||
raw
|
||||
>
|
||||
<UAvatar
|
||||
@@ -15,7 +15,7 @@
|
||||
<ULink
|
||||
to="https://github.com/romhml"
|
||||
target="_blank"
|
||||
class="hover:ring-primary-500 dark:hover:ring-primary-400 transition"
|
||||
class="hover:ring-[--ui-primary] transition"
|
||||
raw
|
||||
>
|
||||
<UAvatar
|
||||
@@ -27,7 +27,7 @@
|
||||
<ULink
|
||||
to="https://github.com/noook"
|
||||
target="_blank"
|
||||
class="hover:ring-primary-500 dark:hover:ring-primary-400 transition"
|
||||
class="hover:ring-[--ui-primary] transition"
|
||||
raw
|
||||
>
|
||||
<UAvatar
|
||||
|
||||
@@ -25,7 +25,7 @@ const items = [{
|
||||
<UBreadcrumb :items="items">
|
||||
<template #dropdown="{ item }">
|
||||
<UDropdownMenu :items="item.children">
|
||||
<UButton :icon="item.icon" color="gray" variant="link" class="p-0.5" />
|
||||
<UButton :icon="item.icon" color="neutral" variant="link" class="p-0.5" />
|
||||
</UDropdownMenu>
|
||||
</template>
|
||||
</UBreadcrumb>
|
||||
|
||||
@@ -14,7 +14,7 @@ const items = [{
|
||||
<template>
|
||||
<UBreadcrumb :items="items">
|
||||
<template #separator>
|
||||
<span class="mx-2 text-gray-500 dark:text-gray-400">/</span>
|
||||
<span class="mx-2 text-[--ui-text-muted]">/</span>
|
||||
</template>
|
||||
</UBreadcrumb>
|
||||
</template>
|
||||
|
||||
@@ -20,11 +20,11 @@ const items = [{
|
||||
|
||||
<template>
|
||||
<UButtonGroup>
|
||||
<UButton color="gray" variant="subtle" label="Settings" />
|
||||
<UButton color="neutral" variant="subtle" label="Settings" />
|
||||
|
||||
<UDropdownMenu :items="items">
|
||||
<UButton
|
||||
color="gray"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
icon="i-heroicons-chevron-down-20-solid"
|
||||
/>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<UButtonGroup>
|
||||
<UInput color="gray" variant="outline" placeholder="Enter token" />
|
||||
<UInput color="neutral" variant="outline" placeholder="Enter token" />
|
||||
|
||||
<UTooltip text="Copy to clipboard">
|
||||
<UButton
|
||||
color="gray"
|
||||
color="neutral"
|
||||
variant="subtle"
|
||||
icon="i-heroicons-clipboard-document"
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
async function onClick() {
|
||||
return new Promise<void>(res => setTimeout(res, 1000))
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UButton loading-auto @click="onClick">
|
||||
Button
|
||||
</UButton>
|
||||
</template>
|
||||
@@ -0,0 +1,23 @@
|
||||
<script setup lang="ts">
|
||||
const state = reactive({ fullName: '' })
|
||||
|
||||
async function onSubmit() {
|
||||
return new Promise<void>(res => setTimeout(res, 1000))
|
||||
}
|
||||
|
||||
async function validate(data: Partial<typeof state>) {
|
||||
if (!data.fullName?.length) return [{ name: 'fullName', message: 'Required' }]
|
||||
return []
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UForm :state="state" :validate="validate" @submit="onSubmit">
|
||||
<UFormField name="fullName" label="Full name">
|
||||
<UInput v-model="state.fullName" />
|
||||
</UFormField>
|
||||
<UButton type="submit" class="mt-2" loading-auto>
|
||||
Submit
|
||||
</UButton>
|
||||
</UForm>
|
||||
</template>
|
||||
@@ -0,0 +1,16 @@
|
||||
<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'
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCarousel v-slot="{ item }" arrows :items="items" class="w-full max-w-xs mx-auto">
|
||||
<img :src="item" width="320" height="320" class="rounded-lg">
|
||||
</UCarousel>
|
||||
</template>
|
||||
@@ -0,0 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
const items = [
|
||||
'https://picsum.photos/640/640?random=1',
|
||||
'https://picsum.photos/640/320?random=2',
|
||||
'https://picsum.photos/640/640?random=3',
|
||||
'https://picsum.photos/640/320?random=4',
|
||||
'https://picsum.photos/640/640?random=5',
|
||||
'https://picsum.photos/640/320?random=6'
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCarousel
|
||||
v-slot="{ item }"
|
||||
auto-height
|
||||
arrows
|
||||
dots
|
||||
:items="items"
|
||||
:ui="{
|
||||
container: 'transition-[height]',
|
||||
controls: 'absolute -top-8 inset-x-12',
|
||||
dots: '-top-7',
|
||||
dot: 'w-6 h-1'
|
||||
}"
|
||||
class="w-full max-w-xs mx-auto"
|
||||
>
|
||||
<img :src="item" width="320" height="320" class="rounded-lg">
|
||||
</UCarousel>
|
||||
</template>
|
||||
@@ -0,0 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
const items = [
|
||||
'https://picsum.photos/468/468?random=1',
|
||||
'https://picsum.photos/468/468?random=2',
|
||||
'https://picsum.photos/468/468?random=3',
|
||||
'https://picsum.photos/468/468?random=4',
|
||||
'https://picsum.photos/468/468?random=5',
|
||||
'https://picsum.photos/468/468?random=6'
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCarousel
|
||||
v-slot="{ item }"
|
||||
loop
|
||||
dots
|
||||
arrows
|
||||
auto-scroll
|
||||
:items="items"
|
||||
:ui="{ item: 'basis-1/3' }"
|
||||
>
|
||||
<img :src="item" width="234" height="234" class="rounded-lg">
|
||||
</UCarousel>
|
||||
</template>
|
||||
@@ -0,0 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
const items = [
|
||||
'https://picsum.photos/468/468?random=1',
|
||||
'https://picsum.photos/468/468?random=2',
|
||||
'https://picsum.photos/468/468?random=3',
|
||||
'https://picsum.photos/468/468?random=4',
|
||||
'https://picsum.photos/468/468?random=5',
|
||||
'https://picsum.photos/468/468?random=6'
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCarousel
|
||||
v-slot="{ item }"
|
||||
loop
|
||||
arrows
|
||||
dots
|
||||
:autoplay="{ delay: 2000 }"
|
||||
:items="items"
|
||||
:ui="{ item: 'basis-1/3' }"
|
||||
>
|
||||
<img :src="item" width="234" height="234" class="rounded-lg">
|
||||
</UCarousel>
|
||||
</template>
|
||||
@@ -0,0 +1,25 @@
|
||||
<script setup lang="ts">
|
||||
const items = [
|
||||
'https://picsum.photos/528/528?random=1',
|
||||
'https://picsum.photos/528/528?random=2',
|
||||
'https://picsum.photos/528/528?random=3',
|
||||
'https://picsum.photos/528/528?random=4',
|
||||
'https://picsum.photos/528/528?random=5',
|
||||
'https://picsum.photos/528/528?random=6'
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCarousel
|
||||
v-slot="{ item }"
|
||||
class-names
|
||||
arrows
|
||||
:items="items"
|
||||
:ui="{
|
||||
item: 'basis-[70%] transition-opacity [&:not(.is-snapped)]:opacity-10'
|
||||
}"
|
||||
class="mx-auto max-w-sm"
|
||||
>
|
||||
<img :src="item" width="264" height="264" class="rounded-lg">
|
||||
</UCarousel>
|
||||
</template>
|
||||
@@ -0,0 +1,16 @@
|
||||
<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'
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCarousel v-slot="{ item }" dots :items="items" class="w-full max-w-xs mx-auto">
|
||||
<img :src="item" width="320" height="320" class="rounded-lg">
|
||||
</UCarousel>
|
||||
</template>
|
||||
@@ -0,0 +1,16 @@
|
||||
<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'
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCarousel v-slot="{ item }" dots :items="items" :ui="{ item: 'basis-1/3' }">
|
||||
<img :src="item" width="320" height="320" class="rounded-lg">
|
||||
</UCarousel>
|
||||
</template>
|
||||
@@ -0,0 +1,23 @@
|
||||
<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'
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCarousel
|
||||
v-slot="{ item }"
|
||||
fade
|
||||
arrows
|
||||
dots
|
||||
:items="items"
|
||||
class="w-full max-w-xs mx-auto"
|
||||
>
|
||||
<img :src="item" width="320" height="320" class="rounded-lg">
|
||||
</UCarousel>
|
||||
</template>
|
||||
@@ -0,0 +1,16 @@
|
||||
<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'
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCarousel v-slot="{ item }" :items="items" class="w-full max-w-xs mx-auto">
|
||||
<img :src="item" width="320" height="320" class="rounded-lg">
|
||||
</UCarousel>
|
||||
</template>
|
||||
@@ -0,0 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
const items = [
|
||||
'https://picsum.photos/468/468?random=1',
|
||||
'https://picsum.photos/468/468?random=2',
|
||||
'https://picsum.photos/468/468?random=3',
|
||||
'https://picsum.photos/468/468?random=4',
|
||||
'https://picsum.photos/468/468?random=5',
|
||||
'https://picsum.photos/468/468?random=6'
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCarousel v-slot="{ item }" :items="items" :ui="{ item: 'basis-1/3' }">
|
||||
<img :src="item" width="234" height="234" class="rounded-lg">
|
||||
</UCarousel>
|
||||
</template>
|
||||
@@ -0,0 +1,22 @@
|
||||
<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'
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCarousel
|
||||
v-slot="{ item }"
|
||||
orientation="vertical"
|
||||
:items="items"
|
||||
class="w-full max-w-xs mx-auto"
|
||||
:ui="{ container: 'h-[336px]' }"
|
||||
>
|
||||
<img :src="item" width="320" height="320" class="rounded-lg">
|
||||
</UCarousel>
|
||||
</template>
|
||||
@@ -0,0 +1,23 @@
|
||||
<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'
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCarousel
|
||||
v-slot="{ item }"
|
||||
arrows
|
||||
:prev="{ color: 'primary' }"
|
||||
:next="{ variant: 'solid' }"
|
||||
:items="items"
|
||||
class="w-full max-w-xs mx-auto"
|
||||
>
|
||||
<img :src="item" width="320" height="320" class="rounded-lg">
|
||||
</UCarousel>
|
||||
</template>
|
||||
@@ -0,0 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
prevIcon?: string
|
||||
nextIcon?: string
|
||||
}>()
|
||||
|
||||
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'
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCarousel
|
||||
v-slot="{ item }"
|
||||
arrows
|
||||
:prev-icon="prevIcon"
|
||||
:next-icon="nextIcon"
|
||||
:items="items"
|
||||
class="w-full max-w-xs mx-auto"
|
||||
>
|
||||
<img :src="item" width="320" height="320" class="rounded-lg">
|
||||
</UCarousel>
|
||||
</template>
|
||||
@@ -0,0 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
const items = [
|
||||
'https://picsum.photos/468/468?random=1',
|
||||
'https://picsum.photos/468/468?random=2',
|
||||
'https://picsum.photos/468/468?random=3',
|
||||
'https://picsum.photos/468/468?random=4',
|
||||
'https://picsum.photos/468/468?random=5',
|
||||
'https://picsum.photos/468/468?random=6'
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCarousel
|
||||
v-slot="{ item }"
|
||||
loop
|
||||
wheel-gestures
|
||||
:items="items"
|
||||
:ui="{ item: 'basis-1/3' }"
|
||||
>
|
||||
<img :src="item" width="234" height="234" class="rounded-lg">
|
||||
</UCarousel>
|
||||
</template>
|
||||
@@ -2,19 +2,16 @@
|
||||
const statuses = ['online', 'away', 'busy', 'offline']
|
||||
const status = ref(statuses[0])
|
||||
|
||||
const color = computed(() => ({
|
||||
online: 'green',
|
||||
away: 'amber',
|
||||
busy: 'red',
|
||||
offline: 'gray'
|
||||
})[status.value] as any)
|
||||
const color = computed(() => status.value ? { online: 'success', away: 'warning', busy: 'error', offline: 'neutral' }[status.value] as any : 'online')
|
||||
|
||||
const show = computed(() => status.value !== 'offline')
|
||||
|
||||
// Note: This is for demonstration purposes only. Don't do this at home.
|
||||
onMounted(() => {
|
||||
setInterval(() => {
|
||||
status.value = statuses[(statuses.indexOf(status.value) + 1) % statuses.length]
|
||||
if (status.value) {
|
||||
status.value = statuses[(statuses.indexOf(status.value) + 1) % statuses.length]
|
||||
}
|
||||
}, 1000)
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<UButton
|
||||
class="group"
|
||||
label="Open"
|
||||
color="gray"
|
||||
color="neutral"
|
||||
variant="subtle"
|
||||
trailing-icon="i-heroicons-chevron-down-20-solid"
|
||||
:ui="{
|
||||
|
||||
@@ -10,7 +10,7 @@ defineShortcuts({
|
||||
<UCollapsible v-model:open="open" class="w-48">
|
||||
<UButton
|
||||
label="Open"
|
||||
color="gray"
|
||||
color="neutral"
|
||||
variant="subtle"
|
||||
trailing-icon="i-heroicons-chevron-down-20-solid"
|
||||
block
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<script setup lang="ts">
|
||||
const groups = [{
|
||||
id: 'settings',
|
||||
items: [{
|
||||
label: 'Profile',
|
||||
icon: 'i-heroicons-user',
|
||||
kbds: ['meta', 'P']
|
||||
}, {
|
||||
label: 'Billing',
|
||||
icon: 'i-heroicons-credit-card',
|
||||
kbds: ['meta', 'B'],
|
||||
slot: 'billing'
|
||||
}, {
|
||||
label: 'Notifications',
|
||||
icon: 'i-heroicons-bell'
|
||||
}, {
|
||||
label: 'Security',
|
||||
icon: 'i-heroicons-lock-closed'
|
||||
}]
|
||||
}, {
|
||||
id: 'users',
|
||||
label: 'Users',
|
||||
slot: 'users',
|
||||
items: [
|
||||
{ id: 1, label: 'Durward Reynolds' },
|
||||
{ id: 2, label: 'Kenton Towne' },
|
||||
{ id: 3, label: 'Therese Wunsch' },
|
||||
{ id: 4, label: 'Benedict Kessler' },
|
||||
{ id: 5, label: 'Katelyn Rohan' }
|
||||
]
|
||||
}]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCommandPalette :groups="groups" class="flex-1 h-80">
|
||||
<template #users-leading="{ index }">
|
||||
<UAvatar :src="`https://i.pravatar.cc/120?img=${index}`" size="2xs" />
|
||||
</template>
|
||||
|
||||
<template #billing-label="{ item }">
|
||||
{{ item.label }}
|
||||
|
||||
<UBadge variant="subtle" size="sm">
|
||||
50% off
|
||||
</UBadge>
|
||||
</template>
|
||||
</UCommandPalette>
|
||||
</template>
|
||||
@@ -0,0 +1,25 @@
|
||||
<script setup lang="ts">
|
||||
const searchTerm = ref('')
|
||||
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
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}` } })) || []
|
||||
},
|
||||
lazy: true
|
||||
})
|
||||
|
||||
const groups = computed(() => [{
|
||||
id: 'users',
|
||||
label: searchTerm.value ? `Users matching “${searchTerm.value}”...` : 'Users',
|
||||
items: users.value || []
|
||||
}])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCommandPalette
|
||||
v-model:search-term="searchTerm"
|
||||
:loading="status === 'pending'"
|
||||
:groups="groups"
|
||||
class="flex-1 h-80"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
const searchTerm = ref('')
|
||||
const searchTermDebounced = refDebounced(searchTerm, 200)
|
||||
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/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}` } })) || []
|
||||
},
|
||||
lazy: true
|
||||
})
|
||||
|
||||
const groups = computed(() => [{
|
||||
id: 'users',
|
||||
label: searchTerm.value ? `Users matching “${searchTerm.value}”...` : 'Users',
|
||||
items: users.value || [],
|
||||
filter: false
|
||||
}])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCommandPalette
|
||||
v-model:search-term="searchTerm"
|
||||
:loading="status === 'pending'"
|
||||
:groups="groups"
|
||||
class="flex-1 h-80"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
const { data: users } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
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}` } })) || []
|
||||
},
|
||||
lazy: true
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCommandPalette
|
||||
:groups="[{ id: 'users', items: users || [] }]"
|
||||
:fuse="{ fuseOptions: { includeMatches: true } }"
|
||||
class="flex-1 h-80"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
const users = [
|
||||
{ id: 1, label: 'Durward Reynolds' },
|
||||
{ id: 2, label: 'Kenton Towne' },
|
||||
{ id: 3, label: 'Therese Wunsch' },
|
||||
{ id: 4, label: 'Benedict Kessler' },
|
||||
{ id: 5, label: 'Katelyn Rohan' }
|
||||
]
|
||||
|
||||
const selected = ref(users[0])
|
||||
|
||||
function onSelect(item: any) {
|
||||
console.log(item)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCommandPalette
|
||||
v-model="selected"
|
||||
:groups="[{ id: 'users', items: users }]"
|
||||
class="flex-1"
|
||||
@update:model-value="onSelect"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,25 @@
|
||||
<script setup lang="ts">
|
||||
const users = [
|
||||
{ id: 1, label: 'Durward Reynolds' },
|
||||
{ id: 2, label: 'Kenton Towne' },
|
||||
{ id: 3, label: 'Therese Wunsch' },
|
||||
{ id: 4, label: 'Benedict Kessler' },
|
||||
{ id: 5, label: 'Katelyn Rohan' }
|
||||
]
|
||||
|
||||
const selected = ref([users[0], users[1]])
|
||||
|
||||
function onSelect(items: any) {
|
||||
console.log(items)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCommandPalette
|
||||
v-model="selected"
|
||||
multiple
|
||||
:groups="[{ id: 'users', items: users }]"
|
||||
class="flex-1"
|
||||
@update:model-value="onSelect"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
const open = ref(false)
|
||||
|
||||
const users = [
|
||||
{ id: 1, label: 'Durward Reynolds' },
|
||||
{ id: 2, label: 'Kenton Towne' },
|
||||
{ id: 3, label: 'Therese Wunsch' },
|
||||
{ id: 4, label: 'Benedict Kessler' },
|
||||
{ id: 5, label: 'Katelyn Rohan' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UModal v-model:open="open">
|
||||
<UButton
|
||||
label="Search users..."
|
||||
color="neutral"
|
||||
variant="subtle"
|
||||
icon="i-heroicons-magnifying-glass"
|
||||
/>
|
||||
|
||||
<template #content>
|
||||
<UCommandPalette
|
||||
close
|
||||
:groups="[{ id: 'users', items: users }]"
|
||||
@update:open="open = $event"
|
||||
/>
|
||||
</template>
|
||||
</UModal>
|
||||
</template>
|
||||
@@ -0,0 +1,40 @@
|
||||
<script setup lang="ts">
|
||||
const items = [{
|
||||
id: '/',
|
||||
label: 'Introduction',
|
||||
level: 1
|
||||
}, {
|
||||
id: '/getting-started#whats-new-in-v3',
|
||||
label: 'What\'s new in v3?',
|
||||
level: 2
|
||||
}, {
|
||||
id: '/getting-started#radix-vue-3',
|
||||
label: 'Radix Vue',
|
||||
level: 3
|
||||
}, {
|
||||
id: '/getting-started#tailwind-css-v4',
|
||||
label: 'Tailwind CSS v4',
|
||||
level: 3
|
||||
}, {
|
||||
id: '/getting-started#tailwind-variants',
|
||||
label: 'Tailwind Variants',
|
||||
level: 3
|
||||
}, {
|
||||
id: '/getting-started/installation',
|
||||
label: 'Installation',
|
||||
level: 1
|
||||
}]
|
||||
|
||||
function postFilter(searchTerm: string, items: any[]) {
|
||||
// Filter only first level items if no searchTerm
|
||||
if (!searchTerm) {
|
||||
return items?.filter(item => item.level === 1)
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCommandPalette :groups="[{ id: 'files', items, postFilter }]" class="flex-1" />
|
||||
</template>
|
||||
@@ -0,0 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
const users = [
|
||||
{ id: 1, label: 'Durward Reynolds' },
|
||||
{ id: 2, label: 'Kenton Towne' },
|
||||
{ id: 3, label: 'Therese Wunsch' },
|
||||
{ id: 4, label: 'Benedict Kessler' },
|
||||
{ id: 5, label: 'Katelyn Rohan' }
|
||||
]
|
||||
|
||||
const searchTerm = ref('Th')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCommandPalette
|
||||
v-model:search-term="searchTerm"
|
||||
:groups="[{ id: 'users', items: users }]"
|
||||
class="flex-1"
|
||||
/>
|
||||
</template>
|
||||
@@ -13,7 +13,7 @@ const items = [{
|
||||
|
||||
<template>
|
||||
<UContextMenu :items="items" class="w-48">
|
||||
<div class="flex items-center justify-center rounded-md border border-dashed border-gray-300 dark:border-gray-700 text-sm aspect-video w-72">
|
||||
<div class="flex items-center justify-center rounded-md border border-dashed border-[--ui-border-accented] text-sm aspect-video w-72">
|
||||
Right click here
|
||||
</div>
|
||||
|
||||
@@ -22,7 +22,7 @@ const items = [{
|
||||
</template>
|
||||
|
||||
<template #refresh-trailing>
|
||||
<UIcon v-if="loading" name="i-heroicons-arrow-path-20-solid" class="shrink-0 size-5 text-primary-500 dark:text-primary-400 animate-spin" />
|
||||
<UIcon v-if="loading" name="i-heroicons-arrow-path-20-solid" class="shrink-0 size-5 text-[--ui-primary] animate-spin" />
|
||||
</template>
|
||||
</UContextMenu>
|
||||
</template>
|
||||
|
||||
@@ -3,7 +3,7 @@ const searchTerm = ref('')
|
||||
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
params: { q: searchTerm },
|
||||
transform: (data: any[]) => {
|
||||
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}` } })) || []
|
||||
},
|
||||
lazy: true
|
||||
@@ -19,7 +19,12 @@ const groups = computed(() => [{
|
||||
|
||||
<template>
|
||||
<UDrawer>
|
||||
<UButton label="Search users..." color="gray" variant="subtle" icon="i-heroicons-magnifying-glass" />
|
||||
<UButton
|
||||
label="Search users..."
|
||||
color="neutral"
|
||||
variant="subtle"
|
||||
icon="i-heroicons-magnifying-glass"
|
||||
/>
|
||||
|
||||
<template #content>
|
||||
<UCommandPalette
|
||||
@@ -27,7 +32,7 @@ const groups = computed(() => [{
|
||||
:loading="status === 'pending'"
|
||||
:groups="groups"
|
||||
placeholder="Search users..."
|
||||
class="h-96 border-t border-gray-200 dark:border-gray-800"
|
||||
class="h-96 border-t border-[--ui-border]"
|
||||
/>
|
||||
</template>
|
||||
</UDrawer>
|
||||
|
||||
@@ -3,16 +3,16 @@ const open = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UDrawer v-model:open="open" title="Drawer with footer" description="This is useful when you want a form in a Drawer.">
|
||||
<UButton label="Open" color="gray" variant="subtle" trailing-icon="i-heroicons-chevron-up-20-solid" />
|
||||
<UDrawer v-model:open="open" title="Drawer with footer" description="This is useful when you want a form in a Drawer." :ui="{ container: 'max-w-xl mx-auto' }">
|
||||
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-heroicons-chevron-up-20-solid" />
|
||||
|
||||
<template #body>
|
||||
<Placeholder class="h-48" />
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<UButton label="Submit" color="gray" class="justify-center" />
|
||||
<UButton label="Cancel" color="gray" variant="outline" class="justify-center" @click="open = false" />
|
||||
<UButton label="Submit" color="neutral" class="justify-center" />
|
||||
<UButton label="Cancel" color="neutral" variant="outline" class="justify-center" @click="open = false" />
|
||||
</template>
|
||||
</UDrawer>
|
||||
</template>
|
||||
|
||||
@@ -8,7 +8,7 @@ defineShortcuts({
|
||||
|
||||
<template>
|
||||
<UDrawer v-model:open="open">
|
||||
<UButton label="Open" color="gray" variant="subtle" trailing-icon="i-heroicons-chevron-up-20-solid" />
|
||||
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-heroicons-chevron-up-20-solid" />
|
||||
|
||||
<template #content>
|
||||
<Placeholder class="h-48 m-4" />
|
||||
|
||||
@@ -14,10 +14,10 @@ const items = [{
|
||||
|
||||
<template>
|
||||
<UDropdownMenu :items="items" class="w-48">
|
||||
<UButton label="Open" color="gray" variant="outline" icon="i-heroicons-bars-3" />
|
||||
<UButton label="Open" color="neutral" variant="outline" icon="i-heroicons-bars-3" />
|
||||
|
||||
<template #profile-trailing>
|
||||
<UIcon name="i-heroicons-check-badge" class="shrink-0 size-5 text-primary-500 dark:text-primary-400" />
|
||||
<UIcon name="i-heroicons-check-badge" class="shrink-0 size-5 text-[--ui-primary]" />
|
||||
</template>
|
||||
</UDropdownMenu>
|
||||
</template>
|
||||
|
||||
@@ -19,6 +19,6 @@ const items = [{
|
||||
|
||||
<template>
|
||||
<UDropdownMenu v-model:open="open" :items="items" class="w-48">
|
||||
<UButton label="Open" color="gray" variant="outline" icon="i-heroicons-bars-3" />
|
||||
<UButton label="Open" color="neutral" variant="outline" icon="i-heroicons-bars-3" />
|
||||
</UDropdownMenu>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormError, FormSubmitEvent } from '#ui/types'
|
||||
|
||||
const state = reactive({
|
||||
email: undefined,
|
||||
password: undefined
|
||||
})
|
||||
|
||||
const validate = (state: any): FormError[] => {
|
||||
const errors = []
|
||||
if (!state.email) errors.push({ name: 'email', message: 'Required' })
|
||||
if (!state.password) errors.push({ name: 'password', message: 'Required' })
|
||||
return errors
|
||||
}
|
||||
|
||||
const toast = useToast()
|
||||
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||
console.log(event.data)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UForm :validate="validate" :state="state" class="space-y-4" @submit="onSubmit">
|
||||
<UFormField label="Email" name="email">
|
||||
<UInput v-model="state.email" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Password" name="password">
|
||||
<UInput v-model="state.password" type="password" />
|
||||
</UFormField>
|
||||
|
||||
<UButton type="submit">
|
||||
Submit
|
||||
</UButton>
|
||||
</UForm>
|
||||
</template>
|
||||
@@ -0,0 +1,115 @@
|
||||
<script setup lang="ts">
|
||||
import { z } from 'zod'
|
||||
import type { FormSubmitEvent, Form } from '@nuxt/ui'
|
||||
|
||||
const schema = z.object({
|
||||
input: z.string().min(10),
|
||||
inputMenu: z.any().refine(option => option?.value === 'option-2', {
|
||||
message: 'Select Option 2'
|
||||
}),
|
||||
inputMenuMultiple: z.any().refine(values => !!values?.find((option: any) => option.value === 'option-2'), {
|
||||
message: 'Include Option 2'
|
||||
}),
|
||||
textarea: z.string().min(10),
|
||||
select: z.string().refine(value => value === 'option-2', {
|
||||
message: 'Select Option 2'
|
||||
}),
|
||||
selectMenu: z.any().refine(option => option?.value === 'option-2', {
|
||||
message: 'Select Option 2'
|
||||
}),
|
||||
selectMenuMultiple: z.any().refine(values => !!values?.find((option: any) => option.value === 'option-2'), {
|
||||
message: 'Include Option 2'
|
||||
}),
|
||||
switch: z.boolean().refine(value => value === true, {
|
||||
message: 'Toggle me'
|
||||
}),
|
||||
checkbox: z.boolean().refine(value => value === true, {
|
||||
message: 'Check me'
|
||||
}),
|
||||
radioGroup: z.string().refine(value => value === 'option-2', {
|
||||
message: 'Select Option 2'
|
||||
}),
|
||||
slider: z.number().max(20, { message: 'Must be less than 20' })
|
||||
})
|
||||
|
||||
type Schema = z.output<typeof schema>
|
||||
|
||||
const state = reactive<Partial<Schema>>({})
|
||||
const form = ref<Form<Schema>>()
|
||||
|
||||
const items = [
|
||||
{ label: 'Option 1', value: 'option-1' },
|
||||
{ label: 'Option 2', value: 'option-2' },
|
||||
{ label: 'Option 3', value: 'option-3' }
|
||||
]
|
||||
|
||||
const toast = useToast()
|
||||
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||
console.log(event.data)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UForm ref="form" :state="state" :schema="schema" @submit="onSubmit">
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<UFormField label="Input" name="input">
|
||||
<UInput v-model="state.input" placeholder="john@lennon.com" class="w-40" />
|
||||
</UFormField>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
<UFormField name="switch">
|
||||
<USwitch v-model="state.switch" label="Switch me" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField name="checkbox">
|
||||
<UCheckbox v-model="state.checkbox" label="Check me" />
|
||||
</UFormField>
|
||||
</div>
|
||||
|
||||
<UFormField name="slider" label="Slider">
|
||||
<USlider v-model="state.slider" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField name="select" label="Select">
|
||||
<USelect v-model="state.select" :items="items" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField name="selectMenu" label="Select Menu">
|
||||
<USelectMenu v-model="state.selectMenu" :items="items" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField name="selectMenuMultiple" label="Select Menu (Multiple)">
|
||||
<USelectMenu v-model="state.selectMenuMultiple" multiple :items="items" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField name="inputMenu" label="Input Menu">
|
||||
<UInputMenu v-model="state.inputMenu" :items="items" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField name="inputMenuMultiple" label="Input Menu (Multiple)">
|
||||
<UInputMenu v-model="state.inputMenuMultiple" multiple :items="items" />
|
||||
</UFormField>
|
||||
|
||||
<span />
|
||||
|
||||
<UFormField label="Textarea" name="textarea">
|
||||
<UTextarea v-model="state.textarea" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField name="radioGroup">
|
||||
<URadioGroup v-model="state.radioGroup" legend="Radio group" :items="items" />
|
||||
</UFormField>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 mt-8">
|
||||
<UButton color="neutral" type="submit">
|
||||
Submit
|
||||
</UButton>
|
||||
|
||||
<UButton color="neutral" variant="outline" @click="form?.clear()">
|
||||
Clear
|
||||
</UButton>
|
||||
</div>
|
||||
</UForm>
|
||||
</template>
|
||||
38
docs/app/components/content/examples/form/FormExampleJoi.vue
Normal file
38
docs/app/components/content/examples/form/FormExampleJoi.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
import Joi from 'joi'
|
||||
import type { FormSubmitEvent } from '#ui/types'
|
||||
|
||||
const schema = Joi.object({
|
||||
email: Joi.string().required(),
|
||||
password: Joi.string()
|
||||
.min(8)
|
||||
.required()
|
||||
})
|
||||
|
||||
const state = reactive({
|
||||
email: undefined,
|
||||
password: undefined
|
||||
})
|
||||
|
||||
const toast = useToast()
|
||||
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||
console.log(event.data)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
|
||||
<UFormField label="Email" name="email">
|
||||
<UInput v-model="state.email" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Password" name="password">
|
||||
<UInput v-model="state.password" type="password" />
|
||||
</UFormField>
|
||||
|
||||
<UButton type="submit">
|
||||
Submit
|
||||
</UButton>
|
||||
</UForm>
|
||||
</template>
|
||||
@@ -0,0 +1,54 @@
|
||||
<script setup lang="ts">
|
||||
import { z } from 'zod'
|
||||
import type { FormSubmitEvent } from '@nuxt/ui'
|
||||
|
||||
const schema = z.object({
|
||||
name: z.string().min(2),
|
||||
news: z.boolean()
|
||||
})
|
||||
|
||||
type Schema = z.output<typeof schema>
|
||||
|
||||
const nestedSchema = z.object({
|
||||
email: z.string().email()
|
||||
})
|
||||
|
||||
type NestedSchema = z.output<typeof nestedSchema>
|
||||
|
||||
const state = reactive<Partial<Schema & NestedSchema>>({ })
|
||||
|
||||
const toast = useToast()
|
||||
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||
console.log(event.data)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UForm
|
||||
:state="state"
|
||||
:schema="schema"
|
||||
class="gap-4 flex flex-col w-60"
|
||||
@submit="onSubmit"
|
||||
>
|
||||
<UFormField label="Name" name="name">
|
||||
<UInput v-model="state.name" placeholder="John Lennon" />
|
||||
</UFormField>
|
||||
|
||||
<div>
|
||||
<UCheckbox v-model="state.news" name="news" label="Register to our newsletter" />
|
||||
</div>
|
||||
|
||||
<UForm v-if="state.news" :state="state" :schema="nestedSchema">
|
||||
<UFormField label="Email" name="email">
|
||||
<UInput v-model="state.email" placeholder="john@lennon.com" />
|
||||
</UFormField>
|
||||
</UForm>
|
||||
|
||||
<div>
|
||||
<UButton type="submit">
|
||||
Submit
|
||||
</UButton>
|
||||
</div>
|
||||
</UForm>
|
||||
</template>
|
||||
@@ -0,0 +1,79 @@
|
||||
<script setup lang="ts">
|
||||
import { z } from 'zod'
|
||||
import type { FormSubmitEvent } from '@nuxt/ui'
|
||||
|
||||
const schema = z.object({
|
||||
customer: z.string().min(2)
|
||||
})
|
||||
|
||||
type Schema = z.output<typeof schema>
|
||||
|
||||
const itemSchema = z.object({
|
||||
description: z.string().min(1),
|
||||
price: z.number().min(0)
|
||||
})
|
||||
|
||||
type ItemSchema = z.output<typeof itemSchema>
|
||||
|
||||
const state = reactive<Partial<Schema & { items: Partial<ItemSchema>[] }>>({
|
||||
items: [{}]
|
||||
})
|
||||
|
||||
function addItem() {
|
||||
if (!state.items) {
|
||||
state.items = []
|
||||
}
|
||||
state.items.push({})
|
||||
}
|
||||
|
||||
function removeItem() {
|
||||
if (state.items) {
|
||||
state.items.pop()
|
||||
}
|
||||
}
|
||||
const formItemRef = ref()
|
||||
|
||||
const toast = useToast()
|
||||
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||
console.log(event.data)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UForm
|
||||
ref="formItemRef"
|
||||
:state="state"
|
||||
:schema="schema"
|
||||
class="gap-4 flex flex-col w-60"
|
||||
@submit="onSubmit"
|
||||
>
|
||||
<UFormField label="Customer" name="customer">
|
||||
<UInput v-model="state.customer" placeholder="Wonka Industries" />
|
||||
</UFormField>
|
||||
|
||||
<UForm v-for="item, count in state.items" :key="count" :state="item" :schema="itemSchema" class="flex gap-2">
|
||||
<UFormField :label="!count ? 'Description' : undefined" name="description">
|
||||
<UInput v-model="item.description" />
|
||||
</UFormField>
|
||||
<UFormField :label="!count ? 'Price' : undefined" name="price" class="w-20">
|
||||
<UInput v-model="item.price" type="number" />
|
||||
</UFormField>
|
||||
</UForm>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<UButton color="neutral" variant="subtle" size="sm" @click="addItem()">
|
||||
Add Item
|
||||
</UButton>
|
||||
|
||||
<UButton color="neutral" variant="ghost" size="sm" @click="removeItem()">
|
||||
Remove Item
|
||||
</UButton>
|
||||
</div>
|
||||
<div>
|
||||
<UButton type="submit">
|
||||
Submit
|
||||
</UButton>
|
||||
</div>
|
||||
</UForm>
|
||||
</template>
|
||||
@@ -0,0 +1,45 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormError, FormErrorEvent, FormSubmitEvent } from '#ui/types'
|
||||
|
||||
const state = reactive({
|
||||
email: undefined,
|
||||
password: undefined
|
||||
})
|
||||
|
||||
const validate = (state: any): FormError[] => {
|
||||
const errors = []
|
||||
if (!state.email) errors.push({ name: 'email', message: 'Required' })
|
||||
if (!state.password) errors.push({ name: 'password', message: 'Required' })
|
||||
return errors
|
||||
}
|
||||
|
||||
const toast = useToast()
|
||||
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||
console.log(event.data)
|
||||
}
|
||||
|
||||
async function onError(event: FormErrorEvent) {
|
||||
if (event?.errors?.[0]?.id) {
|
||||
const element = document.getElementById(event.errors[0].id)
|
||||
element?.focus()
|
||||
element?.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UForm :validate="validate" :state="state" class="space-y-4" @submit="onSubmit" @error="onError">
|
||||
<UFormField label="Email" name="email">
|
||||
<UInput v-model="state.email" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Password" name="password">
|
||||
<UInput v-model="state.password" type="password" />
|
||||
</UFormField>
|
||||
|
||||
<UButton type="submit">
|
||||
Submit
|
||||
</UButton>
|
||||
</UForm>
|
||||
</template>
|
||||
@@ -0,0 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
import * as v from 'valibot'
|
||||
import type { FormSubmitEvent } from '#ui/types'
|
||||
|
||||
const schema = v.object({
|
||||
email: v.pipe(v.string(), v.email('Invalid email')),
|
||||
password: v.pipe(v.string(), v.minLength(8, 'Must be at least 8 characters'))
|
||||
})
|
||||
|
||||
type Schema = v.InferOutput<typeof schema>
|
||||
|
||||
const state = reactive({
|
||||
email: '',
|
||||
password: ''
|
||||
})
|
||||
|
||||
const toast = useToast()
|
||||
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||
console.log(event.data)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UForm :schema="v.safeParser(schema)" :state="state" class="space-y-4" @submit="onSubmit">
|
||||
<UFormField label="Email" name="email">
|
||||
<UInput v-model="state.email" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Password" name="password">
|
||||
<UInput v-model="state.password" type="password" />
|
||||
</UFormField>
|
||||
|
||||
<UButton type="submit">
|
||||
Submit
|
||||
</UButton>
|
||||
</UForm>
|
||||
</template>
|
||||
40
docs/app/components/content/examples/form/FormExampleYup.vue
Normal file
40
docs/app/components/content/examples/form/FormExampleYup.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<script setup lang="ts">
|
||||
import { object, string, type InferType } from 'yup'
|
||||
import type { FormSubmitEvent } from '#ui/types'
|
||||
|
||||
const schema = object({
|
||||
email: string().email('Invalid email').required('Required'),
|
||||
password: string()
|
||||
.min(8, 'Must be at least 8 characters')
|
||||
.required('Required')
|
||||
})
|
||||
|
||||
type Schema = InferType<typeof schema>
|
||||
|
||||
const state = reactive({
|
||||
email: undefined,
|
||||
password: undefined
|
||||
})
|
||||
|
||||
const toast = useToast()
|
||||
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||
console.log(event.data)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
|
||||
<UFormField label="Email" name="email">
|
||||
<UInput v-model="state.email" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Password" name="password">
|
||||
<UInput v-model="state.password" type="password" />
|
||||
</UFormField>
|
||||
|
||||
<UButton type="submit">
|
||||
Submit
|
||||
</UButton>
|
||||
</UForm>
|
||||
</template>
|
||||
38
docs/app/components/content/examples/form/FormExampleZod.vue
Normal file
38
docs/app/components/content/examples/form/FormExampleZod.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
import { z } from 'zod'
|
||||
import type { FormSubmitEvent } from '#ui/types'
|
||||
|
||||
const schema = z.object({
|
||||
email: z.string().email('Invalid email'),
|
||||
password: z.string().min(8, 'Must be at least 8 characters')
|
||||
})
|
||||
|
||||
type Schema = z.output<typeof schema>
|
||||
|
||||
const state = reactive<Partial<Schema>>({
|
||||
email: undefined,
|
||||
password: undefined
|
||||
})
|
||||
|
||||
const toast = useToast()
|
||||
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||
console.log(event.data)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
|
||||
<UFormField label="Email" name="email">
|
||||
<UInput v-model="state.email" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField label="Password" name="password">
|
||||
<UInput v-model="state.password" type="password" />
|
||||
</UFormField>
|
||||
|
||||
<UButton type="submit">
|
||||
Submit
|
||||
</UButton>
|
||||
</UForm>
|
||||
</template>
|
||||
@@ -0,0 +1,31 @@
|
||||
<script setup lang="ts">
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
transform: (data: { id: number, name: string }[]) => {
|
||||
return data?.map(user => ({
|
||||
label: user.name,
|
||||
value: String(user.id),
|
||||
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
|
||||
})) || []
|
||||
},
|
||||
lazy: true
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UInputMenu
|
||||
:items="users || []"
|
||||
:loading="status === 'pending'"
|
||||
icon="i-heroicons-user"
|
||||
placeholder="Select user"
|
||||
class="w-48"
|
||||
>
|
||||
<template #leading="{ modelValue, ui }">
|
||||
<UAvatar
|
||||
v-if="modelValue"
|
||||
v-bind="modelValue.avatar"
|
||||
:size="ui.itemLeadingAvatarSize()"
|
||||
:class="ui.itemLeadingAvatar()"
|
||||
/>
|
||||
</template>
|
||||
</UInputMenu>
|
||||
</template>
|
||||
@@ -0,0 +1,37 @@
|
||||
<script setup lang="ts">
|
||||
const searchTerm = ref('')
|
||||
const searchTermDebounced = refDebounced(searchTerm, 200)
|
||||
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
params: { q: searchTermDebounced },
|
||||
transform: (data: { id: number, name: string }[]) => {
|
||||
return data?.map(user => ({
|
||||
label: user.name,
|
||||
value: String(user.id),
|
||||
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
|
||||
})) || []
|
||||
},
|
||||
lazy: true
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UInputMenu
|
||||
v-model:search-term="searchTerm"
|
||||
:items="users || []"
|
||||
:loading="status === 'pending'"
|
||||
:filter="false"
|
||||
icon="i-heroicons-user"
|
||||
placeholder="Select user"
|
||||
class="w-48"
|
||||
>
|
||||
<template #leading="{ modelValue, ui }">
|
||||
<UAvatar
|
||||
v-if="modelValue"
|
||||
v-bind="modelValue.avatar"
|
||||
:size="ui.itemLeadingAvatarSize()"
|
||||
:class="ui.itemLeadingAvatar()"
|
||||
/>
|
||||
</template>
|
||||
</UInputMenu>
|
||||
</template>
|
||||
@@ -0,0 +1,41 @@
|
||||
<script setup lang="ts">
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
transform: (data: { id: number, name: string, email: string }[]) => {
|
||||
return data?.map(user => ({
|
||||
label: user.name,
|
||||
email: user.email,
|
||||
value: String(user.id),
|
||||
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
|
||||
})) || []
|
||||
},
|
||||
lazy: true
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UInputMenu
|
||||
:items="users || []"
|
||||
:loading="status === 'pending'"
|
||||
:filter="['name', 'email']"
|
||||
icon="i-heroicons-user"
|
||||
placeholder="Select user"
|
||||
class="w-80"
|
||||
>
|
||||
<template #leading="{ modelValue, ui }">
|
||||
<UAvatar
|
||||
v-if="modelValue"
|
||||
v-bind="modelValue.avatar"
|
||||
:size="ui.itemLeadingAvatarSize()"
|
||||
:class="ui.itemLeadingAvatar()"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #item-label="{ item }">
|
||||
{{ item.label }}
|
||||
|
||||
<span class="text-[--ui-text-muted]">
|
||||
{{ item.email }}
|
||||
</span>
|
||||
</template>
|
||||
</UInputMenu>
|
||||
</template>
|
||||
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
||||
const selected = ref('Backlog')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UInputMenu
|
||||
v-model="selected"
|
||||
:items="items"
|
||||
:ui="{
|
||||
trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200'
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,42 @@
|
||||
<script setup lang="ts">
|
||||
const items = ref([
|
||||
{
|
||||
label: 'benjamincanac',
|
||||
value: 'benjamincanac',
|
||||
avatar: {
|
||||
src: 'https://github.com/benjamincanac.png',
|
||||
alt: 'benjamincanac'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'romhml',
|
||||
value: 'romhml',
|
||||
avatar: {
|
||||
src: 'https://github.com/romhml.png',
|
||||
alt: 'romhml'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'noook',
|
||||
value: 'noook',
|
||||
avatar: {
|
||||
src: 'https://github.com/noook.png',
|
||||
alt: 'noook'
|
||||
}
|
||||
}
|
||||
])
|
||||
const selected = ref(items.value[0])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UInputMenu v-model="selected" :items="items" class="w-40">
|
||||
<template #leading="{ modelValue, ui }">
|
||||
<UAvatar
|
||||
v-if="modelValue"
|
||||
v-bind="modelValue.avatar"
|
||||
:size="ui.itemLeadingAvatarSize()"
|
||||
:class="ui.itemLeadingAvatar()"
|
||||
/>
|
||||
</template>
|
||||
</UInputMenu>
|
||||
</template>
|
||||
@@ -0,0 +1,41 @@
|
||||
<script setup lang="ts">
|
||||
const items = ref([
|
||||
{
|
||||
label: 'bug',
|
||||
value: 'bug',
|
||||
chip: {
|
||||
color: 'error' as const
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'feature',
|
||||
value: 'feature',
|
||||
chip: {
|
||||
color: 'success' as const
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'enhancement',
|
||||
value: 'enhancement',
|
||||
chip: {
|
||||
color: 'info' as const
|
||||
}
|
||||
}
|
||||
])
|
||||
const selected = ref(items.value[0])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UInputMenu v-model="selected" :items="items" class="w-40">
|
||||
<template #leading="{ modelValue, ui }">
|
||||
<UChip
|
||||
v-if="modelValue"
|
||||
v-bind="modelValue.chip"
|
||||
inset
|
||||
standalone
|
||||
:size="ui.itemLeadingChipSize()"
|
||||
:class="ui.itemLeadingChip()"
|
||||
/>
|
||||
</template>
|
||||
</UInputMenu>
|
||||
</template>
|
||||
@@ -0,0 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
const items = ref([
|
||||
{
|
||||
label: 'Backlog',
|
||||
value: 'backlog',
|
||||
icon: 'i-heroicons-question-mark-circle'
|
||||
},
|
||||
{
|
||||
label: 'Todo',
|
||||
value: 'todo',
|
||||
icon: 'i-heroicons-plus-circle'
|
||||
},
|
||||
{
|
||||
label: 'In Progress',
|
||||
value: 'in_progress',
|
||||
icon: 'i-heroicons-arrow-up-circle'
|
||||
},
|
||||
{
|
||||
label: 'Done',
|
||||
value: 'done',
|
||||
icon: 'i-heroicons-check-circle'
|
||||
}
|
||||
])
|
||||
const selected = ref(items.value[0])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UInputMenu v-model="selected" :icon="selected?.icon" :items="items" class="w-40" />
|
||||
</template>
|
||||
@@ -0,0 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
const open = ref(false)
|
||||
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
||||
const selected = ref('Backlog')
|
||||
|
||||
defineShortcuts({
|
||||
o: () => open.value = !open.value
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UInputMenu v-model="selected" v-model:open="open" :items="items" />
|
||||
</template>
|
||||
@@ -0,0 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
const searchTerm = ref('D')
|
||||
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
||||
const selected = ref('Backlog')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UInputMenu v-model="selected" v-model:search-term="searchTerm" :items="items" />
|
||||
</template>
|
||||
@@ -3,7 +3,7 @@ const searchTerm = ref('')
|
||||
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
params: { q: searchTerm },
|
||||
transform: (data: any[]) => {
|
||||
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}` } })) || []
|
||||
},
|
||||
lazy: true
|
||||
@@ -19,7 +19,12 @@ const groups = computed(() => [{
|
||||
|
||||
<template>
|
||||
<UModal>
|
||||
<UButton label="Search users..." color="gray" variant="subtle" icon="i-heroicons-magnifying-glass" />
|
||||
<UButton
|
||||
label="Search users..."
|
||||
color="neutral"
|
||||
variant="subtle"
|
||||
icon="i-heroicons-magnifying-glass"
|
||||
/>
|
||||
|
||||
<template #content>
|
||||
<UCommandPalette
|
||||
|
||||
@@ -9,7 +9,7 @@ defineProps<{
|
||||
<template>
|
||||
<UModal :title="`This modal was opened programmatically ${count} times`">
|
||||
<template #footer>
|
||||
<UButton color="gray" label="Close" @click="modal.close()" />
|
||||
<UButton color="neutral" label="Close" @click="modal.close()" />
|
||||
</template>
|
||||
</UModal>
|
||||
</template>
|
||||
|
||||
@@ -4,15 +4,15 @@ const open = ref(false)
|
||||
|
||||
<template>
|
||||
<UModal v-model:open="open" title="Modal with footer" description="This is useful when you want a form in a Modal." :ui="{ footer: 'justify-end' }">
|
||||
<UButton label="Open" color="gray" variant="subtle" />
|
||||
<UButton label="Open" color="neutral" variant="subtle" />
|
||||
|
||||
<template #body>
|
||||
<Placeholder class="h-48" />
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<UButton label="Cancel" color="gray" variant="outline" @click="open = false" />
|
||||
<UButton label="Submit" color="gray" />
|
||||
<UButton label="Cancel" color="neutral" variant="outline" @click="open = false" />
|
||||
<UButton label="Submit" color="neutral" />
|
||||
</template>
|
||||
</UModal>
|
||||
</template>
|
||||
|
||||
@@ -5,16 +5,16 @@ const second = ref(false)
|
||||
|
||||
<template>
|
||||
<UModal v-model:open="first" title="First modal" :ui="{ footer: 'justify-end' }">
|
||||
<UButton color="gray" variant="subtle" label="Open" />
|
||||
<UButton color="neutral" variant="subtle" label="Open" />
|
||||
|
||||
<template #footer>
|
||||
<UButton label="Close" color="gray" variant="outline" @click="first = false" />
|
||||
<UButton label="Close" color="neutral" variant="outline" @click="first = false" />
|
||||
|
||||
<UModal v-model:open="second" title="Second modal" :ui="{ footer: 'justify-end' }">
|
||||
<UButton label="Open second" color="gray" />
|
||||
<UButton label="Open second" color="neutral" />
|
||||
|
||||
<template #footer>
|
||||
<UButton label="Close" color="gray" variant="outline" @click="second = false" />
|
||||
<UButton label="Close" color="neutral" variant="outline" @click="second = false" />
|
||||
</template>
|
||||
</UModal>
|
||||
</template>
|
||||
|
||||
@@ -8,7 +8,7 @@ defineShortcuts({
|
||||
|
||||
<template>
|
||||
<UModal v-model:open="open">
|
||||
<UButton label="Open" color="gray" variant="subtle" />
|
||||
<UButton label="Open" color="neutral" variant="subtle" />
|
||||
|
||||
<template #content>
|
||||
<Placeholder class="h-48 m-4" />
|
||||
|
||||
@@ -16,5 +16,5 @@ function open() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UButton label="Open" color="gray" variant="subtle" @click="open" />
|
||||
<UButton label="Open" color="neutral" variant="subtle" @click="open" />
|
||||
</template>
|
||||
|
||||
@@ -22,7 +22,7 @@ const items = [
|
||||
{
|
||||
label: 'Colors',
|
||||
icon: 'i-heroicons-swatch',
|
||||
description: 'Choose a primary and a gray color from your Tailwind CSS theme.'
|
||||
description: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
|
||||
},
|
||||
{
|
||||
label: 'Theme',
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
const page = ref(5)
|
||||
|
||||
function to(page: number) {
|
||||
return {
|
||||
query: {
|
||||
page
|
||||
},
|
||||
hash: '#with-links'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UPagination v-model:page="page" :total="100" :to="to" :sibling-count="1" show-edges />
|
||||
</template>
|
||||
@@ -0,0 +1,47 @@
|
||||
<script setup lang="ts">
|
||||
const items = ref([
|
||||
{
|
||||
label: 'bug',
|
||||
value: 'bug',
|
||||
chip: {
|
||||
color: 'error' as const
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'feature',
|
||||
value: 'feature',
|
||||
chip: {
|
||||
color: 'success' as const
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'enhancement',
|
||||
value: 'enhancement',
|
||||
chip: {
|
||||
color: 'info' as const
|
||||
}
|
||||
}
|
||||
])
|
||||
const label = ref([])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UPopover :content="{ side: 'right', align: 'start' }">
|
||||
<UButton
|
||||
icon="i-heroicons-tag"
|
||||
label="Select labels"
|
||||
color="neutral"
|
||||
variant="subtle"
|
||||
/>
|
||||
|
||||
<template #content>
|
||||
<UCommandPalette
|
||||
v-model="label"
|
||||
multiple
|
||||
placeholder="Search labels..."
|
||||
:groups="[{ id: 'labels', items }]"
|
||||
:ui="{ input: '[&>input]:h-8' }"
|
||||
/>
|
||||
</template>
|
||||
</UPopover>
|
||||
</template>
|
||||
@@ -8,7 +8,7 @@ defineShortcuts({
|
||||
|
||||
<template>
|
||||
<UPopover v-model:open="open">
|
||||
<UButton label="Open" color="gray" variant="subtle" />
|
||||
<UButton label="Open" color="neutral" variant="subtle" />
|
||||
|
||||
<template #content>
|
||||
<Placeholder class="size-48 m-4 inline-flex" />
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<script setup lang="ts">
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
transform: (data: { id: number, name: string }[]) => {
|
||||
return data?.map(user => ({
|
||||
label: user.name,
|
||||
value: String(user.id),
|
||||
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
|
||||
})) || []
|
||||
},
|
||||
lazy: true
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<USelectMenu
|
||||
:items="users || []"
|
||||
:loading="status === 'pending'"
|
||||
icon="i-heroicons-user"
|
||||
placeholder="Select user"
|
||||
class="w-48"
|
||||
>
|
||||
<template #leading="{ modelValue, ui }">
|
||||
<UAvatar
|
||||
v-if="modelValue"
|
||||
v-bind="modelValue.avatar"
|
||||
:size="ui.itemLeadingAvatarSize()"
|
||||
:class="ui.itemLeadingAvatar()"
|
||||
/>
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</template>
|
||||
@@ -0,0 +1,37 @@
|
||||
<script setup lang="ts">
|
||||
const searchTerm = ref('')
|
||||
const searchTermDebounced = refDebounced(searchTerm, 200)
|
||||
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
params: { q: searchTermDebounced },
|
||||
transform: (data: { id: number, name: string }[]) => {
|
||||
return data?.map(user => ({
|
||||
label: user.name,
|
||||
value: String(user.id),
|
||||
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
|
||||
})) || []
|
||||
},
|
||||
lazy: true
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<USelectMenu
|
||||
v-model:search-term="searchTerm"
|
||||
:items="users || []"
|
||||
:loading="status === 'pending'"
|
||||
:filter="false"
|
||||
icon="i-heroicons-user"
|
||||
placeholder="Select user"
|
||||
class="w-48"
|
||||
>
|
||||
<template #leading="{ modelValue, ui }">
|
||||
<UAvatar
|
||||
v-if="modelValue"
|
||||
v-bind="modelValue.avatar"
|
||||
:size="ui.itemLeadingAvatarSize()"
|
||||
:class="ui.itemLeadingAvatar()"
|
||||
/>
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</template>
|
||||
@@ -0,0 +1,41 @@
|
||||
<script setup lang="ts">
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
transform: (data: { id: number, name: string, email: string }[]) => {
|
||||
return data?.map(user => ({
|
||||
label: user.name,
|
||||
email: user.email,
|
||||
value: String(user.id),
|
||||
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
|
||||
})) || []
|
||||
},
|
||||
lazy: true
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<USelectMenu
|
||||
:items="users || []"
|
||||
:loading="status === 'pending'"
|
||||
:filter="['name', 'email']"
|
||||
icon="i-heroicons-user"
|
||||
placeholder="Select user"
|
||||
class="w-80"
|
||||
>
|
||||
<template #leading="{ modelValue, ui }">
|
||||
<UAvatar
|
||||
v-if="modelValue"
|
||||
v-bind="modelValue.avatar"
|
||||
:size="ui.itemLeadingAvatarSize()"
|
||||
:class="ui.itemLeadingAvatar()"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #item-label="{ item }">
|
||||
{{ item.label }}
|
||||
|
||||
<span class="text-[--ui-text-muted]">
|
||||
{{ item.email }}
|
||||
</span>
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</template>
|
||||
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
||||
const selected = ref('Backlog')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<USelectMenu
|
||||
v-model="selected"
|
||||
:items="items"
|
||||
:ui="{
|
||||
trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200'
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user