mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-24 17:00:36 +01:00
Compare commits
73 Commits
v3.0.0-alp
...
v3.0.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68f0269046 | ||
|
|
47d9955ed9 | ||
|
|
6386a4d99a | ||
|
|
7687ac16fd | ||
|
|
90a775bab9 | ||
|
|
efeb3f9cfa | ||
|
|
b54950e3ed | ||
|
|
34bddd45be | ||
|
|
69d7b57825 | ||
|
|
92873e05ba | ||
|
|
7870288367 | ||
|
|
9d3d5db376 | ||
|
|
090fe16cff | ||
|
|
44ebb35953 | ||
|
|
5000a4e0d5 | ||
|
|
5385944359 | ||
|
|
7802aacf3f | ||
|
|
f59844bb61 | ||
|
|
9359603a0a | ||
|
|
d407c42be7 | ||
|
|
8a06981df2 | ||
|
|
a68016ec5d | ||
|
|
973023a04e | ||
|
|
f6789a156c | ||
|
|
61b232377b | ||
|
|
eb1b30db40 | ||
|
|
b975235c8b | ||
|
|
81a59969f6 | ||
|
|
dc8cd1e664 | ||
|
|
c63920d05b | ||
|
|
37171b9327 | ||
|
|
49abad243c | ||
|
|
c1294f6505 | ||
|
|
53a3796d1b | ||
|
|
df2013ca92 | ||
|
|
0666884b6f | ||
|
|
a54c3e49fe | ||
|
|
716ed10068 | ||
|
|
e137577a72 | ||
|
|
0f9349f920 | ||
|
|
e6143e8600 | ||
|
|
b9380c15ab | ||
|
|
0b7f171268 | ||
|
|
490fb1377b | ||
|
|
8ef6e712ac | ||
|
|
0759e29c22 | ||
|
|
5385f84e0a | ||
|
|
46bd1cb002 | ||
|
|
8258cd3829 | ||
|
|
16b48efa96 | ||
|
|
b39c4d127e | ||
|
|
6af276ef38 | ||
|
|
67fe33f820 | ||
|
|
9e8b9dcc62 | ||
|
|
e9affb6f5b | ||
|
|
6e9f6a8ef4 | ||
|
|
dcce571cda | ||
|
|
ea07dffdd5 | ||
|
|
acfc6cef2d | ||
|
|
f6f9823b15 | ||
|
|
296ae456c9 | ||
|
|
f6631ff7bc | ||
|
|
bcfa4b74a9 | ||
|
|
1a7af6d182 | ||
|
|
558871a46a | ||
|
|
c8c17490ab | ||
|
|
9e03da41b3 | ||
|
|
a2bad2eee2 | ||
|
|
7c21ddefa8 | ||
|
|
0f9ac8733e | ||
|
|
365bc0fc9a | ||
|
|
c34a805e5f | ||
|
|
b4ffcedd2e |
65
.github/ISSUE_TEMPLATE/bug-v3.yml
vendored
Normal file
65
.github/ISSUE_TEMPLATE/bug-v3.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
name: "🐛 Bug report (v3)"
|
||||||
|
description: Report a bug to help us improve the module (v3 only).
|
||||||
|
labels: ["triage", "bug", "v3"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> As Nuxt UI v3 is currently in alpha, we recommend thorough testing before using it in production environments. We're actively working on stabilization and welcome feedback from early adopters to improve the library.
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Before reporting a bug, please make sure that you have read through our [v3 documentation](https://ui3.nuxt.dev/) and existing [issues](https://github.com/nuxt/ui/issues?q=is%3Aissue%20is%3Aopen%20sort%3Aupdated-desc%20label%3Av3).
|
||||||
|
- type: textarea
|
||||||
|
id: env
|
||||||
|
attributes:
|
||||||
|
label: Environment
|
||||||
|
description: You can use `npx nuxi info` to fill this section
|
||||||
|
placeholder: |
|
||||||
|
- Operating System: `Darwin`
|
||||||
|
- Node Version: `v18.16.0`
|
||||||
|
- Nuxt Version: `3.7.3`
|
||||||
|
- CLI Version: `3.8.4`
|
||||||
|
- Nitro Version: `2.6.3`
|
||||||
|
- Package Manager: `pnpm@8.7.4`
|
||||||
|
- Builder: `-`
|
||||||
|
- User Config: `-`
|
||||||
|
- Runtime Modules: `-`
|
||||||
|
- Build Modules: `-`
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: Version
|
||||||
|
placeholder: v3.0.0-alpha.5
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: reproduction
|
||||||
|
attributes:
|
||||||
|
label: Reproduction
|
||||||
|
description: Please provide a reproduction link. A minimal [reproduction is required](https://antfu.me/posts/why-reproductions-are-required) unless you are absolutely sure that the issue is obvious and the provided information is enough to understand the problem. If a report is vague (e.g. just a generic error message) and has no reproduction, it will receive a "needs reproduction" label. If no reproduction is provided we might close it.
|
||||||
|
placeholder: https://github.com/my/reproduction
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Description
|
||||||
|
description: A clear and concise description of what the bug is. If you intend to submit a PR for this issue, tell us in the description.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: additonal
|
||||||
|
attributes:
|
||||||
|
label: Additional context
|
||||||
|
description: If applicable, add any other context or screenshots here.
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: Logs
|
||||||
|
description: |
|
||||||
|
Optional if provided reproduction. Please try not to insert an image but copy paste the log text.
|
||||||
|
render: shell-script
|
||||||
9
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
9
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@@ -6,6 +6,15 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
Before requesting a feature, please make sure that you have read through our [documentation](https://ui.nuxt.com) and existing [issues](https://github.com/nuxt/ui/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc).
|
Before requesting a feature, please make sure that you have read through our [documentation](https://ui.nuxt.com) and existing [issues](https://github.com/nuxt/ui/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc).
|
||||||
|
- type: dropdown
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: For what version of Nuxt UI are you suggesting this?
|
||||||
|
options:
|
||||||
|
- v2.x
|
||||||
|
- v3-alpha
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: description
|
id: description
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
9
.github/ISSUE_TEMPLATE/question.yml
vendored
9
.github/ISSUE_TEMPLATE/question.yml
vendored
@@ -6,6 +6,15 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
Before asking a question, please make sure that you have read through our [documentation](https://ui.nuxt.com) and existing [issues](https://github.com/nuxt/ui/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc).
|
Before asking a question, please make sure that you have read through our [documentation](https://ui.nuxt.com) and existing [issues](https://github.com/nuxt/ui/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc).
|
||||||
|
- type: dropdown
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: For what version of Nuxt UI are you asking this question?
|
||||||
|
options:
|
||||||
|
- v2.x
|
||||||
|
- v3-alpha
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: description
|
id: description
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
4
.github/workflows/ci-v3.yml
vendored
4
.github/workflows/ci-v3.yml
vendored
@@ -49,6 +49,10 @@ jobs:
|
|||||||
- name: Typecheck
|
- name: Typecheck
|
||||||
run: pnpm run typecheck
|
run: pnpm run typecheck
|
||||||
|
|
||||||
|
- name: Docs typecheck
|
||||||
|
run: pnpm run docs:typecheck
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: pnpm run test
|
run: pnpm run test
|
||||||
|
|
||||||
|
|||||||
35
CHANGELOG.md
35
CHANGELOG.md
@@ -1,5 +1,40 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [3.0.0-alpha.7](https://github.com/nuxt/ui/compare/v3.0.0-alpha.6...v3.0.0-alpha.7) (2024-10-23)
|
||||||
|
|
||||||
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
|
* **components:** rename `select` to `onSelect` on items
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Accordion/Breadcrumb/CommandPalette/ContextMenu/DropdownMenu/NavigationMenu/Tabs:** add `labelKey` prop ([acfc6ce](https://github.com/nuxt/ui/commit/acfc6cef2db88774749d38a98416fdd85922d513))
|
||||||
|
* **Button:** handle `avatar` prop ([a54c3e4](https://github.com/nuxt/ui/commit/a54c3e49fe782e329f9245e496c336143e3e4b23))
|
||||||
|
* **CommandPalette:** handle `loading` field in items ([49abad2](https://github.com/nuxt/ui/commit/49abad243cee97b99753e2500c4bdaa0efe5eb75))
|
||||||
|
* **ContextMenu/DropdownMenu:** handle `checkbox` items type ([8ef6e71](https://github.com/nuxt/ui/commit/8ef6e712acbb2fc026eb35cefa8e29fc0b59d70f)), closes [#2144](https://github.com/nuxt/ui/issues/2144)
|
||||||
|
* **ContextMenu/DropdownMenu:** handle `loading` field in items ([b975235](https://github.com/nuxt/ui/commit/b975235c8b8e693a32efd3fd5381eed88fa3db4d))
|
||||||
|
* **Form:** add `superstruct` validation ([#2363](https://github.com/nuxt/ui/issues/2363)) ([5385944](https://github.com/nuxt/ui/commit/53859443593b584f7cd44106021e80f441e9ca06))
|
||||||
|
* **Input/InputMenu/Select/SelectMenu:** handle `avatar` prop ([53a3796](https://github.com/nuxt/ui/commit/53a3796d1b08717a589028f99fc01084df661708))
|
||||||
|
* **InputMenu/RadioGroup/Select/SelectMenu:** handle `labelKey` and use `get` to support dot notation ([f6f9823](https://github.com/nuxt/ui/commit/f6f9823b15d84362d093703cb15ecba64c73c2c2))
|
||||||
|
* **NavigationMenu:** handle children on `vertical` orientation ([#2384](https://github.com/nuxt/ui/issues/2384)) ([34bddd4](https://github.com/nuxt/ui/commit/34bddd45be2ba1d51ddb9b6b40860f2414f63180))
|
||||||
|
* **Table:** implement component ([#2364](https://github.com/nuxt/ui/issues/2364)) ([b54950e](https://github.com/nuxt/ui/commit/b54950e3ed77a466eb048788757a76018638eafa))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **AvatarGroup:** wrong ring on big sizes ([61b2323](https://github.com/nuxt/ui/commit/61b232377b4b1fb41de30fd33e690a36b36ba575))
|
||||||
|
* **Button:** invalid hover on `link` variant ([df2013c](https://github.com/nuxt/ui/commit/df2013ca92a49b5947e2fbc2641fd92860c32042))
|
||||||
|
* **Checkbox:** `indeterminate` prop not working ([f6631ff](https://github.com/nuxt/ui/commit/f6631ff7bc607e140e9db2c7335c409a811820e4))
|
||||||
|
* **components:** rename `select` to `onSelect` on items ([b39c4d1](https://github.com/nuxt/ui/commit/b39c4d127e0ddf7607e868ecc83930ca49436bad))
|
||||||
|
* **css:** `font-sans` already applied on <html> ([9e03da4](https://github.com/nuxt/ui/commit/9e03da41b3537236864ae2a533c47e99a6270b77))
|
||||||
|
* **css:** make `[@theme](https://github.com/theme)` default ([a2bad2e](https://github.com/nuxt/ui/commit/a2bad2eee2d2a9255152692898078d26e9ecad98))
|
||||||
|
* **Drawer/Modal/Slideover:** no need for `z-index` since its isolated ([bcfa4b7](https://github.com/nuxt/ui/commit/bcfa4b74a9713be764ecb6db93d60d1360e52f07)), closes [nuxt/ui#2347](https://github.com/nuxt/ui/issues/2347)
|
||||||
|
* **Input/InputMenu/Select/SelectMenu:** uniformize placeholder color ([f59844b](https://github.com/nuxt/ui/commit/f59844bb617f50ef78ae5abe250b0744d7341a2f))
|
||||||
|
* **InputMenu/SelectMenu:** escape regexp before search ([7c21dde](https://github.com/nuxt/ui/commit/7c21ddefa87bf3d9999c0e790b48c004c078304d))
|
||||||
|
* **InputMenu/SelectMenu:** improve displayed value ([0f9ac87](https://github.com/nuxt/ui/commit/0f9ac8733e402d1f22a3eb6c1e24a8d5607b3572)), closes [nuxt/ui#2353](https://github.com/nuxt/ui/issues/2353)
|
||||||
|
* **InputMenu:** emit `focus` event ([#2386](https://github.com/nuxt/ui/issues/2386)) ([7802aac](https://github.com/nuxt/ui/commit/7802aacf3f5be572dd64c3288196432a41f06b0e))
|
||||||
|
* **module:** stop using tailwind's shorthand arbitrary variable syntax ([#2366](https://github.com/nuxt/ui/issues/2366)) ([dcce571](https://github.com/nuxt/ui/commit/dcce571cdab08de8408c8ba6b236b051eec3a603))
|
||||||
|
* **Slideover:** set max height on `top` / `bottom` positions ([a68016e](https://github.com/nuxt/ui/commit/a68016ec5d6859e892c90333d35fd7db09fdcf10)), closes [nuxt/ui#2388](https://github.com/nuxt/ui/issues/2388)
|
||||||
|
|
||||||
## [3.0.0-alpha.6](https://github.com/nuxt/ui/compare/v3.0.0-alpha.5...v3.0.0-alpha.6) (2024-10-09)
|
## [3.0.0-alpha.6](https://github.com/nuxt/ui/compare/v3.0.0-alpha.5...v3.0.0-alpha.6) (2024-10-09)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<UBanner icon="i-heroicons-wrench-screwdriver" :actions="[{ label: 'Go to Nuxt UI v2', to: 'https://ui.nuxt.com', trailingIcon: 'i-heroicons-arrow-right-20-solid', class: 'rounded-full' }]" :close="false">
|
<UBanner icon="i-heroicons-wrench-screwdriver" :actions="[{ label: 'Go to Nuxt UI v2', to: 'https://ui.nuxt.com', trailingIcon: 'i-heroicons-arrow-right-20-solid' }]" :close="false">
|
||||||
<template #title>
|
<template #title>
|
||||||
You're looking at the documentation for <span class="font-semibold">Nuxt UI v3</span>!
|
You're looking at the documentation for <span class="font-semibold">Nuxt UI v3</span>!
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -23,11 +23,11 @@ const route = useRoute()
|
|||||||
|
|
||||||
<UFooter>
|
<UFooter>
|
||||||
<template #left>
|
<template #left>
|
||||||
<NuxtLink v-if="route.path.startsWith('/pro')" to="https://ui.nuxt.com/pro/purchase" target="_blank" class="text-sm text-[--ui-text-muted]">
|
<NuxtLink v-if="route.path.startsWith('/pro')" to="https://ui.nuxt.com/pro/purchase" target="_blank" class="text-sm text-[var(--ui-text-muted)]">
|
||||||
Purchase <span class="text-[--ui-text-highlighted]">Nuxt UI Pro</span>
|
Purchase <span class="text-[var(--ui-text-highlighted)]">Nuxt UI Pro</span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<NuxtLink v-else to="https://github.com/nuxt/ui" target="_blank" class="text-sm text-[--ui-text-muted]">
|
<NuxtLink v-else to="https://github.com/nuxt/ui" target="_blank" class="text-sm text-[var(--ui-text-muted)]">
|
||||||
Published under <span class="text-[--ui-text-highlighted]">MIT License</span>
|
Published under <span class="text-[var(--ui-text-highlighted)]">MIT License</span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ defineShortcuts({
|
|||||||
<template>
|
<template>
|
||||||
<UHeader :ui="{ left: 'min-w-0' }">
|
<UHeader :ui="{ left: 'min-w-0' }">
|
||||||
<template #left>
|
<template #left>
|
||||||
<NuxtLink to="/" class="flex items-end gap-2 font-bold text-xl text-[--ui-text-highlighted] min-w-0" aria-label="Nuxt UI">
|
<NuxtLink to="/" class="flex items-end gap-2 font-bold text-xl text-[var(--ui-text-highlighted)] min-w-0 focus-visible:outline-[var(--ui-primary)]" aria-label="Nuxt UI">
|
||||||
<Logo class="w-auto h-6 shrink-0" />
|
<Logo class="w-auto h-6 shrink-0" />
|
||||||
|
|
||||||
<UBadge :label="`v${config.version}`" variant="subtle" size="sm" class="-mb-[2px] rounded font-semibold inline-block truncate" />
|
<UBadge :label="`v${config.version}`" variant="subtle" size="sm" class="-mb-[2px] rounded-[var(--ui-radius)] font-semibold inline-block truncate" />
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ const props = defineProps<{
|
|||||||
* @defaultValue false
|
* @defaultValue false
|
||||||
*/
|
*/
|
||||||
collapse?: boolean
|
collapse?: boolean
|
||||||
|
/**
|
||||||
|
* A list of line numbers to highlight in the code block
|
||||||
|
*/
|
||||||
|
highlights?: number[]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -105,7 +109,7 @@ const code = computed(() => {
|
|||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
code += `\`\`\`vue`
|
code += `\`\`\`vue${props.highlights?.length ? ` {${props.highlights.join('-')}}` : ''}`
|
||||||
|
|
||||||
if (props.external?.length) {
|
if (props.external?.length) {
|
||||||
code += `
|
code += `
|
||||||
@@ -201,7 +205,8 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
|
|||||||
formatted = await $prettier.format(code.value, {
|
formatted = await $prettier.format(code.value, {
|
||||||
trailingComma: 'none',
|
trailingComma: 'none',
|
||||||
semi: false,
|
semi: false,
|
||||||
singleQuote: true
|
singleQuote: true,
|
||||||
|
printWidth: 100
|
||||||
})
|
})
|
||||||
} catch {
|
} catch {
|
||||||
formatted = code.value
|
formatted = code.value
|
||||||
@@ -214,15 +219,15 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
|
|||||||
<template>
|
<template>
|
||||||
<div class="my-5">
|
<div class="my-5">
|
||||||
<div>
|
<div>
|
||||||
<div v-if="options.length" class="flex items-center gap-2.5 border border-[--ui-color-neutral-200] dark:border-[--ui-color-neutral-700] border-b-0 relative rounded-t-[calc(var(--ui-radius)*1.5)] px-4 py-2.5 overflow-x-auto">
|
<div v-if="options.length" class="flex items-center gap-2.5 border border-[var(--ui-color-neutral-200)] dark:border-[var(--ui-color-neutral-700)] border-b-0 relative rounded-t-[calc(var(--ui-radius)*1.5)] px-4 py-2.5 overflow-x-auto">
|
||||||
<template v-for="option in options" :key="option.name">
|
<template v-for="option in options" :key="option.name">
|
||||||
<UFormField
|
<UFormField
|
||||||
:label="option.label"
|
:label="option.label"
|
||||||
size="sm"
|
size="sm"
|
||||||
class="inline-flex ring ring-[--ui-border-accented] rounded"
|
class="inline-flex ring ring-[var(--ui-border-accented)] rounded-[var(--ui-radius)]"
|
||||||
:ui="{
|
:ui="{
|
||||||
wrapper: 'bg-[--ui-bg-elevated]/50 rounded-l flex border-r border-[--ui-border-accented]',
|
wrapper: 'bg-[var(--ui-bg-elevated)]/50 rounded-l-[var(--ui-radius)] flex border-r border-[var(--ui-border-accented)]',
|
||||||
label: 'text-[--ui-text-muted] px-2 py-1.5',
|
label: 'text-[var(--ui-text-muted)] px-2 py-1.5',
|
||||||
container: 'mt-0'
|
container: 'mt-0'
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
@@ -233,7 +238,7 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
|
|||||||
value-key="value"
|
value-key="value"
|
||||||
color="neutral"
|
color="neutral"
|
||||||
variant="soft"
|
variant="soft"
|
||||||
class="rounded rounded-l-none min-w-12"
|
class="rounded-[var(--ui-radius)] rounded-l-none min-w-12"
|
||||||
:search-input="false"
|
:search-input="false"
|
||||||
:class="[option.name.toLowerCase().endsWith('color') && 'pl-6']"
|
:class="[option.name.toLowerCase().endsWith('color') && 'pl-6']"
|
||||||
:ui="{ itemLeadingChip: 'size-2' }"
|
:ui="{ itemLeadingChip: 'size-2' }"
|
||||||
@@ -256,14 +261,14 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
|
|||||||
:model-value="getComponentProp(option.name)"
|
:model-value="getComponentProp(option.name)"
|
||||||
color="neutral"
|
color="neutral"
|
||||||
variant="soft"
|
variant="soft"
|
||||||
:ui="{ base: 'rounded rounded-l-none min-w-12' }"
|
:ui="{ base: 'rounded-[var(--ui-radius)] rounded-l-none min-w-12' }"
|
||||||
@update:model-value="setComponentProp(option.name, $event)"
|
@update:model-value="setComponentProp(option.name, $event)"
|
||||||
/>
|
/>
|
||||||
</UFormField>
|
</UFormField>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-center border border-b-0 border-[--ui-color-neutral-200] dark:border-[--ui-color-neutral-700] relative p-4 z-[1]" :class="[!options.length && 'rounded-t-[calc(var(--ui-radius)*1.5)]', props.class]">
|
<div class="flex justify-center border border-b-0 border-[var(--ui-color-neutral-200)] dark:border-[var(--ui-color-neutral-700)] relative p-4 z-[1]" :class="[!options.length && 'rounded-t-[calc(var(--ui-radius)*1.5)]', props.class]">
|
||||||
<component :is="name" v-bind="{ ...componentProps, ...componentEvents }">
|
<component :is="name" v-bind="{ ...componentProps, ...componentEvents }">
|
||||||
<template v-for="slot in Object.keys(slots || {})" :key="slot" #[slot]>
|
<template v-for="slot in Object.keys(slots || {})" :key="slot" #[slot]>
|
||||||
<ContentSlot :name="slot" unwrap="p">
|
<ContentSlot :name="slot" unwrap="p">
|
||||||
|
|||||||
@@ -22,13 +22,11 @@ const props = withDefaults(defineProps<{
|
|||||||
* @defaultValue true
|
* @defaultValue true
|
||||||
*/
|
*/
|
||||||
preview?: boolean
|
preview?: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to show the source code
|
* Whether to show the source code
|
||||||
* @defaultValue true
|
* @defaultValue true
|
||||||
*/
|
*/
|
||||||
source?: boolean
|
source?: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of variable props to link to the component.
|
* A list of variable props to link to the component.
|
||||||
*/
|
*/
|
||||||
@@ -40,6 +38,10 @@ const props = withDefaults(defineProps<{
|
|||||||
default: any
|
default: any
|
||||||
multiple?: boolean
|
multiple?: boolean
|
||||||
}>
|
}>
|
||||||
|
/**
|
||||||
|
* A list of line numbers to highlight in the code block
|
||||||
|
*/
|
||||||
|
highlights?: number[]
|
||||||
}>(), {
|
}>(), {
|
||||||
preview: true,
|
preview: true,
|
||||||
source: true
|
source: true
|
||||||
@@ -65,7 +67,7 @@ const code = computed(() => {
|
|||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
code += `\`\`\`vue${props.preview ? '' : ` [${data.pascalName}.vue]`}
|
code += `\`\`\`vue ${props.preview ? '' : ` [${data.pascalName}.vue]`}${props.highlights?.length ? `{${props.highlights.join('-')}}` : ''}
|
||||||
${data?.code ?? ''}
|
${data?.code ?? ''}
|
||||||
\`\`\``
|
\`\`\``
|
||||||
|
|
||||||
@@ -87,7 +89,8 @@ const { data: ast } = await useAsyncData(`component-example-${camelName}`, async
|
|||||||
formatted = await $prettier.format(code.value, {
|
formatted = await $prettier.format(code.value, {
|
||||||
trailingComma: 'none',
|
trailingComma: 'none',
|
||||||
semi: false,
|
semi: false,
|
||||||
singleQuote: true
|
singleQuote: true,
|
||||||
|
printWidth: 100
|
||||||
})
|
})
|
||||||
} catch {
|
} catch {
|
||||||
formatted = code.value
|
formatted = code.value
|
||||||
@@ -113,9 +116,9 @@ const optionsValues = ref(props.options?.reduce((acc, option) => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="my-5">
|
<div class="my-5">
|
||||||
<div v-if="preview">
|
<template v-if="preview">
|
||||||
<div class="border border-[--ui-color-neutral-200] dark:border-[--ui-color-neutral-700] relative z-[1]" :class="[{ 'border-b-0 rounded-t-[calc(var(--ui-radius)*1.5)]': props.source, 'rounded-[calc(var(--ui-radius)*1.5)]': !props.source }]">
|
<div class="border border-[var(--ui-color-neutral-200)] dark:border-[var(--ui-color-neutral-700)] relative z-[1]" :class="[{ 'border-b-0 rounded-t-[calc(var(--ui-radius)*1.5)]': props.source, 'rounded-[calc(var(--ui-radius)*1.5)]': !props.source }]">
|
||||||
<div v-if="props.options?.length || !!slots.options" class="flex gap-4 p-4 border-b border-[--ui-color-neutral-200] dark:border-[--ui-color-neutral-700]">
|
<div v-if="props.options?.length || !!slots.options" class="flex gap-4 p-4 border-b border-[var(--ui-color-neutral-200)] dark:border-[var(--ui-color-neutral-700)]">
|
||||||
<slot name="options" />
|
<slot name="options" />
|
||||||
|
|
||||||
<UFormField
|
<UFormField
|
||||||
@@ -124,10 +127,10 @@ const optionsValues = ref(props.options?.reduce((acc, option) => {
|
|||||||
:label="option.label"
|
:label="option.label"
|
||||||
:name="option.name"
|
:name="option.name"
|
||||||
size="sm"
|
size="sm"
|
||||||
class="inline-flex ring ring-[--ui-border-accented] rounded"
|
class="inline-flex ring ring-[var(--ui-border-accented)] rounded-[var(--ui-radius)]"
|
||||||
:ui="{
|
:ui="{
|
||||||
wrapper: 'bg-[--ui-bg-elevated]/50 rounded-l flex border-r border-[--ui-border-accented]',
|
wrapper: 'bg-[var(--ui-bg-elevated)]/50 rounded-l-[var(--ui-radius)] flex border-r border-[var(--ui-border-accented)]',
|
||||||
label: 'text-[--ui-text-muted] px-2 py-1.5',
|
label: 'text-[var(--ui-text-muted)] px-2 py-1.5',
|
||||||
container: 'mt-0'
|
container: 'mt-0'
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
@@ -139,7 +142,7 @@ const optionsValues = ref(props.options?.reduce((acc, option) => {
|
|||||||
:value-key="option.name.toLowerCase().endsWith('color') ? 'value' : undefined"
|
:value-key="option.name.toLowerCase().endsWith('color') ? 'value' : undefined"
|
||||||
color="neutral"
|
color="neutral"
|
||||||
variant="soft"
|
variant="soft"
|
||||||
class="rounded rounded-l-none min-w-12"
|
class="rounded-[var(--ui-radius)] rounded-l-none min-w-12"
|
||||||
:multiple="option.multiple"
|
:multiple="option.multiple"
|
||||||
:class="[option.name.toLowerCase().endsWith('color') && 'pl-6']"
|
:class="[option.name.toLowerCase().endsWith('color') && 'pl-6']"
|
||||||
:ui="{ itemLeadingChip: 'size-2' }"
|
:ui="{ itemLeadingChip: 'size-2' }"
|
||||||
@@ -160,7 +163,7 @@ const optionsValues = ref(props.options?.reduce((acc, option) => {
|
|||||||
:model-value="get(optionsValues, option.name)"
|
:model-value="get(optionsValues, option.name)"
|
||||||
color="neutral"
|
color="neutral"
|
||||||
variant="soft"
|
variant="soft"
|
||||||
:ui="{ base: 'rounded rounded-l-none min-w-12' }"
|
:ui="{ base: 'rounded-[var(--ui-radius)] rounded-l-none min-w-12' }"
|
||||||
@update:model-value="set(optionsValues, option.name, $event)"
|
@update:model-value="set(optionsValues, option.name, $event)"
|
||||||
/>
|
/>
|
||||||
</UFormField>
|
</UFormField>
|
||||||
@@ -170,7 +173,7 @@ const optionsValues = ref(props.options?.reduce((acc, option) => {
|
|||||||
<component :is="camelName" v-bind="{ ...componentProps, ...optionsValues }" />
|
<component :is="camelName" v-bind="{ ...componentProps, ...optionsValues }" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
|
|
||||||
<MDCRenderer v-if="ast && props.source" :body="ast.body" :data="ast.data" class="[&_pre]:!rounded-t-none [&_div.my-5]:!mt-0" />
|
<MDCRenderer v-if="ast && props.source" :body="ast.body" :data="ast.data" class="[&_pre]:!rounded-t-none [&_div.my-5]:!mt-0" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,9 +3,29 @@ import { upperFirst, camelCase } from 'scule'
|
|||||||
import type { ComponentMeta } from 'vue-component-meta'
|
import type { ComponentMeta } from 'vue-component-meta'
|
||||||
import * as theme from '#build/ui'
|
import * as theme from '#build/ui'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
ignore?: string[]
|
ignore?: string[]
|
||||||
}>()
|
}>(), {
|
||||||
|
ignore: () => [
|
||||||
|
'activeClass',
|
||||||
|
'inactiveClass',
|
||||||
|
'exactActiveClass',
|
||||||
|
'ariaCurrentValue',
|
||||||
|
'href',
|
||||||
|
'rel',
|
||||||
|
'noRel',
|
||||||
|
'prefetch',
|
||||||
|
'prefetchOn',
|
||||||
|
'noPrefetch',
|
||||||
|
'prefetchedClass',
|
||||||
|
'replace',
|
||||||
|
'exact',
|
||||||
|
'exactQuery',
|
||||||
|
'exactHash',
|
||||||
|
'external',
|
||||||
|
'onClick'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
@@ -77,8 +97,9 @@ const metaProps: ComputedRef<ComponentMeta['props']> = computed(() => {
|
|||||||
<ProseTd>
|
<ProseTd>
|
||||||
<HighlightInlineType v-if="prop.type" :type="prop.type" />
|
<HighlightInlineType v-if="prop.type" :type="prop.type" />
|
||||||
|
|
||||||
<MDC v-if="prop.description" :value="prop.description" class="text-[--ui-text-toned] mt-1" />
|
<MDC v-if="prop.description" :value="prop.description" class="text-[var(--ui-text-toned)] mt-1" />
|
||||||
|
|
||||||
|
<ComponentPropsLinks v-if="prop.tags?.length" :prop="prop" />
|
||||||
<ComponentPropsSchema v-if="prop.schema" :prop="prop" :ignore="ignore" />
|
<ComponentPropsSchema v-if="prop.schema" :prop="prop" :ignore="ignore" />
|
||||||
</ProseTd>
|
</ProseTd>
|
||||||
</ProseTr>
|
</ProseTr>
|
||||||
|
|||||||
17
docs/app/components/content/ComponentPropsLinks.vue
Normal file
17
docs/app/components/content/ComponentPropsLinks.vue
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { PropertyMeta } from 'vue-component-meta'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
prop: PropertyMeta
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const links = computed(() => props.prop.tags?.filter((tag: any) => tag.name === 'link'))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ProseUl v-if="links?.length">
|
||||||
|
<ProseLi v-for="link in links" :key="link.name">
|
||||||
|
<MDC :value="link.text ?? ''" class="my-1" />
|
||||||
|
</ProseLi>
|
||||||
|
</ProseUl>
|
||||||
|
</template>
|
||||||
@@ -40,7 +40,7 @@ const schemaProps = computed(() => {
|
|||||||
<ProseLi v-for="schemaProp in schemaProps" :key="schemaProp.name">
|
<ProseLi v-for="schemaProp in schemaProps" :key="schemaProp.name">
|
||||||
<HighlightInlineType :type="`${schemaProp.name}${schemaProp.required === false ? '?' : ''}: ${schemaProp.type}`" />
|
<HighlightInlineType :type="`${schemaProp.name}${schemaProp.required === false ? '?' : ''}: ${schemaProp.type}`" />
|
||||||
|
|
||||||
<MDC v-if="schemaProp.description" :value="schemaProp.description" class="text-[--ui-text-muted] my-1" />
|
<MDC v-if="schemaProp.description" :value="schemaProp.description" class="text-[var(--ui-text-muted)] my-1" />
|
||||||
</ProseLi>
|
</ProseLi>
|
||||||
</ProseUl>
|
</ProseUl>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const meta = await fetchComponentMeta(name as any)
|
|||||||
<ProseTd>
|
<ProseTd>
|
||||||
<HighlightInlineType v-if="slot.type" :type="slot.type" />
|
<HighlightInlineType v-if="slot.type" :type="slot.type" />
|
||||||
|
|
||||||
<MDC v-if="slot.description" :value="slot.description" class="text-[--ui-text-toned] mt-1" />
|
<MDC v-if="slot.description" :value="slot.description" class="text-[var(--ui-text-toned)] mt-1" />
|
||||||
</ProseTd>
|
</ProseTd>
|
||||||
</ProseTr>
|
</ProseTr>
|
||||||
</ProseTbody>
|
</ProseTbody>
|
||||||
|
|||||||
@@ -28,6 +28,14 @@ function stripCompoundVariants(component?: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (compoundVariant.loadingColor) {
|
||||||
|
if (!['primary', 'neutral'].includes(compoundVariant.loadingColor)) {
|
||||||
|
strippedCompoundVariants.value = true
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="relative overflow-hidden rounded-[--ui-radius] border border-dashed border-[--ui-border-accented] opacity-75 px-4 flex items-center justify-center">
|
<div class="relative overflow-hidden rounded-[var(--ui-radius)] border border-dashed border-[var(--ui-border-accented)] opacity-75 px-4 flex items-center justify-center">
|
||||||
<svg class="absolute inset-0 h-full w-full stroke-[--ui-border-inverted]/10" fill="none">
|
<svg class="absolute inset-0 h-full w-full stroke-[var(--ui-border-inverted)]/10" fill="none">
|
||||||
<defs>
|
<defs>
|
||||||
<pattern
|
<pattern
|
||||||
id="pattern-5c1e4f0e-62d5-498b-8ff0-cf77bb448c8e"
|
id="pattern-5c1e4f0e-62d5-498b-8ff0-cf77bb448c8e"
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const items = [
|
|||||||
<template>
|
<template>
|
||||||
<UAccordion :items="items">
|
<UAccordion :items="items">
|
||||||
<template #content="{ item }">
|
<template #content="{ item }">
|
||||||
<p class="pb-3.5 text-sm text-[--ui-text-muted]">
|
<p class="pb-3.5 text-sm text-[var(--ui-text-muted)]">
|
||||||
This is the {{ item.label }} panel.
|
This is the {{ item.label }} panel.
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const items = [
|
|||||||
<template>
|
<template>
|
||||||
<UAccordion :items="items">
|
<UAccordion :items="items">
|
||||||
<template #colors="{ item }">
|
<template #colors="{ item }">
|
||||||
<p class="text-sm pb-3.5 text-[--ui-primary]">
|
<p class="text-sm pb-3.5 text-[var(--ui-primary)]">
|
||||||
{{ item.content }}
|
{{ item.content }}
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -2,21 +2,21 @@
|
|||||||
<UAvatarGroup>
|
<UAvatarGroup>
|
||||||
<UChip inset color="success">
|
<UChip inset color="success">
|
||||||
<UAvatar
|
<UAvatar
|
||||||
src="https://avatars.githubusercontent.com/u/739984?v=4"
|
src="https://github.com/benjamincanac.png"
|
||||||
alt="Benjamin Canac"
|
alt="Benjamin Canac"
|
||||||
/>
|
/>
|
||||||
</UChip>
|
</UChip>
|
||||||
|
|
||||||
<UChip inset color="warning">
|
<UChip inset color="warning">
|
||||||
<UAvatar
|
<UAvatar
|
||||||
src="https://avatars.githubusercontent.com/u/25613751?v=4"
|
src="https://github.com/romhml.png"
|
||||||
alt="Romain Hamel"
|
alt="Romain Hamel"
|
||||||
/>
|
/>
|
||||||
</UChip>
|
</UChip>
|
||||||
|
|
||||||
<UChip inset color="error">
|
<UChip inset color="error">
|
||||||
<UAvatar
|
<UAvatar
|
||||||
src="https://avatars.githubusercontent.com/u/19751938?v=4"
|
src="https://github.com/noook.png"
|
||||||
alt="Neil Richter"
|
alt="Neil Richter"
|
||||||
/>
|
/>
|
||||||
</UChip>
|
</UChip>
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
<ULink
|
<ULink
|
||||||
to="https://github.com/benjamincanac"
|
to="https://github.com/benjamincanac"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="hover:ring-[--ui-primary] transition"
|
class="hover:ring-[var(--ui-primary)] transition"
|
||||||
raw
|
raw
|
||||||
>
|
>
|
||||||
<UAvatar
|
<UAvatar
|
||||||
src="https://avatars.githubusercontent.com/u/739984?v=4"
|
src="https://github.com/benjamincanac.png"
|
||||||
alt="Benjamin Canac"
|
alt="Benjamin Canac"
|
||||||
/>
|
/>
|
||||||
</ULink>
|
</ULink>
|
||||||
@@ -15,11 +15,11 @@
|
|||||||
<ULink
|
<ULink
|
||||||
to="https://github.com/romhml"
|
to="https://github.com/romhml"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="hover:ring-[--ui-primary] transition"
|
class="hover:ring-[var(--ui-primary)] transition"
|
||||||
raw
|
raw
|
||||||
>
|
>
|
||||||
<UAvatar
|
<UAvatar
|
||||||
src="https://avatars.githubusercontent.com/u/25613751?v=4"
|
src="https://github.com/romhml.png"
|
||||||
alt="Romain Hamel"
|
alt="Romain Hamel"
|
||||||
/>
|
/>
|
||||||
</ULink>
|
</ULink>
|
||||||
@@ -27,11 +27,11 @@
|
|||||||
<ULink
|
<ULink
|
||||||
to="https://github.com/noook"
|
to="https://github.com/noook"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="hover:ring-[--ui-primary] transition"
|
class="hover:ring-[var(--ui-primary)] transition"
|
||||||
raw
|
raw
|
||||||
>
|
>
|
||||||
<UAvatar
|
<UAvatar
|
||||||
src="https://avatars.githubusercontent.com/u/19751938?v=4"
|
src="https://github.com/noook.png"
|
||||||
alt="Neil Richter"
|
alt="Neil Richter"
|
||||||
/>
|
/>
|
||||||
</ULink>
|
</ULink>
|
||||||
|
|||||||
@@ -2,21 +2,21 @@
|
|||||||
<UAvatarGroup>
|
<UAvatarGroup>
|
||||||
<UTooltip text="benjamincanac">
|
<UTooltip text="benjamincanac">
|
||||||
<UAvatar
|
<UAvatar
|
||||||
src="https://avatars.githubusercontent.com/u/739984?v=4"
|
src="https://github.com/benjamincanac.png"
|
||||||
alt="Benjamin Canac"
|
alt="Benjamin Canac"
|
||||||
/>
|
/>
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
|
|
||||||
<UTooltip text="romhml">
|
<UTooltip text="romhml">
|
||||||
<UAvatar
|
<UAvatar
|
||||||
src="https://avatars.githubusercontent.com/u/25613751?v=4"
|
src="https://github.com/romhml.png"
|
||||||
alt="Romain Hamel"
|
alt="Romain Hamel"
|
||||||
/>
|
/>
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
|
|
||||||
<UTooltip text="noook">
|
<UTooltip text="noook">
|
||||||
<UAvatar
|
<UAvatar
|
||||||
src="https://avatars.githubusercontent.com/u/19751938?v=4"
|
src="https://github.com/noook.png"
|
||||||
alt="Neil Richter"
|
alt="Neil Richter"
|
||||||
/>
|
/>
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<UChip inset>
|
<UChip inset>
|
||||||
<UAvatar
|
<UAvatar
|
||||||
src="https://avatars.githubusercontent.com/u/739984?v=4"
|
src="https://github.com/benjamincanac.png"
|
||||||
alt="Benjamin Canac"
|
alt="Benjamin Canac"
|
||||||
/>
|
/>
|
||||||
</UChip>
|
</UChip>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<UTooltip text="Benjamin Canac">
|
<UTooltip text="Benjamin Canac">
|
||||||
<UAvatar
|
<UAvatar
|
||||||
src="https://avatars.githubusercontent.com/u/739984?v=4"
|
src="https://github.com/benjamincanac.png"
|
||||||
alt="Benjamin Canac"
|
alt="Benjamin Canac"
|
||||||
/>
|
/>
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const items = [{
|
|||||||
<template>
|
<template>
|
||||||
<UBreadcrumb :items="items">
|
<UBreadcrumb :items="items">
|
||||||
<template #separator>
|
<template #separator>
|
||||||
<span class="mx-2 text-[--ui-text-muted]">/</span>
|
<span class="mx-2 text-[var(--ui-text-muted)]">/</span>
|
||||||
</template>
|
</template>
|
||||||
</UBreadcrumb>
|
</UBreadcrumb>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ onMounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UChip :color="color" :show="show" inset>
|
<UChip :color="color" :show="show" inset>
|
||||||
<UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" />
|
<UAvatar src="https://github.com/benjamincanac.png" />
|
||||||
</UChip>
|
</UChip>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<UCollapsible class="w-48">
|
<UCollapsible class="flex flex-col gap-2 w-48">
|
||||||
<UButton
|
<UButton
|
||||||
class="group"
|
class="group"
|
||||||
label="Open"
|
label="Open"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ defineShortcuts({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UCollapsible v-model:open="open" class="w-48">
|
<UCollapsible v-model:open="open" class="flex flex-col gap-2 w-48">
|
||||||
<UButton
|
<UButton
|
||||||
label="Open"
|
label="Open"
|
||||||
color="neutral"
|
color="neutral"
|
||||||
|
|||||||
@@ -1,40 +1,68 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const groups = [{
|
const groups = [{
|
||||||
id: 'settings',
|
id: 'settings',
|
||||||
items: [{
|
items: [
|
||||||
label: 'Profile',
|
{
|
||||||
icon: 'i-heroicons-user',
|
label: 'Profile',
|
||||||
kbds: ['meta', 'P']
|
icon: 'i-heroicons-user',
|
||||||
}, {
|
kbds: ['meta', 'P']
|
||||||
label: 'Billing',
|
},
|
||||||
icon: 'i-heroicons-credit-card',
|
{
|
||||||
kbds: ['meta', 'B'],
|
label: 'Billing',
|
||||||
slot: 'billing'
|
icon: 'i-heroicons-credit-card',
|
||||||
}, {
|
kbds: ['meta', 'B'],
|
||||||
label: 'Notifications',
|
slot: 'billing'
|
||||||
icon: 'i-heroicons-bell'
|
},
|
||||||
}, {
|
{
|
||||||
label: 'Security',
|
label: 'Notifications',
|
||||||
icon: 'i-heroicons-lock-closed'
|
icon: 'i-heroicons-bell'
|
||||||
}]
|
},
|
||||||
|
{
|
||||||
|
label: 'Security',
|
||||||
|
icon: 'i-heroicons-lock-closed'
|
||||||
|
}
|
||||||
|
]
|
||||||
}, {
|
}, {
|
||||||
id: 'users',
|
id: 'users',
|
||||||
label: 'Users',
|
label: 'Users',
|
||||||
slot: 'users',
|
slot: 'users',
|
||||||
items: [
|
items: [
|
||||||
{ id: 1, label: 'Durward Reynolds' },
|
{
|
||||||
{ id: 2, label: 'Kenton Towne' },
|
label: 'Benjamin Canac',
|
||||||
{ id: 3, label: 'Therese Wunsch' },
|
suffix: 'benjamincanac'
|
||||||
{ id: 4, label: 'Benedict Kessler' },
|
},
|
||||||
{ id: 5, label: 'Katelyn Rohan' }
|
{
|
||||||
|
label: 'Sylvain Marroufin',
|
||||||
|
suffix: 'smarroufin'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Sébastien Chopin',
|
||||||
|
suffix: 'atinux'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Romain Hamel',
|
||||||
|
suffix: 'romhml'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Haytham A. Salama',
|
||||||
|
suffix: 'Haythamasalama'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Daniel Roe',
|
||||||
|
suffix: 'danielroe'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Neil Richter',
|
||||||
|
suffix: 'noook'
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UCommandPalette :groups="groups" class="flex-1 h-80">
|
<UCommandPalette :groups="groups" class="flex-1 h-80">
|
||||||
<template #users-leading="{ index }">
|
<template #users-leading="{ item }">
|
||||||
<UAvatar :src="`https://i.pravatar.cc/120?img=${index}`" size="2xs" />
|
<UAvatar :src="`https://github.com/${item.suffix}.png`" size="2xs" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #billing-label="{ item }">
|
<template #billing-label="{ item }">
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
const users = [
|
|
||||||
{ id: 1, label: 'Durward Reynolds' },
|
|
||||||
{ id: 2, label: 'Kenton Towne' },
|
|
||||||
{ id: 3, label: 'Therese Wunsch' },
|
|
||||||
{ id: 4, label: 'Benedict Kessler' },
|
|
||||||
{ id: 5, label: 'Katelyn Rohan' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const selected = ref(users[0])
|
|
||||||
|
|
||||||
function onSelect(item: any) {
|
|
||||||
console.log(item)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<UCommandPalette
|
|
||||||
v-model="selected"
|
|
||||||
:groups="[{ id: 'users', items: users }]"
|
|
||||||
class="flex-1"
|
|
||||||
@update:model-value="onSelect"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
const users = [
|
|
||||||
{ id: 1, label: 'Durward Reynolds' },
|
|
||||||
{ id: 2, label: 'Kenton Towne' },
|
|
||||||
{ id: 3, label: 'Therese Wunsch' },
|
|
||||||
{ id: 4, label: 'Benedict Kessler' },
|
|
||||||
{ id: 5, label: 'Katelyn Rohan' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const selected = ref([users[0], users[1]])
|
|
||||||
|
|
||||||
function onSelect(items: any) {
|
|
||||||
console.log(items)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<UCommandPalette
|
|
||||||
v-model="selected"
|
|
||||||
multiple
|
|
||||||
:groups="[{ id: 'users', items: users }]"
|
|
||||||
class="flex-1"
|
|
||||||
@update:model-value="onSelect"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
@@ -2,11 +2,55 @@
|
|||||||
const open = ref(false)
|
const open = ref(false)
|
||||||
|
|
||||||
const users = [
|
const users = [
|
||||||
{ id: 1, label: 'Durward Reynolds' },
|
{
|
||||||
{ id: 2, label: 'Kenton Towne' },
|
label: 'Benjamin Canac',
|
||||||
{ id: 3, label: 'Therese Wunsch' },
|
suffix: 'benjamincanac',
|
||||||
{ id: 4, label: 'Benedict Kessler' },
|
avatar: {
|
||||||
{ id: 5, label: 'Katelyn Rohan' }
|
src: 'https://github.com/benjamincanac.png'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Sylvain Marroufin',
|
||||||
|
suffix: 'smarroufin',
|
||||||
|
avatar: {
|
||||||
|
src: 'https://github.com/smarroufin.png'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Sébastien Chopin',
|
||||||
|
suffix: 'atinux',
|
||||||
|
avatar: {
|
||||||
|
src: 'https://github.com/atinux.png'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Romain Hamel',
|
||||||
|
suffix: 'romhml',
|
||||||
|
avatar: {
|
||||||
|
src: 'https://github.com/romhml.png'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Haytham A. Salama',
|
||||||
|
suffix: 'Haythamasalama',
|
||||||
|
avatar: {
|
||||||
|
src: 'https://github.com/Haythamasalama.png'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Daniel Roe',
|
||||||
|
suffix: 'danielroe',
|
||||||
|
avatar: {
|
||||||
|
src: 'https://github.com/danielroe.png'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Neil Richter',
|
||||||
|
suffix: 'noook',
|
||||||
|
avatar: {
|
||||||
|
src: 'https://github.com/noook.png'
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -20,11 +64,7 @@ const users = [
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<template #content>
|
<template #content>
|
||||||
<UCommandPalette
|
<UCommandPalette close :groups="[{ id: 'users', items: users }]" @update:open="open = $event" />
|
||||||
close
|
|
||||||
:groups="[{ id: 'users', items: users }]"
|
|
||||||
@update:open="open = $event"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
</UModal>
|
</UModal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,29 +1,36 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const items = [{
|
const items = [
|
||||||
id: '/',
|
{
|
||||||
label: 'Introduction',
|
id: '/',
|
||||||
level: 1
|
label: 'Introduction',
|
||||||
}, {
|
level: 1
|
||||||
id: '/getting-started#whats-new-in-v3',
|
},
|
||||||
label: 'What\'s new in v3?',
|
{
|
||||||
level: 2
|
id: '/getting-started#whats-new-in-v3',
|
||||||
}, {
|
label: 'What\'s new in v3?',
|
||||||
id: '/getting-started#radix-vue-3',
|
level: 2
|
||||||
label: 'Radix Vue',
|
},
|
||||||
level: 3
|
{
|
||||||
}, {
|
id: '/getting-started#radix-vue-3',
|
||||||
id: '/getting-started#tailwind-css-v4',
|
label: 'Radix Vue',
|
||||||
label: 'Tailwind CSS v4',
|
level: 3
|
||||||
level: 3
|
},
|
||||||
}, {
|
{
|
||||||
id: '/getting-started#tailwind-variants',
|
id: '/getting-started#tailwind-css-v4',
|
||||||
label: 'Tailwind Variants',
|
label: 'Tailwind CSS v4',
|
||||||
level: 3
|
level: 3
|
||||||
}, {
|
},
|
||||||
id: '/getting-started/installation',
|
{
|
||||||
label: 'Installation',
|
id: '/getting-started#tailwind-variants',
|
||||||
level: 1
|
label: 'Tailwind Variants',
|
||||||
}]
|
level: 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '/getting-started/installation',
|
||||||
|
label: 'Installation',
|
||||||
|
level: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
function postFilter(searchTerm: string, items: any[]) {
|
function postFilter(searchTerm: string, items: any[]) {
|
||||||
// Filter only first level items if no searchTerm
|
// Filter only first level items if no searchTerm
|
||||||
|
|||||||
@@ -1,13 +1,82 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const users = [
|
const users = [
|
||||||
{ id: 1, label: 'Durward Reynolds' },
|
{
|
||||||
{ id: 2, label: 'Kenton Towne' },
|
label: 'Benjamin Canac',
|
||||||
{ id: 3, label: 'Therese Wunsch' },
|
suffix: 'benjamincanac',
|
||||||
{ id: 4, label: 'Benedict Kessler' },
|
to: 'https://github.com/benjamincanac',
|
||||||
{ id: 5, label: 'Katelyn Rohan' }
|
target: '_blank',
|
||||||
|
avatar: {
|
||||||
|
src: 'https://github.com/benjamincanac.png',
|
||||||
|
alt: 'benjamincanac'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Sylvain Marroufin',
|
||||||
|
suffix: 'smarroufin',
|
||||||
|
to: 'https://github.com/smarroufin',
|
||||||
|
target: '_blank',
|
||||||
|
avatar: {
|
||||||
|
src: 'https://github.com/smarroufin.png',
|
||||||
|
alt: 'smarroufin'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Sébastien Chopin',
|
||||||
|
suffix: 'atinux',
|
||||||
|
to: 'https://github.com/atinux',
|
||||||
|
target: '_blank',
|
||||||
|
avatar: {
|
||||||
|
src: 'https://github.com/atinux.png',
|
||||||
|
alt: 'atinux'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Romain Hamel',
|
||||||
|
suffix: 'romhml',
|
||||||
|
to: 'https://github.com/romhml',
|
||||||
|
target: '_blank',
|
||||||
|
avatar: {
|
||||||
|
src: 'https://github.com/romhml.png',
|
||||||
|
alt: 'romhml'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Haytham A. Salama',
|
||||||
|
suffix: 'Haythamasalama',
|
||||||
|
to: 'https://github.com/Haythamasalama',
|
||||||
|
target: '_blank',
|
||||||
|
avatar: {
|
||||||
|
src: 'https://github.com/Haythamasalama.png',
|
||||||
|
alt: 'Haythamasalama'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Daniel Roe',
|
||||||
|
suffix: 'danielroe',
|
||||||
|
to: 'https://github.com/danielroe',
|
||||||
|
target: '_blank',
|
||||||
|
avatar: {
|
||||||
|
src: 'https://github.com/danielroe.png',
|
||||||
|
alt: 'danielroe'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Neil Richter',
|
||||||
|
suffix: 'noook',
|
||||||
|
to: 'https://github.com/noook',
|
||||||
|
target: '_blank',
|
||||||
|
avatar: {
|
||||||
|
src: 'https://github.com/noook.png',
|
||||||
|
alt: 'noook'
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const searchTerm = ref('Th')
|
const searchTerm = ref('')
|
||||||
|
|
||||||
|
function onSelect() {
|
||||||
|
searchTerm.value = ''
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -15,5 +84,6 @@ const searchTerm = ref('Th')
|
|||||||
v-model:search-term="searchTerm"
|
v-model:search-term="searchTerm"
|
||||||
:groups="[{ id: 'users', items: users }]"
|
:groups="[{ id: 'users', items: users }]"
|
||||||
class="flex-1"
|
class="flex-1"
|
||||||
|
@update:model-value="onSelect"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const groups = ref([
|
||||||
|
{
|
||||||
|
id: 'users',
|
||||||
|
label: 'Users',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: 'Benjamin Canac',
|
||||||
|
suffix: 'benjamincanac',
|
||||||
|
to: 'https://github.com/benjamincanac',
|
||||||
|
target: '_blank',
|
||||||
|
avatar: {
|
||||||
|
src: 'https://github.com/benjamincanac.png',
|
||||||
|
alt: 'benjamincanac'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Sylvain Marroufin',
|
||||||
|
suffix: 'smarroufin',
|
||||||
|
to: 'https://github.com/smarroufin',
|
||||||
|
target: '_blank',
|
||||||
|
avatar: {
|
||||||
|
src: 'https://github.com/smarroufin.png',
|
||||||
|
alt: 'smarroufin'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Sébastien Chopin',
|
||||||
|
suffix: 'atinux',
|
||||||
|
to: 'https://github.com/atinux',
|
||||||
|
target: '_blank',
|
||||||
|
avatar: {
|
||||||
|
src: 'https://github.com/atinux.png',
|
||||||
|
alt: 'atinux'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Romain Hamel',
|
||||||
|
suffix: 'romhml',
|
||||||
|
to: 'https://github.com/romhml',
|
||||||
|
target: '_blank',
|
||||||
|
avatar: {
|
||||||
|
src: 'https://github.com/romhml.png',
|
||||||
|
alt: 'romhml'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Haytham A. Salama',
|
||||||
|
suffix: 'Haythamasalama',
|
||||||
|
to: 'https://github.com/Haythamasalama',
|
||||||
|
target: '_blank',
|
||||||
|
avatar: {
|
||||||
|
src: 'https://github.com/Haythamasalama.png',
|
||||||
|
alt: 'Haythamasalama'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Daniel Roe',
|
||||||
|
suffix: 'danielroe',
|
||||||
|
to: 'https://github.com/danielroe',
|
||||||
|
target: '_blank',
|
||||||
|
avatar: {
|
||||||
|
src: 'https://github.com/danielroe.png',
|
||||||
|
alt: 'danielroe'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Neil Richter',
|
||||||
|
suffix: 'noook',
|
||||||
|
to: 'https://github.com/noook',
|
||||||
|
target: '_blank',
|
||||||
|
avatar: {
|
||||||
|
src: 'https://github.com/noook.png',
|
||||||
|
alt: 'noook'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'actions',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: 'Add new file',
|
||||||
|
suffix: 'Create a new file in the current directory or workspace.',
|
||||||
|
icon: 'i-heroicons-document-plus',
|
||||||
|
kbds: [
|
||||||
|
'meta',
|
||||||
|
'N'
|
||||||
|
],
|
||||||
|
onSelect() {
|
||||||
|
console.log('Add new file')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Add new folder',
|
||||||
|
suffix: 'Create a new folder in the current directory or workspace.',
|
||||||
|
icon: 'i-heroicons-folder-plus',
|
||||||
|
kbds: [
|
||||||
|
'meta',
|
||||||
|
'F'
|
||||||
|
],
|
||||||
|
onSelect() {
|
||||||
|
console.log('Add new folder')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Add hashtag',
|
||||||
|
suffix: 'Add a hashtag to the current item.',
|
||||||
|
icon: 'i-heroicons-hashtag',
|
||||||
|
kbds: [
|
||||||
|
'meta',
|
||||||
|
'H'
|
||||||
|
],
|
||||||
|
onSelect() {
|
||||||
|
console.log('Add hashtag')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Add label',
|
||||||
|
suffix: 'Add a label to the current item.',
|
||||||
|
icon: 'i-heroicons-tag',
|
||||||
|
kbds: [
|
||||||
|
'meta',
|
||||||
|
'L'
|
||||||
|
],
|
||||||
|
onSelect() {
|
||||||
|
console.log('Add label')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
function onSelect(item: any) {
|
||||||
|
if (item.onSelect) {
|
||||||
|
item.onSelect()
|
||||||
|
} else if (item.to) {
|
||||||
|
if (typeof item.to === 'string' && (item.target === '_blank' || item.to.startsWith('http'))) {
|
||||||
|
window.open(item.to, item.target || '_blank')
|
||||||
|
} else {
|
||||||
|
router.push(item.to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UCommandPalette
|
||||||
|
:groups="groups"
|
||||||
|
class="flex-1 h-80"
|
||||||
|
@update:model-value="onSelect"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const showSidebar = ref(true)
|
||||||
|
const showToolbar = ref(false)
|
||||||
|
|
||||||
|
const items = computed(() => [{
|
||||||
|
label: 'View',
|
||||||
|
type: 'label' as const
|
||||||
|
}, {
|
||||||
|
type: 'separator' as const
|
||||||
|
}, {
|
||||||
|
label: 'Show Sidebar',
|
||||||
|
type: 'checkbox' as const,
|
||||||
|
checked: showSidebar.value,
|
||||||
|
onUpdateChecked(checked: boolean) {
|
||||||
|
showSidebar.value = checked
|
||||||
|
},
|
||||||
|
onSelect(e: Event) {
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
label: 'Show Toolbar',
|
||||||
|
type: 'checkbox' as const,
|
||||||
|
checked: showToolbar.value,
|
||||||
|
onUpdateChecked(checked: boolean) {
|
||||||
|
showToolbar.value = checked
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
label: 'Collapse Pinned Tabs',
|
||||||
|
type: 'checkbox' as const,
|
||||||
|
disabled: true
|
||||||
|
}])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UContextMenu :items="items" class="w-48">
|
||||||
|
<div class="flex items-center justify-center rounded-md border border-dashed border-[var(--ui-border-accented)] text-sm aspect-video w-72">
|
||||||
|
Right click here
|
||||||
|
</div>
|
||||||
|
</UContextMenu>
|
||||||
|
</template>
|
||||||
@@ -13,7 +13,7 @@ const items = [{
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UContextMenu :items="items" class="w-48">
|
<UContextMenu :items="items" class="w-48">
|
||||||
<div class="flex items-center justify-center rounded-md border border-dashed border-[--ui-border-accented] text-sm aspect-video w-72">
|
<div class="flex items-center justify-center rounded-md border border-dashed border-[var(--ui-border-accented)] text-sm aspect-video w-72">
|
||||||
Right click here
|
Right click here
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ const items = [{
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #refresh-trailing>
|
<template #refresh-trailing>
|
||||||
<UIcon v-if="loading" name="i-heroicons-arrow-path-20-solid" class="shrink-0 size-5 text-[--ui-primary] animate-spin" />
|
<UIcon v-if="loading" name="i-heroicons-arrow-path-20-solid" class="shrink-0 size-5 text-[var(--ui-primary)] animate-spin" />
|
||||||
</template>
|
</template>
|
||||||
</UContextMenu>
|
</UContextMenu>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const groups = computed(() => [{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UDrawer>
|
<UDrawer :handle="false">
|
||||||
<UButton
|
<UButton
|
||||||
label="Search users..."
|
label="Search users..."
|
||||||
color="neutral"
|
color="neutral"
|
||||||
@@ -32,7 +32,7 @@ const groups = computed(() => [{
|
|||||||
:loading="status === 'pending'"
|
:loading="status === 'pending'"
|
||||||
:groups="groups"
|
:groups="groups"
|
||||||
placeholder="Search users..."
|
placeholder="Search users..."
|
||||||
class="h-96 border-t border-[--ui-border]"
|
class="h-80"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</UDrawer>
|
</UDrawer>
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const showBookmarks = ref(true)
|
||||||
|
const showHistory = ref(false)
|
||||||
|
const showDownloads = ref(false)
|
||||||
|
|
||||||
|
const items = computed(() => [{
|
||||||
|
label: 'Interface',
|
||||||
|
icon: 'i-heroicons-window',
|
||||||
|
type: 'label' as const
|
||||||
|
}, {
|
||||||
|
type: 'separator' as const
|
||||||
|
}, {
|
||||||
|
label: 'Show Bookmarks',
|
||||||
|
icon: 'i-heroicons-bookmark',
|
||||||
|
type: 'checkbox' as const,
|
||||||
|
checked: showBookmarks.value,
|
||||||
|
onUpdateChecked(checked: boolean) {
|
||||||
|
showBookmarks.value = checked
|
||||||
|
},
|
||||||
|
onSelect(e: Event) {
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
label: 'Show History',
|
||||||
|
icon: 'i-heroicons-clock',
|
||||||
|
type: 'checkbox' as const,
|
||||||
|
checked: showHistory.value,
|
||||||
|
onUpdateChecked(checked: boolean) {
|
||||||
|
showHistory.value = checked
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
label: 'Show Downloads',
|
||||||
|
icon: 'i-heroicons-arrow-down-on-square',
|
||||||
|
type: 'checkbox' as const,
|
||||||
|
checked: showDownloads.value,
|
||||||
|
onUpdateChecked(checked: boolean) {
|
||||||
|
showDownloads.value = checked
|
||||||
|
}
|
||||||
|
}])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UDropdownMenu :items="items" :content="{ align: 'start' }" class="w-48">
|
||||||
|
<UButton label="Open" color="neutral" variant="outline" icon="i-heroicons-bars-3" />
|
||||||
|
</UDropdownMenu>
|
||||||
|
</template>
|
||||||
@@ -17,7 +17,7 @@ const items = [{
|
|||||||
<UButton label="Open" color="neutral" variant="outline" icon="i-heroicons-bars-3" />
|
<UButton label="Open" color="neutral" variant="outline" icon="i-heroicons-bars-3" />
|
||||||
|
|
||||||
<template #profile-trailing>
|
<template #profile-trailing>
|
||||||
<UIcon name="i-heroicons-check-badge" class="shrink-0 size-5 text-[--ui-primary]" />
|
<UIcon name="i-heroicons-check-badge" class="shrink-0 size-5 text-[var(--ui-primary)]" />
|
||||||
</template>
|
</template>
|
||||||
</UDropdownMenu>
|
</UDropdownMenu>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import type { FormSubmitEvent, Form } from '@nuxt/ui'
|
import type { FormSubmitEvent } from '@nuxt/ui'
|
||||||
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
input: z.string().min(10),
|
input: z.string().min(10),
|
||||||
@@ -35,7 +35,8 @@ const schema = z.object({
|
|||||||
type Schema = z.output<typeof schema>
|
type Schema = z.output<typeof schema>
|
||||||
|
|
||||||
const state = reactive<Partial<Schema>>({})
|
const state = reactive<Partial<Schema>>({})
|
||||||
const form = ref<Form<Schema>>()
|
|
||||||
|
const form = useTemplateRef('form')
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
{ label: 'Option 1', value: 'option-1' },
|
{ label: 'Option 1', value: 'option-1' },
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ function removeItem() {
|
|||||||
state.items.pop()
|
state.items.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const formItemRef = ref()
|
|
||||||
|
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
|
||||||
async function onSubmit(event: FormSubmitEvent<any>) {
|
async function onSubmit(event: FormSubmitEvent<any>) {
|
||||||
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
|
||||||
console.log(event.data)
|
console.log(event.data)
|
||||||
@@ -42,7 +42,6 @@ async function onSubmit(event: FormSubmitEvent<any>) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UForm
|
<UForm
|
||||||
ref="formItemRef"
|
|
||||||
:state="state"
|
:state="state"
|
||||||
:schema="schema"
|
:schema="schema"
|
||||||
class="gap-4 flex flex-col w-60"
|
class="gap-4 flex flex-col w-60"
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { object, string, nonempty, refine, type Infer } from 'superstruct'
|
||||||
|
import type { FormSubmitEvent } from '#ui/types'
|
||||||
|
|
||||||
|
const schema = object({
|
||||||
|
email: nonempty(string()),
|
||||||
|
password: refine(string(), 'Password', (value) => {
|
||||||
|
if (value.length >= 8) return true
|
||||||
|
return 'Must be at least 8 characters'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
email: '',
|
||||||
|
password: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
type Schema = Infer<typeof schema>
|
||||||
|
|
||||||
|
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||||||
|
console.log(event.data)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
|
||||||
|
<UFormField label="Email" name="email">
|
||||||
|
<UInput v-model="state.email" />
|
||||||
|
</UFormField>
|
||||||
|
|
||||||
|
<UFormField label="Password" name="password">
|
||||||
|
<UInput v-model="state.password" type="password" />
|
||||||
|
</UFormField>
|
||||||
|
|
||||||
|
<UButton type="submit">
|
||||||
|
Submit
|
||||||
|
</UButton>
|
||||||
|
</UForm>
|
||||||
|
</template>
|
||||||
@@ -17,14 +17,13 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
|||||||
:loading="status === 'pending'"
|
:loading="status === 'pending'"
|
||||||
icon="i-heroicons-user"
|
icon="i-heroicons-user"
|
||||||
placeholder="Select user"
|
placeholder="Select user"
|
||||||
class="w-48"
|
|
||||||
>
|
>
|
||||||
<template #leading="{ modelValue, ui }">
|
<template #leading="{ modelValue, ui }">
|
||||||
<UAvatar
|
<UAvatar
|
||||||
v-if="modelValue"
|
v-if="modelValue"
|
||||||
v-bind="modelValue.avatar"
|
v-bind="modelValue.avatar"
|
||||||
:size="ui.itemLeadingAvatarSize()"
|
:size="ui.leadingAvatarSize()"
|
||||||
:class="ui.itemLeadingAvatar()"
|
:class="ui.leadingAvatar()"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</UInputMenu>
|
</UInputMenu>
|
||||||
|
|||||||
@@ -23,14 +23,13 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
|||||||
:filter="false"
|
:filter="false"
|
||||||
icon="i-heroicons-user"
|
icon="i-heroicons-user"
|
||||||
placeholder="Select user"
|
placeholder="Select user"
|
||||||
class="w-48"
|
|
||||||
>
|
>
|
||||||
<template #leading="{ modelValue, ui }">
|
<template #leading="{ modelValue, ui }">
|
||||||
<UAvatar
|
<UAvatar
|
||||||
v-if="modelValue"
|
v-if="modelValue"
|
||||||
v-bind="modelValue.avatar"
|
v-bind="modelValue.avatar"
|
||||||
:size="ui.itemLeadingAvatarSize()"
|
:size="ui.leadingAvatarSize()"
|
||||||
:class="ui.itemLeadingAvatar()"
|
:class="ui.leadingAvatar()"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</UInputMenu>
|
</UInputMenu>
|
||||||
|
|||||||
@@ -25,15 +25,15 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
|||||||
<UAvatar
|
<UAvatar
|
||||||
v-if="modelValue"
|
v-if="modelValue"
|
||||||
v-bind="modelValue.avatar"
|
v-bind="modelValue.avatar"
|
||||||
:size="ui.itemLeadingAvatarSize()"
|
:size="ui.leadingAvatarSize()"
|
||||||
:class="ui.itemLeadingAvatar()"
|
:class="ui.leadingAvatar()"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #item-label="{ item }">
|
<template #item-label="{ item }">
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
|
|
||||||
<span class="text-[--ui-text-muted]">
|
<span class="text-[var(--ui-text-muted)]">
|
||||||
{{ item.email }}
|
{{ item.email }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
||||||
const selected = ref('Backlog')
|
const value = ref('Backlog')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UInputMenu
|
<UInputMenu
|
||||||
v-model="selected"
|
v-model="value"
|
||||||
:items="items"
|
:items="items"
|
||||||
:ui="{
|
:ui="{
|
||||||
trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200'
|
trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200'
|
||||||
|
|||||||
@@ -25,18 +25,9 @@ const items = ref([
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
const selected = ref(items.value[0])
|
const value = ref(items.value[0])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UInputMenu v-model="selected" :items="items" class="w-40">
|
<UInputMenu v-model="value" :avatar="value?.avatar" :items="items" />
|
||||||
<template #leading="{ modelValue, ui }">
|
|
||||||
<UAvatar
|
|
||||||
v-if="modelValue"
|
|
||||||
v-bind="modelValue.avatar"
|
|
||||||
:size="ui.itemLeadingAvatarSize()"
|
|
||||||
:class="ui.itemLeadingAvatar()"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</UInputMenu>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ const items = ref([
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
const selected = ref(items.value[0])
|
const value = ref(items.value[0])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UInputMenu v-model="selected" :items="items" class="w-40">
|
<UInputMenu v-model="value" :items="items">
|
||||||
<template #leading="{ modelValue, ui }">
|
<template #leading="{ modelValue, ui }">
|
||||||
<UChip
|
<UChip
|
||||||
v-if="modelValue"
|
v-if="modelValue"
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ const items = ref([
|
|||||||
icon: 'i-heroicons-check-circle'
|
icon: 'i-heroicons-check-circle'
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
const selected = ref(items.value[0])
|
const value = ref(items.value[0])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UInputMenu v-model="selected" :icon="selected?.icon" :items="items" class="w-40" />
|
<UInputMenu v-model="value" :icon="value?.icon" :items="items" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const open = ref(false)
|
const open = ref(false)
|
||||||
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
||||||
const selected = ref('Backlog')
|
const value = ref('Backlog')
|
||||||
|
|
||||||
defineShortcuts({
|
defineShortcuts({
|
||||||
o: () => open.value = !open.value
|
o: () => open.value = !open.value
|
||||||
@@ -9,5 +9,5 @@ defineShortcuts({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UInputMenu v-model="selected" v-model:open="open" :items="items" />
|
<UInputMenu v-model="value" v-model:open="open" :items="items" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const open = ref(false)
|
||||||
|
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
||||||
|
const selected = ref('Backlog')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UInputMenu
|
||||||
|
v-model="selected"
|
||||||
|
v-model:open="open"
|
||||||
|
:items="items"
|
||||||
|
@focus="open = true"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const searchTerm = ref('D')
|
const searchTerm = ref('D')
|
||||||
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
||||||
const selected = ref('Backlog')
|
const value = ref('Backlog')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UInputMenu v-model="selected" v-model:search-term="searchTerm" :items="items" />
|
<UInputMenu v-model="value" v-model:search-term="searchTerm" :items="items" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const value = ref('')
|
||||||
|
const domains = ['.com', '.dev', '.org']
|
||||||
|
const domain = ref(domains[0])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UButtonGroup>
|
||||||
|
<UInput
|
||||||
|
v-model="value"
|
||||||
|
placeholder="nuxt"
|
||||||
|
:ui="{
|
||||||
|
base: 'pl-[57px]',
|
||||||
|
leading: 'pointer-events-none'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #leading>
|
||||||
|
<p class="text-sm text-[var(--ui-text-muted)]">
|
||||||
|
https://
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
</UInput>
|
||||||
|
|
||||||
|
<USelectMenu v-model="domain" :items="domains" />
|
||||||
|
</UButtonGroup>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const value = ref('')
|
||||||
|
const maxLength = 15
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UInput
|
||||||
|
v-model="value"
|
||||||
|
:maxlength="maxLength"
|
||||||
|
aria-describedby="character-count"
|
||||||
|
:ui="{ trailing: 'pointer-events-none' }"
|
||||||
|
>
|
||||||
|
<template #trailing>
|
||||||
|
<div
|
||||||
|
id="character-count"
|
||||||
|
class="text-xs text-[var(--ui-text-muted)] tabular-nums"
|
||||||
|
aria-live="polite"
|
||||||
|
role="status"
|
||||||
|
>
|
||||||
|
{{ value?.length }}/{{ maxLength }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UInput>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const value = ref('Click to clear')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UInput
|
||||||
|
v-model="value"
|
||||||
|
placeholder="Type something..."
|
||||||
|
:ui="{ trailing: 'pr-0.5' }"
|
||||||
|
>
|
||||||
|
<template v-if="value?.length" #trailing>
|
||||||
|
<UButton
|
||||||
|
color="neutral"
|
||||||
|
variant="link"
|
||||||
|
size="sm"
|
||||||
|
icon="i-heroicons-x-circle"
|
||||||
|
aria-label="Clear input"
|
||||||
|
@click="value = ''"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</UInput>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const value = ref('')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UInput v-model="value" placeholder="" :ui="{ base: 'peer' }">
|
||||||
|
<label class="pointer-events-none absolute left-0 -top-2.5 text-[var(--ui-text-highlighted)] text-xs font-medium px-1.5 transition-all peer-focus:-top-2.5 peer-focus:text-[var(--ui-text-highlighted)] peer-focus:text-xs peer-focus:font-medium peer-placeholder-shown:text-sm peer-placeholder-shown:text-[var(--ui-text-dimmed)] peer-placeholder-shown:top-1.5 peer-placeholder-shown:font-normal">
|
||||||
|
<span class="inline-flex bg-[var(--ui-bg)] px-1">Email address</span>
|
||||||
|
</label>
|
||||||
|
</UInput>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const email = ref('')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UFormField label="Email" help="We won't share your email." required>
|
||||||
|
<UInput v-model="email" placeholder="Enter your email" icon="i-heroicons-at-symbol" />
|
||||||
|
</UFormField>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const show = ref(false)
|
||||||
|
const password = ref('')
|
||||||
|
|
||||||
|
function checkStrength(str: string) {
|
||||||
|
const requirements = [
|
||||||
|
{ regex: /.{8,}/, text: 'At least 8 characters' },
|
||||||
|
{ regex: /\d/, text: 'At least 1 number' },
|
||||||
|
{ regex: /[a-z]/, text: 'At least 1 lowercase letter' },
|
||||||
|
{ regex: /[A-Z]/, text: 'At least 1 uppercase letter' }
|
||||||
|
]
|
||||||
|
|
||||||
|
return requirements.map(req => ({ met: req.regex.test(str), text: req.text }))
|
||||||
|
}
|
||||||
|
|
||||||
|
const strength = computed(() => checkStrength(password.value))
|
||||||
|
const score = computed(() => strength.value.filter(req => req.met).length)
|
||||||
|
|
||||||
|
const color = computed(() => {
|
||||||
|
if (score.value === 0) return 'neutral'
|
||||||
|
if (score.value <= 1) return 'error'
|
||||||
|
if (score.value <= 2) return 'warning'
|
||||||
|
if (score.value === 3) return 'warning'
|
||||||
|
return 'success'
|
||||||
|
})
|
||||||
|
|
||||||
|
const text = computed(() => {
|
||||||
|
if (score.value === 0) return 'Enter a password'
|
||||||
|
if (score.value <= 2) return 'Weak password'
|
||||||
|
if (score.value === 3) return 'Medium password'
|
||||||
|
return 'Strong password'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<UFormField label="Password">
|
||||||
|
<UInput
|
||||||
|
v-model="password"
|
||||||
|
placeholder="Password"
|
||||||
|
:color="color"
|
||||||
|
:type="show ? 'text' : 'password'"
|
||||||
|
:ui="{ trailing: 'pr-0.5' }"
|
||||||
|
:aria-invalid="score < 4"
|
||||||
|
aria-describedby="password-strength"
|
||||||
|
class="w-full"
|
||||||
|
>
|
||||||
|
<template #trailing>
|
||||||
|
<UButton
|
||||||
|
color="neutral"
|
||||||
|
variant="link"
|
||||||
|
size="sm"
|
||||||
|
:icon="show ? 'i-heroicons-eye-slash' : 'i-heroicons-eye'"
|
||||||
|
aria-label="show ? 'Hide password' : 'Show password'"
|
||||||
|
:aria-pressed="show"
|
||||||
|
aria-controls="password"
|
||||||
|
@click="show = !show"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</UInput>
|
||||||
|
</UFormField>
|
||||||
|
|
||||||
|
<UProgress
|
||||||
|
:color="color"
|
||||||
|
:indicator="text"
|
||||||
|
:model-value="score"
|
||||||
|
:max="4"
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<p id="password-strength" class="text-sm font-medium">
|
||||||
|
{{ text }}. Must contain:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul class="space-y-1" aria-label="Password requirements">
|
||||||
|
<li
|
||||||
|
v-for="(req, index) in strength"
|
||||||
|
:key="index"
|
||||||
|
class="flex items-center gap-0.5"
|
||||||
|
:class="req.met ? 'text-[var(--ui-success)]' : 'text-[var(--ui-text-muted)]'"
|
||||||
|
>
|
||||||
|
<UIcon :name="req.met ? 'i-heroicons-check-circle' : 'i-heroicons-x-circle'" class="size-4 shrink-0" />
|
||||||
|
|
||||||
|
<span class="text-xs font-light">
|
||||||
|
{{ req.text }}
|
||||||
|
<span class="sr-only">
|
||||||
|
{{ req.met ? ' - Requirement met' : ' - Requirement not met' }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const show = ref(false)
|
||||||
|
const password = ref('password')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UInput
|
||||||
|
v-model="password"
|
||||||
|
placeholder="Password"
|
||||||
|
:type="show ? 'text' : 'password'"
|
||||||
|
:ui="{ trailing: 'pr-0.5' }"
|
||||||
|
>
|
||||||
|
<template #trailing>
|
||||||
|
<UButton
|
||||||
|
color="neutral"
|
||||||
|
variant="link"
|
||||||
|
size="sm"
|
||||||
|
:icon="show ? 'i-heroicons-eye-slash' : 'i-heroicons-eye'"
|
||||||
|
aria-label="show ? 'Hide password' : 'Show password'"
|
||||||
|
:aria-pressed="show"
|
||||||
|
aria-controls="password"
|
||||||
|
@click="show = !show"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</UInput>
|
||||||
|
</template>
|
||||||
@@ -32,7 +32,7 @@ const groups = computed(() => [{
|
|||||||
:loading="status === 'pending'"
|
:loading="status === 'pending'"
|
||||||
:groups="groups"
|
:groups="groups"
|
||||||
placeholder="Search users..."
|
placeholder="Search users..."
|
||||||
class="h-96"
|
class="h-80"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</UModal>
|
</UModal>
|
||||||
|
|||||||
@@ -37,22 +37,22 @@ const items = [
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
label: 'defineShortcuts',
|
label: 'defineShortcuts',
|
||||||
icon: 'i-heroicons-document-text-20-solid',
|
icon: 'i-heroicons-document-text',
|
||||||
description: 'Define shortcuts for your application.'
|
description: 'Define shortcuts for your application.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'useModal',
|
label: 'useModal',
|
||||||
icon: 'i-heroicons-document-text-20-solid',
|
icon: 'i-heroicons-document-text',
|
||||||
description: 'Display a modal within your application.'
|
description: 'Display a modal within your application.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'useSlideover',
|
label: 'useSlideover',
|
||||||
icon: 'i-heroicons-document-text-20-solid',
|
icon: 'i-heroicons-document-text',
|
||||||
description: 'Display a slideover within your application.'
|
description: 'Display a slideover within your application.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'useToast',
|
label: 'useToast',
|
||||||
icon: 'i-heroicons-document-text-20-solid',
|
icon: 'i-heroicons-document-text',
|
||||||
description: 'Display a toast within your application.'
|
description: 'Display a toast within your application.'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -63,45 +63,50 @@ const items = [
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
label: 'Link',
|
label: 'Link',
|
||||||
icon: 'i-heroicons-document-text-20-solid',
|
icon: 'i-heroicons-document-text',
|
||||||
description: 'Use NuxtLink with superpowers.'
|
description: 'Use NuxtLink with superpowers.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Modal',
|
label: 'Modal',
|
||||||
icon: 'i-heroicons-document-text-20-solid',
|
icon: 'i-heroicons-document-text',
|
||||||
description: 'Display a modal within your application.'
|
description: 'Display a modal within your application.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'NavigationMenu',
|
label: 'NavigationMenu',
|
||||||
icon: 'i-heroicons-document-text-20-solid',
|
icon: 'i-heroicons-document-text',
|
||||||
description: 'Display a list of links.'
|
description: 'Display a list of links.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Pagination',
|
label: 'Pagination',
|
||||||
icon: 'i-heroicons-document-text-20-solid',
|
icon: 'i-heroicons-document-text',
|
||||||
description: 'Display a list of pages.'
|
description: 'Display a list of pages.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Popover',
|
label: 'Popover',
|
||||||
icon: 'i-heroicons-document-text-20-solid',
|
icon: 'i-heroicons-document-text',
|
||||||
description: 'Display a non-modal dialog that floats around a trigger element.'
|
description: 'Display a non-modal dialog that floats around a trigger element.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Progress',
|
label: 'Progress',
|
||||||
icon: 'i-heroicons-document-text-20-solid',
|
icon: 'i-heroicons-document-text',
|
||||||
description: 'Show a horizontal bar to indicate task progression.'
|
description: 'Show a horizontal bar to indicate task progression.'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const active = ref('0')
|
const active = ref()
|
||||||
|
|
||||||
// Note: This is for demonstration purposes only. Don't do this at home.
|
defineShortcuts({
|
||||||
onMounted(() => {
|
1: () => {
|
||||||
setInterval(() => {
|
active.value = '0'
|
||||||
active.value = String((Number(active.value) + 1) % items.length)
|
},
|
||||||
}, 2000)
|
2: () => {
|
||||||
|
active.value = '1'
|
||||||
|
},
|
||||||
|
3: () => {
|
||||||
|
active.value = '2'
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
|||||||
<UAvatar
|
<UAvatar
|
||||||
v-if="modelValue"
|
v-if="modelValue"
|
||||||
v-bind="modelValue.avatar"
|
v-bind="modelValue.avatar"
|
||||||
:size="ui.itemLeadingAvatarSize()"
|
:size="ui.leadingAvatarSize()"
|
||||||
:class="ui.itemLeadingAvatar()"
|
:class="ui.leadingAvatar()"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
|||||||
<UAvatar
|
<UAvatar
|
||||||
v-if="modelValue"
|
v-if="modelValue"
|
||||||
v-bind="modelValue.avatar"
|
v-bind="modelValue.avatar"
|
||||||
:size="ui.itemLeadingAvatarSize()"
|
:size="ui.leadingAvatarSize()"
|
||||||
:class="ui.itemLeadingAvatar()"
|
:class="ui.leadingAvatar()"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</USelectMenu>
|
</USelectMenu>
|
||||||
|
|||||||
@@ -25,15 +25,15 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
|
|||||||
<UAvatar
|
<UAvatar
|
||||||
v-if="modelValue"
|
v-if="modelValue"
|
||||||
v-bind="modelValue.avatar"
|
v-bind="modelValue.avatar"
|
||||||
:size="ui.itemLeadingAvatarSize()"
|
:size="ui.leadingAvatarSize()"
|
||||||
:class="ui.itemLeadingAvatar()"
|
:class="ui.leadingAvatar()"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #item-label="{ item }">
|
<template #item-label="{ item }">
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
|
|
||||||
<span class="text-[--ui-text-muted]">
|
<span class="text-[var(--ui-text-muted)]">
|
||||||
{{ item.email }}
|
{{ item.email }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
||||||
const selected = ref('Backlog')
|
const value = ref('Backlog')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<USelectMenu
|
<USelectMenu
|
||||||
v-model="selected"
|
v-model="value"
|
||||||
:items="items"
|
:items="items"
|
||||||
:ui="{
|
:ui="{
|
||||||
trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200'
|
trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200'
|
||||||
}"
|
}"
|
||||||
|
class="w-48"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -25,18 +25,9 @@ const items = ref([
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
const selected = ref(items.value[0])
|
const value = ref(items.value[0])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<USelectMenu v-model="selected" :items="items" class="w-40">
|
<USelectMenu v-model="value" :avatar="value?.avatar" :items="items" class="w-48" />
|
||||||
<template #leading="{ modelValue, ui }">
|
|
||||||
<UAvatar
|
|
||||||
v-if="modelValue"
|
|
||||||
v-bind="modelValue.avatar"
|
|
||||||
:size="ui.itemLeadingAvatarSize()"
|
|
||||||
:class="ui.itemLeadingAvatar()"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</USelectMenu>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ const items = ref([
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
const selected = ref(items.value[0])
|
const value = ref(items.value[0])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<USelectMenu v-model="selected" :items="items" class="w-40">
|
<USelectMenu v-model="value" :items="items" class="w-48">
|
||||||
<template #leading="{ modelValue, ui }">
|
<template #leading="{ modelValue, ui }">
|
||||||
<UChip
|
<UChip
|
||||||
v-if="modelValue"
|
v-if="modelValue"
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ const items = ref([
|
|||||||
icon: 'i-heroicons-check-circle'
|
icon: 'i-heroicons-check-circle'
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
const selected = ref(items.value[0])
|
const value = ref(items.value[0])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<USelectMenu v-model="selected" :icon="selected?.icon" :items="items" class="w-40" />
|
<USelectMenu v-model="value" :icon="value?.icon" :items="items" class="w-48" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const open = ref(false)
|
const open = ref(false)
|
||||||
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
||||||
const selected = ref('Backlog')
|
const value = ref('Backlog')
|
||||||
|
|
||||||
defineShortcuts({
|
defineShortcuts({
|
||||||
o: () => open.value = !open.value
|
o: () => open.value = !open.value
|
||||||
@@ -9,5 +9,5 @@ defineShortcuts({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<USelectMenu v-model="selected" v-model:open="open" :items="items" />
|
<USelectMenu v-model="value" v-model:open="open" :items="items" class="w-48" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const searchTerm = ref('D')
|
const searchTerm = ref('D')
|
||||||
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
||||||
const selected = ref('Backlog')
|
const value = ref('Backlog')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<USelectMenu v-model="selected" v-model:search-term="searchTerm" :items="items" />
|
<USelectMenu v-model="value" v-model:search-term="searchTerm" :items="items" class="w-48" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ function getUserAvatar(value: string) {
|
|||||||
<UAvatar
|
<UAvatar
|
||||||
v-if="modelValue"
|
v-if="modelValue"
|
||||||
v-bind="getUserAvatar(modelValue)"
|
v-bind="getUserAvatar(modelValue)"
|
||||||
:size="ui.itemLeadingAvatarSize()"
|
:size="ui.leadingAvatarSize()"
|
||||||
:class="ui.itemLeadingAvatar()"
|
:class="ui.leadingAvatar()"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</USelect>
|
</USelect>
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
||||||
|
const value = ref('Backlog')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<USelect
|
<USelect
|
||||||
default-value="Backlog"
|
v-model="value"
|
||||||
:items="items"
|
:items="items"
|
||||||
:ui="{
|
:ui="{
|
||||||
trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200'
|
trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200'
|
||||||
}"
|
}"
|
||||||
|
class="w-48"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -25,21 +25,11 @@ const items = ref([
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
const value = ref(items.value[0]?.value)
|
||||||
|
|
||||||
function getAvatar(value: string) {
|
const avatar = computed(() => items.value.find(item => item.value === value.value)?.avatar)
|
||||||
return items.value.find(item => item.value === value)?.avatar
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<USelect default-value="benjamincanac" :items="items" class="w-40">
|
<USelect v-model="value" :avatar="avatar" :items="items" class="w-48" />
|
||||||
<template #leading="{ modelValue, ui }">
|
|
||||||
<UAvatar
|
|
||||||
v-if="modelValue"
|
|
||||||
v-bind="getAvatar(modelValue)"
|
|
||||||
:size="ui.itemLeadingAvatarSize()"
|
|
||||||
:class="ui.itemLeadingAvatar()"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</USelect>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const items = ref([
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
const value = ref(items.value[0]?.value)
|
||||||
|
|
||||||
function getChip(value: string) {
|
function getChip(value: string) {
|
||||||
return items.value.find(item => item.value === value)?.chip
|
return items.value.find(item => item.value === value)?.chip
|
||||||
@@ -29,7 +30,7 @@ function getChip(value: string) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<USelect default-value="bug" :items="items" class="w-40">
|
<USelect v-model="value" :items="items" class="w-48">
|
||||||
<template #leading="{ modelValue, ui }">
|
<template #leading="{ modelValue, ui }">
|
||||||
<UChip
|
<UChip
|
||||||
v-if="modelValue"
|
v-if="modelValue"
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const selected = ref('backlog')
|
|
||||||
const items = ref([
|
const items = ref([
|
||||||
{
|
{
|
||||||
label: 'Backlog',
|
label: 'Backlog',
|
||||||
@@ -22,10 +21,11 @@ const items = ref([
|
|||||||
icon: 'i-heroicons-check-circle'
|
icon: 'i-heroicons-check-circle'
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
const value = ref(items.value[0]?.value)
|
||||||
|
|
||||||
const icon = computed(() => items.value.find(item => item.value === selected.value)?.icon)
|
const icon = computed(() => items.value.find(item => item.value === value.value)?.icon)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<USelect v-model="selected" :icon="icon" :items="items" class="w-40" />
|
<USelect v-model="value" :icon="icon" :items="items" class="w-48" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const open = ref(false)
|
const open = ref(false)
|
||||||
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done'])
|
||||||
|
const value = ref('Backlog')
|
||||||
|
|
||||||
defineShortcuts({
|
defineShortcuts({
|
||||||
o: () => open.value = !open.value
|
o: () => open.value = !open.value
|
||||||
@@ -8,5 +9,5 @@ defineShortcuts({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<USelect v-model:open="open" default-value="Backlog" :items="items" />
|
<USelect v-model="value" v-model:open="open" :items="items" class="w-48" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { h, resolveComponent } from 'vue'
|
||||||
|
import type { TableColumn } from '@nuxt/ui'
|
||||||
|
|
||||||
|
const UBadge = resolveComponent('UBadge')
|
||||||
|
|
||||||
|
type Payment = {
|
||||||
|
id: string
|
||||||
|
date: string
|
||||||
|
status: 'paid' | 'failed' | 'refunded'
|
||||||
|
email: string
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = ref<Payment[]>([{
|
||||||
|
id: '4600',
|
||||||
|
date: '2024-03-11T15:30:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'james.anderson@example.com',
|
||||||
|
amount: 594
|
||||||
|
}, {
|
||||||
|
id: '4599',
|
||||||
|
date: '2024-03-11T10:10:00',
|
||||||
|
status: 'failed',
|
||||||
|
email: 'mia.white@example.com',
|
||||||
|
amount: 276
|
||||||
|
}, {
|
||||||
|
id: '4598',
|
||||||
|
date: '2024-03-11T08:50:00',
|
||||||
|
status: 'refunded',
|
||||||
|
email: 'william.brown@example.com',
|
||||||
|
amount: 315
|
||||||
|
}, {
|
||||||
|
id: '4597',
|
||||||
|
date: '2024-03-10T19:45:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'emma.davis@example.com',
|
||||||
|
amount: 529
|
||||||
|
}, {
|
||||||
|
id: '4596',
|
||||||
|
date: '2024-03-10T15:55:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'ethan.harris@example.com',
|
||||||
|
amount: 639
|
||||||
|
}])
|
||||||
|
|
||||||
|
const columns: TableColumn<Payment>[] = [{
|
||||||
|
accessorKey: 'id',
|
||||||
|
header: '#',
|
||||||
|
cell: ({ row }) => `#${row.getValue('id')}`
|
||||||
|
}, {
|
||||||
|
accessorKey: 'date',
|
||||||
|
header: 'Date',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return new Date(row.getValue('date')).toLocaleString('en-US', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'status',
|
||||||
|
header: 'Status',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const color = ({
|
||||||
|
paid: 'success' as const,
|
||||||
|
failed: 'error' as const,
|
||||||
|
refunded: 'neutral' as const
|
||||||
|
})[row.getValue('status') as string]
|
||||||
|
|
||||||
|
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'email',
|
||||||
|
header: 'Email'
|
||||||
|
}, {
|
||||||
|
accessorKey: 'amount',
|
||||||
|
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const amount = Number.parseFloat(row.getValue('amount'))
|
||||||
|
|
||||||
|
const formatted = new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'EUR'
|
||||||
|
}).format(amount)
|
||||||
|
|
||||||
|
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
const table = useTemplateRef('table')
|
||||||
|
|
||||||
|
const columnFilters = ref([{
|
||||||
|
id: 'email',
|
||||||
|
value: 'james'
|
||||||
|
}])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col flex-1">
|
||||||
|
<div class="flex px-4 py-3.5 border-b border-[var(--ui-border-accented)]">
|
||||||
|
<UInput
|
||||||
|
:model-value="(table?.tableApi?.getColumn('email')?.getFilterValue() as string)"
|
||||||
|
class="max-w-sm"
|
||||||
|
placeholder="Filter emails..."
|
||||||
|
@update:model-value="table?.tableApi?.getColumn('email')?.setFilterValue($event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<UTable
|
||||||
|
ref="table"
|
||||||
|
v-model:column-filters="columnFilters"
|
||||||
|
:data="data"
|
||||||
|
:columns="columns"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { h, resolveComponent } from 'vue'
|
||||||
|
import type { TableColumn } from '@nuxt/ui'
|
||||||
|
import type { Column } from '@tanstack/vue-table'
|
||||||
|
|
||||||
|
const UBadge = resolveComponent('UBadge')
|
||||||
|
const UButton = resolveComponent('UButton')
|
||||||
|
|
||||||
|
type Payment = {
|
||||||
|
id: string
|
||||||
|
date: string
|
||||||
|
status: 'paid' | 'failed' | 'refunded'
|
||||||
|
email: string
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = ref<Payment[]>([{
|
||||||
|
id: '46000000000000000000000000000000000000000',
|
||||||
|
date: '2024-03-11T15:30:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'james.anderson@example.com',
|
||||||
|
amount: 594000
|
||||||
|
}, {
|
||||||
|
id: '45990000000000000000000000000000000000000',
|
||||||
|
date: '2024-03-11T10:10:00',
|
||||||
|
status: 'failed',
|
||||||
|
email: 'mia.white@example.com',
|
||||||
|
amount: 276000
|
||||||
|
}, {
|
||||||
|
id: '45980000000000000000000000000000000000000',
|
||||||
|
date: '2024-03-11T08:50:00',
|
||||||
|
status: 'refunded',
|
||||||
|
email: 'william.brown@example.com',
|
||||||
|
amount: 315000
|
||||||
|
}, {
|
||||||
|
id: '45970000000000000000000000000000000000000',
|
||||||
|
date: '2024-03-10T19:45:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'emma.davis@example.com',
|
||||||
|
amount: 5290000
|
||||||
|
}, {
|
||||||
|
id: '45960000000000000000000000000000000000000',
|
||||||
|
date: '2024-03-10T15:55:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'ethan.harris@example.com',
|
||||||
|
amount: 639000
|
||||||
|
}])
|
||||||
|
|
||||||
|
const columns: TableColumn<Payment>[] = [{
|
||||||
|
accessorKey: 'id',
|
||||||
|
header: ({ column }) => getHeader(column, 'ID', 'left'),
|
||||||
|
cell: ({ row }) => `#${row.getValue('id')}`
|
||||||
|
}, {
|
||||||
|
accessorKey: 'date',
|
||||||
|
header: ({ column }) => getHeader(column, 'Date', 'left')
|
||||||
|
}, {
|
||||||
|
accessorKey: 'status',
|
||||||
|
header: ({ column }) => getHeader(column, 'Status', 'left'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const color = ({
|
||||||
|
paid: 'success' as const,
|
||||||
|
failed: 'error' as const,
|
||||||
|
refunded: 'neutral' as const
|
||||||
|
})[row.getValue('status') as string]
|
||||||
|
|
||||||
|
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'email',
|
||||||
|
header: ({ column }) => getHeader(column, 'Email', 'left')
|
||||||
|
}, {
|
||||||
|
accessorKey: 'amount',
|
||||||
|
header: ({ column }) => h('div', { class: 'text-right' }, getHeader(column, 'Amount', 'right')),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const amount = Number.parseFloat(row.getValue('amount'))
|
||||||
|
|
||||||
|
const formatted = new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'EUR'
|
||||||
|
}).format(amount)
|
||||||
|
|
||||||
|
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
function getHeader(column: Column<Payment>, label: string, position: 'left' | 'right') {
|
||||||
|
const isPinned = column.getIsPinned()
|
||||||
|
|
||||||
|
return h(UButton, {
|
||||||
|
color: 'neutral',
|
||||||
|
variant: 'ghost',
|
||||||
|
label,
|
||||||
|
icon: isPinned ? 'i-heroicons-star-20-solid' : 'i-heroicons-star',
|
||||||
|
class: '-mx-2.5',
|
||||||
|
onClick() {
|
||||||
|
column.pin(isPinned === position ? false : position)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const columnPinning = ref({
|
||||||
|
left: [],
|
||||||
|
right: ['amount']
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UTable
|
||||||
|
v-model:column-pinning="columnPinning"
|
||||||
|
:data="data"
|
||||||
|
:columns="columns"
|
||||||
|
class="flex-1"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { h, resolveComponent } from 'vue'
|
||||||
|
import type { TableColumn } from '@nuxt/ui'
|
||||||
|
|
||||||
|
const UBadge = resolveComponent('UBadge')
|
||||||
|
const UButton = resolveComponent('UButton')
|
||||||
|
|
||||||
|
type Payment = {
|
||||||
|
id: string
|
||||||
|
date: string
|
||||||
|
status: 'paid' | 'failed' | 'refunded'
|
||||||
|
email: string
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = ref<Payment[]>([{
|
||||||
|
id: '4600',
|
||||||
|
date: '2024-03-11T15:30:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'james.anderson@example.com',
|
||||||
|
amount: 594
|
||||||
|
}, {
|
||||||
|
id: '4599',
|
||||||
|
date: '2024-03-11T10:10:00',
|
||||||
|
status: 'failed',
|
||||||
|
email: 'mia.white@example.com',
|
||||||
|
amount: 276
|
||||||
|
}, {
|
||||||
|
id: '4598',
|
||||||
|
date: '2024-03-11T08:50:00',
|
||||||
|
status: 'refunded',
|
||||||
|
email: 'william.brown@example.com',
|
||||||
|
amount: 315
|
||||||
|
}, {
|
||||||
|
id: '4597',
|
||||||
|
date: '2024-03-10T19:45:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'emma.davis@example.com',
|
||||||
|
amount: 529
|
||||||
|
}, {
|
||||||
|
id: '4596',
|
||||||
|
date: '2024-03-10T15:55:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'ethan.harris@example.com',
|
||||||
|
amount: 639
|
||||||
|
}])
|
||||||
|
|
||||||
|
const columns: TableColumn<Payment>[] = [{
|
||||||
|
accessorKey: 'id',
|
||||||
|
header: '#',
|
||||||
|
cell: ({ row }) => `#${row.getValue('id')}`
|
||||||
|
}, {
|
||||||
|
accessorKey: 'date',
|
||||||
|
header: 'Date',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return new Date(row.getValue('date')).toLocaleString('en-US', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'status',
|
||||||
|
header: 'Status',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const color = ({
|
||||||
|
paid: 'success' as const,
|
||||||
|
failed: 'error' as const,
|
||||||
|
refunded: 'neutral' as const
|
||||||
|
})[row.getValue('status') as string]
|
||||||
|
|
||||||
|
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'email',
|
||||||
|
header: ({ column }) => {
|
||||||
|
const isSorted = column.getIsSorted()
|
||||||
|
|
||||||
|
return h(UButton, {
|
||||||
|
color: 'neutral',
|
||||||
|
variant: 'ghost',
|
||||||
|
label: 'Email',
|
||||||
|
icon: isSorted ? (isSorted === 'asc' ? 'i-heroicons-bars-arrow-up-20-solid' : 'i-heroicons-bars-arrow-down-20-solid') : 'i-heroicons-arrows-up-down-20-solid',
|
||||||
|
class: '-mx-2.5',
|
||||||
|
onClick: () => column.toggleSorting(column.getIsSorted() === 'asc')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'amount',
|
||||||
|
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const amount = Number.parseFloat(row.getValue('amount'))
|
||||||
|
|
||||||
|
const formatted = new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'EUR'
|
||||||
|
}).format(amount)
|
||||||
|
|
||||||
|
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
const sorting = ref([{
|
||||||
|
id: 'email',
|
||||||
|
desc: false
|
||||||
|
}])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UTable
|
||||||
|
v-model:sorting="sorting"
|
||||||
|
:data="data"
|
||||||
|
:columns="columns"
|
||||||
|
class="flex-1"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { h, resolveComponent } from 'vue'
|
||||||
|
import type { TableColumn } from '@nuxt/ui'
|
||||||
|
import type { Column } from '@tanstack/vue-table'
|
||||||
|
|
||||||
|
const UBadge = resolveComponent('UBadge')
|
||||||
|
const UButton = resolveComponent('UButton')
|
||||||
|
const UDropdownMenu = resolveComponent('UDropdownMenu')
|
||||||
|
|
||||||
|
type Payment = {
|
||||||
|
id: string
|
||||||
|
date: string
|
||||||
|
status: 'paid' | 'failed' | 'refunded'
|
||||||
|
email: string
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = ref<Payment[]>([{
|
||||||
|
id: '4600',
|
||||||
|
date: '2024-03-11T15:30:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'james.anderson@example.com',
|
||||||
|
amount: 594
|
||||||
|
}, {
|
||||||
|
id: '4599',
|
||||||
|
date: '2024-03-11T10:10:00',
|
||||||
|
status: 'failed',
|
||||||
|
email: 'mia.white@example.com',
|
||||||
|
amount: 276
|
||||||
|
}, {
|
||||||
|
id: '4598',
|
||||||
|
date: '2024-03-11T08:50:00',
|
||||||
|
status: 'refunded',
|
||||||
|
email: 'william.brown@example.com',
|
||||||
|
amount: 315
|
||||||
|
}, {
|
||||||
|
id: '4597',
|
||||||
|
date: '2024-03-10T19:45:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'emma.davis@example.com',
|
||||||
|
amount: 529
|
||||||
|
}, {
|
||||||
|
id: '4596',
|
||||||
|
date: '2024-03-10T15:55:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'ethan.harris@example.com',
|
||||||
|
amount: 639
|
||||||
|
}])
|
||||||
|
|
||||||
|
const columns: TableColumn<Payment>[] = [{
|
||||||
|
accessorKey: 'id',
|
||||||
|
header: ({ column }) => getHeader(column, 'ID'),
|
||||||
|
cell: ({ row }) => `#${row.getValue('id')}`
|
||||||
|
}, {
|
||||||
|
accessorKey: 'date',
|
||||||
|
header: ({ column }) => getHeader(column, 'Date'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return new Date(row.getValue('date')).toLocaleString('en-US', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'status',
|
||||||
|
header: ({ column }) => getHeader(column, 'Status'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const color = ({
|
||||||
|
paid: 'success' as const,
|
||||||
|
failed: 'error' as const,
|
||||||
|
refunded: 'neutral' as const
|
||||||
|
})[row.getValue('status') as string]
|
||||||
|
|
||||||
|
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'email',
|
||||||
|
header: ({ column }) => getHeader(column, 'Email')
|
||||||
|
}, {
|
||||||
|
accessorKey: 'amount',
|
||||||
|
header: ({ column }) => h('div', { class: 'text-right' }, getHeader(column, 'Amount')),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const amount = Number.parseFloat(row.getValue('amount'))
|
||||||
|
|
||||||
|
const formatted = new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'EUR'
|
||||||
|
}).format(amount)
|
||||||
|
|
||||||
|
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
function getHeader(column: Column<Payment>, label: string) {
|
||||||
|
const isSorted = column.getIsSorted()
|
||||||
|
|
||||||
|
return h(UDropdownMenu, {
|
||||||
|
content: {
|
||||||
|
align: 'start'
|
||||||
|
},
|
||||||
|
items: [{
|
||||||
|
label: 'Asc',
|
||||||
|
type: 'checkbox',
|
||||||
|
icon: 'i-heroicons-bars-arrow-up-20-solid',
|
||||||
|
checked: isSorted === 'asc',
|
||||||
|
onSelect: () => {
|
||||||
|
if (isSorted === 'asc') {
|
||||||
|
column.clearSorting()
|
||||||
|
} else {
|
||||||
|
column.toggleSorting(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
label: 'Desc',
|
||||||
|
icon: 'i-heroicons-bars-arrow-down-20-solid',
|
||||||
|
type: 'checkbox',
|
||||||
|
checked: isSorted === 'desc',
|
||||||
|
onSelect: () => {
|
||||||
|
if (isSorted === 'desc') {
|
||||||
|
column.clearSorting()
|
||||||
|
} else {
|
||||||
|
column.toggleSorting(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}, () => h(UButton, {
|
||||||
|
color: 'neutral',
|
||||||
|
variant: 'ghost',
|
||||||
|
label,
|
||||||
|
icon: isSorted ? (isSorted === 'asc' ? 'i-heroicons-bars-arrow-up-20-solid' : 'i-heroicons-bars-arrow-down-20-solid') : 'i-heroicons-arrows-up-down-20-solid',
|
||||||
|
class: '-mx-2.5 data-[state=open]:bg-[var(--ui-bg-elevated)]'
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
const sorting = ref([{
|
||||||
|
id: 'id',
|
||||||
|
desc: false
|
||||||
|
}])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UTable
|
||||||
|
v-model:sorting="sorting"
|
||||||
|
:data="data"
|
||||||
|
:columns="columns"
|
||||||
|
class="flex-1"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { h, resolveComponent } from 'vue'
|
||||||
|
import { upperFirst } from 'scule'
|
||||||
|
import type { TableColumn } from '@nuxt/ui'
|
||||||
|
|
||||||
|
const UBadge = resolveComponent('UBadge')
|
||||||
|
|
||||||
|
type Payment = {
|
||||||
|
id: string
|
||||||
|
date: string
|
||||||
|
status: 'paid' | 'failed' | 'refunded'
|
||||||
|
email: string
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = ref<Payment[]>([{
|
||||||
|
id: '4600',
|
||||||
|
date: '2024-03-11T15:30:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'james.anderson@example.com',
|
||||||
|
amount: 594
|
||||||
|
}, {
|
||||||
|
id: '4599',
|
||||||
|
date: '2024-03-11T10:10:00',
|
||||||
|
status: 'failed',
|
||||||
|
email: 'mia.white@example.com',
|
||||||
|
amount: 276
|
||||||
|
}, {
|
||||||
|
id: '4598',
|
||||||
|
date: '2024-03-11T08:50:00',
|
||||||
|
status: 'refunded',
|
||||||
|
email: 'william.brown@example.com',
|
||||||
|
amount: 315
|
||||||
|
}, {
|
||||||
|
id: '4597',
|
||||||
|
date: '2024-03-10T19:45:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'emma.davis@example.com',
|
||||||
|
amount: 529
|
||||||
|
}, {
|
||||||
|
id: '4596',
|
||||||
|
date: '2024-03-10T15:55:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'ethan.harris@example.com',
|
||||||
|
amount: 639
|
||||||
|
}])
|
||||||
|
|
||||||
|
const columns: TableColumn<Payment>[] = [{
|
||||||
|
accessorKey: 'id',
|
||||||
|
header: '#',
|
||||||
|
cell: ({ row }) => `#${row.getValue('id')}`
|
||||||
|
}, {
|
||||||
|
accessorKey: 'date',
|
||||||
|
header: 'Date',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return new Date(row.getValue('date')).toLocaleString('en-US', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'status',
|
||||||
|
header: 'Status',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const color = ({
|
||||||
|
paid: 'success' as const,
|
||||||
|
failed: 'error' as const,
|
||||||
|
refunded: 'neutral' as const
|
||||||
|
})[row.getValue('status') as string]
|
||||||
|
|
||||||
|
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'email',
|
||||||
|
header: 'Email'
|
||||||
|
}, {
|
||||||
|
accessorKey: 'amount',
|
||||||
|
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const amount = Number.parseFloat(row.getValue('amount'))
|
||||||
|
|
||||||
|
const formatted = new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'EUR'
|
||||||
|
}).format(amount)
|
||||||
|
|
||||||
|
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
const table = useTemplateRef('table')
|
||||||
|
|
||||||
|
const columnVisibility = ref({
|
||||||
|
id: false
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col flex-1">
|
||||||
|
<div class="flex justify-end px-4 py-3.5 border-b border-[var(--ui-border-accented)]">
|
||||||
|
<UDropdownMenu
|
||||||
|
:items="table?.tableApi?.getAllColumns().filter(column => column.getCanHide()).map(column => ({
|
||||||
|
label: upperFirst(column.id),
|
||||||
|
type: 'checkbox' as const,
|
||||||
|
checked: column.getIsVisible(),
|
||||||
|
onUpdateChecked(checked: boolean) {
|
||||||
|
table?.tableApi?.getColumn(column.id)?.toggleVisibility(!!checked)
|
||||||
|
},
|
||||||
|
onSelect(e?: Event) {
|
||||||
|
e?.preventDefault()
|
||||||
|
}
|
||||||
|
}))"
|
||||||
|
:content="{ align: 'end' }"
|
||||||
|
>
|
||||||
|
<UButton
|
||||||
|
label="Columns"
|
||||||
|
color="neutral"
|
||||||
|
variant="outline"
|
||||||
|
trailing-icon="i-heroicons-chevron-down-20-solid"
|
||||||
|
/>
|
||||||
|
</UDropdownMenu>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<UTable
|
||||||
|
ref="table"
|
||||||
|
v-model:column-visibility="columnVisibility"
|
||||||
|
:data="data"
|
||||||
|
:columns="columns"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { h, resolveComponent } from 'vue'
|
||||||
|
import type { TableColumn } from '@nuxt/ui'
|
||||||
|
|
||||||
|
const UBadge = resolveComponent('UBadge')
|
||||||
|
|
||||||
|
type Payment = {
|
||||||
|
id: string
|
||||||
|
date: string
|
||||||
|
status: 'paid' | 'failed' | 'refunded'
|
||||||
|
email: string
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = ref<Payment[]>([{
|
||||||
|
id: '4600',
|
||||||
|
date: '2024-03-11T15:30:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'james.anderson@example.com',
|
||||||
|
amount: 594
|
||||||
|
}, {
|
||||||
|
id: '4599',
|
||||||
|
date: '2024-03-11T10:10:00',
|
||||||
|
status: 'failed',
|
||||||
|
email: 'mia.white@example.com',
|
||||||
|
amount: 276
|
||||||
|
}, {
|
||||||
|
id: '4598',
|
||||||
|
date: '2024-03-11T08:50:00',
|
||||||
|
status: 'refunded',
|
||||||
|
email: 'william.brown@example.com',
|
||||||
|
amount: 315
|
||||||
|
}, {
|
||||||
|
id: '4597',
|
||||||
|
date: '2024-03-10T19:45:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'emma.davis@example.com',
|
||||||
|
amount: 529
|
||||||
|
}, {
|
||||||
|
id: '4596',
|
||||||
|
date: '2024-03-10T15:55:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'ethan.harris@example.com',
|
||||||
|
amount: 639
|
||||||
|
}])
|
||||||
|
|
||||||
|
const columns: TableColumn<Payment>[] = [{
|
||||||
|
accessorKey: 'id',
|
||||||
|
header: '#',
|
||||||
|
cell: ({ row }) => `#${row.getValue('id')}`
|
||||||
|
}, {
|
||||||
|
accessorKey: 'date',
|
||||||
|
header: 'Date',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return new Date(row.getValue('date')).toLocaleString('en-US', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'status',
|
||||||
|
header: 'Status',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const color = ({
|
||||||
|
paid: 'success' as const,
|
||||||
|
failed: 'error' as const,
|
||||||
|
refunded: 'neutral' as const
|
||||||
|
})[row.getValue('status') as string]
|
||||||
|
|
||||||
|
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'email',
|
||||||
|
header: 'Email'
|
||||||
|
}, {
|
||||||
|
accessorKey: 'amount',
|
||||||
|
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const amount = Number.parseFloat(row.getValue('amount'))
|
||||||
|
|
||||||
|
const formatted = new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'EUR'
|
||||||
|
}).format(amount)
|
||||||
|
|
||||||
|
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UTable :data="data" :columns="columns" class="flex-1" />
|
||||||
|
</template>
|
||||||
319
docs/app/components/content/examples/table/TableExample.vue
Normal file
319
docs/app/components/content/examples/table/TableExample.vue
Normal file
@@ -0,0 +1,319 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { h, resolveComponent } from 'vue'
|
||||||
|
import { upperFirst } from 'scule'
|
||||||
|
import type { TableColumn } from '@nuxt/ui'
|
||||||
|
|
||||||
|
const UButton = resolveComponent('UButton')
|
||||||
|
const UCheckbox = resolveComponent('UCheckbox')
|
||||||
|
const UBadge = resolveComponent('UBadge')
|
||||||
|
const UDropdownMenu = resolveComponent('UDropdownMenu')
|
||||||
|
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
type Payment = {
|
||||||
|
id: string
|
||||||
|
date: string
|
||||||
|
status: 'paid' | 'failed' | 'refunded'
|
||||||
|
email: string
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = ref<Payment[]>([{
|
||||||
|
id: '4600',
|
||||||
|
date: '2024-03-11T15:30:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'james.anderson@example.com',
|
||||||
|
amount: 594
|
||||||
|
}, {
|
||||||
|
id: '4599',
|
||||||
|
date: '2024-03-11T10:10:00',
|
||||||
|
status: 'failed',
|
||||||
|
email: 'mia.white@example.com',
|
||||||
|
amount: 276
|
||||||
|
}, {
|
||||||
|
id: '4598',
|
||||||
|
date: '2024-03-11T08:50:00',
|
||||||
|
status: 'refunded',
|
||||||
|
email: 'william.brown@example.com',
|
||||||
|
amount: 315
|
||||||
|
}, {
|
||||||
|
id: '4597',
|
||||||
|
date: '2024-03-10T19:45:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'emma.davis@example.com',
|
||||||
|
amount: 529
|
||||||
|
}, {
|
||||||
|
id: '4596',
|
||||||
|
date: '2024-03-10T15:55:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'ethan.harris@example.com',
|
||||||
|
amount: 639
|
||||||
|
}, {
|
||||||
|
id: '4595',
|
||||||
|
date: '2024-03-10T13:40:00',
|
||||||
|
status: 'refunded',
|
||||||
|
email: 'ava.thomas@example.com',
|
||||||
|
amount: 428
|
||||||
|
}, {
|
||||||
|
id: '4594',
|
||||||
|
date: '2024-03-10T09:15:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'michael.wilson@example.com',
|
||||||
|
amount: 683
|
||||||
|
}, {
|
||||||
|
id: '4593',
|
||||||
|
date: '2024-03-09T20:25:00',
|
||||||
|
status: 'failed',
|
||||||
|
email: 'olivia.taylor@example.com',
|
||||||
|
amount: 947
|
||||||
|
}, {
|
||||||
|
id: '4592',
|
||||||
|
date: '2024-03-09T18:45:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'benjamin.jackson@example.com',
|
||||||
|
amount: 851
|
||||||
|
}, {
|
||||||
|
id: '4591',
|
||||||
|
date: '2024-03-09T16:05:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'sophia.miller@example.com',
|
||||||
|
amount: 762
|
||||||
|
}, {
|
||||||
|
id: '4590',
|
||||||
|
date: '2024-03-09T14:20:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'noah.clark@example.com',
|
||||||
|
amount: 573
|
||||||
|
}, {
|
||||||
|
id: '4589',
|
||||||
|
date: '2024-03-09T11:35:00',
|
||||||
|
status: 'failed',
|
||||||
|
email: 'isabella.lee@example.com',
|
||||||
|
amount: 389
|
||||||
|
}, {
|
||||||
|
id: '4588',
|
||||||
|
date: '2024-03-08T22:50:00',
|
||||||
|
status: 'refunded',
|
||||||
|
email: 'liam.walker@example.com',
|
||||||
|
amount: 701
|
||||||
|
}, {
|
||||||
|
id: '4587',
|
||||||
|
date: '2024-03-08T20:15:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'charlotte.hall@example.com',
|
||||||
|
amount: 856
|
||||||
|
}, {
|
||||||
|
id: '4586',
|
||||||
|
date: '2024-03-08T17:40:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'mason.young@example.com',
|
||||||
|
amount: 492
|
||||||
|
}, {
|
||||||
|
id: '4585',
|
||||||
|
date: '2024-03-08T14:55:00',
|
||||||
|
status: 'failed',
|
||||||
|
email: 'amelia.king@example.com',
|
||||||
|
amount: 637
|
||||||
|
}, {
|
||||||
|
id: '4584',
|
||||||
|
date: '2024-03-08T12:30:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'elijah.wright@example.com',
|
||||||
|
amount: 784
|
||||||
|
}, {
|
||||||
|
id: '4583',
|
||||||
|
date: '2024-03-08T09:45:00',
|
||||||
|
status: 'refunded',
|
||||||
|
email: 'harper.scott@example.com',
|
||||||
|
amount: 345
|
||||||
|
}, {
|
||||||
|
id: '4582',
|
||||||
|
date: '2024-03-07T23:10:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'evelyn.green@example.com',
|
||||||
|
amount: 918
|
||||||
|
}, {
|
||||||
|
id: '4581',
|
||||||
|
date: '2024-03-07T20:25:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'logan.baker@example.com',
|
||||||
|
amount: 567
|
||||||
|
}])
|
||||||
|
|
||||||
|
const columns: TableColumn<Payment>[] = [{
|
||||||
|
id: 'select',
|
||||||
|
header: ({ table }) => h(UCheckbox, {
|
||||||
|
'modelValue': table.getIsAllPageRowsSelected(),
|
||||||
|
'indeterminate': table.getIsSomePageRowsSelected(),
|
||||||
|
'onUpdate:modelValue': (value: boolean) => table.toggleAllPageRowsSelected(!!value),
|
||||||
|
'ariaLabel': 'Select all'
|
||||||
|
}),
|
||||||
|
cell: ({ row }) => h(UCheckbox, {
|
||||||
|
'modelValue': row.getIsSelected(),
|
||||||
|
'onUpdate:modelValue': (value: boolean) => row.toggleSelected(!!value),
|
||||||
|
'ariaLabel': 'Select row'
|
||||||
|
}),
|
||||||
|
enableSorting: false,
|
||||||
|
enableHiding: false
|
||||||
|
}, {
|
||||||
|
accessorKey: 'id',
|
||||||
|
header: '#',
|
||||||
|
cell: ({ row }) => `#${row.getValue('id')}`
|
||||||
|
}, {
|
||||||
|
accessorKey: 'date',
|
||||||
|
header: 'Date',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return new Date(row.getValue('date')).toLocaleString('en-US', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'status',
|
||||||
|
header: 'Status',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const color = ({
|
||||||
|
paid: 'success' as const,
|
||||||
|
failed: 'error' as const,
|
||||||
|
refunded: 'neutral' as const
|
||||||
|
})[row.getValue('status') as string]
|
||||||
|
|
||||||
|
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'email',
|
||||||
|
header: ({ column }) => {
|
||||||
|
const isSorted = column.getIsSorted()
|
||||||
|
|
||||||
|
return h(UButton, {
|
||||||
|
color: 'neutral',
|
||||||
|
variant: 'ghost',
|
||||||
|
label: 'Email',
|
||||||
|
icon: isSorted ? (isSorted === 'asc' ? 'i-heroicons-bars-arrow-up-20-solid' : 'i-heroicons-bars-arrow-down-20-solid') : 'i-heroicons-arrows-up-down-20-solid',
|
||||||
|
class: '-mx-2.5',
|
||||||
|
onClick: () => column.toggleSorting(column.getIsSorted() === 'asc')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
cell: ({ row }) => h('div', { class: 'lowercase' }, row.getValue('email'))
|
||||||
|
}, {
|
||||||
|
accessorKey: 'amount',
|
||||||
|
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const amount = Number.parseFloat(row.getValue('amount'))
|
||||||
|
|
||||||
|
const formatted = new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'EUR'
|
||||||
|
}).format(amount)
|
||||||
|
|
||||||
|
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
id: 'actions',
|
||||||
|
enableHiding: false,
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const items = [{
|
||||||
|
type: 'label',
|
||||||
|
label: 'Actions'
|
||||||
|
}, {
|
||||||
|
label: 'Copy payment ID',
|
||||||
|
onSelect() {
|
||||||
|
navigator.clipboard.writeText(row.original.id)
|
||||||
|
|
||||||
|
toast.add({
|
||||||
|
title: 'Payment ID copied to clipboard!',
|
||||||
|
color: 'success',
|
||||||
|
icon: 'i-heroicons-check-circle'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
label: row.getIsExpanded() ? 'Collapse' : 'Expand',
|
||||||
|
onSelect() {
|
||||||
|
row.toggleExpanded()
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
type: 'separator'
|
||||||
|
}, {
|
||||||
|
label: 'View customer'
|
||||||
|
}, {
|
||||||
|
label: 'View payment details'
|
||||||
|
}]
|
||||||
|
|
||||||
|
return h('div', { class: 'text-right' }, h(UDropdownMenu, {
|
||||||
|
content: {
|
||||||
|
align: 'end'
|
||||||
|
},
|
||||||
|
items
|
||||||
|
}, () => h(UButton, {
|
||||||
|
icon: 'i-heroicons-ellipsis-vertical-20-solid',
|
||||||
|
color: 'neutral',
|
||||||
|
variant: 'ghost',
|
||||||
|
class: 'ml-auto'
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
const table = useTemplateRef('table')
|
||||||
|
|
||||||
|
function randomize() {
|
||||||
|
data.value = [...data.value].sort(() => Math.random() - 0.5)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex-1 divide-y divide-[var(--ui-border-accented)]">
|
||||||
|
<div class="flex items-center gap-2 px-4 py-3.5">
|
||||||
|
<UInput
|
||||||
|
:model-value="(table?.tableApi?.getColumn('email')?.getFilterValue() as string)"
|
||||||
|
class="max-w-sm"
|
||||||
|
placeholder="Filter emails..."
|
||||||
|
@update:model-value="table?.tableApi?.getColumn('email')?.setFilterValue($event)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UButton color="neutral" label="Randomize" @click="randomize" />
|
||||||
|
|
||||||
|
<UDropdownMenu
|
||||||
|
:items="table?.tableApi?.getAllColumns().filter(column => column.getCanHide()).map(column => ({
|
||||||
|
label: upperFirst(column.id),
|
||||||
|
type: 'checkbox' as const,
|
||||||
|
checked: column.getIsVisible(),
|
||||||
|
onUpdateChecked(checked: boolean) {
|
||||||
|
table?.tableApi?.getColumn(column.id)?.toggleVisibility(!!checked)
|
||||||
|
},
|
||||||
|
onSelect(e?: Event) {
|
||||||
|
e?.preventDefault()
|
||||||
|
}
|
||||||
|
}))"
|
||||||
|
:content="{ align: 'end' }"
|
||||||
|
>
|
||||||
|
<UButton
|
||||||
|
label="Columns"
|
||||||
|
color="neutral"
|
||||||
|
variant="outline"
|
||||||
|
trailing-icon="i-heroicons-chevron-down-20-solid"
|
||||||
|
class="ml-auto"
|
||||||
|
/>
|
||||||
|
</UDropdownMenu>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<UTable
|
||||||
|
ref="table"
|
||||||
|
:data="data"
|
||||||
|
:columns="columns"
|
||||||
|
sticky
|
||||||
|
class="h-96"
|
||||||
|
>
|
||||||
|
<template #expanded="{ row }">
|
||||||
|
<pre>{{ row.original }}</pre>
|
||||||
|
</template>
|
||||||
|
</UTable>
|
||||||
|
|
||||||
|
<div class="px-4 py-3.5 text-sm text-[var(--ui-text-muted)]">
|
||||||
|
{{ table?.tableApi?.getFilteredSelectedRowModel().rows.length || 0 }} of
|
||||||
|
{{ table?.tableApi?.getFilteredRowModel().rows.length || 0 }} row(s) selected.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { TableColumn } from '@nuxt/ui'
|
||||||
|
|
||||||
|
const UAvatar = resolveComponent('UAvatar')
|
||||||
|
|
||||||
|
type User = {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
username: string
|
||||||
|
email: string
|
||||||
|
avatar: { src: string }
|
||||||
|
company: { name: string }
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, status } = await useFetch<User[]>('https://jsonplaceholder.typicode.com/users', {
|
||||||
|
transform: (data) => {
|
||||||
|
return data?.map(user => ({
|
||||||
|
...user,
|
||||||
|
avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` }
|
||||||
|
})) || []
|
||||||
|
},
|
||||||
|
lazy: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const columns: TableColumn<User>[] = [{
|
||||||
|
accessorKey: 'id',
|
||||||
|
header: 'ID'
|
||||||
|
}, {
|
||||||
|
accessorKey: 'name',
|
||||||
|
header: 'Name',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return h('div', { class: 'flex items-center gap-3' }, [
|
||||||
|
h(UAvatar, {
|
||||||
|
...row.original.avatar,
|
||||||
|
size: 'lg'
|
||||||
|
}),
|
||||||
|
h('div', undefined, [
|
||||||
|
h('p', { class: 'font-medium text-[var(--ui-text-highlighted)]' }, row.original.name),
|
||||||
|
h('p', { class: '' }, `@${row.original.username}`)
|
||||||
|
])
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'email',
|
||||||
|
header: 'Email'
|
||||||
|
}, {
|
||||||
|
accessorKey: 'company',
|
||||||
|
header: 'Company',
|
||||||
|
cell: ({ row }) => row.original.company.name
|
||||||
|
}]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UTable :data="data" :columns="columns" :loading="status === 'pending'" class="flex-1" />
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { h, resolveComponent } from 'vue'
|
||||||
|
import type { TableColumn } from '@nuxt/ui'
|
||||||
|
|
||||||
|
const UBadge = resolveComponent('UBadge')
|
||||||
|
|
||||||
|
type Payment = {
|
||||||
|
id: string
|
||||||
|
date: string
|
||||||
|
status: 'paid' | 'failed' | 'refunded'
|
||||||
|
email: string
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = ref<Payment[]>([{
|
||||||
|
id: '4600',
|
||||||
|
date: '2024-03-11T15:30:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'james.anderson@example.com',
|
||||||
|
amount: 594
|
||||||
|
}, {
|
||||||
|
id: '4599',
|
||||||
|
date: '2024-03-11T10:10:00',
|
||||||
|
status: 'failed',
|
||||||
|
email: 'mia.white@example.com',
|
||||||
|
amount: 276
|
||||||
|
}, {
|
||||||
|
id: '4598',
|
||||||
|
date: '2024-03-11T08:50:00',
|
||||||
|
status: 'refunded',
|
||||||
|
email: 'william.brown@example.com',
|
||||||
|
amount: 315
|
||||||
|
}, {
|
||||||
|
id: '4597',
|
||||||
|
date: '2024-03-10T19:45:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'emma.davis@example.com',
|
||||||
|
amount: 529
|
||||||
|
}, {
|
||||||
|
id: '4596',
|
||||||
|
date: '2024-03-10T15:55:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'ethan.harris@example.com',
|
||||||
|
amount: 639
|
||||||
|
}])
|
||||||
|
|
||||||
|
const columns: TableColumn<Payment>[] = [{
|
||||||
|
accessorKey: 'id',
|
||||||
|
header: '#',
|
||||||
|
cell: ({ row }) => `#${row.getValue('id')}`
|
||||||
|
}, {
|
||||||
|
accessorKey: 'date',
|
||||||
|
header: 'Date',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return new Date(row.getValue('date')).toLocaleString('en-US', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'status',
|
||||||
|
header: 'Status',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const color = ({
|
||||||
|
paid: 'success' as const,
|
||||||
|
failed: 'error' as const,
|
||||||
|
refunded: 'neutral' as const
|
||||||
|
})[row.getValue('status') as string]
|
||||||
|
|
||||||
|
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'email',
|
||||||
|
header: 'Email'
|
||||||
|
}, {
|
||||||
|
accessorKey: 'amount',
|
||||||
|
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const amount = Number.parseFloat(row.getValue('amount'))
|
||||||
|
|
||||||
|
const formatted = new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'EUR'
|
||||||
|
}).format(amount)
|
||||||
|
|
||||||
|
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
const table = useTemplateRef('table')
|
||||||
|
|
||||||
|
const globalFilter = ref('45')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col flex-1">
|
||||||
|
<div class="flex px-4 py-3.5 border-b border-[var(--ui-border-accented)]">
|
||||||
|
<UInput
|
||||||
|
v-model="globalFilter"
|
||||||
|
class="max-w-sm"
|
||||||
|
placeholder="Filter..."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<UTable
|
||||||
|
ref="table"
|
||||||
|
v-model:global-filter="globalFilter"
|
||||||
|
:data="data"
|
||||||
|
:columns="columns"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { h, resolveComponent } from 'vue'
|
||||||
|
import type { TableColumn } from '@nuxt/ui'
|
||||||
|
import type { Row } from '@tanstack/vue-table'
|
||||||
|
|
||||||
|
const UButton = resolveComponent('UButton')
|
||||||
|
const UBadge = resolveComponent('UBadge')
|
||||||
|
const UDropdownMenu = resolveComponent('UDropdownMenu')
|
||||||
|
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
type Payment = {
|
||||||
|
id: string
|
||||||
|
date: string
|
||||||
|
status: 'paid' | 'failed' | 'refunded'
|
||||||
|
email: string
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = ref<Payment[]>([{
|
||||||
|
id: '4600',
|
||||||
|
date: '2024-03-11T15:30:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'james.anderson@example.com',
|
||||||
|
amount: 594
|
||||||
|
}, {
|
||||||
|
id: '4599',
|
||||||
|
date: '2024-03-11T10:10:00',
|
||||||
|
status: 'failed',
|
||||||
|
email: 'mia.white@example.com',
|
||||||
|
amount: 276
|
||||||
|
}, {
|
||||||
|
id: '4598',
|
||||||
|
date: '2024-03-11T08:50:00',
|
||||||
|
status: 'refunded',
|
||||||
|
email: 'william.brown@example.com',
|
||||||
|
amount: 315
|
||||||
|
}, {
|
||||||
|
id: '4597',
|
||||||
|
date: '2024-03-10T19:45:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'emma.davis@example.com',
|
||||||
|
amount: 529
|
||||||
|
}, {
|
||||||
|
id: '4596',
|
||||||
|
date: '2024-03-10T15:55:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'ethan.harris@example.com',
|
||||||
|
amount: 639
|
||||||
|
}])
|
||||||
|
|
||||||
|
const columns: TableColumn<Payment>[] = [{
|
||||||
|
accessorKey: 'id',
|
||||||
|
header: '#',
|
||||||
|
cell: ({ row }) => `#${row.getValue('id')}`
|
||||||
|
}, {
|
||||||
|
accessorKey: 'date',
|
||||||
|
header: 'Date',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return new Date(row.getValue('date')).toLocaleString('en-US', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'status',
|
||||||
|
header: 'Status',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const color = ({
|
||||||
|
paid: 'success' as const,
|
||||||
|
failed: 'error' as const,
|
||||||
|
refunded: 'neutral' as const
|
||||||
|
})[row.getValue('status') as string]
|
||||||
|
|
||||||
|
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'email',
|
||||||
|
header: 'Email'
|
||||||
|
}, {
|
||||||
|
accessorKey: 'amount',
|
||||||
|
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const amount = Number.parseFloat(row.getValue('amount'))
|
||||||
|
|
||||||
|
const formatted = new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'EUR'
|
||||||
|
}).format(amount)
|
||||||
|
|
||||||
|
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
id: 'actions',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return h('div', { class: 'text-right' }, h(UDropdownMenu, {
|
||||||
|
content: {
|
||||||
|
align: 'end'
|
||||||
|
},
|
||||||
|
items: getRowItems(row)
|
||||||
|
}, () => h(UButton, {
|
||||||
|
icon: 'i-heroicons-ellipsis-vertical-20-solid',
|
||||||
|
color: 'neutral',
|
||||||
|
variant: 'ghost',
|
||||||
|
class: 'ml-auto'
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
function getRowItems(row: Row<Payment>) {
|
||||||
|
return [{
|
||||||
|
type: 'label',
|
||||||
|
label: 'Actions'
|
||||||
|
}, {
|
||||||
|
label: 'Copy payment ID',
|
||||||
|
onSelect() {
|
||||||
|
navigator.clipboard.writeText(row.original.id)
|
||||||
|
|
||||||
|
toast.add({
|
||||||
|
title: 'Payment ID copied to clipboard!',
|
||||||
|
color: 'success',
|
||||||
|
icon: 'i-heroicons-check-circle'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
type: 'separator'
|
||||||
|
}, {
|
||||||
|
label: 'View customer'
|
||||||
|
}, {
|
||||||
|
label: 'View payment details'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UTable :data="data" :columns="columns" class="flex-1" />
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { h, resolveComponent } from 'vue'
|
||||||
|
import type { TableColumn } from '@nuxt/ui'
|
||||||
|
|
||||||
|
const UButton = resolveComponent('UButton')
|
||||||
|
const UBadge = resolveComponent('UBadge')
|
||||||
|
|
||||||
|
type Payment = {
|
||||||
|
id: string
|
||||||
|
date: string
|
||||||
|
status: 'paid' | 'failed' | 'refunded'
|
||||||
|
email: string
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = ref<Payment[]>([{
|
||||||
|
id: '4600',
|
||||||
|
date: '2024-03-11T15:30:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'james.anderson@example.com',
|
||||||
|
amount: 594
|
||||||
|
}, {
|
||||||
|
id: '4599',
|
||||||
|
date: '2024-03-11T10:10:00',
|
||||||
|
status: 'failed',
|
||||||
|
email: 'mia.white@example.com',
|
||||||
|
amount: 276
|
||||||
|
}, {
|
||||||
|
id: '4598',
|
||||||
|
date: '2024-03-11T08:50:00',
|
||||||
|
status: 'refunded',
|
||||||
|
email: 'william.brown@example.com',
|
||||||
|
amount: 315
|
||||||
|
}, {
|
||||||
|
id: '4597',
|
||||||
|
date: '2024-03-10T19:45:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'emma.davis@example.com',
|
||||||
|
amount: 529
|
||||||
|
}, {
|
||||||
|
id: '4596',
|
||||||
|
date: '2024-03-10T15:55:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'ethan.harris@example.com',
|
||||||
|
amount: 639
|
||||||
|
}])
|
||||||
|
|
||||||
|
const columns: TableColumn<Payment>[] = [{
|
||||||
|
id: 'expand',
|
||||||
|
cell: ({ row }) => h(UButton, {
|
||||||
|
color: 'neutral',
|
||||||
|
variant: 'ghost',
|
||||||
|
icon: 'i-heroicons-chevron-down-20-solid',
|
||||||
|
square: true,
|
||||||
|
ui: {
|
||||||
|
leadingIcon: ['transition-transform', row.getIsExpanded() ? 'duration-200 rotate-180' : '']
|
||||||
|
},
|
||||||
|
onClick: () => row.toggleExpanded()
|
||||||
|
})
|
||||||
|
}, {
|
||||||
|
accessorKey: 'id',
|
||||||
|
header: '#',
|
||||||
|
cell: ({ row }) => `#${row.getValue('id')}`
|
||||||
|
}, {
|
||||||
|
accessorKey: 'date',
|
||||||
|
header: 'Date',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return new Date(row.getValue('date')).toLocaleString('en-US', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'status',
|
||||||
|
header: 'Status',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const color = ({
|
||||||
|
paid: 'success' as const,
|
||||||
|
failed: 'error' as const,
|
||||||
|
refunded: 'neutral' as const
|
||||||
|
})[row.getValue('status') as string]
|
||||||
|
|
||||||
|
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'email',
|
||||||
|
header: 'Email'
|
||||||
|
}, {
|
||||||
|
accessorKey: 'amount',
|
||||||
|
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const amount = Number.parseFloat(row.getValue('amount'))
|
||||||
|
|
||||||
|
const formatted = new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'EUR'
|
||||||
|
}).format(amount)
|
||||||
|
|
||||||
|
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
const expanded = ref({ 1: true })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UTable
|
||||||
|
v-model:expanded="expanded"
|
||||||
|
:data="data"
|
||||||
|
:columns="columns"
|
||||||
|
:ui="{ tr: 'data-[expanded=true]:bg-[var(--ui-bg-elevated)]/50' }"
|
||||||
|
class="flex-1"
|
||||||
|
>
|
||||||
|
<template #expanded="{ row }">
|
||||||
|
<pre>{{ row.original }}</pre>
|
||||||
|
</template>
|
||||||
|
</UTable>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { h, resolveComponent } from 'vue'
|
||||||
|
import type { TableColumn } from '@nuxt/ui'
|
||||||
|
|
||||||
|
const UCheckbox = resolveComponent('UCheckbox')
|
||||||
|
const UBadge = resolveComponent('UBadge')
|
||||||
|
|
||||||
|
type Payment = {
|
||||||
|
id: string
|
||||||
|
date: string
|
||||||
|
status: 'paid' | 'failed' | 'refunded'
|
||||||
|
email: string
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = ref<Payment[]>([{
|
||||||
|
id: '4600',
|
||||||
|
date: '2024-03-11T15:30:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'james.anderson@example.com',
|
||||||
|
amount: 594
|
||||||
|
}, {
|
||||||
|
id: '4599',
|
||||||
|
date: '2024-03-11T10:10:00',
|
||||||
|
status: 'failed',
|
||||||
|
email: 'mia.white@example.com',
|
||||||
|
amount: 276
|
||||||
|
}, {
|
||||||
|
id: '4598',
|
||||||
|
date: '2024-03-11T08:50:00',
|
||||||
|
status: 'refunded',
|
||||||
|
email: 'william.brown@example.com',
|
||||||
|
amount: 315
|
||||||
|
}, {
|
||||||
|
id: '4597',
|
||||||
|
date: '2024-03-10T19:45:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'emma.davis@example.com',
|
||||||
|
amount: 529
|
||||||
|
}, {
|
||||||
|
id: '4596',
|
||||||
|
date: '2024-03-10T15:55:00',
|
||||||
|
status: 'paid',
|
||||||
|
email: 'ethan.harris@example.com',
|
||||||
|
amount: 639
|
||||||
|
}])
|
||||||
|
|
||||||
|
const columns: TableColumn<Payment>[] = [{
|
||||||
|
id: 'select',
|
||||||
|
header: ({ table }) => h(UCheckbox, {
|
||||||
|
'modelValue': table.getIsAllPageRowsSelected(),
|
||||||
|
'indeterminate': table.getIsSomePageRowsSelected(),
|
||||||
|
'onUpdate:modelValue': (value: boolean) => table.toggleAllPageRowsSelected(!!value),
|
||||||
|
'ariaLabel': 'Select all'
|
||||||
|
}),
|
||||||
|
cell: ({ row }) => h(UCheckbox, {
|
||||||
|
'modelValue': row.getIsSelected(),
|
||||||
|
'onUpdate:modelValue': (value: boolean) => row.toggleSelected(!!value),
|
||||||
|
'ariaLabel': 'Select row'
|
||||||
|
})
|
||||||
|
}, {
|
||||||
|
accessorKey: 'date',
|
||||||
|
header: 'Date',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return new Date(row.getValue('date')).toLocaleString('en-US', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'status',
|
||||||
|
header: 'Status',
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const color = ({
|
||||||
|
paid: 'success' as const,
|
||||||
|
failed: 'error' as const,
|
||||||
|
refunded: 'neutral' as const
|
||||||
|
})[row.getValue('status') as string]
|
||||||
|
|
||||||
|
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
accessorKey: 'email',
|
||||||
|
header: 'Email'
|
||||||
|
}, {
|
||||||
|
accessorKey: 'amount',
|
||||||
|
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const amount = Number.parseFloat(row.getValue('amount'))
|
||||||
|
|
||||||
|
const formatted = new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'EUR'
|
||||||
|
}).format(amount)
|
||||||
|
|
||||||
|
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
const table = useTemplateRef('table')
|
||||||
|
|
||||||
|
const rowSelection = ref({ 1: true })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex-1">
|
||||||
|
<UTable
|
||||||
|
ref="table"
|
||||||
|
v-model:row-selection="rowSelection"
|
||||||
|
:data="data"
|
||||||
|
:columns="columns"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="px-4 py-3.5 border-t border-[var(--ui-border-accented)] text-sm text-[var(--ui-text-muted)]">
|
||||||
|
{{ table?.tableApi?.getFilteredSelectedRowModel().rows.length || 0 }} of
|
||||||
|
{{ table?.tableApi?.getFilteredRowModel().rows.length || 0 }} row(s) selected.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -26,7 +26,7 @@ const state = reactive({
|
|||||||
<template>
|
<template>
|
||||||
<UTabs :items="items" variant="link" class="gap-4 w-full" :ui="{ trigger: 'flex-1' }">
|
<UTabs :items="items" variant="link" class="gap-4 w-full" :ui="{ trigger: 'flex-1' }">
|
||||||
<template #account="{ item }">
|
<template #account="{ item }">
|
||||||
<p class="text-[--ui-text-muted] mb-4">
|
<p class="text-[var(--ui-text-muted)] mb-4">
|
||||||
{{ item.description }}
|
{{ item.description }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ const state = reactive({
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #password="{ item }">
|
<template #password="{ item }">
|
||||||
<p class="text-[--ui-text-muted] mb-4">
|
<p class="text-[var(--ui-text-muted)] mb-4">
|
||||||
{{ item.description }}
|
{{ item.description }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ const appConfig = useAppConfig()
|
|||||||
<UFormField
|
<UFormField
|
||||||
label="toaster.duration"
|
label="toaster.duration"
|
||||||
size="sm"
|
size="sm"
|
||||||
class="inline-flex ring ring-[--ui-border-accented] rounded"
|
class="inline-flex ring ring-[var(--ui-border-accented)] rounded"
|
||||||
:ui="{
|
:ui="{
|
||||||
wrapper: 'bg-[--ui-bg-elevated]/50 rounded-l flex border-r border-[--ui-border-accented]',
|
wrapper: 'bg-[var(--ui-bg-elevated)]/50 rounded-l flex border-r border-[var(--ui-border-accented)]',
|
||||||
label: 'text-[--ui-text-muted] px-2 py-1.5',
|
label: 'text-[var(--ui-text-muted)] px-2 py-1.5',
|
||||||
container: 'mt-0'
|
container: 'mt-0'
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ const appConfig = useAppConfig()
|
|||||||
<UFormField
|
<UFormField
|
||||||
label="toaster.expand"
|
label="toaster.expand"
|
||||||
size="sm"
|
size="sm"
|
||||||
class="inline-flex ring ring-[--ui-border-accented] rounded"
|
class="inline-flex ring ring-[var(--ui-border-accented)] rounded"
|
||||||
:ui="{
|
:ui="{
|
||||||
wrapper: 'bg-[--ui-bg-elevated]/50 rounded-l flex border-r border-[--ui-border-accented]',
|
wrapper: 'bg-[var(--ui-bg-elevated)]/50 rounded-l flex border-r border-[var(--ui-border-accented)]',
|
||||||
label: 'text-[--ui-text-muted] px-2 py-1.5',
|
label: 'text-[var(--ui-text-muted)] px-2 py-1.5',
|
||||||
container: 'mt-0'
|
container: 'mt-0'
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ const appConfig = useAppConfig()
|
|||||||
<UFormField
|
<UFormField
|
||||||
label="toaster.position"
|
label="toaster.position"
|
||||||
size="sm"
|
size="sm"
|
||||||
class="inline-flex ring ring-[--ui-border-accented] rounded"
|
class="inline-flex ring ring-[var(--ui-border-accented)] rounded"
|
||||||
:ui="{
|
:ui="{
|
||||||
wrapper: 'bg-[--ui-bg-elevated]/50 rounded-l flex border-r border-[--ui-border-accented]',
|
wrapper: 'bg-[var(--ui-bg-elevated)]/50 rounded-l flex border-r border-[var(--ui-border-accented)]',
|
||||||
label: 'text-[--ui-text-muted] px-2 py-1.5',
|
label: 'text-[var(--ui-text-muted)] px-2 py-1.5',
|
||||||
container: 'mt-0'
|
container: 'mt-0'
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
:variant="open ? 'soft' : 'ghost'"
|
:variant="open ? 'soft' : 'ghost'"
|
||||||
square
|
square
|
||||||
aria-label="Color picker"
|
aria-label="Color picker"
|
||||||
:ui="{ leadingIcon: 'text-[--ui-primary]' }"
|
:ui="{ leadingIcon: 'text-[var(--ui-primary)]' }"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
:label="color"
|
:label="color"
|
||||||
:chip="color"
|
:chip="color"
|
||||||
:selected="primary === color"
|
:selected="primary === color"
|
||||||
@select="primary = color"
|
@click="primary = color"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
:label="color"
|
:label="color"
|
||||||
:chip="color"
|
:chip="color"
|
||||||
:selected="neutral === color"
|
:selected="neutral === color"
|
||||||
@select="neutral = color"
|
@click="neutral = color"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
:label="String(r)"
|
:label="String(r)"
|
||||||
class="justify-center px-0"
|
class="justify-center px-0"
|
||||||
:selected="radius === r"
|
:selected="radius === r"
|
||||||
@select="radius = r"
|
@click="radius = r"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
:key="m.label"
|
:key="m.label"
|
||||||
v-bind="m"
|
v-bind="m"
|
||||||
:selected="mode === m.label"
|
:selected="mode === m.label"
|
||||||
@select="mode = m.label"
|
@click="mode = m.label"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|||||||
@@ -5,13 +5,12 @@
|
|||||||
:icon="icon"
|
:icon="icon"
|
||||||
:label="label"
|
:label="label"
|
||||||
:variant="selected ? 'soft' : 'outline'"
|
:variant="selected ? 'soft' : 'outline'"
|
||||||
class="capitalize ring-[--ui-border] rounded-[--ui-radius] text-[11px]"
|
class="capitalize ring-[var(--ui-border)] rounded-[var(--ui-radius)] text-[11px]"
|
||||||
@click.stop.prevent="$emit('select')"
|
|
||||||
>
|
>
|
||||||
<template v-if="chip" #leading>
|
<template v-if="chip" #leading>
|
||||||
<span
|
<span
|
||||||
class="inline-block w-2 h-2 rounded-full"
|
class="inline-block w-2 h-2 rounded-full"
|
||||||
:class="`bg-[--color-light] dark:bg-[--color-dark]`"
|
:class="`bg-[var(--color-light)] dark:bg-[var(--color-dark)]`"
|
||||||
:style="{
|
:style="{
|
||||||
'--color-light': `var(--color-${chip}-500)`,
|
'--color-light': `var(--color-${chip}-500)`,
|
||||||
'--color-dark': `var(--color-${chip}-400)`
|
'--color-dark': `var(--color-${chip}-400)`
|
||||||
@@ -28,5 +27,4 @@ defineProps<{
|
|||||||
chip?: string
|
chip?: string
|
||||||
selected?: boolean
|
selected?: boolean
|
||||||
}>()
|
}>()
|
||||||
defineEmits(['select'])
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,28 +1,20 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { NuxtError } from '#app'
|
import type { NuxtError } from '#app'
|
||||||
// import type { ContentSearchFile } from '@nuxt/ui-pro'
|
import colors from 'tailwindcss/colors'
|
||||||
|
import type { ContentSearchFile } from '@nuxt/ui-pro'
|
||||||
|
|
||||||
useSeoMeta({
|
const props = defineProps<{
|
||||||
title: 'Page not found',
|
|
||||||
description: 'We are sorry but this page could not be found.'
|
|
||||||
})
|
|
||||||
|
|
||||||
defineProps<{
|
|
||||||
error: NuxtError
|
error: NuxtError
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
// const colorMode = useColorMode()
|
const appConfig = useAppConfig()
|
||||||
// const { branch } = useContentSource()
|
const colorMode = useColorMode()
|
||||||
const runtimeConfig = useRuntimeConfig()
|
const runtimeConfig = useRuntimeConfig()
|
||||||
const { integrity, api } = runtimeConfig.public.content
|
const { integrity, api } = runtimeConfig.public.content
|
||||||
|
|
||||||
const { data: navigation } = await useAsyncData('navigation', () => fetchContentNavigation(), { default: () => [] })
|
const { data: navigation } = await useAsyncData('navigation', () => fetchContentNavigation(), { default: () => [] })
|
||||||
const { data: files } = await useLazyFetch<any[]>(`${api.baseURL}/search${integrity ? '.' + integrity : ''}`, { default: () => [] })
|
const { data: files } = await useLazyFetch<ContentSearchFile[]>(`${api.baseURL}/search${integrity ? '-' + integrity : ''}`, { default: () => [] })
|
||||||
|
|
||||||
// Computed
|
|
||||||
|
|
||||||
// const color = computed(() => colorMode.value === 'dark' ? '#18181b' : 'white')
|
|
||||||
|
|
||||||
const links = computed(() => {
|
const links = computed(() => {
|
||||||
return [{
|
return [{
|
||||||
@@ -52,43 +44,52 @@ const links = computed(() => {
|
|||||||
}].filter(Boolean)
|
}].filter(Boolean)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Head
|
const color = computed(() => colorMode.value === 'dark' ? (colors as any)[appConfig.ui.colors.neutral][900] : 'white')
|
||||||
|
const radius = computed(() => `:root { --ui-radius: ${appConfig.theme.radius}rem; }`)
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
meta: [
|
meta: [
|
||||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
|
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
||||||
// { key: 'theme-color', name: 'theme-color', content: color }
|
{ key: 'theme-color', name: 'theme-color', content: color }
|
||||||
],
|
],
|
||||||
link: [
|
link: [
|
||||||
{ rel: 'icon', type: 'image/svg+xml', href: '/icon.svg' }
|
{ rel: 'icon', type: 'image/svg+xml', href: '/icon.svg' }
|
||||||
],
|
],
|
||||||
|
style: [
|
||||||
|
{ innerHTML: radius, id: 'nuxt-ui-radius', tagPriority: -2 }
|
||||||
|
],
|
||||||
htmlAttrs: {
|
htmlAttrs: {
|
||||||
lang: 'en'
|
lang: 'en'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Provide
|
useSeoMeta({
|
||||||
|
titleTemplate: '%s - Nuxt UI v3',
|
||||||
|
title: String(props.error.statusCode)
|
||||||
|
})
|
||||||
|
|
||||||
|
useServerSeoMeta({
|
||||||
|
ogSiteName: 'Nuxt UI',
|
||||||
|
twitterCard: 'summary_large_image'
|
||||||
|
})
|
||||||
|
|
||||||
provide('navigation', navigation)
|
provide('navigation', navigation)
|
||||||
provide('files', files)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UApp>
|
<UApp>
|
||||||
<NuxtLoadingIndicator />
|
<NuxtLoadingIndicator color="#FFF" />
|
||||||
|
|
||||||
|
<Banner />
|
||||||
|
|
||||||
<Header :links="links" />
|
<Header :links="links" />
|
||||||
|
|
||||||
<UContainer>
|
<UError :error="error" />
|
||||||
<UMain>
|
|
||||||
<UPage>
|
|
||||||
<!-- <UPageError :error="error" /> -->
|
|
||||||
</UPage>
|
|
||||||
</UMain>
|
|
||||||
</UContainer>
|
|
||||||
|
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|
||||||
<LazyUContentSearch :files="files" :navigation="navigation" :fuse="{ resultLimit: 42 }" />
|
<ClientOnly>
|
||||||
|
<LazyUContentSearch :files="files" :navigation="navigation" :fuse="{ resultLimit: 42 }" />
|
||||||
|
</ClientOnly>
|
||||||
</UApp>
|
</UApp>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -31,13 +31,12 @@ useSeoMeta({
|
|||||||
titleTemplate: '%s - Nuxt UI v3',
|
titleTemplate: '%s - Nuxt UI v3',
|
||||||
title: page.value.navigation?.title || page.value.title,
|
title: page.value.navigation?.title || page.value.title,
|
||||||
ogTitle: `${page.value.navigation?.title || page.value.title} - Nuxt UI v3`,
|
ogTitle: `${page.value.navigation?.title || page.value.title} - Nuxt UI v3`,
|
||||||
description: page.value.description,
|
description: page.value.seo?.description || page.value.description,
|
||||||
ogDescription: page.value.description
|
ogDescription: page.value.seo?.description || page.value.description
|
||||||
})
|
})
|
||||||
|
|
||||||
defineOgImageComponent('Docs', {
|
defineOgImageComponent('Docs', {
|
||||||
headline: headline.value,
|
headline: headline.value
|
||||||
title: page.value.title
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const communityLinks = computed(() => [{
|
const communityLinks = computed(() => [{
|
||||||
@@ -76,10 +75,24 @@ const communityLinks = computed(() => [{
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UPage v-if="page">
|
<UPage v-if="page">
|
||||||
<UPageHeader :title="page.title" :links="page.links" :headline="headline">
|
<UPageHeader :title="page.title" :headline="headline">
|
||||||
<template #description>
|
<template #description>
|
||||||
<MDC v-if="page.description" :value="page.description" unwrap="p" />
|
<MDC v-if="page.description" :value="page.description" unwrap="p" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template #links>
|
||||||
|
<UButton
|
||||||
|
v-for="link in page.links"
|
||||||
|
:key="link.label"
|
||||||
|
color="neutral"
|
||||||
|
variant="outline"
|
||||||
|
v-bind="link"
|
||||||
|
>
|
||||||
|
<template v-if="link.avatar" #leading>
|
||||||
|
<UAvatar v-bind="link.avatar" size="2xs" />
|
||||||
|
</template>
|
||||||
|
</UButton>
|
||||||
|
</template>
|
||||||
</UPageHeader>
|
</UPageHeader>
|
||||||
|
|
||||||
<UPageBody>
|
<UPageBody>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ const title = 'Roadmap'
|
|||||||
const description = 'Discover our Volta board for @nuxt/ui development status.'
|
const description = 'Discover our Volta board for @nuxt/ui development status.'
|
||||||
|
|
||||||
useSeoMeta({
|
useSeoMeta({
|
||||||
titleTemplate: '%s - Nuxt UI',
|
titleTemplate: '%s - Nuxt UI v3',
|
||||||
title,
|
title,
|
||||||
ogTitle: 'Nuxt UI Roadmap',
|
ogTitle: 'Nuxt UI Roadmap',
|
||||||
description
|
description
|
||||||
@@ -20,7 +20,7 @@ const src = computed(() => `https://volta.net/embed/${token}?theme=${colorMode.v
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="h-[calc(100vh-var(--header-height)-var(--header-height)-1px)]">
|
<div class="h-[calc(100vh-var(--ui-header-height)-var(--ui-header-height)-48px-1px)]">
|
||||||
<ClientOnly>
|
<ClientOnly>
|
||||||
<iframe :src="src" width="100%" height="100%" />
|
<iframe :src="src" width="100%" height="100%" />
|
||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
|
|||||||
@@ -138,3 +138,33 @@ export default defineNuxtConfig({
|
|||||||
::note
|
::note
|
||||||
This option adds the `transition-colors` class on components with hover or active states.
|
This option adds the `transition-colors` class on components with hover or active states.
|
||||||
::
|
::
|
||||||
|
|
||||||
|
## Continuous Releases
|
||||||
|
|
||||||
|
Nuxt UI v3 uses [pkg.pr.new](https://github.com/stackblitz-labs/pkg.pr.new) for continuous preview releases, providing developers with instant access to the latest features and bug fixes without waiting for official releases.
|
||||||
|
|
||||||
|
Preview releases are automatically generated for every commit to the `v3` branch and pull requests targeting the `v3` branch. To use it into your project, use the installation command below by replacing `5385f84` with any commit hash or pull request number.
|
||||||
|
|
||||||
|
::code-group
|
||||||
|
|
||||||
|
```bash [pnpm]
|
||||||
|
pnpm add https://pkg.pr.new/@nuxt/ui@5385f84
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [yarn]
|
||||||
|
yarn add https://pkg.pr.new/@nuxt/ui@5385f84
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [npm]
|
||||||
|
npm install https://pkg.pr.new/@nuxt/ui@5385f84
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [bun]
|
||||||
|
bun add https://pkg.pr.new/@nuxt/ui@5385f84
|
||||||
|
```
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
::note
|
||||||
|
**pkg.pr.new** will automatically comment on PRs with the installation URL, making it easy to test changes.
|
||||||
|
::
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export default defineAppConfig({
|
|||||||
```
|
```
|
||||||
|
|
||||||
::note
|
::note
|
||||||
Try the :prose-icon{name="i-heroicons-swatch" class="text-[--ui-primary]"} theme picker in the header above to change `primary` and `neutral` colors.
|
Try the :prose-icon{name="i-heroicons-swatch" class="text-[var(--ui-primary)]"} theme picker in the header above to change `primary` and `neutral` colors.
|
||||||
::
|
::
|
||||||
|
|
||||||
These colors are used to style the components but also to generate the `color` variants:
|
These colors are used to style the components but also to generate the `color` variants:
|
||||||
@@ -211,7 +211,7 @@ Nuxt UI automatically creates a CSS variable for each color alias you define whi
|
|||||||
::
|
::
|
||||||
|
|
||||||
::note
|
::note
|
||||||
You can use these variables in classes like `text-[--ui-primary]`, it will automatically adapt to the current color scheme.
|
You can use these variables in classes like `text-[var(--ui-primary)]`, it will automatically adapt to the current color scheme.
|
||||||
::
|
::
|
||||||
|
|
||||||
::tip
|
::tip
|
||||||
@@ -308,7 +308,7 @@ Nuxt UI automatically applies a text and background color on the `<body>` elemen
|
|||||||
|
|
||||||
```css
|
```css
|
||||||
body {
|
body {
|
||||||
@apply antialiased font-sans text-[--ui-text] bg-[--ui-bg];
|
@apply antialiased text-[var(--ui-text)] bg-[var(--ui-bg)];
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
::
|
::
|
||||||
@@ -345,7 +345,7 @@ Nuxt UI uses a global `--ui-radius` CSS variable for consistent border rounding.
|
|||||||
```
|
```
|
||||||
|
|
||||||
::note
|
::note
|
||||||
Try the :prose-icon{name="i-heroicons-swatch" class="text-[--ui-primary]"} theme picker in the header above to change the base radius value.
|
Try the :prose-icon{name="i-heroicons-swatch" class="text-[var(--ui-primary)]"} theme picker in the header above to change the base radius value.
|
||||||
::
|
::
|
||||||
|
|
||||||
::tip
|
::tip
|
||||||
@@ -376,7 +376,7 @@ Components in Nuxt UI can have multiple `slots`, each representing a distinct HT
|
|||||||
```ts [src/theme/card.ts]
|
```ts [src/theme/card.ts]
|
||||||
export default {
|
export default {
|
||||||
slots: {
|
slots: {
|
||||||
root: 'bg-[--ui-bg] ring ring-[--ui-border] divide-y divide-[--ui-border] rounded-[calc(var(--ui-radius)*2)] shadow',
|
root: 'bg-[var(--ui-bg)] ring ring-[var(--ui-border)] divide-y divide-[var(--ui-border)] rounded-[calc(var(--ui-radius)*2)] shadow',
|
||||||
header: 'p-4 sm:px-6',
|
header: 'p-4 sm:px-6',
|
||||||
body: 'p-4 sm:p-6',
|
body: 'p-4 sm:p-6',
|
||||||
footer: 'p-4 sm:px-6'
|
footer: 'p-4 sm:px-6'
|
||||||
@@ -435,7 +435,7 @@ Nuxt UI components use `variants` to change the `slots` styles based on props. H
|
|||||||
```ts [src/theme/avatar.ts]
|
```ts [src/theme/avatar.ts]
|
||||||
export default {
|
export default {
|
||||||
slots: {
|
slots: {
|
||||||
root: 'inline-flex items-center justify-center shrink-0 select-none overflow-hidden rounded-full align-middle bg-[--ui-bg-elevated]',
|
root: 'inline-flex items-center justify-center shrink-0 select-none overflow-hidden rounded-full align-middle bg-[var(--ui-bg-elevated)]',
|
||||||
image: 'h-full w-full rounded-[inherit] object-cover'
|
image: 'h-full w-full rounded-[inherit] object-cover'
|
||||||
},
|
},
|
||||||
variants: {
|
variants: {
|
||||||
@@ -464,7 +464,7 @@ This way, the `size` prop will apply the corresponding styles to the `root` slot
|
|||||||
ignore:
|
ignore:
|
||||||
- src
|
- src
|
||||||
props:
|
props:
|
||||||
src: 'https://github.com/benjamincanac.png'
|
src: 'https://github.com/nuxt.png'
|
||||||
size: lg
|
size: lg
|
||||||
---
|
---
|
||||||
::
|
::
|
||||||
|
|||||||
@@ -123,12 +123,16 @@ const items = [{
|
|||||||
label: 'Save',
|
label: 'Save',
|
||||||
icon: 'i-heroicons-document-arrow-down',
|
icon: 'i-heroicons-document-arrow-down',
|
||||||
kbds: ['meta', 'S'],
|
kbds: ['meta', 'S'],
|
||||||
select: () => save()
|
onSelect() {
|
||||||
|
save()
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
label: 'Copy',
|
label: 'Copy',
|
||||||
icon: 'i-heroicons-document-duplicate',
|
icon: 'i-heroicons-document-duplicate',
|
||||||
kbds: ['meta', 'C'],
|
kbds: ['meta', 'C'],
|
||||||
select: () => copy()
|
onSelect() {
|
||||||
|
copy()
|
||||||
|
}
|
||||||
}]
|
}]
|
||||||
|
|
||||||
defineShortcuts(extractShortcuts(items))
|
defineShortcuts(extractShortcuts(items))
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user