mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-15 12:39:35 +01:00
Compare commits
186 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f6fa6ae4a | ||
|
|
c7bb6d4c4b | ||
|
|
c23f85fe33 | ||
|
|
591d59fe89 | ||
|
|
caa3bf9c7e | ||
|
|
2731011bb7 | ||
|
|
aebf0b3dca | ||
|
|
6651987dc6 | ||
|
|
61aabd72e4 | ||
|
|
8dfdd63ce3 | ||
|
|
e6369a6746 | ||
|
|
a4f3f6d531 | ||
|
|
c5bdec0f64 | ||
|
|
87a7828931 | ||
|
|
501468960b | ||
|
|
13cd6be679 | ||
|
|
e421ea57ec | ||
|
|
799d7ae422 | ||
|
|
be9b1f659a | ||
|
|
39e29fccf1 | ||
|
|
195773ec7d | ||
|
|
d7710795df | ||
|
|
53f8b34bef | ||
|
|
df83ab355e | ||
|
|
e5df026993 | ||
|
|
6131871a0d | ||
|
|
9543bce787 | ||
|
|
5e345a8a73 | ||
|
|
8acf3c51db | ||
|
|
f3b8b17dc5 | ||
|
|
bc0a296f9d | ||
|
|
ac4c1946ec | ||
|
|
82b5f322eb | ||
|
|
fb6cf708a6 | ||
|
|
8ab1f75e47 | ||
|
|
e9b19349ba | ||
|
|
b2b206e3f4 | ||
|
|
b779064129 | ||
|
|
94155258e2 | ||
|
|
c63a6dd133 | ||
|
|
79833035de | ||
|
|
79e7c7b729 | ||
|
|
bc06185282 | ||
|
|
6e27304d8c | ||
|
|
b4f8ac7ff7 | ||
|
|
a2fa1accaa | ||
|
|
f486423381 | ||
|
|
975331a7b1 | ||
|
|
75e4792f7f | ||
|
|
4a969a8b9e | ||
|
|
664a8c7524 | ||
|
|
fbcc3139a3 | ||
|
|
1a46394668 | ||
|
|
9ca213bd33 | ||
|
|
3484832822 | ||
|
|
80dfa88ea4 | ||
|
|
d710141a1b | ||
|
|
d3e2a3f33a | ||
|
|
9c3d53a02d | ||
|
|
22edfd708a | ||
|
|
e25aa78050 | ||
|
|
122e8ac8f4 | ||
|
|
5fc6312ab1 | ||
|
|
83f0a24704 | ||
|
|
13c299533f | ||
|
|
db11db6ff1 | ||
|
|
50863635d6 | ||
|
|
29fa46276d | ||
|
|
7a35baebc7 | ||
|
|
b6f6cee1a9 | ||
|
|
d49e0dadee | ||
|
|
2b315fd855 | ||
|
|
f42949820b | ||
|
|
d2ba99797c | ||
|
|
fd23038b1a | ||
|
|
fe3ec0c183 | ||
|
|
1a0d7a3103 | ||
|
|
c31bffad1b | ||
|
|
dd8f7a77a5 | ||
|
|
eb46e31ffb | ||
|
|
8ea99f0c4f | ||
|
|
f6b376110c | ||
|
|
01d8dc72ad | ||
|
|
445aac2d57 | ||
|
|
391828a2c2 | ||
|
|
02b6b38a56 | ||
|
|
47cdc2e1d8 | ||
|
|
a7d3097f8d | ||
|
|
7ac7aa9ba7 | ||
|
|
f9737c8f40 | ||
|
|
4e39cc59f8 | ||
|
|
28accc4aa0 | ||
|
|
5d10f242bd | ||
|
|
2d5c881639 | ||
|
|
ad63753b5e | ||
|
|
e5a1e26f9d | ||
|
|
113e2e7166 | ||
|
|
9d4880be35 | ||
|
|
8dd9d08209 | ||
|
|
f309a46b8d | ||
|
|
d5bcb0da59 | ||
|
|
0a3cc5a25d | ||
|
|
52735fdd40 | ||
|
|
59fd4d52e1 | ||
|
|
8e78eb15c8 | ||
|
|
b7fc69baa7 | ||
|
|
43153c4e91 | ||
|
|
d059efca25 | ||
|
|
eea14155aa | ||
|
|
4ba8503c60 | ||
|
|
fdee2522bb | ||
|
|
39c861a64b | ||
|
|
333b7e4c9b | ||
|
|
f42a79b5ef | ||
|
|
50012d4866 | ||
|
|
b558b8c5aa | ||
|
|
3447a062b6 | ||
|
|
063a23e738 | ||
|
|
da0150a9ec | ||
|
|
981de8b295 | ||
|
|
fb4c210b41 | ||
|
|
8c68af5e3b | ||
|
|
81b46ab880 | ||
|
|
619b6f2a0e | ||
|
|
25913188a7 | ||
|
|
f3098df84a | ||
|
|
3deed4c271 | ||
|
|
e6b1c238b9 | ||
|
|
626b023ddb | ||
|
|
4c667f75f4 | ||
|
|
e04dd53046 | ||
|
|
d25265c8b7 | ||
|
|
7d8353ffdc | ||
|
|
ba534f18b9 | ||
|
|
864083156a | ||
|
|
8435a0fe16 | ||
|
|
d339dcbfb8 | ||
|
|
cd7ab413f6 | ||
|
|
cea881abdc | ||
|
|
d227a105d8 | ||
|
|
f6ff157bc4 | ||
|
|
21fbd07639 | ||
|
|
de234e8aeb | ||
|
|
95a7707963 | ||
|
|
24b54f6d9a | ||
|
|
b3e37688d9 | ||
|
|
eeba3b4049 | ||
|
|
a0c9731f63 | ||
|
|
af1bf1bbde | ||
|
|
54a7d04217 | ||
|
|
dfa2113db4 | ||
|
|
abe0859691 | ||
|
|
cf91f3c4cf | ||
|
|
175fc73e63 | ||
|
|
1d459803dc | ||
|
|
60e2ee9a6c | ||
|
|
7f1c6caa6e | ||
|
|
7ac17ae7e8 | ||
|
|
52a97e2df7 | ||
|
|
041989549a | ||
|
|
31c37ce1a1 | ||
|
|
74cb2c3769 | ||
|
|
5025e15d14 | ||
|
|
36c24ffe5c | ||
|
|
b3a6b861cd | ||
|
|
c21eb32c70 | ||
|
|
44e6ba039d | ||
|
|
ef75610244 | ||
|
|
ffafd81e1e | ||
|
|
06414d344b | ||
|
|
cb193f1d25 | ||
|
|
4d8179ba08 | ||
|
|
ce767c8429 | ||
|
|
1ae5cc09cb | ||
|
|
9d2fed1250 | ||
|
|
924515ad07 | ||
|
|
4d138ad671 | ||
|
|
615fcfd73b | ||
|
|
f5e62849c9 | ||
|
|
f25fed58e9 | ||
|
|
ca15bc0c75 | ||
|
|
29f004db95 | ||
|
|
97274f15b8 | ||
|
|
8471fb9fa4 | ||
|
|
9ec159e207 | ||
|
|
0456670dac |
2
.github/workflows/docs.yml
vendored
2
.github/workflows/docs.yml
vendored
@@ -42,6 +42,8 @@ jobs:
|
||||
|
||||
- name: Build application
|
||||
run: pnpm run docs:build
|
||||
env:
|
||||
NODE_OPTIONS: '--max-old-space-size=8192'
|
||||
|
||||
- name: Deploy to NuxtHub
|
||||
uses: nuxt-hub/action@v1
|
||||
|
||||
51
.github/workflows/module.yml
vendored
51
.github/workflows/module.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest] # macos-latest, windows-latest
|
||||
os: ${{ github.event_name == 'pull_request' && fromJSON('["ubuntu-latest"]') || fromJSON('["ubuntu-latest", "windows-latest"]') }} # macos-latest
|
||||
node: [22]
|
||||
|
||||
env:
|
||||
@@ -65,8 +65,57 @@ jobs:
|
||||
run: pnpm run dev:vue:build
|
||||
|
||||
- name: Publish
|
||||
# Only publish preview package on ubuntu during PRs
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: pnpx pkg-pr-new publish --compact --no-template --pnpm
|
||||
|
||||
playground:
|
||||
needs: build
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./playground
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest] # macos-latest, windows-latest
|
||||
node: [22]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Store commit SHA
|
||||
run: |
|
||||
echo "COMMIT_SHA=$(echo ${{ github.workflow_sha }} | cut -c1-7)" >> $GITHUB_ENV
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: pnpm
|
||||
|
||||
- name: Install latest nuxt/ui
|
||||
run: pnpm install https://pkg.pr.new/@nuxt/ui@${{ env.COMMIT_SHA }} --lockfile-only
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --ignore-workspace
|
||||
|
||||
- name: Prepare
|
||||
run: pnpm nuxi prepare
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm run typecheck
|
||||
|
||||
starter-nuxt:
|
||||
needs: build
|
||||
|
||||
|
||||
54
.github/workflows/release.yml
vendored
Normal file
54
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
name: release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v3*'
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest] # macos-latest, windows-latest
|
||||
node: [22]
|
||||
|
||||
env:
|
||||
NUXT_GITHUB_TOKEN: ${{ secrets.NUXT_GITHUB_TOKEN }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: pnpm
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Prepare
|
||||
run: pnpm run dev:prepare
|
||||
|
||||
- name: Lint
|
||||
run: pnpm run lint
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm run typecheck
|
||||
|
||||
- name: Test
|
||||
run: pnpm run test run
|
||||
|
||||
- name: Test (vue)
|
||||
run: pnpm run test:vue run
|
||||
|
||||
- name: Publish
|
||||
run: ./scripts/release.sh
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
|
||||
1
.npmrc
1
.npmrc
@@ -1,4 +1,3 @@
|
||||
shamefully-hoist=true
|
||||
auto-install-peers=true
|
||||
ignore-workspace-root-check=true
|
||||
shell-emulator=true
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
"commitMessage": "chore(release): v${version}",
|
||||
"tagName": "v${version}"
|
||||
},
|
||||
"npm": {
|
||||
"publish": false
|
||||
},
|
||||
"github": {
|
||||
"release": true,
|
||||
"releaseName": "v${version}",
|
||||
|
||||
91
CHANGELOG.md
91
CHANGELOG.md
@@ -1,5 +1,96 @@
|
||||
# Changelog
|
||||
|
||||
## [3.1.1](https://github.com/nuxt/ui/compare/v3.1.0...v3.1.1) (2025-05-02)
|
||||
|
||||
### Features
|
||||
|
||||
* **useOverlay:** add `closeAll` method ([#3984](https://github.com/nuxt/ui/issues/3984)) ([ac4c194](https://github.com/nuxt/ui/commit/ac4c1946ec399aec59b4bce9d538e3ff67868abf))
|
||||
* **useOverlay:** add `isOpen` method to check overlay state ([#4041](https://github.com/nuxt/ui/issues/4041)) ([a4f3f6d](https://github.com/nuxt/ui/commit/a4f3f6d531f9c0281f99085a6688d296f8f13f2f))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Calendar:** add `place-items-center` to grid row ([#4034](https://github.com/nuxt/ui/issues/4034)) ([8dfdd63](https://github.com/nuxt/ui/commit/8dfdd63ce3b3a0e904f7c013c774cf9aaf46b240))
|
||||
* **defineShortcuts:** bring back `meta` to `ctrl` convert on non macos platforms ([f3b8b17](https://github.com/nuxt/ui/commit/f3b8b17dc5f43936ef7ffb11c1ed7f9a5f94d0bb)), closes [#3869](https://github.com/nuxt/ui/issues/3869) [#3318](https://github.com/nuxt/ui/issues/3318)
|
||||
* **module:** support `nuxt-nightly` ([#3996](https://github.com/nuxt/ui/issues/3996)) ([bc0a296](https://github.com/nuxt/ui/commit/bc0a296f9d68ca72cd991b11cd3489b63c7b13db))
|
||||
* **NavigationMenu:** remove `sm:w-auto` from content slot ([aebf0b3](https://github.com/nuxt/ui/commit/aebf0b3dca50c51c093cb6abf16c4fd995fc1b39)), closes [#3987](https://github.com/nuxt/ui/issues/3987)
|
||||
* **RadioGroup:** improve items `value` field type ([#3995](https://github.com/nuxt/ui/issues/3995)) ([195773e](https://github.com/nuxt/ui/commit/195773ec7dac12ccc3a0a67867751e8ca634cc04))
|
||||
* **templates:** put back args to watch in dev ([#4033](https://github.com/nuxt/ui/issues/4033)) ([c5bdec0](https://github.com/nuxt/ui/commit/c5bdec0f64963ef602975270a09a1ee795cdacf9))
|
||||
* **theme:** add missing `border-bg` / `divide-bg` utilities ([82b5f32](https://github.com/nuxt/ui/commit/82b5f322ebd8a08e63588122bd4ef567dcb8ba8c))
|
||||
* **theme:** add missing `ring-offset-*` utilities ([#3992](https://github.com/nuxt/ui/issues/3992)) ([e5df026](https://github.com/nuxt/ui/commit/e5df0269935be59df759fe0e1378acb2b0d9014a))
|
||||
* **theme:** define default shades for named tailwindcss colors ([8acf3c5](https://github.com/nuxt/ui/commit/8acf3c51db6c2f9443d04be6ba7d9f062c5cf8ab)), closes [#3977](https://github.com/nuxt/ui/issues/3977)
|
||||
* **theme:** improve app config types for `ui` object ([591d59f](https://github.com/nuxt/ui/commit/591d59fe89f1d9bf016c121bf9160f73fe0a290d)), closes [#3579](https://github.com/nuxt/ui/issues/3579)
|
||||
* **theme:** use `[@theme](https://github.com/theme) inline` to properly reference css variables ([6131871](https://github.com/nuxt/ui/commit/6131871a0d124c5942d60dc5dff20981e8542e51)), closes [#4018](https://github.com/nuxt/ui/issues/4018)
|
||||
* **useOverlay:** improve types and docs ([#4012](https://github.com/nuxt/ui/issues/4012)) ([39e29fc](https://github.com/nuxt/ui/commit/39e29fccf1840c723a13237d65002501b2829b70))
|
||||
|
||||
## [3.1.0](https://github.com/nuxt/ui/compare/v3.0.2...v3.1.0) (2025-04-24)
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **OverlayProvider:** return an overlay instance from `.open()` (#3829)
|
||||
|
||||
### Features
|
||||
|
||||
* **App:** add global `portal` prop ([#3688](https://github.com/nuxt/ui/issues/3688)) ([29fa462](https://github.com/nuxt/ui/commit/29fa46276d6bf69b5b87880c476c6f778c2820bf))
|
||||
* **Carousel:** add `select` event ([#3678](https://github.com/nuxt/ui/issues/3678)) ([22edfd7](https://github.com/nuxt/ui/commit/22edfd708ae3eeadbd4ff6c830cdfd5632948286))
|
||||
* **CheckboxGroup:** new component ([#3862](https://github.com/nuxt/ui/issues/3862)) ([9c3d53a](https://github.com/nuxt/ui/commit/9c3d53a02d6254f6b5c90e5fed826b8aefcdb042))
|
||||
* **components:** add new `content-top` and `content-bottom` slots ([#3886](https://github.com/nuxt/ui/issues/3886)) ([1a46394](https://github.com/nuxt/ui/commit/1a463946681e152aa18372118d0fef4a7d8055a5))
|
||||
* **Form:** add `attach` prop to opt-out of nested form attachement ([#3939](https://github.com/nuxt/ui/issues/3939)) ([1a0d7a3](https://github.com/nuxt/ui/commit/1a0d7a3103cf7591b019ef3ad685e2f3786ef6f2))
|
||||
* **Form:** export loading state ([#3861](https://github.com/nuxt/ui/issues/3861)) ([fdee252](https://github.com/nuxt/ui/commit/fdee2522bb9d8361ff3e9fdd4aa2350be8e49b05))
|
||||
* **InputMenu/SelectMenu:** handle `resetSearchTermOnSelect` ([cea881a](https://github.com/nuxt/ui/commit/cea881abdc139b39df89b503cf2ab872f4246c8f)), closes [#3782](https://github.com/nuxt/ui/issues/3782)
|
||||
* **InputNumber:** add support for `stepSnapping` & `disableWheelChange` props ([#3731](https://github.com/nuxt/ui/issues/3731)) ([f5e6284](https://github.com/nuxt/ui/commit/f5e62849c9313063396ab0e3a9b7d22d98ef69bc))
|
||||
* **locale:** add Bulgarian language ([#3783](https://github.com/nuxt/ui/issues/3783)) ([a0c9731](https://github.com/nuxt/ui/commit/a0c9731f634020e76aa98a9a68d673591d35e8c9))
|
||||
* **locale:** add Kazakh language ([#3875](https://github.com/nuxt/ui/issues/3875)) ([43153c4](https://github.com/nuxt/ui/commit/43153c4e91034b728059e7a9bed05888e48f8890))
|
||||
* **locale:** add Tajik language ([#3850](https://github.com/nuxt/ui/issues/3850)) ([f42a79b](https://github.com/nuxt/ui/commit/f42a79b5efe8dc65430a83799ebb0ee737773820))
|
||||
* **locale:** add Uyghur language ([#3878](https://github.com/nuxt/ui/issues/3878)) ([b7fc69b](https://github.com/nuxt/ui/commit/b7fc69baa718ff65b3988d0fa9f143306fa8fac4))
|
||||
* **Modal/Popover/Slideover:** add `close:prevent` event ([#3958](https://github.com/nuxt/ui/issues/3958)) ([f486423](https://github.com/nuxt/ui/commit/f4864233812eac0ed37e0a2d076a95c285a22c01))
|
||||
* **module:** define default color shades ([#3916](https://github.com/nuxt/ui/issues/3916)) ([7ac7aa9](https://github.com/nuxt/ui/commit/7ac7aa9ba73b6aca1bc29b0de2e95c60b2700135))
|
||||
* **module:** define neutral utilities ([#3629](https://github.com/nuxt/ui/issues/3629)) ([d49e0da](https://github.com/nuxt/ui/commit/d49e0dadeea2a58e05e60b2c461b29ce1d334d2b))
|
||||
* **module:** dynamic `rounded-*` utilities ([#3906](https://github.com/nuxt/ui/issues/3906)) ([f9737c8](https://github.com/nuxt/ui/commit/f9737c8f401bf8bc5307674fad6defe2aeeeb907))
|
||||
* **OverlayProvider:** return an overlay instance from `.open()` ([#3829](https://github.com/nuxt/ui/issues/3829)) ([f3098df](https://github.com/nuxt/ui/commit/f3098df84a3b7f58f7ccc1233bc8b45eab99ee10))
|
||||
* **PinInput:** add `autofocus` / `autofocus-delay` props ([0456670](https://github.com/nuxt/ui/commit/0456670dac1153340220603c8c116e3b71f72ae7)), closes [#3717](https://github.com/nuxt/ui/issues/3717)
|
||||
* **RadioGroup:** add `card` and `table` variants ([#3178](https://github.com/nuxt/ui/issues/3178)) ([4d138ad](https://github.com/nuxt/ui/commit/4d138ad6719a074f5f994006d12745ca05bec9c4))
|
||||
* **Select:** handle `onSelect` field in items ([8640831](https://github.com/nuxt/ui/commit/864083156a79dfb5d0be868658b7f9fc77570178))
|
||||
* **Table:** conditionally apply classes to `tr` and `td` ([#3866](https://github.com/nuxt/ui/issues/3866)) ([80dfa88](https://github.com/nuxt/ui/commit/80dfa88ea442571ee1dc673317cc7baa8cacd8a3))
|
||||
* **Tabs:** add `list-leading` and `list-trailing` slots ([#3837](https://github.com/nuxt/ui/issues/3837)) ([3447a06](https://github.com/nuxt/ui/commit/3447a062b636a469089d6e9bdcfcb3dce9063ee5))
|
||||
* **Textarea:** add `autoresize-delay` prop ([06414d3](https://github.com/nuxt/ui/commit/06414d344b151ad6e1a3225a9f5f1f76d58d319c)), closes [#3730](https://github.com/nuxt/ui/issues/3730)
|
||||
* **Textarea:** add `icon`, `loading`, etc. props to match Input ([cb193f1](https://github.com/nuxt/ui/commit/cb193f1d25b5c73ca03dcf10864800350dd1c290))
|
||||
* **Textarea:** add `resize-none` class with `autoresize` prop ([ffafd81](https://github.com/nuxt/ui/commit/ffafd81e1ed25074430668c792e5e1c6afc22bd0))
|
||||
* **unplugin:** routing support for inertia ([#3845](https://github.com/nuxt/ui/issues/3845)) ([d059efc](https://github.com/nuxt/ui/commit/d059efca258da7ae5116e829189a492824ac1d87))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Accordion:** use `div` instead of `h3` for header tag ([75e4792](https://github.com/nuxt/ui/commit/75e4792f7f00c55229253289c4f806f2b6fc9854)), closes [#3963](https://github.com/nuxt/ui/issues/3963)
|
||||
* **Alert/Toast:** display actions when using slots ([5086363](https://github.com/nuxt/ui/commit/50863635d653c8083772046ddc5b828fba7047d0)), closes [#3950](https://github.com/nuxt/ui/issues/3950)
|
||||
* **Carousel:** move arrows inside container on mobile ([d339dcb](https://github.com/nuxt/ui/commit/d339dcbfb8fe244bd198d247d8448e3ef856dfef)), closes [#3813](https://github.com/nuxt/ui/issues/3813)
|
||||
* **CheckboxGroup:** proxy slots & `ui` prop ([bc06185](https://github.com/nuxt/ui/commit/bc061852822edd2dfb832a46dd6388123ec5771e))
|
||||
* **CommandPalette:** consistent alignement with other components ([d25265c](https://github.com/nuxt/ui/commit/d25265c8b7d34e01af8827d9af5eccb98bf30e9e))
|
||||
* **CommandPalette:** increase input font size to avoid zoom ([d227a10](https://github.com/nuxt/ui/commit/d227a105d8d409ea0753153afaecf639ddb80fed))
|
||||
* **CommandPalette:** prevent hover background on disabled items ([ba534f1](https://github.com/nuxt/ui/commit/ba534f18b94383c97b2654d892ee4b8b024b3fab))
|
||||
* **components:** refactor types after `@nuxt/module-builder` upgrade ([#3855](https://github.com/nuxt/ui/issues/3855)) ([39c861a](https://github.com/nuxt/ui/commit/39c861a64bbd452256ebd1a14a257b94c35855d4))
|
||||
* **components:** respect `transform-origin` in popper content ([#3919](https://github.com/nuxt/ui/issues/3919)) ([01d8dc7](https://github.com/nuxt/ui/commit/01d8dc72adb0b32ad68bb4a98bf24b17f435a89c))
|
||||
* **ContextMenu/DropdownMenu:** handle RTL mode ([#3744](https://github.com/nuxt/ui/issues/3744)) ([1ae5cc0](https://github.com/nuxt/ui/commit/1ae5cc09cb2eca6b6f53eb04db9dcc731b696cae))
|
||||
* **ContextMenuContent/DropdownMenuContent:** remove unwanted `any` ([#3741](https://github.com/nuxt/ui/issues/3741)) ([97274f1](https://github.com/nuxt/ui/commit/97274f15b8bfe457e7e206f81b32e3febf0f875d))
|
||||
* **Form:** input and output type inference ([#3938](https://github.com/nuxt/ui/issues/3938)) ([f429498](https://github.com/nuxt/ui/commit/f42949820be9be9fca41abc653dc12c033e1eeec))
|
||||
* **Form:** loses focus on submit ([#3796](https://github.com/nuxt/ui/issues/3796)) ([8e78eb1](https://github.com/nuxt/ui/commit/8e78eb15c85beef1c814206c4a192d4eb00a7e86))
|
||||
* **InputMenu/Select/SelectMenu:** add `min-w-fit` to `content` slot ([#3922](https://github.com/nuxt/ui/issues/3922)) ([f6b3761](https://github.com/nuxt/ui/commit/f6b376110c8bee2c41ae3137bb972aad402ebff1))
|
||||
* **InputMenu/SelectMenu:** correctly call `onSelect` events ([#3735](https://github.com/nuxt/ui/issues/3735)) ([f25fed5](https://github.com/nuxt/ui/commit/f25fed58e988b304e79cdb536d544d257395cf89))
|
||||
* **InputMenu/SelectMenu:** prevent `disabled` items to be selected ([8435a0f](https://github.com/nuxt/ui/commit/8435a0fe1622eb5b6863b6e4751c9d2d1be36db9)), closes [#3474](https://github.com/nuxt/ui/issues/3474)
|
||||
* **InputMenu/SelectMenu:** remove `valueKey` string case ([9ca213b](https://github.com/nuxt/ui/commit/9ca213bd3340492d7503a34bd142e1f79a697050)), closes [#3949](https://github.com/nuxt/ui/issues/3949) [#3331](https://github.com/nuxt/ui/issues/3331)
|
||||
* **InputMenu/SelectMenu:** support arbitrary `value` ([#3779](https://github.com/nuxt/ui/issues/3779)) ([52a97e2](https://github.com/nuxt/ui/commit/52a97e2df7903f91e3134931eb0d6bd4c528f71f))
|
||||
* **InputMenu:** emit `change` on multiple item removal ([9d2fed1](https://github.com/nuxt/ui/commit/9d2fed125013e3bbfbf9435678729cd05254a5e8)), closes [#3756](https://github.com/nuxt/ui/issues/3756)
|
||||
* **Link:** proxy `download` property ([#3879](https://github.com/nuxt/ui/issues/3879)) ([47cdc2e](https://github.com/nuxt/ui/commit/47cdc2e1d8cd9803ebc954ccae110d62b9a08779))
|
||||
* **NavigationMenu:** add `sm:w-auto` content slot ([abe0859](https://github.com/nuxt/ui/commit/abe0859691e06564f68335bd82dcd121e976408e)), closes [#3788](https://github.com/nuxt/ui/issues/3788)
|
||||
* **Skeleton:** improve accessibility ([#3613](https://github.com/nuxt/ui/issues/3613)) ([3484832](https://github.com/nuxt/ui/commit/3484832822015a224ce6fbeae5132018875557e6))
|
||||
* **Stepper:** ui prop override on `icon` and `content` slots ([1d45980](https://github.com/nuxt/ui/commit/1d459803dc052a16b8966ee89c71646bf6ef1c16)), closes [#3785](https://github.com/nuxt/ui/issues/3785)
|
||||
* **Table:** improve `data` reactivity ([#3967](https://github.com/nuxt/ui/issues/3967)) ([6e27304](https://github.com/nuxt/ui/commit/6e27304d8ca459a04667bac404084264a8cf58fd))
|
||||
* **Table:** pass header `colspan` to `th` ([#3926](https://github.com/nuxt/ui/issues/3926)) ([122e8ac](https://github.com/nuxt/ui/commit/122e8ac8f41ba093cd350c3ce642263263f77296))
|
||||
* **Tree:** simplify reusable template types ([#3836](https://github.com/nuxt/ui/issues/3836)) ([3deed4c](https://github.com/nuxt/ui/commit/3deed4c271cad4adc2a4c47d5dd02e95a14ce11a))
|
||||
* **types:** allow color identifiers with dashes ([#3896](https://github.com/nuxt/ui/issues/3896)) ([e5a1e26](https://github.com/nuxt/ui/commit/e5a1e26f9db763b54caed4ca313f44d1b5fe269d))
|
||||
* **types:** handle `ClassValue` in `ui` prop ([eea1415](https://github.com/nuxt/ui/commit/eea14155aa612649bc969d806ec5df4295945c70)), closes [#3860](https://github.com/nuxt/ui/issues/3860)
|
||||
* **types:** improve dynamic slots ([#3857](https://github.com/nuxt/ui/issues/3857)) ([8dd9d08](https://github.com/nuxt/ui/commit/8dd9d08209e47a7d9a5654db4fb936b4cbcfc021))
|
||||
* **usePortal:** adjust portal target resolution logic ([#3954](https://github.com/nuxt/ui/issues/3954)) ([db11db6](https://github.com/nuxt/ui/commit/db11db6ff1ce4b27a66aaa03f07870ba36426181))
|
||||
* **vite:** vitest skipping nuxt imports transformations ([#3925](https://github.com/nuxt/ui/issues/3925)) ([c31bffa](https://github.com/nuxt/ui/commit/c31bffad1b8afeda584bca8c73bb7f790eb12a9f))
|
||||
|
||||
## [3.0.2](https://github.com/nuxt/ui/compare/v3.0.1...v3.0.2) (2025-03-28)
|
||||
|
||||
### Features
|
||||
|
||||
15
README.md
15
README.md
@@ -16,6 +16,10 @@ Nuxt UI harnesses the combined strengths of [Reka UI](https://reka-ui.com/), [Ta
|
||||
> [!NOTE]
|
||||
> You are on the `v3` development branch, check out the [v2 branch](https://github.com/nuxt/ui/tree/v2) for Nuxt UI v2.
|
||||
|
||||
> [!TIP]
|
||||
> **Looking for more components ?**
|
||||
> Check out [Nuxt UI Pro](https://ui.nuxt.com/pro), a collection of premium Vue components, composables, and utilities built on top of Nuxt UI for faster and more powerful app development.
|
||||
|
||||
## Documentation
|
||||
|
||||
Visit https://ui.nuxt.com to explore the documentation.
|
||||
@@ -104,6 +108,17 @@ app.mount('#app')
|
||||
|
||||
Learn more in the [installation guide](https://ui.nuxt.com/getting-started/installation/vue).
|
||||
|
||||
## Contribution
|
||||
|
||||
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/contribution) to share your suggestions.
|
||||
|
||||
## Local Development
|
||||
|
||||
Follow the docs to [set up your local development environment](https://ui.nuxt.com/getting-started/contribution#local-development) and contribute.
|
||||
|
||||
## Credits
|
||||
|
||||
- [nuxt/nuxt](https://github.com/nuxt/nuxt)
|
||||
|
||||
@@ -7,10 +7,13 @@ export default defineBuildConfig({
|
||||
'./src/vite'
|
||||
],
|
||||
rollup: {
|
||||
emitCJS: true
|
||||
},
|
||||
replace: {
|
||||
'process.env.DEV': 'false'
|
||||
replace: {
|
||||
delimiters: ['', ''],
|
||||
values: {
|
||||
// Used in development to import directly from theme
|
||||
'process.argv.includes(\'--uiDev\')': 'false'
|
||||
}
|
||||
}
|
||||
},
|
||||
hooks: {
|
||||
'mkdist:entry:options'(ctx, entry, options) {
|
||||
|
||||
@@ -31,13 +31,10 @@ const component = ({ name, primitive, pro, prose, content }) => {
|
||||
? `
|
||||
<script lang="ts">
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}'
|
||||
import { tv } from '${pro ? '#ui/utils/tv' : '../utils/tv'}'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfig${camelName} = _appConfig as AppConfig & { ${key}: { ${prose ? 'prose: { ' : ''}${camelName}: Partial<typeof theme> } }${prose ? ' }' : ''}
|
||||
|
||||
const ${camelName} = tv({ extend: tv(theme), ...(appConfig${camelName}.${key}?.${prose ? 'prose?.' : ''}${camelName} || {}) })
|
||||
type ${upperName} = ComponentConfig<typeof theme, AppConfig, ${upperName}${pro ? `, '${key}'` : ''}>
|
||||
|
||||
export interface ${upperName}Props {
|
||||
/**
|
||||
@@ -46,7 +43,7 @@ export interface ${upperName}Props {
|
||||
*/
|
||||
as?: any
|
||||
class?: any
|
||||
ui?: Partial<typeof ${camelName}.slots>
|
||||
ui?: ${upperName}['slots']
|
||||
}
|
||||
|
||||
export interface ${upperName}Slots {
|
||||
@@ -55,12 +52,17 @@ export interface ${upperName}Slots {
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { Primitive } from 'reka-ui'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { tv } from '../utils/tv'
|
||||
|
||||
const props = defineProps<${upperName}Props>()
|
||||
defineSlots<${upperName}Slots>()
|
||||
|
||||
const ui = ${camelName}()
|
||||
const appConfig = useAppConfig() as ${upperName}['AppConfig']
|
||||
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.${camelName} || {}) })())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -71,22 +73,16 @@ const ui = ${camelName}()
|
||||
`
|
||||
: `
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from 'tailwind-variants'
|
||||
import type { ${upperName}RootProps, ${upperName}RootEmits } from 'reka-ui'
|
||||
import type { AppConfig } from '@nuxt/schema'
|
||||
import _appConfig from '#build/app.config'
|
||||
import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}'
|
||||
import { tv } from '${pro ? '#ui/utils/tv' : '../utils/tv'}'
|
||||
import type { ComponentConfig } from '../types/utils'
|
||||
|
||||
const appConfig${camelName} = _appConfig as AppConfig & { ${key}: { ${prose ? 'prose: { ' : ''}${camelName}: Partial<typeof theme> } }${prose ? ' }' : ''}
|
||||
|
||||
const ${camelName} = tv({ extend: tv(theme), ...(appConfig${camelName}.${key}?.${prose ? 'prose?.' : ''}${camelName} || {}) })
|
||||
|
||||
type ${upperName}Variants = VariantProps<typeof ${camelName}>
|
||||
type ${upperName} = ComponentConfig<typeof theme, AppConfig, ${upperName}${pro ? `, '${key}'` : ''}>
|
||||
|
||||
export interface ${upperName}Props extends Pick<${upperName}RootProps> {
|
||||
class?: any
|
||||
ui?: Partial<typeof ${camelName}.slots>
|
||||
ui?: ${upperName}['slots']
|
||||
}
|
||||
|
||||
export interface ${upperName}Emits extends ${upperName}RootEmits {}
|
||||
@@ -95,16 +91,21 @@ export interface ${upperName}Slots {}
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { ${upperName}Root, useForwardPropsEmits } from 'reka-ui'
|
||||
import { reactivePick } from '@vueuse/core'
|
||||
import { useAppConfig } from '#imports'
|
||||
import { tv } from '../utils/tv'
|
||||
|
||||
const props = defineProps<${upperName}Props>()
|
||||
const emits = defineEmits<${upperName}Emits>()
|
||||
const slots = defineSlots<${upperName}Slots>()
|
||||
|
||||
const appConfig = useAppConfig() as ${upperName}['AppConfig']
|
||||
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props), emits)
|
||||
|
||||
const ui = ${camelName}()
|
||||
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.${camelName} || {}) })())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -12,6 +12,7 @@ const { data: files } = useLazyAsyncData('search', () => queryCollectionSearchSe
|
||||
})
|
||||
|
||||
const links = useLinks()
|
||||
const searchLinks = useSearchLinks()
|
||||
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; }`)
|
||||
const blackAsPrimary = computed(() => appConfig.theme.blackAsPrimary ? `:root { --ui-primary: black; } .dark { --ui-primary: white; }` : ':root {}')
|
||||
@@ -22,7 +23,7 @@ useHead({
|
||||
{ key: 'theme-color', name: 'theme-color', content: color }
|
||||
],
|
||||
link: [
|
||||
{ rel: 'icon', type: 'image/svg+xml', href: '/icon.svg' },
|
||||
// { rel: 'icon', type: 'image/svg+xml', href: '/icon.svg' },
|
||||
{ rel: 'canonical', href: `https://ui.nuxt.com${withoutTrailingSlash(route.path)}` }
|
||||
],
|
||||
style: [
|
||||
@@ -39,6 +40,8 @@ useServerSeoMeta({
|
||||
twitterCard: 'summary_large_image'
|
||||
})
|
||||
|
||||
useFaviconFromTheme()
|
||||
|
||||
const { frameworks, modules } = useSharedData()
|
||||
const { mappedNavigation, filteredNavigation } = useContentNavigation(navigation)
|
||||
|
||||
@@ -64,6 +67,7 @@ provide('navigation', mappedNavigation)
|
||||
|
||||
<ClientOnly>
|
||||
<LazyUContentSearch
|
||||
:links="searchLinks"
|
||||
:files="files"
|
||||
:groups="[{
|
||||
id: 'framework',
|
||||
@@ -83,5 +87,5 @@ provide('navigation', mappedNavigation)
|
||||
</template>
|
||||
|
||||
<style>
|
||||
/* Safelist (do not remove): [&>div]:*:my-0 [&>div]:*:w-full h-64 !px-0 !py-0 !pt-0 !pb-0 !p-0 !justify-start !justify-end !min-h-96 h-136 */
|
||||
/* Safelist (do not remove): [&>div]:*:my-0 [&>div]:*:w-full h-64 !px-0 !py-0 !pt-0 !pb-0 !p-0 !justify-start !justify-end !min-h-96 h-136 max-h-[341px] */
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@import "tailwindcss" theme(static) source("../../../..");
|
||||
@import "@nuxt/ui-pro";
|
||||
|
||||
@source "../../../content";
|
||||
@source "../../../content/**/*";
|
||||
@source "../../../node_modules/.c12";
|
||||
|
||||
@theme static {
|
||||
|
||||
@@ -23,27 +23,27 @@ onMounted(() => {
|
||||
@reference "../assets/css/main.css";
|
||||
|
||||
.carbon :deep(#carbonads) {
|
||||
@apply relative border border-(--ui-border) rounded-[calc(var(--ui-radius)*1.5)] hover:bg-(--ui-bg-elevated)/50 w-full transition-colors min-h-[220px] p-2;
|
||||
@apply relative border border-default rounded-md hover:bg-elevated/50 w-full transition-colors min-h-[220px] p-2;
|
||||
|
||||
.carbon-img {
|
||||
@apply flex justify-center w-full;
|
||||
|
||||
& > img {
|
||||
@apply !max-w-full w-full rounded-(--ui-radius);
|
||||
@apply !max-w-full w-full rounded-sm;
|
||||
}
|
||||
}
|
||||
|
||||
.carbon-text {
|
||||
@apply text-sm text-(--ui-text-muted) transition-colors text-center text-pretty flex pt-2;
|
||||
@apply text-sm text-muted transition-colors text-center text-pretty flex pt-2;
|
||||
}
|
||||
|
||||
.carbon-poweredby {
|
||||
@apply block text-xs text-center text-(--ui-text-muted) pt-2;
|
||||
@apply block text-xs text-center text-muted pt-2;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.carbon-text {
|
||||
@apply text-(--ui-text);
|
||||
@apply text-default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
const route = useRoute()
|
||||
|
||||
const links = [{
|
||||
label: 'Figma',
|
||||
to: '/figma'
|
||||
label: 'Team',
|
||||
to: '/team'
|
||||
}, {
|
||||
label: 'Roadmap',
|
||||
to: '/roadmap'
|
||||
@@ -22,8 +22,8 @@ const links = [{
|
||||
|
||||
<UFooter>
|
||||
<template #left>
|
||||
<NuxtLink 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 to="https://github.com/nuxt/ui" target="_blank" class="text-sm text-muted">
|
||||
Published under <span class="text-highlighted">MIT License</span>
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@ watch(framework, () => {
|
||||
:content="false"
|
||||
color="neutral"
|
||||
:ui="{
|
||||
indicator: 'bg-(--ui-bg)',
|
||||
trigger: 'px-1 data-[state=active]:text-(--ui-text-highlighted)'
|
||||
indicator: 'bg-default',
|
||||
trigger: 'px-1 data-[state=active]:text-highlighted'
|
||||
}"
|
||||
size="xs"
|
||||
@update:model-value="(framework = $event as string)"
|
||||
|
||||
@@ -22,14 +22,26 @@ onMounted(() => {
|
||||
|
||||
const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
|
||||
|
||||
const githubLink = computed(() => {
|
||||
return `https://github.com/nuxt/${value.value}`
|
||||
})
|
||||
|
||||
const desktopLinks = computed(() => props.links.map(({ icon, ...link }) => link))
|
||||
const mobileLinks = computed(() => props.links.map(link => ({ ...link, defaultOpen: link.children && route.path.startsWith(link.to as string) })))
|
||||
const mobileLinks = computed(() => [
|
||||
...props.links.map(link => ({ ...link, defaultOpen: link.children && route.path.startsWith(link.to as string) })),
|
||||
{
|
||||
label: 'Open on GitHub',
|
||||
to: githubLink.value,
|
||||
icon: 'i-simple-icons-github',
|
||||
target: '_blank'
|
||||
}
|
||||
])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UHeader :ui="{ left: 'min-w-0' }" :menu="{ shouldScaleBackground: true }">
|
||||
<template #left>
|
||||
<NuxtLink to="/" class="flex items-end gap-2 font-bold text-xl text-(--ui-text-highlighted) min-w-0 focus-visible:outline-(--ui-primary) shrink-0" aria-label="Nuxt UI">
|
||||
<NuxtLink to="/" class="flex items-end gap-2 font-bold text-xl text-highlighted min-w-0 focus-visible:outline-primary shrink-0" aria-label="Nuxt UI">
|
||||
<Logo v-if="route.path === '/'" class="w-auto h-6 shrink-0" />
|
||||
<LogoPro v-else-if="route.path.startsWith('/pro')" class="w-auto h-6 shrink-0" />
|
||||
<template v-else>
|
||||
@@ -51,7 +63,7 @@ const mobileLinks = computed(() => props.links.map(link => ({ ...link, defaultOp
|
||||
trailing-icon="i-lucide-chevron-down"
|
||||
size="xs"
|
||||
class="-mb-[6px] font-semibold rounded-full truncate"
|
||||
:class="[open && 'bg-(--ui-primary)/15 ']"
|
||||
:class="[open && 'bg-primary/15 ']"
|
||||
:ui="{
|
||||
trailingIcon: ['transition-transform duration-200', open ? 'rotate-180' : undefined].filter(Boolean).join(' ')
|
||||
}"
|
||||
@@ -73,7 +85,7 @@ const mobileLinks = computed(() => props.links.map(link => ({ ...link, defaultOp
|
||||
:key="value"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
:to="`https://github.com/nuxt/${value}`"
|
||||
:to="githubLink"
|
||||
target="_blank"
|
||||
icon="i-simple-icons-github"
|
||||
aria-label="GitHub"
|
||||
@@ -96,7 +108,7 @@ const mobileLinks = computed(() => props.links.map(link => ({ ...link, defaultOp
|
||||
<span class="inline-flex items-center gap-0.5">
|
||||
{{ link.title }}
|
||||
|
||||
<sup v-if="link.module === 'ui-pro'" class="text-[8px] font-medium text-(--ui-primary)">PRO</sup>
|
||||
<sup v-if="link.module === 'ui-pro'" class="text-[8px] font-medium text-primary">PRO</sup>
|
||||
</span>
|
||||
</template>
|
||||
</UContentNavigation>
|
||||
|
||||
@@ -19,8 +19,8 @@ watch(module, () => {
|
||||
:content="false"
|
||||
color="neutral"
|
||||
:ui="{
|
||||
indicator: 'bg-(--ui-bg)',
|
||||
trigger: 'px-1 data-[state=active]:text-(--ui-text-highlighted)'
|
||||
indicator: 'bg-default',
|
||||
trigger: 'px-1 data-[state=active]:text-highlighted'
|
||||
}"
|
||||
size="xs"
|
||||
@update:model-value="(module = $event as string)"
|
||||
|
||||
@@ -14,6 +14,7 @@ const props = withDefaults(defineProps<{
|
||||
color?: string
|
||||
size?: { min: number, max: number }
|
||||
speed?: 'slow' | 'normal' | 'fast'
|
||||
isIndex?: boolean
|
||||
}>(), {
|
||||
starCount: 50,
|
||||
color: 'var(--ui-primary)',
|
||||
@@ -21,7 +22,8 @@ const props = withDefaults(defineProps<{
|
||||
min: 1,
|
||||
max: 3
|
||||
}),
|
||||
speed: 'normal'
|
||||
speed: 'normal',
|
||||
isIndex: false
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
@@ -53,7 +55,7 @@ const twinkleDuration = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="absolute pointer-events-none z-[-1] inset-y-0 left-4 right-4 lg:right-[50%] overflow-hidden">
|
||||
<div class="absolute pointer-events-none z-[-1] overflow-hidden" :class="isIndex ? 'inset-y-0 left-4 right-4 lg:right-[50%]' : 'inset-0'">
|
||||
<div
|
||||
v-for="star in stars"
|
||||
:key="star.id"
|
||||
|
||||
@@ -153,7 +153,8 @@ const options = computed(() => {
|
||||
const items = propItems.length
|
||||
? propItems.map((item: any) => ({
|
||||
value: item,
|
||||
label: String(item)
|
||||
label: String(item),
|
||||
chip: key.toLowerCase().endsWith('color') ? { color: item } : undefined
|
||||
}))
|
||||
: prop?.type === 'boolean' || prop?.type === 'boolean | undefined'
|
||||
? [{ value: true, label: 'true' }, { value: false, label: 'false' }]
|
||||
@@ -328,16 +329,16 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
|
||||
|
||||
<template>
|
||||
<div class="my-5">
|
||||
<div>
|
||||
<div v-if="options.length" class="flex flex-wrap items-center gap-2.5 border border-(--ui-border-muted) border-b-0 relative rounded-t-[calc(var(--ui-radius)*1.5)] px-4 py-2.5 overflow-x-auto">
|
||||
<div class="relative">
|
||||
<div v-if="options.length" class="flex flex-wrap items-center gap-2.5 border border-muted border-b-0 relative rounded-t-md 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-(--ui-border-accented) rounded-(--ui-radius)"
|
||||
class="inline-flex ring ring-accented rounded-sm"
|
||||
:ui="{
|
||||
wrapper: 'bg-(--ui-bg-elevated)/50 rounded-l-(--ui-radius) flex border-r border-(--ui-border-accented)',
|
||||
label: 'text-(--ui-text-muted) px-2 py-1.5',
|
||||
wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented',
|
||||
label: 'text-muted px-2 py-1.5',
|
||||
container: 'mt-0'
|
||||
}"
|
||||
>
|
||||
@@ -348,7 +349,7 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
|
||||
value-key="value"
|
||||
color="neutral"
|
||||
variant="soft"
|
||||
class="rounded-(--ui-radius) rounded-l-none min-w-12"
|
||||
class="rounded-sm rounded-l-none min-w-12"
|
||||
:class="[option.name.toLowerCase().endsWith('color') && 'pl-6']"
|
||||
:ui="{ itemLeadingChip: 'size-2' }"
|
||||
@update:model-value="setComponentProp(option.name, $event)"
|
||||
@@ -370,14 +371,14 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
|
||||
:model-value="getComponentProp(option.name)"
|
||||
color="neutral"
|
||||
variant="soft"
|
||||
:ui="{ base: 'rounded-(--ui-radius) rounded-l-none min-w-12' }"
|
||||
:ui="{ base: 'rounded-sm rounded-l-none min-w-12' }"
|
||||
@update:model-value="setComponentProp(option.name, $event)"
|
||||
/>
|
||||
</UFormField>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div v-if="component" class="flex justify-center border border-b-0 border-(--ui-border-muted) relative p-4 z-[1]" :class="[!options.length && 'rounded-t-[calc(var(--ui-radius)*1.5)]', props.class, { 'overflow-hidden': props.overflowHidden }]">
|
||||
<div v-if="component" class="flex justify-center border border-b-0 border-muted relative p-4 z-[1]" :class="[!options.length && 'rounded-t-md', props.class, { 'overflow-hidden': props.overflowHidden }]">
|
||||
<component :is="component" v-bind="{ ...componentProps, ...componentEvents }">
|
||||
<template v-for="slot in Object.keys(slots || {})" :key="slot" #[slot]>
|
||||
<slot :name="slot" mdc-unwrap="p">
|
||||
|
||||
@@ -150,8 +150,8 @@ const urlSearchParams = computed(() => {
|
||||
<template>
|
||||
<div ref="el" class="my-5">
|
||||
<template v-if="preview">
|
||||
<div class="border border-(--ui-border-muted) 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, 'overflow-hidden': props.overflowHidden }]">
|
||||
<div v-if="props.options?.length || !!slots.options" class="flex gap-4 p-4 border-b border-(--ui-border-muted)">
|
||||
<div class="border border-muted relative z-[1]" :class="[{ 'border-b-0 rounded-t-md': props.source, 'rounded-md': !props.source, 'overflow-hidden': props.overflowHidden }]">
|
||||
<div v-if="props.options?.length || !!slots.options" class="flex gap-4 p-4 border-b border-muted">
|
||||
<slot name="options" />
|
||||
|
||||
<UFormField
|
||||
@@ -160,10 +160,10 @@ const urlSearchParams = computed(() => {
|
||||
:label="option.label"
|
||||
:name="option.name"
|
||||
size="sm"
|
||||
class="inline-flex ring ring-(--ui-border-accented) rounded-(--ui-radius)"
|
||||
class="inline-flex ring ring-accented rounded-sm"
|
||||
:ui="{
|
||||
wrapper: 'bg-(--ui-bg-elevated)/50 rounded-l-(--ui-radius) flex border-r border-(--ui-border-accented)',
|
||||
label: 'text-(--ui-text-muted) px-2 py-1.5',
|
||||
wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented',
|
||||
label: 'text-muted px-2 py-1.5',
|
||||
container: 'mt-0'
|
||||
}"
|
||||
>
|
||||
@@ -175,7 +175,7 @@ const urlSearchParams = computed(() => {
|
||||
:value-key="option.name.toLowerCase().endsWith('color') ? 'value' : undefined"
|
||||
color="neutral"
|
||||
variant="soft"
|
||||
class="rounded-(--ui-radius) rounded-l-none min-w-12"
|
||||
class="rounded-sm rounded-l-none min-w-12"
|
||||
:multiple="option.multiple"
|
||||
:class="[option.name.toLowerCase().endsWith('color') && 'pl-6']"
|
||||
:ui="{ itemLeadingChip: 'size-2' }"
|
||||
@@ -196,7 +196,7 @@ const urlSearchParams = computed(() => {
|
||||
:model-value="get(optionsValues, option.name)"
|
||||
color="neutral"
|
||||
variant="soft"
|
||||
:ui="{ base: 'rounded-(--ui-radius) rounded-l-none min-w-12' }"
|
||||
:ui="{ base: 'rounded-sm rounded-l-none min-w-12' }"
|
||||
@update:model-value="set(optionsValues, option.name, $event)"
|
||||
/>
|
||||
</UFormField>
|
||||
|
||||
@@ -112,7 +112,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-(--ui-text-toned) mt-1" :cache-key="`${kebabCase(route.path)}-${prop.name}-description`" />
|
||||
<MDC v-if="prop.description" :value="prop.description" class="text-toned mt-1" :cache-key="`${kebabCase(route.path)}-${prop.name}-description`" />
|
||||
|
||||
<ComponentPropsLinks v-if="prop.tags?.length" :prop="prop" />
|
||||
<ComponentPropsSchema v-if="prop.schema" :prop="prop" :ignore="ignore" />
|
||||
|
||||
@@ -43,7 +43,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-(--ui-text-muted) my-1" :cache-key="`${kebabCase(route.path)}-${prop.name}-${schemaProp.name}-description`" />
|
||||
<MDC v-if="schemaProp.description" :value="schemaProp.description" class="text-muted my-1" :cache-key="`${kebabCase(route.path)}-${prop.name}-${schemaProp.name}-description`" />
|
||||
</ProseLi>
|
||||
</ProseUl>
|
||||
</ProseCollapsible>
|
||||
|
||||
@@ -36,7 +36,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-(--ui-text-toned) mt-1" :cache-key="`${kebabCase(route.path)}-${slot.name}-description`" />
|
||||
<MDC v-if="slot.description" :value="slot.description" class="text-toned mt-1" :cache-key="`${kebabCase(route.path)}-${slot.name}-description`" />
|
||||
</ProseTd>
|
||||
</ProseTr>
|
||||
</ProseTbody>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<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">
|
||||
<div class="relative overflow-hidden rounded-sm border border-dashed border-accented opacity-75 px-4 flex items-center justify-center">
|
||||
<svg class="absolute inset-0 h-full w-full stroke-inverted/10" fill="none">
|
||||
<defs>
|
||||
<pattern
|
||||
id="pattern-5c1e4f0e-62d5-498b-8ff0-cf77bb448c8e"
|
||||
|
||||
@@ -22,6 +22,7 @@ function getEmojiFlag(locale: string): string {
|
||||
hi: 'in', // Hindi -> India
|
||||
hy: 'am', // Armenian -> Armenia
|
||||
ja: 'jp', // Japanese -> Japan
|
||||
kk: 'kz', // Kazakh -> Kazakhstan
|
||||
km: 'kh', // Khmer -> Cambodia
|
||||
ko: 'kr', // Korean -> South Korea
|
||||
nb: 'no', // Norwegian Bokmål -> Norway
|
||||
|
||||
@@ -20,7 +20,7 @@ const items: AccordionItem[] = [
|
||||
<template>
|
||||
<UAccordion :items="items">
|
||||
<template #content="{ item }">
|
||||
<p class="pb-3.5 text-sm text-(--ui-text-muted)">
|
||||
<p class="pb-3.5 text-sm text-muted">
|
||||
This is the {{ item.label }} panel.
|
||||
</p>
|
||||
</template>
|
||||
|
||||
@@ -24,7 +24,7 @@ const items = [
|
||||
<template>
|
||||
<UAccordion :items="items">
|
||||
<template #colors="{ item }">
|
||||
<p class="text-sm pb-3.5 text-(--ui-primary)">
|
||||
<p class="text-sm pb-3.5 text-primary">
|
||||
{{ item.content }}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
@@ -11,7 +11,6 @@ const items = shallowRef<AccordionItem[]>([
|
||||
{
|
||||
label: 'Colors',
|
||||
icon: 'i-lucide-swatch-book',
|
||||
slot: 'colors' as const,
|
||||
content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
|
||||
},
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<ULink
|
||||
to="https://github.com/benjamincanac"
|
||||
target="_blank"
|
||||
class="hover:ring-(--ui-primary) transition"
|
||||
class="hover:ring-primary transition"
|
||||
raw
|
||||
>
|
||||
<UAvatar
|
||||
@@ -15,7 +15,7 @@
|
||||
<ULink
|
||||
to="https://github.com/romhml"
|
||||
target="_blank"
|
||||
class="hover:ring-(--ui-primary) transition"
|
||||
class="hover:ring-primary transition"
|
||||
raw
|
||||
>
|
||||
<UAvatar
|
||||
@@ -27,7 +27,7 @@
|
||||
<ULink
|
||||
to="https://github.com/noook"
|
||||
target="_blank"
|
||||
class="hover:ring-(--ui-primary) transition"
|
||||
class="hover:ring-primary transition"
|
||||
raw
|
||||
>
|
||||
<UAvatar
|
||||
|
||||
@@ -1,26 +1,35 @@
|
||||
<script setup lang="ts">
|
||||
import type { BreadcrumbItem } from '@nuxt/ui'
|
||||
|
||||
const items = [{
|
||||
label: 'Home',
|
||||
to: '/'
|
||||
}, {
|
||||
slot: 'dropdown' as const,
|
||||
icon: 'i-lucide-ellipsis',
|
||||
children: [{
|
||||
label: 'Documentation'
|
||||
}, {
|
||||
label: 'Themes'
|
||||
}, {
|
||||
label: 'GitHub'
|
||||
}]
|
||||
}, {
|
||||
label: 'Components',
|
||||
to: '/components'
|
||||
}, {
|
||||
label: 'Breadcrumb',
|
||||
to: '/components/breadcrumb'
|
||||
}] satisfies BreadcrumbItem[]
|
||||
const items = [
|
||||
{
|
||||
label: 'Home',
|
||||
to: '/'
|
||||
},
|
||||
{
|
||||
slot: 'dropdown' as const,
|
||||
icon: 'i-lucide-ellipsis',
|
||||
children: [
|
||||
{
|
||||
label: 'Documentation'
|
||||
},
|
||||
{
|
||||
label: 'Themes'
|
||||
},
|
||||
{
|
||||
label: 'GitHub'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Components',
|
||||
to: '/components'
|
||||
},
|
||||
{
|
||||
label: 'Breadcrumb',
|
||||
to: '/components/breadcrumb'
|
||||
}
|
||||
] satisfies BreadcrumbItem[]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import type { BreadcrumbItem } from '@nuxt/ui'
|
||||
|
||||
const items: BreadcrumbItem[] = [{
|
||||
label: 'Home',
|
||||
to: '/'
|
||||
}, {
|
||||
label: 'Components',
|
||||
to: '/components'
|
||||
}, {
|
||||
label: 'Breadcrumb',
|
||||
to: '/components/breadcrumb'
|
||||
}]
|
||||
const items: BreadcrumbItem[] = [
|
||||
{
|
||||
label: 'Home',
|
||||
to: '/'
|
||||
},
|
||||
{
|
||||
label: 'Components',
|
||||
to: '/components'
|
||||
},
|
||||
{
|
||||
label: 'Breadcrumb',
|
||||
to: '/components/breadcrumb'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UBreadcrumb :items="items">
|
||||
<template #separator>
|
||||
<span class="mx-2 text-(--ui-text-muted)">/</span>
|
||||
<span class="mx-2 text-muted">/</span>
|
||||
</template>
|
||||
</UBreadcrumb>
|
||||
</template>
|
||||
|
||||
@@ -1,23 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import type { DropdownMenuItem } from '@nuxt/ui'
|
||||
|
||||
const items: DropdownMenuItem[] = [{
|
||||
label: 'Team',
|
||||
icon: 'i-lucide-users'
|
||||
}, {
|
||||
label: 'Invite users',
|
||||
icon: 'i-lucide-user-plus',
|
||||
children: [{
|
||||
label: 'Invite by email',
|
||||
icon: 'i-lucide-send-horizontal'
|
||||
}, {
|
||||
label: 'Invite by link',
|
||||
icon: 'i-lucide-link'
|
||||
}]
|
||||
}, {
|
||||
label: 'New team',
|
||||
icon: 'i-lucide-plus'
|
||||
}]
|
||||
const items: DropdownMenuItem[] = [
|
||||
{
|
||||
label: 'Team',
|
||||
icon: 'i-lucide-users'
|
||||
},
|
||||
{
|
||||
label: 'Invite users',
|
||||
icon: 'i-lucide-user-plus',
|
||||
children: [
|
||||
{
|
||||
label: 'Invite by email',
|
||||
icon: 'i-lucide-send-horizontal'
|
||||
},
|
||||
{
|
||||
label: 'Invite by link',
|
||||
icon: 'i-lucide-link'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'New team',
|
||||
icon: 'i-lucide-plus'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import { CalendarDate } from '@internationalized/date'
|
||||
|
||||
const date = shallowRef(new CalendarDate(2025, 4, 2))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-4">
|
||||
<UCalendar v-model="date" :month-controls="false" :year-controls="false" />
|
||||
|
||||
<div class="flex justify-between gap-4">
|
||||
<UButton color="neutral" variant="outline" @click="date = date.subtract({ months: 1 })">
|
||||
Prev
|
||||
</UButton>
|
||||
|
||||
<UButton color="neutral" variant="outline" @click="date = date.add({ months: 1 })">
|
||||
Next
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,58 @@
|
||||
<script setup lang="ts">
|
||||
const items = [
|
||||
'https://picsum.photos/640/640?random=1',
|
||||
'https://picsum.photos/640/640?random=2',
|
||||
'https://picsum.photos/640/640?random=3',
|
||||
'https://picsum.photos/640/640?random=4',
|
||||
'https://picsum.photos/640/640?random=5',
|
||||
'https://picsum.photos/640/640?random=6'
|
||||
]
|
||||
|
||||
const carousel = useTemplateRef('carousel')
|
||||
const activeIndex = ref(0)
|
||||
|
||||
function onClickPrev() {
|
||||
activeIndex.value--
|
||||
}
|
||||
function onClickNext() {
|
||||
activeIndex.value++
|
||||
}
|
||||
function onSelect(index: number) {
|
||||
activeIndex.value = index
|
||||
}
|
||||
|
||||
function select(index: number) {
|
||||
activeIndex.value = index
|
||||
|
||||
carousel.value?.emblaApi?.scrollTo(index)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex-1 w-full">
|
||||
<UCarousel
|
||||
ref="carousel"
|
||||
v-slot="{ item }"
|
||||
arrows
|
||||
:items="items"
|
||||
:prev="{ onClick: onClickPrev }"
|
||||
:next="{ onClick: onClickNext }"
|
||||
class="w-full max-w-xs mx-auto"
|
||||
@select="onSelect"
|
||||
>
|
||||
<img :src="item" width="320" height="320" class="rounded-lg">
|
||||
</UCarousel>
|
||||
|
||||
<div class="flex gap-1 justify-between pt-4 max-w-xs mx-auto">
|
||||
<div
|
||||
v-for="(item, index) in items"
|
||||
:key="index"
|
||||
class="size-11 opacity-25 hover:opacity-100 transition-opacity"
|
||||
:class="{ 'opacity-100': activeIndex === index }"
|
||||
@click="select(index)"
|
||||
>
|
||||
<img :src="item" width="44" height="44" class="rounded-lg">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,76 +1,79 @@
|
||||
<script setup lang="ts">
|
||||
const groups = [{
|
||||
id: 'settings',
|
||||
items: [
|
||||
{
|
||||
label: 'Profile',
|
||||
icon: 'i-lucide-user',
|
||||
kbds: ['meta', 'P']
|
||||
},
|
||||
{
|
||||
label: 'Billing',
|
||||
icon: 'i-lucide-credit-card',
|
||||
kbds: ['meta', 'B'],
|
||||
slot: 'billing' as const
|
||||
},
|
||||
{
|
||||
label: 'Notifications',
|
||||
icon: 'i-lucide-bell'
|
||||
},
|
||||
{
|
||||
label: 'Security',
|
||||
icon: 'i-lucide-lock'
|
||||
}
|
||||
]
|
||||
}, {
|
||||
id: 'users',
|
||||
label: 'Users',
|
||||
slot: 'users' as const,
|
||||
items: [
|
||||
{
|
||||
label: 'Benjamin Canac',
|
||||
suffix: 'benjamincanac',
|
||||
to: 'https://github.com/benjamincanac',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Sylvain Marroufin',
|
||||
suffix: 'smarroufin',
|
||||
to: 'https://github.com/smarroufin',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Sébastien Chopin',
|
||||
suffix: 'atinux',
|
||||
to: 'https://github.com/atinux',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Romain Hamel',
|
||||
suffix: 'romhml',
|
||||
to: 'https://github.com/romhml',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Haytham A. Salama',
|
||||
suffix: 'Haythamasalama',
|
||||
to: 'https://github.com/Haythamasalama',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Daniel Roe',
|
||||
suffix: 'danielroe',
|
||||
to: 'https://github.com/danielroe',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Neil Richter',
|
||||
suffix: 'noook',
|
||||
to: 'https://github.com/noook',
|
||||
target: '_blank'
|
||||
}
|
||||
]
|
||||
}]
|
||||
const groups = [
|
||||
{
|
||||
id: 'settings',
|
||||
items: [
|
||||
{
|
||||
label: 'Profile',
|
||||
icon: 'i-lucide-user',
|
||||
kbds: ['meta', 'P']
|
||||
},
|
||||
{
|
||||
label: 'Billing',
|
||||
icon: 'i-lucide-credit-card',
|
||||
kbds: ['meta', 'B'],
|
||||
slot: 'billing' as const
|
||||
},
|
||||
{
|
||||
label: 'Notifications',
|
||||
icon: 'i-lucide-bell'
|
||||
},
|
||||
{
|
||||
label: 'Security',
|
||||
icon: 'i-lucide-lock'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'users',
|
||||
label: 'Users',
|
||||
slot: 'users' as const,
|
||||
items: [
|
||||
{
|
||||
label: 'Benjamin Canac',
|
||||
suffix: 'benjamincanac',
|
||||
to: 'https://github.com/benjamincanac',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Sylvain Marroufin',
|
||||
suffix: 'smarroufin',
|
||||
to: 'https://github.com/smarroufin',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Sébastien Chopin',
|
||||
suffix: 'atinux',
|
||||
to: 'https://github.com/atinux',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Romain Hamel',
|
||||
suffix: 'romhml',
|
||||
to: 'https://github.com/romhml',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Haytham A. Salama',
|
||||
suffix: 'Haythamasalama',
|
||||
to: 'https://github.com/Haythamasalama',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Daniel Roe',
|
||||
suffix: 'danielroe',
|
||||
to: 'https://github.com/danielroe',
|
||||
target: '_blank'
|
||||
},
|
||||
{
|
||||
label: 'Neil Richter',
|
||||
suffix: 'noook',
|
||||
to: 'https://github.com/noook',
|
||||
target: '_blank'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -35,7 +35,7 @@ const items = computed<ContextMenuItem[]>(() => [{
|
||||
|
||||
<template>
|
||||
<UContextMenu :items="items" :ui="{ content: 'w-48' }">
|
||||
<div class="flex items-center justify-center rounded-md border border-dashed border-(--ui-border-accented) text-sm aspect-video w-72">
|
||||
<div class="flex items-center justify-center rounded-md border border-dashed border-accented text-sm aspect-video w-72">
|
||||
Right click here
|
||||
</div>
|
||||
</UContextMenu>
|
||||
|
||||
@@ -28,7 +28,7 @@ const items: ContextMenuItem[][] = [
|
||||
|
||||
<template>
|
||||
<UContextMenu :items="items" :ui="{ content: 'w-48' }">
|
||||
<div class="flex items-center justify-center rounded-md border border-dashed border-(--ui-border-accented) text-sm aspect-video w-72">
|
||||
<div class="flex items-center justify-center rounded-md border border-dashed border-accented text-sm aspect-video w-72">
|
||||
Right click here
|
||||
</div>
|
||||
</UContextMenu>
|
||||
|
||||
@@ -3,19 +3,23 @@ import type { ContextMenuItem } from '@nuxt/ui'
|
||||
|
||||
const loading = ref(true)
|
||||
|
||||
const items: ContextMenuItem[] = [{
|
||||
label: 'Refresh the Page',
|
||||
slot: 'refresh'
|
||||
}, {
|
||||
label: 'Clear Cookies and Refresh'
|
||||
}, {
|
||||
label: 'Clear Cache and Refresh'
|
||||
}]
|
||||
const items = [
|
||||
{
|
||||
label: 'Refresh the Page',
|
||||
slot: 'refresh' as const
|
||||
},
|
||||
{
|
||||
label: 'Clear Cookies and Refresh'
|
||||
},
|
||||
{
|
||||
label: 'Clear Cache and Refresh'
|
||||
}
|
||||
] satisfies ContextMenuItem[]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UContextMenu :items="items" :ui="{ content: 'w-48' }">
|
||||
<div class="flex items-center justify-center rounded-md border border-dashed border-(--ui-border-accented) text-sm aspect-video w-72">
|
||||
<div class="flex items-center justify-center rounded-md border border-dashed border-accented text-sm aspect-video w-72">
|
||||
Right click here
|
||||
</div>
|
||||
|
||||
@@ -24,7 +28,7 @@ const items: ContextMenuItem[] = [{
|
||||
</template>
|
||||
|
||||
<template #refresh-trailing>
|
||||
<UIcon v-if="loading" name="i-lucide-refresh-cw" class="shrink-0 size-5 text-(--ui-primary) animate-spin" />
|
||||
<UIcon v-if="loading" name="i-lucide-refresh-cw" class="shrink-0 size-5 text-primary animate-spin" />
|
||||
</template>
|
||||
</UContextMenu>
|
||||
</template>
|
||||
|
||||
@@ -7,7 +7,7 @@ const open = ref(false)
|
||||
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
|
||||
|
||||
<template #header>
|
||||
<h2 class="text-(--ui-text-highlighted) font-semibold">
|
||||
<h2 class="text-highlighted font-semibold">
|
||||
Drawer non-dismissible
|
||||
</h2>
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<script lang="ts" setup>
|
||||
import { createReusableTemplate, useMediaQuery } from '@vueuse/core'
|
||||
|
||||
const [DefineFormTemplate, ReuseFormTemplate] = createReusableTemplate()
|
||||
const isDesktop = useMediaQuery('(min-width: 768px)')
|
||||
|
||||
const open = ref(false)
|
||||
|
||||
const state = reactive({
|
||||
email: undefined
|
||||
})
|
||||
|
||||
const title = 'Edit profile'
|
||||
const description = 'Make changes to your profile here. Click save when you\'re done.'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DefineFormTemplate>
|
||||
<UForm :state="state" class="space-y-4">
|
||||
<UFormField label="Email" name="email" required>
|
||||
<UInput v-model="state.email" placeholder="shadcn@example.com" required />
|
||||
</UFormField>
|
||||
|
||||
<UButton label="Save changes" type="submit" />
|
||||
</UForm>
|
||||
</DefineFormTemplate>
|
||||
|
||||
<UModal v-if="isDesktop" v-model:open="open" :title="title" :description="description">
|
||||
<UButton label="Edit profile" color="neutral" variant="outline" />
|
||||
|
||||
<template #body>
|
||||
<ReuseFormTemplate />
|
||||
</template>
|
||||
</UModal>
|
||||
|
||||
<UDrawer v-else v-model:open="open" :title="title" :description="description">
|
||||
<UButton label="Edit profile" color="neutral" variant="outline" />
|
||||
|
||||
<template #body>
|
||||
<ReuseFormTemplate />
|
||||
</template>
|
||||
</UDrawer>
|
||||
</template>
|
||||
@@ -21,7 +21,7 @@ const items = [
|
||||
<UButton label="Open" color="neutral" variant="outline" icon="i-lucide-menu" />
|
||||
|
||||
<template #profile-trailing>
|
||||
<UIcon name="i-lucide-badge-check" class="shrink-0 size-5 text-(--ui-primary)" />
|
||||
<UIcon name="i-lucide-badge-check" class="shrink-0 size-5 text-primary" />
|
||||
</template>
|
||||
</UDropdownMenu>
|
||||
</template>
|
||||
|
||||
@@ -14,7 +14,7 @@ const validate = (state: any): FormError[] => {
|
||||
}
|
||||
|
||||
const toast = useToast()
|
||||
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
async function onSubmit(event: FormSubmitEvent<typeof state>) {
|
||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||
console.log(event.data)
|
||||
}
|
||||
|
||||
@@ -30,6 +30,9 @@ const schema = z.object({
|
||||
radioGroup: z.string().refine(value => value === 'option-2', {
|
||||
message: 'Select Option 2'
|
||||
}),
|
||||
checkboxGroup: z.any().refine(values => !!values?.find((option: any) => option === 'option-2'), {
|
||||
message: 'Include Option 2'
|
||||
}),
|
||||
slider: z.number().max(20, { message: 'Must be less than 20' }),
|
||||
pin: z.string().regex(/^\d$/).array().length(5)
|
||||
})
|
||||
@@ -47,7 +50,7 @@ const items = [
|
||||
]
|
||||
|
||||
const toast = useToast()
|
||||
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||
console.log(event.data)
|
||||
}
|
||||
@@ -101,11 +104,14 @@ async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
<UFormField label="Textarea" name="textarea">
|
||||
<UTextarea v-model="state.textarea" class="w-full" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField name="radioGroup">
|
||||
<URadioGroup v-model="state.radioGroup" legend="Radio group" :items="items" />
|
||||
</UFormField>
|
||||
|
||||
<div class="flex gap-4">
|
||||
<UFormField name="radioGroup">
|
||||
<URadioGroup v-model="state.radioGroup" legend="Radio group" :items="items" />
|
||||
</UFormField>
|
||||
<UFormField name="checkboxGroup">
|
||||
<UCheckboxGroup v-model="state.checkboxGroup" legend="Checkbox group" :items="items" />
|
||||
</UFormField>
|
||||
</div>
|
||||
<UFormField name="pin" label="Pin Input" :error-pattern="/(pin)\..*/">
|
||||
<UPinInput v-model="state.pin" />
|
||||
</UFormField>
|
||||
|
||||
@@ -15,7 +15,7 @@ const state = reactive({
|
||||
})
|
||||
|
||||
const toast = useToast()
|
||||
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
async function onSubmit(event: FormSubmitEvent<typeof state>) {
|
||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||
console.log(event.data)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ type NestedSchema = z.output<typeof nestedSchema>
|
||||
const state = reactive<Partial<Schema & NestedSchema>>({ })
|
||||
|
||||
const toast = useToast()
|
||||
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||
console.log(event.data)
|
||||
}
|
||||
@@ -39,7 +39,7 @@ async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
<UCheckbox v-model="state.news" name="news" label="Register to our newsletter" @update:model-value="state.email = undefined" />
|
||||
</div>
|
||||
|
||||
<UForm v-if="state.news" :state="state" :schema="nestedSchema">
|
||||
<UForm v-if="state.news" :state="state" :schema="nestedSchema" attach>
|
||||
<UFormField label="Email" name="email">
|
||||
<UInput v-model="state.email" placeholder="john@lennon.com" />
|
||||
</UFormField>
|
||||
|
||||
@@ -34,7 +34,7 @@ function removeItem() {
|
||||
|
||||
const toast = useToast()
|
||||
|
||||
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||
console.log(event.data)
|
||||
}
|
||||
@@ -51,7 +51,14 @@ async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
<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">
|
||||
<UForm
|
||||
v-for="item, count in state.items"
|
||||
:key="count"
|
||||
:state="item"
|
||||
:schema="itemSchema"
|
||||
attach
|
||||
class="flex gap-2"
|
||||
>
|
||||
<UFormField :label="!count ? 'Description' : undefined" name="description">
|
||||
<UInput v-model="item.description" />
|
||||
</UFormField>
|
||||
|
||||
@@ -14,7 +14,7 @@ const validate = (state: any): FormError[] => {
|
||||
}
|
||||
|
||||
const toast = useToast()
|
||||
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||
async function onSubmit(event: FormSubmitEvent<typeof state>) {
|
||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||
console.log(event.data)
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
<template #item-label="{ item }">
|
||||
{{ item.label }}
|
||||
|
||||
<span class="text-(--ui-text-muted)">
|
||||
<span class="text-muted">
|
||||
{{ item.email }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
@@ -15,7 +15,7 @@ const domain = ref(domains[0])
|
||||
}"
|
||||
>
|
||||
<template #leading>
|
||||
<p class="text-sm text-(--ui-text-muted)">
|
||||
<p class="text-sm text-muted">
|
||||
https://
|
||||
</p>
|
||||
</template>
|
||||
|
||||
@@ -13,7 +13,7 @@ const maxLength = 15
|
||||
<template #trailing>
|
||||
<div
|
||||
id="character-count"
|
||||
class="text-xs text-(--ui-text-muted) tabular-nums"
|
||||
class="text-xs text-muted tabular-nums"
|
||||
aria-live="polite"
|
||||
role="status"
|
||||
>
|
||||
|
||||
@@ -4,8 +4,8 @@ const value = ref('')
|
||||
|
||||
<template>
|
||||
<UInput v-model="value" placeholder="" :ui="{ base: 'peer' }">
|
||||
<label class="pointer-events-none absolute left-0 -top-2.5 text-(--ui-text-highlighted) text-xs font-medium px-1.5 transition-all peer-focus:-top-2.5 peer-focus:text-(--ui-text-highlighted) peer-focus:text-xs peer-focus:font-medium peer-placeholder-shown:text-sm peer-placeholder-shown:text-(--ui-text-dimmed) peer-placeholder-shown:top-1.5 peer-placeholder-shown:font-normal">
|
||||
<span class="inline-flex bg-(--ui-bg) px-1">Email address</span>
|
||||
<label class="pointer-events-none absolute left-0 -top-2.5 text-highlighted text-xs font-medium px-1.5 transition-all peer-focus:-top-2.5 peer-focus:text-highlighted peer-focus:text-xs peer-focus:font-medium peer-placeholder-shown:text-sm peer-placeholder-shown:text-dimmed peer-placeholder-shown:top-1.5 peer-placeholder-shown:font-normal">
|
||||
<span class="inline-flex bg-default px-1">Email address</span>
|
||||
</label>
|
||||
</UInput>
|
||||
</template>
|
||||
|
||||
@@ -77,7 +77,7 @@ const text = computed(() => {
|
||||
v-for="(req, index) in strength"
|
||||
:key="index"
|
||||
class="flex items-center gap-0.5"
|
||||
:class="req.met ? 'text-(--ui-success)' : 'text-(--ui-text-muted)'"
|
||||
:class="req.met ? 'text-success' : 'text-muted'"
|
||||
>
|
||||
<UIcon :name="req.met ? 'i-lucide-circle-check' : 'i-lucide-circle-x'" class="size-4 shrink-0" />
|
||||
|
||||
|
||||
@@ -13,7 +13,9 @@ const modal = overlay.create(LazyModalExample, {
|
||||
})
|
||||
|
||||
async function open() {
|
||||
const shouldIncrement = await modal.open()
|
||||
const instance = modal.open()
|
||||
|
||||
const shouldIncrement = await instance.result
|
||||
|
||||
if (shouldIncrement) {
|
||||
count.value++
|
||||
|
||||
@@ -65,6 +65,7 @@ const items = [
|
||||
class="w-full justify-center"
|
||||
:ui="{
|
||||
viewport: 'sm:w-(--reka-navigation-menu-viewport-width)',
|
||||
content: 'sm:w-auto',
|
||||
childList: 'sm:w-96',
|
||||
childLinkDescription: 'text-balance line-clamp-2'
|
||||
}"
|
||||
@@ -76,11 +77,11 @@ const items = [
|
||||
</li>
|
||||
|
||||
<li v-for="child in item.children" :key="child.label">
|
||||
<ULink class="text-sm text-left rounded-md p-3 transition-colors hover:bg-(--ui-bg-elevated)/50">
|
||||
<p class="font-medium text-(--ui-text-highlighted)">
|
||||
<ULink class="text-sm text-left rounded-md p-3 transition-colors hover:bg-elevated/50">
|
||||
<p class="font-medium text-highlighted">
|
||||
{{ child.label }}
|
||||
</p>
|
||||
<p class="text-(--ui-text-muted) line-clamp-2">
|
||||
<p class="text-muted line-clamp-2">
|
||||
{{ child.description }}
|
||||
</p>
|
||||
</ULink>
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import type { NavigationMenuItem } from '@nuxt/ui'
|
||||
|
||||
const items: NavigationMenuItem[] = [
|
||||
const items = [
|
||||
{
|
||||
label: 'Guide',
|
||||
icon: 'i-lucide-book-open'
|
||||
|
||||
},
|
||||
{
|
||||
label: 'Composables',
|
||||
icon: 'i-lucide-database'
|
||||
|
||||
},
|
||||
{
|
||||
label: 'Components',
|
||||
icon: 'i-lucide-box',
|
||||
slot: 'components'
|
||||
slot: 'components' as const
|
||||
}
|
||||
]
|
||||
] satisfies NavigationMenuItem[]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -40,7 +40,7 @@ const label = ref([])
|
||||
multiple
|
||||
placeholder="Search labels..."
|
||||
:groups="[{ id: 'labels', items }]"
|
||||
:ui="{ input: '[&>input]:h-8' }"
|
||||
:ui="{ input: '[&>input]:h-8 [&>input]:text-sm' }"
|
||||
/>
|
||||
</template>
|
||||
</UPopover>
|
||||
|
||||
@@ -8,7 +8,7 @@ const open = ref(false)
|
||||
|
||||
<template #content>
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<h2 class="text-(--ui-text-highlighted) font-semibold">
|
||||
<h2 class="text-highlighted font-semibold">
|
||||
Popover non-dismissible
|
||||
</h2>
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
<template #item-label="{ item }">
|
||||
{{ item.label }}
|
||||
|
||||
<span class="text-(--ui-text-muted)">
|
||||
<span class="text-muted">
|
||||
{{ item.email }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
@@ -13,7 +13,9 @@ const slideover = overlay.create(LazySlideoverExample, {
|
||||
})
|
||||
|
||||
async function open() {
|
||||
const shouldIncrement = await slideover.open()
|
||||
const instance = slideover.open()
|
||||
|
||||
const shouldIncrement = await instance.result
|
||||
|
||||
if (shouldIncrement) {
|
||||
count.value++
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
<script setup lang="ts">
|
||||
import type { StepperItem } from '@nuxt/ui'
|
||||
|
||||
const items: StepperItem[] = [
|
||||
const items = [
|
||||
{
|
||||
slot: 'address',
|
||||
slot: 'address' as const,
|
||||
title: 'Address',
|
||||
description: 'Add your address here',
|
||||
icon: 'i-lucide-house'
|
||||
}, {
|
||||
slot: 'shipping',
|
||||
slot: 'shipping' as const,
|
||||
title: 'Shipping',
|
||||
description: 'Set your preferred shipping method',
|
||||
icon: 'i-lucide-truck'
|
||||
}, {
|
||||
slot: 'checkout',
|
||||
slot: 'checkout' as const,
|
||||
title: 'Checkout',
|
||||
description: 'Confirm your order'
|
||||
}
|
||||
]
|
||||
] satisfies StepperItem[]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -3,17 +3,14 @@ import type { StepperItem } from '@nuxt/ui'
|
||||
|
||||
const items: StepperItem[] = [
|
||||
{
|
||||
slot: 'address',
|
||||
title: 'Address',
|
||||
description: 'Add your address here',
|
||||
icon: 'i-lucide-house'
|
||||
}, {
|
||||
slot: 'shipping',
|
||||
title: 'Shipping',
|
||||
description: 'Set your preferred shipping method',
|
||||
icon: 'i-lucide-truck'
|
||||
}, {
|
||||
slot: 'checkout',
|
||||
title: 'Checkout',
|
||||
description: 'Confirm your order'
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ const columnFilters = ref([{
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col flex-1 w-full">
|
||||
<div class="flex px-4 py-3.5 border-b border-(--ui-border-accented)">
|
||||
<div class="flex px-4 py-3.5 border-b border-accented">
|
||||
<UInput
|
||||
:model-value="(table?.tableApi?.getColumn('email')?.getFilterValue() as string)"
|
||||
class="max-w-sm"
|
||||
|
||||
@@ -131,7 +131,7 @@ function getHeader(column: Column<Payment>, label: string) {
|
||||
'variant': 'ghost',
|
||||
label,
|
||||
'icon': isSorted ? (isSorted === 'asc' ? 'i-lucide-arrow-up-narrow-wide' : 'i-lucide-arrow-down-wide-narrow') : 'i-lucide-arrow-up-down',
|
||||
'class': '-mx-2.5 data-[state=open]:bg-(--ui-bg-elevated)',
|
||||
'class': '-mx-2.5 data-[state=open]:bg-elevated',
|
||||
'aria-label': `Sort by ${isSorted === 'asc' ? 'descending' : 'ascending'}`
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ const columnVisibility = ref({
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col flex-1 w-full">
|
||||
<div class="flex justify-end px-4 py-3.5 border-b border-(--ui-border-accented)">
|
||||
<div class="flex justify-end px-4 py-3.5 border-b border-accented">
|
||||
<UDropdownMenu
|
||||
:items="table?.tableApi?.getAllColumns().filter(column => column.getCanHide()).map(column => ({
|
||||
label: upperFirst(column.id),
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
<script setup lang="ts">
|
||||
import type { TableColumn } from '@nuxt/ui'
|
||||
import { useSortable } from '@vueuse/integrations/useSortable.mjs'
|
||||
|
||||
type Payment = {
|
||||
id: string
|
||||
date: string
|
||||
email: string
|
||||
amount: number
|
||||
}
|
||||
|
||||
const data = ref<Payment[]>([{
|
||||
id: '4600',
|
||||
date: '2024-03-11T15:30:00',
|
||||
email: 'james.anderson@example.com',
|
||||
amount: 594
|
||||
}, {
|
||||
id: '4599',
|
||||
date: '2024-03-11T10:10:00',
|
||||
email: 'mia.white@example.com',
|
||||
amount: 276
|
||||
}, {
|
||||
id: '4598',
|
||||
date: '2024-03-11T08:50:00',
|
||||
email: 'william.brown@example.com',
|
||||
amount: 315
|
||||
}, {
|
||||
id: '4597',
|
||||
date: '2024-03-10T19:45:00',
|
||||
email: 'emma.davis@example.com',
|
||||
amount: 529
|
||||
}])
|
||||
|
||||
const columns: TableColumn<Payment>[] = [{
|
||||
accessorKey: 'id',
|
||||
header: '#',
|
||||
cell: ({ row }) => `#${row.getValue('id')}`
|
||||
}, {
|
||||
accessorKey: 'date',
|
||||
header: 'Date',
|
||||
cell: ({ row }) => {
|
||||
return new Date(row.getValue('date')).toLocaleString('en-US', {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false
|
||||
})
|
||||
}
|
||||
}, {
|
||||
accessorKey: 'email',
|
||||
header: 'Email'
|
||||
}, {
|
||||
accessorKey: 'amount',
|
||||
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
||||
cell: ({ row }) => {
|
||||
const amount = Number.parseFloat(row.getValue('amount'))
|
||||
const formatted = new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'EUR'
|
||||
}).format(amount)
|
||||
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||
}
|
||||
}]
|
||||
|
||||
useSortable('.my-table-tbody', data, {
|
||||
animation: 150
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<UTable
|
||||
ref="table"
|
||||
:data="data"
|
||||
:columns="columns"
|
||||
:ui="{
|
||||
tbody: 'my-table-tbody'
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -265,7 +265,7 @@ function randomize() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex-1 divide-y divide-(--ui-border-accented) w-full">
|
||||
<div class="flex-1 divide-y divide-accented w-full">
|
||||
<div class="flex items-center gap-2 px-4 py-3.5 overflow-x-auto">
|
||||
<UInput
|
||||
:model-value="(table?.tableApi?.getColumn('email')?.getFilterValue() as string)"
|
||||
@@ -313,7 +313,7 @@ function randomize() {
|
||||
</template>
|
||||
</UTable>
|
||||
|
||||
<div class="px-4 py-3.5 text-sm text-(--ui-text-muted)">
|
||||
<div class="px-4 py-3.5 text-sm text-muted">
|
||||
{{ table?.tableApi?.getFilteredSelectedRowModel().rows.length || 0 }} of
|
||||
{{ table?.tableApi?.getFilteredRowModel().rows.length || 0 }} row(s) selected.
|
||||
</div>
|
||||
|
||||
@@ -36,7 +36,7 @@ const columns: TableColumn<User>[] = [{
|
||||
size: 'lg'
|
||||
}),
|
||||
h('div', undefined, [
|
||||
h('p', { class: 'font-medium text-(--ui-text-highlighted)' }, row.original.name),
|
||||
h('p', { class: 'font-medium text-highlighted' }, row.original.name),
|
||||
h('p', { class: '' }, `@${row.original.username}`)
|
||||
])
|
||||
])
|
||||
|
||||
@@ -95,7 +95,7 @@ const globalFilter = ref('45')
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col flex-1 w-full">
|
||||
<div class="flex px-4 py-3.5 border-b border-(--ui-border-accented)">
|
||||
<div class="flex px-4 py-3.5 border-b border-accented">
|
||||
<UInput
|
||||
v-model="globalFilter"
|
||||
class="max-w-sm"
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
<script setup lang="ts">
|
||||
import type { TableColumn } from '@nuxt/ui'
|
||||
import { useInfiniteScroll } from '@vueuse/core'
|
||||
|
||||
const UAvatar = resolveComponent('UAvatar')
|
||||
|
||||
type User = {
|
||||
id: number
|
||||
firstName: string
|
||||
username: string
|
||||
email: string
|
||||
image: string
|
||||
}
|
||||
|
||||
type UserResponse = {
|
||||
users: User[]
|
||||
total: number
|
||||
skip: number
|
||||
limit: number
|
||||
}
|
||||
|
||||
const skip = ref(0)
|
||||
|
||||
const { data, status, execute } = await useFetch('https://dummyjson.com/users?limit=10&select=firstName,username,email,image', {
|
||||
key: 'table-users-infinite-scroll',
|
||||
params: { skip },
|
||||
transform: (data?: UserResponse) => {
|
||||
return data?.users
|
||||
},
|
||||
lazy: true,
|
||||
immediate: false
|
||||
})
|
||||
|
||||
const columns: TableColumn<User>[] = [{
|
||||
accessorKey: 'id',
|
||||
header: 'ID'
|
||||
}, {
|
||||
accessorKey: 'image',
|
||||
header: 'Avatar',
|
||||
cell: ({ row }) => h(UAvatar, { src: row.original.image })
|
||||
}, {
|
||||
accessorKey: 'firstName',
|
||||
header: 'First name'
|
||||
}, {
|
||||
accessorKey: 'email',
|
||||
header: 'Email'
|
||||
}, {
|
||||
accessorKey: 'username',
|
||||
header: 'Username'
|
||||
}]
|
||||
|
||||
const users = ref<User[]>([])
|
||||
|
||||
watch(data, () => {
|
||||
users.value = [
|
||||
...users.value,
|
||||
...(data.value || [])
|
||||
]
|
||||
})
|
||||
|
||||
execute()
|
||||
|
||||
const table = useTemplateRef<ComponentPublicInstance>('table')
|
||||
|
||||
onMounted(() => {
|
||||
useInfiniteScroll(table.value?.$el, () => {
|
||||
skip.value += 10
|
||||
}, {
|
||||
distance: 200,
|
||||
canLoadMore: () => {
|
||||
return status.value !== 'pending'
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<UTable
|
||||
ref="table"
|
||||
:data="users"
|
||||
:columns="columns"
|
||||
:loading="status === 'pending'"
|
||||
sticky
|
||||
class="flex-1 h-80"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -162,7 +162,7 @@ const pagination = ref({
|
||||
class="flex-1"
|
||||
/>
|
||||
|
||||
<div class="flex justify-center border-t border-(--ui-border) pt-4">
|
||||
<div class="flex justify-center border-t border-default pt-4">
|
||||
<UPagination
|
||||
:default-page="(table?.tableApi?.getState().pagination.pageIndex || 0) + 1"
|
||||
:items-per-page="table?.tableApi?.getState().pagination.pageSize"
|
||||
|
||||
@@ -112,7 +112,7 @@ const expanded = ref({ 1: true })
|
||||
v-model:expanded="expanded"
|
||||
:data="data"
|
||||
:columns="columns"
|
||||
:ui="{ tr: 'data-[expanded=true]:bg-(--ui-bg-elevated)/50' }"
|
||||
:ui="{ tr: 'data-[expanded=true]:bg-elevated/50' }"
|
||||
class="flex-1"
|
||||
>
|
||||
<template #expanded="{ row }">
|
||||
|
||||
@@ -122,7 +122,7 @@ function onSelect(row: TableRow<Payment>, e?: Event) {
|
||||
@select="onSelect"
|
||||
/>
|
||||
|
||||
<div class="px-4 py-3.5 border-t border-[var(--ui-border-accented)] text-sm text-[var(--ui-text-muted)]">
|
||||
<div class="px-4 py-3.5 border-t border-accented text-sm text-muted">
|
||||
{{ table?.tableApi?.getFilteredSelectedRowModel().rows.length || 0 }} of
|
||||
{{ table?.tableApi?.getFilteredRowModel().rows.length || 0 }} row(s) selected.
|
||||
</div>
|
||||
|
||||
@@ -113,7 +113,7 @@ const rowSelection = ref({ 1: true })
|
||||
:columns="columns"
|
||||
/>
|
||||
|
||||
<div class="px-4 py-3.5 border-t border-(--ui-border-accented) text-sm text-(--ui-text-muted)">
|
||||
<div class="px-4 py-3.5 border-t border-accented text-sm text-muted">
|
||||
{{ table?.tableApi?.getFilteredSelectedRowModel().rows.length || 0 }} of
|
||||
{{ table?.tableApi?.getFilteredRowModel().rows.length || 0 }} row(s) selected.
|
||||
</div>
|
||||
|
||||
@@ -97,7 +97,7 @@ function getDropdownActions(user: User): DropdownMenuItem[][] {
|
||||
<div class="flex items-center gap-3">
|
||||
<UAvatar :src="`https://i.pravatar.cc/120?img=${row.original.id}`" size="lg" :alt="`${row.original.name} avatar`" />
|
||||
<div>
|
||||
<p class="font-medium text-(--ui-text-highlighted)">
|
||||
<p class="font-medium text-highlighted">
|
||||
{{ row.original.name }}
|
||||
</p>
|
||||
<p>
|
||||
|
||||
@@ -28,7 +28,7 @@ const state = reactive({
|
||||
<template>
|
||||
<UTabs :items="items" variant="link" class="gap-4 w-full" :ui="{ trigger: 'flex-1' }">
|
||||
<template #account="{ item }">
|
||||
<p class="text-(--ui-text-muted) mb-4">
|
||||
<p class="text-muted mb-4">
|
||||
{{ item.description }}
|
||||
</p>
|
||||
|
||||
@@ -45,7 +45,7 @@ const state = reactive({
|
||||
</template>
|
||||
|
||||
<template #password="{ item }">
|
||||
<p class="text-(--ui-text-muted) mb-4">
|
||||
<p class="text-muted mb-4">
|
||||
{{ item.description }}
|
||||
</p>
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ const appConfig = useAppConfig()
|
||||
<UFormField
|
||||
label="toaster.duration"
|
||||
size="sm"
|
||||
class="inline-flex ring ring-(--ui-border-accented) rounded-(--ui-radius)"
|
||||
class="inline-flex ring ring-accented rounded-sm"
|
||||
:ui="{
|
||||
wrapper: 'bg-(--ui-bg-elevated)/50 rounded-l-(--ui-radius) flex border-r border-(--ui-border-accented)',
|
||||
label: 'text-(--ui-text-muted) px-2 py-1.5',
|
||||
wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented',
|
||||
label: 'text-muted px-2 py-1.5',
|
||||
container: 'mt-0'
|
||||
}"
|
||||
>
|
||||
@@ -18,7 +18,7 @@ const appConfig = useAppConfig()
|
||||
v-model="appConfig.toaster.duration"
|
||||
color="neutral"
|
||||
variant="soft"
|
||||
:ui="{ base: 'rounded-(--ui-radius) rounded-l-none min-w-12' }"
|
||||
:ui="{ base: 'rounded-sm rounded-l-none min-w-12' }"
|
||||
/>
|
||||
</UFormField>
|
||||
</div>
|
||||
|
||||
@@ -7,10 +7,10 @@ const appConfig = useAppConfig()
|
||||
<UFormField
|
||||
label="toaster.expand"
|
||||
size="sm"
|
||||
class="inline-flex ring ring-(--ui-border-accented) rounded-(--ui-radius)"
|
||||
class="inline-flex ring ring-accented rounded-sm"
|
||||
:ui="{
|
||||
wrapper: 'bg-(--ui-bg-elevated)/50 rounded-l-(--ui-radius) flex border-r border-(--ui-border-accented)',
|
||||
label: 'text-(--ui-text-muted) px-2 py-1.5',
|
||||
wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented',
|
||||
label: 'text-muted px-2 py-1.5',
|
||||
container: 'mt-0'
|
||||
}"
|
||||
>
|
||||
@@ -19,7 +19,7 @@ const appConfig = useAppConfig()
|
||||
:items="[true, false]"
|
||||
color="neutral"
|
||||
variant="soft"
|
||||
class="rounded-(--ui-radius) rounded-l-none min-w-12"
|
||||
class="rounded-sm rounded-l-none min-w-12"
|
||||
:search-input="false"
|
||||
/>
|
||||
</UFormField>
|
||||
|
||||
@@ -10,10 +10,10 @@ const appConfig = useAppConfig()
|
||||
<UFormField
|
||||
label="toaster.position"
|
||||
size="sm"
|
||||
class="inline-flex ring ring-(--ui-border-accented) rounded-(--ui-radius)"
|
||||
class="inline-flex ring ring-accented rounded-sm"
|
||||
:ui="{
|
||||
wrapper: 'bg-(--ui-bg-elevated)/50 rounded-l-(--ui-radius) flex border-r border-(--ui-border-accented)',
|
||||
label: 'text-(--ui-text-muted) px-2 py-1.5',
|
||||
wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented',
|
||||
label: 'text-muted px-2 py-1.5',
|
||||
container: 'mt-0'
|
||||
}"
|
||||
>
|
||||
@@ -22,7 +22,7 @@ const appConfig = useAppConfig()
|
||||
:items="positions"
|
||||
color="neutral"
|
||||
variant="soft"
|
||||
class="rounded-(--ui-radius) rounded-l-none min-w-12"
|
||||
class="rounded-sm rounded-l-none min-w-12"
|
||||
:search-input="false"
|
||||
/>
|
||||
</UFormField>
|
||||
|
||||
@@ -6,21 +6,23 @@ const items = [
|
||||
label: 'app/',
|
||||
slot: 'app' as const,
|
||||
defaultExpanded: true,
|
||||
children: [{
|
||||
label: 'composables/',
|
||||
children: [
|
||||
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
|
||||
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'components/',
|
||||
defaultExpanded: true,
|
||||
children: [
|
||||
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
|
||||
]
|
||||
}]
|
||||
children: [
|
||||
{
|
||||
label: 'composables/',
|
||||
children: [
|
||||
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
|
||||
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'components/',
|
||||
defaultExpanded: true,
|
||||
children: [
|
||||
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
|
||||
|
||||
@@ -5,22 +5,24 @@ const items: TreeItem[] = [
|
||||
{
|
||||
label: 'app/',
|
||||
value: 'app',
|
||||
children: [{
|
||||
label: 'composables/',
|
||||
value: 'composables',
|
||||
children: [
|
||||
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
|
||||
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'components/',
|
||||
value: 'components',
|
||||
children: [
|
||||
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
|
||||
]
|
||||
}]
|
||||
children: [
|
||||
{
|
||||
label: 'composables/',
|
||||
value: 'composables',
|
||||
children: [
|
||||
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
|
||||
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'components/',
|
||||
value: 'components',
|
||||
children: [
|
||||
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
|
||||
|
||||
@@ -5,21 +5,23 @@ const items: TreeItem[] = [
|
||||
{
|
||||
label: 'app/',
|
||||
defaultExpanded: true,
|
||||
children: [{
|
||||
label: 'composables/',
|
||||
children: [
|
||||
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
|
||||
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'components/',
|
||||
defaultExpanded: true,
|
||||
children: [
|
||||
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
|
||||
]
|
||||
}]
|
||||
children: [
|
||||
{
|
||||
label: 'composables/',
|
||||
children: [
|
||||
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
|
||||
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'components/',
|
||||
defaultExpanded: true,
|
||||
children: [
|
||||
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
|
||||
|
||||
@@ -8,21 +8,23 @@ const items: TreeItem[] = [
|
||||
onSelect: (e: Event) => {
|
||||
e.preventDefault()
|
||||
},
|
||||
children: [{
|
||||
label: 'composables/',
|
||||
children: [
|
||||
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
|
||||
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'components/',
|
||||
defaultExpanded: true,
|
||||
children: [
|
||||
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
|
||||
]
|
||||
}]
|
||||
children: [
|
||||
{
|
||||
label: 'composables/',
|
||||
children: [
|
||||
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
|
||||
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'components/',
|
||||
defaultExpanded: true,
|
||||
children: [
|
||||
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
|
||||
|
||||
@@ -8,21 +8,23 @@ const items: TreeItem[] = [
|
||||
onToggle: (e: Event) => {
|
||||
e.preventDefault()
|
||||
},
|
||||
children: [{
|
||||
label: 'composables/',
|
||||
children: [
|
||||
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
|
||||
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'components/',
|
||||
defaultExpanded: true,
|
||||
children: [
|
||||
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
|
||||
]
|
||||
}]
|
||||
children: [
|
||||
{
|
||||
label: 'composables/',
|
||||
children: [
|
||||
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
|
||||
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'components/',
|
||||
defaultExpanded: true,
|
||||
children: [
|
||||
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
|
||||
|
||||
@@ -20,7 +20,7 @@ const { width } = useElementSize(el)
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="isolate rounded-full relative circle w-full aspect-[1/1] p-8 sm:p-12 md:p-14 lg:p-10 xl:p-16 before:absolute before:inset-px before:bg-(--ui-bg) before:rounded-full z-(--level)"
|
||||
class="isolate rounded-full relative circle w-full aspect-[1/1] p-8 sm:p-12 md:p-14 lg:p-10 xl:p-16 before:absolute before:inset-px before:bg-default before:rounded-full z-(--level)"
|
||||
:class="{ 'animation-paused': paused }"
|
||||
:style="{
|
||||
'--duration': `${((level + 1) * 8)}s`,
|
||||
@@ -65,7 +65,7 @@ const { width } = useElementSize(el)
|
||||
:src="`https://ipx.nuxt.com/s_56x56/gh_avatar/${contributor.username}`"
|
||||
:srcset="`https://ipx.nuxt.com/s_112x112/gh_avatar/${contributor.username} 2x`"
|
||||
:alt="contributor.username"
|
||||
class="ring-2 ring-(--ui-border) lg:hover:ring-(--ui-border-inverted) transition rounded-full size-7"
|
||||
class="ring-2 ring-default lg:hover:ring-inverted transition rounded-full size-7"
|
||||
loading="lazy"
|
||||
>
|
||||
</NuxtLink>
|
||||
|
||||
@@ -69,7 +69,7 @@ function setBlackAsPrimary(value: boolean) {
|
||||
:variant="open ? 'soft' : 'ghost'"
|
||||
square
|
||||
aria-label="Color picker"
|
||||
:ui="{ leadingIcon: 'text-(--ui-primary)' }"
|
||||
:ui="{ leadingIcon: 'text-primary' }"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -111,7 +111,7 @@ function setBlackAsPrimary(value: boolean) {
|
||||
v-for="color in neutralColors"
|
||||
:key="color"
|
||||
:label="color"
|
||||
:chip="color"
|
||||
:chip="color === 'neutral' ? 'old-neutral' : color"
|
||||
:selected="neutral === color"
|
||||
@click="neutral = color"
|
||||
/>
|
||||
|
||||
@@ -18,8 +18,8 @@ const slots = defineSlots<{
|
||||
variant="outline"
|
||||
:icon="icon"
|
||||
:label="label"
|
||||
class="capitalize ring-(--ui-border) rounded-[calc(var(--ui-radius))] text-[11px]"
|
||||
:class="[selected ? 'bg-(--ui-bg-elevated)' : 'hover:bg-(--ui-bg-elevated)/50']"
|
||||
class="capitalize ring-default rounded-sm text-[11px]"
|
||||
:class="[selected ? 'bg-elevated' : 'hover:bg-elevated/50']"
|
||||
>
|
||||
<template v-if="chip || !!slots.leading" #leading>
|
||||
<slot name="leading">
|
||||
|
||||
54
docs/app/composables/useFaviconFromTheme.ts
Normal file
54
docs/app/composables/useFaviconFromTheme.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { onMounted, watch } from 'vue'
|
||||
import FaviconSvg from 'public/icon.svg?raw'
|
||||
|
||||
export function useFaviconFromTheme() {
|
||||
const colorMode = useColorMode()
|
||||
|
||||
function generateFaviconSvg(color: string) {
|
||||
const parser = new DOMParser()
|
||||
const doc = parser.parseFromString(FaviconSvg, 'image/svg+xml')
|
||||
const svg = doc.documentElement
|
||||
|
||||
svg.querySelectorAll('path').forEach((path) => {
|
||||
path.setAttribute('fill', color)
|
||||
})
|
||||
|
||||
return new XMLSerializer().serializeToString(svg)
|
||||
}
|
||||
|
||||
function updateFavicon() {
|
||||
const root = document.documentElement
|
||||
const color = getComputedStyle(root).getPropertyValue('--ui-primary').trim() || '#00DC82'
|
||||
|
||||
const svg = generateFaviconSvg(color)
|
||||
const encoded = `data:image/svg+xml,${encodeURIComponent(svg)}`
|
||||
|
||||
useFavicon(encoded)
|
||||
}
|
||||
|
||||
function setupMutationObserver() {
|
||||
const styleTag = document.getElementById('nuxt-ui-colors')
|
||||
if (!styleTag) return
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
updateFavicon()
|
||||
})
|
||||
|
||||
observer.observe(styleTag, {
|
||||
characterData: true,
|
||||
subtree: true,
|
||||
childList: true
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
watch(colorMode, () => {
|
||||
updateFavicon()
|
||||
}, {
|
||||
immediate: true,
|
||||
flush: 'post'
|
||||
})
|
||||
|
||||
setupMutationObserver()
|
||||
})
|
||||
}
|
||||
@@ -84,10 +84,10 @@ export function useLinks() {
|
||||
label: 'Community',
|
||||
icon: 'i-lucide-users',
|
||||
children: [{
|
||||
label: 'Roadmap',
|
||||
description: 'Track our development progress in real-time.',
|
||||
icon: 'i-lucide-map',
|
||||
to: '/roadmap'
|
||||
icon: 'i-lucide-presentation',
|
||||
label: 'Showcase',
|
||||
description: 'Check out some amazing projects built with Nuxt UI.',
|
||||
to: '/showcase'
|
||||
}, {
|
||||
label: 'Devtools Integration',
|
||||
description: 'Integrate Nuxt UI with Nuxt Devtools with Compodium.',
|
||||
@@ -112,5 +112,5 @@ export function useLinks() {
|
||||
icon: 'i-lucide-rocket',
|
||||
to: 'https://github.com/nuxt/ui/releases',
|
||||
target: '_blank'
|
||||
}].filter(Boolean))
|
||||
}])
|
||||
}
|
||||
|
||||
66
docs/app/composables/useSearchLinks.ts
Normal file
66
docs/app/composables/useSearchLinks.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
export function useSearchLinks() {
|
||||
return [{
|
||||
label: 'Docs',
|
||||
icon: 'i-lucide-square-play',
|
||||
to: '/getting-started'
|
||||
}, {
|
||||
label: 'Components',
|
||||
icon: 'i-lucide-square-code',
|
||||
to: '/components'
|
||||
}, {
|
||||
icon: 'i-lucide-sparkles',
|
||||
label: 'Pro > Features',
|
||||
description: 'A collection of premium Vue components.',
|
||||
to: '/pro'
|
||||
}, {
|
||||
icon: 'i-lucide-credit-card',
|
||||
label: 'Pro > Pricing',
|
||||
description: 'Free in development, buy when ready to launch.',
|
||||
to: '/pro/pricing'
|
||||
}, {
|
||||
icon: 'i-lucide-panels-top-left',
|
||||
label: 'Pro > Templates',
|
||||
description: 'Official templates made with Nuxt UI Pro.',
|
||||
to: '/pro/templates'
|
||||
}, {
|
||||
icon: 'i-lucide-circle-check',
|
||||
label: 'Pro > Activate',
|
||||
description: 'Enable Nuxt UI Pro in your production projects.',
|
||||
to: '/pro/activate'
|
||||
}, {
|
||||
label: 'Figma',
|
||||
icon: 'i-simple-icons-figma',
|
||||
to: '/figma'
|
||||
}, {
|
||||
icon: 'i-lucide-presentation',
|
||||
label: 'Community > Showcase',
|
||||
description: 'Check out some of the amazing projects built with Nuxt UI.',
|
||||
to: '/showcase'
|
||||
}, {
|
||||
label: 'Community > Contribution',
|
||||
description: 'A comprehensive guide on contributing to Nuxt UI, including project structure, development workflow, and best practices.',
|
||||
icon: 'i-lucide-git-pull-request-arrow',
|
||||
to: '/getting-started/contribution'
|
||||
}, {
|
||||
label: 'Community > Roadmap',
|
||||
description: 'Track our development progress in real-time.',
|
||||
icon: 'i-lucide-map',
|
||||
to: '/roadmap'
|
||||
}, {
|
||||
label: 'Community > Devtools',
|
||||
description: 'Integrate Nuxt UI with Nuxt Devtools with Compodium.',
|
||||
icon: 'i-lucide-code',
|
||||
to: 'https://github.com/romhml/compodium',
|
||||
target: '_blank'
|
||||
}, {
|
||||
label: 'Community > Team',
|
||||
description: 'Meet the team behind Nuxt UI.',
|
||||
icon: 'i-lucide-users',
|
||||
to: '/team'
|
||||
}, {
|
||||
label: 'Releases',
|
||||
icon: 'i-lucide-rocket',
|
||||
to: 'https://github.com/nuxt/ui/releases',
|
||||
target: '_blank'
|
||||
}]
|
||||
}
|
||||
@@ -15,6 +15,7 @@ const { data: files } = useLazyAsyncData('search', () => queryCollectionSearchSe
|
||||
})
|
||||
|
||||
const links = useLinks()
|
||||
const searchLinks = useSearchLinks()
|
||||
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; }`)
|
||||
const blackAsPrimary = computed(() => appConfig.theme.blackAsPrimary ? `:root { --ui-primary: black; } .dark { --ui-primary: white; }` : ':root {}')
|
||||
@@ -25,7 +26,7 @@ useHead({
|
||||
{ key: 'theme-color', name: 'theme-color', content: color }
|
||||
],
|
||||
link: [
|
||||
{ rel: 'icon', type: 'image/svg+xml', href: '/icon.svg' }
|
||||
// { rel: 'icon', type: 'image/svg+xml', href: '/icon.svg' }
|
||||
],
|
||||
style: [
|
||||
{ innerHTML: radius, id: 'nuxt-ui-radius', tagPriority: -2 },
|
||||
@@ -46,6 +47,8 @@ useServerSeoMeta({
|
||||
twitterCard: 'summary_large_image'
|
||||
})
|
||||
|
||||
useFaviconFromTheme()
|
||||
|
||||
const { frameworks, modules } = useSharedData()
|
||||
const { mappedNavigation, filteredNavigation } = useContentNavigation(navigation)
|
||||
|
||||
@@ -66,6 +69,7 @@ provide('navigation', mappedNavigation)
|
||||
|
||||
<ClientOnly>
|
||||
<LazyUContentSearch
|
||||
:links="searchLinks"
|
||||
:files="files"
|
||||
:groups="[{
|
||||
id: 'framework',
|
||||
|
||||
@@ -22,7 +22,7 @@ const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
|
||||
<span class="inline-flex items-center gap-0.5">
|
||||
{{ link.title }}
|
||||
|
||||
<sup v-if="link.module === 'ui-pro'" class="text-[8px] font-medium text-(--ui-primary)">PRO</sup>
|
||||
<sup v-if="link.module === 'ui-pro'" class="text-[8px] font-medium text-primary">PRO</sup>
|
||||
</span>
|
||||
</template>
|
||||
</UContentNavigation>
|
||||
|
||||
@@ -101,7 +101,7 @@ design_system:
|
||||
@import "@nuxt/ui";
|
||||
|
||||
:root {
|
||||
--ui-radius: var(--radius-sm);
|
||||
--ui-radius: 0.25rem;
|
||||
--ui-container: 90rem;
|
||||
--ui-bg: var(--ui-color-neutral-50);
|
||||
--ui-text: var(--ui-color-neutral-900);
|
||||
@@ -176,7 +176,11 @@ community:
|
||||
links:
|
||||
- label: Star on GitHub
|
||||
color: neutral
|
||||
variant: outline
|
||||
to: https://github.com/nuxt/ui
|
||||
target: _blank
|
||||
icon: i-lucide-star
|
||||
- label: Meet the team
|
||||
color: neutral
|
||||
variant: outline
|
||||
to: /team
|
||||
trailingIcon: i-lucide-arrow-right
|
||||
|
||||
@@ -67,9 +67,9 @@ if (!import.meta.prerender) {
|
||||
|
||||
const type = page.value?.path.includes('components') ? 'Vue Component ' : page.value?.path.includes('composables') ? 'Vue Composable ' : ''
|
||||
useSeoMeta({
|
||||
titleTemplate: `%s ${type}- Nuxt UI ${page.value.module === 'ui-pro' ? 'Pro' : ''} v3${page.value.framework === 'vue' ? ' for Vue' : ''}`,
|
||||
titleTemplate: `%s ${type}- Nuxt UI ${page.value.module === 'ui-pro' ? 'Pro' : ''} ${page.value.framework === 'vue' ? ' for Vue' : ''}`,
|
||||
title: page.value.navigation?.title ? page.value.navigation.title : page.value.title,
|
||||
ogTitle: `${page.value.navigation?.title ? page.value.navigation.title : page.value.title} ${type}- Nuxt UI ${page.value.module === 'ui-pro' ? 'Pro' : ''} v3${page.value.framework === 'vue' ? ' for Vue' : ''}`,
|
||||
ogTitle: `${page.value.navigation?.title ? page.value.navigation.title : page.value.title} ${type}- Nuxt UI ${page.value.module === 'ui-pro' ? 'Pro' : ''} ${page.value.framework === 'vue' ? ' for Vue' : ''}`,
|
||||
description: page.value.description,
|
||||
ogDescription: page.value.description
|
||||
})
|
||||
@@ -112,7 +112,7 @@ const communityLinks = computed(() => [{
|
||||
to: 'https://nuxt.lemonsqueezy.com/affiliates',
|
||||
target: '_blank'
|
||||
}, {
|
||||
icon: 'i-lucide-life-buoy',
|
||||
icon: 'i-lucide-git-pull-request-arrow',
|
||||
label: 'Contribution',
|
||||
to: '/getting-started/contribution'
|
||||
}, {
|
||||
@@ -130,7 +130,7 @@ const communityLinks = computed(() => [{
|
||||
</template>
|
||||
|
||||
<template #title>
|
||||
{{ page.title }}<sup v-if="page.module === 'ui-pro'" class="ml-1 text-xs align-super font-medium text-(--ui-primary)">PRO</sup>
|
||||
{{ page.title }}<sup v-if="page.module === 'ui-pro'" class="ml-1 text-xs align-super font-medium text-primary">PRO</sup>
|
||||
</template>
|
||||
|
||||
<template #description>
|
||||
|
||||
@@ -7,7 +7,7 @@ const title = 'Vue Components'
|
||||
const description = 'Explore 99+ customizable UI components for Vue and Nuxt built with Tailwind CSS and Reka UI.'
|
||||
|
||||
useSeoMeta({
|
||||
titleTemplate: `%s - Nuxt UI`,
|
||||
titleTemplate: '%s - Nuxt UI',
|
||||
title,
|
||||
description,
|
||||
ogTitle: `${title} - Nuxt UI`,
|
||||
@@ -82,7 +82,7 @@ onMounted(() => {
|
||||
:ui="{ title: 'text-balance', container: 'relative' }"
|
||||
>
|
||||
<template #top>
|
||||
<div class="absolute z-[-1] rounded-full bg-(--ui-primary) blur-[300px] size-60 sm:size-80 transform -translate-x-1/2 left-1/2 -translate-y-80" />
|
||||
<div class="absolute z-[-1] rounded-full bg-primary blur-[300px] size-60 sm:size-80 transform -translate-x-1/2 left-1/2 -translate-y-80" />
|
||||
</template>
|
||||
|
||||
<template #headline>
|
||||
@@ -97,7 +97,7 @@ onMounted(() => {
|
||||
/>
|
||||
</template>
|
||||
<template #title>
|
||||
Build beautiful UI with <span class="text-(--ui-primary)">{{ components!.length }}+</span> powerful components
|
||||
Build beautiful UI with <span class="text-primary">{{ components!.length }}+</span> powerful components
|
||||
</template>
|
||||
|
||||
<template #links>
|
||||
@@ -121,22 +121,22 @@ onMounted(() => {
|
||||
|
||||
<LazyStarsBg />
|
||||
|
||||
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
|
||||
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-default inset-0 mx-4 sm:mx-6 lg:mx-8" />
|
||||
</UPageHero>
|
||||
|
||||
<div v-for="category in categories" :key="category.id">
|
||||
<div data-track-sticky class="group mb-4 sm:mb-6 lg:mb-8 sticky top-[calc(var(--ui-header-height)-1px)] bg-(--ui-bg)/75 backdrop-blur z-[1]">
|
||||
<div class="relative border-y border-(--ui-border) py-4 sm:not-group-[[data-stuck]]:py-6 lg:not-group-[[data-stuck]]:py-8 transition-all duration-300">
|
||||
<div data-track-sticky class="group mb-4 sm:mb-6 lg:mb-8 sticky top-[calc(var(--ui-header-height)-1px)] bg-default/75 backdrop-blur z-[1]">
|
||||
<div class="relative border-y border-default py-4 sm:not-group-[[data-stuck]]:py-6 lg:not-group-[[data-stuck]]:py-8 transition-all duration-300">
|
||||
<UContainer>
|
||||
<h2 class="relative text-pretty font-bold text-(--ui-text-highlighted) text-base sm:not-group-[[data-stuck]]:text-xl lg:not-group-[[data-stuck]]:text-2xl transition-all duration-300 ">
|
||||
<h2 class="relative text-pretty font-bold text-highlighted text-base sm:not-group-[[data-stuck]]:text-xl lg:not-group-[[data-stuck]]:text-2xl transition-all duration-300 ">
|
||||
<a :href="`#${category.id}`" class="group lg:not-group-[[data-stuck]]:ps-2 lg:not-group-[[data-stuck]]:-ms-2">
|
||||
<span class="absolute -ms-8 top-1 opacity-0 group-hover:opacity-100 group-focus:opacity-100 p-1 bg-(--ui-bg-elevated) hover:text-(--ui-primary) rounded-[calc(var(--ui-radius)*1.5)] hidden lg:not-group-[[data-stuck]]:flex text-(--ui-text-muted) transition">
|
||||
<span class="absolute -ms-8 top-1 opacity-0 group-hover:opacity-100 group-focus:opacity-100 p-1 bg-elevated hover:text-primary rounded-md hidden lg:not-group-[[data-stuck]]:flex text-muted transition">
|
||||
<UIcon name="i-lucide-hash" class="size-4 shrink-0" />
|
||||
</span>
|
||||
{{ category.title }}
|
||||
</a>
|
||||
</h2>
|
||||
<p class="text-pretty text-(--ui-text-muted) text-sm sm:not-group-[[data-stuck]]:text-base lg:not-group-[[data-stuck]]:text-lg mt-1 sm:not-group-[[data-stuck]]:mt-2 line-clamp-1 transition-all duration-300">
|
||||
<p class="text-pretty text-muted text-sm sm:not-group-[[data-stuck]]:text-base lg:not-group-[[data-stuck]]:text-lg mt-1 sm:not-group-[[data-stuck]]:mt-2 line-clamp-1 transition-all duration-300">
|
||||
{{ category.description }}
|
||||
</p>
|
||||
</UContainer>
|
||||
@@ -157,11 +157,11 @@ onMounted(() => {
|
||||
<template #title>
|
||||
<div class="flex items-center gap-0.5">
|
||||
<span>{{ component.title }}</span>
|
||||
<sup v-if="component.module === 'ui-pro'" class="text-[8px] font-medium text-(--ui-primary)">PRO</sup>
|
||||
<sup v-if="component.module === 'ui-pro'" class="text-[8px] font-medium text-primary">PRO</sup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="rounded-[calc(var(--ui-radius)*1.5)] border border-(--ui-border-muted) overflow-hidden aspect-[16/9]">
|
||||
<div class="rounded-md border border-muted overflow-hidden aspect-[16/9]">
|
||||
<UColorModeImage
|
||||
:light="`${component.path.replace('/components/', '/components/light/')}.png`"
|
||||
:dark="`${component.path.replace('/components/', '/components/dark/')}.png`"
|
||||
|
||||
@@ -27,7 +27,7 @@ features1:
|
||||
description: Start with essential components, or unlock Pro for complete blocks and templates.
|
||||
icon: i-lucide-files
|
||||
cta1:
|
||||
title: Everything you need in a [single file]{class="text-(--ui-primary)"}.
|
||||
title: Everything you need in a [single file]{class="text-primary"}.
|
||||
description: Design and development in perfect sync with our [Free](https://www.figma.com/community/file/1288455405058138934/nuxt-ui-v3-official-design-kit-free) and Pro files. Developers can implement designs faster, while designers work with production-ready components.
|
||||
section1:
|
||||
title: Customize in a few clicks to fit your needs
|
||||
@@ -181,7 +181,7 @@ pricing:
|
||||
# discount: $119
|
||||
billing_period: one-time payment
|
||||
billing_cycle: plus local taxes
|
||||
class: bg-(--ui-bg-elevated)/50
|
||||
class: bg-elevated/50
|
||||
features:
|
||||
- '**1 Designer**'
|
||||
- Nuxt UI & Nuxt UI Pro Components
|
||||
@@ -203,7 +203,7 @@ pricing:
|
||||
# discount: $279
|
||||
billing_period: one-time payment
|
||||
billing_cycle: plus local taxes
|
||||
class: bg-(--ui-bg-elevated)/50
|
||||
class: bg-elevated/50
|
||||
features:
|
||||
- '**Up to 20 Designers**'
|
||||
- Nuxt UI & Nuxt UI Pro Components
|
||||
|
||||
@@ -5,6 +5,7 @@ import { animate } from 'motion-v'
|
||||
import { joinURL } from 'ufo'
|
||||
|
||||
const { url } = useSiteConfig()
|
||||
|
||||
useSeoMeta({
|
||||
title: page.title,
|
||||
description: page.description,
|
||||
@@ -56,7 +57,7 @@ onMounted(async () => {
|
||||
<template>
|
||||
<div class="relative">
|
||||
<div id="cursor1" class="absolute z-10 pointer-events-none" :style="{ opacity: 0 }">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 24 24" class="absolute top-0 left-0 drop-shadow-[0_1px_2px_rgb(0,0,0,0.25)] text-white dark:text-(--ui-bg)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 24 24" class="absolute top-0 left-0 drop-shadow-[0_1px_2px_rgb(0,0,0,0.25)] text-inverted">
|
||||
<path
|
||||
fill="var(--ui-info)"
|
||||
stroke="currentColor"
|
||||
@@ -71,7 +72,7 @@ onMounted(async () => {
|
||||
</UBadge>
|
||||
</div>
|
||||
<div id="cursor2" class="absolute z-10 pointer-events-none" :style="{ opacity: 0 }">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 24 24" class="absolute top-0 left-0 drop-shadow-[0_1px_2px_rgb(0,0,0,0.25)] text-white dark:text-(--ui-bg)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 24 24" class="absolute top-0 left-0 drop-shadow-[0_1px_2px_rgb(0,0,0,0.25)] text-inverted">
|
||||
<path
|
||||
fill="var(--ui-success)"
|
||||
stroke="currentColor"
|
||||
@@ -98,7 +99,7 @@ onMounted(async () => {
|
||||
<template #description>
|
||||
<MDC :value="page.hero.description" unwrap="p" cache-key="figma-hero-description" />
|
||||
</template>
|
||||
<!-- <img src="/pro/figma/nuxt-ui-figma.png" alt="Screnshot of the Nuxt UI Figma design kit" class="w-full h-auto border border-(--ui-border) border-b-0"> -->
|
||||
<!-- <img src="/pro/figma/nuxt-ui-figma.png" alt="Screnshot of the Nuxt UI Figma design kit" class="w-full h-auto border border-default border-b-0"> -->
|
||||
<div class="relative">
|
||||
<video
|
||||
ref="video"
|
||||
@@ -125,10 +126,10 @@ onMounted(async () => {
|
||||
</div>
|
||||
</div>
|
||||
<Motion as-child :initial="{ height: 0 }" :animate="{ height: 'auto' }" :transition="{ delay: 0.2, duration: 1 }">
|
||||
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
|
||||
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-default inset-0 mx-4 sm:mx-6 lg:mx-8" />
|
||||
</Motion>
|
||||
</UPageHero>
|
||||
<UPageSection v-bind="page.features1" :ui="{ container: 'py-16 sm:py-16 lg:py-16', features: 'mt-0' }" class="border-y border-(--ui-border)" />
|
||||
<UPageSection v-bind="page.features1" :ui="{ container: 'py-16 sm:py-16 lg:py-16', features: 'mt-0' }" class="border-y border-default" />
|
||||
<UPageCTA
|
||||
v-if="page.cta1"
|
||||
variant="naked"
|
||||
@@ -137,7 +138,7 @@ onMounted(async () => {
|
||||
wrapper: 'grid grid-cols-1 lg:grid-cols-2',
|
||||
description: 'lg:mt-0' }"
|
||||
orientation="horizontal"
|
||||
class="rounded-none bg-gradient-to-b from-(--ui-bg-muted) to-(--ui-bg)"
|
||||
class="rounded-none bg-gradient-to-b from-elevated/50 to-default"
|
||||
>
|
||||
<template #title>
|
||||
<MDC :value="page.cta1.title" unwrap="p" cache-key="figma-cta-1-title" />
|
||||
@@ -154,8 +155,8 @@ onMounted(async () => {
|
||||
:height="item.height"
|
||||
:src="item.src"
|
||||
:alt="item.alt"
|
||||
class="w-full h-auto rounded-[calc(var(--ui-radius)*2)]"
|
||||
lazy
|
||||
class="w-full h-auto rounded-lg"
|
||||
loading="lazy"
|
||||
/>
|
||||
</template>
|
||||
</UTabs>
|
||||
@@ -164,16 +165,16 @@ onMounted(async () => {
|
||||
<NuxtImg
|
||||
v-if="page.section2.image"
|
||||
v-bind="page.section2.image"
|
||||
class="w-full h-auto rounded-[calc(var(--ui-radius)*2)]"
|
||||
lazy
|
||||
class="w-full h-auto rounded-lg"
|
||||
loading="lazy"
|
||||
/>
|
||||
</UPageSection>
|
||||
<UPageSection v-bind="page.section3" orientation="horizontal" :ui="{ container: 'py-16 sm:pt-16 lg:pt-16' }">
|
||||
<NuxtImg
|
||||
v-if="page.section3.image"
|
||||
v-bind="page.section3.image"
|
||||
class="w-full h-auto rounded-[calc(var(--ui-radius)*2)]"
|
||||
lazy
|
||||
class="w-full h-auto rounded-lg"
|
||||
loading="lazy"
|
||||
/>
|
||||
</UPageSection>
|
||||
<USeparator />
|
||||
@@ -191,27 +192,27 @@ onMounted(async () => {
|
||||
<template #description>
|
||||
<MDC :value="page.section4.description" unwrap="p" cache-key="figma-section-4-description" />
|
||||
</template>
|
||||
<div aria-hidden="true" class="absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
|
||||
<ul class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 items-start justify-center border border-(--ui-border) border-b-0 sm:divide-x divide-y lg:divide-y-0 divide-(--ui-border)">
|
||||
<div aria-hidden="true" class="absolute z-[-1] border-x border-default inset-0 mx-4 sm:mx-6 lg:mx-8" />
|
||||
<ul class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 items-start justify-center border border-default border-b-0 sm:divide-x divide-y lg:divide-y-0 divide-default">
|
||||
<li v-for="(step, index) in page?.section4.steps" :key="step.title" class="flex flex-col gap-y-4 justify-start group h-full p-4">
|
||||
<NuxtImg
|
||||
v-if="step.image"
|
||||
v-bind="step.image"
|
||||
class="rounded-(--ui-radius)"
|
||||
lazy
|
||||
class="rounded-sm"
|
||||
loading="lazy"
|
||||
/>
|
||||
<div>
|
||||
<h2 class="font-semibold inline-flex items-center gap-x-1">
|
||||
<UBadge :label="index + 1" size="sm" color="neutral" variant="subtle" class="rounded-full tabular-nums" /> {{ step.title }}
|
||||
</h2>
|
||||
<p class="text-(--ui-text-muted) text-sm">
|
||||
<p class="text-muted text-sm">
|
||||
{{ step.description }}
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</UPageSection>
|
||||
<UPageSection v-bind="page.features2" :ui="{ container: 'py-16 sm:py-16 lg:py-16', features: 'mt-0' }" class="border-y border-(--ui-border)" />
|
||||
<UPageSection v-bind="page.features2" :ui="{ container: 'py-16 sm:py-16 lg:py-16', features: 'mt-0' }" class="border-y border-default" />
|
||||
<UPageSection
|
||||
v-if="page.pricing"
|
||||
:title="page.pricing.title"
|
||||
@@ -225,7 +226,7 @@ onMounted(async () => {
|
||||
wrapper: 'sm:pl-8'
|
||||
}"
|
||||
>
|
||||
<div aria-hidden="true" class="absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
|
||||
<div aria-hidden="true" class="absolute z-[-1] border-x border-default inset-0 mx-4 sm:mx-6 lg:mx-8" />
|
||||
<UPricingPlans compact class="-space-x-px">
|
||||
<UPricingPlan
|
||||
v-for="(plan, index) in page.pricing.plans"
|
||||
@@ -245,8 +246,8 @@ onMounted(async () => {
|
||||
>
|
||||
<template #features>
|
||||
<li v-for="(feature, i) in plan.features" :key="i" class="flex items-center gap-2 min-w-0">
|
||||
<UIcon name="i-lucide-circle-check" class="size-5 shrink-0 text-(--ui-primary)" />
|
||||
<MDC :value="feature" unwrap="p" tag="span" class="text-sm truncate text-(--ui-text-accented)" :cache-key="`figma-pricing-plan-${index}-feature-${i}`" />
|
||||
<UIcon name="i-lucide-circle-check" class="size-5 shrink-0 text-primary" />
|
||||
<MDC :value="feature" unwrap="p" tag="span" class="text-sm truncate text-accented" :cache-key="`figma-pricing-plan-${index}-feature-${i}`" />
|
||||
</li>
|
||||
</template>
|
||||
<template #button>
|
||||
@@ -272,11 +273,12 @@ onMounted(async () => {
|
||||
:key="index"
|
||||
v-bind="logo"
|
||||
class="h-6 shrink-0 max-w-[140px] filter invert dark:invert-0"
|
||||
loading="lazy"
|
||||
>
|
||||
</UPageMarquee>
|
||||
</UPageCTA>
|
||||
<UPageSection v-bind="page.faq" :ui="{ container: 'relative' }">
|
||||
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
|
||||
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-default inset-0 mx-4 sm:mx-6 lg:mx-8" />
|
||||
<UPageAccordion
|
||||
multiple
|
||||
:items="(page.faq.items as any[])"
|
||||
|
||||
@@ -23,18 +23,7 @@ const { data: components } = await useAsyncData('ui-components', () => {
|
||||
.all()
|
||||
})
|
||||
|
||||
const { data: module } = await useFetch<{
|
||||
stats: {
|
||||
downloads: number
|
||||
stars: number
|
||||
}
|
||||
contributors: {
|
||||
username: string
|
||||
}[]
|
||||
}>('https://api.nuxt.com/modules/ui', {
|
||||
key: 'stats',
|
||||
transform: ({ stats, contributors }) => ({ stats, contributors })
|
||||
})
|
||||
const { data: module } = await useFetch('/api/module.json')
|
||||
|
||||
const { format } = Intl.NumberFormat('en', { notation: 'compact' })
|
||||
|
||||
@@ -59,7 +48,7 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
|
||||
}"
|
||||
>
|
||||
<template #title>
|
||||
The Intuitive <br> <span class="text-(--ui-primary)">Vue UI Library</span>
|
||||
The Intuitive <br> <span class="text-primary">Vue UI Library</span>
|
||||
</template>
|
||||
<template #description>
|
||||
{{ page.hero.description }}
|
||||
@@ -85,21 +74,21 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<LazySkyBg />
|
||||
<LazySkyBg is-index />
|
||||
|
||||
<div class="h-[344px] lg:h-full lg:relative w-full lg:min-h-[calc(100vh-var(--ui-header-height)-1px)] overflow-hidden">
|
||||
<UPageMarquee
|
||||
pause-on-hover
|
||||
:overlay="false"
|
||||
:ui="{
|
||||
root: '[--gap:--spacing(4)] [--duration:40s] border-(--ui-border) absolute w-full left-0 border-y lg:border-x lg:border-y-0 lg:w-[calc(50%-6px)] 2xl:w-[320px] lg:flex-col',
|
||||
root: '[--gap:--spacing(4)] [--duration:40s] border-default absolute w-full left-0 border-y lg:border-x lg:border-y-0 lg:w-[calc(50%-6px)] 2xl:w-[320px] lg:flex-col',
|
||||
content: 'lg:w-auto lg:flex-col lg:animate-[marquee-vertical_var(--duration)_linear_infinite] lg:rtl:animate-[marquee-vertical-rtl_var(--duration)_linear_infinite] lg:h-[fit-content]'
|
||||
}"
|
||||
>
|
||||
<ULink
|
||||
v-for="component of components?.slice(0, 10)"
|
||||
:key="component.path"
|
||||
class="relative group/link aspect-video border-(--ui-border) w-[290px] xl:w-[330px] 2xl:w-[320px] 2xl:p-2 2xl:border-y"
|
||||
class="relative group/link aspect-video border-default w-[290px] xl:w-[330px] 2xl:w-[320px] 2xl:p-2 2xl:border-y"
|
||||
:to="component.path"
|
||||
>
|
||||
<UColorModeImage
|
||||
@@ -109,7 +98,7 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
|
||||
width="290"
|
||||
height="163"
|
||||
format="webp"
|
||||
class="hover:scale-105 lg:hover:scale-110 transition-transform aspect-video w-full border-x lg:border-x-0 lg:border-y border-(--ui-border) 2xl:border-y-0"
|
||||
class="hover:scale-105 lg:hover:scale-110 transition-transform aspect-video w-full border-x lg:border-x-0 lg:border-y border-default 2xl:border-y-0"
|
||||
loading="lazy"
|
||||
/>
|
||||
<UBadge color="neutral" variant="outline" size="md" :label="component.title" class="hidden lg:block absolute mx-auto top-4 left-6 xl:left-4 group-hover/link:opacity-100 opacity-0 transition-all duration-300 pointer-events-none -translate-y-2 group-hover/link:translate-y-0" />
|
||||
@@ -121,14 +110,14 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
|
||||
reverse
|
||||
:overlay="false"
|
||||
:ui="{
|
||||
root: '[--gap:--spacing(4)] [--duration:40s] border-(--ui-border) absolute w-full mt-[180px] left-0 border-y lg:mt-auto lg:left-auto lg:border-y-0 lg:border-x lg:w-[calc(50%-6px)] 2xl:w-[320px] lg:right-0 lg:flex-col',
|
||||
root: '[--gap:--spacing(4)] [--duration:40s] border-default absolute w-full mt-[180px] left-0 border-y lg:mt-auto lg:left-auto lg:border-y-0 lg:border-x lg:w-[calc(50%-6px)] 2xl:w-[320px] lg:right-0 lg:flex-col',
|
||||
content: 'lg:w-auto lg:flex-col lg:animate-[marquee-vertical_var(--duration)_linear_infinite] lg:rtl:animate-[marquee-vertical-rtl_var(--duration)_linear_infinite] lg:h-[fit-content] lg:[animation-direction:reverse]'
|
||||
}"
|
||||
>
|
||||
<ULink
|
||||
v-for="component of components?.slice(10, 20)"
|
||||
:key="component.path"
|
||||
class="relative group/link aspect-video border-(--ui-border) w-[290px] xl:w-[330px] 2xl:w-[320px] 2xl:p-2 2xl:border-y"
|
||||
class="relative group/link aspect-video border-default w-[290px] xl:w-[330px] 2xl:w-[320px] 2xl:p-2 2xl:border-y"
|
||||
:to="component.path"
|
||||
>
|
||||
<UColorModeImage
|
||||
@@ -138,7 +127,7 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
|
||||
width="290"
|
||||
height="163"
|
||||
format="webp"
|
||||
class="hover:scale-105 lg:hover:scale-110 transition-transform aspect-video w-full border-x lg:border-x-0 lg:border-y border-(--ui-border) 2xl:border-y-0"
|
||||
class="hover:scale-105 lg:hover:scale-110 transition-transform aspect-video w-full border-x lg:border-x-0 lg:border-y border-default 2xl:border-y-0"
|
||||
loading="lazy"
|
||||
/>
|
||||
<UBadge color="neutral" variant="outline" size="md" :label="component.title" class="hidden lg:block absolute mx-auto top-4 left-6 xl:left-4 group-hover/link:opacity-100 opacity-0 transition-all duration-300 pointer-events-none -translate-y-2 group-hover/link:translate-y-0" />
|
||||
@@ -176,14 +165,14 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
|
||||
<circle cx="6.53711" cy="37.4551" r="1.5" fill="var(--ui-border-accented)" />
|
||||
<circle cx="38.5957" cy="37.4551" r="1.5" fill="var(--ui-border-accented)" />
|
||||
</svg>
|
||||
<UIcon :name="feature.icon" class="size-5 flex-shrink-0" />
|
||||
<UIcon :name="feature.icon" class="size-5 shrink-0" />
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<h2 class="font-medium text-(--ui-text-highlighted) inline-flex items-center gap-x-1">
|
||||
<h2 class="font-medium text-highlighted inline-flex items-center gap-x-1">
|
||||
{{ feature.title }}
|
||||
<UIcon v-if="feature.to" name="i-lucide-arrow-right" class="size-4 flex-shrink-0 opacity-0 group-hover:opacity-100 transition-all duration-200 -translate-x-1 group-hover:translate-x-0" />
|
||||
<UIcon v-if="feature.to" name="i-lucide-arrow-right" class="size-4 shrink-0 opacity-0 group-hover:opacity-100 transition-all duration-200 -translate-x-1 group-hover:translate-x-0" />
|
||||
</h2>
|
||||
<p class="text-sm text-(--ui-text-muted)">
|
||||
<p class="text-sm text-muted">
|
||||
{{ feature.description }}
|
||||
</p>
|
||||
</div>
|
||||
@@ -226,33 +215,33 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
|
||||
:links="page.community.links"
|
||||
orientation="horizontal"
|
||||
:ui="{ features: 'flex items-center gap-4 lg:gap-8' }"
|
||||
class="border-b border-(--ui-border)"
|
||||
class="border-b border-default"
|
||||
>
|
||||
<template #features>
|
||||
<li>
|
||||
<NuxtLink to="https://npm.chart.dev/@nuxt/ui" target="_blank" class="min-w-0">
|
||||
<p class="text-4xl font-semibold text-(--ui-text-highlighted) truncate">
|
||||
<p class="text-4xl font-semibold text-highlighted truncate">
|
||||
{{ format(module?.stats?.downloads ?? 0) }}+
|
||||
</p>
|
||||
<p class="text-(--ui-text-muted) text-sm truncate">monthly downloads</p>
|
||||
<p class="text-muted text-sm truncate">monthly downloads</p>
|
||||
</NuxtLink>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<NuxtLink to="https://github.com/nuxt/ui" target="_blank" class="min-w-0">
|
||||
<p class="text-4xl font-semibold text-(--ui-text-highlighted) truncate">
|
||||
<p class="text-4xl font-semibold text-highlighted truncate">
|
||||
{{ format(module?.stats?.stars ?? 0) }}+
|
||||
</p>
|
||||
<p class="text-(--ui-text-muted) text-sm truncate">GitHub stars</p>
|
||||
<p class="text-muted text-sm truncate">GitHub stars</p>
|
||||
</NuxtLink>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<NuxtLink to="https://github.com/nuxt/ui/graphs/contributors" target="_blank" class="min-w-0">
|
||||
<p class="text-4xl font-semibold text-(--ui-text-highlighted) truncate">
|
||||
<p class="text-4xl font-semibold text-highlighted truncate">
|
||||
175+
|
||||
</p>
|
||||
<p class="text-(--ui-text-muted) text-sm truncate">Contributors</p>
|
||||
<p class="text-muted text-sm truncate">Contributors</p>
|
||||
</NuxtLink>
|
||||
</li>
|
||||
</template>
|
||||
@@ -264,10 +253,10 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
|
||||
|
||||
<UPageSection :ui="{ container: 'relative !pb-0 overflow-hidden' }">
|
||||
<template #title>
|
||||
Build faster with Nuxt UI <span class="text-(--ui-primary)">Pro</span>.
|
||||
Build faster with Nuxt UI <span class="text-primary">Pro</span>.
|
||||
</template>
|
||||
<template #description>
|
||||
A collection of premium Vue components, composables and utils built on top of Nuxt UI. <br> Focused on structure and layout, these <span class="text-(--ui-text)">responsive components</span> are designed to be the perfect <span class="text-(--ui-text)">building blocks for your next idea</span>.
|
||||
A collection of premium Vue components, composables and utils built on top of Nuxt UI. <br> Focused on structure and layout, these <span class="text-default">responsive components</span> are designed to be the perfect <span class="text-default">building blocks for your next idea</span>.
|
||||
</template>
|
||||
<template #links>
|
||||
<UButton to="/pro" size="lg">
|
||||
@@ -280,8 +269,8 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
|
||||
|
||||
<LazyStarsBg />
|
||||
|
||||
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
|
||||
<div class="relative h-[400px] border border-(--ui-border) bg-(--ui-bg-muted) overflow-hidden border-x-0 -mx-4 sm:-mx-6 lg:mx-0 lg:border-x w-screen lg:w-full">
|
||||
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-default inset-0 mx-4 sm:mx-6 lg:mx-8" />
|
||||
<div class="relative h-[400px] border border-default bg-muted overflow-hidden border-x-0 -mx-4 sm:-mx-6 lg:mx-0 lg:border-x w-screen lg:w-full">
|
||||
<UPageMarquee reverse orientation="vertical" :overlay="false" :ui="{ root: '[--duration:40s] absolute w-[460px] -left-[100px] -top-[300px] h-[940px] transform-3d rotate-x-55 rotate-y-0 rotate-z-30' }">
|
||||
<img
|
||||
v-for="i in 4"
|
||||
@@ -291,7 +280,7 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
|
||||
height="258"
|
||||
loading="lazy"
|
||||
:alt="`Nuxt UI Pro Screenshot ${i}`"
|
||||
class="aspect-video border border-(--ui-border) rounded-[calc(var(--ui-radius)*2)] bg-white"
|
||||
class="aspect-video border border-default rounded-lg bg-white"
|
||||
>
|
||||
</UPageMarquee>
|
||||
<UPageMarquee orientation="vertical" :overlay="false" :ui="{ root: '[--duration:40s] absolute w-[460px] -top-[400px] left-[480px] h-[1160px] transform-3d rotate-x-55 rotate-y-0 rotate-z-30' }">
|
||||
@@ -303,7 +292,7 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
|
||||
height="258"
|
||||
loading="lazy"
|
||||
:alt="`Nuxt UI Pro Screenshot ${i}`"
|
||||
class="aspect-video border border-(--ui-border) rounded-[calc(var(--ui-radius)*2)] bg-white"
|
||||
class="aspect-video border border-default rounded-lg bg-white"
|
||||
>
|
||||
</UPageMarquee>
|
||||
<UPageMarquee reverse orientation="vertical" :overlay="false" :ui="{ root: 'hidden md:flex [--duration:40s] absolute w-[460px] -top-[300px] left-[1020px] h-[1060px] transform-3d rotate-x-55 rotate-y-0 rotate-z-30' }">
|
||||
@@ -313,9 +302,9 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
|
||||
:src="`/pro/blocks/image${i}.png`"
|
||||
width="460"
|
||||
height="258"
|
||||
:alt="`Nuxt UI Pro Screenshot ${i}`"
|
||||
loading="lazy"
|
||||
class="aspect-video border border-(--ui-border) rounded-[calc(var(--ui-radius)*2)] bg-white"
|
||||
:alt="`Nuxt UI Pro Screenshot ${i}`"
|
||||
class="aspect-video border border-default rounded-lg bg-white"
|
||||
>
|
||||
</UPageMarquee>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@ title: Nuxt UI Pro Pricing
|
||||
description: Start for free in development mode, then upgrade to a paid plan to unlock the full features of Nuxt UI Pro when you are ready to launch.
|
||||
pricing:
|
||||
headline: Pricing
|
||||
title: Upgrade to Nuxt UI [Pro]{class="text-(--ui-primary)"}.
|
||||
title: Upgrade to Nuxt UI [Pro]{class="text-primary"}.
|
||||
description: On top of 40+ open source components from Nuxt UI, Pro gives you access to 50+ premium Vue components to create beautiful & responsive Nuxt applications in minutes. It includes all primitives to build landing pages, documentations, blogs, dashboards or entire SaaS products.
|
||||
freePlan:
|
||||
title: Free in development
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
title: Build faster with Nuxt UI Pro.
|
||||
description: A collection of premium Vue components, composables and utils built on top of Nuxt UI, oriented on structure and layout and designed to be used as building blocks for your application.
|
||||
hero:
|
||||
title: Build faster with Nuxt UI [Pro]{class="text-(--ui-primary)"}.
|
||||
description: A collection of premium Vue components, composables and utils built on top of Nuxt UI. :br Focused on structure and layout, these [responsive components]{class="text-(--ui-text)"} are designed to be the perfect [building blocks for your next idea]{class="text-(--ui-text)"}.
|
||||
title: Build faster with Nuxt UI [Pro]{class="text-primary"}.
|
||||
description: A collection of premium Vue components, composables and utils built on top of Nuxt UI. :br Focused on structure and layout, these [responsive components]{class="text-default"} are designed to be the perfect [building blocks for your next idea]{class="text-default"}.
|
||||
links:
|
||||
- label: Buy a license
|
||||
size: xl
|
||||
@@ -62,7 +62,7 @@ testimonial:
|
||||
# avatar:
|
||||
# src: https://github.com/benjamincanac.png
|
||||
mainSection:
|
||||
title: Meet the [Pro Components]{class="text-(--ui-primary)"}.
|
||||
title: Meet the [Pro Components]{class="text-primary"}.
|
||||
description: Code with 50+ components and sections of Nuxt UI Pro to build your next application by reducing the amount of code you need to write.
|
||||
sections:
|
||||
- title: The freedom to build anything
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
title: Official Nuxt UI Pro Templates
|
||||
description: 'Ready to use templates powered by our premium Vue components and Nuxt Content. The templates are responsive, accessible and easy to customize so you can get started in no time.'
|
||||
hero:
|
||||
title: Ship [in minutes]{.text-(--ui-primary)} with :br Nuxt UI Pro Templates
|
||||
title: Ship [in minutes]{.text-primary} with :br Nuxt UI Pro Templates
|
||||
description: 'Ready to use templates powered by our premium Vue components and Nuxt Content.<br class="hidden lg:block"> The templates are responsive, accessible and easy to customize so you can get started in no time.'
|
||||
navigation: false
|
||||
links:
|
||||
@@ -16,8 +16,60 @@ links:
|
||||
variant: outline
|
||||
trailingIcon: i-lucide-arrow-right
|
||||
templates:
|
||||
- title: 'Portfolio'
|
||||
description: "A sleek, modern portfolio template to showcase your work, skills, blog posts, speaking engagements, and provide contact information. Which can customized easily from the `content/` directory."
|
||||
icon: i-lucide-user
|
||||
thumbnail:
|
||||
dark: https://assets.hub.nuxt.com/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL3BvcnRmb2xpby10ZW1wbGF0ZS5udXh0LmRldiIsImlhdCI6MTc0NTkzNDczMX0.XDWnQoyVy3XVtKQD6PLQ8RFUwr4yr1QnVwPxRrjCrro.jpg?theme=dark
|
||||
light: https://assets.hub.nuxt.com/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL3BvcnRmb2xpby10ZW1wbGF0ZS5udXh0LmRldiIsImlhdCI6MTc0NTkzNDczMX0.XDWnQoyVy3XVtKQD6PLQ8RFUwr4yr1QnVwPxRrjCrro.jpg?theme=light
|
||||
features:
|
||||
- title: Sections for Projects, Blog, Speaking & About
|
||||
icon: i-lucide-layout-list
|
||||
- title: Easily editable content via Markdown & YAML
|
||||
icon: i-simple-icons-markdown
|
||||
- title: Fully responsive design
|
||||
icon: i-lucide-smartphone
|
||||
links:
|
||||
- label: Preview
|
||||
to: https://portfolio-template.nuxt.dev
|
||||
target: _blank
|
||||
leadingIcon: i-logos-nuxt-icon
|
||||
trailingIcon: i-lucide-arrow-up-right
|
||||
color: neutral
|
||||
- label: Nuxt Template
|
||||
to: https://github.com/nuxt-ui-pro/portfolio
|
||||
target: _blank
|
||||
icon: i-simple-icons-github
|
||||
color: neutral
|
||||
variant: outline
|
||||
- title: 'Chat'
|
||||
description: "An AI chatbot template designed to help you build your own chatbot with Nuxt UI Pro components and deployed on [NuxtHub](https://hub.nuxt.com)."
|
||||
icon: i-lucide-message-circle
|
||||
thumbnail:
|
||||
dark: https://assets.hub.nuxt.com/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2NoYXQtdGVtcGxhdGUubnV4dC5kZXYiLCJpYXQiOjE3NDI4NDY2ODB9.n4YCsoNz8xatox7UMoYZFNo7iS1mC_DT0h0A9cKRoTw.jpg?theme=dark
|
||||
light: https://assets.hub.nuxt.com/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2NoYXQtdGVtcGxhdGUubnV4dC5kZXYiLCJpYXQiOjE3NDI4NDY2ODB9.n4YCsoNz8xatox7UMoYZFNo7iS1mC_DT0h0A9cKRoTw.jpg?theme=light
|
||||
features:
|
||||
- title: Powered by Cloudflare AI models
|
||||
icon: i-simple-icons-cloudflare
|
||||
- title: GitHub OAuth authentication
|
||||
icon: i-lucide-lock
|
||||
- title: Saved chats and messages
|
||||
icon: i-lucide-database
|
||||
links:
|
||||
- label: Preview
|
||||
to: https://chat-template.nuxt.dev
|
||||
target: _blank
|
||||
leadingIcon: i-logos-nuxt-icon
|
||||
trailingIcon: i-lucide-arrow-up-right
|
||||
color: neutral
|
||||
- label: Nuxt Template
|
||||
to: https://github.com/nuxt-ui-pro/chat
|
||||
target: _blank
|
||||
icon: i-simple-icons-github
|
||||
color: neutral
|
||||
variant: outline
|
||||
- title: 'Dashboard'
|
||||
description: "A template to illustrate how to build your own dashboard with the 15+ latest Nuxt UI Pro components, designed specifically to create a consistent look and feel."
|
||||
description: "A template to illustrate how to build your own dashboard with 15+ Nuxt UI Pro components, designed specifically to create a consistent look and feel."
|
||||
icon: i-lucide-bar-chart-big
|
||||
thumbnail:
|
||||
dark: https://assets.hub.nuxt.com/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2Rhc2hib2FyZC10ZW1wbGF0ZS5udXh0LmRldiIsImlhdCI6MTczOTQ2MzU2N30._VElt4uvLjvAMdnTLytCInOajMElzWDKbmvOaMZhZUI.jpg?theme=dark
|
||||
|
||||
@@ -12,7 +12,7 @@ const { url } = useSiteConfig()
|
||||
useSeoMeta({
|
||||
title,
|
||||
description,
|
||||
ogTitle: `${title} - Nuxt UI Pro`,
|
||||
ogTitle: title,
|
||||
ogDescription: description,
|
||||
ogImage: joinURL(url, '/pro/og-image.png')
|
||||
})
|
||||
@@ -33,7 +33,7 @@ const activating = ref(false)
|
||||
const successMessage = ref()
|
||||
const errorMessage = ref('')
|
||||
|
||||
async function submit(event: FormSubmitEvent<any>) {
|
||||
async function submit(event: FormSubmitEvent<Schema>) {
|
||||
activating.value = true
|
||||
errorMessage.value = ''
|
||||
successMessage.value = ''
|
||||
@@ -73,9 +73,9 @@ onMounted(() => {
|
||||
<UPageHero headline="License Activation" :title="title" :description="description" :ui="{ container: 'relative overflow-hidden', wrapper: 'lg:px-12', description: 'text-pretty' }">
|
||||
<LazyStarsBg />
|
||||
|
||||
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
|
||||
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-default inset-0 mx-4 sm:mx-6 lg:mx-8" />
|
||||
|
||||
<div class="px-4 py-10 lg:border border-(--ui-border) bg-(--ui-bg)">
|
||||
<div class="px-4 py-10 lg:border border-default bg-default">
|
||||
<div class="max-w-xl mx-auto">
|
||||
<UForm
|
||||
:schema="schema"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user