mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-15 20:48:12 +01:00
Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f05dbd26d1 | ||
|
|
9be36d328c | ||
|
|
4f28e1fe96 | ||
|
|
bd99c2d850 | ||
|
|
23bfeb9370 | ||
|
|
88f349d0d7 | ||
|
|
e7e6745599 | ||
|
|
d2c832292a | ||
|
|
bc61d29cce | ||
|
|
777fe4a309 | ||
|
|
3074632523 | ||
|
|
94b6e520f5 | ||
|
|
754dd36473 | ||
|
|
67a52b6f4e | ||
|
|
2e7d4989b5 | ||
|
|
c07a79571f | ||
|
|
4cebdb5152 | ||
|
|
06dfb963be | ||
|
|
4ebb94cd7e | ||
|
|
afff54fecd | ||
|
|
7ec08017e0 | ||
|
|
3dd88bacec | ||
|
|
d6d7971d44 | ||
|
|
20c33920d0 | ||
|
|
a63047b79a | ||
|
|
f3e32ba5a2 | ||
|
|
bd3f54aa80 | ||
|
|
9509c37af8 | ||
|
|
df00149598 | ||
|
|
f72c886d3a | ||
|
|
c531d0248b | ||
|
|
d73768b704 | ||
|
|
b9983549a4 | ||
|
|
370054b20c | ||
|
|
4a2b77d86c | ||
|
|
ade16b76cf | ||
|
|
1babad4f74 | ||
|
|
8fb8dc29cf | ||
|
|
e0ec60d1b1 | ||
|
|
3db3058395 | ||
|
|
0095d8916b | ||
|
|
58ae62413d | ||
|
|
1965495768 | ||
|
|
5465fcb61d | ||
|
|
ef8ebaf687 | ||
|
|
1094881878 | ||
|
|
d6fd18fc4f | ||
|
|
e2e079f0d8 | ||
|
|
cec9dadc7a | ||
|
|
c76f590097 | ||
|
|
4dee7c3bd3 | ||
|
|
47bbe35d2a | ||
|
|
60bcd4fdf0 | ||
|
|
c231fe5f26 | ||
|
|
1769d5ed6e | ||
|
|
68787b26fd | ||
|
|
f7604e565f | ||
|
|
82e26655a4 | ||
|
|
2453714136 | ||
|
|
973f36539d |
90
.github/workflows/module.yml
vendored
90
.github/workflows/module.yml
vendored
@@ -50,16 +50,19 @@ jobs:
|
||||
run: pnpm run typecheck
|
||||
|
||||
- name: Test
|
||||
run: pnpm run test
|
||||
run: pnpm run test run
|
||||
|
||||
- name: Test (vue)
|
||||
run: pnpm run test:vue
|
||||
run: pnpm run test:vue run
|
||||
|
||||
- name: Build
|
||||
run: pnpm run build
|
||||
|
||||
- name: Build vue fixture
|
||||
run: pnpm run test:vue:build
|
||||
- name: Build playground
|
||||
run: pnpm run dev:build
|
||||
|
||||
- name: Build playground (vue)
|
||||
run: pnpm run dev:vue:build
|
||||
|
||||
- name: Publish
|
||||
run: pnpx pkg-pr-new publish --compact --no-template --pnpm
|
||||
@@ -154,54 +157,57 @@ jobs:
|
||||
- name: Build
|
||||
run: pnpm run build
|
||||
|
||||
# nuxt-ui-pro:
|
||||
# needs: build
|
||||
nuxt-ui-pro:
|
||||
needs: build
|
||||
|
||||
# runs-on: ${{ matrix.os }}
|
||||
# Only run this job if not a fork PR (when push event or PR from same repo)
|
||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
|
||||
# permissions:
|
||||
# contents: read
|
||||
# pull-requests: read
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
# strategy:
|
||||
# matrix:
|
||||
# os: [ubuntu-latest] # macos-latest, windows-latest
|
||||
# node: [22]
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
# env:
|
||||
# NUXT_UI_PRO_LICENSE: ${{ secrets.NUXT_UI_PRO_LICENSE }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest] # macos-latest, windows-latest
|
||||
node: [22]
|
||||
|
||||
# steps:
|
||||
# - name: Checkout
|
||||
# uses: actions/checkout@v4
|
||||
# with:
|
||||
# repository: nuxt/ui-pro
|
||||
# token: ${{ secrets.NUXT_GITHUB_TOKEN }}
|
||||
env:
|
||||
NUXT_UI_PRO_LICENSE: ${{ secrets.NUXT_UI_PRO_LICENSE }}
|
||||
|
||||
# - name: Store commit SHA
|
||||
# run: |
|
||||
# echo "COMMIT_SHA=$(echo ${{ github.workflow_sha }} | cut -c1-7)" >> $GITHUB_ENV
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: nuxt/ui-pro
|
||||
token: ${{ secrets.NUXT_GITHUB_TOKEN }}
|
||||
|
||||
# - name: Install pnpm
|
||||
# uses: pnpm/action-setup@v4
|
||||
- name: Store commit SHA
|
||||
run: |
|
||||
echo "COMMIT_SHA=$(echo ${{ github.workflow_sha }} | cut -c1-7)" >> $GITHUB_ENV
|
||||
|
||||
# - name: Install node
|
||||
# uses: actions/setup-node@v4
|
||||
# with:
|
||||
# node-version: 22
|
||||
# cache: pnpm
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
# - name: Install latest nuxt/ui
|
||||
# run: pnpm install https://pkg.pr.new/@nuxt/ui@${{ env.COMMIT_SHA }} --lockfile-only
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: pnpm
|
||||
|
||||
# - name: Install dependencies
|
||||
# run: pnpm install
|
||||
- name: Install latest nuxt/ui
|
||||
run: pnpm install https://pkg.pr.new/@nuxt/ui@${{ env.COMMIT_SHA }} --lockfile-only
|
||||
|
||||
# - name: Prepare
|
||||
# run: pnpm run dev:prepare
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
# - name: Typecheck
|
||||
# run: pnpm run typecheck
|
||||
- name: Prepare
|
||||
run: pnpm run dev:prepare
|
||||
|
||||
# - name: Build
|
||||
# run: pnpm run build
|
||||
- name: Typecheck
|
||||
run: pnpm run typecheck
|
||||
|
||||
- name: Build
|
||||
run: pnpm run build
|
||||
|
||||
16
.github/workflows/stale.yml
vendored
16
.github/workflows/stale.yml
vendored
@@ -10,14 +10,16 @@ jobs:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
exempt-issue-labels: triage,v3
|
||||
stale-issue-message: 'This issue is stale because it has been open for 30 days with no activity.'
|
||||
stale-issue-label: stale
|
||||
stale-pr-label: stale
|
||||
days-before-stale: 30
|
||||
days-before-close: -1
|
||||
days-before-stale: -1 # Issues and PR will never be flagged stale automatically.
|
||||
stale-issue-label: 'needs reproduction' # Label that flags an issue as stale.
|
||||
only-labels: 'needs reproduction' # Only process these issues
|
||||
days-before-issue-close: 7
|
||||
ignore-updates: true
|
||||
remove-stale-when-updated: false
|
||||
close-issue-message: This issue was closed because it was open for 7 days without a reproduction.
|
||||
close-issue-label: closed-by-bot
|
||||
operations-per-run: 300 #default 30
|
||||
|
||||
33
CHANGELOG.md
33
CHANGELOG.md
@@ -1,5 +1,38 @@
|
||||
# Changelog
|
||||
|
||||
## [3.0.2](https://github.com/nuxt/ui/compare/v3.0.1...v3.0.2) (2025-03-28)
|
||||
|
||||
### Features
|
||||
|
||||
* **Calendar:** allow year and month buttons styling ([#3672](https://github.com/nuxt/ui/issues/3672)) ([4a2b77d](https://github.com/nuxt/ui/commit/4a2b77d86c28806234002340eda39de4dc78cce0))
|
||||
* **locale:** add Armenian language ([#3664](https://github.com/nuxt/ui/issues/3664)) ([c76f590](https://github.com/nuxt/ui/commit/c76f5900970e3f5c451192b1207ccea04771e8b3))
|
||||
* **Table:** add `empty` prop ([afff54f](https://github.com/nuxt/ui/commit/afff54fecd31497238461e0a44abd8668ed734c3))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Avatar:** proxy `$attrs` to default slot ([#3712](https://github.com/nuxt/ui/issues/3712)) ([88f349d](https://github.com/nuxt/ui/commit/88f349d0d74eb1c2ce5066818731759c25a9e83e))
|
||||
* **Button:** use `focus:outline-none` instead of `focus:outline-hidden` ([c231fe5](https://github.com/nuxt/ui/commit/c231fe5f26ca7614df46a7ec8a5ce7f4ec8884e7)), closes [#3658](https://github.com/nuxt/ui/issues/3658)
|
||||
* **CommandPalette:** use `group.id` as key ([bc61d29](https://github.com/nuxt/ui/commit/bc61d29cce531715a6279444845f02a002a22af7))
|
||||
* **components:** improve generic types ([#3331](https://github.com/nuxt/ui/issues/3331)) ([b998354](https://github.com/nuxt/ui/commit/b9983549a4b743724ea3ef99cc4a243f5ca41e53))
|
||||
* **Container:** add `w-full` class ([df00149](https://github.com/nuxt/ui/commit/df001495980647cab1e67fd16154f1bc778de5e2))
|
||||
* **defineLocale/defineShortcuts:** remove `@__NO_SIDE_EFFECTS__` ([82e2665](https://github.com/nuxt/ui/commit/82e26655a40782555299516f32a76046fa0dbd3a))
|
||||
* **Drawer:** remove `fadeFromIndex` prop proxy ([f7604e5](https://github.com/nuxt/ui/commit/f7604e565f717001a4d4c2974cf23559a3f01c21))
|
||||
* **Form:** clear dirty state after submit ([#3692](https://github.com/nuxt/ui/issues/3692)) ([3dd88ba](https://github.com/nuxt/ui/commit/3dd88bacecb2945efba8cc3cb4fe59fcbc056e9a))
|
||||
* **FormField:** add `help` to `aria-describedby` attribute ([#3691](https://github.com/nuxt/ui/issues/3691)) ([20c3392](https://github.com/nuxt/ui/commit/20c33920d005332db3c83f33a8c54c7c227ce0a0))
|
||||
* **InputMenu/SelectMenu:** empty search results ([94b6e52](https://github.com/nuxt/ui/commit/94b6e520f5ccf011204e953421fcc5b44b637e51))
|
||||
* **InputMenu:** reset `searchTerm` on `update:open` ([3074632](https://github.com/nuxt/ui/commit/3074632523e67fa6a0ad3d9a71e5692c285bdc3a)), closes [#3620](https://github.com/nuxt/ui/issues/3620)
|
||||
* **Link:** handle `aria-current` like `NuxtLink` / `RouterLink` ([c531d02](https://github.com/nuxt/ui/commit/c531d0248be7863980a1f676643c2dea8301c009))
|
||||
* **Link:** prevent `active="true"` binding on html ([d73768b](https://github.com/nuxt/ui/commit/d73768b70453d60dd4186a996c1cf808b0294bf6))
|
||||
* **Link:** properly pick all `aria-*` & `data-*` attrs ([ade16b7](https://github.com/nuxt/ui/commit/ade16b76cf535924a8d0f402b4d5d65cb67a55eb))
|
||||
* **Link:** proxy `onClick` ([370054b](https://github.com/nuxt/ui/commit/370054b20c0201c9dba84ddfcd1e916594619b93)), closes [#3631](https://github.com/nuxt/ui/issues/3631)
|
||||
* **NavigationMenu:** add `z-index` on viewport ([0095d89](https://github.com/nuxt/ui/commit/0095d8916bf361c0c89972e2f86b79850510c6a9)), closes [#3654](https://github.com/nuxt/ui/issues/3654)
|
||||
* **Switch:** prevent transition on focus outline ([68787b2](https://github.com/nuxt/ui/commit/68787b26fdf2bd5f9d9e812e5bfddb19abe45d1d))
|
||||
* **Table:** wrong condition on `caption` slot ([4ebb94c](https://github.com/nuxt/ui/commit/4ebb94cd7ef909b3547bce0922f75fe3ff74de4c))
|
||||
* **Tabs:** remove `focus:outline-hidden` class ([1769d5e](https://github.com/nuxt/ui/commit/1769d5ed6ea46b1f7eafdc48cb6456512229f98b))
|
||||
* **types:** add missing export for ButtonGroup ([#3709](https://github.com/nuxt/ui/issues/3709)) ([e7e6745](https://github.com/nuxt/ui/commit/e7e674559981177ad08be42418746060d7737df9))
|
||||
* **useOverlay:** refine `open` method type to infer close emit return type ([#3716](https://github.com/nuxt/ui/issues/3716)) ([bd99c2d](https://github.com/nuxt/ui/commit/bd99c2d850d57baccc51e049c0b578a6fc6ab431))
|
||||
* **vue:** mock `nuxtApp.hooks` & `useRuntimeHook` ([23bfeb9](https://github.com/nuxt/ui/commit/23bfeb937004d619187a67fb43e4c76b13d00069))
|
||||
|
||||
## [3.0.1](https://github.com/nuxt/ui/compare/v3.0.0...v3.0.1) (2025-03-21)
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
@@ -38,7 +38,7 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.carbon-poweredby {
|
||||
@apply block text-[10px] text-center text-(--ui-text-dimmed) pt-2;
|
||||
@apply block text-xs text-center text-(--ui-text-muted) pt-2;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<!-- eslint-disable no-useless-escape -->
|
||||
<script setup lang="ts">
|
||||
import type { ChipProps } from '@nuxt/ui'
|
||||
import json5 from 'json5'
|
||||
import { upperFirst, camelCase, kebabCase } from 'scule'
|
||||
import { hash } from 'ohash'
|
||||
@@ -53,6 +54,8 @@ const props = defineProps<{
|
||||
hide?: string[]
|
||||
/** List of props to externalize in script setup */
|
||||
external?: string[]
|
||||
/** The types of the externalized props */
|
||||
externalTypes?: string[]
|
||||
/** List of props to use with `v-model` */
|
||||
model?: string[]
|
||||
/** List of props to cast from code and selection */
|
||||
@@ -209,11 +212,21 @@ ${props.slots?.default}
|
||||
code += `
|
||||
<script setup lang="ts">
|
||||
`
|
||||
for (const key of props.external) {
|
||||
if (props.externalTypes?.length) {
|
||||
const removeArrayBrackets = (type: string): string => type.endsWith('[]') ? removeArrayBrackets(type.slice(0, -2)) : type
|
||||
|
||||
const types = props.externalTypes.map(type => removeArrayBrackets(type))
|
||||
code += `import type { ${types.join(', ')} } from '@nuxt/ui${props.pro ? '-pro' : ''}'
|
||||
|
||||
`
|
||||
}
|
||||
|
||||
for (const [i, key] of props.external.entries()) {
|
||||
const cast = props.cast?.[key]
|
||||
const value = cast ? castMap[cast]!.template(componentProps[key]) : json5.stringify(componentProps[key], null, 2)?.replace(/,([ |\t\n]+[}|\]])/g, '$1')
|
||||
const type = props.externalTypes?.[i] ? `<${props.externalTypes[i]}>` : ''
|
||||
|
||||
code += `const ${key === 'modelValue' ? 'value' : key} = ref(${value})
|
||||
code += `const ${key === 'modelValue' ? 'value' : key} = ref${type}(${value})
|
||||
`
|
||||
}
|
||||
code += `<\/script>
|
||||
@@ -346,7 +359,7 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
|
||||
inset
|
||||
standalone
|
||||
:color="(modelValue as any)"
|
||||
:size="ui.itemLeadingChipSize()"
|
||||
:size="(ui.itemLeadingChipSize() as ChipProps['size'])"
|
||||
class="size-2"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { ChipProps } from '@nuxt/ui'
|
||||
import { camelCase } from 'scule'
|
||||
import { useElementSize } from '@vueuse/core'
|
||||
import { get, set } from '#ui/utils'
|
||||
@@ -185,7 +186,7 @@ const urlSearchParams = computed(() => {
|
||||
inset
|
||||
standalone
|
||||
:color="(modelValue as any)"
|
||||
:size="ui.itemLeadingChipSize()"
|
||||
:size="(ui.itemLeadingChipSize() as ChipProps['size'])"
|
||||
class="size-2"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -12,14 +12,15 @@ function getEmojiFlag(locale: string): string {
|
||||
ar: 'sa', // Arabic -> Saudi Arabia
|
||||
bn: 'bd', // Bengali -> Bangladesh
|
||||
ca: 'es', // Catalan -> Spain
|
||||
cs: 'cz', // Czech -> Czech Republic (note: modern country code is actually 'cz')
|
||||
ckb: 'iq', // Central Kurdish -> Iraq
|
||||
cs: 'cz', // Czech -> Czech Republic (note: modern country code is actually 'cz')
|
||||
da: 'dk', // Danish -> Denmark
|
||||
el: 'gr', // Greek -> Greece
|
||||
et: 'ee', // Estonian -> Estonia
|
||||
en: 'gb', // English -> Great Britain
|
||||
et: 'ee', // Estonian -> Estonia
|
||||
he: 'il', // Hebrew -> Israel
|
||||
hi: 'in', // Hindi -> India
|
||||
hy: 'am', // Armenian -> Armenia
|
||||
ja: 'jp', // Japanese -> Japan
|
||||
km: 'kh', // Khmer -> Cambodia
|
||||
ko: 'kr', // Korean -> South Korea
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
const items = [
|
||||
import type { AccordionItem } from '@nuxt/ui'
|
||||
|
||||
const items: AccordionItem[] = [
|
||||
{
|
||||
label: 'Icons',
|
||||
icon: 'i-lucide-smile'
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
const items = [
|
||||
import type { AccordionItem } from '@nuxt/ui'
|
||||
|
||||
const items: AccordionItem[] = [
|
||||
{
|
||||
label: 'Icons',
|
||||
icon: 'i-lucide-smile'
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { AccordionItem } from '@nuxt/ui'
|
||||
|
||||
const items = [
|
||||
{
|
||||
label: 'Icons',
|
||||
@@ -8,7 +10,7 @@ const items = [
|
||||
{
|
||||
label: 'Colors',
|
||||
icon: 'i-lucide-swatch-book',
|
||||
slot: 'colors',
|
||||
slot: 'colors' as const,
|
||||
content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
|
||||
},
|
||||
{
|
||||
@@ -16,7 +18,7 @@ const items = [
|
||||
icon: 'i-lucide-box',
|
||||
content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
|
||||
}
|
||||
]
|
||||
] satisfies AccordionItem[]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<script setup lang="ts">
|
||||
import type { AccordionItem } from '@nuxt/ui'
|
||||
import { useSortable } from '@vueuse/integrations/useSortable'
|
||||
|
||||
const items = shallowRef<AccordionItem[]>([
|
||||
{
|
||||
label: 'Icons',
|
||||
icon: 'i-lucide-smile',
|
||||
content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
|
||||
},
|
||||
{
|
||||
label: 'Colors',
|
||||
icon: 'i-lucide-swatch-book',
|
||||
slot: 'colors' as const,
|
||||
content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
|
||||
},
|
||||
{
|
||||
label: 'Components',
|
||||
icon: 'i-lucide-box',
|
||||
content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
|
||||
}
|
||||
])
|
||||
|
||||
const accordion = useTemplateRef<HTMLElement>('accordion')
|
||||
|
||||
useSortable(accordion, items, {
|
||||
animation: 150
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UAccordion ref="accordion" :items="items" />
|
||||
</template>
|
||||
@@ -1,5 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
const items = [
|
||||
import type { AccordionItem } from '@nuxt/ui'
|
||||
|
||||
const items: AccordionItem[] = [
|
||||
{
|
||||
label: 'Icons',
|
||||
icon: 'i-lucide-smile',
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import type { BreadcrumbItem } from '@nuxt/ui'
|
||||
|
||||
const items = [{
|
||||
label: 'Home',
|
||||
to: '/'
|
||||
}, {
|
||||
slot: 'dropdown',
|
||||
slot: 'dropdown' as const,
|
||||
icon: 'i-lucide-ellipsis',
|
||||
children: [{
|
||||
label: 'Documentation'
|
||||
@@ -18,7 +20,7 @@ const items = [{
|
||||
}, {
|
||||
label: 'Breadcrumb',
|
||||
to: '/components/breadcrumb'
|
||||
}]
|
||||
}] satisfies BreadcrumbItem[]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
const items = [{
|
||||
import type { BreadcrumbItem } from '@nuxt/ui'
|
||||
|
||||
const items: BreadcrumbItem[] = [{
|
||||
label: 'Home',
|
||||
to: '/'
|
||||
}, {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
const items = [{
|
||||
import type { DropdownMenuItem } from '@nuxt/ui'
|
||||
|
||||
const items: DropdownMenuItem[] = [{
|
||||
label: 'Team',
|
||||
icon: 'i-lucide-users'
|
||||
}, {
|
||||
|
||||
@@ -11,7 +11,7 @@ const groups = [{
|
||||
label: 'Billing',
|
||||
icon: 'i-lucide-credit-card',
|
||||
kbds: ['meta', 'B'],
|
||||
slot: 'billing'
|
||||
slot: 'billing' as const
|
||||
},
|
||||
{
|
||||
label: 'Notifications',
|
||||
@@ -25,7 +25,7 @@ const groups = [{
|
||||
}, {
|
||||
id: 'users',
|
||||
label: 'Users',
|
||||
slot: 'users',
|
||||
slot: 'users' as const,
|
||||
items: [
|
||||
{
|
||||
label: 'Benjamin Canac',
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import type { ContextMenuItem } from '@nuxt/ui'
|
||||
|
||||
const showSidebar = ref(true)
|
||||
const showToolbar = ref(false)
|
||||
|
||||
const items = computed(() => [{
|
||||
const items = computed<ContextMenuItem[]>(() => [{
|
||||
label: 'View',
|
||||
type: 'label' as const
|
||||
}, {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
const items = [
|
||||
import type { ContextMenuItem } from '@nuxt/ui'
|
||||
|
||||
const items: ContextMenuItem[][] = [
|
||||
[
|
||||
{
|
||||
label: 'View',
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { ContextMenuItem } from '@nuxt/ui'
|
||||
|
||||
const loading = ref(true)
|
||||
|
||||
const items = [{
|
||||
const items: ContextMenuItem[] = [{
|
||||
label: 'Refresh the Page',
|
||||
slot: 'refresh'
|
||||
}, {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { DropdownMenuItem } from '@nuxt/ui'
|
||||
|
||||
const showBookmarks = ref(true)
|
||||
const showHistory = ref(false)
|
||||
const showDownloads = ref(false)
|
||||
@@ -36,7 +38,7 @@ const items = computed(() => [{
|
||||
onUpdateChecked(checked: boolean) {
|
||||
showDownloads.value = checked
|
||||
}
|
||||
}])
|
||||
}] satisfies DropdownMenuItem[])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
const items = [
|
||||
import type { DropdownMenuItem } from '@nuxt/ui'
|
||||
|
||||
const items: DropdownMenuItem[][] = [
|
||||
[
|
||||
{
|
||||
label: 'View',
|
||||
@@ -17,7 +19,7 @@ const items = [
|
||||
[
|
||||
{
|
||||
label: 'Delete',
|
||||
color: 'error' as const,
|
||||
color: 'error',
|
||||
icon: 'i-lucide-trash'
|
||||
}
|
||||
]
|
||||
@@ -27,9 +29,5 @@ const items = [
|
||||
<template>
|
||||
<UDropdownMenu :items="items" :ui="{ content: 'w-48' }">
|
||||
<UButton label="Open" color="neutral" variant="outline" icon="i-lucide-menu" />
|
||||
|
||||
<template #profile-trailing>
|
||||
<UIcon name="i-lucide-badge-check" class="shrink-0 size-5 text-(--ui-primary)" />
|
||||
</template>
|
||||
</UDropdownMenu>
|
||||
</template>
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
const items = [{
|
||||
label: 'Profile',
|
||||
icon: 'i-lucide-user',
|
||||
slot: 'profile'
|
||||
}, {
|
||||
label: 'Billing',
|
||||
icon: 'i-lucide-credit-card'
|
||||
}, {
|
||||
label: 'Settings',
|
||||
icon: 'i-lucide-cog'
|
||||
}]
|
||||
import type { DropdownMenuItem } from '@nuxt/ui'
|
||||
|
||||
const items = [
|
||||
{
|
||||
label: 'Profile',
|
||||
icon: 'i-lucide-user',
|
||||
slot: 'profile' as const
|
||||
}, {
|
||||
label: 'Billing',
|
||||
icon: 'i-lucide-credit-card'
|
||||
}, {
|
||||
label: 'Settings',
|
||||
icon: 'i-lucide-cog'
|
||||
}
|
||||
] satisfies DropdownMenuItem[]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import type { DropdownMenuItem } from '@nuxt/ui'
|
||||
|
||||
const open = ref(false)
|
||||
|
||||
defineShortcuts({
|
||||
o: () => open.value = !open.value
|
||||
})
|
||||
|
||||
const items = [{
|
||||
label: 'Profile',
|
||||
icon: 'i-lucide-user'
|
||||
}, {
|
||||
label: 'Billing',
|
||||
icon: 'i-lucide-credit-card'
|
||||
}, {
|
||||
label: 'Settings',
|
||||
icon: 'i-lucide-cog'
|
||||
}]
|
||||
const items: DropdownMenuItem[] = [
|
||||
{
|
||||
label: 'Profile',
|
||||
icon: 'i-lucide-user'
|
||||
}, {
|
||||
label: 'Billing',
|
||||
icon: 'i-lucide-credit-card'
|
||||
}, {
|
||||
label: 'Settings',
|
||||
icon: 'i-lucide-cog'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -16,7 +16,7 @@ function onOpen() {
|
||||
|
||||
<template>
|
||||
<UInputMenu
|
||||
:items="countries || []"
|
||||
:items="countries"
|
||||
:loading="status === 'pending'"
|
||||
label-key="name"
|
||||
:search-input="{ icon: 'i-lucide-search' }"
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { AvatarProps } from '@nuxt/ui'
|
||||
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
key: 'typicode-users',
|
||||
transform: (data: { id: number, name: string }[]) => {
|
||||
@@ -6,7 +8,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
label: user.name,
|
||||
value: String(user.id),
|
||||
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
|
||||
})) || []
|
||||
}))
|
||||
},
|
||||
lazy: true
|
||||
})
|
||||
@@ -14,7 +16,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
|
||||
<template>
|
||||
<UInputMenu
|
||||
:items="users || []"
|
||||
:items="users"
|
||||
:loading="status === 'pending'"
|
||||
icon="i-lucide-user"
|
||||
placeholder="Select user"
|
||||
@@ -23,7 +25,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
<UAvatar
|
||||
v-if="modelValue"
|
||||
v-bind="modelValue.avatar"
|
||||
:size="ui.leadingAvatarSize()"
|
||||
:size="(ui.leadingAvatarSize() as AvatarProps['size'])"
|
||||
:class="ui.leadingAvatar()"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { AvatarProps } from '@nuxt/ui'
|
||||
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
key: 'typicode-users-email',
|
||||
transform: (data: { id: number, name: string, email: string }[]) => {
|
||||
@@ -7,7 +9,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
email: user.email,
|
||||
value: String(user.id),
|
||||
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
|
||||
})) || []
|
||||
}))
|
||||
},
|
||||
lazy: true
|
||||
})
|
||||
@@ -15,7 +17,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
|
||||
<template>
|
||||
<UInputMenu
|
||||
:items="users || []"
|
||||
:items="users"
|
||||
:loading="status === 'pending'"
|
||||
:filter-fields="['label', 'email']"
|
||||
icon="i-lucide-user"
|
||||
@@ -26,7 +28,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
<UAvatar
|
||||
v-if="modelValue"
|
||||
v-bind="modelValue.avatar"
|
||||
:size="ui.leadingAvatarSize()"
|
||||
:size="(ui.leadingAvatarSize() as AvatarProps['size'])"
|
||||
:class="ui.leadingAvatar()"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { AvatarProps } from '@nuxt/ui'
|
||||
|
||||
const searchTerm = ref('')
|
||||
const searchTermDebounced = refDebounced(searchTerm, 200)
|
||||
|
||||
@@ -10,7 +12,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
label: user.name,
|
||||
value: String(user.id),
|
||||
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
|
||||
})) || []
|
||||
}))
|
||||
},
|
||||
lazy: true
|
||||
})
|
||||
@@ -19,7 +21,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
<template>
|
||||
<UInputMenu
|
||||
v-model:search-term="searchTerm"
|
||||
:items="users || []"
|
||||
:items="users"
|
||||
:loading="status === 'pending'"
|
||||
ignore-filter
|
||||
icon="i-lucide-user"
|
||||
@@ -29,7 +31,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
<UAvatar
|
||||
v-if="modelValue"
|
||||
v-bind="modelValue.avatar"
|
||||
:size="ui.leadingAvatarSize()"
|
||||
:size="(ui.leadingAvatarSize() as AvatarProps['size'])"
|
||||
:class="ui.leadingAvatar()"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { InputMenuItem } from '@nuxt/ui'
|
||||
|
||||
const items = ref([
|
||||
{
|
||||
label: 'benjamincanac',
|
||||
@@ -23,8 +25,16 @@ const items = ref([
|
||||
src: 'https://github.com/noook.png',
|
||||
alt: 'noook'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'sandros94',
|
||||
value: 'sandros94',
|
||||
avatar: {
|
||||
src: 'https://github.com/sandros94.png',
|
||||
alt: 'sandros94'
|
||||
}
|
||||
}
|
||||
])
|
||||
] satisfies InputMenuItem[])
|
||||
const value = ref(items.value[0])
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import type { InputMenuItem, ChipProps } from '@nuxt/ui'
|
||||
|
||||
const items = ref([
|
||||
{
|
||||
label: 'bug',
|
||||
value: 'bug',
|
||||
chip: {
|
||||
color: 'error' as const
|
||||
color: 'error'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'feature',
|
||||
value: 'feature',
|
||||
chip: {
|
||||
color: 'success' as const
|
||||
color: 'success'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'enhancement',
|
||||
value: 'enhancement',
|
||||
chip: {
|
||||
color: 'info' as const
|
||||
color: 'info'
|
||||
}
|
||||
}
|
||||
])
|
||||
] satisfies InputMenuItem[])
|
||||
|
||||
const value = ref(items.value[0])
|
||||
</script>
|
||||
|
||||
@@ -33,7 +36,7 @@ const value = ref(items.value[0])
|
||||
v-bind="modelValue.chip"
|
||||
inset
|
||||
standalone
|
||||
:size="ui.itemLeadingChipSize()"
|
||||
:size="(ui.itemLeadingChipSize() as ChipProps['size'])"
|
||||
:class="ui.itemLeadingChip()"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { InputMenuItem } from '@nuxt/ui'
|
||||
|
||||
const items = ref([
|
||||
{
|
||||
label: 'Backlog',
|
||||
@@ -20,7 +22,8 @@ const items = ref([
|
||||
value: 'done',
|
||||
icon: 'i-lucide-circle-check'
|
||||
}
|
||||
])
|
||||
] satisfies InputMenuItem[])
|
||||
|
||||
const value = ref(items.value[0])
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import type { NavigationMenuItem } from '@nuxt/ui'
|
||||
|
||||
const items = [
|
||||
{
|
||||
label: 'Docs',
|
||||
icon: 'i-lucide-book-open',
|
||||
slot: 'docs',
|
||||
slot: 'docs' as const,
|
||||
children: [
|
||||
{
|
||||
label: 'Icons',
|
||||
@@ -22,7 +24,7 @@ const items = [
|
||||
{
|
||||
label: 'Components',
|
||||
icon: 'i-lucide-box',
|
||||
slot: 'components',
|
||||
slot: 'components' as const,
|
||||
children: [
|
||||
{
|
||||
label: 'Link',
|
||||
@@ -54,7 +56,7 @@ const items = [
|
||||
label: 'GitHub',
|
||||
icon: 'i-simple-icons-github'
|
||||
}
|
||||
]
|
||||
] satisfies NavigationMenuItem[]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
const items = [
|
||||
import type { NavigationMenuItem } from '@nuxt/ui'
|
||||
|
||||
const items: NavigationMenuItem[] = [
|
||||
{
|
||||
label: 'Guide',
|
||||
icon: 'i-lucide-book-open'
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
const items = [
|
||||
import type { NavigationMenuItem } from '@nuxt/ui'
|
||||
|
||||
const items: NavigationMenuItem[] = [
|
||||
{
|
||||
label: 'Guide',
|
||||
icon: 'i-lucide-book-open',
|
||||
|
||||
@@ -4,8 +4,7 @@ const { data: countries, status, execute } = await useLazyFetch<{
|
||||
code: string
|
||||
emoji: string
|
||||
}[]>('/api/countries.json', {
|
||||
immediate: false,
|
||||
default: () => []
|
||||
immediate: false
|
||||
})
|
||||
|
||||
function onOpen() {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { AvatarProps } from '@nuxt/ui'
|
||||
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
key: 'typicode-users',
|
||||
transform: (data: { id: number, name: string }[]) => {
|
||||
@@ -6,7 +8,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
label: user.name,
|
||||
value: String(user.id),
|
||||
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
|
||||
})) || []
|
||||
}))
|
||||
},
|
||||
lazy: true
|
||||
})
|
||||
@@ -14,7 +16,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
|
||||
<template>
|
||||
<USelectMenu
|
||||
:items="users || []"
|
||||
:items="users"
|
||||
:loading="status === 'pending'"
|
||||
icon="i-lucide-user"
|
||||
placeholder="Select user"
|
||||
@@ -24,7 +26,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
<UAvatar
|
||||
v-if="modelValue"
|
||||
v-bind="modelValue.avatar"
|
||||
:size="ui.leadingAvatarSize()"
|
||||
:size="(ui.leadingAvatarSize() as AvatarProps['size'])"
|
||||
:class="ui.leadingAvatar()"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { AvatarProps } from '@nuxt/ui'
|
||||
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
key: 'typicode-users-email',
|
||||
transform: (data: { id: number, name: string, email: string }[]) => {
|
||||
@@ -7,7 +9,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
email: user.email,
|
||||
value: String(user.id),
|
||||
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
|
||||
})) || []
|
||||
}))
|
||||
},
|
||||
lazy: true
|
||||
})
|
||||
@@ -15,7 +17,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
|
||||
<template>
|
||||
<USelectMenu
|
||||
:items="users || []"
|
||||
:items="users"
|
||||
:loading="status === 'pending'"
|
||||
:filter-fields="['label', 'email']"
|
||||
icon="i-lucide-user"
|
||||
@@ -26,7 +28,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
<UAvatar
|
||||
v-if="modelValue"
|
||||
v-bind="modelValue.avatar"
|
||||
:size="ui.leadingAvatarSize()"
|
||||
:size="(ui.leadingAvatarSize() as AvatarProps['size'])"
|
||||
:class="ui.leadingAvatar()"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { AvatarProps } from '@nuxt/ui'
|
||||
|
||||
const searchTerm = ref('')
|
||||
const searchTermDebounced = refDebounced(searchTerm, 200)
|
||||
|
||||
@@ -10,7 +12,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
label: user.name,
|
||||
value: String(user.id),
|
||||
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
|
||||
})) || []
|
||||
}))
|
||||
},
|
||||
lazy: true
|
||||
})
|
||||
@@ -19,7 +21,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
<template>
|
||||
<USelectMenu
|
||||
v-model:search-term="searchTerm"
|
||||
:items="users || []"
|
||||
:items="users"
|
||||
:loading="status === 'pending'"
|
||||
ignore-filter
|
||||
icon="i-lucide-user"
|
||||
@@ -30,7 +32,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
<UAvatar
|
||||
v-if="modelValue"
|
||||
v-bind="modelValue.avatar"
|
||||
:size="ui.leadingAvatarSize()"
|
||||
:size="(ui.leadingAvatarSize() as AvatarProps['size'])"
|
||||
:class="ui.leadingAvatar()"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectMenuItem } from '@nuxt/ui'
|
||||
|
||||
const items = ref([
|
||||
{
|
||||
label: 'benjamincanac',
|
||||
@@ -23,8 +25,16 @@ const items = ref([
|
||||
src: 'https://github.com/noook.png',
|
||||
alt: 'noook'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'sandros94',
|
||||
value: 'sandros94',
|
||||
avatar: {
|
||||
src: 'https://github.com/sandros94.png',
|
||||
alt: 'sandros94'
|
||||
}
|
||||
}
|
||||
])
|
||||
] satisfies SelectMenuItem[])
|
||||
const value = ref(items.value[0])
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,27 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectMenuItem, ChipProps } from '@nuxt/ui'
|
||||
|
||||
const items = ref([
|
||||
{
|
||||
label: 'bug',
|
||||
value: 'bug',
|
||||
chip: {
|
||||
color: 'error' as const
|
||||
color: 'error'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'feature',
|
||||
value: 'feature',
|
||||
chip: {
|
||||
color: 'success' as const
|
||||
color: 'success'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'enhancement',
|
||||
value: 'enhancement',
|
||||
chip: {
|
||||
color: 'info' as const
|
||||
color: 'info'
|
||||
}
|
||||
}
|
||||
])
|
||||
] satisfies SelectMenuItem[])
|
||||
const value = ref(items.value[0])
|
||||
</script>
|
||||
|
||||
@@ -33,7 +35,7 @@ const value = ref(items.value[0])
|
||||
v-bind="modelValue.chip"
|
||||
inset
|
||||
standalone
|
||||
:size="ui.itemLeadingChipSize()"
|
||||
:size="(ui.itemLeadingChipSize() as ChipProps['size'])"
|
||||
:class="ui.itemLeadingChip()"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectMenuItem } from '@nuxt/ui'
|
||||
|
||||
const items = ref([
|
||||
{
|
||||
label: 'Backlog',
|
||||
@@ -20,7 +22,7 @@ const items = ref([
|
||||
value: 'done',
|
||||
icon: 'i-lucide-circle-check'
|
||||
}
|
||||
])
|
||||
] satisfies SelectMenuItem[])
|
||||
const value = ref(items.value[0])
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { AvatarProps } from '@nuxt/ui'
|
||||
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
key: 'typicode-users',
|
||||
transform: (data: { id: number, name: string }[]) => {
|
||||
@@ -6,7 +8,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
label: user.name,
|
||||
value: String(user.id),
|
||||
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
|
||||
})) || []
|
||||
}))
|
||||
},
|
||||
lazy: true
|
||||
})
|
||||
@@ -18,17 +20,18 @@ function getUserAvatar(value: string) {
|
||||
|
||||
<template>
|
||||
<USelect
|
||||
:items="users || []"
|
||||
:items="users"
|
||||
:loading="status === 'pending'"
|
||||
icon="i-lucide-user"
|
||||
placeholder="Select user"
|
||||
class="w-48"
|
||||
value-key="value"
|
||||
>
|
||||
<template #leading="{ modelValue, ui }">
|
||||
<UAvatar
|
||||
v-if="modelValue"
|
||||
v-bind="getUserAvatar(modelValue as string)"
|
||||
:size="ui.leadingAvatarSize()"
|
||||
v-bind="getUserAvatar(modelValue)"
|
||||
:size="(ui.leadingAvatarSize() as AvatarProps['size'])"
|
||||
:class="ui.leadingAvatar()"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectItem } from '@nuxt/ui'
|
||||
|
||||
const items = ref([
|
||||
{
|
||||
label: 'benjamincanac',
|
||||
@@ -23,13 +25,21 @@ const items = ref([
|
||||
src: 'https://github.com/noook.png',
|
||||
alt: 'noook'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'sandros94',
|
||||
value: 'sandros94',
|
||||
avatar: {
|
||||
src: 'https://github.com/sandros94.png',
|
||||
alt: 'sandros94'
|
||||
}
|
||||
}
|
||||
])
|
||||
] satisfies SelectItem[])
|
||||
const value = ref(items.value[0]?.value)
|
||||
|
||||
const avatar = computed(() => items.value.find(item => item.value === value.value)?.avatar)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<USelect v-model="value" :avatar="avatar" :items="items" class="w-48" />
|
||||
<USelect v-model="value" :items="items" value-key="value" :avatar="avatar" class="w-48" />
|
||||
</template>
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectItem, ChipProps } from '@nuxt/ui'
|
||||
|
||||
const items = ref([
|
||||
{
|
||||
label: 'bug',
|
||||
value: 'bug',
|
||||
chip: {
|
||||
color: 'error' as const
|
||||
color: 'error'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'feature',
|
||||
value: 'feature',
|
||||
chip: {
|
||||
color: 'success' as const
|
||||
color: 'success'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'enhancement',
|
||||
value: 'enhancement',
|
||||
chip: {
|
||||
color: 'info' as const
|
||||
color: 'info'
|
||||
}
|
||||
}
|
||||
])
|
||||
] satisfies SelectItem[])
|
||||
|
||||
const value = ref(items.value[0]?.value)
|
||||
|
||||
function getChip(value: string) {
|
||||
@@ -30,14 +33,14 @@ function getChip(value: string) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<USelect v-model="value" :items="items" class="w-48">
|
||||
<USelect v-model="value" :items="items" value-key="value" class="w-48">
|
||||
<template #leading="{ modelValue, ui }">
|
||||
<UChip
|
||||
v-if="modelValue"
|
||||
v-bind="getChip(modelValue as string)"
|
||||
v-bind="getChip(modelValue)"
|
||||
inset
|
||||
standalone
|
||||
:size="ui.itemLeadingChipSize()"
|
||||
:size="(ui.itemLeadingChipSize() as ChipProps['size'])"
|
||||
:class="ui.itemLeadingChip()"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectItem } from '@nuxt/ui'
|
||||
|
||||
const items = ref([
|
||||
{
|
||||
label: 'Backlog',
|
||||
@@ -20,12 +22,12 @@ const items = ref([
|
||||
value: 'done',
|
||||
icon: 'i-lucide-circle-check'
|
||||
}
|
||||
])
|
||||
] satisfies SelectItem[])
|
||||
const value = ref(items.value[0]?.value)
|
||||
|
||||
const icon = computed(() => items.value.find(item => item.value === value.value)?.icon)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<USelect v-model="value" :icon="icon" :items="items" class="w-48" />
|
||||
<USelect v-model="value" :items="items" value-key="value" :icon="icon" class="w-48" />
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
const items = [
|
||||
import type { StepperItem } from '@nuxt/ui'
|
||||
|
||||
const items: StepperItem[] = [
|
||||
{
|
||||
title: 'Address',
|
||||
description: 'Add your address here',
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
const items = [
|
||||
import type { StepperItem } from '@nuxt/ui'
|
||||
|
||||
const items: StepperItem[] = [
|
||||
{
|
||||
slot: 'address',
|
||||
title: 'Address',
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import type { StepperItem } from '@nuxt/ui'
|
||||
import { onMounted, ref } from 'vue'
|
||||
|
||||
const items = [
|
||||
const items: StepperItem[] = [
|
||||
{
|
||||
title: 'Address',
|
||||
description: 'Add your address here',
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
const items = [
|
||||
import type { StepperItem } from '@nuxt/ui'
|
||||
|
||||
const items: StepperItem[] = [
|
||||
{
|
||||
slot: 'address',
|
||||
title: 'Address',
|
||||
|
||||
@@ -97,10 +97,11 @@ function getHeader(column: Column<Payment>, label: string) {
|
||||
const isSorted = column.getIsSorted()
|
||||
|
||||
return h(UDropdownMenu, {
|
||||
content: {
|
||||
'content': {
|
||||
align: 'start'
|
||||
},
|
||||
items: [{
|
||||
'aria-label': 'Actions dropdown',
|
||||
'items': [{
|
||||
label: 'Asc',
|
||||
type: 'checkbox',
|
||||
icon: 'i-lucide-arrow-up-narrow-wide',
|
||||
@@ -126,11 +127,12 @@ function getHeader(column: Column<Payment>, label: string) {
|
||||
}
|
||||
}]
|
||||
}, () => h(UButton, {
|
||||
color: 'neutral',
|
||||
variant: 'ghost',
|
||||
'color': 'neutral',
|
||||
'variant': 'ghost',
|
||||
label,
|
||||
icon: isSorted ? (isSorted === 'asc' ? 'i-lucide-arrow-up-narrow-wide' : 'i-lucide-arrow-down-wide-narrow') : 'i-lucide-arrow-up-down',
|
||||
class: '-mx-2.5 data-[state=open]:bg-(--ui-bg-elevated)'
|
||||
'icon': isSorted ? (isSorted === 'asc' ? 'i-lucide-arrow-up-narrow-wide' : 'i-lucide-arrow-down-wide-narrow') : 'i-lucide-arrow-up-down',
|
||||
'class': '-mx-2.5 data-[state=open]:bg-(--ui-bg-elevated)',
|
||||
'aria-label': `Sort by ${isSorted === 'asc' ? 'descending' : 'ascending'}`
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -145,12 +145,12 @@ const columns: TableColumn<Payment>[] = [{
|
||||
header: ({ table }) => h(UCheckbox, {
|
||||
'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(),
|
||||
'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value),
|
||||
'ariaLabel': 'Select all'
|
||||
'aria-label': 'Select all'
|
||||
}),
|
||||
cell: ({ row }) => h(UCheckbox, {
|
||||
'modelValue': row.getIsSelected(),
|
||||
'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value),
|
||||
'ariaLabel': 'Select row'
|
||||
'aria-label': 'Select row'
|
||||
}),
|
||||
enableSorting: false,
|
||||
enableHiding: false
|
||||
@@ -242,15 +242,17 @@ const columns: TableColumn<Payment>[] = [{
|
||||
}]
|
||||
|
||||
return h('div', { class: 'text-right' }, h(UDropdownMenu, {
|
||||
content: {
|
||||
'content': {
|
||||
align: 'end'
|
||||
},
|
||||
items
|
||||
items,
|
||||
'aria-label': 'Actions dropdown'
|
||||
}, () => h(UButton, {
|
||||
icon: 'i-lucide-ellipsis-vertical',
|
||||
color: 'neutral',
|
||||
variant: 'ghost',
|
||||
class: 'ml-auto'
|
||||
'icon': 'i-lucide-ellipsis-vertical',
|
||||
'color': 'neutral',
|
||||
'variant': 'ghost',
|
||||
'class': 'ml-auto',
|
||||
'aria-label': 'Actions dropdown'
|
||||
})))
|
||||
}
|
||||
}]
|
||||
@@ -294,6 +296,7 @@ function randomize() {
|
||||
variant="outline"
|
||||
trailing-icon="i-lucide-chevron-down"
|
||||
class="ml-auto"
|
||||
aria-label="Columns select dropdown"
|
||||
/>
|
||||
</UDropdownMenu>
|
||||
</div>
|
||||
|
||||
@@ -17,7 +17,7 @@ const { data, status } = await useFetch<User[]>('https://jsonplaceholder.typicod
|
||||
transform: (data) => {
|
||||
return data?.map(user => ({
|
||||
...user,
|
||||
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
|
||||
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}`, alt: `${user.name} avatar` }
|
||||
})) || []
|
||||
},
|
||||
lazy: true
|
||||
|
||||
@@ -97,15 +97,17 @@ const columns: TableColumn<Payment>[] = [{
|
||||
id: 'actions',
|
||||
cell: ({ row }) => {
|
||||
return h('div', { class: 'text-right' }, h(UDropdownMenu, {
|
||||
content: {
|
||||
'content': {
|
||||
align: 'end'
|
||||
},
|
||||
items: getRowItems(row)
|
||||
'items': getRowItems(row),
|
||||
'aria-label': 'Actions dropdown'
|
||||
}, () => h(UButton, {
|
||||
icon: 'i-lucide-ellipsis-vertical',
|
||||
color: 'neutral',
|
||||
variant: 'ghost',
|
||||
class: 'ml-auto'
|
||||
'icon': 'i-lucide-ellipsis-vertical',
|
||||
'color': 'neutral',
|
||||
'variant': 'ghost',
|
||||
'class': 'ml-auto',
|
||||
'aria-label': 'Actions dropdown'
|
||||
})))
|
||||
}
|
||||
}]
|
||||
|
||||
@@ -48,14 +48,15 @@ const data = ref<Payment[]>([{
|
||||
const columns: TableColumn<Payment>[] = [{
|
||||
id: 'expand',
|
||||
cell: ({ row }) => h(UButton, {
|
||||
color: 'neutral',
|
||||
variant: 'ghost',
|
||||
icon: 'i-lucide-chevron-down',
|
||||
square: true,
|
||||
ui: {
|
||||
'color': 'neutral',
|
||||
'variant': 'ghost',
|
||||
'icon': 'i-lucide-chevron-down',
|
||||
'square': true,
|
||||
'aria-label': 'Expand',
|
||||
'ui': {
|
||||
leadingIcon: ['transition-transform', row.getIsExpanded() ? 'duration-200 rotate-180' : '']
|
||||
},
|
||||
onClick: () => row.toggleExpanded()
|
||||
'onClick': () => row.toggleExpanded()
|
||||
})
|
||||
}, {
|
||||
accessorKey: 'id',
|
||||
|
||||
@@ -50,12 +50,12 @@ const columns: TableColumn<Payment>[] = [{
|
||||
header: ({ table }) => h(UCheckbox, {
|
||||
'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(),
|
||||
'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value),
|
||||
'ariaLabel': 'Select all'
|
||||
'aria-label': 'Select all'
|
||||
}),
|
||||
cell: ({ row }) => h(UCheckbox, {
|
||||
'modelValue': row.getIsSelected(),
|
||||
'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value),
|
||||
'ariaLabel': 'Select row'
|
||||
'aria-label': 'Select row'
|
||||
})
|
||||
}, {
|
||||
accessorKey: 'date',
|
||||
|
||||
@@ -50,12 +50,12 @@ const columns: TableColumn<Payment>[] = [{
|
||||
header: ({ table }) => h(UCheckbox, {
|
||||
'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(),
|
||||
'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value),
|
||||
'ariaLabel': 'Select all'
|
||||
'aria-label': 'Select all'
|
||||
}),
|
||||
cell: ({ row }) => h(UCheckbox, {
|
||||
'modelValue': row.getIsSelected(),
|
||||
'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value),
|
||||
'ariaLabel': 'Select row'
|
||||
'aria-label': 'Select row'
|
||||
})
|
||||
}, {
|
||||
accessorKey: 'date',
|
||||
|
||||
@@ -95,7 +95,7 @@ function getDropdownActions(user: User): DropdownMenuItem[][] {
|
||||
<UTable :data="data" :columns="columns" class="flex-1">
|
||||
<template #name-cell="{ row }">
|
||||
<div class="flex items-center gap-3">
|
||||
<UAvatar :src="`https://i.pravatar.cc/120?img=${row.original.id}`" size="lg" />
|
||||
<UAvatar :src="`https://i.pravatar.cc/120?img=${row.original.id}`" size="lg" :alt="`${row.original.name} avatar`" />
|
||||
<div>
|
||||
<p class="font-medium text-(--ui-text-highlighted)">
|
||||
{{ row.original.name }}
|
||||
@@ -108,7 +108,7 @@ function getDropdownActions(user: User): DropdownMenuItem[][] {
|
||||
</template>
|
||||
<template #action-cell="{ row }">
|
||||
<UDropdownMenu :items="getDropdownActions(row.original)">
|
||||
<UButton icon="i-lucide-ellipsis-vertical" color="neutral" variant="ghost" />
|
||||
<UButton icon="i-lucide-ellipsis-vertical" color="neutral" variant="ghost" aria-label="Actions" />
|
||||
</UDropdownMenu>
|
||||
</template>
|
||||
</UTable>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
const items = [
|
||||
import type { TabsItem } from '@nuxt/ui'
|
||||
|
||||
const items: TabsItem[] = [
|
||||
{
|
||||
label: 'Account',
|
||||
icon: 'i-lucide-user'
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import type { TabsItem } from '@nuxt/ui'
|
||||
|
||||
const items = [
|
||||
{
|
||||
label: 'Account',
|
||||
description: 'Make changes to your account here. Click save when you\'re done.',
|
||||
icon: 'i-lucide-user',
|
||||
slot: 'account'
|
||||
slot: 'account' as const
|
||||
},
|
||||
{
|
||||
label: 'Password',
|
||||
description: 'Change your password here. After saving, you\'ll be logged out.',
|
||||
icon: 'i-lucide-lock',
|
||||
slot: 'password'
|
||||
slot: 'password' as const
|
||||
}
|
||||
]
|
||||
] satisfies TabsItem[]
|
||||
|
||||
const state = reactive({
|
||||
name: 'Benjamin Canac',
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
const items = [
|
||||
import type { TabsItem } from '@nuxt/ui'
|
||||
|
||||
const items: TabsItem[] = [
|
||||
{
|
||||
label: 'Account'
|
||||
},
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import type { TreeItem } from '@nuxt/ui'
|
||||
|
||||
const items: TreeItem[] = [
|
||||
const items = [
|
||||
{
|
||||
label: 'app/',
|
||||
slot: 'app',
|
||||
slot: 'app' as const,
|
||||
defaultExpanded: true,
|
||||
children: [{
|
||||
label: 'composables/',
|
||||
@@ -24,7 +24,7 @@ const items: TreeItem[] = [
|
||||
},
|
||||
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
|
||||
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
|
||||
]
|
||||
] satisfies TreeItem[]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -25,7 +25,7 @@ const items: TreeItem[] = [
|
||||
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
|
||||
]
|
||||
|
||||
const value = ref(items[items.length - 1])
|
||||
const value = ref()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { kebabCase } from 'scule'
|
||||
import type { ContentNavigationItem } from '@nuxt/content'
|
||||
import { findPageBreadcrumb, mapContentNavigation } from '#ui-pro/utils/content'
|
||||
import type { PageLink } from '@nuxt/ui-pro'
|
||||
import { findPageBreadcrumb, mapContentNavigation } from '@nuxt/ui-pro/utils/content'
|
||||
|
||||
const route = useRoute()
|
||||
const { framework, module } = useSharedData()
|
||||
@@ -100,6 +101,16 @@ const communityLinks = computed(() => [{
|
||||
label: 'Star on GitHub',
|
||||
to: `https://github.com/nuxt/${page.value?.module === 'ui-pro' ? 'ui-pro' : 'ui'}`,
|
||||
target: '_blank'
|
||||
}, module.value === 'ui-pro' && {
|
||||
icon: 'i-lucide-credit-card',
|
||||
label: 'Purchase a license',
|
||||
to: 'https://nuxt.lemonsqueezy.com/checkout/buy/057dacb2-87ba-4dc1-9256-59ee5b3bd394',
|
||||
target: '_blank'
|
||||
}, module.value === 'ui-pro' && {
|
||||
icon: 'i-lucide-ticket-percent',
|
||||
label: 'Become an affiliate',
|
||||
to: 'https://nuxt.lemonsqueezy.com/affiliates',
|
||||
target: '_blank'
|
||||
}, {
|
||||
icon: 'i-lucide-life-buoy',
|
||||
label: 'Contribution',
|
||||
@@ -108,7 +119,7 @@ const communityLinks = computed(() => [{
|
||||
label: 'Roadmap',
|
||||
icon: 'i-lucide-map',
|
||||
to: '/roadmap'
|
||||
}])
|
||||
}].filter(Boolean) as PageLink[])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -136,7 +147,7 @@ const communityLinks = computed(() => [{
|
||||
v-bind="link"
|
||||
>
|
||||
<template v-if="link.avatar" #leading>
|
||||
<UAvatar v-bind="link.avatar" size="2xs" />
|
||||
<UAvatar v-bind="link.avatar" size="2xs" :alt="`${link.label} avatar`" />
|
||||
</template>
|
||||
</UButton>
|
||||
</template>
|
||||
|
||||
@@ -169,6 +169,7 @@ onMounted(() => {
|
||||
:loading="index >= 4 ? 'lazy' : 'eager'"
|
||||
width="640"
|
||||
height="360"
|
||||
:alt="`${component.name} preview`"
|
||||
/>
|
||||
</div>
|
||||
</UPageCard>
|
||||
|
||||
@@ -103,10 +103,14 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
|
||||
:to="component.path"
|
||||
>
|
||||
<UColorModeImage
|
||||
|
||||
:light="`${component.path.replace('/components/', '/components/light/')}.png`"
|
||||
:dark="`${component.path.replace('/components/', '/components/dark/')}.png`"
|
||||
:alt="`${component.title} preview`"
|
||||
width="290"
|
||||
height="163"
|
||||
format="webp"
|
||||
class="hover:scale-105 lg:hover:scale-110 transition-transform aspect-video w-full border-x lg:border-x-0 lg:border-y border-(--ui-border) 2xl:border-y-0"
|
||||
loading="lazy"
|
||||
/>
|
||||
<UBadge color="neutral" variant="outline" size="md" :label="component.title" class="hidden lg:block absolute mx-auto top-4 left-6 xl:left-4 group-hover/link:opacity-100 opacity-0 transition-all duration-300 pointer-events-none -translate-y-2 group-hover/link:translate-y-0" />
|
||||
</ULink>
|
||||
@@ -130,7 +134,12 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
|
||||
<UColorModeImage
|
||||
:light="`${component.path.replace('/components/', '/components/light/')}.png`"
|
||||
:dark="`${component.path.replace('/components/', '/components/dark/')}.png`"
|
||||
:alt="`${component.title} preview`"
|
||||
width="290"
|
||||
height="163"
|
||||
format="webp"
|
||||
class="hover:scale-105 lg:hover:scale-110 transition-transform aspect-video w-full border-x lg:border-x-0 lg:border-y border-(--ui-border) 2xl:border-y-0"
|
||||
loading="lazy"
|
||||
/>
|
||||
<UBadge color="neutral" variant="outline" size="md" :label="component.title" class="hidden lg:block absolute mx-auto top-4 left-6 xl:left-4 group-hover/link:opacity-100 opacity-0 transition-all duration-300 pointer-events-none -translate-y-2 group-hover/link:translate-y-0" />
|
||||
</ULink>
|
||||
@@ -152,7 +161,9 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
|
||||
:in-view-options="{ once: true }"
|
||||
class="flex items-start gap-x-3 relative group"
|
||||
>
|
||||
<NuxtLink v-if="feature.to" :to="feature.to" class="absolute inset-0 z-10" />
|
||||
<NuxtLink v-if="feature.to" :to="feature.to" class="absolute inset-0 z-10">
|
||||
<span class="sr-only">Go to {{ feature.title }}</span>
|
||||
</NuxtLink>
|
||||
|
||||
<div class="relative p-3">
|
||||
<svg class="absolute inset-0" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
@@ -218,26 +229,32 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
|
||||
class="border-b border-(--ui-border)"
|
||||
>
|
||||
<template #features>
|
||||
<NuxtLink to="https://npm.chart.dev/@nuxt/ui" target="_blank" class="min-w-0">
|
||||
<p class="text-4xl font-semibold text-(--ui-text-highlighted) truncate">
|
||||
{{ format(module?.stats?.downloads ?? 0) }}+
|
||||
</p>
|
||||
<p class="text-(--ui-text-muted) text-sm truncate">monthly downloads</p>
|
||||
</NuxtLink>
|
||||
<li>
|
||||
<NuxtLink to="https://npm.chart.dev/@nuxt/ui" target="_blank" class="min-w-0">
|
||||
<p class="text-4xl font-semibold text-(--ui-text-highlighted) truncate">
|
||||
{{ format(module?.stats?.downloads ?? 0) }}+
|
||||
</p>
|
||||
<p class="text-(--ui-text-muted) text-sm truncate">monthly downloads</p>
|
||||
</NuxtLink>
|
||||
</li>
|
||||
|
||||
<NuxtLink to="https://github.com/nuxt/ui" target="_blank" class="min-w-0">
|
||||
<p class="text-4xl font-semibold text-(--ui-text-highlighted) truncate">
|
||||
{{ format(module?.stats?.stars ?? 0) }}+
|
||||
</p>
|
||||
<p class="text-(--ui-text-muted) text-sm truncate">GitHub stars</p>
|
||||
</NuxtLink>
|
||||
<li>
|
||||
<NuxtLink to="https://github.com/nuxt/ui" target="_blank" class="min-w-0">
|
||||
<p class="text-4xl font-semibold text-(--ui-text-highlighted) truncate">
|
||||
{{ format(module?.stats?.stars ?? 0) }}+
|
||||
</p>
|
||||
<p class="text-(--ui-text-muted) text-sm truncate">GitHub stars</p>
|
||||
</NuxtLink>
|
||||
</li>
|
||||
|
||||
<NuxtLink to="https://github.com/nuxt/ui/graphs/contributors" target="_blank" class="min-w-0">
|
||||
<p class="text-4xl font-semibold text-(--ui-text-highlighted) truncate">
|
||||
175+
|
||||
</p>
|
||||
<p class="text-(--ui-text-muted) text-sm truncate">Contributors</p>
|
||||
</NuxtLink>
|
||||
<li>
|
||||
<NuxtLink to="https://github.com/nuxt/ui/graphs/contributors" target="_blank" class="min-w-0">
|
||||
<p class="text-4xl font-semibold text-(--ui-text-highlighted) truncate">
|
||||
175+
|
||||
</p>
|
||||
<p class="text-(--ui-text-muted) text-sm truncate">Contributors</p>
|
||||
</NuxtLink>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<div ref="contributorsRef" class="p-4 sm:px-6 md:px-8 lg:px-12 xl:px-14 overflow-hidden flex relative">
|
||||
|
||||
@@ -56,6 +56,7 @@ useSeoMeta({
|
||||
v-if="template.thumbnail"
|
||||
v-bind="template.thumbnail"
|
||||
class="w-full h-auto border lg:border-y lg:border-x-0 border-(--ui-border) rounded-(--ui-radius) lg:rounded-none"
|
||||
:alt="`Template ${index} thumbnail`"
|
||||
width="656"
|
||||
height="369"
|
||||
loading="lazy"
|
||||
|
||||
@@ -6,7 +6,7 @@ navigation.icon: i-lucide-house
|
||||
|
||||
<iframe width="100%" height="100%" src="https://www.youtube-nocookie.com/embed/_eQxomah-nA?si=pDSzchUBDKb2NQu7" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen style="aspect-ratio: 16/9;" class="rounded-[calc(var(--ui-radius)*1.5)]"></iframe>
|
||||
|
||||
### Reka UI
|
||||
## Reka UI
|
||||
|
||||
We've transitioned from [Headless UI](https://headlessui.com/) to [Reka UI](https://reka-ui.com/) as our core component foundation. This shift brings several key advantages:
|
||||
|
||||
@@ -17,7 +17,7 @@ We've transitioned from [Headless UI](https://headlessui.com/) to [Reka UI](http
|
||||
|
||||
This transition empowers Nuxt UI to become a more comprehensive and flexible UI library, offering developers greater power and customization options.
|
||||
|
||||
### Tailwind CSS v4
|
||||
## Tailwind CSS v4
|
||||
|
||||
Nuxt UI integrates the latest Tailwind CSS v4, bringing significant improvements:
|
||||
|
||||
@@ -30,7 +30,7 @@ Nuxt UI integrates the latest Tailwind CSS v4, bringing significant improvements
|
||||
Learn about all the breaking changes in Tailwind CSS v4.
|
||||
::
|
||||
|
||||
### Tailwind Variants
|
||||
## Tailwind Variants
|
||||
|
||||
We've adopted [Tailwind Variants](https://www.tailwind-variants.org/) to manage our design system, offering:
|
||||
|
||||
@@ -40,7 +40,7 @@ We've adopted [Tailwind Variants](https://www.tailwind-variants.org/) to manage
|
||||
|
||||
This integration unifies the styling of components, ensuring consistency and code maintainability.
|
||||
|
||||
### TypeScript Integration
|
||||
## TypeScript Integration
|
||||
|
||||
Nuxt UI offers significantly improved TypeScript integration, providing a superior developer experience:
|
||||
|
||||
@@ -60,7 +60,7 @@ Nuxt UI offers significantly improved TypeScript integration, providing a superi
|
||||
Check out an example of the Accordion component with auto-completion for props and slots.
|
||||
::
|
||||
|
||||
### Vue compatibility
|
||||
## Vue compatibility
|
||||
|
||||
You can now use Nuxt UI in any Vue project without Nuxt by adding the Vite and Vue plugins to your configuration. This provides:
|
||||
|
||||
@@ -72,7 +72,7 @@ You can now use Nuxt UI in any Vue project without Nuxt by adding the Vite and V
|
||||
Learn how to install and configure Nuxt UI in a Vue project in the **Vue installation guide**.
|
||||
::
|
||||
|
||||
### Nuxt DevTools Integration
|
||||
## Nuxt DevTools Integration
|
||||
|
||||
You can play with Nuxt UI components as well as your app components directly from Nuxt Devtools with the [compodium](https://github.com/romhml/compodium) module, providing a powerful development experience:
|
||||
|
||||
|
||||
@@ -136,19 +136,19 @@ To dynamically switch between languages, you can use the [Nuxt I18n](https://i18
|
||||
::code-group{sync="pm"}
|
||||
|
||||
```bash [pnpm]
|
||||
pnpm add @nuxtjs/i18n@next
|
||||
pnpm add @nuxtjs/i18n
|
||||
```
|
||||
|
||||
```bash [yarn]
|
||||
yarn add @nuxtjs/i18n@next
|
||||
yarn add @nuxtjs/i18n
|
||||
```
|
||||
|
||||
```bash [npm]
|
||||
npm install @nuxtjs/i18n@next
|
||||
npm install @nuxtjs/i18n
|
||||
```
|
||||
|
||||
```bash [bun]
|
||||
bun add @nuxtjs/i18n@next
|
||||
bun add @nuxtjs/i18n
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
@@ -30,6 +30,8 @@ ignore:
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- AccordionItem[]
|
||||
hide:
|
||||
- class
|
||||
props:
|
||||
@@ -58,6 +60,8 @@ ignore:
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- AccordionItem[]
|
||||
hide:
|
||||
- class
|
||||
props:
|
||||
@@ -87,6 +91,8 @@ ignore:
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- AccordionItem[]
|
||||
hide:
|
||||
- class
|
||||
props:
|
||||
@@ -115,6 +121,8 @@ ignore:
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- AccordionItem[]
|
||||
hide:
|
||||
- class
|
||||
props:
|
||||
@@ -149,6 +157,8 @@ ignore:
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- AccordionItem[]
|
||||
hide:
|
||||
- class
|
||||
props:
|
||||
@@ -182,6 +192,8 @@ ignore:
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- AccordionItem[]
|
||||
hide:
|
||||
- class
|
||||
props:
|
||||
@@ -280,6 +292,18 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
### With drag and drop
|
||||
|
||||
Use the [`useSortable`](https://vueuse.org/integrations/useSortable/) composable from [`@vueuse/integrations`](https://vueuse.org/integrations/README.html) to enable drag and drop functionality on the accordion. This integration wraps [Sortable.js](https://sortablejs.github.io/Sortable/) to provide a seamless drag and drop experience.
|
||||
|
||||
The `useSortable` composable accepts various options, see the [Usage](https://vueuse.org/integrations/useSortable/#usage) for more examples.
|
||||
|
||||
::component-example
|
||||
---
|
||||
name: 'accordion-drag-and-drop-example'
|
||||
---
|
||||
::
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
@@ -27,6 +27,8 @@ ignore:
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- BreadcrumbItem[]
|
||||
props:
|
||||
items:
|
||||
- label: 'Home'
|
||||
@@ -54,6 +56,8 @@ ignore:
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- BreadcrumbItem[]
|
||||
props:
|
||||
separatorIcon: 'i-lucide-arrow-right'
|
||||
items:
|
||||
|
||||
@@ -44,6 +44,8 @@ ignore:
|
||||
- ui.content
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- ContextMenuItem[][]
|
||||
props:
|
||||
items:
|
||||
- - label: Appearance
|
||||
@@ -124,6 +126,8 @@ ignore:
|
||||
- ui.content
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- ContextMenuItem[]
|
||||
props:
|
||||
size: xl
|
||||
items:
|
||||
@@ -158,6 +162,8 @@ ignore:
|
||||
- ui.content
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- ContextMenuItem[]
|
||||
props:
|
||||
disabled: true
|
||||
items:
|
||||
|
||||
@@ -44,6 +44,8 @@ ignore:
|
||||
- ui.content
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- DropdownMenuItem[][]
|
||||
props:
|
||||
items:
|
||||
- - label: Benjamin
|
||||
@@ -123,6 +125,8 @@ ignore:
|
||||
- ui.content
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- DropdownMenuItem[]
|
||||
items:
|
||||
content.align:
|
||||
- start
|
||||
@@ -169,6 +173,8 @@ ignore:
|
||||
- ui.content
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- DropdownMenuItem[]
|
||||
props:
|
||||
arrow: true
|
||||
items:
|
||||
@@ -202,6 +208,8 @@ ignore:
|
||||
- ui.content
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- DropdownMenuItem[]
|
||||
props:
|
||||
size: xl
|
||||
items:
|
||||
@@ -244,6 +252,8 @@ ignore:
|
||||
- ui.content
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- DropdownMenuItem[]
|
||||
props:
|
||||
disabled: true
|
||||
items:
|
||||
@@ -334,7 +344,9 @@ Inside the `defineShortcuts` composable, there is an `extractShortcuts` utility
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
const items = [{
|
||||
import type { DropdownMenuItem } from '@nuxt/ui'
|
||||
|
||||
const items: DropdownMenuItem[] = [{
|
||||
label: 'Invite users',
|
||||
icon: 'i-lucide-user-plus',
|
||||
children: [{
|
||||
|
||||
@@ -39,6 +39,8 @@ ignore:
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- NavigationMenuItem[]
|
||||
props:
|
||||
items:
|
||||
- label: Guide
|
||||
@@ -148,6 +150,8 @@ ignore:
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- NavigationMenuItem[][]
|
||||
props:
|
||||
orientation: 'vertical'
|
||||
items:
|
||||
@@ -247,6 +251,8 @@ ignore:
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- NavigationMenuItem[][]
|
||||
props:
|
||||
highlight: true
|
||||
highlightColor: 'primary'
|
||||
@@ -346,6 +352,8 @@ ignore:
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- NavigationMenuItem[][]
|
||||
props:
|
||||
color: neutral
|
||||
items:
|
||||
@@ -379,6 +387,8 @@ ignore:
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- NavigationMenuItem[][]
|
||||
props:
|
||||
color: neutral
|
||||
variant: link
|
||||
@@ -423,6 +433,8 @@ ignore:
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- NavigationMenuItem[]
|
||||
props:
|
||||
trailingIcon: 'i-lucide-arrow-down'
|
||||
items:
|
||||
@@ -519,6 +531,8 @@ ignore:
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- NavigationMenuItem[]
|
||||
props:
|
||||
arrow: true
|
||||
items:
|
||||
@@ -611,6 +625,8 @@ ignore:
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- NavigationMenuItem[]
|
||||
props:
|
||||
arrow: true
|
||||
contentOrientation: 'vertical'
|
||||
@@ -682,6 +698,8 @@ ignore:
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- NavigationMenuItem[]
|
||||
props:
|
||||
unmountOnHide: false
|
||||
items:
|
||||
|
||||
@@ -28,6 +28,9 @@ ignore:
|
||||
external:
|
||||
- items
|
||||
- modelValue
|
||||
externalTypes:
|
||||
- RadioGroupItem[]
|
||||
- RadioGroupValue
|
||||
props:
|
||||
modelValue: 'System'
|
||||
items:
|
||||
@@ -52,6 +55,9 @@ ignore:
|
||||
external:
|
||||
- items
|
||||
- modelValue
|
||||
externalTypes:
|
||||
- RadioGroupItem[]
|
||||
- RadioGroupValue
|
||||
props:
|
||||
modelValue: 'system'
|
||||
items:
|
||||
@@ -84,6 +90,9 @@ ignore:
|
||||
external:
|
||||
- items
|
||||
- modelValue
|
||||
externalTypes:
|
||||
- RadioGroupItem[]
|
||||
- RadioGroupValue
|
||||
props:
|
||||
modelValue: 'light'
|
||||
valueKey: 'id'
|
||||
@@ -112,6 +121,8 @@ ignore:
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- RadioGroupItem[]
|
||||
props:
|
||||
legend: 'Theme'
|
||||
defaultValue: 'System'
|
||||
@@ -134,6 +145,8 @@ ignore:
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- RadioGroupItem[]
|
||||
props:
|
||||
orientation: 'horizontal'
|
||||
defaultValue: 'System'
|
||||
@@ -156,6 +169,8 @@ ignore:
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- RadioGroupItem[]
|
||||
props:
|
||||
color: neutral
|
||||
defaultValue: 'System'
|
||||
@@ -178,6 +193,8 @@ ignore:
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- RadioGroupItem[]
|
||||
props:
|
||||
size: 'xl'
|
||||
defaultValue: 'System'
|
||||
@@ -200,6 +217,8 @@ ignore:
|
||||
- items
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- RadioGroupItem[]
|
||||
props:
|
||||
disabled: true
|
||||
defaultValue: 'System'
|
||||
|
||||
@@ -31,6 +31,8 @@ ignore:
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- StepperItem[]
|
||||
props:
|
||||
items:
|
||||
- title: 'Address'
|
||||
@@ -61,6 +63,8 @@ ignore:
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- StepperItem[]
|
||||
props:
|
||||
color: neutral
|
||||
items:
|
||||
@@ -88,6 +92,8 @@ ignore:
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- StepperItem[]
|
||||
props:
|
||||
size: xl
|
||||
items:
|
||||
@@ -115,6 +121,8 @@ ignore:
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- StepperItem[]
|
||||
props:
|
||||
orientation: vertical
|
||||
items:
|
||||
@@ -142,6 +150,8 @@ ignore:
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- StepperItem[]
|
||||
props:
|
||||
disabled: true
|
||||
items:
|
||||
|
||||
@@ -23,7 +23,7 @@ class: '!p-0'
|
||||
---
|
||||
::
|
||||
|
||||
::callout{icon="i-simple-icons-github" to="https://github.com/nuxt/ui/tree/v3/docs/app/components/content/examples/table/TableExample.vue"}
|
||||
::callout{icon="i-simple-icons-github" to="https://github.com/nuxt/ui/tree/v3/docs/app/components/content/examples/table/TableExample.vue" aria-label="View source code"}
|
||||
This example demonstrates the most common use case of the `Table` component. Check out the source code on GitHub.
|
||||
::
|
||||
|
||||
@@ -85,7 +85,7 @@ Use the `columns` prop as an array of [ColumnDef](https://tanstack.com/table/lat
|
||||
|
||||
In order to render components or other HTML elements, you will need to use the Vue [`h` function](https://vuejs.org/api/render-function.html#h) inside the `header` and `cell` props. This is different from other components that use slots but allows for more flexibility.
|
||||
|
||||
::tip{to="#with-slots"}
|
||||
::tip{to="#with-slots" aria-label="Table columns with slots"}
|
||||
You can also use slots to customize the header and data cells of the table.
|
||||
::
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@ ignore:
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- TabsItem[]
|
||||
props:
|
||||
items:
|
||||
- label: Account
|
||||
@@ -55,6 +57,8 @@ ignore:
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- TabsItem[]
|
||||
props:
|
||||
content: false
|
||||
items:
|
||||
@@ -80,6 +84,8 @@ ignore:
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- TabsItem[]
|
||||
props:
|
||||
unmountOnHide: false
|
||||
items:
|
||||
@@ -109,6 +115,8 @@ ignore:
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- TabsItem[]
|
||||
props:
|
||||
color: neutral
|
||||
content: false
|
||||
@@ -131,6 +139,8 @@ ignore:
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- TabsItem[]
|
||||
props:
|
||||
color: neutral
|
||||
variant: link
|
||||
@@ -154,6 +164,8 @@ ignore:
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- TabsItem[]
|
||||
props:
|
||||
size: md
|
||||
variant: pill
|
||||
@@ -177,6 +189,8 @@ ignore:
|
||||
- class
|
||||
external:
|
||||
- items
|
||||
externalTypes:
|
||||
- TabsItem[]
|
||||
props:
|
||||
orientation: vertical
|
||||
variant: pill
|
||||
|
||||
@@ -139,6 +139,7 @@ export default defineNuxtConfig({
|
||||
'/pro/components/pricing-grid': { redirect: { to: '/components/pricing-plans', statusCode: 301 }, prerender: false },
|
||||
'/pro/components/pricing-switch': { redirect: { to: '/components/switch', statusCode: 301 }, prerender: false },
|
||||
'/pro/components/**': { redirect: { to: '/components/**', statusCode: 301 }, prerender: false },
|
||||
'/getting-started/shortcuts': { redirect: { to: '/composables/define-shortcuts', statusCode: 301 }, prerender: false },
|
||||
'/releases': { redirect: 'https://github.com/nuxt/ui/releases', prerender: false }
|
||||
},
|
||||
|
||||
@@ -182,6 +183,10 @@ export default defineNuxtConfig({
|
||||
fs: {
|
||||
allow: process.env.NUXT_UI_PRO_PATH ? [resolve(process.env.NUXT_UI_PRO_PATH)] : undefined
|
||||
}
|
||||
},
|
||||
optimizeDeps: {
|
||||
// prevents reloading page when navigating between components
|
||||
include: ['@internationalized/date', '@vueuse/shared', '@vueuse/integrations/useFuse', '@tanstack/vue-table', 'reka-ui', 'reka-ui/namespaced', 'embla-carousel-vue', 'embla-carousel-autoplay', 'embla-carousel-auto-scroll', 'embla-carousel-auto-height', 'embla-carousel-class-names', 'embla-carousel-fade', 'embla-carousel-wheel-gestures', 'colortranslator', 'tailwindcss/colors', 'tailwind-variants', 'ufo', 'zod', 'vaul-vue', 'scule', 'motion-v', 'json5', 'ohash', 'shiki-transformer-color-highlight']
|
||||
}
|
||||
},
|
||||
|
||||
@@ -220,6 +225,7 @@ export default defineNuxtConfig({
|
||||
},
|
||||
|
||||
image: {
|
||||
format: ['webp', 'jpeg', 'jpg', 'png', 'svg'],
|
||||
provider: 'ipx'
|
||||
},
|
||||
|
||||
|
||||
@@ -4,24 +4,26 @@
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@iconify-json/logos": "^1.2.4",
|
||||
"@iconify-json/lucide": "^1.2.31",
|
||||
"@iconify-json/lucide": "^1.2.33",
|
||||
"@iconify-json/simple-icons": "^1.2.29",
|
||||
"@iconify-json/vscode-icons": "^1.2.16",
|
||||
"@iconify-json/vscode-icons": "^1.2.17",
|
||||
"@nuxt/content": "^3.4.0",
|
||||
"@nuxt/image": "^1.10.0",
|
||||
"@nuxt/ui": "latest",
|
||||
"@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@d96a086",
|
||||
"@nuxthub/core": "^0.8.18",
|
||||
"@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@63da8be",
|
||||
"@nuxthub/core": "^0.8.21",
|
||||
"@nuxtjs/plausible": "^1.2.0",
|
||||
"@octokit/rest": "^21.1.1",
|
||||
"@rollup/plugin-yaml": "^4.1.2",
|
||||
"@vueuse/nuxt": "^13.0.0",
|
||||
"@vueuse/integrations": "^13.0.0",
|
||||
"sortablejs": "^1.15.6",
|
||||
"joi": "^17.13.3",
|
||||
"motion-v": "0.13.1",
|
||||
"nuxt": "^3.16.1",
|
||||
"nuxt-component-meta": "^0.10.0",
|
||||
"nuxt-llms": "^0.1.0",
|
||||
"nuxt-og-image": "^5.0.5",
|
||||
"nuxt-component-meta": "^0.10.1",
|
||||
"nuxt-llms": "^0.1.2",
|
||||
"nuxt-og-image": "^5.1.1",
|
||||
"prettier": "^3.5.3",
|
||||
"shiki-transformer-color-highlight": "^1.0.0",
|
||||
"superstruct": "^2.0.2",
|
||||
@@ -31,6 +33,6 @@
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"wrangler": "^3.114.2"
|
||||
"wrangler": "^4.6.0"
|
||||
}
|
||||
}
|
||||
|
||||
61
package.json
61
package.json
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "@nuxt/ui",
|
||||
"description": "A UI Library for Modern Web Apps, powered by Vue & Tailwind CSS.",
|
||||
"version": "3.0.1",
|
||||
"packageManager": "pnpm@10.6.5",
|
||||
"version": "3.0.2",
|
||||
"packageManager": "pnpm@10.7.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/nuxt/ui.git"
|
||||
@@ -63,8 +63,9 @@
|
||||
"build": "nuxt-module-build build",
|
||||
"prepack": "pnpm build",
|
||||
"dev": "DEV=true nuxi dev playground",
|
||||
"dev:vue": "DEV=true vite playground-vue",
|
||||
"dev:build": "nuxi build playground",
|
||||
"dev:vue": "DEV=true vite playground-vue",
|
||||
"dev:vue:build": "vite build playground-vue",
|
||||
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground && nuxi prepare docs && vite build playground-vue",
|
||||
"docs": "DEV=true nuxi dev docs",
|
||||
"docs:build": "NODE_OPTIONS='--max-old-space-size=8192' nuxi build docs",
|
||||
@@ -74,7 +75,6 @@
|
||||
"typecheck": "vue-tsc --noEmit && nuxi typecheck playground && nuxi typecheck docs && cd playground-vue && vue-tsc --noEmit",
|
||||
"test": "vitest",
|
||||
"test:vue": "vitest -c vitest.vue.config.ts",
|
||||
"test:vue:build": "vite build playground-vue",
|
||||
"release": "release-it"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -86,10 +86,11 @@
|
||||
"@nuxt/kit": "^3.16.1",
|
||||
"@nuxt/schema": "^3.16.1",
|
||||
"@nuxtjs/color-mode": "^3.5.2",
|
||||
"@tailwindcss/postcss": "^4.0.15",
|
||||
"@tailwindcss/vite": "^4.0.15",
|
||||
"@standard-schema/spec": "^1.0.0",
|
||||
"@tailwindcss/postcss": "^4.0.17",
|
||||
"@tailwindcss/vite": "^4.0.17",
|
||||
"@tanstack/vue-table": "^8.21.2",
|
||||
"@unhead/vue": "^2.0.0-rc.13",
|
||||
"@unhead/vue": "^2.0.2",
|
||||
"@vueuse/core": "^13.0.0",
|
||||
"@vueuse/integrations": "^13.0.0",
|
||||
"colortranslator": "^4.1.0",
|
||||
@@ -103,18 +104,19 @@
|
||||
"embla-carousel-vue": "^8.5.2",
|
||||
"embla-carousel-wheel-gestures": "^8.0.1",
|
||||
"fuse.js": "^7.1.0",
|
||||
"hookable": "^5.5.3",
|
||||
"knitwork": "^1.2.0",
|
||||
"magic-string": "^0.30.17",
|
||||
"mlly": "^1.7.4",
|
||||
"ohash": "^2.0.11",
|
||||
"pathe": "^2.0.3",
|
||||
"reka-ui": "^2.1.0",
|
||||
"reka-ui": "^2.1.1",
|
||||
"scule": "^1.3.0",
|
||||
"tailwind-variants": "^1.0.0",
|
||||
"tailwindcss": "^4.0.15",
|
||||
"tailwindcss": "^4.0.17",
|
||||
"tinyglobby": "^0.2.12",
|
||||
"unplugin": "^2.2.1",
|
||||
"unplugin-auto-import": "^19.1.1",
|
||||
"unplugin": "^2.2.2",
|
||||
"unplugin-auto-import": "^19.1.2",
|
||||
"unplugin-vue-components": "^28.4.1",
|
||||
"vaul-vue": "^0.4.1",
|
||||
"vue": "^3.5.13",
|
||||
@@ -125,32 +127,47 @@
|
||||
"@nuxt/module-builder": "^0.8.4",
|
||||
"@nuxt/test-utils": "^3.17.2",
|
||||
"@release-it/conventional-changelog": "^10.0.0",
|
||||
"@standard-schema/spec": "^1.0.0",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"embla-carousel": "^8.5.2",
|
||||
"eslint": "^9.22.0",
|
||||
"eslint": "^9.23.0",
|
||||
"happy-dom": "^17.4.4",
|
||||
"joi": "^17.13.3",
|
||||
"nuxt": "^3.16.1",
|
||||
"release-it": "^18.1.2",
|
||||
"superstruct": "^2.0.2",
|
||||
"valibot": "^1.0.0",
|
||||
"vitest": "^3.0.9",
|
||||
"vitest-environment-nuxt": "^1.0.1",
|
||||
"vue-tsc": "^2.2.0",
|
||||
"yup": "^1.6.1",
|
||||
"zod": "^3.24.2"
|
||||
"vue-tsc": "^2.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.6.3"
|
||||
"joi": "^17.13.0",
|
||||
"superstruct": "^2.0.0",
|
||||
"typescript": "^5.6.3",
|
||||
"valibot": "^1.0.0",
|
||||
"yup": "^1.6.0",
|
||||
"zod": "^3.24.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"joi": {
|
||||
"optional": true
|
||||
},
|
||||
"valibot": {
|
||||
"optional": true
|
||||
},
|
||||
"superstruct": {
|
||||
"optional": true
|
||||
},
|
||||
"yup": {
|
||||
"optional": true
|
||||
},
|
||||
"zod": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"resolutions": {
|
||||
"@nuxt/ui": "workspace:*",
|
||||
"chokidar": "3.6.0",
|
||||
"debug": "4.3.7",
|
||||
"rollup": "4.34.9",
|
||||
"typescript": "5.6.3",
|
||||
"unplugin": "^2.2.1",
|
||||
"unplugin": "^2.2.2",
|
||||
"vue-tsc": "2.2.0"
|
||||
},
|
||||
"pnpm": {
|
||||
|
||||
@@ -12,12 +12,13 @@
|
||||
"dependencies": {
|
||||
"@nuxt/ui": "latest",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0"
|
||||
"vue-router": "^4.5.0",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
"typescript": "^5.6.3",
|
||||
"vite": "^6.2.2",
|
||||
"typescript": "^5.8.2",
|
||||
"vite": "^6.2.3",
|
||||
"vue-tsc": "^2.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ defineShortcuts({
|
||||
<UNavigationMenu :items="items" orientation="vertical" class="hidden lg:flex border-e border-(--ui-border) overflow-y-auto w-48 p-4" />
|
||||
<UNavigationMenu :items="items" orientation="horizontal" class="lg:hidden border-b border-(--ui-border) [&>div]:min-w-min overflow-x-auto" />
|
||||
|
||||
<div class="fixed top-15 lg:top-3 right-4 flex items-center gap-2">
|
||||
<div class="fixed top-15 lg:top-3 end-4 flex items-center gap-2">
|
||||
<UButton
|
||||
:icon="mode === 'dark' ? 'i-lucide-moon' : 'i-lucide-sun'"
|
||||
color="neutral"
|
||||
|
||||
@@ -29,6 +29,6 @@ export default defineConfig({
|
||||
],
|
||||
optimizeDeps: {
|
||||
// prevents reloading page when navigating between components
|
||||
include: ['reka-ui/namespaced', 'vaul-vue', 'embla-carousel-vue', 'embla-carousel-autoplay', 'embla-carousel-auto-scroll', 'embla-carousel-auto-height', 'embla-carousel-class-names', 'embla-carousel-fade', 'embla-carousel-wheel-gestures']
|
||||
include: ['@unhead/vue/client', '@iconify/vue', '@internationalized/date', '@vueuse/shared', '@vueuse/integrations/useFuse', '@tanstack/vue-table', 'reka-ui', 'reka-ui/namespaced', 'embla-carousel-vue', 'embla-carousel-autoplay', 'embla-carousel-auto-scroll', 'embla-carousel-auto-height', 'embla-carousel-class-names', 'embla-carousel-fade', 'embla-carousel-wheel-gestures', 'colortranslator', 'tailwindcss/colors', 'tailwind-variants', 'ufo', 'zod', 'vaul-vue']
|
||||
}
|
||||
})
|
||||
|
||||
@@ -80,6 +80,10 @@ function onSelect(item: any) {
|
||||
defineShortcuts({
|
||||
meta_k: () => isCommandPaletteOpen.value = true
|
||||
})
|
||||
|
||||
useHead({
|
||||
title: 'Nuxt UI - Playground'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -89,7 +93,7 @@ defineShortcuts({
|
||||
<UNavigationMenu :items="items" orientation="vertical" class="hidden lg:flex border-e border-(--ui-border) overflow-y-auto w-48 p-4" />
|
||||
<UNavigationMenu :items="items" orientation="horizontal" class="lg:hidden border-b border-(--ui-border) [&>div]:min-w-min overflow-x-auto" />
|
||||
|
||||
<div class="fixed top-15 lg:top-3 right-4 flex items-center gap-2">
|
||||
<div class="fixed top-15 lg:top-3 end-4 flex items-center gap-2">
|
||||
<ClientOnly v-if="!colorMode?.forced">
|
||||
<UButton
|
||||
:icon="isDark ? 'i-lucide-moon' : 'i-lucide-sun'"
|
||||
|
||||
@@ -18,6 +18,7 @@ const items = [{
|
||||
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
|
||||
}, {
|
||||
label: 'Components',
|
||||
slot: 'test' as const,
|
||||
icon: 'i-lucide-layers-3',
|
||||
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.'
|
||||
}, {
|
||||
@@ -37,6 +38,11 @@ const items = [{
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<template #custom="{ item }">
|
||||
<p class="text-(--ui-text-muted)">
|
||||
Custom: {{ item.content }}
|
||||
</p>
|
||||
</template>
|
||||
<template #custom-body="{ item }">
|
||||
<p class="text-(--ui-text-muted)">
|
||||
Custom: {{ item.content }}
|
||||
|
||||
@@ -150,10 +150,6 @@ defineShortcuts(extractShortcuts(items.value))
|
||||
|
||||
<UDropdownMenu :items="itemsWithColor" :size="size" arrow :content="{ side: 'bottom', align: 'start' }" :ui="{ content: 'w-48' }">
|
||||
<UButton label="Color" color="neutral" variant="outline" icon="i-lucide-menu" />
|
||||
|
||||
<template #custom-trailing>
|
||||
<UIcon name="i-lucide-badge-check" class="shrink-0 size-5 text-(--ui-primary)" />
|
||||
</template>
|
||||
</UDropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { InputMenuItem, AvatarProps } from '@nuxt/ui'
|
||||
|
||||
import { upperFirst } from 'scule'
|
||||
import { refDebounced } from '@vueuse/core'
|
||||
import type { User } from '~/types'
|
||||
@@ -10,7 +12,7 @@ const variants = Object.keys(theme.variants.variant) as Array<keyof typeof theme
|
||||
const fruits = ['Apple', 'Banana', 'Blueberry', 'Grapes', 'Pineapple']
|
||||
const vegetables = ['Aubergine', 'Broccoli', 'Carrot', 'Courgette', 'Leek']
|
||||
|
||||
const items = [[{ label: 'Fruits', type: 'label' }, ...fruits], [{ label: 'Vegetables', type: 'label' }, ...vegetables]]
|
||||
const items = [[{ label: 'Fruits', type: 'label' as const }, ...fruits], [{ label: 'Vegetables', type: 'label' as const }, ...vegetables]]
|
||||
const selectedItems = ref([fruits[0]!, vegetables[0]!])
|
||||
|
||||
const statuses = [{
|
||||
@@ -28,7 +30,7 @@ const statuses = [{
|
||||
}, {
|
||||
label: 'Canceled',
|
||||
icon: 'i-lucide-circle-x'
|
||||
}]
|
||||
}] satisfies InputMenuItem[]
|
||||
|
||||
const searchTerm = ref('')
|
||||
const searchTermDebounced = refDebounced(searchTerm, 200)
|
||||
@@ -126,7 +128,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
class="w-48"
|
||||
>
|
||||
<template #leading="{ modelValue, ui }">
|
||||
<UAvatar v-if="modelValue?.avatar" :size="ui.itemLeadingAvatarSize()" v-bind="modelValue.avatar" />
|
||||
<UAvatar v-if="modelValue?.avatar" :size="(ui.itemLeadingAvatarSize() as AvatarProps['size'])" v-bind="modelValue.avatar" />
|
||||
</template>
|
||||
</UInputMenu>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectMenuItem, AvatarProps } from '@nuxt/ui'
|
||||
|
||||
import { upperFirst } from 'scule'
|
||||
import { refDebounced } from '@vueuse/core'
|
||||
import theme from '#build/ui/select-menu'
|
||||
@@ -10,7 +12,7 @@ const variants = Object.keys(theme.variants.variant) as Array<keyof typeof theme
|
||||
const fruits = ['Apple', 'Banana', 'Blueberry', 'Grapes', 'Pineapple']
|
||||
const vegetables = ['Aubergine', 'Broccoli', 'Carrot', 'Courgette', 'Leek']
|
||||
|
||||
const items = [[{ label: 'Fruits', type: 'label' }, ...fruits], [{ label: 'Vegetables', type: 'label' }, ...vegetables]]
|
||||
const items = [[{ label: 'Fruits', type: 'label' }, ...fruits], [{ label: 'Vegetables', type: 'label' }, ...vegetables]] satisfies SelectMenuItem[][]
|
||||
const selectedItems = ref([fruits[0]!, vegetables[0]!])
|
||||
|
||||
const statuses = [{
|
||||
@@ -33,7 +35,7 @@ const statuses = [{
|
||||
label: 'Canceled',
|
||||
value: 'canceled',
|
||||
icon: 'i-lucide-circle-x'
|
||||
}]
|
||||
}] satisfies SelectMenuItem[]
|
||||
|
||||
const searchTerm = ref('')
|
||||
const searchTermDebounced = refDebounced(searchTerm, 200)
|
||||
@@ -41,7 +43,7 @@ const searchTermDebounced = refDebounced(searchTerm, 200)
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
params: { q: searchTermDebounced },
|
||||
transform: (data: User[]) => {
|
||||
return data?.map(user => ({ id: user.id, label: user.name, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || []
|
||||
return data?.map(user => ({ id: user.id, label: user.name, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } }))
|
||||
},
|
||||
lazy: true
|
||||
})
|
||||
@@ -122,7 +124,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
v-for="size in sizes"
|
||||
:key="size"
|
||||
v-model:search-term="searchTerm"
|
||||
:items="users || []"
|
||||
:items="users"
|
||||
:loading="status === 'pending'"
|
||||
ignore-filter
|
||||
icon="i-lucide-user"
|
||||
@@ -132,7 +134,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
||||
@update:open="searchTerm = ''"
|
||||
>
|
||||
<template #leading="{ modelValue, ui }">
|
||||
<UAvatar v-if="modelValue?.avatar" :size="ui.itemLeadingAvatarSize()" v-bind="modelValue.avatar" />
|
||||
<UAvatar v-if="modelValue?.avatar" :size="(ui.itemLeadingAvatarSize() as AvatarProps['size'])" v-bind="modelValue.avatar" />
|
||||
</template>
|
||||
</USelectMenu>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectItem, AvatarProps } from '@nuxt/ui'
|
||||
import { upperFirst } from 'scule'
|
||||
import theme from '#build/ui/select'
|
||||
import type { User } from '~/types'
|
||||
@@ -9,7 +10,7 @@ const variants = Object.keys(theme.variants.variant) as Array<keyof typeof theme
|
||||
const fruits = ['Apple', 'Banana', 'Blueberry', 'Grapes', 'Pineapple']
|
||||
const vegetables = ['Aubergine', 'Broccoli', 'Carrot', 'Courgette', 'Leek']
|
||||
|
||||
const items = [[{ label: 'Fruits', type: 'label' }, ...fruits], [{ label: 'Vegetables', type: 'label' }, ...vegetables]]
|
||||
const items = [[{ label: 'Fruits', type: 'label' as const }, ...fruits], [{ label: 'Vegetables', type: 'label' as const }, ...vegetables]]
|
||||
const selectedItems = ref([fruits[0]!, vegetables[0]!])
|
||||
|
||||
const statuses = [{
|
||||
@@ -32,7 +33,7 @@ const statuses = [{
|
||||
label: 'Canceled',
|
||||
value: 'canceled',
|
||||
icon: 'i-lucide-circle-x'
|
||||
}]
|
||||
}] satisfies SelectItem[]
|
||||
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
transform: (data: User[]) => {
|
||||
@@ -114,9 +115,10 @@ function getUserAvatar(value: string) {
|
||||
trailing-icon="i-lucide-chevrons-up-down"
|
||||
:size="size"
|
||||
class="w-48"
|
||||
value-key="value"
|
||||
>
|
||||
<template #leading="{ modelValue, ui }">
|
||||
<UIcon v-if="modelValue" :name="getStatusIcon(modelValue as string)" :class="ui.leadingIcon()" />
|
||||
<UIcon v-if="modelValue" :name="getStatusIcon(modelValue)" :class="ui.leadingIcon()" />
|
||||
</template>
|
||||
</USelect>
|
||||
</div>
|
||||
@@ -130,9 +132,10 @@ function getUserAvatar(value: string) {
|
||||
placeholder="Search users..."
|
||||
:size="size"
|
||||
class="w-48"
|
||||
value-key="value"
|
||||
>
|
||||
<template #leading="{ modelValue, ui }">
|
||||
<UAvatar v-if="modelValue" :size="ui.itemLeadingAvatarSize()" v-bind="getUserAvatar(modelValue as string)" />
|
||||
<UAvatar v-if="modelValue" :size="(ui.itemLeadingAvatarSize() as AvatarProps['size'])" v-bind="getUserAvatar(modelValue)" />
|
||||
</template>
|
||||
</USelect>
|
||||
</div>
|
||||
|
||||
@@ -11,22 +11,22 @@ const size = ref('md' as const)
|
||||
|
||||
const items = [
|
||||
{
|
||||
slot: 'address',
|
||||
slot: 'address' as const,
|
||||
title: 'Address',
|
||||
description: 'Add your address here',
|
||||
icon: 'i-lucide-house'
|
||||
}, {
|
||||
slot: 'shipping',
|
||||
slot: 'shipping' as const,
|
||||
title: 'Shipping',
|
||||
description: 'Set your preferred shipping method',
|
||||
icon: 'i-lucide-truck'
|
||||
}, {
|
||||
slot: 'payment',
|
||||
slot: 'payment' as const,
|
||||
title: 'Payment',
|
||||
description: 'Select your payment method',
|
||||
icon: 'i-lucide-credit-card'
|
||||
}, {
|
||||
slot: 'checkout',
|
||||
slot: 'checkout' as const,
|
||||
title: 'Checkout',
|
||||
description: 'Confirm your order'
|
||||
}
|
||||
@@ -50,27 +50,27 @@ const stepper = useTemplateRef('stepper')
|
||||
:orientation="orientation"
|
||||
:size="size"
|
||||
>
|
||||
<template #address>
|
||||
<template #address="{ item }">
|
||||
<Placeholder class="size-full min-h-60 min-w-60">
|
||||
Address
|
||||
{{ item.title }}
|
||||
</Placeholder>
|
||||
</template>
|
||||
|
||||
<template #shipping>
|
||||
<template #shipping="{ item }">
|
||||
<Placeholder class="size-full min-h-60 min-w-60">
|
||||
Shipping
|
||||
{{ item.title }}
|
||||
</Placeholder>
|
||||
</template>
|
||||
|
||||
<template #payment>
|
||||
<template #payment="{ item }">
|
||||
<Placeholder class="size-full min-h-60 min-w-60">
|
||||
Payment
|
||||
{{ item.title }}
|
||||
</Placeholder>
|
||||
</template>
|
||||
|
||||
<template #checkout>
|
||||
<template #checkout="{ item }">
|
||||
<Placeholder class="size-full min-h-60 min-w-60">
|
||||
Checkout
|
||||
{{ item.title }}
|
||||
</Placeholder>
|
||||
</template>
|
||||
</UStepper>
|
||||
|
||||
@@ -148,12 +148,12 @@ const columns: TableColumn<Payment>[] = [{
|
||||
header: ({ table }) => h(UCheckbox, {
|
||||
'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(),
|
||||
'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value),
|
||||
'ariaLabel': 'Select all'
|
||||
'aria-label': 'Select all'
|
||||
}),
|
||||
cell: ({ row }) => h(UCheckbox, {
|
||||
'modelValue': row.getIsSelected(),
|
||||
'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value),
|
||||
'ariaLabel': 'Select row'
|
||||
'aria-label': 'Select row'
|
||||
}),
|
||||
enableSorting: false,
|
||||
enableHiding: false
|
||||
@@ -251,15 +251,17 @@ const columns: TableColumn<Payment>[] = [{
|
||||
}]
|
||||
|
||||
return h('div', { class: 'text-right' }, h(UDropdownMenu, {
|
||||
content: {
|
||||
'content': {
|
||||
align: 'end'
|
||||
},
|
||||
items
|
||||
items,
|
||||
'aria-label': 'Actions dropdown'
|
||||
}, () => h(UButton, {
|
||||
icon: 'i-lucide-ellipsis-vertical',
|
||||
color: 'neutral',
|
||||
variant: 'ghost',
|
||||
class: 'ms-auto'
|
||||
'icon': 'i-lucide-ellipsis-vertical',
|
||||
'color': 'neutral',
|
||||
'variant': 'ghost',
|
||||
'class': 'ms-auto',
|
||||
'aria-label': 'Actions dropdown'
|
||||
})))
|
||||
}
|
||||
}]
|
||||
|
||||
@@ -41,8 +41,8 @@ const itemsWithMappedId = [
|
||||
{ id: 'id3', title: 'obiwan kenobi' }
|
||||
]
|
||||
|
||||
const modelValue = ref<TreeItem>()
|
||||
const modelValues = ref<TreeItem[]>([])
|
||||
const modelValue = ref<string>()
|
||||
const modelValues = ref<string[]>([])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -64,22 +64,14 @@ const modelValues = ref<TreeItem[]>([])
|
||||
|
||||
<!-- Typescript tests -->
|
||||
<template v-if="false">
|
||||
<!-- @vue-expect-error - multiple props should type modelValue to array. -->
|
||||
<UTree :model-value="modelValue" :items="items" multiple />
|
||||
<!-- @vue-expect-error - multiple props should type defaultValue to array. -->
|
||||
<UTree :default-value="modelValue" :items="items" multiple />
|
||||
<!-- @vue-expect-error - multiple props should type @update:modelValue to array. -->
|
||||
<UTree :items="items" multiple @update:model-value="(payload: TreeItem) => payload" />
|
||||
<!-- @vue-expect-error - default should type modelValue to single item. -->
|
||||
<UTree :model-value="modelValues" :items="items" />
|
||||
<!-- @vue-expect-error - default should type defaultValue to single item. -->
|
||||
<UTree :default-value="modelValues" :items="items" />
|
||||
<!-- @vue-expect-error - default should type @update:modelValue to single item. -->
|
||||
<UTree :items="items" @update:model-value="(payload: TreeItem[]) => payload" />
|
||||
<UTree :model-value="modelValues" :items="items" multiple />
|
||||
<UTree :default-value="modelValues" :items="items" multiple />
|
||||
<UTree :items="items" multiple @update:model-value="(payload) => payload" />
|
||||
<UTree :model-value="modelValue" :items="items" />
|
||||
<UTree :default-value="modelValue" :items="items" />
|
||||
<UTree :items="items" @update:model-value="(payload) => payload" />
|
||||
|
||||
<!-- @vue-expect-error - value key should type v-model. -->
|
||||
<UTree v-model="modelValue" :items="itemsWithMappedId" value-key="id" />
|
||||
<!-- @vue-expect-error - label key should type v-model. -->
|
||||
<UTree v-model="modelValue" :items="itemsWithMappedId" label-key="title" />
|
||||
</template>
|
||||
</div>
|
||||
|
||||
@@ -14,5 +14,12 @@ export default defineNuxtConfig({
|
||||
compatibilityVersion: 4
|
||||
},
|
||||
|
||||
compatibilityDate: '2024-07-09'
|
||||
compatibilityDate: '2024-07-09',
|
||||
|
||||
vite: {
|
||||
optimizeDeps: {
|
||||
// prevents reloading page when navigating between components
|
||||
include: ['@internationalized/date', '@vueuse/shared', '@vueuse/integrations/useFuse', '@tanstack/vue-table', 'reka-ui', 'reka-ui/namespaced', 'embla-carousel-vue', 'embla-carousel-autoplay', 'embla-carousel-auto-scroll', 'embla-carousel-auto-height', 'embla-carousel-class-names', 'embla-carousel-fade', 'embla-carousel-wheel-gestures', 'colortranslator', 'tailwindcss/colors', 'tailwind-variants', 'ufo', 'zod', 'vaul-vue']
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -8,10 +8,11 @@
|
||||
"generate": "nuxi generate"
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify-json/lucide": "^1.2.31",
|
||||
"@iconify-json/lucide": "^1.2.33",
|
||||
"@iconify-json/simple-icons": "^1.2.29",
|
||||
"@nuxt/ui": "latest",
|
||||
"@nuxthub/core": "^0.8.18",
|
||||
"nuxt": "^3.16.1"
|
||||
"@nuxthub/core": "^0.8.21",
|
||||
"nuxt": "^3.16.1",
|
||||
"zod": "^3.24.2"
|
||||
}
|
||||
}
|
||||
|
||||
2379
pnpm-lock.yaml
generated
2379
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,6 @@
|
||||
"enabled": true
|
||||
},
|
||||
"ignoreDeps": [
|
||||
"typescript",
|
||||
"vue-tsc"
|
||||
],
|
||||
"baseBranches": ["v2", "v3"],
|
||||
@@ -20,6 +19,9 @@
|
||||
"@tailwindcss/postcss",
|
||||
"@tailwindcss/vite"
|
||||
]
|
||||
}, {
|
||||
"matchDepTypes": ["peerDependencies"],
|
||||
"enabled": false
|
||||
}, {
|
||||
"matchDepTypes": ["resolutions"],
|
||||
"enabled": false
|
||||
|
||||
@@ -26,9 +26,10 @@ export interface AccordionItem {
|
||||
/** A unique value for the accordion item. Defaults to the index. */
|
||||
value?: string
|
||||
disabled?: boolean
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export interface AccordionProps<T> extends Pick<AccordionRootProps, 'collapsible' | 'defaultValue' | 'modelValue' | 'type' | 'disabled' | 'unmountOnHide'> {
|
||||
export interface AccordionProps<T extends AccordionItem = AccordionItem> extends Pick<AccordionRootProps, 'collapsible' | 'defaultValue' | 'modelValue' | 'type' | 'disabled' | 'unmountOnHide'> {
|
||||
/**
|
||||
* The element or component this component should render as.
|
||||
* @defaultValue 'div'
|
||||
@@ -52,15 +53,15 @@ export interface AccordionProps<T> extends Pick<AccordionRootProps, 'collapsible
|
||||
|
||||
export interface AccordionEmits extends AccordionRootEmits {}
|
||||
|
||||
type SlotProps<T> = (props: { item: T, index: number, open: boolean }) => any
|
||||
type SlotProps<T extends AccordionItem> = (props: { item: T, index: number, open: boolean }) => any
|
||||
|
||||
export type AccordionSlots<T extends { slot?: string }> = {
|
||||
export type AccordionSlots<T extends AccordionItem = AccordionItem> = {
|
||||
leading: SlotProps<T>
|
||||
default: SlotProps<T>
|
||||
trailing: SlotProps<T>
|
||||
content: SlotProps<T>
|
||||
body: SlotProps<T>
|
||||
} & DynamicSlots<T, SlotProps<T>>
|
||||
} & DynamicSlots<T, 'body', { index: number, open: boolean }>
|
||||
|
||||
</script>
|
||||
|
||||
@@ -92,7 +93,7 @@ const ui = computed(() => accordion({
|
||||
<template>
|
||||
<AccordionRoot v-bind="rootProps" :class="ui.root({ class: [props.class, props.ui?.root] })">
|
||||
<AccordionItem
|
||||
v-for="(item, index) in items"
|
||||
v-for="(item, index) in props.items"
|
||||
v-slot="{ open }"
|
||||
:key="index"
|
||||
:value="item.value || String(index)"
|
||||
@@ -115,10 +116,10 @@ const ui = computed(() => accordion({
|
||||
</AccordionTrigger>
|
||||
</AccordionHeader>
|
||||
|
||||
<AccordionContent v-if="item.content || !!slots.content || (item.slot && !!slots[item.slot]) || !!slots.body || (item.slot && !!slots[`${item.slot}-body`])" :class="ui.content({ class: props.ui?.content })">
|
||||
<slot :name="item.slot || 'content'" :item="item" :index="index" :open="open">
|
||||
<AccordionContent v-if="item.content || !!slots.content || (item.slot && !!slots[item.slot as keyof AccordionSlots<T>]) || !!slots.body || (item.slot && !!slots[`${item.slot}-body` as keyof AccordionSlots<T>])" :class="ui.content({ class: props.ui?.content })">
|
||||
<slot :name="((item.slot || 'content') as keyof AccordionSlots<T>)" :item="item" :index="index" :open="open">
|
||||
<div :class="ui.body({ class: props.ui?.body })">
|
||||
<slot :name="item.slot ? `${item.slot}-body`: 'body'" :item="item" :index="index" :open="open">
|
||||
<slot :name="((item.slot ? `${item.slot}-body`: 'body') as keyof AccordionSlots<T>)" :item="item" :index="index" :open="open">
|
||||
{{ item.content }}
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
@@ -71,7 +71,7 @@ export interface AlertSlots {
|
||||
title(props?: {}): any
|
||||
description(props?: {}): any
|
||||
actions(props?: {}): any
|
||||
close(props: { ui: any }): any
|
||||
close(props: { ui: ReturnType<typeof alert> }): any
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<script setup lang="ts" generic="T extends Messages = Messages">
|
||||
<script setup lang="ts" generic="T extends Messages">
|
||||
import { toRef, useId, provide } from 'vue'
|
||||
import { ConfigProvider, TooltipProvider, useForwardProps } from 'reka-ui'
|
||||
import { reactivePick } from '@vueuse/core'
|
||||
|
||||
@@ -40,7 +40,7 @@ export interface AvatarSlots {
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { Primitive } from 'reka-ui'
|
||||
import { Primitive, Slot } from 'reka-ui'
|
||||
import ImageComponent from '#build/ui-image-component'
|
||||
import { useAvatarGroup } from '../composables/useAvatarGroup'
|
||||
import UIcon from './Icon.vue'
|
||||
@@ -98,9 +98,11 @@ function onError() {
|
||||
@error="onError"
|
||||
/>
|
||||
|
||||
<slot v-else>
|
||||
<UIcon v-if="icon" :name="icon" :class="ui.icon({ class: props.ui?.icon })" />
|
||||
<span v-else :class="ui.fallback({ class: props.ui?.fallback })">{{ fallback || ' ' }}</span>
|
||||
</slot>
|
||||
<Slot v-else v-bind="$attrs">
|
||||
<slot>
|
||||
<UIcon v-if="icon" :name="icon" :class="ui.icon({ class: props.ui?.icon })" />
|
||||
<span v-else :class="ui.fallback({ class: props.ui?.fallback })">{{ fallback || ' ' }}</span>
|
||||
</slot>
|
||||
</Slot>
|
||||
</Primitive>
|
||||
</template>
|
||||
|
||||
@@ -19,9 +19,10 @@ export interface BreadcrumbItem extends Omit<LinkProps, 'raw' | 'custom'> {
|
||||
icon?: string
|
||||
avatar?: AvatarProps
|
||||
slot?: string
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export interface BreadcrumbProps<T> {
|
||||
export interface BreadcrumbProps<T extends BreadcrumbItem = BreadcrumbItem> {
|
||||
/**
|
||||
* The element or component this component should render as.
|
||||
* @defaultValue 'nav'
|
||||
@@ -43,15 +44,15 @@ export interface BreadcrumbProps<T> {
|
||||
ui?: PartialString<typeof breadcrumb.slots>
|
||||
}
|
||||
|
||||
type SlotProps<T> = (props: { item: T, index: number, active?: boolean }) => any
|
||||
type SlotProps<T extends BreadcrumbItem> = (props: { item: T, index: number, active?: boolean }) => any
|
||||
|
||||
export type BreadcrumbSlots<T extends { slot?: string }> = {
|
||||
export type BreadcrumbSlots<T extends BreadcrumbItem = BreadcrumbItem> = {
|
||||
'item': SlotProps<T>
|
||||
'item-leading': SlotProps<T>
|
||||
'item-label': SlotProps<T>
|
||||
'item-trailing': SlotProps<T>
|
||||
'separator'(props?: {}): any
|
||||
} & DynamicSlots<T, SlotProps<T>>
|
||||
'separator': any
|
||||
} & DynamicSlots<T, 'leading' | 'label' | 'trailing', { index: number, active?: boolean }>
|
||||
|
||||
</script>
|
||||
|
||||
@@ -88,19 +89,19 @@ const ui = breadcrumb()
|
||||
<li :class="ui.item({ class: props.ui?.item })">
|
||||
<ULink v-slot="{ active, ...slotProps }" v-bind="pickLinkProps(item)" custom>
|
||||
<ULinkBase v-bind="slotProps" as="span" :aria-current="active && (index === items!.length - 1) ? 'page' : undefined" :class="ui.link({ class: [props.ui?.link, item.class], active: index === items!.length - 1, disabled: !!item.disabled, to: !!item.to })">
|
||||
<slot :name="item.slot || 'item'" :item="item" :index="index">
|
||||
<slot :name="item.slot ? `${item.slot}-leading`: 'item-leading'" :item="item" :active="index === items!.length - 1" :index="index">
|
||||
<slot :name="((item.slot || 'item') as keyof BreadcrumbSlots<T>)" :item="item" :index="index">
|
||||
<slot :name="((item.slot ? `${item.slot}-leading`: 'item-leading') as keyof BreadcrumbSlots<T>)" :item="item" :active="index === items!.length - 1" :index="index">
|
||||
<UIcon v-if="item.icon" :name="item.icon" :class="ui.linkLeadingIcon({ class: props.ui?.linkLeadingIcon, active: index === items!.length - 1 })" />
|
||||
<UAvatar v-else-if="item.avatar" :size="((props.ui?.linkLeadingAvatarSize || ui.linkLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.linkLeadingAvatar({ class: props.ui?.linkLeadingAvatar, active: index === items!.length - 1 })" />
|
||||
</slot>
|
||||
|
||||
<span v-if="get(item, props.labelKey as string) || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.linkLabel({ class: props.ui?.linkLabel })">
|
||||
<slot :name="item.slot ? `${item.slot}-label`: 'item-label'" :item="item" :active="index === items!.length - 1" :index="index">
|
||||
<span v-if="get(item, props.labelKey as string) || !!slots[(item.slot ? `${item.slot}-label`: 'item-label') as keyof BreadcrumbSlots<T>]" :class="ui.linkLabel({ class: props.ui?.linkLabel })">
|
||||
<slot :name="((item.slot ? `${item.slot}-label`: 'item-label') as keyof BreadcrumbSlots<T>)" :item="item" :active="index === items!.length - 1" :index="index">
|
||||
{{ get(item, props.labelKey as string) }}
|
||||
</slot>
|
||||
</span>
|
||||
|
||||
<slot :name="item.slot ? `${item.slot}-trailing`: 'item-trailing'" :item="item" :active="index === items!.length - 1" :index="index" />
|
||||
<slot :name="((item.slot ? `${item.slot}-trailing`: 'item-trailing') as keyof BreadcrumbSlots<T>)" :item="item" :active="index === items!.length - 1" :index="index" />
|
||||
</slot>
|
||||
</ULinkBase>
|
||||
</ULink>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user