Merge remote-tracking branch 'upstream' into feat/cli/overwrite-existing

This commit is contained in:
Dunqing 2023-09-20 15:37:20 +08:00
commit b08cc50bf8
227 changed files with 6973 additions and 2767 deletions

View File

@ -11,5 +11,6 @@ module.exports = {
'symbol-description': 'off',
'no-console': 'warn',
'no-tabs': 'off',
'no-invalid-character': 'off',
},
}

72
.github/workflows/publish.yaml vendored Normal file
View File

@ -0,0 +1,72 @@
name: Publish www
on:
push:
branches:
- dev
paths:
- 'apps/www/**'
pull_request:
branches:
- dev
paths:
- 'apps/www/**'
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
deployments: write
name: Publish to Cloudflare Pages
steps:
- name: Checkout
uses: actions/checkout@v3
# Run a build step here
- name: Setup Node.js environment
uses: actions/setup-node@v2
with:
node-version: 18
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 8
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm i --frozen-lockfile
- name: Build www
run: pnpm build
# Run a action to publish docs
- name: Publish to Cloudflare Pages
uses: cloudflare/pages-action@v1.5.0
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: shadcn-vue
directory: .vitepress/dist
# Optional: Enable this if you want to have GitHub Deployments triggered
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
# Optional: Switch what branch you are publishing to.
# By default this will be the branch which triggered this workflow
# branch: main
# Optional: Change the working directory
workingDirectory: apps/www
wranglerVersion: '3'

52
.github/workflows/test.yaml vendored Normal file
View File

@ -0,0 +1,52 @@
name: Test
on:
push:
branches:
- dev
paths:
- 'packages/**'
pull_request:
branches:
- dev
paths:
- 'packages/**'
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Setup Node.js environment
uses: actions/setup-node@v2
with:
node-version: 16
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 8
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm i --frozen-lockfile
- name: Test
run: pnpm test

1
.gitignore vendored
View File

@ -7,6 +7,7 @@ yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
.nuxt
.env
node_modules
.DS_Store

