Merge branch 'dev' into carousel-dot-buttons

This commit is contained in:
Sadegh Barati 2024-06-24 00:00:15 +03:30
commit 3f2dddfb74
254 changed files with 6593 additions and 4242 deletions

16
.github/actions/setup/action.yml vendored Normal file
View File

@ -0,0 +1,16 @@
name: Setup
description: Installs Node, Enables Corepack and caches pnpm.
runs:
using: composite
steps:
- name: Enable corepack
run: corepack enable
shell: bash
- name: Setup node & pnpm
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: pnpm

View File

@ -48,32 +48,10 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
# Run a build step here - name: Setup (Install Node & pnpm)
- name: Setup Node.js environment uses: ./.github/actions/setup
uses: actions/setup-node@v2
with:
node-version: 18
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 9.0.5
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies - name: Install dependencies
run: pnpm i --frozen-lockfile run: pnpm i --frozen-lockfile

View File

@ -14,14 +14,13 @@ jobs:
release: release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-node@v3 - name: Setup (Install Node & pnpm)
with: uses: ./.github/actions/setup
node-version: 18.x
- run: npx changelogithub - run: pnpm dlx changelogithub
env: env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

View File

@ -19,31 +19,10 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Setup Node.js environment - name: Setup (Install Node & pnpm)
uses: actions/setup-node@v2 uses: ./.github/actions/setup
with:
node-version: 18
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 9.0.5
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies - name: Install dependencies
run: pnpm i --frozen-lockfile run: pnpm i --frozen-lockfile

View File

