mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-20 06:51:46 +01:00
Compare commits
111 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64897a39bf | ||
|
|
dfda33c1aa | ||
|
|
d46eafb248 | ||
|
|
ee6f0d0c49 | ||
|
|
b7b86bcc44 | ||
|
|
bbf3424933 | ||
|
|
2fc938575d | ||
|
|
ff9d51863e | ||
|
|
adb0a0fbe4 | ||
|
|
a071e4b875 | ||
|
|
a74de152d7 | ||
|
|
109ec52d50 | ||
|
|
874447cb41 | ||
|
|
8b7a013319 | ||
|
|
3e647e4af1 | ||
|
|
0da85e1463 | ||
|
|
dcf6e63471 | ||
|
|
cbb2f28c3f | ||
|
|
be734fc026 | ||
|
|
b306138574 | ||
|
|
1ebf456ffc | ||
|
|
8257a11dcb | ||
|
|
6887f732ee | ||
|
|
d088d8a7b8 | ||
|
|
f60543a234 | ||
|
|
2531c8e66d | ||
|
|
4b68760f6a | ||
|
|
568772382f | ||
|
|
46879dc1b7 | ||
|
|
a94782d94b | ||
|
|
853d58ad5f | ||
|
|
38b1eb6c5f | ||
|
|
f24ff9c47f | ||
|
|
60210aad75 | ||
|
|
67e85f98e2 | ||
|
|
b3a52482f2 | ||
|
|
86dc49ecc9 | ||
|
|
c937736734 | ||
|
|
d379c579c0 | ||
|
|
f983c974c4 | ||
|
|
b90b151588 | ||
|
|
34d2f57801 | ||
|
|
2c98628f98 | ||
|
|
681f0e5684 | ||
|
|
e40491208a | ||
|
|
00594ea59b | ||
|
|
3ba95d3c4d | ||
|
|
3424ce118d | ||
|
|
40b1d30f5c | ||
|
|
8ec23c042d | ||
|
|
81463cd21d | ||
|
|
c44d363f62 | ||
|
|
fbfa14a6a3 | ||
|
|
4127caac76 | ||
|
|
d6476d17f9 | ||
|
|
5fc44b97c6 | ||
|
|
15e418e6c6 | ||
|
|
3b8ca9886d | ||
|
|
4c5833083f | ||
|
|
83d609d530 | ||
|
|
1b34df15ac | ||
|
|
0178ca9586 | ||
|
|
40ecb23d9a | ||
|
|
85734b8615 | ||
|
|
ab26e4ba7d | ||
|
|
edbbb33f69 | ||
|
|
3de3aa006c | ||
|
|
784f1f51dd | ||
|
|
0787ec2d12 | ||
|
|
a8f643939e | ||
|
|
6f77ee80ce | ||
|
|
e2d4ba529d | ||
|
|
1c707ca00d | ||
|
|
00e951f708 | ||
|
|
0544a01c5b | ||
|
|
290ab1d9c5 | ||
|
|
254c4ed7d3 | ||
|
|
a603ea56c1 | ||
|
|
a90e95f7d1 | ||
|
|
bc2315b7d9 | ||
|
|
87fd85ec3f | ||
|
|
3fef86834f | ||
|
|
b5e8685a2c | ||
|
|
15ee768729 | ||
|
|
8955595dc6 | ||
|
|
fd6bcd3f84 | ||
|
|
9d23b82d1d | ||
|
|
511ed6a86c | ||
|
|
e8daf7f810 | ||
|
|
a43c68c501 | ||
|
|
ef7d3ce549 | ||
|
|
c2e561cfe4 | ||
|
|
1d1c36b44c | ||
|
|
95abc759b9 | ||
|
|
700b2bb4d7 | ||
|
|
1d077c45d5 | ||
|
|
14cca48e96 | ||
|
|
22430e168a | ||
|
|
c1e0654417 | ||
|
|
1a7eb27cad | ||
|
|
0d5f008168 | ||
|
|
ba2716a66a | ||
|
|
5d66155885 | ||
|
|
02f3164af3 | ||
|
|
240db8ee19 | ||
|
|
b905216a95 | ||
|
|
c47d928f49 | ||
|
|
7e0a655c64 | ||
|
|
02bbc9b9cf | ||
|
|
98e1d1b90e | ||
|
|
2b1e7bcc57 |
31
.github/ISSUE_TEMPLATE/bug-report.md
vendored
31
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@@ -1,31 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Report a bug report to help us improve the module.
|
|
||||||
title: ''
|
|
||||||
labels: 'bug'
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!-- **IMPORTANT!**
|
|
||||||
Before reporting a bug, please make sure that you have read through our documentation and you think your problem is indeed an issue related to our module. -->
|
|
||||||
|
|
||||||
### Version
|
|
||||||
@nuxt/ui: <!-- ex: v2.0.0 -->
|
|
||||||
nuxt: <!-- ex: v3.5.0 -->
|
|
||||||
|
|
||||||
### Reproduction Link
|
|
||||||
|
|
||||||
<!--
|
|
||||||
A minimal test case based on one of:
|
|
||||||
- a GitHub repository that can reproduce the bug
|
|
||||||
- https://stackblitz.com/edit/nuxt-ui
|
|
||||||
-->
|
|
||||||
|
|
||||||
### Steps to reproduce
|
|
||||||
|
|
||||||
|
|
||||||
### What is Expected?
|
|
||||||
|
|
||||||
|
|
||||||
### What is actually happening?
|
|
||||||
60
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
60
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
name: "🐛 Bug report"
|
||||||
|
description: Report a bug to help us improve the module.
|
||||||
|
labels: ["bug"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Before reporting a bug, please make sure that you have read through our [documentation](https://ui.nuxt.com) and existing [issues](https://github.com/nuxt/ui/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc).
|
||||||
|
- type: textarea
|
||||||
|
id: env
|
||||||
|
attributes:
|
||||||
|
label: Environment
|
||||||
|
description: You can use `npx nuxi info` to fill this section
|
||||||
|
placeholder: |
|
||||||
|
- Operating System: `Darwin`
|
||||||
|
- Node Version: `v18.16.0`
|
||||||
|
- Nuxt Version: `3.7.3`
|
||||||
|
- CLI Version: `3.8.4`
|
||||||
|
- Nitro Version: `2.6.3`
|
||||||
|
- Package Manager: `pnpm@8.7.4`
|
||||||
|
- Builder: `-`
|
||||||
|
- User Config: `-`
|
||||||
|
- Runtime Modules: `-`
|
||||||
|
- Build Modules: `-`
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: Version
|
||||||
|
placeholder: v2.8.0
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: reproduction
|
||||||
|
attributes:
|
||||||
|
label: Reproduction
|
||||||
|
description: Please provide a reproduction link using this template https://stackblitz.com/edit/nuxt-ui. A minimal [reproduction is required](https://antfu.me/posts/why-reproductions-are-required) unless you are absolutely sure that the issue is obvious and the provided information is enough to understand the problem. If a report is vague (e.g. just a generic error message) and has no reproduction, it will receive a "needs reproduction" label. If no reproduction is provided we might close it.
|
||||||
|
placeholder: https://stackblitz.com/edit/nuxt-ui
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Description
|
||||||
|
description: A clear and concise description of what the bug is. If you intend to submit a PR for this issue, tell us in the description.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: additonal
|
||||||
|
attributes:
|
||||||
|
label: Additional context
|
||||||
|
description: If applicable, add any other context or screenshots here.
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: Logs
|
||||||
|
description: |
|
||||||
|
Optional if provided reproduction. Please try not to insert an image but copy paste the log text.
|
||||||
|
render: shell-script
|
||||||
9
.github/ISSUE_TEMPLATE/config.yml
vendored
9
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,8 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Nuxt Community Discord
|
- name: 📖 Documentation
|
||||||
url: https://discord.nuxtjs.org/
|
url: https://ui.nuxt.com
|
||||||
about: Consider asking questions about the module here.
|
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.
|
||||||
|
|||||||
20
.github/ISSUE_TEMPLATE/feature-request.md
vendored
20
.github/ISSUE_TEMPLATE/feature-request.md
vendored
@@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest an idea or enhancement for the module.
|
|
||||||
title: ''
|
|
||||||
labels: 'enhancement'
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Is your feature request related to a problem? Please describe.
|
|
||||||
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
|
||||||
|
|
||||||
### Describe the solution you'd like
|
|
||||||
<!-- A clear and concise description of what you want to happen. -->
|
|
||||||
|
|
||||||
### Describe alternatives you've considered
|
|
||||||
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
|
||||||
|
|
||||||
### Additional context
|
|
||||||
<!-- Add any other context or screenshots about the feature request here. -->
|
|
||||||
20
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
name: "🚀 Feature request"
|
||||||
|
description: Suggest an idea or enhancement for the module.
|
||||||
|
labels: ["enhancement"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Before requesting a feature, please make sure that you have read through our [documentation](https://ui.nuxt.com) and existing [issues](https://github.com/nuxt/ui/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc).
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Description
|
||||||
|
description: A clear and concise description of what you think would be an helpful addition to the module, including the possible use cases and alternatives you have considered. If you have a working prototype or module that implements it, please include a link.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: additonal
|
||||||
|
attributes:
|
||||||
|
label: Additional context
|
||||||
|
description: If applicable, add any other context or screenshots here.
|
||||||
16
.github/ISSUE_TEMPLATE/question.md
vendored
16
.github/ISSUE_TEMPLATE/question.md
vendored
@@ -1,16 +0,0 @@
|
|||||||
---
|
|
||||||
name: Question
|
|
||||||
about: Ask a question about the module.
|
|
||||||
title: ''
|
|
||||||
labels: 'question'
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!-- **IMPORTANT!**
|
|
||||||
Please make sure to look for an answer to your question in our documentation and the documentation before asking a question here.
|
|
||||||
|
|
||||||
If you have a general question regarding the module use Discord `modules` channel. Thanks!
|
|
||||||
|
|
||||||
Nuxt Discord: https://discord.nuxtjs.org/
|
|
||||||
-->
|
|
||||||
14
.github/ISSUE_TEMPLATE/question.yml
vendored
Normal file
14
.github/ISSUE_TEMPLATE/question.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
name: "💬 Question"
|
||||||
|
description: Ask a question about the module.
|
||||||
|
labels: ["question"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Before asking a question, please make sure that you have read through our [documentation](https://ui.nuxt.com) and existing [issues](https://github.com/nuxt/ui/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc).
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Description
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
33
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
33
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<!---
|
||||||
|
☝️ PR title should follow conventional commits (https://conventionalcommits.org)
|
||||||
|
-->
|
||||||
|
|
||||||
|
### 🔗 Linked issue
|
||||||
|
|
||||||
|
<!-- Please ensure there is an open issue and mention its number as #123 -->
|
||||||
|
|
||||||
|
### ❓ Type of change
|
||||||
|
|
||||||
|
<!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply. -->
|
||||||
|
|
||||||
|
- [ ] 📖 Documentation (updates to the documentation or readme)
|
||||||
|
- [ ] 🐞 Bug fix (a non-breaking change that fixes an issue)
|
||||||
|
- [ ] 👌 Enhancement (improving an existing functionality)
|
||||||
|
- [ ] ✨ New feature (a non-breaking change that adds functionality)
|
||||||
|
- [ ] 🧹 Chore (updates to the build process or auxiliary tools and libraries)
|
||||||
|
- [ ] ⚠️ Breaking change (fix or feature that would cause existing functionality to change)
|
||||||
|
|
||||||
|
### 📚 Description
|
||||||
|
|
||||||
|
<!-- Describe your changes in detail -->
|
||||||
|
<!-- Why is this change required? What problem does it solve? -->
|
||||||
|
<!-- If it resolves an open issue, please link to the issue here. For example "Resolves #1337" -->
|
||||||
|
|
||||||
|
### 📝 Checklist
|
||||||
|
|
||||||
|
<!-- Put an `x` in all the boxes that apply. -->
|
||||||
|
<!-- If your change requires a documentation PR, please link it appropriately -->
|
||||||
|
<!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
||||||
|
|
||||||
|
- [ ] I have linked an issue or discussion.
|
||||||
|
- [ ] I have updated the documentation accordingly.
|
||||||
9
.github/workflows/ci-dev.yml
vendored
9
.github/workflows/ci-dev.yml
vendored
@@ -49,15 +49,18 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
|
- name: Prepare
|
||||||
|
run: pnpm run dev:prepare
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: pnpm run lint
|
run: pnpm run lint
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: pnpm run build
|
|
||||||
|
|
||||||
- name: Typecheck
|
- name: Typecheck
|
||||||
run: pnpm run typecheck
|
run: pnpm run typecheck
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: pnpm run build
|
||||||
|
|
||||||
- name: Release Edge
|
- name: Release Edge
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
run: ./scripts/release-edge.sh
|
run: ./scripts/release-edge.sh
|
||||||
|
|||||||
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
@@ -49,15 +49,18 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
|
- name: Prepare
|
||||||
|
run: pnpm run dev:prepare
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: pnpm run lint
|
run: pnpm run lint
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: pnpm run build
|
|
||||||
|
|
||||||
- name: Typecheck
|
- name: Typecheck
|
||||||
run: pnpm run typecheck
|
run: pnpm run typecheck
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: pnpm run build
|
||||||
|
|
||||||
- name: Version Check
|
- name: Version Check
|
||||||
id: check
|
id: check
|
||||||
uses: EndBug/version-check@v2
|
uses: EndBug/version-check@v2
|
||||||
|
|||||||
3
.nuxtrc
Normal file
3
.nuxtrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
imports.autoImport=false
|
||||||
|
typescript.includeWorkspace=true
|
||||||
|
typescript.strict=false
|
||||||
42
CHANGELOG.md
42
CHANGELOG.md
@@ -1,5 +1,47 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [2.9.0](https://github.com/nuxt/ui/compare/v2.8.1...v2.9.0) (2023-10-02)
|
||||||
|
|
||||||
|
|
||||||
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
|
* **module:** use `tailwind-merge` for `app.config` & move config to components & type props (#692)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **FormGroup:** add slots ([#714](https://github.com/nuxt/ui/issues/714)) ([2fc9385](https://github.com/nuxt/ui/commit/2fc938575d2e409ba9df9fb2ddb8d51d021a1756))
|
||||||
|
* **Link:** add `active` prop to override default behaviour ([#732](https://github.com/nuxt/ui/issues/732)) ([8257a11](https://github.com/nuxt/ui/commit/8257a11dcba9c34053f8061ed1383894d06b2a6c))
|
||||||
|
* **Link:** add `as` prop ([#535](https://github.com/nuxt/ui/issues/535)) ([e404912](https://github.com/nuxt/ui/commit/e40491208ac1096e505803072df0d9e2e771008e))
|
||||||
|
* **module:** use `tailwind-merge` for `app.config` & move config to components & type props ([#692](https://github.com/nuxt/ui/issues/692)) ([34d2f57](https://github.com/nuxt/ui/commit/34d2f57801d08d26262fdff4398ec3d3329b4bb0))
|
||||||
|
* remove `lodash-es` ([#648](https://github.com/nuxt/ui/issues/648)) ([d6476d1](https://github.com/nuxt/ui/commit/d6476d17f9b17317a7160271dacdb854f30237ae))
|
||||||
|
* **Table:** add ability to custom style for `td` and `tr` ([#741](https://github.com/nuxt/ui/issues/741)) ([874447c](https://github.com/nuxt/ui/commit/874447cb41a77868513459eee5d3301fe8b8e9a1))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Accordion:** close other items in circular order ([#735](https://github.com/nuxt/ui/issues/735)) ([6887f73](https://github.com/nuxt/ui/commit/6887f732ee8e14625459a0576460523845cb0a6d))
|
||||||
|
* **FormGroup:** prevent input click from propagating to label ([#651](https://github.com/nuxt/ui/issues/651)) ([4c58330](https://github.com/nuxt/ui/commit/4c5833083f0840add52f3c67efc42b8db5687d37))
|
||||||
|
* **FormGroup:** use explicit label instead of implicit label ([#638](https://github.com/nuxt/ui/issues/638)) ([681f0e5](https://github.com/nuxt/ui/commit/681f0e5684feaad0c711130404751f2fd65ddbe4))
|
||||||
|
* **module:** move `@headlessui/tailwindcss` to plugins on module install ([3e647e4](https://github.com/nuxt/ui/commit/3e647e4af154dad7fa186f062ce984e4d8d0e202))
|
||||||
|
* **module:** retain props reactivity through `useUI` ([#745](https://github.com/nuxt/ui/issues/745)) ([109ec52](https://github.com/nuxt/ui/commit/109ec52d50b0b32b0f0b24ece5b92cd7bbce29da))
|
||||||
|
* **Pagination:** handle `max > 5` and `max` equal total pages ([#728](https://github.com/nuxt/ui/issues/728)) ([a071e4b](https://github.com/nuxt/ui/commit/a071e4b8755f5dbbdfd05985c8fcb65c3cdab3ec))
|
||||||
|
* **Range:** fix track pseudo-elements for mozilla ([#636](https://github.com/nuxt/ui/issues/636)) ([8955595](https://github.com/nuxt/ui/commit/8955595dc6904d0090ad7f82ed8b376a15e51f94))
|
||||||
|
* **SelectMenu:** handle numbers ([0544a01](https://github.com/nuxt/ui/commit/0544a01c5b7ae534a595e6c91d2884a601ae3185)), closes [#574](https://github.com/nuxt/ui/issues/574)
|
||||||
|
* **Table:** add missing classes in `app.config.ts` ([a603ea5](https://github.com/nuxt/ui/commit/a603ea56c165e9ad01482d092460da3991f3e41d)), closes [#655](https://github.com/nuxt/ui/issues/655)
|
||||||
|
* **Table:** select all rows without select listener ([#652](https://github.com/nuxt/ui/issues/652)) ([83d609d](https://github.com/nuxt/ui/commit/83d609d53067b2639a55a0e367a5e7adbd8a22fc))
|
||||||
|
* **Tabs:** add visible focus indicator on active tabs ([#690](https://github.com/nuxt/ui/issues/690)) ([be734fc](https://github.com/nuxt/ui/commit/be734fc026b75bc8c921e9401ba6e97f65356cec))
|
||||||
|
* **Tabs:** allow custom keys in `TabItem` ([#671](https://github.com/nuxt/ui/issues/671)) ([15e418e](https://github.com/nuxt/ui/commit/15e418e6c6f981afd2c0e8f27dedb303b8cbad70))
|
||||||
|
* **Tabs:** prevent focus of `TabPanel` with `tabindex="-1"` ([cbb2f28](https://github.com/nuxt/ui/commit/cbb2f28c3fd96e45c7af20675b5b67576ddc0d63))
|
||||||
|
|
||||||
|
## [2.8.1](https://github.com/nuxt/ui/compare/v2.8.0...v2.8.1) (2023-09-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Form:** fix `getValibotError` to avoid importing `safeParseAsync` ([#640](https://github.com/nuxt/ui/issues/640)) ([e8daf7f](https://github.com/nuxt/ui/commit/e8daf7f81018c01c28c2c38aed6ee57ef887f823))
|
||||||
|
* **Form:** fix valibot imports ([#617](https://github.com/nuxt/ui/issues/617)) ([1a7eb27](https://github.com/nuxt/ui/commit/1a7eb27cad9f3357c4dcde188530cdb0001d3ae6))
|
||||||
|
* **Pagination:** page numbers not clickable ([#624](https://github.com/nuxt/ui/issues/624)) ([c1e0654](https://github.com/nuxt/ui/commit/c1e0654417ad39df8be3f2172ab4e0af6dacb631))
|
||||||
|
|
||||||
## [2.8.0](https://github.com/nuxt/ui/compare/v2.7.0...v2.8.0) (2023-09-07)
|
## [2.8.0](https://github.com/nuxt/ui/compare/v2.7.0...v2.8.0) (2023-09-07)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ Is has been developed by [NuxtLabs](https://nuxtlabs.com/) for [Volta](https://v
|
|||||||
- Keyboard shortcuts
|
- Keyboard shortcuts
|
||||||
- Bundled icons
|
- Bundled icons
|
||||||
- Fully typed
|
- Fully typed
|
||||||
|
- [Figma Kit](https://www.figma.com/community/file/1288455405058138934)
|
||||||
|
|
||||||
Read more on [ui.nuxt.com](https://ui.nuxt.com)
|
Read more on [ui.nuxt.com](https://ui.nuxt.com)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
# To use Nuxt Elements in production
|
# To link Nuxt UI Pro in development
|
||||||
NUXT_ELEMENTS_TOKEN=
|
NUXT_UI_PRO_PATH=
|
||||||
|
# To use Nuxt UI Pro in production
|
||||||
|
NUXT_UI_PRO_TOKEN=
|
||||||
# Used when pre-rendering the docs for dynamic OG images
|
# Used when pre-rendering the docs for dynamic OG images
|
||||||
NUXT_PUBLIC_SITE_URL=
|
NUXT_PUBLIC_SITE_URL=
|
||||||
|
|||||||
1
docs/.nuxtrc
Normal file
1
docs/.nuxtrc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
imports.autoImport=true
|
||||||
45
docs/app.vue
45
docs/app.vue
@@ -10,7 +10,7 @@
|
|||||||
<Footer />
|
<Footer />
|
||||||
|
|
||||||
<ClientOnly>
|
<ClientOnly>
|
||||||
<LazyUDocsSearch :files="files" :navigation="navigation" />
|
<LazyUDocsSearch ref="searchRef" :files="files" :navigation="navigation" :groups="groups" />
|
||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
|
|
||||||
<UNotifications>
|
<UNotifications>
|
||||||
@@ -27,36 +27,52 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { withoutTrailingSlash } from 'ufo'
|
import { withoutTrailingSlash } from 'ufo'
|
||||||
|
import { debounce } from 'perfect-debounce'
|
||||||
|
import type { ParsedContent } from '@nuxt/content/dist/runtime/types'
|
||||||
|
|
||||||
|
const searchRef = ref()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const colorMode = useColorMode()
|
const colorMode = useColorMode()
|
||||||
const { prefix, removePrefixFromNavigation, removePrefixFromFiles } = useContentSource()
|
const { branch, branches } = useContentSource()
|
||||||
|
|
||||||
const { data: nav } = await useAsyncData('navigation', () => fetchContentNavigation())
|
const { data: nav } = await useAsyncData('navigation', () => fetchContentNavigation())
|
||||||
|
const { data: files } = useLazyFetch<ParsedContent[]>('/api/search.json', { default: () => [], server: false })
|
||||||
const { data: search } = useLazyFetch('/api/search.json', {
|
|
||||||
default: () => [],
|
|
||||||
server: false
|
|
||||||
})
|
|
||||||
|
|
||||||
// Computed
|
// Computed
|
||||||
const navigation = computed(() => {
|
|
||||||
const navigation = nav.value.find(link => link._path === prefix.value)?.children || []
|
|
||||||
|
|
||||||
return prefix.value === '/main' ? removePrefixFromNavigation(navigation) : navigation
|
const navigation = computed(() => {
|
||||||
|
const main = nav.value.filter(item => item._path !== '/dev')
|
||||||
|
const dev = nav.value.find(item => item._path === '/dev')?.children
|
||||||
|
|
||||||
|
return branch.value?.name === 'dev' ? dev : main
|
||||||
})
|
})
|
||||||
|
|
||||||
const files = computed(() => {
|
const groups = computed(() => {
|
||||||
const files = search.value.filter(file => file._path.startsWith(prefix.value))
|
if (route.path === '/') {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
return prefix.value === '/main' ? removePrefixFromFiles(files) : files
|
return [{ key: 'branch', label: 'Branch', commands: branches.value }]
|
||||||
})
|
})
|
||||||
|
|
||||||
const color = computed(() => colorMode.value === 'dark' ? '#18181b' : 'white')
|
const color = computed(() => colorMode.value === 'dark' ? '#18181b' : 'white')
|
||||||
|
|
||||||
|
// Watch
|
||||||
|
|
||||||
|
watch(() => searchRef.value?.commandPaletteRef?.query, debounce((query: string) => {
|
||||||
|
if (!query) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
useTrackEvent('Search', { props: { query: `${query} - ${searchRef.value?.commandPaletteRef.results.length} results` } })
|
||||||
|
}, 500))
|
||||||
|
|
||||||
// Head
|
// Head
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
meta: [
|
meta: [
|
||||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1' },
|
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
||||||
{ key: 'theme-color', name: 'theme-color', content: color }
|
{ key: 'theme-color', name: 'theme-color', content: color }
|
||||||
],
|
],
|
||||||
link: [
|
link: [
|
||||||
@@ -74,6 +90,7 @@ useServerSeoMeta({
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Provide
|
// Provide
|
||||||
|
|
||||||
provide('navigation', navigation)
|
provide('navigation', navigation)
|
||||||
provide('files', files)
|
provide('files', files)
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
color="gray"
|
color="gray"
|
||||||
:ui="{ icon: { trailing: { padding: { sm: 'pe-1.5' } } } }"
|
:ui="{ icon: { trailing: { padding: { sm: 'pe-1.5' } } } }"
|
||||||
:ui-menu="{ option: { container: 'gap-1.5' } }"
|
:ui-menu="{ option: { container: 'gap-1.5' } }"
|
||||||
@update:model-value="selectBranch"
|
@update:model-value="select"
|
||||||
>
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
<UIcon v-if="branch.icon" :name="branch.icon" class="w-4 h-4 flex-shrink-0 text-gray-600 dark:text-gray-300" />
|
<UIcon v-if="branch.icon" :name="branch.icon" class="w-4 h-4 flex-shrink-0 text-gray-600 dark:text-gray-300" />
|
||||||
@@ -32,19 +32,5 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const route = useRoute()
|
const { branches, branch, select } = useContentSource()
|
||||||
const router = useRouter()
|
|
||||||
const { branches, branch } = useContentSource()
|
|
||||||
|
|
||||||
function selectBranch (branch) {
|
|
||||||
if (branch.name === 'dev') {
|
|
||||||
if (route.path.startsWith('/dev')) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
router.push(`/dev${route.path}`)
|
|
||||||
} else {
|
|
||||||
router.push(route.path.replace('/dev', ''))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="w-full h-px bg-gray-200 dark:bg-gray-800 flex items-center justify-center">
|
<div v-if="$route.path !== '/playground'" class="w-full h-px bg-gray-200 dark:bg-gray-800 flex items-center justify-center">
|
||||||
<div class="bg-white dark:bg-gray-900 px-4">
|
<div class="bg-white dark:bg-gray-900 px-4">
|
||||||
<LogoOnly class="w-5 h-5" />
|
<LogoOnly class="w-5 h-5" />
|
||||||
</div>
|
</div>
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
<UFooter :links="[]" :ui="{ bottom: { container: 'lg:py-4' } }">
|
<UFooter :links="[]" :ui="{ bottom: { container: 'lg:py-4' } }">
|
||||||
<template #left>
|
<template #left>
|
||||||
<div class="text-sm text-gray-600 dark:text-gray-300">
|
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||||||
Made by
|
Made by
|
||||||
<NuxtLink to="https://nuxtlabs.com" aria-label="NuxtLabs" class="inline-block">
|
<NuxtLink to="https://nuxtlabs.com" aria-label="NuxtLabs" class="inline-block">
|
||||||
<LogoLabs class="text-gray-900 dark:text-white h-4 w-auto" />
|
<LogoLabs class="text-gray-900 dark:text-white h-4 w-auto" />
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #center>
|
<template #center>
|
||||||
<span class="text-sm text-gray-600 dark:text-gray-300">
|
<span class="text-sm text-gray-500 dark:text-gray-400">
|
||||||
Published under <NuxtLink to="https://github.com/nuxt/ui" target="_blank" class="text-gray-900 dark:text-white">
|
Published under <NuxtLink to="https://github.com/nuxt/ui" target="_blank" class="text-gray-900 dark:text-white">
|
||||||
MIT License
|
MIT License
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
@@ -24,9 +24,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #right>
|
<template #right>
|
||||||
<USocialButton aria-label="Nuxt Website" icon="i-simple-icons-nuxtdotjs" to="https://nuxt.com" />
|
<UButton aria-label="Nuxt Website" icon="i-simple-icons-nuxtdotjs" to="https://nuxt.com" target="_blank" v-bind="($ui.button.secondary as any)" />
|
||||||
<USocialButton aria-label="Nuxt on X" icon="i-simple-icons-x" to="https://x.com/nuxtlabs" />
|
<UButton aria-label="Nuxt UI on Discord" icon="i-simple-icons-discord" to="https://discord.com/invite/ps2h6QT" target="_blank" v-bind="($ui.button.secondary as any)" />
|
||||||
<USocialButton aria-label="Nuxt UI on GitHub" icon="i-simple-icons-github" to="https://github.com/nuxt/ui" />
|
<UButton aria-label="Nuxt on X" icon="i-simple-icons-x" to="https://x.com/nuxt_js" target="_blank" v-bind="($ui.button.secondary as any)" />
|
||||||
|
<UButton aria-label="Nuxt UI on GitHub" icon="i-simple-icons-github" to="https://github.com/nuxt/ui" target="_blank" v-bind="($ui.button.secondary as any)" />
|
||||||
</template>
|
</template>
|
||||||
</UFooter>
|
</UFooter>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
// force typescript
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -7,23 +7,28 @@
|
|||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<template #left>
|
<template #left>
|
||||||
<NuxtLink to="/" class="flex items-end gap-1.5 font-bold text-xl text-gray-900 dark:text-white">
|
<NuxtLink to="/" class="flex items-end gap-1.5 font-bold text-xl text-gray-900 dark:text-white" aria-label="Nuxt UI">
|
||||||
<Logo class="w-auto h-6" />
|
<Logo class="w-auto h-6" />
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="$route.path !== '/'" #center>
|
|
||||||
<UDocsSearchButton class="ml-1.5 flg:w-64 xl:w-96" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #right>
|
<template #right>
|
||||||
<ColorPicker />
|
<ColorPicker />
|
||||||
|
|
||||||
<UDocsSearchButton v-if="$route.path === '/'" icon-only />
|
<UTooltip text="Search" :shortcuts="[metaSymbol, 'K']">
|
||||||
|
<UDocsSearchButton :label="null" />
|
||||||
|
</UTooltip>
|
||||||
|
|
||||||
<UColorModeButton v-if="!$colorMode.forced" />
|
<UColorModeButton />
|
||||||
|
|
||||||
<USocialButton to="https://github.com/nuxt/ui" target="_blank" icon="i-simple-icons-github" class="hidden lg:inline-flex" />
|
<UButton
|
||||||
|
to="https://github.com/nuxt/ui"
|
||||||
|
target="_blank"
|
||||||
|
icon="i-simple-icons-github"
|
||||||
|
aria-label="GitHub"
|
||||||
|
class="hidden lg:inline-flex"
|
||||||
|
v-bind="($ui.button.secondary as any)"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #panel>
|
<template #panel>
|
||||||
@@ -37,25 +42,23 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { NavItem } from '@nuxt/content/dist/runtime/types'
|
import type { NavItem } from '@nuxt/content/dist/runtime/types'
|
||||||
|
|
||||||
const route = useRoute()
|
const { metaSymbol } = useShortcuts()
|
||||||
const { mapContentNavigation } = useElementsHelpers()
|
|
||||||
|
|
||||||
const navigation = inject<Ref<NavItem[]>>('navigation')
|
const navigation = inject<Ref<NavItem[]>>('navigation')
|
||||||
|
|
||||||
const links = computed(() => {
|
const links = computed(() => {
|
||||||
if (route.path !== '/') {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
return [{
|
return [{
|
||||||
label: 'Documentation',
|
label: 'Documentation',
|
||||||
icon: 'i-heroicons-book-open-solid',
|
icon: 'i-heroicons-book-open-solid',
|
||||||
to: '/getting-started'
|
to: '/getting-started'
|
||||||
|
}, {
|
||||||
|
label: 'Examples',
|
||||||
|
icon: 'i-heroicons-square-3-stack-3d',
|
||||||
|
to: '/getting-started/examples'
|
||||||
}, {
|
}, {
|
||||||
label: 'Playground',
|
label: 'Playground',
|
||||||
icon: 'i-simple-icons-stackblitz',
|
icon: 'i-simple-icons-stackblitz',
|
||||||
to: 'https://stackblitz.com/edit/nuxt-ui?file=app.config.ts,app.vue',
|
to: '/playground'
|
||||||
target: '_blank'
|
|
||||||
}, {
|
}, {
|
||||||
label: 'Releases',
|
label: 'Releases',
|
||||||
icon: 'i-heroicons-rocket-launch-solid',
|
icon: 'i-heroicons-rocket-launch-solid',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<UPopover mode="hover">
|
<UPopover mode="hover">
|
||||||
<template #default="{ open }">
|
<template #default="{ open }">
|
||||||
<UButton color="gray" variant="ghost" square :class="[open && 'bg-gray-50 dark:bg-gray-800']">
|
<UButton color="gray" variant="ghost" square :class="[open && 'bg-gray-50 dark:bg-gray-800']" aria-label="Color picker">
|
||||||
<UIcon name="i-heroicons-swatch-20-solid" class="w-5 h-5 text-primary-500 dark:text-primary-400" />
|
<UIcon name="i-heroicons-swatch-20-solid" class="w-5 h-5 text-primary-500 dark:text-primary-400" />
|
||||||
</UButton>
|
</UButton>
|
||||||
</template>
|
</template>
|
||||||
@@ -30,7 +30,7 @@ const colorMode = useColorMode()
|
|||||||
|
|
||||||
// Computed
|
// Computed
|
||||||
|
|
||||||
const primaryColors = computed(() => useWithout(appConfig.ui.colors, 'primary').map(color => ({ value: color, text: color, hex: colors[color][colorMode.value === 'dark' ? 400 : 500] })))
|
const primaryColors = computed(() => appConfig.ui.colors.filter(color => color !== 'primary').map(color => ({ value: color, text: color, hex: colors[color][colorMode.value === 'dark' ? 400 : 500] })))
|
||||||
const primary = computed({
|
const primary = computed({
|
||||||
get () {
|
get () {
|
||||||
return primaryColors.value.find(option => option.value === appConfig.ui.primary)
|
return primaryColors.value.find(option => option.value === appConfig.ui.primary)
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<UTooltip :text="color.value" class="capitalize" :open-delay="500">
|
<UTooltip :text="color.value" class="capitalize" :open-delay="500">
|
||||||
<UButton
|
<UButton
|
||||||
color="transparent"
|
color="white"
|
||||||
square
|
square
|
||||||
:ui="{
|
:ui="{
|
||||||
color: {
|
color: {
|
||||||
transparent: {
|
white: {
|
||||||
solid: 'bg-gray-100 dark:bg-gray-800',
|
solid: 'ring-0 bg-gray-100 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-800',
|
||||||
ghost: 'hover:bg-gray-50 dark:hover:bg-gray-800/50'
|
ghost: 'hover:bg-gray-50 dark:hover:bg-gray-800/50'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,13 +46,17 @@
|
|||||||
</component>
|
</component>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ContentRenderer v-if="!previewOnly" :value="ast" class="[&>div>pre]:!rounded-t-none" />
|
<ContentRenderer v-if="!previewOnly" :value="ast" class="[&>div>pre]:!rounded-t-none [&>div>pre]:!mt-0" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import { transformContent } from '@nuxt/content/transformers'
|
import { transformContent } from '@nuxt/content/transformers'
|
||||||
|
// @ts-ignore
|
||||||
|
import { useShikiHighlighter } from '@nuxtjs/mdc/runtime'
|
||||||
|
import { upperFirst, camelCase, kebabCase } from 'scule'
|
||||||
|
import * as config from '#ui/ui.config'
|
||||||
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -114,15 +118,14 @@ const appConfig = useAppConfig()
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
const slug = props.slug || route.params.slug[route.params.slug.length - 1]
|
const slug = props.slug || route.params.slug[route.params.slug.length - 1]
|
||||||
const camelName = useCamelCase(slug)
|
const camelName = camelCase(slug)
|
||||||
const name = `U${useUpperFirst(camelName)}`
|
const name = `U${upperFirst(camelName)}`
|
||||||
|
|
||||||
const meta = await fetchComponentMeta(name)
|
const meta = await fetchComponentMeta(name)
|
||||||
|
|
||||||
// Computed
|
// Computed
|
||||||
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
const ui = computed(() => ({ ...config[camelName], ...props.ui }))
|
||||||
const ui = computed(() => ({ ...appConfig.ui[camelName], ...props.ui }))
|
|
||||||
|
|
||||||
const fullProps = computed(() => ({ ...baseProps, ...componentProps }))
|
const fullProps = computed(() => ({ ...baseProps, ...componentProps }))
|
||||||
const vModel = computed({
|
const vModel = computed({
|
||||||
@@ -138,8 +141,8 @@ const propsToSelect = computed(() => Object.keys(componentProps).map((key) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const prop = meta?.meta?.props?.find((prop: any) => prop.name === key)
|
const prop = meta?.meta?.props?.find((prop: any) => prop.name === key)
|
||||||
const dottedKey = useKebabCase(key).replaceAll('-', '.')
|
const dottedKey = kebabCase(key).replaceAll('-', '.')
|
||||||
const keys = useGet(ui.value, dottedKey, {})
|
const keys = ui.value[dottedKey] ?? {}
|
||||||
let options = typeof keys === 'object' && Object.keys(keys)
|
let options = typeof keys === 'object' && Object.keys(keys)
|
||||||
if (key.toLowerCase().endsWith('color')) {
|
if (key.toLowerCase().endsWith('color')) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -149,7 +152,7 @@ const propsToSelect = computed(() => Object.keys(componentProps).map((key) => {
|
|||||||
return {
|
return {
|
||||||
type: prop?.type || 'string',
|
type: prop?.type || 'string',
|
||||||
name: key,
|
name: key,
|
||||||
label: key === 'modelValue' ? 'value' : useCamelCase(key),
|
label: key === 'modelValue' ? 'value' : camelCase(key),
|
||||||
options
|
options
|
||||||
}
|
}
|
||||||
}).filter(Boolean))
|
}).filter(Boolean))
|
||||||
@@ -163,7 +166,7 @@ const code = computed(() => {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
code += ` ${(typeof value === 'boolean' && value !== true) || typeof value === 'object' || typeof value === 'number' ? ':' : ''}${key === 'modelValue' ? 'value' : useKebabCase(key)}${typeof value === 'boolean' && !!value && key !== 'modelValue' ? '' : `="${typeof value === 'object' ? renderObject(value) : value}"`}`
|
code += ` ${(typeof value === 'boolean' && value !== true) || typeof value === 'object' || typeof value === 'number' ? ':' : ''}${key === 'modelValue' ? 'value' : kebabCase(key)}${typeof value === 'boolean' && !!value && key !== 'modelValue' ? '' : `="${typeof value === 'object' ? renderObject(value) : value}"`}`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.slots) {
|
if (props.slots) {
|
||||||
@@ -205,12 +208,17 @@ function renderObject (obj: any) {
|
|||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data: ast } = await useAsyncData(`${name}-ast-${JSON.stringify(props)}`, () => transformContent('content:_markdown.md', code.value, {
|
const shikiHighlighter = useShikiHighlighter({})
|
||||||
highlight: {
|
const codeHighlighter = async (code: string, lang: string, theme: any, highlights: number[]) => shikiHighlighter.getHighlightedAST(code, lang, theme, { highlights })
|
||||||
theme: {
|
const { data: ast } = await useAsyncData(`${name}-ast-${JSON.stringify({ props: componentProps, slots: props.slots })}`, () => transformContent('content:_markdown.md', code.value, {
|
||||||
light: 'material-theme-lighter',
|
markdown: {
|
||||||
default: 'material-theme',
|
highlight: {
|
||||||
dark: 'material-theme-palenight'
|
highlighter: codeHighlighter,
|
||||||
|
theme: {
|
||||||
|
light: 'material-theme-lighter',
|
||||||
|
default: 'material-theme',
|
||||||
|
dark: 'material-theme-palenight'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}), { watch: [code] })
|
}), { watch: [code] })
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="[&>div>pre]:!rounded-t-none">
|
<div class="[&>div>pre]:!rounded-t-none [&>div>pre]:!mt-0">
|
||||||
<div class="flex border border-gray-200 dark:border-gray-700 relative not-prose rounded-t-md" :class="[{ 'p-4': padding, 'rounded-b-md': !$slots.code, 'border-b-0': !!$slots.code }, backgroundClass, overflowClass]">
|
<div class="flex border border-gray-200 dark:border-gray-700 relative not-prose rounded-t-md" :class="[{ 'p-4': padding, 'rounded-b-md': !$slots.code, 'border-b-0': !!$slots.code }, backgroundClass, overflowClass]">
|
||||||
<ContentSlot v-if="$slots.default" :use="$slots.default" />
|
<ContentSlot v-if="$slots.default" :use="$slots.default" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import { transformContent } from '@nuxt/content/transformers'
|
import { transformContent } from '@nuxt/content/transformers'
|
||||||
|
import { upperFirst, camelCase } from 'scule'
|
||||||
|
import * as config from '#ui/ui.config'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
slug: {
|
slug: {
|
||||||
@@ -13,17 +15,16 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const appConfig = useAppConfig()
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
const slug = props.slug || route.params.slug[route.params.slug.length - 1]
|
const slug = props.slug || route.params.slug[route.params.slug.length - 1]
|
||||||
const camelName = useCamelCase(slug)
|
const camelName = camelCase(slug)
|
||||||
const name = `U${useUpperFirst(camelName)}`
|
const name = `U${upperFirst(camelName)}`
|
||||||
|
|
||||||
const preset = appConfig.ui[camelName]
|
const preset = config[camelName]
|
||||||
|
|
||||||
const { data: ast } = await useAsyncData(`${name}-preset`, () => transformContent('content:_markdown.md', `
|
const { data: ast } = await useAsyncData(`${name}-preset`, () => transformContent('content:_markdown.md', `
|
||||||
\`\`\`json [appConfig.ui.${camelName}]
|
\`\`\`json
|
||||||
${JSON.stringify(preset, null, 2)}
|
${JSON.stringify(preset, null, 2)}
|
||||||
\`\`\`\
|
\`\`\`\
|
||||||
`, {
|
`, {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { upperFirst, camelCase } from 'scule'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
slug: {
|
slug: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -17,8 +19,8 @@ const props = defineProps({
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
const slug = props.slug || route.params.slug[route.params.slug.length - 1]
|
const slug = props.slug || route.params.slug[route.params.slug.length - 1]
|
||||||
const camelName = useCamelCase(slug)
|
const camelName = camelCase(slug)
|
||||||
const name = `U${useUpperFirst(camelName)}`
|
const name = `U${upperFirst(camelName)}`
|
||||||
|
|
||||||
const meta = await fetchComponentMeta(name)
|
const meta = await fetchComponentMeta(name)
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -19,8 +19,8 @@
|
|||||||
<ComponentPropsField v-for="subProp in Object.values(prop.schema.schema)" :key="(subProp as any).name" :prop="subProp" />
|
<ComponentPropsField v-for="subProp in Object.values(prop.schema.schema)" :key="(subProp as any).name" :prop="subProp" />
|
||||||
</FieldGroup>
|
</FieldGroup>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
<div v-else-if="prop.schema?.kind === 'enum' && prop.schema.type !== 'boolean' && startsWithCapital(prop.schema.type)" class="space-x-1 leading-7 -my-1">
|
<div v-else-if="prop.schema?.kind === 'enum' && prop.schema.type !== 'boolean' && startsWithCapital(prop.schema.type) && !prop.schema.type.startsWith(prop.schema.schema[0])" class="space-x-1 leading-7 -my-1">
|
||||||
<code v-for="value in prop.schema.schema" :key="value">{{ value }}</code>
|
<code v-for="value in prop.schema.schema.filter(value => typeof value === 'string')" :key="value" class="whitespace-pre-wrap break-words">{{ value }}</code>
|
||||||
</div>
|
</div>
|
||||||
</Field>
|
</Field>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { upperFirst, camelCase } from 'scule'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
slug: {
|
slug: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -28,8 +30,8 @@ const props = defineProps({
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
const slug = props.slug || route.params.slug[route.params.slug.length - 1]
|
const slug = props.slug || route.params.slug[route.params.slug.length - 1]
|
||||||
const camelName = useCamelCase(slug)
|
const camelName = camelCase(slug)
|
||||||
const name = `U${useUpperFirst(camelName)}`
|
const name = `U${upperFirst(camelName)}`
|
||||||
|
|
||||||
const meta = await fetchComponentMeta(name)
|
const meta = await fetchComponentMeta(name)
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -61,9 +61,9 @@ const items = [{
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col items-center">
|
<div class="flex flex-col items-center">
|
||||||
<code>$ npm install @nuxt/ui</code>
|
<code>$ npm i @nuxt/ui</code>
|
||||||
<code>$ nnpm install -D @nuxt/ui</code>
|
<code>$ yarn add @nuxt/ui</code>
|
||||||
<code>$ pnpm i -D @nuxt/ui</code>
|
<code>$ pnpm add @nuxt/ui</code>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</UAccordion>
|
</UAccordion>
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ const toast = useToast()
|
|||||||
const commandPaletteRef = ref()
|
const commandPaletteRef = ref()
|
||||||
|
|
||||||
const users = [
|
const users = [
|
||||||
{ id: 'benjamincanac', label: 'benjamincanac', href: 'https://github.com/benjamincanac', target: '_blank', avatar: { src: 'https://ipx.nuxt.com/s_16x16/gh_avatar/benjamincanac', srcset: 'https://ipx.nuxt.com/s_32x32/gh_avatar/benjamincanac 2x' } },
|
{ id: 'benjamincanac', label: 'benjamincanac', href: 'https://github.com/benjamincanac', target: '_blank', avatar: { src: 'https://ipx.nuxt.com/s_16x16/gh_avatar/benjamincanac', srcset: 'https://ipx.nuxt.com/s_32x32/gh_avatar/benjamincanac 2x', loading: 'lazy' } },
|
||||||
{ id: 'Atinux', label: 'Atinux', href: 'https://github.com/Atinux', target: '_blank', avatar: { src: 'https://ipx.nuxt.com/s_16x16/gh_avatar/Atinux', srcset: 'https://ipx.nuxt.com/s_32x32/gh_avatar/Atinux 2x' } },
|
{ id: 'Atinux', label: 'Atinux', href: 'https://github.com/Atinux', target: '_blank', avatar: { src: 'https://ipx.nuxt.com/s_16x16/gh_avatar/Atinux', srcset: 'https://ipx.nuxt.com/s_32x32/gh_avatar/Atinux 2x', loading: 'lazy' } },
|
||||||
{ id: 'smarroufin', label: 'smarroufin', href: 'https://github.com/smarroufin', target: '_blank', avatar: { src: 'https://ipx.nuxt.com/s_16x16/gh_avatar/smarroufin', srcset: 'https://ipx.nuxt.com/s_32x32/gh_avatar/smarroufin 2x' } }
|
{ id: 'smarroufin', label: 'smarroufin', href: 'https://github.com/smarroufin', target: '_blank', avatar: { src: 'https://ipx.nuxt.com/s_16x16/gh_avatar/smarroufin', srcset: 'https://ipx.nuxt.com/s_32x32/gh_avatar/smarroufin 2x', loading: 'lazy' } }
|
||||||
]
|
]
|
||||||
|
|
||||||
const actions = [
|
const actions = [
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const label = computed(() => date.value.toLocaleDateString('en-us', { weekday: '
|
|||||||
<UButton icon="i-heroicons-calendar-days-20-solid" :label="label" />
|
<UButton icon="i-heroicons-calendar-days-20-solid" :label="label" />
|
||||||
|
|
||||||
<template #panel="{ close }">
|
<template #panel="{ close }">
|
||||||
<DatePicker v-model="date" @close="close" />
|
<LazyDatePicker v-model="date" @close="close" />
|
||||||
</template>
|
</template>
|
||||||
</UPopover>
|
</UPopover>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ async function submit (event: FormSubmitEvent<Schema>) {
|
|||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UFormGroup name="checkbox" label="Checkbox">
|
<UFormGroup name="checkbox" label="Checkbox">
|
||||||
<UCheckbox v-model="state.checkbox" />
|
<UCheckbox v-model="state.checkbox" label="Check me" />
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UFormGroup name="radio" label="Radio">
|
<UFormGroup name="radio" label="Radio">
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { string, object, email, minLength, Input } from 'valibot'
|
import { string, objectAsync, email, minLength, Input } from 'valibot'
|
||||||
import type { FormSubmitEvent } from '@nuxt/ui/dist/runtime/types'
|
import type { FormSubmitEvent } from '@nuxt/ui/dist/runtime/types'
|
||||||
|
|
||||||
const schema = object({
|
const schema = objectAsync({
|
||||||
email: string([email('Invalid email')]),
|
email: string([email('Invalid email')]),
|
||||||
password: string([minLength(8, 'Must be at least 8 characters')])
|
password: string([minLength(8, 'Must be at least 8 characters')])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
<UFormGroup label="Email" :error="!email && 'You must enter an email'" help="This is a nice email!">
|
||||||
|
<template #default="{ error }">
|
||||||
|
<UInput v-model="email" type="email" placeholder="Enter email" :trailing-icon="error ? 'i-heroicons-exclamation-triangle-20-solid' : undefined" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #error="{ error }">
|
||||||
|
<UAlert v-if="error" icon="i-heroicons-exclamation-triangle-20-solid" :title="error" color="red" />
|
||||||
|
<UAlert v-else icon="i-heroicons-check-circle-20-solid" title="Your email is valid" color="green" />
|
||||||
|
</template>
|
||||||
|
</UFormGroup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const email = ref('')
|
||||||
|
</script>
|
||||||
@@ -5,7 +5,7 @@ const items = ref(Array(55))
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full divide-y divide-gray-200 dark:divide-gray-700 space-y-4">
|
<div class="w-full divide-y divide-gray-200 dark:divide-gray-700 space-y-4">
|
||||||
<div class="flex justify-between w-full">
|
<div class="flex flex-wrap gap-1 justify-between w-full">
|
||||||
<div dir="ltr">
|
<div dir="ltr">
|
||||||
<UInput
|
<UInput
|
||||||
icon="i-heroicons-magnifying-glass-20-solid"
|
icon="i-heroicons-magnifying-glass-20-solid"
|
||||||
@@ -27,7 +27,7 @@ const items = ref(Array(55))
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-between w-full pt-4">
|
<div class="flex flex-wrap gap-1 justify-between w-full pt-4">
|
||||||
<div dir="ltr">
|
<div dir="ltr">
|
||||||
<UPagination
|
<UPagination
|
||||||
v-model="page"
|
v-model="page"
|
||||||
|
|||||||
@@ -3,5 +3,6 @@ const value = ref(50)
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<URange v-model="value" />
|
<label for="range" class="sr-only" />
|
||||||
|
<URange id="range" v-model="value" name="range" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const selected = ref(people[3])
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<USelectMenu v-slot="{ open }" v-model="selected" :options="people">
|
<USelectMenu v-slot="{ open }" v-model="selected" :options="people">
|
||||||
<UButton color="gray">
|
<UButton color="gray" class="flex-1 justify-between">
|
||||||
{{ selected }}
|
{{ selected }}
|
||||||
|
|
||||||
<UIcon name="i-heroicons-chevron-right-20-solid" class="w-5 h-5 transition-transform text-gray-400 dark:text-gray-500" :class="[open && 'transform rotate-90']" />
|
<UIcon name="i-heroicons-chevron-right-20-solid" class="w-5 h-5 transition-transform text-gray-400 dark:text-gray-500" :class="[open && 'transform rotate-90']" />
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ const { data: todos, pending } = await useLazyAsyncData('todos', () => $fetch<{
|
|||||||
<UButton
|
<UButton
|
||||||
icon="i-heroicons-chevron-down"
|
icon="i-heroicons-chevron-down"
|
||||||
trailing
|
trailing
|
||||||
variant="soft"
|
color="gray"
|
||||||
size="xs"
|
size="xs"
|
||||||
>
|
>
|
||||||
Mark as
|
Mark as
|
||||||
@@ -153,7 +153,7 @@ const { data: todos, pending } = await useLazyAsyncData('todos', () => $fetch<{
|
|||||||
<USelectMenu v-model="selectedColumns" :options="columns" multiple>
|
<USelectMenu v-model="selectedColumns" :options="columns" multiple>
|
||||||
<UButton
|
<UButton
|
||||||
icon="i-heroicons-view-columns"
|
icon="i-heroicons-view-columns"
|
||||||
variant="soft"
|
color="gray"
|
||||||
size="xs"
|
size="xs"
|
||||||
>
|
>
|
||||||
Columns
|
Columns
|
||||||
@@ -162,8 +162,7 @@ const { data: todos, pending } = await useLazyAsyncData('todos', () => $fetch<{
|
|||||||
|
|
||||||
<UButton
|
<UButton
|
||||||
icon="i-heroicons-funnel"
|
icon="i-heroicons-funnel"
|
||||||
variant="soft"
|
color="gray"
|
||||||
color="red"
|
|
||||||
size="xs"
|
size="xs"
|
||||||
:disabled="search === '' && selectedStatus.length === 0"
|
:disabled="search === '' && selectedStatus.length === 0"
|
||||||
@click="resetFilters"
|
@click="resetFilters"
|
||||||
@@ -181,6 +180,8 @@ const { data: todos, pending } = await useLazyAsyncData('todos', () => $fetch<{
|
|||||||
:loading="pending"
|
:loading="pending"
|
||||||
sort-asc-icon="i-heroicons-arrow-up"
|
sort-asc-icon="i-heroicons-arrow-up"
|
||||||
sort-desc-icon="i-heroicons-arrow-down"
|
sort-desc-icon="i-heroicons-arrow-down"
|
||||||
|
class="w-full"
|
||||||
|
:ui="{ td: { base: 'max-w-[0] truncate' } }"
|
||||||
@select="select"
|
@select="select"
|
||||||
>
|
>
|
||||||
<template #completed-data="{ row }">
|
<template #completed-data="{ row }">
|
||||||
@@ -212,7 +213,7 @@ const { data: todos, pending } = await useLazyAsyncData('todos', () => $fetch<{
|
|||||||
|
|
||||||
<!-- Number of rows & Pagination -->
|
<!-- Number of rows & Pagination -->
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex flex-wrap justify-between items-center">
|
||||||
<div>
|
<div>
|
||||||
<span class="text-sm leading-5">
|
<span class="text-sm leading-5">
|
||||||
Showing
|
Showing
|
||||||
|
|||||||
40
docs/components/content/examples/TableExampleStyle.vue
Normal file
40
docs/components/content/examples/TableExampleStyle.vue
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<script setup>
|
||||||
|
const columns = [{
|
||||||
|
key: 'id',
|
||||||
|
label: '#'
|
||||||
|
}, {
|
||||||
|
key: 'quantity',
|
||||||
|
label: 'Quantity',
|
||||||
|
class: 'italic'
|
||||||
|
}, {
|
||||||
|
key: 'name',
|
||||||
|
label: 'Name'
|
||||||
|
}]
|
||||||
|
|
||||||
|
const items = [{
|
||||||
|
id: 1,
|
||||||
|
name: 'Apple',
|
||||||
|
quantity: { value: 100, class: 'bg-green-500/50 dark:bg-green-400/50' }
|
||||||
|
}, {
|
||||||
|
id: 2,
|
||||||
|
name: 'Orange',
|
||||||
|
quantity: { value: 0 },
|
||||||
|
class: 'bg-red-500/50 dark:bg-red-400/50 animate-pulse'
|
||||||
|
}, {
|
||||||
|
id: 3,
|
||||||
|
name: 'Banana',
|
||||||
|
quantity: { value: 30, class: 'bg-green-500/50 dark:bg-green-400/50' }
|
||||||
|
}, {
|
||||||
|
id: 4,
|
||||||
|
name: 'Mango',
|
||||||
|
quantity: { value: 5, class: 'bg-green-500/50 dark:bg-green-400/50' }
|
||||||
|
}]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UTable :rows="items" :columns="columns">
|
||||||
|
<template #quantity-data="{ row }">
|
||||||
|
{{ row.quantity.value }}
|
||||||
|
</template>
|
||||||
|
</UTable>
|
||||||
|
</template>
|
||||||
@@ -24,9 +24,9 @@ function onSubmitPassword () {
|
|||||||
<template #account="{ item }">
|
<template #account="{ item }">
|
||||||
<UCard @submit.prevent="onSubmitAccount">
|
<UCard @submit.prevent="onSubmitAccount">
|
||||||
<template #header>
|
<template #header>
|
||||||
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
<p class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
</h3>
|
</p>
|
||||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||||
Make changes to your account here. Click save when you're done.
|
Make changes to your account here. Click save when you're done.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ function onSubmit (form) {
|
|||||||
<template #item="{ item }">
|
<template #item="{ item }">
|
||||||
<UCard @submit.prevent="() => onSubmit(item.key === 'account' ? accountForm : passwordForm)">
|
<UCard @submit.prevent="() => onSubmit(item.key === 'account' ? accountForm : passwordForm)">
|
||||||
<template #header>
|
<template #header>
|
||||||
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
<p class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
</h3>
|
</p>
|
||||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||||
{{ item.description }}
|
{{ item.description }}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
const links = [{
|
const links = [{
|
||||||
avatar: {
|
avatar: {
|
||||||
src: 'https://ipx.nuxt.com/s_16x16/gh_avatar/benjamincanac',
|
src: 'https://ipx.nuxt.com/s_16x16/gh_avatar/benjamincanac',
|
||||||
srcset: 'https://ipx.nuxt.com/s_32x32/gh_avatar/benjamincanac 2x'
|
srcset: 'https://ipx.nuxt.com/s_32x32/gh_avatar/benjamincanac 2x',
|
||||||
|
alt: ''
|
||||||
},
|
},
|
||||||
label: 'benjamincanac',
|
label: 'benjamincanac',
|
||||||
to: 'https://github.com/benjamincanac',
|
to: 'https://github.com/benjamincanac',
|
||||||
@@ -10,7 +11,8 @@ const links = [{
|
|||||||
}, {
|
}, {
|
||||||
avatar: {
|
avatar: {
|
||||||
src: 'https://ipx.nuxt.com/s_16x16/gh_avatar/Atinux',
|
src: 'https://ipx.nuxt.com/s_16x16/gh_avatar/Atinux',
|
||||||
srcset: 'https://ipx.nuxt.com/s_32x32/gh_avatar/Atinux 2x'
|
srcset: 'https://ipx.nuxt.com/s_32x32/gh_avatar/Atinux 2x',
|
||||||
|
alt: ''
|
||||||
},
|
},
|
||||||
label: 'Atinux',
|
label: 'Atinux',
|
||||||
to: 'https://github.com/Atinux',
|
to: 'https://github.com/Atinux',
|
||||||
@@ -18,14 +20,13 @@ const links = [{
|
|||||||
}, {
|
}, {
|
||||||
avatar: {
|
avatar: {
|
||||||
src: 'https://ipx.nuxt.com/s_16x16/gh_avatar/smarroufin',
|
src: 'https://ipx.nuxt.com/s_16x16/gh_avatar/smarroufin',
|
||||||
srcset: 'https://ipx.nuxt.com/s_32x32/gh_avatar/smarroufin 2x'
|
srcset: 'https://ipx.nuxt.com/s_32x32/gh_avatar/smarroufin 2x',
|
||||||
|
alt: ''
|
||||||
},
|
},
|
||||||
label: 'smarroufin',
|
label: 'smarroufin',
|
||||||
to: 'https://github.com/smarroufin',
|
to: 'https://github.com/smarroufin',
|
||||||
target: '_blank'
|
target: '_blank'
|
||||||
}]
|
}]
|
||||||
|
|
||||||
const { ui } = useAppConfig()
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -33,8 +34,9 @@ const { ui } = useAppConfig()
|
|||||||
<template #avatar="{ link }">
|
<template #avatar="{ link }">
|
||||||
<UAvatar
|
<UAvatar
|
||||||
v-if="link.avatar"
|
v-if="link.avatar"
|
||||||
v-bind="{ size: ui.verticalNavigation.avatar.size, ...link.avatar }"
|
v-bind="link.avatar"
|
||||||
:class="[ui.verticalNavigation.avatar.base]"
|
size="3xs"
|
||||||
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
<UIcon v-else name="i-heroicons-user-circle-20-solid" class="text-lg" />
|
<UIcon v-else name="i-heroicons-user-circle-20-solid" class="text-lg" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,99 +1,206 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const refs = ref([])
|
||||||
|
const section = ref()
|
||||||
|
|
||||||
|
const { stop } = useIntersectionObserver(
|
||||||
|
section,
|
||||||
|
([{ isIntersecting }]) => {
|
||||||
|
if (!isIntersecting) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
refs.value.forEach(element => element.style.animationPlayState = 'running')
|
||||||
|
|
||||||
|
stop()
|
||||||
|
},
|
||||||
|
{ threshold: 0.3 }
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
refs.value.forEach((element) => {
|
||||||
|
if (!element) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
element.style.animationFillMode = 'forwards'
|
||||||
|
element.style.transformOrigin = 'center'
|
||||||
|
element.style.animationPlayState = 'paused'
|
||||||
|
element.style.animationDuration = '1s'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Transition appear name="fade">
|
<ULandingGrid ref="section" class="lg:grid-cols-10 lg:gap-8">
|
||||||
<ULandingGrid class="lg:grid-cols-10 lg:gap-8">
|
<div :ref="(el) => (refs[1] = el)" class="col-span-8 flex items-center animate-top">
|
||||||
<div class="col-span-8 flex items-center">
|
<RangeExample />
|
||||||
<RangeExample />
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-span-2 row-span-2 flex items-center">
|
<div :ref="(el) => (refs[2] = el)" class="col-span-2 row-span-2 flex items-center animate-right">
|
||||||
<RadioExample />
|
<RadioExample />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-span-2">
|
<div :ref="(el) => (refs[4] = el)" class="col-span-2 animate-left z-10">
|
||||||
<DropdownExampleBasic :popper="{ placement: 'bottom-start', strategy: 'absolute' }" />
|
<DropdownExampleBasic :popper="{ placement: 'bottom-start', strategy: 'absolute' }" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-span-6 flex flex-wrap items-center justify-between gap-1">
|
<div
|
||||||
<UAvatarGroup :max="2">
|
:ref="(el) => (refs[3] = el)"
|
||||||
<UAvatar
|
class="col-span-6 flex flex-wrap items-center justify-between gap-1 animate-bottom"
|
||||||
src="https://ipx.nuxt.com/s_32x32/gh_avatar/benjamincanac"
|
>
|
||||||
srcset="https://ipx.nuxt.com/s_64x64/gh_avatar/benjamincanac 2x"
|
<UAvatarGroup :max="2">
|
||||||
alt="benjamincanac"
|
<UAvatar
|
||||||
/>
|
src="https://ipx.nuxt.com/s_32x32/gh_avatar/benjamincanac"
|
||||||
<UAvatar
|
srcset="https://ipx.nuxt.com/s_64x64/gh_avatar/benjamincanac 2x"
|
||||||
src="https://ipx.nuxt.com/s_32x32/gh_avatar/Atinux"
|
alt="benjamincanac"
|
||||||
srcset="https://ipx.nuxt.com/s_64x64/gh_avatar/Atinux 2x"
|
width="40"
|
||||||
alt="Atinux"
|
height="40"
|
||||||
/>
|
loading="lazy"
|
||||||
<UAvatar
|
/>
|
||||||
src="https://ipx.nuxt.com/s_32x32/gh_avatar/smarroufin"
|
<UAvatar
|
||||||
srcset="https://ipx.nuxt.com/s_64x64/gh_avatar/smarroufin 2x"
|
src="https://ipx.nuxt.com/s_32x32/gh_avatar/Atinux"
|
||||||
alt="smarroufin"
|
srcset="https://ipx.nuxt.com/s_64x64/gh_avatar/Atinux 2x"
|
||||||
/>
|
alt="Atinux"
|
||||||
</UAvatarGroup>
|
width="40"
|
||||||
|
height="40"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
<UAvatar
|
||||||
|
src="https://ipx.nuxt.com/s_32x32/gh_avatar/smarroufin"
|
||||||
|
srcset="https://ipx.nuxt.com/s_64x64/gh_avatar/smarroufin 2x"
|
||||||
|
alt="smarroufin"
|
||||||
|
width="40"
|
||||||
|
height="40"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
</UAvatarGroup>
|
||||||
|
|
||||||
<UButton label="Button" icon="i-heroicons-pencil-square" />
|
<UButton label="Button" icon="i-heroicons-pencil-square" />
|
||||||
|
|
||||||
<UBadge label="Badge" />
|
<UBadge label="Badge" />
|
||||||
|
|
||||||
<UColorModeToggle />
|
<UColorModeToggle />
|
||||||
|
|
||||||
<PaginationExampleBasic />
|
<PaginationExampleBasic />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-span-3 row-span-8 gap-6 flex flex-col justify-between">
|
<div :ref="(el) => (refs[5] = el)" class="col-span-3 row-span-8 gap-6 flex flex-col justify-between animate-left">
|
||||||
<UNotification :id="1" title="Notification" description="This is a notification!" icon="i-heroicons-command-line" />
|
<UNotification
|
||||||
|
:id="1"
|
||||||
|
title="Notification"
|
||||||
|
description="This is a notification!"
|
||||||
|
icon="i-heroicons-command-line"
|
||||||
|
:ui="{ shadow: 'shadow' }"
|
||||||
|
:close-button="null"
|
||||||
|
:timeout="30000"
|
||||||
|
/>
|
||||||
|
|
||||||
<TabsExampleItemCustomSlot />
|
<TabsExampleItemCustomSlot />
|
||||||
|
|
||||||
<UCard class="flex-shrink-0">
|
<UCard class="flex-shrink-0">
|
||||||
<div class="flex items-center gap-4 justify-center">
|
<div class="flex items-center gap-4 justify-center">
|
||||||
<USkeleton class="h-14 w-14 flex-shrink-0" :ui="{ rounded: 'rounded-full' }" />
|
<USkeleton class="h-14 w-14 flex-shrink-0" :ui="{ rounded: 'rounded-full' }" />
|
||||||
<div class="space-y-3 flex-1">
|
<div class="space-y-3 flex-1">
|
||||||
<USkeleton class="h-4 w-full" />
|
<USkeleton class="h-4 w-full" />
|
||||||
<USkeleton class="h-4 w-2/3" />
|
<USkeleton class="h-4 w-2/3" />
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</UCard>
|
</div>
|
||||||
</div>
|
</UCard>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-span-5 row-span-2 flex flex-col">
|
<div :ref="(el) => (refs[6] = el)" class="col-span-5 row-span-2 flex flex-col animate-bottom">
|
||||||
<UCard :ui="{ body: { base: 'flex-1 flex flex-col overflow-y-auto', padding: '' } }" class="col-span-4 row-span-6 flex-1 flex flex-col">
|
<UCard
|
||||||
<CommandPaletteExampleGroups />
|
:ui="{ body: { base: 'flex-1 flex flex-col overflow-y-auto', padding: '' } }"
|
||||||
</UCard>
|
class="col-span-4 row-span-6 flex-1 flex flex-col"
|
||||||
</div>
|
>
|
||||||
|
<CommandPaletteExampleGroups />
|
||||||
|
</UCard>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-span-2 row-span-2 gap-6 flex flex-col">
|
<div :ref="(el) => (refs[7] = el)" class="col-span-2 row-span-2 gap-6 flex flex-col animate-right z-10">
|
||||||
<CheckboxExample />
|
<CheckboxExample />
|
||||||
|
|
||||||
<InputExampleClearable />
|
<InputExampleClearable />
|
||||||
|
|
||||||
<UFormGroup label="Labels">
|
<UFormGroup label="Labels">
|
||||||
<SelectMenuExampleCreatable />
|
<SelectMenuExampleCreatable />
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UCard :ui="{ body: { padding: '!p-1' } }">
|
<UCard :ui="{ body: { padding: '!p-1' } }">
|
||||||
<VerticalNavigationExampleAvatarSlot />
|
<VerticalNavigationExampleAvatarSlot />
|
||||||
</UCard>
|
</UCard>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-span-7 row-span-6">
|
<div :ref="(el) => (refs[8] = el)" class="col-span-7 row-span-6 animate-bottom">
|
||||||
<UCard :ui="{ body: { padding: '' } }">
|
<UCard :ui="{ body: { padding: '' } }">
|
||||||
<TableExampleClickable :ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }" />
|
<TableExampleClickable :ui="{ divide: 'divide-gray-200 dark:divide-gray-800' }" />
|
||||||
</UCard>
|
</UCard>
|
||||||
</div>
|
</div>
|
||||||
</ULandingGrid>
|
</ULandingGrid>
|
||||||
</Transition>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped lang="postcss">
|
||||||
.fade-enter-active,
|
.animate-top {
|
||||||
.fade-leave-active {
|
animation: translateDown;
|
||||||
transition: opacity 0.5s ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-enter-from,
|
.animate-bottom {
|
||||||
.fade-leave-to {
|
animation: translateUp;
|
||||||
opacity: 0;
|
}
|
||||||
|
|
||||||
|
.animate-left {
|
||||||
|
animation: translateLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-right {
|
||||||
|
animation-name: translateRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes translateDown {
|
||||||
|
0% {
|
||||||
|
transform: translate3D(0, -100px, 0);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateY(0, 0, 0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes translateUp {
|
||||||
|
0% {
|
||||||
|
transform: translate3D(0, 100px, 0);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateY(0, 0, 0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes translateLeft {
|
||||||
|
0% {
|
||||||
|
transform: translate3D(-100px, 0, 0);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translate3D(0, 0, 0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes translateRight {
|
||||||
|
0% {
|
||||||
|
transform: translate3D(100px, 0, 0);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translate3D(0, 0, 0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -8,13 +8,19 @@
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
ref="el"
|
ref="el"
|
||||||
class="absolute inset-0 grid justify-center auto-rows-[var(--cell)] -space-y-px"
|
class="absolute inset-0 grid justify-center auto-rows-[--cell] -space-y-px"
|
||||||
>
|
>
|
||||||
<div v-for="(row, rowIndex) in grid" :key="rowIndex" class="grid grid-flow-col auto-cols-[--cell] flex-1 -space-x-px">
|
<div v-for="(row, rowIndex) in grid" :key="rowIndex" class="grid grid-flow-col auto-cols-[--cell] flex-1 -space-x-px">
|
||||||
<div v-for="(cell, cellIndex) in row" :key="cellIndex" class="transition-[background] duration-1000 border border-primary-200/50 dark:border-primary-900/25 bg-opacity-10 hover:bg-opacity-20 dark:bg-opacity-5 dark:hover:bg-opacity-10" :class="[cell && `bg-primary-500 dark:bg-primary-400 cursor-pointer`]" @click="removeCell(rowIndex, cellIndex)" />
|
<div
|
||||||
|
v-for="(cell, cellIndex) in row"
|
||||||
|
:key="cellIndex"
|
||||||
|
class="relative border border-primary-200/50 dark:border-primary-900/25"
|
||||||
|
>
|
||||||
|
<div class="absolute inset-0 bg-primary-500/10 hover:bg-primary-500/20 dark:bg-primary-400/5 dark:hover:bg-primary-400/10 opacity-0 transition-opacity will-change-[opacity] duration-1000" :class="[cell && 'opacity-100 cursor-pointer']" @click="cell && removeCell(rowIndex, cellIndex)" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="absolute top-[calc((var(--cell)*var(--rows))+1px)] inset-x-0 h-[calc(var(--cell)*2)] bg-gradient-to-t from-white dark:from-gray-900" />
|
<div class="absolute top-[calc((var(--cell)*var(--rows))+1px)] inset-x-0 h-[calc(var(--cell)*2)] bg-gradient-to-t from-white dark:from-gray-900 pointer-events-none" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
@@ -28,13 +34,8 @@ const grid = ref([])
|
|||||||
const rows = ref(0)
|
const rows = ref(0)
|
||||||
const cols = ref(0)
|
const cols = ref(0)
|
||||||
|
|
||||||
const colors = useAppConfig()?.ui.colors
|
|
||||||
const { width, height } = useElementSize(el)
|
const { width, height } = useElementSize(el)
|
||||||
|
|
||||||
function getRandomColor () {
|
|
||||||
return colors[Math.floor(Math.random() * (colors.length - 1))]
|
|
||||||
}
|
|
||||||
|
|
||||||
function createGrid () {
|
function createGrid () {
|
||||||
grid.value = []
|
grid.value = []
|
||||||
|
|
||||||
@@ -44,10 +45,9 @@ function createGrid () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createNewCell () {
|
function createNewCell () {
|
||||||
const color = getRandomColor()
|
|
||||||
const x = Math.floor(Math.random() * cols.value)
|
const x = Math.floor(Math.random() * cols.value)
|
||||||
|
|
||||||
grid.value[0][x] = color
|
grid.value[0][x] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveCellsDown () {
|
function moveCellsDown () {
|
||||||
|
|||||||
@@ -1,57 +1,43 @@
|
|||||||
import type { NavItem, ParsedContent } from '@nuxt/content/dist/runtime/types'
|
|
||||||
|
|
||||||
export const useContentSource = () => {
|
export const useContentSource = () => {
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
const config = useRuntimeConfig().public
|
const config = useRuntimeConfig().public
|
||||||
|
|
||||||
const branches = [{
|
const branches = computed(() => [{
|
||||||
|
id: 'dev',
|
||||||
name: 'dev',
|
name: 'dev',
|
||||||
icon: 'i-heroicons-cube',
|
icon: 'i-heroicons-cube',
|
||||||
suffix: 'dev',
|
suffix: 'dev',
|
||||||
label: 'Edge'
|
label: 'Edge',
|
||||||
|
disabled: route.path.startsWith('/dev'),
|
||||||
|
click: () => select({ name: 'dev' })
|
||||||
}, {
|
}, {
|
||||||
|
id: 'main',
|
||||||
name: 'main',
|
name: 'main',
|
||||||
icon: 'i-heroicons-cube',
|
icon: 'i-heroicons-cube',
|
||||||
suffix: 'latest',
|
suffix: 'latest',
|
||||||
label: `v${config.version}`
|
label: `v${config.version}`,
|
||||||
}]
|
disabled: !route.path.startsWith('/dev'),
|
||||||
|
click: () => select({ name: 'main' })
|
||||||
|
}])
|
||||||
|
|
||||||
const branch = computed(() => branches.find(b => b.name === (route.path.startsWith('/dev') ? 'dev' : 'main')))
|
const branch = computed(() => branches.value.find(b => b.name === (route.path.startsWith('/dev') ? 'dev' : 'main')))
|
||||||
|
|
||||||
const prefix = computed(() => `/${branch.value.name}`)
|
function select (branch) {
|
||||||
|
if (branch.name === 'dev') {
|
||||||
function removePrefixFromNavigation (navigation: NavItem[]): NavItem[] {
|
if (route.path.startsWith('/dev')) {
|
||||||
return navigation.map((link) => {
|
|
||||||
const { _path, children, ...rest } = link
|
|
||||||
|
|
||||||
return {
|
|
||||||
...rest,
|
|
||||||
_path: route.path.startsWith(prefix.value) ? _path : _path.replace(new RegExp(`^${prefix.value}`, 'g'), ''),
|
|
||||||
children: children?.length ? removePrefixFromNavigation(children) : undefined
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function removePrefixFromFiles (files: ParsedContent[]) {
|
|
||||||
return files.map((file) => {
|
|
||||||
if (!file) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const { _path, ...rest } = file
|
router.push(`/dev${route.path}`)
|
||||||
|
} else {
|
||||||
return {
|
router.push(route.path.replace('/dev', ''))
|
||||||
...rest,
|
}
|
||||||
_path: route.path.startsWith(prefix.value) ? _path : _path.replace(new RegExp(`^${prefix.value}`, 'g'), '')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
branches,
|
branches,
|
||||||
branch,
|
branch,
|
||||||
prefix,
|
select
|
||||||
removePrefixFromNavigation,
|
|
||||||
removePrefixFromFiles
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ This module has been developed by the [NuxtLabs](https://nuxtlabs.com/) team for
|
|||||||
- Keyboard shortcuts
|
- Keyboard shortcuts
|
||||||
- Bundled icons
|
- Bundled icons
|
||||||
- Fully typed
|
- Fully typed
|
||||||
|
- [Figma Kit](https://www.figma.com/community/file/1288455405058138934)
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
|
|||||||
@@ -9,15 +9,15 @@ description: 'Learn how to install and configure the module in your Nuxt app.'
|
|||||||
::code-group
|
::code-group
|
||||||
|
|
||||||
```sh [pnpm]
|
```sh [pnpm]
|
||||||
pnpm i -D @nuxt/ui
|
pnpm add @nuxt/ui
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash [yarn]
|
```bash [yarn]
|
||||||
yarn add -D @nuxt/ui
|
yarn add @nuxt/ui
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash [npm]
|
```bash [npm]
|
||||||
npm install -D @nuxt/ui
|
npm install @nuxt/ui
|
||||||
```
|
```
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
description: 'Learn how to customize the look and feel of the components.'
|
description: 'Learn how to customize the look and feel of the components.'
|
||||||
navigation:
|
navigation:
|
||||||
badge: 'New'
|
badge: New
|
||||||
---
|
---
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
@@ -39,7 +39,7 @@ Likewise, you can't define a `primary` color in your `tailwind.config.ts` as it
|
|||||||
We'd advise you to use those colors in your components and pages, e.g. `text-primary-500 dark:text-primary-400`, `bg-gray-100 dark:bg-gray-900`, etc. so your app automatically adapts when changing your `app.config.ts`.
|
We'd advise you to use those colors in your components and pages, e.g. `text-primary-500 dark:text-primary-400`, `bg-gray-100 dark:bg-gray-900`, etc. so your app automatically adapts when changing your `app.config.ts`.
|
||||||
::
|
::
|
||||||
|
|
||||||
The `primary` color also has a `DEFAULT` shade that changes based on the theme. It is `500` in light mode and `400` in dark mode. You can use as a shortcut in your components and pages, e.g. `text-primary`, `bg-primary`, `focus-visible:ring-primary`, etc. :u-badge{label="New" class="!rounded-full" variant="subtle"}
|
The `primary` color also has a `DEFAULT` shade that changes based on the theme. It is `500` in light mode and `400` in dark mode. You can use as a shortcut in your components and pages, e.g. `text-primary`, `bg-primary`, `focus-visible:ring-primary`, etc.
|
||||||
|
|
||||||
### Smart Safelisting
|
### Smart Safelisting
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ This can also happen when you bind a dynamic color to a component: `<UBadge :col
|
|||||||
|
|
||||||
### `app.config.ts`
|
### `app.config.ts`
|
||||||
|
|
||||||
Components are styled with Tailwind CSS but classes are all defined in the default [app.config.ts](https://github.com/nuxt/ui/blob/dev/src/runtime/app.config.ts) file. You can override those in your own `app.config.ts`.
|
Components are styled with Tailwind CSS but classes are all defined in the default [ui.config.ts](https://github.com/nuxt/ui/blob/dev/src/runtime/ui.config.ts) file. You can override those in your own `app.config.ts`.
|
||||||
|
|
||||||
```ts [app.config.ts]
|
```ts [app.config.ts]
|
||||||
export default defineAppConfig({
|
export default defineAppConfig({
|
||||||
@@ -93,6 +93,25 @@ export default defineAppConfig({
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
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 behaviour by setting `strategy` to `override` in your `app.config.ts`: :u-badge{label="New" class="!rounded-full" variant="subtle"}
|
||||||
|
|
||||||
|
```ts [app.config.ts]
|
||||||
|
export default defineAppConfig({
|
||||||
|
ui: {
|
||||||
|
strategy: 'override',
|
||||||
|
button: {
|
||||||
|
color: {
|
||||||
|
white: {
|
||||||
|
solid: 'bg-white dark:bg-gray-900'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
### `ui` prop
|
### `ui` prop
|
||||||
|
|
||||||
Each component has a `ui` prop that allows you to customize everything specifically.
|
Each component has a `ui` prop that allows you to customize everything specifically.
|
||||||
@@ -109,31 +128,42 @@ Each component has a `ui` prop that allows you to customize everything specifica
|
|||||||
You can find the default classes for each component under the `Preset` section.
|
You can find the default classes for each component under the `Preset` section.
|
||||||
::
|
::
|
||||||
|
|
||||||
Thanks to [tailwind-merge](https://github.com/dcastil/tailwind-merge), the `ui` prop is smartly merged with the config. This means you don't have to rewrite everything. :u-badge{label="New" class="!rounded-full" variant="subtle"}
|
Thanks to [tailwind-merge](https://github.com/dcastil/tailwind-merge), the `ui` prop is smartly merged with the config. This means you don't have to rewrite everything.
|
||||||
|
|
||||||
For example, the default preset of the `FormGroup` component looks like this:
|
For example, the default preset of the `FormGroup` component looks like this:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
...
|
|
||||||
"label": {
|
"label": {
|
||||||
"base": "block font-medium text-gray-700 dark:text-gray-200",
|
"base": "block font-medium text-gray-700 dark:text-gray-200"
|
||||||
...
|
|
||||||
}
|
}
|
||||||
...
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
To change the font of the `label`, you only need to write:
|
To change the font of the `label`, you only need to write:
|
||||||
|
|
||||||
```vue
|
```vue
|
||||||
<UFormGroup name="email" label="Email" :ui="{ label: { base: 'font-semibold' } }">
|
<UFormGroup name="email" label="Email" :ui="{ label: { base: 'font-semibold' } }" />
|
||||||
...
|
|
||||||
</UFormGroup>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This will smartly replace the `font-medium` by `font-semibold` and prevent any class duplication and any class priority issue.
|
This will smartly replace the `font-medium` by `font-semibold` and prevent any class duplication and any class priority issue.
|
||||||
|
|
||||||
|
You can change this behaviour by setting `strategy` to `override` inside the `ui` prop: :u-badge{label="New" class="!rounded-full" variant="subtle"}
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<UButton
|
||||||
|
to="https://github.com/nuxt/ui"
|
||||||
|
:ui="{
|
||||||
|
strategy: 'override',
|
||||||
|
color: {
|
||||||
|
white: {
|
||||||
|
solid: 'bg-white dark:bg-gray-900'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
### `class` attribute
|
### `class` attribute
|
||||||
|
|
||||||
You can also use the `class` attribute to add classes to the component.
|
You can also use the `class` attribute to add classes to the component.
|
||||||
@@ -144,7 +174,7 @@ You can also use the `class` attribute to add classes to the component.
|
|||||||
</template>
|
</template>
|
||||||
```
|
```
|
||||||
|
|
||||||
Again, with [tailwind-merge](https://github.com/dcastil/tailwind-merge), this will smartly merge the classes with the `ui` prop and the config. :u-badge{label="New" class="!rounded-full" variant="subtle"}
|
Again, with [tailwind-merge](https://github.com/dcastil/tailwind-merge), this will smartly merge the classes with the `ui` prop and the config.
|
||||||
|
|
||||||
### Default values
|
### Default values
|
||||||
|
|
||||||
@@ -229,15 +259,15 @@ However, you will need to install either `@iconify/json` (full icon collections,
|
|||||||
::code-group
|
::code-group
|
||||||
|
|
||||||
```bash [yarn]
|
```bash [yarn]
|
||||||
yarn add -D @iconify-json/{collection_name}
|
yarn add @iconify-json/{collection_name}
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash [npm]
|
```bash [npm]
|
||||||
npm install -D @iconify-json/{collection_name}
|
npm install @iconify-json/{collection_name}
|
||||||
```
|
```
|
||||||
|
|
||||||
```sh [pnpm]
|
```sh [pnpm]
|
||||||
pnpm i -D @iconify-json/{collection_name}
|
pnpm i @iconify-json/{collection_name}
|
||||||
```
|
```
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|||||||
188
docs/content/1.getting-started/6.contributing.md
Normal file
188
docs/content/1.getting-started/6.contributing.md
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
---
|
||||||
|
title: Contributing
|
||||||
|
description: Learn how to contribute to Nuxt UI.
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Nuxt UI thrives thanks to its fantastic community ❤️, which contributes by submitting issues, creating pull requests, and offering valuable feedback.
|
||||||
|
|
||||||
|
Before reporting a bug or reporting a feature, please make sure that you have read through our documentation and existing [issues](https://github.com/nuxt/ui/issues).
|
||||||
|
|
||||||
|
## Submitting a Pull Request
|
||||||
|
|
||||||
|
### 1. Before You Start
|
||||||
|
|
||||||
|
Check if there's an existing issue describing the problem or feature request you're working on. If there is, please leave a comment on the issue to let us know you're working on it.
|
||||||
|
|
||||||
|
If there isn't, open a new issue to discuss the problem or feature.
|
||||||
|
|
||||||
|
### 2. Local Development Setup
|
||||||
|
|
||||||
|
To begin local development, follow these steps:
|
||||||
|
|
||||||
|
1. Clone the `nuxt/ui` repository to your local machine:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/nuxt/ui.git
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Install dependencies and prepare the project:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pnpm install
|
||||||
|
pnpm run dev:prepare
|
||||||
|
```
|
||||||
|
|
||||||
|
3. To configure your local development environment, use the following commands:
|
||||||
|
|
||||||
|
- To work on the **documentation** in the `docs` folder, run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pnpm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
- To test the components located in the `playground` folder within `app.vue`, run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pnpm run play
|
||||||
|
```
|
||||||
|
|
||||||
|
#### IDE Setup
|
||||||
|
|
||||||
|
We recommend using VS Code along with the ESLint extension. You can enable auto-fix and formatting when saving your code. Here's how:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll": false,
|
||||||
|
"source.fixAll.eslint": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also use the `lint` command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pnpm run lint # check for linting errors
|
||||||
|
pnpm run lint:fix # fix linting errors
|
||||||
|
```
|
||||||
|
|
||||||
|
#### No Prettier
|
||||||
|
|
||||||
|
Since ESLint is already configured to format the code, there's no need for duplicating functionality with Prettier.
|
||||||
|
|
||||||
|
If you have Prettier installed in your editor, we recommend disabling it to avoid conflicts.
|
||||||
|
|
||||||
|
#### Type Checking
|
||||||
|
|
||||||
|
We use TypeScript for type checking. You can use the `typecheck` command to check for type errors:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pnpm run typecheck
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Commit Conventions
|
||||||
|
|
||||||
|
Use Conventional Commits for commit messages with the following format:
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>(optional scope): <description>
|
||||||
|
|
||||||
|
[optional body]
|
||||||
|
|
||||||
|
[optional footer(s)]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Types
|
||||||
|
|
||||||
|
- `feat` : for new features.
|
||||||
|
- `fix` : for bug fixes.
|
||||||
|
- `refactor` : for code changes that are neither bug fixes nor new features.
|
||||||
|
- `perf` : for code refactoring that improves performance.
|
||||||
|
- `test` : for code related to automatic testing.
|
||||||
|
- `style` : for refactoring related to code style (not for CSS).
|
||||||
|
- `docs` : for changes related to documentation.
|
||||||
|
- `chore` : for anything else.
|
||||||
|
|
||||||
|
#### Scope
|
||||||
|
|
||||||
|
Where the change occurred (e.g., `Table`, `Alert`, `Accordion`, etc.).
|
||||||
|
|
||||||
|
#### Description
|
||||||
|
|
||||||
|
A summary of the changes made.
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
feat(Alert): new component
|
||||||
|
chore(Table): improve accessibility
|
||||||
|
docs: migrate to @nuxt/ui-pro
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Making the Pull Request
|
||||||
|
|
||||||
|
- Follow the guide for creating a pull request and ensure your PR's title adheres to the Commit Convention. Mention any related issues in the PR description.
|
||||||
|
|
||||||
|
- Multiple commits are fine; no need to rebase or force push. We'll use `Squash and Merge` when merging.
|
||||||
|
|
||||||
|
- Ensure linting and make tests manually before submitting the PR. Avoid making unrelated changes.
|
||||||
|
|
||||||
|
### 5. After You've Made a Pull Request
|
||||||
|
|
||||||
|
We'll review it promptly. If assigned to a maintainer, they'll review it carefully. Ignore the red text; it's for tracking purposes.
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
In this project, you'll find a variety of folders and files that serve different purposes. Here's an overview of the main ones:
|
||||||
|
|
||||||
|
- **Documentation - `docs`** :
|
||||||
|
|
||||||
|
The documentation is located in the `docs` folder. It's a Nuxt app that uses the `@nuxt/content` module to generate the documentation pages from Markdown files. Here's a breakdown of its structure:
|
||||||
|
|
||||||
|
```
|
||||||
|
docs/
|
||||||
|
├── components/
|
||||||
|
│ ├── examples/ # Components used in documentation as examples
|
||||||
|
│ └── themes/ # Components used in the examples page in the theming section
|
||||||
|
├── content/ # Documentation, separated into categories according to component types
|
||||||
|
│ ├── 1.getting-started/
|
||||||
|
│ │ ├── 1.index.md
|
||||||
|
│ │ ├── 2.installation.md
|
||||||
|
│ │ ├── ... etc
|
||||||
|
│ ├── 2.elements/ # The category of components, which are elements
|
||||||
|
│ │ ├── 1.accordion.md # Docs for a single component (i.e., accordion)
|
||||||
|
│ │ ├── 2.alert.md
|
||||||
|
│ │ ├── ... etc
|
||||||
|
└── ... etc
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Components - `src`** :
|
||||||
|
|
||||||
|
The components are located in the `src` folder. It's separated into categories according to component types. Here's a breakdown of its structure:
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── runtime/
|
||||||
|
│ ├── composables/ # Composable functions used in components
|
||||||
|
│ ├── components/ # Components folder, separated into categories according to component types
|
||||||
|
│ │ ├── data/ # The category of components, which are data related
|
||||||
|
│ │ │ ├── table.vue/ # Table component
|
||||||
|
│ │ │ ├── elements/ # Elements category
|
||||||
|
│ │ │ │ ├── ...etc/
|
||||||
|
│ │ │ └── ... etc/
|
||||||
|
│ │ ├── plugins/ # Plugins used in components
|
||||||
|
│ │ ├── utils/ # Utility functions used on the components page (e.g., lodash)
|
||||||
|
│ │ ├── types/ # Types used in components
|
||||||
|
│ │ │ ├── accordion.d.ts/ # [componentName].d.ts type used for single component
|
||||||
|
│ │ │ ├── avatar.d.ts/
|
||||||
|
│ │ │ └── ... etc/
|
||||||
|
│ │ ├── ui.config.ts/ # Configuration file used to apply styles to every component
|
||||||
|
├── colors.ts/ # Everything related to color functions (e.g., safelistByComponent, generateSafelist)
|
||||||
|
└── ... etc/ # Other files and folders
|
||||||
|
```
|
||||||
|
|
||||||
|
## Thanks
|
||||||
|
|
||||||
|
Thank you again for being interested in this project! You are awesome! ❤️
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
title: Roadmap
|
|
||||||
description: Discover our Volta board for @nuxt/ui development status.
|
|
||||||
toc: false
|
|
||||||
---
|
|
||||||
|
|
||||||
:volta-embed{token="eyJ2aWV3IjoiYm9hcmQiLCJib2FyZFN0YXR1c2VzIjpbInRyaWFnZSIsImJhY2tsb2ciLCJ0b2RvIiwiaW5fcHJvZ3Jlc3MiLCJpbl9yZXZpZXciLCJkb25lIiwicmVsZWFzZWQiXSwiYm9hcmRMaW5rZWRQcnMiOnRydWUsImxpc3RHcm91cCI6InN0YXRlIiwibGlzdE9yZGVyIjoiY3JlYXRlZF9hdCIsInRpbWVsaW5lWm9vbSI6Im1vbnRoIiwidGltZWxpbmVPcmRlciI6InN0YXRlIiwidGltZWxpbmVEaXNwbGF5IjoiYWxsX21pbGVzdG9uZXMiLCJmaWx0ZXJzIjp7fSwib3duZXIiOiJudXh0bGFicyIsIm5hbWUiOiJ1aSJ9"}
|
|
||||||
7
docs/content/1.getting-started/7.roadmap.md
Normal file
7
docs/content/1.getting-started/7.roadmap.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
title: Roadmap
|
||||||
|
description: Discover our Volta board for @nuxt/ui development status.
|
||||||
|
toc: false
|
||||||
|
---
|
||||||
|
|
||||||
|
:volta-embed{token="eyJ2aWV3IjoiYm9hcmQiLCJib2FyZFN0YXR1c2VzIjpbInRyaWFnZSIsImJhY2tsb2ciLCJ0b2RvIiwiaW5fcHJvZ3Jlc3MiLCJpbl9yZXZpZXciLCJkb25lIiwicmVsZWFzZWQiLCJjYW5jZWxsZWQiXSwiYm9hcmRMaW5rZWRQcnMiOnRydWUsImxpc3RHcm91cCI6InN0YXR1cyIsImxpc3RPcmRlciI6ImNyZWF0ZWRfYXQiLCJ0aW1lbGluZVpvb20iOiJtb250aCIsInRpbWVsaW5lT3JkZXIiOiJzdGF0ZSIsInRpbWVsaW5lRGlzcGxheSI6ImFsbF9taWxlc3RvbmVzIiwiZmlsdGVycyI6e30sIm93bmVyIjoibnV4dCIsIm5hbWUiOiJ1aSJ9"}
|
||||||
@@ -263,8 +263,8 @@ const items = [{
|
|||||||
|
|
||||||
<div class="flex flex-col items-center">
|
<div class="flex flex-col items-center">
|
||||||
<code>$ npm install @nuxt/ui</code>
|
<code>$ npm install @nuxt/ui</code>
|
||||||
<code>$ nnpm install -D @nuxt/ui</code>
|
<code>$ yarn add @nuxt/ui</code>
|
||||||
<code>$ pnpm i -D @nuxt/ui</code>
|
<code>$ pnpm add @nuxt/ui</code>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</UAccordion>
|
</UAccordion>
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ links:
|
|||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/elements/Avatar.vue
|
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/elements/Avatar.vue
|
||||||
navigation:
|
|
||||||
badge: 'New'
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@@ -55,7 +53,7 @@ baseProps:
|
|||||||
|
|
||||||
If there is an error loading the `src` of the avatar or `src` is null / false a background placeholder will be displayed, customizable in `ui.avatar.background`.
|
If there is an error loading the `src` of the avatar or `src` is null / false a background placeholder will be displayed, customizable in `ui.avatar.background`.
|
||||||
|
|
||||||
#### Icon :u-badge{label="New" class="ml-2 align-text-bottom !rounded-full" variant="subtle"}
|
#### Icon
|
||||||
|
|
||||||
Use any icon from [Iconify](https://icones.js.org) by setting the `icon` prop by using this pattern: `i-{collection_name}-{icon_name}` or change it globally in `ui.avatar.default.icon` to display an icon on top of the background.
|
Use any icon from [Iconify](https://icones.js.org) by setting the `icon` prop by using this pattern: `i-{collection_name}-{icon_name}` or change it globally in `ui.avatar.default.icon` to display an icon on top of the background.
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ links:
|
|||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/elements/Button.vue
|
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/elements/Button.vue
|
||||||
navigation:
|
|
||||||
badge: 'New'
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@@ -278,7 +276,7 @@ excludedProps:
|
|||||||
To stack buttons as a group, use the `ButtonGroup` component.
|
To stack buttons as a group, use the `ButtonGroup` component.
|
||||||
|
|
||||||
- To size all the buttons equally, pass the `size` prop
|
- To size all the buttons equally, pass the `size` prop
|
||||||
- To change the orientation of the buttons, set the `orientation` prop to `vertical` :u-badge{label="New" class="!rounded-full" variant="subtle"}
|
- To change the orientation of the buttons, set the `orientation` prop to `vertical`
|
||||||
- To adjust the rounded or the shadow around buttons, customize with `ui.buttonGroup.rounded` or `ui.buttonGroup.shadow`
|
- To adjust the rounded or the shadow around buttons, customize with `ui.buttonGroup.rounded` or `ui.buttonGroup.shadow`
|
||||||
|
|
||||||
::component-card{slug="ButtonGroup"}
|
::component-card{slug="ButtonGroup"}
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ The Link component is a wrapper around [`<NuxtLink>`](https://nuxt.com/docs/api/
|
|||||||
|
|
||||||
The incentive behind this is to provide the same API as NuxtLink back in Nuxt 2 / Vue 2. You can read more about it in the Vue Router [migration from Vue 2](https://router.vuejs.org/guide/migration/#removal-of-the-exact-prop-in-router-link) guide.
|
The incentive behind this is to provide the same API as NuxtLink back in Nuxt 2 / Vue 2. You can read more about it in the Vue Router [migration from Vue 2](https://router.vuejs.org/guide/migration/#removal-of-the-exact-prop-in-router-link) guide.
|
||||||
|
|
||||||
It also renders an `<a>` tag when a `to` prop is provided, otherwise it renders a `<button>` tag.
|
It also renders an `<a>` tag when a `to` prop is provided, otherwise it defaults to rendering a `<button>` tag. The default behavior can be customized using the `as` prop.
|
||||||
|
|
||||||
It is used underneath by the [Button](/elements/button), [Dropdown](/elements/dropdown) and [VerticalNavigation](/navigation/vertical-navigation) components.
|
It is used underneath by the [Button](/elements/button), [Dropdown](/elements/dropdown) and [VerticalNavigation](/navigation/vertical-navigation) components.
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ links:
|
|||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/Form.vue
|
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/Form.vue
|
||||||
navigation:
|
|
||||||
badge: 'New'
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@@ -234,7 +232,7 @@ async function submit (event: FormSubmitEvent<any>) {
|
|||||||
```
|
```
|
||||||
::
|
::
|
||||||
|
|
||||||
### Valibot :u-badge{label="New" class="ml-2 align-text-bottom !rounded-full" variant="subtle"}
|
### Valibot
|
||||||
|
|
||||||
::component-example
|
::component-example
|
||||||
#default
|
#default
|
||||||
|
|||||||
@@ -375,7 +375,7 @@ const selected = ref(people[3])
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<USelectMenu v-slot="{ open }" v-model="selected" :options="people">
|
<USelectMenu v-slot="{ open }" v-model="selected" :options="people">
|
||||||
<UButton color="gray">
|
<UButton color="gray" class="flex-1 justify-between">
|
||||||
{{ selected }}
|
{{ selected }}
|
||||||
|
|
||||||
<UIcon name="i-heroicons-chevron-right-20-solid" class="w-5 h-5 transition-transform" :class="[open && 'transform rotate-90']" />
|
<UIcon name="i-heroicons-chevron-right-20-solid" class="w-5 h-5 transition-transform" :class="[open && 'transform rotate-90']" />
|
||||||
|
|||||||
@@ -190,6 +190,115 @@ code: >-
|
|||||||
This will only work with form elements that support the `size` prop.
|
This will only work with form elements that support the `size` prop.
|
||||||
::
|
::
|
||||||
|
|
||||||
|
## Slots
|
||||||
|
|
||||||
|
### `label`
|
||||||
|
|
||||||
|
Use the `#label` slot to set the custom content for label.
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
slots:
|
||||||
|
label: <UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs" />
|
||||||
|
default: <UInput model-value="benjamincanac" placeholder="you@example.com" />
|
||||||
|
---
|
||||||
|
|
||||||
|
#label
|
||||||
|
:u-avatar{src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs" class="mr-2 inline-flex"}
|
||||||
|
|
||||||
|
#default
|
||||||
|
:u-input{model-value="benjamincanac" placeholder="you@example.com"}
|
||||||
|
::
|
||||||
|
|
||||||
|
### `description`
|
||||||
|
|
||||||
|
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" />
|
||||||
|
default: <UInput model-value="benjamincanac" placeholder="you@example.com" />
|
||||||
|
props:
|
||||||
|
label: 'Email'
|
||||||
|
---
|
||||||
|
|
||||||
|
#description
|
||||||
|
Write only valid email address :u-icon{name="i-heroicons-information-circle" class="align-middle"}
|
||||||
|
|
||||||
|
#default
|
||||||
|
:u-input{model-value="benjamincanac" placeholder="you@example.com"}
|
||||||
|
::
|
||||||
|
|
||||||
|
### `hint`
|
||||||
|
|
||||||
|
Use the `#hint` slot to set the custom content for hint.
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
slots:
|
||||||
|
hint: <UIcon name="i-heroicons-arrow-right-20-solid" />
|
||||||
|
default: <UInput model-value="benjamincanac" placeholder="you@example.com" />
|
||||||
|
props:
|
||||||
|
label: 'Step 1'
|
||||||
|
---
|
||||||
|
|
||||||
|
#hint
|
||||||
|
:u-icon{name="i-heroicons-arrow-right-20-solid"}
|
||||||
|
|
||||||
|
#default
|
||||||
|
:u-input{model-value="benjamincanac" placeholder="you@example.com"}
|
||||||
|
::
|
||||||
|
|
||||||
|
### `help`
|
||||||
|
|
||||||
|
Use the `#help` slot to set the custom content for help.
|
||||||
|
|
||||||
|
::component-card
|
||||||
|
---
|
||||||
|
slots:
|
||||||
|
help: Here are some examples <UIcon name="i-heroicons-arrow-right-20-solid" />
|
||||||
|
default: <UInput model-value="benjamincanac" placeholder="you@example.com" />
|
||||||
|
props:
|
||||||
|
label: 'Email'
|
||||||
|
---
|
||||||
|
|
||||||
|
#help
|
||||||
|
Here are some examples :u-icon{name="i-heroicons-information-circle" class="align-middle"}
|
||||||
|
|
||||||
|
#default
|
||||||
|
:u-input{model-value="benjamincanac" placeholder="you@example.com"}
|
||||||
|
::
|
||||||
|
|
||||||
|
### `error`
|
||||||
|
|
||||||
|
Use the `#error` slot to set the custom content for error.
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
#default
|
||||||
|
:form-group-error-slot-example{class="w-60"}
|
||||||
|
|
||||||
|
#code
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<UFormGroup label="Email" :error="!email && 'You must enter an email'" help="This is a nice email!">
|
||||||
|
<template #default="{ error }">
|
||||||
|
<UInput v-model="email" type="email" placeholder="Enter email" :trailing-icon="error ? 'i-heroicons-exclamation-triangle-20-solid' : undefined" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #error="{ error }">
|
||||||
|
<UAlert v-if="error" icon="i-heroicons-exclamation-triangle-20-solid" :title="error" color="red" />
|
||||||
|
<UAlert v-else icon="i-heroicons-check-circle-20-solid" title="Your email is valid" color="green" />
|
||||||
|
</template>
|
||||||
|
</UFormGroup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const email = ref('')
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
::
|
||||||
|
|
||||||
## Props
|
## Props
|
||||||
|
|
||||||
:component-props
|
:component-props
|
||||||
|
|||||||
@@ -524,6 +524,65 @@ excludedProps:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
## Styling
|
||||||
|
|
||||||
|
You can apply styles to `tr` and `td` elements by passing a `class` to rows.
|
||||||
|
|
||||||
|
Also, you can apply styles to `th` elements by passing a `class` to columns.
|
||||||
|
|
||||||
|
::component-example
|
||||||
|
---
|
||||||
|
padding: false
|
||||||
|
---
|
||||||
|
|
||||||
|
#default
|
||||||
|
:table-example-style{class="w-full"}
|
||||||
|
|
||||||
|
#code
|
||||||
|
```vue
|
||||||
|
<script setup>
|
||||||
|
const columns = [{
|
||||||
|
key: 'id',
|
||||||
|
label: '#'
|
||||||
|
}, {
|
||||||
|
key: 'quantity',
|
||||||
|
label: 'Quantity',
|
||||||
|
class: 'italic' // Apply style to column header
|
||||||
|
}, {
|
||||||
|
key: 'name',
|
||||||
|
label: 'Name'
|
||||||
|
}]
|
||||||
|
|
||||||
|
const items = [{
|
||||||
|
id: 1,
|
||||||
|
name: 'Apple',
|
||||||
|
quantity: { value: 100, class: 'bg-green-500/50 dark:bg-green-400/50' } // Apply style to td
|
||||||
|
}, {
|
||||||
|
id: 2,
|
||||||
|
name: 'Orange',
|
||||||
|
quantity: { value: 0 },
|
||||||
|
class: 'bg-red-500/50 dark:bg-red-400/50 animate-pulse' // Apply style to tr
|
||||||
|
}, {
|
||||||
|
id: 3,
|
||||||
|
name: 'Banana',
|
||||||
|
quantity: { value: 30, class: 'bg-green-500/50 dark:bg-green-400/50' }
|
||||||
|
}, {
|
||||||
|
id: 4,
|
||||||
|
name: 'Mango',
|
||||||
|
quantity: { value: 5, class: 'bg-green-500/50 dark:bg-green-400/50' }
|
||||||
|
}]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UTable :rows="items" :columns="columns">
|
||||||
|
<template #quantity-data="{ row }">
|
||||||
|
{{ row.quantity.value }}
|
||||||
|
</template>
|
||||||
|
</UTable>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
::
|
||||||
|
|
||||||
## Slots
|
## Slots
|
||||||
|
|
||||||
You can use slots to customize the header and data cells of the table.
|
You can use slots to customize the header and data cells of the table.
|
||||||
|
|||||||
@@ -125,8 +125,6 @@ const links = [{
|
|||||||
to: 'https://github.com/benjamincanac',
|
to: 'https://github.com/benjamincanac',
|
||||||
target: '_blank'
|
target: '_blank'
|
||||||
}, ...]
|
}, ...]
|
||||||
|
|
||||||
const { ui } = useAppConfig()
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -134,8 +132,8 @@ const { ui } = useAppConfig()
|
|||||||
<template #avatar="{ link }">
|
<template #avatar="{ link }">
|
||||||
<UAvatar
|
<UAvatar
|
||||||
v-if="link.avatar"
|
v-if="link.avatar"
|
||||||
v-bind="{ size: ui.verticalNavigation.avatar.size, ...link.avatar }"
|
v-bind="link.avatar"
|
||||||
:class="[ui.verticalNavigation.avatar.base]"
|
size="3xs"
|
||||||
/>
|
/>
|
||||||
<UIcon v-else name="i-heroicons-user-circle-20-solid" class="text-lg" />
|
<UIcon v-else name="i-heroicons-user-circle-20-solid" class="text-lg" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ links:
|
|||||||
- label: GitHub
|
- label: GitHub
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/navigation/Tabs.vue
|
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/navigation/Tabs.vue
|
||||||
navigation:
|
|
||||||
badge: 'New'
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@@ -87,7 +85,7 @@ const items = [...]
|
|||||||
This will have no effect if you are using a `v-model` to control the selected index.
|
This will have no effect if you are using a `v-model` to control the selected index.
|
||||||
::
|
::
|
||||||
|
|
||||||
### Listen to changes :u-badge{label="New" class="ml-2 align-text-bottom !rounded-full" variant="subtle"}
|
### Listen to changes
|
||||||
|
|
||||||
You can listen to changes by using the `@change` event. The event will emit the index of the selected item.
|
You can listen to changes by using the `@change` event. The event will emit the index of the selected item.
|
||||||
|
|
||||||
@@ -113,7 +111,7 @@ function onChange (index) {
|
|||||||
```
|
```
|
||||||
::
|
::
|
||||||
|
|
||||||
### Control the selected index :u-badge{label="New" class="ml-2 align-text-bottom !rounded-full" variant="subtle"}
|
### Control the selected index
|
||||||
|
|
||||||
Use a `v-model` to control the selected index.
|
Use a `v-model` to control the selected index.
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ links:
|
|||||||
- label: 'Dialog'
|
- label: 'Dialog'
|
||||||
icon: i-simple-icons-headlessui
|
icon: i-simple-icons-headlessui
|
||||||
to: 'https://headlessui.com/vue/dialog'
|
to: 'https://headlessui.com/vue/dialog'
|
||||||
navigation:
|
|
||||||
badge: 'New'
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@@ -180,7 +178,7 @@ defineShortcuts({
|
|||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Fullscreen :u-badge{label="New" class="ml-2 align-text-bottom !rounded-full" variant="subtle"}
|
### Fullscreen
|
||||||
|
|
||||||
Set the `fullscreen` prop to `true` to enable it.
|
Set the `fullscreen` prop to `true` to enable it.
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ hero:
|
|||||||
description: 'Nuxt UI simplifies the creation of stunning and responsive web applications with its<br class="hidden lg:block"> comprehensive collection of fully styled and customizable UI components designed for Nuxt.'
|
description: 'Nuxt UI simplifies the creation of stunning and responsive web applications with its<br class="hidden lg:block"> comprehensive collection of fully styled and customizable UI components designed for Nuxt.'
|
||||||
sections:
|
sections:
|
||||||
- slot: demo
|
- slot: demo
|
||||||
class: 'hidden lg:block dark:bg-gradient-to-b from-gray-900 to-gray-950/50 !pt-0'
|
class: 'hidden lg:block dark:bg-gradient-to-b from-gray-900 to-gray-950/50 !pt-12'
|
||||||
- title: Everything you expect from a<br class="hidden lg:block"> <span class="text-primary">UI component library</span>
|
- title: Everything you expect from a<br class="hidden lg:block"> <span class="text-primary">UI component library</span>
|
||||||
slot: features
|
slot: features
|
||||||
class: 'dark:bg-gradient-to-b from-gray-900 to-gray-950/50 dark:lg:bg-none dark:lg:bg-gray-950/50'
|
class: 'dark:bg-gradient-to-b from-gray-900 to-gray-950/50 dark:lg:bg-none dark:lg:bg-gray-950/50'
|
||||||
@@ -16,27 +16,39 @@ sections:
|
|||||||
icon: i-heroicons-swatch
|
icon: i-heroicons-swatch
|
||||||
to: /getting-started/theming#colors
|
to: /getting-started/theming#colors
|
||||||
class: 'col-span-7 row-span-3'
|
class: 'col-span-7 row-span-3'
|
||||||
image: /illustrations/color-palette
|
image:
|
||||||
|
path: /illustrations/color-palette
|
||||||
|
width: 363
|
||||||
|
height: 152
|
||||||
orientation: 'horizontal'
|
orientation: 'horizontal'
|
||||||
- title: Fully Customizable
|
- title: Fully Customizable
|
||||||
description: 'Change the style of any component in your App Config or customize them specifically through the ui prop.'
|
description: 'Change the style of any component in your App Config or customize them specifically through the ui prop.'
|
||||||
icon: i-heroicons-wrench-screwdriver
|
icon: i-heroicons-wrench-screwdriver
|
||||||
to: /getting-started/theming#components
|
to: /getting-started/theming#components
|
||||||
image: /illustrations/fully-customizable
|
image:
|
||||||
|
path: /illustrations/fully-customizable
|
||||||
|
width: 444
|
||||||
|
height: 160
|
||||||
class: 'col-span-5 row-span-5 lg:mb-10'
|
class: 'col-span-5 row-span-5 lg:mb-10'
|
||||||
orientation: 'vertical'
|
orientation: 'vertical'
|
||||||
- title: Icons
|
- title: Icons
|
||||||
description: 'Choose any of the 100k+ icons from the most popular icon libraries with the Icon component or the icon prop.'
|
description: 'Choose any of the 100k+ icons from the most popular icon libraries with the Icon component or the icon prop.'
|
||||||
icon: i-heroicons-face-smile
|
icon: i-heroicons-face-smile
|
||||||
to: /getting-started/theming#icons
|
to: /getting-started/theming#icons
|
||||||
image: /illustrations/icon-library
|
image:
|
||||||
|
path: /illustrations/icon-library
|
||||||
|
width: 362
|
||||||
|
height: 184
|
||||||
class: 'col-span-7 row-span-3'
|
class: 'col-span-7 row-span-3'
|
||||||
orientation: 'horizontal'
|
orientation: 'horizontal'
|
||||||
- title: Light & Dark
|
- title: Light & Dark
|
||||||
description: 'Every component is designed with dark mode in mind. Works out of the box with @nuxtjs/color-mode.'
|
description: 'Every component is designed with dark mode in mind. Works out of the box with @nuxtjs/color-mode.'
|
||||||
to: /getting-started/theming#dark-mode
|
to: /getting-started/theming#dark-mode
|
||||||
icon: i-heroicons-moon
|
icon: i-heroicons-moon
|
||||||
image: /illustrations/dark-mode
|
image:
|
||||||
|
path: /illustrations/dark-mode
|
||||||
|
width: 444
|
||||||
|
height: 160
|
||||||
class: 'col-span-5 row-span-5 lg:-mt-10 lg:mb-20'
|
class: 'col-span-5 row-span-5 lg:-mt-10 lg:mb-20'
|
||||||
orientation: 'vertical'
|
orientation: 'vertical'
|
||||||
- title: Keyboard Shortcuts
|
- title: Keyboard Shortcuts
|
||||||
@@ -44,10 +56,13 @@ sections:
|
|||||||
icon: i-heroicons-computer-desktop
|
icon: i-heroicons-computer-desktop
|
||||||
to: /getting-started/shortcuts
|
to: /getting-started/shortcuts
|
||||||
class: 'col-span-7 row-span-3'
|
class: 'col-span-7 row-span-3'
|
||||||
image: /illustrations/keyboard-shortcuts
|
image:
|
||||||
|
path: /illustrations/keyboard-shortcuts
|
||||||
|
width: 444
|
||||||
|
height: 160
|
||||||
orientation: 'horizontal'
|
orientation: 'horizontal'
|
||||||
links:
|
links:
|
||||||
- label: Learn more
|
- label: Explore the docs
|
||||||
to: /getting-started/theming
|
to: /getting-started/theming
|
||||||
color: white
|
color: white
|
||||||
size: lg
|
size: lg
|
||||||
@@ -65,27 +80,33 @@ sections:
|
|||||||
categories:
|
categories:
|
||||||
- label: Elements
|
- label: Elements
|
||||||
to: /elements/dropdown
|
to: /elements/dropdown
|
||||||
image: /illustrations/elements
|
image:
|
||||||
|
path: /illustrations/elements
|
||||||
badge: 9
|
badge: 9
|
||||||
- label: Forms
|
- label: Forms
|
||||||
to: /forms/form
|
to: /forms/form
|
||||||
image: /illustrations/forms
|
image:
|
||||||
|
path: /illustrations/forms
|
||||||
badge: 10
|
badge: 10
|
||||||
- label: Data
|
- label: Data
|
||||||
to: /data/table
|
to: /data/table
|
||||||
image: /illustrations/data
|
image:
|
||||||
|
path: /illustrations/data
|
||||||
badge: 1
|
badge: 1
|
||||||
- label: Navigation
|
- label: Navigation
|
||||||
to: /navigation/command-palette
|
to: /navigation/command-palette
|
||||||
image: /illustrations/navigation
|
image:
|
||||||
|
path: /illustrations/navigation
|
||||||
badge: 4
|
badge: 4
|
||||||
- label: Overlays
|
- label: Overlays
|
||||||
to: /overlays/modal
|
to: /overlays/modal
|
||||||
image: /illustrations/overlays
|
image:
|
||||||
|
path: /illustrations/overlays
|
||||||
badge: 6
|
badge: 6
|
||||||
- label: Layout
|
- label: Layout
|
||||||
to: /layout/card
|
to: /layout/card
|
||||||
image: /illustrations/layout
|
image:
|
||||||
|
path: /illustrations/layout
|
||||||
badge: 3
|
badge: 3
|
||||||
cta:
|
cta:
|
||||||
title: Trusted and supported by our<br class="hidden lg:block"> amazing community
|
title: Trusted and supported by our<br class="hidden lg:block"> amazing community
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
</UContainer>
|
</UContainer>
|
||||||
|
|
||||||
<ClientOnly>
|
<ClientOnly>
|
||||||
<UDocsSearch :files="files" :navigation="navigation" />
|
<LazyUDocsSearch :files="files" :navigation="navigation" />
|
||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
|
|
||||||
<UNotifications />
|
<UNotifications />
|
||||||
@@ -20,8 +20,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { NuxtError } from '#app'
|
import type { NuxtError } from '#app'
|
||||||
|
import type { ParsedContent } from '@nuxt/content/dist/runtime/types'
|
||||||
const { prefix, removePrefixFromNavigation, removePrefixFromFiles } = useContentSource()
|
|
||||||
|
|
||||||
useSeoMeta({
|
useSeoMeta({
|
||||||
title: 'Page not found',
|
title: 'Page not found',
|
||||||
@@ -32,22 +31,18 @@ defineProps<{
|
|||||||
error: NuxtError
|
error: NuxtError
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { data: navigation } = await useLazyAsyncData('navigation', () => fetchContentNavigation(), {
|
const { branch } = useContentSource()
|
||||||
default: () => [],
|
|
||||||
transform: (navigation) => {
|
|
||||||
navigation = navigation.find(link => link._path === prefix.value)?.children || []
|
|
||||||
|
|
||||||
return prefix.value === '/main' ? removePrefixFromNavigation(navigation) : navigation
|
const { data: nav } = await useAsyncData('navigation', () => fetchContentNavigation())
|
||||||
}
|
const { data: files } = useLazyFetch<ParsedContent[]>('/api/search.json', { default: () => [], server: false })
|
||||||
})
|
|
||||||
|
|
||||||
const { data: files } = await useLazyAsyncData('files', () => queryContent().where({ _type: 'markdown', navigation: { $ne: false } }).find(), {
|
// Computed
|
||||||
default: () => [],
|
|
||||||
transform: (files) => {
|
|
||||||
files = files.filter(file => file._path.startsWith(prefix.value))
|
|
||||||
|
|
||||||
return prefix.value === '/main' ? removePrefixFromFiles(files) : files
|
const navigation = computed(() => {
|
||||||
}
|
const main = nav.value.filter(item => item._path !== '/dev')
|
||||||
|
const dev = nav.value.find(item => item._path === '/dev')?.children
|
||||||
|
|
||||||
|
return branch.value?.name === 'dev' ? dev : main
|
||||||
})
|
})
|
||||||
|
|
||||||
// Provide
|
// Provide
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<UContainer>
|
<UContainer>
|
||||||
<UPage>
|
<UPage>
|
||||||
<template #left>
|
<template #left>
|
||||||
<UAside :links="anchors">
|
<UAside>
|
||||||
<BranchSelect />
|
<BranchSelect />
|
||||||
|
|
||||||
<UNavigationTree :links="mapContentNavigation(navigation)" />
|
<UNavigationTree :links="mapContentNavigation(navigation)" />
|
||||||
@@ -19,23 +19,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { NavItem } from '@nuxt/content/dist/runtime/types'
|
import type { NavItem } from '@nuxt/content/dist/runtime/types'
|
||||||
|
|
||||||
const { mapContentNavigation } = useElementsHelpers()
|
|
||||||
|
|
||||||
const navigation = inject<NavItem[]>('navigation')
|
const navigation = inject<NavItem[]>('navigation')
|
||||||
|
|
||||||
const anchors = [{
|
|
||||||
label: 'Documentation',
|
|
||||||
icon: 'i-heroicons-book-open-solid',
|
|
||||||
to: '/getting-started'
|
|
||||||
}, {
|
|
||||||
label: 'Playground',
|
|
||||||
icon: 'i-simple-icons-stackblitz',
|
|
||||||
to: 'https://stackblitz.com/edit/nuxt-ui?file=app.config.ts,app.vue',
|
|
||||||
target: '_blank'
|
|
||||||
}, {
|
|
||||||
label: 'Releases',
|
|
||||||
icon: 'i-heroicons-rocket-launch-solid',
|
|
||||||
to: 'https://github.com/nuxt/ui/releases',
|
|
||||||
target: '_blank'
|
|
||||||
}]
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import pkg from '../package.json'
|
|||||||
const { resolve } = createResolver(import.meta.url)
|
const { resolve } = createResolver(import.meta.url)
|
||||||
|
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
extends: process.env.NUXT_ELEMENTS_PATH || '@nuxthq/elements',
|
extends: process.env.NUXT_UI_PRO_PATH || '@nuxt/ui-pro',
|
||||||
modules: [
|
modules: [
|
||||||
'@nuxt/content',
|
'@nuxt/content',
|
||||||
'nuxt-og-image',
|
'nuxt-og-image',
|
||||||
@@ -18,8 +18,7 @@ export default defineNuxtConfig({
|
|||||||
'@nuxtjs/google-fonts',
|
'@nuxtjs/google-fonts',
|
||||||
'@nuxtjs/plausible',
|
'@nuxtjs/plausible',
|
||||||
'@vueuse/nuxt',
|
'@vueuse/nuxt',
|
||||||
'nuxt-component-meta',
|
'nuxt-component-meta'
|
||||||
'nuxt-lodash'
|
|
||||||
],
|
],
|
||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
public: {
|
public: {
|
||||||
@@ -33,14 +32,13 @@ export default defineNuxtConfig({
|
|||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
sources: {
|
sources: {
|
||||||
// overwrite default source AKA `content` directory
|
dev: {
|
||||||
content: {
|
|
||||||
prefix: '/dev',
|
prefix: '/dev',
|
||||||
driver: 'fs',
|
driver: 'fs',
|
||||||
base: resolve('./content')
|
base: resolve('./content')
|
||||||
},
|
},
|
||||||
main: {
|
// overwrite default source AKA `content` directory
|
||||||
prefix: '/main',
|
content: {
|
||||||
driver: 'github',
|
driver: 'github',
|
||||||
repo: 'nuxt/ui',
|
repo: 'nuxt/ui',
|
||||||
branch: 'main',
|
branch: 'main',
|
||||||
@@ -70,7 +68,7 @@ export default defineNuxtConfig({
|
|||||||
},
|
},
|
||||||
componentMeta: {
|
componentMeta: {
|
||||||
globalsOnly: true,
|
globalsOnly: true,
|
||||||
exclude: ['@nuxtjs/mdc', resolve('./components'), resolve('@nuxthq/elements/components')],
|
exclude: ['@nuxtjs/mdc', resolve('./components'), resolve('@nuxt/ui-pro/components')],
|
||||||
metaFields: {
|
metaFields: {
|
||||||
props: true,
|
props: true,
|
||||||
slots: false,
|
slots: false,
|
||||||
@@ -78,10 +76,6 @@ export default defineNuxtConfig({
|
|||||||
exposed: false
|
exposed: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
typescript: {
|
|
||||||
strict: false,
|
|
||||||
includeWorkspace: true
|
|
||||||
},
|
|
||||||
hooks: {
|
hooks: {
|
||||||
// Related to https://github.com/nuxt/nuxt/pull/22558
|
// Related to https://github.com/nuxt/nuxt/pull/22558
|
||||||
'components:extend': (components) => {
|
'components:extend': (components) => {
|
||||||
|
|||||||
@@ -5,27 +5,27 @@
|
|||||||
"@nuxt/ui": "workspace:latest"
|
"@nuxt/ui": "workspace:latest"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify-json/heroicons": "latest",
|
"@iconify-json/heroicons": "^1.1.12",
|
||||||
"@iconify-json/simple-icons": "latest",
|
"@iconify-json/simple-icons": "^1.1.73",
|
||||||
"@nuxt/content": "^2.8.2",
|
"@nuxt/content": "^2.8.5",
|
||||||
"@nuxt/devtools": "^0.8.2",
|
"@nuxt/devtools": "^0.8.5",
|
||||||
"@nuxt/eslint-config": "^0.2.0",
|
"@nuxt/eslint-config": "^0.2.0",
|
||||||
"@nuxthq/elements": "npm:@nuxthq/elements-edge@0.0.1-28234841.b091775",
|
"@nuxt/ui-pro": "npm:@nuxt/ui-pro-edge@0.0.1-28265230.40bb224",
|
||||||
"@nuxthq/studio": "^0.13.4",
|
"@nuxthq/studio": "^0.14.1",
|
||||||
"@nuxtjs/fontaine": "^0.4.1",
|
"@nuxtjs/fontaine": "^0.4.1",
|
||||||
"@nuxtjs/google-fonts": "^3.0.2",
|
"@nuxtjs/google-fonts": "^3.0.2",
|
||||||
"@nuxtjs/plausible": "^0.2.1",
|
"@nuxtjs/plausible": "^0.2.3",
|
||||||
"@vueuse/nuxt": "^10.4.1",
|
"@vueuse/nuxt": "^10.4.1",
|
||||||
"eslint": "^8.48.0",
|
"eslint": "^8.50.0",
|
||||||
"joi": "^17.10.1",
|
"joi": "^17.10.2",
|
||||||
"nuxt": "^3.7.1",
|
"nuxt": "^3.7.4",
|
||||||
"nuxt-component-meta": "^0.5.3",
|
"nuxt-component-meta": "^0.5.4",
|
||||||
"nuxt-lodash": "^2.5.0",
|
"nuxt-og-image": "^2.0.28",
|
||||||
"nuxt-og-image": "^2.0.25",
|
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"ufo": "^1.3.0",
|
"ufo": "^1.3.1",
|
||||||
"v-calendar": "^3.0.3",
|
"v-calendar": "^3.1.0",
|
||||||
"yup": "^1.2.0",
|
"valibot": "^0.17.1",
|
||||||
|
"yup": "^1.3.1",
|
||||||
"zod": "^3.22.2"
|
"zod": "^3.22.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
<UDivider v-if="surround?.length" />
|
<UDivider v-if="surround?.length" />
|
||||||
|
|
||||||
<UDocsSurround :surround="removePrefixFromFiles(surround)" />
|
<UDocsSurround :surround="(surround as ParsedContent[])" />
|
||||||
</UPageBody>
|
</UPageBody>
|
||||||
|
|
||||||
<template v-if="page.body?.toc?.links?.length" #right>
|
<template v-if="page.body?.toc?.links?.length" #right>
|
||||||
@@ -25,25 +25,34 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { withoutTrailingSlash } from 'ufo'
|
||||||
|
import type { ParsedContent } from '@nuxt/content/dist/runtime/types'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const { prefix, removePrefixFromFiles } = useContentSource()
|
const { branch } = useContentSource()
|
||||||
const { findPageHeadline } = useElementsHelpers()
|
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: 'docs'
|
layout: 'docs'
|
||||||
})
|
})
|
||||||
|
|
||||||
const path = computed(() => route.path.startsWith(prefix.value) ? route.path : `${prefix.value}${route.path}`)
|
const { data: page } = await useAsyncData(route.path, () => queryContent(route.path).findOne())
|
||||||
|
|
||||||
const { data: page } = await useAsyncData(path.value, () => queryContent(path.value).findOne())
|
|
||||||
if (!page.value) {
|
if (!page.value) {
|
||||||
throw createError({ statusCode: 404, statusMessage: 'Page not found' })
|
throw createError({ statusCode: 404, statusMessage: 'Page not found' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data: surround } = await useAsyncData(`${path.value}-surround`, () => {
|
const { data: surround } = await useAsyncData(`${route.path}-surround`, () => {
|
||||||
return queryContent(prefix.value)
|
return queryContent()
|
||||||
.where({ _extension: 'md', navigation: { $ne: false } })
|
.where({
|
||||||
.findSurround((path.value.endsWith('/') ? path.value.slice(0, -1) : path.value))
|
_extension: 'md',
|
||||||
|
_path: {
|
||||||
|
[branch.value?.name === 'dev' ? '$eq' : '$ne']: new RegExp('^/dev')
|
||||||
|
},
|
||||||
|
navigation: {
|
||||||
|
$ne: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.only(['title', 'description', '_path'])
|
||||||
|
.findSurround(withoutTrailingSlash(route.path))
|
||||||
})
|
})
|
||||||
|
|
||||||
useSeoMeta({
|
useSeoMeta({
|
||||||
@@ -65,17 +74,27 @@ const headline = computed(() => findPageHeadline(page.value))
|
|||||||
const links = computed(() => [{
|
const links = computed(() => [{
|
||||||
icon: 'i-heroicons-pencil-square',
|
icon: 'i-heroicons-pencil-square',
|
||||||
label: 'Edit this page',
|
label: 'Edit this page',
|
||||||
to: `https://github.com/nuxt/ui/edit/dev/docs/content/${page?.value?._file}`,
|
to: `https://github.com/nuxt/ui/edit/dev/docs/content/${branch.value?.name === 'dev' ? page?.value?._file.split('/').slice(1).join('/') : page?.value?._file}`,
|
||||||
target: '_blank'
|
target: '_blank'
|
||||||
}, {
|
}, {
|
||||||
icon: 'i-heroicons-star',
|
icon: 'i-heroicons-star',
|
||||||
label: 'Star on GitHub',
|
label: 'Star on GitHub',
|
||||||
to: 'https://github.com/nuxt/ui',
|
to: 'https://github.com/nuxt/ui',
|
||||||
target: '_blank'
|
target: '_blank'
|
||||||
|
}, {
|
||||||
|
icon: 'i-heroicons-chat-bubble-bottom-center-text',
|
||||||
|
label: 'Chat on Discord',
|
||||||
|
to: 'https://discord.com/channels/473401852243869706/1153996761426300948',
|
||||||
|
target: '_blank'
|
||||||
}, {
|
}, {
|
||||||
icon: 'i-heroicons-book-open',
|
icon: 'i-heroicons-book-open',
|
||||||
label: 'Nuxt documentation',
|
label: 'Nuxt docs',
|
||||||
to: 'https://nuxt.com',
|
to: 'https://nuxt.com',
|
||||||
target: '_blank'
|
target: '_blank'
|
||||||
|
}, {
|
||||||
|
icon: 'i-simple-icons-figma',
|
||||||
|
label: 'Figma Kit',
|
||||||
|
to: 'https://www.figma.com/community/file/1288455405058138934/nuxt-ui',
|
||||||
|
target: '_blank'
|
||||||
}])
|
}])
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<ULandingHero v-bind="page.hero" :ui="{ base: 'relative z-[1]' }" class="mb-[calc(var(--header-height)*2)]">
|
<ULandingHero v-bind="page.hero" :ui="{ base: 'relative z-[1]', container: 'max-w-3xl' }" class="mb-[calc(var(--header-height)*2)]">
|
||||||
<template #title>
|
<template #title>
|
||||||
<span v-html="page.hero?.title" />
|
<span v-html="page.hero?.title" />
|
||||||
</template>
|
</template>
|
||||||
@@ -19,6 +20,7 @@
|
|||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
icon="i-heroicons-command-line"
|
icon="i-heroicons-command-line"
|
||||||
input-class="select-none"
|
input-class="select-none"
|
||||||
|
aria-label="Install @nuxt/ui"
|
||||||
size="lg"
|
size="lg"
|
||||||
:ui="{ base: 'disabled:cursor-default', icon: { trailing: { pointer: '' } } }"
|
:ui="{ base: 'disabled:cursor-default', icon: { trailing: { pointer: '' } } }"
|
||||||
>
|
>
|
||||||
@@ -71,7 +73,14 @@
|
|||||||
class="flex flex-col"
|
class="flex flex-col"
|
||||||
>
|
>
|
||||||
<div v-if="feature.image">
|
<div v-if="feature.image">
|
||||||
<UColorModeImage :light="`${feature.image}-light.svg`" :dark="`${feature.image}-dark.svg`" class="object-cover w-full" />
|
<UColorModeImage
|
||||||
|
:light="`${feature.image.path}-light.svg`"
|
||||||
|
:dark="`${feature.image.path}-dark.svg`"
|
||||||
|
:width="feature.image.width"
|
||||||
|
:height="feature.image.height"
|
||||||
|
:alt="feature.title"
|
||||||
|
class="object-cover w-full"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ULandingCard>
|
</ULandingCard>
|
||||||
</ULandingGrid>
|
</ULandingGrid>
|
||||||
@@ -85,7 +94,14 @@
|
|||||||
:to="category.to"
|
:to="category.to"
|
||||||
class="hover:bg-gradient-to-b hover:from-gray-200/50 dark:hover:from-gray-800/50 rounded-lg"
|
class="hover:bg-gradient-to-b hover:from-gray-200/50 dark:hover:from-gray-800/50 rounded-lg"
|
||||||
>
|
>
|
||||||
<UColorModeImage :light="`${category.image}-light.svg`" :dark="`${category.image}-dark.svg`" class="object-cover w-full" />
|
<UColorModeImage
|
||||||
|
:light="`${category.image.path}-light.svg`"
|
||||||
|
:dark="`${category.image.path}-dark.svg`"
|
||||||
|
width="363"
|
||||||
|
height="190"
|
||||||
|
:alt="category.label"
|
||||||
|
class="object-cover w-full"
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="flex items-center justify-center gap-2 mt-1 mb-2">
|
<div class="flex items-center justify-center gap-2 mt-1 mb-2">
|
||||||
<span class="font-semibold text-lg">{{ category.label }}</span>
|
<span class="font-semibold text-lg">{{ category.label }}</span>
|
||||||
@@ -116,28 +132,33 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #links>
|
<template #links>
|
||||||
<UAvatarGroup :max="xlAndLarger ? 13 : lgAndLarger ? 10 : mdAndLarger ? 16 : 8" size="md" class="flex-wrap-reverse [&_span:first-child]:text-xs justify-center">
|
<ClientOnly>
|
||||||
<UTooltip
|
<UAvatarGroup :max="xlAndLarger ? 13 : lgAndLarger ? 10 : mdAndLarger ? 16 : 8" size="md" class="flex-wrap-reverse [&_span:first-child]:text-xs justify-center">
|
||||||
v-for="(contributor, index) of module.contributors"
|
<UTooltip
|
||||||
:key="index"
|
v-for="(contributor, index) of module.contributors"
|
||||||
:text="contributor.username"
|
:key="index"
|
||||||
class="rounded-full"
|
:text="contributor.username"
|
||||||
:ui="{ background: 'bg-gray-50 dark:bg-gray-800/50' }"
|
class="rounded-full"
|
||||||
:popper="{ offsetDistance: 16 }"
|
: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"
|
|
||||||
size="md"
|
|
||||||
>
|
>
|
||||||
<NuxtLink :to="`https://github.com/${contributor.username}`" target="_blank" class="focus:outline-none" tabindex="-1">
|
<UAvatar
|
||||||
<span class="absolute inset-0" aria-hidden="true" />
|
:alt="contributor.username"
|
||||||
</NuxtLink>
|
:src="`https://ipx.nuxt.com/s_40x40/gh_avatar/${contributor.username}`"
|
||||||
</UAvatar>
|
:srcset="`https://ipx.nuxt.com/s_80x80/gh_avatar/${contributor.username} 2x`"
|
||||||
</UTooltip>
|
class="lg:hover:scale-125 lg:hover:ring-2 lg:hover:ring-primary-500 dark:lg:hover:ring-primary-400 transition-transform"
|
||||||
</UAvatarGroup>
|
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>
|
</template>
|
||||||
|
|
||||||
<div class="flex flex-col sm:flex-row items-center justify-center gap-8 lg:gap-16">
|
<div class="flex flex-col sm:flex-row items-center justify-center gap-8 lg:gap-16">
|
||||||
@@ -161,10 +182,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { pick } from 'lodash-es'
|
|
||||||
import { breakpointsTailwind, useBreakpoints } from '@vueuse/core'
|
import { breakpointsTailwind, useBreakpoints } from '@vueuse/core'
|
||||||
|
|
||||||
const { data: page } = await useAsyncData('index', () => queryContent('/').findOne())
|
const { data: page } = await useAsyncData('index', () => queryContent('/dev').findOne())
|
||||||
const { data: module } = await useFetch<{
|
const { data: module } = await useFetch<{
|
||||||
stats: {
|
stats: {
|
||||||
downloads: number
|
downloads: number
|
||||||
@@ -174,7 +194,7 @@ const { data: module } = await useFetch<{
|
|||||||
username: string
|
username: string
|
||||||
}[]
|
}[]
|
||||||
}>('https://api.nuxt.com/modules/ui', {
|
}>('https://api.nuxt.com/modules/ui', {
|
||||||
transform: (module) => pick(module, ['stats', 'contributors'])
|
transform: ({ stats, contributors }) => ({ stats, contributors })
|
||||||
})
|
})
|
||||||
|
|
||||||
const source = ref('npm i @nuxt/ui')
|
const source = ref('npm i @nuxt/ui')
|
||||||
|
|||||||
24
docs/pages/playground.vue
Normal file
24
docs/pages/playground.vue
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<script setup>
|
||||||
|
const title = 'Playground'
|
||||||
|
const description = 'Play online with our interactive Nuxt Image playground.'
|
||||||
|
|
||||||
|
useSeoMeta({
|
||||||
|
title,
|
||||||
|
ogTitle: 'Nuxt UI Playground',
|
||||||
|
description
|
||||||
|
})
|
||||||
|
|
||||||
|
defineOgImage({
|
||||||
|
component: 'Docs',
|
||||||
|
title,
|
||||||
|
description
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="h-[calc(100vh-var(--header-height))]">
|
||||||
|
<ClientOnly>
|
||||||
|
<iframe :src="`https://stackblitz.com/edit/nuxt-ui?embed=1&file=app.config.ts,app.vue&theme=${$colorMode.preference}`" width="100%" height="100%" />
|
||||||
|
</ClientOnly>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -1,15 +1,3 @@
|
|||||||
<svg width="900" height="900" viewBox="0 0 900 900" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="900" height="900" viewBox="0 0 900 900" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<style>
|
<path d="M504.908 750H839.476C850.103 750.001 860.542 747.229 869.745 741.963C878.948 736.696 886.589 729.121 891.9 719.999C897.211 710.876 900.005 700.529 900 689.997C899.995 679.465 897.193 669.12 891.873 660.002L667.187 274.289C661.876 265.169 654.237 257.595 645.036 252.329C635.835 247.064 625.398 244.291 614.773 244.291C604.149 244.291 593.711 247.064 584.511 252.329C575.31 257.595 567.67 265.169 562.36 274.289L504.908 372.979L392.581 179.993C387.266 170.874 379.623 163.301 370.42 158.036C361.216 152.772 350.777 150 340.151 150C329.525 150 319.086 152.772 309.883 158.036C300.679 163.301 293.036 170.874 287.721 179.993L8.12649 660.002C2.80743 669.12 0.00462935 679.465 5.72978e-06 689.997C-0.00461789 700.529 2.78909 710.876 8.10015 719.999C13.4112 729.121 21.0523 736.696 30.255 741.963C39.4576 747.229 49.8973 750.001 60.524 750H270.538C353.748 750 415.112 713.775 457.336 643.101L559.849 467.145L614.757 372.979L779.547 655.834H559.849L504.908 750ZM267.114 655.737L120.551 655.704L340.249 278.586L449.87 467.145L376.474 593.175C348.433 639.03 316.577 655.737 267.114 655.737Z" fill="#00DC82" />
|
||||||
path {
|
|
||||||
fill: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
path {
|
|
||||||
fill: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<path d="M504.908 750H839.476C850.103 750.001 860.542 747.229 869.745 741.963C878.948 736.696 886.589 729.121 891.9 719.999C897.211 710.876 900.005 700.529 900 689.997C899.995 679.465 897.193 669.12 891.873 660.002L667.187 274.289C661.876 265.169 654.237 257.595 645.036 252.329C635.835 247.064 625.398 244.291 614.773 244.291C604.149 244.291 593.711 247.064 584.511 252.329C575.31 257.595 567.67 265.169 562.36 274.289L504.908 372.979L392.581 179.993C387.266 170.874 379.623 163.301 370.42 158.036C361.216 152.772 350.777 150 340.151 150C329.525 150 319.086 152.772 309.883 158.036C300.679 163.301 293.036 170.874 287.721 179.993L8.12649 660.002C2.80743 669.12 0.00462935 679.465 5.72978e-06 689.997C-0.00461789 700.529 2.78909 710.876 8.10015 719.999C13.4112 729.121 21.0523 736.696 30.255 741.963C39.4576 747.229 49.8973 750.001 60.524 750H270.538C353.748 750 415.112 713.775 457.336 643.101L559.849 467.145L614.757 372.979L779.547 655.834H559.849L504.908 750ZM267.114 655.737L120.551 655.704L340.249 278.586L449.87 467.145L376.474 593.175C348.433 639.03 316.577 655.737 267.114 655.737Z" />
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.2 KiB |
@@ -1 +1,2 @@
|
|||||||
Disallow: /dev/*
|
user-agent: *
|
||||||
|
disallow: /dev/*
|
||||||
|
|||||||
@@ -2,11 +2,6 @@ import type { Config } from 'tailwindcss'
|
|||||||
import defaultTheme from 'tailwindcss/defaultTheme'
|
import defaultTheme from 'tailwindcss/defaultTheme'
|
||||||
|
|
||||||
export default <Partial<Config>>{
|
export default <Partial<Config>>{
|
||||||
content: {
|
|
||||||
files: [
|
|
||||||
'content/**/*.yml'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
|
|||||||
40
package.json
40
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@nuxt/ui",
|
"name": "@nuxt/ui",
|
||||||
"version": "2.8.0",
|
"version": "2.9.0",
|
||||||
"repository": "https://github.com/nuxt/ui",
|
"repository": "https://github.com/nuxt/ui",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"exports": {
|
"exports": {
|
||||||
@@ -25,15 +25,16 @@
|
|||||||
"build:docs": "nuxi generate docs",
|
"build:docs": "nuxi generate docs",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"lint:fix": "eslint . --fix",
|
"lint:fix": "eslint . --fix",
|
||||||
"typecheck": "nuxi typecheck",
|
"typecheck": "vue-tsc --noEmit && nuxi typecheck docs",
|
||||||
"prepare": "nuxi prepare docs",
|
"dev:prepare": "nuxt-module-build --stub && nuxt-module-build prepare && nuxi prepare docs",
|
||||||
"release": "release-it"
|
"release": "release-it"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@egoist/tailwindcss-icons": "^1.1.0",
|
"@egoist/tailwindcss-icons": "^1.2.0",
|
||||||
|
"@headlessui/tailwindcss": "^0.2.0",
|
||||||
"@headlessui/vue": "^1.7.16",
|
"@headlessui/vue": "^1.7.16",
|
||||||
"@iconify-json/heroicons": "^1.1.12",
|
"@iconify-json/heroicons": "^1.1.12",
|
||||||
"@nuxt/kit": "^3.7.1",
|
"@nuxt/kit": "^3.7.4",
|
||||||
"@nuxtjs/color-mode": "^3.3.0",
|
"@nuxtjs/color-mode": "^3.3.0",
|
||||||
"@nuxtjs/tailwindcss": "^6.8.0",
|
"@nuxtjs/tailwindcss": "^6.8.0",
|
||||||
"@popperjs/core": "^2.11.8",
|
"@popperjs/core": "^2.11.8",
|
||||||
@@ -46,28 +47,25 @@
|
|||||||
"@vueuse/math": "^10.4.1",
|
"@vueuse/math": "^10.4.1",
|
||||||
"defu": "^6.1.2",
|
"defu": "^6.1.2",
|
||||||
"fuse.js": "^6.6.2",
|
"fuse.js": "^6.6.2",
|
||||||
"lodash-es": "^4.17.21",
|
"ohash": "^1.1.3",
|
||||||
|
"pathe": "^1.1.1",
|
||||||
|
"scule": "^1.0.0",
|
||||||
"tailwind-merge": "^1.14.0",
|
"tailwind-merge": "^1.14.0",
|
||||||
"tailwindcss": "^3.3.3"
|
"tailwindcss": "^3.3.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nuxt/eslint-config": "^0.2.0",
|
"@nuxt/eslint-config": "^0.2.0",
|
||||||
"@nuxt/module-builder": "^0.5.1",
|
"@nuxt/module-builder": "^0.5.2",
|
||||||
"@release-it/conventional-changelog": "^7.0.1",
|
"@release-it/conventional-changelog": "^7.0.2",
|
||||||
"eslint": "^8.48.0",
|
"eslint": "^8.50.0",
|
||||||
"joi": "^17.10.1",
|
"joi": "^17.10.2",
|
||||||
"nuxt": "^3.7.1",
|
"nuxt": "^3.7.4",
|
||||||
"release-it": "^16.1.5",
|
"release-it": "^16.2.1",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"unbuild": "^2.0.0",
|
"unbuild": "^2.0.0",
|
||||||
"vue-tsc": "^1.8.10",
|
"valibot": "^0.17.1",
|
||||||
"yup": "^1.2.0",
|
"vue-tsc": "^1.8.15",
|
||||||
"zod": "^3.22.2",
|
"yup": "^1.3.1",
|
||||||
"valibot": "^0.13.1"
|
"zod": "^3.22.2"
|
||||||
},
|
|
||||||
"pnpm": {
|
|
||||||
"patchedDependencies": {
|
|
||||||
"nuxt-component-meta@0.5.3": "patches/nuxt-component-meta@0.5.3.patch"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
diff --git a/dist/module.mjs b/dist/module.mjs
|
|
||||||
index 286319046560f8ead5a26f811e7643fe990f9ee6..14be2f9551c24cd1e5c35dadaad9f83009f4c5a0 100644
|
|
||||||
--- a/dist/module.mjs
|
|
||||||
+++ b/dist/module.mjs
|
|
||||||
@@ -142,10 +142,6 @@ const module = defineNuxtModule({
|
|
||||||
references.push({
|
|
||||||
path: join(nuxt.options.buildDir, "component-meta.d.ts")
|
|
||||||
});
|
|
||||||
- tsConfig.compilerOptions = tsConfig.compilerOptions || {};
|
|
||||||
- tsConfig.compilerOptions.paths = tsConfig.compilerOptions.paths || {};
|
|
||||||
- tsConfig.compilerOptions.paths["#nuxt-component-meta"] = [withoutLeadingSlash(join(nuxt.options.buildDir, "/component-meta.mjs").replace(nuxt.options.rootDir, ""))];
|
|
||||||
- tsConfig.compilerOptions.paths["#nuxt-component-meta/types"] = [withoutLeadingSlash(join(nuxt.options.buildDir, "/component-meta.d.ts").replace(nuxt.options.rootDir, ""))];
|
|
||||||
});
|
|
||||||
nuxt.hook("nitro:config", (nitroConfig) => {
|
|
||||||
nitroConfig.handlers = nitroConfig.handlers || [];
|
|
||||||
1
playground/.nuxtrc
Normal file
1
playground/.nuxtrc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
imports.autoImport=true
|
||||||
3376
pnpm-lock.yaml
generated
3376
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -6,9 +6,6 @@ git restore -s@ -SW -- .
|
|||||||
# Bump versions to edge
|
# Bump versions to edge
|
||||||
pnpm jiti ./scripts/bump-edge
|
pnpm jiti ./scripts/bump-edge
|
||||||
|
|
||||||
# Resolve pnpm
|
|
||||||
pnpm install
|
|
||||||
|
|
||||||
# Update token
|
# Update token
|
||||||
if [[ ! -z ${NODE_AUTH_TOKEN} ]] ; then
|
if [[ ! -z ${NODE_AUTH_TOKEN} ]] ; then
|
||||||
echo "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}" >> ~/.npmrc
|
echo "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}" >> ~/.npmrc
|
||||||
|
|||||||
@@ -3,9 +3,6 @@
|
|||||||
# Restore all git changes
|
# Restore all git changes
|
||||||
git restore -s@ -SW -- .
|
git restore -s@ -SW -- .
|
||||||
|
|
||||||
# Resolve pnpm
|
|
||||||
pnpm install
|
|
||||||
|
|
||||||
# Update token
|
# Update token
|
||||||
if [[ ! -z ${NODE_AUTH_TOKEN} ]] ; then
|
if [[ ! -z ${NODE_AUTH_TOKEN} ]] ; then
|
||||||
echo "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}" >> ~/.npmrc
|
echo "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}" >> ~/.npmrc
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { omit, kebabCase, camelCase, upperFirst } from 'lodash-es'
|
import { omit } from './runtime/utils/lodash'
|
||||||
|
import { kebabCase, camelCase, upperFirst } from 'scule'
|
||||||
|
|
||||||
const colorsToExclude = [
|
const colorsToExclude = [
|
||||||
'inherit',
|
'inherit',
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { defineNuxtModule, installModule, addComponentsDir, addImportsDir, createResolver, addPlugin, resolvePath } from '@nuxt/kit'
|
import { defineNuxtModule, installModule, addComponentsDir, addImportsDir, createResolver, addPlugin } from '@nuxt/kit'
|
||||||
import defaultColors from 'tailwindcss/colors.js'
|
import defaultColors from 'tailwindcss/colors.js'
|
||||||
import { defaultExtractor as createDefaultExtractor } from 'tailwindcss/lib/lib/defaultExtractor.js'
|
import { defaultExtractor as createDefaultExtractor } from 'tailwindcss/lib/lib/defaultExtractor.js'
|
||||||
import { iconsPlugin, getIconCollections } from '@egoist/tailwindcss-icons'
|
import { iconsPlugin, getIconCollections } from '@egoist/tailwindcss-icons'
|
||||||
import { name, version } from '../package.json'
|
import { name, version } from '../package.json'
|
||||||
import { generateSafelist, excludeColors, customSafelistExtractor } from './colors'
|
import { generateSafelist, excludeColors, customSafelistExtractor } from './colors'
|
||||||
import appConfig from './runtime/app.config'
|
import createTemplates from './templates'
|
||||||
|
import * as config from './runtime/ui.config'
|
||||||
type DeepPartial<T> = Partial<{ [P in keyof T]: DeepPartial<T[P]> | { [key: string]: string } }>
|
import type { DeepPartial, Strategy } from './runtime/types/utils'
|
||||||
|
|
||||||
const defaultExtractor = createDefaultExtractor({ tailwindConfig: { separator: ':' } })
|
const defaultExtractor = createDefaultExtractor({ tailwindConfig: { separator: ':' } })
|
||||||
|
|
||||||
@@ -16,13 +16,22 @@ delete defaultColors.trueGray
|
|||||||
delete defaultColors.coolGray
|
delete defaultColors.coolGray
|
||||||
delete defaultColors.blueGray
|
delete defaultColors.blueGray
|
||||||
|
|
||||||
|
type UI = {
|
||||||
|
primary?: string
|
||||||
|
gray?: string
|
||||||
|
colors?: string[]
|
||||||
|
strategy?: Strategy
|
||||||
|
[key: string]: any
|
||||||
|
} & DeepPartial<typeof config>
|
||||||
|
|
||||||
|
declare module 'nuxt/schema' {
|
||||||
|
interface AppConfigInput {
|
||||||
|
ui?: UI
|
||||||
|
}
|
||||||
|
}
|
||||||
declare module '@nuxt/schema' {
|
declare module '@nuxt/schema' {
|
||||||
interface AppConfigInput {
|
interface AppConfigInput {
|
||||||
ui?: {
|
ui?: UI
|
||||||
primary?: string
|
|
||||||
gray?: string
|
|
||||||
colors?: string[]
|
|
||||||
} & DeepPartial<typeof appConfig.ui>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,12 +73,9 @@ export default defineNuxtModule<ModuleOptions>({
|
|||||||
nuxt.options.build.transpile.push(runtimeDir)
|
nuxt.options.build.transpile.push(runtimeDir)
|
||||||
nuxt.options.build.transpile.push('@popperjs/core', '@headlessui/vue')
|
nuxt.options.build.transpile.push('@popperjs/core', '@headlessui/vue')
|
||||||
|
|
||||||
nuxt.options.css.push(resolve(runtimeDir, 'ui.css'))
|
nuxt.options.alias['#ui'] = runtimeDir
|
||||||
|
|
||||||
const appConfigFile = await resolvePath(resolve(runtimeDir, 'app.config'))
|
nuxt.options.css.push(resolve(runtimeDir, 'ui.css'))
|
||||||
nuxt.hook('app:resolve', (app) => {
|
|
||||||
app.configs.push(appConfigFile)
|
|
||||||
})
|
|
||||||
|
|
||||||
nuxt.hook('tailwindcss:config', function (tailwindConfig) {
|
nuxt.hook('tailwindcss:config', function (tailwindConfig) {
|
||||||
const globalColors: any = {
|
const globalColors: any = {
|
||||||
@@ -116,11 +122,12 @@ export default defineNuxtModule<ModuleOptions>({
|
|||||||
|
|
||||||
const colors = excludeColors(globalColors)
|
const colors = excludeColors(globalColors)
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
nuxt.options.appConfig.ui = {
|
nuxt.options.appConfig.ui = {
|
||||||
...nuxt.options.appConfig.ui,
|
|
||||||
primary: 'green',
|
primary: 'green',
|
||||||
gray: 'cool',
|
gray: 'cool',
|
||||||
colors
|
colors,
|
||||||
|
strategy: 'merge'
|
||||||
}
|
}
|
||||||
|
|
||||||
tailwindConfig.safelist = tailwindConfig.safelist || []
|
tailwindConfig.safelist = tailwindConfig.safelist || []
|
||||||
@@ -130,6 +137,8 @@ export default defineNuxtModule<ModuleOptions>({
|
|||||||
tailwindConfig.plugins.push(iconsPlugin({ collections: getIconCollections(options.icons as any[]) }))
|
tailwindConfig.plugins.push(iconsPlugin({ collections: getIconCollections(options.icons as any[]) }))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
createTemplates(nuxt)
|
||||||
|
|
||||||
// Modules
|
// Modules
|
||||||
|
|
||||||
await installModule('@nuxtjs/color-mode', { classSuffix: '' })
|
await installModule('@nuxtjs/color-mode', { classSuffix: '' })
|
||||||
@@ -141,7 +150,8 @@ export default defineNuxtModule<ModuleOptions>({
|
|||||||
require('@tailwindcss/forms')({ strategy: 'class' }),
|
require('@tailwindcss/forms')({ strategy: 'class' }),
|
||||||
require('@tailwindcss/aspect-ratio'),
|
require('@tailwindcss/aspect-ratio'),
|
||||||
require('@tailwindcss/typography'),
|
require('@tailwindcss/typography'),
|
||||||
require('@tailwindcss/container-queries')
|
require('@tailwindcss/container-queries'),
|
||||||
|
require('@headlessui/tailwindcss')
|
||||||
],
|
],
|
||||||
content: {
|
content: {
|
||||||
files: [
|
files: [
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="wrapperClass" v-bind="attrs">
|
<div :class="ui.wrapper" v-bind="attrs">
|
||||||
<table :class="[ui.base, ui.divide]">
|
<table :class="[ui.base, ui.divide]">
|
||||||
<thead :class="ui.thead">
|
<thead :class="ui.thead">
|
||||||
<tr :class="ui.tr.base">
|
<tr :class="ui.tr.base">
|
||||||
<th v-if="modelValue" scope="col" class="ps-4">
|
<th v-if="modelValue" scope="col" :class="ui.checkbox.padding">
|
||||||
<UCheckbox :checked="indeterminate || selected.length === rows.length" :indeterminate="indeterminate" @change="onChange" />
|
<UCheckbox :checked="indeterminate || selected.length === rows.length" :indeterminate="indeterminate" aria-label="Select all" @change="onChange" />
|
||||||
</th>
|
</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-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]">
|
||||||
@@ -49,12 +49,12 @@
|
|||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<template v-else>
|
<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]" @click="() => onSelect(row)">
|
<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="ps-4">
|
<td v-if="modelValue" :class="ui.checkbox.padding">
|
||||||
<UCheckbox v-model="selected" :value="row" @click.stop />
|
<UCheckbox v-model="selected" :value="row" aria-label="Select row" @click.stop />
|
||||||
</td>
|
</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]">
|
<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)">
|
<slot :name="`${column.key}-data`" :column="column" :row="row" :index="index" :get-row-data="(defaultValue) => getRowData(row, column.key, defaultValue)">
|
||||||
{{ getRowData(row, column.key) }}
|
{{ getRowData(row, column.key) }}
|
||||||
</slot>
|
</slot>
|
||||||
@@ -67,22 +67,21 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ref, computed, defineComponent, toRaw } from 'vue'
|
import { ref, computed, defineComponent, toRaw, toRef } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { omit, capitalize, orderBy, get } from 'lodash-es'
|
import { upperFirst } from 'scule'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { twMerge } from 'tailwind-merge'
|
|
||||||
import { defuTwMerge } from '../../utils'
|
|
||||||
import UButton from '../elements/Button.vue'
|
import UButton from '../elements/Button.vue'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import UCheckbox from '../forms/Checkbox.vue'
|
import UCheckbox from '../forms/Checkbox.vue'
|
||||||
import type { Button } from '../../types/button'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useAppConfig } from '#imports'
|
import { mergeConfig, omit, get } from '../../utils'
|
||||||
// TODO: Remove
|
import type { Strategy, Button } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
import { table } from '#ui/ui.config'
|
||||||
|
|
||||||
// const appConfig = useAppConfig()
|
const config = mergeConfig<typeof table>(appConfig.ui.strategy, appConfig.ui.table, table)
|
||||||
|
|
||||||
function defaultComparator<T> (a: T, z: T): boolean {
|
function defaultComparator<T> (a: T, z: T): boolean {
|
||||||
return a === z
|
return a === z
|
||||||
@@ -122,15 +121,15 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
sortButton: {
|
sortButton: {
|
||||||
type: Object as PropType<Button>,
|
type: Object as PropType<Button>,
|
||||||
default: () => appConfig.ui.table.default.sortButton
|
default: () => config.default.sortButton as Button
|
||||||
},
|
},
|
||||||
sortAscIcon: {
|
sortAscIcon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => appConfig.ui.table.default.sortAscIcon
|
default: () => config.default.sortAscIcon
|
||||||
},
|
},
|
||||||
sortDescIcon: {
|
sortDescIcon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => appConfig.ui.table.default.sortDescIcon
|
default: () => config.default.sortDescIcon
|
||||||
},
|
},
|
||||||
loading: {
|
loading: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -138,27 +137,26 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
loadingState: {
|
loadingState: {
|
||||||
type: Object as PropType<{ icon: string, label: string }>,
|
type: Object as PropType<{ icon: string, label: string }>,
|
||||||
default: () => appConfig.ui.table.default.loadingState
|
default: () => config.default.loadingState
|
||||||
},
|
},
|
||||||
emptyState: {
|
emptyState: {
|
||||||
type: Object as PropType<{ icon: string, label: string }>,
|
type: Object as PropType<{ icon: string, label: string }>,
|
||||||
default: () => appConfig.ui.table.default.emptyState
|
default: () => config.default.emptyState
|
||||||
|
},
|
||||||
|
class: {
|
||||||
|
type: [String, Object, Array] as PropType<any>,
|
||||||
|
default: undefined
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof appConfig.ui.table>>,
|
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
||||||
default: () => ({})
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue'],
|
emits: ['update:modelValue'],
|
||||||
setup (props, { emit, attrs }) {
|
setup (props, { emit, attrs: $attrs }) {
|
||||||
// TODO: Remove
|
const { ui, attrs } = useUI('table', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
const appConfig = useAppConfig()
|
|
||||||
|
|
||||||
const ui = computed<Partial<typeof appConfig.ui.table>>(() => defuTwMerge({}, props.ui, appConfig.ui.table))
|
const columns = computed(() => props.columns ?? Object.keys(omit(props.rows[0] ?? {}, ['click'])).map((key) => ({ key, label: upperFirst(key), sortable: false })))
|
||||||
|
|
||||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
|
||||||
|
|
||||||
const columns = computed(() => props.columns ?? Object.keys(omit(props.rows[0] ?? {}, ['click'])).map((key) => ({ key, label: capitalize(key), sortable: false })))
|
|
||||||
|
|
||||||
const sort = ref(defu({}, props.sort, { column: null, direction: 'asc' }))
|
const sort = ref(defu({}, props.sort, { column: null, direction: 'asc' }))
|
||||||
|
|
||||||
@@ -169,7 +167,20 @@ export default defineComponent({
|
|||||||
|
|
||||||
const { column, direction } = sort.value
|
const { column, direction } = sort.value
|
||||||
|
|
||||||
return orderBy(props.rows, column, direction)
|
return props.rows.slice().sort((a, b) => {
|
||||||
|
const aValue = a[column]
|
||||||
|
const bValue = b[column]
|
||||||
|
|
||||||
|
if (aValue === bValue) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (direction === 'asc') {
|
||||||
|
return aValue < bValue ? -1 : 1
|
||||||
|
} else {
|
||||||
|
return aValue > bValue ? -1 : 1
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const selected = computed({
|
const selected = computed({
|
||||||
@@ -224,12 +235,12 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onSelect (row) {
|
function onSelect (row) {
|
||||||
if (!attrs.onSelect) {
|
if (!$attrs.onSelect) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
attrs.onSelect(row)
|
$attrs.onSelect(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectAllRows () {
|
function selectAllRows () {
|
||||||
@@ -239,7 +250,8 @@ export default defineComponent({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
onSelect(row)
|
// @ts-ignore
|
||||||
|
$attrs.onSelect ? $attrs.onSelect(row) : selected.value.push(row)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,10 +268,9 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attrs: omit(attrs, ['class']),
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
ui,
|
ui,
|
||||||
wrapperClass,
|
attrs,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
sort,
|
sort,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="wrapperClass">
|
<div :class="ui.wrapper">
|
||||||
<HDisclosure v-for="(item, index) in items" v-slot="{ open, close }" :key="index" :default-open="defaultOpen || item.defaultOpen">
|
<HDisclosure v-for="(item, index) in items" v-slot="{ open, close }" :key="index" :default-open="defaultOpen || item.defaultOpen">
|
||||||
<HDisclosureButton :ref="() => buttonRefs[index] = close" as="template" :disabled="item.disabled">
|
<HDisclosureButton :ref="() => buttonRefs[index] = close" as="template" :disabled="item.disabled">
|
||||||
<slot :item="item" :index="index" :open="open" :close="close">
|
<slot :item="item" :index="index" :open="open" :close="close">
|
||||||
@@ -40,20 +40,22 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ref, computed, defineComponent } from 'vue'
|
import { ref, computed, toRef, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { Disclosure as HDisclosure, DisclosureButton as HDisclosureButton, DisclosurePanel as HDisclosurePanel } from '@headlessui/vue'
|
import { Disclosure as HDisclosure, DisclosureButton as HDisclosureButton, DisclosurePanel as HDisclosurePanel } from '@headlessui/vue'
|
||||||
import { omit } from 'lodash-es'
|
|
||||||
import { twMerge } from 'tailwind-merge'
|
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import UButton from '../elements/Button.vue'
|
import UButton from '../elements/Button.vue'
|
||||||
import { defuTwMerge } from '../../utils'
|
import { useUI } from '../../composables/useUI'
|
||||||
|
import { mergeConfig, omit } from '../../utils'
|
||||||
import StateEmitter from '../../utils/StateEmitter'
|
import StateEmitter from '../../utils/StateEmitter'
|
||||||
import type { AccordionItem } from '../../types/accordion'
|
import type { AccordionItem, Strategy } from '../../types'
|
||||||
import { useAppConfig } from '#imports'
|
|
||||||
// TODO: Remove
|
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
import { accordion, button } from '#ui/ui.config'
|
||||||
|
|
||||||
|
const config = mergeConfig<typeof accordion>(appConfig.ui.strategy, appConfig.ui.accordion, accordion)
|
||||||
|
|
||||||
|
const configButton = mergeConfig<typeof button>(appConfig.ui.strategy, appConfig.ui.button, button)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
@@ -76,43 +78,47 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
openIcon: {
|
openIcon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => appConfig.ui.accordion.default.openIcon
|
default: () => config.default.openIcon
|
||||||
},
|
},
|
||||||
closeIcon: {
|
closeIcon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => appConfig.ui.accordion.default.closeIcon
|
default: () => config.default.closeIcon
|
||||||
},
|
},
|
||||||
multiple: {
|
multiple: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
|
class: {
|
||||||
|
type: [String, Object, Array] as PropType<any>,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof appConfig.ui.accordion>>,
|
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
||||||
default: () => ({})
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props, { attrs }) {
|
setup (props) {
|
||||||
// TODO: Remove
|
const { ui, attrs } = useUI('accordion', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
const appConfig = useAppConfig()
|
|
||||||
|
|
||||||
const ui = computed<Partial<typeof appConfig.ui.accordion>>(() => defuTwMerge({}, props.ui, appConfig.ui.accordion))
|
const uiButton = computed<Partial<typeof configButton>>(() => configButton)
|
||||||
|
|
||||||
const uiButton = computed<Partial<typeof appConfig.ui.button>>(() => appConfig.ui.button)
|
|
||||||
|
|
||||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
|
||||||
|
|
||||||
const buttonRefs = ref<Function[]>([])
|
const buttonRefs = ref<Function[]>([])
|
||||||
|
|
||||||
function closeOthers (itemIndex: number) {
|
function closeOthers (currentIndex: number) {
|
||||||
if (!props.items[itemIndex].closeOthers && props.multiple) {
|
if (!props.items[currentIndex].closeOthers && props.multiple) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
buttonRefs.value.forEach((close, index) => {
|
const totalItems = buttonRefs.value.length
|
||||||
if (index === itemIndex) return
|
|
||||||
|
|
||||||
|
const order = Array.from({ length: totalItems }, (_, i) => (currentIndex + i) % totalItems)
|
||||||
|
.filter(index => index !== currentIndex)
|
||||||
|
.reverse()
|
||||||
|
|
||||||
|
for (const index of order) {
|
||||||
|
const close = buttonRefs.value[index]
|
||||||
close()
|
close()
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onEnter (el: HTMLElement, done) {
|
function onEnter (el: HTMLElement, done) {
|
||||||
@@ -139,11 +145,10 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attrs: omit(attrs, ['class']),
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
ui,
|
ui,
|
||||||
uiButton,
|
uiButton,
|
||||||
wrapperClass,
|
attrs,
|
||||||
buttonRefs,
|
buttonRefs,
|
||||||
closeOthers,
|
closeOthers,
|
||||||
omit,
|
omit,
|
||||||
|
|||||||
@@ -25,29 +25,28 @@
|
|||||||
<UButton v-for="(action, index) of actions" :key="index" v-bind="{ ...ui.default.actionButton, ...action }" @click.stop="action.click" />
|
<UButton v-for="(action, index) of actions" :key="index" v-bind="{ ...ui.default.actionButton, ...action }" @click.stop="action.click" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UButton v-if="closeButton" v-bind="{ ...ui.default.closeButton, ...closeButton }" @click.stop="$emit('close')" />
|
<UButton v-if="closeButton" aria-label="Close" v-bind="{ ...ui.default.closeButton, ...closeButton }" @click.stop="$emit('close')" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent } from 'vue'
|
import { computed, toRef, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twMerge, twJoin } from 'tailwind-merge'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import UAvatar from '../elements/Avatar.vue'
|
import UAvatar from '../elements/Avatar.vue'
|
||||||
import UButton from '../elements/Button.vue'
|
import UButton from '../elements/Button.vue'
|
||||||
import type { Avatar } from '../../types/avatar'
|
import { useUI } from '../../composables/useUI'
|
||||||
import type { Button } from '../../types/button'
|
import type { Avatar, Button, NestedKeyOf, Strategy } from '../../types'
|
||||||
import { defuTwMerge } from '../../utils'
|
import { mergeConfig } from '../../utils'
|
||||||
import { useAppConfig } from '#imports'
|
|
||||||
// TODO: Remove
|
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
import { omit } from 'lodash-es'
|
import { alert } from '#ui/ui.config'
|
||||||
|
import colors from '#ui-colors'
|
||||||
|
|
||||||
// const appConfig = useAppConfig()
|
const config = mergeConfig<typeof alert>(appConfig.ui.strategy, appConfig.ui.alert, alert)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
@@ -67,7 +66,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => appConfig.ui.alert.default.icon
|
default: () => config.default.icon
|
||||||
},
|
},
|
||||||
avatar: {
|
avatar: {
|
||||||
type: Object as PropType<Avatar>,
|
type: Object as PropType<Avatar>,
|
||||||
@@ -75,40 +74,41 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
closeButton: {
|
closeButton: {
|
||||||
type: Object as PropType<Button>,
|
type: Object as PropType<Button>,
|
||||||
default: () => appConfig.ui.alert.default.closeButton
|
default: () => config.default.closeButton as Button
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
type: Array as PropType<(Button & { click?: Function })[]>,
|
type: Array as PropType<(Button & { click?: Function })[]>,
|
||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
type: String,
|
type: String as PropType<keyof typeof config.color | typeof colors[number]>,
|
||||||
default: () => appConfig.ui.alert.default.color,
|
default: () => config.default.color,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return [...appConfig.ui.colors, ...Object.keys(appConfig.ui.alert.color)].includes(value)
|
return [...appConfig.ui.colors, ...Object.keys(config.color)].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
variant: {
|
variant: {
|
||||||
type: String,
|
type: String as PropType<keyof typeof config.variant | NestedKeyOf<typeof config.color>>,
|
||||||
default: () => appConfig.ui.alert.default.variant,
|
default: () => config.default.variant,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return [
|
return [
|
||||||
...Object.keys(appConfig.ui.alert.variant),
|
...Object.keys(config.variant),
|
||||||
...Object.values(appConfig.ui.alert.color).flatMap(value => Object.keys(value))
|
...Object.values(config.color).flatMap(value => Object.keys(value))
|
||||||
].includes(value)
|
].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
class: {
|
||||||
|
type: [String, Object, Array] as PropType<any>,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof appConfig.ui.alert>>,
|
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
||||||
default: () => ({})
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['close'],
|
emits: ['close'],
|
||||||
setup (props, { attrs }) {
|
setup (props) {
|
||||||
// TODO: Remove
|
const { ui, attrs } = useUI('alert', toRef(props, 'ui'), config)
|
||||||
const appConfig = useAppConfig()
|
|
||||||
|
|
||||||
const ui = computed<Partial<typeof appConfig.ui.alert>>(() => defuTwMerge({}, props.ui, appConfig.ui.alert))
|
|
||||||
|
|
||||||
const alertClass = computed(() => {
|
const alertClass = computed(() => {
|
||||||
const variant = ui.value.color?.[props.color as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
const variant = ui.value.color?.[props.color as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
||||||
@@ -119,13 +119,13 @@ export default defineComponent({
|
|||||||
ui.value.shadow,
|
ui.value.shadow,
|
||||||
ui.value.padding,
|
ui.value.padding,
|
||||||
variant?.replaceAll('{color}', props.color)
|
variant?.replaceAll('{color}', props.color)
|
||||||
), attrs.class as string)
|
), props.class)
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attrs: omit(attrs, ['class']),
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
ui,
|
ui,
|
||||||
|
attrs,
|
||||||
alertClass
|
alertClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,18 +20,18 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref, computed, watch } from 'vue'
|
import { defineComponent, ref, computed, toRef, watch } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twMerge, twJoin } from 'tailwind-merge'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import { defuTwMerge } from '../../utils'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useAppConfig } from '#imports'
|
import { mergeConfig } from '../../utils'
|
||||||
// TODO: Remove
|
import type { AvatarSize, AvatarChipColor, AvatarChipPosition, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
import { omit } from 'lodash-es'
|
import { avatar } from '#ui/ui.config'
|
||||||
|
|
||||||
// const appConfig = useAppConfig()
|
const config = mergeConfig<typeof avatar>(appConfig.ui.strategy, appConfig.ui.avatar, avatar)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
@@ -53,27 +53,27 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => appConfig.ui.avatar.default.icon
|
default: () => config.default.icon
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String as PropType<AvatarSize>,
|
||||||
default: () => appConfig.ui.avatar.default.size,
|
default: () => config.default.size,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(appConfig.ui.avatar.size).includes(value)
|
return Object.keys(config.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
chipColor: {
|
chipColor: {
|
||||||
type: String,
|
type: String as PropType<AvatarChipColor>,
|
||||||
default: () => appConfig.ui.avatar.default.chipColor,
|
default: () => config.default.chipColor,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return ['gray', ...appConfig.ui.colors].includes(value)
|
return ['gray', ...appConfig.ui.colors].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
chipPosition: {
|
chipPosition: {
|
||||||
type: String,
|
type: String as PropType<AvatarChipPosition>,
|
||||||
default: () => appConfig.ui.avatar.default.chipPosition,
|
default: () => config.default.chipPosition,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(appConfig.ui.avatar.chip.position).includes(value)
|
return Object.keys(config.chip.position).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
chipText: {
|
chipText: {
|
||||||
@@ -84,16 +84,17 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
},
|
},
|
||||||
|
class: {
|
||||||
|
type: [String, Object, Array] as PropType<any>,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof appConfig.ui.avatar>>,
|
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
||||||
default: () => ({})
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props, { attrs }) {
|
setup (props) {
|
||||||
// TODO: Remove
|
const { ui, attrs } = useUI('avatar', toRef(props, 'ui'), config)
|
||||||
const appConfig = useAppConfig()
|
|
||||||
|
|
||||||
const ui = computed<Partial<typeof appConfig.ui.avatar>>(() => defuTwMerge({}, props.ui, appConfig.ui.avatar))
|
|
||||||
|
|
||||||
const url = computed(() => {
|
const url = computed(() => {
|
||||||
if (typeof props.src === 'boolean') {
|
if (typeof props.src === 'boolean') {
|
||||||
@@ -112,7 +113,7 @@ export default defineComponent({
|
|||||||
(error.value || !url.value) && ui.value.background,
|
(error.value || !url.value) && ui.value.background,
|
||||||
ui.value.rounded,
|
ui.value.rounded,
|
||||||
ui.value.size[props.size]
|
ui.value.size[props.size]
|
||||||
), attrs.class as string)
|
), props.class)
|
||||||
})
|
})
|
||||||
|
|
||||||
const imgClass = computed(() => {
|
const imgClass = computed(() => {
|
||||||
@@ -151,7 +152,9 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attrs: omit(attrs, ['class']),
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
ui,
|
||||||
|
attrs,
|
||||||
wrapperClass,
|
wrapperClass,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
imgClass,
|
imgClass,
|
||||||
|
|||||||
@@ -1,40 +1,43 @@
|
|||||||
import { h, cloneVNode, computed, defineComponent } from 'vue'
|
import { h, cloneVNode, computed, toRef, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { omit } from 'lodash-es'
|
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twMerge, twJoin } from 'tailwind-merge'
|
||||||
import { defuTwMerge, getSlotsChildren } from '../../utils'
|
import UAvatar from './Avatar.vue'
|
||||||
import Avatar from './Avatar.vue'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useAppConfig } from '#imports'
|
import { mergeConfig, getSlotsChildren } from '../../utils'
|
||||||
// TODO: Remove
|
import type { AvatarSize, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
import { avatar, avatarGroup } from '#ui/ui.config'
|
||||||
|
|
||||||
// const appConfig = useAppConfig()
|
const avatarConfig = mergeConfig<typeof avatar>(appConfig.ui.strategy, appConfig.ui.avatar, avatar)
|
||||||
|
|
||||||
|
const avatarGroupConfig = mergeConfig<typeof avatarGroup>(appConfig.ui.strategy, appConfig.ui.avatarGroup, avatarGroup)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: {
|
props: {
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String as PropType<keyof typeof avatarConfig.size>,
|
||||||
default: null,
|
default: null,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(appConfig.ui.avatar.size).includes(value)
|
return Object.keys(avatarConfig.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
max: {
|
max: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
|
class: {
|
||||||
|
type: [String, Object, Array] as PropType<any>,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof appConfig.ui.avatarGroup>>,
|
type: Object as PropType<Partial<typeof avatarGroupConfig & { strategy?: Strategy }>>,
|
||||||
default: () => ({})
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props, { attrs, slots }) {
|
setup (props, { slots }) {
|
||||||
// TODO: Remove
|
const { ui, attrs } = useUI('avatarGroup', toRef(props, 'ui'), avatarGroupConfig, toRef(props, 'class'))
|
||||||
const appConfig = useAppConfig()
|
|
||||||
|
|
||||||
const ui = computed<Partial<typeof appConfig.ui.avatarGroup>>(() => defuTwMerge({}, props.ui, appConfig.ui.avatarGroup))
|
|
||||||
|
|
||||||
const children = computed(() => getSlotsChildren(slots))
|
const children = computed(() => getSlotsChildren(slots))
|
||||||
|
|
||||||
@@ -55,8 +58,8 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (max.value !== undefined && index === max.value) {
|
if (max.value !== undefined && index === max.value) {
|
||||||
return h(Avatar, {
|
return h(UAvatar, {
|
||||||
size: props.size || appConfig.ui.avatar.default.size,
|
size: props.size || (avatarConfig.default.size as AvatarSize),
|
||||||
text: `+${children.value.length - max.value}`,
|
text: `+${children.value.length - max.value}`,
|
||||||
class: twJoin(ui.value.ring, ui.value.margin)
|
class: twJoin(ui.value.ring, ui.value.margin)
|
||||||
})
|
})
|
||||||
@@ -65,6 +68,6 @@ export default defineComponent({
|
|||||||
return null
|
return null
|
||||||
}).filter(Boolean).reverse())
|
}).filter(Boolean).reverse())
|
||||||
|
|
||||||
return () => h('div', { class: twMerge(ui.value.wrapper, attrs.class as string), ...omit(attrs, ['class']) }, clones.value)
|
return () => h('div', { class: ui.value.wrapper, ...attrs.value }, clones.value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,42 +5,42 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent } from 'vue'
|
import { computed, toRef, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { omit } from 'lodash-es'
|
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twMerge, twJoin } from 'tailwind-merge'
|
||||||
import { defuTwMerge } from '../../utils'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useAppConfig } from '#imports'
|
import { mergeConfig } from '../../utils'
|
||||||
// TODO: Remove
|
import type { BadgeColor, BadgeSize, BadgeVariant, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
import { badge } from '#ui/ui.config'
|
||||||
|
|
||||||
// const appConfig = useAppConfig()
|
const config = mergeConfig<typeof badge>(appConfig.ui.strategy, appConfig.ui.badge, badge)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: {
|
props: {
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String as PropType<BadgeSize>,
|
||||||
default: () => appConfig.ui.badge.default.size,
|
default: () => config.default.size,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(appConfig.ui.badge.size).includes(value)
|
return Object.keys(config.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
type: String,
|
type: String as PropType<BadgeColor>,
|
||||||
default: () => appConfig.ui.badge.default.color,
|
default: () => config.default.color,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return [...appConfig.ui.colors, ...Object.keys(appConfig.ui.badge.color)].includes(value)
|
return [...appConfig.ui.colors, ...Object.keys(config.color)].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
variant: {
|
variant: {
|
||||||
type: String,
|
type: String as PropType<BadgeVariant>,
|
||||||
default: () => appConfig.ui.badge.default.variant,
|
default: () => config.default.variant,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return [
|
return [
|
||||||
...Object.keys(appConfig.ui.badge.variant),
|
...Object.keys(config.variant),
|
||||||
...Object.values(appConfig.ui.badge.color).flatMap(value => Object.keys(value))
|
...Object.values(config.color).flatMap(value => Object.keys(value))
|
||||||
].includes(value)
|
].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -48,16 +48,17 @@ export default defineComponent({
|
|||||||
type: [String, Number],
|
type: [String, Number],
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
|
class: {
|
||||||
|
type: [String, Object, Array] as PropType<any>,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof appConfig.ui.badge>>,
|
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
||||||
default: () => ({})
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props, { attrs }) {
|
setup (props) {
|
||||||
// TODO: Remove
|
const { ui, attrs } = useUI('badge', toRef(props, 'ui'), config)
|
||||||
const appConfig = useAppConfig()
|
|
||||||
|
|
||||||
const ui = computed<Partial<typeof appConfig.ui.badge>>(() => defuTwMerge({}, props.ui, appConfig.ui.badge))
|
|
||||||
|
|
||||||
const badgeClass = computed(() => {
|
const badgeClass = computed(() => {
|
||||||
const variant = ui.value.color?.[props.color as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
const variant = ui.value.color?.[props.color as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
||||||
@@ -68,11 +69,11 @@ export default defineComponent({
|
|||||||
ui.value.rounded,
|
ui.value.rounded,
|
||||||
ui.value.size[props.size],
|
ui.value.size[props.size],
|
||||||
variant?.replaceAll('{color}', props.color)
|
variant?.replaceAll('{color}', props.color)
|
||||||
), attrs.class as string)
|
), props.class)
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attrs: omit(attrs, ['class']),
|
attrs,
|
||||||
badgeClass
|
badgeClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,19 +17,19 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent } from 'vue'
|
import { computed, defineComponent, toRef } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { omit } from 'lodash-es'
|
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twMerge, twJoin } from 'tailwind-merge'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import ULink from '../elements/Link.vue'
|
import ULink from '../elements/Link.vue'
|
||||||
import { defuTwMerge } from '../../utils'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useAppConfig } from '#imports'
|
import { mergeConfig } from '../../utils'
|
||||||
// TODO: Remove
|
import type { ButtonColor, ButtonSize, ButtonVariant, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
import { button } from '#ui/ui.config'
|
||||||
|
|
||||||
// const appConfig = useAppConfig()
|
const config = mergeConfig<typeof button>(appConfig.ui.strategy, appConfig.ui.button, button)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
@@ -63,26 +63,26 @@ export default defineComponent({
|
|||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String as PropType<ButtonSize>,
|
||||||
default: () => appConfig.ui.button.default.size,
|
default: () => config.default.size,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(appConfig.ui.button.size).includes(value)
|
return Object.keys(config.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
type: String,
|
type: String as PropType<ButtonColor>,
|
||||||
default: () => appConfig.ui.button.default.color,
|
default: () => config.default.color,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return [...appConfig.ui.colors, ...Object.keys(appConfig.ui.button.color)].includes(value)
|
return [...appConfig.ui.colors, ...Object.keys(config.color)].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
variant: {
|
variant: {
|
||||||
type: String,
|
type: String as PropType<ButtonVariant>,
|
||||||
default: () => appConfig.ui.button.default.variant,
|
default: () => config.default.variant,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return [
|
return [
|
||||||
...Object.keys(appConfig.ui.button.variant),
|
...Object.keys(config.variant),
|
||||||
...Object.values(appConfig.ui.button.color).flatMap(value => Object.keys(value))
|
...Object.values(config.color).flatMap(value => Object.keys(value))
|
||||||
].includes(value)
|
].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -92,7 +92,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
loadingIcon: {
|
loadingIcon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => appConfig.ui.button.default.loadingIcon
|
default: () => config.default.loadingIcon
|
||||||
},
|
},
|
||||||
leadingIcon: {
|
leadingIcon: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -118,16 +118,17 @@ export default defineComponent({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
|
class: {
|
||||||
|
type: [String, Object, Array] as PropType<any>,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof appConfig.ui.button>>,
|
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
||||||
default: () => ({})
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props, { attrs, slots }) {
|
setup (props, { slots }) {
|
||||||
// TODO: Remove
|
const { ui, attrs } = useUI('button', toRef(props, 'ui'), config)
|
||||||
const appConfig = useAppConfig()
|
|
||||||
|
|
||||||
const ui = computed<Partial<typeof appConfig.ui.button>>(() => defuTwMerge({}, props.ui, appConfig.ui.button))
|
|
||||||
|
|
||||||
const isLeading = computed(() => {
|
const isLeading = computed(() => {
|
||||||
return (props.icon && props.leading) || (props.icon && !props.trailing) || (props.loading && !props.trailing) || props.leadingIcon
|
return (props.icon && props.leading) || (props.icon && !props.trailing) || (props.loading && !props.trailing) || props.leadingIcon
|
||||||
@@ -151,7 +152,7 @@ export default defineComponent({
|
|||||||
props.padded && ui.value[isSquare.value ? 'square' : 'padding'][props.size],
|
props.padded && ui.value[isSquare.value ? 'square' : 'padding'][props.size],
|
||||||
variant?.replaceAll('{color}', props.color),
|
variant?.replaceAll('{color}', props.color),
|
||||||
props.block ? 'w-full flex justify-center items-center' : 'inline-flex items-center'
|
props.block ? 'w-full flex justify-center items-center' : 'inline-flex items-center'
|
||||||
), attrs.class as string)
|
), props.class)
|
||||||
})
|
})
|
||||||
|
|
||||||
const leadingIconName = computed(() => {
|
const leadingIconName = computed(() => {
|
||||||
@@ -187,7 +188,7 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attrs: omit(attrs, ['class']),
|
attrs,
|
||||||
isLeading,
|
isLeading,
|
||||||
isTrailing,
|
isTrailing,
|
||||||
isSquare,
|
isSquare,
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
import { h, cloneVNode, computed, defineComponent } from 'vue'
|
import { h, cloneVNode, computed, toRef, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { omit } from 'lodash-es'
|
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twMerge, twJoin } from 'tailwind-merge'
|
||||||
import { defuTwMerge, getSlotsChildren } from '../../utils'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useAppConfig } from '#imports'
|
import { mergeConfig, getSlotsChildren } from '../../utils'
|
||||||
// TODO: Remove
|
import type { ButtonSize, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
import { button, buttonGroup } from '#ui/ui.config'
|
||||||
|
|
||||||
// const appConfig = useAppConfig()
|
const buttonConfig = mergeConfig<typeof button>(appConfig.ui.strategy, appConfig.ui.button, button)
|
||||||
|
const buttonGroupConfig = mergeConfig<typeof buttonGroup>(appConfig.ui.strategy, appConfig.ui.buttonGroup, buttonGroup)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: {
|
props: {
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String as PropType<ButtonSize>,
|
||||||
default: null,
|
default: null,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(appConfig.ui.button.size).includes(value)
|
return Object.keys(buttonConfig.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
orientation: {
|
orientation: {
|
||||||
@@ -27,16 +28,17 @@ export default defineComponent({
|
|||||||
return ['horizontal', 'vertical'].includes(value)
|
return ['horizontal', 'vertical'].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
class: {
|
||||||
|
type: [String, Object, Array] as PropType<any>,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof appConfig.ui.buttonGroup>>,
|
type: Object as PropType<Partial<typeof buttonGroupConfig & { strategy?: Strategy }>>,
|
||||||
default: () => ({})
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props, { attrs, slots }) {
|
setup (props, { slots }) {
|
||||||
// TODO: Remove
|
const { ui, attrs } = useUI('buttonGroup', toRef(props, 'ui'), buttonGroupConfig)
|
||||||
const appConfig = useAppConfig()
|
|
||||||
|
|
||||||
const ui = computed<Partial<typeof appConfig.ui.buttonGroup>>(() => defuTwMerge({}, props.ui, appConfig.ui.buttonGroup))
|
|
||||||
|
|
||||||
const children = computed(() => getSlotsChildren(slots))
|
const children = computed(() => getSlotsChildren(slots))
|
||||||
|
|
||||||
@@ -58,12 +60,6 @@ export default defineComponent({
|
|||||||
const clones = computed(() => children.value.map((node, index) => {
|
const clones = computed(() => children.value.map((node, index) => {
|
||||||
const vProps: any = {}
|
const vProps: any = {}
|
||||||
|
|
||||||
if (props.orientation === 'vertical') {
|
|
||||||
ui.value.wrapper = 'flex flex-col -space-y-px'
|
|
||||||
} else {
|
|
||||||
ui.value.wrapper = 'inline-flex -space-x-px'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.size) {
|
if (props.size) {
|
||||||
vProps.size = props.size
|
vProps.size = props.size
|
||||||
}
|
}
|
||||||
@@ -83,6 +79,14 @@ export default defineComponent({
|
|||||||
return cloneVNode(node, vProps)
|
return cloneVNode(node, vProps)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return () => h('div', { class: twMerge(twJoin(ui.value.wrapper, ui.value.rounded, ui.value.shadow), attrs.class as string), ...omit(attrs, ['class']) }, clones.value)
|
const wrapperClass = computed(() => {
|
||||||
|
return twMerge(twJoin(
|
||||||
|
ui.value.wrapper[props.orientation],
|
||||||
|
ui.value.rounded,
|
||||||
|
ui.value.shadow
|
||||||
|
), props.class)
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => h('div', { class: wrapperClass.value, ...attrs.value }, clones.value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<HMenu v-slot="{ open }" as="div" :class="wrapperClass" v-bind="attrs" @mouseleave="onMouseLeave">
|
<HMenu v-slot="{ open }" as="div" :class="ui.wrapper" v-bind="attrs" @mouseleave="onMouseLeave">
|
||||||
<HMenuButton
|
<HMenuButton
|
||||||
ref="trigger"
|
ref="trigger"
|
||||||
as="div"
|
as="div"
|
||||||
@@ -45,26 +45,23 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref, computed, onMounted } from 'vue'
|
import { defineComponent, ref, computed, toRef, onMounted } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { Menu as HMenu, MenuButton as HMenuButton, MenuItems as HMenuItems, MenuItem as HMenuItem } from '@headlessui/vue'
|
import { Menu as HMenu, MenuButton as HMenuButton, MenuItems as HMenuItems, MenuItem as HMenuItem } from '@headlessui/vue'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { omit } from 'lodash-es'
|
|
||||||
import { twMerge } from 'tailwind-merge'
|
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import UAvatar from '../elements/Avatar.vue'
|
import UAvatar from '../elements/Avatar.vue'
|
||||||
import UKbd from '../elements/Kbd.vue'
|
import UKbd from '../elements/Kbd.vue'
|
||||||
import ULink from '../elements/Link.vue'
|
import ULink from '../elements/Link.vue'
|
||||||
|
import { useUI } from '../../composables/useUI'
|
||||||
import { usePopper } from '../../composables/usePopper'
|
import { usePopper } from '../../composables/usePopper'
|
||||||
import { defuTwMerge } from '../../utils'
|
import { mergeConfig, omit } from '../../utils'
|
||||||
import type { DropdownItem } from '../../types/dropdown'
|
import type { DropdownItem, PopperOptions, Strategy } from '../../types'
|
||||||
import type { PopperOptions } from '../../types/popper'
|
|
||||||
import { useAppConfig } from '#imports'
|
|
||||||
// TODO: Remove
|
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
import { dropdown } from '#ui/ui.config'
|
||||||
|
|
||||||
// const appConfig = useAppConfig()
|
const config = mergeConfig<typeof dropdown>(appConfig.ui.strategy, appConfig.ui.dropdown, dropdown)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
@@ -104,16 +101,17 @@ export default defineComponent({
|
|||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0
|
||||||
},
|
},
|
||||||
|
class: {
|
||||||
|
type: [String, Object, Array] as PropType<any>,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof appConfig.ui.dropdown>>,
|
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
||||||
default: () => ({})
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props, { attrs }) {
|
setup (props) {
|
||||||
// TODO: Remove
|
const { ui, attrs } = useUI('dropdown', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
const appConfig = useAppConfig()
|
|
||||||
|
|
||||||
const ui = computed<Partial<typeof appConfig.ui.dropdown>>(() => defuTwMerge({}, props.ui, appConfig.ui.dropdown))
|
|
||||||
|
|
||||||
const popper = computed<PopperOptions>(() => defu(props.mode === 'hover' ? { offsetDistance: 0 } : {}, props.popper, ui.value.popper as PopperOptions))
|
const popper = computed<PopperOptions>(() => defu(props.mode === 'hover' ? { offsetDistance: 0 } : {}, props.popper, ui.value.popper as PopperOptions))
|
||||||
|
|
||||||
@@ -143,8 +141,6 @@ export default defineComponent({
|
|||||||
return props.mode === 'hover' ? { paddingTop: `${offsetDistance}px`, paddingBottom: `${offsetDistance}px` } : {}
|
return props.mode === 'hover' ? { paddingTop: `${offsetDistance}px`, paddingBottom: `${offsetDistance}px` } : {}
|
||||||
})
|
})
|
||||||
|
|
||||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
|
||||||
|
|
||||||
function onMouseOver () {
|
function onMouseOver () {
|
||||||
if (props.mode !== 'hover' || !menuApi.value) {
|
if (props.mode !== 'hover' || !menuApi.value) {
|
||||||
return
|
return
|
||||||
@@ -186,13 +182,12 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attrs: omit(attrs, ['class']),
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
ui,
|
ui,
|
||||||
|
attrs,
|
||||||
trigger,
|
trigger,
|
||||||
container,
|
container,
|
||||||
containerStyle,
|
containerStyle,
|
||||||
wrapperClass,
|
|
||||||
onMouseOver,
|
onMouseOver,
|
||||||
onMouseLeave,
|
onMouseLeave,
|
||||||
omit
|
omit
|
||||||
|
|||||||
@@ -5,17 +5,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, computed } from 'vue'
|
import { toRef, defineComponent, computed } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { omit } from 'lodash-es'
|
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twMerge, twJoin } from 'tailwind-merge'
|
||||||
import { defuTwMerge } from '../../utils'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useAppConfig } from '#imports'
|
import { mergeConfig } from '../../utils'
|
||||||
// TODO: Remove
|
import type { Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
import { kbd } from '#ui/ui.config'
|
||||||
|
|
||||||
// const appConfig = useAppConfig()
|
const config = mergeConfig<typeof kbd>(appConfig.ui.strategy, appConfig.ui.kbd, kbd)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
@@ -25,22 +25,23 @@ export default defineComponent({
|
|||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String as PropType<keyof typeof config.size>,
|
||||||
default: () => appConfig.ui.kbd.default.size,
|
default: () => config.default.size,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(appConfig.ui.kbd.size).includes(value)
|
return Object.keys(config.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
class: {
|
||||||
|
type: [String, Object, Array] as PropType<any>,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof appConfig.ui.kbd>>,
|
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
||||||
default: () => ({})
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props, { attrs }) {
|
setup (props) {
|
||||||
// TODO: Remove
|
const { ui, attrs } = useUI('kbd', toRef(props, 'ui'), config)
|
||||||
const appConfig = useAppConfig()
|
|
||||||
|
|
||||||
const ui = computed<Partial<typeof appConfig.ui.kbd>>(() => defuTwMerge({}, props.ui, appConfig.ui.kbd))
|
|
||||||
|
|
||||||
const kbdClass = computed(() => {
|
const kbdClass = computed(() => {
|
||||||
return twMerge(twJoin(
|
return twMerge(twJoin(
|
||||||
@@ -51,13 +52,13 @@ export default defineComponent({
|
|||||||
ui.value.font,
|
ui.value.font,
|
||||||
ui.value.background,
|
ui.value.background,
|
||||||
ui.value.ring
|
ui.value.ring
|
||||||
), attrs.class as string)
|
), props.class)
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attrs: omit(attrs, ['class']),
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
ui,
|
ui,
|
||||||
|
attrs,
|
||||||
kbdClass
|
kbdClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<button v-if="!to" :type="type" :disabled="disabled" v-bind="$attrs" :class="inactiveClass">
|
<component
|
||||||
|
:is="as"
|
||||||
|
v-if="!to"
|
||||||
|
:disabled="disabled"
|
||||||
|
v-bind="$attrs"
|
||||||
|
:class="inactiveClass"
|
||||||
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</button>
|
</component>
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
v-else
|
v-else
|
||||||
v-slot="{ route, href, target, rel, navigate, isActive, isExactActive, isExternal }"
|
v-slot="{ route, href, target, rel, navigate, isActive, isExactActive, isExternal }"
|
||||||
@@ -24,7 +30,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { isEqual } from 'lodash-es'
|
import { isEqual } from 'ohash'
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
import { NuxtLink } from '#components'
|
import { NuxtLink } from '#components'
|
||||||
|
|
||||||
@@ -32,14 +38,18 @@ export default defineComponent({
|
|||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: {
|
props: {
|
||||||
...NuxtLink.props,
|
...NuxtLink.props,
|
||||||
type: {
|
as: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null
|
default: 'button'
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
|
active: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
exact: {
|
exact: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
@@ -59,6 +69,10 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
setup (props) {
|
setup (props) {
|
||||||
function resolveLinkClass (route, $route, { isActive, isExactActive }: { isActive: boolean, isExactActive: boolean }) {
|
function resolveLinkClass (route, $route, { isActive, isExactActive }: { isActive: boolean, isExactActive: boolean }) {
|
||||||
|
if (props.active) {
|
||||||
|
return props.activeClass
|
||||||
|
}
|
||||||
|
|
||||||
if (props.exactQuery && !isEqual(route.query, $route.query)) {
|
if (props.exactQuery && !isEqual(route.query, $route.query)) {
|
||||||
return props.inactiveClass
|
return props.inactiveClass
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="wrapperClass">
|
<div :class="ui.wrapper">
|
||||||
<div class="flex items-center h-5">
|
<div class="flex items-center h-5">
|
||||||
<input
|
<input
|
||||||
:id="name"
|
:id="inputId"
|
||||||
v-model="toggle"
|
v-model="toggle"
|
||||||
:name="name"
|
:name="name"
|
||||||
:required="required"
|
:required="required"
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="label || $slots.label" class="ms-3 text-sm">
|
<div v-if="label || $slots.label" class="ms-3 text-sm">
|
||||||
<label :for="name" :class="ui.label">
|
<label :for="inputId" :class="ui.label">
|
||||||
<slot name="label">{{ label }}</slot>
|
<slot name="label">{{ label }}</slot>
|
||||||
<span v-if="required" :class="ui.required">*</span>
|
<span v-if="required" :class="ui.required">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -30,22 +30,29 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent } from 'vue'
|
import { computed, toRef, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { omit } from 'lodash-es'
|
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twMerge, twJoin } from 'tailwind-merge'
|
||||||
import { defuTwMerge } from '../../utils'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useFormGroup } from '../../composables/useFormGroup'
|
import { useFormGroup } from '../../composables/useFormGroup'
|
||||||
import { useAppConfig } from '#imports'
|
import { mergeConfig } from '../../utils'
|
||||||
// TODO: Remove
|
import { uid } from '../../utils/uid'
|
||||||
|
import type { Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
import { checkbox } from '#ui/ui.config'
|
||||||
|
import colors from '#ui-colors'
|
||||||
|
|
||||||
// const appConfig = useAppConfig()
|
const config = mergeConfig<typeof checkbox>(appConfig.ui.strategy, appConfig.ui.checkbox, checkbox)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: {
|
props: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
// A default value is needed here to bind the label
|
||||||
|
default: () => uid()
|
||||||
|
},
|
||||||
value: {
|
value: {
|
||||||
type: [String, Number, Boolean, Object],
|
type: [String, Number, Boolean, Object],
|
||||||
default: null
|
default: null
|
||||||
@@ -83,8 +90,8 @@ export default defineComponent({
|
|||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
type: String,
|
type: String as PropType<typeof colors[number]>,
|
||||||
default: () => appConfig.ui.checkbox.default.color,
|
default: () => config.default.color,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return appConfig.ui.colors.includes(value)
|
return appConfig.ui.colors.includes(value)
|
||||||
}
|
}
|
||||||
@@ -93,20 +100,20 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
},
|
},
|
||||||
|
class: {
|
||||||
|
type: [String, Object, Array] as PropType<any>,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof appConfig.ui.checkbox>>,
|
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
||||||
default: () => ({})
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'change'],
|
emits: ['update:modelValue', 'change'],
|
||||||
setup (props, { emit, attrs }) {
|
setup (props, { emit }) {
|
||||||
// TODO: Remove
|
const { ui, attrs } = useUI('checkbox', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
const appConfig = useAppConfig()
|
|
||||||
|
|
||||||
const ui = computed<Partial<typeof appConfig.ui.checkbox>>(() => defuTwMerge({}, props.ui, appConfig.ui.checkbox))
|
const { emitFormChange, color, name, inputId } = useFormGroup(props)
|
||||||
|
|
||||||
const { emitFormChange, formGroup } = useFormGroup()
|
|
||||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
|
||||||
|
|
||||||
const toggle = computed({
|
const toggle = computed({
|
||||||
get () {
|
get () {
|
||||||
@@ -122,8 +129,6 @@ export default defineComponent({
|
|||||||
emitFormChange()
|
emitFormChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
|
||||||
|
|
||||||
const inputClass = computed(() => {
|
const inputClass = computed(() => {
|
||||||
return twMerge(twJoin(
|
return twMerge(twJoin(
|
||||||
ui.value.base,
|
ui.value.base,
|
||||||
@@ -136,11 +141,13 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attrs: omit(attrs, ['class']),
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
ui,
|
ui,
|
||||||
|
attrs,
|
||||||
toggle,
|
toggle,
|
||||||
wrapperClass,
|
inputId,
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
name,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
inputClass,
|
inputClass,
|
||||||
onChange
|
onChange
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import { useEventBus } from '@vueuse/core'
|
|||||||
import type { ZodSchema } from 'zod'
|
import type { ZodSchema } from 'zod'
|
||||||
import type { ValidationError as JoiError, Schema as JoiSchema } from 'joi'
|
import type { ValidationError as JoiError, Schema as JoiSchema } from 'joi'
|
||||||
import type { ObjectSchema as YupObjectSchema, ValidationError as YupError } from 'yup'
|
import type { ObjectSchema as YupObjectSchema, ValidationError as YupError } from 'yup'
|
||||||
import type { ObjectSchema as ValibotObjectSchema } from 'valibot'
|
import type { ObjectSchemaAsync as ValibotObjectSchema } from 'valibot'
|
||||||
import { safeParseAsync } from 'valibot'
|
|
||||||
import type { FormError, FormEvent, FormEventType, FormSubmitEvent, Form } from '../../types/form'
|
import type { FormError, FormEvent, FormEventType, FormSubmitEvent, Form } from '../../types/form'
|
||||||
|
import { uid } from '../../utils/uid'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
@@ -41,8 +41,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
emits: ['submit'],
|
emits: ['submit'],
|
||||||
setup (props, { expose, emit }) {
|
setup (props, { expose, emit }) {
|
||||||
const seed = Math.random().toString(36).substring(7)
|
const bus = useEventBus<FormEvent>(`form-${uid()}`)
|
||||||
const bus = useEventBus<FormEvent>(`form-${seed}`)
|
|
||||||
|
|
||||||
bus.on(async (event) => {
|
bus.on(async (event) => {
|
||||||
if (event.type !== 'submit' && props.validateOn?.includes(event.type)) {
|
if (event.type !== 'submit' && props.validateOn?.includes(event.type)) {
|
||||||
@@ -218,8 +217,8 @@ async function getValibotError (
|
|||||||
state: any,
|
state: any,
|
||||||
schema: ValibotObjectSchema<any>
|
schema: ValibotObjectSchema<any>
|
||||||
): Promise<FormError[]> {
|
): Promise<FormError[]> {
|
||||||
const result = await safeParseAsync(schema, state)
|
const result = await schema._parse(state)
|
||||||
if (result.success === false) {
|
if (result.issues) {
|
||||||
return result.issues.map((issue) => ({
|
return result.issues.map((issue) => ({
|
||||||
path: issue.path.map(p => p.key).join('.'),
|
path: issue.path.map(p => p.key).join('.'),
|
||||||
message: issue.message
|
message: issue.message
|
||||||
|
|||||||
@@ -1,36 +1,54 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="wrapperClass" v-bind="attrs">
|
<div :class="ui.wrapper" v-bind="attrs">
|
||||||
<label>
|
<div v-if="label || $slots.label" :class="[ui.label.wrapper, size]">
|
||||||
<div v-if="label" :class="[ui.label.wrapper, size]">
|
<label :for="inputId" :class="[ui.label.base, required ? ui.label.required : '']">
|
||||||
<p :class="[ui.label.base, required ? ui.label.required : '']">{{ label }}</p>
|
<slot v-if="$slots.label" name="label" v-bind="{ error, label, name, hint, description, help }" />
|
||||||
<span v-if="hint" :class="[ui.hint]">{{ hint }}</span>
|
<template v-else>{{ label }}</template>
|
||||||
</div>
|
</label>
|
||||||
|
<span v-if="hint || $slots.hint" :class="[ui.hint]">
|
||||||
|
<slot v-if="$slots.hint" name="hint" v-bind="{ error, label, name, hint, description, help }" />
|
||||||
|
<template v-else>{{ hint }}</template>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<p v-if="description" :class="[ui.description, size]">{{ description }}</p>
|
<p v-if="description || $slots.description" :class="[ui.description, size]">
|
||||||
|
<slot v-if="$slots.description" name="description" v-bind="{ error, label, name, hint, description, help }" />
|
||||||
|
<template v-else>
|
||||||
|
{{ description }}
|
||||||
|
</template>
|
||||||
|
</p>
|
||||||
|
|
||||||
<div :class="[label ? ui.container : '']">
|
<div :class="[label ? ui.container : '']">
|
||||||
<slot v-bind="{ error }" />
|
<slot v-bind="{ error }" />
|
||||||
|
|
||||||
<p v-if="error && typeof error !== 'boolean'" :class="[ui.error, size]">{{ error }}</p>
|
<p v-if="(typeof error === 'string' && error) || $slots.error" :class="[ui.error, size]">
|
||||||
<p v-else-if="help" :class="[ui.help, size]">{{ help }}</p>
|
<slot v-if="$slots.error" name="error" v-bind="{ error, label, name, hint, description, help }" />
|
||||||
</div>
|
<template v-else>
|
||||||
</label>
|
{{ error }}
|
||||||
|
</template>
|
||||||
|
</p>
|
||||||
|
<p v-else-if="help || $slots.help" :class="[ui.help, size]">
|
||||||
|
<slot v-if="$slots.help" name="help" v-bind="{ error, label, name, hint, description, help }" />
|
||||||
|
<template v-else>
|
||||||
|
{{ help }}
|
||||||
|
</template>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, provide, inject } from 'vue'
|
import { computed, defineComponent, provide, inject, ref, toRef } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { Ref, PropType } from 'vue'
|
||||||
import { omit } from 'lodash-es'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { twMerge } from 'tailwind-merge'
|
import { mergeConfig } from '../../utils'
|
||||||
import type { FormError } from '../../types/form'
|
import type { FormError, InjectedFormGroupValue, Strategy } from '../../types'
|
||||||
import { defuTwMerge } from '../../utils'
|
|
||||||
import { useAppConfig } from '#imports'
|
|
||||||
// TODO: Remove
|
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
import { formGroup } from '#ui/ui.config'
|
||||||
|
import { uid } from '../../utils/uid'
|
||||||
|
|
||||||
// const appConfig = useAppConfig()
|
const config = mergeConfig<typeof formGroup>(appConfig.ui.strategy, appConfig.ui.formGroup, formGroup)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
@@ -40,10 +58,10 @@ export default defineComponent({
|
|||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String as PropType<keyof typeof config.size>,
|
||||||
default: null,
|
default: null,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(appConfig.ui.formGroup.size).includes(value)
|
return Object.keys(config.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
@@ -70,18 +88,17 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
|
class: {
|
||||||
|
type: [String, Object, Array] as PropType<any>,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof appConfig.ui.formGroup>>,
|
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
||||||
default: () => ({})
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props, { attrs }) {
|
setup (props) {
|
||||||
// TODO: Remove
|
const { ui, attrs } = useUI('formGroup', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
const appConfig = useAppConfig()
|
|
||||||
|
|
||||||
const ui = computed<Partial<typeof appConfig.ui.formGroup>>(() => defuTwMerge({}, props.ui, appConfig.ui.formGroup))
|
|
||||||
|
|
||||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
|
||||||
|
|
||||||
const formErrors = inject<Ref<FormError[]> | null>('form-errors', null)
|
const formErrors = inject<Ref<FormError[]> | null>('form-errors', null)
|
||||||
|
|
||||||
@@ -91,19 +108,21 @@ export default defineComponent({
|
|||||||
: formErrors?.value?.find((error) => error.path === props.name)?.message
|
: formErrors?.value?.find((error) => error.path === props.name)?.message
|
||||||
})
|
})
|
||||||
|
|
||||||
const size = computed(() => ui.value.size[props.size ?? appConfig.ui.input.default.size])
|
const size = computed(() => ui.value.size[props.size ?? config.default.size])
|
||||||
|
const inputId = ref(uid())
|
||||||
|
|
||||||
provide('form-group', {
|
provide<InjectedFormGroupValue>('form-group', {
|
||||||
error,
|
error,
|
||||||
|
inputId,
|
||||||
name: computed(() => props.name),
|
name: computed(() => props.name),
|
||||||
size: computed(() => props.size)
|
size: computed(() => props.size)
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attrs: omit(attrs, ['class']),
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
ui,
|
ui,
|
||||||
wrapperClass,
|
attrs,
|
||||||
|
inputId,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
size,
|
size,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="wrapperClass">
|
<div :class="ui.wrapper">
|
||||||
<input
|
<input
|
||||||
|
:id="inputId"
|
||||||
ref="input"
|
ref="input"
|
||||||
:name="name"
|
:name="name"
|
||||||
:value="modelValue"
|
:value="modelValue"
|
||||||
@@ -31,19 +32,20 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ref, computed, onMounted, defineComponent } from 'vue'
|
import { ref, computed, toRef, onMounted, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { omit } from 'lodash-es'
|
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twMerge, twJoin } from 'tailwind-merge'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import { defuTwMerge } from '../../utils'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useFormGroup } from '../../composables/useFormGroup'
|
import { useFormGroup } from '../../composables/useFormGroup'
|
||||||
import { useAppConfig } from '#imports'
|
import { mergeConfig } from '../../utils'
|
||||||
// TODO: Remove
|
import type { NestedKeyOf, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
import { input } from '#ui/ui.config'
|
||||||
|
import colors from '#ui-colors'
|
||||||
|
|
||||||
// const appConfig = useAppConfig()
|
const config = mergeConfig<typeof input>(appConfig.ui.strategy, appConfig.ui.input, input)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
@@ -59,6 +61,10 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: 'text'
|
default: 'text'
|
||||||
},
|
},
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null
|
default: null
|
||||||
@@ -85,7 +91,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
loadingIcon: {
|
loadingIcon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => appConfig.ui.input.default.loadingIcon
|
default: () => config.default.loadingIcon
|
||||||
},
|
},
|
||||||
leadingIcon: {
|
leadingIcon: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -112,26 +118,26 @@ export default defineComponent({
|
|||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String as PropType<keyof typeof config.size>,
|
||||||
default: () => appConfig.ui.input.default.size,
|
default: null,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(appConfig.ui.input.size).includes(value)
|
return Object.keys(config.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
type: String,
|
type: String as PropType<keyof typeof config.color | typeof colors[number]>,
|
||||||
default: () => appConfig.ui.input.default.color,
|
default: () => config.default.color,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return [...appConfig.ui.colors, ...Object.keys(appConfig.ui.input.color)].includes(value)
|
return [...appConfig.ui.colors, ...Object.keys(config.color)].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
variant: {
|
variant: {
|
||||||
type: String,
|
type: String as PropType<keyof typeof config.variant | NestedKeyOf<typeof config.color>>,
|
||||||
default: () => appConfig.ui.input.default.variant,
|
default: () => config.default.variant,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return [
|
return [
|
||||||
...Object.keys(appConfig.ui.input.variant),
|
...Object.keys(config.variant),
|
||||||
...Object.values(appConfig.ui.input.color).flatMap(value => Object.keys(value))
|
...Object.values(config.color).flatMap(value => Object.keys(value))
|
||||||
].includes(value)
|
].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -139,21 +145,20 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
|
class: {
|
||||||
|
type: [String, Object, Array] as PropType<any>,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof appConfig.ui.input>>,
|
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
||||||
default: () => ({})
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'blur'],
|
emits: ['update:modelValue', 'blur'],
|
||||||
setup (props, { emit, attrs, slots }) {
|
setup (props, { emit, slots }) {
|
||||||
// TODO: Remove
|
const { ui, attrs } = useUI('input', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
const appConfig = useAppConfig()
|
|
||||||
|
|
||||||
const ui = computed<Partial<typeof appConfig.ui.input>>(() => defuTwMerge({}, props.ui, appConfig.ui.input))
|
const { emitFormBlur, emitFormInput, size, color, inputId, name } = useFormGroup(props, config)
|
||||||
|
|
||||||
const { emitFormBlur, emitFormInput, formGroup } = useFormGroup()
|
|
||||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
|
||||||
const size = computed(() => formGroup?.size?.value ?? props.size)
|
|
||||||
|
|
||||||
const input = ref<HTMLInputElement | null>(null)
|
const input = ref<HTMLInputElement | null>(null)
|
||||||
|
|
||||||
@@ -179,8 +184,6 @@ export default defineComponent({
|
|||||||
}, 100)
|
}, 100)
|
||||||
})
|
})
|
||||||
|
|
||||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
|
||||||
|
|
||||||
const inputClass = computed(() => {
|
const inputClass = computed(() => {
|
||||||
const variant = ui.value.color?.[color.value as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
const variant = ui.value.color?.[color.value as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
||||||
|
|
||||||
@@ -255,13 +258,15 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attrs: omit(attrs, ['class']),
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
ui,
|
ui,
|
||||||
|
attrs,
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
name,
|
||||||
|
inputId,
|
||||||
input,
|
input,
|
||||||
isLeading,
|
isLeading,
|
||||||
isTrailing,
|
isTrailing,
|
||||||
wrapperClass,
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
inputClass,
|
inputClass,
|
||||||
leadingIconName,
|
leadingIconName,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="wrapperClass">
|
<div :class="ui.wrapper">
|
||||||
<div class="flex items-center h-5">
|
<div class="flex items-center h-5">
|
||||||
<input
|
<input
|
||||||
:id="`${name}-${value}`"
|
:id="inputId"
|
||||||
v-model="pick"
|
v-model="pick"
|
||||||
:name="name"
|
:name="name"
|
||||||
:required="required"
|
:required="required"
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="label || $slots.label" class="ms-3 text-sm">
|
<div v-if="label || $slots.label" class="ms-3 text-sm">
|
||||||
<label :for="`${name}-${value}`" :class="ui.label">
|
<label :for="inputId" :class="ui.label">
|
||||||
<slot name="label">{{ label }}</slot>
|
<slot name="label">{{ label }}</slot>
|
||||||
<span v-if="required" :class="ui.required">*</span>
|
<span v-if="required" :class="ui.required">*</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -27,22 +27,29 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent } from 'vue'
|
import { computed, toRef, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { omit } from 'lodash-es'
|
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twMerge, twJoin } from 'tailwind-merge'
|
||||||
import { defuTwMerge } from '../../utils'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useFormGroup } from '../../composables/useFormGroup'
|
import { useFormGroup } from '../../composables/useFormGroup'
|
||||||
import { useAppConfig } from '#imports'
|
import { mergeConfig } from '../../utils'
|
||||||
// TODO: Remove
|
import type { Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
import { radio } from '#ui/ui.config'
|
||||||
|
import colors from '#ui-colors'
|
||||||
|
import { uid } from '../../utils/uid'
|
||||||
|
|
||||||
// const appConfig = useAppConfig()
|
const config = mergeConfig<typeof radio>(appConfig.ui.strategy, appConfig.ui.radio, radio)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: {
|
props: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
// A default value is needed here to bind the label
|
||||||
|
default: () => uid()
|
||||||
|
},
|
||||||
value: {
|
value: {
|
||||||
type: [String, Number, Boolean],
|
type: [String, Number, Boolean],
|
||||||
default: null
|
default: null
|
||||||
@@ -72,8 +79,8 @@ export default defineComponent({
|
|||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
type: String,
|
type: String as PropType<typeof colors[number]>,
|
||||||
default: () => appConfig.ui.radio.default.color,
|
default: () => config.default.color,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return appConfig.ui.colors.includes(value)
|
return appConfig.ui.colors.includes(value)
|
||||||
}
|
}
|
||||||
@@ -82,20 +89,20 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
|
class: {
|
||||||
|
type: [String, Object, Array] as PropType<any>,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof appConfig.ui.radio>>,
|
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
||||||
default: () => ({})
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue'],
|
emits: ['update:modelValue'],
|
||||||
setup (props, { emit, attrs }) {
|
setup (props, { emit }) {
|
||||||
// TODO: Remove
|
const { ui, attrs } = useUI('radio', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
const appConfig = useAppConfig()
|
|
||||||
|
|
||||||
const ui = computed<Partial<typeof appConfig.ui.radio>>(() => defuTwMerge({}, props.ui, appConfig.ui.radio))
|
const { emitFormChange, color, name, inputId } = useFormGroup(props)
|
||||||
|
|
||||||
const { emitFormChange, formGroup } = useFormGroup()
|
|
||||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
|
||||||
|
|
||||||
const pick = computed({
|
const pick = computed({
|
||||||
get () {
|
get () {
|
||||||
@@ -109,8 +116,6 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
|
||||||
|
|
||||||
const inputClass = computed(() => {
|
const inputClass = computed(() => {
|
||||||
return twMerge(twJoin(
|
return twMerge(twJoin(
|
||||||
ui.value.base,
|
ui.value.base,
|
||||||
@@ -122,11 +127,13 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attrs: omit(attrs, ['class']),
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
ui,
|
ui,
|
||||||
|
inputId,
|
||||||
|
attrs,
|
||||||
pick,
|
pick,
|
||||||
wrapperClass,
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
name,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
inputClass
|
inputClass
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="wrapperClass">
|
<div :class="wrapperClass">
|
||||||
<input
|
<input
|
||||||
|
:id="inputId"
|
||||||
ref="input"
|
ref="input"
|
||||||
v-model.number="value"
|
v-model.number="value"
|
||||||
:name="name"
|
:name="name"
|
||||||
@@ -19,16 +20,19 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent } from 'vue'
|
import { computed, toRef, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { omit } from 'lodash-es'
|
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twMerge, twJoin } from 'tailwind-merge'
|
||||||
import { defuTwMerge } from '../../utils'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useFormGroup } from '../../composables/useFormGroup'
|
import { useFormGroup } from '../../composables/useFormGroup'
|
||||||
import { useAppConfig } from '#imports'
|
import { mergeConfig } from '../../utils'
|
||||||
// TODO: Remove
|
import type { Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
import { range } from '#ui/ui.config'
|
||||||
|
import colors from '#ui-colors'
|
||||||
|
|
||||||
|
const config = mergeConfig<typeof range>(appConfig.ui.strategy, appConfig.ui.range, range)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
@@ -37,6 +41,10 @@ export default defineComponent({
|
|||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0
|
||||||
},
|
},
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null
|
default: null
|
||||||
@@ -58,15 +66,15 @@ export default defineComponent({
|
|||||||
default: 1
|
default: 1
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String as PropType<keyof typeof config.size>,
|
||||||
default: () => appConfig.ui.range.default.size,
|
default: null,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(appConfig.ui.range.size).includes(value)
|
return Object.keys(config.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
type: String,
|
type: String as PropType<typeof colors[number]>,
|
||||||
default: () => appConfig.ui.range.default.color,
|
default: () => config.default.color,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return appConfig.ui.colors.includes(value)
|
return appConfig.ui.colors.includes(value)
|
||||||
}
|
}
|
||||||
@@ -75,21 +83,20 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
|
class: {
|
||||||
|
type: [String, Object, Array] as PropType<any>,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof appConfig.ui.range>>,
|
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
||||||
default: () => ({})
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'change'],
|
emits: ['update:modelValue', 'change'],
|
||||||
setup (props, { emit, attrs }) {
|
setup (props, { emit }) {
|
||||||
// TODO: Remove
|
const { ui, attrs } = useUI('range', toRef(props, 'ui'), config)
|
||||||
const appConfig = useAppConfig()
|
|
||||||
|
|
||||||
const ui = computed<Partial<typeof appConfig.ui.range>>(() => defuTwMerge({}, props.ui, appConfig.ui.range))
|
const { emitFormChange, inputId, color, size, name } = useFormGroup(props, config)
|
||||||
|
|
||||||
const { emitFormChange, formGroup } = useFormGroup()
|
|
||||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
|
||||||
const size = computed(() => formGroup?.size?.value ?? props.size)
|
|
||||||
|
|
||||||
const value = computed({
|
const value = computed({
|
||||||
get () {
|
get () {
|
||||||
@@ -109,7 +116,7 @@ export default defineComponent({
|
|||||||
return twMerge(twJoin(
|
return twMerge(twJoin(
|
||||||
ui.value.wrapper,
|
ui.value.wrapper,
|
||||||
ui.value.size[size.value]
|
ui.value.size[size.value]
|
||||||
), attrs.class as string)
|
), props.class)
|
||||||
})
|
})
|
||||||
|
|
||||||
const inputClass = computed(() => {
|
const inputClass = computed(() => {
|
||||||
@@ -161,9 +168,12 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attrs: omit(attrs, ['class']),
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
ui,
|
ui,
|
||||||
|
attrs,
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
name,
|
||||||
|
inputId,
|
||||||
value,
|
value,
|
||||||
wrapperClass,
|
wrapperClass,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="wrapperClass">
|
<div :class="ui.wrapper">
|
||||||
<select
|
<select
|
||||||
|
:id="inputId"
|
||||||
:name="name"
|
:name="name"
|
||||||
:value="modelValue"
|
:value="modelValue"
|
||||||
:required="required"
|
:required="required"
|
||||||
@@ -53,19 +54,20 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent } from 'vue'
|
import { computed, toRef, defineComponent } from 'vue'
|
||||||
import type { PropType, ComputedRef } from 'vue'
|
import type { PropType, ComputedRef } from 'vue'
|
||||||
import { get, omit } from 'lodash-es'
|
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twMerge, twJoin } from 'tailwind-merge'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import { defuTwMerge } from '../../utils'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useFormGroup } from '../../composables/useFormGroup'
|
import { useFormGroup } from '../../composables/useFormGroup'
|
||||||
import { useAppConfig } from '#imports'
|
import { mergeConfig, get } from '../../utils'
|
||||||
// TODO: Remove
|
import type { NestedKeyOf, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
import { select } from '#ui/ui.config'
|
||||||
|
import colors from '#ui-colors'
|
||||||
|
|
||||||
// const appConfig = useAppConfig()
|
const config = mergeConfig<typeof select>(appConfig.ui.strategy, appConfig.ui.select, select)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
@@ -77,6 +79,10 @@ export default defineComponent({
|
|||||||
type: [String, Number, Object],
|
type: [String, Number, Object],
|
||||||
default: ''
|
default: ''
|
||||||
},
|
},
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null
|
default: null
|
||||||
@@ -99,7 +105,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
loadingIcon: {
|
loadingIcon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => appConfig.ui.input.default.loadingIcon
|
default: () => config.default.loadingIcon
|
||||||
},
|
},
|
||||||
leadingIcon: {
|
leadingIcon: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -107,7 +113,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
trailingIcon: {
|
trailingIcon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => appConfig.ui.select.default.trailingIcon
|
default: () => config.default.trailingIcon
|
||||||
},
|
},
|
||||||
trailing: {
|
trailing: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -130,26 +136,26 @@ export default defineComponent({
|
|||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String as PropType<keyof typeof config.size>,
|
||||||
default: () => appConfig.ui.select.default.size,
|
default: null,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(appConfig.ui.select.size).includes(value)
|
return Object.keys(config.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
type: String,
|
type: String as PropType<keyof typeof config.color | typeof colors[number]>,
|
||||||
default: () => appConfig.ui.select.default.color,
|
default: () => config.default.color,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return [...appConfig.ui.colors, ...Object.keys(appConfig.ui.select.color)].includes(value)
|
return [...appConfig.ui.colors, ...Object.keys(config.color)].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
variant: {
|
variant: {
|
||||||
type: String,
|
type: String as PropType<keyof typeof config.variant | NestedKeyOf<typeof config.color>>,
|
||||||
default: () => appConfig.ui.select.default.variant,
|
default: () => config.default.variant,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return [
|
return [
|
||||||
...Object.keys(appConfig.ui.select.variant),
|
...Object.keys(config.variant),
|
||||||
...Object.values(appConfig.ui.select.color).flatMap(value => Object.keys(value))
|
...Object.values(config.color).flatMap(value => Object.keys(value))
|
||||||
].includes(value)
|
].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -165,22 +171,20 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
|
class: {
|
||||||
|
type: [String, Object, Array] as PropType<any>,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof appConfig.ui.select>>,
|
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
||||||
default: () => ({})
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'change'],
|
emits: ['update:modelValue', 'change'],
|
||||||
setup (props, { emit, attrs, slots }) {
|
setup (props, { emit, slots }) {
|
||||||
// TODO: Remove
|
const { ui, attrs } = useUI('select', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
const appConfig = useAppConfig()
|
|
||||||
|
|
||||||
const ui = computed<Partial<typeof appConfig.ui.select>>(() => defuTwMerge({}, props.ui, appConfig.ui.select))
|
|
||||||
|
|
||||||
const { emitFormChange, formGroup } = useFormGroup()
|
|
||||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
|
||||||
const size = computed(() => formGroup?.size?.value ?? props.size)
|
|
||||||
|
|
||||||
|
const { emitFormChange, inputId, color, size, name } = useFormGroup(props, config)
|
||||||
|
|
||||||
const onInput = (event: InputEvent) => {
|
const onInput = (event: InputEvent) => {
|
||||||
emit('update:modelValue', (event.target as HTMLInputElement).value)
|
emit('update:modelValue', (event.target as HTMLInputElement).value)
|
||||||
@@ -243,8 +247,6 @@ export default defineComponent({
|
|||||||
return foundOption[props.valueAttribute]
|
return foundOption[props.valueAttribute]
|
||||||
})
|
})
|
||||||
|
|
||||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
|
||||||
|
|
||||||
const selectClass = computed(() => {
|
const selectClass = computed(() => {
|
||||||
const variant = ui.value.color?.[color.value as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
const variant = ui.value.color?.[color.value as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
||||||
|
|
||||||
@@ -318,14 +320,16 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attrs: omit(attrs, ['class']),
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
ui,
|
ui,
|
||||||
|
attrs,
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
name,
|
||||||
|
inputId,
|
||||||
normalizedOptionsWithPlaceholder,
|
normalizedOptionsWithPlaceholder,
|
||||||
normalizedValue,
|
normalizedValue,
|
||||||
isLeading,
|
isLeading,
|
||||||
isTrailing,
|
isTrailing,
|
||||||
wrapperClass,
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
selectClass,
|
selectClass,
|
||||||
leadingIconName,
|
leadingIconName,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
:multiple="multiple"
|
:multiple="multiple"
|
||||||
:disabled="disabled || loading"
|
:disabled="disabled || loading"
|
||||||
as="div"
|
as="div"
|
||||||
:class="wrapperClass"
|
:class="ui.wrapper"
|
||||||
@update:model-value="onUpdate"
|
@update:model-value="onUpdate"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
class="inline-flex w-full"
|
class="inline-flex w-full"
|
||||||
>
|
>
|
||||||
<slot :open="open" :disabled="disabled" :loading="loading">
|
<slot :open="open" :disabled="disabled" :loading="loading">
|
||||||
<button :class="selectClass" :disabled="disabled || loading" type="button" v-bind="attrs">
|
<button :id="inputId" :class="selectClass" :disabled="disabled || loading" type="button" v-bind="attrs">
|
||||||
<span v-if="(isLeading && leadingIconName) || $slots.leading" :class="leadingWrapperIconClass">
|
<span v-if="(isLeading && leadingIconName) || $slots.leading" :class="leadingWrapperIconClass">
|
||||||
<slot name="leading" :disabled="disabled" :loading="loading">
|
<slot name="leading" :disabled="disabled" :loading="loading">
|
||||||
<UIcon :name="leadingIconName" :class="leadingIconClass" />
|
<UIcon :name="leadingIconName" :class="leadingIconClass" />
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
<slot name="label">
|
<slot name="label">
|
||||||
<span v-if="multiple && Array.isArray(modelValue) && modelValue.length" class="block truncate">{{ modelValue.length }} selected</span>
|
<span v-if="multiple && Array.isArray(modelValue) && modelValue.length" class="block truncate">{{ modelValue.length }} selected</span>
|
||||||
<span v-else-if="!multiple && modelValue" class="block truncate">{{ typeof modelValue === 'string' ? modelValue : modelValue[optionAttribute] }}</span>
|
<span v-else-if="!multiple && modelValue" class="block truncate">{{ ['string', 'number'].includes(typeof modelValue) ? modelValue : modelValue[optionAttribute] }}</span>
|
||||||
<span v-else class="block truncate" :class="uiMenu.placeholder">{{ placeholder || ' ' }}</span>
|
<span v-else class="block truncate" :class="uiMenu.placeholder">{{ placeholder || ' ' }}</span>
|
||||||
</slot>
|
</slot>
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
/>
|
/>
|
||||||
<span v-else-if="option.chip" :class="uiMenu.option.chip.base" :style="{ background: `#${option.chip}` }" />
|
<span v-else-if="option.chip" :class="uiMenu.option.chip.base" :style="{ background: `#${option.chip}` }" />
|
||||||
|
|
||||||
<span class="truncate">{{ typeof option === 'string' ? option : option[optionAttribute] }}</span>
|
<span class="truncate">{{ ['string', 'number'].includes(typeof option) ? option : option[optionAttribute] }}</span>
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ref, computed, watch, defineComponent } from 'vue'
|
import { ref, computed, toRef, watch, defineComponent } from 'vue'
|
||||||
import type { PropType, ComponentPublicInstance } from 'vue'
|
import type { PropType, ComponentPublicInstance } from 'vue'
|
||||||
import {
|
import {
|
||||||
Combobox as HCombobox,
|
Combobox as HCombobox,
|
||||||
@@ -131,20 +131,22 @@ import {
|
|||||||
} from '@headlessui/vue'
|
} from '@headlessui/vue'
|
||||||
import { computedAsync, useDebounceFn } from '@vueuse/core'
|
import { computedAsync, useDebounceFn } from '@vueuse/core'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { omit } from 'lodash-es'
|
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twMerge, twJoin } from 'tailwind-merge'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import UAvatar from '../elements/Avatar.vue'
|
import UAvatar from '../elements/Avatar.vue'
|
||||||
import { defuTwMerge } from '../../utils'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { usePopper } from '../../composables/usePopper'
|
import { usePopper } from '../../composables/usePopper'
|
||||||
import { useFormGroup } from '../../composables/useFormGroup'
|
import { useFormGroup } from '../../composables/useFormGroup'
|
||||||
import type { PopperOptions } from '../../types/popper'
|
import { mergeConfig } from '../../utils'
|
||||||
import { useAppConfig } from '#imports'
|
import type { PopperOptions, NestedKeyOf, Strategy } from '../../types'
|
||||||
// TODO: Remove
|
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
import { select, selectMenu } from '#ui/ui.config'
|
||||||
|
import colors from '#ui-colors'
|
||||||
|
|
||||||
// const appConfig = useAppConfig()
|
const config = mergeConfig<typeof select>(appConfig.ui.strategy, appConfig.ui.select, select)
|
||||||
|
|
||||||
|
const configMenu = mergeConfig<typeof selectMenu>(appConfig.ui.strategy, appConfig.ui.selectMenu, selectMenu)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
@@ -174,6 +176,10 @@ export default defineComponent({
|
|||||||
type: Array as PropType<{ [key: string]: any, disabled?: boolean }[] | string[]>,
|
type: Array as PropType<{ [key: string]: any, disabled?: boolean }[] | string[]>,
|
||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null
|
default: null
|
||||||
@@ -188,7 +194,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
loadingIcon: {
|
loadingIcon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => appConfig.ui.input.default.loadingIcon
|
default: () => config.default.loadingIcon
|
||||||
},
|
},
|
||||||
leadingIcon: {
|
leadingIcon: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -196,7 +202,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
trailingIcon: {
|
trailingIcon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => appConfig.ui.select.default.trailingIcon
|
default: () => config.default.trailingIcon
|
||||||
},
|
},
|
||||||
trailing: {
|
trailing: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -212,7 +218,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
selectedIcon: {
|
selectedIcon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => appConfig.ui.selectMenu.default.selectedIcon
|
default: () => configMenu.default.selectedIcon
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -247,26 +253,26 @@ export default defineComponent({
|
|||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String as PropType<keyof typeof config.size>,
|
||||||
default: () => appConfig.ui.select.default.size,
|
default: null,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(appConfig.ui.select.size).includes(value)
|
return Object.keys(config.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
type: String,
|
type: String as PropType<keyof typeof config.color | typeof colors[number]>,
|
||||||
default: () => appConfig.ui.select.default.color,
|
default: () => config.default.color,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return [...appConfig.ui.colors, ...Object.keys(appConfig.ui.select.color)].includes(value)
|
return [...appConfig.ui.colors, ...Object.keys(config.color)].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
variant: {
|
variant: {
|
||||||
type: String,
|
type: String as PropType<keyof typeof config.variant | NestedKeyOf<typeof config.color>>,
|
||||||
default: () => appConfig.ui.select.default.variant,
|
default: () => config.default.variant,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return [
|
return [
|
||||||
...Object.keys(appConfig.ui.select.variant),
|
...Object.keys(config.variant),
|
||||||
...Object.values(appConfig.ui.select.color).flatMap(value => Object.keys(value))
|
...Object.values(config.color).flatMap(value => Object.keys(value))
|
||||||
].includes(value)
|
].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -290,35 +296,33 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
|
class: {
|
||||||
|
type: [String, Object, Array] as PropType<any>,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof appConfig.ui.select>>,
|
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
||||||
default: () => ({})
|
default: undefined
|
||||||
},
|
},
|
||||||
uiMenu: {
|
uiMenu: {
|
||||||
type: Object as PropType<Partial<typeof appConfig.ui.selectMenu>>,
|
type: Object as PropType<Partial<typeof configMenu & { strategy?: Strategy }>>,
|
||||||
default: () => ({})
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'open', 'close', 'change'],
|
emits: ['update:modelValue', 'open', 'close', 'change'],
|
||||||
setup (props, { emit, attrs, slots }) {
|
setup (props, { emit, slots }) {
|
||||||
// TODO: Remove
|
const { ui, attrs } = useUI('select', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
const appConfig = useAppConfig()
|
|
||||||
|
|
||||||
const ui = computed<Partial<typeof appConfig.ui.select>>(() => defuTwMerge({}, props.ui, appConfig.ui.select))
|
const { ui: uiMenu } = useUI('selectMenu', toRef(props, 'uiMenu'), configMenu)
|
||||||
const uiMenu = computed<Partial<typeof appConfig.ui.selectMenu>>(() => defuTwMerge({}, props.uiMenu, appConfig.ui.selectMenu))
|
|
||||||
|
|
||||||
const popper = computed<PopperOptions>(() => defu({}, props.popper, uiMenu.value.popper as PopperOptions))
|
const popper = computed<PopperOptions>(() => defu({}, props.popper, uiMenu.value.popper as PopperOptions))
|
||||||
|
|
||||||
const [trigger, container] = usePopper(popper.value)
|
const [trigger, container] = usePopper(popper.value)
|
||||||
const { emitFormBlur, emitFormChange, formGroup } = useFormGroup()
|
const { emitFormBlur, emitFormChange, inputId, color, size, name } = useFormGroup(props, config)
|
||||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
|
||||||
const size = computed(() => formGroup?.size?.value ?? props.size)
|
|
||||||
|
|
||||||
const query = ref('')
|
const query = ref('')
|
||||||
const searchInput = ref<ComponentPublicInstance<HTMLElement>>()
|
const searchInput = ref<ComponentPublicInstance<HTMLElement>>()
|
||||||
|
|
||||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
|
||||||
|
|
||||||
const selectClass = computed(() => {
|
const selectClass = computed(() => {
|
||||||
const variant = ui.value.color?.[color.value as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
const variant = ui.value.color?.[color.value as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
||||||
|
|
||||||
@@ -407,7 +411,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
return (props.options as any[]).filter((option: any) => {
|
return (props.options as any[]).filter((option: any) => {
|
||||||
return (props.searchAttributes?.length ? props.searchAttributes : [props.optionAttribute]).some((searchAttribute: any) => {
|
return (props.searchAttributes?.length ? props.searchAttributes : [props.optionAttribute]).some((searchAttribute: any) => {
|
||||||
return typeof option === 'string' ? option.search(new RegExp(query.value, 'i')) !== -1 : (option[searchAttribute] && option[searchAttribute].search(new RegExp(query.value, 'i')) !== -1)
|
return ['string', 'number'].includes(typeof option) ? option.toString().search(new RegExp(query.value, 'i')) !== -1 : (option[searchAttribute] && option[searchAttribute].search(new RegExp(query.value, 'i')) !== -1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -437,14 +441,18 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attrs: omit(attrs, ['class']),
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
ui,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
uiMenu,
|
uiMenu,
|
||||||
|
attrs,
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
name,
|
||||||
|
inputId,
|
||||||
trigger,
|
trigger,
|
||||||
container,
|
container,
|
||||||
isLeading,
|
isLeading,
|
||||||
isTrailing,
|
isTrailing,
|
||||||
wrapperClass,
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
selectClass,
|
selectClass,
|
||||||
leadingIconName,
|
leadingIconName,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="wrapperClass">
|
<div :class="ui.wrapper">
|
||||||
<textarea
|
<textarea
|
||||||
|
:id="inputId"
|
||||||
ref="textarea"
|
ref="textarea"
|
||||||
:value="modelValue"
|
:value="modelValue"
|
||||||
:name="name"
|
:name="name"
|
||||||
@@ -18,18 +19,19 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ref, computed, watch, onMounted, nextTick, defineComponent } from 'vue'
|
import { ref, computed, toRef, watch, onMounted, nextTick, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { omit } from 'lodash-es'
|
|
||||||
import { twMerge, twJoin } from 'tailwind-merge'
|
import { twMerge, twJoin } from 'tailwind-merge'
|
||||||
import { defuTwMerge } from '../../utils'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useFormGroup } from '../../composables/useFormGroup'
|
import { useFormGroup } from '../../composables/useFormGroup'
|
||||||
import { useAppConfig } from '#imports'
|
import { mergeConfig } from '../../utils'
|
||||||
// TODO: Remove
|
import type { NestedKeyOf, Strategy } from '../../types'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
|
import { textarea } from '#ui/ui.config'
|
||||||
|
import colors from '#ui-colors'
|
||||||
|
|
||||||
// const appConfig = useAppConfig()
|
const config = mergeConfig<typeof textarea>(appConfig.ui.strategy, appConfig.ui.textarea, textarea)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
@@ -38,6 +40,10 @@ export default defineComponent({
|
|||||||
type: [String, Number],
|
type: [String, Number],
|
||||||
default: ''
|
default: ''
|
||||||
},
|
},
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null
|
default: null
|
||||||
@@ -75,26 +81,26 @@ export default defineComponent({
|
|||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String as PropType<keyof typeof config.size>,
|
||||||
default: () => appConfig.ui.textarea.default.size,
|
default: null,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return Object.keys(appConfig.ui.textarea.size).includes(value)
|
return Object.keys(config.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
type: String,
|
type: String as PropType<keyof typeof config.color | typeof colors[number]>,
|
||||||
default: () => appConfig.ui.textarea.default.color,
|
default: () => config.default.color,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return [...appConfig.ui.colors, ...Object.keys(appConfig.ui.textarea.color)].includes(value)
|
return [...appConfig.ui.colors, ...Object.keys(config.color)].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
variant: {
|
variant: {
|
||||||
type: String,
|
type: String as PropType<keyof typeof config.variant | NestedKeyOf<typeof config.color>>,
|
||||||
default: () => appConfig.ui.textarea.default.variant,
|
default: () => config.default.variant,
|
||||||
validator (value: string) {
|
validator (value: string) {
|
||||||
return [
|
return [
|
||||||
...Object.keys(appConfig.ui.textarea.variant),
|
...Object.keys(config.variant),
|
||||||
...Object.values(appConfig.ui.textarea.color).flatMap(value => Object.keys(value))
|
...Object.values(config.color).flatMap(value => Object.keys(value))
|
||||||
].includes(value)
|
].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -102,24 +108,23 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
|
class: {
|
||||||
|
type: [String, Object, Array] as PropType<any>,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<Partial<typeof appConfig.ui.textarea>>,
|
type: Object as PropType<Partial<typeof config & { strategy?: Strategy }>>,
|
||||||
default: () => ({})
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'blur'],
|
emits: ['update:modelValue', 'blur'],
|
||||||
setup (props, { emit, attrs }) {
|
setup (props, { emit }) {
|
||||||
|
const { ui, attrs } = useUI('textarea', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
|
|
||||||
|
const { emitFormBlur, emitFormInput, inputId, color, size, name } = useFormGroup(props, config)
|
||||||
|
|
||||||
const textarea = ref<HTMLTextAreaElement | null>(null)
|
const textarea = ref<HTMLTextAreaElement | null>(null)
|
||||||
|
|
||||||
// TODO: Remove
|
|
||||||
const appConfig = useAppConfig()
|
|
||||||
|
|
||||||
const ui = computed<Partial<typeof appConfig.ui.textarea>>(() => defuTwMerge({}, props.ui, appConfig.ui.textarea))
|
|
||||||
|
|
||||||
const { emitFormBlur, emitFormInput, formGroup } = useFormGroup()
|
|
||||||
const color = computed(() => formGroup?.error?.value ? 'red' : props.color)
|
|
||||||
const size = computed(() => formGroup?.size?.value ?? props.size)
|
|
||||||
|
|
||||||
const autoFocus = () => {
|
const autoFocus = () => {
|
||||||
if (props.autofocus) {
|
if (props.autofocus) {
|
||||||
textarea.value?.focus()
|
textarea.value?.focus()
|
||||||
@@ -177,8 +182,6 @@ export default defineComponent({
|
|||||||
}, 100)
|
}, 100)
|
||||||
})
|
})
|
||||||
|
|
||||||
const wrapperClass = computed(() => twMerge(ui.value.wrapper, attrs.class as string))
|
|
||||||
|
|
||||||
const textareaClass = computed(() => {
|
const textareaClass = computed(() => {
|
||||||
const variant = ui.value.color?.[color.value as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
const variant = ui.value.color?.[color.value as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
||||||
|
|
||||||
@@ -194,11 +197,13 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attrs: omit(attrs, ['class']),
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
ui,
|
ui,
|
||||||
|
attrs,
|
||||||
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
|
name,
|
||||||
|
inputId,
|
||||||
textarea,
|
textarea,
|
||||||
wrapperClass,
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
textareaClass,
|
textareaClass,
|
||||||
onInput,
|
onInput,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user