158
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,158 @@
# Contributing
Thanks for your interest in contributing to shadcn-vue.com. We're happy to have you here.
Please take a moment to review this document before submitting your first pull request. We also strongly recommend that you check for open issues and pull requests to see if someone else is working on something similar.
If you need any help, feel free to reach out to the core team on [Discord](https://chat.radix-vue.com/).
## About this repository
This repository is a monorepo.
- We use [pnpm](https://pnpm.io) and [`workspaces`](https://pnpm.io/workspaces) for development.
## Structure
This repository is structured as follows:
```
apps
└── www
├── src
│ └── content
└── registry
├── default
│ ├── example
│ └── ui
└── new-york
├── example
└── ui
packages
└── cli
```
| Path | Description |
| --------------------- | ---------------------------------------- |
| `apps/www/app` | The Next.js application for the website. |
| `apps/www/content` | The content for the website. |
| `apps/www/registry` | The registry for the components. |
| `packages/cli` | The `shadcn-vue` package. |
## Development
### Start by cloning the repository:
```
git clone git@github.com:radix-vue/shadcn-vue.git
```
### Install dependencies
```
pnpm install
```
### Run a workspace
You can use the `pnpm --filter=[WORKSPACE]` command to start the development process for a workspace or some of the shortcut command that we have setup.
#### Examples
1. To run the `shadcn-vue.com` website:
```
pnpm dev
```
2. To run the `shadcn-vue` cli package:
```
pnpm dev:cli
```
## Documentation
The documentation for this project is located in the `www` workspace. You can run the documentation locally by running the following command:
```bash
pnpm dev
```
Documentation is written using [md](https://vitepress.dev/guide/markdown). You can find the documentation files in the `apps/www/content/docs` directory.
## Components
We use a registry system for developing components. You can find the source code for the components under `apps/www/registry`. The components are organized by styles.
```bash
apps
└── www
└── registry
├── default
│ ├── example
│ └── ui
└── new-york
├── example
└── ui
```
When adding or modifying components, please ensure that:
1. You make the changes for every style.
2. You update the documentation.
3. You run `pnpm build:registry` to update the registry.
## Commit Convention
Before you create a Pull Request, please check whether your commits comply with
the commit conventions used in this repository.
When you create a commit we kindly ask you to follow the convention
`category(scope or module): message` in your commit message while using one of
the following categories:
- `feat / feature`: all changes that introduce completely new code or new
features
- `fix`: changes that fix a bug (ideally you will additionally reference an
issue if present)
- `refactor`: any code related change that is not a fix nor a feature
- `docs`: changing existing or creating new documentation (i.e. README, docs for
usage of a lib or cli usage)
- `build`: all changes regarding the build of the software, changes to
dependencies or the addition of new dependencies
- `test`: all changes regarding tests (adding new tests or changing existing
ones)
- `ci`: all changes regarding the configuration of continuous integration (i.e.
github actions, ci system)
- `chore`: all changes to the repository that do not fit into any of the above
categories
e.g. `feat(components): add new prop to the avatar component`
If you are interested in the detailed specification you can visit
https://www.conventionalcommits.org/ or check out the
[Angular Commit Message Guidelines](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#-commit-message-guidelines).
## Requests for new components
If you have a request for a new component, please open a discussion on GitHub. We'll be happy to help you out.
## CLI
The `shadcn-vue` package is a CLI for adding components to your project. You can find the documentation for the CLI [here](https://shadcn-vue.com/docs/cli).
Any changes to the CLI should be made in the `packages/cli` directory. If you can, it would be great if you could add tests for your changes.
## Testing
Tests are written using [Vitest](https://vitest.dev). You can run all the tests from the root of the repository.
```bash
pnpm test
```
Please ensure that the tests are passing when submitting a pull request. If you're adding new features, please include tests.

View File

@ -15,19 +15,19 @@ Accessible and customizable components that you can copy and paste into your app
## Documentation
Visit https://shadcn-vue.com/docs to view the documentation.
[View documentation here](https://www.shadcn-vue.com/docs/introduction.html)
## Credits
All credits go to these open-source works and resources
- [Shadnc UI](https://ui.shadcn.com) for creating this beautiful project
- [Shadnc Svelte](https://shadcn-svelte.com) for some inspiration for registry
- [Radix Vue](https://radix-vue.com) for doing all the hard work to make sure components are accessible
- [Shadcn UI](https://ui.shadcn.com) for creating this beautiful project.
- [Shadcn Svelte](https://shadcn-svelte.com) for some inspiration for registry.
- [Radix Vue](https://radix-vue.com) for doing all the hard work to make sure components are accessible.
- [VueUse](https://vueuse.org) for providing many useful utilities.
- [ahmedmayara](https://github.com/ahmedmayara/shadcn-vue) for populating many components
## License
Licensed under the [MIT license](https://github.com/shadcn/ui/blob/main/LICENSE.md).
Licensed under the [MIT license](https://github.com/shadcn/ui/blob/main/LICENSE.md).

View File

@ -1,6 +1,8 @@
import path from 'node:path'
import { defineConfig } from 'vitepress'
import Icons from 'unplugin-icons/vite'
import tailwind from 'tailwindcss'
import autoprefixer from 'autoprefixer'
import { siteConfig } from './theme/config/site'
import ComponentPreviewPlugin from './theme/plugins/previewer'
@ -54,8 +56,16 @@ export default defineConfig({
'content/(.*)': '(.*)',
},
vite: {
css: {
postcss: {
plugins: [
tailwind(),
autoprefixer(),
],
},
},
plugins: [
Icons({ compiler: 'vue3', autoInstall: true }) as any,
Icons({ compiler: 'vue3', autoInstall: true }),
],
resolve: {
alias: {

View File

@ -41,7 +41,7 @@ const { style } = useConfigStore()
<StyleSwitcher />
</div>
<div
:class="cn('preview flex min-h-[350px] w-full justify-center p-10', {
:class="cn('preview flex min-h-[350px] w-full justify-center p-6 lg:p-10', {
'items-center': align === 'center',
'items-start': align === 'start',
'items-end': align === 'end',

View File

@ -0,0 +1,60 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import { useClipboard } from '@vueuse/core'
import { useConfigStore } from '@/stores/config'
import { themes } from '@/lib/registry'
import { Button } from '@/lib/registry/new-york/ui/button'
import CheckIcon from '~icons/radix-icons/check'
import CopyIcon from '~icons/radix-icons/copy'
const { theme, config } = useConfigStore()
const activeTheme = computed(() => themes.find(i => i.name === theme.value))
const { copy, copied } = useClipboard()
const codeRef = ref<HTMLElement>()
async function copyCode() {
await copy(codeRef.value?.innerText ?? '')
}
</script>
<template>
<div class="relative">
<pre class="max-h-[450px] overflow-x-auto rounded-lg border bg-zinc-950 !py-0 dark:bg-zinc-900">
<code ref="codeRef" class="relative block rounded font-mono text-sm">
<span class="line">@layer base &#123;</span>
<span class="line">:root &#123;</span>
<span class="line">&nbsp;&nbsp;--background: {{ activeTheme?.cssVars.light.background }};</span>
<span class="line">&nbsp;&nbsp;--foreground: {{ activeTheme?.cssVars.light.foreground }};</span>
<template v-for="prefix in (['card', 'popover', 'primary', 'secondary', 'muted', 'accent', 'destructive'] as const)" :key="prefix">
<span class="line">--{{ prefix }}: {{ activeTheme?.cssVars.light[prefix] }};</span>
<span class="line">--{{ prefix }}-foreground: {{ activeTheme?.cssVars.light[ `${prefix}-foreground`] }};</span>
</template>
<span class="line">&nbsp;&nbsp;--border:{{ activeTheme?.cssVars.light.border }};</span>
<span class="line">&nbsp;&nbsp;--input:{{ activeTheme?.cssVars.light.input }};</span>
<span class="line">&nbsp;&nbsp;--ring:{{ activeTheme?.cssVars.light.ring }};</span>
<span class="line">&nbsp;&nbsp;--radius: {{ config.radius }}rem;</span>
<span class="line">&#125;</span>
<span class="line">&nbsp;</span>
<span class="line">.dark &#123;</span>
<span class="line">&nbsp;&nbsp;--background:{{ activeTheme?.cssVars.dark.background }};</span>
<span class="line">&nbsp;&nbsp;--foreground:{{ activeTheme?.cssVars.dark.foreground }};</span>
<template v-for="prefix in (['card', 'popover', 'primary', 'secondary', 'muted', 'accent', 'destructive'] as const)" :key="prefix">
<span class="line">--{{ prefix }}:{{ activeTheme?.cssVars.dark[ prefix] }};</span>
<span class="line">--{{ prefix }}-foreground:{{ activeTheme?.cssVars.dark[ `${prefix}-foreground`] }};</span>
</template>
<span class="line">&nbsp;&nbsp;--border:{{ activeTheme?.cssVars.dark.border }};</span>
<span class="line">&nbsp;&nbsp;--input:{{ activeTheme?.cssVars.dark.input }};</span>
<span class="line">&nbsp;&nbsp;--ring:{{ activeTheme?.cssVars.dark.ring }};</span>
<span class="line">&#125;</span>
<span class="line">&#125;</span>
</code>
</pre>
<Button size="sm" class="absolute right-4 top-4 bg-muted text-muted-foreground hover:bg-muted hover:text-muted-foreground" @click="copyCode">
<CheckIcon v-if="copied" class="mr-2 h-4 w-4" />
<CopyIcon v-else class="mr-2 h-4 w-4" />
{{ copied ? 'Copied' : 'Copy' }}
</Button>
</div>
</template>

View File

@ -30,7 +30,7 @@ const examples = [
},
{
name: 'Forms',
href: '/examples/forms',
href: '/examples/forms/forms',
code: 'https://github.com/radix-vue/shadcn-vue/tree/dev/apps/www/src/examples/forms',
},
{

View File

@ -0,0 +1,37 @@
<script setup lang="ts">
import { cva } from 'class-variance-authority'
import { computed } from 'vue'
interface KbdProps {
as?: string
size?: 'xs' | 'sm' | 'md'
}
const props = withDefaults(defineProps<KbdProps>(), {
as: 'kbd',
size: 'sm',
})
const kbdClass = computed(() => {
return cva(
'inline-flex items-center pointer-events-none h-5 select-none items-center gap-1 rounded border border-border bg-muted font-sans font-medium',
{
variants: {
size: {
xs: 'min-h-[16px] text-[10px] h-4 px-1',
sm: 'min-h-[20px] text-[11px] h-5 px-1',
md: 'min-h-[24px] text-[12px] h-6 px-1.5',
},
},
},
)({
size: props.size,
})
})
</script>
<template>
<component :is="props.as" :class="kbdClass">
<slot />
</component>
</template>

View File

@ -19,12 +19,11 @@ import DashboardExample from '@/examples/dashboard/Example.vue'
href="/docs/changelog"
class="inline-flex items-center rounded-lg bg-muted px-3 py-1 text-sm font-medium"
>
🎉 <Separator class="mx-2 h-4" orientation="vertical" />
<span class="sm:hidden">Style, a new CLI and more.</span>
<span class="hidden sm:inline">
Introducing Style, a new CLI and more.
🚧 <Separator class="mx-2 h-4" orientation="vertical" />
<span class="sm:hidden">WIP</span>
<span class="hidden sm:inline">WIP
</span>
<ArrowRightIcon class="ml-1 h-4 w-4" />
<!-- <ArrowRightIcon class="ml-1 h-4 w-4" /> -->
</a>
<PageHeaderHeading>Build your component library.</PageHeaderHeading>
<PageHeaderDescription>

View File

@ -0,0 +1,9 @@
<script setup lang="ts">
import { cn } from '@/lib/utils'
</script>
<template>
<a :class="cn('flex w-full flex-col items-center rounded-xl border bg-card p-6 text-card-foreground shadow transition-colors hover:bg-muted/50 sm:p-10', $attrs.class ?? '')">
<slot />
</a>
</template>

View File

@ -20,9 +20,7 @@ const { config } = useConfigStore()
<Select v-model="config.style">
<SelectTrigger :class="cn('h-7 w-[145px] text-xs [&_svg]:h-4 [&_svg]:w-4', props.class)">
<span class="text-muted-foreground">Style: </span>
<SelectValue placeholder="Select style">
{{ styles.find(s => s.name === config.style)?.label }}
</SelectValue>
<SelectValue placeholder="Select style" />
</SelectTrigger>
<SelectContent>
<SelectItem v-for="style in styles" :key="style.name" :value="style.name" class="text-xs">

View File

@ -0,0 +1,39 @@
<script setup lang="ts">
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/lib/registry/default/ui/tabs'
const props = withDefaults(defineProps<{
name: string
align?: 'center' | 'start' | 'end'
sfcTsCode?: string
sfcTsHtml?: string
}>(), { align: 'center' })
</script>
<template>
<div>
<Tabs :default-value="props.name" class="relative mr-auto w-full">
<div class="flex items-center justify-between pb-3">
<TabsList class="w-full justify-start rounded-none border-b bg-transparent p-0">
<TabsTrigger
value="CLI"
class="relative h-9 rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none"
>
CLI
</TabsTrigger>
<TabsTrigger
value="Manual"
class="relative h-9 rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none"
>
Manual
</TabsTrigger>
</TabsList>
</div>
<TabsContent value="CLI" class="relative space-y-10">
<slot name="CLI" />
</TabsContent>
<TabsContent value="Manual">
<slot name="Manual" />
</TabsContent>
</Tabs>
</div>
</template>

View File

@ -42,6 +42,9 @@ function getHeadingsWithHierarchy(divId: string) {
else if (level === 3 && currentLevel?.items) {
currentLevel.items.push(item)
}
else {
hierarchy.items.push(item)
}
})
return hierarchy
}

View File

@ -1,5 +1,7 @@
export { default as ComponentPreview } from './ComponentPreview.vue'
export { default as TabPreview } from './TabPreview.vue'
export { default as Callout } from './Callout.vue'
export { default as LinkedCard } from './LinkedCard.vue'
export { default as ManualInstall } from './ManualInstall.vue'
export { default as Steps } from './Steps.vue'
export { default as VPImage } from './VPImage.vue'

View File

@ -376,30 +376,24 @@ const range = ref({
<CardContent>
<div class="space-y-4">
<div
class="flex w-max max-w-[75%] flex-col gap-2 rounded-lg px-3 py-2 text-sm bg-secondary"
class="flex w-auto max-w-[75%] flex-col gap-2 rounded-lg px-3 py-2 text-sm bg-muted"
>
<p class="text-foreground">
Hi There!, I'm Bear, the founder of Bear Studios. I'm here
to help you with anything you need.
</p>
Hi, how can I help you today?
</div>
<div
class="flex w-max max-w-[75%] flex-col gap-2 rounded-lg px-3 py-2 text-sm ml-auto bg-primary text-primary-foreground"
class="flex w-auto max-w-[75%] flex-col gap-2 rounded-lg px-3 py-2 text-sm ml-auto bg-primary text-primary-foreground"
>
<p>Hey, I'm having trouble with my account.</p>
Hey, I'm having trouble with my account.
</div>
<div
class="flex w-max max-w-[75%] flex-col gap-2 rounded-lg px-3 py-2 text-sm bg-secondary"
class="flex w-auto max-w-[75%] flex-col gap-2 rounded-lg px-3 py-2 text-sm bg-muted"
>
<p class="text-foreground">
Sure, I can help you with that. What seems to be the
problem?
</p>
Sure, I can help you with that. What seems to be the problem?
</div>
<div
class="flex w-max max-w-[75%] flex-col gap-2 rounded-lg px-3 py-2 text-sm ml-auto bg-primary text-primary-foreground"
class="flex w-auto max-w-[75%] flex-col gap-2 rounded-lg px-3 py-2 text-sm ml-auto bg-primary text-primary-foreground"
>
<p>I can't log in.</p>
I can't log in.
</div>
</div>
</CardContent>

View File

@ -95,6 +95,31 @@ export const docsConfig: DocsConfig = {
},
],
},
{
title: 'Installation',
items: [
{
title: 'Vite',
href: '/docs/installation/vite',
items: [],
},
{
title: 'Nuxt',
href: '/docs/installation/nuxt',
items: [],
},
// {
// title: 'Astro',
// href: '/docs/installation/astro',
// items: [],
// },
{
title: 'Laravel',
href: '/docs/installation/laravel',
items: [],
},
],
},
{
title: 'Components',
items: [
@ -155,18 +180,16 @@ export const docsConfig: DocsConfig = {
},
{
title: 'Combobox',
disabled: true,
label: 'Soon',
href: '#',
href: '/docs/components/combobox',
label: 'New',
items: [],
},
{
title: 'Command',
href: '/docs/components/command',
label: 'New',
items: [],
},
// {
// title: "Command",
// href: "#",
// label: "Soon",
// disabled: true,
// items: []
// },
{
title: 'Context Menu',
href: '/docs/components/context-menu',

View File

@ -1,19 +1,25 @@
<script setup lang="ts">
import { useToggle } from '@vueuse/core'
import { Content, useData, useRoute } from 'vitepress'
import { onMounted } from 'vue'
import { docsConfig } from '../config/docs'
import { useMagicKeys, useToggle } from '@vueuse/core'
import { onMounted, ref, watch } from 'vue'
import { Content, useData, useRoute, useRouter } from 'vitepress'
import { SearchIcon } from 'lucide-vue-next'
import { type NavItem, docsConfig } from '../config/docs'
import Logo from '../components/Logo.vue'
import MobileNav from '../components/MobileNav.vue'
// import { Button } from '@/lib/registry/default/ui/button'
// import { Kbd } from '@/lib/registry/default/ui/kbd'
// import LucideSearch from '~icons/lucide/search'
import Kbd from '../components/Kbd.vue'
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator } from '@/lib/registry/default/ui/command'
import { Button } from '@/lib/registry/default/ui/button'
import RadixIconsGithubLogo from '~icons/radix-icons/github-logo'
import TablerBrandX from '~icons/tabler/brand-x'
import RadixIconsMoon from '~icons/radix-icons/moon'
import RadixIconsSun from '~icons/radix-icons/sun'
import { useConfigStore } from '@/stores/config'
import { Dialog, DialogContent } from '@/lib/registry/default/ui/dialog'
import File from '~icons/radix-icons/file'
import Circle from '~icons/radix-icons/circle'
const { radius, theme } = useConfigStore()
// Whenever the component is mounted, update the document class list
@ -25,20 +31,48 @@ onMounted(() => {
const { frontmatter, isDark } = useData()
const $route = useRoute()
const $router = useRouter()
const toggleDark = useToggle(isDark)
const links = [
{
name: 'GitHub',
href: 'https://github.com/radix-vue',
href: 'https://github.com/radix-vue/shadcn-vue',
icon: RadixIconsGithubLogo,
},
{
name: 'X',
href: 'https://x.com',
icon: TablerBrandX,
},
// {
// name: 'X',
// href: 'https://x.com',
// icon: TablerBrandX,
// },
]
const isOpen = ref(false)
const { Meta_K, Ctrl_K } = useMagicKeys()
watch([Meta_K, Ctrl_K], (v) => {
if (v[0] || v[1])
isOpen.value = true
})
function handleSelectLink(item: NavItem) {
if (item.external)
window.open(item.href, '_blank')
else
$router.go(item.href)
isOpen.value = false
}
watch(() => $route.path, (n) => {
// @ts-expect-error View Transition API not supported by all the browser yet
if (document.startViewTransition) {
// @ts-expect-error View Transition API not supported by all the browser yet
document.startViewTransition(() => {
console.log('soft navigating to: ', n)
})
}
})
</script>
<template>
@ -71,19 +105,19 @@ const links = [
</div>
<div class=" flex items-center justify-end space-x-4 ">
<!-- <Button
<Button
variant="outline"
class="w-72 h-8 px-3 hidden lg:flex lg:justify-between lg:items-center"
@click="isOpen = true"
>
<div class="flex items-center">
<LucideSearch class="w-4 h-4 mr-2 text-muted-foreground" />
<SearchIcon class="w-4 h-4 mr-2 text-muted-foreground" />
<span class="text-muted-foreground"> Search for anything... </span>
</div>
<div class="flex items-center gap-x-1">
<Kbd></Kbd>
<Kbd>K</Kbd>
<Kbd> <span></span>K </Kbd>
</div>
</Button> -->
</Button>
<div
v-for="link in links"
@ -110,18 +144,26 @@ const links = [
</header>
<div class="flex-1 bg-background">
<component :is="'docs'" v-if="$route.path.includes('docs')">
<Content />
</component>
<component :is="'examples'" v-else-if="$route.path.includes('examples')">
<Content />
</component>
<component :is="frontmatter.layout" v-else-if="frontmatter.layout">
<slot />
</component>
<main v-else class="container">
<Content />
</main>
<Transition name="fade" mode="out-in">
<component :is="'docs'" v-if="$route.path.includes('docs')">
<Transition name="fade" mode="out-in">
<Content :key="$route.path" />
</Transition>
</component>
<component :is="'examples'" v-else-if="$route.path.includes('examples')">
<Transition name="fade" mode="out-in">
<Content :key="$route.path" />
</Transition>
</component>
<component :is="frontmatter.layout" v-else-if="frontmatter.layout">
<slot />
</component>
<main v-else class="container">
<Transition name="fade" mode="out-in">
<Content :key="$route.path" />
</Transition>
</main>
</Transition>
</div>
<footer class="py-6 md:px-8 md:py-0">
@ -162,5 +204,77 @@ const links = [
</div>
</div>
</footer>
<Dialog v-model:open="isOpen">
<DialogContent class="p-0">
<Command>
<CommandInput placeholder="Type a command or search..." />
<CommandEmpty>
No results found.
</CommandEmpty>
<CommandList
@escape-key-down=" isOpen = false"
>
<CommandGroup heading="Links">
<CommandItem
v-for="item in docsConfig.mainNav"
:key="item.title"
:heading="item.title"
:value="item.title"
class="py-3"
@select="handleSelectLink(item)"
>
<File class="mr-2 h-5 w-5" />
<span>{{ item.title }}</span>
</CommandItem>
</CommandGroup>
<CommandSeparator />
<CommandGroup v-for="item in docsConfig.sidebarNav" :key="item.title" :heading="item.title">
<CommandItem
v-for="subItem in item.items"
:key="subItem.title"
:heading="subItem.title"
:value="subItem.title"
class="py-3"
@select="
handleSelectLink(subItem)"
>
<Circle class="mr-2 h-4 w-4" />
<span>{{ subItem.title }}</span>
</CommandItem>
</CommandGroup>
<CommandSeparator />
<CommandGroup heading="Theme">
<CommandItem
value="light-theme"
class="py-3"
@select="
() => {
isDark = false;
isOpen = false;
}
"
>
<RadixIconsSun class="mr-2 h-5 w-5" />
<span>Light Theme</span>
</CommandItem>
<CommandItem
value="dark-theme"
class="py-3"
@select="
() => {
isDark = true;
isOpen = false;
}
"
>
<RadixIconsMoon class="mr-2 h-5 w-5" />
<span>Dark Theme</span>
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</DialogContent>
</Dialog>
</div>
</template>

View File

@ -5,6 +5,7 @@ import { useData } from 'vitepress'
import PageHeader from '../components/PageHeader.vue'
import PageHeaderHeading from '../components/PageHeaderHeading.vue'
import PageHeaderDescription from '../components/PageHeaderDescription.vue'
import CustomizerCode from '../components/CustomizerCode.vue'
import { RADII, useConfigStore } from '@/stores/config'
import { colors } from '@/lib/registry'
import { Button } from '@/lib/registry/new-york/ui/button'
@ -231,6 +232,7 @@ watch(radius, (radius) => {
Copy and paste the following code into your CSS file.
</DialogDescription>
</DialogHeader>
<CustomizerCode />
</DialogContent>
</Dialog>
</div>

View File

@ -145,3 +145,8 @@ pre code .line {
.line-number {
@apply min-h-[1.375rem] !text-sm !inline-block text-muted-foreground;
}
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 0.3s;
}

View File

@ -86,6 +86,20 @@ export const Index = {
component: () => import('../src/lib/registry/default/example/CollapsibleDemo.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/CollapsibleDemo.vue'],
},
ComboboxDemo: {
name: 'ComboboxDemo',
type: 'components:example',
registryDependencies: ['utils', 'button', 'command', 'popover'],
component: () => import('../src/lib/registry/default/example/ComboboxDemo.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/ComboboxDemo.vue'],
},
CommandDemo: {
name: 'CommandDemo',
type: 'components:example',
registryDependencies: ['command'],
component: () => import('../src/lib/registry/default/example/CommandDemo.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/CommandDemo.vue'],
},
ContextMenuDemo: {
name: 'ContextMenuDemo',
type: 'components:example',
@ -466,6 +480,20 @@ export const Index = {
component: () => import('../src/lib/registry/new-york/example/CollapsibleDemo.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/CollapsibleDemo.vue'],
},
ComboboxDemo: {
name: 'ComboboxDemo',
type: 'components:example',
registryDependencies: ['utils', 'button', 'command', 'popover'],
component: () => import('../src/lib/registry/new-york/example/ComboboxDemo.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/ComboboxDemo.vue'],
},
CommandDemo: {
name: 'CommandDemo',
type: 'components:example',
registryDependencies: ['command'],
component: () => import('../src/lib/registry/new-york/example/CommandDemo.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/CommandDemo.vue'],
},
ContextMenuDemo: {
name: 'ContextMenuDemo',
type: 'components:example',
@ -666,99 +694,99 @@ export const Index = {
name: 'TypographyBlockquote',
type: 'components:example',
registryDependencies: [],
component: () => import('../src/lib/registry/new-york/example/TypographyBlockquote.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/TypographyBlockquote.vue'],
component: () => import('../src/lib/registry/default/example/TypographyBlockquote.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/TypographyBlockquote.vue'],
},
TypographyDemo: {
name: 'TypographyDemo',
type: 'components:example',
registryDependencies: [],
component: () => import('../src/lib/registry/new-york/example/TypographyDemo.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/TypographyDemo.vue'],
component: () => import('../src/lib/registry/default/example/TypographyDemo.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/TypographyDemo.vue'],
},
TypographyH1: {
name: 'TypographyH1',
type: 'components:example',
registryDependencies: [],
component: () => import('../src/lib/registry/new-york/example/TypographyH1.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/TypographyH1.vue'],
component: () => import('../src/lib/registry/default/example/TypographyH1.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/TypographyH1.vue'],
},
TypographyH2: {
name: 'TypographyH2',
type: 'components:example',
registryDependencies: [],
component: () => import('../src/lib/registry/new-york/example/TypographyH2.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/TypographyH2.vue'],
component: () => import('../src/lib/registry/default/example/TypographyH2.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/TypographyH2.vue'],
},
TypographyH3: {
name: 'TypographyH3',
type: 'components:example',
registryDependencies: [],
component: () => import('../src/lib/registry/new-york/example/TypographyH3.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/TypographyH3.vue'],
component: () => import('../src/lib/registry/default/example/TypographyH3.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/TypographyH3.vue'],
},
TypographyH4: {
name: 'TypographyH4',
type: 'components:example',
registryDependencies: [],
component: () => import('../src/lib/registry/new-york/example/TypographyH4.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/TypographyH4.vue'],
component: () => import('../src/lib/registry/default/example/TypographyH4.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/TypographyH4.vue'],
},
TypographyInlineCode: {
name: 'TypographyInlineCode',
type: 'components:example',
registryDependencies: [],
component: () => import('../src/lib/registry/new-york/example/TypographyInlineCode.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/TypographyInlineCode.vue'],
component: () => import('../src/lib/registry/default/example/TypographyInlineCode.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/TypographyInlineCode.vue'],
},
TypographyLarge: {
name: 'TypographyLarge',
type: 'components:example',
registryDependencies: [],
component: () => import('../src/lib/registry/new-york/example/TypographyLarge.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/TypographyLarge.vue'],
component: () => import('../src/lib/registry/default/example/TypographyLarge.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/TypographyLarge.vue'],
},
TypographyLead: {
name: 'TypographyLead',
type: 'components:example',
registryDependencies: [],
component: () => import('../src/lib/registry/new-york/example/TypographyLead.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/TypographyLead.vue'],
component: () => import('../src/lib/registry/default/example/TypographyLead.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/TypographyLead.vue'],
},
TypographyList: {
name: 'TypographyList',
type: 'components:example',
registryDependencies: [],
component: () => import('../src/lib/registry/new-york/example/TypographyList.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/TypographyList.vue'],
component: () => import('../src/lib/registry/default/example/TypographyList.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/TypographyList.vue'],
},
TypographyMuted: {
name: 'TypographyMuted',
type: 'components:example',
registryDependencies: [],
component: () => import('../src/lib/registry/new-york/example/TypographyMuted.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/TypographyMuted.vue'],
component: () => import('../src/lib/registry/default/example/TypographyMuted.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/TypographyMuted.vue'],
},
TypographyP: {
name: 'TypographyP',
type: 'components:example',
registryDependencies: [],
component: () => import('../src/lib/registry/new-york/example/TypographyP.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/TypographyP.vue'],
component: () => import('../src/lib/registry/default/example/TypographyP.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/TypographyP.vue'],
},
TypographySmall: {
name: 'TypographySmall',
type: 'components:example',
registryDependencies: [],
component: () => import('../src/lib/registry/new-york/example/TypographySmall.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/TypographySmall.vue'],
component: () => import('../src/lib/registry/default/example/TypographySmall.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/TypographySmall.vue'],
},
TypographyTable: {
name: 'TypographyTable',
type: 'components:example',
registryDependencies: [],
component: () => import('../src/lib/registry/new-york/example/TypographyTable.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/TypographyTable.vue'],
component: () => import('../src/lib/registry/default/example/TypographyTable.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/TypographyTable.vue'],
},
},
}

View File

@ -9,21 +9,19 @@
"dev": "vitepress dev",
"build": "vitepress build",
"preview": "vitepress preview",
"build:registry": "ts-node --esm --project ./tsconfig.scripts.json ./scripts/build-registry.ts"
"build:registry": "tsx ./scripts/build-registry.ts"
},
"dependencies": {
"@morev/vue-transitions": "^2.3.6",
"@tanstack/vue-table": "^8.9.3",
"@tanstack/vue-table": "^8.9.9",
"@unovis/ts": "^1.2.1",
"@vitejs/plugin-vue-jsx": "^3.0.2",
"@vueuse/core": "^10.2.1",
"class-variance-authority": "^0.6.1",
"@vueuse/core": "^10.4.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"date-fns": "^2.30.0",
"lucide-vue-next": "^0.268.0",
"tailwindcss-animate": "^1.0.6",
"lucide-vue-next": "^0.276.0",
"tailwindcss-animate": "^1.0.7",
"v-calendar": "^3.0.3",
"vitepress": "^1.0.0-rc.10",
"vue": "^3.3.4",
"vue-wrap-balancer": "^1.1.3",
"zod": "^3.22.2"
@ -34,21 +32,22 @@
"@iconify/json": "^2.2.108",
"@iconify/vue": "^4.1.1",
"@types/lodash.template": "^4.5.1",
"@types/node": "^20.5.7",
"@vitejs/plugin-vue": "^4.1.0",
"@types/node": "^20.6.0",
"@vitejs/plugin-vue": "^4.3.4",
"@vitejs/plugin-vue-jsx": "^3.0.2",
"@vue/compiler-core": "^3.3.4",
"@vue/compiler-dom": "^3.3.4",
"autoprefixer": "^10.4.14",
"autoprefixer": "^10.4.15",
"lodash.template": "^4.5.0",
"postcss": "^8.4.24",
"radix-vue": "^0.1.32",
"radix-vue": "^0.2.2",
"rimraf": "^5.0.1",
"tailwind-merge": "^1.14.0",
"tailwindcss": "^3.3.3",
"ts-node": "^10.9.1",
"typescript": "^5.0.2",
"unplugin-icons": "^0.16.6",
"vite": "^4.3.9",
"vue-tsc": "^1.4.2"
"tsx": "^3.12.10",
"typescript": "^5.2.2",
"unplugin-icons": "^0.17.0",
"vite": "^4.4.9",
"vitepress": "^1.0.0-rc.13",
"vue-tsc": "^1.8.11"
}
}

View File

@ -1,6 +0,0 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View File

@ -17,15 +17,14 @@ You will be asked a few questions to configure `components.json`:
```txt:line-numbers
Would you like to use TypeScript (recommended)? no / yes
Which framework are you using? Vite + Vue / Nuxt
Which framework are you using? Vite / Nuxt / Laravel
Which style would you like to use? Default
Which color would you like to use as base color? Slate
Where is your global CSS file? src/index.css
Do you want to use CSS variables for colors? no / yes
Where is your tailwind.config.js located? tailwind.config.js
Configure the import alias for components: @/components
Configure the import alias for utils: @/lib/utils
Are you using React Server Components? no / yes (no)
Configure the import alias for utils: @/lib/utils
```
### Options

View File

@ -8,38 +8,64 @@ primitive: https://www.radix-vue.com/components/accordion.html
<ComponentPreview name="AccordionDemo" class="[&_.accordion]:sm:max-w-[70%]" />
## Installation
<Steps>
### Run the following command
```bash
npx shadcn-vue@latest add accordion
```
<ManualInstall>
### Update `tailwind.config.js`
1. Install `radix-vue`:
Add the following animations to your `tailwind.config.js` file:
```bash
npm install radix-vue
```js title="tailwind.config.js" {5-18}
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
extend: {
keyframes: {
'accordion-down': {
from: { height: 0 },
to: { height: 'var(--radix-accordion-content-height)' },
},
'accordion-up': {
from: { height: 'var(--radix-accordion-content-height)' },
to: { height: 0 },
},
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
},
},
},
}
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
</Steps>
## Usage
```vue
<script setup lang="ts">
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/default/ui/accordion'
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion'
</script>
<AccordionRoot>
<AccordionItem value="item-1">
<AccordionTrigger>Is it accessible?</AccordionTrigger>
<AccordionContent>
Yes. It adheres to the WAI-ARIA design pattern.
</AccordionContent>
</AccordionItem>
</AccordionRoot>
```
<template>
<Accordion type="single" collapsible>
<AccordionItem value="item-1">
<AccordionTrigger>Is it accessible?</AccordionTrigger>
<AccordionContent>
Yes. It adheres to the WAI-ARIA design pattern.
</AccordionContent>
</AccordionItem>
</Accordion>
</template>
```

View File

@ -7,27 +7,15 @@ primitive: https://www.radix-vue.com/components/alert-dialog.html
<ComponentPreview name="AlertDialogDemo" />
## Installation
```bash
npx shadcn-vue@latest add alert-dialog
```
<ManualInstall>
1. Install `radix-vue`:
```bash
npm install radix-vue
npx shadcn-vue@latest add alert-dialog
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
## Usage
```vue
@ -42,20 +30,18 @@ import {
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from '@/lib/registry/default/ui/alert-dialog'
} from '@/components/ui/alert-dialog'
</script>
<template>
<AlertDialog>
<AlertDialogTrigger>
Open
</AlertDialogTrigger>
<AlertDialogTrigger>Open</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete your
account and remove your data from our servers.
This action cannot be undone. This will permanently delete your account
and remove your data from our servers.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>

View File

@ -6,30 +6,18 @@ description: Displays a callout for user attention.
<ComponentPreview name="AlertDemo" />
## Installation
```bash
npx shadcn-vue@latest add alert
```
<ManualInstall>
1. Install `radix-vue`:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
## Usage
```vue
<script setup lang="ts">
import { Alert, AlertDescription, AlertTitle } from '@/lib/registry/default/ui/alert'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
</script>
<template>

View File

@ -10,33 +10,45 @@ primitive: https://www.radix-vue.com/components/aspect-ratio.html
## Installation
<TabPreview name="CLI">
<template #CLI>
```bash
npx shadcn-vue@latest add aspect-ratio
```
</template>
<ManualInstall>
<template #Manual>
1. Install `radix-vue`:
<Steps>
### Install the following dependency:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
### Copy and paste the following code into your project:
<<< @/lib/registry/default/ui/aspect-ratio/AspectRatio.vue
</Steps>
</template>
</TabPreview>
## Usage
```vue
<script setup lang="ts">
import { AspectRatio } from '@/lib/registry/default/ui/aspect-ratio'
import { AspectRatio } from '@/components/ui/aspect-ratio'
</script>
<template>
<AspectRatio>
<img
src="https://images.unsplash.com/photo-1588345921523-c2dcdb7f1dcd?w=800&dpr=2&q=80"
>
</AspectRatio>
<div class="w-[450px]">
<AspectRatio :ratio="16 / 9">
<img src="..." alt="Image" class="rounded-md object-cover">
</AspectRatio>
</div>
</template>
```

View File

@ -11,31 +11,21 @@ primitive: https://www.radix-vue.com/components/avatar.html
## Installation
```bash
npx shadcn-vue@latest add avatar
```
<ManualInstall>
1. Install `radix-vue`:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
```
## Usage
```vue
<script setup lang="ts">
import { Avatar, AvatarFallback, AvatarImage } from '@/lib/registry/default/ui/avatar'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
</script>
<template>
<Avatar>
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
<AvatarImage src="https://github.com/radix-vue.png" alt="@radix-vue" />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
</template>

View File

@ -6,29 +6,73 @@ description: Displays a badge or a component that looks like a badge.
<ComponentPreview name="BadgeDemo" />
## Installation
<TabPreview name="CLI">
<template #CLI>
```bash
npx shadcn-vue@latest add badge
```
</template>
<ManualInstall>
<template #Manual>
1. Install `radix-vue`:
<Steps>
```bash
npm install radix-vue
### Copy and paste the following code into your project
```vue
<script setup lang="ts">
import { type VariantProps, cva } from 'class-variance-authority'
import { cn } from '@/lib/utils'
defineProps<Props>()
const badgeVariants = cva(
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
{
variants: {
variant: {
default:
'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
secondary:
'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
destructive:
'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
outline: 'text-foreground',
},
},
defaultVariants: {
variant: 'default',
},
},
)
interface BadgeVariantProps extends VariantProps<typeof badgeVariants> {}
interface Props {
variant?: BadgeVariantProps['variant']
}
</script>
<template>
<div :class="cn(badgeVariants({ variant }), $attrs.class ?? '')">
<slot />
</div>
</template>
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
</Steps>
</template>
</TabPreview>
## Usage
```vue
<script setup lang="ts">
import { Badge } from '@/lib/registry/default/ui/badge'
import { Badge } from '@/components/ui/badge'
</script>
<template>

View File

@ -8,26 +8,85 @@ description: Displays a button or a component that looks like a button.
## Installation
<TabPreview name="CLI">
<template #CLI>
```bash
npx shadcn-vue@latest add button
```
</template>
<ManualInstall>
<template #Manual>
1. Install `radix-vue`:
<Steps>
```bash
npm install radix-vue
### Copy and paste the following code into your project
```vue
<script setup lang="ts">
import { cva } from 'class-variance-authority'
import { cn } from '@/lib/utils'
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive:
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline:
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
secondary:
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
},
)
interface Props {
variant?: NonNullable<Parameters<typeof buttonVariants>[0]>['variant']
size?: NonNullable<Parameters<typeof buttonVariants>[0]>['size']
as?: string
}
// eslint-disable-next-line vue/define-macros-order
withDefaults(defineProps<Props>(), {
as: 'button',
})
</script>
<template>
<component
:is="as"
:class="cn(buttonVariants({ variant, size }), $attrs.class ?? '')"
>
<slot />
</component>
</template>
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
</Steps>
</template>
</TabPreview>
## Usage
```vue
<script setup lang="ts">
import { Button } from '@/lib/registry/default/ui/button'
import { Button } from '@/components/ui/button'
</script>
<template>

View File

@ -11,27 +11,42 @@ description: A date field component that allows users to enter and edit date.
The `Calendar` component is built on top of [VCalendar](https://vcalendar.io/getting-started/installation.html).
## Installation
<TabPreview name="CLI">
<template #CLI>
```bash
npx shadcn-vue@latest add calendar
```
</template>
<ManualInstall>
<template #Manual>
1. Install `radix-vue`:
<Steps>
### Install the following dependency
```bash
npm install radix-vue
npm install v-calendar
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
### Copy and paste the following code into your project
<<< @/lib/registry/default/ui/calendar/Calendar.vue
</Steps>
</template>
</TabPreview>
## Usage
```vue
<script setup lang="ts">
import { Calendar } from '@/lib/registry/default/ui/calendar'
import { Calendar } from '@/components/ui/calendar'
</script>
<template>

View File

@ -6,23 +6,13 @@ description: Displays a card with header, content, and footer.
<ComponentPreview name="CardDemo" />
## Installation
```bash
npx shadcn-vue@latest add card
```
<ManualInstall>
1. Install `radix-vue`:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
```
## Usage
@ -35,7 +25,7 @@ import {
CardFooter,
CardHeader,
CardTitle,
} from '@/lib/registry/default/ui/card'
} from '@/components/ui/card'
</script>
<template>

View File

@ -10,30 +10,26 @@ primitive: https://www.radix-vue.com/components/checkbox.html
## Installation
```bash
npx shadcn-vue@latest add checkbox
```
<ManualInstall>
1. Install `radix-vue`:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
```
## Usage
```vue
<script setup lang="ts">
import { Checkbox } from '@/lib/registry/default/ui/checkbox'
import { Checkbox } from '@/components/ui/checkbox'
</script>
<template>
<Checkbox id="terms" />
</template>
```
```
## Examples
### With text
<ComponentPreview name="CheckboxDisabled" />

View File

@ -8,47 +8,33 @@ primitive: https://www.radix-vue.com/components/collapsible.html
<ComponentPreview name="CollapsibleDemo" />
## Installation
```bash
npx shadcn-vue@latest add collapsible
```
<ManualInstall>
1. Install `radix-vue`:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
```
## Usage
```vue
<script setup lang="ts">
import { ref } from 'vue'
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from '@/lib/registry/default/ui/collapsible'
} from '@/components/ui/collapsible'
const isOpen = ref(false)
</script>
<template>
<Collapsible
v-model:open="isOpen"
>
<CollapsibleTrigger>
Trigger
</CollapsibleTrigger>
<Collapsible v-model:open="isOpen">
<CollapsibleTrigger>Can I use this in my project?</CollapsibleTrigger>
<CollapsibleContent>
Content
Yes. Free to use for personal and commercial projects. No attribution
required.
</CollapsibleContent>
</Collapsible>
</template>

View File

@ -0,0 +1,89 @@
---
title: Combobox
description: Autocomplete input and command palette with a list of suggestions.
component: true
---
<ComponentPreview name="ComboboxDemo" />
## Installation
The Combobox is built using a composition of the `<Popover />` and the `<Command />` components.
See installation instructions for the [Popover](/docs/components/popover#installation) and the [Command](/docs/components/command#installation) components.
## Usage
```vue
<script setup lang="ts">
import { Check, ChevronsUpDown } from 'lucide-vue-next'
import { ref } from 'vue'
import { cn } from '@/lib/utils'
import { Button } from '@/components/ui/button'
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
} from '@/components/ui/command'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/components/ui/popover'
const frameworks = [
{ value: 'next.js', label: 'Next.js' },
{ value: 'sveltekit', label: 'SvelteKit' },
{ value: 'nuxt.js', label: 'Nuxt.js' },
{ value: 'remix', label: 'Remix' },
{ value: 'astro', label: 'Astro' },
]
const open = ref(false)
const value = ref({})
</script>
<template>
<Popover v-model:open="open">
<PopoverTrigger as-child>
<Button
variant="outline"
role="combobox"
:aria-expanded="open"
class="w-[200px] justify-between"
>
{{ value ? frameworks.find((framework) => framework.value === value)?.label : 'Select framework...' }}
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent class="w-[200px] p-0">
<Command v-model="value">
<CommandInput placeholder="Search framework..." />
<CommandEmpty>No framework found.</CommandEmpty>
<CommandGroup>
<CommandItem
v-for="framework in frameworks"
:key="framework.value"
:value="framework"
@select="open = false"
>
<Check
:class="cn(
'mr-2 h-4 w-4',
value === framework.value ? 'opacity-100' : 'opacity-0',
)"
/>
{{ framework.label }}
</CommandItem>
</CommandGroup>
</Command>
</PopoverContent>
</Popover>
</template>
```

View File

@ -0,0 +1,54 @@
---
title: Command
description: Fast, composable, unstyled command menu.
source: apps/www/src/lib/registry/default/ui/command
primitive: https://www.radix-vue.com/components/combobox.html
---
<ComponentPreview name="CommandDemo" />
## Installation
```bash
npx shadcn-vue@latest add command
```
## Usage
```vue
<script setup lang="ts">
import {
Command,
CommandDialog,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
CommandShortcut,
} from '@/components/ui/command'
</script>
<template>
<Command>
<CommandInput placeholder="Type a command or search..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup heading="Suggestions">
<CommandItem>Calendar</CommandItem>
<CommandItem>Search Emoji</CommandItem>
<CommandItem>Calculator</CommandItem>
</CommandGroup>
<CommandSeparator />
<CommandGroup heading="Settings">
<CommandItem>Profile</CommandItem>
<CommandItem>Billing</CommandItem>
<CommandItem>Settings</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</template>```

View File

@ -8,24 +8,11 @@ primitive: https://www.radix-vue.com/components/context-menu.html
<ComponentPreview name="ContextMenuDemo" />
## Installation
```bash
npx shadcn-vue@latest add context-menu
```
<ManualInstall>
1. Install `radix-vue`:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
```
## Usage
@ -45,7 +32,7 @@ import {
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from '@/lib/registry/default/ui/context-menu'
} from '@/components/ui/context-menu'
</script>
<template>

View File

@ -21,13 +21,13 @@ import { Calendar as CalendarIcon } from 'lucide-vue-next'
import { ref } from 'vue'
import { cn } from '@/lib/utils'
import { Button } from '@/lib/registry/default/ui/button'
import { Calendar } from '@/lib/registry/default/ui/calendar'
import { Button } from '@/components/ui/button'
import { Calendar } from '@/components/ui/calendar'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/lib/registry/default/ui/popover'
} from '@/components/ui/popover'
const date = ref<Date>()
</script>

View File

@ -7,25 +7,12 @@ primitive: https://www.radix-vue.com/components/dialog.html
<ComponentPreview name="DialogDemo" />
## Installation
## Installation
```bash
npx shadcn-vue@latest add dialog
```
<ManualInstall>
1. Install `radix-vue`:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
## Usage
```vue
@ -38,7 +25,7 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/lib/registry/default/ui/dialog'
} from '@/components/ui/dialog'
</script>
<template>

View File

@ -8,24 +8,11 @@ primitive: https://www.radix-vue.com/components/dropdown-menu.html
<ComponentPreview name="DropdownMenuDemo" />
## Installation
```bash
npx shadcn-vue@latest add dropdown-menu
```
<ManualInstall>
1. Install `radix-vue`:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
```
## Usage
```vue
@ -37,7 +24,7 @@ import {
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/lib/registry/default/ui/dropdown-menu'
} from '@/components/ui/dropdown-menu'
</script>
<template>

View File

@ -8,24 +8,11 @@ primitive: https://www.radix-vue.com/components/hover-card.html
<ComponentPreview name="HoverCardDemo" />
## Installation
```bash
npx shadcn-vue@latest add hover-card
```
<ManualInstall>
1. Install `radix-vue`:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
```
## Usage
```vue
@ -34,14 +21,14 @@ import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from '@/lib/registry/default/ui/hover-card'
} from '@/components/ui/hover-card'
</script>
<template>
<HoverCard>
<HoverCardTrigger>Hover</HoverCardTrigger>
<HoverCardContent>
The React Framework created and maintained by @vercel.
The Vue Framework created and maintained by @vuejs.
</HoverCardContent>
</HoverCard>
</template>

View File

@ -6,30 +6,36 @@ description: Displays a form input field or a component that looks like an input
<ComponentPreview name="InputDemo" class="[&_input]:max-w-xs" />
## Installation
<TabPreview name="CLI">
<template #CLI>
```bash
npx shadcn-vue@latest add input
```
</template>
<ManualInstall>
<template #Manual>
1. Install `radix-vue`:
<Steps>
```bash
npm install radix-vue
```
### Copy and paste the following code into your project:
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
<<< @/lib/registry/default/ui/input/Input.vue
</Steps>
</template>
</TabPreview>
## Usage
```vue
<script setup lang="ts">
import { Input } from '@/lib/registry/default/ui/input'
import { Input } from '@/components/ui/input'
</script>
<template>

View File

@ -10,26 +10,38 @@ primitive: https://www.radix-vue.com/components/label.html
## Installation
<TabPreview name="CLI">
<template #CLI>
```bash
npx shadcn-vue@latest add input
npx shadcn-vue@latest add label
```
</template>
<ManualInstall>
<template #Manual>
1. Install `radix-vue`:
<Steps>
### Install the following dependency:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
### Copy and paste the following code into your project:
<<< @/lib/registry/default/ui/label/Label.vue
</Steps>
</template>
</TabPreview>
## Usage
```vue
<script setup lang="ts">
import { Label } from '@/lib/registry/default/ui/label'
import { Label } from '@/components/ui/label'
</script>
<template>

View File

@ -7,25 +7,13 @@ primitive: https://www.radix-vue.com/components/menubar.html
<ComponentPreview name="MenubarDemo" />
## Installation
```bash
npx shadcn-vue@latest add menubar
```
<ManualInstall>
1. Install `radix-vue`:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
## Usage
```vue
@ -38,7 +26,7 @@ import {
MenubarSeparator,
MenubarShortcut,
MenubarTrigger,
} from '@/lib/registry/default/ui/menubar'
} from '@/components/ui/menubar'
</script>
<template>

View File

@ -7,24 +7,12 @@ primitive: https://www.radix-vue.com/components/navigation-menu.html
<ComponentPreview name="NavigationMenuDemo" />
## Installation
## Installation
```bash
npx shadcn-vue@latest add navigation-menu
```
<ManualInstall>
1. Install `radix-vue`:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
## Usage
```vue
@ -38,7 +26,7 @@ import {
NavigationMenuList,
NavigationMenuTrigger,
NavigationMenuViewport,
} from '@/lib/registry/default/ui/navigation-menu'
} from '@/components/ui/navigation-menu'
</script>
<template>

View File

@ -8,25 +8,13 @@ primitive: https://www.radix-vue.com/components/popover.html
<ComponentPreview name="PopoverDemo" />
## Installation
```bash
npx shadcn-vue@latest add popover
```
<ManualInstall>
1. Install `radix-vue`:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
## Usage
```vue
@ -35,7 +23,7 @@ import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/lib/registry/default/ui/popover'
} from '@/components/ui/popover'
</script>
<template>

View File

@ -11,26 +11,38 @@ primitive: https://www.radix-vue.com/components/progress.html
## Installation
<TabPreview name="CLI">
<template #CLI>
```bash
npx shadcn-vue@latest add progress
```
</template>
<ManualInstall>
<template #Manual>
1. Install `radix-vue`:
<Steps>
### Install the following dependency:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
### Copy and paste the following code into your project:
<<< @/lib/registry/default/ui/progress/Progress.vue
</Steps>
</template>
</TabPreview>
## Usage
```vue
<script setup lang="ts">
import { Progress } from '@/lib/registry/default/ui/progress'
import { Progress } from '@/components/ui/progress'
</script>
<template>

View File

@ -7,31 +7,19 @@ primitive: https://www.radix-vue.com/components/radio-group.html
<ComponentPreview name="RadioGroupDemo" />
## Installation
```bash
npx shadcn-vue@latest add radio-group
```
<ManualInstall>
1. Install `radix-vue`:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
## Usage
```vue
<script setup lang="ts">
import { Label } from '@/lib/registry/default/ui/label'
import { RadioGroup, RadioGroupItem } from '@/lib/registry/default/ui/radio-group'
import { Label } from '@/components/ui/label'
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
</script>
<template>

View File

@ -7,30 +7,17 @@ primitive: https://www.radix-vue.com/components/scroll-area.html
<ComponentPreview name="ScrollAreaDemo" />
## Installation
```bash
npx shadcn-vue@latest add scroll-area
```
<ManualInstall>
1. Install `radix-vue`:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
## Usage
```vue
<script setup lang="ts">
import { ScrollArea } from '@/lib/registry/default/ui/scroll-area'
import { ScrollArea } from '@/components/ui/scroll-area'
</script>
<template>

View File

@ -8,25 +8,13 @@ primitive: https://www.radix-vue.com/components/popover.html
<ComponentPreview name="SelectDemo" />
## Installation
```bash
npx shadcn-vue@latest add select
```
<ManualInstall>
1. Install `radix-vue`:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
## Usage
```vue
@ -39,7 +27,7 @@ import {
SelectLabel,
SelectTrigger,
SelectValue,
} from '@/lib/registry/default/ui/select'
} from '@/components/ui/select'
</script>
<template>

View File

@ -8,29 +8,41 @@ primitive: https://www.radix-vue.com/components/separator.html
<ComponentPreview name="SeparatorDemo" />
## Installation
<TabPreview name="CLI">
<template #CLI>
```bash
npx shadcn-vue@latest add separator
```
</template>
<ManualInstall>
<template #Manual>
1. Install `radix-vue`:
<Steps>
### Install the following dependency
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
### Copy and paste the following code into your project
<<< @/lib/registry/default/ui/separator/Separator.vue
</Steps>
</template>
</TabPreview>
## Usage
```vue
<script setup lang="ts">
import { Separator } from '@/lib/registry/default/ui/separator'
import { Separator } from '@/components/ui/separator'
</script>
<template>

View File

@ -9,22 +9,11 @@ primitive: https://www.radix-vue.com/components/dialog.html
## Installation
```bash
npx shadcn-vue@latest add sheet
```
<ManualInstall>
1. Install `radix-vue`:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
## Usage
```vue
@ -36,7 +25,7 @@ import {
SheetHeader,
SheetTitle,
SheetTrigger,
} from '@/lib/registry/default/ui/sheet'
} from '@/components/ui/sheet'
</script>
<template>

View File

@ -5,29 +5,35 @@ description: Use to show a placeholder while content is loading.
<ComponentPreview name="SkeletonDemo" />
## Installation
<TabPreview name="CLI">
<template #CLI>
```bash
npx shadcn-vue@latest add skeleton
```
</template>
<ManualInstall>
<template #Manual>
1. Install `radix-vue`:
<Steps>
```bash
npm install radix-vue
```
### Copy and paste the following code into your project
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
<<< @/lib/registry/default/ui/skeleton/Skeleton.vue
</Steps>
</template>
</TabPreview>
## Usage
```vue
<script setup lang="ts">
import { Skeleton } from '@/lib/registry/default/ui/skeleton'
import { Skeleton } from '@/components/ui/skeleton'
</script>
<template>

View File

@ -7,30 +7,17 @@ primitive: https://www.radix-vue.com/components/slider.html
<ComponentPreview name="SliderDemo" />
## Installation
```bash
npx shadcn-vue@latest add slider
```
<ManualInstall>
1. Install `radix-vue`:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
## Usage
```vue
<script setup lang="ts">
import { Slider } from '@/lib/registry/default/ui/slider'
import { Slider } from '@/components/ui/slider'
</script>
<template>

View File

@ -10,26 +10,38 @@ primitive: https://www.radix-vue.com/components/switch.html
## Installation
<TabPreview name="CLI">
<template #CLI>
```bash
npx shadcn-vue@latest add switch
```
</template>
<ManualInstall>
<template #Manual>
1. Install `radix-vue`:
<Steps>
### Install the following dependency:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
### Copy and paste the following code into your project
<<< @/lib/registry/default/ui/switch/Switch.vue
</Steps>
</template>
</TabPreview>
## Usage
```vue
<script setup lang="ts">
import { Switch } from '@/lib/registry/default/ui/switch'
import { Switch } from '@/components/ui/switch'
</script>
<template>

View File

@ -12,17 +12,6 @@ description: A responsive table component.
npx shadcn-vue@latest add table
```
<ManualInstall>
1. Install `radix-vue`:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
## Usage
```vue
@ -35,7 +24,7 @@ import {
TableHead,
TableHeader,
TableRow,
} from '@/lib/registry/default/ui/table'
} from '@/components/ui/table'
</script>
<template>

View File

@ -10,26 +10,17 @@ primitive: https://www.radix-vue.com/components/tabs.html
## Installation
```bash
npx shadcn-vue@latest add tabs
```
<ManualInstall>
1. Install `radix-vue`:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
## Usage
```vue
<script setup lang="ts">
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/lib/registry/default/ui/tabs'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
</script>
<template>

View File

@ -8,26 +8,38 @@ description: Displays a form textarea or a component that looks like a textarea.
## Installation
<TabPreview name="CLI">
<template #CLI>
```bash
npx shadcn-vue@latest add textarea
```
</template>
<ManualInstall>
<template #Manual>
1. Install `radix-vue`:
<Steps>
### Install the following dependency:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
### Copy and paste the following code into your project
<<< @/lib/registry/default/ui/textarea/Textarea.vue
</Steps>
</template>
</TabPreview>
## Usage
```vue
<script setup lang="ts">
import { Textarea } from '@/lib/registry/default/ui/textarea'
import { Textarea } from '@/components/ui/textarea'
</script>
<template>

View File

@ -11,26 +11,38 @@ primitive: https://www.radix-vue.com/components/toggle.html
## Installation
<TabPreview name="CLI">
<template #CLI>
```bash
npx shadcn-vue@latest add toggle
```
</template>
<ManualInstall>
<template #Manual>
1. Install `radix-vue`:
<Steps>
### Install the following dependencies:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
### Copy and paste the following code into your project
<<< @/lib/registry/default/ui/toggle/Toggle.vue
</Steps>
</template>
</TabPreview>
## Usage
```vue
<script setup lang="ts">
import { Toggle } from '@/lib/registry/default/ui/toggle'
import { Toggle } from '@/components/ui/toggle'
</script>
<template>

View File

@ -14,17 +14,6 @@ primitive: https://www.radix-vue.com/components/tooltip.html
npx shadcn-vue@latest add tooltip
```
<ManualInstall>
1. Install `radix-vue`:
```bash
npm install radix-vue
```
2. Copy and paste the component source files linked at the top of this page into your project.
</ManualInstall>
## Usage
```vue
@ -34,7 +23,7 @@ import {
TooltipContent,
TooltipProvider,
TooltipTrigger
} from '@/lib/registry/default/ui/tooltip'
} from '@/components/ui/tooltip'
</script>
<template>

View File

@ -18,4 +18,8 @@ The Figma UI Kit is open sourced by [Pietro Schirano](https://twitter.com/skiran
## Grab a copy
https://www.figma.com/community/file/1203061493325953101
<div class="break-words">
https://www.figma.com/community/file/1203061493325953101
</div>

View File

@ -2,128 +2,94 @@
title: Installation
description: How to install dependencies and structure your app.
---
<script setup>
import { Alert, AlertDescription } from "@/lib/registry/default/ui/alert";
</script>
Unlike the original [shadcn/ui](https://ui.shadcn.com) for React, where the full components can exist in a single file, components in this port are split into multiple files due to majority vote from [Vue community](https://twitter.com/zernonia/status/1694351679540580524) to use `SFC` rather than `h()` render function or `JSX`, so utilizing the CLI to add components will be the optimal approach.
## Frameworks
The CLI will create a folder for _each_ component, which will sometimes just contain a single Vue file, and in other times, multiple files. Within each folder, there will be an `index.ts` file that exports the component(s), so you can import them from a single file.
<div class="grid sm:grid-cols-2 gap-4 mt-8 sm:gap-6 not-docs">
<LinkedCard href="/docs/installation/vite">
<svg
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
class="w-10 h-10"
fill="currentColor"
>
<title>Vite</title>
<path d="m8.286 10.578.512-8.657a.306.306 0 0 1 .247-.282L17.377.006a.306.306 0 0 1 .353.385l-1.558 5.403a.306.306 0 0 0 .352.385l2.388-.46a.306.306 0 0 1 .332.438l-6.79 13.55-.123.19a.294.294 0 0 1-.252.14c-.177 0-.35-.152-.305-.369l1.095-5.301a.306.306 0 0 0-.388-.355l-1.433.435a.306.306 0 0 1-.389-.354l.69-3.375a.306.306 0 0 0-.37-.36l-2.32.536a.306.306 0 0 1-.374-.316zm14.976-7.926L17.284 3.74l-.544 1.887 2.077-.4a.8.8 0 0 1 .84.369.8.8 0 0 1 .034.783L12.9 19.93l-.013.025-.015.023-.122.19a.801.801 0 0 1-.672.37.826.826 0 0 1-.634-.302.8.8 0 0 1-.16-.67l1.029-4.981-1.12.34a.81.81 0 0 1-.86-.262.802.802 0 0 1-.165-.67l.63-3.08-2.027.468a.808.808 0 0 1-.768-.233.81.81 0 0 1-.217-.6l.389-6.57-7.44-1.33a.612.612 0 0 0-.64.906L11.58 23.691a.612.612 0 0 0 1.066-.004l11.26-20.135a.612.612 0 0 0-.644-.9z" />
</svg>
<p class="font-medium mt-2">Vite</p>
</LinkedCard>
<LinkedCard href="/docs/installation/nuxt">
<svg xmlns="http://www.w3.org/2000/svg" class="w-12 h-12" viewBox="0 0 900 900" fill="none">
<title>Nuxt</title>
<path d="M504.908 750H839.476C850.103 750.001 860.542 747.229 869.745 741.963C878.948 736.696 886.589 729.121 891.9 719.999C897.211 710.876 900.005 700.529 900 689.997C899.995 679.465 897.193 669.12 891.873 660.002L667.187 274.289C661.876 265.169 654.237 257.595 645.036 252.329C635.835 247.064 625.398 244.291 614.773 244.291C604.149 244.291 593.711 247.064 584.511 252.329C575.31 257.595 567.67 265.169 562.36 274.289L504.908 372.979L392.581 179.993C387.266 170.874 379.623 163.301 370.42 158.036C361.216 152.772 350.777 150 340.151 150C329.525 150 319.086 152.772 309.883 158.036C300.679 163.301 293.036 170.874 287.721 179.993L8.12649 660.002C2.80743 669.12 0.00462935 679.465 5.72978e-06 689.997C-0.00461789 700.529 2.78909 710.876 8.10015 719.999C13.4112 729.121 21.0523 736.696 30.255 741.963C39.4576 747.229 49.8973 750.001 60.524 750H270.538C353.748 750 415.112 713.775 457.336 643.101L559.849 467.145L614.757 372.979L779.547 655.834H559.849L504.908 750ZM267.114 655.737L120.551 655.704L340.249 278.586L449.87 467.145L376.474 593.175C348.433 639.03 316.577 655.737 267.114 655.737Z" fill="white"/>
</svg>
<p class="font-medium mt-2">Nuxt</p>
</LinkedCard>
<!-- <LinkedCard href="/docs/installation/astro">
<svg
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
class="w-10 h-10"
fill="currentColor"
>
<title>Astro</title>
<path
d="M16.074 16.86C15.354 17.476 13.917 17.895 12.262 17.895C10.23 17.895 8.527 17.263 8.075 16.412C7.914 16.9 7.877 17.458 7.877 17.814C7.877 17.814 7.771 19.564 8.988 20.782C8.988 20.15 9.501 19.637 10.133 19.637C11.216 19.637 11.215 20.582 11.214 21.349V21.418C11.214 22.582 11.925 23.579 12.937 24C12.7812 23.6794 12.7005 23.3275 12.701 22.971C12.701 21.861 13.353 21.448 14.111 20.968C14.713 20.585 15.383 20.161 15.844 19.308C16.0926 18.8493 16.2225 18.3357 16.222 17.814C16.2221 17.4903 16.1722 17.1685 16.074 16.86ZM15.551 0.6C15.747 0.844 15.847 1.172 16.047 1.829L20.415 16.176C18.7743 15.3246 17.0134 14.7284 15.193 14.408L12.35 4.8C12.3273 4.72337 12.2803 4.65616 12.2162 4.60844C12.152 4.56072 12.0742 4.53505 11.9943 4.53528C11.9143 4.5355 11.8366 4.56161 11.7727 4.60969C11.7089 4.65777 11.6623 4.72524 11.64 4.802L8.83 14.405C7.00149 14.724 5.23264 15.3213 3.585 16.176L7.974 1.827C8.174 1.171 8.274 0.843 8.471 0.6C8.64406 0.385433 8.86922 0.218799 9.125 0.116C9.415 0 9.757 0 10.443 0H13.578C14.264 0 14.608 0 14.898 0.117C15.1529 0.219851 15.3783 0.386105 15.551 0.6Z"
fill="currentColor"
/>
</svg>
<p class="font-medium mt-2">Astro</p>
</LinkedCard> -->
<LinkedCard href="/docs/installation/laravel">
<svg
role="img"
viewBox="0 0 62 65"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
class="w-10 h-10"
>
<path d="M61.8548 14.6253C61.8778 14.7102 61.8895 14.7978 61.8897 14.8858V28.5615C61.8898 28.737 61.8434 28.9095 61.7554 29.0614C61.6675 29.2132 61.5409 29.3392 61.3887 29.4265L49.9104 36.0351V49.1337C49.9104 49.4902 49.7209 49.8192 49.4118 49.9987L25.4519 63.7916C25.3971 63.8227 25.3372 63.8427 25.2774 63.8639C25.255 63.8714 25.2338 63.8851 25.2101 63.8913C25.0426 63.9354 24.8666 63.9354 24.6991 63.8913C24.6716 63.8838 24.6467 63.8689 24.6205 63.8589C24.5657 63.8389 24.5084 63.8215 24.456 63.7916L0.501061 49.9987C0.348882 49.9113 0.222437 49.7853 0.134469 49.6334C0.0465019 49.4816 0.000120578 49.3092 0 49.1337L0 8.10652C0 8.01678 0.0124642 7.92953 0.0348998 7.84477C0.0423783 7.8161 0.0598282 7.78993 0.0697995 7.76126C0.0884958 7.70891 0.105946 7.65531 0.133367 7.6067C0.152063 7.5743 0.179485 7.54812 0.20192 7.51821C0.230588 7.47832 0.256763 7.43719 0.290416 7.40229C0.319084 7.37362 0.356476 7.35243 0.388883 7.32751C0.425029 7.29759 0.457436 7.26518 0.498568 7.2415L12.4779 0.345059C12.6296 0.257786 12.8015 0.211853 12.9765 0.211853C13.1515 0.211853 13.3234 0.257786 13.475 0.345059L25.4531 7.2415H25.4556C25.4955 7.26643 25.5292 7.29759 25.5653 7.32626C25.5977 7.35119 25.6339 7.37362 25.6625 7.40104C25.6974 7.43719 25.7224 7.47832 25.7523 7.51821C25.7735 7.54812 25.8021 7.5743 25.8196 7.6067C25.8483 7.65656 25.8645 7.70891 25.8844 7.76126C25.8944 7.78993 25.9118 7.8161 25.9193 7.84602C25.9423 7.93096 25.954 8.01853 25.9542 8.10652V33.7317L35.9355 27.9844V14.8846C35.9355 14.7973 35.948 14.7088 35.9704 14.6253C35.9792 14.5954 35.9954 14.5692 36.0053 14.5405C36.0253 14.4882 36.0427 14.4346 36.0702 14.386C36.0888 14.3536 36.1163 14.3274 36.1375 14.2975C36.1674 14.2576 36.1923 14.2165 36.2272 14.1816C36.2559 14.1529 36.292 14.1317 36.3244 14.1068C36.3618 14.0769 36.3942 14.0445 36.4341 14.0208L48.4147 7.12434C48.5663 7.03694 48.7383 6.99094 48.9133 6.99094C49.0883 6.99094 49.2602 7.03694 49.4118 7.12434L61.3899 14.0208C61.4323 14.0457 61.4647 14.0769 61.5021 14.1055C61.5333 14.1305 61.5694 14.1529 61.5981 14.1803C61.633 14.2165 61.6579 14.2576 61.6878 14.2975C61.7103 14.3274 61.7377 14.3536 61.7551 14.386C61.7838 14.4346 61.8 14.4882 61.8199 14.5405C61.8312 14.5692 61.8474 14.5954 61.8548 14.6253ZM59.893 27.9844V16.6121L55.7013 19.0252L49.9104 22.3593V33.7317L59.8942 27.9844H59.893ZM47.9149 48.5566V37.1768L42.2187 40.4299L25.953 49.7133V61.2003L47.9149 48.5566ZM1.99677 9.83281V48.5566L23.9562 61.199V49.7145L12.4841 43.2219L12.4804 43.2194L12.4754 43.2169C12.4368 43.1945 12.4044 43.1621 12.3682 43.1347C12.3371 43.1097 12.3009 43.0898 12.2735 43.0624L12.271 43.0586C12.2386 43.0275 12.2162 42.9888 12.1887 42.9539C12.1638 42.9203 12.1339 42.8916 12.114 42.8567L12.1127 42.853C12.0903 42.8156 12.0766 42.7707 12.0604 42.7283C12.0442 42.6909 12.023 42.656 12.013 42.6161C12.0005 42.5688 11.998 42.5177 11.9931 42.4691C11.9881 42.4317 11.9781 42.3943 11.9781 42.3569V15.5801L6.18848 12.2446L1.99677 9.83281ZM12.9777 2.36177L2.99764 8.10652L12.9752 13.8513L22.9541 8.10527L12.9752 2.36177H12.9777ZM18.1678 38.2138L23.9574 34.8809V9.83281L19.7657 12.2459L13.9749 15.5801V40.6281L18.1678 38.2138ZM48.9133 9.14105L38.9344 14.8858L48.9133 20.6305L58.8909 14.8846L48.9133 9.14105ZM47.9149 22.3593L42.124 19.0252L37.9323 16.6121V27.9844L43.7219 31.3174L47.9149 33.7317V22.3593ZM24.9533 47.987L39.59 39.631L46.9065 35.4555L36.9352 29.7145L25.4544 36.3242L14.9907 42.3482L24.9533 47.987Z" />
</svg>
<p class="font-medium mt-2">Laravel</p>
</LinkedCard>
</div>
For example, the Accordion component is split into four `.vue` files:
- `Accordion.vue`
- `AccordionContent.vue`
- `AccordionItem.vue`
- `AccordionTrigger.vue`
They can then be imported from the `accordion/index.ts` file like so:
## TypeScript
```ts
import * as Accordion from '@/components/ui/accordion'
This project and the components are written in TypeScript. We recommend using TypeScript for your project as well.
// or
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger
} from '@/components/ui/accordion'
```
However we provide a JavaScript version of the components as well. The JavaScript version is available via the [cli](/docs/cli).
Regardless of the import approach you take, the components will be tree-shaken by Rollup, so you don't have to worry about unused components being bundled into your app.
To opt-out of TypeScript, you can use the `typescript` flag in your `components.json` file.
## New Project
<Steps>
### Create project
Use the Vue CLI to create a new project.
```bash
npm create vue@latest
```
### Add Tailwind and its configuration
Install `tailwindcss` and its peer dependencies, then generate your `tailwind.config.js` and `postcss.config.js` files:
```bash
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
```
### Install dependencies
```bash
npm install
```
### Run the CLI
```bash
npx shadcn-vue@latest init
```
### Configure components.json
You will be asked a few questions to configure `components.json`:
```txt:line-numbers
Would you like to use TypeScript (recommended)? no / yes
Which framework are you using? Vite + Vue / Nuxt
Which style would you like to use? Default
Which color would you like to use as base color? Slate
Where is your global CSS file? src/index.css
Do you want to use CSS variables for colors? no / yes
Where is your tailwind.config.js located? tailwind.config.js
Configure the import alias for components: @/components
Configure the import alias for utils: @/lib/utils
Are you using React Server Components? no / yes (no)
```
### Edit tsconfig.json
By default your `tsconfig.json` for new project should be configured nicely. However, make sure the code below is added in the compilerOptions of your tsconfig.json so your app can resolve paths without error
```json
```json {9} title="components.json"
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
"style": "default",
"tailwind": {
"config": "tailwind.config.js",
"css": "src/app/globals.css",
"baseColor": "zinc",
"cssVariables": true
},
"typescript": false,
"aliases": {
"utils": "~/lib/utils",
"components": "~/components"
}
}
```
To configure import aliases, you can use the following `jsconfig.json`:
### That's it
You can now start adding components to your project.
```bash
npx shadcn-vue@latest add button
```
The command above will add the `Button` component to your project. You can then import it like this:
```vue {2,7}
<script setup lang="ts">
import { Button } from '@/components/ui/button'
</script>
<template>
<div>
<Button>Click me</Button>
</div>
</template>
```
</Steps>
```json {4} title="jsconfig.json"
{
"compilerOptions": {
"paths": {
"@/*": ["./*"]
}
}
}

View File

@ -0,0 +1,149 @@
---
title: Astro
description: Install and configure Astro.
---
<Steps>
### Create project
Start by creating a new Astro project:
```bash
npm create astro@latest
```
### Configure your Astro project
You will be asked a few questions to configure your project:
```txt showLineNumbers
- Where should we create your new project?
./your-app-name
- How would you like to start your new project?
Choose a starter template (or Empty)
- Install dependencies?
Yes
- Do you plan to write TypeScript?
Yes
- How strict should TypeScript be?
Strict
- Initialize a new git repository? (optional)
Yes/No
```
### Add React to your project
Install React using the Astro CLI:
```bash
npx astro add react
```
<Callout className="mt-4">
Answer `Yes` to all the question prompted by the CLI when installing React.
</Callout>
### Add Tailwind CSS to your project
Install Tailwind CSS using the Astro CLI:
```bash
npx astro add tailwind
```
<Callout className="mt-4">
Answer `Yes` to all the question prompted by the CLI when installing Tailwind CSS.
</Callout>
### Edit tsconfig.json file
Add the code below to the tsconfig.json file to resolve paths:
```json {2-7} showLineNumbers
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}
```
### Run the CLI
Run the `shadcn-ui` init command to setup your project:
```bash
npx shadcn-ui@latest init
```
### Configure components.json
You will be asked a few questions to configure `components.json`:
```txt showLineNumbers
Would you like to use TypeScript (recommended)? no / yes
Which style would you like to use? Default
Which color would you like to use as base color? Slate
Where is your global CSS file? ./src/styles/globals.css
Do you want to use CSS variables for colors? no / yes
Where is your tailwind.config.js located? tailwind.config.cjs
Configure the import alias for components: @/components
Configure the import alias for utils: @/lib/utils
Are you using React Server Components? no
```
### Import the globals.css file
Import the `globals.css` file in the `src/index.astro` file:
```ts {2} showLineNumbers
import '@/styles/globals.css'
```
### Update astro tailwind config
To prevent serving the Tailwind base styles twice, we need to tell Astro not to apply the base styles, since we already include them in our own `globals.css` file. To do this, set the `applyBaseStyles` config option for the tailwind plugin in `astro.config.mjs` to `false`.
```ts {3-5} showLineNumbers
export default defineConfig({
integrations: [
tailwind({
applyBaseStyles: false,
}),
],
})
```
### That's it
You can now start adding components to your project.
```bash
npx shadcn-ui@latest add button
```
The command above will add the `Button` component to your project. You can then import it like this:
```astro {2,10} showLineNumbers
---
import { Button } from "@/components/ui/button"
---
<html lang="en">
<head>
<title>Astro</title>
</head>
<body>
<Button>Hello World</Button>
</body>
</html>
```
</Steps>

View File

@ -0,0 +1,153 @@
---
title: Laravel
description: Install and configure Laravel with Inertia
---
<Steps>
### Create project
Start by creating a new Laravel project with Inertia and Vue using the Laravel installer `laravel new my-app`:
```bash
laravel new my-app --typescript --breeze --stack=vue --git --no-interaction
```
### Run the CLI
Run the `shadcn-vue` init command to setup your project:
```bash
npx shadcn-vue@latest init
```
### Configure components.json
You will be asked a few questions to configure `components.json`:
```txt showLineNumbers
Would you like to use TypeScript (recommended)? no / yes
Which framework are you using? Vite / Nuxt / Laravel
Which style would you like to use? Default
Which color would you like to use as base color? Slate
Where is your global CSS file? resources/css/app.css
Do you want to use CSS variables for colors? no / yes
Where is your tailwind.config.js located? tailwind.config.js
Configure the import alias for components: @/Components
Configure the import alias for utils: @/lib/utils
```
### Update tailwind.config.js
The `shadcn-vue` CLI will automatically overwrite your `tailwind.config.js`. Update it to look like this:
```js
import forms from '@tailwindcss/forms'
import defaultTheme from 'tailwindcss/defaultTheme'
/** @type {import('tailwindcss').Config} */
export default {
darkMode: 'class',
content: [
'./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php',
'./storage/framework/views/*.php',
'./resources/views/**/*.blade.php',
'./resources/js/**/*.tsx',
],
theme: {
container: {
center: true,
padding: '2rem',
screens: {
'2xl': '1400px',
},
},
extend: {
colors: {
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))',
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))',
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))',
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))',
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))',
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))',
},
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))',
},
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)',
},
fontFamily: {
sans: ['Figtree', ...defaultTheme.fontFamily.sans],
},
keyframes: {
'accordion-down': {
from: { height: 0 },
to: { height: 'var(--radix-accordion-content-height)' },
},
'accordion-up': {
from: { height: 'var(--radix-accordion-content-height)' },
to: { height: 0 },
},
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
},
},
},
plugins: [forms, require('tailwindcss-animate')],
}
```
### That's it
You can now start adding components to your project.
```bash
npx shadcn-vue@latest add button
```
The command above will add the `Button` component to your project. You can then import it like this:
```vue {2,7}
<script setup lang="ts">
import { Button } from '@/Components/ui/button'
</script>
<template>
<div>
<Button>Click me</Button>
</div>
</template>
```
</Steps>

View File

@ -0,0 +1,124 @@
---
title: Nuxt
description: Install and configure Nuxt.
---
<Steps>
### Create project
Start by creating a new Nuxt project using `create-next-app`:
```bash
npx nuxi@latest init my-app
```
### Install TailwindCSS module
```bash
npm install -D @nuxtjs/tailwindcss
```
### Configure `nuxt.config.ts`
```ts
export default defineNuxtConfig({
modules: ['@nuxtjs/tailwindcss'],
components: [
{
path: '~/components/ui',
// this is required else Nuxt will autoImport `.ts` file
extensions: ['.vue'],
// prefix for your components, eg: UiButton
prefix: 'Ui'
},
],
})
```
### Run the CLI
Run the `shadcn-vue` init command to setup your project:
```bash
npx shadcn-vue@latest init
```
### Configure components.json
You will be asked a few questions to configure `components.json`:
```txt showLineNumbers
Would you like to use TypeScript (recommended)? no / yes
Which framework are you using? Vite / Nuxt / Laravel
Which style would you like to use? Default
Which color would you like to use as base color? Slate
Where is your global CSS file? src/index.css
Do you want to use CSS variables for colors? no / yes
Where is your tailwind.config.js located? tailwind.config.js
Configure the import alias for components: @/components
Configure the import alias for utils: @/lib/utils
```
### App structure
Here's the default structure of Nuxt app. You can use this as a reference:
```txt {6-16,20-21}
.
├── pages
│ ├── index.vue
│ └── dashboard.vue
├── components
│ ├── ui
│ │ ├── alert-dialog
│ │ │ ├── AlertDialog.vue
│ │ │ └── ...
│ │ ├── button
│ │ │ ├── Button.vue
│ │ │ └── ...
│ │ ├── dropdown-menu
│ │ │ ├── Dropdown.vue
│ │ │ └── ...
│ │ └── ...
│ ├── MainNav.vue
│ ├── PageHeader.vue
│ └── ...
├── lib
│ └── utils.ts
├── assets
│ ├── css
│ │ └── tailwind.css
├── app.vue
├── nuxt.config.ts
├── package.json
├── tailwind.config.js
└── tsconfig.json
```
- I place the UI components in the `components/ui` folder.
- The rest of the components such as `<PageHeader />` and `<MainNav />` are placed in the `components` folder.
- The `lib` folder contains all the utility functions. I have a `utils.ts` where I define the `cn` helper.
- The `assets/css` folder contains the global CSS.
### That's it
You can now start adding components to your project.
```bash
npx shadcn-vue@latest add button
```
The command above will add the `Button` component to your project. Nuxt autoImport will handle importing the components, you can just use it as such:
```vue {3}
<template>
<div>
<UiButton>Click me</UiButton>
</div>
</template>
```
</Steps>

View File

@ -0,0 +1,106 @@
---
title: Vite
description: Install and configure Vite.
---
<Steps>
### Create project
Start by creating a new Vue project using `vite`:
```bash
# npm 6.x
npm create vite@latest my-vue-app --template vue
# npm 7+, extra double-dash is needed:
npm create vite@latest my-vue-app -- --template vue
```
### Add Tailwind and its configuration
Install `tailwindcss` and its peer dependencies, then generate your `tailwind.config.js` and `postcss.config.js` files:
```bash
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
```
### Edit tsconfig.json
Add the code below to the compilerOptions of your tsconfig.json so your app can resolve paths without error
```typescript
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
```
### Update vite.config.ts
Add the code below to the vite.config.ts so your app can resolve paths without error
```typescript
import path from "path"
import vue from "@vitejs/plugin-vue"
import { defineConfig } from "vite"
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
})
```
### Run the CLI
Run the `shadcn-vue` init command to setup your project:
```bash
npx shadcn-vue@latest init
```
### Configure components.json
You will be asked a few questions to configure `components.json`:
```txt showLineNumbers
Would you like to use TypeScript (recommended)? no / yes
Which framework are you using? Vite / Nuxt / Laravel
Which style would you like to use? Default
Which color would you like to use as base color? Slate
Where is your global CSS file? src/index.css
Do you want to use CSS variables for colors? no / yes
Where is your tailwind.config.js located? tailwind.config.js
Configure the import alias for components: @/components
Configure the import alias for utils: @/lib/utils
```
### That's it
You can now start adding components to your project.
```bash
npx shadcn-vue@latest add button
```
The command above will add the `Button` component to your project. You can then import it like this:
```vue {2,7}
<script setup lang="ts">
import { Button } from '@/components/ui/button'
</script>
<template>
<div>
<Button>Click me</Button>
</div>
</template>
```
</Steps>

View File

@ -17,7 +17,6 @@ To use utility classes for theming set `tailwind.cssVariables` to `false` in you
```json {8} title="components.json"
{
"style": "default",
"rsc": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "app/globals.css",
@ -43,7 +42,6 @@ To use CSS variables for theming set `tailwind.cssVariables` to `true` in your `
```json {8} title="components.json"
{
"style": "default",
"rsc": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "app/globals.css",

View File

@ -4,112 +4,56 @@ description: Styles for headings, paragraphs, lists...etc
component: true
---
<ComponentPreview name="TypographyDemo">
<div/>
</ComponentPreview>
<ComponentPreview name="TypographyDemo" />
## h1
<ComponentPreview name="TypographyH1">
<div/>
</ComponentPreview>
<ComponentPreview name="TypographyH1" />
## h2
<ComponentPreview name="TypographyH2">
<div/>
</ComponentPreview>
<ComponentPreview name="TypographyH2" />
## h3
<ComponentPreview name="TypographyH3">
<div/>
</ComponentPreview>
<ComponentPreview name="TypographyH3" />
## h4
<ComponentPreview name="TypographyH4">
<div/>
</ComponentPreview>
<ComponentPreview name="TypographyH4" />
## p
<ComponentPreview name="TypographyP">
<div/>
</ComponentPreview>
<ComponentPreview name="TypographyP" />
## blockquote
<ComponentPreview name="TypographyBlockquote">
<div/>
</ComponentPreview>
<ComponentPreview name="TypographyBlockquote" />
## table
<ComponentPreview name="TypographyTable">
<div/>
</ComponentPreview>
<ComponentPreview name="TypographyTable" />
## list
<ComponentPreview name="TypographyList">
<div/>
</ComponentPreview>
<ComponentPreview name="TypographyList" />
## Inline code
<ComponentPreview name="TypographyInlineCode">
<div/>
</ComponentPreview>
<ComponentPreview name="TypographyInlineCode" />
## Lead
<ComponentPreview name="TypographyLead">
<div/>
</ComponentPreview>
<ComponentPreview name="TypographyLead" />
## Large
<ComponentPreview name="TypographyLarge">
<div/>
</ComponentPreview>
<ComponentPreview name="TypographyLarge" />
## Small
<ComponentPreview name="TypographySmall">
<div/>
</ComponentPreview>
<ComponentPreview name="TypographySmall" />
## Muted
<ComponentPreview name="TypographyMuted">
<div/>
</ComponentPreview>
<ComponentPreview name="TypographyMuted" />

View File

@ -0,0 +1,5 @@
<script setup>
import AccountExample from "@/examples/forms/Account.vue"
</script>
<AccountExample />

View File

@ -0,0 +1,5 @@
<script setup>
import AppearanceExample from "@/examples/forms/Appearance.vue"
</script>
<AppearanceExample />

View File

@ -0,0 +1,5 @@
<script setup>
import DisplayExample from "@/examples/forms/Display.vue"
</script>
<DisplayExample />

View File

@ -0,0 +1,5 @@
<script setup>
import FormsExample from "@/examples/forms/Example.vue"
</script>
<FormsExample />

View File

@ -0,0 +1,5 @@
<script setup>
import NotificationsExample from "@/examples/forms/Notifications.vue"
</script>
<NotificationsExample />

View File

@ -1,4 +1,5 @@
<script setup lang="ts">
import { ref } from 'vue'
import ChevronDownIcon from '~icons/radix-icons/chevron-down'
import {
@ -14,12 +15,16 @@ import {
CardHeader,
CardTitle,
} from '@/lib/registry/new-york/ui/card'
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/lib/registry/new-york/ui/command'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/lib/registry/new-york/ui/popover'
const sofiaRole = ref('Owner')
const jacksonRole = ref('Member')
</script>
<template>
@ -49,43 +54,43 @@ import {
<Popover>
<PopoverTrigger as-child>
<Button variant="outline" class="ml-auto">
Owner
{{ sofiaRole }}
<ChevronDownIcon class="ml-2 h-4 w-4 text-muted-foreground" />
</Button>
</PopoverTrigger>
<PopoverContent class="p-0" align="end">
<!-- <Command>
<CommandInput placeholder="Select new role..." />
<CommandList>
<CommandEmpty>No roles found.</CommandEmpty>
<CommandGroup>
<CommandItem class="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Viewer</p>
<p class="text-sm text-muted-foreground">
Can view and comment.
</p>
</CommandItem>
<CommandItem class="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Developer</p>
<p class="text-sm text-muted-foreground">
Can view, comment and edit.
</p>
</CommandItem>
<CommandItem class="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Billing</p>
<p class="text-sm text-muted-foreground">
Can view, comment and manage billing.
</p>
</CommandItem>
<CommandItem class="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Owner</p>
<p class="text-sm text-muted-foreground">
Admin-level access to all resources.
</p>
</CommandItem>
</CommandGroup>
</CommandList>
</Command> -->
<Command v-model="sofiaRole">
<CommandInput placeholder="Select new role..." />
<CommandList>
<CommandEmpty>No roles found.</CommandEmpty>
<CommandGroup>
<CommandItem value="Viewer" class="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Viewer</p>
<p class="text-sm text-muted-foreground">
Can view and comment.
</p>
</CommandItem>
<CommandItem value="Developer" class="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Developer</p>
<p class="text-sm text-muted-foreground">
Can view, comment and edit.
</p>
</CommandItem>
<CommandItem value="Billing" class="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Billing</p>
<p class="text-sm text-muted-foreground">
Can view, comment and manage billing.
</p>
</CommandItem>
<CommandItem value="Owner" class="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Owner</p>
<p class="text-sm text-muted-foreground">
Admin-level access to all resources.
</p>
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
</div>
@ -107,43 +112,43 @@ import {
<Popover>
<PopoverTrigger as-child>
<Button variant="outline" class="ml-auto">
Member
{{ jacksonRole }}
<ChevronDownIcon class="ml-2 h-4 w-4 text-muted-foreground" />
</Button>
</PopoverTrigger>
<PopoverContent class="p-0" align="end">
<!-- <Command>
<CommandInput placeholder="Select new role..." />
<CommandList>
<CommandEmpty>No roles found.</CommandEmpty>
<CommandGroup class="p-1.5">
<CommandItem class="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Viewer</p>
<p class="text-sm text-muted-foreground">
Can view and comment.
</p>
</CommandItem>
<CommandItem class="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Developer</p>
<p class="text-sm text-muted-foreground">
Can view, comment and edit.
</p>
</CommandItem>
<CommandItem class="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Billing</p>
<p class="text-sm text-muted-foreground">
Can view, comment and manage billing.
</p>
</CommandItem>
<CommandItem class="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Owner</p>
<p class="text-sm text-muted-foreground">
Admin-level access to all resources.
</p>
</CommandItem>
</CommandGroup>
</CommandList>
</Command> -->
<Command v-model="jacksonRole">
<CommandInput placeholder="Select new role..." />
<CommandList>
<CommandEmpty>No roles found.</CommandEmpty>
<CommandGroup>
<CommandItem value="Viewer" class="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Viewer</p>
<p class="text-sm text-muted-foreground">
Can view and comment.
</p>
</CommandItem>
<CommandItem value="Developer" class="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Developer</p>
<p class="text-sm text-muted-foreground">
Can view, comment and edit.
</p>
</CommandItem>
<CommandItem value="Billing" class="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Billing</p>
<p class="text-sm text-muted-foreground">
Can view, comment and manage billing.
</p>
</CommandItem>
<CommandItem value="Owner" class="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Owner</p>
<p class="text-sm text-muted-foreground">
Admin-level access to all resources.
</p>
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
</div>

View File

@ -1,6 +1,8 @@
<script setup lang="ts">
import { ref } from 'vue'
import CaretSortIcon from '~icons/radix-icons/caret-sort'
import CheckIcon from '~icons/radix-icons/check'
import PlusCircledIcon from '~icons/radix-icons/plus-circled'
import { cn } from '@/lib/utils'
import {
@ -17,7 +19,9 @@ import {
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/lib/registry/new-york/ui/dialog'
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator } from '@/lib/registry/new-york/ui/command'
import { Input } from '@/lib/registry/new-york/ui/input'
import { Label } from '@/lib/registry/new-york/ui/label'
import {
@ -88,60 +92,58 @@ const selectedTeam = ref<Team>(groups[0].teams[0])
</Button>
</PopoverTrigger>
<PopoverContent class="w-[200px] p-0">
<!-- <Command>
<CommandList>
<CommandInput placeholder="Search team..." />
<CommandEmpty>No team found.</CommandEmpty>
{groups.map((group) => (
<CommandGroup key={group.label} heading={group.label}>
{group.teams.map((team) => (
<CommandItem
key={team.value}
onSelect={() => {
setSelectedTeam(team)
setOpen(false)
}}
class="text-sm"
>
<Avatar class="mr-2 h-5 w-5">
<AvatarImage
src={`https://avatar.vercel.sh/${team.value}.png`}
alt={team.label}
class="grayscale"
/>
<AvatarFallback>SC</AvatarFallback>
</Avatar>
{team.label}
<CheckIcon
class={cn(
"ml-auto h-4 w-4",
selectedTeam.value === team.value
? "opacity-100"
: "opacity-0"
)}
/>
</CommandItem>
))}
</CommandGroup>
))}
</CommandList>
<CommandSeparator />
<CommandList>
<CommandGroup>
<DialogTrigger asChild>
<CommandItem
onSelect={() => {
setOpen(false)
setShowNewTeamDialog(true)
}}
>
<PlusCircledIcon class="mr-2 h-5 w-5" />
Create Team
</CommandItem>
</DialogTrigger>
</CommandGroup>
</CommandList>
</Command> -->
<Command :filter-function="(list, term) => list.filter(i => i.label?.toLowerCase()?.includes(term)) ">
<CommandList>
<CommandInput placeholder="Search team..." />
<CommandEmpty>No team found.</CommandEmpty>
<CommandGroup v-for="group in groups" :key="group.label" :heading="group.label">
<CommandItem
v-for="team in group.teams"
:key="team.value"
:value="team"
class="text-sm"
@select="() => {
selectedTeam = team
open = false
}"
>
<Avatar class="mr-2 h-5 w-5">
<AvatarImage
:src="`https://avatar.vercel.sh/${team.value}.png`"
:alt="team.label"
class="grayscale"
/>
<AvatarFallback>SC</AvatarFallback>
</Avatar>
{{ team.label }}
<CheckIcon
:class="cn('ml-auto h-4 w-4',
selectedTeam.value === team.value
? 'opacity-100'
: 'opacity-0',
)"
/>
</CommandItem>
</CommandGroup>
</CommandList>
<CommandSeparator />
<CommandList>
<CommandGroup>
<DialogTrigger as-child>
<CommandItem
:value="{ label: 'Create Team' }"
@select="() => {
open = false
showNewTeamDialog = true
}"
>
<PlusCircledIcon class="mr-2 h-5 w-5" />
Create Team
</CommandItem>
</DialogTrigger>
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
<DialogContent>

View File

@ -0,0 +1,10 @@
<script setup lang="ts">
import FormsLayout from './layouts/FormsLayout.vue'
import AccountForm from './components/AccountForm.vue'
</script>
<template>
<FormsLayout>
<AccountForm />
</FormsLayout>
</template>

View File

@ -0,0 +1,10 @@
<script setup lang="ts">
import FormsLayout from './layouts/FormsLayout.vue'
import AppearanceForm from './components/AppearanceForm.vue'
</script>
<template>
<FormsLayout>
<AppearanceForm />
</FormsLayout>
</template>

View File

@ -0,0 +1,10 @@
<script setup lang="ts">
import FormsLayout from './layouts/FormsLayout.vue'
import DisplayForm from './components/DisplayForm.vue'
</script>
<template>
<FormsLayout>
<DisplayForm />
</FormsLayout>
</template>

View File

@ -0,0 +1,10 @@
<script setup lang="ts">
import FormsLayout from './layouts/FormsLayout.vue'
import ProfileForm from './components/ProfileForm.vue'
</script>
<template>
<FormsLayout>
<ProfileForm />
</FormsLayout>
</template>

View File

@ -0,0 +1,10 @@
<script setup lang="ts">
import FormsLayout from './layouts/FormsLayout.vue'
import NotificationsForm from './components/NotificationsForm.vue'
</script>
<template>
<FormsLayout>
<NotificationsForm />
</FormsLayout>
</template>

View File

@ -0,0 +1,158 @@
<script setup lang="ts">
import { ref } from 'vue'
import * as z from 'zod'
import { format } from 'date-fns'
import { cn } from '@/lib/utils'
import RadixIconsCalendar from '~icons/radix-icons/calendar'
import { Input } from '@/lib/registry/new-york/ui/input'
import { Label } from '@/lib/registry/new-york/ui/label'
import { Separator } from '@/lib/registry/new-york/ui/separator'
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/lib/registry/new-york/ui/select'
import { Button } from '@/lib/registry/new-york/ui/button'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/lib/registry/default/ui/popover'
import { Calendar } from '@/lib/registry/new-york/ui/calendar'
const accountForm = ref({
name: '',
dob: null,
language: '',
})
const languages = [
{ label: 'English', value: 'en' },
{ label: 'French', value: 'fr' },
{ label: 'German', value: 'de' },
{ label: 'Spanish', value: 'es' },
{ label: 'Portuguese', value: 'pt' },
{ label: 'Russian', value: 'ru' },
{ label: 'Japanese', value: 'ja' },
{ label: 'Korean', value: 'ko' },
{ label: 'Chinese', value: 'zh' },
] as const
const accountFormSchema = z.object({
name: z
.string()
.min(2, {
message: 'Name must be at least 2 characters.',
})
.max(30, {
message: 'Name must not be longer than 30 characters.',
}),
dob: z.date({
required_error: 'A date of birth is required.',
}),
language: z.string().nonempty({
message: 'Please select a language.',
}),
})
type AccountFormValues = z.infer<typeof accountFormSchema>
const errors = ref<z.ZodFormattedError<AccountFormValues> | null>(null)
async function handleSubmit() {
const result = accountFormSchema.safeParse(accountForm.value)
if (!result.success) {
errors.value = result.error.format()
console.log(errors.value)
return
}
console.log('Form submitted!', accountForm.value)
}
</script>
<template>
<div>
<h3 class="text-lg font-medium">
Account
</h3>
<p class="text-sm text-muted-foreground">
Update your account settings. Set your preferred language and timezone.
</p>
</div>
<Separator />
<form class="space-y-8" @submit.prevent="handleSubmit">
<div class="grid gap-2">
<Label for="name" :class="cn('text-sm', errors?.name && 'text-destructive')">
Name
</Label>
<Input id="name" v-model="accountForm.name" placeholder="Your name" />
<span class="text-muted-foreground text-sm">
This is the name that will be displayed on your profile and in emails.
</span>
<div v-if="errors?.name" class="text-sm text-destructive">
<span v-for="error in errors.name._errors" :key="error">{{ error }}</span>
</div>
</div>
<div class="grid gap-2">
<Label for="dob" :class="cn('text-sm', errors?.dob && 'text-destructive')">
Date of Birth
</Label>
<Popover>
<PopoverTrigger as-child>
<Button
variant="outline"
:class="cn(
'w-[280px] pl-3 text-left font-normal',
!accountForm.dob && 'text-muted-foreground',
)"
>
<span>{{ accountForm.dob ? format(accountForm.dob, "PPP") : "Pick a date" }}</span>
<RadixIconsCalendar class="ml-auto h-4 w-4 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent class="p-0">
<Calendar v-model="accountForm.dob" />
</PopoverContent>
</Popover>
<span class="text-muted-foreground text-sm">
Your date of birth is used to calculate your age.
</span>
<div v-if="errors?.dob" class="text-sm text-destructive">
<span v-for="error in errors.dob._errors" :key="error">{{ error }}</span>
</div>
</div>
<div class="grid gap-2">
<Label for="language" :class="cn('text-sm', errors?.language && 'text-destructive')">
Language
</Label>
<Select id="language" v-model="accountForm.language">
<SelectTrigger class="w-[200px]">
<SelectValue placeholder="Select a language" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem v-for="language in languages" :key="language.value" :value="language.value">
{{ language.label }}
</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<span class="text-muted-foreground text-sm">
This is the language that will be used in the dashboard.
</span>
<div v-if="errors?.language" class="text-sm text-destructive">
<span v-for="error in errors.language._errors" :key="error">{{ error }}</span>
</div>
</div>
<div class="flex justify-start">
<Button type="submit">
Update account
</Button>
</div>
</form>
</template>

View File

@ -0,0 +1,169 @@
<script setup lang="ts">
import { ref } from 'vue'
import * as z from 'zod'
import { cn } from '@/lib/utils'
import { Label } from '@/lib/registry/new-york/ui/label'
import { Separator } from '@/lib/registry/new-york/ui/separator'
import { RadioGroup, RadioGroupItem } from '@/lib/registry/default/ui/radio-group'
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/lib/registry/new-york/ui/select'
import { Button } from '@/lib/registry/new-york/ui/button'
const appearenceForm = ref({
theme: 'light',
font: '',
})
const appearanceFormSchema = z.object({
theme: z.enum(['light', 'dark'], {
required_error: 'Please select a theme.',
}),
font: z.enum(['inter', 'manrope', 'system'], {
invalid_type_error: 'Select a font',
required_error: 'Please select a font.',
}),
})
type AppearanceFormValues = z.infer<typeof appearanceFormSchema>
const errors = ref<z.ZodFormattedError<AppearanceFormValues> | null>(null)
async function handleSubmit() {
const result = appearanceFormSchema.safeParse(appearenceForm.value)
if (!result.success) {
errors.value = result.error.format()
console.log(errors.value)
return
}
console.log('Form submitted!', appearenceForm.value)
}
</script>
<template>
<div>
<h3 class="text-lg font-medium">
Appearence
</h3>
<p class="text-sm text-muted-foreground">
Customize the appearance of the app. Automatically switch between day and night themes.
</p>
</div>
<Separator />
<form class="space-y-8" @submit.prevent="handleSubmit">
<div class="grid gap-2">
<Label for="font" :class="cn('text-sm', errors?.font && 'text-destructive')">
Font
</Label>
<Select id="font" v-model="appearenceForm.font">
<SelectTrigger class="w-[200px]">
<SelectValue placeholder="Select a font" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value="inter">
Inter
</SelectItem>
<SelectItem value="manrope">
Manrope
</SelectItem>
<SelectItem value="system">
System
</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<span class="text-muted-foreground text-xs">
Set the font you want to use in the dashboard.
</span>
<div v-if="errors?.font" class="text-sm text-destructive">
<span v-for="error in errors.font._errors" :key="error">{{ error }}</span>
</div>
</div>
<div class="grid gap-2">
<Label for="theme" :class="cn('text-sm', errors?.theme && 'text-destructive')">
Theme
</Label>
<span class="text-muted-foreground text-xs">
Select the theme for the dashboard.
</span>
<RadioGroup
v-model="appearenceForm.theme"
default-value="light"
class="grid max-w-md grid-cols-2 gap-8 pt-2"
>
<div class="grid gap-2">
<Label class="[&:has([data-state=checked])>div]:border-primary">
<div>
<RadioGroupItem value="light" class="sr-only" />
</div>
<div class="items-center rounded-md border-2 border-muted p-1 hover:border-accent">
<div class="space-y-2 rounded-sm bg-[#ecedef] p-2">
<div class="space-y-2 rounded-md bg-white p-2 shadow-sm">
<div class="h-2 w-[80px] rounded-lg bg-[#ecedef]" />
<div class="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
</div>
<div class="flex items-center space-x-2 rounded-md bg-white p-2 shadow-sm">
<div class="h-4 w-4 rounded-full bg-[#ecedef]" />
<div class="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
</div>
<div class="flex items-center space-x-2 rounded-md bg-white p-2 shadow-sm">
<div class="h-4 w-4 rounded-full bg-[#ecedef]" />
<div class="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
</div>
</div>
</div>
<span class="block w-full p-2 text-center font-normal">
Light
</span>
</Label>
</div>
<div>
<Label class="[&:has([data-state=checked])>div]:border-primary">
<div>
<RadioGroupItem value="dark" class="sr-only" />
</div>
<div class="items-center rounded-md border-2 border-muted bg-popover p-1 hover:bg-accent hover:text-accent-foreground">
<div class="space-y-2 rounded-sm bg-slate-950 p-2">
<div class="space-y-2 rounded-md bg-slate-800 p-2 shadow-sm">
<div class="h-2 w-[80px] rounded-lg bg-slate-400" />
<div class="h-2 w-[100px] rounded-lg bg-slate-400" />
</div>
<div class="flex items-center space-x-2 rounded-md bg-slate-800 p-2 shadow-sm">
<div class="h-4 w-4 rounded-full bg-slate-400" />
<div class="h-2 w-[100px] rounded-lg bg-slate-400" />
</div>
<div class="flex items-center space-x-2 rounded-md bg-slate-800 p-2 shadow-sm">
<div class="h-4 w-4 rounded-full bg-slate-400" />
<div class="h-2 w-[100px] rounded-lg bg-slate-400" />
</div>
</div>
</div>
<span class="block w-full p-2 text-center font-normal">
Dark
</span>
</Label>
</div>
<div class="col-span-2">
<span v-if="errors?.theme" class="text-sm text-destructive">
<span v-for="error in errors.theme._errors" :key="error">{{ error }}</span>
</span>
</div>
</RadioGroup>
</div>
<div class="flex justify-start">
<Button type="submit">
Update preferences
</Button>
</div>
</form>
</template>

View File

@ -0,0 +1,98 @@
<script setup lang="ts">
import { ref } from 'vue'
import * as z from 'zod'
import { cn } from '@/lib/utils'
import { Label } from '@/lib/registry/new-york/ui/label'
import { Separator } from '@/lib/registry/new-york/ui/separator'
import { Checkbox } from '@/lib/registry/new-york/ui/checkbox'
import { Button } from '@/lib/registry/new-york/ui/button'
const displayForm = ref({
items: ['recents', 'home'],
})
const items = [
{
id: 'recents',
label: 'Recents',
},
{
id: 'home',
label: 'Home',
},
{
id: 'applications',
label: 'Applications',
},
{
id: 'desktop',
label: 'Desktop',
},
{
id: 'downloads',
label: 'Downloads',
},
{
id: 'documents',
label: 'Documents',
},
] as const
const displayFormSchema = z.object({
items: z.array(z.string()).refine(value => value.some(item => item), {
message: 'You have to select at least one item.',
}),
})
type DisplayFormValues = z.infer<typeof displayFormSchema>
const errors = ref<z.ZodFormattedError<DisplayFormValues> | null>(null)
async function handleSubmit() {
const result = displayFormSchema.safeParse(displayForm.value)
if (!result.success) {
errors.value = result.error.format()
console.log(errors.value)
return
}
console.log('Form submitted!', displayForm.value)
}
</script>
<template>
<div>
<h3 class="text-lg font-medium">
Display
</h3>
<p class="text-sm text-muted-foreground">
Turn items on or off to control what's displayed in the app.
</p>
</div>
<Separator />
<form @submit.prevent="handleSubmit">
<div class="mb-4">
<Label for="sidebar" :class="cn('text-md', errors?.items && 'text-destructive')">
Sidebar
</Label>
<span class="text-xs text-muted-foreground">
Select the items you want to display in the sidebar.
</span>
</div>
<div v-for="item in items" :key="item.id" class="pb-1">
<div class="flex flex-row items-center space-x-3 space-y-0">
<Checkbox :id="item.id" :checked="displayForm.items.includes(item.id)" @change="displayForm.items.includes(item.id) ? displayForm.items.splice(displayForm.items.indexOf(item.id), 1) : displayForm.items.push(item.id)" />
<Label :for="item.id">{{ item.label }}</Label>
</div>
</div>
<div class="flex justify-start mt-4">
<Button type="submit">
Update display
</Button>
</div>
</form>
</template>

View File

@ -0,0 +1,176 @@
<script setup lang="ts">
import { ref } from 'vue'
import * as z from 'zod'
import { cn } from '@/lib/utils'
import { Label } from '@/lib/registry/new-york/ui/label'
import { Separator } from '@/lib/registry/new-york/ui/separator'
import { RadioGroup, RadioGroupItem } from '@/lib/registry/new-york/ui/radio-group'
import { Switch } from '@/lib/registry/new-york/ui/switch'
import { Checkbox } from '@/lib/registry/new-york/ui/checkbox'
import { Button } from '@/lib/registry/new-york/ui/button'
const notificationsForm = ref({
type: '',
mobile: false,
communication_emails: false,
social_emails: true,
marketing_emails: false,
security_emails: true,
})
const notificationsFormSchema = z.object({
type: z.enum(['all', 'mentions', 'none'], {
required_error: 'You need to select a notification type.',
}),
mobile: z.boolean().default(false).optional(),
communication_emails: z.boolean().default(false).optional(),
social_emails: z.boolean().default(false).optional(),
marketing_emails: z.boolean().default(false).optional(),
security_emails: z.boolean(),
})
type notificationsFormValues = z.infer<typeof notificationsFormSchema>
const errors = ref<z.ZodFormattedError<notificationsFormValues> | null>(null)
async function handleSubmit() {
const result = notificationsFormSchema.safeParse(notificationsForm.value)
if (!result.success) {
errors.value = result.error.format()
console.log(errors.value)
return
}
console.log('Form submitted!', notificationsForm.value)
}
</script>
<template>
<div>
<h3 class="text-lg font-medium">
Notifications
</h3>
<p class="text-sm text-muted-foreground">
Configure how you receive notifications.
</p>
</div>
<Separator />
<form class="space-y-8" @submit.prevent="handleSubmit">
<div class="grid gap-2">
<Label for="font" :class="cn('text-sm', errors?.type && 'text-destructive')">
Notify me about...
</Label>
<RadioGroup
v-model="notificationsForm.type"
default-value="all"
class="flex flex-col space-y-1"
>
<div class="flex items-center space-x-3 space-y-0">
<RadioGroupItem id="all" value="all" />
<Label for="all">All new messages</Label>
</div>
<div class="flex items-center space-x-3 space-y-0">
<RadioGroupItem id="mentions" value="mentions" />
<Label for="mentions">Direct messages and mentions</Label>
</div>
<div class="flex items-center space-x-3 space-y-0">
<RadioGroupItem id="none" value="none" />
<Label for="none">Nothing</Label>
</div>
<div v-if="errors?.type" class="text-sm text-destructive">
<span v-for="error in errors.type._errors" :key="error">{{ error }}</span>
</div>
</RadioGroup>
</div>
<div class="grid gap-2">
<h3 class="mb-4 text-lg font-medium">
Email Notifications
</h3>
<div class="space-y-4">
<div class="flex flex-row items-center justify-between rounded-lg border p-4">
<div class="space-y-0.5">
<Label class="text-base" for="communication_emails">
Communication emails
</Label>
<span class="text-xs text-muted-foreground">
Receive emails about your account activity.
</span>
</div>
<Switch
id="communication_emails"
v-model:checked="notificationsForm.communication_emails"
/>
</div>
</div>
<div class="flex flex-row items-center justify-between rounded-lg border p-4">
<div class="space-y-0.5">
<Label class="text-base" for="marketing_emails">
Marketing emails
</Label>
<span class="text-xs text-muted-foreground">
Receive emails about new products, features, and more.
</span>
</div>
<Switch
id="marketing_emails"
v-model:checked="notificationsForm.marketing_emails"
/>
</div>
<div class="flex flex-row items-center justify-between rounded-lg border p-4">
<div class="space-y-0.5">
<Label class="text-base" for="social_emails">
Social emails
</Label>
<span class="text-xs text-muted-foreground">
Receive emails for friend requests, follows, and more.
</span>
</div>
<Switch
id="social_emails"
v-model:checked="notificationsForm.social_emails"
/>
</div>
<div class="flex flex-row items-center justify-between rounded-lg border p-4">
<div class="space-y-0.5">
<Label class="text-base" for="security_emails">
Security emails
</Label>
<span class="text-xs text-muted-foreground">
Receive emails about your account activity and security.
</span>
</div>
<Switch
id="security_emails"
v-model:checked="notificationsForm.security_emails"
disabled
/>
</div>
</div>
<div class="grid gap-2">
<div class="flex flex-row items-start space-x-3 space-y-0">
<Checkbox
id="mobile"
v-model:checked="notificationsForm.mobile"
/>
<div>
<Label for="mobile">
Use different settings for my mobile devices
</Label>
<span class="text-xs text-muted-foreground">
You can manage your mobile notifications in the {{ " " }}
<a href="/examples/forms">mobile settings</a> page.
</span>
</div>
</div>
</div>
<div class="flex justify-start">
<Button type="submit">
Update notifications
</Button>
</div>
</form>
</template>

View File

@ -0,0 +1,152 @@
<script setup lang="ts">
import { ref } from 'vue'
import * as z from 'zod'
import { cn } from '@/lib/utils'
import { Input } from '@/lib/registry/new-york/ui/input'
import { Label } from '@/lib/registry/new-york/ui/label'
import { Separator } from '@/lib/registry/new-york/ui/separator'
import { Textarea } from '@/lib/registry/new-york/ui/textarea'
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/lib/registry/new-york/ui/select'
import { Button } from '@/lib/registry/new-york/ui/button'
const verifiedEmails = ref(['m@example.com', 'm@google.com', 'm@support.com'])
const profileForm = ref({
username: '',
email: '',
bio: 'I own a computer.',
urls: [
{ value: 'https://shadcn.com' },
{ value: 'http://twitter.com/shadcn' },
],
})
const profileFormSchema = z.object({
username: z
.string()
.min(2, {
message: 'Username must be at least 2 characters.',
})
.max(30, {
message: 'Username must not be longer than 30 characters.',
}),
email: z
.string({
required_error: 'Please select an email to display.',
})
.email(),
bio: z.string().max(160, { message: 'Bio must not be longer than 160 characters.' }).min(4, { message: 'Bio must be at least 2 characters.' }),
urls: z
.array(
z.object({
value: z.string().url({ message: 'Please enter a valid URL.' }),
}),
)
.optional(),
})
type ProfileFormValues = z.infer<typeof profileFormSchema>
const errors = ref<z.ZodFormattedError<ProfileFormValues> | null>(null)
async function handleSubmit() {
const result = profileFormSchema.safeParse(profileForm.value)
if (!result.success) {
errors.value = result.error.format()
return
}
errors.value = null
console.log('Form submitted!')
}
</script>
<template>
<div>
<h3 class="text-lg font-medium">
Profile
</h3>
<p class="text-sm text-muted-foreground">
This is how others will see you on the site.
</p>
</div>
<Separator />
<form class="space-y-8" @submit.prevent="handleSubmit">
<div class="grid gap-2">
<Label for="username" :class="cn('text-sm', errors?.username && 'text-destructive')">
Username
</Label>
<Input id="username" v-model="profileForm.username" placeholder="shadcn" />
<span class="text-muted-foreground text-sm">
This is your public display name. It can be your real name or a pseudonym. You can only change this once every 30 days.
</span>
<div v-if="errors?.username" class="text-sm text-destructive">
<span v-for="error in errors.username._errors" :key="error">{{ error }}</span>
</div>
</div>
<div class="grid gap-2">
<Label for="email" :class="cn('text-sm', errors?.email && 'text-destructive')">
Email
</Label>
<Select id="email" v-model="profileForm.email">
<SelectTrigger>
<SelectValue placeholder="Select an email" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem v-for="email in verifiedEmails" :key="email" :value="email">
{{ email }}
</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<span class="text-muted-foreground text-sm">
You can manage verified email addresses in your email settings.
</span>
<div v-if="errors?.email" class="text-sm text-destructive">
<span v-for="error in errors.email._errors" :key="error">{{ error }}</span>
</div>
</div>
<div class="grid gap-2">
<Label for="bio" :class="cn('text-sm', errors?.bio && 'text-destructive')">
Bio
</Label>
<Textarea id="bio" v-model="profileForm.bio" placeholder="Tell us about yourself." />
<span class="text-muted-foreground text-sm">
You can @mention other users and organizations to link to them.
</span>
<div v-if="errors?.bio" class="text-sm text-destructive">
<span v-for="error in errors.bio._errors" :key="error">{{ error }}</span>
</div>
</div>
<div class="grid gap-2">
<Label for="urls" :class="cn('text-sm', errors?.urls && 'text-destructive')">
URLs
</Label>
<Input v-for="(url, index) in profileForm.urls" id="urls" :key="index" v-model="url.value" />
<div v-if="errors?.urls" class="text-sm text-destructive">
<span v-for="error in errors.urls._errors" :key="error">{{ error }}</span>
</div>
<Button
type="button"
variant="outline"
size="sm"
class="text-xs w-20 mt-2"
@click="profileForm.urls?.push({ value: '' })"
>
Add URL
</Button>
</div>
<div class="flex justify-start">
<Button type="submit">
Update profile
</Button>
</div>
</form>
</template>

View File

@ -0,0 +1,53 @@
<script setup lang="ts">
import { useRoute } from 'vitepress'
import { cn } from '@/lib/utils'
import { Button } from '@/lib/registry/new-york/ui/button'
interface Item {
title: string
href: string
}
const $route = useRoute()
const sidebarNavItems: Item[] = [
{
title: 'Profile',
href: '/examples/forms/forms',
},
{
title: 'Account',
href: '/examples/forms/account',
},
{
title: 'Appearance',
href: '/examples/forms/appearance',
},
{
title: 'Notifications',
href: '/examples/forms/notifications',
},
{
title: 'Display',
href: '/examples/forms/display',
},
]
</script>
<template>
<nav class="flex space-x-2 lg:flex-col lg:space-x-0 lg:space-y-1">
<Button
v-for="item in sidebarNavItems"
:key="item.title"
as="a"
:href="item.href"
variant="ghost"
:class="cn(
'w-full text-left justify-start',
$route.path === `${item.href}.html` && 'bg-muted hover:bg-muted',
)"
>
{{ item.title }}
</Button>
</nav>
</template>

View File

@ -0,0 +1,38 @@
<script setup lang="ts">
import SidebarNav from '../components/SidebarNav.vue'
import { Separator } from '@/lib/registry/new-york/ui/separator'
</script>
<template>
<div class="md:hidden">
<VPImage
alt="Forms"
width="1280"
height="1214" class="block" :image="{
dark: '/examples/forms-dark.png',
light: '/examples/forms-light.png',
}"
/>
</div>
<div class="hidden space-y-6 p-10 pb-16 md:block">
<div class="space-y-0.5">
<h2 class="text-2xl font-bold tracking-tight">
Settings
</h2>
<p class="text-muted-foreground">
Manage your account settings and set e-mail preferences.
</p>
</div>
<Separator class="my-6" />
<div class="flex flex-col space-y-8 lg:flex-row lg:space-x-12 lg:space-y-0">
<aside class="-mx-4 lg:w-1/5">
<SidebarNav />
</aside>
<div class="flex-1 lg:max-w-2xl">
<div class="space-y-6">
<slot />
</div>
</div>
</div>
</div>
</template>

View File

@ -1,12 +1,14 @@
<script setup lang="ts">
import type { Column } from '@tanstack/vue-table'
import type { Component } from 'vue'
import { computed } from 'vue'
import { computed, ref } from 'vue'
import { type Task } from '../data/schema'
import PlusCircledIcon from '~icons/radix-icons/plus-circled'
import CheckIcon from '~icons/radix-icons/check'
import { Badge } from '@/lib/registry/new-york/ui/badge'
import { Button } from '@/lib/registry/new-york/ui/button'
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator } from '@/lib/registry/new-york/ui/command'
import {
Popover,
@ -14,6 +16,7 @@ import {
PopoverTrigger,
} from '@/lib/registry/new-york/ui/popover'
import { Separator } from '@/lib/registry/new-york/ui/separator'
import { cn } from '@/lib/utils'
interface DataTableFacetedFilter {
column?: Column<Task, any>
@ -70,66 +73,63 @@ const selectedValues = computed(() => new Set(props.column?.getFilterValue() as
</Button>
</PopoverTrigger>
<PopoverContent class="w-[200px] p-0" align="start">
<!-- <Command>
<CommandInput placeholder={title} />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup>
{options.map((option) => {
<Command
:filter-function="(list: DataTableFacetedFilter['options'], term) => list.filter(i => i.label.toLowerCase()?.includes(term)) "
>
<CommandInput :placeholder="title" />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup>
<CommandItem
v-for="option in options"
:key="option.value"
:value="option"
@select="() => {
const isSelected = selectedValues.has(option.value)
return (
<CommandItem
key={option.value}
onSelect={() => {
if (isSelected) {
selectedValues.delete(option.value)
} else {
selectedValues.add(option.value)
}
const filterValues = Array.from(selectedValues)
column?.setFilterValue(
filterValues.length ? filterValues : undefined
)
}}
>
<div
class={cn(
"mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary",
isSelected
? "bg-primary text-primary-foreground"
: "opacity-50 [&_svg]:invisible"
)}
>
<CheckIcon class={cn("h-4 w-4")} />
</div>
{option.icon && (
<option.icon class="mr-2 h-4 w-4 text-muted-foreground" />
)}
<span>{option.label}</span>
{facets?.get(option.value) && (
<span class="ml-auto flex h-4 w-4 items-center justify-center font-mono text-xs">
{facets.get(option.value)}
</span>
)}
</CommandItem>
if (isSelected) {
selectedValues.delete(option.value)
}
else {
selectedValues.add(option.value)
}
const filterValues = Array.from(selectedValues)
column?.setFilterValue(
filterValues.length ? filterValues : undefined,
)
})}
}"
>
<div
:class="cn(
'mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary',
selectedValues.has(option.value)
? 'bg-primary text-primary-foreground'
: 'opacity-50 [&_svg]:invisible',
)"
>
<CheckIcon :class="cn('h-4 w-4')" />
</div>
<option.icon v-if="option.icon" class="mr-2 h-4 w-4 text-muted-foreground" />
<span>{{ option.label }}</span>
<span v-if="facets?.get(option.value)" class="ml-auto flex h-4 w-4 items-center justify-center font-mono text-xs">
{{ facets.get(option.value) }}
</span>
</CommandItem>
</CommandGroup>
<template v-if="selectedValues.size > 0">
<CommandSeparator />
<CommandGroup>
<CommandItem
:value="{ label: 'Clear filters' }"
class="justify-center text-center"
@select="column?.setFilterValue(undefined)"
>
Clear filters
</CommandItem>
</CommandGroup>
{selectedValues.size > 0 && (
<>
<CommandSeparator />
<CommandGroup>
<CommandItem
onSelect={() => column?.setFilterValue(undefined)}
class="justify-center text-center"
>
Clear filters
</CommandItem>
</CommandGroup>
</>
)}
</CommandList>
</Command> -->
</template>
</CommandList>
</Command>
</PopoverContent>
</Popover>
</template>

View File

@ -0,0 +1,100 @@
<script setup lang="ts">
import { ref } from 'vue'
import { Button } from '@/lib/registry/default/ui/button'
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '@/lib/registry/default/ui/card'
import { themes } from '@/lib/registry/themes'
import { useConfigStore } from '@/stores/config'
const { theme, radius, setRadius, setTheme } = useConfigStore()
const goal = ref(350)
const data = [
{ goal: 400 },
{ goal: 300 },
{ goal: 200 },
{ goal: 300 },
{ goal: 200 },
{ goal: 278 },
{ goal: 189 },
{ goal: 239 },
{ goal: 300 },
{ goal: 200 },
{ goal: 278 },
{ goal: 189 },
{ goal: 349 },
]
</script>
<template>
<Card>
<CardHeader class-name="pb-4">
<CardTitle class-name="text-base">
Move Goal
</CardTitle>
<CardDescription>Set your daily activity goal.</CardDescription>
</CardHeader>
<CardContent class-name="pb-2">
<div className="flex items-center justify-center space-x-2">
<Button
variant="outline"
size="icon"
class-name="h-8 w-8 shrink-0 rounded-full"
:disabled="goal <= 200"
@click="goal -= 10"
>
<Minus class-name="h-4 w-4" />
<span className="sr-only">Decrease</span>
</Button>
<div className="flex-1 text-center">
<div className="text-5xl font-bold tracking-tighter">
{{ goal }}
</div>
<div className="text-[0.70rem] uppercase text-muted-foreground">
Calories/day
</div>
</div>
<Button
variant="outline"
size="icon"
class-name="h-8 w-8 shrink-0 rounded-full"
:disabled="goal >= 400"
@click="goal += 10 "
>
<Plus class-name="h-4 w-4" />
<span className="sr-only">Increase</span>
</Button>
</div>
<div className="my-3 h-[60px]">
<!-- <ResponsiveContainer width="100%" height="100%">
<BarChart data="{data}">
<Bar
data-key="goal"
style="{"
{
fill: "var(--theme-primary)",
opacity: 0.2,
"--theme-primary": `hsl(${
theme?.cssVars[mode="==" "dark" ? "dark" : "light"].primary
})`,
} as React.CSSProperties
}
/>
</BarChart>
</ResponsiveContainer> -->
</div>
</CardContent>
<CardFooter>
<Button class-name="w-full">
Set Goal
</Button>
</CardFooter>
</Card>
</template>

View File

@ -0,0 +1,125 @@
<script setup lang="ts">
import { ref } from 'vue'
import { ChevronDown, Minus, Plus, Send } from 'lucide-vue-next'
import { addDays, startOfToday } from 'date-fns'
import {
months,
payments,
roles,
teamMembers,
years,
} from './utils/data'
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '@/lib/registry/default/ui/card'
import {
Avatar,
AvatarFallback,
AvatarImage,
} from '@/lib/registry/default/ui/avatar'
import { Button } from '@/lib/registry/default/ui/button'
import { Textarea } from '@/lib/registry/default/ui/textarea'
import { Calendar } from '@/lib/registry/default/ui/calendar'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/lib/registry/default/ui/dropdown-menu'
import { Label } from '@/lib/registry/default/ui/label'
import { Switch } from '@/lib/registry/default/ui/switch'
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/lib/registry/default/ui/select'
import { Input } from '@/lib/registry/default/ui/input'
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from '@/lib/registry/default/ui/tooltip'
import { Separator } from '@/lib/registry/default/ui/separator'
import RadixIconsGithubLogo from '~icons/radix-icons/github-logo'
import RiGoogleLine from '~icons/ri/google-line'
const strictlyNecessarySwitch = ref<boolean>(true)
const functionalCookiesSwitch = ref<boolean>(false)
const performanceCookiesSwitch = ref<boolean>(false)
const selectedArea = ref('Billing')
const selectedSecurity = ref('Medium')
const selectedMonth = ref<string>(months[0])
const selectedYear = ref<string>(years[0])
const selectedPayment = ref(payments[0])
const goal = ref(350)
function switchPayment(payment: any) {
selectedPayment.value = payment
}
const range = ref({
start: startOfToday(),
end: addDays(startOfToday(), 8),
})
</script>
<template>
<div className="md:grids-col-2 grid md:gap-4 lg:grid-cols-10 xl:grid-cols-11 xl:gap-4">
<div className="space-y-4 lg:col-span-4 xl:col-span-6 xl:space-y-4">
<CardsStats />
<div className="grid gap-1 sm:grid-cols-[280px_1fr] md:hidden">
<CardsCalendar />
<div className="pt-3 sm:pl-2 sm:pt-0 xl:pl-4">
<CardsActivityGoal />
</div>
<div className="pt-3 sm:col-span-2 xl:pt-4">
<CardsMetric />
</div>
</div>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-1 xl:grid-cols-2">
<div className="space-y-4 xl:space-y-4">
<CardsTeamMembers />
<CardsCookieSettings />
<CardsPaymentMethod />
</div>
<div className="space-y-4 xl:space-y-4">
<CardsChat />
<CardsCreateAccount />
<div className="hidden xl:block">
<CardsReportIssue />
</div>
</div>
</div>
</div>
<div className="space-y-4 lg:col-span-6 xl:col-span-5 xl:space-y-4">
<div className="hidden gap-1 sm:grid-cols-[280px_1fr] md:grid">
<CardsCalendar />
<div className="pt-3 sm:pl-2 sm:pt-0 xl:pl-3">
<CardsActivityGoal />
</div>
<div className="pt-3 sm:col-span-2 xl:pt-3">
<CardsMetric />
</div>
</div>
<div className="hidden md:block">
<CardsDataTable />
</div>
<CardsShare />
<div className="xl:hidden">
<CardsReportIssue />
</div>
</div>
</div>
</template>

View File

@ -0,0 +1,20 @@
<script setup lang="ts">
import { Checkbox } from '@/lib/registry/default/ui/checkbox'
</script>
<template>
<div className="items-top flex space-x-2">
<Checkbox id="terms1" disabled />
<div className="grid gap-1.5 leading-none">
<label
htmlFor="terms1"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Accept terms and conditions
</label>
<p className="text-sm text-muted-foreground">
You agree to our Terms of Service and Privacy Policy.
</p>
</div>
</div>
</template>

View File

@ -0,0 +1,70 @@
<script setup lang="ts">
import { Check, ChevronsUpDown } from 'lucide-vue-next'
import { ref } from 'vue'
import { cn } from '@/lib/utils'
import { Button } from '@/lib/registry/default/ui/button'
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
} from '@/lib/registry/default/ui/command'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/lib/registry/default/ui/popover'
const frameworks = [
{ value: 'next.js', label: 'Next.js' },
{ value: 'sveltekit', label: 'SvelteKit' },
{ value: 'nuxt.js', label: 'Nuxt.js' },
{ value: 'remix', label: 'Remix' },
{ value: 'astro', label: 'Astro' },
]
const open = ref(false)
const value = ref<typeof frameworks[number]>()
const filterFunction = (list: typeof frameworks, search: string) => list.filter(i => i.value.toLowerCase().includes(search.toLowerCase()))
</script>
<template>
<Popover v-model:open="open">
<PopoverTrigger as-child>
<Button
variant="outline"
role="combobox"
:aria-expanded="open"
class="w-[200px] justify-between"
>
{{ value ? value.label : 'Select framework...' }}
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent class="w-[200px] p-0">
<Command v-model="value" :filter-function="filterFunction">
<CommandInput placeholder="Search framework..." />
<CommandEmpty>No framework found.</CommandEmpty>
<CommandGroup>
<CommandItem
v-for="framework in frameworks"
:key="framework.value"
:value="framework"
@select="open = false"
>
<Check
:class="cn(
'mr-2 h-4 w-4',
value?.value === framework.value ? 'opacity-100' : 'opacity-0',
)"
/>
{{ framework.label }}
</CommandItem>
</CommandGroup>
</Command>
</PopoverContent>
</Popover>
</template>

View File

@ -0,0 +1,62 @@
<script setup lang="ts">
import {
Calculator,
Calendar,
CreditCard,
Settings,
Smile,
User,
} from 'lucide-vue-next'
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
CommandShortcut,
} from '@/lib/registry/default/ui/command'
</script>
<template>
<Command class="rounded-lg border shadow-md max-w-[450px]">
<CommandInput placeholder="Type a command or search..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup heading="Suggestions">
<CommandItem value="Calendar">
<Calendar class="mr-2 h-4 w-4" />
<span>Calendar</span>
</CommandItem>
<CommandItem value="Search Emoji">
<Smile class="mr-2 h-4 w-4" />
<span>Search Emoji</span>
</CommandItem>
<CommandItem value="Calculator">
<Calculator class="mr-2 h-4 w-4" />
<span>Calculator</span>
</CommandItem>
</CommandGroup>
<CommandSeparator />
<CommandGroup heading="Settings">
<CommandItem value="Profile">
<User class="mr-2 h-4 w-4" />
<span>Profile</span>
<CommandShortcut>P</CommandShortcut>
</CommandItem>
<CommandItem value="Billing">
<CreditCard class="mr-2 h-4 w-4" />
<span>Billing</span>
<CommandShortcut>B</CommandShortcut>
</CommandItem>
<CommandItem value="Settings">
<Settings class="mr-2 h-4 w-4" />
<span>Settings</span>
<CommandShortcut>S</CommandShortcut>
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</template>

View File

@ -103,7 +103,7 @@ const columns: ColumnDef<Payment>[] = [
return h(Button, {
variant: 'ghost',
onClick: () => column.toggleSorting(column.getIsSorted() === 'asc'),
}, ['Email', h(ArrowUpDown, { class: 'ml-2 h-4 w-4' })])
}, () => ['Email', h(ArrowUpDown, { class: 'ml-2 h-4 w-4' })])
},
cell: ({ row }) => h('div', { class: 'lowercase' }, row.getValue('email')),
},
@ -128,9 +128,9 @@ const columns: ColumnDef<Payment>[] = [
cell: ({ row }) => {
const payment = row.original
return h(DropdownAction, {
return h('div', { class: 'relative' }, h(DropdownAction, {
payment,
})
}))
},
},
]
@ -162,7 +162,7 @@ const table = useVueTable({
<template>
<div class="w-full">
<div class="flex items-center py-4">
<div class="flex gap-2 items-center py-4">
<Input
class="max-w-sm"
placeholder="Filter emails..."
@ -244,7 +244,6 @@ const table = useVueTable({
:disabled="!table.getCanNextPage()"
@click="table.nextPage()"
>
>
Next
</Button>
</div>

View File

@ -1,8 +1,9 @@
<script setup lang="ts">
import { ref } from 'vue'
import { cn } from '@/lib/utils'
import { Slider } from '@/lib/registry/default/ui/slider'
const modelValue = [50]
const modelValue = ref([50])
</script>
<template>

View File

@ -0,0 +1,21 @@
<script setup lang="ts">
import type { ComboboxRootEmits, ComboboxRootProps } from 'radix-vue'
import { ComboboxRoot } from 'radix-vue'
import { cn, useEmitAsProps } from '@/lib/utils'
const props = defineProps<ComboboxRootProps>()
const emits = defineEmits<ComboboxRootEmits>()
const emitsAsProps = useEmitAsProps(emits)
</script>
<template>
<ComboboxRoot
v-bind="{ ...props, ...emitsAsProps }"
:open="true"
:model-value="''"
:class="cn('flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground', $attrs.class ?? '')"
>
<slot />
</ComboboxRoot>
</template>

View File

@ -0,0 +1,21 @@
<script setup lang="ts">
import type { DialogRootEmits, DialogRootProps } from 'radix-vue'
import Command from './Command.vue'
import { Dialog, DialogContent } from '@/lib/registry/default/ui/dialog'
import { useEmitAsProps } from '@/lib/utils'
const props = defineProps<DialogRootProps>()
const emits = defineEmits<DialogRootEmits>()
const emitsAsProps = useEmitAsProps(emits)
</script>
<template>
<Dialog v-bind="{ ...props, ...emitsAsProps }">
<DialogContent class="overflow-hidden p-0 shadow-lg">
<Command class="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
<slot />
</Command>
</DialogContent>
</Dialog>
</template>

Some files were not shown because too many files have changed in this diff Show More