mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-15 12:39:35 +01:00
Compare commits
172 Commits
chore/comp
...
v2.18.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6658209b6 | ||
|
|
9c04969022 | ||
|
|
606c7b6567 | ||
|
|
7e37668940 | ||
|
|
4d61936e7e | ||
|
|
8ac9ca4978 | ||
|
|
3485092edb | ||
|
|
1cc7e2a306 | ||
|
|
3411b89191 | ||
|
|
1b869dc1fb | ||
|
|
4ae6e31bd9 | ||
|
|
69f605fa72 | ||
|
|
93ddf1d60b | ||
|
|
03c5820f5d | ||
|
|
c88bb8c56b | ||
|
|
aaabf61c01 | ||
|
|
4fd1be2892 | ||
|
|
e60911010a | ||
|
|
ea721a3705 | ||
|
|
4614aca70e | ||
|
|
ec2c1162dd | ||
|
|
64c38cb35e | ||
|
|
503885a5fe | ||
|
|
635b0f41e2 | ||
|
|
5db18c0056 | ||
|
|
c3122f776d | ||
|
|
b264ad2ebd | ||
|
|
f374b14dba | ||
|
|
7155318029 | ||
|
|
c39c770a70 | ||
|
|
d18477def5 | ||
|
|
748e49175d | ||
|
|
f74f1df6ca | ||
|
|
d2d37e4093 | ||
|
|
b57676c1a5 | ||
|
|
e8eb3941ad | ||
|
|
839bf72c61 | ||
|
|
fcf46d7171 | ||
|
|
8c531615ef | ||
|
|
51e6cb8417 | ||
|
|
2443fd70eb | ||
|
|
4974280d82 | ||
|
|
b7d90f78b8 | ||
|
|
b2e0566c16 | ||
|
|
64e8a87073 | ||
|
|
720c44dd5e | ||
|
|
6b216cab1b | ||
|
|
82c8717fbf | ||
|
|
d1c80861a1 | ||
|
|
2ac31c0173 | ||
|
|
77f0156b43 | ||
|
|
7e974b55d7 | ||
|
|
6aaf12b9af | ||
|
|
4b397c0f7e | ||
|
|
80b2a60718 | ||
|
|
6691311782 | ||
|
|
2c55fb6336 | ||
|
|
e2881d3801 | ||
|
|
46e9b128da | ||
|
|
53003fcd07 | ||
|
|
2bb8360223 | ||
|
|
bce94db9fd | ||
|
|
cf482581f4 | ||
|
|
19cab36d73 | ||
|
|
df2b5e2419 | ||
|
|
1334ec59b1 | ||
|
|
3f8ea5dbde | ||
|
|
c0ef69d314 | ||
|
|
567045211a | ||
|
|
9e12e9519e | ||
|
|
67e6a4a565 | ||
|
|
2dcf70cf3c | ||
|
|
8eca5a0d62 | ||
|
|
3e9c96bb7a | ||
|
|
cb36094dbe | ||
|
|
839a6029e7 | ||
|
|
4c0a3d8dc0 | ||
|
|
c904604c23 | ||
|
|
bfa2e707d8 | ||
|
|
f16291d290 | ||
|
|
abd13f1f8f | ||
|
|
0a47a2b5f2 | ||
|
|
99c52e5008 | ||
|
|
4d5f250902 | ||
|
|
6b6b03d59f | ||
|
|
4ad904f83d | ||
|
|
c28ef0abb8 | ||
|
|
9c3ed4b78f | ||
|
|
a1c116de7b | ||
|
|
755c4d32b3 | ||
|
|
05c7836387 | ||
|
|
e9d801c52a | ||
|
|
7211e3a1eb | ||
|
|
89d18f8f1d | ||
|
|
1e7470f531 | ||
|
|
b6736d1efd | ||
|
|
f6e695ffc8 | ||
|
|
e8898d15a6 | ||
|
|
f65aefb706 | ||
|
|
9b9ccdb59e | ||
|
|
688232215d | ||
|
|
ebfb835033 | ||
|
|
838d6c832f | ||
|
|
e7c2f7856c | ||
|
|
1d5bd89d58 | ||
|
|
6c124bb1ac | ||
|
|
49174b7628 | ||
|
|
50ad14f9df | ||
|
|
6e2678d1d8 | ||
|
|
831c560a96 | ||
|
|
06990beabf | ||
|
|
3ebff4d133 | ||
|
|
d66cfa9d7d | ||
|
|
75c0d9e31f | ||
|
|
6033872ef8 | ||
|
|
838cb7212a | ||
|
|
c8dd71c4f5 | ||
|
|
4f0d00f7a6 | ||
|
|
3b975634e8 | ||
|
|
249bbd49dc | ||
|
|
3c1602af37 | ||
|
|
e1ca6e0cde | ||
|
|
3b3bd16afe | ||
|
|
fab9cbebd8 | ||
|
|
581b470cc7 | ||
|
|
24d30cd1f3 | ||
|
|
cc52bffccf | ||
|
|
eb2601d4da | ||
|
|
f726b5f094 | ||
|
|
37ce62acb9 | ||
|
|
f97b728968 | ||
|
|
7e6ba78681 | ||
|
|
ed5c74dc17 | ||
|
|
bb3ea40218 | ||
|
|
821e15b696 | ||
|
|
bd3fa8658f | ||
|
|
82d619b2a7 | ||
|
|
8d9d9736ba | ||
|
|
3fca66857d | ||
|
|
4853520eb3 | ||
|
|
5481dab53d | ||
|
|
6f60fa9a98 | ||
|
|
cba9ad78db | ||
|
|
bbc8f4e6ac | ||
|
|
ed3a3babdb | ||
|
|
4415d4111e | ||
|
|
c75c0152ce | ||
|
|
993bb89e02 | ||
|
|
9f01145bc6 | ||
|
|
c1d9e0ecd4 | ||
|
|
f610c96a0b | ||
|
|
8b546600db | ||
|
|
f08471ccda | ||
|
|
d19d7077e4 | ||
|
|
07a4d13c0f | ||
|
|
9e90d1768b | ||
|
|
91e5002050 | ||
|
|
eb68d0d453 | ||
|
|
2bdb5d2b42 | ||
|
|
b62cd7905d | ||
|
|
58faa1053b | ||
|
|
e909884d03 | ||
|
|
5e84fd0570 | ||
|
|
98c0f567fc | ||
|
|
379d20fc3c | ||
|
|
c12f94653e | ||
|
|
2392b4aa40 | ||
|
|
36055ba978 | ||
|
|
73541f2d4f | ||
|
|
03030ab1db | ||
|
|
c98d6e31c0 | ||
|
|
49b73aa024 |
@@ -15,6 +15,7 @@ module.exports = {
|
||||
'space-before-blocks': ['error', 'always'],
|
||||
'space-infix-ops': ['error', { int32Hint: false }],
|
||||
'no-multi-spaces': ['error', { ignoreEOLComments: true }],
|
||||
'no-trailing-spaces': ['error'],
|
||||
|
||||
// Typescript
|
||||
'@typescript-eslint/type-annotation-spacing': 'error',
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -1,6 +1,6 @@
|
||||
name: "🐛 Bug report"
|
||||
description: Report a bug to help us improve the module.
|
||||
labels: ["bug"]
|
||||
labels: ["triage"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/config.yml
vendored
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -4,5 +4,5 @@ contact_links:
|
||||
url: https://ui.nuxt.com
|
||||
about: Check the documentation for guides and examples.
|
||||
- name: 📚 Discord
|
||||
url: https://discord.com/channels/473401852243869706/1153996761426300948
|
||||
about: Consider asking questions in the `#ui` channel.
|
||||
url: https://go.nuxt.com/discord
|
||||
about: Consider asking questions in the help channel.
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
2
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@@ -1,6 +1,6 @@
|
||||
name: "🚀 Feature request"
|
||||
description: Suggest an idea or enhancement for the module.
|
||||
labels: ["enhancement"]
|
||||
labels: ["triage"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
||||
39
.github/workflows/ci-dev.yml
vendored
39
.github/workflows/ci-dev.yml
vendored
@@ -19,41 +19,26 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest] # macos-latest, windows-latest
|
||||
node: [18]
|
||||
node: [20]
|
||||
|
||||
env:
|
||||
NUXT_GITHUB_TOKEN: ${{ secrets.NUXT_GITHUB_TOKEN }}
|
||||
|
||||
steps:
|
||||
- uses: actions/setup-node@v4
|
||||
- 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: checkout
|
||||
uses: actions/checkout@master
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
name: Install pnpm
|
||||
id: pnpm-install
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
id: pnpm-cache
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- uses: dorny/paths-filter@v3
|
||||
- name: Filter changes
|
||||
uses: dorny/paths-filter@v3
|
||||
id: changes
|
||||
with:
|
||||
filters: |
|
||||
|
||||
36
.github/workflows/ci.yml
vendored
36
.github/workflows/ci.yml
vendored
@@ -12,39 +12,23 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest] # macos-latest, windows-latest
|
||||
node: [18]
|
||||
node: [20]
|
||||
|
||||
env:
|
||||
NUXT_GITHUB_TOKEN: ${{ secrets.NUXT_GITHUB_TOKEN }}
|
||||
|
||||
steps:
|
||||
- uses: actions/setup-node@v4
|
||||
- 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 }}
|
||||
|
||||
- name: checkout
|
||||
uses: actions/checkout@master
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
name: Install pnpm
|
||||
id: pnpm-install
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
id: pnpm-cache
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
cache: pnpm
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
23
.github/workflows/stale.yml
vendored
Normal file
23
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: stale
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
exempt-issue-labels: triage,v3
|
||||
stale-issue-message: 'This issue is stale because it has been open for 30 days with no activity.'
|
||||
stale-issue-label: stale
|
||||
stale-pr-label: stale
|
||||
days-before-stale: 30
|
||||
days-before-close: -1
|
||||
151
CHANGELOG.md
151
CHANGELOG.md
@@ -1,5 +1,156 @@
|
||||
# Changelog
|
||||
|
||||
## [2.18.4](https://github.com/nuxt/ui/compare/v2.18.3...v2.18.4) (2024-08-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Form:** submit event data ([#2012](https://github.com/nuxt/ui/issues/2012)) ([4d61936](https://github.com/nuxt/ui/commit/4d61936e7e90b664846a8f265825612c509511d7))
|
||||
* **module:** handle nested colors from ui config ([#2008](https://github.com/nuxt/ui/issues/2008)) ([1cc7e2a](https://github.com/nuxt/ui/commit/1cc7e2a306e0f3f666b9a588f6ed02e7eabc0272))
|
||||
* **module:** reduce css bundle size by fixing safelist regex ([#2005](https://github.com/nuxt/ui/issues/2005)) ([8ac9ca4](https://github.com/nuxt/ui/commit/8ac9ca49789a9a7281f7a40926e7e9a8068cc395))
|
||||
* **module:** suffix types imports with `/index` ([7e37668](https://github.com/nuxt/ui/commit/7e37668940d06c5aa20b60d9bfd600d50a171014)), closes [#2018](https://github.com/nuxt/ui/issues/2018)
|
||||
* **Tabs:** use `nextTick` before marker calc ([#2020](https://github.com/nuxt/ui/issues/2020)) ([9c04969](https://github.com/nuxt/ui/commit/9c049690227af8aba61a1f7c002b00c5dfeb63ff))
|
||||
* **useFormGroup:** app config default input size ([#2011](https://github.com/nuxt/ui/issues/2011)) ([3485092](https://github.com/nuxt/ui/commit/3485092edb55f9ef2ca038a8c137431866d6c28a))
|
||||
|
||||
## [2.18.3](https://github.com/nuxt/ui/compare/v2.18.2...v2.18.3) (2024-07-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Link:** define `rel` as any ([69f605f](https://github.com/nuxt/ui/commit/69f605fa724454e4be9e4cee9666a5d57f43a129))
|
||||
* **types:** only use `.ts` for index ([93ddf1d](https://github.com/nuxt/ui/commit/93ddf1d60b0ea5f99f564f3d3969c397ad91cc72))
|
||||
|
||||
## [2.18.2](https://github.com/nuxt/ui/compare/v2.18.1...v2.18.2) (2024-07-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Tabs:** add missing `UIcon` import ([4fd1be2](https://github.com/nuxt/ui/commit/4fd1be28922bf39584005c14982e5cd9a7d0c624))
|
||||
|
||||
## [2.18.1](https://github.com/nuxt/ui/compare/v2.18.0...v2.18.1) (2024-07-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **components:** use relative imports ([ea721a3](https://github.com/nuxt/ui/commit/ea721a3705cfbcef3075f8c9c1f4acf359974597))
|
||||
|
||||
## [2.18.0](https://github.com/nuxt/ui/compare/v2.17.0...v2.18.0) (2024-07-25)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **Icon:** migrate from `@egoist/tailwindcss-icons` to new `@nuxt/icon` (#1789)
|
||||
|
||||
### Features
|
||||
|
||||
* **Checkbox/Radio/RadioGroup:** add `help` slot ([c3122f7](https://github.com/nuxt/ui/commit/c3122f776daa6d68f201f22c37e0084aac37ed06)), closes [#1957](https://github.com/nuxt/ui/issues/1957)
|
||||
* **CommandPalette:** handle `static` groups ([#1458](https://github.com/nuxt/ui/issues/1458)) ([b264ad2](https://github.com/nuxt/ui/commit/b264ad2ebdc8d4ee4aab5c994df968025207021f))
|
||||
* **Icon:** migrate from `@egoist/tailwindcss-icons` to new `@nuxt/icon` ([#1789](https://github.com/nuxt/ui/issues/1789)) ([c904604](https://github.com/nuxt/ui/commit/c904604c23987c2535e0e91e9c4fec50477f6b34))
|
||||
* **module:** improve app config types autocomplete ([#1870](https://github.com/nuxt/ui/issues/1870)) ([3f8ea5d](https://github.com/nuxt/ui/commit/3f8ea5dbded7b6836495103739688905ff26fe22))
|
||||
* **RadioGroup:** add `selected` to label slot props ([#1587](https://github.com/nuxt/ui/issues/1587)) ([d18477d](https://github.com/nuxt/ui/commit/d18477def58171d51bdb7d00e31e2807b2e7015b))
|
||||
* **SelectMenu:** add selected to `label` / `leading` / `trailing` slots props ([#1349](https://github.com/nuxt/ui/issues/1349)) ([6b216ca](https://github.com/nuxt/ui/commit/6b216cab1ba3bb69cb317254dfd562ab020c5e92))
|
||||
* **SelectMenu:** handle function in `showCreateOptionWhen` prop ([#1853](https://github.com/nuxt/ui/issues/1853)) ([7e974b5](https://github.com/nuxt/ui/commit/7e974b55d72b8ac0ab42ef722a2d1904c3e4e091))
|
||||
* **Skeleton:** add `as` prop ([#1955](https://github.com/nuxt/ui/issues/1955)) ([bce94db](https://github.com/nuxt/ui/commit/bce94db9fdb2c29a4f2e5981e5dce49a44a4ac8a))
|
||||
* **Table:** expand row ([#1036](https://github.com/nuxt/ui/issues/1036)) ([7155318](https://github.com/nuxt/ui/commit/71553180294c53024c28de9bbebf4ea69f616da7))
|
||||
* **Table:** handle `rowClass` property in `columns` ([#1632](https://github.com/nuxt/ui/issues/1632)) ([748e491](https://github.com/nuxt/ui/commit/748e49175da37b85bd18d62a8455875990866d5b))
|
||||
* **Tabs:** handle `icon` in items ([#1798](https://github.com/nuxt/ui/issues/1798)) ([e8eb394](https://github.com/nuxt/ui/commit/e8eb3941ad4c1c306ccbe9e11d979d5f6c808330))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Accordion:** truncate buttons ([5db18c0](https://github.com/nuxt/ui/commit/5db18c00565f9d2bb9f2768c2de2ab291a55bcae)), closes [#1909](https://github.com/nuxt/ui/issues/1909)
|
||||
* **Alert/Notification:** missing margin on description ([2c55fb6](https://github.com/nuxt/ui/commit/2c55fb63365ee7cc1e993ebd5aa5f83ddadcd26a)), closes [#1959](https://github.com/nuxt/ui/issues/1959)
|
||||
* **Breadcrumb:** use `rotate` on rtl icon ([53003fc](https://github.com/nuxt/ui/commit/53003fcd07d67d13ada0759ff6c5cd3635fba0e3))
|
||||
* **ButtonGroup/FormGroup:** pass default sizes to children ([#1875](https://github.com/nuxt/ui/issues/1875)) ([6b6b03d](https://github.com/nuxt/ui/commit/6b6b03d59f5ab3096b731c59d18a1085d25b5e8e))
|
||||
* **Carousel:** remove `mix-blend-overlay` on indicators ([#1714](https://github.com/nuxt/ui/issues/1714)) ([f74f1df](https://github.com/nuxt/ui/commit/f74f1df6ca5f93e11e542245b611c1aa7c4b8308))
|
||||
* **FormGroup:** don't check for `error` slot so `help` slot can render ([#1888](https://github.com/nuxt/ui/issues/1888)) ([99c52e5](https://github.com/nuxt/ui/commit/99c52e50082d5e99440894c7a077a17510f0de50))
|
||||
* **InputMenu/SelectMenu:** invalid `label` with `value-attribute` and async search ([4d5f250](https://github.com/nuxt/ui/commit/4d5f2509022e4fb74fc268d5479f7cc8f0415040)), closes [#1780](https://github.com/nuxt/ui/issues/1780)
|
||||
* **InputMenu/SelectMenu:** prevent double filter with async search ([e2881d3](https://github.com/nuxt/ui/commit/e2881d3801c54c49d66d41d4f0ba312a7b3ebce7)), closes [#1966](https://github.com/nuxt/ui/issues/1966)
|
||||
* **Link:** allow `ariaLabel` to be picked ([720c44d](https://github.com/nuxt/ui/commit/720c44dd5ee90bb3b30aef32f01ff6eae1397aa4)), closes [#1934](https://github.com/nuxt/ui/issues/1934)
|
||||
* **Progress:** pass down attrs to `<progress>` to improve accessibility ([#1881](https://github.com/nuxt/ui/issues/1881)) ([abd13f1](https://github.com/nuxt/ui/commit/abd13f1f8fd4c8b10069174534c5fdec6c83576e))
|
||||
* **RadioGroup:** allow boolean in `modelValue` prop ([#1913](https://github.com/nuxt/ui/issues/1913)) ([8eca5a0](https://github.com/nuxt/ui/commit/8eca5a0d627e22f42350a060f09c4e44b6de422f))
|
||||
|
||||
## [2.17.0](https://github.com/nuxt/ui/compare/v2.16.0...v2.17.0) (2024-06-13)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Alert:** add `actions` slot ([#1785](https://github.com/nuxt/ui/issues/1785)) ([c8dd71c](https://github.com/nuxt/ui/commit/c8dd71c4f5a5239811b07b50f1dc802101af07d5))
|
||||
* **Form:** update and migrate `valibot` to v0.31.0 ([#1848](https://github.com/nuxt/ui/issues/1848)) ([1d5bd89](https://github.com/nuxt/ui/commit/1d5bd89d5881163fc6dc917e138b9d8304dff6c4))
|
||||
* **Notification:** allow ring customization with `{color}` ([#1830](https://github.com/nuxt/ui/issues/1830)) ([3ebff4d](https://github.com/nuxt/ui/commit/3ebff4d133372e339e2c4c439576e9e192b29cc3))
|
||||
* **Slideover:** handle `top` and `bottom` side ([#1834](https://github.com/nuxt/ui/issues/1834)) ([50ad14f](https://github.com/nuxt/ui/commit/50ad14f9dffe4f76bef888cd10d30b417c75bca5))
|
||||
* **Tabs:** add `content` prop to avoid the render of the HTML markup ([#1831](https://github.com/nuxt/ui/issues/1831)) ([6e2678d](https://github.com/nuxt/ui/commit/6e2678d1d8a498322eb3eff909ccbba55e40a2b7))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Alert/Notification:** use `div` for description ([e8898d1](https://github.com/nuxt/ui/commit/e8898d15a667ba66e78828315e3cc4e92845cd3f)), closes [#1551](https://github.com/nuxt/ui/issues/1551)
|
||||
* **Alert:** base style not applied on icon ([#1859](https://github.com/nuxt/ui/issues/1859)) ([f65aefb](https://github.com/nuxt/ui/commit/f65aefb7067c1c64c1355b5d699129e716ef1281))
|
||||
* **Breadcrumb:** allow `aria-current` to be overrideable ([ebfb835](https://github.com/nuxt/ui/commit/ebfb8350339725c0a6f88c73f16bff01d61538c2)), closes [#1856](https://github.com/nuxt/ui/issues/1856)
|
||||
* **Carousel:** prevent mouse click when dragging ([#1781](https://github.com/nuxt/ui/issues/1781)) ([4f0d00f](https://github.com/nuxt/ui/commit/4f0d00f7a6eebf05adceaf1e7c2869ad91949cf3))
|
||||
* **CommandPalette:** hide `empty-state` when `null` ([249bbd4](https://github.com/nuxt/ui/commit/249bbd49dc8420603e8d561543d237abeb400908)), closes [#1787](https://github.com/nuxt/ui/issues/1787)
|
||||
* **Form:** maintain other errors when using `setErrors` with a path ([#1818](https://github.com/nuxt/ui/issues/1818)) ([06990be](https://github.com/nuxt/ui/commit/06990beabf67f668322b4d3fb2ec93cc4f3bdcd4))
|
||||
* **Input:** hide wrapper when type is `hidden` ([#1797](https://github.com/nuxt/ui/issues/1797)) ([e7c2f78](https://github.com/nuxt/ui/commit/e7c2f7856c05ed96f48c83d64d8e1d3f41ab58fe))
|
||||
* **Link:** typo in `exactHash` type ([581b470](https://github.com/nuxt/ui/commit/581b470cc79c2315bb2d56e02a7c134a7861c616)), closes [#1767](https://github.com/nuxt/ui/issues/1767)
|
||||
* **SelectMenu:** wrong placeholder color when `modelValue` is an empty string ([9b9ccdb](https://github.com/nuxt/ui/commit/9b9ccdb59e98fed096dd18809af646b10de46b9f)), closes [#1862](https://github.com/nuxt/ui/issues/1862)
|
||||
* **Select:** remove defaults for `value` and `text` ([6c124bb](https://github.com/nuxt/ui/commit/6c124bb1ac2fef116161da56a3a8e5f92144ce3a)), closes [#1702](https://github.com/nuxt/ui/issues/1702)
|
||||
|
||||
## [2.16.0](https://github.com/nuxt/ui/compare/v2.15.2...v2.16.0) (2024-05-07)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **Input:** redesign `file` type without absolute positioning (#1712)
|
||||
|
||||
### Features
|
||||
|
||||
* **InputMenu/SelectMenu:** allow lazy search ([#1705](https://github.com/nuxt/ui/issues/1705)) ([7e6ba78](https://github.com/nuxt/ui/commit/7e6ba786816516ab5007a2ff15fc974cfdd796ab))
|
||||
* **module:** HMR support with `@nuxtjs/tailwindcss` ([#1665](https://github.com/nuxt/ui/issues/1665)) ([821e15b](https://github.com/nuxt/ui/commit/821e15b696b03d0f5e20e001d39f86a8b3cec426))
|
||||
* **Table:** allow providing a `<caption>` ([#1680](https://github.com/nuxt/ui/issues/1680)) ([3fca668](https://github.com/nuxt/ui/commit/3fca66857d3616bf24a1b0579c90179a7883869d))
|
||||
* **useToast:** allow clearing all notifications ([#1695](https://github.com/nuxt/ui/issues/1695)) ([82d619b](https://github.com/nuxt/ui/commit/82d619b2a75b9d08f3f5b314d37c30d77d8341e9))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Breadcrumb:** pass `click` event to `ULink` ([5481dab](https://github.com/nuxt/ui/commit/5481dab53dbe0b28188b4a16811f3e8816d93edf))
|
||||
* **Input:** redesign `file` type without absolute positioning ([#1712](https://github.com/nuxt/ui/issues/1712)) ([ed5c74d](https://github.com/nuxt/ui/commit/ed5c74dc17df784485eabc39c83e62ada9210a49))
|
||||
* **Notification:** update timer when timeout prop changes ([#1673](https://github.com/nuxt/ui/issues/1673)) ([cba9ad7](https://github.com/nuxt/ui/commit/cba9ad78db58cb9228bb9c96f0469d43bde2bf3e))
|
||||
* **Slideover:** export and clean types ([#1692](https://github.com/nuxt/ui/issues/1692)) ([bd3fa86](https://github.com/nuxt/ui/commit/bd3fa8658f84fb7bd96d322968462c5eaa987b86))
|
||||
* **Table:** provide `aria-sort` for sortable table headings ([#1675](https://github.com/nuxt/ui/issues/1675)) ([6f60fa9](https://github.com/nuxt/ui/commit/6f60fa9a980020f6a5afc2916e699a7f9a47e8ce))
|
||||
|
||||
## [2.15.2](https://github.com/nuxt/ui/compare/v2.15.1...v2.15.2) (2024-04-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Accordion:** add `unmount` prop to allow lazy mounting for heavy components ([#1590](https://github.com/nuxt/ui/issues/1590)) ([91e5002](https://github.com/nuxt/ui/commit/91e50020507ac66992dfb52b3e0ad1a1ae5614b5))
|
||||
* **Table:** add `checkbox` ui config ([#1409](https://github.com/nuxt/ui/issues/1409)) ([8b54660](https://github.com/nuxt/ui/commit/8b546600dbfbff187d9c5be1b35ea1772e94f83f))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Breadcrumb:** missing `min-w-0` on wrapper to truncate ([9f01145](https://github.com/nuxt/ui/commit/9f01145bc674378371ff34d7110f3235b57d2459)), closes [#1650](https://github.com/nuxt/ui/issues/1650)
|
||||
* **Carousel:** next and prev buttons disabled ([#1619](https://github.com/nuxt/ui/issues/1619)) ([e909884](https://github.com/nuxt/ui/commit/e909884d0327bfd7b4d5551382123f8998beff6a))
|
||||
* **Popover/Dropdown:** prevent unintended closure on touchstart in mobile devices ([#1609](https://github.com/nuxt/ui/issues/1609)) ([2392b4a](https://github.com/nuxt/ui/commit/2392b4aa405430fc22766f130448a7cc5ced9a3a))
|
||||
* **Slideover:** remove dynamic component when closing ([#1615](https://github.com/nuxt/ui/issues/1615)) ([58faa10](https://github.com/nuxt/ui/commit/58faa1053b9be3f627c3fcff1bcaa14850bb9e7f))
|
||||
* **Slideover:** wait for transition to complete to reset state ([#1624](https://github.com/nuxt/ui/issues/1624)) ([07a4d13](https://github.com/nuxt/ui/commit/07a4d13c0fcb05c87fb42e02a3a2d6c5c52ccf09))
|
||||
|
||||
## [2.15.1](https://github.com/nuxt/ui/compare/v2.15.0...v2.15.1) (2024-04-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Avatar:** add `as` prop to use `NuxtImg` underneath ([49b73aa](https://github.com/nuxt/ui/commit/49b73aa024be14a9aa150a2804f4dcb18542fa49)), closes [#1577](https://github.com/nuxt/ui/issues/1577)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Checkbox:** `[@change](https://github.com/change)` event value ([#1580](https://github.com/nuxt/ui/issues/1580)) ([c98d6e3](https://github.com/nuxt/ui/commit/c98d6e31c0e3f46b97957d5cf3de7f9da1f70c58))
|
||||
* **Divider:** add `w-full` only on horizontal wrapper ([#1565](https://github.com/nuxt/ui/issues/1565)) ([bd8b737](https://github.com/nuxt/ui/commit/bd8b737642280e6a83b67f9a27dd7a823a77e963))
|
||||
* **Dropdown:** missing `mouseenter` event on container ([7288953](https://github.com/nuxt/ui/commit/72889535e7e9763e7ebf59498f22c39bf09d6477))
|
||||
* **Input/SelectMenu:** handle `file` type and `change` events ([#1570](https://github.com/nuxt/ui/issues/1570)) ([878f707](https://github.com/nuxt/ui/commit/878f7078a28c5e70a662682d1293db466d518c7d))
|
||||
* **Popover:** missing `mouseenter` event on container ([8517897](https://github.com/nuxt/ui/commit/8517897c34adaa9e3624f867b43106deb59fcbe8)), closes [#1564](https://github.com/nuxt/ui/issues/1564)
|
||||
|
||||
## [2.15.0](https://github.com/nuxt/ui/compare/v2.14.2...v2.15.0) (2024-03-26)
|
||||
|
||||
|
||||
|
||||
19
README.md
19
README.md
@@ -27,24 +27,7 @@ Read more on [ui.nuxt.com](https://ui.nuxt.com)
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm install @nuxt/ui
|
||||
# yarn
|
||||
yarn add @nuxt/ui
|
||||
# pnpm
|
||||
pnpm add @nuxt/ui
|
||||
# bun
|
||||
bun add @nuxt/ui
|
||||
```
|
||||
|
||||
Then, register the module in your `nuxt.config.ts`:
|
||||
|
||||
```js
|
||||
export default defineNuxtConfig({
|
||||
modules: [
|
||||
'@nuxt/ui'
|
||||
]
|
||||
})
|
||||
npx nuxi@latest module add ui
|
||||
```
|
||||
|
||||
If you want latest updates, please use `@nuxt/ui-edge` in your `package.json`:
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<script setup lang="ts">
|
||||
import { withoutTrailingSlash } from 'ufo'
|
||||
import { debounce } from 'perfect-debounce'
|
||||
import type { ParsedContent } from '@nuxt/content/dist/runtime/types'
|
||||
import type { ParsedContent } from '@nuxt/content'
|
||||
|
||||
const searchRef = ref()
|
||||
|
||||
@@ -72,7 +72,7 @@ const links = computed(() => {
|
||||
active: route.path.startsWith('/pro/getting-started') || route.path.startsWith('/pro/components') || route.path.startsWith('/pro/prose')
|
||||
}, {
|
||||
label: 'Pricing',
|
||||
icon: 'i-heroicons-credit-card',
|
||||
icon: 'i-heroicons-ticket',
|
||||
to: '/pro/pricing'
|
||||
}, {
|
||||
label: 'Templates',
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { $ui } = useNuxtApp()
|
||||
|
||||
const links = [{
|
||||
icon: 'i-simple-icons-figma',
|
||||
label: 'Figma Kit',
|
||||
|
||||
34
docs/components/Gradient.vue
Normal file
34
docs/components/Gradient.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 1440 181" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="path-1-inside-1_1086_7239" fill="white">
|
||||
<path d="M0 0H1440V181H0V0Z" />
|
||||
</mask>
|
||||
<path d="M0 0H1440V181H0V0Z" fill="url(#paint0_linear_1086_7239)" fill-opacity="0.22" />
|
||||
<path d="M0 2H1440V-2H0V2Z" fill="url(#paint1_linear_1086_7239)" mask="url(#path-1-inside-1_1086_7239)" />
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_1086_7239"
|
||||
x1="720"
|
||||
y1="0"
|
||||
x2="720"
|
||||
y2="181"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="rgb(var(--color-primary-DEFAULT))" />
|
||||
<stop offset="1" stop-color="rgb(var(--color-primary-DEFAULT))" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint1_linear_1086_7239"
|
||||
x1="0"
|
||||
y1="90.5"
|
||||
x2="1440"
|
||||
y2="90.5"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="rgb(var(--color-primary-DEFAULT))" stop-opacity="0" />
|
||||
<stop offset="0.395" stop-color="rgb(var(--color-primary-DEFAULT))" />
|
||||
<stop offset="1" stop-color="rgb(var(--color-primary-DEFAULT))" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -46,7 +46,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { NavItem } from '@nuxt/content/dist/runtime/types'
|
||||
import type { NavItem } from '@nuxt/content'
|
||||
import type { HeaderLink } from '#ui-pro/types'
|
||||
|
||||
defineProps<{
|
||||
@@ -54,6 +54,7 @@ defineProps<{
|
||||
}>()
|
||||
|
||||
const route = useRoute()
|
||||
const { $ui } = useNuxtApp()
|
||||
const { metaSymbol } = useShortcuts()
|
||||
|
||||
const nav = inject<Ref<NavItem[]>>('navigation')
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
<script lang="ts" setup>
|
||||
defineOptions({
|
||||
inheritAttrs: false
|
||||
})
|
||||
|
||||
defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div class="[&>div>pre]:!rounded-t-none [&>div>pre]:!mt-0">
|
||||
<div
|
||||
v-if="hasPreview"
|
||||
class="flex border border-gray-200 dark:border-gray-700 relative rounded-t-md"
|
||||
:class="[{ 'p-4': padding, 'rounded-b-md': !hasCode, 'border-b-0': hasCode, 'not-prose': !prose }, backgroundClass, extraClass]"
|
||||
>
|
||||
@@ -37,6 +38,10 @@ const props = defineProps({
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
hiddenPreview: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
hiddenCode: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
@@ -79,6 +84,7 @@ const data = await fetchContentExampleCode(camelName)
|
||||
const highlighter = useShikiHighlighter()
|
||||
|
||||
const hasCode = computed(() => !props.hiddenCode && (data?.code || instance.slots.code))
|
||||
const hasPreview = computed(() => !props.hiddenPreview && (props.component || instance.slots.default))
|
||||
|
||||
const { data: ast } = await useAsyncData(`content-example-${camelName}-ast`, () => transformContent('content:_markdown.md', `\`\`\`vue\n${data?.code ?? ''}\n\`\`\``, {
|
||||
markdown: {
|
||||
|
||||
@@ -61,9 +61,7 @@ const items = [{
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col items-center">
|
||||
<code>$ npm i @nuxt/ui</code>
|
||||
<code>$ yarn add @nuxt/ui</code>
|
||||
<code>$ pnpm add @nuxt/ui</code>
|
||||
<code>$ npx nuxi@latest module add ui</code>
|
||||
</div>
|
||||
</template>
|
||||
</UAccordion>
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<template>
|
||||
<UAlert
|
||||
title="Customize Alert Avatar"
|
||||
description="Insert custom content into the avatar slot!"
|
||||
:avatar="{
|
||||
src: 'https://avatars.githubusercontent.com/u/739984?v=4',
|
||||
alt: 'Avatar'
|
||||
<UAlert
|
||||
title="Customize Alert Avatar"
|
||||
description="Insert custom content into the avatar slot!"
|
||||
:avatar="{
|
||||
src: 'https://avatars.githubusercontent.com/u/739984?v=4',
|
||||
alt: 'Avatar'
|
||||
}"
|
||||
>
|
||||
<template #avatar="{ avatar }">
|
||||
<UAvatar
|
||||
v-bind="avatar"
|
||||
<UAvatar
|
||||
v-bind="avatar"
|
||||
chip-color="primary"
|
||||
chip-text=""
|
||||
chip-position="top-right"
|
||||
@@ -17,4 +17,3 @@
|
||||
</template>
|
||||
</UAlert>
|
||||
</template>
|
||||
|
||||
@@ -7,4 +7,3 @@
|
||||
</template>
|
||||
</UAlert>
|
||||
</template>
|
||||
|
||||
@@ -12,7 +12,7 @@ const links = [{
|
||||
<template>
|
||||
<UBreadcrumb :links="links">
|
||||
<template #default="{ link, isActive, index }">
|
||||
<UBadge :color="isActive ? 'primary' : 'gray'" class="rounded-full">
|
||||
<UBadge :color="isActive ? 'primary' : 'gray'" class="rounded-full truncate">
|
||||
{{ index + 1 }}. {{ link.label }}
|
||||
</UBadge>
|
||||
</template>
|
||||
|
||||
@@ -7,7 +7,8 @@ const groups = [{
|
||||
return []
|
||||
}
|
||||
|
||||
const users = await $fetch<any[]>('https://jsonplaceholder.typicode.com/users', { params: { q } })
|
||||
// @ts-ignore
|
||||
const users: any[] = await $fetch('https://jsonplaceholder.typicode.com/users', { params: { q } })
|
||||
|
||||
return users.map(user => ({ id: user.id, label: user.name, suffix: user.email }))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { NavItem, ParsedContent } from '@nuxt/content/dist/runtime/types'
|
||||
import type { NavItem, ParsedContent } from '@nuxt/content'
|
||||
import type { Button } from '#ui/types'
|
||||
|
||||
const commandPaletteRef = ref()
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useMouse, useWindowScroll } from '@vueuse/core'
|
||||
|
||||
const { x, y } = useMouse()
|
||||
const { y: windowY } = useWindowScroll()
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useMouse, useWindowScroll } from '@vueuse/core'
|
||||
|
||||
const { x, y } = useMouse()
|
||||
const { y: windowY } = useWindowScroll()
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useMouse, useWindowScroll } from '@vueuse/core'
|
||||
|
||||
const { x, y } = useMouse()
|
||||
const { y: windowY } = useWindowScroll()
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useMouse, useWindowScroll } from '@vueuse/core'
|
||||
|
||||
const { x, y } = useMouse()
|
||||
const { y: windowY } = useWindowScroll()
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { string, objectAsync, email, minLength, type Input } from 'valibot'
|
||||
import * as v from 'valibot'
|
||||
import type { FormSubmitEvent } from '#ui/types'
|
||||
|
||||
const schema = objectAsync({
|
||||
email: string([email('Invalid email')]),
|
||||
password: string([minLength(8, 'Must be at least 8 characters')])
|
||||
const schema = v.object({
|
||||
email: v.pipe(v.string(), v.email('Invalid email')),
|
||||
password: v.pipe(v.string(), v.minLength(8, 'Must be at least 8 characters'))
|
||||
})
|
||||
|
||||
type Schema = Input<typeof schema>
|
||||
type Schema = v.InferOutput<typeof schema>
|
||||
|
||||
const state = reactive({
|
||||
email: '',
|
||||
@@ -21,7 +21,7 @@ async function onSubmit (event: FormSubmitEvent<Schema>) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
|
||||
<UForm :schema="v.safeParser(schema)" :state="state" class="space-y-4" @submit="onSubmit">
|
||||
<UFormGroup label="Email" name="email">
|
||||
<UInput v-model="state.email" />
|
||||
</UFormGroup>
|
||||
|
||||
@@ -5,7 +5,7 @@ const selected = ref()
|
||||
async function search (q: string) {
|
||||
loading.value = true
|
||||
|
||||
const users = await $fetch<any[]>('https://jsonplaceholder.typicode.com/users', { params: { q } })
|
||||
const users: any[] = await $fetch('https://jsonplaceholder.typicode.com/users', { params: { q } })
|
||||
|
||||
loading.value = false
|
||||
|
||||
|
||||
@@ -5,13 +5,24 @@ defineProps({
|
||||
default: 0
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['success'])
|
||||
|
||||
function onSuccess () {
|
||||
emit('success')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UModal>
|
||||
<UCard>
|
||||
<p>This modal was opened programmatically !</p>
|
||||
<p>Count: {{ count }}</p>
|
||||
<div class="space-y-2">
|
||||
<p>This modal was opened programmatically !</p>
|
||||
<p>Count: {{ count }}</p>
|
||||
<UButton @click="onSuccess">
|
||||
Click to emit a success event
|
||||
</UButton>
|
||||
</div>
|
||||
</UCard>
|
||||
</UModal>
|
||||
</template>
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import { ModalExampleComponent } from '#components'
|
||||
|
||||
const toast = useToast()
|
||||
const modal = useModal()
|
||||
const count = ref(0)
|
||||
|
||||
function openModal () {
|
||||
count.value += 1
|
||||
modal.open(ModalExampleComponent, {
|
||||
count: count.value
|
||||
count: count.value,
|
||||
onSuccess () {
|
||||
toast.add({
|
||||
title: 'Success !',
|
||||
id: 'modal-success'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
17
docs/components/content/examples/PaginationExampleTo.vue
Normal file
17
docs/components/content/examples/PaginationExampleTo.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
const page = ref(1)
|
||||
const items = ref(Array(50))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UPagination
|
||||
v-model="page"
|
||||
:page-count="5"
|
||||
:total="items.length"
|
||||
:to="(page: number) => ({
|
||||
query: { page },
|
||||
// Hash is specified here to prevent the page from scrolling to the top
|
||||
hash: '#links',
|
||||
})"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,60 @@
|
||||
<script setup lang="ts">
|
||||
const options = ref([
|
||||
{ id: 1, name: 'bug' },
|
||||
{ id: 2, name: 'documentation' },
|
||||
{ id: 3, name: 'duplicate' },
|
||||
{ id: 4, name: 'enhancement' },
|
||||
{ id: 5, name: 'good first issue' },
|
||||
{ id: 6, name: 'help wanted' },
|
||||
{ id: 7, name: 'invalid' },
|
||||
{ id: 8, name: 'question' },
|
||||
{ id: 9, name: 'wontfix' }
|
||||
])
|
||||
|
||||
const selected = ref([])
|
||||
|
||||
const labels = computed({
|
||||
get: () => selected.value,
|
||||
set: async (labels) => {
|
||||
const promises = labels.map(async (label) => {
|
||||
if (label.id) {
|
||||
return label
|
||||
}
|
||||
|
||||
// In a real app, you would make an API call to create the label
|
||||
const response = {
|
||||
id: options.value.length + 1,
|
||||
name: label.name
|
||||
}
|
||||
|
||||
options.value.push(response)
|
||||
|
||||
return response
|
||||
})
|
||||
|
||||
selected.value = await Promise.all(promises)
|
||||
}
|
||||
})
|
||||
|
||||
const showCreateOption = (query, results) => {
|
||||
const lowercaseQuery = String.prototype.toLowerCase.apply(query || '')
|
||||
return lowercaseQuery.length >= 3 && !results.find(option => {
|
||||
return String.prototype.toLowerCase.apply(option['name'] || '') === lowercaseQuery
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<USelectMenu
|
||||
v-model="labels"
|
||||
by="id"
|
||||
name="labels"
|
||||
:options="options"
|
||||
option-attribute="name"
|
||||
multiple
|
||||
searchable
|
||||
creatable
|
||||
:show-create-option-when="showCreateOption"
|
||||
placeholder="Select labels"
|
||||
/>
|
||||
</template>
|
||||
@@ -5,7 +5,7 @@ const selected = ref([])
|
||||
async function search (q: string) {
|
||||
loading.value = true
|
||||
|
||||
const users = await $fetch<any[]>('https://jsonplaceholder.typicode.com/users', { params: { q } })
|
||||
const users: any[] = await $fetch('https://jsonplaceholder.typicode.com/users', { params: { q } })
|
||||
|
||||
loading.value = false
|
||||
|
||||
|
||||
@@ -8,6 +8,16 @@ const isOpen = ref(false)
|
||||
|
||||
<USlideover v-model="isOpen">
|
||||
<div class="p-4 flex-1">
|
||||
<UButton
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
icon="i-heroicons-x-mark-20-solid"
|
||||
class="flex sm:hidden absolute end-5 top-5 z-10"
|
||||
square
|
||||
padded
|
||||
@click="isOpen = false"
|
||||
/>
|
||||
<Placeholder class="h-full" />
|
||||
</div>
|
||||
</USlideover>
|
||||
|
||||
@@ -7,8 +7,22 @@ const isOpen = ref(false)
|
||||
<UButton label="Open" @click="isOpen = true" />
|
||||
|
||||
<USlideover v-model="isOpen">
|
||||
<UCard class="flex flex-col flex-1" :ui="{ body: { base: 'flex-1' }, ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
|
||||
<UCard
|
||||
class="flex flex-col flex-1"
|
||||
:ui="{ body: { base: 'flex-1' }, ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }"
|
||||
>
|
||||
<template #header>
|
||||
<UButton
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
icon="i-heroicons-x-mark-20-solid"
|
||||
class="flex sm:hidden absolute end-5 top-5 z-10"
|
||||
square
|
||||
padded
|
||||
@click="isOpen = false"
|
||||
/>
|
||||
|
||||
<Placeholder class="h-8" />
|
||||
</template>
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ const emits = defineEmits<{
|
||||
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="emits('close')" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<Placeholder class="h-full" />
|
||||
</UCard>
|
||||
</USlideover>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { SlideoverExampleComponent } from '#components'
|
||||
|
||||
const slideover = useSlideover()
|
||||
const count = ref(0)
|
||||
function openSlideover () {
|
||||
@@ -13,4 +14,4 @@ function openSlideover () {
|
||||
|
||||
<template>
|
||||
<UButton label="Reveal slideover" @click="openSlideover" />
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -8,6 +8,17 @@ const isOpen = ref(false)
|
||||
|
||||
<USlideover v-model="isOpen" :overlay="false">
|
||||
<div class="p-4 flex-1">
|
||||
<UButton
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
icon="i-heroicons-x-mark-20-solid"
|
||||
class="flex sm:hidden absolute end-5 top-5 z-10"
|
||||
square
|
||||
padded
|
||||
@click="isOpen = false"
|
||||
/>
|
||||
|
||||
<Placeholder class="h-full" />
|
||||
</div>
|
||||
</USlideover>
|
||||
|
||||
@@ -8,6 +8,17 @@ const isOpen = ref(false)
|
||||
|
||||
<USlideover v-model="isOpen" :transition="false">
|
||||
<div class="p-4 flex-1">
|
||||
<UButton
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
icon="i-heroicons-x-mark-20-solid"
|
||||
class="flex sm:hidden absolute end-5 top-5 z-10"
|
||||
square
|
||||
padded
|
||||
@click="isOpen = false"
|
||||
/>
|
||||
|
||||
<Placeholder class="h-full" />
|
||||
</div>
|
||||
</USlideover>
|
||||
|
||||
@@ -186,7 +186,7 @@ const { data: todos, pending } = await useLazyAsyncData<{
|
||||
sort-desc-icon="i-heroicons-arrow-down"
|
||||
sort-mode="manual"
|
||||
class="w-full"
|
||||
:ui="{ td: { base: 'max-w-[0] truncate' } }"
|
||||
:ui="{ td: { base: 'max-w-[0] truncate' }, default: { checkbox: { color: 'gray' } } }"
|
||||
@select="select"
|
||||
>
|
||||
<template #completed-data="{ row }">
|
||||
|
||||
63
docs/components/content/examples/TableExampleCaptionSlot.vue
Normal file
63
docs/components/content/examples/TableExampleCaptionSlot.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<script setup lang="ts">
|
||||
const columns = [{
|
||||
key: 'name',
|
||||
label: 'Name'
|
||||
}, {
|
||||
key: 'title',
|
||||
label: 'Title'
|
||||
}, {
|
||||
key: 'email',
|
||||
label: 'Email'
|
||||
}, {
|
||||
key: 'role',
|
||||
label: 'Role'
|
||||
}, {
|
||||
key: 'actions'
|
||||
}]
|
||||
|
||||
const people = [{
|
||||
id: 1,
|
||||
name: 'Lindsay Walton',
|
||||
title: 'Front-end Developer',
|
||||
email: 'lindsay.walton@example.com',
|
||||
role: 'Member'
|
||||
}, {
|
||||
id: 2,
|
||||
name: 'Courtney Henry',
|
||||
title: 'Designer',
|
||||
email: 'courtney.henry@example.com',
|
||||
role: 'Admin'
|
||||
}, {
|
||||
id: 3,
|
||||
name: 'Tom Cook',
|
||||
title: 'Director of Product',
|
||||
email: 'tom.cook@example.com',
|
||||
role: 'Member'
|
||||
}, {
|
||||
id: 4,
|
||||
name: 'Whitney Francis',
|
||||
title: 'Copywriter',
|
||||
email: 'whitney.francis@example.com',
|
||||
role: 'Admin'
|
||||
}, {
|
||||
id: 5,
|
||||
name: 'Leonard Krasner',
|
||||
title: 'Senior Designer',
|
||||
email: 'leonard.krasner@example.com',
|
||||
role: 'Owner'
|
||||
}, {
|
||||
id: 6,
|
||||
name: 'Floyd Miles',
|
||||
title: 'Principal Designer',
|
||||
email: 'floyd.miles@example.com',
|
||||
role: 'Member'
|
||||
}]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UTable :rows="people" :columns="columns">
|
||||
<template #caption>
|
||||
<caption>Employees of ACME</caption>
|
||||
</template>
|
||||
</UTable>
|
||||
</template>
|
||||
49
docs/components/content/examples/TableExampleExpandable.vue
Normal file
49
docs/components/content/examples/TableExampleExpandable.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<script setup>
|
||||
const people = [{
|
||||
id: 1,
|
||||
name: 'Lindsay Walton',
|
||||
title: 'Front-end Developer',
|
||||
email: 'lindsay.walton@example.com',
|
||||
role: 'Member'
|
||||
}, {
|
||||
id: 2,
|
||||
name: 'Courtney Henry',
|
||||
title: 'Designer',
|
||||
email: 'courtney.henry@example.com',
|
||||
role: 'Admin'
|
||||
}, {
|
||||
id: 3,
|
||||
name: 'Tom Cook',
|
||||
title: 'Director of Product',
|
||||
email: 'tom.cook@example.com',
|
||||
role: 'Member'
|
||||
}, {
|
||||
id: 4,
|
||||
name: 'Whitney Francis',
|
||||
title: 'Copywriter',
|
||||
email: 'whitney.francis@example.com',
|
||||
role: 'Admin'
|
||||
}, {
|
||||
id: 5,
|
||||
name: 'Leonard Krasner',
|
||||
title: 'Senior Designer',
|
||||
email: 'leonard.krasner@example.com',
|
||||
role: 'Owner'
|
||||
}, {
|
||||
id: 6,
|
||||
name: 'Floyd Miles',
|
||||
title: 'Principal Designer',
|
||||
email: 'floyd.miles@example.com',
|
||||
role: 'Member'
|
||||
}]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UTable :rows="people">
|
||||
<template #expand="{ row }">
|
||||
<div class="p-4">
|
||||
<pre>{{ row }}</pre>
|
||||
</div>
|
||||
</template>
|
||||
</UTable>
|
||||
</template>
|
||||
@@ -1,13 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
const items = [{
|
||||
label: 'Tab1',
|
||||
icon: 'i-heroicons-information-circle',
|
||||
content: 'This is the content shown for Tab1'
|
||||
}, {
|
||||
label: 'Tab2',
|
||||
icon: 'i-heroicons-arrow-down-tray',
|
||||
disabled: true,
|
||||
content: 'And, this is the content for Tab2'
|
||||
}, {
|
||||
label: 'Tab3',
|
||||
icon: 'i-heroicons-eye-dropper',
|
||||
content: 'Finally, this is the content for Tab3'
|
||||
}]
|
||||
</script>
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
const items = [{
|
||||
label: 'Tab1',
|
||||
icon: 'i-heroicons-information-circle',
|
||||
content: 'This is the content shown for Tab1'
|
||||
}, {
|
||||
label: 'Tab2',
|
||||
icon: 'i-heroicons-arrow-down-tray',
|
||||
content: 'And, this is the content for Tab2'
|
||||
}, {
|
||||
label: 'Tab3',
|
||||
icon: 'i-heroicons-eye-dropper',
|
||||
content: 'Finally, this is the content for Tab3'
|
||||
}]
|
||||
|
||||
|
||||
@@ -17,13 +17,7 @@ const items = [{
|
||||
<template>
|
||||
<UTabs :items="items" class="w-full">
|
||||
<template #default="{ item, index, selected }">
|
||||
<div class="flex items-center gap-2 relative truncate">
|
||||
<UIcon :name="item.icon" class="w-4 h-4 flex-shrink-0" />
|
||||
|
||||
<span class="truncate">{{ index + 1 }}. {{ item.label }}</span>
|
||||
|
||||
<span v-if="selected" class="absolute -right-4 w-2 h-2 rounded-full bg-primary-500 dark:bg-primary-400" />
|
||||
</div>
|
||||
<span class="truncate" :class="[selected && 'text-primary-500 dark:text-primary-400']">{{ index + 1 }}. {{ item.label }}</span>
|
||||
</template>
|
||||
</UTabs>
|
||||
</template>
|
||||
|
||||
23
docs/components/content/examples/TabsExampleIconSlot.vue
Normal file
23
docs/components/content/examples/TabsExampleIconSlot.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<script setup lang="ts">
|
||||
const items = [{
|
||||
label: 'Getting Started',
|
||||
icon: 'i-heroicons-information-circle',
|
||||
content: 'This is the content shown for Tab1'
|
||||
}, {
|
||||
label: 'Installation',
|
||||
icon: 'i-heroicons-arrow-down-tray',
|
||||
content: 'And, this is the content for Tab2'
|
||||
}, {
|
||||
label: 'Theming',
|
||||
icon: 'i-heroicons-eye-dropper',
|
||||
content: 'Finally, this is the content for Tab3'
|
||||
}]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UTabs :items="items" class="w-full">
|
||||
<template #icon="{ item, selected }">
|
||||
<UIcon :name="item.icon" class="w-4 h-4 flex-shrink-0 mr-2" :class="[selected && 'text-primary-500 dark:text-primary-400']" />
|
||||
</template>
|
||||
</UTabs>
|
||||
</template>
|
||||
@@ -1,12 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
const items = [{
|
||||
label: 'Tab1',
|
||||
icon: 'i-heroicons-information-circle',
|
||||
content: 'This is the content shown for Tab1'
|
||||
}, {
|
||||
label: 'Tab2',
|
||||
icon: 'i-heroicons-arrow-down-tray',
|
||||
content: 'And, this is the content for Tab2'
|
||||
}, {
|
||||
label: 'Tab3',
|
||||
icon: 'i-heroicons-eye-dropper',
|
||||
content: 'Finally, this is the content for Tab3'
|
||||
}]
|
||||
</script>
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
const items = [{
|
||||
label: 'Tab1',
|
||||
icon: 'i-heroicons-information-circle',
|
||||
content: 'This is the content shown for Tab1'
|
||||
}, {
|
||||
label: 'Tab2',
|
||||
icon: 'i-heroicons-arrow-down-tray',
|
||||
content: 'And, this is the content for Tab2'
|
||||
}, {
|
||||
label: 'Tab3',
|
||||
icon: 'i-heroicons-eye-dropper',
|
||||
content: 'Finally, this is the content for Tab3'
|
||||
}]
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
const items = [{
|
||||
label: 'Tab1',
|
||||
icon: 'i-heroicons-information-circle',
|
||||
content: 'This is the content shown for Tab1'
|
||||
}, {
|
||||
label: 'Tab2',
|
||||
icon: 'i-heroicons-arrow-down-tray',
|
||||
content: 'And, this is the content for Tab2'
|
||||
}, {
|
||||
label: 'Tab3',
|
||||
icon: 'i-heroicons-eye-dropper',
|
||||
content: 'Finally, this is the content for Tab3'
|
||||
}]
|
||||
</script>
|
||||
|
||||
135
docs/components/home/HomeContributors.vue
Normal file
135
docs/components/home/HomeContributors.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<script setup lang="ts">
|
||||
import { breakpointsTailwind } from '@vueuse/core'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
contributors: {
|
||||
username: string
|
||||
}[]
|
||||
level?: number
|
||||
max?: number
|
||||
}>(), {
|
||||
level: 0,
|
||||
max: 4
|
||||
})
|
||||
|
||||
const contributors = computed(() => props.contributors.slice(0, 5))
|
||||
|
||||
const el = ref(null)
|
||||
const { width } = useElementSize(el)
|
||||
const breakpoints = useBreakpoints(breakpointsTailwind)
|
||||
|
||||
const smallerThanSm = breakpoints.smaller('sm')
|
||||
</script>
|
||||
|
||||
<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-white dark:before:bg-gradient-to-b dark:before:from-gray-950 dark:before:to-gray-900 before:rounded-full z-[--level]"
|
||||
:style="{
|
||||
'--duration': `${((level + 1) * 5)}s`,
|
||||
'--level': level + 1
|
||||
}"
|
||||
>
|
||||
<HomeContributors
|
||||
v-if="(level + 1) < max"
|
||||
:max="max"
|
||||
:level="level + 1"
|
||||
:contributors="props.contributors.slice(5)"
|
||||
/>
|
||||
|
||||
<div
|
||||
ref="el"
|
||||
class="avatars absolute inset-0 grid place-items-center"
|
||||
:style="{
|
||||
'--total': contributors.length,
|
||||
'--offset': `${width / 2}px`
|
||||
}"
|
||||
>
|
||||
<UTooltip
|
||||
v-for="(contributor, index) in contributors"
|
||||
:key="contributor.username"
|
||||
:text="contributor.username"
|
||||
:style="{
|
||||
'--index': index + 1
|
||||
}"
|
||||
class="avatar flex absolute"
|
||||
>
|
||||
<UAvatar
|
||||
:alt="contributor.username"
|
||||
:src="`https://ipx.nuxt.com/s_40x40/gh_avatar/${contributor.username}`"
|
||||
:srcset="`https://ipx.nuxt.com/s_80x80/gh_avatar/${contributor.username} 2x`"
|
||||
class="ring-2 ring-gray-200 dark:ring-gray-700 lg:hover:ring-gray-900 dark:lg:hover:ring-white transition"
|
||||
width="40"
|
||||
height="40"
|
||||
:size="smallerThanSm ? 'xs' : 'sm'"
|
||||
loading="lazy"
|
||||
>
|
||||
<NuxtLink :to="`https://github.com/${contributor.username}`" :aria-label="contributor.username" target="_blank" class="focus:outline-none" tabindex="-1">
|
||||
<span class="absolute inset-0" aria-hidden="true" />
|
||||
</NuxtLink>
|
||||
</UAvatar>
|
||||
</UTooltip>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.circle:after {
|
||||
--start: 0deg;
|
||||
--end: 360deg;
|
||||
--border-color: rgb(var(--color-gray-200));
|
||||
--highlight-color: rgb(var(--color-gray-700));
|
||||
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: -1px;
|
||||
opacity: 1;
|
||||
border-radius: 9999px;
|
||||
z-index: -1;
|
||||
background: var(--border-color);
|
||||
|
||||
@supports (background: paint(houdini)) {
|
||||
background: linear-gradient(var(--angle), var(--border-color), var(--border-color), var(--border-color), var(--border-color), var(--highlight-color));
|
||||
animation: var(--duration) rotate linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
.dark .circle:after {
|
||||
--border-color: rgb(var(--color-gray-700));
|
||||
--highlight-color: white;
|
||||
}
|
||||
|
||||
.avatars {
|
||||
--start: calc(var(--level) * 36deg);
|
||||
--end: calc(360deg + (var(--level) * 36deg));
|
||||
|
||||
transform: rotate(var(--angle));
|
||||
animation: calc(var(--duration) + 60s) rotate linear infinite;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
--deg: calc(var(--index) * (360deg / var(--total)));
|
||||
--transformX: calc(cos(var(--deg)) * var(--offset));
|
||||
--transformY: calc(sin(var(--deg)) * var(--offset));
|
||||
|
||||
transform: translate(var(--transformX), var(--transformY)) rotate(calc(360deg - var(--angle)));
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from {
|
||||
--angle: var(--start);
|
||||
}
|
||||
to {
|
||||
--angle: var(--end);
|
||||
}
|
||||
}
|
||||
|
||||
@property --angle {
|
||||
syntax: '<angle>';
|
||||
initial-value: 0deg;
|
||||
inherits: true;
|
||||
}
|
||||
</style>
|
||||
@@ -31,7 +31,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ULandingGrid ref="section" class="lg:grid-cols-10 lg:gap-8">
|
||||
<ULandingGrid ref="section" class="lg:grid-cols-10 lg:gap-8 overflow-hidden p-px">
|
||||
<div :ref="(el) => (refs[1] = el)" class="col-span-8 flex items-center animate-top">
|
||||
<RangeExample />
|
||||
</div>
|
||||
|
||||
@@ -5,29 +5,15 @@ description: 'Learn how to install and configure Nuxt UI in your application.'
|
||||
|
||||
## Setup
|
||||
|
||||
1. Install `@nuxt/ui` dependency to your project:
|
||||
### Add to a Nuxt project
|
||||
|
||||
::code-group
|
||||
1. Add `@nuxt/ui` module to your project:
|
||||
|
||||
```bash [pnpm]
|
||||
pnpm add @nuxt/ui
|
||||
```bash
|
||||
npx nuxi@latest module add ui
|
||||
```
|
||||
|
||||
```bash [yarn]
|
||||
yarn add @nuxt/ui
|
||||
```
|
||||
|
||||
```bash [npm]
|
||||
npm install @nuxt/ui
|
||||
```
|
||||
|
||||
```bash [bun]
|
||||
bun add @nuxt/ui
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
2. Add it to your `modules` section in your `nuxt.config`:
|
||||
2. Add it to the `modules` section in your `nuxt.config.ts`:
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
@@ -37,14 +23,33 @@ export default defineNuxtConfig({
|
||||
|
||||
That's it! You can now use all the components and composables in your Nuxt app ✨
|
||||
|
||||
### Use Nuxt starter
|
||||
|
||||
[Nuxt Starter](https://nuxt.new/) template makes it easy to get started with Nuxt UI.
|
||||
The Nuxt Starter template is available from the `nuxi init` command.
|
||||
|
||||
```bash
|
||||
npx nuxi@latest init -t ui
|
||||
```
|
||||
|
||||
Please check [nuxt/starter](https://github.com/nuxt/starter/tree/ui) for details.
|
||||
|
||||
## Modules
|
||||
|
||||
Nuxt UI will automatically install the [@nuxtjs/tailwindcss](https://tailwindcss.nuxtjs.org/), [@nuxtjs/color-mode](https://color-mode.nuxtjs.org/) and [nuxt-icon](https://github.com/nuxt-modules/icon) modules for you.
|
||||
Nuxt UI will automatically install the [@nuxt/icon](https://github.com/nuxt/icon), [@nuxtjs/tailwindcss](https://tailwindcss.nuxtjs.org/) and [@nuxtjs/color-mode](https://color-mode.nuxtjs.org/) modules for you.
|
||||
|
||||
::callout{icon="i-heroicons-exclamation-triangle"}
|
||||
You should remove them from your `modules` and `dependencies` if you've previously installed them.
|
||||
::
|
||||
|
||||
### `@nuxt/icon`
|
||||
|
||||
This module is installed to provide an easy way to use icons in your application. You can use the `UIcon` component to display any icon from Iconify.
|
||||
|
||||
::callout{icon="i-heroicons-light-bulb"}
|
||||
You can read more about this in the [Theming](/getting-started/theming#icons) section.
|
||||
::
|
||||
|
||||
### `@nuxtjs/tailwindcss`
|
||||
|
||||
This module is pre-configured and will automatically load the following plugins:
|
||||
@@ -87,14 +92,6 @@ This module is installed to provide dark mode support out of the box thanks to t
|
||||
You can read more about this in the [Theming](/getting-started/theming#dark-mode) section.
|
||||
::
|
||||
|
||||
### `nuxt-icon`
|
||||
|
||||
This module is installed when using the `dynamic` prop on the `Icon` component or globally through the `ui.icons.dynamic` option in your `app.config.ts`.
|
||||
|
||||
::callout{icon="i-heroicons-light-bulb"}
|
||||
You can read more about this in the [Theming](/getting-started/theming#dynamic-icons) section and on the [Icon](/components/icon) component page.
|
||||
::
|
||||
|
||||
## TypeScript
|
||||
|
||||
This module is written in TypeScript and provides typings for all the components and composables. You can look at the [source code](https://github.com/nuxt/ui/tree/dev/src/runtime/types) to see all the available types.
|
||||
@@ -232,7 +229,6 @@ You can also add the following to your `.vscode/settings.json` to enable Intelli
|
||||
|-----------------------|-----------------|-------------------------------------------------------------------------------------------------------------|
|
||||
| `prefix` | `u` | Define the prefix of the imported components. |
|
||||
| `global` | `false` | Expose components globally. |
|
||||
| `icons` | `['heroicons']` | Icon collections to load. |
|
||||
| `safelistColors` | `['primary']` | Force safelisting of colors to need be purged. |
|
||||
| `disableGlobalStyles` | `false` | Disable [global CSS styles](https://github.com/nuxt/ui/blob/dev/src/runtime/ui.css) injected by the module. |
|
||||
|
||||
@@ -242,8 +238,7 @@ Configure options in your `nuxt.config.ts` as such:
|
||||
export default defineNuxtConfig({
|
||||
modules: ['@nuxt/ui'],
|
||||
ui: {
|
||||
global: true,
|
||||
icons: ['mdi', 'simple-icons']
|
||||
global: true
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
@@ -23,7 +23,7 @@ export default defineAppConfig({
|
||||
Try to change the `primary` and `gray` colors by clicking on the :u-icon{name="i-heroicons-swatch-20-solid" class="w-4 h-4 align-middle text-primary-500 dark:text-primary-400"} button in the header.
|
||||
::
|
||||
|
||||
As this module uses Tailwind CSS under the hood, you can use any of the [Tailwind CSS colors](https://tailwindcss.com/docs/customizing-colors#color-palette-reference) or your own custom colors. By default, the `primary` color is `green` and the `gray` color is `cool`.
|
||||
As this module uses Tailwind CSS under the hood, you can use any of the [Tailwind CSS colors](https://tailwindcss.com/docs/customizing-colors#color-palette-reference) or your own custom colors or groups, such as `brand.primary`. By default, the `primary` color is `green` and the `gray` color is `cool`.
|
||||
|
||||
When [using custom colors](https://tailwindcss.com/docs/customizing-colors#using-custom-colors) or [adding additional colors](https://tailwindcss.com/docs/customizing-colors#adding-additional-colors) through the `extend` key in your `tailwind.config.ts`, you'll need to make sure to define all the shades from `50` to `950` as most of them are used in the components config defined in [`ui.config/`](https://github.com/nuxt/ui/tree/dev/src/runtime/ui.config) directory. You can [generate your colors](https://tailwindcss.com/docs/customizing-colors#generating-colors) using tools such as https://uicolors.app/ for example.
|
||||
|
||||
@@ -106,7 +106,7 @@ This can also happen when you bind a dynamic color to a component: `<UBadge :col
|
||||
|
||||
### `app.config.ts`
|
||||
|
||||
You can find the config of each component in the [`ui.config/`](https://github.com/nuxt/ui/tree/dev/src/runtime/ui.config) directory. You can override those classes in your own `app.config.ts`.
|
||||
You can override component config in your own `app.config.ts`:
|
||||
|
||||
```ts [app.config.ts]
|
||||
export default defineAppConfig({
|
||||
@@ -118,6 +118,8 @@ export default defineAppConfig({
|
||||
})
|
||||
```
|
||||
|
||||
The available options for each component should auto-complete, and you can review the defaults for each component using your IDE's function such as `Cmd`+`Click` (these files can be found in [`src/runtime/ui.config/`](https://github.com/nuxt/ui/tree/dev/src/runtime/ui.config)).
|
||||
|
||||
Thanks to [tailwind-merge](https://github.com/dcastil/tailwind-merge), the `app.config.ts` is smartly merged with the default config. This means you don't have to rewrite everything.
|
||||
|
||||
You can change this behavior by setting `strategy` to `override` in your `app.config.ts`:
|
||||
@@ -258,6 +260,7 @@ const isDark = computed({
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ClientOnly>
|
||||
<UButton
|
||||
@@ -277,9 +280,11 @@ const isDark = computed({
|
||||
|
||||
## Icons
|
||||
|
||||
You can use any icon (100,000+) from [Iconify](https://iconify.design/).
|
||||
Thanks to [`@nuxt/icon`](https://github.com/nuxt/icon), add 200,000+ ready to use icons to your Nuxt application based on [Iconify](https://iconify.design).
|
||||
|
||||
Some components have an `icon` prop that allows you to add an icon to the component.
|
||||
You can use any name from the https://icones.js.org collection such as the `i-` prefix (for example, `i-heroicons-cog`) with:
|
||||
|
||||
- any `icon` prop available across the components:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
@@ -287,31 +292,17 @@ Some components have an `icon` prop that allows you to add an icon to the compon
|
||||
</template>
|
||||
```
|
||||
|
||||
You can also use the [Icon](/components/icon) component to add an icon anywhere in your app by following this pattern: `i-{collection_name}-{icon_name}`.
|
||||
- the `UIcon` component to use icons anywhere:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<UIcon name="i-heroicons-moon" />
|
||||
<UIcon name="i-heroicons-moon" class="w-5 h-5 text-primary-500" />
|
||||
</template>
|
||||
```
|
||||
|
||||
### Collections
|
||||
|
||||
By default, the module uses [Heroicons](https://heroicons.com/) but you can change it from the module options in your `nuxt.config.ts`.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
ui: {
|
||||
icons: ['mdi', 'simple-icons']
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
::callout{icon="i-heroicons-light-bulb"}
|
||||
Search the icon you want to use on https://icones.js.org built by [@antfu](https://github.com/antfu).
|
||||
::
|
||||
|
||||
Thanks to [@egoist/tailwindcss-icons](https://github.com/egoist/tailwindcss-icons) plugin, only the icons you use in your app will be bundled in your CSS. However, you need to install the icon collections you specified in the `ui.icons` key:
|
||||
It's highly recommended to install the icons collections locally with:
|
||||
|
||||
::code-group
|
||||
|
||||
@@ -329,55 +320,11 @@ npm install @iconify-json/{collection_name}
|
||||
|
||||
::
|
||||
|
||||
If you choose to use the full `@iconify/json` icon collection (50MB), you can specifiy `icons: 'all'` or `icons: {}` in your `nuxt.config.ts` to use any icon in your app.
|
||||
For example, to use the `i-uil-github` icon, install it's collection with `@iconify-json/uil`. This way the icons can be served locally or from your serverless functions, which is faster and more reliable on both SSR and client-side.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
ui: {
|
||||
icons: {}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Custom config
|
||||
|
||||
If you have specific needs, like using a custom icon collection, you can use the `icons` option in your `nuxt.config.ts` as an object to override the config of the [@egoist/tailwindcss-icons](https://github.com/egoist/tailwindcss-icons#plugin-options) plugin.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
import { getIconCollections } from '@egoist/tailwindcss-icons'
|
||||
|
||||
export default defineNuxtConfig({
|
||||
ui: {
|
||||
icons: {
|
||||
// might solve stretch bug on generate, see https://github.com/egoist/tailwindcss-icons/issues/23
|
||||
extraProperties: {
|
||||
'mask-size': 'contain',
|
||||
'mask-position': 'center'
|
||||
},
|
||||
collections: {
|
||||
foo: {
|
||||
icons: {
|
||||
'arrow-left': {
|
||||
// svg body
|
||||
body: '<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18" />',
|
||||
// svg width and height, optional
|
||||
width: 24,
|
||||
height: 24
|
||||
}
|
||||
}
|
||||
},
|
||||
...getIconCollections(['heroicons', 'simple-icons'])
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Dynamic icons
|
||||
|
||||
The `Icon` component also has a `dynamic` prop to use the [nuxt-icon](https://github.com/nuxt-modules/icon/) module instead of the [@egoist/tailwindcss-icons](https://github.com/egoist/tailwindcss-icons#plugin-options) plugin.
|
||||
|
||||
Read more about this in the [Icon](/components/icon#dynamic) component page.
|
||||
::callout{icon="i-heroicons-light-bulb" to="https://github.com/nuxt/icon?tab=readme-ov-file#custom-local-collections" target="_blank"}
|
||||
Read more about custom collections in the `@nuxt/icon` documentation.
|
||||
::
|
||||
|
||||
### Defaults
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ description: Display togglable accordion panels.
|
||||
links:
|
||||
- label: Disclosure
|
||||
icon: i-simple-icons-headlessui
|
||||
to: 'https://headlessui.com/vue/disclosure'
|
||||
to: 'https://headlessui.com/v1/vue/disclosure'
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/elements/Accordion.vue
|
||||
|
||||
@@ -163,18 +163,22 @@ This can be handy when you want to display HTML content. To achieve this, you ca
|
||||
|
||||
:component-example{component="alert-example-html"}
|
||||
|
||||
### `icon` :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
|
||||
### `icon`
|
||||
|
||||
Use the `#icon` slot to customize the displayed icon.
|
||||
|
||||
:component-example{component="alert-example-icon"}
|
||||
|
||||
### `avatar` :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
|
||||
### `avatar`
|
||||
|
||||
Use the `#avatar` slot to customize the displayable avatar.
|
||||
|
||||
:component-example{component="alert-example-avatar"}
|
||||
|
||||
### `actions`
|
||||
|
||||
Use the `#actions` slot to add custom user interaction elements.
|
||||
|
||||
## Props
|
||||
|
||||
:component-props
|
||||
|
||||
@@ -98,7 +98,7 @@ The number of indicators will be automatically generated based on the number of
|
||||
|
||||
:component-example{component="carousel-example-indicators-size"}
|
||||
|
||||
## Autoplay :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
|
||||
## Autoplay
|
||||
|
||||
You can easily implement an autoplay behavior using the exposed [API](#api) through a template ref.
|
||||
|
||||
|
||||
@@ -87,6 +87,10 @@ slots:
|
||||
[Label]{.italic}
|
||||
::
|
||||
|
||||
### `help` :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
|
||||
|
||||
Like the `#label` slot, use the `#help` slot to override the content of the help text.
|
||||
|
||||
## Props
|
||||
|
||||
:component-props
|
||||
|
||||
@@ -4,7 +4,7 @@ description: Add a customizable command palette to your app.
|
||||
links:
|
||||
- label: 'Combobox'
|
||||
icon: i-simple-icons-headlessui
|
||||
to: 'https://headlessui.com/vue/combobox'
|
||||
to: 'https://headlessui.com/v1/vue/combobox'
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/navigation/CommandPalette.vue
|
||||
|
||||
@@ -66,7 +66,7 @@ excludedProps:
|
||||
---
|
||||
::
|
||||
|
||||
### Size :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
|
||||
### Size
|
||||
|
||||
Use the `size` prop to change the size of the divider.
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ description: Display a list of actions in a dropdown menu.
|
||||
links:
|
||||
- label: Menu
|
||||
icon: i-simple-icons-headlessui
|
||||
to: https://headlessui.com/vue/menu
|
||||
to: https://headlessui.com/v1/vue/menu
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/elements/Dropdown.vue
|
||||
|
||||
@@ -196,7 +196,7 @@ Use the `#description` slot to set the custom content for description.
|
||||
::component-card
|
||||
---
|
||||
slots:
|
||||
description: Write only valid email address <UIcon name="i-heroicons-arrow-right-20-solid" />
|
||||
description: Write only valid email address <UIcon name="i-heroicons-information-circle" />
|
||||
default: <UInput model-value="benjamincanac" placeholder="you@example.com" />
|
||||
props:
|
||||
label: 'Email'
|
||||
|
||||
@@ -8,13 +8,13 @@ links:
|
||||
|
||||
## Usage
|
||||
|
||||
Use the Form component to validate form data using schema libraries such as [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod), [Joi](https://github.com/hapijs/joi), [Valibot](https://valibot.dev/), or your own validation logic.
|
||||
Use the Form component to validate form data using schema libraries such as [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod), [Joi](https://github.com/hapijs/joi), [Valibot](https://github.com/fabian-hiller/valibot), or your own validation logic.
|
||||
|
||||
It works with the [FormGroup](/components/input) component to display error messages around form elements automatically.
|
||||
It works with the [FormGroup](/components/form-group) component to display error messages around form elements automatically.
|
||||
|
||||
The form component requires two props:
|
||||
- `state` - a reactive object holding the form's state.
|
||||
- `schema` - a schema object from [Yup](#yup), [Zod](#zod), [Joi](#joi), or [Valibot](#valibot).
|
||||
- `schema` - a schema object from a validation library like [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod), [Joi](https://github.com/hapijs/joi) or [Valibot](https://github.com/fabian-hiller/valibot).
|
||||
|
||||
::callout{icon="i-heroicons-light-bulb"}
|
||||
Note that **no validation library is included** by default, so ensure you **install the one you need**.
|
||||
@@ -63,7 +63,7 @@ The validation function must return a list of errors with the following attribut
|
||||
- `path` - Path to the form element corresponding to the `name` attribute.
|
||||
|
||||
::callout{icon="i-heroicons-light-bulb"}
|
||||
Note: this can be used alongside the `schema` prop to handle complex use cases.
|
||||
Note that it can be used alongside the `schema` prop to handle complex use cases.
|
||||
::
|
||||
|
||||
::component-example
|
||||
@@ -105,7 +105,7 @@ defineExpose({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UForm ref="form" :model="model" :validate="validateWithVuelidate">
|
||||
<UForm ref="form" :state="model" :validate="validateWithVuelidate">
|
||||
<slot />
|
||||
</UForm>
|
||||
</template>
|
||||
@@ -184,13 +184,13 @@ Take a look at the component!
|
||||
|
||||
## Error event
|
||||
|
||||
You can listen to the `@error` event to handle errors. This event is triggered when the form is validated and contains an array of `FormError` objects with the following fields:
|
||||
You can listen to the `@error` event to handle errors. This event is triggered when the form is submitted and contains an array of `FormError` objects with the following fields:
|
||||
|
||||
- `id` - the identifier of the form element.
|
||||
- `path` - the path to the form element matching the `name`.
|
||||
- `message` - the error message to display.
|
||||
|
||||
Here is an example of how to focus the first form element with an error:
|
||||
Here's an example that focuses the first input element with an error after the form is submitted:
|
||||
|
||||
::component-example
|
||||
---
|
||||
|
||||
@@ -1,56 +1,27 @@
|
||||
---
|
||||
description: Display any icon (100,000+) from Iconify.
|
||||
description: Add 200,000+ ready to use icons to your Nuxt application, based on Iconify.
|
||||
links:
|
||||
- label: GitHub
|
||||
- label: Iconify
|
||||
icon: i-simple-icons-iconify
|
||||
to: https://icones.js.org
|
||||
- label: Nuxt Icon
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/elements/Icon.vue
|
||||
to: https://github.com/nuxt/icon
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
Use the `name` prop by following this pattern: `i-{collection_name}-{icon_name}`. You can search any icon from [Iconify](https://iconify.design/) using https://icones.js.org.
|
||||
You can use any name from the https://icones.js.org collection such as the `i-` prefix:
|
||||
|
||||
::component-card
|
||||
---
|
||||
baseProps:
|
||||
class: 'w-5 h-5'
|
||||
props:
|
||||
name: 'i-heroicons-light-bulb'
|
||||
---
|
||||
::
|
||||
|
||||
::callout{icon="i-heroicons-exclamation-triangle"}
|
||||
You won't be able to use all icons in the `name` prop here as icons are bundled using [egoist/tailwindcss-icons](https://github.com/egoist/tailwindcss-icons).
|
||||
::
|
||||
|
||||
::callout{icon="i-heroicons-light-bulb"}
|
||||
Don't forget to install and specify the icon collections you need in your `nuxt.config.ts`, read more about this in [Theming](/getting-started/theming#icons).
|
||||
It's highly recommended to install the icons collections you need, read more about this in [Theming](/getting-started/theming#icons).
|
||||
::
|
||||
|
||||
### Dynamic
|
||||
|
||||
You can use the `dynamic` prop to enable dynamic icon loading. This will use [`nuxt-icon`](https://github.com/nuxt-modules/icon) instead and allow you to use any icon from [Iconify](https://iconify.design/) as well as the `{collection_name}:{icon_name}` pattern.
|
||||
|
||||
This can be quite useful when using [dynamic class names](https://tailwindcss.com/docs/content-configuration#dynamic-class-names) or for icons that are not bundled by default (fetched from a database for example).
|
||||
|
||||
::component-card
|
||||
---
|
||||
props:
|
||||
name: 'i-ph-rocket-launch'
|
||||
dynamic: true
|
||||
---
|
||||
::
|
||||
|
||||
You can also change the default behavior of the `dynamic` prop by setting the `ui.icons.dynamic` option in your `app.config.ts`.
|
||||
|
||||
```ts [app.config.ts]
|
||||
export default defineAppConfig({
|
||||
ui: {
|
||||
icons: {
|
||||
dynamic: true
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
:component-props
|
||||
|
||||
@@ -4,7 +4,7 @@ description: Display an autocomplete input with real-time suggestions.
|
||||
links:
|
||||
- label: 'Combobox'
|
||||
icon: i-simple-icons-headlessui
|
||||
to: 'https://headlessui.com/vue/combobox'
|
||||
to: 'https://headlessui.com/v1/vue/combobox'
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/InputMenu.vue
|
||||
@@ -130,6 +130,8 @@ Pass a function to the `search` prop to customize the search behavior and filter
|
||||
|
||||
Use the `debounce` prop to adjust the delay of the function.
|
||||
|
||||
Use the `searchLazy` prop to control the immediacy of data requests.
|
||||
|
||||
::component-example
|
||||
---
|
||||
component: 'input-menu-example-search-async'
|
||||
|
||||
@@ -71,10 +71,12 @@ props:
|
||||
|
||||
Use the `type` prop to change the input type, the default `type` is set to `text`, you can check all the available types at [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types).
|
||||
|
||||
Some types have been implemented in their own components, such as [Checkbox](/components/checkbox), [Radio](/components/radio-group), etc. and others have been styled like `file` for example. :u-badge{label="New" class="!rounded-full" variant="subtle"}
|
||||
Some types have been implemented in their own components, such as [Checkbox](/components/checkbox), [Radio](/components/radio-group), etc. and others have been styled like `file` for example.
|
||||
|
||||
::component-card
|
||||
---
|
||||
baseProps:
|
||||
icon: 'i-heroicons-folder'
|
||||
props:
|
||||
type: 'file'
|
||||
size: sm
|
||||
|
||||
@@ -3,7 +3,7 @@ description: Display a modal within your application.
|
||||
links:
|
||||
- label: 'Dialog'
|
||||
icon: i-simple-icons-headlessui
|
||||
to: 'https://headlessui.com/vue/dialog'
|
||||
to: 'https://headlessui.com/v1/vue/dialog'
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/overlays/Modal.vue
|
||||
@@ -59,7 +59,7 @@ Set the `fullscreen` prop to `true` to enable it.
|
||||
|
||||
:component-example{component="modal-example-fullscreen"}
|
||||
|
||||
### Control programmatically :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
|
||||
### Control programmatically
|
||||
|
||||
First of all, add the `Modals` component to your app, preferably inside `app.vue`.
|
||||
|
||||
@@ -77,9 +77,15 @@ First of all, add the `Modals` component to your app, preferably inside `app.vue
|
||||
|
||||
Then, you can use the `useModal` composable to control your modals within your app.
|
||||
|
||||
:component-example{component="modal-example-composable"}
|
||||
<!-- For prerendering -->
|
||||
:component-example{component="modal-example-component" hiddenCode hiddenPreview }
|
||||
|
||||
Additionally, you can close the modal within the modal component by calling `modal.close()`.
|
||||
::code-group{class="[&>div:last-child>div:first-child]:!rounded-t-none"}
|
||||
:component-example{component="modal-example-composable" label="app.vue" }
|
||||
:component-example{component="modal-example-component" hiddenPreview label="modal.vue" }
|
||||
::
|
||||
|
||||
Additionally, you can close the modal within the modal component by calling `modal.close()`.
|
||||
|
||||
## Props
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ export default defineAppConfig({
|
||||
ui: {
|
||||
notifications: {
|
||||
// Show toasts at the top right of the screen
|
||||
position: 'top-0 bottom-auto'
|
||||
position: 'top-0 right-0'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -46,6 +46,12 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
### Links
|
||||
|
||||
Use the `to` property to transform buttons into links. Note that it must be a function that receives the page number and returns a route destination.
|
||||
|
||||
:component-example{component="pagination-example-to"}
|
||||
|
||||
### Disabled
|
||||
|
||||
Use the `disabled` prop to disable all the buttons.
|
||||
|
||||
@@ -3,7 +3,7 @@ description: Display a non-modal dialog that floats around a trigger element.
|
||||
links:
|
||||
- label: 'Popover'
|
||||
icon: i-simple-icons-headlessui
|
||||
to: 'https://headlessui.com/vue/popover'
|
||||
to: 'https://headlessui.com/v1/vue/popover'
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/overlays/Popover.vue
|
||||
|
||||
@@ -126,6 +126,10 @@ slots:
|
||||
[Label]{.italic}
|
||||
::
|
||||
|
||||
### `help` :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
|
||||
|
||||
Like the `#label` slot, use the `#help` slot to override the content of the help text.
|
||||
|
||||
### `legend`
|
||||
|
||||
Use the `#legend` slot to override the content of the legend.
|
||||
|
||||
@@ -4,7 +4,7 @@ description: Display a select menu with advanced features.
|
||||
links:
|
||||
- label: 'Listbox'
|
||||
icon: i-simple-icons-headlessui
|
||||
to: 'https://headlessui.com/vue/listbox'
|
||||
to: 'https://headlessui.com/v1/vue/listbox'
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/SelectMenu.vue
|
||||
@@ -87,7 +87,7 @@ Use the `searchable` prop to enable search.
|
||||
|
||||
Use the `searchable-placeholder` prop to set a different placeholder.
|
||||
|
||||
This will use Headless UI [Combobox](https://headlessui.com/vue/combobox) component instead of [Listbox](https://headlessui.com/vue/listbox).
|
||||
This will use Headless UI [Combobox](https://headlessui.com/v1/vue/combobox) component instead of [Listbox](https://headlessui.com/v1/vue/listbox).
|
||||
|
||||
::component-card
|
||||
---
|
||||
@@ -150,6 +150,8 @@ Pass a function to the `searchable` prop to customize the search behavior and fi
|
||||
|
||||
Use the `debounce` prop to adjust the delay of the function.
|
||||
|
||||
Use the `searchableLazy` prop to control the immediacy of data requests.
|
||||
|
||||
::component-example
|
||||
---
|
||||
component: 'select-menu-example-search-async'
|
||||
@@ -186,6 +188,18 @@ componentProps:
|
||||
---
|
||||
::
|
||||
|
||||
Pass a function to the `show-create-option-when` prop to control wether or not to show the create option. This function takes two arguments: the query (as the first argument) and an array of current results (as the second argument). It should return true to display the create option. :u-badge{label="New" class="!rounded-full" variant="subtle"}
|
||||
|
||||
The example below shows how to make the create option visible when the query is at least three characters long and does not exactly match any of the current results (case insensitive).
|
||||
|
||||
::component-example
|
||||
---
|
||||
component: 'select-menu-example-creatable-function'
|
||||
componentProps:
|
||||
class: 'w-full lg:w-48'
|
||||
---
|
||||
::
|
||||
|
||||
## Popper
|
||||
|
||||
Use the `popper` prop to customize the popper instance.
|
||||
|
||||
@@ -3,7 +3,7 @@ description: Display a dialog that slides in from the edge of the screen.
|
||||
links:
|
||||
- label: 'Dialog'
|
||||
icon: i-simple-icons-headlessui
|
||||
to: 'https://headlessui.com/vue/dialog'
|
||||
to: 'https://headlessui.com/v1/vue/dialog'
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/overlays/Slideover.vue
|
||||
@@ -53,7 +53,7 @@ defineShortcuts({
|
||||
</script>
|
||||
```
|
||||
|
||||
### Control programmatically :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
|
||||
### Control programmatically
|
||||
|
||||
First of all, add the `USlideovers` component to your app, preferably inside `app.vue`.
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ Use the `columns` prop to configure which columns to display. It's an array of o
|
||||
- `sortable` - Whether the column is sortable. Defaults to `false`.
|
||||
- `direction` - The sort direction to use on first click. Defaults to `asc`.
|
||||
- `class` - The class to apply to the column cells.
|
||||
- `rowClass` - The class to apply to the data column cells. :u-badge{label="New" class="!rounded-full" variant="subtle"}
|
||||
- `sort` - Pass your own `sort` function. Defaults to a simple _greater than_ / _less than_ comparison.
|
||||
|
||||
::component-example{class="grid"}
|
||||
@@ -49,7 +50,7 @@ extraClass: 'overflow-hidden'
|
||||
padding: false
|
||||
component: 'table-example-columns-selectable'
|
||||
componentProps:
|
||||
class: 'flex-1'
|
||||
class: 'flex-1 flex-col overflow-hidden'
|
||||
---
|
||||
::
|
||||
|
||||
@@ -255,7 +256,7 @@ componentProps:
|
||||
::
|
||||
|
||||
::callout{icon="i-heroicons-light-bulb"}
|
||||
You can use the `by` prop to compare objects by a field instead of comparing object instances. We've replicated the behavior of Headless UI [Combobox](https://headlessui.com/vue/combobox#binding-objects-as-values).
|
||||
You can use the `by` prop to compare objects by a field instead of comparing object instances. We've replicated the behavior of Headless UI [Combobox](https://headlessui.com/v1/vue/combobox#binding-objects-as-values).
|
||||
::
|
||||
|
||||
You can also add a `select` listener on your Table to make the rows clickable. The function will receive the row as the first argument.
|
||||
@@ -282,7 +283,7 @@ extraClass: 'overflow-hidden'
|
||||
padding: false
|
||||
component: 'table-example-searchable'
|
||||
componentProps:
|
||||
class: 'flex-1'
|
||||
class: 'flex-1 flex-col overflow-hidden'
|
||||
---
|
||||
::
|
||||
|
||||
@@ -295,6 +296,19 @@ You can easily use the [Pagination](/components/pagination) component to paginat
|
||||
extraClass: 'overflow-hidden'
|
||||
padding: false
|
||||
component: 'table-example-paginable'
|
||||
componentProps:
|
||||
class: 'flex-1 flex-col overflow-hidden'
|
||||
---
|
||||
::
|
||||
|
||||
### Expandable :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
|
||||
|
||||
You can use the `expand` slot to display extra information about a row. You will have access to the `row` property in the slot scope.
|
||||
|
||||
::component-example{class="grid"}
|
||||
---
|
||||
padding: false
|
||||
component: 'table-example-expandable'
|
||||
componentProps:
|
||||
class: 'flex-1'
|
||||
---
|
||||
@@ -450,6 +464,19 @@ componentProps:
|
||||
---
|
||||
::
|
||||
|
||||
### `caption`
|
||||
|
||||
Use the `#caption` slot to customize the table's caption.
|
||||
|
||||
::component-example
|
||||
---
|
||||
padding: false
|
||||
component: 'table-example-caption-slot'
|
||||
componentProps:
|
||||
class: 'flex-1'
|
||||
---
|
||||
::
|
||||
|
||||
## Props
|
||||
|
||||
:component-props
|
||||
|
||||
@@ -11,6 +11,7 @@ links:
|
||||
Pass an array to the `items` prop of the Tabs component. Each item can have the following properties:
|
||||
|
||||
- `label` - The label of the item.
|
||||
- `icon` - The icon of the item.
|
||||
- `slot` - A key to customize the item with a slot.
|
||||
- `content` - The content to display in the panel by default.
|
||||
- `disabled` - Determines whether the item is disabled or not.
|
||||
@@ -63,6 +64,8 @@ componentProps:
|
||||
---
|
||||
::
|
||||
|
||||
You can use the `content` prop and set it to `false` to avoid the rendering of the HTML content if you don't need it.
|
||||
|
||||
### Control the selected index
|
||||
|
||||
Use a `v-model` to control the selected index.
|
||||
@@ -89,6 +92,12 @@ Use the `#default` slot to customize the content of the trigger buttons. You wil
|
||||
|
||||
:component-example{component="tabs-example-default-slot"}
|
||||
|
||||
### `icon` :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
|
||||
|
||||
Use the `#icon` slot to customize the icon of the trigger buttons. You will have access to the `item`, `index`, `selected` and `disabled` in the slot scope.
|
||||
|
||||
:component-example{component="tabs-example-icon-slot"}
|
||||
|
||||
### `item`
|
||||
|
||||
Use the `#item` slot to customize the items content. You will have access to the `item`, `index` and `selected` properties in the slot scope.
|
||||
|
||||
@@ -119,8 +119,7 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
Use the `maxrows` prop to set a maximum number of rows when autoresizing. If set to `0`, the Textarea will infinitely grow up. :u-badge{label="New" class="!rounded-full" variant="subtle"}
|
||||
|
||||
Use the `maxrows` prop to set a maximum number of rows when autoresizing. If set to `0`, the Textarea will infinitely grow up.
|
||||
::component-card
|
||||
---
|
||||
baseProps:
|
||||
|
||||
@@ -3,7 +3,7 @@ description: Display a toggle field.
|
||||
links:
|
||||
- label: 'Switch'
|
||||
icon: i-simple-icons-headlessui
|
||||
to: 'https://headlessui.com/vue/switch'
|
||||
to: 'https://headlessui.com/v1/vue/switch'
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/Toggle.vue
|
||||
@@ -52,7 +52,7 @@ excludedProps:
|
||||
---
|
||||
::
|
||||
|
||||
### Loading :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
|
||||
### Loading
|
||||
|
||||
Use the `loading` prop to show a loading icon and disable the Toggle.
|
||||
|
||||
|
||||
@@ -67,14 +67,19 @@ sections:
|
||||
color: black
|
||||
size: lg
|
||||
icon: i-heroicons-cube
|
||||
- label: Star on GitHub
|
||||
to: https://github.com/nuxt/ui
|
||||
target: _blank
|
||||
color: white
|
||||
size: lg
|
||||
icon: i-simple-icons-github
|
||||
cta:
|
||||
title: Trusted and supported by our<br class="hidden lg:block"> amazing community
|
||||
title: Built and driven by an <span class="text-primary">amazing community</span>
|
||||
align: center
|
||||
users:
|
||||
- name: Benjamin Canac
|
||||
username: benjamincanac
|
||||
to: https://twitter.com/benjamincanac
|
||||
- name: Romain Hamel
|
||||
username: romhml
|
||||
to: https://github.com/romhml
|
||||
- name: Neil Richter
|
||||
username: noook
|
||||
to: https://nook.sh/
|
||||
pro:
|
||||
title: Upgrade to <span class="text-primary">Nuxt UI Pro</span>
|
||||
description: 'Nuxt UI Pro is a collection of premium Vue components built on top of Nuxt UI to create beautiful & responsive Nuxt applications in minutes.<br>It includes all primitives to build landing pages, documentations, blogs, dashboards or entire SaaS products.'
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { NuxtError } from '#app'
|
||||
import type { ParsedContent } from '@nuxt/content/dist/runtime/types'
|
||||
import type { ParsedContent } from '@nuxt/content'
|
||||
|
||||
useSeoMeta({
|
||||
title: 'Page not found',
|
||||
@@ -79,7 +79,7 @@ const links = computed(() => {
|
||||
active: route.path.startsWith('/pro/getting-started') || route.path.startsWith('/pro/components') || route.path.startsWith('/pro/prose')
|
||||
}, {
|
||||
label: 'Pricing',
|
||||
icon: 'i-heroicons-credit-card',
|
||||
icon: 'i-heroicons-ticket',
|
||||
to: '/pro/pricing'
|
||||
}, {
|
||||
label: 'Templates',
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { NavItem } from '@nuxt/content/dist/runtime/types'
|
||||
import type { NavItem } from '@nuxt/content'
|
||||
|
||||
const nav = inject<Ref<NavItem[]>>('navigation')
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createResolver } from '@nuxt/kit'
|
||||
import colors from 'tailwindcss/colors'
|
||||
import module from '../src/module'
|
||||
import { excludeColors } from '../src/colors'
|
||||
import { excludeColors } from '../src/runtime/utils/colors'
|
||||
import pkg from '../package.json'
|
||||
|
||||
const { resolve } = createResolver(import.meta.url)
|
||||
@@ -15,6 +15,7 @@ export default defineNuxtConfig({
|
||||
'@nuxt/ui-pro',
|
||||
process.env.NUXT_GITHUB_TOKEN && ['github:nuxt/ui-pro/.docs#dev', { giget: { auth: process.env.NUXT_GITHUB_TOKEN } }]
|
||||
].filter(Boolean),
|
||||
|
||||
modules: [
|
||||
'@nuxt/content',
|
||||
'@nuxt/fonts',
|
||||
@@ -27,16 +28,18 @@ export default defineNuxtConfig({
|
||||
'nuxt-cloudflare-analytics',
|
||||
'modules/content-examples-code'
|
||||
],
|
||||
|
||||
runtimeConfig: {
|
||||
public: {
|
||||
version: pkg.version
|
||||
}
|
||||
},
|
||||
|
||||
ui: {
|
||||
global: true,
|
||||
icons: ['heroicons', 'simple-icons'],
|
||||
safelistColors: excludeColors(colors)
|
||||
},
|
||||
|
||||
content: {
|
||||
highlight: {
|
||||
langs: [
|
||||
@@ -71,19 +74,11 @@ export default defineNuxtConfig({
|
||||
} : undefined
|
||||
}
|
||||
},
|
||||
|
||||
image: {
|
||||
provider: 'ipx'
|
||||
},
|
||||
fontMetrics: {
|
||||
fonts: ['DM Sans']
|
||||
},
|
||||
googleFonts: {
|
||||
display: 'swap',
|
||||
download: true,
|
||||
families: {
|
||||
'DM+Sans': [400, 500, 600, 700]
|
||||
}
|
||||
},
|
||||
|
||||
nitro: {
|
||||
prerender: {
|
||||
routes: [
|
||||
@@ -97,10 +92,12 @@ export default defineNuxtConfig({
|
||||
ignore: !process.env.NUXT_UI_PRO_PATH && !process.env.NUXT_GITHUB_TOKEN ? ['/pro'] : []
|
||||
}
|
||||
},
|
||||
|
||||
routeRules: {
|
||||
'/components': { redirect: '/components/accordion', prerender: false },
|
||||
'/dev/components': { redirect: '/dev/components/accordion', prerender: false }
|
||||
},
|
||||
|
||||
componentMeta: {
|
||||
exclude: [
|
||||
'@nuxt/content',
|
||||
@@ -121,10 +118,12 @@ export default defineNuxtConfig({
|
||||
exposed: false
|
||||
}
|
||||
},
|
||||
|
||||
cloudflareAnalytics: {
|
||||
token: '1e2b0c5e9a214f0390b9b94e043d8d4c',
|
||||
scriptPath: false
|
||||
},
|
||||
|
||||
hooks: {
|
||||
// Related to https://github.com/nuxt/nuxt/pull/22558
|
||||
'components:extend': (components) => {
|
||||
@@ -137,12 +136,20 @@ export default defineNuxtConfig({
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
typescript: {
|
||||
strict: false
|
||||
},
|
||||
|
||||
site: {
|
||||
url: 'https://ui.nuxt.com'
|
||||
},
|
||||
|
||||
vite: {
|
||||
optimizeDeps: {
|
||||
include: ['date-fns']
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
compatibilityDate: '2024-07-23'
|
||||
})
|
||||
@@ -3,32 +3,30 @@
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@nuxt/ui": "workspace:latest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/heroicons": "^1.1.20",
|
||||
"@iconify-json/simple-icons": "^1.1.97",
|
||||
"@nuxt/content": "^2.12.1",
|
||||
"@nuxt/eslint-config": "^0.2.0",
|
||||
"@nuxt/fonts": "^0.5.1",
|
||||
"@nuxt/image": "^1.4.0",
|
||||
"@nuxt/ui-pro": "npm:@nuxt/ui-pro-edge@1.1.0-28524317.f36a434",
|
||||
"@nuxtjs/plausible": "^0.2.4",
|
||||
"@octokit/rest": "^20.0.2",
|
||||
"@vueuse/nuxt": "^10.9.0",
|
||||
"@iconify-json/heroicons": "^1.1.23",
|
||||
"@iconify-json/simple-icons": "^1.1.111",
|
||||
"@iconify-json/vscode-icons": "^1.1.36",
|
||||
"@nuxt/content": "^2.13.2",
|
||||
"@nuxt/eslint-config": "^0.4.0",
|
||||
"@nuxt/fonts": "^0.7.1",
|
||||
"@nuxt/image": "^1.7.0",
|
||||
"@nuxt/ui": "latest",
|
||||
"@nuxt/ui-pro": "npm:@nuxt/ui-pro-edge@1.3.1-28698404.4d54eb2",
|
||||
"@nuxtjs/plausible": "^1.0.0",
|
||||
"@octokit/rest": "^21.0.1",
|
||||
"@vueuse/nuxt": "^10.11.0",
|
||||
"date-fns": "^3.6.0",
|
||||
"eslint": "^8.57.0",
|
||||
"joi": "^17.12.2",
|
||||
"nuxt": "^3.11.1",
|
||||
"joi": "^17.13.3",
|
||||
"nuxt": "^3.12.4",
|
||||
"nuxt-cloudflare-analytics": "^1.0.8",
|
||||
"nuxt-component-meta": "^0.6.3",
|
||||
"nuxt-og-image": "^2.2.4",
|
||||
"prettier": "^3.2.5",
|
||||
"typescript": "^5.4.3",
|
||||
"ufo": "^1.5.3",
|
||||
"nuxt-component-meta": "^0.6.4",
|
||||
"nuxt-og-image": "^3.0.0-rc.64",
|
||||
"prettier": "^3.3.3",
|
||||
"ufo": "^1.5.4",
|
||||
"v-calendar": "^3.1.2",
|
||||
"valibot": "^0.30.0",
|
||||
"valibot": "^0.36.0",
|
||||
"yup": "^1.4.0",
|
||||
"zod": "^3.22.4"
|
||||
"zod": "^3.23.8"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,10 +75,7 @@ useSeoMeta({
|
||||
ogDescription: page.value.description
|
||||
})
|
||||
|
||||
defineOgImage({
|
||||
component: 'Docs',
|
||||
title: page.value.title,
|
||||
description: page.value.description,
|
||||
defineOgImageComponent('Docs', {
|
||||
headline: headline.value
|
||||
})
|
||||
|
||||
|
||||
@@ -31,10 +31,11 @@
|
||||
readonly
|
||||
autocomplete="off"
|
||||
icon="i-heroicons-command-line"
|
||||
input-class="select-none"
|
||||
class="w-72"
|
||||
input-class="focus:ring-1 focus:ring-gray-300 dark:focus:ring-gray-700"
|
||||
aria-label="Install @nuxt/ui"
|
||||
size="lg"
|
||||
:ui="{ base: 'disabled:cursor-default', icon: { trailing: { pointer: '' } } }"
|
||||
:ui="{ icon: { trailing: { pointer: '' } } }"
|
||||
>
|
||||
<template #trailing>
|
||||
<UButton
|
||||
@@ -101,77 +102,79 @@
|
||||
<ULandingSection class="!pt-0 dark:bg-gradient-to-b from-gray-950/50 to-gray-900">
|
||||
<ULandingCTA
|
||||
align="left"
|
||||
card
|
||||
:card="false"
|
||||
:ui="{
|
||||
background: 'dark:bg-gradient-to-b from-gray-800 to-gray-900',
|
||||
shadow: 'dark:shadow-2xl',
|
||||
body: {
|
||||
background: 'bg-gray-50/50 dark:bg-gray-900/50'
|
||||
padding: '!p-0'
|
||||
},
|
||||
title: 'text-center lg:text-left',
|
||||
links: 'justify-center lg:justify-start'
|
||||
title: 'text-center lg:text-left lg:text-5xl',
|
||||
description: 'mt-10 flex flex-col sm:flex-row items-center justify-center lg:justify-start gap-8 lg:gap-16',
|
||||
links: '-ml-3 justify-center lg:justify-start flex-wrap gap-y-3'
|
||||
}"
|
||||
>
|
||||
<template #title>
|
||||
<span v-html="page.cta.title" />
|
||||
</template>
|
||||
|
||||
<template #links>
|
||||
<ClientOnly>
|
||||
<UAvatarGroup :max="xlAndLarger ? 13 : lgAndLarger ? 10 : mdAndLarger ? 16 : 8" size="md" class="flex-wrap-reverse [&_span:first-child]:text-xs justify-center">
|
||||
<UTooltip
|
||||
v-for="(contributor, index) of module.contributors"
|
||||
:key="index"
|
||||
:text="contributor.username"
|
||||
class="rounded-full"
|
||||
:ui="{ background: 'bg-gray-50 dark:bg-gray-800/50' }"
|
||||
:popper="{ offsetDistance: 16 }"
|
||||
>
|
||||
<UAvatar
|
||||
:alt="contributor.username"
|
||||
:src="`https://ipx.nuxt.com/s_40x40/gh_avatar/${contributor.username}`"
|
||||
:srcset="`https://ipx.nuxt.com/s_80x80/gh_avatar/${contributor.username} 2x`"
|
||||
class="lg:hover:scale-125 lg:hover:ring-2 lg:hover:ring-primary-500 dark:lg:hover:ring-primary-400 transition-transform"
|
||||
width="40"
|
||||
height="40"
|
||||
size="md"
|
||||
loading="lazy"
|
||||
>
|
||||
<NuxtLink :to="`https://github.com/${contributor.username}`" :aria-label="contributor.username" target="_blank" class="focus:outline-none" tabindex="-1">
|
||||
<span class="absolute inset-0" aria-hidden="true" />
|
||||
</NuxtLink>
|
||||
</UAvatar>
|
||||
</UTooltip>
|
||||
</UAvatarGroup>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
|
||||
<div class="flex flex-col sm:flex-row items-center justify-center gap-8 lg:gap-16">
|
||||
<NuxtLink class="text-center group" to="https://npmjs.org/package/@nuxt/ui" target="_blank">
|
||||
<p class="text-6xl font-semibold text-gray-900 dark:text-white group-hover:text-primary-500 dark:group-hover:text-primary-400">
|
||||
<template #description>
|
||||
<NuxtLink class="text-center lg:text-left group" to="https://npmjs.org/package/@nuxt/ui" target="_blank">
|
||||
<p class="text-5xl font-semibold text-gray-900 dark:text-white group-hover:text-primary-500 dark:group-hover:text-primary-400">
|
||||
{{ format(module.stats.downloads) }}+
|
||||
</p>
|
||||
<p>monthly downloads</p>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink class="text-center group" to="https://github.com/nuxt/ui" target="_blank">
|
||||
<p class="text-6xl font-semibold text-gray-900 dark:text-white group-hover:text-primary-500 dark:group-hover:text-primary-400">
|
||||
<NuxtLink class="text-center lg:text-left group" to="https://github.com/nuxt/ui" target="_blank">
|
||||
<p class="text-5xl font-semibold text-gray-900 dark:text-white group-hover:text-primary-500 dark:group-hover:text-primary-400">
|
||||
{{ format(module.stats.stars) }}+
|
||||
</p>
|
||||
<p>stars</p>
|
||||
<p>GitHub stars</p>
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
<template #links>
|
||||
<UButton
|
||||
v-for="user in page.cta.users"
|
||||
:key="user.username"
|
||||
:to="user.to"
|
||||
size="md"
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
target="_blank"
|
||||
>
|
||||
<UAvatar
|
||||
:alt="user.username"
|
||||
:src="`https://ipx.nuxt.com/s_80x80/gh_avatar/${user.username}`"
|
||||
:srcset="`https://ipx.nuxt.com/s_160x160/gh_avatar/${user.username} 2x`"
|
||||
width="80"
|
||||
height="80"
|
||||
size="md"
|
||||
loading="lazy"
|
||||
/>
|
||||
|
||||
<div class="text-left">
|
||||
<p class="font-medium">
|
||||
{{ user.name }}
|
||||
</p>
|
||||
<p class="text-gray-500 dark:text-gray-400 leading-4">
|
||||
{{ `@${user.username}` }}
|
||||
</p>
|
||||
</div>
|
||||
</UButton>
|
||||
</template>
|
||||
|
||||
<div class="p-5 overflow-hidden flex">
|
||||
<HomeContributors :contributors="module.contributors" />
|
||||
</div>
|
||||
</ULandingCTA>
|
||||
</ULandingSection>
|
||||
|
||||
<template v-if="navigation.find(item => item._path === '/pro')">
|
||||
<div class="relative">
|
||||
<UDivider class="absolute inset-x-0" />
|
||||
|
||||
<div class="w-full relative overflow-hidden h-px bg-gradient-to-r from-gray-800 via-primary-400 to-gray-800 max-w-5xl mx-auto" />
|
||||
</div>
|
||||
|
||||
<ULandingHero id="pro" :links="page.pro.links" :ui="{ title: 'sm:text-6xl' }" class="bg-gradient-to-b from-gray-50 dark:from-gray-950/50 to-white dark:to-gray-900 relative">
|
||||
<template #top>
|
||||
<Gradient class="absolute inset-x-0 top-0 w-full block" />
|
||||
</template>
|
||||
|
||||
<template #title>
|
||||
<span v-html="page.pro.title" />
|
||||
</template>
|
||||
@@ -406,7 +409,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ParsedContent, NavItem } from '@nuxt/content/dist/runtime/types'
|
||||
import type { ParsedContent, NavItem } from '@nuxt/content'
|
||||
import { useElementBounding, useWindowScroll, useElementSize, breakpointsTailwind, useBreakpoints } from '@vueuse/core'
|
||||
import type { HomeProBlock } from '~/types'
|
||||
|
||||
@@ -435,11 +438,12 @@ useSeoMeta({
|
||||
twitterImage: 'https://ui.nuxt.com/social-card.png'
|
||||
})
|
||||
|
||||
const source = ref('npm i @nuxt/ui')
|
||||
const source = ref('npx nuxi@latest module add ui')
|
||||
const sectionRef = ref()
|
||||
const demoRef = ref()
|
||||
const demoRef = ref(null)
|
||||
const start = ref(0)
|
||||
|
||||
const { $ui } = useNuxtApp()
|
||||
const { height } = useElementSize(demoRef)
|
||||
const { top } = useElementBounding(sectionRef)
|
||||
const { y } = useWindowScroll()
|
||||
@@ -447,9 +451,7 @@ const config = useRuntimeConfig().public
|
||||
const { copy, copied } = useClipboard({ source })
|
||||
const breakpoints = useBreakpoints(breakpointsTailwind)
|
||||
|
||||
const mdAndLarger = breakpoints.greaterOrEqual('md')
|
||||
const lgAndLarger = breakpoints.greaterOrEqual('lg')
|
||||
const xlAndLarger = breakpoints.greaterOrEqual('xl')
|
||||
|
||||
const { format } = Intl.NumberFormat('en', { notation: 'compact' })
|
||||
|
||||
|
||||
@@ -9,11 +9,7 @@ useSeoMeta({
|
||||
description
|
||||
})
|
||||
|
||||
defineOgImage({
|
||||
component: 'Docs',
|
||||
title,
|
||||
description
|
||||
})
|
||||
defineOgImageComponent('Docs')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -69,11 +69,7 @@ useSeoMeta({
|
||||
ogDescription: description
|
||||
})
|
||||
|
||||
defineOgImage({
|
||||
component: 'Docs',
|
||||
title,
|
||||
description
|
||||
})
|
||||
defineOgImageComponent('Docs')
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -9,11 +9,7 @@ useSeoMeta({
|
||||
description
|
||||
})
|
||||
|
||||
defineOgImage({
|
||||
component: 'Docs',
|
||||
title,
|
||||
description
|
||||
})
|
||||
defineOgImageComponent('Docs')
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
const colorMode = useColorMode()
|
||||
|
||||
64
package.json
64
package.json
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "@nuxt/ui",
|
||||
"version": "2.15.0",
|
||||
"version": "2.18.4",
|
||||
"packageManager": "pnpm@9.6.0",
|
||||
"repository": "nuxt/ui",
|
||||
"homepage": "https://ui.nuxt.com",
|
||||
"type": "module",
|
||||
@@ -33,55 +34,52 @@
|
||||
"test": "vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@egoist/tailwindcss-icons": "^1.7.4",
|
||||
"@headlessui/tailwindcss": "^0.2.0",
|
||||
"@headlessui/vue": "^1.7.19",
|
||||
"@iconify-json/heroicons": "^1.1.20",
|
||||
"@nuxt/kit": "^3.11.1",
|
||||
"@nuxtjs/color-mode": "^3.3.3",
|
||||
"@nuxtjs/tailwindcss": "^6.11.4",
|
||||
"@headlessui/tailwindcss": "^0.2.1",
|
||||
"@headlessui/vue": "^1.7.22",
|
||||
"@iconify-json/heroicons": "^1.1.23",
|
||||
"@nuxt/icon": "^1.4.0",
|
||||
"@nuxt/kit": "^3.12.4",
|
||||
"@nuxtjs/color-mode": "^3.4.2",
|
||||
"@nuxtjs/tailwindcss": "^6.12.1",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@tailwindcss/aspect-ratio": "^0.4.2",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@tailwindcss/forms": "^0.5.7",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@vueuse/core": "^10.9.0",
|
||||
"@vueuse/integrations": "^10.9.0",
|
||||
"@vueuse/math": "^10.9.0",
|
||||
"@tailwindcss/typography": "^0.5.13",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"@vueuse/integrations": "^10.11.0",
|
||||
"@vueuse/math": "^10.11.0",
|
||||
"defu": "^6.1.4",
|
||||
"fuse.js": "^6.6.2",
|
||||
"nuxt-icon": "^0.6.10",
|
||||
"ohash": "^1.1.3",
|
||||
"pathe": "^1.1.2",
|
||||
"scule": "^1.3.0",
|
||||
"tailwind-merge": "^2.2.2",
|
||||
"tailwindcss": "^3.4.1"
|
||||
"tailwind-merge": "^2.4.0",
|
||||
"tailwindcss": "^3.4.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/eslint-config": "^0.2.0",
|
||||
"@nuxt/module-builder": "^0.5.5",
|
||||
"@nuxt/test-utils": "^3.12.0",
|
||||
"@nuxt/eslint-config": "^0.4.0",
|
||||
"@nuxt/module-builder": "^0.8.1",
|
||||
"@nuxt/test-utils": "^3.14.0",
|
||||
"@release-it/conventional-changelog": "^8.0.1",
|
||||
"@vue/test-utils": "^2.4.5",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"eslint": "^8.57.0",
|
||||
"happy-dom": "^14.3.6",
|
||||
"joi": "^17.12.2",
|
||||
"nuxt": "^3.11.1",
|
||||
"release-it": "^17.1.1",
|
||||
"typescript": "^5.4.3",
|
||||
"happy-dom": "^14.12.3",
|
||||
"joi": "^17.13.3",
|
||||
"nuxt": "^3.12.4",
|
||||
"release-it": "^17.6.0",
|
||||
"unbuild": "^2.0.0",
|
||||
"valibot": "^0.30.0",
|
||||
"vitest": "^1.4.0",
|
||||
"valibot": "^0.36.0",
|
||||
"valibot30": "npm:valibot@0.30.0",
|
||||
"valibot31": "npm:valibot@0.31.0",
|
||||
"vitest": "^2.0.4",
|
||||
"vitest-environment-nuxt": "^1.0.0",
|
||||
"vue-tsc": "^2.0.7",
|
||||
"vue-tsc": "^2.0.29",
|
||||
"yup": "^1.4.0",
|
||||
"zod": "^3.22.4"
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"resolutions": {
|
||||
"@nuxt/kit": "^3.11.1",
|
||||
"@nuxt/schema": "3.11.1",
|
||||
"tailwindcss": "3.4.1",
|
||||
"@headlessui/vue": "1.7.19",
|
||||
"vue": "3.4.21"
|
||||
"@nuxt/ui": "workspace:*",
|
||||
"nuxt-component-meta": "0.6.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export default defineNuxtConfig({
|
||||
modules: [
|
||||
'../src/module'
|
||||
]
|
||||
})
|
||||
],
|
||||
|
||||
compatibilityDate: '2024-07-23'
|
||||
})
|
||||
14
playground/package.json
Normal file
14
playground/package.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "@nuxt/ui-playground",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "nuxi dev",
|
||||
"build": "nuxi build",
|
||||
"generate": "nuxi generate"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxt/ui": "latest",
|
||||
"nuxt": "^3.12.4"
|
||||
}
|
||||
}
|
||||
20969
pnpm-lock.yaml
generated
20969
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,4 @@
|
||||
packages:
|
||||
- "docs"
|
||||
- "./"
|
||||
- "docs"
|
||||
- "playground"
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
{
|
||||
"extends": [
|
||||
"github>nuxt/renovate-config-nuxt"
|
||||
],
|
||||
"ignoreDeps": [
|
||||
"nuxt-component-meta",
|
||||
"valibot30",
|
||||
"valibot31"
|
||||
]
|
||||
}
|
||||
|
||||
285
src/colors.ts
285
src/colors.ts
@@ -1,285 +0,0 @@
|
||||
import { omit } from './runtime/utils/lodash'
|
||||
import { kebabCase, camelCase, upperFirst } from 'scule'
|
||||
|
||||
const colorsToExclude = [
|
||||
'inherit',
|
||||
'transparent',
|
||||
'current',
|
||||
'white',
|
||||
'black',
|
||||
'slate',
|
||||
'gray',
|
||||
'zinc',
|
||||
'neutral',
|
||||
'stone',
|
||||
'cool'
|
||||
]
|
||||
|
||||
const safelistByComponent = {
|
||||
alert: (colorsAsRegex) => [{
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-50`)
|
||||
}, {
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-400`),
|
||||
variants: ['dark']
|
||||
}, {
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-500`)
|
||||
}, {
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-400`),
|
||||
variants: ['dark']
|
||||
}, {
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-500`)
|
||||
}, {
|
||||
pattern: new RegExp(`ring-(${colorsAsRegex})-400`),
|
||||
variants: ['dark']
|
||||
}, {
|
||||
pattern: new RegExp(`ring-(${colorsAsRegex})-500`)
|
||||
}],
|
||||
avatar: (colorsAsRegex) => [{
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-400`),
|
||||
variants: ['dark']
|
||||
}, {
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-500`)
|
||||
}],
|
||||
badge: (colorsAsRegex) => [{
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-50`)
|
||||
}, {
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-400`),
|
||||
variants: ['dark']
|
||||
}, {
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-500`)
|
||||
}, {
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-400`),
|
||||
variants: ['dark']
|
||||
}, {
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-500`)
|
||||
}, {
|
||||
pattern: new RegExp(`ring-(${colorsAsRegex})-400`),
|
||||
variants: ['dark']
|
||||
}, {
|
||||
pattern: new RegExp(`ring-(${colorsAsRegex})-500`)
|
||||
}],
|
||||
button: (colorsAsRegex) => [{
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-50`),
|
||||
variants: ['hover', 'disabled']
|
||||
}, {
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-100`),
|
||||
variants: ['hover']
|
||||
}, {
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-400`),
|
||||
variants: ['dark', 'dark:disabled']
|
||||
}, {
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-500`),
|
||||
variants: ['disabled', 'dark:hover']
|
||||
}, {
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-600`),
|
||||
variants: ['hover']
|
||||
}, {
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-900`),
|
||||
variants: ['dark:hover']
|
||||
}, {
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-950`),
|
||||
variants: ['dark', 'dark:hover', 'dark:disabled']
|
||||
}, {
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-400`),
|
||||
variants: ['dark', 'dark:disabled']
|
||||
}, {
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-500`),
|
||||
variants: ['dark:hover', 'disabled']
|
||||
}, {
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-600`),
|
||||
variants: ['hover']
|
||||
}, {
|
||||
pattern: new RegExp(`outline-(${colorsAsRegex})-400`),
|
||||
variants: ['dark:focus-visible']
|
||||
}, {
|
||||
pattern: new RegExp(`outline-(${colorsAsRegex})-500`),
|
||||
variants: ['focus-visible']
|
||||
}, {
|
||||
pattern: new RegExp(`ring-(${colorsAsRegex})-400`),
|
||||
variants: ['dark:focus-visible']
|
||||
}, {
|
||||
pattern: new RegExp(`ring-(${colorsAsRegex})-500`),
|
||||
variants: ['focus-visible']
|
||||
}],
|
||||
input: (colorsAsRegex) => [{
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-400`),
|
||||
variants: ['dark']
|
||||
}, {
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-500`)
|
||||
}, {
|
||||
pattern: new RegExp(`ring-(${colorsAsRegex})-400`),
|
||||
variants: ['dark', 'dark:focus']
|
||||
}, {
|
||||
pattern: new RegExp(`ring-(${colorsAsRegex})-500`),
|
||||
variants: ['focus']
|
||||
}],
|
||||
radio: (colorsAsRegex) => [{
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-400`),
|
||||
variants: ['dark']
|
||||
}, {
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-500`)
|
||||
}, {
|
||||
pattern: new RegExp(`ring-(${colorsAsRegex})-400`),
|
||||
variants: ['dark:focus-visible']
|
||||
}, {
|
||||
pattern: new RegExp(`ring-(${colorsAsRegex})-500`),
|
||||
variants: ['focus-visible']
|
||||
}],
|
||||
checkbox: (colorsAsRegex) => [{
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-400`),
|
||||
variants: ['dark']
|
||||
}, {
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-500`)
|
||||
}, {
|
||||
pattern: new RegExp(`ring-(${colorsAsRegex})-400`),
|
||||
variants: ['dark:focus-visible']
|
||||
}, {
|
||||
pattern: new RegExp(`ring-(${colorsAsRegex})-500`),
|
||||
variants: ['focus-visible']
|
||||
}],
|
||||
toggle: (colorsAsRegex) => [{
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-400`),
|
||||
variants: ['dark']
|
||||
}, {
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-500`)
|
||||
}, {
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-400`),
|
||||
variants: ['dark']
|
||||
}, {
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-500`)
|
||||
}, {
|
||||
pattern: new RegExp(`ring-(${colorsAsRegex})-400`),
|
||||
variants: ['dark:focus-visible']
|
||||
}, {
|
||||
pattern: new RegExp(`ring-(${colorsAsRegex})-500`),
|
||||
variants: ['focus-visible']
|
||||
}],
|
||||
range: (colorsAsRegex) => [{
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-400`),
|
||||
variants: ['dark']
|
||||
}, {
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-500`)
|
||||
}, {
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-400`),
|
||||
variants: ['dark']
|
||||
}, {
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-500`)
|
||||
}, {
|
||||
pattern: new RegExp(`ring-(${colorsAsRegex})-400`),
|
||||
variants: ['dark:focus-visible']
|
||||
}, {
|
||||
pattern: new RegExp(`ring-(${colorsAsRegex})-500`),
|
||||
variants: ['focus-visible']
|
||||
}],
|
||||
progress: (colorsAsRegex) => [{
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-400`),
|
||||
variants: ['dark']
|
||||
}, {
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-500`)
|
||||
}],
|
||||
meter: (colorsAsRegex) => [{
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-400`),
|
||||
variants: ['dark']
|
||||
}, {
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-500`)
|
||||
}, {
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-400`),
|
||||
variants: ['dark']
|
||||
}, {
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-500`)
|
||||
}],
|
||||
notification: (colorsAsRegex) => [{
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-400`),
|
||||
variants: ['dark']
|
||||
}, {
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-500`)
|
||||
}, {
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-400`),
|
||||
variants: ['dark']
|
||||
}, {
|
||||
pattern: new RegExp(`text-(${colorsAsRegex})-500`)
|
||||
}],
|
||||
chip: (colorsAsRegex) => [{
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-400`),
|
||||
variants: ['dark']
|
||||
}, {
|
||||
pattern: new RegExp(`bg-(${colorsAsRegex})-500`)
|
||||
}]
|
||||
}
|
||||
|
||||
const safelistComponentAliasesMap = {
|
||||
'USelect': 'UInput',
|
||||
'USelectMenu': 'UInput',
|
||||
'UTextarea': 'UInput',
|
||||
'URadioGroup': 'URadio',
|
||||
'UMeterGroup': 'UMeter'
|
||||
}
|
||||
|
||||
const colorsAsRegex = (colors: string[]): string => colors.join('|')
|
||||
|
||||
export const excludeColors = (colors: object): string[] => {
|
||||
return Object.entries(omit(colors, colorsToExclude))
|
||||
.filter(([, value]) => typeof value === 'object')
|
||||
.map(([key]) => kebabCase(key))
|
||||
}
|
||||
|
||||
export const generateSafelist = (colors: string[], globalColors) => {
|
||||
const baseSafelist = Object.keys(safelistByComponent).flatMap(component => safelistByComponent[component](colorsAsRegex(colors)))
|
||||
|
||||
// Ensure `red` color is safelisted for form elements so that `error` prop of `UFormGroup` always works
|
||||
const formsSafelist = ['input', 'radio', 'checkbox', 'toggle', 'range'].flatMap(component => safelistByComponent[component](colorsAsRegex(['red'])))
|
||||
|
||||
return [
|
||||
...baseSafelist,
|
||||
...formsSafelist,
|
||||
// Ensure all global colors are safelisted for the Notification (toast.add)
|
||||
...safelistByComponent['notification'](colorsAsRegex(globalColors)),
|
||||
// Gray safelist for Avatar & Notification
|
||||
'bg-gray-500',
|
||||
'dark:bg-gray-400',
|
||||
'text-gray-500',
|
||||
'dark:text-gray-400'
|
||||
]
|
||||
}
|
||||
|
||||
export const customSafelistExtractor = (prefix, content: string, colors: string[], safelistColors: string[]) => {
|
||||
const classes: string[] = []
|
||||
const regex = /<([A-Za-z][A-Za-z0-9]*(?:-[A-Za-z][A-Za-z0-9]*)*)\s+(?![^>]*:color\b)[^>]*\bcolor=["']([^"']+)["'][^>]*>/gs
|
||||
|
||||
const matches = content.matchAll(regex)
|
||||
|
||||
const components = Object.keys(safelistByComponent).map(component => `${prefix}${component.charAt(0).toUpperCase() + component.slice(1)}`)
|
||||
|
||||
for (const match of matches) {
|
||||
const [, component, color] = match
|
||||
|
||||
const camelComponent = upperFirst(camelCase(component))
|
||||
|
||||
if (!colors.includes(color) || safelistColors.includes(color)) {
|
||||
continue
|
||||
}
|
||||
|
||||
let name = safelistComponentAliasesMap[camelComponent] ? safelistComponentAliasesMap[camelComponent] : camelComponent
|
||||
|
||||
if (!components.includes(name)) {
|
||||
continue
|
||||
}
|
||||
|
||||
name = name.replace(prefix, '').toLowerCase()
|
||||
|
||||
const matchClasses = safelistByComponent[name](color).flatMap(group => {
|
||||
return ['', ...(group.variants || [])].flatMap(variant => {
|
||||
const matches = group.pattern.source.match(/\(([^)]+)\)/g)
|
||||
|
||||
return matches.map(match => {
|
||||
const colorOptions = match.substring(1, match.length - 1).split('|')
|
||||
return colorOptions.map(color => `${variant ? variant + ':' : ''}` + group.pattern.source.replace(match, color))
|
||||
}).flat()
|
||||
})
|
||||
})
|
||||
|
||||
classes.push(...matchClasses)
|
||||
}
|
||||
|
||||
return classes
|
||||
}
|
||||
112
src/module.ts
112
src/module.ts
@@ -1,14 +1,11 @@
|
||||
import { createRequire } from 'node:module'
|
||||
import { defineNuxtModule, installModule, addComponentsDir, addImportsDir, createResolver, addPlugin } from '@nuxt/kit'
|
||||
import { defaultExtractor as createDefaultExtractor } from 'tailwindcss/lib/lib/defaultExtractor.js'
|
||||
import { iconsPlugin, getIconCollections, type CollectionNames, type IconsPluginOptions } from '@egoist/tailwindcss-icons'
|
||||
import { name, version } from '../package.json'
|
||||
import createTemplates from './templates'
|
||||
import { generateSafelist, excludeColors, customSafelistExtractor } from './colors'
|
||||
import * as config from './runtime/ui.config'
|
||||
import type { DeepPartial, Strategy } from './runtime/types/utils'
|
||||
import type { DeepPartial, Strategy } from './runtime/types'
|
||||
import installTailwind from './tailwind'
|
||||
|
||||
const defaultExtractor = createDefaultExtractor({ tailwindConfig: { separator: ':' } })
|
||||
const _require = createRequire(import.meta.url)
|
||||
const defaultColors = _require('tailwindcss/colors.js')
|
||||
|
||||
@@ -24,7 +21,7 @@ type UI = {
|
||||
colors?: string[]
|
||||
strategy?: Strategy
|
||||
[key: string]: any
|
||||
} & DeepPartial<typeof config>
|
||||
} & DeepPartial<typeof config, string>
|
||||
|
||||
declare module 'nuxt/schema' {
|
||||
interface AppConfigInput {
|
||||
@@ -50,8 +47,6 @@ export interface ModuleOptions {
|
||||
*/
|
||||
global?: boolean
|
||||
|
||||
icons: CollectionNames[] | 'all' | IconsPluginOptions
|
||||
|
||||
safelistColors?: string[]
|
||||
/**
|
||||
* Disables the global css styles added by the module.
|
||||
@@ -65,12 +60,11 @@ export default defineNuxtModule<ModuleOptions>({
|
||||
version,
|
||||
configKey: 'ui',
|
||||
compatibility: {
|
||||
nuxt: '^3.10.0'
|
||||
nuxt: '>=3.10.0'
|
||||
}
|
||||
},
|
||||
defaults: {
|
||||
prefix: 'U',
|
||||
icons: ['heroicons'],
|
||||
safelistColors: ['primary'],
|
||||
disableGlobalStyles: false
|
||||
},
|
||||
@@ -88,107 +82,13 @@ export default defineNuxtModule<ModuleOptions>({
|
||||
nuxt.options.css.push(resolve(runtimeDir, 'ui.css'))
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
nuxt.hook('tailwindcss:config', function (tailwindConfig) {
|
||||
tailwindConfig.theme = tailwindConfig.theme || {}
|
||||
tailwindConfig.theme.extend = tailwindConfig.theme.extend || {}
|
||||
tailwindConfig.theme.extend.colors = tailwindConfig.theme.extend.colors || {}
|
||||
|
||||
const globalColors: any = {
|
||||
...(tailwindConfig.theme.colors || defaultColors),
|
||||
...tailwindConfig.theme.extend?.colors
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
globalColors.primary = tailwindConfig.theme.extend.colors.primary = {
|
||||
50: 'rgb(var(--color-primary-50) / <alpha-value>)',
|
||||
100: 'rgb(var(--color-primary-100) / <alpha-value>)',
|
||||
200: 'rgb(var(--color-primary-200) / <alpha-value>)',
|
||||
300: 'rgb(var(--color-primary-300) / <alpha-value>)',
|
||||
400: 'rgb(var(--color-primary-400) / <alpha-value>)',
|
||||
500: 'rgb(var(--color-primary-500) / <alpha-value>)',
|
||||
600: 'rgb(var(--color-primary-600) / <alpha-value>)',
|
||||
700: 'rgb(var(--color-primary-700) / <alpha-value>)',
|
||||
800: 'rgb(var(--color-primary-800) / <alpha-value>)',
|
||||
900: 'rgb(var(--color-primary-900) / <alpha-value>)',
|
||||
950: 'rgb(var(--color-primary-950) / <alpha-value>)',
|
||||
DEFAULT: 'rgb(var(--color-primary-DEFAULT) / <alpha-value>)'
|
||||
}
|
||||
|
||||
if (globalColors.gray) {
|
||||
// @ts-ignore
|
||||
globalColors.cool = tailwindConfig.theme.extend.colors.cool = defaultColors.gray
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
globalColors.gray = tailwindConfig.theme.extend.colors.gray = {
|
||||
50: 'rgb(var(--color-gray-50) / <alpha-value>)',
|
||||
100: 'rgb(var(--color-gray-100) / <alpha-value>)',
|
||||
200: 'rgb(var(--color-gray-200) / <alpha-value>)',
|
||||
300: 'rgb(var(--color-gray-300) / <alpha-value>)',
|
||||
400: 'rgb(var(--color-gray-400) / <alpha-value>)',
|
||||
500: 'rgb(var(--color-gray-500) / <alpha-value>)',
|
||||
600: 'rgb(var(--color-gray-600) / <alpha-value>)',
|
||||
700: 'rgb(var(--color-gray-700) / <alpha-value>)',
|
||||
800: 'rgb(var(--color-gray-800) / <alpha-value>)',
|
||||
900: 'rgb(var(--color-gray-900) / <alpha-value>)',
|
||||
950: 'rgb(var(--color-gray-950) / <alpha-value>)'
|
||||
}
|
||||
|
||||
const colors = excludeColors(globalColors)
|
||||
|
||||
// @ts-ignore
|
||||
nuxt.options.appConfig.ui = {
|
||||
primary: 'green',
|
||||
gray: 'cool',
|
||||
colors,
|
||||
strategy: 'merge'
|
||||
}
|
||||
|
||||
tailwindConfig.safelist = tailwindConfig.safelist || []
|
||||
tailwindConfig.safelist.push(...generateSafelist(options.safelistColors || [], colors))
|
||||
})
|
||||
|
||||
createTemplates(nuxt)
|
||||
|
||||
// Modules
|
||||
|
||||
await installModule('nuxt-icon')
|
||||
await installModule('@nuxt/icon')
|
||||
await installModule('@nuxtjs/color-mode', { classSuffix: '' })
|
||||
await installModule('@nuxtjs/tailwindcss', {
|
||||
exposeConfig: true,
|
||||
config: {
|
||||
darkMode: 'class',
|
||||
plugins: [
|
||||
require('@tailwindcss/forms')({ strategy: 'class' }),
|
||||
require('@tailwindcss/aspect-ratio'),
|
||||
require('@tailwindcss/typography'),
|
||||
require('@tailwindcss/container-queries'),
|
||||
require('@headlessui/tailwindcss'),
|
||||
iconsPlugin(Array.isArray(options.icons) || options.icons === 'all' ? { collections: getIconCollections(options.icons) } : typeof options.icons === 'object' ? options.icons as IconsPluginOptions : {})
|
||||
],
|
||||
content: {
|
||||
files: [
|
||||
resolve(runtimeDir, 'components/**/*.{vue,mjs,ts}'),
|
||||
resolve(runtimeDir, 'ui.config/**/*.{mjs,js,ts}')
|
||||
],
|
||||
transform: {
|
||||
vue: (content) => {
|
||||
return content.replaceAll(/(?:\r\n|\r|\n)/g, ' ')
|
||||
}
|
||||
},
|
||||
extract: {
|
||||
vue: (content) => {
|
||||
return [
|
||||
...defaultExtractor(content),
|
||||
// @ts-ignore
|
||||
...customSafelistExtractor(options.prefix, content, nuxt.options.appConfig.ui.colors, options.safelistColors)
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
await installTailwind(options, nuxt, resolve)
|
||||
|
||||
// Plugins
|
||||
|
||||
|
||||
@@ -1,13 +1,28 @@
|
||||
<template>
|
||||
<div :class="ui.wrapper" v-bind="attrs">
|
||||
<table :class="[ui.base, ui.divide]">
|
||||
<slot v-if="$slots.caption || caption" name="caption">
|
||||
<caption :class="ui.caption">
|
||||
{{ caption }}
|
||||
</caption>
|
||||
</slot>
|
||||
<thead :class="ui.thead">
|
||||
<tr :class="ui.tr.base">
|
||||
<th v-if="modelValue" scope="col" :class="ui.checkbox.padding">
|
||||
<UCheckbox :model-value="indeterminate || selected.length === rows.length" :indeterminate="indeterminate" aria-label="Select all" @change="onChange" />
|
||||
<UCheckbox :model-value="indeterminate || selected.length === rows.length" :indeterminate="indeterminate" v-bind="ui.default.checkbox" aria-label="Select all" @change="onChange" />
|
||||
</th>
|
||||
|
||||
<th v-for="(column, index) in columns" :key="index" scope="col" :class="[ui.th.base, ui.th.padding, ui.th.color, ui.th.font, ui.th.size, column.class]">
|
||||
<th v-if="$slots.expand" scope="col" :class="ui.tr.base">
|
||||
<span class="sr-only">Expand</span>
|
||||
</th>
|
||||
|
||||
<th
|
||||
v-for="(column, index) in columns"
|
||||
:key="index"
|
||||
scope="col"
|
||||
:class="[ui.th.base, ui.th.padding, ui.th.color, ui.th.font, ui.th.size, column.class]"
|
||||
:aria-sort="getAriaSort(column)"
|
||||
>
|
||||
<slot :name="`${column.key}-header`" :column="column" :sort="sort" :on-sort="onSort">
|
||||
<UButton
|
||||
v-if="column.sortable"
|
||||
@@ -55,17 +70,39 @@
|
||||
</tr>
|
||||
|
||||
<template v-else>
|
||||
<tr v-for="(row, index) in rows" :key="index" :class="[ui.tr.base, isSelected(row) && ui.tr.selected, $attrs.onSelect && ui.tr.active, row?.class]" @click="() => onSelect(row)">
|
||||
<td v-if="modelValue" :class="ui.checkbox.padding">
|
||||
<UCheckbox v-model="selected" :value="row" aria-label="Select row" @click.stop />
|
||||
</td>
|
||||
<template v-for="(row, index) in rows" :key="index">
|
||||
<tr :class="[ui.tr.base, isSelected(row) && ui.tr.selected, $attrs.onSelect && ui.tr.active, row?.class]" @click="() => onSelect(row)">
|
||||
<td v-if="modelValue" :class="ui.checkbox.padding">
|
||||
<UCheckbox v-model="selected" :value="row" v-bind="ui.default.checkbox" aria-label="Select row" @click.stop />
|
||||
</td>
|
||||
|
||||
<td v-for="(column, subIndex) in columns" :key="subIndex" :class="[ui.td.base, ui.td.padding, ui.td.color, ui.td.font, ui.td.size, row[column.key]?.class]">
|
||||
<slot :name="`${column.key}-data`" :column="column" :row="row" :index="index" :get-row-data="(defaultValue) => getRowData(row, column.key, defaultValue)">
|
||||
{{ getRowData(row, column.key) }}
|
||||
</slot>
|
||||
</td>
|
||||
</tr>
|
||||
<td
|
||||
v-if="$slots.expand"
|
||||
:class="[ui.td.base, ui.td.padding, ui.td.color, ui.td.font, ui.td.size]"
|
||||
>
|
||||
<UButton
|
||||
v-bind="{ ...(ui.default.expandButton || {}), ...expandButton }"
|
||||
:ui="{ icon: { base: [ui.expand.icon, openedRows.includes(index) && 'rotate-180'] } }"
|
||||
@click="toggleOpened(index)"
|
||||
/>
|
||||
</td>
|
||||
|
||||
<td v-for="(column, subIndex) in columns" :key="subIndex" :class="[ui.td.base, ui.td.padding, ui.td.color, ui.td.font, ui.td.size, column?.rowClass, row[column.key]?.class]">
|
||||
<slot :name="`${column.key}-data`" :column="column" :row="row" :index="index" :get-row-data="(defaultValue) => getRowData(row, column.key, defaultValue)">
|
||||
{{ getRowData(row, column.key) }}
|
||||
</slot>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="openedRows.includes(index)">
|
||||
<td colspan="100%">
|
||||
<slot
|
||||
name="expand"
|
||||
:row="row"
|
||||
:index="index"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -73,8 +110,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, toRaw, toRef } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { ref, computed, defineComponent, toRaw, toRef } from 'vue'
|
||||
import type { PropType, AriaAttributes } from 'vue'
|
||||
import { upperFirst } from 'scule'
|
||||
import { defu } from 'defu'
|
||||
import { useVModel } from '@vueuse/core'
|
||||
@@ -84,7 +121,7 @@ import UProgress from '../elements/Progress.vue'
|
||||
import UCheckbox from '../forms/Checkbox.vue'
|
||||
import { useUI } from '../../composables/useUI'
|
||||
import { mergeConfig, get } from '../../utils'
|
||||
import type { Strategy, Button, ProgressColor, ProgressAnimation } from '../../types'
|
||||
import type { Strategy, Button, ProgressColor, ProgressAnimation } from '../../types/index'
|
||||
// @ts-expect-error
|
||||
import appConfig from '#build/app.config'
|
||||
import { table } from '#ui/ui.config'
|
||||
@@ -107,6 +144,16 @@ function defaultSort (a: any, b: any, direction: 'asc' | 'desc') {
|
||||
}
|
||||
}
|
||||
|
||||
interface Column {
|
||||
key: string
|
||||
sortable?: boolean
|
||||
sort?: (a: any, b: any, direction: 'asc' | 'desc') => number
|
||||
direction?: 'asc' | 'desc'
|
||||
class?: string
|
||||
rowClass?: string
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
UIcon,
|
||||
@@ -129,7 +176,7 @@ export default defineComponent({
|
||||
default: () => []
|
||||
},
|
||||
columns: {
|
||||
type: Array as PropType<{ key: string, sortable?: boolean, sort?: (a: any, b: any, direction: 'asc' | 'desc') => number, direction?: 'asc' | 'desc', class?: string, [key: string]: any }[]>,
|
||||
type: Array as PropType<Column[]>,
|
||||
default: null
|
||||
},
|
||||
columnAttribute: {
|
||||
@@ -156,6 +203,10 @@ export default defineComponent({
|
||||
type: String,
|
||||
default: () => config.default.sortDescIcon
|
||||
},
|
||||
expandButton: {
|
||||
type: Object as PropType<Button>,
|
||||
default: () => config.default.expandButton as Button
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
@@ -168,6 +219,10 @@ export default defineComponent({
|
||||
type: Object as PropType<{ icon: string, label: string }>,
|
||||
default: () => config.default.emptyState
|
||||
},
|
||||
caption: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
progress: {
|
||||
type: Object as PropType<{ color: ProgressColor, animation: ProgressAnimation }>,
|
||||
default: () => config.default.progress
|
||||
@@ -185,10 +240,12 @@ export default defineComponent({
|
||||
setup (props, { emit, attrs: $attrs }) {
|
||||
const { ui, attrs } = useUI('table', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||
|
||||
const columns = computed(() => props.columns ?? Object.keys(props.rows[0] ?? {}).map((key) => ({ key, label: upperFirst(key), sortable: false, class: undefined, sort: defaultSort })))
|
||||
const columns = computed(() => props.columns ?? Object.keys(props.rows[0] ?? {}).map((key) => ({ key, label: upperFirst(key), sortable: false, class: undefined, sort: defaultSort }) as Column))
|
||||
|
||||
const sort = useVModel(props, 'sort', emit, { passive: true, defaultValue: defu({}, props.sort, { column: null, direction: 'asc' }) })
|
||||
|
||||
const openedRows = ref([])
|
||||
|
||||
const savedSort = { column: sort.value.column, direction: null }
|
||||
|
||||
const rows = computed(() => {
|
||||
@@ -280,8 +337,8 @@ export default defineComponent({
|
||||
})
|
||||
}
|
||||
|
||||
function onChange (event: any) {
|
||||
if (event.target.checked) {
|
||||
function onChange (checked: boolean) {
|
||||
if (checked) {
|
||||
selectAllRows()
|
||||
} else {
|
||||
selected.value = []
|
||||
@@ -292,6 +349,34 @@ export default defineComponent({
|
||||
return get(row, rowKey, defaultValue)
|
||||
}
|
||||
|
||||
function toggleOpened (index: number) {
|
||||
if (openedRows.value.includes(index)) {
|
||||
openedRows.value = openedRows.value.filter((i) => i !== index)
|
||||
} else {
|
||||
openedRows.value.push(index)
|
||||
}
|
||||
}
|
||||
|
||||
function getAriaSort (column: Column): AriaAttributes['aria-sort'] {
|
||||
if (!column.sortable) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (sort.value.column !== column.key) {
|
||||
return 'none'
|
||||
}
|
||||
|
||||
if (sort.value.direction === 'asc') {
|
||||
return 'ascending'
|
||||
}
|
||||
|
||||
if (sort.value.direction === 'desc') {
|
||||
return 'descending'
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
return {
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
ui,
|
||||
@@ -308,11 +393,14 @@ export default defineComponent({
|
||||
emptyState,
|
||||
// eslint-disable-next-line vue/no-dupe-keys
|
||||
loadingState,
|
||||
openedRows,
|
||||
isSelected,
|
||||
onSort,
|
||||
onSelect,
|
||||
onChange,
|
||||
getRowData
|
||||
getRowData,
|
||||
toggleOpened,
|
||||
getAriaSort
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -39,13 +39,27 @@
|
||||
@before-leave="onBeforeLeave"
|
||||
@leave="onLeave"
|
||||
>
|
||||
<div v-show="open">
|
||||
<HDisclosurePanel :class="[ui.item.base, ui.item.size, ui.item.color, ui.item.padding]" static>
|
||||
<slot :name="item.slot || 'item'" :item="item" :index="index" :open="open" :close="close">
|
||||
{{ item.content }}
|
||||
</slot>
|
||||
</HDisclosurePanel>
|
||||
</div>
|
||||
<HDisclosurePanel
|
||||
v-if="unmount"
|
||||
:class="[ui.item.base, ui.item.size, ui.item.color, ui.item.padding]"
|
||||
unmount
|
||||
>
|
||||
<slot :name="item.slot || 'item'" :item="item" :index="index" :open="open" :close="close">
|
||||
{{ item.content }}
|
||||
</slot>
|
||||
</HDisclosurePanel>
|
||||
<template v-else>
|
||||
<div v-show="open">
|
||||
<HDisclosurePanel
|
||||
:class="[ui.item.base, ui.item.size, ui.item.color, ui.item.padding]"
|
||||
static
|
||||
>
|
||||
<slot :name="item.slot || 'item'" :item="item" :index="index" :open="open" :close="close">
|
||||
{{ item.content }}
|
||||
</slot>
|
||||
</HDisclosurePanel>
|
||||
</div>
|
||||
</template>
|
||||
</Transition>
|
||||
</HDisclosure>
|
||||
</div>
|
||||
@@ -59,7 +73,7 @@ import UIcon from '../elements/Icon.vue'
|
||||
import UButton from '../elements/Button.vue'
|
||||
import { useUI } from '../../composables/useUI'
|
||||
import { mergeConfig, omit } from '../../utils'
|
||||
import type { AccordionItem, Strategy } from '../../types'
|
||||
import type { AccordionItem, Strategy } from '../../types/index'
|
||||
// @ts-expect-error
|
||||
import appConfig from '#build/app.config'
|
||||
import { accordion, button } from '#ui/ui.config'
|
||||
@@ -91,6 +105,10 @@ export default defineComponent({
|
||||
type: String,
|
||||
default: () => config.default.openIcon
|
||||
},
|
||||
unmount: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
closeIcon: {
|
||||
type: String,
|
||||
default: () => config.default.closeIcon
|
||||
@@ -115,7 +133,7 @@ export default defineComponent({
|
||||
const uiButton = computed<typeof configButton>(() => configButton)
|
||||
|
||||
const buttonRefs = ref<{ open: boolean, close: (e: EventTarget) => {} }[]>([])
|
||||
|
||||
|
||||
const openedStates = computed(() => buttonRefs.value.map(({ open }) => open))
|
||||
watch(openedStates, (newValue, oldValue) => {
|
||||
for (const index in newValue) {
|
||||
@@ -176,7 +194,7 @@ export default defineComponent({
|
||||
attrs,
|
||||
buttonRefs,
|
||||
closeOthers,
|
||||
omit,
|
||||
omit: (omit as any),
|
||||
onEnter,
|
||||
onBeforeLeave,
|
||||
onAfterEnter,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div :class="alertClass" v-bind="attrs">
|
||||
<div class="flex" :class="[ui.gap, { 'items-start': (description || $slots.description), 'items-center': !description && !$slots.description }]">
|
||||
<slot name="icon" :icon="icon">
|
||||
<UIcon v-if="icon" :name="icon" :ui="ui.icon.base" />
|
||||
<UIcon v-if="icon" :name="icon" :class="ui.icon.base" />
|
||||
</slot>
|
||||
<slot name="avatar" :avatar="avatar">
|
||||
<UAvatar v-if="avatar" v-bind="{ size: ui.avatar.size, ...avatar }" :class="ui.avatar.base" />
|
||||
@@ -14,19 +14,23 @@
|
||||
{{ title }}
|
||||
</slot>
|
||||
</p>
|
||||
<p v-if="description || $slots.description" :class="twMerge(ui.description, !(title && $slots.title) && 'mt-0 leading-5')">
|
||||
<div v-if="description || $slots.description" :class="twMerge(ui.description, !title && !$slots.title && 'mt-0 leading-5')">
|
||||
<slot name="description" :description="description">
|
||||
{{ description }}
|
||||
</slot>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-if="(description || $slots.description) && actions.length" :class="ui.actions">
|
||||
<UButton v-for="(action, index) of actions" :key="index" v-bind="{ ...(ui.default.actionButton || {}), ...action }" @click.stop="onAction(action)" />
|
||||
<div v-if="(description || $slots.description) && (actions.length || $slots.actions)" :class="ui.actions">
|
||||
<slot name="actions">
|
||||
<UButton v-for="(action, index) of actions" :key="index" v-bind="{ ...(ui.default.actionButton || {}), ...action }" @click.stop="onAction(action)" />
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="closeButton || (!description && !$slots.description && actions.length)" :class="twMerge(ui.actions, 'mt-0')">
|
||||
<template v-if="!description && !$slots.description && actions.length">
|
||||
<UButton v-for="(action, index) of actions" :key="index" v-bind="{ ...(ui.default.actionButton || {}), ...action }" @click.stop="onAction(action)" />
|
||||
<template v-if="!description && !$slots.description && (actions.length || $slots.actions)">
|
||||
<slot name="actions">
|
||||
<UButton v-for="(action, index) of actions" :key="index" v-bind="{ ...(ui.default.actionButton || {}), ...action }" @click.stop="onAction(action)" />
|
||||
</slot>
|
||||
</template>
|
||||
|
||||
<UButton v-if="closeButton" aria-label="Close" v-bind="{ ...(ui.default.closeButton || {}), ...closeButton }" @click.stop="$emit('close')" />
|
||||
@@ -43,7 +47,7 @@ import UIcon from '../elements/Icon.vue'
|
||||
import UAvatar from '../elements/Avatar.vue'
|
||||
import UButton from '../elements/Button.vue'
|
||||
import { useUI } from '../../composables/useUI'
|
||||
import type { Avatar, Button, AlertColor, AlertVariant, AlertAction, Strategy } from '../../types'
|
||||
import type { Avatar, Button, AlertColor, AlertVariant, AlertAction, Strategy } from '../../types/index'
|
||||
import { mergeConfig } from '../../utils'
|
||||
// @ts-expect-error
|
||||
import appConfig from '#build/app.config'
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<template>
|
||||
<span :class="wrapperClass">
|
||||
<img
|
||||
<component
|
||||
:is="as"
|
||||
v-if="url && !error"
|
||||
:class="imgClass"
|
||||
:alt="alt"
|
||||
:src="url"
|
||||
v-bind="attrs"
|
||||
@error="onError"
|
||||
>
|
||||
/>
|
||||
<span v-else-if="text" :class="ui.text">{{ text }}</span>
|
||||
<UIcon v-else-if="icon" :name="icon" :class="iconClass" />
|
||||
<span v-else-if="placeholder" :class="ui.placeholder">{{ placeholder }}</span>
|
||||
@@ -26,7 +27,7 @@ import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import UIcon from '../elements/Icon.vue'
|
||||
import { useUI } from '../../composables/useUI'
|
||||
import { mergeConfig } from '../../utils'
|
||||
import type { AvatarSize, AvatarChipColor, AvatarChipPosition, Strategy } from '../../types'
|
||||
import type { AvatarSize, AvatarChipColor, AvatarChipPosition, Strategy } from '../../types/index'
|
||||
// @ts-expect-error
|
||||
import appConfig from '#build/app.config'
|
||||
import { avatar } from '#ui/ui.config'
|
||||
@@ -39,6 +40,10 @@ export default defineComponent({
|
||||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
as: {
|
||||
type: [String, Object],
|
||||
default: 'img'
|
||||
},
|
||||
src: {
|
||||
type: [String, Boolean],
|
||||
default: null
|
||||
|
||||
@@ -4,7 +4,7 @@ import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import UAvatar from './Avatar.vue'
|
||||
import { useUI } from '../../composables/useUI'
|
||||
import { mergeConfig, getSlotsChildren } from '../../utils'
|
||||
import type { AvatarSize, Strategy } from '../../types'
|
||||
import type { AvatarSize, Strategy } from '../../types/index'
|
||||
// @ts-expect-error
|
||||
import appConfig from '#build/app.config'
|
||||
import { avatar, avatarGroup } from '#ui/ui.config'
|
||||
|
||||
@@ -11,7 +11,7 @@ import { twMerge, twJoin } from 'tailwind-merge'
|
||||
import { useUI } from '../../composables/useUI'
|
||||
import { mergeConfig } from '../../utils'
|
||||
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
||||
import type { BadgeColor, BadgeSize, BadgeVariant, Strategy } from '../../types'
|
||||
import type { BadgeColor, BadgeSize, BadgeVariant, Strategy } from '../../types/index'
|
||||
// @ts-expect-error
|
||||
import appConfig from '#build/app.config'
|
||||
import { badge } from '#ui/ui.config'
|
||||
|
||||
@@ -25,7 +25,7 @@ import ULink from '../elements/Link.vue'
|
||||
import { useUI } from '../../composables/useUI'
|
||||
import { mergeConfig, nuxtLinkProps, getNuxtLinkProps } from '../../utils'
|
||||
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
||||
import type { ButtonColor, ButtonSize, ButtonVariant, Strategy } from '../../types'
|
||||
import type { ButtonColor, ButtonSize, ButtonVariant, Strategy } from '../../types/index'
|
||||
// @ts-expect-error
|
||||
import appConfig from '#build/app.config'
|
||||
import { button } from '#ui/ui.config'
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user