@ -1,5 +1,9 @@
{ {
"vue.server.hybridMode": true, "vue.server.hybridMode": true,
"vue.server.includeLanguages": [
"vue",
"markdown"
],
"prettier.enable": false, "prettier.enable": false,
"editor.formatOnSave": false, "editor.formatOnSave": false,
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {

View File

@ -3,9 +3,9 @@ import { defineConfig } from 'vitepress'
import Icons from 'unplugin-icons/vite' import Icons from 'unplugin-icons/vite'
import tailwind from 'tailwindcss' import tailwind from 'tailwindcss'
import autoprefixer from 'autoprefixer' import autoprefixer from 'autoprefixer'
import { transformerMetaWordHighlight } from '@shikijs/transformers'
import { cssVariables } from './theme/config/shiki' import { cssVariables } from './theme/config/shiki'
// import { transformerMetaWordHighlight, transformerNotationWordHighlight } from '@shikijs/transformers'
import { siteConfig } from './theme/config/site' import { siteConfig } from './theme/config/site'
import ComponentPreviewPlugin from './theme/plugins/previewer' import ComponentPreviewPlugin from './theme/plugins/previewer'
import CodeWrapperPlugin from './theme/plugins/codewrapper' import CodeWrapperPlugin from './theme/plugins/codewrapper'
@ -56,8 +56,7 @@ export default defineConfig({
markdown: { markdown: {
theme: cssVariables, theme: cssVariables,
codeTransformers: [ codeTransformers: [
// transformerMetaWordHighlight(), transformerMetaWordHighlight(),
// transformerNotationWordHighlight(),
], ],
config(md) { config(md) {
md.use(ComponentPreviewPlugin) md.use(ComponentPreviewPlugin)
@ -77,7 +76,7 @@ export default defineConfig({
}, },
}, },
plugins: [ plugins: [
Icons({ compiler: 'vue3', autoInstall: true }), Icons({ compiler: 'vue3', autoInstall: true }) as any,
], ],
resolve: { resolve: {
alias: { alias: {

View File

@ -1,7 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch } from 'vue' import { computed, ref, watch } from 'vue'
import { codeToHtml } from 'shiki' import { codeToHtml } from 'shiki'
import MagicString from 'magic-string' import MagicString from 'magic-string'
import { useClipboard } from '@vueuse/core'
import { cssVariables } from '../config/shiki' import { cssVariables } from '../config/shiki'
import StyleSwitcher from './StyleSwitcher.vue' import StyleSwitcher from './StyleSwitcher.vue'
import ComponentLoader from './ComponentLoader.vue' import ComponentLoader from './ComponentLoader.vue'
@ -24,6 +25,7 @@ const { style, codeConfig } = useConfigStore()
const rawString = ref('') const rawString = ref('')
const codeHtml = ref('') const codeHtml = ref('')
const transformedRawString = computed(() => transformImportPath(rawString.value))
function transformImportPath(code: string) { function transformImportPath(code: string) {
const s = new MagicString(code) const s = new MagicString(code)
@ -35,7 +37,7 @@ function transformImportPath(code: string) {
watch([style, codeConfig], async () => { watch([style, codeConfig], async () => {
try { try {
rawString.value = await import(`../../../src/lib/registry/${style.value}/example/${props.name}.vue?raw`).then(res => res.default.trim()) rawString.value = await import(`../../../src/lib/registry/${style.value}/example/${props.name}.vue?raw`).then(res => res.default.trim())
codeHtml.value = await codeToHtml(transformImportPath(rawString.value), { codeHtml.value = await codeToHtml(transformedRawString.value, {
lang: 'vue', lang: 'vue',
theme: cssVariables, theme: cssVariables,
}) })
@ -44,6 +46,8 @@ watch([style, codeConfig], async () => {
console.error(err) console.error(err)
} }
}, { immediate: true, deep: true }) }, { immediate: true, deep: true })
const { copy, copied } = useClipboard()
</script> </script>
<template> <template>
@ -86,8 +90,12 @@ watch([style, codeConfig], async () => {
<ComponentLoader v-bind="$attrs" :key="style" :name="name" :type-name="'example'" /> <ComponentLoader v-bind="$attrs" :key="style" :name="name" :type-name="'example'" />
</div> </div>
</TabsContent> </TabsContent>
<TabsContent value="code"> <TabsContent value="code" class="vp-doc">
<div v-if="codeHtml" class="language-vue" style="flex: 1;" v-html="codeHtml" /> <div v-if="codeHtml" class="language-vue" style="flex: 1;">
<button title="Copy Code" class="copy" :class="{ copied }" @click="copy(transformedRawString)" />
<div v-html="codeHtml" />
</div>
<slot v-else /> <slot v-else />
</TabsContent> </TabsContent>
</Tabs> </Tabs>

View File

@ -6,7 +6,6 @@ import {
BreadcrumbItem, BreadcrumbItem,
BreadcrumbLink, BreadcrumbLink,
BreadcrumbList, BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator, BreadcrumbSeparator,
} from '@/lib/registry/new-york/ui/breadcrumb' } from '@/lib/registry/new-york/ui/breadcrumb'

View File

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

View File

@ -8,7 +8,6 @@ import Announcement from '../components/Announcement.vue'
import GitHubIcon from '~icons/radix-icons/github-logo' import GitHubIcon from '~icons/radix-icons/github-logo'
import { buttonVariants } from '@/lib/registry/new-york/ui/button' import { buttonVariants } from '@/lib/registry/new-york/ui/button'
import { Separator } from '@/lib/registry/new-york/ui/separator'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import MailExample from '@/examples/mail/Example.vue' import MailExample from '@/examples/mail/Example.vue'

View File

@ -5,7 +5,6 @@ import Logo from './Logo.vue'
import { Sheet, SheetContent, SheetTrigger } from '@/lib/registry/default/ui/sheet' import { Sheet, SheetContent, SheetTrigger } from '@/lib/registry/default/ui/sheet'
import { Button } from '@/lib/registry/default/ui/button' import { Button } from '@/lib/registry/default/ui/button'
import { ScrollArea } from '@/lib/registry/default/ui/scroll-area' import { ScrollArea } from '@/lib/registry/default/ui/scroll-area'
import { Badge } from '@/lib/registry/new-york/ui/badge'
const open = ref(false) const open = ref(false)
</script> </script>

View File

@ -4,7 +4,7 @@ import { cn } from '@/lib/utils'
</script> </script>
<template> <template>
<WrapBalancer :class="cn('max-w-[750px] text-center text-lg text-muted-foreground sm:text-xl', $attrs.class ?? '')" :prefer-native="false"> <WrapBalancer :class="cn('max-w-[750px] text-center text-lg font-light text-foreground', $attrs.class ?? '')" :prefer-native="false">
<slot /> <slot />
</WrapBalancer> </WrapBalancer>
</template> </template>

View File

@ -5,7 +5,7 @@ import { cn } from '@/lib/utils'
<template> <template>
<h1 <h1
:class="cn( :class="cn(
'text-center text-3xl font-bold leading-tight tracking-tighter md:text-6xl lg:leading-[1.1]', 'text-center text-3xl font-bold leading-tight tracking-tighter md:text-5xl lg:leading-[1.1]',
$attrs.class ?? '', $attrs.class ?? '',
)" )"
> >

View File

@ -24,7 +24,7 @@ function getHeadingsWithHierarchy(divId: string) {
const level = Number.parseInt(heading.tagName.charAt(1)) const level = Number.parseInt(heading.tagName.charAt(1))
if (!heading.id) { if (!heading.id) {
const newId = heading.textContent const newId = heading.textContent
.replaceAll(/[^a-zA-Z0-9 ]/g, '') .replaceAll(/[^a-z0-9 ]/gi, '')
.replaceAll(' ', '-') .replaceAll(' ', '-')
.toLowerCase() .toLowerCase()
heading.id = `${newId}` heading.id = `${newId}`

View File

@ -32,11 +32,11 @@ export const allColors: Color[] = [
'violet', 'violet',
] ]
interface Payment { // interface Payment {
status: string // status: string
email: string // email: string
amount: number // amount: number
} // }
interface TeamMember { interface TeamMember {
name: string name: string

View File

@ -256,12 +256,17 @@ export const docsConfig: DocsConfig = {
title: 'Navigation Menu', title: 'Navigation Menu',
href: '/docs/components/navigation-menu', href: '/docs/components/navigation-menu',
}, },
{
title: 'Number Field',
href: '/docs/components/number-field',
label: 'New Alpha',
},
{ {
title: 'Pagination', title: 'Pagination',
href: '/docs/components/pagination', href: '/docs/components/pagination',
}, },
{ {
title: 'Pin Input', title: 'PIN Input',
href: '/docs/components/pin-input', href: '/docs/components/pin-input',
items: [], items: [],
}, },

View File

@ -7,7 +7,6 @@ import DocsBreadcrumb from '../components/DocsBreadcrumb.vue'
import { ScrollArea } from '@/lib/registry/default/ui/scroll-area' import { ScrollArea } from '@/lib/registry/default/ui/scroll-area'
import RadixIconsCode from '~icons/radix-icons/code' import RadixIconsCode from '~icons/radix-icons/code'
import RadixIconsExternalLink from '~icons/radix-icons/external-link' import RadixIconsExternalLink from '~icons/radix-icons/external-link'
import ChevronRightIcon from '~icons/lucide/chevron-right'
const $route = useRoute() const $route = useRoute()
const { frontmatter } = useData() const { frontmatter } = useData()

View File

@ -7,7 +7,6 @@ import ExamplesNav from '../components/ExamplesNav.vue'
import Announcement from '../components/Announcement.vue' import Announcement from '../components/Announcement.vue'
import { buttonVariants } from '@/lib/registry/new-york/ui/button' import { buttonVariants } from '@/lib/registry/new-york/ui/button'
import { Separator } from '@/lib/registry/new-york/ui/separator'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
</script> </script>

View File

@ -303,7 +303,7 @@ watch(() => $route.path, (n) => {
</DialogContent> </DialogContent>
</Dialog> </Dialog>
<DefaultToaster /> <DefaultToaster />
<NewYorkSonner :theme="'system'" /> <NewYorkSonner class="pointer-events-auto" :theme="'system'" />
<NewYorkToaster /> <NewYorkToaster />
</div> </div>
</TooltipProvider> </TooltipProvider>

View File

@ -167,12 +167,20 @@ pre code {
@apply relative font-mono text-sm ; @apply relative font-mono text-sm ;
} }
.line-numbers-wrapper, code {
--vp-code-line-height: 1.7;
}
.line-numbers-wrapper {
@apply font-mono;
}
pre code .line { pre code .line {
@apply px-4 min-h-6 !py-0.5 w-full inline-block; @apply px-4 min-h-4 !py-0.5 w-full inline-block leading-[--vp-code-line-height];
} }
.line-number { .line-number {
@apply min-h-[1.375rem] !text-sm !inline-block text-muted-foreground; @apply !text-[.75rem] !inline-block text-muted-foreground leading-[--vp-code-line-height];
} }
::view-transition-old(root), ::view-transition-old(root),

View File

@ -352,7 +352,7 @@
display: inline-block; display: inline-block;
@apply bg-[hsl(var(--foreground))] dark:bg-[hsl(var(--background)_/_50%)] @apply bg-[hsl(var(--foreground))] dark:bg-[hsl(var(--background)_/_50%)]
} }
hsl(var(--foreground) / 50%)
.vp-doc [class*='language-'] code .highlighted.error { .vp-doc [class*='language-'] code .highlighted.error {
background-color: var(--vp-code-line-error-color); background-color: var(--vp-code-line-error-color);
} }

View File

@ -2,7 +2,9 @@ import { getParameters } from 'codesandbox/lib/api/define'
import sdk from '@stackblitz/sdk' import sdk from '@stackblitz/sdk'
import { dependencies as deps } from '../../../package.json' import { dependencies as deps } from '../../../package.json'
import { Index as demoIndex } from '../../../../www/__registry__' import { Index as demoIndex } from '../../../../www/__registry__'
// @ts-expect-error ?raw
import tailwindConfigRaw from '../../../tailwind.config?raw' import tailwindConfigRaw from '../../../tailwind.config?raw'
// @ts-expect-error ?raw
import cssRaw from '../../../../../packages/cli/test/fixtures/nuxt/assets/css/tailwind.css?raw' import cssRaw from '../../../../../packages/cli/test/fixtures/nuxt/assets/css/tailwind.css?raw'
import type { Style } from '@/lib/registry/styles' import type { Style } from '@/lib/registry/styles'
@ -35,7 +37,15 @@ const viteConfig = {
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
import tailwind from 'tailwindcss';
import autoprefixer from 'autoprefixer';
export default defineConfig({ export default defineConfig({
css: {
postcss: {
plugins: [tailwind(), autoprefixer()],
},
},
plugins: [vue()], plugins: [vue()],
resolve: { resolve: {
alias: { alias: {
@ -102,7 +112,6 @@ function constructFiles(componentName: string, style: Style, sources: Record<str
'@vitejs/plugin-vue': 'latest', '@vitejs/plugin-vue': 'latest',
'vue-tsc': 'latest', 'vue-tsc': 'latest',
'tailwindcss': 'latest', 'tailwindcss': 'latest',
'postcss': 'latest',
'autoprefixer': 'latest', 'autoprefixer': 'latest',
} }
@ -145,15 +154,6 @@ function constructFiles(componentName: string, style: Style, sources: Record<str
content: tailwindConfigRaw, content: tailwindConfigRaw,
isBinary: false, isBinary: false,
}, },
'postcss.config.js': {
content: `module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
}`,
isBinary: false,
},
'tsconfig.json': { 'tsconfig.json': {
content: `{ content: `{
"$schema": "https://json.schemastore.org/tsconfig", "$schema": "https://json.schemastore.org/tsconfig",

View File

@ -759,6 +759,48 @@ export const Index = {
component: () => import("../src/lib/registry/default/example/NavigationMenuDemoItem.vue").then((m) => m.default), component: () => import("../src/lib/registry/default/example/NavigationMenuDemoItem.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/NavigationMenuDemoItem.vue"], files: ["../src/lib/registry/default/example/NavigationMenuDemoItem.vue"],
}, },
"NumberFieldCurrency": {
name: "NumberFieldCurrency",
type: "components:example",
registryDependencies: ["number-field","label"],
component: () => import("../src/lib/registry/default/example/NumberFieldCurrency.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/NumberFieldCurrency.vue"],
},
"NumberFieldDecimal": {
name: "NumberFieldDecimal",
type: "components:example",
registryDependencies: ["number-field","label"],
component: () => import("../src/lib/registry/default/example/NumberFieldDecimal.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/NumberFieldDecimal.vue"],
},
"NumberFieldDemo": {
name: "NumberFieldDemo",
type: "components:example",
registryDependencies: ["number-field","label"],
component: () => import("../src/lib/registry/default/example/NumberFieldDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/NumberFieldDemo.vue"],
},
"NumberFieldDisabled": {
name: "NumberFieldDisabled",
type: "components:example",
registryDependencies: ["number-field","label"],
component: () => import("../src/lib/registry/default/example/NumberFieldDisabled.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/NumberFieldDisabled.vue"],
},
"NumberFieldForm": {
name: "NumberFieldForm",
type: "components:example",
registryDependencies: ["button","form","number-field","toast"],
component: () => import("../src/lib/registry/default/example/NumberFieldForm.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/NumberFieldForm.vue"],
},
"NumberFieldPercentage": {
name: "NumberFieldPercentage",
type: "components:example",
registryDependencies: ["number-field","label"],
component: () => import("../src/lib/registry/default/example/NumberFieldPercentage.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/NumberFieldPercentage.vue"],
},
"PaginationDemo": { "PaginationDemo": {
name: "PaginationDemo", name: "PaginationDemo",
type: "components:example", type: "components:example",
@ -948,6 +990,13 @@ export const Index = {
component: () => import("../src/lib/registry/default/example/SonnerDemo.vue").then((m) => m.default), component: () => import("../src/lib/registry/default/example/SonnerDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/SonnerDemo.vue"], files: ["../src/lib/registry/default/example/SonnerDemo.vue"],
}, },
"SonnerWithDialog": {
name: "SonnerWithDialog",
type: "components:example",
registryDependencies: ["button","dialog"],
component: () => import("../src/lib/registry/default/example/SonnerWithDialog.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/SonnerWithDialog.vue"],
},
"SwitchDemo": { "SwitchDemo": {
name: "SwitchDemo", name: "SwitchDemo",
type: "components:example", type: "components:example",
@ -1308,7 +1357,7 @@ export const Index = {
"ActivityGoal": { "ActivityGoal": {
name: "ActivityGoal", name: "ActivityGoal",
type: "components:example", type: "components:example",
registryDependencies: ["button","card","themes","config"], registryDependencies: ["button","card"],
component: () => import("../src/lib/registry/default/example/Cards/ActivityGoal.vue").then((m) => m.default), component: () => import("../src/lib/registry/default/example/Cards/ActivityGoal.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/Cards/ActivityGoal.vue"], files: ["../src/lib/registry/default/example/Cards/ActivityGoal.vue"],
}, },
@ -1322,7 +1371,7 @@ export const Index = {
"Metric": { "Metric": {
name: "Metric", name: "Metric",
type: "components:example", type: "components:example",
registryDependencies: ["card","config"], registryDependencies: ["card"],
component: () => import("../src/lib/registry/default/example/Cards/Metric.vue").then((m) => m.default), component: () => import("../src/lib/registry/default/example/Cards/Metric.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/Cards/Metric.vue"], files: ["../src/lib/registry/default/example/Cards/Metric.vue"],
}, },
@ -2160,6 +2209,48 @@ export const Index = {
component: () => import("../src/lib/registry/new-york/example/NavigationMenuDemoItem.vue").then((m) => m.default), component: () => import("../src/lib/registry/new-york/example/NavigationMenuDemoItem.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/NavigationMenuDemoItem.vue"], files: ["../src/lib/registry/new-york/example/NavigationMenuDemoItem.vue"],
}, },
"NumberFieldCurrency": {
name: "NumberFieldCurrency",
type: "components:example",
registryDependencies: ["number-field","label"],
component: () => import("../src/lib/registry/new-york/example/NumberFieldCurrency.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/NumberFieldCurrency.vue"],
},
"NumberFieldDecimal": {
name: "NumberFieldDecimal",
type: "components:example",
registryDependencies: ["number-field","label"],
component: () => import("../src/lib/registry/new-york/example/NumberFieldDecimal.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/NumberFieldDecimal.vue"],
},
"NumberFieldDemo": {
name: "NumberFieldDemo",
type: "components:example",
registryDependencies: ["number-field","label"],
component: () => import("../src/lib/registry/new-york/example/NumberFieldDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/NumberFieldDemo.vue"],
},
"NumberFieldDisabled": {
name: "NumberFieldDisabled",
type: "components:example",
registryDependencies: ["number-field","label"],
component: () => import("../src/lib/registry/new-york/example/NumberFieldDisabled.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/NumberFieldDisabled.vue"],
},
"NumberFieldForm": {
name: "NumberFieldForm",
type: "components:example",
registryDependencies: ["button","form","number-field","toast"],
component: () => import("../src/lib/registry/new-york/example/NumberFieldForm.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/NumberFieldForm.vue"],
},
"NumberFieldPercentage": {
name: "NumberFieldPercentage",
type: "components:example",
registryDependencies: ["number-field","label"],
component: () => import("../src/lib/registry/new-york/example/NumberFieldPercentage.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/NumberFieldPercentage.vue"],
},
"PaginationDemo": { "PaginationDemo": {
name: "PaginationDemo", name: "PaginationDemo",
type: "components:example", type: "components:example",
@ -2349,6 +2440,13 @@ export const Index = {
component: () => import("../src/lib/registry/new-york/example/SonnerDemo.vue").then((m) => m.default), component: () => import("../src/lib/registry/new-york/example/SonnerDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/SonnerDemo.vue"], files: ["../src/lib/registry/new-york/example/SonnerDemo.vue"],
}, },
"SonnerWithDialog": {
name: "SonnerWithDialog",
type: "components:example",
registryDependencies: ["button","dialog"],
component: () => import("../src/lib/registry/new-york/example/SonnerWithDialog.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/SonnerWithDialog.vue"],
},
"SwitchDemo": { "SwitchDemo": {
name: "SwitchDemo", name: "SwitchDemo",
type: "components:example", type: "components:example",
@ -2709,7 +2807,7 @@ export const Index = {
"ActivityGoal": { "ActivityGoal": {
name: "ActivityGoal", name: "ActivityGoal",
type: "components:example", type: "components:example",
registryDependencies: ["button","card","themes","config"], registryDependencies: ["button","card"],
component: () => import("../src/lib/registry/new-york/example/Cards/ActivityGoal.vue").then((m) => m.default), component: () => import("../src/lib/registry/new-york/example/Cards/ActivityGoal.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/Cards/ActivityGoal.vue"], files: ["../src/lib/registry/new-york/example/Cards/ActivityGoal.vue"],
}, },
@ -2723,7 +2821,7 @@ export const Index = {
"Metric": { "Metric": {
name: "Metric", name: "Metric",
type: "components:example", type: "components:example",
registryDependencies: ["card","config"], registryDependencies: ["card"],
component: () => import("../src/lib/registry/new-york/example/Cards/Metric.vue").then((m) => m.default), component: () => import("../src/lib/registry/new-york/example/Cards/Metric.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/Cards/Metric.vue"], files: ["../src/lib/registry/new-york/example/Cards/Metric.vue"],
}, },

View File

@ -1,7 +1,7 @@
{ {
"name": "www", "name": "www",
"type": "module", "type": "module",
"version": "0.10.4", "version": "0.10.5",
"files": [ "files": [
"dist" "dist"
], ],
@ -17,66 +17,65 @@
}, },
"dependencies": { "dependencies": {
"@formkit/auto-animate": "^0.8.2", "@formkit/auto-animate": "^0.8.2",
"@internationalized/date": "^3.5.2", "@internationalized/date": "^3.5.4",
"@radix-icons/vue": "^1.0.0", "@radix-icons/vue": "^1.0.0",
"@stackblitz/sdk": "^1.9.0", "@stackblitz/sdk": "^1.10.0",
"@tanstack/vue-table": "^8.16.0", "@tanstack/vue-table": "^8.17.3",
"@unovis/ts": "^1.4.0", "@unovis/ts": "^1.4.1",
"@unovis/vue": "^1.4.0", "@unovis/vue": "^1.4.1",
"@vee-validate/zod": "^4.12.6", "@vee-validate/zod": "^4.13.1",
"@vueuse/core": "^10.9.0", "@vueuse/core": "^10.11.0",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"codesandbox": "^2.2.3", "codesandbox": "^2.2.3",
"date-fns": "^3.6.0", "date-fns": "^3.6.0",
"embla-carousel": "^8.0.2", "embla-carousel-autoplay": "^8.1.5",
"embla-carousel-autoplay": "^8.0.2", "embla-carousel-vue": "^8.1.5",
"embla-carousel-vue": "^8.0.2", "lucide-vue-next": "^0.383.0",
"lucide-vue-next": "^0.359.0",
"magic-string": "^0.30.10", "magic-string": "^0.30.10",
"radix-vue": "^1.7.2", "radix-vue": "^1.8.4",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"v-calendar": "^3.1.2", "v-calendar": "^3.1.2",
"vaul-vue": "^0.1.0", "vaul-vue": "^0.2.0",
"vee-validate": "4.12.6", "vee-validate": "4.13.1",
"vue": "^3.4.24", "vue": "^3.4.29",
"vue-sonner": "^1.1.2", "vue-sonner": "^1.1.2",
"vue-wrap-balancer": "^1.1.3", "vue-wrap-balancer": "^1.1.3",
"zod": "^3.23.3" "zod": "^3.23.8"
}, },
"devDependencies": { "devDependencies": {
"@babel/traverse": "^7.24.1", "@babel/traverse": "^7.24.7",
"@iconify-json/gravity-ui": "^1.1.2", "@iconify-json/gravity-ui": "^1.1.3",
"@iconify-json/lucide": "^1.1.180", "@iconify-json/lucide": "^1.1.190",
"@iconify-json/ph": "^1.1.12", "@iconify-json/ph": "^1.1.13",
"@iconify-json/radix-icons": "^1.1.14", "@iconify-json/radix-icons": "^1.1.14",
"@iconify-json/ri": "^1.1.18", "@iconify-json/ri": "^1.1.20",
"@iconify-json/simple-icons": "^1.1.94", "@iconify-json/simple-icons": "^1.1.104",
"@iconify-json/tabler": "^1.1.106", "@iconify-json/tabler": "^1.1.113",
"@iconify/vue": "^4.1.2", "@iconify/vue": "^4.1.2",
"@oxc-parser/wasm": "^0.1.0", "@oxc-parser/wasm": "^0.14.0",
"@shikijs/transformers": "^1.3.0", "@shikijs/transformers": "^1.7.0",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/node": "^20.12.7", "@types/node": "^20.14.4",
"@vitejs/plugin-vue": "^5.0.4", "@vitejs/plugin-vue": "^5.0.5",
"@vitejs/plugin-vue-jsx": "^3.1.0", "@vitejs/plugin-vue-jsx": "^4.0.0",
"@vue/compiler-core": "^3.4.24", "@vue/compiler-core": "^3.4.29",
"@vue/compiler-dom": "^3.4.24", "@vue/compiler-dom": "^3.4.29",
"@vue/tsconfig": "^0.5.1", "@vue/tsconfig": "^0.5.1",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"fast-glob": "^3.3.2", "fast-glob": "^3.3.2",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"markdown-it": "^14.1.0", "markdown-it": "^14.1.0",
"pathe": "^1.1.2", "pathe": "^1.1.2",
"rimraf": "^5.0.5", "rimraf": "^5.0.7",
"shiki": "^1.3.0", "shiki": "^1.7.0",
"tailwind-merge": "^2.3.0", "tailwind-merge": "^2.3.0",
"tailwindcss": "^3.4.3", "tailwindcss": "^3.4.4",
"tsx": "^4.7.2", "tsx": "^4.15.6",
"typescript": "^5.4.5", "typescript": "^5.4.5",
"unplugin-icons": "^0.18.5", "unplugin-icons": "^0.19.0",
"vitepress": "^1.1.3", "vitepress": "^1.2.3",
"vue-component-meta": "^2.0.13", "vue-component-meta": "^2.0.21",
"vue-tsc": "^2.0.14" "vue-tsc": "^2.0.21"
} }
} }

View File

@ -282,7 +282,7 @@ for (const baseColor of ['slate', 'gray', 'zinc', 'neutral', 'stone', 'lime']) {
for (const [key, value] of Object.entries(values)) { for (const [key, value] of Object.entries(values)) {
if (typeof value === 'string') { if (typeof value === 'string') {
const resolvedColor = value.replace( const resolvedColor = value.replace(
/{{base}}-/g, /\{\{base\}\}-/g,
`${baseColor}-`, `${baseColor}-`,
) )
base.inlineColors[mode][key] = resolvedColor base.inlineColors[mode][key] = resolvedColor

10
apps/www/src/components.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
/* eslint-disable */
// @ts-nocheck
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
ComponentPreview: typeof import('../.vitepress/theme/components/ComponentPreview.vue')['default']
}
}

View File

@ -1,3 +1,139 @@
--- ---
title: Changelog title: Changelog
description: Latest updates and announcements.
--- ---
## June 2024
### New Component - Number Field
A new component has been added to the project [`NumberField`](/docs/components/number-field.html).
A number field allows a user to enter a number and increment or decrement the value using stepper buttons.
<ComponentPreview name="NumberFieldDemo" class="max-w-[180px]" />
## May 2024
### New Component - Charts
Several kinds of chart components has been added to the project.
Charts are versatile visualization tools, allowing users to represent data using various options for effective analysis.
1. [`Area Chart`](/docs/charts/area) - An area chart visually represents data over time, displaying trends and patterns through filled-in areas under a line graph.
<ComponentPreview name="AreaChartDemo" />
2. [`Bar Chart`](/docs/charts/bar) - A line chart visually represents data using rectangular bars of varying lengths to compare quantities across different categories or groups.
<ComponentPreview name="BarChartDemo" />
3. [`Donut Chart`](/docs/charts/donut) - A line chart visually represents data in a circular form, similar to a pie chart but with a central void, emphasizing proportions within categories.
<ComponentPreview name="DonutChartDemo" />
4. [`Line Chart`](/docs/charts/line) - A line chart visually displays data points connected by straight lines, illustrating trends or relationships over a continuous axis.
<ComponentPreview name="LineChartDemo" />
### New Component - Auto Form
[`Auto Form`](/docs/components/auto-form.html) is a drop-in form builder for your internal and low-priority forms with existing zod schemas.
For example, if you already have zod schemas for your API and want to create a simple admin panel to edit user profiles, simply pass the schema to AutoForm and you're done.
The following form has been created by passing a `zod` schema object to our `AutoForm` component.
<ComponentPreview name="AutoFormBasic" />
## April 2024
### Component Updated - Calendar
The [`Calendar`](/docs/components/calendar.html) component has been updated and is now built on top of the [RadixVue Calendar](https://www.radix-vue.com/components/calendar.html) component, which uses the [@internationalized/date](https://react-spectrum.adobe.com/internationalized/date/index.html) package to handle dates.
If you're looking for a range calendar, check out the [`Range Calendar`](/docs/components/range-calendar.html) component.
And if you're looking for a date picker input, check out the [`Date Picker`](/docs/components/date-picker.html) component.
<ComponentPreview name="CalendarDemo" />
<ComponentPreview name="RangeCalendarDemo" />
<ComponentPreview name="DatePickerDemo" />
### Building Blocks for the Web
[`Blocks`](/blocks) are composed of different components that can be used to build your apps, with each block being a standalone section of your application. These blocks are fully responsive, accessible, and composable, and are built using the same principles as the other components in `shadcn-vue`.
<div>
<image
src="/examples/block-dark.png"
:width="1280"
:height="727"
alt="Building Blocks"
class="hidden dark:block"
/>
<image
src="/examples/block-light.png"
:width="1280"
:height="727"
alt="Building Blocks"
class="block dark:hidden"
/>
</div>
## March 2024
### New Component - Breadcrumb
[`Breadcrumb`](/docs/components/breadcrumb.html) displays the path to the current resource using a hierarchy of links.
<ComponentPreview name="BreadcrumbDemo" />
### New Component - Pin Input (OTP Input)
[`Pin Input`](/docs/components/pin-input.html) allows users to input a sequence of one-character alphanumeric inputs.
<ComponentPreview name="PinInputDemo" />
### New Component - Resizable
[`Resizable`](/docs/components/resizable.html) - Accessible resizable panel groups and layouts with keyboard support.
<ComponentPreview name="ResizableDemo" />
### New Component - Drawer
[`Drawer`](/docs/components/drawer.html) - A drawer component for vue that is built on top of [Vaul Vue](https://github.com/radix-vue/vaul-vue).
<ComponentPreview name="DrawerDemo" />
## February 2024
### New Component - Tag Inputs
[`Tag inputs`](/docs/components/tags-input.html) render tags inside an input, followed by an actual text input.
<ComponentPreview name="TagsInputDemo" />
## January 2024
### New Component - Sonner
[`Sonner`](/docs/components/sonner.html) is an opinionated toast component for Vue.
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.
<ComponentPreview name="SonnerDemo" />
### New Component - Toggle Group
[`Toggle Group`](/docs/components/toggle-group.html) - A set of two-state buttons that can be toggled on or off.
<ComponentPreview name="ToggleGroupDemo" />
### New Component - Carousel
[`Carousel`](/docs/components/toggle-group.html) - A carousel with motion and swipe built using [Embla](https://www.embla-carousel.com/) library.
<ComponentPreview name="CarouselDemo" />

View File

@ -1,6 +1,6 @@
--- ---
title: Bar title: Bar
description: An line chart visually represents data using rectangular bars of varying lengths to compare quantities across different categories or groups. description: A line chart visually represents data using rectangular bars of varying lengths to compare quantities across different categories or groups.
source: apps/www/src/lib/registry/default/ui/chart-bar source: apps/www/src/lib/registry/default/ui/chart-bar
label: Alpha label: Alpha
--- ---

View File

@ -1,6 +1,6 @@
--- ---
title: Donut title: Donut
description: An line chart visually represents data in a circular form, similar to a pie chart but with a central void, emphasizing proportions within categories. description: A line chart visually represents data in a circular form, similar to a pie chart but with a central void, emphasizing proportions within categories.
source: apps/www/src/lib/registry/default/ui/chart-donut source: apps/www/src/lib/registry/default/ui/chart-donut
label: Alpha label: Alpha
--- ---

View File

@ -1,6 +1,6 @@
--- ---
title: Line title: Line
description: An line chart visually displays data points connected by straight lines, illustrating trends or relationships over a continuous axis. description: A line chart visually displays data points connected by straight lines, illustrating trends or relationships over a continuous axis.
source: apps/www/src/lib/registry/default/ui/chart-line source: apps/www/src/lib/registry/default/ui/chart-line
label: Alpha label: Alpha
--- ---

View File

@ -414,9 +414,7 @@ const form = useForm({
validationSchema: toTypedSchema(schema), validationSchema: toTypedSchema(schema),
}) })
form.setValues({ form.setFieldValue('username', 'bar')
username: 'foo'
})
</script> </script>
<template> <template>

View File

@ -22,7 +22,7 @@ import {
BreadcrumbList, BreadcrumbList,
BreadcrumbPage, BreadcrumbPage,
BreadcrumbSeparator, BreadcrumbSeparator,
} from '@/lib/components/ui/breadcrumb' } from '@/components/ui/breadcrumb'
</script> </script>
<template> <template>
@ -65,7 +65,7 @@ import {
BreadcrumbLink, BreadcrumbLink,
BreadcrumbList, BreadcrumbList,
BreadcrumbSeparator, BreadcrumbSeparator,
} from '@/lib/components/ui/breadcrumb' } from '@/components/ui/breadcrumb'
</script> </script>
<template> <template>
@ -106,7 +106,7 @@ import {
DropdownMenuTrigger, DropdownMenuTrigger,
} from '@/lib/components/ui/dropdown-menu' } from '@/lib/components/ui/dropdown-menu'
import { BreadcrumbItem } from '@/lib/components/ui/breadcrumb' import { BreadcrumbItem } from '@/components/ui/breadcrumb'
import ChevronDownIcon from '~icons/radix-icons/chevron-down' import ChevronDownIcon from '~icons/radix-icons/chevron-down'
</script> </script>
@ -143,7 +143,7 @@ import {
BreadcrumbEllipsis, BreadcrumbEllipsis,
BreadcrumbItem, BreadcrumbItem,
BreadcrumbList, BreadcrumbList,
} from '@/lib/components/ui/breadcrumb' } from '@/components/ui/breadcrumb'
</script> </script>
<template> <template>
@ -175,7 +175,7 @@ import {
BreadcrumbItem, BreadcrumbItem,
BreadcrumbLink, BreadcrumbLink,
BreadcrumbList, BreadcrumbList,
} from '@/lib/components/ui/breadcrumb' } from '@/components/ui/breadcrumb'
</script> </script>
<template> <template>

View File

@ -44,7 +44,7 @@ import {
const frameworks = [ const frameworks = [
{ value: 'next.js', label: 'Next.js' }, { value: 'next.js', label: 'Next.js' },
{ value: 'sveltekit', label: 'SvelteKit' }, { value: 'sveltekit', label: 'SvelteKit' },
{ value: 'nuxt.js', label: 'Nuxt.js' }, { value: 'nuxt', label: 'Nuxt' },
{ value: 'remix', label: 'Remix' }, { value: 'remix', label: 'Remix' },
{ value: 'astro', label: 'Astro' }, { value: 'astro', label: 'Astro' },
] ]

View File

@ -101,7 +101,7 @@ watch(CmdJ, (v) => {
<span class="text-xs"></span>J <span class="text-xs"></span>J
</kbd> </kbd>
</p> </p>
<CommandDialog :open="open" :on-open-change="handleOpenChange"> <CommandDialog :open="open" @update:open="handleOpenChange">
<CommandInput placeholder="Type a command or search..." /> <CommandInput placeholder="Type a command or search..." />
<CommandList> <CommandList>
<CommandEmpty>No results found.</CommandEmpty> <CommandEmpty>No results found.</CommandEmpty>

View File

@ -97,7 +97,7 @@ Start by creating the following file structure:
└── app.vue └── app.vue
``` ```
I'm using a Nuxt.js example here but this works for any other Vue framework. I'm using a Nuxt example here but this works for any other Vue framework.
- `columns.ts` It will contain our column definitions. - `columns.ts` It will contain our column definitions.
- `data-table.vue` It will contain our `<DataTable />` component. - `data-table.vue` It will contain our `<DataTable />` component.
@ -114,7 +114,7 @@ Let's start by building a basic table.
First, we'll define our columns in the `columns.ts` file. First, we'll define our columns in the `columns.ts` file.
```ts:line-numbers {1,12-27} ```ts:line-numbers
import { h } from 'vue' import { h } from 'vue'
export const columns: ColumnDef<Payment>[] = [ export const columns: ColumnDef<Payment>[] = [
@ -146,14 +146,14 @@ formatted, sorted and filtered.
Next, we'll create a `<DataTable />` component to render our table. Next, we'll create a `<DataTable />` component to render our table.
```vue:line-numbers ```vue
<script setup lang="ts" generic="TData, TValue"> <script setup lang="ts" generic="TData, TValue">
import type { ColumnDef } from '@tanstack/vue-table' import type { ColumnDef } from '@tanstack/vue-table'
import { import {
FlexRender, FlexRender,
getCoreRowModel, getCoreRowModel,
useVueTable, useVueTable,
} from "@tanstack/vue-table" } from '@tanstack/vue-table'
import { import {
Table, Table,
@ -162,7 +162,7 @@ import {
TableHead, TableHead,
TableHeader, TableHeader,
TableRow, TableRow,
} from "@/components/ui/table" } from '@/components/ui/table'
const props = defineProps<{ const props = defineProps<{
columns: ColumnDef<TData, TValue>[] columns: ColumnDef<TData, TValue>[]
@ -182,15 +182,19 @@ const table = useVueTable({
<TableHeader> <TableHeader>
<TableRow v-for="headerGroup in table.getHeaderGroups()" :key="headerGroup.id"> <TableRow v-for="headerGroup in table.getHeaderGroups()" :key="headerGroup.id">
<TableHead v-for="header in headerGroup.headers" :key="header.id"> <TableHead v-for="header in headerGroup.headers" :key="header.id">
<FlexRender v-if="!header.isPlaceholder" :render="header.column.columnDef.header" <FlexRender
:props="header.getContext()" /> v-if="!header.isPlaceholder" :render="header.column.columnDef.header"
:props="header.getContext()"
/>
</TableHead> </TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
<template v-if="table.getRowModel().rows?.length"> <template v-if="table.getRowModel().rows?.length">
<TableRow v-for="row in table.getRowModel().rows" :key="row.id" <TableRow
:data-state="row.getIsSelected() ? 'selected' : undefined"> v-for="row in table.getRowModel().rows" :key="row.id"
:data-state="row.getIsSelected() ? 'selected' : undefined"
>
<TableCell v-for="cell in row.getVisibleCells()" :key="cell.id"> <TableCell v-for="cell in row.getVisibleCells()" :key="cell.id">
<FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" /> <FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
</TableCell> </TableCell>
@ -198,7 +202,7 @@ const table = useVueTable({
</template> </template>
<template v-else> <template v-else>
<TableRow> <TableRow>
<TableCell :colSpan="columns.length" class="h-24 text-center"> <TableCell :colspan="columns.length" class="h-24 text-center">
No results. No results.
</TableCell> </TableCell>
</TableRow> </TableRow>
@ -221,12 +225,12 @@ const table = useVueTable({
Finally, we'll render our table in our index component. Finally, we'll render our table in our index component.
```vue:line-numbers {28} ```vue
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { onMounted, ref } from 'vue'
import { columns } from "./components/columns" import { columns } from './components/columns'
import type { Payment } from './components/columns'; import type { Payment } from './components/columns'
import DataTable from "./components/DataTable.vue" import DataTable from './components/DataTable.vue'
const data = ref<Payment[]>([]) const data = ref<Payment[]>([])
@ -234,18 +238,18 @@ async function getData(): Promise<Payment[]> {
// Fetch data from your API here. // Fetch data from your API here.
return [ return [
{ {
id: "728ed52f", id: '728ed52f',
amount: 100, amount: 100,
status: "pending", status: 'pending',
email: "m@example.com", email: 'm@example.com',
}, },
// ... // ...
] ]
} }
onMounted(async () => { onMounted(async () => {
data.value = await getData(); data.value = await getData()
}); })
</script> </script>
<template> <template>
@ -267,18 +271,18 @@ 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: Update the `header` and `cell` definitions for amount as follows:
```ts:line-numbers title="components/payments/columns.ts" {5-17} ```ts
import { h } from 'vue' import { h } from 'vue'
export const columns: ColumnDef<Payment>[] = [ export const columns: ColumnDef<Payment>[] = [
{ {
accessorKey: "amount", accessorKey: 'amount',
header: () => h('div', { class: 'text-right' }, 'Amount'), header: () => h('div', { class: 'text-right' }, 'Amount'),
cell: ({ row }) => { cell: ({ row }) => {
const amount = parseFloat(row.getValue("amount")) const amount = Number.parseFloat(row.getValue('amount'))
const formatted = new Intl.NumberFormat("en-US", { const formatted = new Intl.NumberFormat('en-US', {
style: "currency", style: 'currency',
currency: "USD", currency: 'USD',
}).format(amount) }).format(amount)
return h('div', { class: 'text-right font-medium' }, formatted) return h('div', { class: 'text-right font-medium' }, formatted)
@ -295,10 +299,9 @@ Let's add row actions to our table. We'll use a `<Dropdown />` component for thi
<Steps> <Steps>
### Add the following into your `DataTableDropDown.vue` component: ### Add the following into your `DataTableDropDown.vue` component
```vue:line-numbers ```vue
// DataTableDropDown.vue
<script setup lang="ts"> <script setup lang="ts">
import { MoreHorizontal } from 'lucide-vue-next' import { MoreHorizontal } from 'lucide-vue-next'
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from '@/components/ui/dropdown-menu' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'
@ -334,15 +337,14 @@ function copy(id: string) {
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</template> </template>
``` ```
### Update columns definition ### Update columns definition
Update our columns definition to add a new `actions` column. The `actions` cell returns a `<Dropdown />` component. Update our columns definition to add a new `actions` column. The `actions` cell returns a `<Dropdown />` component.
```ts:line-numbers showLineNumber{2,6-16} ```ts
import { ColumnDef } from "@tanstack/vue-table" import { ColumnDef } from '@tanstack/vue-table'
import DropdownAction from '@/components/DataTableDropDown.vue' import DropdownAction from '@/components/DataTableDropDown.vue'
export const columns: ColumnDef<Payment>[] = [ export const columns: ColumnDef<Payment>[] = [
@ -359,7 +361,6 @@ export const columns: ColumnDef<Payment>[] = [
}, },
}, },
] ]
``` ```
You can access the row data using `row.original` in the `cell` function. Use this to handle actions for your row eg. use the `id` to make a DELETE call to your API. You can access the row data using `row.original` in the `cell` function. Use this to handle actions for your row eg. use the `id` to make a DELETE call to your API.
@ -396,9 +397,9 @@ 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. We can add pagination controls to our table using the `<Button />` component and the `table.previousPage()`, `table.nextPage()` API methods.
```vue:line-numbers {3,15,21-39} ```vue
<script lang="ts" generic="TData, TValue"> <script lang="ts" generic="TData, TValue">
import { Button } from "@/components/ui/button" import { Button } from '@/components/ui/button'
const table = useVueTable({ const table = useVueTable({
get data() { return props.data }, get data() { return props.data },
@ -406,7 +407,6 @@ const table = useVueTable({
getCoreRowModel: getCoreRowModel(), getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(), getPaginationRowModel: getPaginationRowModel(),
}) })
</script> </script>
<template> <template>
@ -436,7 +436,6 @@ const table = useVueTable({
</div> </div>
</div> </div>
</template> </template>
``` ```
See [Reusable Components](#reusable-components) section for a more advanced pagination component. See [Reusable Components](#reusable-components) section for a more advanced pagination component.
@ -449,22 +448,21 @@ Let's make the email column sortable.
<Steps> <Steps>
### Add the following into your `utils` file: ### Add the following into your `utils` file
```ts:line-numbers {5,6,12-17} ```ts
import { type ClassValue, clsx } from 'clsx' import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge' import { twMerge } from 'tailwind-merge'
import type { Updater } from '@tanstack/vue-table' import type { Updater } from '@tanstack/vue-table'
import { type Ref } from 'vue' import type { Ref } from 'vue'
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs)) return twMerge(clsx(inputs))
} }
export function valueUpdater<T extends Updater<any>>(updaterOrValue: T, ref: Ref) { export function valueUpdater<T extends Updater<any>>(updaterOrValue: T, ref: Ref) {
ref.value ref.value = typeof updaterOrValue === 'function'
= typeof updaterOrValue === 'function'
? updaterOrValue(ref.value) ? updaterOrValue(ref.value)
: updaterOrValue : updaterOrValue
} }
@ -474,15 +472,13 @@ The `valueUpdater` function updates a Vue `ref` object's value. It handles both
### Update `<DataTable>` ### Update `<DataTable>`
```vue:line-numbers {4,7,16,34,41-44} ```vue:line-numbers {4,14,17,33,40-44}
<script setup lang="ts" generic="TData, TValue"> <script setup lang="ts" generic="TData, TValue">
import type { import type {
ColumnDef, ColumnDef,
SortingState, SortingState,
} from '@tanstack/vue-table' } from '@tanstack/vue-table'
import { valueUpdater } from '@/lib/utils'
import { ArrowUpDown, ChevronDown } from 'lucide-vue-next' import { ArrowUpDown, ChevronDown } from 'lucide-vue-next'
import { h, ref } from 'vue' import { h, ref } from 'vue'
@ -492,7 +488,8 @@ import {
getPaginationRowModel, getPaginationRowModel,
getSortedRowModel, getSortedRowModel,
useVueTable, useVueTable,
} from "@tanstack/vue-table" } from '@tanstack/vue-table'
import { valueUpdater } from '@/lib/utils'
import { import {
Table, Table,
@ -501,7 +498,7 @@ import {
TableHead, TableHead,
TableHeader, TableHeader,
TableRow, TableRow,
} from "@/components/ui/table" } from '@/components/ui/table'
const props = defineProps<{ const props = defineProps<{
columns: ColumnDef<TData, TValue>[] columns: ColumnDef<TData, TValue>[]
@ -521,7 +518,6 @@ const table = useVueTable({
get sorting() { return sorting.value }, get sorting() { return sorting.value },
}, },
}) })
</script> </script>
<template> <template>

View File

@ -47,7 +47,7 @@ import {
### Link Component ### 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 `<NuxtLink />` component, you can use `navigationMenuTriggerStyle()` to apply the correct styles to the trigger.
```ts ```ts
import { navigationMenuTriggerStyle } from '@/components/ui/navigation-menu' import { navigationMenuTriggerStyle } from '@/components/ui/navigation-menu'

View File

@ -0,0 +1,71 @@
---
title: Number Field
description: A number field allows a user to enter a number and increment or decrement the value using stepper buttons.
source: apps/www/src/lib/registry/default/ui/number-field
primitive: https://www.radix-vue.com/components/number-field.html
---
<ComponentPreview name="NumberFieldDemo" class="max-w-[180px]" />
## Installation
<TabPreview name="CLI">
<template #CLI>
```bash
npx shadcn-vue@latest add number-field
```
</template>
</TabPreview>
## Usage
```vue
<script setup lang="ts">
import {
NumberField,
NumberFieldContent,
NumberFieldDecrement,
NumberFieldIncrement,
NumberFieldInput,
} from '@/lib/registry/default/ui/number-field'
import { Label } from '@/lib/registry/default/ui/label'
</script>
<template>
<NumberField>
<Label>Age</Label>
<NumberFieldContent>
<NumberFieldDecrement />
<NumberFieldInput />
<NumberFieldIncrement />
</NumberFieldContent>
</NumberField>
</template>
```
## Examples
### Default
<ComponentPreview name="NumberFieldDemo" class="max-w-[180px]" />
### Disabled
<ComponentPreview name="NumberFieldDisabled" class="max-w-[180px]" />
### Decimal
<ComponentPreview name="NumberFieldDecimal" class="max-w-[180px]" />
### Percentage
<ComponentPreview name="NumberFieldPercentage" class="max-w-[180px]" />
### Currency
<ComponentPreview name="NumberFieldCurrency" class="max-w-[220px]" />
### Form
<ComponentPreview name="NumberFieldForm" class="max-w-xs" />

View File

@ -44,6 +44,6 @@ import { Separator } from '@/components/ui/separator'
</script> </script>
<template> <template>
<Separator /> <Separator label="Or" />
</template> </template>
``` ```

View File

@ -32,7 +32,7 @@ import {
<SheetTrigger>Open</SheetTrigger> <SheetTrigger>Open</SheetTrigger>
<SheetContent> <SheetContent>
<SheetHeader> <SheetHeader>
<SheetTitle>Are you sure absolutely sure?</SheetTitle> <SheetTitle>Are you absolutely sure?</SheetTitle>
<SheetDescription> <SheetDescription>
This action cannot be undone. This will permanently delete your account This action cannot be undone. This will permanently delete your account
and remove your data from our servers. and remove your data from our servers.
@ -61,7 +61,7 @@ You can adjust the size of the sheet using CSS classes:
<SheetTrigger>Open</SheetTrigger> <SheetTrigger>Open</SheetTrigger>
<SheetContent class="w-[400px] sm:w-[540px]"> <SheetContent class="w-[400px] sm:w-[540px]">
<SheetHeader> <SheetHeader>
<SheetTitle>Are you sure absolutely sure?</SheetTitle> <SheetTitle>Are you absolutely sure?</SheetTitle>
<SheetDescription> <SheetDescription>
This action cannot be undone. This will permanently delete your account This action cannot be undone. This will permanently delete your account
and remove your data from our servers. and remove your data from our servers.

View File

@ -61,3 +61,23 @@ import { Button } from '@/components/ui/button'
</Button> </Button>
</template> </template>
``` ```
## Examples
### Sonner with Dialog
Related issue https://github.com/radix-vue/shadcn-vue/issues/462
Add `pointer-events-auto` class to Toaster component in your `App.vue` file:
```vue {6}
<script setup lang="ts">
import { Toaster } from '@/components/ui/sonner'
</script>
<template>
<Toaster class="pointer-events-auto" />
</template>
```
<ComponentPreview name="SonnerWithDialog" />

View File

@ -104,6 +104,7 @@ Would you like to use TypeScript (recommended)? no / yes
Which framework are you using? Astro Which framework are you using? Astro
Which style would you like to use? Default Which style would you like to use? Default
Which color would you like to use as base color? Slate Which color would you like to use as base color? Slate
Where is your tsconfig.json or jsconfig.json file? ./tsconfig.json
Where is your global CSS file? src/styles/globals.css Where is your global CSS file? src/styles/globals.css
Do you want to use CSS variables for colors? no / yes Do you want to use CSS variables for colors? no / yes
Where is your tailwind.config located? tailwind.config.mjs Where is your tailwind.config located? tailwind.config.mjs

View File

@ -30,11 +30,13 @@ Would you like to use TypeScript (recommended)? no / yes
Which framework are you using? Vite / Nuxt / Laravel Which framework are you using? Vite / Nuxt / Laravel
Which style would you like to use? Default Which style would you like to use? Default
Which color would you like to use as base color? Slate Which color would you like to use as base color? Slate
Where is your tsconfig.json or jsconfig.json file? ./tsconfig.json
Where is your global CSS file? resources/css/app.css Where is your global CSS file? resources/css/app.css
Do you want to use CSS variables for colors? no / yes Do you want to use CSS variables for colors? no / yes
Where is your tailwind.config.js located? tailwind.config.js Where is your tailwind.config.js located? tailwind.config.js
Configure the import alias for components: @/Components Configure the import alias for components: @/Components
Configure the import alias for utils: @/lib/utils Configure the import alias for utils: @/lib/utils
Write configuration to components.json. Proceed? > Y/n
``` ```
### Update tailwind.config.js ### Update tailwind.config.js

View File

@ -20,6 +20,7 @@ If you encounter the error `ERROR: Cannot read properties of undefined (reading
```bash ```bash
npm install -D typescript npm install -D typescript
``` ```
### Install TailwindCSS module ### Install TailwindCSS module
```bash ```bash
@ -76,7 +77,7 @@ export default defineNuxtModule<ShadcnVueOptions>({
configKey: 'shadcn', configKey: 'shadcn',
version: '0.0.1', version: '0.0.1',
compatibility: { compatibility: {
nuxt: '^3.9.0', nuxt: '>=3.9.0',
bridge: false, bridge: false,
}, },
}, },
@ -183,11 +184,13 @@ Would you like to use TypeScript (recommended)? no / yes
Which framework are you using? Vite / Nuxt / Laravel Which framework are you using? Vite / Nuxt / Laravel
Which style would you like to use? Default Which style would you like to use? Default
Which color would you like to use as base color? Slate Which color would you like to use as base color? Slate
Where is your tsconfig.json or jsconfig.json file? ./tsconfig.json
Where is your global CSS file? src/index.css Where is your global CSS file? src/index.css
Do you want to use CSS variables for colors? no / yes Do you want to use CSS variables for colors? no / yes
Where is your tailwind.config.js located? tailwind.config.js Where is your tailwind.config.js located? tailwind.config.js
Configure the import alias for components: @/components Configure the import alias for components: @/components
Configure the import alias for utils: @/lib/utils Configure the import alias for utils: @/lib/utils
Write configuration to components.json. Proceed? > Y/n
``` ```
### App structure ### App structure

View File

@ -45,12 +45,12 @@ Install `tailwindcss` and its peer dependencies, then generate your `tailwind.co
#### `vite.config` #### `vite.config`
```typescript {5,6,9-13} ```typescript {5,6,9-13}
import path from "path" import path from 'node:path'
import { defineConfig } from "vite" import { defineConfig } from 'vite'
import vue from "@vitejs/plugin-vue" import vue from '@vitejs/plugin-vue'
import tailwind from "tailwindcss" import tailwind from 'tailwindcss'
import autoprefixer from "autoprefixer" import autoprefixer from 'autoprefixer'
export default defineConfig({ export default defineConfig({
css: { css: {
@ -61,7 +61,7 @@ Install `tailwindcss` and its peer dependencies, then generate your `tailwind.co
plugins: [vue()], plugins: [vue()],
resolve: { resolve: {
alias: { alias: {
"@": path.resolve(__dirname, "./src"), '@': path.resolve(__dirname, './src'),
}, },
}, },
}) })
@ -75,7 +75,7 @@ Install `tailwindcss` and its peer dependencies, then generate your `tailwind.co
npm install -D tailwindcss autoprefixer postcss npm install -D tailwindcss autoprefixer postcss
``` ```
#### `postcss.config.js` #### `postcss.config.js`
```js ```js
module.exports = { module.exports = {
@ -116,12 +116,12 @@ npm i -D @types/node
``` ```
```typescript {15-19} ```typescript {15-19}
import path from "path" import path from 'node:path'
import vue from "@vitejs/plugin-vue" import vue from '@vitejs/plugin-vue'
import { defineConfig } from "vite" import { defineConfig } from 'vite'
import tailwind from "tailwindcss" import tailwind from 'tailwindcss'
import autoprefixer from "autoprefixer" import autoprefixer from 'autoprefixer'
export default defineConfig({ export default defineConfig({
css: { css: {
@ -132,7 +132,7 @@ export default defineConfig({
plugins: [vue()], plugins: [vue()],
resolve: { resolve: {
alias: { alias: {
"@": path.resolve(__dirname, "./src"), '@': path.resolve(__dirname, './src'),
}, },
}, },
}) })
@ -159,11 +159,13 @@ Would you like to use TypeScript (recommended)? no / yes
Which framework are you using? Vite / Nuxt / Laravel Which framework are you using? Vite / Nuxt / Laravel
Which style would you like to use? Default Which style would you like to use? Default
Which color would you like to use as base color? Slate Which color would you like to use as base color? Slate
Where is your global CSS file? src/index.css Where is your tsconfig.json or jsconfig.json file? ./tsconfig.json
Where is your global CSS file? src/assets/index.css
Do you want to use CSS variables for colors? no / yes Do you want to use CSS variables for colors? no / yes
Where is your tailwind.config.js located? tailwind.config.js Where is your tailwind.config.js located? tailwind.config.js
Configure the import alias for components: @/components Configure the import alias for components: @/components
Configure the import alias for utils: @/lib/utils Configure the import alias for utils: @/lib/utils
Write configuration to components.json. Proceed? > Y/n
``` ```
### Update main.ts ### Update main.ts

View File

@ -27,7 +27,7 @@ import { Label } from '@/lib/registry/new-york/ui/label'
<div class="grid grid-cols-2 gap-6"> <div class="grid grid-cols-2 gap-6">
<Button variant="outline"> <Button variant="outline">
<GitHubIcon class="mr-2 h-4 w-4" /> <GitHubIcon class="mr-2 h-4 w-4" />
Github GitHub
</Button> </Button>
<Button variant="outline"> <Button variant="outline">
<svg role="img" viewBox="0 0 24 24" class="mr-2 h-4 w-4"> <svg role="img" viewBox="0 0 24 24" class="mr-2 h-4 w-4">

View File

@ -84,7 +84,7 @@ async function onSubmit(values: any) {
</p> </p>
</div> </div>
<Separator /> <Separator />
<Form v-slot="{ setValues }" :validation-schema="accountFormSchema" class="space-y-8" @submit="onSubmit"> <Form v-slot="{ setFieldValue }" :validation-schema="accountFormSchema" class="space-y-8" @submit="onSubmit">
<FormField v-slot="{ componentField }" name="name"> <FormField v-slot="{ componentField }" name="name">
<FormItem> <FormItem>
<FormLabel>Name</FormLabel> <FormLabel>Name</FormLabel>
@ -126,15 +126,11 @@ async function onSubmit(values: any) {
@update:model-value="(v) => { @update:model-value="(v) => {
if (v) { if (v) {
dateValue = v dateValue = v
setValues({ setFieldValue('dob', toDate(v).toISOString())
dob: toDate(v).toISOString(),
}, false)
} }
else { else {
dateValue = undefined dateValue = undefined
setValues({ setFieldValue('dob', undefined)
dob: undefined,
}, false)
} }
}" }"
/> />
@ -178,9 +174,7 @@ async function onSubmit(values: any) {
<CommandItem <CommandItem
v-for="language in languages" :key="language.value" :value="language.label" v-for="language in languages" :key="language.value" :value="language.label"
@select="() => { @select="() => {
setValues({ setFieldValue('language', language.value)
language: language.value,
}, false)
open = false open = false
}" }"
> >

View File

@ -57,7 +57,7 @@ const onSubmit = handleSubmit((values) => {
<select <select
:class="cn( :class="cn(
buttonVariants({ variant: 'outline' }), buttonVariants({ variant: 'outline' }),
'w-[200px] appearance-none bg-transparent font-normal', 'w-[200px] appearance-none font-normal',
)" )"
v-bind="field" v-bind="field"
> >

View File

@ -33,7 +33,7 @@ const { handleSubmit } = useForm({
}, },
}) })
const onSubmit = handleSubmit((values, { resetForm }) => { const onSubmit = handleSubmit((values) => {
toast({ toast({
title: 'You submitted the following values:', 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(values, null, 2))), description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),

View File

@ -13,7 +13,7 @@ const $route = useRoute()
const sidebarNavItems: Item[] = [ const sidebarNavItems: Item[] = [
{ {
title: 'Profile', title: 'Profile',
href: '/examples/forms/forms', href: '/examples/forms',
}, },
{ {
title: 'Account', title: 'Account',

View File

@ -94,7 +94,7 @@ const showDeleteDialog = ref(false)
<AlertDialog v-model:open="showDeleteDialog"> <AlertDialog v-model:open="showDeleteDialog">
<AlertDialogContent> <AlertDialogContent>
<AlertDialogHeader> <AlertDialogHeader>
<AlertDialogTitle>Are you sure absolutely sure?</AlertDialogTitle> <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription> <AlertDialogDescription>
This action cannot be undone. This preset will no longer be This action cannot be undone. This preset will no longer be
accessible by you or others you&apos;ve shared it with. accessible by you or others you&apos;ve shared it with.

View File

@ -36,7 +36,7 @@ export const models: Model<ModelType>[] = [
strengths: 'Moderate classification, semantic search', strengths: 'Moderate classification, semantic search',
}, },
{ {
id: ' be638fb1-973b-4471-a49c-290325085802', id: 'be638fb1-973b-4471-a49c-290325085802',
name: 'text-ada-001', name: 'text-ada-001',
description: description:
'Capable of very simple tasks, usually the fastest model in the GPT-3 series, and lowest cost.', 'Capable of very simple tasks, usually the fastest model in the GPT-3 series, and lowest cost.',

View File

@ -53,18 +53,18 @@ export const statuses = [
export const priorities = [ export const priorities = [
{ {
label: 'Low',
value: 'low', value: 'low',
label: 'Low',
icon: h(ArrowDownIcon), icon: h(ArrowDownIcon),
}, },
{ {
label: 'Medium',
value: 'medium', value: 'medium',
label: 'Medium',
icon: h(ArrowRightIcon), icon: h(ArrowRightIcon),
}, },
{ {
label: 'High',
value: 'high', value: 'high',
label: 'High',
icon: h(ArrowUpIcon), icon: h(ArrowUpIcon),
}, },
] ]

View File

@ -14,11 +14,11 @@ import { Input } from '@/lib/registry/default/ui/input'
import { Label } from '@/lib/registry/default/ui/label' import { Label } from '@/lib/registry/default/ui/label'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/lib/registry/default/ui/select' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/lib/registry/default/ui/select'
import { Textarea } from '@/lib/registry/default/ui/textarea' import { Textarea } from '@/lib/registry/default/ui/textarea'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/lib/registry/default/ui/tooltip' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/lib/registry/default/ui/tooltip'
</script> </script>
<template> <template>
<div class="grid h-screen w-full pl-[53px]"> <div class="grid h-screen w-full pl-[56px]">
<aside class="inset-y fixed left-0 z-20 flex h-full flex-col border-r"> <aside class="inset-y fixed left-0 z-20 flex h-full flex-col border-r">
<div class="border-b p-2"> <div class="border-b p-2">
<Button variant="outline" size="icon" aria-label="Home"> <Button variant="outline" size="icon" aria-label="Home">
@ -26,6 +26,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/lib/registry/default/
</Button> </Button>
</div> </div>
<nav class="grid gap-1 p-2"> <nav class="grid gap-1 p-2">
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<Button <Button
@ -41,6 +42,8 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/lib/registry/default/
Playground Playground
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<Button <Button
@ -56,6 +59,8 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/lib/registry/default/
Models Models
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<Button <Button
@ -71,6 +76,8 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/lib/registry/default/
API API
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<Button <Button
@ -86,6 +93,8 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/lib/registry/default/
Documentation Documentation
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<Button <Button
@ -101,8 +110,10 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/lib/registry/default/
Settings Settings
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</nav> </nav>
<nav class="mt-auto grid gap-1 p-2"> <nav class="mt-auto grid gap-1 p-2">
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<Button <Button
@ -118,6 +129,8 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/lib/registry/default/
Help Help
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<Button <Button
@ -133,6 +146,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/lib/registry/default/
Account Account
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</nav> </nav>
</aside> </aside>
<div class="flex flex-col"> <div class="flex flex-col">
@ -406,6 +420,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/lib/registry/default/
class="min-h-12 resize-none border-0 p-3 shadow-none focus-visible:ring-0" class="min-h-12 resize-none border-0 p-3 shadow-none focus-visible:ring-0"
/> />
<div class="flex items-center p-3 pt-0"> <div class="flex items-center p-3 pt-0">
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<Button variant="ghost" size="icon"> <Button variant="ghost" size="icon">
@ -417,6 +432,8 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/lib/registry/default/
Attach File Attach File
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<Button variant="ghost" size="icon"> <Button variant="ghost" size="icon">
@ -428,6 +445,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/lib/registry/default/
Use Microphone Use Microphone
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
<Button type="submit" size="sm" class="ml-auto gap-1.5"> <Button type="submit" size="sm" class="ml-auto gap-1.5">
Send Message Send Message
<CornerDownLeft class="size-3.5" /> <CornerDownLeft class="size-3.5" />

View File

@ -6,8 +6,6 @@ export const containerClass = 'w-full h-full'
<script setup lang="ts"> <script setup lang="ts">
import { import {
ChevronLeft,
ChevronRight,
CircleUser, CircleUser,
Copy, Copy,
CreditCard, CreditCard,
@ -42,11 +40,7 @@ import {
import { Sheet, SheetContent, SheetTrigger } from '@/lib/registry/default/ui/sheet' import { Sheet, SheetContent, SheetTrigger } from '@/lib/registry/default/ui/sheet'
import { import {
Pagination, Pagination,
PaginationEllipsis,
PaginationFirst,
PaginationLast,
PaginationList, PaginationList,
PaginationListItem,
PaginationNext, PaginationNext,
PaginationPrev, PaginationPrev,
} from '@/lib/registry/default/ui/pagination' } from '@/lib/registry/default/ui/pagination'
@ -69,6 +63,7 @@ import {
import { import {
Tooltip, Tooltip,
TooltipContent, TooltipContent,
TooltipProvider,
TooltipTrigger, TooltipTrigger,
} from '@/lib/registry/default/ui/tooltip' } from '@/lib/registry/default/ui/tooltip'
import { Checkbox } from '@/lib/registry/default/ui/checkbox' import { Checkbox } from '@/lib/registry/default/ui/checkbox'
@ -85,6 +80,7 @@ import { Checkbox } from '@/lib/registry/default/ui/checkbox'
<Package2 class="h-4 w-4 transition-all group-hover:scale-110" /> <Package2 class="h-4 w-4 transition-all group-hover:scale-110" />
<span class="sr-only">Acme Inc</span> <span class="sr-only">Acme Inc</span>
</a> </a>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<a <a
@ -99,6 +95,8 @@ import { Checkbox } from '@/lib/registry/default/ui/checkbox'
Dashboard Dashboard
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<a <a
@ -113,6 +111,9 @@ import { Checkbox } from '@/lib/registry/default/ui/checkbox'
Orders Orders
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<a <a
@ -127,6 +128,9 @@ import { Checkbox } from '@/lib/registry/default/ui/checkbox'
Products Products
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<a <a
@ -141,6 +145,9 @@ import { Checkbox } from '@/lib/registry/default/ui/checkbox'
Customers Customers
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<a <a
@ -155,8 +162,10 @@ import { Checkbox } from '@/lib/registry/default/ui/checkbox'
Analytics Analytics
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</nav> </nav>
<nav class="mt-auto flex flex-col items-center gap-4 px-2 sm:py-5"> <nav class="mt-auto flex flex-col items-center gap-4 px-2 sm:py-5">
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<a <a
@ -171,6 +180,7 @@ import { Checkbox } from '@/lib/registry/default/ui/checkbox'
Settings Settings
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</nav> </nav>
</aside> </aside>
<div class="flex flex-col sm:gap-4 sm:py-4 sm:pl-14"> <div class="flex flex-col sm:gap-4 sm:py-4 sm:pl-14">

View File

@ -53,6 +53,7 @@ import {
import { import {
Tooltip, Tooltip,
TooltipContent, TooltipContent,
TooltipProvider,
TooltipTrigger, TooltipTrigger,
} from '@/lib/registry/default/ui/tooltip' } from '@/lib/registry/default/ui/tooltip'
</script> </script>
@ -68,6 +69,7 @@ import {
<Package2 class="h-4 w-4 transition-all group-hover:scale-110" /> <Package2 class="h-4 w-4 transition-all group-hover:scale-110" />
<span class="sr-only">Acme Inc</span> <span class="sr-only">Acme Inc</span>
</a> </a>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<a <a
@ -82,6 +84,8 @@ import {
Dashboard Dashboard
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<a <a
@ -96,6 +100,8 @@ import {
Orders Orders
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<a <a
@ -110,6 +116,8 @@ import {
Products Products
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<a <a
@ -124,6 +132,8 @@ import {
Customers Customers
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<a <a
@ -138,8 +148,10 @@ import {
Analytics Analytics
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</nav> </nav>
<nav class="mt-auto flex flex-col items-center gap-4 px-2 py-4"> <nav class="mt-auto flex flex-col items-center gap-4 px-2 py-4">
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<a <a
@ -154,6 +166,7 @@ import {
Settings Settings
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</nav> </nav>
</aside> </aside>
<div class="flex flex-col sm:gap-4 sm:py-4 sm:pl-14"> <div class="flex flex-col sm:gap-4 sm:py-4 sm:pl-14">

View File

@ -56,6 +56,7 @@ import {
import { import {
Tooltip, Tooltip,
TooltipContent, TooltipContent,
TooltipProvider,
TooltipTrigger, TooltipTrigger,
} from '@/lib/registry/default/ui/tooltip' } from '@/lib/registry/default/ui/tooltip'
</script> </script>
@ -71,6 +72,7 @@ import {
<Package2 class="h-4 w-4 transition-all group-hover:scale-110" /> <Package2 class="h-4 w-4 transition-all group-hover:scale-110" />
<span class="sr-only">Acme Inc</span> <span class="sr-only">Acme Inc</span>
</a> </a>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<a <a
@ -85,6 +87,8 @@ import {
Dashboard Dashboard
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<a <a
@ -99,6 +103,8 @@ import {
Orders Orders
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<a <a
@ -113,6 +119,8 @@ import {
Products Products
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<a <a
@ -127,6 +135,8 @@ import {
Customers Customers
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<a <a
@ -141,8 +151,10 @@ import {
Analytics Analytics
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</nav> </nav>
<nav class="mt-auto flex flex-col items-center gap-4 px-2 sm:py-5"> <nav class="mt-auto flex flex-col items-center gap-4 px-2 sm:py-5">
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger as-child> <TooltipTrigger as-child>
<a <a
@ -157,6 +169,7 @@ import {
Settings Settings
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</nav> </nav>
</aside> </aside>
<div class="flex flex-col sm:gap-4 sm:py-4 sm:pl-14"> <div class="flex flex-col sm:gap-4 sm:py-4 sm:pl-14">

View File

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import * as z from 'zod' import * as z from 'zod'
import { h, onMounted, ref, shallowRef } from 'vue' import { h, onMounted, shallowRef } from 'vue'
import { Button } from '@/lib/registry/default/ui/button' import { Button } from '@/lib/registry/default/ui/button'
import { toast } from '@/lib/registry/default/ui/toast' import { toast } from '@/lib/registry/default/ui/toast'
import { AutoForm } from '@/lib/registry/default/ui/auto-form' import { AutoForm } from '@/lib/registry/default/ui/auto-form'

View File

@ -1,12 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import * as z from 'zod' import * as z from 'zod'
import { h, reactive, ref } from 'vue' import { h } from 'vue'
import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
import { DependencyType } from '../ui/auto-form/interface'
import { Button } from '@/lib/registry/default/ui/button' import { Button } from '@/lib/registry/default/ui/button'
import { toast } from '@/lib/registry/default/ui/toast' import { toast } from '@/lib/registry/default/ui/toast'
import type { Config } from '@/lib/registry/default/ui/auto-form'
import { AutoForm, AutoFormField } from '@/lib/registry/default/ui/auto-form' import { AutoForm, AutoFormField } from '@/lib/registry/default/ui/auto-form'
enum Sports { enum Sports {

View File

@ -3,7 +3,7 @@ import * as z from 'zod'
import { h } from 'vue' import { h } from 'vue'
import { Button } from '@/lib/registry/default/ui/button' import { Button } from '@/lib/registry/default/ui/button'
import { toast } from '@/lib/registry/default/ui/toast' import { toast } from '@/lib/registry/default/ui/toast'
import { AutoForm, AutoFormField } from '@/lib/registry/default/ui/auto-form' import { AutoForm } from '@/lib/registry/default/ui/auto-form'
const schema = z.object({ const schema = z.object({
subObject: z.object({ subObject: z.object({

View File

@ -32,7 +32,7 @@ const formSchema = toTypedSchema(z.object({
const placeholder = ref() const placeholder = ref()
const { handleSubmit, setValues, values } = useForm({ const { handleSubmit, setFieldValue, values } = useForm({
validationSchema: formSchema, validationSchema: formSchema,
}) })
@ -79,14 +79,10 @@ const onSubmit = handleSubmit((values) => {
:max-value="today(getLocalTimeZone())" :max-value="today(getLocalTimeZone())"
@update:model-value="(v) => { @update:model-value="(v) => {
if (v) { if (v) {
setValues({ setFieldValue('dob', v.toString())
dob: v.toString(),
})
} }
else { else {
setValues({ setFieldValue('dob', undefined)
dob: '',
})
} }
}" }"
/> />

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, type Ref, computed, toRef } from 'vue' import { type HTMLAttributes, type Ref, computed } from 'vue'
import { CalendarRoot, type CalendarRootEmits, type CalendarRootProps, useDateFormatter, useForwardPropsEmits } from 'radix-vue' import { CalendarRoot, type CalendarRootEmits, type CalendarRootProps, useDateFormatter, useForwardPropsEmits } from 'radix-vue'
import { createDecade, createYear, toDate } from 'radix-vue/date' import { createDecade, createYear, toDate } from 'radix-vue/date'
import { type DateValue, getLocalTimeZone, today } from '@internationalized/date' import { type DateValue, getLocalTimeZone, today } from '@internationalized/date'

View File

@ -40,7 +40,7 @@ import { Button } from '@/lib/registry/default/ui/button'
</SelectTrigger> </SelectTrigger>
<SelectContent position="popper"> <SelectContent position="popper">
<SelectItem value="nuxt"> <SelectItem value="nuxt">
Nuxt.js Nuxt
</SelectItem> </SelectItem>
<SelectItem value="next"> <SelectItem value="next">
Next.js Next.js

View File

@ -49,7 +49,7 @@ import {
Astro Astro
</SelectItem> </SelectItem>
<SelectItem value="nuxt"> <SelectItem value="nuxt">
Nuxt.js Nuxt
</SelectItem> </SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>

View File

@ -1,8 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue' import { ref } from 'vue'
import { Minus, Plus } from 'lucide-vue-next' import { Minus, Plus } from 'lucide-vue-next'
import { VisStackedBar, VisXYContainer } from '@unovis/vue' import { VisStackedBar, VisXYContainer } from '@unovis/vue'
import { useData } from 'vitepress'
import { Button } from '@/lib/registry/default/ui/button' import { Button } from '@/lib/registry/default/ui/button'
import { import {
@ -13,8 +12,6 @@ import {
CardHeader, CardHeader,
CardTitle, CardTitle,
} from '@/lib/registry/default/ui/card' } from '@/lib/registry/default/ui/card'
import { themes } from '@/lib/registry/themes'
import { useConfigStore } from '@/stores/config'
const goal = ref(350) const goal = ref(350)

View File

@ -8,7 +8,6 @@ import {
CardHeader, CardHeader,
CardTitle, CardTitle,
} from '@/lib/registry/default/ui/card' } from '@/lib/registry/default/ui/card'
import { useConfigStore } from '@/stores/config'
type Data = typeof data[number] type Data = typeof data[number]
const data = [ const data = [

View File

@ -21,7 +21,7 @@ import {
const frameworks = [ const frameworks = [
{ value: 'next.js', label: 'Next.js' }, { value: 'next.js', label: 'Next.js' },
{ value: 'sveltekit', label: 'SvelteKit' }, { value: 'sveltekit', label: 'SvelteKit' },
{ value: 'nuxt.js', label: 'Nuxt.js' }, { value: 'nuxt', label: 'Nuxt' },
{ value: 'remix', label: 'Remix' }, { value: 'remix', label: 'Remix' },
{ value: 'astro', label: 'Astro' }, { value: 'astro', label: 'Astro' },
] ]

View File

@ -48,7 +48,7 @@ const formSchema = toTypedSchema(z.object({
}), }),
})) }))
const { handleSubmit, setValues, values } = useForm({ const { handleSubmit, setFieldValue, values } = useForm({
validationSchema: formSchema, validationSchema: formSchema,
}) })
@ -91,9 +91,7 @@ const onSubmit = handleSubmit((values) => {
:key="language.value" :key="language.value"
:value="language.label" :value="language.label"
@select="() => { @select="() => {
setValues({ setFieldValue('language', language.value)
language: language.value,
})
}" }"
> >
<Check <Check

View File

@ -60,7 +60,7 @@ const statuses: Status[] = [
] ]
const open = ref(false) const open = ref(false)
const value = ref<typeof statuses[number]>() // const value = ref<typeof statuses[number]>()
const selectedStatus = ref<Status>() const selectedStatus = ref<Status>()
</script> </script>

View File

@ -32,7 +32,7 @@ const formSchema = toTypedSchema(z.object({
const placeholder = ref() const placeholder = ref()
const { handleSubmit, setValues, values } = useForm({ const { handleSubmit, setFieldValue, values } = useForm({
validationSchema: formSchema, validationSchema: formSchema,
initialValues: { initialValues: {
@ -82,14 +82,10 @@ const onSubmit = handleSubmit((values) => {
:max-value="today(getLocalTimeZone())" :max-value="today(getLocalTimeZone())"
@update:model-value="(v) => { @update:model-value="(v) => {
if (v) { if (v) {
setValues({ setFieldValue('dob', v.toString())
dob: v.toString(),
})
} }
else { else {
setValues({ setFieldValue('dob', undefined)
dob: '',
})
} }
}" }"

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { DonutChart } from '@/lib/registry/new-york/ui/chart-donut' import { DonutChart } from '@/lib/registry/default/ui/chart-donut'
const data = [ const data = [
{ name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 }, { name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },

View File

@ -0,0 +1,30 @@
<script setup lang="ts">
import {
NumberField,
NumberFieldContent,
NumberFieldDecrement,
NumberFieldIncrement,
NumberFieldInput,
} from '@/lib/registry/default/ui/number-field'
import { Label } from '@/lib/registry/default/ui/label'
</script>
<template>
<NumberField
id="balance"
:default-value="1500"
:format-options="{
style: 'currency',
currency: 'EUR',
currencyDisplay: 'code',
currencySign: 'accounting',
}"
>
<Label for="balance">Balance</Label>
<NumberFieldContent>
<NumberFieldDecrement />
<NumberFieldInput />
<NumberFieldIncrement />
</NumberFieldContent>
</NumberField>
</template>

View File

@ -0,0 +1,28 @@
<script setup lang="ts">
import {
NumberField,
NumberFieldContent,
NumberFieldDecrement,
NumberFieldIncrement,
NumberFieldInput,
} from '@/lib/registry/default/ui/number-field'
import { Label } from '@/lib/registry/default/ui/label'
</script>
<template>
<NumberField
id="number"
:default-value="5"
:format-options="{
signDisplay: 'exceptZero',
minimumFractionDigits: 1,
}"
>
<Label for="number">Number</Label>
<NumberFieldContent>
<NumberFieldDecrement />
<NumberFieldInput />
<NumberFieldIncrement />
</NumberFieldContent>
</NumberField>
</template>

View File

@ -0,0 +1,21 @@
<script setup lang="ts">
import {
NumberField,
NumberFieldContent,
NumberFieldDecrement,
NumberFieldIncrement,
NumberFieldInput,
} from '@/lib/registry/default/ui/number-field'
import { Label } from '@/lib/registry/default/ui/label'
</script>
<template>
<NumberField id="age" :default-value="18" :min="0">
<Label for="age">Age</Label>
<NumberFieldContent>
<NumberFieldDecrement />
<NumberFieldInput />
<NumberFieldIncrement />
</NumberFieldContent>
</NumberField>
</template>

View File

@ -0,0 +1,21 @@
<script setup lang="ts">
import {
NumberField,
NumberFieldContent,
NumberFieldDecrement,
NumberFieldIncrement,
NumberFieldInput,
} from '@/lib/registry/default/ui/number-field'
import { Label } from '@/lib/registry/default/ui/label'
</script>
<template>
<NumberField id="age-disabled" :default-value="18" disabled>
<Label for="age-disabled">Age</Label>
<NumberFieldContent>
<NumberFieldDecrement />
<NumberFieldInput />
<NumberFieldIncrement />
</NumberFieldContent>
</NumberField>
</template>

View File

@ -0,0 +1,85 @@
<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 { Button } from '@/lib/registry/default/ui/button'
import {
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/lib/registry/default/ui/form'
import {
NumberField,
NumberFieldContent,
NumberFieldDecrement,
NumberFieldIncrement,
NumberFieldInput,
} from '@/lib/registry/default/ui/number-field'
import { toast } from '@/lib/registry/default/ui/toast'
const formSchema = toTypedSchema(z.object({
payment: z.number().min(10, 'Min 10 euros to send payment').max(5000, 'Max 5000 euros to send payment'),
}))
const { handleSubmit, setFieldValue } = useForm({
validationSchema: formSchema,
initialValues: {
payment: 10,
},
})
const onSubmit = handleSubmit((values) => {
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(values, null, 2))),
})
})
</script>
<template>
<form class="w-2/3 space-y-6" @submit="onSubmit">
<FormField name="payment">
<FormItem>
<FormLabel>Payment</FormLabel>
<NumberField
class="gap-2"
:min="0"
:format-options="{
style: 'currency',
currency: 'EUR',
currencyDisplay: 'code',
currencySign: 'accounting',
}"
@update:model-value="(v) => {
if (v) {
setFieldValue('payment', v)
}
else {
setFieldValue('payment', undefined)
}
}"
>
<NumberFieldContent>
<NumberFieldDecrement />
<FormControl>
<NumberFieldInput />
</FormControl>
<NumberFieldIncrement />
</NumberFieldContent>
</NumberField>
<FormDescription>
Enter value between 10 and 5000.
</FormDescription>
<FormMessage />
</FormItem>
</FormField>
<Button type="submit">
Submit
</Button>
</form>
</template>

View File

@ -0,0 +1,28 @@
<script setup lang="ts">
import {
NumberField,
NumberFieldContent,
NumberFieldDecrement,
NumberFieldIncrement,
NumberFieldInput,
} from '@/lib/registry/default/ui/number-field'
import { Label } from '@/lib/registry/default/ui/label'
</script>
<template>
<NumberField
id="percent"
:default-value="0.05"
:step="0.01"
:format-options="{
style: 'percent',
}"
>
<Label for="percent">Percent</Label>
<NumberFieldContent>
<NumberFieldDecrement />
<NumberFieldInput />
<NumberFieldIncrement />
</NumberFieldContent>
</NumberField>
</template>

View File

@ -23,7 +23,7 @@ const formSchema = toTypedSchema(z.object({
pin: z.array(z.coerce.string()).length(5, { message: 'Invalid input' }), pin: z.array(z.coerce.string()).length(5, { message: 'Invalid input' }),
})) }))
const { handleSubmit, setValues } = useForm({ const { handleSubmit, setFieldValue } = useForm({
validationSchema: formSchema, validationSchema: formSchema,
initialValues: { initialValues: {
pin: ['1', '2', '3'], pin: ['1', '2', '3'],
@ -56,9 +56,7 @@ const handleComplete = (e: string[]) => console.log(e.join(''))
:name="componentField.name" :name="componentField.name"
@complete="handleComplete" @complete="handleComplete"
@update:model-value="(arrStr) => { @update:model-value="(arrStr) => {
setValues({ setFieldValue('pin', arrStr.filter(Boolean))
pin: arrStr.filter(Boolean),
})
}" }"
> >
<PinInputGroup> <PinInputGroup>

View File

@ -12,7 +12,7 @@ import { Separator } from '@/lib/registry/default/ui/separator'
An open-source UI component library. An open-source UI component library.
</p> </p>
</div> </div>
<Separator class="my-4" /> <Separator class="my-4" label="Or" />
<div class="flex h-5 items-center space-x-4 text-sm"> <div class="flex h-5 items-center space-x-4 text-sm">
<div>Blog</div> <div>Blog</div>
<Separator orientation="vertical" /> <Separator orientation="vertical" />

View File

@ -0,0 +1,53 @@
<script setup lang="ts">
import { toast } from 'vue-sonner'
import { Button } from '@/lib/registry/default/ui/button'
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/lib/registry/default/ui/dialog'
</script>
<template>
<Dialog>
<DialogTrigger as-child>
<Button variant="outline">
Dialog with toast
</Button>
</DialogTrigger>
<DialogContent
class="sm:max-w-md"
@interact-outside="event => {
const target = event.target as HTMLElement;
if (target?.closest('[data-sonner-toaster]')) return event.preventDefault()
}"
>
<DialogHeader>
<DialogTitle>Vue Sonner Toast</DialogTitle>
<DialogDescription> Dialog with toast </DialogDescription>
</DialogHeader>
<div class="grid gap-4">
<Button
size="sm"
class="px-3"
@click="
() => {
toast('Event has been created', {
description: 'Sunday, December 03, 2023 at 9:00 AM',
action: {
label: 'Undo',
onClick: () => console.log('Undo'),
},
});
}
"
>
Toast
</Button>
</div>
</DialogContent>
</Dialog>
</template>

View File

@ -7,7 +7,7 @@ import { TagsInput, TagsInputInput, TagsInputItem, TagsInputItemDelete, TagsInpu
const frameworks = [ const frameworks = [
{ value: 'next.js', label: 'Next.js' }, { value: 'next.js', label: 'Next.js' },
{ value: 'sveltekit', label: 'SvelteKit' }, { value: 'sveltekit', label: 'SvelteKit' },
{ value: 'nuxt.js', label: 'Nuxt.js' }, { value: 'nuxt', label: 'Nuxt' },
{ value: 'remix', label: 'Remix' }, { value: 'remix', label: 'Remix' },
{ value: 'astro', label: 'Astro' }, { value: 'astro', label: 'Astro' },
] ]

View File

@ -5,7 +5,7 @@ import { Toggle } from '@/lib/registry/default/ui/toggle'
</script> </script>
<template> <template>
<Toggle aria-label="Toggle italic"> <Toggle aria-label="Toggle bold">
<Bold class="h-4 w-4" /> <Bold class="h-4 w-4" />
</Toggle> </Toggle>
</template> </template>

View File

@ -5,7 +5,7 @@ import { Toggle } from '@/lib/registry/default/ui/toggle'
</script> </script>
<template> <template>
<Toggle aria-label="Toggle italic" disabled> <Toggle aria-label="Toggle underline" disabled>
<Underline class="w-4 h-4" /> <Underline class="w-4 h-4" />
</Toggle> </Toggle>
</template> </template>

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="text-lg font-semibold"> <div class="text-lg font-semibold">
Are you sure absolutely sure? Are you absolutely sure?
</div> </div>
</template> </template>

View File

@ -26,7 +26,7 @@ const date = ref({
id="date" id="date"
:variant="'outline'" :variant="'outline'"
:class="cn( :class="cn(
'w-[300px] justify-start text-left font-normal', 'w-[280px] justify-start text-left font-normal',
!date && 'text-muted-foreground', !date && 'text-muted-foreground',
)" )"
> >

View File

@ -26,7 +26,7 @@ const date = ref({
id="date" id="date"
:variant="'outline'" :variant="'outline'"
:class="cn( :class="cn(
'w-[300px] justify-start text-left font-normal', 'w-[280px] justify-start text-left font-normal',
!date && 'text-muted-foreground', !date && 'text-muted-foreground',
)" )"
> >

View File

@ -1,5 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'
import AutoFormLabel from './AutoFormLabel.vue' import AutoFormLabel from './AutoFormLabel.vue'
import { beautifyObjectName } from './utils' import { beautifyObjectName } from './utils'
import type { FieldProps } from './interface' import type { FieldProps } from './interface'

View File

@ -28,7 +28,8 @@ const shapes = computed(() => {
return return
Object.keys(shape).forEach((name) => { Object.keys(shape).forEach((name) => {
const item = shape[name] as ZodAny const item = shape[name] as ZodAny
let options = 'values' in item._def ? item._def.values as string[] : undefined const baseItem = getBaseSchema(item) as ZodAny
let options = (baseItem && 'values' in baseItem._def) ? baseItem._def.values as string[] : undefined
if (!Array.isArray(options) && typeof options === 'object') if (!Array.isArray(options) && typeof options === 'object')
options = Object.values(options) options = Object.values(options)

View File

@ -1,4 +1,4 @@
import type { Component, InputHTMLAttributes, SelectHTMLAttributes } from 'vue' import type { Component, InputHTMLAttributes } from 'vue'
import type { ZodAny, z } from 'zod' import type { ZodAny, z } from 'zod'
import type { INPUT_COMPONENTS } from './constant' import type { INPUT_COMPONENTS } from './constant'

View File

@ -122,7 +122,7 @@ type NestedRecord = Record<string, unknown> | { [k: string]: NestedRecord }
* Checks if the path opted out of nested fields using `[fieldName]` syntax * Checks if the path opted out of nested fields using `[fieldName]` syntax
*/ */
export function isNotNestedPath(path: string) { export function isNotNestedPath(path: string) {
return /^\[.+\]$/i.test(path) return /^\[.+\]$/.test(path)
} }
function isObject(obj: unknown): obj is Record<string, unknown> { function isObject(obj: unknown): obj is Record<string, unknown> {
return obj !== null && !!obj && typeof obj === 'object' && !Array.isArray(obj) return obj !== null && !!obj && typeof obj === 'object' && !Array.isArray(obj)
@ -132,7 +132,7 @@ function isContainerValue(value: unknown): value is Record<string, unknown> {
} }
function cleanupNonNestedPath(path: string) { function cleanupNonNestedPath(path: string) {
if (isNotNestedPath(path)) if (isNotNestedPath(path))
return path.replace(/\[|\]/gi, '') return path.replace(/\[|\]/g, '')
return path return path
} }

View File

@ -12,9 +12,8 @@ const { scrollTo, selectedIndex, scrollSnaps, orientation } = useCarousel()
:class="cn('flex gap-2 justify-center relative -translate-x-1/2 left-1/2', { 'top-10': orientation === 'vertical' }, props.class)" :class="cn('flex gap-2 justify-center relative -translate-x-1/2 left-1/2', { 'top-10': orientation === 'vertical' }, props.class)"
> >
<div <div
v-for="(_, index) in scrollSnaps" :key="index" class="border-1 w-4 h-4 rounded-full" v-for="(_, index) in scrollSnaps" :key="index" class="w-2 h-2 rounded-full"
:class="cn(index === selectedIndex ? 'border-transparent bg-primary' : 'bg-border', props.class)" :class="cn(index === selectedIndex ? 'border-transparent bg-primary' : 'bg-border')" @click="scrollTo(index)"
@click="scrollTo(index)"
/> />
</div> </div>
</template> </template>

View File

@ -25,6 +25,7 @@ const { orientation, canScrollNext, scrollNext } = useCarousel()
> >
<slot> <slot>
<ArrowRight class="h-4 w-4 text-current" /> <ArrowRight class="h-4 w-4 text-current" />
<span class="sr-only">Next Slide</span>
</slot> </slot>
</Button> </Button>
</template> </template>

View File

@ -25,6 +25,7 @@ const { orientation, canScrollPrev, scrollPrev } = useCarousel()
> >
<slot> <slot>
<ArrowLeft class="h-4 w-4 text-current" /> <ArrowLeft class="h-4 w-4 text-current" />
<span class="sr-only">Previous Slide</span>
</slot> </slot>
</Button> </Button>
</template> </template>

View File

@ -7,5 +7,5 @@ export { default as CarouselDotButtons } from './CarouselDotButtons.vue'
export { useCarousel } from './useCarousel' export { useCarousel } from './useCarousel'
export type { export type {
EmblaCarouselType as CarouselApi, UnwrapRefCarouselApi as CarouselApi,
} from 'embla-carousel' } from './interface'

View File

@ -1,18 +1,24 @@
import type { HTMLAttributes, UnwrapRef } from 'vue'
import type useEmblaCarousel from 'embla-carousel-vue'
import type { import type {
EmblaCarouselType as CarouselApi, EmblaCarouselVueType,
EmblaOptionsType as CarouselOptions, } from 'embla-carousel-vue'
EmblaPluginType as CarouselPlugin,
} from 'embla-carousel' type CarouselApi = EmblaCarouselVueType[1]
import type { HTMLAttributes, Ref } from 'vue' type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
type CarouselOptions = UseCarouselParameters[0]
type CarouselPlugin = UseCarouselParameters[1]
export type UnwrapRefCarouselApi = UnwrapRef<CarouselApi>
export interface CarouselProps { export interface CarouselProps {
opts?: CarouselOptions | Ref<CarouselOptions> opts?: CarouselOptions
plugins?: CarouselPlugin[] | Ref<CarouselPlugin[]> plugins?: CarouselPlugin
orientation?: 'horizontal' | 'vertical' orientation?: 'horizontal' | 'vertical'
} }
export interface CarouselEmits { export interface CarouselEmits {
(e: 'init-api', payload: CarouselApi): void (e: 'init-api', payload: UnwrapRefCarouselApi): void
} }
export interface WithClassAsProps { export interface WithClassAsProps {

View File

@ -1,10 +1,7 @@
import { createInjectionState } from '@vueuse/core' import { createInjectionState } from '@vueuse/core'
import emblaCarouselVue from 'embla-carousel-vue' import emblaCarouselVue from 'embla-carousel-vue'
import { onMounted, ref } from 'vue' import { onMounted, ref } from 'vue'
import type { import type { UnwrapRefCarouselApi as CarouselApi, CarouselEmits, CarouselProps } from './interface'
EmblaCarouselType as CarouselApi,
} from 'embla-carousel'
import type { CarouselEmits, CarouselProps } from './interface'
const [useProvideCarousel, useInjectCarousel] = createInjectionState( const [useProvideCarousel, useInjectCarousel] = createInjectionState(
({ ({
@ -27,16 +24,16 @@ const [useProvideCarousel, useInjectCarousel] = createInjectionState(
emblaApi.value?.scrollTo(index) emblaApi.value?.scrollTo(index)
} }
const canScrollNext = ref(true) const canScrollNext = ref(false)
const canScrollPrev = ref(true) const canScrollPrev = ref(false)
const selectedIndex = ref(0) const selectedIndex = ref(0)
const scrollSnaps = ref([]) const scrollSnaps = ref<number[]>([])
function onSelect(api: CarouselApi) { function onSelect(api: CarouselApi) {
canScrollNext.value = api.canScrollNext() canScrollNext.value = api?.canScrollNext() || false
canScrollPrev.value = api.canScrollPrev() canScrollPrev.value = api?.canScrollPrev() || false
selectedIndex.value = api.selectedScrollSnap() selectedIndex.value = api?.selectedScrollSnap() || 0
scrollSnaps.value = api.scrollSnapList() scrollSnaps.value = api?.scrollSnapList() || []
} }
onMounted(() => { onMounted(() => {

View File

@ -4,7 +4,8 @@ import { VisArea, VisAxis, VisLine, VisXYContainer } from '@unovis/vue'
import { Area, Axis, Line } from '@unovis/ts' import { Area, Axis, Line } from '@unovis/ts'
import { type Component, computed, ref } from 'vue' import { type Component, computed, ref } from 'vue'
import { useMounted } from '@vueuse/core' import { useMounted } from '@vueuse/core'
import { type BaseChartProps, ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/default/ui/chart' import type { BaseChartProps } from '.'
import { ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/default/ui/chart'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = withDefaults(defineProps<BaseChartProps<T> & { const props = withDefaults(defineProps<BaseChartProps<T> & {

View File

@ -1 +1,66 @@
export { default as AreaChart } from './AreaChart.vue' export { default as AreaChart } from './AreaChart.vue'
import type { Spacing } from '@unovis/ts'
type KeyOf<T extends Record<string, any>> = Extract<keyof T, string>
export interface BaseChartProps<T extends Record<string, any>> {
/**
* The source data, in which each entry is a dictionary.
*/
data: T[]
/**
* Select the categories from your data. Used to populate the legend and toolip.
*/
categories: KeyOf<T>[]
/**
* Sets the key to map the data to the axis.
*/
index: KeyOf<T>
/**
* Change the default colors.
*/
colors?: string[]
/**
* Margin of each the container
*/
margin?: Spacing
/**
* Change the opacity of the non-selected field
* @default 0.2
*/
filterOpacity?: number
/**
* Function to format X label
*/
xFormatter?: (tick: number | Date, i: number, ticks: number[] | Date[]) => string
/**
* Function to format Y label
*/
yFormatter?: (tick: number | Date, i: number, ticks: number[] | Date[]) => string
/**
* Controls the visibility of the X axis.
* @default true
*/
showXAxis?: boolean
/**
* Controls the visibility of the Y axis.
* @default true
*/
showYAxis?: boolean
/**
* Controls the visibility of tooltip.
* @default true
*/
showTooltip?: boolean
/**
* Controls the visibility of legend.
* @default true
*/
showLegend?: boolean
/**
* Controls the visibility of gridline.
* @default true
*/
showGridLine?: boolean
}

View File

@ -1,10 +1,11 @@
<script setup lang="ts" generic="T extends Record<string, any>"> <script setup lang="ts" generic="T extends Record<string, any>">
import type { BulletLegendItemInterface, Spacing } from '@unovis/ts' import type { BulletLegendItemInterface } from '@unovis/ts'
import { VisAxis, VisGroupedBar, VisStackedBar, VisXYContainer } from '@unovis/vue' import { VisAxis, VisGroupedBar, VisStackedBar, VisXYContainer } from '@unovis/vue'
import { Axis, GroupedBar, StackedBar } from '@unovis/ts' import { Axis, GroupedBar, StackedBar } from '@unovis/ts'
import { type Component, computed, ref } from 'vue' import { type Component, computed, ref } from 'vue'
import { useMounted } from '@vueuse/core' import { useMounted } from '@vueuse/core'
import { type BaseChartProps, ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/default/ui/chart' import type { BaseChartProps } from '.'
import { ChartCrosshair, ChartLegend, defaultColors } from '@/lib/registry/default/ui/chart'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = withDefaults(defineProps<BaseChartProps<T> & { const props = withDefaults(defineProps<BaseChartProps<T> & {

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