Merge remote-tracking branch 'origin/dev' into charting
This commit is contained in:
commit
595bbe04ea
77
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
77
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
|
|
@ -6,62 +6,27 @@ body:
|
|||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**Before You Start...**
|
||||
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
This form is only for submitting bug reports. If you have a usage question
|
||||
or are unsure if this is really a bug, make sure to:
|
||||
|
||||
- Read the [docs](https://radix-vue.com/)
|
||||
- Ask on [Discord Chat](https://chat.radix-vue.com/)
|
||||
- Ask on [GitHub Discussions](https://github.com/shadcn-vue/shadcn-vue/discussions)
|
||||
|
||||
Also try to search for your issue - it may have already been answered or even fixed.
|
||||
However, if you find that an old, closed issue still persists in the latest version,
|
||||
you should open a new issue using the form below instead of commenting on the old issue.
|
||||
- type: textarea
|
||||
id: bug-env
|
||||
attributes:
|
||||
label: Environment
|
||||
description: Please provide the following information about your environment.
|
||||
value: |
|
||||
Developement/Production OS: Windows 10 19043.1110
|
||||
Node version: 16.0.0
|
||||
Package manager: pnpm@8.6.0
|
||||
Radix Vue version: 1.0.0
|
||||
Shadcn Vue version: 1.0.0
|
||||
Vue version: 3.0.0
|
||||
Nuxt version: 3.0.0
|
||||
Nuxt mode: universal
|
||||
Nuxt target: server
|
||||
CSS framework: tailwindcss@3.3.3
|
||||
Client OS: Windows 10 19043.1110
|
||||
Browser: Chrome 90.0.4430.212
|
||||
render: bash
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: reproduction-link
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Link to minimal reproduction
|
||||
label: Reproduction
|
||||
description: |
|
||||
Please provide a link to a minimal reproduction of the bug.
|
||||
A minimal reproduction is a CodeSandbox, CodePen, or a StackBlitz that contains the bare minimum amount of code needed to show the bug.
|
||||
A minimal reproduction is required unless you are absolutely sure that the issue is obvious and the provided information is enough to understand the problem
|
||||
A [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) is **required**, otherwise the issue might be closed without further notice. [**Why?**](https://antfu.me/posts/why-reproductions-are-required)
|
||||
|
||||
This is **required** for us to be able to triage your issue in a timely manner.
|
||||
To get started, you can use the StackBlitz and CodeSandbox playgrounds in shadcn-vue demos:
|
||||
https://www.shadcn-vue.com/docs/components/accordion.html
|
||||
|
||||
Please do not just fill in a random link. The issue will be closed if no valid reproduction is provided.
|
||||
placeholder: Reproduction Link
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: steps-to-reproduce
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: |
|
||||
How do you trigger this bug? Please walk us through it step by step.
|
||||
Note that you can use [Markdown](https://guides.github.com/features/mastering-markdown/) to format lists and code.
|
||||
placeholder: Steps to reproduce
|
||||
or use template presets
|
||||
https://vite.new
|
||||
https://nuxt.new
|
||||
placeholder: Reproduction
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
|
@ -73,14 +38,18 @@ body:
|
|||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
id: system-info
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: A clear and concise description of what you expected to happen.
|
||||
- type: textarea
|
||||
id: screenshots
|
||||
label: System Info
|
||||
description: Output of `npx envinfo --system --npmPackages vue,@vueuse/core,radix-vue,nuxt,shadcn-vue,shadcn-nuxt,unplugin-auto-import --binaries --browsers`
|
||||
render: bash
|
||||
placeholder: System, Binaries, Browsers
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: contributes
|
||||
attributes:
|
||||
label: Conext & Screenshots (if applicable)
|
||||
description: |
|
||||
If applicable, provide any additional context or screenshots of the bug.
|
||||
You can drag and drop images here to add them to the issue.
|
||||
label: Contributes
|
||||
options:
|
||||
- label: I am willing to submit a PR to fix this issue
|
||||
- label: I am willing to submit a PR with failing tests
|
||||
|
|
|
|||
51
.github/workflows/publish.yaml
vendored
51
.github/workflows/publish.yaml
vendored
|
|
@ -11,14 +11,42 @@ on:
|
|||
- dev
|
||||
paths:
|
||||
- 'apps/www/**'
|
||||
pull_request_target:
|
||||
types:
|
||||
# When a created pull request from forked repo, it will be comment 'Should deploy to add label'
|
||||
- opened
|
||||
# When a labeled '🚀request-deploy' pull request from forked repo, it will be deploy to Cloudflare Pages
|
||||
- labeled
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
# eslint-disable-next-line yml/no-empty-mapping-value
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
# default contents: read & write (in forked repos, only read)
|
||||
contents: write
|
||||
# default deployments: read & write (in forked repos, only read)
|
||||
deployments: write
|
||||
# default pull-requests: read & write (in forked repos, only read)
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
deployments: write
|
||||
name: Publish to Cloudflare Pages
|
||||
# push event in main branch
|
||||
# workflow_dispatch event
|
||||
# pull_request event from not forked repo
|
||||
# pull_request_target event with label "🚀request-deploy" from forked repo
|
||||
if: ${{
|
||||
github.event_name == 'push' ||
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false) ||
|
||||
(github.event_name == 'pull_request_target' &&
|
||||
github.event.action == 'labeled' &&
|
||||
github.event.pull_request.head.repo.fork == true &&
|
||||
contains(github.event.label.name, '🚀request-deploy'))
|
||||
}}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
|
@ -56,7 +84,7 @@ jobs:
|
|||
|
||||
# Run a action to publish docs
|
||||
- name: Publish to Cloudflare Pages
|
||||
uses: cloudflare/pages-action@v1.5.0
|
||||
uses: zernonia/cloudflare-pages-action@v0.0.7
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
|
|
@ -66,7 +94,20 @@ jobs:
|
|||
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
|
||||
branch: ${{ github.ref == 'refs/heads/dev' && 'dev' || format('refs/pull/{0}/merge', github.event.number) }}
|
||||
# Optional: Change the working directory
|
||||
workingDirectory: apps/www
|
||||
wranglerVersion: '3'
|
||||
|
||||
- name: Remove label
|
||||
if: ${{ github.event_name == 'pull_request_target' && contains(github.event.label.name, '🚀request-deploy') }}
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
github.rest.issues.removeLabel({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name: ['🚀request-deploy']
|
||||
})
|
||||
|
|
|
|||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
|
|
@ -2,8 +2,8 @@
|
|||
"prettier.enable": false,
|
||||
"editor.formatOnSave": false,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true,
|
||||
"source.organizeImports": false
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.organizeImports": "never"
|
||||
},
|
||||
|
||||
"eslint.validate": [
|
||||
|
|
|
|||
|
|
@ -3,9 +3,17 @@ import { defineConfig } from 'vitepress'
|
|||
import Icons from 'unplugin-icons/vite'
|
||||
import tailwind from 'tailwindcss'
|
||||
import autoprefixer from 'autoprefixer'
|
||||
import { createCssVariablesTheme } from 'shiki'
|
||||
|
||||
// import { transformerMetaWordHighlight, transformerNotationWordHighlight } from '@shikijs/transformers'
|
||||
import { siteConfig } from './theme/config/site'
|
||||
import ComponentPreviewPlugin from './theme/plugins/previewer'
|
||||
|
||||
const cssVariables = createCssVariablesTheme({
|
||||
variablePrefix: '--shiki-',
|
||||
variableDefaults: {},
|
||||
})
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
title: siteConfig.name,
|
||||
|
|
@ -50,7 +58,11 @@ export default defineConfig({
|
|||
|
||||
srcDir: path.resolve(__dirname, '../src'),
|
||||
markdown: {
|
||||
theme: 'css-variables',
|
||||
theme: cssVariables,
|
||||
codeTransformers: [
|
||||
// transformerMetaWordHighlight(),
|
||||
// transformerNotationWordHighlight(),
|
||||
],
|
||||
config(md) {
|
||||
md.use(ComponentPreviewPlugin)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ const { style } = useConfigStore()
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
:class="cn('preview flex min-h-[350px] w-full justify-center p-6 lg:p-10', {
|
||||
:class="cn('preview flex min-h-[350px] w-full justify-center p-10 items-center', {
|
||||
'items-center': align === 'center',
|
||||
'items-start': align === 'start',
|
||||
'items-end': align === 'end',
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ const { copy, copied } = useClipboard()
|
|||
|
||||
const codeRef = ref<HTMLElement>()
|
||||
async function copyCode() {
|
||||
await copy(codeRef.value?.innerText ?? '')
|
||||
await copy(codeRef.value?.innerText.replace(/\u00A0/g, ' ') ?? '')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ const kbdClass = computed(() => {
|
|||
{
|
||||
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',
|
||||
xs: 'min-h-4 text-[10px] h-4 px-1',
|
||||
sm: 'min-h-5 text-[11px] h-5 px-1',
|
||||
md: 'min-h-6 text-[12px] h-6 px-1.5',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import PageHeader from '../components/PageHeader.vue'
|
||||
import PageHeaderHeading from '../components/PageHeaderHeading.vue'
|
||||
import PageHeaderDescription from '../components/PageHeaderDescription.vue'
|
||||
import PageAction from '../components/PageAction.vue'
|
||||
import ExamplesNav from '../components/ExamplesNav.vue'
|
||||
import { announcementConfig } from '../config/site'
|
||||
import GitHubIcon from '~icons/radix-icons/github-logo'
|
||||
|
|
@ -31,7 +32,7 @@ import DashboardExample from '@/examples/dashboard/Example.vue'
|
|||
apps. Accessible. Customizable. Open Source.
|
||||
</PageHeaderDescription>
|
||||
|
||||
<section class="flex w-full items-center space-x-4 pb-8 pt-4 md:pb-10">
|
||||
<PageAction>
|
||||
<a
|
||||
href="/docs/introduction"
|
||||
:class="cn(buttonVariants(), 'rounded-[6px]')"
|
||||
|
|
@ -49,7 +50,7 @@ import DashboardExample from '@/examples/dashboard/Example.vue'
|
|||
<GitHubIcon class="mr-2 h-4 w-4" />
|
||||
GitHub
|
||||
</a>
|
||||
</section>
|
||||
</PageAction>
|
||||
</PageHeader>
|
||||
<ExamplesNav />
|
||||
<section class="space-y-8 overflow-hidden rounded-lg border-2 border-primary dark:border-muted md:hidden">
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ 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 ?? '')">
|
||||
<a :class="cn('flex w-full flex-col items-center rounded-lg border bg-card p-6 text-card-foreground shadow transition-colors hover:bg-muted/50 sm:p-10', $attrs.class ?? '')">
|
||||
<slot />
|
||||
</a>
|
||||
</template>
|
||||
|
|
|
|||
14
apps/www/.vitepress/theme/components/PageAction.vue
Normal file
14
apps/www/.vitepress/theme/components/PageAction.vue
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<script setup lang="ts">
|
||||
import { cn } from '@/lib/utils'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section
|
||||
:class="cn(
|
||||
'flex w-full items-center justify-center space-x-4 py-4 md:pb-10',
|
||||
$attrs.class ?? '',
|
||||
)"
|
||||
>
|
||||
<slot />
|
||||
</section>
|
||||
</template>
|
||||
|
|
@ -7,7 +7,7 @@ import { cn } from '@/lib/utils'
|
|||
<template>
|
||||
<section
|
||||
:class="cn(
|
||||
'flex max-w-[980px] flex-col items-start gap-2 px-4 pt-8 md:pt-12',
|
||||
'mx-auto flex max-w-[980px] flex-col items-center gap-2 py-8 md:py-12 md:pb-8 lg:py-24 lg:pb-20',
|
||||
$attrs.class ?? '',
|
||||
)"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { cn } from '@/lib/utils'
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<WrapBalancer :class="cn('max-w-[750px] text-lg text-muted-foreground sm:text-xl', $attrs.class ?? '')" :prefer-native="false">
|
||||
<WrapBalancer :class="cn('max-w-[750px] text-center text-lg text-muted-foreground sm:text-xl', $attrs.class ?? '')" :prefer-native="false">
|
||||
<slot />
|
||||
</WrapBalancer>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { cn } from '@/lib/utils'
|
|||
<template>
|
||||
<h1
|
||||
:class="cn(
|
||||
'text-3xl font-bold leading-tight tracking-tighter md:text-5xl lg:leading-[1.1]',
|
||||
'text-center text-3xl font-bold leading-tight tracking-tighter md:text-6xl lg:leading-[1.1]',
|
||||
$attrs.class ?? '',
|
||||
)"
|
||||
>
|
||||
|
|
|
|||
16
apps/www/.vitepress/theme/components/TabMarkdown.vue
Normal file
16
apps/www/.vitepress/theme/components/TabMarkdown.vue
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<script setup lang="ts">
|
||||
import { useSlots } from 'vue'
|
||||
import { TabsContent, TabsTrigger } from '@/lib/registry/default/ui/tabs'
|
||||
|
||||
withDefaults(defineProps<{
|
||||
title?: string
|
||||
}>(), {
|
||||
title: '',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TabsContent :value="title" class="relative space-y-10">
|
||||
<slot />
|
||||
</TabsContent>
|
||||
</template>
|
||||
22
apps/www/.vitepress/theme/components/TabsMarkdown.vue
Normal file
22
apps/www/.vitepress/theme/components/TabsMarkdown.vue
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, useSlots } from 'vue'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/lib/registry/default/ui/tabs'
|
||||
|
||||
const slots = useSlots()
|
||||
|
||||
const tabs = computed(() => slots.default?.()?.map(i => i?.props?.title as string) ?? [])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Tabs :default-value="tabs[0]" class="relative mr-auto w-full">
|
||||
<div class="flex items-center justify-between">
|
||||
<TabsList class="w-full justify-start rounded-none border-b bg-transparent p-0">
|
||||
<TabsTrigger v-for="tab in tabs" :key="tab" :value="tab" 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">
|
||||
{{ tab }}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
<slot />
|
||||
</Tabs>
|
||||
</template>
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
export { default as ComponentPreview } from './ComponentPreview.vue'
|
||||
export { default as TabPreview } from './TabPreview.vue'
|
||||
export { default as TabMarkdown } from './TabMarkdown.vue'
|
||||
export { default as TabsMarkdown } from './TabsMarkdown.vue'
|
||||
export { default as Callout } from './Callout.vue'
|
||||
export { default as LinkedCard } from './LinkedCard.vue'
|
||||
export { default as ManualInstall } from './ManualInstall.vue'
|
||||
|
|
|
|||
|
|
@ -168,6 +168,12 @@ export const docsConfig: DocsConfig = {
|
|||
title: 'Card',
|
||||
href: '/docs/components/card',
|
||||
},
|
||||
{
|
||||
title: 'Carousel',
|
||||
href: '/docs/components/carousel',
|
||||
label: 'New',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Checkbox',
|
||||
href: '/docs/components/checkbox',
|
||||
|
|
@ -232,6 +238,12 @@ export const docsConfig: DocsConfig = {
|
|||
title: 'Pagination',
|
||||
href: '/docs/components/pagination',
|
||||
},
|
||||
{
|
||||
title: 'Pin Input',
|
||||
href: '/docs/components/pin-input',
|
||||
label: 'New',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Popover',
|
||||
href: '/docs/components/popover',
|
||||
|
|
@ -268,6 +280,12 @@ export const docsConfig: DocsConfig = {
|
|||
title: 'Slider',
|
||||
href: '/docs/components/slider',
|
||||
},
|
||||
{
|
||||
title: 'Sonner',
|
||||
href: '/docs/components/sonner',
|
||||
label: 'New',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Switch',
|
||||
href: '/docs/components/switch',
|
||||
|
|
@ -292,6 +310,10 @@ export const docsConfig: DocsConfig = {
|
|||
title: 'Toggle',
|
||||
href: '/docs/components/toggle',
|
||||
},
|
||||
{
|
||||
title: 'Toggle Group',
|
||||
href: '/docs/components/toggle-group',
|
||||
},
|
||||
{
|
||||
title: 'Tooltip',
|
||||
href: '/docs/components/tooltip',
|
||||
|
|
|
|||
|
|
@ -15,6 +15,6 @@ export const siteConfig = {
|
|||
|
||||
export const announcementConfig = {
|
||||
icon: '✨',
|
||||
title: 'VSCode extension',
|
||||
link: '/docs/installation.html#vscode-extension',
|
||||
title: 'New components!',
|
||||
link: '/docs/components/carousel.html',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import EditLink from '../components/EditLink.vue'
|
|||
import { ScrollArea } from '@/lib/registry/default/ui/scroll-area'
|
||||
import { Badge } from '@/lib/registry/default/ui/badge'
|
||||
import RadixIconsCode from '~icons/radix-icons/code'
|
||||
import RadixIconsExternalLink from '~icons/radix-icons/external-link'
|
||||
import ChevronRightIcon from '~icons/lucide/chevron-right'
|
||||
|
||||
const $route = useRoute()
|
||||
|
|
@ -20,7 +21,7 @@ const sourceLink = 'https://github.com/radix-vue/shadcn-vue/tree/dev/'
|
|||
<aside
|
||||
class="fixed top-14 z-30 -ml-2 hidden h-[calc(100vh-3.5rem)] w-full shrink-0 md:sticky md:block overflow-y-auto"
|
||||
>
|
||||
<ScrollArea orientation="vertical" class="h-full py-6 pl-8 pr-6 lg:py-8" :type="'auto'">
|
||||
<ScrollArea orientation="vertical" class="relative overflow-hidden h-full py-6 pr-6 lg:py-8" :type="'auto'">
|
||||
<div class="w-full">
|
||||
<div v-for="docsGroup in docsConfig.sidebarNav" :key="docsGroup.title" class="pb-4">
|
||||
<h4
|
||||
|
|
@ -49,9 +50,9 @@ const sourceLink = 'https://github.com/radix-vue/shadcn-vue/tree/dev/'
|
|||
>
|
||||
{{ doc.title }}
|
||||
|
||||
<Badge v-if="doc.label" class="ml-2">
|
||||
<span v-if="doc.label" class="ml-2 rounded-md bg-[#adfa1d] px-1.5 py-0.5 text-xs leading-none text-[#000000] no-underline group-hover:no-underline">
|
||||
{{ doc.label }}
|
||||
</Badge>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -90,6 +91,10 @@ const sourceLink = 'https://github.com/radix-vue/shadcn-vue/tree/dev/'
|
|||
</div>
|
||||
|
||||
<div class="flex items-center space-x-2 pt-4">
|
||||
<a v-if="frontmatter.docs" :href="frontmatter.docs" target="_blank" class="inline-flex items-center rounded-md 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 select-none border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80">
|
||||
<RadixIconsExternalLink class="mr-1" />
|
||||
Docs
|
||||
</a>
|
||||
<a v-if="frontmatter.source" :href="sourceLink + frontmatter.source" target="_blank" class="inline-flex items-center rounded-md 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 select-none border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80">
|
||||
<RadixIconsCode class="mr-1" />
|
||||
Component Source
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import PageHeader from '../components/PageHeader.vue'
|
||||
import PageHeaderHeading from '../components/PageHeaderHeading.vue'
|
||||
import PageHeaderDescription from '../components/PageHeaderDescription.vue'
|
||||
import PageAction from '../components/PageAction.vue'
|
||||
import ExamplesNav from '../components/ExamplesNav.vue'
|
||||
import { announcementConfig } from '../config/site'
|
||||
import ArrowRightIcon from '~icons/radix-icons/arrow-right'
|
||||
|
|
@ -36,7 +37,7 @@ import { cn } from '@/lib/utils'
|
|||
components. Use this as a guide to build your own.
|
||||
</PageHeaderDescription>
|
||||
|
||||
<section class="flex w-full items-center space-x-4 pb-8 pt-4 md:pb-10">
|
||||
<PageAction>
|
||||
<a
|
||||
href="/docs/introduction"
|
||||
:class="cn(buttonVariants(), 'rounded-[6px]')"
|
||||
|
|
@ -52,7 +53,7 @@ import { cn } from '@/lib/utils'
|
|||
>
|
||||
Components
|
||||
</a>
|
||||
</section>
|
||||
</PageAction>
|
||||
</PageHeader>
|
||||
<section>
|
||||
<ExamplesNav />
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import RadixIconsSun from '~icons/radix-icons/sun'
|
|||
import { useConfigStore } from '@/stores/config'
|
||||
import { Dialog, DialogContent } from '@/lib/registry/default/ui/dialog'
|
||||
import { Toaster as DefaultToaster } from '@/lib/registry/default/ui/toast'
|
||||
import { Toaster as NewYorkSonner } from '@/lib/registry/new-york/ui/sonner'
|
||||
import { Toaster as NewYorkToaster } from '@/lib/registry/new-york/ui/toast'
|
||||
|
||||
import File from '~icons/radix-icons/file'
|
||||
|
|
@ -50,7 +51,13 @@ const links = [
|
|||
]
|
||||
|
||||
const isOpen = ref(false)
|
||||
const { Meta_K, Ctrl_K } = useMagicKeys()
|
||||
const { Meta_K, Ctrl_K } = useMagicKeys({
|
||||
passive: false,
|
||||
onEventFired(e) {
|
||||
if (e.key === 'k' && (e.metaKey || e.ctrlKey))
|
||||
e.preventDefault()
|
||||
},
|
||||
})
|
||||
|
||||
watch([Meta_K, Ctrl_K], (v) => {
|
||||
if (v[0] || v[1])
|
||||
|
|
@ -81,7 +88,7 @@ watch(() => $route.path, (n) => {
|
|||
<div class="flex min-h-screen flex-col bg-background">
|
||||
<header class="sticky z-40 top-0 bg-background/80 backdrop-blur-lg border-b border-border">
|
||||
<div
|
||||
class="container flex justify-between h-14 items-center"
|
||||
class="container flex justify-between h-14 max-w-screen-2xl items-center"
|
||||
>
|
||||
<MobileNav />
|
||||
|
||||
|
|
@ -131,21 +138,22 @@ watch(() => $route.path, (n) => {
|
|||
:variant="'ghost'"
|
||||
:size="'sm'"
|
||||
>
|
||||
<component :is="link.icon" />
|
||||
<component :is="link.icon" class="w-[20px] h-5" />
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
class="flex items-center justify-center"
|
||||
aria-label="Toggle dark mode"
|
||||
:variant="'ghost'"
|
||||
:size="'sm'"
|
||||
@click="toggleDark()"
|
||||
>
|
||||
<component
|
||||
:is="isDark ? RadixIconsSun : RadixIconsMoon"
|
||||
class="text-foreground"
|
||||
/>
|
||||
</Button>
|
||||
<ClientOnly>
|
||||
<Button
|
||||
class="flex items-center justify-center"
|
||||
aria-label="Toggle dark mode"
|
||||
:variant="'ghost'"
|
||||
:size="'icon'" @click="toggleDark()"
|
||||
>
|
||||
<component
|
||||
:is="isDark ? RadixIconsSun : RadixIconsMoon"
|
||||
class="w-[20px] h-5 text-foreground"
|
||||
/>
|
||||
</Button>
|
||||
</ClientOnly>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -285,6 +293,9 @@ watch(() => $route.path, (n) => {
|
|||
</DialogContent>
|
||||
</Dialog>
|
||||
<DefaultToaster />
|
||||
<ClientOnly>
|
||||
<NewYorkSonner :theme="isDark ? 'dark' : 'light'" />
|
||||
</ClientOnly>
|
||||
<NewYorkToaster />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -5,57 +5,56 @@
|
|||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 240 10% 3.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 240 10% 3.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 240 10% 3.9%;
|
||||
--primary: 240 5.9% 10%;
|
||||
--primary-foreground: 0 0% 98%;
|
||||
--secondary: 240 4.8% 95.9%;
|
||||
--secondary-foreground: 240 5.9% 10%;
|
||||
--muted: 240 4.8% 95.9%;
|
||||
--muted-foreground: 240 3.8% 46.1%;
|
||||
--accent: 240 4.8% 95.9%;
|
||||
--accent-foreground: 240 5.9% 10%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 240 5.9% 90%;
|
||||
--input: 240 5.9% 90%;
|
||||
--ring: 240 5% 64.9%;
|
||||
--radius: 0.5rem;
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 240 10% 3.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 240 10% 3.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 240 10% 3.9%;
|
||||
--primary: 240 5.9% 10%;
|
||||
--primary-foreground: 0 0% 98%;
|
||||
--secondary: 240 4.8% 95.9%;
|
||||
--secondary-foreground: 240 5.9% 10%;
|
||||
--muted: 240 4.8% 95.9%;
|
||||
--muted-foreground: 240 3.8% 46.1%;
|
||||
--accent: 240 4.8% 95.9%;
|
||||
--accent-foreground: 240 5.9% 10%;
|
||||
--destructive: 0 72.22% 50.59%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 240 5.9% 90%;
|
||||
--input: 240 5.9% 90%;
|
||||
--ring: 240 5% 64.9%;
|
||||
--radius: 0.5rem;
|
||||
|
||||
|
||||
--vis-tooltip-background-color: none !important;
|
||||
--vis-tooltip-background-color: none !important;
|
||||
--vis-tooltip-border-color: none !important;
|
||||
--vis-tooltip-text-color: none !important;
|
||||
--vis-tooltip-shadow-color: none !important;
|
||||
--vis-tooltip-backdrop-filter: none !important;
|
||||
--vis-tooltip-padding: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 240 10% 3.9%;
|
||||
--foreground: 0 0% 98%;
|
||||
--card: 240 10% 3.9%;
|
||||
--card-foreground: 0 0% 98%;
|
||||
--popover: 240 10% 3.9%;
|
||||
--popover-foreground: 0 0% 98%;
|
||||
--primary: 0 0% 98%;
|
||||
--primary-foreground: 240 5.9% 10%;
|
||||
--secondary: 240 3.7% 15.9%;
|
||||
--secondary-foreground: 0 0% 98%;
|
||||
--muted: 240 3.7% 15.9%;
|
||||
--muted-foreground: 240 5% 64.9%;
|
||||
--accent: 240 3.7% 15.9%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 0 85.7% 97.3%;
|
||||
--border: 240 3.7% 15.9%;
|
||||
--input: 240 3.7% 15.9%;
|
||||
--ring: 240 4.9% 83.9%;
|
||||
}
|
||||
.dark {
|
||||
--background: 240 10% 3.9%;
|
||||
--foreground: 0 0% 98%;
|
||||
--card: 240 10% 3.9%;
|
||||
--card-foreground: 0 0% 98%;
|
||||
--popover: 240 10% 3.9%;
|
||||
--popover-foreground: 0 0% 98%;
|
||||
--primary: 0 0% 98%;
|
||||
--primary-foreground: 240 5.9% 10%;
|
||||
--secondary: 240 3.7% 15.9%;
|
||||
--secondary-foreground: 0 0% 98%;
|
||||
--muted: 240 3.7% 15.9%;
|
||||
--muted-foreground: 240 5% 64.9%;
|
||||
--accent: 240 3.7% 15.9%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 0 85.7% 97.3%;
|
||||
--border: 240 3.7% 15.9%;
|
||||
--input: 240 3.7% 15.9%;
|
||||
--ring: 240 4.9% 83.9%;
|
||||
}
|
||||
|
||||
|
||||
* {
|
||||
|
|
@ -124,7 +123,7 @@
|
|||
|
||||
.step:before {
|
||||
@apply absolute w-9 h-9 bg-muted rounded-full font-mono font-medium text-center text-base inline-flex items-center justify-center -indent-px border-4 border-background;
|
||||
@apply ml-[-50px] mt-[-4px];
|
||||
@apply -ml-[50px] -mt-1;
|
||||
content: counter(step);
|
||||
}
|
||||
}
|
||||
|
|
@ -147,7 +146,7 @@ pre code {
|
|||
}
|
||||
|
||||
pre code .line {
|
||||
@apply px-4 min-h-[1.5rem] !py-0.5 w-full inline-block;
|
||||
@apply px-4 min-h-6 !py-0.5 w-full inline-block;
|
||||
}
|
||||
|
||||
.line-number {
|
||||
|
|
@ -157,4 +156,4 @@ pre code .line {
|
|||
::view-transition-old(root),
|
||||
::view-transition-new(root) {
|
||||
animation-duration: 0.3s;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
:root {
|
||||
--shiki-color-text: #EEEEEE;
|
||||
--shiki-foreground: #FFF8;
|
||||
--shiki-color-background: #ffffff;
|
||||
--shiki-token-constant: #ffffff;
|
||||
--shiki-token-string: #ffffff88;
|
||||
|
|
@ -7,7 +7,14 @@
|
|||
--shiki-token-keyword: #ffffff88;
|
||||
--shiki-token-parameter: #AA0000;
|
||||
--shiki-token-function: #ffffff;
|
||||
--shiki-token-string-expression: #ffffff88;
|
||||
--shiki-token-string-expression: #ebebeb;
|
||||
--shiki-token-punctuation: #ffffff;
|
||||
--shiki-token-link: #EE0000;
|
||||
}
|
||||
|
||||
.shiki .highlighted-word {
|
||||
border-radius: calc(var(--radius) - 2px);
|
||||
border-color: rgba(63,63,70,.7);
|
||||
background-color: rgba(63,63,70,.5);
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
--vp-icon-copied: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m-6 9 2 2 4-4'/%3E%3C/svg%3E");
|
||||
--vp-code-bg: hsl(var(--muted));
|
||||
--vp-c-divider: hsl(var(--muted));
|
||||
--vp-code-block-color: #fff
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -414,7 +415,7 @@ hsl(var(--foreground) / 50%)
|
|||
|
||||
.vp-doc div[class*='language-'].line-numbers-mode {
|
||||
/*rtl:ignore*/
|
||||
padding-left: 32px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
.vp-doc .line-numbers-wrapper {
|
||||
|
|
@ -566,4 +567,12 @@ hsl(var(--foreground) / 50%)
|
|||
|
||||
.vp-external-link-icon::after {
|
||||
content: '';
|
||||
}
|
||||
|
||||
.vp-doc [class*=language-] code {
|
||||
color: var(--vp-code-block-color);
|
||||
}
|
||||
|
||||
.line-numbers-mode pre code .line {
|
||||
padding-left: 3rem !important;
|
||||
}
|
||||
|
|
@ -198,6 +198,48 @@ export const Index = {
|
|||
component: () => import('../src/lib/registry/default/example/CardWithForm.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/CardWithForm.vue'],
|
||||
},
|
||||
CarouselApi: {
|
||||
name: 'CarouselApi',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['carousel', 'card'],
|
||||
component: () => import('../src/lib/registry/default/example/CarouselApi.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/CarouselApi.vue'],
|
||||
},
|
||||
CarouselDemo: {
|
||||
name: 'CarouselDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['carousel', 'card'],
|
||||
component: () => import('../src/lib/registry/default/example/CarouselDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/CarouselDemo.vue'],
|
||||
},
|
||||
CarouselOrientation: {
|
||||
name: 'CarouselOrientation',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['carousel', 'card'],
|
||||
component: () => import('../src/lib/registry/default/example/CarouselOrientation.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/CarouselOrientation.vue'],
|
||||
},
|
||||
CarouselPlugin: {
|
||||
name: 'CarouselPlugin',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['carousel', 'card'],
|
||||
component: () => import('../src/lib/registry/default/example/CarouselPlugin.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/CarouselPlugin.vue'],
|
||||
},
|
||||
CarouselSize: {
|
||||
name: 'CarouselSize',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['carousel', 'card'],
|
||||
component: () => import('../src/lib/registry/default/example/CarouselSize.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/CarouselSize.vue'],
|
||||
},
|
||||
CarouselSpacing: {
|
||||
name: 'CarouselSpacing',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['carousel', 'card'],
|
||||
component: () => import('../src/lib/registry/default/example/CarouselSpacing.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/CarouselSpacing.vue'],
|
||||
},
|
||||
CheckboxDemo: {
|
||||
name: 'CheckboxDemo',
|
||||
type: 'components:example',
|
||||
|
|
@ -289,6 +331,13 @@ export const Index = {
|
|||
component: () => import('../src/lib/registry/default/example/ContextMenuDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/ContextMenuDemo.vue'],
|
||||
},
|
||||
DataTableColumnPinningDemo: {
|
||||
name: 'DataTableColumnPinningDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['button', 'checkbox', 'dropdown-menu', 'input', 'table', 'utils'],
|
||||
component: () => import('../src/lib/registry/default/example/DataTableColumnPinningDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/DataTableColumnPinningDemo.vue'],
|
||||
},
|
||||
DataTableDemo: {
|
||||
name: 'DataTableDemo',
|
||||
type: 'components:example',
|
||||
|
|
@ -331,6 +380,13 @@ export const Index = {
|
|||
component: () => import('../src/lib/registry/default/example/DatePickerWithRange.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/DatePickerWithRange.vue'],
|
||||
},
|
||||
DateTimePickerDemo: {
|
||||
name: 'DateTimePickerDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['utils', 'button', 'calendar', 'popover'],
|
||||
component: () => import('../src/lib/registry/default/example/DateTimePickerDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/DateTimePickerDemo.vue'],
|
||||
},
|
||||
DialogCustomCloseButton: {
|
||||
name: 'DialogCustomCloseButton',
|
||||
type: 'components:example',
|
||||
|
|
@ -457,6 +513,20 @@ export const Index = {
|
|||
component: () => import('../src/lib/registry/default/example/PaginationDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/PaginationDemo.vue'],
|
||||
},
|
||||
PinInputDemo: {
|
||||
name: 'PinInputDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['pin-input'],
|
||||
component: () => import('../src/lib/registry/default/example/PinInputDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/PinInputDemo.vue'],
|
||||
},
|
||||
PinInputFormDemo: {
|
||||
name: 'PinInputFormDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['pin-input', 'button', 'form', 'toast'],
|
||||
component: () => import('../src/lib/registry/default/example/PinInputFormDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/PinInputFormDemo.vue'],
|
||||
},
|
||||
PopoverDemo: {
|
||||
name: 'PopoverDemo',
|
||||
type: 'components:example',
|
||||
|
|
@ -485,6 +555,13 @@ export const Index = {
|
|||
component: () => import('../src/lib/registry/default/example/RadioGroupForm.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/RadioGroupForm.vue'],
|
||||
},
|
||||
RangePickerWithSlot: {
|
||||
name: 'RangePickerWithSlot',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['utils', 'button', 'calendar', 'popover'],
|
||||
component: () => import('../src/lib/registry/default/example/RangePickerWithSlot.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/RangePickerWithSlot.vue'],
|
||||
},
|
||||
ScrollAreaDemo: {
|
||||
name: 'ScrollAreaDemo',
|
||||
type: 'components:example',
|
||||
|
|
@ -548,6 +625,13 @@ export const Index = {
|
|||
component: () => import('../src/lib/registry/default/example/SliderDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/SliderDemo.vue'],
|
||||
},
|
||||
SonnerDemo: {
|
||||
name: 'SonnerDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['button'],
|
||||
component: () => import('../src/lib/registry/default/example/SonnerDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/SonnerDemo.vue'],
|
||||
},
|
||||
SwitchDemo: {
|
||||
name: 'SwitchDemo',
|
||||
type: 'components:example',
|
||||
|
|
@ -667,6 +751,48 @@ export const Index = {
|
|||
component: () => import('../src/lib/registry/default/example/ToggleDisabledDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/ToggleDisabledDemo.vue'],
|
||||
},
|
||||
ToggleGroupDemo: {
|
||||
name: 'ToggleGroupDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['toggle-group'],
|
||||
component: () => import('../src/lib/registry/default/example/ToggleGroupDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/ToggleGroupDemo.vue'],
|
||||
},
|
||||
ToggleGroupDisabledDemo: {
|
||||
name: 'ToggleGroupDisabledDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['toggle-group'],
|
||||
component: () => import('../src/lib/registry/default/example/ToggleGroupDisabledDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/ToggleGroupDisabledDemo.vue'],
|
||||
},
|
||||
ToggleGroupLargeDemo: {
|
||||
name: 'ToggleGroupLargeDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['toggle-group'],
|
||||
component: () => import('../src/lib/registry/default/example/ToggleGroupLargeDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/ToggleGroupLargeDemo.vue'],
|
||||
},
|
||||
ToggleGroupOutlineDemo: {
|
||||
name: 'ToggleGroupOutlineDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['toggle-group'],
|
||||
component: () => import('../src/lib/registry/default/example/ToggleGroupOutlineDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/ToggleGroupOutlineDemo.vue'],
|
||||
},
|
||||
ToggleGroupSingleDemo: {
|
||||
name: 'ToggleGroupSingleDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['toggle-group'],
|
||||
component: () => import('../src/lib/registry/default/example/ToggleGroupSingleDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/ToggleGroupSingleDemo.vue'],
|
||||
},
|
||||
ToggleGroupSmallDemo: {
|
||||
name: 'ToggleGroupSmallDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['toggle-group'],
|
||||
component: () => import('../src/lib/registry/default/example/ToggleGroupSmallDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/ToggleGroupSmallDemo.vue'],
|
||||
},
|
||||
ToggleItalicDemo: {
|
||||
name: 'ToggleItalicDemo',
|
||||
type: 'components:example',
|
||||
|
|
@ -1019,6 +1145,48 @@ export const Index = {
|
|||
component: () => import('../src/lib/registry/new-york/example/CardWithForm.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/CardWithForm.vue'],
|
||||
},
|
||||
CarouselApi: {
|
||||
name: 'CarouselApi',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['carousel', 'card'],
|
||||
component: () => import('../src/lib/registry/new-york/example/CarouselApi.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/CarouselApi.vue'],
|
||||
},
|
||||
CarouselDemo: {
|
||||
name: 'CarouselDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['carousel', 'card'],
|
||||
component: () => import('../src/lib/registry/new-york/example/CarouselDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/CarouselDemo.vue'],
|
||||
},
|
||||
CarouselOrientation: {
|
||||
name: 'CarouselOrientation',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['carousel', 'card'],
|
||||
component: () => import('../src/lib/registry/new-york/example/CarouselOrientation.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/CarouselOrientation.vue'],
|
||||
},
|
||||
CarouselPlugin: {
|
||||
name: 'CarouselPlugin',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['carousel', 'card'],
|
||||
component: () => import('../src/lib/registry/new-york/example/CarouselPlugin.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/CarouselPlugin.vue'],
|
||||
},
|
||||
CarouselSize: {
|
||||
name: 'CarouselSize',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['carousel', 'card'],
|
||||
component: () => import('../src/lib/registry/new-york/example/CarouselSize.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/CarouselSize.vue'],
|
||||
},
|
||||
CarouselSpacing: {
|
||||
name: 'CarouselSpacing',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['carousel', 'card'],
|
||||
component: () => import('../src/lib/registry/new-york/example/CarouselSpacing.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/CarouselSpacing.vue'],
|
||||
},
|
||||
CheckboxDemo: {
|
||||
name: 'CheckboxDemo',
|
||||
type: 'components:example',
|
||||
|
|
@ -1110,6 +1278,13 @@ export const Index = {
|
|||
component: () => import('../src/lib/registry/new-york/example/ContextMenuDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/ContextMenuDemo.vue'],
|
||||
},
|
||||
DataTableColumnPinningDemo: {
|
||||
name: 'DataTableColumnPinningDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['button', 'checkbox', 'dropdown-menu', 'input', 'table', 'utils'],
|
||||
component: () => import('../src/lib/registry/new-york/example/DataTableColumnPinningDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/DataTableColumnPinningDemo.vue'],
|
||||
},
|
||||
DataTableDemo: {
|
||||
name: 'DataTableDemo',
|
||||
type: 'components:example',
|
||||
|
|
@ -1152,6 +1327,13 @@ export const Index = {
|
|||
component: () => import('../src/lib/registry/new-york/example/DatePickerWithRange.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/DatePickerWithRange.vue'],
|
||||
},
|
||||
DateTimePickerDemo: {
|
||||
name: 'DateTimePickerDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['utils', 'button', 'calendar', 'popover'],
|
||||
component: () => import('../src/lib/registry/new-york/example/DateTimePickerDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/DateTimePickerDemo.vue'],
|
||||
},
|
||||
DialogCustomCloseButton: {
|
||||
name: 'DialogCustomCloseButton',
|
||||
type: 'components:example',
|
||||
|
|
@ -1278,6 +1460,20 @@ export const Index = {
|
|||
component: () => import('../src/lib/registry/new-york/example/PaginationDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/PaginationDemo.vue'],
|
||||
},
|
||||
PinInputDemo: {
|
||||
name: 'PinInputDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['pin-input'],
|
||||
component: () => import('../src/lib/registry/new-york/example/PinInputDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/PinInputDemo.vue'],
|
||||
},
|
||||
PinInputFormDemo: {
|
||||
name: 'PinInputFormDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['pin-input', 'button', 'form', 'toast'],
|
||||
component: () => import('../src/lib/registry/new-york/example/PinInputFormDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/PinInputFormDemo.vue'],
|
||||
},
|
||||
PopoverDemo: {
|
||||
name: 'PopoverDemo',
|
||||
type: 'components:example',
|
||||
|
|
@ -1306,6 +1502,13 @@ export const Index = {
|
|||
component: () => import('../src/lib/registry/new-york/example/RadioGroupForm.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/RadioGroupForm.vue'],
|
||||
},
|
||||
RangePickerWithSlot: {
|
||||
name: 'RangePickerWithSlot',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['utils', 'button', 'calendar', 'popover'],
|
||||
component: () => import('../src/lib/registry/new-york/example/RangePickerWithSlot.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/RangePickerWithSlot.vue'],
|
||||
},
|
||||
ScrollAreaDemo: {
|
||||
name: 'ScrollAreaDemo',
|
||||
type: 'components:example',
|
||||
|
|
@ -1369,6 +1572,13 @@ export const Index = {
|
|||
component: () => import('../src/lib/registry/new-york/example/SliderDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/SliderDemo.vue'],
|
||||
},
|
||||
SonnerDemo: {
|
||||
name: 'SonnerDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['button'],
|
||||
component: () => import('../src/lib/registry/new-york/example/SonnerDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/SonnerDemo.vue'],
|
||||
},
|
||||
SwitchDemo: {
|
||||
name: 'SwitchDemo',
|
||||
type: 'components:example',
|
||||
|
|
@ -1488,6 +1698,48 @@ export const Index = {
|
|||
component: () => import('../src/lib/registry/new-york/example/ToggleDisabledDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/ToggleDisabledDemo.vue'],
|
||||
},
|
||||
ToggleGroupDemo: {
|
||||
name: 'ToggleGroupDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['toggle-group'],
|
||||
component: () => import('../src/lib/registry/new-york/example/ToggleGroupDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/ToggleGroupDemo.vue'],
|
||||
},
|
||||
ToggleGroupDisabledDemo: {
|
||||
name: 'ToggleGroupDisabledDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['toggle-group'],
|
||||
component: () => import('../src/lib/registry/new-york/example/ToggleGroupDisabledDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/ToggleGroupDisabledDemo.vue'],
|
||||
},
|
||||
ToggleGroupLargeDemo: {
|
||||
name: 'ToggleGroupLargeDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['toggle-group'],
|
||||
component: () => import('../src/lib/registry/new-york/example/ToggleGroupLargeDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/ToggleGroupLargeDemo.vue'],
|
||||
},
|
||||
ToggleGroupOutlineDemo: {
|
||||
name: 'ToggleGroupOutlineDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['toggle-group'],
|
||||
component: () => import('../src/lib/registry/new-york/example/ToggleGroupOutlineDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/ToggleGroupOutlineDemo.vue'],
|
||||
},
|
||||
ToggleGroupSingleDemo: {
|
||||
name: 'ToggleGroupSingleDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['toggle-group'],
|
||||
component: () => import('../src/lib/registry/new-york/example/ToggleGroupSingleDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/ToggleGroupSingleDemo.vue'],
|
||||
},
|
||||
ToggleGroupSmallDemo: {
|
||||
name: 'ToggleGroupSmallDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['toggle-group'],
|
||||
component: () => import('../src/lib/registry/new-york/example/ToggleGroupSmallDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/ToggleGroupSmallDemo.vue'],
|
||||
},
|
||||
ToggleItalicDemo: {
|
||||
name: 'ToggleItalicDemo',
|
||||
type: 'components:example',
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "www",
|
||||
"type": "module",
|
||||
"version": "0.8.4",
|
||||
"version": "0.9.0",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
|
|
@ -9,30 +9,35 @@
|
|||
"dev": "vitepress dev",
|
||||
"build": "vitepress build",
|
||||
"preview": "vitepress preview",
|
||||
"typecheck": "vue-tsc --noEmit",
|
||||
"typecheck:registry": "vue-tsc --noEmit -p tsconfig.registry.json",
|
||||
"build:registry": "pnpm typecheck:registry && tsx ./scripts/build-registry.ts"
|
||||
"typecheck": "vue-tsc",
|
||||
"typecheck:registry": "vue-tsc -p tsconfig.registry.json",
|
||||
"build:registry": "tsx ./scripts/build-registry.ts",
|
||||
"build:registry-strict": "pnpm typecheck:registry && tsx ./scripts/build-registry.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@formkit/auto-animate": "^0.8.0",
|
||||
"@formkit/auto-animate": "^0.8.1",
|
||||
"@morev/vue-transitions": "^2.3.6",
|
||||
"@radix-icons/vue": "^1.0.0",
|
||||
"@stackblitz/sdk": "^1.9.0",
|
||||
"@tanstack/vue-table": "^8.10.7",
|
||||
"@unovis/ts": "^1.2.1",
|
||||
"@unovis/vue": "1.3.0",
|
||||
"@vee-validate/zod": "^4.11.8",
|
||||
"@vueuse/core": "^10.5.0",
|
||||
"@tanstack/vue-table": "^8.11.8",
|
||||
"@unovis/ts": "^1.3.3",
|
||||
"@unovis/vue": "^1.3.3",
|
||||
"@vee-validate/zod": "^4.12.5",
|
||||
"@vueuse/core": "^10.7.2",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"clsx": "^2.1.0",
|
||||
"codesandbox": "^2.2.3",
|
||||
"date-fns": "^2.30.0",
|
||||
"embla-carousel": "^8.0.0-rc22",
|
||||
"embla-carousel-autoplay": "^8.0.0-rc22",
|
||||
"embla-carousel-vue": "^8.0.0-rc22",
|
||||
"lucide-vue-next": "^0.276.0",
|
||||
"radix-vue": "^1.2.3",
|
||||
"radix-vue": "^1.4.1",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"v-calendar": "^3.1.2",
|
||||
"vee-validate": "4.11.8",
|
||||
"vue": "^3.3.7",
|
||||
"vee-validate": "4.12.5",
|
||||
"vue": "^3.4.15",
|
||||
"vue-sonner": "^1.0.3",
|
||||
"vue-wrap-balancer": "^1.1.3",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
|
|
@ -41,24 +46,27 @@
|
|||
"@iconify-json/tabler": "^1.1.89",
|
||||
"@iconify/json": "^2.2.108",
|
||||
"@iconify/vue": "^4.1.1",
|
||||
"@shikijs/transformers": "^1.0.0-beta.3",
|
||||
"@types/lodash.template": "^4.5.2",
|
||||
"@types/node": "^20.8.10",
|
||||
"@vitejs/plugin-vue": "^4.4.0",
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.2",
|
||||
"@vue/compiler-core": "^3.3.7",
|
||||
"@vue/compiler-dom": "^3.3.7",
|
||||
"@vue/tsconfig": "^0.4.0",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"@vitejs/plugin-vue": "^5.0.3",
|
||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||
"@vue/compiler-core": "^3.4.15",
|
||||
"@vue/compiler-dom": "^3.4.15",
|
||||
"@vue/tsconfig": "^0.5.1",
|
||||
"autoprefixer": "^10.4.17",
|
||||
"lodash.template": "^4.5.0",
|
||||
"pathe": "^1.1.1",
|
||||
"oxc-parser": "^0.2.0",
|
||||
"pathe": "^1.1.2",
|
||||
"rimraf": "^5.0.5",
|
||||
"tailwind-merge": "^2.0.0",
|
||||
"tailwindcss": "^3.3.5",
|
||||
"tsx": "^3.14.0",
|
||||
"typescript": "^5.2.2",
|
||||
"unplugin-icons": "^0.17.1",
|
||||
"vite": "^4.5.0",
|
||||
"vitepress": "^1.0.0-rc.24",
|
||||
"vue-tsc": "^1.8.22"
|
||||
"shiki": "^1.0.0-beta.3",
|
||||
"tailwind-merge": "^2.2.1",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"tsx": "^4.7.0",
|
||||
"typescript": "^5.3.3",
|
||||
"unplugin-icons": "^0.18.3",
|
||||
"vite": "^5.0.12",
|
||||
"vitepress": "^1.0.0-rc.41",
|
||||
"vue-tsc": "^1.8.27"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,8 @@ fs.writeFileSync(path.join(process.cwd(), '__registry__/index.ts'), index)
|
|||
// ----------------------------------------------------------------------------
|
||||
// Build registry/styles/[style]/[name].json.
|
||||
// ----------------------------------------------------------------------------
|
||||
const newLine = '\n'
|
||||
|
||||
for (const style of styles) {
|
||||
const targetPath = path.join(REGISTRY_PATH, 'styles', style.name)
|
||||
|
||||
|
|
@ -78,11 +80,14 @@ for (const style of styles) {
|
|||
continue
|
||||
|
||||
const files = item.files?.map((file) => {
|
||||
const content = fs.readFileSync(
|
||||
let content = fs.readFileSync(
|
||||
path.join(process.cwd(), 'src/lib/registry', style.name, file),
|
||||
'utf8',
|
||||
)
|
||||
|
||||
// Replace Windows-style newlines with Unix-style newlines
|
||||
content = content.replace(/\r\n/g, newLine)
|
||||
|
||||
return {
|
||||
name: basename(file),
|
||||
content,
|
||||
|
|
@ -94,9 +99,11 @@ for (const style of styles) {
|
|||
files,
|
||||
}
|
||||
|
||||
const payloadStr = JSON.stringify(payload, null, 2).replace(/\r\n/g, newLine)
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(targetPath, `${item.name}.json`),
|
||||
JSON.stringify(payload, null, 2),
|
||||
payloadStr,
|
||||
'utf8',
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ description: Powered by amazing open source projects.
|
|||
|
||||
## About
|
||||
|
||||
[shadcn-vue](https://shadcn-vuee.com) is a port of [shadcn/ui](https://ui.shadcn.com) for Vue/Nuxt. It's maintained by [radix-vue](https://github.com/radix-vue).
|
||||
[shadcn-vue](https://shadcn-vue.com) is a port of [shadcn/ui](https://ui.shadcn.com) for Vue/Nuxt. It's maintained by [radix-vue](https://github.com/radix-vue).
|
||||
|
||||
## Credits
|
||||
|
||||
|
|
@ -17,4 +17,4 @@ description: Powered by amazing open source projects.
|
|||
|
||||
## License
|
||||
|
||||
MIT © [shadcn](https://shadcn.com) & [radix-vue](https://github.com/radix-vue)
|
||||
MIT © [shadcn](https://shadcn.com) & [radix-vue](https://github.com/radix-vue)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: Calendar
|
||||
description: A date field component that allows users to enter and edit date.
|
||||
source: apps/www/src/lib/registry/default/ui/calendar
|
||||
primitive: https://vcalendar.io/
|
||||
---
|
||||
|
||||
|
||||
|
|
@ -54,5 +56,40 @@ import { Calendar } from '@/components/ui/calendar'
|
|||
</template>
|
||||
```
|
||||
|
||||
See the [VCalendar](https://vcalendar.io/getting-started/installation.html) documentation for more information.
|
||||
The API is essentially the same, i.e. props and slots. See the [VCalendar](https://vcalendar.io/getting-started/installation.html) documentation for more information.
|
||||
|
||||
### Slots
|
||||
|
||||
The slots available are [those currently supported](https://github.com/nathanreyes/v-calendar/blob/v3.1.2/src/components/Calendar/CalendarSlot.vue#L16-L28) by VCalendar, namely :
|
||||
|
||||
- `day-content`
|
||||
- `day-popover`
|
||||
- `dp-footer`
|
||||
- `footer`
|
||||
- `header-title-wrapper`
|
||||
- `header-title`
|
||||
- `header-prev-button`
|
||||
- `header-next-button`
|
||||
- `nav`
|
||||
- `nav-prev-button`
|
||||
- `nav-next-button`
|
||||
- `page`
|
||||
- `time-header`
|
||||
|
||||
Example using the `day-content` slot:
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { Calendar } from '@/components/ui/calendar'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Calendar>
|
||||
<template #day-content="{ day, dayProps, dayEvents }">
|
||||
<div v-bind="dayProps" v-on="dayEvents">
|
||||
{{ day.label }}
|
||||
</div>
|
||||
</template>
|
||||
</Calendar>
|
||||
</template>
|
||||
```
|
||||
282
apps/www/src/content/docs/components/carousel.md
Normal file
282
apps/www/src/content/docs/components/carousel.md
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
---
|
||||
title: Carousel
|
||||
description: A carousel with motion and swipe built using Embla.
|
||||
source: apps/www/src/lib/registry/default/ui/carousel
|
||||
primitive: https://www.embla-carousel.com/api
|
||||
---
|
||||
|
||||
|
||||
<ComponentPreview name="CarouselDemo" />
|
||||
|
||||
|
||||
## About
|
||||
|
||||
The carousel component is built using the [Embla Carousel](https://www.embla-carousel.com/) library.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
```bash
|
||||
npx shadcn-vue@latest add carousel
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
Carousel,
|
||||
CarouselContent,
|
||||
CarouselItem,
|
||||
CarouselNext,
|
||||
CarouselPrevious,
|
||||
} from '@/components/ui/carousel'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Carousel>
|
||||
<CarouselContent>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
</CarouselContent>
|
||||
<CarouselPrevious />
|
||||
<CarouselNext />
|
||||
</Carousel>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Sizes
|
||||
|
||||
To set the size of the items, you can use the `basis` utility class on the `<CarouselItem />`.
|
||||
|
||||
<ComponentPreview name="CarouselSize" />
|
||||
|
||||
|
||||
Example
|
||||
|
||||
```vue:line-numbers title="Example" {4-6}
|
||||
// 33% of the carousel width.
|
||||
<Carousel>
|
||||
<CarouselContent>
|
||||
<CarouselItem class="basis-1/3">...</CarouselItem>
|
||||
<CarouselItem class="basis-1/3">...</CarouselItem>
|
||||
<CarouselItem class="basis-1/3">...</CarouselItem>
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
```
|
||||
|
||||
|
||||
Responsive
|
||||
|
||||
```vue:line-numbers title="Responsive" {4-6}
|
||||
// 50% on small screens and 33% on larger screens.
|
||||
<Carousel>
|
||||
<CarouselContent>
|
||||
<CarouselItem class="md:basis-1/2 lg:basis-1/3">...</CarouselItem>
|
||||
<CarouselItem class="md:basis-1/2 lg:basis-1/3">...</CarouselItem>
|
||||
<CarouselItem class="md:basis-1/2 lg:basis-1/3">...</CarouselItem>
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
```
|
||||
|
||||
### Spacing
|
||||
|
||||
To set the spacing between the items, we use a `pl-[VALUE]` utility on the `<CarouselItem />` and a negative `-ml-[VALUE]` on the `<CarouselContent />`.
|
||||
|
||||
<Callout class="mt-6">
|
||||
|
||||
**Why:** I tried to use the `gap` property or a `grid` layout on the `
|
||||
CarouselContent` but it required a lot of math and mental effort to get the
|
||||
spacing right. I found `pl-[VALUE]` and `-ml-[VALUE]` utilities much easier to
|
||||
use.
|
||||
<br/><br/>
|
||||
You can always adjust this in your own project if you need to.
|
||||
|
||||
</Callout>
|
||||
|
||||
<ComponentPreview name="CarouselSpacing" />
|
||||
|
||||
Example
|
||||
|
||||
```vue:line-numbers /-ml-4/ /pl-4/
|
||||
<template>
|
||||
<Carousel>
|
||||
<CarouselContent class="-ml-4">
|
||||
<CarouselItem class="pl-4">
|
||||
...
|
||||
</CarouselItem>
|
||||
<CarouselItem class="pl-4">
|
||||
...
|
||||
</CarouselItem>
|
||||
<CarouselItem class="pl-4">
|
||||
...
|
||||
</CarouselItem>
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
</template>
|
||||
```
|
||||
|
||||
Responsive
|
||||
|
||||
```vue:line-numbers /-ml-2/ /pl-2/ /md:-ml-4/ /md:pl-4/
|
||||
<template>
|
||||
<Carousel>
|
||||
<CarouselContent class="-ml-2 md:-ml-4">
|
||||
<CarouselItem class="pl-2 md:pl-4">
|
||||
...
|
||||
</CarouselItem>
|
||||
<CarouselItem class="pl-2 md:pl-4">
|
||||
...
|
||||
</CarouselItem>
|
||||
<CarouselItem class="pl-2 md:pl-4">
|
||||
...
|
||||
</CarouselItem>
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Orientation
|
||||
|
||||
Use the `orientation` prop to set the orientation of the carousel.
|
||||
|
||||
<ComponentPreview name="CarouselOrientation" />
|
||||
|
||||
```vue
|
||||
<Carousel orientation="vertical | horizontal">
|
||||
...
|
||||
</Carousel>
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
You can pass options to the carousel using the `opts` prop. See the [Embla Carousel docs](https://www.embla-carousel.com/api/options/) for more information.
|
||||
|
||||
```vue:line-numbers {3-6}
|
||||
<template>
|
||||
<Carousel
|
||||
:opts="{
|
||||
align: 'start',
|
||||
loop: true,
|
||||
}"
|
||||
>
|
||||
<CarouselContent>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
<CarouselItem>...</CarouselItem>
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
</template>
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Method 1
|
||||
|
||||
Use the `@init-api` emit method on `<Carousel />` component to set the instance of the API.
|
||||
|
||||
<ComponentPreview name="CarouselApi" />
|
||||
|
||||
### Method 2
|
||||
|
||||
You can access it through setting a template ref on the `<Carousel />` component.
|
||||
|
||||
```vue:line-numbers {2,5,9}
|
||||
<script setup>
|
||||
const carouselContainerRef = ref<InstanceType<typeof Carousel> | null>(null)
|
||||
|
||||
function accessApi() {
|
||||
carouselContainerRef.value?.carouselApi.on('select', () => {})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Carousel ref="carouselContainerRef">
|
||||
...
|
||||
</Carousel>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
You can listen to events using the API. To get the API instance use the `@init-api` emit method on the `<Carousel />` component
|
||||
|
||||
```vue:line-numbers {5,7-9,25}
|
||||
<script setup>
|
||||
import { nextTick, ref, watch } from 'vue'
|
||||
import { useCarousel } from '@/components/ui/carousel'
|
||||
|
||||
const api = ref<CarouselApi>()
|
||||
|
||||
function setApi(val: CarouselApi) {
|
||||
api.value = val
|
||||
}
|
||||
|
||||
const stop = watch(api, (api) => {
|
||||
if (!api)
|
||||
return
|
||||
|
||||
// Watch only once or use watchOnce() in @vueuse/core
|
||||
nextTick(() => stop())
|
||||
|
||||
api.on('select', () => {
|
||||
// Do something on select.
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Carousel @init-api="setApi">
|
||||
...
|
||||
</Carousel>
|
||||
</template>
|
||||
```
|
||||
|
||||
See the [Embla Carousel docs](https://www.embla-carousel.com/api/events/) for more information on using events.
|
||||
|
||||
## Slot Props
|
||||
|
||||
You can get the reactive slot props like `carouselRef, canScrollNext..Prev, scrollNext..Prev` using the `v-slot` directive in the `<Carousel v-slot="slotProps" />` component to extend the functionality.
|
||||
|
||||
```vue:line-numbers {2}
|
||||
<template>
|
||||
<Carousel v-slot="{ canScrollNext, canScrollPrev }">
|
||||
...
|
||||
<CarouselPrevious v-if="canScrollPrev" />
|
||||
<CarouselNext v-if="canScrollNext" />
|
||||
</Carousel>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Plugins
|
||||
|
||||
You can use the `plugins` prop to add plugins to the carousel.
|
||||
|
||||
```bash
|
||||
npm i embla-carousel-autoplay
|
||||
```
|
||||
|
||||
|
||||
```vue:line-numbers {2,8-10}
|
||||
<script setup lang="ts">
|
||||
import Autoplay from 'embla-carousel-autoplay'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Carousel
|
||||
class="w-full max-w-xs"
|
||||
:plugins="[Autoplay({
|
||||
delay: 2000,
|
||||
})]"
|
||||
>
|
||||
...
|
||||
</Carousel>
|
||||
</template>
|
||||
```
|
||||
|
||||
<ComponentPreview name="CarouselPlugin" />
|
||||
|
||||
See the [Embla Carousel docs](https://www.embla-carousel.com/api/plugins/) for more information on using plugins.
|
||||
|
|
@ -40,6 +40,8 @@ import { Checkbox } from '@/components/ui/checkbox'
|
|||
|
||||
### Form
|
||||
|
||||
Please first read `vee-validate` section for [Checkbox and Radio Inputs](https://vee-validate.logaretm.com/v4/examples/checkboxes-and-radio/)
|
||||
|
||||
<ComponentPreview name="CheckboxFormSingle" />
|
||||
|
||||
<ComponentPreview name="CheckboxFormMultiple" />
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
---
|
||||
title: Combobox
|
||||
description: Autocomplete input and command palette with a list of suggestions.
|
||||
component: true
|
||||
description: Autocomplete input and command palette with a list of suggestions.
|
||||
---
|
||||
|
||||
<ComponentPreview name="ComboboxDemo" />
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
---
|
||||
title: Data Table
|
||||
description: Powerful table and datagrids built using TanStack Table.
|
||||
primitive: https://tanstack.com/table/v8/docs/guide/introduction
|
||||
---
|
||||
|
||||
|
||||
<ComponentPreview name="DataTableDemo" />
|
||||
<ComponentPreview name="DataTableDemo" />
|
||||
|
||||
## Introduction
|
||||
|
||||
|
|
@ -22,9 +23,9 @@ We'll start with the basic `<Table />` component and build a complex data table
|
|||
|
||||
</Callout>
|
||||
|
||||
## Table of Contents
|
||||
## Table of Contents
|
||||
|
||||
This guide will show you how to use [TanStack Table](https://tanstack.com/table/v8) and the <Table /> component to build your own custom data table. We'll cover the following topics:
|
||||
This guide will show you how to use [TanStack Table](https://tanstack.com/table/v8) and the `<Table />` component to build your own custom data table. We'll cover the following topics:
|
||||
|
||||
- [Basic Table](#basic-table)
|
||||
- [Row Actions](#row-actions)
|
||||
|
|
@ -49,6 +50,13 @@ npx shadcn-vue@latest add table
|
|||
npm install @tanstack/vue-table
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Column Pinning
|
||||
|
||||
<ComponentPreview name="DataTableColumnPinningDemo" />
|
||||
|
||||
|
||||
## Prerequisites
|
||||
|
||||
We are going to build a table to show recent payments. Here's what our data looks like:
|
||||
|
|
@ -108,31 +116,23 @@ Let's start by building a basic table.
|
|||
|
||||
First, we'll define our columns in the `columns.ts` file.
|
||||
|
||||
```ts:line-numbers title="components/payments/columns.ts" {1,12-27}
|
||||
import type { ColumnDef } from '@tanstack/vue-table'
|
||||
|
||||
// This type is used to define the shape of our data.
|
||||
// You can use a Zod schema here if you want.
|
||||
export interface Payment {
|
||||
id: string
|
||||
amount: number
|
||||
status: 'pending' | 'processing' | 'success' | 'failed'
|
||||
email: string
|
||||
}
|
||||
```ts:line-numbers {1,12-27}
|
||||
import { h } from 'vue'
|
||||
|
||||
export const columns: ColumnDef<Payment>[] = [
|
||||
{
|
||||
accessorKey: 'status',
|
||||
header: 'Status',
|
||||
},
|
||||
{
|
||||
accessorKey: 'email',
|
||||
header: 'Email',
|
||||
},
|
||||
{
|
||||
accessorKey: 'amount',
|
||||
header: 'Amount',
|
||||
},
|
||||
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
||||
cell: ({ row }) => {
|
||||
const amount = Number.parseFloat(row.getValue('amount'))
|
||||
const formatted = new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
}).format(amount)
|
||||
|
||||
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||
},
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
|
|
@ -148,7 +148,7 @@ formatted, sorted and filtered.
|
|||
|
||||
Next, we'll create a `<DataTable />` component to render our table.
|
||||
|
||||
```ts:line-numbers
|
||||
```vue:line-numbers
|
||||
<script setup lang="ts" generic="TData, TValue">
|
||||
import type { ColumnDef } from '@tanstack/vue-table'
|
||||
import {
|
||||
|
|
@ -172,8 +172,8 @@ const props = defineProps<{
|
|||
}>()
|
||||
|
||||
const table = useVueTable({
|
||||
data: props.data,
|
||||
columns: props.columns,
|
||||
get data() { return props.data },
|
||||
get columns() { return props.columns },
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
})
|
||||
</script>
|
||||
|
|
@ -224,7 +224,7 @@ const table = useVueTable({
|
|||
|
||||
Finally, we'll render our table in our index component.
|
||||
|
||||
```ts:line-numbers showLineNumbers{28}
|
||||
```vue:line-numbers {28}
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { columns } from "./components/columns"
|
||||
|
|
@ -271,7 +271,7 @@ Let's format the amount cell to display the dollar amount. We'll also align the
|
|||
Update the `header` and `cell` definitions for amount as follows:
|
||||
|
||||
|
||||
```ts:line-numbers showLineNumbers title="components/payments/columns.ts" {5-17}
|
||||
```ts:line-numbers title="components/payments/columns.ts" {5-17}
|
||||
import { h } from 'vue'
|
||||
|
||||
export const columns: ColumnDef<Payment>[] = [
|
||||
|
|
@ -301,7 +301,7 @@ Let's add row actions to our table. We'll use a `<Dropdown />` component for thi
|
|||
|
||||
### Add the following into your `DataTableDropDown.vue` component:
|
||||
|
||||
```ts:line-numbers
|
||||
```vue:line-numbers
|
||||
// DataTableDropDown.vue
|
||||
<script setup lang="ts">
|
||||
import { MoreHorizontal } from 'lucide-vue-next'
|
||||
|
|
@ -379,7 +379,7 @@ Next, we'll add pagination to our table.
|
|||
|
||||
### Update `<DataTable>`
|
||||
|
||||
```ts:line-numbers showLineNumbers{4,12}
|
||||
```ts:line-numbers {4,12}
|
||||
import {
|
||||
FlexRender,
|
||||
getCoreRowModel,
|
||||
|
|
@ -388,8 +388,8 @@ import {
|
|||
} from "@tanstack/vue-table"
|
||||
|
||||
const table = useVueTable({
|
||||
data: props.data,
|
||||
columns: props.columns,
|
||||
get data() { return props.data },
|
||||
get columns() { return props.columns },
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
})
|
||||
|
|
@ -401,14 +401,13 @@ This will automatically paginate your rows into pages of 10. See the [pagination
|
|||
|
||||
We can add pagination controls to our table using the `<Button />` component and the `table.previousPage()`, `table.nextPage()` API methods.
|
||||
|
||||
```ts:line-numbers showLineNumbers{3,15,21-39}
|
||||
// components/payments/DataTable.vue
|
||||
```vue:line-numbers {3,15,21-39}
|
||||
<script lang="ts" generic="TData, TValue">
|
||||
import { Button } from "@/components/ui/button"
|
||||
|
||||
const table = useVueTable({
|
||||
data: props.data,
|
||||
columns: props.columns,
|
||||
get data() { return props.data },
|
||||
get columns() { return props.columns },
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
})
|
||||
|
|
@ -457,7 +456,7 @@ Let's make the email column sortable.
|
|||
|
||||
### Add the following into your `utils` file:
|
||||
|
||||
```ts:line-numbers showLineNumbers{5,6,12-17}
|
||||
```ts:line-numbers {5,6,12-17}
|
||||
import { type ClassValue, clsx } from 'clsx'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { camelize, getCurrentInstance, toHandlerKey } from 'vue'
|
||||
|
|
@ -481,7 +480,7 @@ The `valueUpdater` function updates a Vue `ref` object's value. It handles both
|
|||
|
||||
### Update `<DataTable>`
|
||||
|
||||
```ts:line-numbers showLineNumbers{4,7,16,34,41-44}
|
||||
```vue:line-numbers {4,7,16,34,41-44}
|
||||
<script setup lang="ts" generic="TData, TValue">
|
||||
import type {
|
||||
ColumnDef,
|
||||
|
|
@ -518,8 +517,8 @@ const props = defineProps<{
|
|||
const sorting = ref<SortingState>([])
|
||||
|
||||
const table = useVueTable({
|
||||
data: props.data,
|
||||
columns: props.columns,
|
||||
get data() { return props.data },
|
||||
get columns() { return props.columns },
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
|
|
@ -544,7 +543,7 @@ const table = useVueTable({
|
|||
|
||||
We can now update the `email` header cell to add sorting controls.
|
||||
|
||||
```ts:line-numbers showLineNumbers{5,10-17}
|
||||
```ts:line-numbers {5,10-17}
|
||||
// components/payments/columns.ts
|
||||
import type {
|
||||
ColumnDef,
|
||||
|
|
@ -578,7 +577,7 @@ Let's add a search input to filter emails in our table.
|
|||
|
||||
### Update `<DataTable>`
|
||||
|
||||
```ts:line-numbers showLineNumbers{4,11,19,39,48-49,52,60-64}
|
||||
```vue:line-numbers {4,11,19,39,48-49,52,60-64}
|
||||
<script setup lang="ts" generic="TData, TValue">
|
||||
import type {
|
||||
ColumnDef,
|
||||
|
|
@ -620,8 +619,8 @@ const sorting = ref<SortingState>([])
|
|||
const columnFilters = ref<ColumnFiltersState>([])
|
||||
|
||||
const table = useVueTable({
|
||||
data: props.data,
|
||||
columns: props.columns,
|
||||
get data() { return props.data },
|
||||
get columns() { return props.columns },
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
|
|
@ -663,7 +662,7 @@ Adding column visibility is fairly simple using `@tanstack/vue-table` visibility
|
|||
|
||||
### Update `<DataTable>`
|
||||
|
||||
```ts:line-numbers showLineNumbers{6,9-14,48,59,63,75-91}
|
||||
```vue:line-numbers {6,9-14,48,59,63,75-91}
|
||||
<script setup lang="ts" generic="TData, TValue">
|
||||
import type {
|
||||
ColumnDef,
|
||||
|
|
@ -714,11 +713,11 @@ const columnFilters = ref<ColumnFiltersState>([])
|
|||
const columnVisibility = ref<VisibilityState>({})
|
||||
|
||||
const table = useVueTable({
|
||||
data: props.data,
|
||||
columns: props.columns,
|
||||
get data() { return props.data },
|
||||
get columns() { return props.columns },
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onSortingChange: updaterOrValue => valueUpdater(updaterOrValue, sorting),
|
||||
onColumnFiltersChange: updaterOrValue => valueUpdater(updaterOrValue, columnFilters),
|
||||
|
|
@ -802,7 +801,7 @@ Next, we're going to add row selection to our table.
|
|||
|
||||
### Update column definitions
|
||||
|
||||
```ts:line-numbers showLineNumbers{3,6-20}
|
||||
```ts:line-numbers {3,6-20}
|
||||
import type { ColumnDef } from '@tanstack/vue-table'
|
||||
|
||||
import { Checkbox } from '@/components/ui/checkbox'
|
||||
|
|
@ -828,7 +827,7 @@ export const columns: ColumnDef<Payment>[] = [
|
|||
|
||||
### Update `<DataTable>`
|
||||
|
||||
```ts:line-numbers showLineNumbers{10,22,27}
|
||||
```vue:line-numbers {10,22,27}
|
||||
<script setup lang="ts" generic="TData, TValue">
|
||||
const props = defineProps<{
|
||||
columns: ColumnDef<TData, TValue>[]
|
||||
|
|
@ -841,11 +840,11 @@ const columnVisibility = ref<VisibilityState>({})
|
|||
const rowSelection = ref({})
|
||||
|
||||
const table = useVueTable({
|
||||
data: props.data,
|
||||
columns: props.columns,
|
||||
get data() { return props.data },
|
||||
get columns() { return props.columns },
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onSortingChange: updaterOrValue => valueUpdater(updaterOrValue, sorting),
|
||||
onColumnFiltersChange: updaterOrValue => valueUpdater(updaterOrValue, columnFilters),
|
||||
|
|
@ -894,7 +893,7 @@ Here are some components you can use to build your data tables. This is from the
|
|||
|
||||
Make any column header sortable and hideable.
|
||||
|
||||
```ts:line-numbers
|
||||
```vue:line-numbers
|
||||
<script setup lang="ts">
|
||||
import type { Column } from '@tanstack/vue-table'
|
||||
import { type Task } from '../data/schema'
|
||||
|
|
@ -987,7 +986,7 @@ export const columns = [
|
|||
|
||||
Add pagination controls to your table including page size and selection count.
|
||||
|
||||
```ts:line-numbers
|
||||
```vue:line-numbers
|
||||
<script setup lang="ts">
|
||||
import { type Table } from '@tanstack/vue-table'
|
||||
import { type Task } from '../data/schema'
|
||||
|
|
@ -1092,8 +1091,7 @@ defineProps<DataTablePaginationProps>()
|
|||
|
||||
A component to toggle column visibility.
|
||||
|
||||
```ts:line-numbers
|
||||
|
||||
```vue:line-numbers
|
||||
<script setup lang="ts">
|
||||
import type { Table } from '@tanstack/vue-table'
|
||||
import { computed } from 'vue'
|
||||
|
|
|
|||
|
|
@ -64,10 +64,18 @@ const date = ref<Date>()
|
|||
|
||||
<ComponentPreview name="DatePickerWithRange" />
|
||||
|
||||
### Date Time Picker
|
||||
|
||||
<ComponentPreview name="DateTimePickerDemo" />
|
||||
|
||||
### With Presets
|
||||
|
||||
<ComponentPreview name="DatePickerWithPresets" />
|
||||
|
||||
### With Slot
|
||||
|
||||
<ComponentPreview name="RangePickerWithSlot" />
|
||||
|
||||
### Form
|
||||
|
||||
<ComponentPreview name="DatePickerForm" />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
title: VeeValidate
|
||||
description: Building forms with VeeValidate and Zod.
|
||||
primitive: https://vee-validate.logaretm.com/v4/guide/overview/
|
||||
---
|
||||
|
||||
Forms are tricky. They are one of the most common things you'll build in a web application, but also one of the most complex.
|
||||
|
|
@ -158,7 +159,7 @@ Use `@vee-validate/zod` to integrate Zod schema validation with `vee-validate`
|
|||
|
||||
`toTypedSchema` also makes the form values and submitted values typed automatically and caters for both input and output types of that schema.
|
||||
|
||||
```vue showLineNumbers {2-3,5-7}
|
||||
```vue:line-numbers {2-3,5-7}
|
||||
<script setup lang="ts">
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import * as z from 'zod'
|
||||
|
|
@ -172,13 +173,13 @@ const formSchema = toTypedSchema(z.object({
|
|||
|
||||
### Define a form
|
||||
|
||||
Use the `useForm` composable from `vee-validate` or use `<Form />` component to create a from.
|
||||
Use the `useForm` composable from `vee-validate` or use `<Form />` component to create a form.
|
||||
|
||||
|
||||
<TabPreview name="Composition" :names="['Composition', 'Component']">
|
||||
<template #Composition>
|
||||
|
||||
```vue showLineNumbers {2,19-21}
|
||||
```vue:line-numbers {2,19-21}
|
||||
<script setup lang="ts">
|
||||
import { useForm } from 'vee-validate'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
|
|
@ -217,7 +218,7 @@ const onSubmit = form.handleSubmit((values) => {
|
|||
|
||||
<template #Component>
|
||||
|
||||
```vue showLineNumbers {5,24-26}
|
||||
```vue:line-numbers {5,24-26}
|
||||
<script setup lang="ts">
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import * as z from 'zod'
|
||||
|
|
@ -255,7 +256,7 @@ function onSubmit(values) {
|
|||
Based on last step we can either use `<Form />` component or `useForm` composable
|
||||
`useForm` is recommended cause values are typed automatically
|
||||
|
||||
```vue showLineNumbers {2}
|
||||
```vue:line-numbers {2}
|
||||
<script setup lang="ts">
|
||||
import { useForm } from 'vee-validate'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ import {
|
|||
|
||||
### Link Component
|
||||
|
||||
When using the Nuxt.js <NuxtLink /> component, you can use `navigationMenuTriggerStyle()` to apply the correct styles to the trigger.
|
||||
When using the Nuxt.js `<NuxtLink />` component, you can use `navigationMenuTriggerStyle()` to apply the correct styles to the trigger.
|
||||
|
||||
```ts
|
||||
import { navigationMenuTriggerStyle } from '@/components/ui/navigation-menu'
|
||||
|
|
|
|||
21
apps/www/src/content/docs/components/pin-input.md
Normal file
21
apps/www/src/content/docs/components/pin-input.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
title: PIN Input
|
||||
description: Allows users to input a sequence of one-character alphanumeric inputs.
|
||||
source: apps/www/src/lib/registry/default/ui/pin-input
|
||||
primitive: https://www.radix-vue.com/components/pin-input.html
|
||||
---
|
||||
|
||||
<ComponentPreview name="PinInputDemo" />
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npx shadcn-vue@latest add pin-input
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Form
|
||||
|
||||
<ComponentPreview name="PinInputFormDemo" />
|
||||
|
|
@ -40,4 +40,6 @@ import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
|
|||
|
||||
### Form
|
||||
|
||||
Please first read `vee-validate` section for [Checkbox and Radio Inputs](https://vee-validate.logaretm.com/v4/examples/checkboxes-and-radio/)
|
||||
|
||||
<ComponentPreview name="RadioGroupForm" />
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: Sheet
|
||||
description: Extends the Dialog component to display content that complements the main content of the screen.
|
||||
source: apps/www/src/lib/registry/default/ui/dialog
|
||||
source: apps/www/src/lib/registry/default/ui/sheet
|
||||
primitive: https://www.radix-vue.com/components/dialog.html
|
||||
---
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ Use the `side` property to `<SheetContent />` to indicate the edge of the screen
|
|||
|
||||
You can adjust the size of the sheet using CSS classes:
|
||||
|
||||
```vue:line-numbers showLineNumbers{4}
|
||||
```vue:line-numbers {4}
|
||||
<template>
|
||||
<Sheet>
|
||||
<SheetTrigger>Open</SheetTrigger>
|
||||
|
|
@ -72,4 +72,4 @@ You can adjust the size of the sheet using CSS classes:
|
|||
</SheetContent>
|
||||
</Sheet>
|
||||
</template>
|
||||
```
|
||||
```
|
||||
|
|
|
|||
|
|
@ -37,6 +37,6 @@ import { Skeleton } from '@/components/ui/skeleton'
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<Skeleton class="w-[100px] h-[20px] rounded-full" />
|
||||
<Skeleton class="w-[100px] h-5 rounded-full" />
|
||||
</template>
|
||||
```
|
||||
```
|
||||
|
|
|
|||
63
apps/www/src/content/docs/components/sonner.md
Normal file
63
apps/www/src/content/docs/components/sonner.md
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
---
|
||||
title: Sonner
|
||||
description: An opinionated toast component for Vue.
|
||||
docs: https://vue-sonner.vercel.app
|
||||
source: apps/www/src/lib/registry/default/ui/sonner
|
||||
---
|
||||
|
||||
<ComponentPreview name="SonnerDemo" />
|
||||
|
||||
## About
|
||||
|
||||
The Sonner component is provided by [vue-sonner](https://vue-sonner.vercel.app/), which is a Vue port of Sonner, originally created by [Emil Kowalski](https://twitter.com/emilkowalski_) for React.
|
||||
|
||||
## Installation
|
||||
|
||||
<Steps>
|
||||
|
||||
### Run the following command
|
||||
|
||||
```bash
|
||||
npx shadcn-vue@latest add sonner
|
||||
```
|
||||
|
||||
### Add the Toaster component
|
||||
|
||||
Add the following `Toaster` component to your `App.vue` file:
|
||||
|
||||
```vue title="App.vue" {2,6}
|
||||
<script setup lang="ts">
|
||||
import { Toaster } from '@/components/ui/sonner'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Toaster />
|
||||
</template>
|
||||
```
|
||||
|
||||
</Steps>
|
||||
|
||||
## Usage
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { toast } from 'vue-sonner'
|
||||
import { Button } from '@/components/ui/button'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Button
|
||||
variant="outline" @click="() => {
|
||||
toast('Event has been created', {
|
||||
description: 'Sunday, December 03, 2023 at 9:00 AM',
|
||||
action: {
|
||||
label: 'Undo',
|
||||
onClick: () => console.log('Undo'),
|
||||
},
|
||||
})
|
||||
}"
|
||||
>
|
||||
Add to calander
|
||||
</Button>
|
||||
</template>
|
||||
```
|
||||
93
apps/www/src/content/docs/components/toggle-group.md
Normal file
93
apps/www/src/content/docs/components/toggle-group.md
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
---
|
||||
title: Toggle Group
|
||||
description: A set of two-state buttons that can be toggled on or off.
|
||||
source: apps/www/src/lib/registry/default/ui/toggle-group
|
||||
primitive: https://www.radix-vue.com/components/toggle-group.html
|
||||
---
|
||||
|
||||
<ComponentPreview name="ToggleGroupDemo" />
|
||||
|
||||
## Installation
|
||||
|
||||
<TabPreview name="CLI">
|
||||
<template #CLI>
|
||||
|
||||
```bash
|
||||
npx shadcn-vue@latest add toggle-group
|
||||
```
|
||||
</template>
|
||||
|
||||
<template #Manual>
|
||||
|
||||
<Steps>
|
||||
|
||||
### Install the following dependencies:
|
||||
|
||||
```bash
|
||||
npm install radix-vue
|
||||
```
|
||||
|
||||
### Copy and paste the following code into your project
|
||||
|
||||
<<< @/lib/registry/default/ui/toggle-group/ToggleGroup.vue
|
||||
|
||||
</Steps>
|
||||
|
||||
</template>
|
||||
</TabPreview>
|
||||
|
||||
## Usage
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ToggleGroup type="single">
|
||||
<ToggleGroupItem value="a">
|
||||
A
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="b">
|
||||
B
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="c">
|
||||
C
|
||||
</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Default
|
||||
|
||||
<ComponentPreview name="ToggleGroupDemo" />
|
||||
|
||||
|
||||
### Outline
|
||||
|
||||
<ComponentPreview name="ToggleGroupOutlineDemo" />
|
||||
|
||||
|
||||
### Single
|
||||
|
||||
<ComponentPreview name="ToggleGroupSingleDemo" />
|
||||
|
||||
|
||||
### Small
|
||||
|
||||
<ComponentPreview name="ToggleGroupSmallDemo" />
|
||||
|
||||
|
||||
### Large
|
||||
|
||||
<ComponentPreview name="ToggleGroupLargeDemo" />
|
||||
|
||||
|
||||
### Disabled
|
||||
|
||||
<ComponentPreview name="ToggleGroupDisabledDemo" />
|
||||
|
||||
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ npm create astro@latest
|
|||
|
||||
You will be asked a few questions to configure your project:
|
||||
|
||||
```txt showLineNumbers
|
||||
```txt:line-numbers
|
||||
- Where should we create your new project?
|
||||
./your-app-name
|
||||
- How would you like to start your new project?
|
||||
|
|
@ -76,7 +76,7 @@ This will install `tailwindcss` and `@astrojs/tailwind` as dependencies and set
|
|||
|
||||
Add the code below to the tsconfig.json file to resolve paths:
|
||||
|
||||
```json {2-7} showLineNumbers
|
||||
```json:line-numbers {2-7}
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
|
|
@ -99,7 +99,7 @@ npx shadcn-vue@latest init
|
|||
|
||||
You will be asked a few questions to configure `components.json`:
|
||||
|
||||
```txt showLineNumbers
|
||||
```txt:line-numbers
|
||||
Would you like to use TypeScript (recommended)? no / yes
|
||||
Which framework are you using? Astro
|
||||
Which style would you like to use? › Default
|
||||
|
|
@ -116,15 +116,17 @@ Write configuration to components.json. Proceed? > Y/n
|
|||
|
||||
Import the `globals.css` file in the `src/index.astro` file:
|
||||
|
||||
```ts {2} showLineNumbers
|
||||
```ts:line-numbers {2}
|
||||
---
|
||||
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
|
||||
```ts:line-numbers {3-5}
|
||||
export default defineConfig({
|
||||
integrations: [
|
||||
tailwind({
|
||||
|
|
@ -144,7 +146,7 @@ npx shadcn-vue@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
|
||||
```astro:line-numbers {2,10}
|
||||
---
|
||||
import { Button } from "@/components/ui/button"
|
||||
---
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ npx shadcn-vue@latest init
|
|||
|
||||
You will be asked a few questions to configure `components.json`:
|
||||
|
||||
```txt showLineNumbers
|
||||
```txt:line-numbers
|
||||
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
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ description: Install and configure Nuxt.
|
|||
|
||||
### Create project
|
||||
|
||||
Start by creating a new Nuxt project using `create-next-app`:
|
||||
Start by creating a new Nuxt project using `create-nuxt-app`:
|
||||
|
||||
```bash
|
||||
npx nuxi@latest init my-app
|
||||
|
|
@ -26,12 +26,128 @@ npm install -D typescript
|
|||
npm install -D @nuxtjs/tailwindcss
|
||||
```
|
||||
|
||||
### Install `shadcn-nuxt` module (New ✨)
|
||||
### Add `Nuxt` module
|
||||
|
||||
<br>
|
||||
|
||||
<TabsMarkdown>
|
||||
<TabMarkdown title="shadcn-nuxt">
|
||||
|
||||
Install the package below.
|
||||
|
||||
```bash
|
||||
npm install -D shadcn-nuxt
|
||||
```
|
||||
|
||||
</TabMarkdown>
|
||||
|
||||
|
||||
<TabMarkdown title="manual">
|
||||
|
||||
Add the following code to `modules/shadcn.ts`.
|
||||
|
||||
```bash
|
||||
npm install -D shadcn-nuxt
|
||||
import {
|
||||
defineNuxtModule,
|
||||
addComponent,
|
||||
addComponentsDir,
|
||||
tryResolveModule,
|
||||
} from 'nuxt/kit';
|
||||
|
||||
export interface ShadcnVueOptions {
|
||||
/**
|
||||
* Prefix for all the imported component
|
||||
*/
|
||||
prefix: string;
|
||||
|
||||
/**
|
||||
* Directory that the component lives in.
|
||||
* @default "~/components/ui"
|
||||
*/
|
||||
componentDir: string;
|
||||
}
|
||||
|
||||
export default defineNuxtModule<ShadcnVueOptions>({
|
||||
defaults: {
|
||||
prefix: 'Ui',
|
||||
componentDir: '~/components/ui',
|
||||
},
|
||||
meta: {
|
||||
name: 'ShadcnVue',
|
||||
configKey: 'shadcn',
|
||||
version: '0.0.1',
|
||||
compatibility: {
|
||||
nuxt: '^3.9.0',
|
||||
bridge: false,
|
||||
},
|
||||
},
|
||||
async setup({ componentDir, prefix }) {
|
||||
const isVeeValidateExist = await tryResolveModule('vee-validate');
|
||||
|
||||
addComponentsDir(
|
||||
{
|
||||
path: componentDir,
|
||||
extensions: ['.vue'],
|
||||
prefix,
|
||||
pathPrefix: false,
|
||||
},
|
||||
{
|
||||
prepend: true,
|
||||
}
|
||||
);
|
||||
|
||||
if (isVeeValidateExist !== undefined) {
|
||||
addComponent({
|
||||
filePath: 'vee-validate',
|
||||
export: 'Form',
|
||||
name: `${prefix}Form`,
|
||||
priority: 999,
|
||||
});
|
||||
|
||||
addComponent({
|
||||
filePath: 'vee-validate',
|
||||
export: 'Field',
|
||||
name: `${prefix}FormField`,
|
||||
priority: 999,
|
||||
});
|
||||
}
|
||||
|
||||
addComponent({
|
||||
filePath: 'radix-vue',
|
||||
export: 'PaginationRoot',
|
||||
name: `${prefix}Pagination`,
|
||||
priority: 999,
|
||||
});
|
||||
|
||||
addComponent({
|
||||
filePath: 'radix-vue',
|
||||
export: 'PaginationList',
|
||||
name: `${prefix}PaginationList`,
|
||||
priority: 999,
|
||||
});
|
||||
|
||||
addComponent({
|
||||
filePath: 'radix-vue',
|
||||
export: 'PaginationListItem',
|
||||
name: `${prefix}PaginationListItem`,
|
||||
priority: 999,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
declare module '@nuxt/schema' {
|
||||
interface NuxtConfig {
|
||||
shadcn?: ShadcnVueOptions;
|
||||
}
|
||||
interface NuxtOptions {
|
||||
shadcn?: ShadcnVueOptions;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabMarkdown>
|
||||
</TabsMarkdown>
|
||||
|
||||
|
||||
### Configure `nuxt.config.ts`
|
||||
|
||||
|
|
@ -64,7 +180,7 @@ npx shadcn-vue@latest init
|
|||
|
||||
You will be asked a few questions to configure `components.json`:
|
||||
|
||||
```txt showLineNumbers
|
||||
```txt:line-numbers
|
||||
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
|
||||
|
|
|
|||
|
|
@ -19,13 +19,63 @@ npm create vite@latest my-vue-app -- --template vue-ts
|
|||
|
||||
### Add Tailwind and its configuration
|
||||
|
||||
Install `tailwindcss` and its peer dependencies, then generate your `tailwind.config.js` and `postcss.config.js` files:
|
||||
Install `tailwindcss` and its peer dependencies, then generate your `tailwind.config.js` and configure `postcss` plugins
|
||||
|
||||
```bash
|
||||
npm install -D tailwindcss postcss autoprefixer
|
||||
|
||||
npx tailwindcss init -p
|
||||
```
|
||||
|
||||
<TabsMarkdown>
|
||||
<TabMarkdown title="vite.config.ts">
|
||||
|
||||
Vite already has [`postcss`](https://github.com/vitejs/vite/blob/main/packages/vite/package.json#L78) dependency so you don't have to install it again in your package.json
|
||||
|
||||
```bash
|
||||
npm install -D tailwindcss autoprefixer
|
||||
```
|
||||
|
||||
#### `vite.config`
|
||||
|
||||
```typescript {5,6,10-14}
|
||||
import path from "path"
|
||||
import { defineConfig } from "vite"
|
||||
import vue from "@vitejs/plugin-vue"
|
||||
|
||||
import tailwind from "tailwindcss"
|
||||
import autoprefixer from "autoprefixer"
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
css: {
|
||||
postcss: {
|
||||
plugins: [tailwind(), autoprefixer()],
|
||||
},
|
||||
},
|
||||
resolve: {...}
|
||||
})
|
||||
```
|
||||
|
||||
</TabMarkdown>
|
||||
|
||||
|
||||
<TabMarkdown title="postcss.config.js">
|
||||
|
||||
```bash
|
||||
npm install -D tailwindcss autoprefixer postcss
|
||||
```
|
||||
|
||||
#### `postcss.config.js`
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
</TabMarkdown>
|
||||
</TabsMarkdown>
|
||||
|
||||
|
||||
### Edit tsconfig.json
|
||||
|
||||
|
|
@ -42,6 +92,11 @@ Add the code below to the compilerOptions of your tsconfig.json so your app can
|
|||
|
||||
Add the code below to the vite.config.ts so your app can resolve paths without error
|
||||
|
||||
```bash
|
||||
# (so you can import "path" without error)
|
||||
npm i -D @types/node
|
||||
```
|
||||
|
||||
```typescript
|
||||
import path from "path"
|
||||
import vue from "@vitejs/plugin-vue"
|
||||
|
|
@ -69,7 +124,7 @@ npx shadcn-vue@latest init
|
|||
|
||||
You will be asked a few questions to configure `components.json`:
|
||||
|
||||
```txt showLineNumbers
|
||||
```txt:line-numbers
|
||||
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
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import { Separator } from '@/lib/registry/new-york/ui/separator'
|
|||
|
||||
<template>
|
||||
<Card>
|
||||
<CardHeader class="grid grid-cols-[1fr_110px] items-start gap-4 space-y-0">
|
||||
<CardHeader class="grid grid-cols-[minmax(0,1fr)_110px] items-start gap-4 space-y-0">
|
||||
<div class="space-y-1">
|
||||
<CardTitle>shadcn/ui</CardTitle>
|
||||
<CardDescription>
|
||||
|
|
@ -39,7 +39,7 @@ import { Separator } from '@/lib/registry/new-york/ui/separator'
|
|||
<StarIcon class="mr-2 h-4 w-4" />
|
||||
Star
|
||||
</Button>
|
||||
<Separator orientation="vertical" class="h-[20px]" />
|
||||
<Separator orientation="vertical" class="h-5" />
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger as-child>
|
||||
<Button variant="secondary" class="px-2 shadow-none">
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ const date = ref({
|
|||
</span>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-auto p-0" :align="'end'" :avoid-collisions="true">
|
||||
<PopoverContent class="w-auto p-0" :align="'end'">
|
||||
<Calendar
|
||||
v-model.range="date"
|
||||
:columns="2"
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ async function onSubmit(values: any) {
|
|||
</FormField>
|
||||
|
||||
<FormField v-slot="{ componentField, value }" name="dob">
|
||||
<FormItem>
|
||||
<FormItem class="flex flex-col">
|
||||
<FormLabel>Date of birth</FormLabel>
|
||||
<Popover>
|
||||
<PopoverTrigger as-child>
|
||||
|
|
@ -123,7 +123,7 @@ async function onSubmit(values: any) {
|
|||
</FormField>
|
||||
|
||||
<FormField v-slot="{ value }" name="language">
|
||||
<FormItem>
|
||||
<FormItem class="flex flex-col">
|
||||
<FormLabel>Language</FormLabel>
|
||||
|
||||
<Popover v-model:open="open">
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ const onSubmit = handleSubmit((values) => {
|
|||
<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-20 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">
|
||||
|
|
@ -127,7 +127,7 @@ const onSubmit = handleSubmit((values) => {
|
|||
<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-20 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">
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ import CounterClockwiseClockIcon from '~icons/radix-icons/counter-clockwise-cloc
|
|||
<Separator />
|
||||
<Tabs default-value="complete" class="flex-1">
|
||||
<div class="container h-full py-6">
|
||||
<div class="grid h-full items-stretch gap-6 md:grid-cols-[1fr_200px]">
|
||||
<div class="grid h-full items-stretch gap-6 md:grid-cols-[minmax(0,1fr)_200px]">
|
||||
<div class="hidden flex-col space-y-4 sm:flex md:order-2">
|
||||
<div class="grid gap-2">
|
||||
<HoverCard :open-delay="200">
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ const columnVisibility = ref<VisibilityState>({})
|
|||
const rowSelection = ref({})
|
||||
|
||||
const table = useVueTable({
|
||||
data: props.data,
|
||||
columns: props.columns,
|
||||
get data() { return props.data },
|
||||
get columns() { return props.columns },
|
||||
state: {
|
||||
get sorting() { return sorting.value },
|
||||
get columnFilters() { return columnFilters.value },
|
||||
|
|
|
|||
|
|
@ -12,16 +12,16 @@ export const columns: ColumnDef<Task>[] = [
|
|||
{
|
||||
id: 'select',
|
||||
header: ({ table }) => h(Checkbox,
|
||||
{ 'checked': table.getIsAllPageRowsSelected(), 'onUpdate:checked': value => table.toggleAllPageRowsSelected(!!value), 'ariaLabel': 'Select all', 'class': 'translate-y-[2px]' }),
|
||||
{ 'checked': table.getIsAllPageRowsSelected(), 'onUpdate:checked': value => table.toggleAllPageRowsSelected(!!value), 'ariaLabel': 'Select all', 'class': 'translate-y-0.5' }),
|
||||
cell: ({ row }) => h(Checkbox,
|
||||
{ 'checked': row.getIsSelected(), 'onUpdate:checked': value => row.toggleSelected(!!value), 'ariaLabel': 'Select row', 'class': 'translate-y-[2px]' }),
|
||||
{ 'checked': row.getIsSelected(), 'onUpdate:checked': value => row.toggleSelected(!!value), 'ariaLabel': 'Select row', 'class': 'translate-y-0.5' }),
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
},
|
||||
{
|
||||
accessorKey: 'id',
|
||||
header: ({ column }) => h(DataTableColumnHeader, { column, title: 'Task' }),
|
||||
cell: ({ row }) => h('div', { class: 'w-[80px]' }, row.getValue('id')),
|
||||
cell: ({ row }) => h('div', { class: 'w-20' }, row.getValue('id')),
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import { Button } from '@/lib/registry/default/ui/button'
|
|||
|
||||
<template>
|
||||
<Button as-child>
|
||||
<NuxtLink to="/login">
|
||||
<a href="/login">
|
||||
Login
|
||||
</NuxtLink>
|
||||
</a>
|
||||
</Button>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ const notifications = [
|
|||
<div>
|
||||
<div
|
||||
v-for="(notification, index) in notifications" :key="index"
|
||||
class="mb-4 grid grid-cols-[25px_1fr] items-start pb-4 last:mb-0 last:pb-0"
|
||||
class="mb-4 grid grid-cols-[25px_minmax(0,1fr)] items-start pb-4 last:mb-0 last:pb-0"
|
||||
>
|
||||
<span class="flex h-2 w-2 translate-y-1 rounded-full bg-sky-500" />
|
||||
<div class="space-y-1">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
<script setup lang='ts'>
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/lib/registry/default/ui/card'
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/lib/registry/default/ui/card'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
|
|
@ -22,11 +29,11 @@ import { Button } from '@/lib/registry/default/ui/button'
|
|||
<form>
|
||||
<div class="grid items-center w-full gap-4">
|
||||
<div class="flex flex-col space-y-1.5">
|
||||
<Label html-for="name">Name</Label>
|
||||
<Label for="name">Name</Label>
|
||||
<Input id="name" placeholder="Name of your project" />
|
||||
</div>
|
||||
<div class="flex flex-col space-y-1.5">
|
||||
<Label html-for="framework">Framework</Label>
|
||||
<Label for="framework">Framework</Label>
|
||||
<Select>
|
||||
<SelectTrigger id="framework">
|
||||
<SelectValue placeholder="Select" />
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ const lineY = (d: Data) => d.revenue
|
|||
+20.1% from last month
|
||||
</p>
|
||||
|
||||
<div class="h-[80px]">
|
||||
<div class="h-20">
|
||||
<VisXYContainer
|
||||
height="80px"
|
||||
:data="data" :margin="{
|
||||
|
|
@ -74,7 +74,7 @@ const lineY = (d: Data) => d.revenue
|
|||
+54.8% from last month
|
||||
</p>
|
||||
|
||||
<div class="mt-4 h-[80px]">
|
||||
<div class="mt-4 h-20">
|
||||
<VisXYContainer
|
||||
height="80px" :data="data" :style="{
|
||||
'--theme-primary': `hsl(${
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {
|
|||
useVueTable,
|
||||
} from '@tanstack/vue-table'
|
||||
import { h, ref } from 'vue'
|
||||
import { CaretSortIcon, ChevronDownIcon } from '@radix-icons/vue'
|
||||
import { ChevronDown, ChevronsUpDown } from 'lucide-vue-next'
|
||||
import DropdownAction from '../DataTableDemoColumn.vue'
|
||||
|
||||
import { Button } from '@/lib/registry/default/ui/button'
|
||||
|
|
@ -104,7 +104,7 @@ const columns: ColumnDef<Payment>[] = [
|
|||
return h(Button, {
|
||||
variant: 'ghost',
|
||||
onClick: () => column.toggleSorting(column.getIsSorted() === 'asc'),
|
||||
}, ['Email', h(CaretSortIcon, { class: 'ml-2 h-4 w-4' })])
|
||||
}, ['Email', h(ChevronsUpDown, { class: 'ml-2 h-4 w-4' })])
|
||||
},
|
||||
cell: ({ row }) => h('div', { class: 'lowercase' }, row.getValue('email')),
|
||||
},
|
||||
|
|
@ -179,7 +179,7 @@ const table = useVueTable({
|
|||
<DropdownMenu>
|
||||
<DropdownMenuTrigger as-child>
|
||||
<Button variant="outline" class="ml-auto">
|
||||
Columns <ChevronDownIcon class="ml-2 h-4 w-4" />
|
||||
Columns <ChevronDown class="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
|
|
|
|||
51
apps/www/src/lib/registry/default/example/CarouselApi.vue
Normal file
51
apps/www/src/lib/registry/default/example/CarouselApi.vue
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { watchOnce } from '@vueuse/core'
|
||||
import type { CarouselApi } from '@/lib/registry/default/ui/carousel'
|
||||
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from '@/lib/registry/default/ui/carousel'
|
||||
import { Card, CardContent } from '@/lib/registry/default/ui/card'
|
||||
|
||||
const api = ref<CarouselApi>()
|
||||
const totalCount = ref(0)
|
||||
const current = ref(0)
|
||||
|
||||
function setApi(val: CarouselApi) {
|
||||
api.value = val
|
||||
}
|
||||
|
||||
watchOnce(api, (api) => {
|
||||
if (!api)
|
||||
return
|
||||
|
||||
totalCount.value = api.scrollSnapList().length
|
||||
current.value = api.selectedScrollSnap() + 1
|
||||
|
||||
api.on('select', () => {
|
||||
current.value = api.selectedScrollSnap() + 1
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full sm:w-auto">
|
||||
<Carousel class="relative w-full max-w-xs" @init-api="setApi">
|
||||
<CarouselContent>
|
||||
<CarouselItem v-for="(_, index) in 5" :key="index">
|
||||
<div class="p-1">
|
||||
<Card>
|
||||
<CardContent class="flex aspect-square items-center justify-center p-6">
|
||||
<span class="text-4xl font-semibold">{{ index + 1 }}</span>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</CarouselItem>
|
||||
</CarouselContent>
|
||||
<CarouselPrevious />
|
||||
<CarouselNext />
|
||||
</Carousel>
|
||||
|
||||
<div class="py-2 text-center text-sm text-muted-foreground">
|
||||
Slide {{ current }} of {{ totalCount }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
22
apps/www/src/lib/registry/default/example/CarouselDemo.vue
Normal file
22
apps/www/src/lib/registry/default/example/CarouselDemo.vue
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<script setup lang="ts">
|
||||
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from '@/lib/registry/default/ui/carousel'
|
||||
import { Card, CardContent } from '@/lib/registry/default/ui/card'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Carousel class="relative w-full max-w-xs">
|
||||
<CarouselContent>
|
||||
<CarouselItem v-for="(_, index) in 5" :key="index">
|
||||
<div class="p-1">
|
||||
<Card>
|
||||
<CardContent class="flex aspect-square items-center justify-center p-6">
|
||||
<span class="text-4xl font-semibold">{{ index + 1 }}</span>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</CarouselItem>
|
||||
</CarouselContent>
|
||||
<CarouselPrevious />
|
||||
<CarouselNext />
|
||||
</Carousel>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<script setup lang="ts">
|
||||
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from '@/lib/registry/default/ui/carousel'
|
||||
import { Card, CardContent } from '@/lib/registry/default/ui/card'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Carousel
|
||||
orientation="vertical"
|
||||
class="relative w-full max-w-xsw-full max-w-xs"
|
||||
:opts="{
|
||||
align: 'start',
|
||||
}"
|
||||
>
|
||||
<CarouselContent class="-mt-1 h-[200px]">
|
||||
<CarouselItem v-for="(_, index) in 5" :key="index" class="p-1 md:basis-1/2">
|
||||
<div class="p-1">
|
||||
<Card>
|
||||
<CardContent class="flex items-center justify-center p-6">
|
||||
<span class="text-3xl font-semibold">{{ index + 1 }}</span>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</CarouselItem>
|
||||
</CarouselContent>
|
||||
<CarouselPrevious />
|
||||
<CarouselNext />
|
||||
</Carousel>
|
||||
</template>
|
||||
34
apps/www/src/lib/registry/default/example/CarouselPlugin.vue
Normal file
34
apps/www/src/lib/registry/default/example/CarouselPlugin.vue
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<script setup lang="ts">
|
||||
import Autoplay from 'embla-carousel-autoplay'
|
||||
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from '@/lib/registry/default/ui/carousel'
|
||||
import { Card, CardContent } from '@/lib/registry/default/ui/card'
|
||||
|
||||
const plugin = Autoplay({
|
||||
delay: 2000,
|
||||
stopOnMouseEnter: true,
|
||||
stopOnInteraction: false,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Carousel
|
||||
class="relative w-full max-w-xs"
|
||||
:plugins="[plugin]"
|
||||
@mouseenter="plugin.stop"
|
||||
@mouseleave="[plugin.reset(), plugin.play(), console.log('Runing')];"
|
||||
>
|
||||
<CarouselContent>
|
||||
<CarouselItem v-for="(_, index) in 5" :key="index">
|
||||
<div class="p-1">
|
||||
<Card>
|
||||
<CardContent class="flex aspect-square items-center justify-center p-6">
|
||||
<span class="text-4xl font-semibold">{{ index + 1 }}</span>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</CarouselItem>
|
||||
</CarouselContent>
|
||||
<CarouselPrevious />
|
||||
<CarouselNext />
|
||||
</Carousel>
|
||||
</template>
|
||||
27
apps/www/src/lib/registry/default/example/CarouselSize.vue
Normal file
27
apps/www/src/lib/registry/default/example/CarouselSize.vue
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<script setup lang="ts">
|
||||
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from '@/lib/registry/default/ui/carousel'
|
||||
import { Card, CardContent } from '@/lib/registry/default/ui/card'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Carousel
|
||||
class="relative w-full max-w-sm"
|
||||
:opts="{
|
||||
align: 'start',
|
||||
}"
|
||||
>
|
||||
<CarouselContent>
|
||||
<CarouselItem v-for="(_, index) in 5" :key="index" class="md:basis-1/2 lg:basis-1/3">
|
||||
<div class="p-1">
|
||||
<Card>
|
||||
<CardContent class="flex aspect-square items-center justify-center p-6">
|
||||
<span class="text-3xl font-semibold">{{ index + 1 }}</span>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</CarouselItem>
|
||||
</CarouselContent>
|
||||
<CarouselPrevious />
|
||||
<CarouselNext />
|
||||
</Carousel>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<script setup lang="ts">
|
||||
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from '@/lib/registry/default/ui/carousel'
|
||||
import { Card, CardContent } from '@/lib/registry/default/ui/card'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Carousel
|
||||
class="relative w-full max-w-sm"
|
||||
:opts="{
|
||||
align: 'start',
|
||||
}"
|
||||
>
|
||||
<CarouselContent class="-ml-1">
|
||||
<CarouselItem v-for="(_, index) in 5" :key="index" class="pl-1 md:basis-1/2 lg:basis-1/3">
|
||||
<div class="p-1">
|
||||
<Card>
|
||||
<CardContent class="flex aspect-square items-center justify-center p-6">
|
||||
<span class="text-2xl font-semibold">{{ index + 1 }}</span>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</CarouselItem>
|
||||
</CarouselContent>
|
||||
<CarouselPrevious />
|
||||
<CarouselNext />
|
||||
</Carousel>
|
||||
</template>
|
||||
|
|
@ -77,16 +77,12 @@ const onSubmit = handleSubmit((values) => {
|
|||
</FormDescription>
|
||||
</div>
|
||||
|
||||
<FormField v-for="item in items" v-slot="{ value, handleChange }" :key="item.id" name="items">
|
||||
<FormItem :key="item.id" class="flex flex-row items-start space-x-3 space-y-0">
|
||||
<FormField v-for="item in items" v-slot="{ value, handleChange }" :key="item.id" type="checkbox" :value="item.id" :unchecked-value="false" name="items">
|
||||
<FormItem class="flex flex-row items-start space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
:checked="value.includes(item.id)"
|
||||
@update:checked="(checked) => {
|
||||
if (Array.isArray(value)) {
|
||||
handleChange(checked ? [...value, item.id] : value.filter(id => id !== item.id))
|
||||
}
|
||||
}"
|
||||
@update:checked="handleChange"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel class="font-normal">
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ const onSubmit = handleSubmit((values) => {
|
|||
|
||||
<template>
|
||||
<form class="space-y-6" @submit="onSubmit">
|
||||
<FormField v-slot="{ value, handleChange }" name="mobile">
|
||||
<FormField v-slot="{ value, handleChange }" type="checkbox" name="mobile">
|
||||
<FormItem class="flex flex-row items-start gap-x-3 space-y-0 rounded-md border p-4">
|
||||
<FormControl>
|
||||
<Checkbox :checked="value" @update:checked="handleChange" />
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
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 {
|
||||
|
|
@ -27,9 +27,9 @@ const frameworks = [
|
|||
]
|
||||
|
||||
const open = ref(false)
|
||||
const value = ref<typeof frameworks[number]>()
|
||||
const value = ref<string>('')
|
||||
|
||||
const filterFunction = (list: typeof frameworks, search: string) => list.filter(i => i.value.toLowerCase().includes(search.toLowerCase()))
|
||||
// const filterFunction = (list: typeof frameworks, search: string) => list.filter(i => i.value.toLowerCase().includes(search.toLowerCase()))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -41,33 +41,36 @@ const filterFunction = (list: typeof frameworks, search: string) => list.filter(
|
|||
:aria-expanded="open"
|
||||
class="w-[200px] justify-between"
|
||||
>
|
||||
{{ value ? value.label : 'Select framework...' }}
|
||||
{{ 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 :filter-function="filterFunction">
|
||||
<CommandInput placeholder="Search framework..." />
|
||||
<Command>
|
||||
<CommandInput class="h-9" placeholder="Search framework..." />
|
||||
<CommandEmpty>No framework found.</CommandEmpty>
|
||||
<CommandList>
|
||||
<CommandGroup>
|
||||
<CommandItem
|
||||
v-for="framework in frameworks"
|
||||
:key="framework.value"
|
||||
:value="framework"
|
||||
:value="framework.value"
|
||||
@select="(ev) => {
|
||||
value = ev.detail.value
|
||||
console.log(ev)
|
||||
if (typeof ev.detail.value === 'string') {
|
||||
value = ev.detail.value
|
||||
}
|
||||
open = false
|
||||
}"
|
||||
>
|
||||
{{ framework.label }}
|
||||
<Check
|
||||
:class="cn(
|
||||
'mr-2 h-4 w-4',
|
||||
value?.value === framework.value ? 'opacity-100' : 'opacity-0',
|
||||
'ml-auto h-4 w-4',
|
||||
value === framework.value ? 'opacity-100' : 'opacity-0',
|
||||
)"
|
||||
/>
|
||||
{{ framework.label }}
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
|
|
|
|||
|
|
@ -14,17 +14,22 @@ import {
|
|||
|
||||
const open = ref(false)
|
||||
|
||||
const keys = useMagicKeys()
|
||||
const CmdJ = keys['Cmd+J']
|
||||
const { Meta_J, Ctrl_J } = useMagicKeys({
|
||||
passive: false,
|
||||
onEventFired(e) {
|
||||
if (e.key === 'j' && (e.metaKey || e.ctrlKey))
|
||||
e.preventDefault()
|
||||
},
|
||||
})
|
||||
|
||||
watch([Meta_J, Ctrl_J], (v) => {
|
||||
if (v[0] || v[1])
|
||||
handleOpenChange()
|
||||
})
|
||||
|
||||
function handleOpenChange() {
|
||||
open.value = !open.value
|
||||
}
|
||||
|
||||
watch(CmdJ, (v) => {
|
||||
if (v)
|
||||
handleOpenChange()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -37,7 +42,7 @@ watch(CmdJ, (v) => {
|
|||
<span class="text-xs">⌘</span>J
|
||||
</kbd>
|
||||
</p>
|
||||
<CommandDialog :open="open" :on-open-change="handleOpenChange">
|
||||
<CommandDialog v-model:open="open">
|
||||
<CommandInput placeholder="Type a command or search..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>No results found.</CommandEmpty>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,273 @@
|
|||
<script setup lang="ts">
|
||||
import type {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
SortingState,
|
||||
VisibilityState,
|
||||
} from '@tanstack/vue-table'
|
||||
import {
|
||||
FlexRender,
|
||||
createColumnHelper,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
|
||||
useVueTable,
|
||||
} from '@tanstack/vue-table'
|
||||
import { ArrowUpDown, ChevronDown } from 'lucide-vue-next'
|
||||
|
||||
import { h, ref } from 'vue'
|
||||
import DropdownAction from './DataTableDemoColumn.vue'
|
||||
import { Button } from '@/lib/registry/default/ui/button'
|
||||
import { Checkbox } from '@/lib/registry/default/ui/checkbox'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/lib/registry/default/ui/dropdown-menu'
|
||||
import { Input } from '@/lib/registry/default/ui/input'
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/lib/registry/default/ui/table'
|
||||
import { cn, valueUpdater } from '@/lib/utils'
|
||||
|
||||
export interface Payment {
|
||||
id: string
|
||||
amount: number
|
||||
status: 'pending' | 'processing' | 'success' | 'failed'
|
||||
email: string
|
||||
}
|
||||
|
||||
const data: Payment[] = [
|
||||
{
|
||||
id: 'm5gr84i9',
|
||||
amount: 316,
|
||||
status: 'success',
|
||||
email: 'ken99@yahoo.com',
|
||||
},
|
||||
{
|
||||
id: '3u1reuv4',
|
||||
amount: 242,
|
||||
status: 'success',
|
||||
email: 'Abe45@gmail.com',
|
||||
},
|
||||
{
|
||||
id: 'derv1ws0',
|
||||
amount: 837,
|
||||
status: 'processing',
|
||||
email: 'Monserrat44@gmail.com',
|
||||
},
|
||||
{
|
||||
id: '5kma53ae',
|
||||
amount: 874,
|
||||
status: 'success',
|
||||
email: 'Silas22@gmail.com',
|
||||
},
|
||||
{
|
||||
id: 'bhqecj4p',
|
||||
amount: 721,
|
||||
status: 'failed',
|
||||
email: 'carmella@hotmail.com',
|
||||
},
|
||||
]
|
||||
|
||||
const columnHelper = createColumnHelper<Payment>()
|
||||
|
||||
const columns = [
|
||||
columnHelper.display({
|
||||
id: 'select',
|
||||
header: ({ table }) => h(Checkbox, {
|
||||
'checked': table.getIsAllPageRowsSelected(),
|
||||
'onUpdate:checked': value => table.toggleAllPageRowsSelected(!!value),
|
||||
'ariaLabel': 'Select all',
|
||||
}),
|
||||
cell: ({ row, column }) => {
|
||||
return h(Checkbox, {
|
||||
'checked': row.getIsSelected(),
|
||||
'onUpdate:checked': value => row.toggleSelected(!!value),
|
||||
'ariaLabel': 'Select row',
|
||||
})
|
||||
},
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
}),
|
||||
columnHelper.accessor('status', {
|
||||
enablePinning: true,
|
||||
header: 'Status',
|
||||
cell: ({ row }) => h('div', { class: 'capitalize' }, row.getValue('status')),
|
||||
}),
|
||||
columnHelper.accessor('email', {
|
||||
header: ({ column }) => {
|
||||
return h(Button, {
|
||||
variant: 'ghost',
|
||||
onClick: () => column.toggleSorting(column.getIsSorted() === 'asc'),
|
||||
}, () => ['Email', h(ArrowUpDown, { class: 'ml-2 h-4 w-4' })])
|
||||
},
|
||||
cell: ({ row }) => h('div', { class: 'lowercase' }, row.getValue('email')),
|
||||
}),
|
||||
columnHelper.accessor('amount', {
|
||||
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
||||
cell: ({ row }) => {
|
||||
const amount = Number.parseFloat(row.getValue('amount'))
|
||||
|
||||
// Format the amount as a dollar amount
|
||||
const formatted = new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
}).format(amount)
|
||||
|
||||
return h('div', { class: 'text-right font-medium' }, formatted)
|
||||
},
|
||||
}),
|
||||
columnHelper.display({
|
||||
id: 'actions',
|
||||
enableHiding: false,
|
||||
cell: ({ row }) => {
|
||||
const payment = row.original
|
||||
|
||||
return h('div', { class: 'relative' }, h(DropdownAction, {
|
||||
payment,
|
||||
}))
|
||||
},
|
||||
}),
|
||||
]
|
||||
|
||||
const sorting = ref<SortingState>([])
|
||||
const columnFilters = ref<ColumnFiltersState>([])
|
||||
const columnVisibility = ref<VisibilityState>({})
|
||||
const rowSelection = ref({})
|
||||
|
||||
const table = useVueTable({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onSortingChange: updaterOrValue => valueUpdater(updaterOrValue, sorting),
|
||||
onColumnFiltersChange: updaterOrValue => valueUpdater(updaterOrValue, columnFilters),
|
||||
onColumnVisibilityChange: updaterOrValue => valueUpdater(updaterOrValue, columnVisibility),
|
||||
onRowSelectionChange: updaterOrValue => valueUpdater(updaterOrValue, rowSelection),
|
||||
state: {
|
||||
get sorting() { return sorting.value },
|
||||
get columnFilters() { return columnFilters.value },
|
||||
get columnVisibility() { return columnVisibility.value },
|
||||
get rowSelection() { return rowSelection.value },
|
||||
columnPinning: {
|
||||
left: ['status'],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const getState = table.getState()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<div class="flex gap-2 items-center py-4">
|
||||
<Input
|
||||
class="max-w-sm"
|
||||
placeholder="Filter emails..."
|
||||
:model-value="table.getColumn('email')?.getFilterValue() as string"
|
||||
@update:model-value=" table.getColumn('email')?.setFilterValue($event)"
|
||||
/>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger as-child>
|
||||
<Button variant="outline" class="ml-auto">
|
||||
Columns <ChevronDown class="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuCheckboxItem
|
||||
v-for="column in table.getAllColumns().filter((column) => column.getCanHide())"
|
||||
:key="column.id"
|
||||
class="capitalize"
|
||||
:checked="column.getIsVisible()"
|
||||
@update:checked="(value) => {
|
||||
column.toggleVisibility(!!value)
|
||||
}"
|
||||
>
|
||||
{{ column.id }}
|
||||
</DropdownMenuCheckboxItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<div class="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow v-for="headerGroup in table.getHeaderGroups()" :key="headerGroup.id">
|
||||
<TableHead
|
||||
v-for="header in headerGroup.headers" :key="header.id" :data-pinned="header.column.getIsPinned()"
|
||||
:class="cn(
|
||||
{ 'sticky bg-background/95': header.column.getIsPinned() },
|
||||
header.column.getIsPinned() === 'left' ? 'left-0' : 'right-0',
|
||||
)"
|
||||
>
|
||||
<FlexRender v-if="!header.isPlaceholder" :render="header.column.columnDef.header" :props="header.getContext()" />
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<template v-if="table.getRowModel().rows?.length">
|
||||
<TableRow
|
||||
v-for="row in table.getRowModel().rows"
|
||||
:key="row.id"
|
||||
:data-state="row.getIsSelected() && 'selected'"
|
||||
>
|
||||
<TableCell
|
||||
v-for="cell in row.getVisibleCells()" :key="cell.id" :data-pinned="cell.column.getIsPinned()"
|
||||
:class="cn(
|
||||
{ 'sticky bg-background/95': cell.column.getIsPinned() },
|
||||
cell.column.getIsPinned() === 'left' ? 'left-0' : 'right-0',
|
||||
)"
|
||||
>
|
||||
<FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</template>
|
||||
|
||||
<TableRow v-else>
|
||||
<TableCell
|
||||
col-span="{columns.length}"
|
||||
class="h-24 text-center"
|
||||
>
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-end space-x-2 py-4">
|
||||
<div class="flex-1 text-sm text-muted-foreground">
|
||||
{{ table.getFilteredSelectedRowModel().rows.length }} of
|
||||
{{ table.getFilteredRowModel().rows.length }} row(s) selected.
|
||||
</div>
|
||||
<div class="space-x-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
:disabled="!table.getCanPreviousPage()"
|
||||
@click="table.previousPage()"
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
:disabled="!table.getCanNextPage()"
|
||||
@click="table.nextPage()"
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -40,7 +40,7 @@ const date = ref({
|
|||
</span>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-auto p-0" align="start" :avoid-collisions="true">
|
||||
<PopoverContent class="w-auto p-0" align="start">
|
||||
<Calendar
|
||||
v-model.range="date"
|
||||
:columns="2"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
<script setup lang="ts">
|
||||
import { format } from 'date-fns'
|
||||
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 {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from '@/lib/registry/default/ui/popover'
|
||||
|
||||
const date = ref<Date>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Popover>
|
||||
<PopoverTrigger as-child>
|
||||
<Button
|
||||
:variant="'outline'"
|
||||
:class="cn(
|
||||
'w-[280px] justify-start text-left font-normal',
|
||||
!date && 'text-muted-foreground',
|
||||
)"
|
||||
>
|
||||
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||
<span>{{ date ? format(date, 'PPP - hh:mm') : "Pick a date" }}</span>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-auto p-0">
|
||||
<Calendar v-model="date" mode="datetime" />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</template>
|
||||
|
|
@ -31,7 +31,7 @@ import { Label } from '@/lib/registry/default/ui/label'
|
|||
</DialogHeader>
|
||||
<div class="flex items-center space-x-2">
|
||||
<div class="grid flex-1 gap-2">
|
||||
<Label html-for="link" class="sr-only">
|
||||
<Label for="link" class="sr-only">
|
||||
Link
|
||||
</Label>
|
||||
<Input
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ const components: { title: string; href: string; description: string }[] = [
|
|||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger>Getting started</NavigationMenuTrigger>
|
||||
<NavigationMenuContent>
|
||||
<ul class="grid gap-3 p-6 md:w-[400px] lg:w-[500px] lg:grid-cols-[.75fr_1fr]">
|
||||
<ul class="grid gap-3 p-6 md:w-[400px] lg:w-[500px] lg:grid-cols-[minmax(0,.75fr)_minmax(0,1fr)]">
|
||||
<li class="row-span-3">
|
||||
<NavigationMenuLink as-child>
|
||||
<a
|
||||
|
|
|
|||
28
apps/www/src/lib/registry/default/example/PinInputDemo.vue
Normal file
28
apps/www/src/lib/registry/default/example/PinInputDemo.vue
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import {
|
||||
PinInput,
|
||||
PinInputInput,
|
||||
} from '@/lib/registry/default/ui/pin-input'
|
||||
|
||||
const value = ref<string[]>([])
|
||||
const handleComplete = (e: string[]) => alert(e.join(''))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<PinInput
|
||||
id="pin-input"
|
||||
v-model="value"
|
||||
placeholder="○"
|
||||
class="flex gap-2 items-center mt-1"
|
||||
@complete="handleComplete"
|
||||
>
|
||||
<PinInputInput
|
||||
v-for="(id, index) in 5"
|
||||
:key="id"
|
||||
:index="index"
|
||||
/>
|
||||
</PinInput>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
<script setup lang="ts">
|
||||
import { h } from 'vue'
|
||||
import { useForm } from 'vee-validate'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import * as z from 'zod'
|
||||
import {
|
||||
PinInput,
|
||||
PinInputInput,
|
||||
} from '@/lib/registry/new-york/ui/pin-input'
|
||||
import { Button } from '@/lib/registry/default/ui/button'
|
||||
import {
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/lib/registry/default/ui/form'
|
||||
import { toast } from '@/lib/registry/default/ui/toast'
|
||||
|
||||
const formSchema = toTypedSchema(z.object({
|
||||
pin: z.array(z.coerce.string()).length(5, { message: 'Invalid input' }),
|
||||
}))
|
||||
|
||||
const { handleSubmit, setValues } = useForm({
|
||||
validationSchema: formSchema,
|
||||
initialValues: {
|
||||
pin: [],
|
||||
},
|
||||
})
|
||||
|
||||
const onSubmit = handleSubmit(({ pin }) => {
|
||||
toast({
|
||||
title: 'You submitted the following values:',
|
||||
description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(pin.join(''), null, 2))),
|
||||
})
|
||||
})
|
||||
|
||||
const handleComplete = (e: string[]) => console.log(e.join(''))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form class="w-2/3 space-y-6 mx-auto" @submit="onSubmit">
|
||||
<FormField v-slot="{ componentField }" name="pin">
|
||||
<FormItem>
|
||||
<FormLabel>OTP</FormLabel>
|
||||
<FormControl>
|
||||
<PinInput
|
||||
id="pin-input"
|
||||
placeholder="○"
|
||||
class="flex gap-2 items-center mt-1"
|
||||
otp
|
||||
type="number"
|
||||
:name="componentField.name"
|
||||
@complete="handleComplete"
|
||||
@update:model-value="(arrStr) => {
|
||||
setValues({
|
||||
pin: arrStr.filter(Boolean),
|
||||
})
|
||||
}"
|
||||
>
|
||||
<PinInputInput
|
||||
v-for="(id, index) in 5"
|
||||
:key="id"
|
||||
:index="index"
|
||||
/>
|
||||
</PinInput>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
Allows users to input a sequence of one-character alphanumeric inputs.
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<Button>Submit</Button>
|
||||
</form>
|
||||
</template>
|
||||
|
|
@ -11,5 +11,5 @@ watchEffect((cleanupFn) => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<Progress v-model="progress" class="w-[60%]" />
|
||||
<Progress v-model="progress" class="w-3/5" />
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ const onSubmit = handleSubmit((values) => {
|
|||
|
||||
<template>
|
||||
<form class="w-2/3 space-y-6" @submit="onSubmit">
|
||||
<FormField v-slot="{ componentField }" name="type">
|
||||
<FormField v-slot="{ componentField }" type="radio" name="type">
|
||||
<FormItem class="space-y-3">
|
||||
<FormLabel>Notify me about...</FormLabel>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
<script setup lang="ts">
|
||||
import { addDays, format } from 'date-fns'
|
||||
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 {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from '@/lib/registry/default/ui/popover'
|
||||
|
||||
const date = ref({
|
||||
start: new Date(2022, 0, 20),
|
||||
end: addDays(new Date(2022, 0, 20), 20),
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('grid gap-2', $attrs.class ?? '')">
|
||||
<Popover>
|
||||
<PopoverTrigger as-child>
|
||||
<Button
|
||||
id="date"
|
||||
:variant="'outline'"
|
||||
:class="cn(
|
||||
'w-[300px] justify-start text-left font-normal',
|
||||
!date && 'text-muted-foreground',
|
||||
)"
|
||||
>
|
||||
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||
|
||||
<span>
|
||||
{{ date.start ? (
|
||||
date.end ? `${format(date.start, 'LLL dd, y')} - ${format(date.end, 'LLL dd, y')}`
|
||||
: format(date.start, 'LLL dd, y')
|
||||
) : 'Pick a date' }}
|
||||
</span>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-auto p-0" align="start">
|
||||
<Calendar
|
||||
v-model.range="date"
|
||||
mode="date"
|
||||
:columns="2"
|
||||
>
|
||||
<template #footer>
|
||||
<div class="w-full px-3 pb-3">
|
||||
Entry time
|
||||
<Calendar
|
||||
v-model="date.start"
|
||||
mode="time"
|
||||
hide-time-header
|
||||
/>
|
||||
Exit time
|
||||
<Calendar
|
||||
v-model="date.end"
|
||||
mode="time"
|
||||
hide-time-header
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Calendar>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -11,6 +11,6 @@ const modelValue = ref([50])
|
|||
v-model="modelValue"
|
||||
:max="100"
|
||||
:step="1"
|
||||
:class="cn('w-[60%]', $attrs.class ?? '')"
|
||||
:class="cn('w-3/5', $attrs.class ?? '')"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
|||
20
apps/www/src/lib/registry/default/example/SonnerDemo.vue
Normal file
20
apps/www/src/lib/registry/default/example/SonnerDemo.vue
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<script setup lang="ts">
|
||||
import { toast } from 'vue-sonner'
|
||||
import { Button } from '@/lib/registry/default/ui/button'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Button
|
||||
variant="outline" @click="() => {
|
||||
toast('Event has been created', {
|
||||
description: 'Sunday, December 03, 2023 at 9:00 AM',
|
||||
action: {
|
||||
label: 'Undo',
|
||||
onClick: () => console.log('Undo'),
|
||||
},
|
||||
})
|
||||
}"
|
||||
>
|
||||
Add to calander
|
||||
</Button>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<script setup lang="ts">
|
||||
import { Bold, Italic, Underline } from 'lucide-vue-next'
|
||||
|
||||
import { ToggleGroup, ToggleGroupItem } from '@/lib/registry/default/ui/toggle-group'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ToggleGroup type="multiple">
|
||||
<ToggleGroupItem value="bold" aria-label="Toggle bold">
|
||||
<Bold class="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="italic" aria-label="Toggle italic">
|
||||
<Italic class="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="underline" aria-label="Toggle underline">
|
||||
<Underline class="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<script setup lang="ts">
|
||||
import { Bold, Italic, Underline } from 'lucide-vue-next'
|
||||
|
||||
import { ToggleGroup, ToggleGroupItem } from '@/lib/registry/default/ui/toggle-group'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ToggleGroup type="multiple" disabled>
|
||||
<ToggleGroupItem value="bold" aria-label="Toggle bold">
|
||||
<Bold class="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="italic" aria-label="Toggle italic">
|
||||
<Italic class="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="underline" aria-label="Toggle underline">
|
||||
<Underline class="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<script setup lang="ts">
|
||||
import { Bold, Italic, Underline } from 'lucide-vue-next'
|
||||
|
||||
import { ToggleGroup, ToggleGroupItem } from '@/lib/registry/default/ui/toggle-group'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ToggleGroup type="multiple" size="lg">
|
||||
<ToggleGroupItem value="bold" aria-label="Toggle bold">
|
||||
<Bold class="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="italic" aria-label="Toggle italic">
|
||||
<Italic class="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="underline" aria-label="Toggle underline">
|
||||
<Underline class="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<script setup lang="ts">
|
||||
import { Bold, Italic, Underline } from 'lucide-vue-next'
|
||||
|
||||
import { ToggleGroup, ToggleGroupItem } from '@/lib/registry/default/ui/toggle-group'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ToggleGroup type="multiple" variant="outline">
|
||||
<ToggleGroupItem value="bold" aria-label="Toggle bold">
|
||||
<Bold class="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="italic" aria-label="Toggle italic">
|
||||
<Italic class="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="underline" aria-label="Toggle underline">
|
||||
<Underline class="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<script setup lang="ts">
|
||||
import { Bold, Italic, Underline } from 'lucide-vue-next'
|
||||
|
||||
import { ToggleGroup, ToggleGroupItem } from '@/lib/registry/default/ui/toggle-group'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ToggleGroup type="single">
|
||||
<ToggleGroupItem value="bold" aria-label="Toggle bold">
|
||||
<Bold class="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="italic" aria-label="Toggle italic">
|
||||
<Italic class="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="underline" aria-label="Toggle underline">
|
||||
<Underline class="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<script setup lang="ts">
|
||||
import { Bold, Italic, Underline } from 'lucide-vue-next'
|
||||
|
||||
import { ToggleGroup, ToggleGroupItem } from '@/lib/registry/default/ui/toggle-group'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ToggleGroup type="multiple" size="sm">
|
||||
<ToggleGroupItem value="bold" aria-label="Toggle bold">
|
||||
<Bold class="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="italic" aria-label="Toggle italic">
|
||||
<Italic class="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="underline" aria-label="Toggle underline">
|
||||
<Underline class="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
</template>
|
||||
|
|
@ -1 +0,0 @@
|
|||
export { default as AccordionDemo } from './AccordionDemo.vue'
|
||||
|
|
@ -3,15 +3,17 @@ import {
|
|||
AccordionRoot,
|
||||
type AccordionRootEmits,
|
||||
type AccordionRootProps,
|
||||
useEmitAsProps,
|
||||
useForwardPropsEmits,
|
||||
} from 'radix-vue'
|
||||
|
||||
const props = defineProps<AccordionRootProps>()
|
||||
const emits = defineEmits<AccordionRootEmits>()
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AccordionRoot v-bind="{ ...props, ...useEmitAsProps(emits) }">
|
||||
<AccordionRoot v-bind="forwarded">
|
||||
<slot />
|
||||
</AccordionRoot>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,21 +1,23 @@
|
|||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import { AccordionContent, type AccordionContentProps } from 'radix-vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<AccordionContentProps & { class?: string }>()
|
||||
const props = defineProps<AccordionContentProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AccordionContent
|
||||
v-bind="props"
|
||||
:class="
|
||||
cn(
|
||||
'overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
v-bind="delegatedProps"
|
||||
class="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
||||
>
|
||||
<div class="pb-4 pt-0">
|
||||
<div :class="cn('pb-4 pt-0', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</AccordionContent>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,23 @@
|
|||
<script setup lang="ts">
|
||||
import { AccordionItem, type AccordionItemProps } from 'radix-vue'
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import { AccordionItem, type AccordionItemProps, useForwardProps } from 'radix-vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<AccordionItemProps & { class?: string }>()
|
||||
const props = defineProps<AccordionItemProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AccordionItem
|
||||
v-bind="props"
|
||||
:class="cn('border-b', props.class ?? '')"
|
||||
v-bind="forwardedProps"
|
||||
:class="cn('border-b', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</AccordionItem>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import {
|
||||
AccordionHeader,
|
||||
AccordionTrigger,
|
||||
|
|
@ -7,13 +8,19 @@ import {
|
|||
import { ChevronDown } from 'lucide-vue-next'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<AccordionTriggerProps & { class?: string }>()
|
||||
const props = defineProps<AccordionTriggerProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AccordionHeader class="flex" as="div">
|
||||
<AccordionHeader class="flex">
|
||||
<AccordionTrigger
|
||||
v-bind="props"
|
||||
v-bind="delegatedProps"
|
||||
:class="
|
||||
cn(
|
||||
'flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180',
|
||||
|
|
@ -22,9 +29,11 @@ const props = defineProps<AccordionTriggerProps & { class?: string }>()
|
|||
"
|
||||
>
|
||||
<slot />
|
||||
<ChevronDown
|
||||
class="h-4 w-4 shrink-0 transition-transform duration-200"
|
||||
/>
|
||||
<slot name="icon">
|
||||
<ChevronDown
|
||||
class="h-4 w-4 shrink-0 transition-transform duration-200"
|
||||
/>
|
||||
</slot>
|
||||
</AccordionTrigger>
|
||||
</AccordionHeader>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,20 @@
|
|||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import { AlertDialogAction, type AlertDialogActionProps } from 'radix-vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { buttonVariants } from '@/lib/registry/default/ui/button'
|
||||
|
||||
const props = defineProps<AlertDialogActionProps>()
|
||||
const props = defineProps<AlertDialogActionProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogAction v-bind="props" :class="cn(buttonVariants(), $attrs.class ?? '')">
|
||||
<AlertDialogAction v-bind="delegatedProps" :class="cn(buttonVariants(), props.class)">
|
||||
<slot />
|
||||
</AlertDialogAction>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,20 @@
|
|||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import { AlertDialogCancel, type AlertDialogCancelProps } from 'radix-vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { buttonVariants } from '@/lib/registry/default/ui/button'
|
||||
|
||||
const props = defineProps<AlertDialogCancelProps>()
|
||||
const props = defineProps<AlertDialogCancelProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogCancel v-bind="props" :class="cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0', $attrs.class ?? '')">
|
||||
<AlertDialogCancel v-bind="delegatedProps" :class="cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0', props.class)">
|
||||
<slot />
|
||||
</AlertDialogCancel>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,30 +1,37 @@
|
|||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import {
|
||||
AlertDialogContent,
|
||||
type AlertDialogContentEmits,
|
||||
type AlertDialogContentProps,
|
||||
AlertDialogOverlay,
|
||||
AlertDialogPortal,
|
||||
useEmitAsProps,
|
||||
useForwardPropsEmits,
|
||||
} from 'radix-vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<AlertDialogContentProps & { class?: string }>()
|
||||
const props = defineProps<AlertDialogContentProps & { class?: HTMLAttributes['class'] }>()
|
||||
const emits = defineEmits<AlertDialogContentEmits>()
|
||||
|
||||
const emitsAsProps = useEmitAsProps(emits)
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogPortal>
|
||||
<AlertDialogOverlay
|
||||
class="fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
|
||||
class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
|
||||
/>
|
||||
<AlertDialogContent
|
||||
v-bind="{ ...props, ...emitsAsProps }"
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full',
|
||||
'fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
|
|
|
|||
|
|
@ -1,17 +1,24 @@
|
|||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import {
|
||||
AlertDialogDescription,
|
||||
type AlertDialogDescriptionProps,
|
||||
} from 'radix-vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<AlertDialogDescriptionProps & { class?: string }>()
|
||||
const props = defineProps<AlertDialogDescriptionProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogDescription
|
||||
:class="cn('text-muted-foreground text-sm', props.class)"
|
||||
:as-child="props.asChild"
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('text-sm text-muted-foreground', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</AlertDialogDescription>
|
||||
|
|
|
|||
|
|
@ -1,19 +1,17 @@
|
|||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps({
|
||||
class: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="
|
||||
cn(
|
||||
'flex flex-col space-y-2 sm:space-y-0 mt-3.5 sm:flex-row sm:justify-end sm:space-x-2',
|
||||
'flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-x-2',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
|
|
|
|||
|
|
@ -1,17 +1,15 @@
|
|||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps({
|
||||
class: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="cn('flex flex-col space-y-2 text-center sm:text-left', props.class)"
|
||||
:class="cn('flex flex-col gap-y-2 text-center sm:text-left', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,21 @@
|
|||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import { AlertDialogTitle, type AlertDialogTitleProps } from 'radix-vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<AlertDialogTitleProps & { class?: string }>()
|
||||
const props = defineProps<AlertDialogTitleProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogTitle
|
||||
:as-child="props.asChild"
|
||||
:class="cn('text-lg text-foreground font-semibold', props.class)"
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('text-lg font-semibold', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</AlertDialogTitle>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
<script setup lang="ts">
|
||||
import { alertVariants } from '.'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { type AlertVariants, alertVariants } from '.'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
interface Props {
|
||||
variant?: NonNullable<Parameters<typeof alertVariants>[0]>['variant']
|
||||
class?: string
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
variant?: AlertVariants['variant']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn(alertVariants({ variant }), props.class ?? '')">
|
||||
<div :class="cn(alertVariants({ variant }), props.class)" role="alert">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user