mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-19 22:41:42 +01:00
Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
159acd664c | ||
|
|
212f7df35b | ||
|
|
d0d37a06d2 | ||
|
|
cb6f5f2d71 | ||
|
|
22da1a839a | ||
|
|
c5f76a25db | ||
|
|
ceecb60c3b | ||
|
|
23971efdb0 | ||
|
|
1a94b55caa | ||
|
|
c71fdc8795 | ||
|
|
6844f7bbd9 | ||
|
|
1acd01a440 | ||
|
|
0b2a3989a2 | ||
|
|
5f8d645231 | ||
|
|
2cc838ea8b | ||
|
|
2e41e3f238 | ||
|
|
7cb8218ed5 | ||
|
|
ddf67a060b | ||
|
|
54e713d31a | ||
|
|
09e232ed05 | ||
|
|
1d455b092d | ||
|
|
13957ba206 | ||
|
|
ff1806143c | ||
|
|
b6ed1c59ff | ||
|
|
424efe783e | ||
|
|
c3cd3c9940 | ||
|
|
8ab4a14394 | ||
|
|
25378df1d8 | ||
|
|
070d2f89b6 | ||
|
|
8e413f0681 | ||
|
|
03ac697167 | ||
|
|
c6a9b499e3 | ||
|
|
cae4f0c4a8 | ||
|
|
b29fcd2650 | ||
|
|
3671b2fbbe | ||
|
|
2577eb2780 | ||
|
|
3d1be39221 | ||
|
|
49e04389fa | ||
|
|
ee364318d1 | ||
|
|
b14afbebe9 | ||
|
|
4bf81be364 | ||
|
|
7846ca35b5 | ||
|
|
b72d3434e9 | ||
|
|
20fb46a3ba | ||
|
|
1b7e36cf70 | ||
|
|
3768cd9803 | ||
|
|
3d0bba2e83 | ||
|
|
494e73932b | ||
|
|
38200aa392 | ||
|
|
19b01f43f1 | ||
|
|
c36964b5ea | ||
|
|
4de8f2e2f7 | ||
|
|
3cf19ea5af | ||
|
|
9dd7e615e9 | ||
|
|
33b9a445c4 | ||
|
|
46cec7ecd1 | ||
|
|
f8e2c94375 | ||
|
|
71e0492179 | ||
|
|
3cda6c6478 | ||
|
|
428ee44fc0 | ||
|
|
c68ba76fd0 | ||
|
|
dd0d0551be | ||
|
|
3efcf3026a |
@@ -1,14 +0,0 @@
|
|||||||
node_modules
|
|
||||||
dist
|
|
||||||
.nuxt
|
|
||||||
coverage
|
|
||||||
*.log*
|
|
||||||
.DS_Store
|
|
||||||
.code
|
|
||||||
*.iml
|
|
||||||
package-lock.json
|
|
||||||
templates/*
|
|
||||||
sw.js
|
|
||||||
|
|
||||||
# Templates
|
|
||||||
src/templates
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
extends: ['@nuxt/eslint-config'],
|
|
||||||
rules: {
|
|
||||||
// General
|
|
||||||
semi: ['error', 'never'],
|
|
||||||
quotes: ['error', 'single'],
|
|
||||||
'comma-dangle': ['error', 'never'],
|
|
||||||
'comma-spacing': ['error', { before: false, after: true }],
|
|
||||||
'keyword-spacing': ['error', { before: true, after: true }],
|
|
||||||
'space-before-function-paren': ['error', 'always'],
|
|
||||||
'object-curly-spacing': ['error', 'always'],
|
|
||||||
'arrow-spacing': ['error', { before: true, after: true }],
|
|
||||||
'key-spacing': ['error', { beforeColon: false, afterColon: true, mode: 'strict' }],
|
|
||||||
'space-before-blocks': ['error', 'always'],
|
|
||||||
'space-infix-ops': ['error', { int32Hint: false }],
|
|
||||||
'no-multi-spaces': ['error', { ignoreEOLComments: true }],
|
|
||||||
'no-trailing-spaces': ['error'],
|
|
||||||
|
|
||||||
// Typescript
|
|
||||||
'@typescript-eslint/type-annotation-spacing': 'error',
|
|
||||||
|
|
||||||
// Vuejs
|
|
||||||
'vue/multi-word-component-names': 0,
|
|
||||||
'vue/html-indent': ['error', 2],
|
|
||||||
'vue/comma-spacing': ['error', { before: false, after: true }],
|
|
||||||
'vue/script-indent': ['error', 2, { baseIndent: 0 }],
|
|
||||||
'vue/keyword-spacing': ['error', { before: true, after: true }],
|
|
||||||
'vue/object-curly-spacing': ['error', 'always'],
|
|
||||||
'vue/key-spacing': ['error', { beforeColon: false, afterColon: true, mode: 'strict' }],
|
|
||||||
'vue/arrow-spacing': ['error', { before: true, after: true }],
|
|
||||||
'vue/array-bracket-spacing': ['error', 'never'],
|
|
||||||
'vue/block-spacing': ['error', 'always'],
|
|
||||||
'vue/brace-style': ['error', 'stroustrup', { allowSingleLine: true }],
|
|
||||||
'vue/space-infix-ops': ['error', { int32Hint: false }],
|
|
||||||
'vue/max-attributes-per-line': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
singleline: {
|
|
||||||
max: 5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'vue/padding-line-between-blocks': ['error', 'always']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
9
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
9
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@@ -6,6 +6,15 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
value: |
|
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).
|
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: dropdown
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: For what version of Nuxt UI are you suggesting this?
|
||||||
|
options:
|
||||||
|
- v2.x
|
||||||
|
- v3-alpha
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: description
|
id: description
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
9
.github/ISSUE_TEMPLATE/question.yml
vendored
9
.github/ISSUE_TEMPLATE/question.yml
vendored
@@ -6,6 +6,15 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
value: |
|
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).
|
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: dropdown
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: For what version of Nuxt UI are you asking this question?
|
||||||
|
options:
|
||||||
|
- v2.x
|
||||||
|
- v3-alpha
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: description
|
id: description
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
42
CHANGELOG.md
42
CHANGELOG.md
@@ -1,5 +1,47 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [2.19.2](https://github.com/nuxt/ui/compare/v2.19.1...v2.19.2) (2024-11-05)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Button:** put back `target` override ([212f7df](https://github.com/nuxt/ui/commit/212f7df35b9f81d189e1ee3e34f6fd2234cf52fe))
|
||||||
|
|
||||||
|
## [2.19.1](https://github.com/nuxt/ui/compare/v2.19.0...v2.19.1) (2024-11-05)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **InputMenu/SelectMenu:** regex breaks build ([cb6f5f2](https://github.com/nuxt/ui/commit/cb6f5f2d71ea8bb526a8f958daec8e9871469b63))
|
||||||
|
|
||||||
|
## [2.19.0](https://github.com/nuxt/ui/compare/v2.18.7...v2.19.0) (2024-11-05)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Form:** add `superstruct` validation ([#2357](https://github.com/nuxt/ui/issues/2357)) ([3cda6c6](https://github.com/nuxt/ui/commit/3cda6c6478d5284a3ffcb973270831601e8e5657))
|
||||||
|
* **Form:** apply transformations ([#2460](https://github.com/nuxt/ui/issues/2460)) ([ceecb60](https://github.com/nuxt/ui/commit/ceecb60c3bbd5507b1f54faed001818639d9269c))
|
||||||
|
* **Input/Textarea:** nullify model modifier ([#2309](https://github.com/nuxt/ui/issues/2309)) ([9dd7e61](https://github.com/nuxt/ui/commit/9dd7e615e97b6bf3c4c4096edd35a86ca3cfd53c))
|
||||||
|
* **InputMenu:** allows to customize labels ([#2295](https://github.com/nuxt/ui/issues/2295)) ([ddf67a0](https://github.com/nuxt/ui/commit/ddf67a060ba659f102673eff31eb2e30231c2d93))
|
||||||
|
* **Pagination:** improve slot props ([#2522](https://github.com/nuxt/ui/issues/2522)) ([c71fdc8](https://github.com/nuxt/ui/commit/c71fdc8795812bed779ab247451efd3db031e4cd))
|
||||||
|
* **SelectMenu:** allows to customize labels ([#2266](https://github.com/nuxt/ui/issues/2266)) ([54e713d](https://github.com/nuxt/ui/commit/54e713d31ae0b80b0f69dd507f71387100204ac3))
|
||||||
|
* **Table:** improve `expanded` row ([#2485](https://github.com/nuxt/ui/issues/2485)) ([1acd01a](https://github.com/nuxt/ui/commit/1acd01a440db7a7fa765189d8bde424ade9074e9))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Accordion:** improve `items` type ([#2487](https://github.com/nuxt/ui/issues/2487)) ([25378df](https://github.com/nuxt/ui/commit/25378df1d894546c4b08eb43a58b02b40ab9649b))
|
||||||
|
* **Button:** wrong `to` type ([8ab4a14](https://github.com/nuxt/ui/commit/8ab4a14394e0890b33a610e6491d891e89386959)), closes [#1253](https://github.com/nuxt/ui/issues/1253)
|
||||||
|
* **Divider:** default `type` from app config ([7846ca3](https://github.com/nuxt/ui/commit/7846ca35b5332a9e70f9990059f6041d60770e79)), closes [nuxt/ui#2398](https://github.com/nuxt/ui/issues/2398)
|
||||||
|
* **HorizontalNavigation/VerticalNavigation:** handle `badge` in RTL mode ([#2420](https://github.com/nuxt/ui/issues/2420)) ([4bf81be](https://github.com/nuxt/ui/commit/4bf81be36463bf280f31099c97a751e65240dcf5))
|
||||||
|
* **InputMenu/SelectMenu:** allow access nested object in `option-attribute` ([#2465](https://github.com/nuxt/ui/issues/2465)) ([ff18061](https://github.com/nuxt/ui/commit/ff1806143c45a7d83b00e78bec979a8f412a2827))
|
||||||
|
* **InputMenu/SelectMenu:** escape regexp before search ([c68ba76](https://github.com/nuxt/ui/commit/c68ba76fd0eebf411ccd5f047ee9a01b8ec5f5de)), closes [nuxt/ui#2308](https://github.com/nuxt/ui/issues/2308)
|
||||||
|
* **InputMenu/SelectMenu:** prevent unnecessary updates when modelValue is unchanged ([#2507](https://github.com/nuxt/ui/issues/2507)) ([1a94b55](https://github.com/nuxt/ui/commit/1a94b55caac91685f518ae4c24ca8dcbee827f86))
|
||||||
|
* **module:** missing types in `ui` config ([#2467](https://github.com/nuxt/ui/issues/2467)) ([23971ef](https://github.com/nuxt/ui/commit/23971efdb007701352ce58412db597cd95b9996b))
|
||||||
|
* **Progress:** handle `carousel` and `carousel-inverse` animations in RTL mode ([#2400](https://github.com/nuxt/ui/issues/2400)) ([20fb46a](https://github.com/nuxt/ui/commit/20fb46a3ba8d74fcaa1407b23d65b117cc9d6802))
|
||||||
|
* **RadioGroup:** rendering empty slots ([#2456](https://github.com/nuxt/ui/issues/2456)) ([b6ed1c5](https://github.com/nuxt/ui/commit/b6ed1c59ffe8c8aaac78a34d8559ca793bb92eaa))
|
||||||
|
* **Table:** `checkbox` not checked while using props by ([#2401](https://github.com/nuxt/ui/issues/2401)) ([1b7e36c](https://github.com/nuxt/ui/commit/1b7e36cf70a7252915c58657bc878cb29c719a7f))
|
||||||
|
* **Table:** `indeterminate` checkbox with pagination ([#2439](https://github.com/nuxt/ui/issues/2439)) ([070d2f8](https://github.com/nuxt/ui/commit/070d2f89b6d1cb9c236eeb779cb3918ed5770434))
|
||||||
|
* **Table:** export `TableRow` and `TableColumn` types ([c36964b](https://github.com/nuxt/ui/commit/c36964b5eacbd61a661f02953f0297a390fd1d34)), closes [nuxt/ui#2373](https://github.com/nuxt/ui/issues/2373)
|
||||||
|
* **Table:** handle dot nation with `by` prop ([#2413](https://github.com/nuxt/ui/issues/2413)) ([b72d343](https://github.com/nuxt/ui/commit/b72d3434e9ab024e8622611d32b5a4467c8364b9))
|
||||||
|
* **Tabs:** allow `aria-label` on items ([3cf19ea](https://github.com/nuxt/ui/commit/3cf19ea5afcf97ef226d8be231d3b297c5f23b9f)), closes [nuxt/ui#1934](https://github.com/nuxt/ui/issues/1934)
|
||||||
|
|
||||||
## [2.18.7](https://github.com/nuxt/ui/compare/v2.18.6...v2.18.7) (2024-10-09)
|
## [2.18.7](https://github.com/nuxt/ui/compare/v2.18.6...v2.18.7) (2024-10-09)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
32
docs/app.vue
32
docs/app.vue
@@ -3,7 +3,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<NuxtLoadingIndicator />
|
<NuxtLoadingIndicator />
|
||||||
|
|
||||||
<!-- <Banner v-if="!$route.path.startsWith('/examples')" /> -->
|
<Banner v-if="!$route.path.startsWith('/examples')" />
|
||||||
|
|
||||||
<Header v-if="!$route.path.startsWith('/examples')" :links="links" />
|
<Header v-if="!$route.path.startsWith('/examples')" :links="links" />
|
||||||
|
|
||||||
@@ -50,20 +50,22 @@ const links = computed(() => {
|
|||||||
icon: 'i-heroicons-book-open',
|
icon: 'i-heroicons-book-open',
|
||||||
to: '/getting-started',
|
to: '/getting-started',
|
||||||
active: route.path.startsWith('/getting-started') || route.path.startsWith('/components')
|
active: route.path.startsWith('/getting-started') || route.path.startsWith('/components')
|
||||||
}, ...(navigation.value.find(item => item._path === '/pro') ? [{
|
}, ...(navigation.value.find(item => item._path === '/pro')
|
||||||
label: 'Pro',
|
? [{
|
||||||
icon: 'i-heroicons-square-3-stack-3d',
|
label: 'Pro',
|
||||||
to: '/pro',
|
icon: 'i-heroicons-square-3-stack-3d',
|
||||||
active: route.path.startsWith('/pro/getting-started') || route.path.startsWith('/pro/components') || route.path.startsWith('/pro/prose')
|
to: '/pro',
|
||||||
}, {
|
active: route.path.startsWith('/pro/getting-started') || route.path.startsWith('/pro/components') || route.path.startsWith('/pro/prose')
|
||||||
label: 'Pricing',
|
}, {
|
||||||
icon: 'i-heroicons-ticket',
|
label: 'Pricing',
|
||||||
to: '/pro/pricing'
|
icon: 'i-heroicons-ticket',
|
||||||
}, {
|
to: '/pro/pricing'
|
||||||
label: 'Templates',
|
}, {
|
||||||
icon: 'i-heroicons-computer-desktop',
|
label: 'Templates',
|
||||||
to: '/pro/templates'
|
icon: 'i-heroicons-computer-desktop',
|
||||||
}] : []), {
|
to: '/pro/templates'
|
||||||
|
}]
|
||||||
|
: []), {
|
||||||
label: 'Releases',
|
label: 'Releases',
|
||||||
icon: 'i-heroicons-rocket-launch',
|
icon: 'i-heroicons-rocket-launch',
|
||||||
to: '/releases'
|
to: '/releases'
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const id = 'nuxt-ui-banner-1'
|
const id = 'nuxt-ui-banner-2'
|
||||||
const to = '/pro/pricing'
|
const to = 'https://ui3.nuxt.dev'
|
||||||
|
|
||||||
const hideBanner = () => {
|
const hideBanner = () => {
|
||||||
localStorage.setItem(id, 'true')
|
localStorage.setItem(id, 'true')
|
||||||
@@ -25,7 +25,14 @@ if (import.meta.server) {
|
|||||||
<template>
|
<template>
|
||||||
<div class="relative bg-primary hover:bg-primary/90 transition-[background] backdrop-blur z-50 app-banner">
|
<div class="relative bg-primary hover:bg-primary/90 transition-[background] backdrop-blur z-50 app-banner">
|
||||||
<UContainer class="py-2">
|
<UContainer class="py-2">
|
||||||
<NuxtLink v-if="to" :to="to" class="focus:outline-none" aria-label="Nuxt UI Pro pricing" tabindex="-1">
|
<NuxtLink
|
||||||
|
v-if="to"
|
||||||
|
:to="to"
|
||||||
|
target="_blank"
|
||||||
|
class="focus:outline-none"
|
||||||
|
aria-label="Nuxt UI Pro pricing"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
<span class="absolute inset-0 " aria-hidden="true" />
|
<span class="absolute inset-0 " aria-hidden="true" />
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
||||||
@@ -34,9 +41,19 @@ if (import.meta.server) {
|
|||||||
|
|
||||||
<p class="text-sm font-medium text-white dark:text-gray-900 truncate">
|
<p class="text-sm font-medium text-white dark:text-gray-900 truncate">
|
||||||
<UIcon name="i-heroicons-rocket-launch" class="w-5 h-5 align-top flex-shrink-0 pointer-events-none mr-2" />
|
<UIcon name="i-heroicons-rocket-launch" class="w-5 h-5 align-top flex-shrink-0 pointer-events-none mr-2" />
|
||||||
<span class="font-semibold">Nuxt UI Pro v1.0</span> is out with dashboard components!
|
<span class="font-semibold">Nuxt UI v3-alpha</span> has been released!
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<UButton
|
||||||
|
to="https://ui3.nuxt.dev"
|
||||||
|
target="_blank"
|
||||||
|
label="Try it out"
|
||||||
|
color="black"
|
||||||
|
variant="solid"
|
||||||
|
size="2xs"
|
||||||
|
trailing-icon="i-heroicons-arrow-right-20-solid"
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="flex items-center justify-end lg:flex-1">
|
<div class="flex items-center justify-end lg:flex-1">
|
||||||
<button
|
<button
|
||||||
class="p-1.5 rounded-md inline-flex hover:bg-primary/90"
|
class="p-1.5 rounded-md inline-flex hover:bg-primary/90"
|
||||||
|
|||||||
@@ -48,8 +48,8 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { NavItem } from '@nuxt/content'
|
import type { NavItem } from '@nuxt/content'
|
||||||
import type { HeaderLink } from '#ui-pro/types'
|
|
||||||
import pkg from '@nuxt/ui-pro/package.json'
|
import pkg from '@nuxt/ui-pro/package.json'
|
||||||
|
import type { HeaderLink } from '#ui-pro/types'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
links: HeaderLink[]
|
links: HeaderLink[]
|
||||||
|
|||||||
@@ -32,10 +32,10 @@ const colorMode = useColorMode()
|
|||||||
|
|
||||||
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 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)
|
||||||
},
|
},
|
||||||
set (option) {
|
set(option) {
|
||||||
appConfig.ui.primary = option.value
|
appConfig.ui.primary = option.value
|
||||||
|
|
||||||
window.localStorage.setItem('nuxt-ui-primary', appConfig.ui.primary)
|
window.localStorage.setItem('nuxt-ui-primary', appConfig.ui.primary)
|
||||||
@@ -44,10 +44,10 @@ const primary = computed({
|
|||||||
|
|
||||||
const grayColors = computed(() => ['slate', 'cool', 'zinc', 'neutral', 'stone'].map(color => ({ value: color, text: color, hex: colors[color][colorMode.value === 'dark' ? 400 : 500] })))
|
const grayColors = computed(() => ['slate', 'cool', 'zinc', 'neutral', 'stone'].map(color => ({ value: color, text: color, hex: colors[color][colorMode.value === 'dark' ? 400 : 500] })))
|
||||||
const gray = computed({
|
const gray = computed({
|
||||||
get () {
|
get() {
|
||||||
return grayColors.value.find(option => option.value === appConfig.ui.gray)
|
return grayColors.value.find(option => option.value === appConfig.ui.gray)
|
||||||
},
|
},
|
||||||
set (option) {
|
set(option) {
|
||||||
appConfig.ui.gray = option.value
|
appConfig.ui.gray = option.value
|
||||||
|
|
||||||
window.localStorage.setItem('nuxt-ui-gray', appConfig.ui.gray)
|
window.localStorage.setItem('nuxt-ui-gray', appConfig.ui.gray)
|
||||||
|
|||||||
@@ -20,6 +20,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
defineProps<{ color: { value: string, hex: string }, selected: { value: string} }>()
|
defineProps<{ color: { value: string, hex: string }, selected: { value: string } }>()
|
||||||
defineEmits(['select'])
|
defineEmits(['select'])
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -18,10 +18,10 @@
|
|||||||
const colorMode = useColorMode()
|
const colorMode = useColorMode()
|
||||||
|
|
||||||
const isDark = computed({
|
const isDark = computed({
|
||||||
get () {
|
get() {
|
||||||
return colorMode.value === 'dark'
|
return colorMode.value === 'dark'
|
||||||
},
|
},
|
||||||
set () {
|
set() {
|
||||||
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
|
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -51,11 +51,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { transformContent } from '@nuxt/content/transformers'
|
|
||||||
import { upperFirst, camelCase, kebabCase } from 'scule'
|
import { upperFirst, camelCase, kebabCase } from 'scule'
|
||||||
import { useShikiHighlighter } from '~/composables/useShikiHighlighter'
|
import { useShikiHighlighter } from '~/composables/useShikiHighlighter'
|
||||||
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
slug: {
|
slug: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -90,7 +88,7 @@ const props = defineProps({
|
|||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
type: Array as PropType<{ name: string; values: string[]; restriction: 'expected' | 'included' | 'excluded' | 'only' }[]>,
|
type: Array as PropType<{ name: string, values: string[], restriction: 'expected' | 'included' | 'excluded' | 'only' }[]>,
|
||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
backgroundClass: {
|
backgroundClass: {
|
||||||
@@ -115,7 +113,6 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
|
||||||
const baseProps = reactive({ ...props.baseProps })
|
const baseProps = reactive({ ...props.baseProps })
|
||||||
const componentProps = reactive({ ...props.props })
|
const componentProps = reactive({ ...props.props })
|
||||||
|
|
||||||
@@ -159,13 +156,13 @@ const generateOptions = (key: string, schema: { kind: string, schema: [], type:
|
|||||||
const schemaOptions = Object.values(schema?.schema || {})
|
const schemaOptions = Object.values(schema?.schema || {})
|
||||||
|
|
||||||
if (key.toLowerCase() === 'size' && schemaOptions?.length > 0) {
|
if (key.toLowerCase() === 'size' && schemaOptions?.length > 0) {
|
||||||
const baseSizeOrder = { 'xs': 1, 'sm': 2, 'md': 3, 'lg': 4, 'xl': 5 }
|
const baseSizeOrder = { xs: 1, sm: 2, md: 3, lg: 4, xl: 5 }
|
||||||
schemaOptions.sort((a: string, b: string) => {
|
schemaOptions.sort((a: string, b: string) => {
|
||||||
const aBase = a.match(/[a-zA-Z]+/)[0].toLowerCase()
|
const aBase = a.match(/[a-z]+/i)[0].toLowerCase()
|
||||||
const bBase = b.match(/[a-zA-Z]+/)[0].toLowerCase()
|
const bBase = b.match(/[a-z]+/i)[0].toLowerCase()
|
||||||
|
|
||||||
const aNum = parseInt(a.match(/\d+/)?.[0]) || 1
|
const aNum = Number.parseInt(a.match(/\d+/)?.[0]) || 1
|
||||||
const bNum = parseInt(b.match(/\d+/)?.[0]) || 1
|
const bNum = Number.parseInt(b.match(/\d+/)?.[0]) || 1
|
||||||
|
|
||||||
if (aBase === bBase) {
|
if (aBase === bBase) {
|
||||||
return aBase === 'xs' ? bNum - aNum : aNum - bNum
|
return aBase === 'xs' ? bNum - aNum : aNum - bNum
|
||||||
@@ -215,7 +212,6 @@ const propsToSelect = computed(() => Object.keys(componentProps).map((key) => {
|
|||||||
}
|
}
|
||||||
}).filter(Boolean))
|
}).filter(Boolean))
|
||||||
|
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
|
||||||
const code = computed(() => {
|
const code = computed(() => {
|
||||||
let code = `\`\`\`html
|
let code = `\`\`\`html
|
||||||
<template>
|
<template>
|
||||||
@@ -254,7 +250,7 @@ const code = computed(() => {
|
|||||||
return code
|
return code
|
||||||
})
|
})
|
||||||
|
|
||||||
function renderObject (obj: any) {
|
function renderObject(obj: any) {
|
||||||
if (Array.isArray(obj)) {
|
if (Array.isArray(obj)) {
|
||||||
return `[${obj.map(renderObject).join(', ')}]`
|
return `[${obj.map(renderObject).join(', ')}]`
|
||||||
}
|
}
|
||||||
@@ -270,27 +266,28 @@ function renderObject (obj: any) {
|
|||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data: ast } = await useAsyncData(
|
const { data: ast } = await useAsyncData(`${name}-ast-${JSON.stringify({ props: componentProps, slots: props.slots, code: props.code })}`, async () => {
|
||||||
`${name}-ast-${JSON.stringify({ props: componentProps, slots: props.slots, code: props.code })}`,
|
let formatted = ''
|
||||||
async () => {
|
try {
|
||||||
let formatted = ''
|
// @ts-ignore
|
||||||
try {
|
formatted = await $prettier.format(code.value, {
|
||||||
formatted = await $prettier.format(code.value) || code.value
|
trailingComma: 'none',
|
||||||
} catch (error) {
|
semi: false,
|
||||||
formatted = code.value
|
singleQuote: true
|
||||||
}
|
|
||||||
|
|
||||||
return transformContent('content:_markdown.md', formatted, {
|
|
||||||
markdown: {
|
|
||||||
highlight: {
|
|
||||||
highlighter,
|
|
||||||
theme: {
|
|
||||||
light: 'material-theme-lighter',
|
|
||||||
default: 'material-theme',
|
|
||||||
dark: 'material-theme-palenight'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}, { watch: [code] })
|
} catch {
|
||||||
|
formatted = code.value
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseMarkdown(formatted, {
|
||||||
|
highlight: {
|
||||||
|
highlighter,
|
||||||
|
theme: {
|
||||||
|
light: 'material-theme-lighter',
|
||||||
|
default: 'material-theme',
|
||||||
|
dark: 'material-theme-palenight'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, { watch: [code] })
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="[&>div>pre]:!rounded-t-none [&>div>pre]:!mt-0">
|
<div class="[&>div>pre]:!rounded-t-none [&>div>pre]:!mt-0">
|
||||||
<div
|
<div v-if="hasPreview" class="flex border border-gray-200 dark:border-gray-700 relative rounded-t-md" :class="[{ 'p-4': padding, 'rounded-b-md': !hasCode, 'border-b-0': hasCode, 'not-prose': !prose }, backgroundClass, extraClass]">
|
||||||
v-if="hasPreview"
|
|
||||||
class="flex border border-gray-200 dark:border-gray-700 relative rounded-t-md"
|
|
||||||
:class="[{ 'p-4': padding, 'rounded-b-md': !hasCode, 'border-b-0': hasCode, 'not-prose': !prose }, backgroundClass, extraClass]"
|
|
||||||
>
|
|
||||||
<template v-if="component">
|
<template v-if="component">
|
||||||
<iframe v-if="iframe" :src="`/examples/${component}`" v-bind="iframeProps" :class="backgroundClass" class="w-full" />
|
<iframe v-if="iframe" :src="`/examples/${component}`" v-bind="iframeProps" :class="backgroundClass" class="w-full" />
|
||||||
<component :is="camelName" v-else v-bind="componentProps" :class="componentClass" />
|
<component :is="camelName" v-else v-bind="componentProps" :class="componentClass" />
|
||||||
@@ -22,7 +18,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { camelCase } from 'scule'
|
import { camelCase } from 'scule'
|
||||||
import { fetchContentExampleCode } from '~/composables/useContentExamplesCode'
|
import { fetchContentExampleCode } from '~/composables/useContentExamplesCode'
|
||||||
import { transformContent } from '@nuxt/content/transformers'
|
|
||||||
import { useShikiHighlighter } from '~/composables/useShikiHighlighter'
|
import { useShikiHighlighter } from '~/composables/useShikiHighlighter'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -86,15 +81,13 @@ const highlighter = useShikiHighlighter()
|
|||||||
const hasCode = computed(() => !props.hiddenCode && (data?.code || instance.slots.code))
|
const hasCode = computed(() => !props.hiddenCode && (data?.code || instance.slots.code))
|
||||||
const hasPreview = computed(() => !props.hiddenPreview && (props.component || instance.slots.default))
|
const hasPreview = computed(() => !props.hiddenPreview && (props.component || instance.slots.default))
|
||||||
|
|
||||||
const { data: ast } = await useAsyncData(`content-example-${camelName}-ast`, () => transformContent('content:_markdown.md', `\`\`\`vue\n${data?.code ?? ''}\n\`\`\``, {
|
const { data: ast } = await useAsyncData(`content-example-${camelName}-ast`, () => parseMarkdown(`\`\`\`vue\n${data?.code ?? ''}\n\`\`\``, {
|
||||||
markdown: {
|
highlight: {
|
||||||
highlight: {
|
highlighter,
|
||||||
highlighter,
|
theme: {
|
||||||
theme: {
|
light: 'material-theme-lighter',
|
||||||
light: 'material-theme-lighter',
|
default: 'material-theme',
|
||||||
default: 'material-theme',
|
dark: 'material-theme-palenight'
|
||||||
dark: 'material-theme-palenight'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -3,11 +3,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { transformContent } from '@nuxt/content/transformers'
|
|
||||||
import { upperFirst, camelCase } from 'scule'
|
import { upperFirst, camelCase } from 'scule'
|
||||||
import json5 from 'json5'
|
import json5 from 'json5'
|
||||||
import { useShikiHighlighter } from '~/composables/useShikiHighlighter'
|
|
||||||
import * as config from '#ui/ui.config'
|
import * as config from '#ui/ui.config'
|
||||||
|
import { useShikiHighlighter } from '~/composables/useShikiHighlighter'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
slug: {
|
slug: {
|
||||||
@@ -18,26 +17,24 @@ const props = defineProps({
|
|||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const highlighter = useShikiHighlighter()
|
const highlighter = useShikiHighlighter()
|
||||||
// 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 = camelCase(slug)
|
const camelName = camelCase(slug)
|
||||||
const name = `U${upperFirst(camelName)}`
|
const name = `U${upperFirst(camelName)}`
|
||||||
|
|
||||||
const preset = config[camelName]
|
const preset = config[camelName]
|
||||||
|
|
||||||
const { data: ast } = await useAsyncData(`${name}-preset`, () => transformContent('content:_markdown.md', `
|
const { data: ast } = await useAsyncData(`${name}-preset`, () => parseMarkdown(`
|
||||||
\`\`\`yml
|
\`\`\`yml
|
||||||
${json5.stringify(preset, null, 2)}
|
${json5.stringify(preset, null, 2).replace(/,([ |\t\n]+[}|\])])/g, '$1')}
|
||||||
\`\`\`\
|
\`\`\`\
|
||||||
`, {
|
`, {
|
||||||
markdown: {
|
highlight: {
|
||||||
highlight: {
|
highlighter,
|
||||||
highlighter,
|
theme: {
|
||||||
theme: {
|
light: 'material-theme-lighter',
|
||||||
light: 'material-theme-lighter',
|
default: 'material-theme',
|
||||||
default: 'material-theme',
|
dark: 'material-theme-palenight'
|
||||||
dark: 'material-theme-palenight'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
function startsWithCapital (word) {
|
function startsWithCapital(word) {
|
||||||
if (word.charAt(0).startsWith('"')) {
|
if (word.charAt(0).startsWith('"')) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const links = [{
|
|||||||
<UBreadcrumb :links="links" :divider="null" :ui="{ ol: 'gap-x-3' }">
|
<UBreadcrumb :links="links" :divider="null" :ui="{ ol: 'gap-x-3' }">
|
||||||
<template #icon="{ link, index, isActive }">
|
<template #icon="{ link, index, isActive }">
|
||||||
<UAvatar
|
<UAvatar
|
||||||
:alt="(index + 1 ).toString()"
|
:alt="(index + 1).toString()"
|
||||||
:ui="{
|
:ui="{
|
||||||
background: isActive ? 'bg-primary-500 dark:bg-primary-400' : undefined,
|
background: isActive ? 'bg-primary-500 dark:bg-primary-400' : undefined,
|
||||||
placeholder: isActive ? 'text-white dark:text-gray-900' : !!link.to ? 'group-hover:text-gray-700 dark:group-hover:text-gray-200' : ''
|
placeholder: isActive ? 'text-white dark:text-gray-900' : !!link.to ? 'group-hover:text-gray-700 dark:group-hover:text-gray-200' : ''
|
||||||
|
|||||||
@@ -18,19 +18,21 @@ const actions = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const groups = computed(() =>
|
const groups = computed(() =>
|
||||||
[commandPaletteRef.value?.query ? {
|
[commandPaletteRef.value?.query
|
||||||
key: 'users',
|
? {
|
||||||
commands: users
|
key: 'users',
|
||||||
} : {
|
commands: users
|
||||||
key: 'recent',
|
}
|
||||||
label: 'Recent searches',
|
: {
|
||||||
commands: users.slice(0, 1)
|
key: 'recent',
|
||||||
}, {
|
label: 'Recent searches',
|
||||||
|
commands: users.slice(0, 1)
|
||||||
|
}, {
|
||||||
key: 'actions',
|
key: 'actions',
|
||||||
commands: actions
|
commands: actions
|
||||||
}].filter(Boolean))
|
}].filter(Boolean))
|
||||||
|
|
||||||
function onSelect (option) {
|
function onSelect(option) {
|
||||||
if (option.click) {
|
if (option.click) {
|
||||||
option.click()
|
option.click()
|
||||||
} else if (option.to) {
|
} else if (option.to) {
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ const ui = {
|
|||||||
:autoselect="false"
|
:autoselect="false"
|
||||||
command-attribute="title"
|
command-attribute="title"
|
||||||
:fuse="{
|
:fuse="{
|
||||||
fuseOptions: { keys: ['title', 'category'] },
|
fuseOptions: { keys: ['title', 'category'] }
|
||||||
}"
|
}"
|
||||||
placeholder="Search docs"
|
placeholder="Search docs"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const { y: windowY } = useWindowScroll()
|
|||||||
const isOpen = ref(false)
|
const isOpen = ref(false)
|
||||||
const virtualElement = ref({ getBoundingClientRect: () => ({}) })
|
const virtualElement = ref({ getBoundingClientRect: () => ({}) })
|
||||||
|
|
||||||
function onContextMenu () {
|
function onContextMenu() {
|
||||||
const top = unref(y) - unref(windowY)
|
const top = unref(y) - unref(windowY)
|
||||||
const left = unref(x)
|
const left = unref(x)
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const { y: windowY } = useWindowScroll()
|
|||||||
const isOpen = ref(false)
|
const isOpen = ref(false)
|
||||||
const virtualElement = ref({ getBoundingClientRect: () => ({}) })
|
const virtualElement = ref({ getBoundingClientRect: () => ({}) })
|
||||||
|
|
||||||
function onContextMenu () {
|
function onContextMenu() {
|
||||||
const top = unref(y) - unref(windowY)
|
const top = unref(y) - unref(windowY)
|
||||||
const left = unref(x)
|
const left = unref(x)
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const { y: windowY } = useWindowScroll()
|
|||||||
const isOpen = ref(false)
|
const isOpen = ref(false)
|
||||||
const virtualElement = ref({ getBoundingClientRect: () => ({}) })
|
const virtualElement = ref({ getBoundingClientRect: () => ({}) })
|
||||||
|
|
||||||
function onContextMenu () {
|
function onContextMenu() {
|
||||||
const top = unref(y) - unref(windowY)
|
const top = unref(y) - unref(windowY)
|
||||||
const left = unref(x)
|
const left = unref(x)
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const { y: windowY } = useWindowScroll()
|
|||||||
const isOpen = ref(false)
|
const isOpen = ref(false)
|
||||||
const virtualElement = ref({ getBoundingClientRect: () => ({}) })
|
const virtualElement = ref({ getBoundingClientRect: () => ({}) })
|
||||||
|
|
||||||
function onContextMenu () {
|
function onContextMenu() {
|
||||||
const top = unref(y) - unref(windowY)
|
const top = unref(y) - unref(windowY)
|
||||||
const left = unref(x)
|
const left = unref(x)
|
||||||
|
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ const ranges = [
|
|||||||
]
|
]
|
||||||
const selected = ref({ start: sub(new Date(), { days: 14 }), end: new Date() })
|
const selected = ref({ start: sub(new Date(), { days: 14 }), end: new Date() })
|
||||||
|
|
||||||
function isRangeSelected (duration: Duration) {
|
function isRangeSelected(duration: Duration) {
|
||||||
return isSameDay(selected.value.start, sub(new Date(), duration)) && isSameDay(selected.value.end, new Date())
|
return isSameDay(selected.value.start, sub(new Date(), duration)) && isSameDay(selected.value.end, new Date())
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectRange (duration: Duration) {
|
function selectRange(duration: Duration) {
|
||||||
selected.value = { start: sub(new Date(), duration), end: new Date() }
|
selected.value = { start: sub(new Date(), duration), end: new Date() }
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const validate = (state: any): FormError[] => {
|
|||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onSubmit (event: FormSubmitEvent<any>) {
|
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||||
// Do something with data
|
// Do something with data
|
||||||
console.log(event.data)
|
console.log(event.data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ type Schema = z.infer<typeof schema>
|
|||||||
|
|
||||||
const form = ref()
|
const form = ref()
|
||||||
|
|
||||||
async function onSubmit (event: FormSubmitEvent<Schema>) {
|
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||||
// Do something with event.data
|
// Do something with event.data
|
||||||
console.log(event.data)
|
console.log(event.data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const state = reactive({
|
|||||||
password: undefined
|
password: undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
async function onSubmit (event: FormSubmitEvent<any>) {
|
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||||
// Do something with event.data
|
// Do something with event.data
|
||||||
console.log(event.data)
|
console.log(event.data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,12 +13,12 @@ const validate = (state: any): FormError[] => {
|
|||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onSubmit (event: FormSubmitEvent<any>) {
|
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||||
// Do something with data
|
// Do something with data
|
||||||
console.log(event.data)
|
console.log(event.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onError (event: FormErrorEvent) {
|
async function onError(event: FormErrorEvent) {
|
||||||
const element = document.getElementById(event.errors[0].id)
|
const element = document.getElementById(event.errors[0].id)
|
||||||
element?.focus()
|
element?.focus()
|
||||||
element?.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
element?.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||||
|
|||||||
36
docs/components/content/examples/FormExampleSuperstruct.vue
Normal file
36
docs/components/content/examples/FormExampleSuperstruct.vue
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { object, string, nonempty, type Infer } from 'superstruct'
|
||||||
|
import type { FormSubmitEvent } from '#ui/types'
|
||||||
|
|
||||||
|
const schema = object({
|
||||||
|
email: nonempty(string()),
|
||||||
|
password: nonempty(string())
|
||||||
|
})
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
email: '',
|
||||||
|
password: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
type Schema = Infer<typeof schema>
|
||||||
|
|
||||||
|
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||||
|
console.log(event.data)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
|
||||||
|
<UFormGroup label="Email" name="email">
|
||||||
|
<UInput v-model="state.email" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Password" name="password">
|
||||||
|
<UInput v-model="state.password" type="password" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UButton type="submit">
|
||||||
|
Submit
|
||||||
|
</UButton>
|
||||||
|
</UForm>
|
||||||
|
</template>
|
||||||
@@ -14,7 +14,7 @@ const state = reactive({
|
|||||||
password: ''
|
password: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
async function onSubmit (event: FormSubmitEvent<Schema>) {
|
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||||
// Do something with event.data
|
// Do something with event.data
|
||||||
console.log(event.data)
|
console.log(event.data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const state = reactive({
|
|||||||
password: undefined
|
password: undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
async function onSubmit (event: FormSubmitEvent<Schema>) {
|
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||||
// Do something with event.data
|
// Do something with event.data
|
||||||
console.log(event.data)
|
console.log(event.data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const state = reactive({
|
|||||||
password: undefined
|
password: undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
async function onSubmit (event: FormSubmitEvent<Schema>) {
|
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||||
// Do something with data
|
// Do something with data
|
||||||
console.log(event.data)
|
console.log(event.data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const selected = ref()
|
const selected = ref()
|
||||||
|
|
||||||
async function search (q: string) {
|
async function search(q: string) {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
|
||||||
const users: any[] = await $fetch('https://jsonplaceholder.typicode.com/users', { params: { q } })
|
const users: any[] = await $fetch('https://jsonplaceholder.typicode.com/users', { params: { q } })
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ defineProps({
|
|||||||
|
|
||||||
const emit = defineEmits(['success'])
|
const emit = defineEmits(['success'])
|
||||||
|
|
||||||
function onSuccess () {
|
function onSuccess() {
|
||||||
emit('success')
|
emit('success')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ const toast = useToast()
|
|||||||
const modal = useModal()
|
const modal = useModal()
|
||||||
const count = ref(0)
|
const count = ref(0)
|
||||||
|
|
||||||
function openModal () {
|
function openModal() {
|
||||||
count.value += 1
|
count.value += 1
|
||||||
modal.open(ModalExampleComponent, {
|
modal.open(ModalExampleComponent, {
|
||||||
count: count.value,
|
count: count.value,
|
||||||
onSuccess () {
|
onSuccess() {
|
||||||
toast.add({
|
toast.add({
|
||||||
title: 'Success !',
|
title: 'Success !',
|
||||||
id: 'modal-success'
|
id: 'modal-success'
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
|
||||||
function onCallback () {
|
function onCallback() {
|
||||||
alert('Notification expired!')
|
alert('Notification expired!')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
|
||||||
function onClick () {
|
function onClick() {
|
||||||
alert('Clicked!')
|
alert('Clicked!')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -5,15 +5,29 @@ const items = ref(Array(55))
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UPagination v-model="page" :total="items.length" :ui="{ rounded: 'first-of-type:rounded-s-md last-of-type:rounded-e-md' }">
|
<UPagination v-model="page" :total="items.length" :ui="{ rounded: 'first-of-type:rounded-s-md last-of-type:rounded-e-md' }">
|
||||||
<template #first="{ onClick }">
|
<template #first="{ onClick, canGoFirst }">
|
||||||
<UTooltip text="First page">
|
<UTooltip text="First page">
|
||||||
<UButton icon="i-heroicons-arrow-uturn-left" color="primary" :ui="{ rounded: 'rounded-full' }" class="rtl:[&_span:first-child]:rotate-180 me-2" @click="onClick" />
|
<UButton
|
||||||
|
icon="i-heroicons-arrow-uturn-left"
|
||||||
|
color="primary"
|
||||||
|
:ui="{ rounded: 'rounded-full' }"
|
||||||
|
class="rtl:[&_span:first-child]:rotate-180 me-2"
|
||||||
|
:disabled="!canGoFirst"
|
||||||
|
@click="onClick"
|
||||||
|
/>
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #last="{ onClick }">
|
<template #last="{ onClick, canGoLast }">
|
||||||
<UTooltip text="Last page">
|
<UTooltip text="Last page">
|
||||||
<UButton icon="i-heroicons-arrow-uturn-right-20-solid" color="primary" :ui="{ rounded: 'rounded-full' }" class="rtl:[&_span:last-child]:rotate-180 ms-2" @click="onClick" />
|
<UButton
|
||||||
|
icon="i-heroicons-arrow-uturn-right-20-solid"
|
||||||
|
color="primary"
|
||||||
|
:ui="{ rounded: 'rounded-full' }"
|
||||||
|
class="rtl:[&_span:last-child]:rotate-180 ms-2"
|
||||||
|
:disabled="!canGoLast"
|
||||||
|
@click="onClick"
|
||||||
|
/>
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
</template>
|
</template>
|
||||||
</UPagination>
|
</UPagination>
|
||||||
|
|||||||
@@ -5,15 +5,29 @@ const items = ref(Array(55))
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UPagination v-model="page" :total="items.length" :ui="{ rounded: 'first-of-type:rounded-s-md last-of-type:rounded-e-md' }">
|
<UPagination v-model="page" :total="items.length" :ui="{ rounded: 'first-of-type:rounded-s-md last-of-type:rounded-e-md' }">
|
||||||
<template #prev="{ onClick }">
|
<template #prev="{ onClick, canGoPrev }">
|
||||||
<UTooltip text="Previous page">
|
<UTooltip text="Previous page">
|
||||||
<UButton icon="i-heroicons-arrow-small-left-20-solid" color="primary" :ui="{ rounded: 'rounded-full' }" class="rtl:[&_span:first-child]:rotate-180 me-2" @click="onClick" />
|
<UButton
|
||||||
|
icon="i-heroicons-arrow-small-left-20-solid"
|
||||||
|
color="primary"
|
||||||
|
:ui="{ rounded: 'rounded-full' }"
|
||||||
|
class="rtl:[&_span:first-child]:rotate-180 me-2"
|
||||||
|
:disabled="!canGoPrev"
|
||||||
|
@click="onClick"
|
||||||
|
/>
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #next="{ onClick }">
|
<template #next="{ onClick, canGoNext }">
|
||||||
<UTooltip text="Next page">
|
<UTooltip text="Next page">
|
||||||
<UButton icon="i-heroicons-arrow-small-right-20-solid" color="primary" :ui="{ rounded: 'rounded-full' }" class="rtl:[&_span:last-child]:rotate-180 ms-2" @click="onClick" />
|
<UButton
|
||||||
|
icon="i-heroicons-arrow-small-right-20-solid"
|
||||||
|
color="primary"
|
||||||
|
:ui="{ rounded: 'rounded-full' }"
|
||||||
|
class="rtl:[&_span:last-child]:rotate-180 ms-2"
|
||||||
|
:disabled="!canGoNext"
|
||||||
|
@click="onClick"
|
||||||
|
/>
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
</template>
|
</template>
|
||||||
</UPagination>
|
</UPagination>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const items = ref(Array(50))
|
|||||||
:to="(page: number) => ({
|
:to="(page: number) => ({
|
||||||
query: { page },
|
query: { page },
|
||||||
// Hash is specified here to prevent the page from scrolling to the top
|
// Hash is specified here to prevent the page from scrolling to the top
|
||||||
hash: '#links',
|
hash: '#links'
|
||||||
})"
|
})"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ const temp = ref(35)
|
|||||||
|
|
||||||
const color = computed(() => {
|
const color = computed(() => {
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case temp.value < 10: return 'blue'
|
case temp.value < 10: return 'blue'
|
||||||
case temp.value < 20: return 'amber'
|
case temp.value < 20: return 'amber'
|
||||||
case temp.value < 30: return 'orange'
|
case temp.value < 30: return 'orange'
|
||||||
default: return 'red'
|
default: return 'red'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ const labels = computed({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
function hashCode (str) {
|
function hashCode(str) {
|
||||||
let hash = 0
|
let hash = 0
|
||||||
for (let i = 0; i < str.length; i++) {
|
for (let i = 0; i < str.length; i++) {
|
||||||
hash = str.charCodeAt(i) + ((hash << 5) - hash)
|
hash = str.charCodeAt(i) + ((hash << 5) - hash)
|
||||||
@@ -45,7 +45,7 @@ function hashCode (str) {
|
|||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|
||||||
function intToRGB (i) {
|
function intToRGB(i) {
|
||||||
const c = (i & 0x00FFFFFF)
|
const c = (i & 0x00FFFFFF)
|
||||||
.toString(16)
|
.toString(16)
|
||||||
.toUpperCase()
|
.toUpperCase()
|
||||||
@@ -53,7 +53,7 @@ function intToRGB (i) {
|
|||||||
return '00000'.substring(0, 6 - c.length) + c
|
return '00000'.substring(0, 6 - c.length) + c
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateColorFromString (str) {
|
function generateColorFromString(str) {
|
||||||
return intToRGB(hashCode(str))
|
return intToRGB(hashCode(str))
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ const labels = computed({
|
|||||||
|
|
||||||
const showCreateOption = (query, results) => {
|
const showCreateOption = (query, results) => {
|
||||||
const lowercaseQuery = String.prototype.toLowerCase.apply(query || '')
|
const lowercaseQuery = String.prototype.toLowerCase.apply(query || '')
|
||||||
return lowercaseQuery.length >= 3 && !results.find(option => {
|
return lowercaseQuery.length >= 3 && !results.find((option) => {
|
||||||
return String.prototype.toLowerCase.apply(option['name'] || '') === lowercaseQuery
|
return String.prototype.toLowerCase.apply(option['name'] || '') === lowercaseQuery
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const selected = ref([])
|
const selected = ref([])
|
||||||
|
|
||||||
async function search (q: string) {
|
async function search(q: string) {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
|
||||||
const users: any[] = await $fetch('https://jsonplaceholder.typicode.com/users', { params: { q } })
|
const users: any[] = await $fetch('https://jsonplaceholder.typicode.com/users', { params: { q } })
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
count: {
|
count: {
|
||||||
type: Number,
|
type: Number,
|
||||||
@@ -8,7 +7,7 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const emits = defineEmits<{
|
const emits = defineEmits<{
|
||||||
close: [];
|
close: []
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -27,4 +26,4 @@ const emits = defineEmits<{
|
|||||||
<Placeholder class="h-full" />
|
<Placeholder class="h-full" />
|
||||||
</UCard>
|
</UCard>
|
||||||
</USlideover>
|
</USlideover>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { SlideoverExampleComponent } from '#components'
|
|||||||
|
|
||||||
const slideover = useSlideover()
|
const slideover = useSlideover()
|
||||||
const count = ref(0)
|
const count = ref(0)
|
||||||
function openSlideover () {
|
function openSlideover() {
|
||||||
count.value += 1
|
count.value += 1
|
||||||
slideover.open(SlideoverExampleComponent, {
|
slideover.open(SlideoverExampleComponent, {
|
||||||
count: count.value,
|
count: count.value,
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ const columns = [{
|
|||||||
}]
|
}]
|
||||||
|
|
||||||
const selectedColumns = ref(columns)
|
const selectedColumns = ref(columns)
|
||||||
const columnsTable = computed(() => columns.filter((column) => selectedColumns.value.includes(column)))
|
const columnsTable = computed(() => columns.filter(column => selectedColumns.value.includes(column)))
|
||||||
|
|
||||||
// Selected Rows
|
// Selected Rows
|
||||||
const selectedRows = ref([])
|
const selectedRows = ref([])
|
||||||
|
|
||||||
function select (row) {
|
function select(row) {
|
||||||
const index = selectedRows.value.findIndex((item) => item.id === row.id)
|
const index = selectedRows.value.findIndex(item => item.id === row.id)
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
selectedRows.value.push(row)
|
selectedRows.value.push(row)
|
||||||
} else {
|
} else {
|
||||||
@@ -92,10 +92,10 @@ const { data: todos, status } = await useLazyAsyncData<{
|
|||||||
}[]>('todos', () => ($fetch as any)(`https://jsonplaceholder.typicode.com/todos${searchStatus.value}`, {
|
}[]>('todos', () => ($fetch as any)(`https://jsonplaceholder.typicode.com/todos${searchStatus.value}`, {
|
||||||
query: {
|
query: {
|
||||||
q: search.value,
|
q: search.value,
|
||||||
'_page': page.value,
|
_page: page.value,
|
||||||
'_limit': pageCount.value,
|
_limit: pageCount.value,
|
||||||
'_sort': sort.value.column,
|
_sort: sort.value.column,
|
||||||
'_order': sort.value.direction
|
_order: sort.value.direction
|
||||||
}
|
}
|
||||||
}), {
|
}), {
|
||||||
default: () => [],
|
default: () => [],
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ const people = [{
|
|||||||
role: 'Owner'
|
role: 'Owner'
|
||||||
}]
|
}]
|
||||||
|
|
||||||
function select (row) {
|
function select(row) {
|
||||||
const index = selected.value.findIndex((item) => item.id === row.id)
|
const index = selected.value.findIndex(item => item.id === row.id)
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
selected.value.push(row)
|
selected.value.push(row)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
<script setup>
|
||||||
|
const people = [{
|
||||||
|
id: 1,
|
||||||
|
name: 'Lindsay Walton',
|
||||||
|
title: 'Front-end Developer',
|
||||||
|
email: 'lindsay.walton@example.com',
|
||||||
|
role: 'Member'
|
||||||
|
}, {
|
||||||
|
id: 2,
|
||||||
|
name: 'Courtney Henry',
|
||||||
|
title: 'Designer',
|
||||||
|
email: 'courtney.henry@example.com',
|
||||||
|
role: 'Admin',
|
||||||
|
disabledExpand: true
|
||||||
|
}, {
|
||||||
|
id: 3,
|
||||||
|
name: 'Tom Cook',
|
||||||
|
title: 'Director of Product',
|
||||||
|
email: 'tom.cook@example.com',
|
||||||
|
role: 'Member'
|
||||||
|
}, {
|
||||||
|
id: 4,
|
||||||
|
name: 'Whitney Francis',
|
||||||
|
title: 'Copywriter',
|
||||||
|
email: 'whitney.francis@example.com',
|
||||||
|
role: 'Admin',
|
||||||
|
disabledExpand: true
|
||||||
|
}, {
|
||||||
|
id: 5,
|
||||||
|
name: 'Leonard Krasner',
|
||||||
|
title: 'Senior Designer',
|
||||||
|
email: 'leonard.krasner@example.com',
|
||||||
|
role: 'Owner'
|
||||||
|
}, {
|
||||||
|
id: 6,
|
||||||
|
name: 'Floyd Miles',
|
||||||
|
title: 'Principal Designer',
|
||||||
|
email: 'floyd.miles@example.com',
|
||||||
|
role: 'Member',
|
||||||
|
disabledExpand: true
|
||||||
|
}]
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
label: 'Name',
|
||||||
|
key: 'name'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'title',
|
||||||
|
key: 'title'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Email',
|
||||||
|
key: 'email'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'role',
|
||||||
|
key: 'role'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const expand = ref({
|
||||||
|
openedRows: [],
|
||||||
|
row: null
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UTable v-model:expand="expand" :rows="people" :columns="columns">
|
||||||
|
<template #expand="{ row }">
|
||||||
|
<div class="p-4">
|
||||||
|
<pre>{{ row }}</pre>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UTable>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
<script setup lang='ts'>
|
||||||
|
const people = [{
|
||||||
|
id: 1,
|
||||||
|
name: 'Lindsay Walton',
|
||||||
|
title: 'Front-end Developer',
|
||||||
|
email: 'lindsay.walton@example.com',
|
||||||
|
role: 'Member',
|
||||||
|
hasExpand: false
|
||||||
|
}, {
|
||||||
|
id: 2,
|
||||||
|
name: 'Courtney Henry',
|
||||||
|
title: 'Designer',
|
||||||
|
email: 'courtney.henry@example.com',
|
||||||
|
role: 'Admin',
|
||||||
|
hasExpand: true
|
||||||
|
}, {
|
||||||
|
id: 3,
|
||||||
|
name: 'Tom Cook',
|
||||||
|
title: 'Director of Product',
|
||||||
|
email: 'tom.cook@example.com',
|
||||||
|
role: 'Member',
|
||||||
|
hasExpand: false
|
||||||
|
}, {
|
||||||
|
id: 4,
|
||||||
|
name: 'Whitney Francis',
|
||||||
|
title: 'Copywriter',
|
||||||
|
email: 'whitney.francis@example.com',
|
||||||
|
role: 'Admin',
|
||||||
|
hasExpand: true
|
||||||
|
}, {
|
||||||
|
id: 5,
|
||||||
|
name: 'Leonard Krasner',
|
||||||
|
title: 'Senior Designer',
|
||||||
|
email: 'leonard.krasner@example.com',
|
||||||
|
role: 'Owner',
|
||||||
|
hasExpand: false
|
||||||
|
}, {
|
||||||
|
id: 6,
|
||||||
|
name: 'Floyd Miles',
|
||||||
|
title: 'Principal Designer',
|
||||||
|
email: 'floyd.miles@example.com',
|
||||||
|
role: 'Member',
|
||||||
|
hasExpand: true
|
||||||
|
}]
|
||||||
|
|
||||||
|
const expand = ref({
|
||||||
|
openedRows: [people.find(v => v.hasExpand)],
|
||||||
|
row: {}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UTable v-model:expand="expand" :rows="people">
|
||||||
|
<template #expand="{ row }">
|
||||||
|
<div class="p-4">
|
||||||
|
<pre>{{ row }}</pre>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #expand-action="{ row, isExpanded, toggle }">
|
||||||
|
<UButton v-if="row.hasExpand" @click="toggle">
|
||||||
|
{{ isExpanded ? 'collapse' : 'expand' }}
|
||||||
|
</UButton>
|
||||||
|
</template>
|
||||||
|
</UTable>
|
||||||
|
</template>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<script setup>
|
<script setup lang='ts'>
|
||||||
const people = [{
|
const people = [{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Lindsay Walton',
|
name: 'Lindsay Walton',
|
||||||
@@ -36,10 +36,15 @@ const people = [{
|
|||||||
email: 'floyd.miles@example.com',
|
email: 'floyd.miles@example.com',
|
||||||
role: 'Member'
|
role: 'Member'
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
const expand = ref({
|
||||||
|
openedRows: [people[0]],
|
||||||
|
row: {}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UTable :rows="people">
|
<UTable v-model:expand="expand" :rows="people">
|
||||||
<template #expand="{ row }">
|
<template #expand="{ row }">
|
||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
<pre>{{ row }}</pre>
|
<pre>{{ row }}</pre>
|
||||||
|
|||||||
@@ -34,53 +34,55 @@ const pending = ref(true)
|
|||||||
/* https://codepen.io/jenning/pen/YzNmzaV */
|
/* https://codepen.io/jenning/pen/YzNmzaV */
|
||||||
|
|
||||||
.loader {
|
.loader {
|
||||||
--color: rgb(var(--color-primary-400));
|
--color: rgb(var(--color-primary-400));
|
||||||
--size-mid: 6vmin;
|
--size-mid: 6vmin;
|
||||||
--size-dot: 1.5vmin;
|
--size-dot: 1.5vmin;
|
||||||
--size-bar: 0.4vmin;
|
--size-bar: 0.4vmin;
|
||||||
--size-square: 3vmin;
|
--size-square: 3vmin;
|
||||||
|
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 50%;
|
width: 50%;
|
||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loader::before,
|
.loader::before,
|
||||||
.loader::after {
|
.loader::after {
|
||||||
content: '';
|
content: '';
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
loader --6
|
loader --6
|
||||||
**/
|
**/
|
||||||
.loader.--6::before {
|
.loader.--6::before {
|
||||||
width: var(--size-square);
|
width: var(--size-square);
|
||||||
height: var(--size-square);
|
height: var(--size-square);
|
||||||
background-color: var(--color);
|
background-color: var(--color);
|
||||||
top: calc(50% - var(--size-square));
|
top: calc(50% - var(--size-square));
|
||||||
left: calc(50% - var(--size-square));
|
left: calc(50% - var(--size-square));
|
||||||
animation: loader-6 2.4s cubic-bezier(0, 0, 0.24, 1.21) infinite;
|
animation: loader-6 2.4s cubic-bezier(0, 0, 0.24, 1.21) infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes loader-6 {
|
@keyframes loader-6 {
|
||||||
0%, 100% {
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
25% {
|
0%,
|
||||||
transform: translateX(100%);
|
100% {
|
||||||
}
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
50% {
|
25% {
|
||||||
transform: translateX(100%) translateY(100%);
|
transform: translateX(100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
75% {
|
50% {
|
||||||
transform: translateY(100%);
|
transform: translateX(100%) translateY(100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
75% {
|
||||||
|
transform: translateY(100%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ const people = [{
|
|||||||
role: 'Member'
|
role: 'Member'
|
||||||
}]
|
}]
|
||||||
|
|
||||||
const items = (row) => [
|
const items = row => [
|
||||||
[{
|
[{
|
||||||
label: 'Edit',
|
label: 'Edit',
|
||||||
icon: 'i-heroicons-pencil-square-20-solid',
|
icon: 'i-heroicons-pencil-square-20-solid',
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const items = [{
|
|||||||
content: 'Finally, this is the content for Tab3'
|
content: 'Finally, this is the content for Tab3'
|
||||||
}]
|
}]
|
||||||
|
|
||||||
function onChange (index) {
|
function onChange(index) {
|
||||||
const item = items[index]
|
const item = items[index]
|
||||||
|
|
||||||
alert(`${item.label} was clicked!`)
|
alert(`${item.label} was clicked!`)
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ const items = [{
|
|||||||
const accountForm = reactive({ name: 'Benjamin', username: 'benjamincanac' })
|
const accountForm = reactive({ name: 'Benjamin', username: 'benjamincanac' })
|
||||||
const passwordForm = reactive({ currentPassword: '', newPassword: '' })
|
const passwordForm = reactive({ currentPassword: '', newPassword: '' })
|
||||||
|
|
||||||
function onSubmitAccount () {
|
function onSubmitAccount() {
|
||||||
console.log('Submitted form:', accountForm)
|
console.log('Submitted form:', accountForm)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSubmitPassword () {
|
function onSubmitPassword() {
|
||||||
console.log('Submitted form:', passwordForm)
|
console.log('Submitted form:', passwordForm)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const items = [{
|
|||||||
const accountForm = reactive({ name: 'Benjamin', username: 'benjamincanac' })
|
const accountForm = reactive({ name: 'Benjamin', username: 'benjamincanac' })
|
||||||
const passwordForm = reactive({ currentPassword: '', newPassword: '' })
|
const passwordForm = reactive({ currentPassword: '', newPassword: '' })
|
||||||
|
|
||||||
function onSubmit (form) {
|
function onSubmit(form) {
|
||||||
console.log('Submitted form:', form)
|
console.log('Submitted form:', form)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -17,15 +17,15 @@ const route = useRoute()
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const selected = computed({
|
const selected = computed({
|
||||||
get () {
|
get() {
|
||||||
const index = items.findIndex((item) => item.label === route.query.tab)
|
const index = items.findIndex(item => item.label === route.query.tab)
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return index
|
return index
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
// Hash is specified here to prevent the page from scrolling to the top
|
// Hash is specified here to prevent the page from scrolling to the top
|
||||||
router.replace({ query: { tab: items[value].label }, hash: '#control-the-selected-index' })
|
router.replace({ query: { tab: items[value].label }, hash: '#control-the-selected-index' })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useBreakpoints, breakpointsTailwind } from '@vueuse/core'
|
|
||||||
import { DatePicker as VCalendarDatePicker } from 'v-calendar'
|
import { DatePicker as VCalendarDatePicker } from 'v-calendar'
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import type { DatePickerDate, DatePickerRangeObject } from 'v-calendar/dist/types/src/use/datePicker'
|
import type { DatePickerDate, DatePickerRangeObject } from 'v-calendar/dist/types/src/use/datePicker'
|
||||||
@@ -26,22 +25,27 @@ const date = computed({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const breakpoints = useBreakpoints(breakpointsTailwind)
|
|
||||||
|
|
||||||
const smallerThanSm = breakpoints.smaller('sm')
|
|
||||||
|
|
||||||
const attrs = {
|
const attrs = {
|
||||||
transparent: true,
|
'transparent': true,
|
||||||
borderless: true,
|
'borderless': true,
|
||||||
color: 'primary',
|
'color': 'primary',
|
||||||
'is-dark': { selector: 'html', darkClass: 'dark' },
|
'is-dark': { selector: 'html', darkClass: 'dark' },
|
||||||
'first-day-of-week': 2
|
'first-day-of-week': 2
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VCalendarDatePicker v-if="date && (date as DatePickerRangeObject)?.start && (date as DatePickerRangeObject)?.end" v-model.range="date" :columns="smallerThanSm ? 1 : 2" :rows="smallerThanSm ? 2 : 1" v-bind="{ ...attrs, ...$attrs }" />
|
<VCalendarDatePicker
|
||||||
<VCalendarDatePicker v-else v-model="date" v-bind="{ ...attrs, ...$attrs }" />
|
v-if="date && (date as DatePickerRangeObject)?.start && (date as DatePickerRangeObject)?.end"
|
||||||
|
v-model.range="date"
|
||||||
|
:columns="2"
|
||||||
|
v-bind="{ ...attrs, ...$attrs }"
|
||||||
|
/>
|
||||||
|
<VCalendarDatePicker
|
||||||
|
v-else
|
||||||
|
v-model="date"
|
||||||
|
v-bind="{ ...attrs, ...$attrs }"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
v-else
|
v-else
|
||||||
class="font-semibold flex flex-col gap-1 text-center"
|
class="font-semibold flex flex-col gap-1 text-center"
|
||||||
:class="[
|
:class="[
|
||||||
!block.slot && (block.inactive || block.inactive === undefined ? 'text-gray-900 dark:text-white' : 'text-white dark:text-gray-900'),
|
!block.slot && (block.inactive || block.inactive === undefined ? 'text-gray-900 dark:text-white' : 'text-white dark:text-gray-900')
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
{{ block.name }}
|
{{ block.name }}
|
||||||
|
|||||||
@@ -36,21 +36,22 @@ const cols = ref(0)
|
|||||||
|
|
||||||
const { width, height } = useElementSize(el)
|
const { width, height } = useElementSize(el)
|
||||||
|
|
||||||
function createGrid () {
|
function createGrid() {
|
||||||
grid.value = []
|
grid.value = []
|
||||||
|
|
||||||
for (let i = 0; i <= rows.value; i++) {
|
for (let i = 0; i <= rows.value; i++) {
|
||||||
|
// eslint-disable-next-line unicorn/no-new-array
|
||||||
grid.value.push(new Array(cols.value).fill(null))
|
grid.value.push(new Array(cols.value).fill(null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNewCell () {
|
function createNewCell() {
|
||||||
const x = Math.floor(Math.random() * cols.value)
|
const x = Math.floor(Math.random() * cols.value)
|
||||||
|
|
||||||
grid.value[0][x] = true
|
grid.value[0][x] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveCellsDown () {
|
function moveCellsDown() {
|
||||||
for (let row = rows.value - 1; row >= 0; row--) {
|
for (let row = rows.value - 1; row >= 0; row--) {
|
||||||
for (let col = 0; col < cols.value; col++) {
|
for (let col = 0; col < cols.value; col++) {
|
||||||
if (grid.value[row][col] !== null && grid.value[row + 1][col] === null) {
|
if (grid.value[row][col] !== null && grid.value[row + 1][col] === null) {
|
||||||
@@ -69,11 +70,11 @@ function moveCellsDown () {
|
|||||||
}, 500)
|
}, 500)
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeCell (row, col) {
|
function removeCell(row, col) {
|
||||||
grid.value[row][col] = null
|
grid.value[row][col] = null
|
||||||
}
|
}
|
||||||
|
|
||||||
function calcGrid () {
|
function calcGrid() {
|
||||||
const base = Math.ceil(width.value / 60)
|
const base = Math.ceil(width.value / 60)
|
||||||
const cell = width.value / base
|
const cell = width.value / base
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
const useComponentsMetaState = () => useState('components-meta', () => ({}))
|
const useComponentsMetaState = () => useState('components-meta', () => ({}))
|
||||||
|
|
||||||
export async function fetchComponentMeta (name: string) {
|
export async function fetchComponentMeta(name: string) {
|
||||||
const state = useComponentsMetaState()
|
const state = useComponentsMetaState()
|
||||||
|
|
||||||
if (state.value[name]?.then) {
|
if (state.value[name]?.then) {
|
||||||
await state.value[name]
|
await state.value[name]
|
||||||
return state.value[name]
|
return state.value[name]
|
||||||
}
|
}
|
||||||
if (state.value[name]) { return state.value[name] }
|
if (state.value[name]) {
|
||||||
|
return state.value[name]
|
||||||
|
}
|
||||||
|
|
||||||
// Store promise to avoid multiple calls
|
// Store promise to avoid multiple calls
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
const useContentExamplesCodeState = () => useState('content-examples-code', () => ({}))
|
const useContentExamplesCodeState = () => useState('content-examples-code', () => ({}))
|
||||||
|
|
||||||
export async function fetchContentExampleCode (name?: string) {
|
export async function fetchContentExampleCode(name?: string) {
|
||||||
if (!name) return
|
if (!name) return
|
||||||
const state = useContentExamplesCodeState()
|
const state = useContentExamplesCodeState()
|
||||||
|
|
||||||
@@ -8,7 +8,9 @@ export async function fetchContentExampleCode (name?: string) {
|
|||||||
await state.value[name]
|
await state.value[name]
|
||||||
return state.value[name]
|
return state.value[name]
|
||||||
}
|
}
|
||||||
if (state.value[name]) { return state.value[name] }
|
if (state.value[name]) {
|
||||||
|
return state.value[name]
|
||||||
|
}
|
||||||
|
|
||||||
// add to nitro prerender
|
// add to nitro prerender
|
||||||
if (import.meta.server) {
|
if (import.meta.server) {
|
||||||
|
|||||||
@@ -38,9 +38,14 @@ The following example is styled based on the `primary` and `gray` colors and sup
|
|||||||
```vue [components/DatePicker.vue]
|
```vue [components/DatePicker.vue]
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { DatePicker as VCalendarDatePicker } from 'v-calendar'
|
import { DatePicker as VCalendarDatePicker } from 'v-calendar'
|
||||||
|
// @ts-ignore
|
||||||
import type { DatePickerDate, DatePickerRangeObject } from 'v-calendar/dist/types/src/use/datePicker'
|
import type { DatePickerDate, DatePickerRangeObject } from 'v-calendar/dist/types/src/use/datePicker'
|
||||||
import 'v-calendar/dist/style.css'
|
import 'v-calendar/dist/style.css'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
inheritAttrs: false
|
||||||
|
})
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: [Date, Object] as PropType<DatePickerDate | DatePickerRangeObject | null>,
|
type: [Date, Object] as PropType<DatePickerDate | DatePickerRangeObject | null>,
|
||||||
@@ -59,17 +64,26 @@ const date = computed({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const attrs = {
|
const attrs = {
|
||||||
transparent: true,
|
'transparent': true,
|
||||||
borderless: true,
|
'borderless': true,
|
||||||
color: 'primary',
|
'color': 'primary',
|
||||||
'is-dark': { selector: 'html', darkClass: 'dark' },
|
'is-dark': { selector: 'html', darkClass: 'dark' },
|
||||||
'first-day-of-week': 2,
|
'first-day-of-week': 2
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VCalendarDatePicker v-if="date && (typeof date === 'object')" v-model.range="date" :columns="2" v-bind="{ ...attrs, ...$attrs }" />
|
<VCalendarDatePicker
|
||||||
<VCalendarDatePicker v-else v-model="date" v-bind="{ ...attrs, ...$attrs }" />
|
v-if="date && (date as DatePickerRangeObject)?.start && (date as DatePickerRangeObject)?.end"
|
||||||
|
v-model.range="date"
|
||||||
|
:columns="2"
|
||||||
|
v-bind="{ ...attrs, ...$attrs }"
|
||||||
|
/>
|
||||||
|
<VCalendarDatePicker
|
||||||
|
v-else
|
||||||
|
v-model="date"
|
||||||
|
v-bind="{ ...attrs, ...$attrs }"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ links:
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Use the Form component to validate form data using schema libraries such as [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod), [Joi](https://github.com/hapijs/joi), [Valibot](https://github.com/fabian-hiller/valibot), or your own validation logic.
|
Use the Form component to validate form data using schema libraries such as [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod), [Joi](https://github.com/hapijs/joi), [Valibot](https://github.com/fabian-hiller/valibot), [Superstruct](https://github.com/ianstormtaylor/superstruct), or your own validation logic.
|
||||||
|
|
||||||
It works with the [FormGroup](/components/form-group) component to display error messages around form elements automatically.
|
It works with the [FormGroup](/components/form-group) component to display error messages around form elements automatically.
|
||||||
|
|
||||||
The form component requires two props:
|
The form component requires two props:
|
||||||
- `state` - a reactive object holding the form's state.
|
- `state` - a reactive object holding the form's state.
|
||||||
- `schema` - a schema object from a validation library like [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod), [Joi](https://github.com/hapijs/joi) or [Valibot](https://github.com/fabian-hiller/valibot).
|
- `schema` - a schema object from a validation library like [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod), [Joi](https://github.com/hapijs/joi), [Valibot](https://github.com/fabian-hiller/valibot) or [Superstruct](https://github.com/ianstormtaylor/superstruct).
|
||||||
|
|
||||||
::callout{icon="i-heroicons-light-bulb"}
|
::callout{icon="i-heroicons-light-bulb"}
|
||||||
Note that **no validation library is included** by default, so ensure you **install the one you need**.
|
Note that **no validation library is included** by default, so ensure you **install the one you need**.
|
||||||
@@ -52,6 +52,13 @@ Note that **no validation library is included** by default, so ensure you **inst
|
|||||||
class: 'w-60'
|
class: 'w-60'
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
::component-example{label="Superstruct"}
|
||||||
|
---
|
||||||
|
component: 'form-example-superstruct'
|
||||||
|
componentProps:
|
||||||
|
class: 'w-60'
|
||||||
|
---
|
||||||
|
::
|
||||||
::
|
::
|
||||||
|
|
||||||
## Custom validation
|
## Custom validation
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ This component does not support multiple values. Use the [SelectMenu](/component
|
|||||||
|
|
||||||
### Objects
|
### Objects
|
||||||
|
|
||||||
You can pass an array of objects to `options` and either compare on the whole object or use the `by` prop to compare on a specific key. You can configure which field will be used to display the label through the `option-attribute` prop that defaults to `label`.
|
You can pass an array of objects to `options` and either compare on the whole object or use the `by` prop to compare on a specific key. You can configure which field will be used to display the label through the `option-attribute` prop that defaults to `label`. Additionally, you can use dot notation (e.g., `user.name`) to access nested object properties.
|
||||||
|
|
||||||
::component-example
|
::component-example
|
||||||
---
|
---
|
||||||
@@ -174,6 +174,8 @@ componentProps:
|
|||||||
|
|
||||||
Use the `#option-empty` slot to customize the content displayed when the `searchable` prop is `true` and there is no options. You will have access to the `query` property in the slot scope.
|
Use the `#option-empty` slot to customize the content displayed when the `searchable` prop is `true` and there is no options. You will have access to the `query` property in the slot scope.
|
||||||
|
|
||||||
|
You can also configure this globally through the `ui.inputMenu.default.optionEmpty.label` config. The token `{query}` will be replaced by `query` property. Defaults to `No results for "{query}".`.
|
||||||
|
|
||||||
::component-example
|
::component-example
|
||||||
---
|
---
|
||||||
component: 'input-menu-example-option-empty-slot'
|
component: 'input-menu-example-option-empty-slot'
|
||||||
@@ -186,6 +188,8 @@ componentProps:
|
|||||||
|
|
||||||
Use the `#empty` slot to customize the content displayed when there is no options. Defaults to `No options.`.
|
Use the `#empty` slot to customize the content displayed when there is no options. Defaults to `No options.`.
|
||||||
|
|
||||||
|
You can also configure this globally through the `ui.inputMenu.default.empty.label` config. Defaults to `No options.`.
|
||||||
|
|
||||||
::component-example
|
::component-example
|
||||||
---
|
---
|
||||||
component: 'input-menu-example-empty-slot'
|
component: 'input-menu-example-empty-slot'
|
||||||
|
|||||||
@@ -29,12 +29,16 @@ export default defineAppConfig({
|
|||||||
ui: {
|
ui: {
|
||||||
notifications: {
|
notifications: {
|
||||||
// Show toasts at the top right of the screen
|
// Show toasts at the top right of the screen
|
||||||
position: 'top-0 right-0'
|
position: 'top-0 bottom-[unset]'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
::callout{icon="i-heroicons-light-bulb"}
|
||||||
|
The `position` defaults to `bottom-0 end-0`, the `bottom-[unset]` class overrides `bottom-0` so the result is `top-0 end-0`.
|
||||||
|
::
|
||||||
|
|
||||||
Then, you can use the `useToast` composable to add notifications to your app:
|
Then, you can use the `useToast` composable to add notifications to your app:
|
||||||
|
|
||||||
:component-example{component="notification-example-basic"}
|
:component-example{component="notification-example-basic"}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ componentProps:
|
|||||||
|
|
||||||
### Objects
|
### Objects
|
||||||
|
|
||||||
You can pass an array of objects to `options` and either compare on the whole object or use the `by` prop to compare on a specific key. You can configure which field will be used to display the label through the `option-attribute` prop that defaults to `label`.
|
You can pass an array of objects to `options` and either compare on the whole object or use the `by` prop to compare on a specific key. You can configure which field will be used to display the label through the `option-attribute` prop that defaults to `label`. Additionally, you can use dot notation (e.g., `user.name`) to access nested object properties.
|
||||||
|
|
||||||
::component-example
|
::component-example
|
||||||
---
|
---
|
||||||
@@ -85,7 +85,7 @@ Learn how to customize icons from the [Select](/components/select#icon) componen
|
|||||||
|
|
||||||
Use the `searchable` prop to enable search.
|
Use the `searchable` prop to enable search.
|
||||||
|
|
||||||
Use the `searchable-placeholder` prop to set a different placeholder.
|
Use the `searchable-placeholder` prop to set a different placeholder or globally through the `ui.selectMenu.default.searchablePlaceholder.label` config. Defaults to `Search...`.
|
||||||
|
|
||||||
This will use Headless UI [Combobox](https://headlessui.com/v1/vue/combobox) component instead of [Listbox](https://headlessui.com/v1/vue/listbox).
|
This will use Headless UI [Combobox](https://headlessui.com/v1/vue/combobox) component instead of [Listbox](https://headlessui.com/v1/vue/listbox).
|
||||||
|
|
||||||
@@ -258,6 +258,8 @@ componentProps:
|
|||||||
|
|
||||||
Use the `#option-empty` slot to customize the content displayed when the `searchable` prop is `true` and there is no options. You will have access to the `query` property in the slot scope.
|
Use the `#option-empty` slot to customize the content displayed when the `searchable` prop is `true` and there is no options. You will have access to the `query` property in the slot scope.
|
||||||
|
|
||||||
|
You can also configure this globally through the `ui.selectMenu.default.optionEmpty.label` config. The token `{query}` will be replaced by `query` property. Defaults to `No results for "{query}".`.
|
||||||
|
|
||||||
::component-example
|
::component-example
|
||||||
---
|
---
|
||||||
component: 'select-menu-example-option-empty-slot'
|
component: 'select-menu-example-option-empty-slot'
|
||||||
@@ -276,7 +278,9 @@ An example is available in the [Creatable](#creatable) section.
|
|||||||
|
|
||||||
### `empty`
|
### `empty`
|
||||||
|
|
||||||
Use the `#empty` slot to customize the content displayed when there is no options. Defaults to `No options.`.
|
Use the `#empty` slot to customize the content displayed when there is no options.
|
||||||
|
|
||||||
|
You can also configure this globally through the `ui.selectMenu.default.empty.label` config. Defaults to `No options.`.
|
||||||
|
|
||||||
::component-example
|
::component-example
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ Use the `columns` prop to configure which columns to display. It's an array of o
|
|||||||
- `direction` - The sort direction to use on first click. Defaults to `asc`.
|
- `direction` - The sort direction to use on first click. Defaults to `asc`.
|
||||||
- `class` - The class to apply to the column cells.
|
- `class` - The class to apply to the column cells.
|
||||||
- `rowClass` - The class to apply to the data column cells. :u-badge{label="New" class="!rounded-full" variant="subtle"}
|
- `rowClass` - The class to apply to the data column cells. :u-badge{label="New" class="!rounded-full" variant="subtle"}
|
||||||
- `sort` - Pass your own `sort` function. Defaults to a simple _greater than_ / _less than_ comparison.
|
- `sort` - Pass your own `sort` function. Defaults to a simple _greater than_ / _less than_ comparison.
|
||||||
|
|
||||||
Arguments for the `sort` function are: Value A, Value B, Direction - 'asc' or 'desc'
|
Arguments for the `sort` function are: Value A, Value B, Direction - 'asc' or 'desc'
|
||||||
|
|
||||||
@@ -315,10 +315,13 @@ componentProps:
|
|||||||
|
|
||||||
### Expandable :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
|
### Expandable :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"}
|
||||||
|
|
||||||
You can use the `expand` slot to display extra information about a row. You will have access to the `row` property in the slot scope.
|
You can use the `v-model:expand` to enables row expansion functionality in the table component. It maintains an object containing an `openedRows` an array and `row` an object, which tracks the indices of currently expanded rows.
|
||||||
|
|
||||||
|
When using the expand slot, you have access to the `row` property in the slot scope, which contains the data of the row that triggered the expand/collapse action. This allows you to customize the expanded content based on the row's data.
|
||||||
|
|
||||||
::component-example{class="grid"}
|
::component-example{class="grid"}
|
||||||
---
|
---
|
||||||
|
extraClass: 'overflow-hidden'
|
||||||
padding: false
|
padding: false
|
||||||
component: 'table-example-expandable'
|
component: 'table-example-expandable'
|
||||||
componentProps:
|
componentProps:
|
||||||
@@ -326,6 +329,73 @@ componentProps:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
#### Event expand
|
||||||
|
|
||||||
|
The `@update:expand` event is emitted when a row is expanded. This event provides the current state of expanded rows and the data of the row that triggered the event.
|
||||||
|
|
||||||
|
To use the `@update:expand` event, add it to your `UTable` component. The event handler will receive an object with the following properties:
|
||||||
|
- `openedRows`: An array of indices of the currently expanded rows.
|
||||||
|
- `row`: The row data that triggered the expand/collapse action.
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
const { data, pending } = await useLazyFetch(() => `/api/users`)
|
||||||
|
|
||||||
|
const handleExpand = ({ openedRows, row }) => {
|
||||||
|
console.log('opened Rows:', openedRows);
|
||||||
|
console.log('Row Data:', row);
|
||||||
|
};
|
||||||
|
|
||||||
|
const expand = ref({
|
||||||
|
openedRows: [],
|
||||||
|
row: null
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<UTable v-model="expand" :loading="pending" :rows="data" @update:expand="handleExpand">
|
||||||
|
<template #expand="{ row }">
|
||||||
|
<div class="p-4">
|
||||||
|
<pre>{{ row }}</pre>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UTable>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Multiple expand
|
||||||
|
Controls whether multiple rows can be expanded simultaneously in the table.
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<!-- Allow only one row to be expanded at a time -->
|
||||||
|
<UTable :multiple-expand="false" />
|
||||||
|
|
||||||
|
<!-- Default behavior: Allow multiple rows to be expanded simultaneously -->
|
||||||
|
<UTable :multiple-expand="true" />
|
||||||
|
|
||||||
|
<!-- Or simply -->
|
||||||
|
<UTable />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Disable Row Expansion
|
||||||
|
|
||||||
|
You can disable the expansion functionality for specific rows in the UTable component by adding the `disabledExpand` property to your row data.
|
||||||
|
|
||||||
|
> Important: When using `disabledExpand`, you must define the `columns` prop for the UTable component. Otherwise, the table will render all properties as columns, including the `disabledExpand` property.
|
||||||
|
|
||||||
|
::component-example{class="grid"}
|
||||||
|
---
|
||||||
|
extraClass: 'overflow-hidden'
|
||||||
|
padding: false
|
||||||
|
component: 'table-example-disabled-expandable'
|
||||||
|
componentProps:
|
||||||
|
class: 'flex-1'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
### Loading
|
### Loading
|
||||||
|
|
||||||
Use the `loading` prop to indicate that data is currently loading with an indeterminate [Progress](/components/progress#indeterminate) bar.
|
Use the `loading` prop to indicate that data is currently loading with an indeterminate [Progress](/components/progress#indeterminate) bar.
|
||||||
@@ -448,6 +518,43 @@ componentProps:
|
|||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|
||||||
|
### `expand-action`
|
||||||
|
|
||||||
|
The `#expand-action` slot allows you to customize the expansion control interface for expandable table rows. This feature provides a flexible way to implement custom expand/collapse functionality while maintaining access to essential row data and state.
|
||||||
|
|
||||||
|
#### Usage
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<UTable>
|
||||||
|
<template #expand-action="{ row, toggle, isExpanded }">
|
||||||
|
<!-- Your custom expand action content -->
|
||||||
|
</template>
|
||||||
|
</UTable>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Slot Props
|
||||||
|
|
||||||
|
The slot provides three key props:
|
||||||
|
|
||||||
|
| Prop | Type | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `row` | `Object` | Contains the current row's data |
|
||||||
|
| `toggle` | `Function` | Function to toggle the expanded state |
|
||||||
|
| `isExpanded` | `Boolean` | Current expansion state of the row |
|
||||||
|
|
||||||
|
::component-example{class="grid"}
|
||||||
|
---
|
||||||
|
extraClass: 'overflow-hidden'
|
||||||
|
padding: false
|
||||||
|
component: 'table-example-expand-action-slot'
|
||||||
|
componentProps:
|
||||||
|
class: 'flex-1'
|
||||||
|
---
|
||||||
|
::
|
||||||
|
|
||||||
|
|
||||||
### `loading-state`
|
### `loading-state`
|
||||||
|
|
||||||
Use the `#loading-state` slot to customize the loading state.
|
Use the `#loading-state` slot to customize the loading state.
|
||||||
|
|||||||
@@ -29,8 +29,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { NuxtError } from '#app'
|
|
||||||
import type { ParsedContent } from '@nuxt/content'
|
import type { ParsedContent } from '@nuxt/content'
|
||||||
|
import type { NuxtError } from '#app'
|
||||||
|
|
||||||
useSeoMeta({
|
useSeoMeta({
|
||||||
title: 'Page not found',
|
title: 'Page not found',
|
||||||
@@ -57,20 +57,22 @@ const links = computed(() => {
|
|||||||
icon: 'i-heroicons-book-open',
|
icon: 'i-heroicons-book-open',
|
||||||
to: '/getting-started',
|
to: '/getting-started',
|
||||||
active: route.path.startsWith('/getting-started') || route.path.startsWith('/components')
|
active: route.path.startsWith('/getting-started') || route.path.startsWith('/components')
|
||||||
}, ...(navigation.value.find(item => item._path === '/pro') ? [{
|
}, ...(navigation.value.find(item => item._path === '/pro')
|
||||||
label: 'Pro',
|
? [{
|
||||||
icon: 'i-heroicons-square-3-stack-3d',
|
label: 'Pro',
|
||||||
to: '/pro',
|
icon: 'i-heroicons-square-3-stack-3d',
|
||||||
active: route.path.startsWith('/pro/getting-started') || route.path.startsWith('/pro/components') || route.path.startsWith('/pro/prose')
|
to: '/pro',
|
||||||
}, {
|
active: route.path.startsWith('/pro/getting-started') || route.path.startsWith('/pro/components') || route.path.startsWith('/pro/prose')
|
||||||
label: 'Pricing',
|
}, {
|
||||||
icon: 'i-heroicons-ticket',
|
label: 'Pricing',
|
||||||
to: '/pro/pricing'
|
icon: 'i-heroicons-ticket',
|
||||||
}, {
|
to: '/pro/pricing'
|
||||||
label: 'Templates',
|
}, {
|
||||||
icon: 'i-heroicons-computer-desktop',
|
label: 'Templates',
|
||||||
to: '/pro/templates'
|
icon: 'i-heroicons-computer-desktop',
|
||||||
}] : []), {
|
to: '/pro/templates'
|
||||||
|
}]
|
||||||
|
: []), {
|
||||||
label: 'Releases',
|
label: 'Releases',
|
||||||
icon: 'i-heroicons-rocket-launch',
|
icon: 'i-heroicons-rocket-launch',
|
||||||
to: '/releases'
|
to: '/releases'
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import { existsSync, readFileSync } from 'node:fs'
|
||||||
|
import fsp from 'node:fs/promises'
|
||||||
|
import { dirname, join } from 'pathe'
|
||||||
import {
|
import {
|
||||||
defineNuxtModule,
|
defineNuxtModule,
|
||||||
addTemplate,
|
addTemplate,
|
||||||
@@ -5,28 +8,24 @@ import {
|
|||||||
createResolver
|
createResolver
|
||||||
} from '@nuxt/kit'
|
} from '@nuxt/kit'
|
||||||
|
|
||||||
import { existsSync, readFileSync } from 'fs'
|
|
||||||
import { dirname, join } from 'pathe'
|
|
||||||
import fsp from 'fs/promises'
|
|
||||||
|
|
||||||
export default defineNuxtModule({
|
export default defineNuxtModule({
|
||||||
meta: {
|
meta: {
|
||||||
name: 'content-examples-code'
|
name: 'content-examples-code'
|
||||||
},
|
},
|
||||||
async setup (_options, nuxt) {
|
async setup(_options, nuxt) {
|
||||||
const resolver = createResolver(import.meta.url)
|
const resolver = createResolver(import.meta.url)
|
||||||
let _configResolved: any
|
let _configResolved: any
|
||||||
let components: Record<string, any>
|
let components: Record<string, any>
|
||||||
const outputPath = join(nuxt.options.buildDir, 'content-examples-code')
|
const outputPath = join(nuxt.options.buildDir, 'content-examples-code')
|
||||||
|
|
||||||
async function stubOutput () {
|
async function stubOutput() {
|
||||||
if (existsSync(outputPath + '.mjs')) {
|
if (existsSync(outputPath + '.mjs')) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await updateOutput('export default {}')
|
await updateOutput('export default {}')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchComponent (component: string | any) {
|
async function fetchComponent(component: string | any) {
|
||||||
if (typeof component === 'string') {
|
if (typeof component === 'string') {
|
||||||
if (components[component]) {
|
if (components[component]) {
|
||||||
component = components[component]
|
component = components[component]
|
||||||
@@ -57,7 +56,7 @@ export default defineNuxtModule({
|
|||||||
const getVirtualModuleContent = () =>
|
const getVirtualModuleContent = () =>
|
||||||
`export default ${getStringifiedComponents()}`
|
`export default ${getStringifiedComponents()}`
|
||||||
|
|
||||||
async function updateOutput (content?: string) {
|
async function updateOutput(content?: string) {
|
||||||
const path = outputPath + '.mjs'
|
const path = outputPath + '.mjs'
|
||||||
if (!existsSync(dirname(path))) {
|
if (!existsSync(dirname(path))) {
|
||||||
await fsp.mkdir(dirname(path), { recursive: true })
|
await fsp.mkdir(dirname(path), { recursive: true })
|
||||||
@@ -68,13 +67,13 @@ export default defineNuxtModule({
|
|||||||
await fsp.writeFile(path, content || getVirtualModuleContent(), 'utf-8')
|
await fsp.writeFile(path, content || getVirtualModuleContent(), 'utf-8')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchComponents () {
|
async function fetchComponents() {
|
||||||
await Promise.all(Object.keys(components).map(fetchComponent))
|
await Promise.all(Object.keys(components).map(fetchComponent))
|
||||||
}
|
}
|
||||||
|
|
||||||
nuxt.hook('components:extend', async (_components) => {
|
nuxt.hook('components:extend', async (_components) => {
|
||||||
components = _components
|
components = _components
|
||||||
.filter((v) => v.shortPath.includes('components/content/examples/'))
|
.filter(v => v.shortPath.includes('components/content/examples/'))
|
||||||
.reduce((acc, component) => {
|
.reduce((acc, component) => {
|
||||||
acc[component.pascalName] = component
|
acc[component.pascalName] = component
|
||||||
return acc
|
return acc
|
||||||
@@ -93,17 +92,17 @@ export default defineNuxtModule({
|
|||||||
vite.config.plugins.push({
|
vite.config.plugins.push({
|
||||||
name: 'content-examples-code',
|
name: 'content-examples-code',
|
||||||
enforce: 'post',
|
enforce: 'post',
|
||||||
async buildStart () {
|
async buildStart() {
|
||||||
if (_configResolved?.build.ssr) {
|
if (_configResolved?.build.ssr) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await fetchComponents()
|
await fetchComponents()
|
||||||
await updateOutput()
|
await updateOutput()
|
||||||
},
|
},
|
||||||
configResolved (config) {
|
configResolved(config) {
|
||||||
_configResolved = config
|
_configResolved = config
|
||||||
},
|
},
|
||||||
async handleHotUpdate ({ file }) {
|
async handleHotUpdate({ file }) {
|
||||||
if (
|
if (
|
||||||
Object.entries(components).some(
|
Object.entries(components).some(
|
||||||
([, comp]: any) => comp.filePath === file
|
([, comp]: any) => comp.filePath === file
|
||||||
|
|||||||
@@ -8,13 +8,15 @@ const { resolve } = createResolver(import.meta.url)
|
|||||||
|
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
extends: process.env.NUXT_UI_PRO_PATH ? [
|
extends: process.env.NUXT_UI_PRO_PATH
|
||||||
process.env.NUXT_UI_PRO_PATH,
|
? [
|
||||||
resolve(process.env.NUXT_UI_PRO_PATH, '.docs')
|
process.env.NUXT_UI_PRO_PATH,
|
||||||
] : [
|
resolve(process.env.NUXT_UI_PRO_PATH, '.docs')
|
||||||
'@nuxt/ui-pro',
|
]
|
||||||
process.env.NUXT_GITHUB_TOKEN && ['github:nuxt/ui-pro/.docs#dev', { giget: { auth: process.env.NUXT_GITHUB_TOKEN } }]
|
: [
|
||||||
].filter(Boolean),
|
'@nuxt/ui-pro',
|
||||||
|
process.env.NUXT_GITHUB_TOKEN && ['github:nuxt/ui-pro/.docs#dev', { giget: { auth: process.env.NUXT_GITHUB_TOKEN } }]
|
||||||
|
].filter(Boolean),
|
||||||
|
|
||||||
modules: [
|
modules: [
|
||||||
'@nuxt/content',
|
'@nuxt/content',
|
||||||
@@ -29,15 +31,8 @@ export default defineNuxtConfig({
|
|||||||
'modules/content-examples-code'
|
'modules/content-examples-code'
|
||||||
],
|
],
|
||||||
|
|
||||||
runtimeConfig: {
|
site: {
|
||||||
public: {
|
url: 'https://ui.nuxt.com'
|
||||||
version: pkg.version
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
ui: {
|
|
||||||
global: true,
|
|
||||||
safelistColors: excludeColors(colors)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
content: {
|
content: {
|
||||||
@@ -48,31 +43,42 @@ export default defineNuxtConfig({
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
sources: {
|
sources: {
|
||||||
pro: process.env.NUXT_UI_PRO_PATH ? {
|
pro: process.env.NUXT_UI_PRO_PATH
|
||||||
prefix: '/pro',
|
? {
|
||||||
driver: 'fs',
|
prefix: '/pro',
|
||||||
base: resolve(process.env.NUXT_UI_PRO_PATH, '.docs/content/pro')
|
driver: 'fs',
|
||||||
} : process.env.NUXT_GITHUB_TOKEN ? {
|
base: resolve(process.env.NUXT_UI_PRO_PATH, '.docs/content/pro')
|
||||||
prefix: '/pro',
|
}
|
||||||
driver: 'github',
|
: process.env.NUXT_GITHUB_TOKEN
|
||||||
repo: 'nuxt/ui-pro',
|
? {
|
||||||
branch: 'dev',
|
prefix: '/pro',
|
||||||
dir: '.docs/content/pro',
|
driver: 'github',
|
||||||
token: process.env.NUXT_GITHUB_TOKEN || ''
|
repo: 'nuxt/ui-pro',
|
||||||
} : undefined
|
branch: 'dev',
|
||||||
|
dir: '.docs/content/pro',
|
||||||
|
token: process.env.NUXT_GITHUB_TOKEN || ''
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
image: {
|
ui: {
|
||||||
provider: 'ipx'
|
global: true,
|
||||||
|
safelistColors: excludeColors(colors)
|
||||||
},
|
},
|
||||||
|
|
||||||
icon: {
|
runtimeConfig: {
|
||||||
clientBundle: {
|
public: {
|
||||||
scan: true
|
version: pkg.version
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
routeRules: {
|
||||||
|
'/components': { redirect: '/components/accordion', prerender: false }
|
||||||
|
},
|
||||||
|
|
||||||
|
compatibilityDate: '2024-07-23',
|
||||||
|
|
||||||
nitro: {
|
nitro: {
|
||||||
prerender: {
|
prerender: {
|
||||||
routes: [
|
routes: [
|
||||||
@@ -86,8 +92,32 @@ export default defineNuxtConfig({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
routeRules: {
|
vite: {
|
||||||
'/components': { redirect: '/components/accordion', prerender: false }
|
optimizeDeps: {
|
||||||
|
include: ['date-fns']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
typescript: {
|
||||||
|
strict: false
|
||||||
|
},
|
||||||
|
|
||||||
|
hooks: {
|
||||||
|
// Related to https://github.com/nuxt/nuxt/pull/22558
|
||||||
|
'components:extend': (components) => {
|
||||||
|
components.forEach((component) => {
|
||||||
|
if (component.shortPath.includes(process.env.NUXT_UI_PRO_PATH || '@nuxt/ui-pro')) {
|
||||||
|
component.global = true
|
||||||
|
} else if (component.global) {
|
||||||
|
component.global = 'sync'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
cloudflareAnalytics: {
|
||||||
|
token: '1e2b0c5e9a214f0390b9b94e043d8d4c',
|
||||||
|
scriptPath: false
|
||||||
},
|
},
|
||||||
|
|
||||||
componentMeta: {
|
componentMeta: {
|
||||||
@@ -111,37 +141,13 @@ export default defineNuxtConfig({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
cloudflareAnalytics: {
|
icon: {
|
||||||
token: '1e2b0c5e9a214f0390b9b94e043d8d4c',
|
clientBundle: {
|
||||||
scriptPath: false
|
scan: true
|
||||||
},
|
|
||||||
|
|
||||||
hooks: {
|
|
||||||
// Related to https://github.com/nuxt/nuxt/pull/22558
|
|
||||||
'components:extend': (components) => {
|
|
||||||
components.forEach((component) => {
|
|
||||||
if (component.shortPath.includes(process.env.NUXT_UI_PRO_PATH || '@nuxt/ui-pro')) {
|
|
||||||
component.global = true
|
|
||||||
} else if (component.global) {
|
|
||||||
component.global = 'sync'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
typescript: {
|
image: {
|
||||||
strict: false
|
provider: 'ipx'
|
||||||
},
|
}
|
||||||
|
|
||||||
site: {
|
|
||||||
url: 'https://ui.nuxt.com'
|
|
||||||
},
|
|
||||||
|
|
||||||
vite: {
|
|
||||||
optimizeDeps: {
|
|
||||||
include: ['date-fns']
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
compatibilityDate: '2024-07-23'
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,24 +4,22 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iconify-json/heroicons": "^1.2.1",
|
"@iconify-json/heroicons": "^1.2.1",
|
||||||
"@iconify-json/simple-icons": "^1.2.7",
|
"@iconify-json/simple-icons": "^1.2.11",
|
||||||
"@iconify-json/vscode-icons": "^1.2.2",
|
"@iconify-json/vscode-icons": "^1.2.2",
|
||||||
"@nuxt/content": "^2.13.2",
|
"@nuxt/content": "^2.13.4",
|
||||||
"@nuxt/eslint-config": "^0.4.0",
|
"@nuxt/fonts": "^0.10.2",
|
||||||
"@nuxt/fonts": "^0.10.0",
|
|
||||||
"@nuxt/image": "^1.8.1",
|
"@nuxt/image": "^1.8.1",
|
||||||
"@nuxt/ui": "latest",
|
"@nuxt/ui": "latest",
|
||||||
"@nuxt/ui-pro": "^1.4.3",
|
"@nuxt/ui-pro": "npm:@nuxt/ui-pro-edge@1.4.4-28846941.4241122",
|
||||||
"@nuxtjs/plausible": "^1.0.3",
|
"@nuxtjs/plausible": "^1.0.3",
|
||||||
"@octokit/rest": "^21.0.2",
|
"@octokit/rest": "^21.0.2",
|
||||||
"@vueuse/nuxt": "^11.1.0",
|
"@vueuse/nuxt": "^11.2.0",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"eslint": "^8.57.0",
|
|
||||||
"joi": "^17.13.3",
|
"joi": "^17.13.3",
|
||||||
"nuxt": "^3.13.2",
|
"nuxt": "^3.14.0",
|
||||||
"nuxt-cloudflare-analytics": "^1.0.8",
|
"nuxt-cloudflare-analytics": "^1.0.8",
|
||||||
"nuxt-component-meta": "^0.8.2",
|
"nuxt-component-meta": "^0.9.0",
|
||||||
"nuxt-og-image": "^3.0.4",
|
"nuxt-og-image": "^3.0.8",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"ufo": "^1.5.4",
|
"ufo": "^1.5.4",
|
||||||
"v-calendar": "^3.1.2",
|
"v-calendar": "^3.1.2",
|
||||||
|
|||||||
@@ -295,7 +295,7 @@
|
|||||||
wrapper: 'px-4 py-2.5 border-gray-800/10 dark:border-gray-200/10 cursor-pointer',
|
wrapper: 'px-4 py-2.5 border-gray-800/10 dark:border-gray-200/10 cursor-pointer',
|
||||||
icon: {
|
icon: {
|
||||||
wrapper: 'mb-2 p-1',
|
wrapper: 'mb-2 p-1',
|
||||||
base: 'h-4 w-4',
|
base: 'h-4 w-4'
|
||||||
},
|
},
|
||||||
title: 'text-sm',
|
title: 'text-sm',
|
||||||
description: 'text-xs'
|
description: 'text-xs'
|
||||||
@@ -466,188 +466,212 @@ const steps = {
|
|||||||
|
|
||||||
const inc = computed(() => (height.value - 32 - 64 - 32 - 32) / 4)
|
const inc = computed(() => (height.value - 32 - 64 - 32 - 32) / 4)
|
||||||
|
|
||||||
const landingBlocks = computed(() => isAfterStep(steps.landing) && isBeforeStep(steps.docs) ? [{
|
const landingBlocks = computed(() => isAfterStep(steps.landing) && isBeforeStep(steps.docs)
|
||||||
class: 'inset-x-0 top-20 bottom-20 overflow-hidden',
|
? [{
|
||||||
inactive: true,
|
class: 'inset-x-0 top-20 bottom-20 overflow-hidden',
|
||||||
children: [{
|
inactive: true,
|
||||||
name: 'ULandingHero',
|
|
||||||
to: '/pro/components/landing-hero',
|
|
||||||
class: [
|
|
||||||
'inset-4',
|
|
||||||
isAfterStep(steps.landing + 2) && '-top-[calc(var(--y)-var(--step-y)-1rem)] bottom-[calc(var(--y)-var(--step-y)+1rem)]'
|
|
||||||
].filter(Boolean).join(' '),
|
|
||||||
style: {
|
|
||||||
'--step-y': `${getStepY(steps.landing + 2)}px`
|
|
||||||
},
|
|
||||||
inactive: isAfterStep(steps.landing + 1),
|
|
||||||
children: [{
|
|
||||||
slot: 'landing-hero',
|
|
||||||
class: 'inset-4'
|
|
||||||
}]
|
|
||||||
}, isAfterStep(steps.landing + 2) && {
|
|
||||||
name: 'ULandingSection',
|
|
||||||
to: '/pro/components/landing-section',
|
|
||||||
class: [
|
|
||||||
'inset-4',
|
|
||||||
isBeforeStep(steps.landing + 6) && '-top-[calc(var(--y)-var(--prev-step-y)-var(--height)-1rem)] bottom-[calc(var(--y)-var(--prev-step-y)-var(--height)+1rem)]',
|
|
||||||
isAfterStep(steps.landing + 10) && '-top-[calc(var(--y)-var(--step-y)-1rem)] bottom-[calc(var(--y)-var(--step-y)+1rem)]'
|
|
||||||
].filter(Boolean).join(' '),
|
|
||||||
style: {
|
|
||||||
'--height': (inc.value * 4) + 'px',
|
|
||||||
'--step-y': `${getStepY(steps.landing + 10)}px`,
|
|
||||||
'--prev-step-y': `${getStepY(steps.landing + 2)}px`
|
|
||||||
},
|
|
||||||
inactive: isAfterStep(steps.landing + 7),
|
|
||||||
children: [{
|
|
||||||
slot: 'landing-section',
|
|
||||||
class: 'inset-x-4 top-16'
|
|
||||||
}, {
|
|
||||||
name: 'ULandingGrid',
|
|
||||||
to: '/pro/components/landing-grid',
|
|
||||||
class: ['inset-x-4 bottom-4 top-48', isAfterStep(steps.landing + 8) && 'grid grid-cols-4 gap-4 p-4'].filter(Boolean).join(' '),
|
|
||||||
inactive: isAfterStep(steps.landing + 8),
|
|
||||||
children: [isAfterStep(steps.landing + 9) ? {
|
|
||||||
slot: 'landing-card-1',
|
|
||||||
class: '!relative'
|
|
||||||
} : {
|
|
||||||
name: 'ULandingCard',
|
|
||||||
to: '/pro/components/landing-card',
|
|
||||||
class: '!relative h-full',
|
|
||||||
inactive: false
|
|
||||||
}, isAfterStep(steps.landing + 9) ? {
|
|
||||||
slot: 'landing-card-2',
|
|
||||||
class: '!relative h-full'
|
|
||||||
} : {
|
|
||||||
name: 'ULandingCard',
|
|
||||||
to: '/pro/components/landing-card',
|
|
||||||
class: '!relative h-full',
|
|
||||||
inactive: false
|
|
||||||
}, isAfterStep(steps.landing + 9) ? {
|
|
||||||
slot: 'landing-card-3',
|
|
||||||
class: '!relative h-full'
|
|
||||||
} : {
|
|
||||||
name: 'ULandingCard',
|
|
||||||
to: '/pro/components/landing-card',
|
|
||||||
class: '!relative h-full',
|
|
||||||
inactive: false
|
|
||||||
}, isAfterStep(steps.landing + 9) ? {
|
|
||||||
slot: 'landing-card-4',
|
|
||||||
class: '!relative h-full'
|
|
||||||
} : {
|
|
||||||
name: 'ULandingCard',
|
|
||||||
to: '/pro/components/landing-card',
|
|
||||||
class: '!relative h-full',
|
|
||||||
inactive: false
|
|
||||||
}]
|
|
||||||
}]
|
|
||||||
}, isAfterStep(steps.landing + 10) && {
|
|
||||||
name: 'ULandingSection',
|
|
||||||
to: '/pro/components/landing-section',
|
|
||||||
class: [
|
|
||||||
'inset-4',
|
|
||||||
isBeforeStep(steps.landing + 14) && '-top-[calc(var(--y)-var(--prev-step-y)-var(--height)-1rem)] bottom-[calc(var(--y)-var(--prev-step-y)-var(--height)+1rem)]'
|
|
||||||
].filter(Boolean).join(' '),
|
|
||||||
style: {
|
|
||||||
'--height': (inc.value * 4) + 'px',
|
|
||||||
'--step-y': `${getStepY(steps.landing + 18)}px`,
|
|
||||||
'--prev-step-y': `${getStepY(steps.landing + 10)}px`
|
|
||||||
},
|
|
||||||
inactive: isAfterStep(steps.landing + 15),
|
|
||||||
children: [{
|
|
||||||
name: 'ULandingCTA',
|
|
||||||
class: 'inset-4',
|
|
||||||
inactive: isAfterStep(steps.landing + 16),
|
|
||||||
children: [{
|
children: [{
|
||||||
slot: 'landing-cta',
|
name: 'ULandingHero',
|
||||||
class: 'inset-0'
|
to: '/pro/components/landing-hero',
|
||||||
}]
|
class: [
|
||||||
|
'inset-4',
|
||||||
|
isAfterStep(steps.landing + 2) && '-top-[calc(var(--y)-var(--step-y)-1rem)] bottom-[calc(var(--y)-var(--step-y)+1rem)]'
|
||||||
|
].filter(Boolean).join(' '),
|
||||||
|
style: {
|
||||||
|
'--step-y': `${getStepY(steps.landing + 2)}px`
|
||||||
|
},
|
||||||
|
inactive: isAfterStep(steps.landing + 1),
|
||||||
|
children: [{
|
||||||
|
slot: 'landing-hero',
|
||||||
|
class: 'inset-4'
|
||||||
|
}]
|
||||||
|
}, isAfterStep(steps.landing + 2) && {
|
||||||
|
name: 'ULandingSection',
|
||||||
|
to: '/pro/components/landing-section',
|
||||||
|
class: [
|
||||||
|
'inset-4',
|
||||||
|
isBeforeStep(steps.landing + 6) && '-top-[calc(var(--y)-var(--prev-step-y)-var(--height)-1rem)] bottom-[calc(var(--y)-var(--prev-step-y)-var(--height)+1rem)]',
|
||||||
|
isAfterStep(steps.landing + 10) && '-top-[calc(var(--y)-var(--step-y)-1rem)] bottom-[calc(var(--y)-var(--step-y)+1rem)]'
|
||||||
|
].filter(Boolean).join(' '),
|
||||||
|
style: {
|
||||||
|
'--height': (inc.value * 4) + 'px',
|
||||||
|
'--step-y': `${getStepY(steps.landing + 10)}px`,
|
||||||
|
'--prev-step-y': `${getStepY(steps.landing + 2)}px`
|
||||||
|
},
|
||||||
|
inactive: isAfterStep(steps.landing + 7),
|
||||||
|
children: [{
|
||||||
|
slot: 'landing-section',
|
||||||
|
class: 'inset-x-4 top-16'
|
||||||
|
}, {
|
||||||
|
name: 'ULandingGrid',
|
||||||
|
to: '/pro/components/landing-grid',
|
||||||
|
class: ['inset-x-4 bottom-4 top-48', isAfterStep(steps.landing + 8) && 'grid grid-cols-4 gap-4 p-4'].filter(Boolean).join(' '),
|
||||||
|
inactive: isAfterStep(steps.landing + 8),
|
||||||
|
children: [isAfterStep(steps.landing + 9)
|
||||||
|
? {
|
||||||
|
slot: 'landing-card-1',
|
||||||
|
class: '!relative'
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
name: 'ULandingCard',
|
||||||
|
to: '/pro/components/landing-card',
|
||||||
|
class: '!relative h-full',
|
||||||
|
inactive: false
|
||||||
|
}, isAfterStep(steps.landing + 9)
|
||||||
|
? {
|
||||||
|
slot: 'landing-card-2',
|
||||||
|
class: '!relative h-full'
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
name: 'ULandingCard',
|
||||||
|
to: '/pro/components/landing-card',
|
||||||
|
class: '!relative h-full',
|
||||||
|
inactive: false
|
||||||
|
}, isAfterStep(steps.landing + 9)
|
||||||
|
? {
|
||||||
|
slot: 'landing-card-3',
|
||||||
|
class: '!relative h-full'
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
name: 'ULandingCard',
|
||||||
|
to: '/pro/components/landing-card',
|
||||||
|
class: '!relative h-full',
|
||||||
|
inactive: false
|
||||||
|
}, isAfterStep(steps.landing + 9)
|
||||||
|
? {
|
||||||
|
slot: 'landing-card-4',
|
||||||
|
class: '!relative h-full'
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
name: 'ULandingCard',
|
||||||
|
to: '/pro/components/landing-card',
|
||||||
|
class: '!relative h-full',
|
||||||
|
inactive: false
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}, isAfterStep(steps.landing + 10) && {
|
||||||
|
name: 'ULandingSection',
|
||||||
|
to: '/pro/components/landing-section',
|
||||||
|
class: [
|
||||||
|
'inset-4',
|
||||||
|
isBeforeStep(steps.landing + 14) && '-top-[calc(var(--y)-var(--prev-step-y)-var(--height)-1rem)] bottom-[calc(var(--y)-var(--prev-step-y)-var(--height)+1rem)]'
|
||||||
|
].filter(Boolean).join(' '),
|
||||||
|
style: {
|
||||||
|
'--height': (inc.value * 4) + 'px',
|
||||||
|
'--step-y': `${getStepY(steps.landing + 18)}px`,
|
||||||
|
'--prev-step-y': `${getStepY(steps.landing + 10)}px`
|
||||||
|
},
|
||||||
|
inactive: isAfterStep(steps.landing + 15),
|
||||||
|
children: [{
|
||||||
|
name: 'ULandingCTA',
|
||||||
|
class: 'inset-4',
|
||||||
|
inactive: isAfterStep(steps.landing + 16),
|
||||||
|
children: [{
|
||||||
|
slot: 'landing-cta',
|
||||||
|
class: 'inset-0'
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}].filter(Boolean)
|
||||||
}]
|
}]
|
||||||
}].filter(Boolean)
|
: [])
|
||||||
}] : [])
|
|
||||||
|
|
||||||
const docsBlocks = computed(() => [isAfterStep(steps.docs) && {
|
const docsBlocks = computed(() => [isAfterStep(steps.docs) && {
|
||||||
name: 'UPage',
|
name: 'UPage',
|
||||||
to: '/pro/components/page',
|
to: '/pro/components/page',
|
||||||
class: 'inset-x-0 top-20 bottom-20',
|
class: 'inset-x-0 top-20 bottom-20',
|
||||||
inactive: isAfterStep(steps.docs + 1),
|
inactive: isAfterStep(steps.docs + 1),
|
||||||
children: [isAfterStep(steps.docs + 2) ? {
|
children: [isAfterStep(steps.docs + 2)
|
||||||
name: 'UAside',
|
? {
|
||||||
to: '/pro/components/aside',
|
name: 'UAside',
|
||||||
class: 'left-4 inset-y-4 w-64',
|
to: '/pro/components/aside',
|
||||||
inactive: isAfterStep(steps.docs + 3),
|
class: 'left-4 inset-y-4 w-64',
|
||||||
children: [isAfterStep(steps.docs + 4) ? {
|
inactive: isAfterStep(steps.docs + 3),
|
||||||
slot: 'aside-top',
|
children: [isAfterStep(steps.docs + 4)
|
||||||
class: 'inset-x-4 top-4'
|
? {
|
||||||
} : {
|
slot: 'aside-top',
|
||||||
name: '#top',
|
class: 'inset-x-4 top-4'
|
||||||
class: 'inset-x-4 top-4 h-9'
|
}
|
||||||
}, isAfterStep(steps.docs + 5) ? {
|
: {
|
||||||
name: 'UNavigationTree',
|
name: '#top',
|
||||||
to: '/pro/components/navigation-tree',
|
class: 'inset-x-4 top-4 h-9'
|
||||||
class: ['inset-x-4 top-[4.25rem] bottom-4', isAfterStep(steps.docs + 6) && '!bg-transparent !border-0'].join(' '),
|
}, isAfterStep(steps.docs + 5)
|
||||||
inactive: isAfterStep(steps.docs + 6),
|
? {
|
||||||
children: [{
|
name: 'UNavigationTree',
|
||||||
slot: 'aside-default',
|
to: '/pro/components/navigation-tree',
|
||||||
class: 'inset-0'
|
class: ['inset-x-4 top-[4.25rem] bottom-4', isAfterStep(steps.docs + 6) && '!bg-transparent !border-0'].join(' '),
|
||||||
|
inactive: isAfterStep(steps.docs + 6),
|
||||||
|
children: [{
|
||||||
|
slot: 'aside-default',
|
||||||
|
class: 'inset-0'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
name: '#default',
|
||||||
|
class: 'inset-x-4 top-[4.25rem] bottom-4'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
name: '#left',
|
||||||
|
class: 'left-4 inset-y-4 w-64'
|
||||||
|
}, isAfterStep(steps.docs + 7)
|
||||||
|
? {
|
||||||
|
name: 'UPage',
|
||||||
|
to: '/pro/components/page',
|
||||||
|
class: 'left-72 right-4 inset-y-4',
|
||||||
|
inactive: isAfterStep(steps.docs + 8),
|
||||||
|
children: [...(isAfterStep(steps.docs + 9)
|
||||||
|
? [{
|
||||||
|
name: 'UPageHeader',
|
||||||
|
to: '/pro/components/page-header',
|
||||||
|
class: 'top-4 left-4 right-72 h-32',
|
||||||
|
inactive: isAfterStep(steps.docs + 10),
|
||||||
|
children: [{
|
||||||
|
slot: 'page-header',
|
||||||
|
class: 'inset-4 justify-start'
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
name: 'UPageBody',
|
||||||
|
to: '/pro/components/page-body',
|
||||||
|
class: 'top-40 left-4 right-72 bottom-4 overflow-y-auto',
|
||||||
|
inactive: isAfterStep(steps.docs + 11),
|
||||||
|
children: [{
|
||||||
|
slot: 'page-body',
|
||||||
|
class: 'inset-x-4 top-4 justify-start'
|
||||||
|
}, isAfterStep(steps.docs + 12)
|
||||||
|
? {
|
||||||
|
slot: 'content-surround',
|
||||||
|
class: 'bottom-4 inset-x-4 h-28'
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
name: 'UContentSurround',
|
||||||
|
to: '/pro/components/content-surround',
|
||||||
|
class: 'bottom-4 inset-x-4 h-28',
|
||||||
|
inactive: false
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
: [{
|
||||||
|
name: '#default',
|
||||||
|
class: 'left-4 right-72 inset-y-4'
|
||||||
|
}]), isAfterStep(steps.docs + 13)
|
||||||
|
? {
|
||||||
|
name: 'UContentToc',
|
||||||
|
to: '/pro/components/content-toc',
|
||||||
|
class: 'right-4 inset-y-4 w-64',
|
||||||
|
inactive: isAfterStep(steps.docs + 14),
|
||||||
|
children: [{
|
||||||
|
slot: 'content-toc',
|
||||||
|
class: 'inset-4 overflow-y-auto'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
name: '#right',
|
||||||
|
class: 'right-4 inset-y-4 w-64'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
name: '#default',
|
||||||
|
class: 'left-72 right-4 inset-y-4'
|
||||||
}]
|
}]
|
||||||
} : {
|
|
||||||
name: '#default',
|
|
||||||
class: 'inset-x-4 top-[4.25rem] bottom-4'
|
|
||||||
}]
|
|
||||||
} : {
|
|
||||||
name: '#left',
|
|
||||||
class: 'left-4 inset-y-4 w-64'
|
|
||||||
}, isAfterStep(steps.docs + 7) ? {
|
|
||||||
name: 'UPage',
|
|
||||||
to: '/pro/components/page',
|
|
||||||
class: 'left-72 right-4 inset-y-4',
|
|
||||||
inactive: isAfterStep(steps.docs + 8),
|
|
||||||
children: [...(isAfterStep(steps.docs + 9) ? [{
|
|
||||||
name: 'UPageHeader',
|
|
||||||
to: '/pro/components/page-header',
|
|
||||||
class: 'top-4 left-4 right-72 h-32',
|
|
||||||
inactive: isAfterStep(steps.docs + 10),
|
|
||||||
children: [{
|
|
||||||
slot: 'page-header',
|
|
||||||
class: 'inset-4 justify-start'
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
name: 'UPageBody',
|
|
||||||
to: '/pro/components/page-body',
|
|
||||||
class: 'top-40 left-4 right-72 bottom-4 overflow-y-auto',
|
|
||||||
inactive: isAfterStep(steps.docs + 11),
|
|
||||||
children: [{
|
|
||||||
slot: 'page-body',
|
|
||||||
class: 'inset-x-4 top-4 justify-start'
|
|
||||||
}, isAfterStep(steps.docs + 12) ? {
|
|
||||||
slot: 'content-surround',
|
|
||||||
class: 'bottom-4 inset-x-4 h-28'
|
|
||||||
} : {
|
|
||||||
name: 'UContentSurround',
|
|
||||||
to: '/pro/components/content-surround',
|
|
||||||
class: 'bottom-4 inset-x-4 h-28',
|
|
||||||
inactive: false
|
|
||||||
}]
|
|
||||||
}] : [{
|
|
||||||
name: '#default',
|
|
||||||
class: 'left-4 right-72 inset-y-4'
|
|
||||||
}]), isAfterStep(steps.docs + 13) ? {
|
|
||||||
name: 'UContentToc',
|
|
||||||
to: '/pro/components/content-toc',
|
|
||||||
class: 'right-4 inset-y-4 w-64',
|
|
||||||
inactive: isAfterStep(steps.docs + 14),
|
|
||||||
children: [{
|
|
||||||
slot: 'content-toc',
|
|
||||||
class: 'inset-4 overflow-y-auto'
|
|
||||||
}]
|
|
||||||
} : {
|
|
||||||
name: '#right',
|
|
||||||
class: 'right-4 inset-y-4 w-64'
|
|
||||||
}]
|
|
||||||
} : {
|
|
||||||
name: '#default',
|
|
||||||
class: 'left-72 right-4 inset-y-4'
|
|
||||||
}]
|
|
||||||
}].filter(Boolean))
|
}].filter(Boolean))
|
||||||
|
|
||||||
const blocks = computed(() => [isAfterStep(steps.header) && {
|
const blocks = computed(() => [isAfterStep(steps.header) && {
|
||||||
@@ -655,62 +679,74 @@ const blocks = computed(() => [isAfterStep(steps.header) && {
|
|||||||
to: '/pro/components/header',
|
to: '/pro/components/header',
|
||||||
class: 'h-16 inset-x-0 top-0',
|
class: 'h-16 inset-x-0 top-0',
|
||||||
inactive: isAfterStep(steps.header + 1),
|
inactive: isAfterStep(steps.header + 1),
|
||||||
children: [isAfterStep(steps.header + 2) ? {
|
children: [isAfterStep(steps.header + 2)
|
||||||
slot: 'header-left',
|
? {
|
||||||
class: 'left-4 top-4'
|
slot: 'header-left',
|
||||||
} : {
|
class: 'left-4 top-4'
|
||||||
name: '#left',
|
}
|
||||||
class: 'left-4 inset-y-4 w-64'
|
: {
|
||||||
}, isAfterStep(steps.header + 3) ? {
|
name: '#left',
|
||||||
slot: 'header-center',
|
class: 'left-4 inset-y-4 w-64'
|
||||||
class: 'inset-x-72 top-5'
|
}, isAfterStep(steps.header + 3)
|
||||||
} : {
|
? {
|
||||||
name: '#center',
|
slot: 'header-center',
|
||||||
class: 'inset-x-72 inset-y-4'
|
class: 'inset-x-72 top-5'
|
||||||
}, isAfterStep(steps.header + 4) ? {
|
}
|
||||||
slot: 'header-right',
|
: {
|
||||||
class: 'right-4 top-4'
|
name: '#center',
|
||||||
} : {
|
class: 'inset-x-72 inset-y-4'
|
||||||
name: '#right',
|
}, isAfterStep(steps.header + 4)
|
||||||
class: 'right-4 inset-y-4 w-64'
|
? {
|
||||||
}]
|
slot: 'header-right',
|
||||||
|
class: 'right-4 top-4'
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
name: '#right',
|
||||||
|
class: 'right-4 inset-y-4 w-64'
|
||||||
|
}]
|
||||||
}, isAfterStep(steps.footer) && {
|
}, isAfterStep(steps.footer) && {
|
||||||
name: 'UFooter',
|
name: 'UFooter',
|
||||||
to: '/pro/components/footer',
|
to: '/pro/components/footer',
|
||||||
class: 'h-16 inset-x-0 bottom-0',
|
class: 'h-16 inset-x-0 bottom-0',
|
||||||
inactive: isAfterStep(steps.footer + 1),
|
inactive: isAfterStep(steps.footer + 1),
|
||||||
children: [isAfterStep(steps.footer + 2) ? {
|
children: [isAfterStep(steps.footer + 2)
|
||||||
slot: 'footer-left',
|
? {
|
||||||
class: 'left-4 bottom-5'
|
slot: 'footer-left',
|
||||||
} : {
|
class: 'left-4 bottom-5'
|
||||||
name: '#left',
|
}
|
||||||
class: 'left-4 inset-y-4 w-64'
|
: {
|
||||||
}, isAfterStep(steps.footer + 3) ? {
|
name: '#left',
|
||||||
slot: 'footer-center',
|
class: 'left-4 inset-y-4 w-64'
|
||||||
class: 'inset-x-72 bottom-5'
|
}, isAfterStep(steps.footer + 3)
|
||||||
} : {
|
? {
|
||||||
name: '#center',
|
slot: 'footer-center',
|
||||||
class: 'inset-x-72 inset-y-4'
|
class: 'inset-x-72 bottom-5'
|
||||||
}, isAfterStep(steps.footer + 4) ? {
|
}
|
||||||
slot: 'footer-right',
|
: {
|
||||||
class: 'right-4 bottom-4'
|
name: '#center',
|
||||||
} : {
|
class: 'inset-x-72 inset-y-4'
|
||||||
name: '#right',
|
}, isAfterStep(steps.footer + 4)
|
||||||
class: 'right-4 inset-y-4 w-64'
|
? {
|
||||||
}]
|
slot: 'footer-right',
|
||||||
|
class: 'right-4 bottom-4'
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
name: '#right',
|
||||||
|
class: 'right-4 inset-y-4 w-64'
|
||||||
|
}]
|
||||||
}, ...landingBlocks.value, ...docsBlocks.value].filter(Boolean))
|
}, ...landingBlocks.value, ...docsBlocks.value].filter(Boolean))
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
|
|
||||||
function isBeforeStep (i = 0) {
|
function isBeforeStep(i = 0) {
|
||||||
return y.value < (start.value + (i * inc.value))
|
return y.value < (start.value + (i * inc.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAfterStep (i = 0) {
|
function isAfterStep(i = 0) {
|
||||||
return y.value >= (start.value + (i * inc.value))
|
return y.value >= (start.value + (i * inc.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStepY (step: number) {
|
function getStepY(step: number) {
|
||||||
return start.value + (step * inc.value)
|
return start.value + (step * inc.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ const dates = computed(() => {
|
|||||||
|
|
||||||
const days = eachDayOfInterval({ start: new Date(first.published_at), end: new Date() })
|
const days = eachDayOfInterval({ start: new Date(first.published_at), end: new Date() })
|
||||||
|
|
||||||
return days.reverse().map(day => {
|
return days.reverse().map((day) => {
|
||||||
return {
|
return {
|
||||||
day,
|
day,
|
||||||
release: releases.value.find(release => isSameDay(new Date(release.published_at), day)),
|
release: releases.value.find(release => isSameDay(new Date(release.published_at), day)),
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import type { Options } from 'prettier'
|
import type { Options } from 'prettier'
|
||||||
|
import { defu } from 'defu'
|
||||||
import PrettierWorker from '@/workers/prettier.js?worker&inline'
|
import PrettierWorker from '@/workers/prettier.js?worker&inline'
|
||||||
|
|
||||||
export interface SimplePrettier {
|
export interface SimplePrettier {
|
||||||
format: (source: string, options?: Options) => Promise<string>;
|
format: (source: string, options?: Options) => Promise<string>
|
||||||
}
|
}
|
||||||
|
|
||||||
function createPrettierWorkerApi (worker: Worker): SimplePrettier {
|
function createPrettierWorkerApi(worker: Worker): SimplePrettier {
|
||||||
let counter = 0
|
let counter = 0
|
||||||
const handlers = {}
|
const handlers: any = {}
|
||||||
|
|
||||||
worker.addEventListener('message', (event) => {
|
worker.addEventListener('message', (event) => {
|
||||||
const { uid, message, error } = event.data
|
const { uid, message, error } = event.data
|
||||||
@@ -17,6 +18,7 @@ function createPrettierWorkerApi (worker: Worker): SimplePrettier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const [resolve, reject] = handlers[uid]
|
const [resolve, reject] = handlers[uid]
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||||
delete handlers[uid]
|
delete handlers[uid]
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -26,7 +28,7 @@ function createPrettierWorkerApi (worker: Worker): SimplePrettier {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
function postMessage<T> (message) {
|
function postMessage<T>(message: any) {
|
||||||
const uid = ++counter
|
const uid = ++counter
|
||||||
return new Promise<T>((resolve, reject) => {
|
return new Promise<T>((resolve, reject) => {
|
||||||
handlers[uid] = [resolve, reject]
|
handlers[uid] = [resolve, reject]
|
||||||
@@ -35,33 +37,31 @@ function createPrettierWorkerApi (worker: Worker): SimplePrettier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
format (source: string, options?: Options) {
|
format(source: string, options?: Options) {
|
||||||
return postMessage({ type: 'format', source, options })
|
return postMessage({ type: 'format', source, options })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineNuxtPlugin({
|
export default defineNuxtPlugin(async () => {
|
||||||
async setup () {
|
let prettier: SimplePrettier
|
||||||
let prettier: SimplePrettier
|
if (import.meta.server) {
|
||||||
if (import.meta.server) {
|
const prettierModule = await import('prettier')
|
||||||
const prettierModule = await import('prettier')
|
prettier = {
|
||||||
prettier = {
|
format(source, options = {}) {
|
||||||
format (source, options = {
|
return prettierModule.format(source, defu(options, {
|
||||||
parser: 'markdown'
|
parser: 'markdown'
|
||||||
}) {
|
}))
|
||||||
return prettierModule.format(source, options)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
const worker = new PrettierWorker()
|
|
||||||
prettier = createPrettierWorkerApi(worker)
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const worker = new PrettierWorker()
|
||||||
|
prettier = createPrettierWorkerApi(worker)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
provide: {
|
provide: {
|
||||||
prettier
|
prettier
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import colors from '#tailwind-config/theme/colors'
|
|||||||
|
|
||||||
export default defineNuxtPlugin({
|
export default defineNuxtPlugin({
|
||||||
enforce: 'post',
|
enforce: 'post',
|
||||||
setup () {
|
setup() {
|
||||||
const appConfig = useAppConfig()
|
const appConfig = useAppConfig()
|
||||||
|
|
||||||
const root = computed(() => {
|
const root = computed(() => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Octokit } from '@octokit/rest'
|
import { Octokit } from '@octokit/rest'
|
||||||
|
|
||||||
function isUserABot (user) {
|
function isUserABot(user) {
|
||||||
return user?.login?.endsWith('-bot') || user?.login?.endsWith('[bot]')
|
return user?.login?.endsWith('-bot') || user?.login?.endsWith('[bot]')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
/* eslint-disable no-undef */
|
/* eslint-disable no-undef */
|
||||||
import('https://unpkg.com/prettier@3.0.3/standalone.js')
|
|
||||||
import('https://unpkg.com/prettier@3.0.3/plugins/html.js')
|
|
||||||
import('https://unpkg.com/prettier@3.0.3/plugins/markdown.js')
|
|
||||||
|
|
||||||
self.onmessage = async function (event) {
|
self.onmessage = async function (event) {
|
||||||
self.postMessage({
|
self.postMessage({
|
||||||
uid: event.data.uid,
|
uid: event.data.uid,
|
||||||
@@ -10,14 +6,22 @@ self.onmessage = async function (event) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMessage (message) {
|
function handleMessage(message) {
|
||||||
switch (message.type) {
|
switch (message.type) {
|
||||||
case 'format':
|
case 'format':
|
||||||
return handleFormatMessage(message)
|
return handleFormatMessage(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleFormatMessage (message) {
|
async function handleFormatMessage(message) {
|
||||||
|
if (!globalThis.prettier) {
|
||||||
|
await Promise.all([
|
||||||
|
import('https://unpkg.com/prettier@3.3.3/standalone.js'),
|
||||||
|
import('https://unpkg.com/prettier@3.3.3/plugins/html.js'),
|
||||||
|
import('https://unpkg.com/prettier@3.3.3/plugins/markdown.js')
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
const { options, source } = message
|
const { options, source } = message
|
||||||
const formatted = await prettier.format(source, {
|
const formatted = await prettier.format(source, {
|
||||||
parser: 'markdown',
|
parser: 'markdown',
|
||||||
|
|||||||
19
eslint.config.mjs
Normal file
19
eslint.config.mjs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { createConfigForNuxt } from '@nuxt/eslint-config/flat'
|
||||||
|
|
||||||
|
export default createConfigForNuxt({
|
||||||
|
features: {
|
||||||
|
tooling: true,
|
||||||
|
stylistic: {
|
||||||
|
commaDangle: 'never',
|
||||||
|
braceStyle: '1tbs'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).overrideRules({
|
||||||
|
'vue/multi-word-component-names': 'off',
|
||||||
|
'vue/max-attributes-per-line': ['error', { singleline: 5 }],
|
||||||
|
'@typescript-eslint/ban-types': 'off',
|
||||||
|
'@typescript-eslint/ban-ts-comment': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-function-type': 'off',
|
||||||
|
'@typescript-eslint/no-empty-object-type': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off'
|
||||||
|
})
|
||||||
40
package.json
40
package.json
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "@nuxt/ui",
|
"name": "@nuxt/ui",
|
||||||
"description": "A UI Library for Modern Web Apps, powered by Vue & Tailwind CSS.",
|
"description": "A UI Library for Modern Web Apps, powered by Vue & Tailwind CSS.",
|
||||||
"version": "2.18.7",
|
"version": "2.19.2",
|
||||||
"packageManager": "pnpm@9.12.1",
|
"packageManager": "pnpm@9.12.3",
|
||||||
"repository": "nuxt/ui",
|
"repository": "nuxt/ui",
|
||||||
"homepage": "https://ui.nuxt.com",
|
"homepage": "https://ui.nuxt.com",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -35,49 +35,51 @@
|
|||||||
"@headlessui/tailwindcss": "^0.2.1",
|
"@headlessui/tailwindcss": "^0.2.1",
|
||||||
"@headlessui/vue": "^1.7.23",
|
"@headlessui/vue": "^1.7.23",
|
||||||
"@iconify-json/heroicons": "^1.2.1",
|
"@iconify-json/heroicons": "^1.2.1",
|
||||||
"@nuxt/icon": "^1.5.5",
|
"@nuxt/icon": "^1.6.1",
|
||||||
"@nuxt/kit": "^3.13.2",
|
"@nuxt/kit": "^3.14.0",
|
||||||
"@nuxtjs/color-mode": "^3.5.1",
|
"@nuxtjs/color-mode": "^3.5.2",
|
||||||
"@nuxtjs/tailwindcss": "^6.12.1",
|
"@nuxtjs/tailwindcss": "^6.12.2",
|
||||||
"@popperjs/core": "^2.11.8",
|
"@popperjs/core": "^2.11.8",
|
||||||
"@tailwindcss/aspect-ratio": "^0.4.2",
|
"@tailwindcss/aspect-ratio": "^0.4.2",
|
||||||
"@tailwindcss/container-queries": "^0.1.1",
|
"@tailwindcss/container-queries": "^0.1.1",
|
||||||
"@tailwindcss/forms": "^0.5.9",
|
"@tailwindcss/forms": "^0.5.9",
|
||||||
"@tailwindcss/typography": "^0.5.15",
|
"@tailwindcss/typography": "^0.5.15",
|
||||||
"@vueuse/core": "^11.1.0",
|
"@vueuse/core": "^11.2.0",
|
||||||
"@vueuse/integrations": "^11.1.0",
|
"@vueuse/integrations": "^11.2.0",
|
||||||
"@vueuse/math": "^11.1.0",
|
"@vueuse/math": "^11.2.0",
|
||||||
"defu": "^6.1.4",
|
"defu": "^6.1.4",
|
||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
"ohash": "^1.1.4",
|
"ohash": "^1.1.4",
|
||||||
"pathe": "^1.1.2",
|
"pathe": "^1.1.2",
|
||||||
"scule": "^1.3.0",
|
"scule": "^1.3.0",
|
||||||
"tailwind-merge": "^2.5.3",
|
"tailwind-merge": "^2.5.4",
|
||||||
"tailwindcss": "^3.4.13"
|
"tailwindcss": "^3.4.14"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nuxt/eslint-config": "^0.4.0",
|
"@nuxt/eslint-config": "^0.6.1",
|
||||||
"@nuxt/module-builder": "^0.8.4",
|
"@nuxt/module-builder": "^0.8.4",
|
||||||
"@nuxt/test-utils": "^3.14.3",
|
"@nuxt/test-utils": "^3.14.4",
|
||||||
"@release-it/conventional-changelog": "^8.0.2",
|
"@release-it/conventional-changelog": "^9.0.2",
|
||||||
"@vue/test-utils": "^2.4.6",
|
"@vue/test-utils": "^2.4.6",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^9.14.0",
|
||||||
"happy-dom": "^14.12.3",
|
"happy-dom": "^14.12.3",
|
||||||
"joi": "^17.13.3",
|
"joi": "^17.13.3",
|
||||||
"nuxt": "^3.13.2",
|
"nuxt": "^3.14.0",
|
||||||
"release-it": "^17.7.0",
|
"release-it": "^17.10.0",
|
||||||
|
"superstruct": "^2.0.2",
|
||||||
"unbuild": "^2.0.0",
|
"unbuild": "^2.0.0",
|
||||||
"valibot": "^0.42.1",
|
"valibot": "^0.42.1",
|
||||||
"valibot30": "npm:valibot@0.30.0",
|
"valibot30": "npm:valibot@0.30.0",
|
||||||
"valibot31": "npm:valibot@0.31.0",
|
"valibot31": "npm:valibot@0.31.0",
|
||||||
"vitest": "^2.1.2",
|
"vitest": "^2.1.4",
|
||||||
"vitest-environment-nuxt": "^1.0.1",
|
"vitest-environment-nuxt": "^1.0.1",
|
||||||
"vue-tsc": "^2.1.6",
|
"vue-tsc": "^2.1.10",
|
||||||
"yup": "^1.4.0",
|
"yup": "^1.4.0",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@nuxt/ui": "workspace:*",
|
"@nuxt/ui": "workspace:*",
|
||||||
|
"@nuxt/content": "2.13.2",
|
||||||
"@nuxtjs/mdc": "0.9.0"
|
"@nuxtjs/mdc": "0.9.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ export default defineNuxtConfig({
|
|||||||
],
|
],
|
||||||
|
|
||||||
compatibilityDate: '2024-07-23'
|
compatibilityDate: '2024-07-23'
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -9,6 +9,6 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/ui": "latest",
|
"@nuxt/ui": "latest",
|
||||||
"nuxt": "^3.13.2"
|
"nuxt": "^3.14.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5251
pnpm-lock.yaml
generated
5251
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -6,8 +6,6 @@
|
|||||||
"enabled": true
|
"enabled": true
|
||||||
},
|
},
|
||||||
"ignoreDeps": [
|
"ignoreDeps": [
|
||||||
"@nuxt/eslint-config",
|
|
||||||
"eslint",
|
|
||||||
"happy-dom",
|
"happy-dom",
|
||||||
"valibot30",
|
"valibot30",
|
||||||
"valibot31"
|
"valibot31"
|
||||||
@@ -23,5 +21,8 @@
|
|||||||
"@tailwindcss/postcss",
|
"@tailwindcss/postcss",
|
||||||
"@tailwindcss/vite"
|
"@tailwindcss/vite"
|
||||||
]
|
]
|
||||||
|
}, {
|
||||||
|
"matchDepTypes": ["resolutions"],
|
||||||
|
"enabled": false
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { promises as fsp } from 'fs'
|
import { promises as fsp } from 'node:fs'
|
||||||
import { resolve } from 'path'
|
import { resolve } from 'node:path'
|
||||||
import { execSync } from 'child_process'
|
import { execSync } from 'node:child_process'
|
||||||
|
|
||||||
async function loadPackage (dir: string) {
|
async function loadPackage(dir: string) {
|
||||||
const pkgPath = resolve(dir, 'package.json')
|
const pkgPath = resolve(dir, 'package.json')
|
||||||
|
|
||||||
const data = JSON.parse(await fsp.readFile(pkgPath, 'utf-8').catch(() => '{}'))
|
const data = JSON.parse(await fsp.readFile(pkgPath, 'utf-8').catch(() => '{}'))
|
||||||
@@ -16,7 +16,7 @@ async function loadPackage (dir: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main () {
|
async function main() {
|
||||||
const pkg = await loadPackage(process.cwd())
|
const pkg = await loadPackage(process.cwd())
|
||||||
|
|
||||||
const commit = execSync('git rev-parse --short HEAD').toString('utf-8').trim()
|
const commit = execSync('git rev-parse --short HEAD').toString('utf-8').trim()
|
||||||
@@ -31,7 +31,6 @@ async function main () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main().catch((err) => {
|
main().catch((err) => {
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error(err)
|
console.error(err)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { createRequire } from 'node:module'
|
|||||||
import { defineNuxtModule, installModule, addComponentsDir, addImportsDir, createResolver, addPlugin } from '@nuxt/kit'
|
import { defineNuxtModule, installModule, addComponentsDir, addImportsDir, createResolver, addPlugin } from '@nuxt/kit'
|
||||||
import { name, version } from '../package.json'
|
import { name, version } from '../package.json'
|
||||||
import createTemplates from './templates'
|
import createTemplates from './templates'
|
||||||
import * as config from './runtime/ui.config'
|
import type * as config from './runtime/ui.config'
|
||||||
import type { DeepPartial, Strategy } from './runtime/types'
|
import type { DeepPartial, Strategy } from './runtime/types'
|
||||||
import installTailwind from './tailwind'
|
import installTailwind from './tailwind'
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ type UI = {
|
|||||||
colors?: string[]
|
colors?: string[]
|
||||||
strategy?: Strategy
|
strategy?: Strategy
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
} & DeepPartial<typeof config, string>
|
} & DeepPartial<typeof config, string | number | boolean>
|
||||||
|
|
||||||
declare module '@nuxt/schema' {
|
declare module '@nuxt/schema' {
|
||||||
interface AppConfigInput {
|
interface AppConfigInput {
|
||||||
@@ -62,7 +62,7 @@ export default defineNuxtModule<ModuleOptions>({
|
|||||||
safelistColors: ['primary'],
|
safelistColors: ['primary'],
|
||||||
disableGlobalStyles: false
|
disableGlobalStyles: false
|
||||||
},
|
},
|
||||||
async setup (options, nuxt) {
|
async setup(options, nuxt) {
|
||||||
const { resolve } = createResolver(import.meta.url)
|
const { resolve } = createResolver(import.meta.url)
|
||||||
|
|
||||||
// Transpile runtime
|
// Transpile runtime
|
||||||
|
|||||||
@@ -9,10 +9,16 @@
|
|||||||
<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="ui.checkbox.padding">
|
<th v-if="modelValue" scope="col" :class="ui.checkbox.padding">
|
||||||
<UCheckbox :model-value="indeterminate || selected.length === rows.length" :indeterminate="indeterminate" v-bind="ui.default.checkbox" aria-label="Select all" @change="onChange" />
|
<UCheckbox
|
||||||
|
:model-value="isAllRowChecked"
|
||||||
|
:indeterminate="indeterminate"
|
||||||
|
v-bind="ui.default.checkbox"
|
||||||
|
aria-label="Select all"
|
||||||
|
@change="onChange"
|
||||||
|
/>
|
||||||
</th>
|
</th>
|
||||||
|
|
||||||
<th v-if="$slots.expand" scope="col" :class="ui.tr.base">
|
<th v-if="expand" scope="col" :class="ui.tr.base">
|
||||||
<span class="sr-only">Expand</span>
|
<span class="sr-only">Expand</span>
|
||||||
</th>
|
</th>
|
||||||
|
|
||||||
@@ -44,7 +50,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody :class="ui.tbody">
|
<tbody :class="ui.tbody">
|
||||||
<tr v-if="loadingState && loading && !rows.length">
|
<tr v-if="loadingState && loading && !rows.length">
|
||||||
<td :colspan="columns.length + (modelValue ? 1 : 0) + ($slots.expand ? 1 : 0)">
|
<td :colspan="columns.length + (modelValue ? 1 : 0) + (expand ? 1 : 0)">
|
||||||
<slot name="loading-state">
|
<slot name="loading-state">
|
||||||
<div :class="ui.loadingState.wrapper">
|
<div :class="ui.loadingState.wrapper">
|
||||||
<UIcon v-if="loadingState.icon" :name="loadingState.icon" :class="ui.loadingState.icon" aria-hidden="true" />
|
<UIcon v-if="loadingState.icon" :name="loadingState.icon" :class="ui.loadingState.icon" aria-hidden="true" />
|
||||||
@@ -57,7 +63,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr v-else-if="emptyState && !rows.length">
|
<tr v-else-if="emptyState && !rows.length">
|
||||||
<td :colspan="columns.length + (modelValue ? 1 : 0) + ($slots.expand ? 1 : 0)">
|
<td :colspan="columns.length + (modelValue ? 1 : 0) + (expand ? 1 : 0)">
|
||||||
<slot name="empty-state">
|
<slot name="empty-state">
|
||||||
<div :class="ui.emptyState.wrapper">
|
<div :class="ui.emptyState.wrapper">
|
||||||
<UIcon v-if="emptyState.icon" :name="emptyState.icon" :class="ui.emptyState.icon" aria-hidden="true" />
|
<UIcon v-if="emptyState.icon" :name="emptyState.icon" :class="ui.emptyState.icon" aria-hidden="true" />
|
||||||
@@ -71,29 +77,38 @@
|
|||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<template v-for="(row, index) in rows" :key="index">
|
<template v-for="(row, index) in rows" :key="index">
|
||||||
<tr :class="[ui.tr.base, isSelected(row) && ui.tr.selected, $attrs.onSelect && ui.tr.active, row?.class]" @click="() => onSelect(row)">
|
<tr :class="[ui.tr.base, isSelected(row) && ui.tr.selected, isExpanded(row) && ui.tr.expanded, $attrs.onSelect && ui.tr.active, row?.class]" @click="() => onSelect(row)">
|
||||||
<td v-if="modelValue" :class="ui.checkbox.padding">
|
<td v-if="modelValue" :class="ui.checkbox.padding">
|
||||||
<UCheckbox v-model="selected" :value="row" v-bind="ui.default.checkbox" aria-label="Select row" @click.capture.stop="() => onSelect(row)" />
|
<UCheckbox
|
||||||
</td>
|
:model-value="isSelected(row)"
|
||||||
|
v-bind="ui.default.checkbox"
|
||||||
<td
|
aria-label="Select row"
|
||||||
v-if="$slots.expand"
|
@change="onChangeCheckbox($event, row)"
|
||||||
:class="[ui.td.base, ui.td.padding, ui.td.color, ui.td.font, ui.td.size]"
|
@click.capture.stop="() => onSelect(row)"
|
||||||
>
|
/>
|
||||||
<UButton
|
</td>
|
||||||
v-bind="{ ...(ui.default.expandButton || {}), ...expandButton }"
|
<td
|
||||||
:ui="{ icon: { base: [ui.expand.icon, openedRows.includes(index) && 'rotate-180'].join(' ') } }"
|
v-if="expand"
|
||||||
@click="toggleOpened(index)"
|
:class="[ui.td.base, ui.td.padding, ui.td.color, ui.td.font, ui.td.size]"
|
||||||
|
>
|
||||||
|
<template v-if="$slots['expand-action']">
|
||||||
|
<slot name="expand-action" :row="row" :is-expanded="isExpanded(row)" :toggle="() => toggleOpened(row)" />
|
||||||
|
</template>
|
||||||
|
<UButton
|
||||||
|
v-else
|
||||||
|
:disabled="row.disabledExpand"
|
||||||
|
v-bind="{ ...(ui.default.expandButton || {}), ...expandButton }"
|
||||||
|
:ui="{ icon: { base: [ui.expand.icon, isExpanded(row) && 'rotate-180'].join(' ') } }"
|
||||||
|
@click.capture.stop="toggleOpened(row)"
|
||||||
/>
|
/>
|
||||||
</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, column?.rowClass, row[column.key]?.class]">
|
<td v-for="(column, subIndex) in columns" :key="subIndex" :class="[ui.td.base, ui.td.padding, ui.td.color, ui.td.font, ui.td.size, column?.rowClass, row[column.key]?.class]">
|
||||||
<slot :name="`${column.key}-data`" :column="column" :row="row" :index="index" :get-row-data="(defaultValue) => getRowData(row, column.key, defaultValue)">
|
<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>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-if="openedRows.includes(index)">
|
<tr v-if="isExpanded(row)">
|
||||||
<td colspan="100%">
|
<td colspan="100%">
|
||||||
<slot
|
<slot
|
||||||
name="expand"
|
name="expand"
|
||||||
@@ -110,7 +125,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ref, computed, defineComponent, toRaw, toRef } from 'vue'
|
import { computed, defineComponent, toRaw, toRef } from 'vue'
|
||||||
import type { PropType, AriaAttributes } from 'vue'
|
import type { PropType, AriaAttributes } from 'vue'
|
||||||
import { upperFirst } from 'scule'
|
import { upperFirst } from 'scule'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
@@ -121,18 +136,18 @@ import UProgress from '../elements/Progress.vue'
|
|||||||
import UCheckbox from '../forms/Checkbox.vue'
|
import UCheckbox from '../forms/Checkbox.vue'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { mergeConfig, get } from '../../utils'
|
import { mergeConfig, get } from '../../utils'
|
||||||
import type { Strategy, Button, ProgressColor, ProgressAnimation, DeepPartial } from '../../types/index'
|
import type { TableRow, TableColumn, Strategy, Button, ProgressColor, ProgressAnimation, DeepPartial, Expanded } from '../../types/index'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
import { table } from '#ui/ui.config'
|
import { table } from '#ui/ui.config'
|
||||||
|
|
||||||
const config = mergeConfig<typeof table>(appConfig.ui.strategy, appConfig.ui.table, table)
|
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 JSON.stringify(a) === JSON.stringify(z)
|
||||||
}
|
}
|
||||||
|
|
||||||
function defaultSort (a: any, b: any, direction: 'asc' | 'desc') {
|
function defaultSort(a: any, b: any, direction: 'asc' | 'desc') {
|
||||||
if (a === b) {
|
if (a === b) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -144,16 +159,6 @@ function defaultSort (a: any, b: any, direction: 'asc' | 'desc') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Column {
|
|
||||||
key: string
|
|
||||||
sortable?: boolean
|
|
||||||
sort?: (a: any, b: any, direction: 'asc' | 'desc') => number
|
|
||||||
direction?: 'asc' | 'desc'
|
|
||||||
class?: string
|
|
||||||
rowClass?: string
|
|
||||||
[key: string]: any
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
UIcon,
|
UIcon,
|
||||||
@@ -172,11 +177,11 @@ export default defineComponent({
|
|||||||
default: () => defaultComparator
|
default: () => defaultComparator
|
||||||
},
|
},
|
||||||
rows: {
|
rows: {
|
||||||
type: Array as PropType<{ [key: string]: any }[]>,
|
type: Array as PropType<TableRow[]>,
|
||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
columns: {
|
columns: {
|
||||||
type: Array as PropType<Column[]>,
|
type: Array as PropType<TableColumn[]>,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
columnAttribute: {
|
columnAttribute: {
|
||||||
@@ -207,6 +212,10 @@ export default defineComponent({
|
|||||||
type: Object as PropType<Button>,
|
type: Object as PropType<Button>,
|
||||||
default: () => config.default.expandButton as Button
|
default: () => config.default.expandButton as Button
|
||||||
},
|
},
|
||||||
|
expand: {
|
||||||
|
type: Object as PropType<Expanded<TableRow>>,
|
||||||
|
default: () => null
|
||||||
|
},
|
||||||
loading: {
|
loading: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
@@ -234,17 +243,26 @@ export default defineComponent({
|
|||||||
ui: {
|
ui: {
|
||||||
type: Object as PropType<DeepPartial<typeof config> & { strategy?: Strategy }>,
|
type: Object as PropType<DeepPartial<typeof config> & { strategy?: Strategy }>,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
|
},
|
||||||
|
multipleExpand: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'update:sort'],
|
emits: ['update:modelValue', 'update:sort', 'update:expand'],
|
||||||
setup (props, { emit, attrs: $attrs }) {
|
setup(props, { emit, attrs: $attrs }) {
|
||||||
const { ui, attrs } = useUI('table', toRef(props, 'ui'), config, toRef(props, 'class'))
|
const { ui, attrs } = useUI('table', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
|
|
||||||
const columns = computed(() => props.columns ?? Object.keys(props.rows[0] ?? {}).map((key) => ({ key, label: upperFirst(key), sortable: false, class: undefined, sort: defaultSort }) as Column))
|
const columns = computed(() => props.columns ?? Object.keys(props.rows[0] ?? {}).map(key => ({ key, label: upperFirst(key), sortable: false, class: undefined, sort: defaultSort }) as TableColumn))
|
||||||
|
|
||||||
const sort = useVModel(props, 'sort', emit, { passive: true, defaultValue: defu({}, props.sort, { column: null, direction: 'asc' }) })
|
const sort = useVModel(props, 'sort', emit, { passive: true, defaultValue: defu({}, props.sort, { column: null, direction: 'asc' }) })
|
||||||
|
const expand = useVModel(props, 'expand', emit, {
|
||||||
const openedRows = ref([])
|
passive: true,
|
||||||
|
defaultValue: defu({}, props.expand, {
|
||||||
|
openedRows: [],
|
||||||
|
row: null
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
const savedSort = { column: sort.value.column, direction: null }
|
const savedSort = { column: sort.value.column, direction: null }
|
||||||
|
|
||||||
@@ -259,22 +277,38 @@ export default defineComponent({
|
|||||||
const aValue = get(a, column)
|
const aValue = get(a, column)
|
||||||
const bValue = get(b, column)
|
const bValue = get(b, column)
|
||||||
|
|
||||||
const sort = columns.value.find((col) => col.key === column)?.sort ?? defaultSort
|
const sort = columns.value.find(col => col.key === column)?.sort ?? defaultSort
|
||||||
|
|
||||||
return sort(aValue, bValue, direction)
|
return sort(aValue, bValue, direction)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const selected = computed({
|
const selected = computed({
|
||||||
get () {
|
get() {
|
||||||
return props.modelValue
|
return props.modelValue
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
emit('update:modelValue', value)
|
emit('update:modelValue', value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const indeterminate = computed(() => selected.value && selected.value.length > 0 && selected.value.length < props.rows.length)
|
const getStringifiedSet = (arr: TableRow[]) => new Set(arr.map(item => JSON.stringify(item)))
|
||||||
|
|
||||||
|
const totalRows = computed(() => props.rows.length)
|
||||||
|
|
||||||
|
const countCheckedRow = computed(() => {
|
||||||
|
const selectedData = getStringifiedSet(selected.value)
|
||||||
|
const rowsData = getStringifiedSet(props.rows)
|
||||||
|
|
||||||
|
return Array.from(selectedData).filter(item => rowsData.has(item)).length
|
||||||
|
})
|
||||||
|
|
||||||
|
const indeterminate = computed(() => {
|
||||||
|
if (!selected.value || !props.rows) return false
|
||||||
|
return countCheckedRow.value > 0 && countCheckedRow.value < totalRows.value
|
||||||
|
})
|
||||||
|
|
||||||
|
const isAllRowChecked = computed(() => countCheckedRow.value === totalRows.value)
|
||||||
|
|
||||||
const emptyState = computed(() => {
|
const emptyState = computed(() => {
|
||||||
if (props.emptyState === null) return null
|
if (props.emptyState === null) return null
|
||||||
@@ -286,23 +320,27 @@ export default defineComponent({
|
|||||||
return { ...ui.value.default.loadingState, ...props.loadingState }
|
return { ...ui.value.default.loadingState, ...props.loadingState }
|
||||||
})
|
})
|
||||||
|
|
||||||
function compare (a: any, z: any) {
|
function compare(a: any, z: any) {
|
||||||
if (typeof props.by === 'string') {
|
if (typeof props.by === 'string') {
|
||||||
const property = props.by as unknown as any
|
const accesorFn = accessor(props.by)
|
||||||
return a?.[property] === z?.[property]
|
return accesorFn(a) === accesorFn(z)
|
||||||
}
|
}
|
||||||
return props.by(a, z)
|
return props.by(a, z)
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSelected (row) {
|
function accessor<T extends Record<string, any>>(key: string) {
|
||||||
|
return (obj: T) => get(obj, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSelected(row: TableRow) {
|
||||||
if (!props.modelValue) {
|
if (!props.modelValue) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return selected.value.some((item) => compare(toRaw(item), toRaw(row)))
|
return selected.value.some(item => compare(toRaw(item), toRaw(row)))
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSort (column: { key: string, direction?: 'asc' | 'desc' }) {
|
function onSort(column: { key: string, direction?: 'asc' | 'desc' }) {
|
||||||
if (sort.value.column === column.key) {
|
if (sort.value.column === column.key) {
|
||||||
const direction = !column.direction || column.direction === 'asc' ? 'desc' : 'asc'
|
const direction = !column.direction || column.direction === 'asc' ? 'desc' : 'asc'
|
||||||
|
|
||||||
@@ -316,7 +354,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSelect (row) {
|
function onSelect(row: TableRow) {
|
||||||
if (!$attrs.onSelect) {
|
if (!$attrs.onSelect) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -325,7 +363,7 @@ export default defineComponent({
|
|||||||
$attrs.onSelect(row)
|
$attrs.onSelect(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectAllRows () {
|
function selectAllRows() {
|
||||||
// Create a new array to ensure reactivity
|
// Create a new array to ensure reactivity
|
||||||
const newSelected = [...selected.value]
|
const newSelected = [...selected.value]
|
||||||
|
|
||||||
@@ -340,7 +378,7 @@ export default defineComponent({
|
|||||||
selected.value = newSelected
|
selected.value = newSelected
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChange (checked: boolean) {
|
function onChange(checked: boolean) {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
selectAllRows()
|
selectAllRows()
|
||||||
} else {
|
} else {
|
||||||
@@ -348,19 +386,31 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRowData (row: Object, rowKey: string | string[], defaultValue: any = '') {
|
function onChangeCheckbox(checked: boolean, row: TableRow) {
|
||||||
return get(row, rowKey, defaultValue)
|
if (checked) {
|
||||||
}
|
selected.value.push(row)
|
||||||
|
|
||||||
function toggleOpened (index: number) {
|
|
||||||
if (openedRows.value.includes(index)) {
|
|
||||||
openedRows.value = openedRows.value.filter((i) => i !== index)
|
|
||||||
} else {
|
} else {
|
||||||
openedRows.value.push(index)
|
const index = selected.value.findIndex(item => compare(item, row))
|
||||||
|
selected.value.splice(index, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAriaSort (column: Column): AriaAttributes['aria-sort'] {
|
function getRowData(row: TableRow, rowKey: string | string[], defaultValue: any = '') {
|
||||||
|
return get(row, rowKey, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isExpanded(row: TableRow) {
|
||||||
|
return expand.value?.openedRows ? expand.value.openedRows.some(openedRow => compare(openedRow, row)) : false
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleOpened(row: TableRow) {
|
||||||
|
expand.value = {
|
||||||
|
openedRows: isExpanded(row) ? expand.value.openedRows.filter(v => !compare(v, row)) : props.multipleExpand ? [...expand.value.openedRows, row] : [row],
|
||||||
|
row
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAriaSort(column: TableColumn): AriaAttributes['aria-sort'] {
|
||||||
if (!column.sortable) {
|
if (!column.sortable) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
@@ -396,14 +446,16 @@ export default defineComponent({
|
|||||||
emptyState,
|
emptyState,
|
||||||
// eslint-disable-next-line vue/no-dupe-keys
|
// eslint-disable-next-line vue/no-dupe-keys
|
||||||
loadingState,
|
loadingState,
|
||||||
openedRows,
|
isAllRowChecked,
|
||||||
|
onChangeCheckbox,
|
||||||
isSelected,
|
isSelected,
|
||||||
onSort,
|
onSort,
|
||||||
onSelect,
|
onSelect,
|
||||||
onChange,
|
onChange,
|
||||||
getRowData,
|
getRowData,
|
||||||
toggleOpened,
|
toggleOpened,
|
||||||
getAriaSort
|
getAriaSort,
|
||||||
|
isExpanded
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['open'],
|
emits: ['open'],
|
||||||
setup (props, { emit }) {
|
setup(props, { emit }) {
|
||||||
const { ui, attrs } = useUI('accordion', toRef(props, 'ui'), config, toRef(props, 'class'))
|
const { ui, attrs } = useUI('accordion', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
|
|
||||||
const uiButton = computed<typeof configButton>(() => configButton)
|
const uiButton = computed<typeof configButton>(() => configButton)
|
||||||
@@ -146,7 +146,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
|
|
||||||
function closeOthers (currentIndex: number, e: Event) {
|
function closeOthers(currentIndex: number, e: Event) {
|
||||||
if (!props.items[currentIndex].closeOthers && props.multiple) {
|
if (!props.items[currentIndex].closeOthers && props.multiple) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -158,27 +158,29 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function onEnter (_el: Element, done: () => void) {
|
function onEnter(_el: Element, done: () => void) {
|
||||||
const el = _el as HTMLElement
|
const el = _el as HTMLElement
|
||||||
el.style.height = '0'
|
el.style.height = '0'
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||||
el.offsetHeight // Trigger a reflow, flushing the CSS changes
|
el.offsetHeight // Trigger a reflow, flushing the CSS changes
|
||||||
el.style.height = el.scrollHeight + 'px'
|
el.style.height = el.scrollHeight + 'px'
|
||||||
|
|
||||||
el.addEventListener('transitionend', done, { once: true })
|
el.addEventListener('transitionend', done, { once: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
function onBeforeLeave (_el: Element) {
|
function onBeforeLeave(_el: Element) {
|
||||||
const el = _el as HTMLElement
|
const el = _el as HTMLElement
|
||||||
el.style.height = el.scrollHeight + 'px'
|
el.style.height = el.scrollHeight + 'px'
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||||
el.offsetHeight // Trigger a reflow, flushing the CSS changes
|
el.offsetHeight // Trigger a reflow, flushing the CSS changes
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAfterEnter (_el: Element) {
|
function onAfterEnter(_el: Element) {
|
||||||
const el = _el as HTMLElement
|
const el = _el as HTMLElement
|
||||||
el.style.height = 'auto'
|
el.style.height = 'auto'
|
||||||
}
|
}
|
||||||
|
|
||||||
function onLeave (_el: Element, done: () => void) {
|
function onLeave(_el: Element, done: () => void) {
|
||||||
const el = _el as HTMLElement
|
const el = _el as HTMLElement
|
||||||
el.style.height = '0'
|
el.style.height = '0'
|
||||||
|
|
||||||
|
|||||||
@@ -90,14 +90,14 @@ export default defineComponent({
|
|||||||
color: {
|
color: {
|
||||||
type: String as PropType<AlertColor>,
|
type: String as PropType<AlertColor>,
|
||||||
default: () => config.default.color,
|
default: () => config.default.color,
|
||||||
validator (value: string) {
|
validator(value: string) {
|
||||||
return [...appConfig.ui.colors, ...Object.keys(config.color)].includes(value)
|
return [...appConfig.ui.colors, ...Object.keys(config.color)].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
variant: {
|
variant: {
|
||||||
type: String as PropType<AlertVariant>,
|
type: String as PropType<AlertVariant>,
|
||||||
default: () => config.default.variant,
|
default: () => config.default.variant,
|
||||||
validator (value: string) {
|
validator(value: string) {
|
||||||
return [
|
return [
|
||||||
...Object.keys(config.variant),
|
...Object.keys(config.variant),
|
||||||
...Object.values(config.color).flatMap(value => Object.keys(value))
|
...Object.values(config.color).flatMap(value => Object.keys(value))
|
||||||
@@ -114,7 +114,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['close'],
|
emits: ['close'],
|
||||||
setup (props) {
|
setup(props) {
|
||||||
const { ui, attrs } = useUI('alert', toRef(props, 'ui'), config)
|
const { ui, attrs } = useUI('alert', toRef(props, 'ui'), config)
|
||||||
|
|
||||||
const alertClass = computed(() => {
|
const alertClass = computed(() => {
|
||||||
@@ -129,7 +129,7 @@ export default defineComponent({
|
|||||||
), props.class)
|
), props.class)
|
||||||
})
|
})
|
||||||
|
|
||||||
function onAction (action: AlertAction) {
|
function onAction(action: AlertAction) {
|
||||||
if (action.click) {
|
if (action.click) {
|
||||||
action.click()
|
action.click()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,21 +63,21 @@ export default defineComponent({
|
|||||||
size: {
|
size: {
|
||||||
type: String as PropType<AvatarSize>,
|
type: String as PropType<AvatarSize>,
|
||||||
default: () => config.default.size,
|
default: () => config.default.size,
|
||||||
validator (value: string) {
|
validator(value: string) {
|
||||||
return Object.keys(config.size).includes(value)
|
return Object.keys(config.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
chipColor: {
|
chipColor: {
|
||||||
type: String as PropType<AvatarChipColor>,
|
type: String as PropType<AvatarChipColor>,
|
||||||
default: () => config.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 as PropType<AvatarChipPosition>,
|
type: String as PropType<AvatarChipPosition>,
|
||||||
default: () => config.default.chipPosition,
|
default: () => config.default.chipPosition,
|
||||||
validator (value: string) {
|
validator(value: string) {
|
||||||
return Object.keys(config.chip.position).includes(value)
|
return Object.keys(config.chip.position).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -98,7 +98,7 @@ export default defineComponent({
|
|||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup(props) {
|
||||||
const { ui, attrs } = useUI('avatar', toRef(props, 'ui'), config)
|
const { ui, attrs } = useUI('avatar', toRef(props, 'ui'), config)
|
||||||
|
|
||||||
const url = computed(() => {
|
const url = computed(() => {
|
||||||
@@ -152,7 +152,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
function onError () {
|
function onError() {
|
||||||
error.value = true
|
error.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { h, cloneVNode, computed, toRef, defineComponent } from 'vue'
|
import { h, cloneVNode, 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 UAvatar from './Avatar.vue'
|
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { mergeConfig, getSlotsChildren } from '../../utils'
|
import { mergeConfig, getSlotsChildren } from '../../utils'
|
||||||
import type { AvatarSize, Strategy } from '../../types/index'
|
import type { AvatarSize, Strategy } from '../../types/index'
|
||||||
|
import UAvatar from './Avatar.vue'
|
||||||
// @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'
|
import { avatar, avatarGroup } from '#ui/ui.config'
|
||||||
@@ -19,7 +19,7 @@ export default defineComponent({
|
|||||||
size: {
|
size: {
|
||||||
type: String as PropType<AvatarSize>,
|
type: String as PropType<AvatarSize>,
|
||||||
default: null,
|
default: null,
|
||||||
validator (value: string) {
|
validator(value: string) {
|
||||||
return Object.keys(avatarConfig.size).includes(value)
|
return Object.keys(avatarConfig.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -36,12 +36,12 @@ export default defineComponent({
|
|||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props, { slots }) {
|
setup(props, { slots }) {
|
||||||
const { ui, attrs } = useUI('avatarGroup', toRef(props, 'ui'), avatarGroupConfig, toRef(props, 'class'))
|
const { ui, attrs } = useUI('avatarGroup', toRef(props, 'ui'), avatarGroupConfig, toRef(props, 'class'))
|
||||||
|
|
||||||
const children = computed(() => getSlotsChildren(slots))
|
const children = computed(() => getSlotsChildren(slots))
|
||||||
|
|
||||||
const max = computed(() => typeof props.max === 'string' ? parseInt(props.max, 10) : props.max)
|
const max = computed(() => typeof props.max === 'string' ? Number.parseInt(props.max, 10) : props.max)
|
||||||
|
|
||||||
const clones = computed(() => children.value.map((node, index) => {
|
const clones = computed(() => children.value.map((node, index) => {
|
||||||
const vProps: any = {}
|
const vProps: any = {}
|
||||||
|
|||||||
@@ -24,21 +24,21 @@ export default defineComponent({
|
|||||||
size: {
|
size: {
|
||||||
type: String as PropType<BadgeSize>,
|
type: String as PropType<BadgeSize>,
|
||||||
default: () => config.default.size,
|
default: () => config.default.size,
|
||||||
validator (value: string) {
|
validator(value: string) {
|
||||||
return Object.keys(config.size).includes(value)
|
return Object.keys(config.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
type: String as PropType<BadgeColor>,
|
type: String as PropType<BadgeColor>,
|
||||||
default: () => config.default.color,
|
default: () => config.default.color,
|
||||||
validator (value: string) {
|
validator(value: string) {
|
||||||
return [...appConfig.ui.colors, ...Object.keys(config.color)].includes(value)
|
return [...appConfig.ui.colors, ...Object.keys(config.color)].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
variant: {
|
variant: {
|
||||||
type: String as PropType<BadgeVariant>,
|
type: String as PropType<BadgeVariant>,
|
||||||
default: () => config.default.variant,
|
default: () => config.default.variant,
|
||||||
validator (value: string) {
|
validator(value: string) {
|
||||||
return [
|
return [
|
||||||
...Object.keys(config.variant),
|
...Object.keys(config.variant),
|
||||||
...Object.values(config.color).flatMap(value => Object.keys(value))
|
...Object.values(config.color).flatMap(value => Object.keys(value))
|
||||||
@@ -58,7 +58,7 @@ export default defineComponent({
|
|||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup(props) {
|
||||||
const { ui, attrs } = useUI('badge', toRef(props, 'ui'), config)
|
const { ui, attrs } = useUI('badge', toRef(props, 'ui'), config)
|
||||||
|
|
||||||
const { size, rounded } = useInjectButtonGroup({ ui, props })
|
const { size, rounded } = useInjectButtonGroup({ ui, props })
|
||||||
|
|||||||
@@ -67,21 +67,21 @@ export default defineComponent({
|
|||||||
size: {
|
size: {
|
||||||
type: String as PropType<ButtonSize>,
|
type: String as PropType<ButtonSize>,
|
||||||
default: () => config.default.size,
|
default: () => config.default.size,
|
||||||
validator (value: string) {
|
validator(value: string) {
|
||||||
return Object.keys(config.size).includes(value)
|
return Object.keys(config.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
type: String as PropType<ButtonColor>,
|
type: String as PropType<ButtonColor>,
|
||||||
default: () => config.default.color,
|
default: () => config.default.color,
|
||||||
validator (value: string) {
|
validator(value: string) {
|
||||||
return [...appConfig.ui.colors, ...Object.keys(config.color)].includes(value)
|
return [...appConfig.ui.colors, ...Object.keys(config.color)].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
variant: {
|
variant: {
|
||||||
type: String as PropType<ButtonVariant>,
|
type: String as PropType<ButtonVariant>,
|
||||||
default: () => config.default.variant,
|
default: () => config.default.variant,
|
||||||
validator (value: string) {
|
validator(value: string) {
|
||||||
return [
|
return [
|
||||||
...Object.keys(config.variant),
|
...Object.keys(config.variant),
|
||||||
...Object.values(config.color).flatMap(value => Object.keys(value))
|
...Object.values(config.color).flatMap(value => Object.keys(value))
|
||||||
@@ -129,7 +129,7 @@ export default defineComponent({
|
|||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props, { slots }) {
|
setup(props, { slots }) {
|
||||||
const { ui, attrs } = useUI('button', toRef(props, 'ui'), config)
|
const { ui, attrs } = useUI('button', toRef(props, 'ui'), config)
|
||||||
|
|
||||||
const { size, rounded } = useInjectButtonGroup({ ui, props })
|
const { size, rounded } = useInjectButtonGroup({ ui, props })
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ export default defineComponent({
|
|||||||
size: {
|
size: {
|
||||||
type: String as PropType<ButtonSize>,
|
type: String as PropType<ButtonSize>,
|
||||||
default: null,
|
default: null,
|
||||||
validator (value: string) {
|
validator(value: string) {
|
||||||
return Object.keys(buttonConfig.size).includes(value)
|
return Object.keys(buttonConfig.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
orientation: {
|
orientation: {
|
||||||
type: String as PropType<'horizontal' | 'vertical'>,
|
type: String as PropType<'horizontal' | 'vertical'>,
|
||||||
default: 'horizontal',
|
default: 'horizontal',
|
||||||
validator (value: string) {
|
validator(value: string) {
|
||||||
return ['horizontal', 'vertical'].includes(value)
|
return ['horizontal', 'vertical'].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -39,7 +39,7 @@ export default defineComponent({
|
|||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props, { slots }) {
|
setup(props, { slots }) {
|
||||||
const { ui, attrs } = useUI('buttonGroup', toRef(props, 'ui'), buttonGroupConfig)
|
const { ui, attrs } = useUI('buttonGroup', toRef(props, 'ui'), buttonGroupConfig)
|
||||||
|
|
||||||
const children = computed(() => getSlotsChildren(slots))
|
const children = computed(() => getSlotsChildren(slots))
|
||||||
|
|||||||
@@ -59,12 +59,12 @@
|
|||||||
import { ref, toRef, computed, defineComponent } from 'vue'
|
import { ref, toRef, computed, defineComponent } from 'vue'
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import { twMerge } from 'tailwind-merge'
|
import { twMerge } from 'tailwind-merge'
|
||||||
|
import { useScroll, useResizeObserver, useElementSize } from '@vueuse/core'
|
||||||
import { mergeConfig } from '../../utils'
|
import { mergeConfig } from '../../utils'
|
||||||
import UButton from '../elements/Button.vue'
|
import UButton from '../elements/Button.vue'
|
||||||
import type { Strategy, Button, DeepPartial } from '../../types/index'
|
import type { Strategy, Button, DeepPartial } from '../../types/index'
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { useCarouselScroll } from '../../composables/useCarouselScroll'
|
import { useCarouselScroll } from '../../composables/useCarouselScroll'
|
||||||
import { useScroll, useResizeObserver, useElementSize } from '@vueuse/core'
|
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
import { carousel } from '#ui/ui.config'
|
import { carousel } from '#ui/ui.config'
|
||||||
@@ -110,7 +110,7 @@ export default defineComponent({
|
|||||||
default: undefined
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props, { expose }) {
|
setup(props, { expose }) {
|
||||||
const { ui, attrs } = useUI('carousel', toRef(props, 'ui'), config, toRef(props, 'class'))
|
const { ui, attrs } = useUI('carousel', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
|
|
||||||
const carouselRef = ref<HTMLElement>()
|
const carouselRef = ref<HTMLElement>()
|
||||||
@@ -157,15 +157,15 @@ export default defineComponent({
|
|||||||
const isFirst = computed(() => currentPage.value <= 1)
|
const isFirst = computed(() => currentPage.value <= 1)
|
||||||
const isLast = computed(() => currentPage.value === pages.value)
|
const isLast = computed(() => currentPage.value === pages.value)
|
||||||
|
|
||||||
function onClickNext () {
|
function onClickNext() {
|
||||||
x.value += isRtl.value ? -itemWidth.value : itemWidth.value
|
x.value += isRtl.value ? -itemWidth.value : itemWidth.value
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClickPrev () {
|
function onClickPrev() {
|
||||||
x.value -= isRtl.value ? -itemWidth.value : itemWidth.value
|
x.value -= isRtl.value ? -itemWidth.value : itemWidth.value
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClick (page: number) {
|
function onClick(page: number) {
|
||||||
x.value = (page - 1) * itemWidth.value * (isRtl.value ? -1 : 1)
|
x.value = (page - 1) * itemWidth.value * (isRtl.value ? -1 : 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,21 +29,21 @@ export default defineComponent({
|
|||||||
size: {
|
size: {
|
||||||
type: String as PropType<ChipSize>,
|
type: String as PropType<ChipSize>,
|
||||||
default: () => config.default.size,
|
default: () => config.default.size,
|
||||||
validator (value: string) {
|
validator(value: string) {
|
||||||
return Object.keys(config.size).includes(value)
|
return Object.keys(config.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
type: String as PropType<ChipColor>,
|
type: String as PropType<ChipColor>,
|
||||||
default: () => config.default.color,
|
default: () => config.default.color,
|
||||||
validator (value: string) {
|
validator(value: string) {
|
||||||
return ['gray', ...appConfig.ui.colors].includes(value)
|
return ['gray', ...appConfig.ui.colors].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
position: {
|
position: {
|
||||||
type: String as PropType<ChipPosition>,
|
type: String as PropType<ChipPosition>,
|
||||||
default: () => config.default.position,
|
default: () => config.default.position,
|
||||||
validator (value: string) {
|
validator(value: string) {
|
||||||
return Object.keys(config.position).includes(value)
|
return Object.keys(config.position).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -68,7 +68,7 @@ export default defineComponent({
|
|||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup(props) {
|
||||||
const { ui, attrs } = useUI('chip', toRef(props, 'ui'), config, toRef(props, 'class'))
|
const { ui, attrs } = useUI('chip', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
|
|
||||||
const chipClass = computed(() => {
|
const chipClass = computed(() => {
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['update:open'],
|
emits: ['update:open'],
|
||||||
setup (props, { emit }) {
|
setup(props, { emit }) {
|
||||||
const { ui, attrs } = useUI('dropdown', toRef(props, 'ui'), config, toRef(props, 'class'))
|
const { ui, attrs } = useUI('dropdown', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
|
|
||||||
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))
|
||||||
@@ -182,7 +182,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
function onTouchStart (event: TouchEvent) {
|
function onTouchStart(event: TouchEvent) {
|
||||||
if (!event.cancelable || !menuApi.value || props.mode === 'click') {
|
if (!event.cancelable || !menuApi.value || props.mode === 'click') {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -194,7 +194,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMouseEnter () {
|
function onMouseEnter() {
|
||||||
if (props.mode !== 'hover' || !menuApi.value) {
|
if (props.mode !== 'hover' || !menuApi.value) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -209,12 +209,14 @@ export default defineComponent({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
openTimeout = openTimeout || setTimeout(() => {
|
openTimeout = openTimeout || setTimeout(() => {
|
||||||
menuApi.value.openMenu && menuApi.value.openMenu()
|
if (menuApi.value.openMenu) {
|
||||||
|
menuApi.value.openMenu()
|
||||||
|
}
|
||||||
openTimeout = null
|
openTimeout = null
|
||||||
}, props.openDelay)
|
}, props.openDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMouseLeave () {
|
function onMouseLeave() {
|
||||||
if (props.mode !== 'hover' || !menuApi.value) {
|
if (props.mode !== 'hover' || !menuApi.value) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -229,12 +231,14 @@ export default defineComponent({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
closeTimeout = closeTimeout || setTimeout(() => {
|
closeTimeout = closeTimeout || setTimeout(() => {
|
||||||
menuApi.value.closeMenu && menuApi.value.closeMenu()
|
if (menuApi.value.closeMenu) {
|
||||||
|
menuApi.value.closeMenu()
|
||||||
|
}
|
||||||
closeTimeout = null
|
closeTimeout = null
|
||||||
}, props.closeDelay)
|
}, props.closeDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClick (e, item, { href, navigate, close, isExternal }) {
|
function onClick(e, item, { href, navigate, close, isExternal }) {
|
||||||
if (item.click) {
|
if (item.click) {
|
||||||
item.click(e)
|
item.click(e)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default defineComponent({
|
|||||||
size: {
|
size: {
|
||||||
type: String as PropType<KbdSize>,
|
type: String as PropType<KbdSize>,
|
||||||
default: () => config.default.size,
|
default: () => config.default.size,
|
||||||
validator (value: string) {
|
validator(value: string) {
|
||||||
return Object.keys(config.size).includes(value)
|
return Object.keys(config.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -40,7 +40,7 @@ export default defineComponent({
|
|||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup(props) {
|
||||||
const { ui, attrs } = useUI('kbd', toRef(props, 'ui'), config)
|
const { ui, attrs } = useUI('kbd', toRef(props, 'ui'), config)
|
||||||
|
|
||||||
const kbdClass = computed(() => {
|
const kbdClass = computed(() => {
|
||||||
|
|||||||
@@ -73,8 +73,8 @@ export default defineComponent({
|
|||||||
default: undefined
|
default: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
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.exactQuery && !isEqual(route.query, $route.query)) {
|
if (props.exactQuery && !isEqual(route.query, $route.query)) {
|
||||||
return props.inactiveClass
|
return props.inactiveClass
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
slots: Object as SlotsType<{
|
slots: Object as SlotsType<{
|
||||||
indicator?: { percent: number, value: number },
|
indicator?: { percent: number, value: number }
|
||||||
label?: { percent: number, value: number },
|
label?: { percent: number, value: number }
|
||||||
}>,
|
}>,
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
@@ -74,14 +74,14 @@ export default defineComponent({
|
|||||||
size: {
|
size: {
|
||||||
type: String as PropType<MeterSize>,
|
type: String as PropType<MeterSize>,
|
||||||
default: () => config.default.size,
|
default: () => config.default.size,
|
||||||
validator (value: string) {
|
validator(value: string) {
|
||||||
return Object.keys(config.meter.size).includes(value)
|
return Object.keys(config.meter.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
type: String as PropType<MeterColor>,
|
type: String as PropType<MeterColor>,
|
||||||
default: () => config.default.color,
|
default: () => config.default.color,
|
||||||
validator (value: string) {
|
validator(value: string) {
|
||||||
return [...appConfig.ui.colors, ...Object.keys(config.color)].includes(value)
|
return [...appConfig.ui.colors, ...Object.keys(config.color)].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -98,10 +98,10 @@ export default defineComponent({
|
|||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup(props) {
|
||||||
const { ui, attrs } = useUI('meter', toRef(props, 'ui'), config, toRef(props, 'class'))
|
const { ui, attrs } = useUI('meter', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||||
|
|
||||||
function clampPercent (value: number, min: number, max: number): number {
|
function clampPercent(value: number, min: number, max: number): number {
|
||||||
if (min == max) {
|
if (min == max) {
|
||||||
return value < min ? 0 : 100
|
return value < min ? 0 : 100
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import { h, cloneVNode, computed, toRef, defineComponent } from 'vue'
|
|||||||
import type { ComputedRef, VNode, SlotsType, PropType } from 'vue'
|
import type { ComputedRef, VNode, SlotsType, PropType } from 'vue'
|
||||||
import { twJoin } from 'tailwind-merge'
|
import { twJoin } from 'tailwind-merge'
|
||||||
import UIcon from '../elements/Icon.vue'
|
import UIcon from '../elements/Icon.vue'
|
||||||
import Meter from './Meter.vue'
|
|
||||||
import { useUI } from '../../composables/useUI'
|
import { useUI } from '../../composables/useUI'
|
||||||
import { mergeConfig, getSlotsChildren } from '../../utils'
|
import { mergeConfig, getSlotsChildren } from '../../utils'
|
||||||
import type { Strategy, MeterSize } from '../../types/index'
|
import type { Strategy, MeterSize } from '../../types/index'
|
||||||
|
import type Meter from './Meter.vue'
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
import appConfig from '#build/app.config'
|
import appConfig from '#build/app.config'
|
||||||
import { meter, meterGroup } from '#ui/ui.config'
|
import { meter, meterGroup } from '#ui/ui.config'
|
||||||
@@ -19,8 +19,8 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
slots: Object as SlotsType<{
|
slots: Object as SlotsType<{
|
||||||
default?: typeof Meter[],
|
default?: typeof Meter[]
|
||||||
indicator?: { percent: number },
|
indicator?: { percent: number }
|
||||||
}>,
|
}>,
|
||||||
props: {
|
props: {
|
||||||
min: {
|
min: {
|
||||||
@@ -34,7 +34,7 @@ export default defineComponent({
|
|||||||
size: {
|
size: {
|
||||||
type: String as PropType<MeterSize>,
|
type: String as PropType<MeterSize>,
|
||||||
default: () => meterConfig.default.size,
|
default: () => meterConfig.default.size,
|
||||||
validator (value: string) {
|
validator(value: string) {
|
||||||
return Object.keys(meterConfig.meter.bar.size).includes(value)
|
return Object.keys(meterConfig.meter.bar.size).includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -55,7 +55,7 @@ export default defineComponent({
|
|||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup (props, { slots }) {
|
setup(props, { slots }) {
|
||||||
const { ui, attrs } = useUI('meterGroup', toRef(props, 'ui'), meterGroupConfig)
|
const { ui, attrs } = useUI('meterGroup', toRef(props, 'ui'), meterGroupConfig)
|
||||||
const { ui: uiMeter } = useUI('meter', undefined, meterConfig)
|
const { ui: uiMeter } = useUI('meter', undefined, meterConfig)
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
const rounded = computed(() => ui.value.orientation[ui.value.rounded])
|
const rounded = computed(() => ui.value.orientation[ui.value.rounded])
|
||||||
|
|
||||||
function clampPercent (value: number, min: number, max: number): number {
|
function clampPercent(value: number, min: number, max: number): number {
|
||||||
if (min == max) {
|
if (min == max) {
|
||||||
return value < min ? 0 : 100
|
return value < min ? 0 : 100
|
||||||
}
|
}
|
||||||
@@ -138,9 +138,9 @@ export default defineComponent({
|
|||||||
const clone = cloneVNode(node, vProps)
|
const clone = cloneVNode(node, vProps)
|
||||||
|
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
delete(clone.children?.label)
|
delete (clone.children?.label)
|
||||||
delete(clone.props?.indicator)
|
delete (clone.props?.indicator)
|
||||||
delete(clone.props?.label)
|
delete (clone.props?.label)
|
||||||
|
|
||||||
return clone
|
return clone
|
||||||
}))
|
}))
|
||||||
@@ -196,7 +196,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
return h('li', { class: labelClass.value }, [
|
return h('li', { class: labelClass.value }, [
|
||||||
h(UIcon, { name: clones.value[key]?.props.icon ?? props.icon }),
|
h(UIcon, { name: clones.value[key]?.props.icon ?? props.icon }),
|
||||||
`${label} (${ Math.round(percents.value[key]) }%)`
|
`${label} (${Math.round(percents.value[key])}%)`
|
||||||
])
|
])
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user