Merge remote-tracking branch 'origin/dev' into charting

This commit is contained in:
zernonia 2023-12-05 09:52:06 +08:00
commit a84e1ece5c
77 changed files with 6447 additions and 1158 deletions

View File

@ -17,6 +17,9 @@ export default defineConfig({
['link', { rel: 'apple-touch-icon', href: '/apple-touch-icon.png' }], ['link', { rel: 'apple-touch-icon', href: '/apple-touch-icon.png' }],
['link', { rel: 'manifest', href: '/site.webmanifest' }], ['link', { rel: 'manifest', href: '/site.webmanifest' }],
['meta', { name: 'theme-color', media: '(prefers-color-scheme: light)', content: 'white' }],
['meta', { name: 'theme-color', media: '(prefers-color-scheme: dark)', content: 'black' }],
['meta', { name: 'creator', content: 'radix-vue' }], ['meta', { name: 'creator', content: 'radix-vue' }],
['meta', { name: 'theme-color', content: '#41b883' }], ['meta', { name: 'theme-color', content: '#41b883' }],
['meta', { name: 'og:type', content: 'website' }], ['meta', { name: 'og:type', content: 'website' }],

View File

@ -4,7 +4,6 @@ import PageHeaderHeading from '../components/PageHeaderHeading.vue'
import PageHeaderDescription from '../components/PageHeaderDescription.vue' import PageHeaderDescription from '../components/PageHeaderDescription.vue'
import ExamplesNav from '../components/ExamplesNav.vue' import ExamplesNav from '../components/ExamplesNav.vue'
import { announcementConfig } from '../config/site' import { announcementConfig } from '../config/site'
import ArrowRightIcon from '~icons/radix-icons/arrow-right'
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'

View File

@ -1,5 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
import { Button } from '@/lib/registry/default/ui/button'
import { import {
Tooltip, Tooltip,
TooltipContent, TooltipContent,

View File

@ -44,14 +44,14 @@ const { theme, setTheme } = useConfigStore()
class="flex h-9 w-9 items-center justify-center rounded-full border-2 border-border text-xs" class="flex h-9 w-9 items-center justify-center rounded-full border-2 border-border text-xs"
:class=" :class="
color === theme color === theme
? 'border-foreground' ? 'border-primary'
: 'border-transparent' : 'border-transparent'
" "
@click="setTheme(color)" @click="setTheme(color)"
> >
<span <span
class="flex h-6 w-6 items-center justify-center rounded-full" class="flex h-6 w-6 items-center justify-center rounded-full"
:style="{ backgroundColor: colors[color][7].rgb }" :style="{ backgroundColor: colors[color][6].rgb }"
> >
<RadixIconsCheck <RadixIconsCheck
v-if="color === theme" v-if="color === theme"
@ -63,7 +63,7 @@ const { theme, setTheme } = useConfigStore()
<TooltipContent <TooltipContent
align="center" align="center"
:side-offset="1" :side-offset="1"
class="capitalize" class="capitalize bg-zinc-900 text-zinc-50"
> >
{{ allColors[index] }} {{ allColors[index] }}
</TooltipContent> </TooltipContent>

View File

@ -1,3 +1,11 @@
html {
color-scheme: light;
}
html.dark {
color-scheme: dark;
}
.theme-zinc { .theme-zinc {
--background: 0 0% 100%; --background: 0 0% 100%;
--foreground: 240 10% 3.9%; --foreground: 240 10% 3.9%;

View File

@ -7,7 +7,7 @@ import cssRaw from '../../../../../packages/cli/test/fixtures/nuxt/assets/css/ta
import { type Style } from '@/lib/registry/styles' import { type Style } from '@/lib/registry/styles'
export function makeCodeSandboxParams(componentName: string, style: Style, sources: Record<string, string>) { export function makeCodeSandboxParams(componentName: string, style: Style, sources: Record<string, string>) {
let files = {} let files: Record<string, any> = {}
files = constructFiles(componentName, style, sources) files = constructFiles(componentName, style, sources)
files['.codesandbox/Dockerfile'] = { files['.codesandbox/Dockerfile'] = {
content: 'FROM node:18', content: 'FROM node:18',

View File

@ -1,7 +1,7 @@
{ {
"name": "www", "name": "www",
"type": "module", "type": "module",
"version": "0.8.2", "version": "0.8.4",
"files": [ "files": [
"dist" "dist"
], ],
@ -9,7 +9,9 @@
"dev": "vitepress dev", "dev": "vitepress dev",
"build": "vitepress build", "build": "vitepress build",
"preview": "vitepress preview", "preview": "vitepress preview",
"build:registry": "tsx ./scripts/build-registry.ts" "typecheck": "vue-tsc --noEmit",
"typecheck:registry": "vue-tsc --noEmit -p tsconfig.registry.json",
"build:registry": "pnpm typecheck:registry && tsx ./scripts/build-registry.ts"
}, },
"dependencies": { "dependencies": {
"@formkit/auto-animate": "^0.8.0", "@formkit/auto-animate": "^0.8.0",
@ -26,7 +28,7 @@
"codesandbox": "^2.2.3", "codesandbox": "^2.2.3",
"date-fns": "^2.30.0", "date-fns": "^2.30.0",
"lucide-vue-next": "^0.276.0", "lucide-vue-next": "^0.276.0",
"radix-vue": "^1.1.1", "radix-vue": "^1.2.3",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"v-calendar": "^3.1.2", "v-calendar": "^3.1.2",
"vee-validate": "4.11.8", "vee-validate": "4.11.8",
@ -45,9 +47,10 @@
"@vitejs/plugin-vue-jsx": "^3.0.2", "@vitejs/plugin-vue-jsx": "^3.0.2",
"@vue/compiler-core": "^3.3.7", "@vue/compiler-core": "^3.3.7",
"@vue/compiler-dom": "^3.3.7", "@vue/compiler-dom": "^3.3.7",
"@vue/tsconfig": "^0.4.0",
"autoprefixer": "^10.4.16", "autoprefixer": "^10.4.16",
"lodash.template": "^4.5.0", "lodash.template": "^4.5.0",
"radix-vue": "^1.1.1", "pathe": "^1.1.1",
"rimraf": "^5.0.5", "rimraf": "^5.0.5",
"tailwind-merge": "^2.0.0", "tailwind-merge": "^2.0.0",
"tailwindcss": "^3.3.5", "tailwindcss": "^3.3.5",

View File

@ -11,10 +11,45 @@ primitive: https://www.radix-vue.com/components/collapsible.html
## Installation ## Installation
<Steps>
### Run the following command
```bash ```bash
npx shadcn-vue@latest add collapsible npx shadcn-vue@latest add collapsible
``` ```
### Update `tailwind.config.js`
Add the following animations to your `tailwind.config.js` file:
```js title="tailwind.config.js" {5-18}
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
extend: {
keyframes: {
'collapsible-down': {
from: { height: 0 },
to: { height: 'var(--radix-collapsible-content-height)' },
},
'collapsible-up': {
from: { height: 'var(--radix-collapsible-content-height)' },
to: { height: 0 },
},
},
animation: {
'collapsible-down': 'collapsible-down 0.2s ease-in-out',
'collapsible-up': 'collapsible-up 0.2s ease-in-out',
},
},
},
}
```
</Steps>
## Usage ## Usage
```vue ```vue

View File

@ -6,6 +6,14 @@ component: true
<ComponentPreview name="ComboboxDemo" /> <ComponentPreview name="ComboboxDemo" />
<br>
<Callout title="Note" class="bg-destructive">
[Radix Vue](https://github.com/radix-vue/radix-vue/releases/tag/v1.2.0) introduced a breaking change. You will need to wrap `ComboboxGroup` and `ComboboxItem` inside of `ComboboxList` now.
</Callout>
## Installation ## Installation
The Combobox is built using a composition of the `<Popover />` and the `<Command />` components. The Combobox is built using a composition of the `<Popover />` and the `<Command />` components.
@ -27,6 +35,7 @@ import {
CommandGroup, CommandGroup,
CommandInput, CommandInput,
CommandItem, CommandItem,
CommandList
} from '@/components/ui/command' } from '@/components/ui/command'
import { import {
Popover, Popover,
@ -64,6 +73,7 @@ const value = ref({})
<Command v-model="value"> <Command v-model="value">
<CommandInput placeholder="Search framework..." /> <CommandInput placeholder="Search framework..." />
<CommandEmpty>No framework found.</CommandEmpty> <CommandEmpty>No framework found.</CommandEmpty>
<CommandList>
<CommandGroup> <CommandGroup>
<CommandItem <CommandItem
v-for="framework in frameworks" v-for="framework in frameworks"
@ -80,6 +90,7 @@ const value = ref({})
{{ framework.label }} {{ framework.label }}
</CommandItem> </CommandItem>
</CommandGroup> </CommandGroup>
</CommandList>
</Command> </Command>
</PopoverContent> </PopoverContent>
</Popover> </Popover>

View File

@ -76,7 +76,7 @@ To activate the `Dialog` component from within a `Context Menu` or `Dropdown Men
</ContextMenu> </ContextMenu>
<DialogContent> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle>Are you sure absolutely sure?</DialogTitle> <DialogTitle>Are you absolutely sure?</DialogTitle>
<DialogDescription> <DialogDescription>
This action cannot be undone. Are you sure you want to permanently This action cannot be undone. Are you sure you want to permanently
delete this file from our servers? delete this file from our servers?

View File

@ -209,7 +209,7 @@ const onSubmit = form.handleSubmit((values) => {
<template> <template>
<form @submit="onSubmit"> <form @submit="onSubmit">
... ...
</Form> </form>
</template> </template>
``` ```
@ -302,7 +302,7 @@ const onSubmit = form.handleSubmit((values) => {
<Button type="submit"> <Button type="submit">
Submit Submit
</Button> </Button>
</Form> </form>
</template> </template>
``` ```

View File

@ -26,31 +26,28 @@ npm install -D typescript
npm install -D @nuxtjs/tailwindcss npm install -D @nuxtjs/tailwindcss
``` ```
### Install `shadcn-nuxt` module (New ✨)
```bash
npm install -D shadcn-nuxt
```
### Configure `nuxt.config.ts` ### Configure `nuxt.config.ts`
<Callout class="mt-4">
**Tip:** It's better to use Nuxt `components:dirs` hook to extend auto-import components directories.
If you use `components` key in `nuxt.config.ts` default config will disposed
</Callout>
```ts ```ts
export default defineNuxtConfig({ export default defineNuxtConfig({
modules: ['@nuxtjs/tailwindcss'], modules: ['@nuxtjs/tailwindcss', 'shadcn-nuxt'],
hooks: { shadcn: {
'components:dirs': (dirs) => { /**
dirs.unshift({ * Prefix for all the imported component
path: '~/components/ui', */
// this is required else Nuxt will autoImport `.ts` file prefix: '',
extensions: ['.vue'], /**
// prefix for your components, eg: UiButton * Directory that the component lives in.
prefix: 'Ui', * @default "./components/ui"
// prevent adding another prefix component by it's path. */
pathPrefix: false componentDir: './components/ui'
})
}
} }
}) })
``` ```
@ -133,7 +130,7 @@ The command above will add the `Button` component to your project. Nuxt autoImpo
```vue {3} ```vue {3}
<template> <template>
<div> <div>
<UiButton>Click me</UiButton> <Button>Click me</Button>
</div> </div>
</template> </template>
``` ```

View File

@ -131,7 +131,7 @@ const selectedTeam = ref<Team>(groups[0].teams[0])
<CommandGroup> <CommandGroup>
<DialogTrigger as-child> <DialogTrigger as-child>
<CommandItem <CommandItem
:value="{ label: 'Create Team' }" value="create-team"
@select="() => { @select="() => {
open = false open = false
showNewTeamDialog = true showNewTeamDialog = true

View File

@ -3,7 +3,6 @@ import { h, ref } from 'vue'
import * as z from 'zod' import * as z from 'zod'
import { format } from 'date-fns' import { format } from 'date-fns'
import { toTypedSchema } from '@vee-validate/zod' import { toTypedSchema } from '@vee-validate/zod'
import { configure } from 'vee-validate'
import { Check, ChevronsUpDown } from 'lucide-vue-next' import { Check, ChevronsUpDown } from 'lucide-vue-next'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'

View File

@ -7,7 +7,7 @@ import { Cross1Icon } from '@radix-icons/vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { Input } from '@/lib/registry/new-york/ui/input' import { Input } from '@/lib/registry/new-york/ui/input'
import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/lib/registry/default/ui/form' import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/lib/registry/default/ui/form'
import { Separator } from '@/lib/registry/new-york/ui/separator' import { Separator } from '@/lib/registry/new-york/ui/separator'
import { Textarea } from '@/lib/registry/new-york/ui/textarea' import { Textarea } from '@/lib/registry/new-york/ui/textarea'
import { import {

View File

@ -236,7 +236,7 @@ import CounterClockwiseClockIcon from '~icons/radix-icons/counter-clockwise-cloc
</TabsTrigger> </TabsTrigger>
</TabsList> </TabsList>
</div> </div>
<ModelSelector :types="types" :models="models" /> <ModelSelector />
<TemperatureSelector :default-value="[0.56]" /> <TemperatureSelector :default-value="[0.56]" />
<MaxLengthSelector :default-value="[256]" /> <MaxLengthSelector :default-value="[256]" />
<TopPSelector :default-value="[0.9]" /> <TopPSelector :default-value="[0.9]" />

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Column } from '@tanstack/vue-table' import type { Column } from '@tanstack/vue-table'
import type { Component } from 'vue' import type { Component } from 'vue'
import { computed, ref } from 'vue' import { computed } from 'vue'
import { type Task } from '../data/schema' import { type Task } from '../data/schema'
import PlusCircledIcon from '~icons/radix-icons/plus-circled' import PlusCircledIcon from '~icons/radix-icons/plus-circled'
import CheckIcon from '~icons/radix-icons/check' import CheckIcon from '~icons/radix-icons/check'

View File

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { ChevronDown, Minus, Plus, Send } 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 { useData } from 'vitepress'
import { Button } from '@/lib/registry/default/ui/button' import { Button } from '@/lib/registry/default/ui/button'

View File

@ -10,6 +10,7 @@ import {
CommandGroup, CommandGroup,
CommandInput, CommandInput,
CommandItem, CommandItem,
CommandList,
} from '@/lib/registry/default/ui/command' } from '@/lib/registry/default/ui/command'
import { import {
Popover, Popover,
@ -48,13 +49,15 @@ const filterFunction = (list: typeof frameworks, search: string) => list.filter(
<Command :filter-function="filterFunction"> <Command :filter-function="filterFunction">
<CommandInput placeholder="Search framework..." /> <CommandInput placeholder="Search framework..." />
<CommandEmpty>No framework found.</CommandEmpty> <CommandEmpty>No framework found.</CommandEmpty>
<CommandList>
<CommandGroup> <CommandGroup>
<CommandItem <CommandItem
v-for="framework in frameworks" v-for="framework in frameworks"
:key="framework.value" :key="framework.value"
:value="framework" :value="framework"
@select="(ev) => { @select="(ev) => {
value = ev.detail.value as typeof framework value = ev.detail.value
console.log(ev)
open = false open = false
}" }"
> >
@ -67,6 +70,7 @@ const filterFunction = (list: typeof frameworks, search: string) => list.filter(
{{ framework.label }} {{ framework.label }}
</CommandItem> </CommandItem>
</CommandGroup> </CommandGroup>
</CommandList>
</Command> </Command>
</PopoverContent> </PopoverContent>
</Popover> </Popover>

View File

@ -13,6 +13,7 @@ import {
CommandGroup, CommandGroup,
CommandInput, CommandInput,
CommandItem, CommandItem,
CommandList,
} from '@/lib/registry/default/ui/command' } from '@/lib/registry/default/ui/command'
import { import {
FormControl, FormControl,
@ -83,6 +84,7 @@ const onSubmit = handleSubmit((values) => {
<Command> <Command>
<CommandInput placeholder="Search language..." /> <CommandInput placeholder="Search language..." />
<CommandEmpty>Nothing found.</CommandEmpty> <CommandEmpty>Nothing found.</CommandEmpty>
<CommandList>
<CommandGroup> <CommandGroup>
<CommandItem <CommandItem
v-for="language in languages" v-for="language in languages"
@ -100,6 +102,7 @@ const onSubmit = handleSubmit((values) => {
{{ language.label }} {{ language.label }}
</CommandItem> </CommandItem>
</CommandGroup> </CommandGroup>
</CommandList>
</Command> </Command>
</PopoverContent> </PopoverContent>
</Popover> </Popover>

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { h, ref } from 'vue' import { ref } from 'vue'
import { import {
ArrowUpCircle, ArrowUpCircle,
CheckCircle2, CheckCircle2,

View File

@ -2,20 +2,24 @@
import { ScrollArea, ScrollBar } from '@/lib/registry/default/ui/scroll-area' import { ScrollArea, ScrollBar } from '@/lib/registry/default/ui/scroll-area'
interface Artwork { interface Artwork {
id: string
artist: string artist: string
art: string art: string
} }
const works: Artwork[] = [ const works: Artwork[] = [
{ {
id: '1',
artist: 'Ornella Binni', artist: 'Ornella Binni',
art: 'https://images.unsplash.com/photo-1465869185982-5a1a7522cbcb?auto=format&fit=crop&w=300&q=80', art: 'https://images.unsplash.com/photo-1465869185982-5a1a7522cbcb?auto=format&fit=crop&w=300&q=80',
}, },
{ {
id: '2',
artist: 'Tom Byrom', artist: 'Tom Byrom',
art: 'https://images.unsplash.com/photo-1548516173-3cabfa4607e9?auto=format&fit=crop&w=300&q=80', art: 'https://images.unsplash.com/photo-1548516173-3cabfa4607e9?auto=format&fit=crop&w=300&q=80',
}, },
{ {
id: '3',
artist: 'Vladimir Malyavko', artist: 'Vladimir Malyavko',
art: 'https://images.unsplash.com/photo-1494337480532-3725c85fd2ab?auto=format&fit=crop&w=300&q=80', art: 'https://images.unsplash.com/photo-1494337480532-3725c85fd2ab?auto=format&fit=crop&w=300&q=80',
}, },
@ -30,7 +34,7 @@ const works: Artwork[] = [
<div class="overflow-hidden rounded-md"> <div class="overflow-hidden rounded-md">
<img <img
:src="artwork.art" :src="artwork.art"
:alt="`Photo by ${artwork.name}`" :alt="`Photo by ${artwork.artist}`"
class="aspect-[3/4] w-36 h-56 object-cover" class="aspect-[3/4] w-36 h-56 object-cover"
> >
</div> </div>

View File

@ -7,15 +7,30 @@ import { computed, nextTick, onMounted, ref } from 'vue'
import { buttonVariants } from '@/lib/registry/default/ui/button' import { buttonVariants } from '@/lib/registry/default/ui/button'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
/* Extracted from v-calendar */
type DatePickerModel = DatePickerDate | DatePickerRangeObject
type DateSource = Date | string | number
type DatePickerDate = DateSource | Partial<SimpleDateParts> | null
interface DatePickerRangeObject {
start: Exclude<DatePickerDate, null>
end: Exclude<DatePickerDate, null>
}
interface SimpleDateParts {
year: number
month: number
day: number
hours: number
minutes: number
seconds: number
milliseconds: number
}
defineOptions({ defineOptions({
inheritAttrs: false, inheritAttrs: false,
}) })
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
modelValue?: string | number | Date | Partial<{ modelValue?: string | number | Date | DatePickerModel
start: Date
end: Date
}>
modelModifiers?: object modelModifiers?: object
columns?: number columns?: number
type?: 'single' | 'range' type?: 'single' | 'range'
@ -53,7 +68,7 @@ onMounted(async () => {
<template> <template>
<div class="relative"> <div class="relative">
<div class="absolute top-3 flex justify-between w-full px-4"> <div class="absolute flex justify-between w-full px-4 top-3 z-[1]">
<button :class="cn(buttonVariants({ variant: 'outline' }), 'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100')" @click="handleNav('prev')"> <button :class="cn(buttonVariants({ variant: 'outline' }), 'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100')" @click="handleNav('prev')">
<ChevronLeft class="w-4 h-4" /> <ChevronLeft class="w-4 h-4" />
</button> </button>
@ -119,4 +134,94 @@ onMounted(async () => {
.calendar .vc-highlight-content-light { .calendar .vc-highlight-content-light {
@apply bg-accent text-accent-foreground; @apply bg-accent text-accent-foreground;
} }
.calendar .vc-pane-container.in-transition {
@apply overflow-hidden;
}
.calendar .vc-pane-container {
@apply w-full relative;
}
:root {
--vc-slide-translate: 22px;
--vc-slide-duration: 0.15s;
--vc-slide-timing: ease;
}
.calendar .vc-fade-enter-active,
.calendar .vc-fade-leave-active,
.calendar .vc-slide-left-enter-active,
.calendar .vc-slide-left-leave-active,
.calendar .vc-slide-right-enter-active,
.calendar .vc-slide-right-leave-active,
.calendar .vc-slide-up-enter-active,
.calendar .vc-slide-up-leave-active,
.calendar .vc-slide-down-enter-active,
.calendar .vc-slide-down-leave-active,
.calendar .vc-slide-fade-enter-active,
.calendar .vc-slide-fade-leave-active {
transition:
opacity var(--vc-slide-duration) var(--vc-slide-timing),
-webkit-transform var(--vc-slide-duration) var(--vc-slide-timing);
transition:
transform var(--vc-slide-duration) var(--vc-slide-timing),
opacity var(--vc-slide-duration) var(--vc-slide-timing);
transition:
transform var(--vc-slide-duration) var(--vc-slide-timing),
opacity var(--vc-slide-duration) var(--vc-slide-timing),
-webkit-transform var(--vc-slide-duration) var(--vc-slide-timing);
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
pointer-events: none;
}
.calendar .vc-none-leave-active,
.calendar .vc-fade-leave-active,
.calendar .vc-slide-left-leave-active,
.calendar .vc-slide-right-leave-active,
.calendar .vc-slide-up-leave-active,
.calendar .vc-slide-down-leave-active {
position: absolute !important;
width: 100%;
}
.calendar .vc-none-enter-from,
.calendar .vc-none-leave-to,
.calendar .vc-fade-enter-from,
.calendar .vc-fade-leave-to,
.calendar .vc-slide-left-enter-from,
.calendar .vc-slide-left-leave-to,
.calendar .vc-slide-right-enter-from,
.calendar .vc-slide-right-leave-to,
.calendar .vc-slide-up-enter-from,
.calendar .vc-slide-up-leave-to,
.calendar .vc-slide-down-enter-from,
.calendar .vc-slide-down-leave-to,
.calendar .vc-slide-fade-enter-from,
.calendar .vc-slide-fade-leave-to {
opacity: 0;
}
.calendar .vc-slide-left-enter-from,
.calendar .vc-slide-right-leave-to,
.calendar .vc-slide-fade-enter-from.direction-left,
.calendar .vc-slide-fade-leave-to.direction-left {
-webkit-transform: translateX(var(--vc-slide-translate));
transform: translateX(var(--vc-slide-translate));
}
.calendar .vc-slide-right-enter-from,
.calendar .vc-slide-left-leave-to,
.calendar .vc-slide-fade-enter-from.direction-right,
.calendar .vc-slide-fade-leave-to.direction-right {
-webkit-transform: translateX(calc(-1 * var(--vc-slide-translate)));
transform: translateX(calc(-1 * var(--vc-slide-translate)));
}
.calendar .vc-slide-up-enter-from,
.calendar .vc-slide-down-leave-to,
.calendar .vc-slide-fade-enter-from.direction-top,
.calendar .vc-slide-fade-leave-to.direction-top {
-webkit-transform: translateY(var(--vc-slide-translate));
transform: translateY(var(--vc-slide-translate));
}
.calendar .vc-slide-down-enter-from,
.calendar .vc-slide-up-leave-to,
.calendar .vc-slide-fade-enter-from.direction-bottom,
.calendar .vc-slide-fade-leave-to.direction-bottom {
-webkit-transform: translateY(calc(-1 * var(--vc-slide-translate)));
transform: translateY(calc(-1 * var(--vc-slide-translate)));
}
</style> </style>

View File

@ -17,7 +17,7 @@ const forwarded = useForwardPropsEmits(props, emits)
cn('peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground', cn('peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
$attrs.class ?? '')" $attrs.class ?? '')"
> >
<CheckboxIndicator class="flex items-center justify-center text-current"> <CheckboxIndicator class="flex h-full w-full items-center justify-center text-current">
<Check class="h-4 w-4" /> <Check class="h-4 w-4" />
</CheckboxIndicator> </CheckboxIndicator>
</CheckboxRoot> </CheckboxRoot>

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { DropdownMenuRoot, type DropdownMenuRootEmits, type DropdownMenuRootProps, useEmitAsProps, useForwardPropsEmits } from 'radix-vue' import { DropdownMenuRoot, type DropdownMenuRootEmits, type DropdownMenuRootProps, useForwardPropsEmits } from 'radix-vue'
const props = defineProps<DropdownMenuRootProps>() const props = defineProps<DropdownMenuRootProps>()
const emits = defineEmits<DropdownMenuRootEmits>() const emits = defineEmits<DropdownMenuRootEmits>()

View File

@ -1,7 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { useAttrs } from 'vue'
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
defineOptions({
inheritAttrs: false,
})
const props = defineProps<{ const props = defineProps<{
defaultValue?: string | number defaultValue?: string | number
modelValue?: string | number modelValue?: string | number
@ -11,6 +16,8 @@ const emits = defineEmits<{
(e: 'update:modelValue', payload: string | number): void (e: 'update:modelValue', payload: string | number): void
}>() }>()
const { class: className, ...rest } = useAttrs()
const modelValue = useVModel(props, 'modelValue', emits, { const modelValue = useVModel(props, 'modelValue', emits, {
passive: true, passive: true,
defaultValue: props.defaultValue, defaultValue: props.defaultValue,
@ -18,5 +25,5 @@ const modelValue = useVModel(props, 'modelValue', emits, {
</script> </script>
<template> <template>
<input v-model="modelValue" type="text" :class="cn('flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50', $attrs.class ?? '')"> <input v-model="modelValue" :class="cn('flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50', className ?? '')" v-bind="rest">
</template> </template>

View File

@ -10,6 +10,7 @@ import {
CommandGroup, CommandGroup,
CommandInput, CommandInput,
CommandItem, CommandItem,
CommandList,
} from '@/lib/registry/new-york/ui/command' } from '@/lib/registry/new-york/ui/command'
import { import {
Popover, Popover,
@ -48,13 +49,14 @@ const filterFunction = (list: typeof frameworks, search: string) => list.filter(
<Command v-model="value" :filter-function="filterFunction"> <Command v-model="value" :filter-function="filterFunction">
<CommandInput class="h-9" placeholder="Search framework..." /> <CommandInput class="h-9" placeholder="Search framework..." />
<CommandEmpty>No framework found.</CommandEmpty> <CommandEmpty>No framework found.</CommandEmpty>
<CommandList>
<CommandGroup> <CommandGroup>
<CommandItem <CommandItem
v-for="framework in frameworks" v-for="framework in frameworks"
:key="framework.value" :key="framework.value"
:value="framework" :value="framework"
@select="(ev) => { @select="(ev) => {
value = ev.detail.value as typeof framework value = ev.detail.value
open = false open = false
}" }"
> >
@ -67,6 +69,7 @@ const filterFunction = (list: typeof frameworks, search: string) => list.filter(
/> />
</CommandItem> </CommandItem>
</CommandGroup> </CommandGroup>
</CommandList>
</Command> </Command>
</PopoverContent> </PopoverContent>
</Popover> </Popover>

View File

@ -13,6 +13,7 @@ import {
CommandGroup, CommandGroup,
CommandInput, CommandInput,
CommandItem, CommandItem,
CommandList,
} from '@/lib/registry/new-york/ui/command' } from '@/lib/registry/new-york/ui/command'
import { import {
FormControl, FormControl,
@ -86,6 +87,7 @@ const onSubmit = handleSubmit((values) => {
<Command> <Command>
<CommandInput placeholder="Search language..." /> <CommandInput placeholder="Search language..." />
<CommandEmpty>Nothing found.</CommandEmpty> <CommandEmpty>Nothing found.</CommandEmpty>
<CommandList>
<CommandGroup> <CommandGroup>
<CommandItem <CommandItem
v-for="language in languages" v-for="language in languages"
@ -103,6 +105,7 @@ const onSubmit = handleSubmit((values) => {
/> />
</CommandItem> </CommandItem>
</CommandGroup> </CommandGroup>
</CommandList>
</Command> </Command>
</PopoverContent> </PopoverContent>
</Popover> </Popover>

View File

@ -2,20 +2,24 @@
import { ScrollArea, ScrollBar } from '@/lib/registry/new-york/ui/scroll-area' import { ScrollArea, ScrollBar } from '@/lib/registry/new-york/ui/scroll-area'
interface Artwork { interface Artwork {
id: string
artist: string artist: string
art: string art: string
} }
const works: Artwork[] = [ const works: Artwork[] = [
{ {
id: '1',
artist: 'Ornella Binni', artist: 'Ornella Binni',
art: 'https://images.unsplash.com/photo-1465869185982-5a1a7522cbcb?auto=format&fit=crop&w=300&q=80', art: 'https://images.unsplash.com/photo-1465869185982-5a1a7522cbcb?auto=format&fit=crop&w=300&q=80',
}, },
{ {
id: '2',
artist: 'Tom Byrom', artist: 'Tom Byrom',
art: 'https://images.unsplash.com/photo-1548516173-3cabfa4607e9?auto=format&fit=crop&w=300&q=80', art: 'https://images.unsplash.com/photo-1548516173-3cabfa4607e9?auto=format&fit=crop&w=300&q=80',
}, },
{ {
id: '3',
artist: 'Vladimir Malyavko', artist: 'Vladimir Malyavko',
art: 'https://images.unsplash.com/photo-1494337480532-3725c85fd2ab?auto=format&fit=crop&w=300&q=80', art: 'https://images.unsplash.com/photo-1494337480532-3725c85fd2ab?auto=format&fit=crop&w=300&q=80',
}, },
@ -30,7 +34,7 @@ const works: Artwork[] = [
<div class="overflow-hidden rounded-md"> <div class="overflow-hidden rounded-md">
<img <img
:src="artwork.art" :src="artwork.art"
:alt="`Photo by ${artwork.name}`" :alt="`Photo by ${artwork.artist}`"
class="aspect-[3/4] w-36 h-56 object-cover" class="aspect-[3/4] w-36 h-56 object-cover"
> >
</div> </div>

View File

@ -7,15 +7,29 @@ import { computed, nextTick, onMounted, ref } from 'vue'
import { buttonVariants } from '@/lib/registry/new-york/ui/button' import { buttonVariants } from '@/lib/registry/new-york/ui/button'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
/* Extracted from v-calendar */
type DatePickerModel = DatePickerDate | DatePickerRangeObject
type DateSource = Date | string | number
type DatePickerDate = DateSource | Partial<SimpleDateParts> | null
interface DatePickerRangeObject {
start: Exclude<DatePickerDate, null>
end: Exclude<DatePickerDate, null>
}
interface SimpleDateParts {
year: number
month: number
day: number
hours: number
minutes: number
seconds: number
milliseconds: number
}
defineOptions({ defineOptions({
inheritAttrs: false, inheritAttrs: false,
}) })
const props = withDefaults(defineProps< { const props = withDefaults(defineProps< {
modelValue?: string | number | Date | Partial<{ modelValue?: string | number | Date | DatePickerModel
start: Date
end: Date
}>
modelModifiers?: object modelModifiers?: object
columns?: number columns?: number
type?: 'single' | 'range' type?: 'single' | 'range'
@ -53,7 +67,7 @@ onMounted(async () => {
<template> <template>
<div class="relative"> <div class="relative">
<div class="absolute top-3 flex justify-between w-full px-4"> <div class="absolute flex justify-between w-full px-4 top-3 z-[1]">
<button :class="cn(buttonVariants({ variant: 'outline' }), 'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100')" @click="handleNav('prev')"> <button :class="cn(buttonVariants({ variant: 'outline' }), 'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100')" @click="handleNav('prev')">
<ChevronLeftIcon class="w-4 h-4" /> <ChevronLeftIcon class="w-4 h-4" />
</button> </button>
@ -119,4 +133,101 @@ onMounted(async () => {
.calendar .vc-highlight-content-light { .calendar .vc-highlight-content-light {
@apply bg-accent text-accent-foreground; @apply bg-accent text-accent-foreground;
} }
.calendar .vc-pane-container.in-transition {
@apply overflow-hidden;
}
.calendar .vc-pane-container {
@apply w-full relative;
}
:root {
--vc-slide-translate: 22px;
--vc-slide-duration: 0.15s;
--vc-slide-timing: ease;
}
.calendar .vc-fade-enter-active,
.calendar .vc-fade-leave-active,
.calendar .vc-slide-left-enter-active,
.calendar .vc-slide-left-leave-active,
.calendar .vc-slide-right-enter-active,
.calendar .vc-slide-right-leave-active,
.calendar .vc-slide-up-enter-active,
.calendar .vc-slide-up-leave-active,
.calendar .vc-slide-down-enter-active,
.calendar .vc-slide-down-leave-active,
.calendar .vc-slide-fade-enter-active,
.calendar .vc-slide-fade-leave-active {
transition:
opacity var(--vc-slide-duration) var(--vc-slide-timing),
-webkit-transform var(--vc-slide-duration) var(--vc-slide-timing);
transition:
transform var(--vc-slide-duration) var(--vc-slide-timing),
opacity var(--vc-slide-duration) var(--vc-slide-timing);
transition:
transform var(--vc-slide-duration) var(--vc-slide-timing),
opacity var(--vc-slide-duration) var(--vc-slide-timing),
-webkit-transform var(--vc-slide-duration) var(--vc-slide-timing);
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
pointer-events: none;
}
.calendar .vc-none-leave-active,
.calendar .vc-fade-leave-active,
.calendar .vc-slide-left-leave-active,
.calendar .vc-slide-right-leave-active,
.calendar .vc-slide-up-leave-active,
.calendar .vc-slide-down-leave-active {
position: absolute !important;
width: 100%;
}
.calendar .vc-none-enter-from,
.calendar .vc-none-leave-to,
.calendar .vc-fade-enter-from,
.calendar .vc-fade-leave-to,
.calendar .vc-slide-left-enter-from,
.calendar .vc-slide-left-leave-to,
.calendar .vc-slide-right-enter-from,
.calendar .vc-slide-right-leave-to,
.calendar .vc-slide-up-enter-from,
.calendar .vc-slide-up-leave-to,
.calendar .vc-slide-down-enter-from,
.calendar .vc-slide-down-leave-to,
.calendar .vc-slide-fade-enter-from,
.calendar .vc-slide-fade-leave-to {
opacity: 0;
}
.calendar .vc-slide-left-enter-from,
.calendar .vc-slide-right-leave-to,
.calendar .vc-slide-fade-enter-from.direction-left,
.calendar .vc-slide-fade-leave-to.direction-left {
-webkit-transform: translateX(var(--vc-slide-translate));
transform: translateX(var(--vc-slide-translate));
}
.calendar .vc-slide-right-enter-from,
.calendar .vc-slide-left-leave-to,
.calendar .vc-slide-fade-enter-from.direction-right,
.calendar .vc-slide-fade-leave-to.direction-right {
-webkit-transform: translateX(calc(-1 * var(--vc-slide-translate)));
transform: translateX(calc(-1 * var(--vc-slide-translate)));
}
.calendar .vc-slide-up-enter-from,
.calendar .vc-slide-down-leave-to,
.calendar .vc-slide-fade-enter-from.direction-top,
.calendar .vc-slide-fade-leave-to.direction-top {
-webkit-transform: translateY(var(--vc-slide-translate));
transform: translateY(var(--vc-slide-translate));
}
.calendar .vc-slide-down-enter-from,
.calendar .vc-slide-up-leave-to,
.calendar .vc-slide-fade-enter-from.direction-bottom,
.calendar .vc-slide-fade-leave-to.direction-bottom {
-webkit-transform: translateY(calc(-1 * var(--vc-slide-translate)));
transform: translateY(calc(-1 * var(--vc-slide-translate)));
}
</style> </style>

View File

@ -17,7 +17,7 @@ const forwarded = useForwardPropsEmits(props, emits)
cn('peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground', cn('peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
$attrs.class ?? '')" $attrs.class ?? '')"
> >
<CheckboxIndicator class="flex items-center justify-center text-current"> <CheckboxIndicator class="flex h-full w-full items-center justify-center text-current">
<CheckIcon class="h-4 w-4" /> <CheckIcon class="h-4 w-4" />
</CheckboxIndicator> </CheckboxIndicator>
</CheckboxRoot> </CheckboxRoot>

View File

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import type { ComboboxRootEmits, ComboboxRootProps } from 'radix-vue' import type { ComboboxRootEmits, ComboboxRootProps } from 'radix-vue'
import { ComboboxRoot, useEmitAsProps, useForwardPropsEmits } from 'radix-vue' import { ComboboxRoot, useForwardPropsEmits } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<ComboboxRootProps>() const props = defineProps<ComboboxRootProps>()

View File

@ -11,6 +11,8 @@ const forwarded = useForwardPropsEmits(props, emits)
<template> <template>
<ComboboxContent v-bind="forwarded" :class="cn('max-h-[300px] overflow-y-auto overflow-x-hidden', $attrs.class ?? '')"> <ComboboxContent v-bind="forwarded" :class="cn('max-h-[300px] overflow-y-auto overflow-x-hidden', $attrs.class ?? '')">
<div role="presentation">
<slot /> <slot />
</div>
</ComboboxContent> </ComboboxContent>
</template> </template>

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { DropdownMenuRoot, type DropdownMenuRootEmits, type DropdownMenuRootProps, useEmitAsProps, useForwardPropsEmits } from 'radix-vue' import { DropdownMenuRoot, type DropdownMenuRootEmits, type DropdownMenuRootProps, useForwardPropsEmits } from 'radix-vue'
const props = defineProps<DropdownMenuRootProps>() const props = defineProps<DropdownMenuRootProps>()
const emits = defineEmits<DropdownMenuRootEmits>() const emits = defineEmits<DropdownMenuRootEmits>()

View File

@ -1,7 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { useAttrs } from 'vue'
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
defineOptions({
inheritAttrs: false,
})
const props = defineProps<{ const props = defineProps<{
defaultValue?: string | number defaultValue?: string | number
modelValue?: string | number modelValue?: string | number
@ -11,6 +16,8 @@ const emits = defineEmits<{
(e: 'update:modelValue', payload: string | number): void (e: 'update:modelValue', payload: string | number): void
}>() }>()
const { class: className, ...rest } = useAttrs()
const modelValue = useVModel(props, 'modelValue', emits, { const modelValue = useVModel(props, 'modelValue', emits, {
passive: true, passive: true,
defaultValue: props.defaultValue, defaultValue: props.defaultValue,
@ -18,5 +25,5 @@ const modelValue = useVModel(props, 'modelValue', emits, {
</script> </script>
<template> <template>
<input v-model="modelValue" type="text" :class="cn('flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50', $attrs.class ?? '')"> <input v-model="modelValue" :class="cn('flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50', className ?? '')" v-bind="rest">
</template> </template>

View File

@ -1,5 +1,5 @@
import { readFile, readdir } from 'node:fs/promises' import { readFile, readdir } from 'node:fs/promises'
import { join, resolve } from 'node:path' import { join, resolve } from 'pathe'
import { compileScript, parse } from 'vue/compiler-sfc' import { compileScript, parse } from 'vue/compiler-sfc'
import type { Registry } from '../../lib/registry' import type { Registry } from '../../lib/registry'

View File

@ -1,7 +1,7 @@
import type { Updater } from '@tanstack/vue-table' import type { Updater } from '@tanstack/vue-table'
import { type ClassValue, clsx } from 'clsx' import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge' import { twMerge } from 'tailwind-merge'
import { type Ref, camelize, getCurrentInstance, toHandlerKey } 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))

View File

@ -11,7 +11,7 @@
"files": [ "files": [
{ {
"name": "Calendar.vue", "name": "Calendar.vue",
"content": "<script setup lang=\"ts\">\nimport { useVModel } from '@vueuse/core'\nimport type { Calendar } from 'v-calendar'\nimport { DatePicker } from 'v-calendar'\nimport { ChevronLeft, ChevronRight } from 'lucide-vue-next'\nimport { computed, nextTick, onMounted, ref } from 'vue'\nimport { buttonVariants } from '@/lib/registry/default/ui/button'\nimport { cn } from '@/lib/utils'\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<{\n modelValue?: string | number | Date | Partial<{\n start: Date\n end: Date\n }>\n modelModifiers?: object\n columns?: number\n type?: 'single' | 'range'\n}>(), {\n type: 'single',\n columns: 1,\n})\nconst emits = defineEmits<{\n (e: 'update:modelValue', payload: typeof props.modelValue): void\n}>()\n\nconst modelValue = useVModel(props, 'modelValue', emits, {\n passive: true,\n})\n\nconst datePicker = ref<InstanceType<typeof DatePicker>>()\n// @ts-expect-error in this current version of v-calendar has the calendaRef instance, which is required to handle arrow nav.\nconst calendarRef = computed<InstanceType<typeof Calendar>>(() => datePicker.value.calendarRef)\n\nfunction handleNav(direction: 'prev' | 'next') {\n if (!calendarRef.value)\n return\n\n if (direction === 'prev')\n calendarRef.value.movePrev()\n else calendarRef.value.moveNext()\n}\n\nonMounted(async () => {\n await nextTick()\n if (modelValue.value instanceof Date && calendarRef.value)\n calendarRef.value.focusDate(modelValue.value)\n})\n</script>\n\n<template>\n <div class=\"relative\">\n <div class=\"absolute top-3 flex justify-between w-full px-4\">\n <button :class=\"cn(buttonVariants({ variant: 'outline' }), 'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100')\" @click=\"handleNav('prev')\">\n <ChevronLeft class=\"w-4 h-4\" />\n </button>\n <button :class=\"cn(buttonVariants({ variant: 'outline' }), 'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100')\" @click=\"handleNav('next')\">\n <ChevronRight class=\"w-4 h-4\" />\n </button>\n </div>\n\n <DatePicker\n ref=\"datePicker\"\n v-model=\"modelValue\"\n v-bind=\"$attrs\"\n :model-modifiers=\"modelModifiers\"\n class=\"calendar\"\n trim-weeks\n :transition=\"'none'\"\n :columns=\"columns\"\n />\n </div>\n</template>\n\n<style lang=\"postcss\">\n.calendar {\n @apply p-3 text-center;\n}\n.calendar .vc-pane-layout {\n @apply grid gap-4;\n}\n.calendar .vc-title {\n @apply text-sm font-medium pointer-events-none;\n}\n.calendar .vc-pane-header-wrapper {\n @apply hidden;\n}\n.calendar .vc-weeks {\n @apply mt-4;\n}\n.calendar .vc-weekdays {\n @apply flex;\n}\n.calendar .vc-weekday {\n @apply text-muted-foreground rounded-md w-9 font-normal text-[0.8rem];\n}\n.calendar .vc-weeks {\n @apply w-full space-y-2 flex flex-col [&>_div]:grid [&>_div]:grid-cols-7;\n}\n.calendar .vc-day:has(.vc-highlights) {\n @apply bg-accent first:rounded-l-md last:rounded-r-md overflow-hidden;\n}\n.calendar .vc-day-content {\n @apply text-center text-sm p-0 relative focus-within:relative focus-within:z-20 inline-flex items-center justify-center ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 hover:bg-accent hover:text-accent-foreground h-9 w-9 font-normal aria-selected:opacity-100 select-none;\n}\n.calendar .vc-day-content:not(.vc-highlight-content-light) {\n @apply rounded-md;\n}\n.calendar .is-not-in-month:not(:has(.vc-highlight-content-solid)):not(:has(.vc-highlight-content-light)):not(:has(.vc-highlight-content-outline)),\n.calendar .vc-disabled {\n @apply text-muted-foreground opacity-50;\n}\n.calendar .vc-highlight-content-solid, .calendar .vc-highlight-content-outline {\n @apply bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground;\n}\n.calendar .vc-highlight-content-light {\n @apply bg-accent text-accent-foreground;\n}\n</style>\n" "content": "<script setup lang=\"ts\">\nimport { useVModel } from '@vueuse/core'\nimport type { Calendar } from 'v-calendar'\nimport { DatePicker } from 'v-calendar'\nimport { ChevronLeft, ChevronRight } from 'lucide-vue-next'\nimport { computed, nextTick, onMounted, ref } from 'vue'\nimport { buttonVariants } from '@/lib/registry/default/ui/button'\nimport { cn } from '@/lib/utils'\n\n/* Extracted from v-calendar */\ntype DatePickerModel = DatePickerDate | DatePickerRangeObject\ntype DateSource = Date | string | number\ntype DatePickerDate = DateSource | Partial<SimpleDateParts> | null\ninterface DatePickerRangeObject {\n start: Exclude<DatePickerDate, null>\n end: Exclude<DatePickerDate, null>\n}\ninterface SimpleDateParts {\n year: number\n month: number\n day: number\n hours: number\n minutes: number\n seconds: number\n milliseconds: number\n}\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<{\n modelValue?: string | number | Date | DatePickerModel\n modelModifiers?: object\n columns?: number\n type?: 'single' | 'range'\n}>(), {\n type: 'single',\n columns: 1,\n})\nconst emits = defineEmits<{\n (e: 'update:modelValue', payload: typeof props.modelValue): void\n}>()\n\nconst modelValue = useVModel(props, 'modelValue', emits, {\n passive: true,\n})\n\nconst datePicker = ref<InstanceType<typeof DatePicker>>()\n// @ts-expect-error in this current version of v-calendar has the calendaRef instance, which is required to handle arrow nav.\nconst calendarRef = computed<InstanceType<typeof Calendar>>(() => datePicker.value.calendarRef)\n\nfunction handleNav(direction: 'prev' | 'next') {\n if (!calendarRef.value)\n return\n\n if (direction === 'prev')\n calendarRef.value.movePrev()\n else calendarRef.value.moveNext()\n}\n\nonMounted(async () => {\n await nextTick()\n if (modelValue.value instanceof Date && calendarRef.value)\n calendarRef.value.focusDate(modelValue.value)\n})\n</script>\n\n<template>\n <div class=\"relative\">\n <div class=\"absolute top-3 flex justify-between w-full px-4\">\n <button :class=\"cn(buttonVariants({ variant: 'outline' }), 'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100')\" @click=\"handleNav('prev')\">\n <ChevronLeft class=\"w-4 h-4\" />\n </button>\n <button :class=\"cn(buttonVariants({ variant: 'outline' }), 'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100')\" @click=\"handleNav('next')\">\n <ChevronRight class=\"w-4 h-4\" />\n </button>\n </div>\n\n <DatePicker\n ref=\"datePicker\"\n v-model=\"modelValue\"\n v-bind=\"$attrs\"\n :model-modifiers=\"modelModifiers\"\n class=\"calendar\"\n trim-weeks\n :transition=\"'none'\"\n :columns=\"columns\"\n />\n </div>\n</template>\n\n<style lang=\"postcss\">\n.calendar {\n @apply p-3 text-center;\n}\n.calendar .vc-pane-layout {\n @apply grid gap-4;\n}\n.calendar .vc-title {\n @apply text-sm font-medium pointer-events-none;\n}\n.calendar .vc-pane-header-wrapper {\n @apply hidden;\n}\n.calendar .vc-weeks {\n @apply mt-4;\n}\n.calendar .vc-weekdays {\n @apply flex;\n}\n.calendar .vc-weekday {\n @apply text-muted-foreground rounded-md w-9 font-normal text-[0.8rem];\n}\n.calendar .vc-weeks {\n @apply w-full space-y-2 flex flex-col [&>_div]:grid [&>_div]:grid-cols-7;\n}\n.calendar .vc-day:has(.vc-highlights) {\n @apply bg-accent first:rounded-l-md last:rounded-r-md overflow-hidden;\n}\n.calendar .vc-day-content {\n @apply text-center text-sm p-0 relative focus-within:relative focus-within:z-20 inline-flex items-center justify-center ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 hover:bg-accent hover:text-accent-foreground h-9 w-9 font-normal aria-selected:opacity-100 select-none;\n}\n.calendar .vc-day-content:not(.vc-highlight-content-light) {\n @apply rounded-md;\n}\n.calendar .is-not-in-month:not(:has(.vc-highlight-content-solid)):not(:has(.vc-highlight-content-light)):not(:has(.vc-highlight-content-outline)),\n.calendar .vc-disabled {\n @apply text-muted-foreground opacity-50;\n}\n.calendar .vc-highlight-content-solid, .calendar .vc-highlight-content-outline {\n @apply bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground;\n}\n.calendar .vc-highlight-content-light {\n @apply bg-accent text-accent-foreground;\n}\n</style>\n"
}, },
{ {
"name": "index.ts", "name": "index.ts",

View File

@ -9,7 +9,7 @@
"files": [ "files": [
{ {
"name": "Checkbox.vue", "name": "Checkbox.vue",
"content": "<script setup lang=\"ts\">\nimport type { CheckboxRootEmits, CheckboxRootProps } from 'radix-vue'\nimport { CheckboxIndicator, CheckboxRoot, useForwardPropsEmits } from 'radix-vue'\nimport { Check } from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<CheckboxRootProps>()\nconst emits = defineEmits<CheckboxRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n <CheckboxRoot\n v-bind=\"forwarded\"\n :class=\"\n cn('peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',\n $attrs.class ?? '')\"\n >\n <CheckboxIndicator class=\"flex items-center justify-center text-current\">\n <Check class=\"h-4 w-4\" />\n </CheckboxIndicator>\n </CheckboxRoot>\n</template>\n" "content": "<script setup lang=\"ts\">\nimport type { CheckboxRootEmits, CheckboxRootProps } from 'radix-vue'\nimport { CheckboxIndicator, CheckboxRoot, useForwardPropsEmits } from 'radix-vue'\nimport { Check } from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<CheckboxRootProps>()\nconst emits = defineEmits<CheckboxRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n <CheckboxRoot\n v-bind=\"forwarded\"\n :class=\"\n cn('peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',\n $attrs.class ?? '')\"\n >\n <CheckboxIndicator class=\"flex h-full w-full items-center justify-center text-current\">\n <Check class=\"h-4 w-4\" />\n </CheckboxIndicator>\n </CheckboxRoot>\n</template>\n"
}, },
{ {
"name": "index.ts", "name": "index.ts",

View File

@ -11,7 +11,7 @@
"files": [ "files": [
{ {
"name": "Calendar.vue", "name": "Calendar.vue",
"content": "<script setup lang=\"ts\">\nimport { useVModel } from '@vueuse/core'\nimport type { Calendar } from 'v-calendar'\nimport { DatePicker } from 'v-calendar'\nimport { ChevronLeftIcon, ChevronRightIcon } from '@radix-icons/vue'\nimport { computed, nextTick, onMounted, ref } from 'vue'\nimport { buttonVariants } from '@/lib/registry/new-york/ui/button'\nimport { cn } from '@/lib/utils'\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps< {\n modelValue?: string | number | Date | Partial<{\n start: Date\n end: Date\n }>\n modelModifiers?: object\n columns?: number\n type?: 'single' | 'range'\n}>(), {\n type: 'single',\n columns: 1,\n})\nconst emits = defineEmits<{\n (e: 'update:modelValue', payload: typeof props.modelValue): void\n}>()\n\nconst modelValue = useVModel(props, 'modelValue', emits, {\n passive: true,\n})\n\nconst datePicker = ref<InstanceType<typeof DatePicker>>()\n// @ts-expect-error in this current version of v-calendar has the calendaRef instance, which is required to handle arrow nav.\nconst calendarRef = computed<InstanceType<typeof Calendar>>(() => datePicker.value.calendarRef)\n\nfunction handleNav(direction: 'prev' | 'next') {\n if (!calendarRef.value)\n return\n\n if (direction === 'prev')\n calendarRef.value.movePrev()\n else calendarRef.value.moveNext()\n}\n\nonMounted(async () => {\n await nextTick()\n if (modelValue.value instanceof Date && calendarRef.value)\n calendarRef.value.focusDate(modelValue.value)\n})\n</script>\n\n<template>\n <div class=\"relative\">\n <div class=\"absolute top-3 flex justify-between w-full px-4\">\n <button :class=\"cn(buttonVariants({ variant: 'outline' }), 'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100')\" @click=\"handleNav('prev')\">\n <ChevronLeftIcon class=\"w-4 h-4\" />\n </button>\n <button :class=\"cn(buttonVariants({ variant: 'outline' }), 'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100')\" @click=\"handleNav('next')\">\n <ChevronRightIcon class=\"w-4 h-4\" />\n </button>\n </div>\n\n <DatePicker\n ref=\"datePicker\"\n v-bind=\"$attrs\"\n v-model=\"modelValue\"\n :model-modifiers=\"modelModifiers\"\n class=\"calendar\"\n trim-weeks\n :transition=\"'none'\"\n :columns=\"columns\"\n />\n </div>\n</template>\n\n<style lang=\"postcss\">\n.calendar {\n @apply p-3 text-center;\n}\n.calendar .vc-pane-layout {\n @apply grid gap-4;\n}\n.calendar .vc-title {\n @apply text-sm font-medium pointer-events-none;\n}\n.calendar .vc-pane-header-wrapper {\n @apply hidden;\n}\n.calendar .vc-weeks {\n @apply mt-4;\n}\n.calendar .vc-weekdays {\n @apply flex;\n}\n.calendar .vc-weekday {\n @apply text-muted-foreground rounded-md w-8 font-normal text-[0.8rem];\n}\n.calendar .vc-weeks {\n @apply w-full space-y-2 flex flex-col [&>_div]:grid [&>_div]:grid-cols-7;\n}\n.calendar .vc-day:has(.vc-highlights) {\n @apply bg-accent first:rounded-l-md last:rounded-r-md overflow-hidden;\n}\n.calendar .vc-day-content {\n @apply text-center text-sm p-0 relative focus-within:relative focus-within:z-20 inline-flex items-center justify-center ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 hover:bg-accent hover:text-accent-foreground h-8 w-8 font-normal aria-selected:opacity-100 select-none;\n}\n.calendar .vc-day-content:not(.vc-highlight-content-light) {\n @apply rounded-md;\n}\n.calendar .is-not-in-month:not(:has(.vc-highlight-content-solid)):not(:has(.vc-highlight-content-light)):not(:has(.vc-highlight-content-outline)),\n.calendar .vc-disabled {\n @apply text-muted-foreground opacity-50;\n}\n.calendar .vc-highlight-content-solid, .calendar .vc-highlight-content-outline {\n @apply bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground;\n}\n.calendar .vc-highlight-content-light {\n @apply bg-accent text-accent-foreground;\n}\n</style>\n" "content": "<script setup lang=\"ts\">\nimport { useVModel } from '@vueuse/core'\nimport type { Calendar } from 'v-calendar'\nimport { DatePicker } from 'v-calendar'\nimport { ChevronLeftIcon, ChevronRightIcon } from '@radix-icons/vue'\nimport { computed, nextTick, onMounted, ref } from 'vue'\nimport { buttonVariants } from '@/lib/registry/new-york/ui/button'\nimport { cn } from '@/lib/utils'\n\n/* Extracted from v-calendar */\ntype DatePickerModel = DatePickerDate | DatePickerRangeObject\ntype DateSource = Date | string | number\ntype DatePickerDate = DateSource | Partial<SimpleDateParts> | null\ninterface DatePickerRangeObject {\n start: Exclude<DatePickerDate, null>\n end: Exclude<DatePickerDate, null>\n}\ninterface SimpleDateParts {\n year: number\n month: number\n day: number\n hours: number\n minutes: number\n seconds: number\n milliseconds: number\n}\n\ndefineOptions({\n inheritAttrs: false,\n})\nconst props = withDefaults(defineProps< {\n modelValue?: string | number | Date | DatePickerModel\n modelModifiers?: object\n columns?: number\n type?: 'single' | 'range'\n}>(), {\n type: 'single',\n columns: 1,\n})\nconst emits = defineEmits<{\n (e: 'update:modelValue', payload: typeof props.modelValue): void\n}>()\n\nconst modelValue = useVModel(props, 'modelValue', emits, {\n passive: true,\n})\n\nconst datePicker = ref<InstanceType<typeof DatePicker>>()\n// @ts-expect-error in this current version of v-calendar has the calendaRef instance, which is required to handle arrow nav.\nconst calendarRef = computed<InstanceType<typeof Calendar>>(() => datePicker.value.calendarRef)\n\nfunction handleNav(direction: 'prev' | 'next') {\n if (!calendarRef.value)\n return\n\n if (direction === 'prev')\n calendarRef.value.movePrev()\n else calendarRef.value.moveNext()\n}\n\nonMounted(async () => {\n await nextTick()\n if (modelValue.value instanceof Date && calendarRef.value)\n calendarRef.value.focusDate(modelValue.value)\n})\n</script>\n\n<template>\n <div class=\"relative\">\n <div class=\"absolute top-3 flex justify-between w-full px-4\">\n <button :class=\"cn(buttonVariants({ variant: 'outline' }), 'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100')\" @click=\"handleNav('prev')\">\n <ChevronLeftIcon class=\"w-4 h-4\" />\n </button>\n <button :class=\"cn(buttonVariants({ variant: 'outline' }), 'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100')\" @click=\"handleNav('next')\">\n <ChevronRightIcon class=\"w-4 h-4\" />\n </button>\n </div>\n\n <DatePicker\n ref=\"datePicker\"\n v-bind=\"$attrs\"\n v-model=\"modelValue\"\n :model-modifiers=\"modelModifiers\"\n class=\"calendar\"\n trim-weeks\n :transition=\"'none'\"\n :columns=\"columns\"\n />\n </div>\n</template>\n\n<style lang=\"postcss\">\n.calendar {\n @apply p-3 text-center;\n}\n.calendar .vc-pane-layout {\n @apply grid gap-4;\n}\n.calendar .vc-title {\n @apply text-sm font-medium pointer-events-none;\n}\n.calendar .vc-pane-header-wrapper {\n @apply hidden;\n}\n.calendar .vc-weeks {\n @apply mt-4;\n}\n.calendar .vc-weekdays {\n @apply flex;\n}\n.calendar .vc-weekday {\n @apply text-muted-foreground rounded-md w-8 font-normal text-[0.8rem];\n}\n.calendar .vc-weeks {\n @apply w-full space-y-2 flex flex-col [&>_div]:grid [&>_div]:grid-cols-7;\n}\n.calendar .vc-day:has(.vc-highlights) {\n @apply bg-accent first:rounded-l-md last:rounded-r-md overflow-hidden;\n}\n.calendar .vc-day-content {\n @apply text-center text-sm p-0 relative focus-within:relative focus-within:z-20 inline-flex items-center justify-center ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 hover:bg-accent hover:text-accent-foreground h-8 w-8 font-normal aria-selected:opacity-100 select-none;\n}\n.calendar .vc-day-content:not(.vc-highlight-content-light) {\n @apply rounded-md;\n}\n.calendar .is-not-in-month:not(:has(.vc-highlight-content-solid)):not(:has(.vc-highlight-content-light)):not(:has(.vc-highlight-content-outline)),\n.calendar .vc-disabled {\n @apply text-muted-foreground opacity-50;\n}\n.calendar .vc-highlight-content-solid, .calendar .vc-highlight-content-outline {\n @apply bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground;\n}\n.calendar .vc-highlight-content-light {\n @apply bg-accent text-accent-foreground;\n}\n</style>\n"
}, },
{ {
"name": "index.ts", "name": "index.ts",

View File

@ -9,7 +9,7 @@
"files": [ "files": [
{ {
"name": "Checkbox.vue", "name": "Checkbox.vue",
"content": "<script setup lang=\"ts\">\nimport type { CheckboxRootEmits, CheckboxRootProps } from 'radix-vue'\nimport { CheckboxIndicator, CheckboxRoot, useForwardPropsEmits } from 'radix-vue'\nimport { CheckIcon } from '@radix-icons/vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<CheckboxRootProps>()\nconst emits = defineEmits<CheckboxRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n <CheckboxRoot\n v-bind=\"forwarded\"\n :class=\"\n cn('peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',\n $attrs.class ?? '')\"\n >\n <CheckboxIndicator class=\"flex items-center justify-center text-current\">\n <CheckIcon class=\"h-4 w-4\" />\n </CheckboxIndicator>\n </CheckboxRoot>\n</template>\n" "content": "<script setup lang=\"ts\">\nimport type { CheckboxRootEmits, CheckboxRootProps } from 'radix-vue'\nimport { CheckboxIndicator, CheckboxRoot, useForwardPropsEmits } from 'radix-vue'\nimport { CheckIcon } from '@radix-icons/vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<CheckboxRootProps>()\nconst emits = defineEmits<CheckboxRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n <CheckboxRoot\n v-bind=\"forwarded\"\n :class=\"\n cn('peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',\n $attrs.class ?? '')\"\n >\n <CheckboxIndicator class=\"flex h-full w-full items-center justify-center text-current\">\n <CheckIcon class=\"h-4 w-4\" />\n </CheckboxIndicator>\n </CheckboxRoot>\n</template>\n"
}, },
{ {
"name": "index.ts", "name": "index.ts",

View File

@ -34,7 +34,7 @@
}, },
{ {
"name": "CommandList.vue", "name": "CommandList.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxContentEmits, ComboboxContentProps } from 'radix-vue'\nimport { ComboboxContent, useForwardPropsEmits } from 'radix-vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<ComboboxContentProps>()\nconst emits = defineEmits<ComboboxContentEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n <ComboboxContent v-bind=\"forwarded\" :class=\"cn('max-h-[300px] overflow-y-auto overflow-x-hidden', $attrs.class ?? '')\">\n <slot />\n </ComboboxContent>\n</template>\n" "content": "<script setup lang=\"ts\">\nimport type { ComboboxContentEmits, ComboboxContentProps } from 'radix-vue'\nimport { ComboboxContent, useForwardPropsEmits } from 'radix-vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<ComboboxContentProps>()\nconst emits = defineEmits<ComboboxContentEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n <ComboboxContent v-bind=\"forwarded\" :class=\"cn('max-h-[300px] overflow-y-auto overflow-x-hidden', $attrs.class ?? '')\">\n <div role=\"presentation\">\n <slot />\n </div>\n </ComboboxContent>\n</template>\n"
}, },
{ {
"name": "CommandSeparator.vue", "name": "CommandSeparator.vue",

View File

@ -1,23 +1,17 @@
{ {
"extends": "@vue/tsconfig/tsconfig.dom.json",
"compilerOptions": { "compilerOptions": {
"target": "esnext", "lib": ["ESNext", "DOM", "DOM.Iterable"],
"lib": ["esnext", "dom"], "moduleResolution": "Node",
"jsx": "preserve",
"module": "esnext",
"moduleResolution": "node",
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@/*": ["./src/*"] "@/*": ["./src/*"]
}, },
"types": ["unplugin-icons/types/vue", "node"], "types": ["unplugin-icons/types/vue", "node"],
"resolveJsonModule": true,
"declaration": false, "declaration": false,
"sourceMap": true, "sourceMap": true,
"outDir": "dist", "outDir": "dist"
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true
}, },
"include": ["/**/*.vue", "src", ".vitepress/**/*.vue", "/**/*.ts", ".vitepress/**/*.mts", ".vitepress/**/*.vue", "src/lib/**/*"], "include": ["src", ".vitepress/**/*.vue", ".vitepress/**/*.mts", ".vitepress/**/*.vue", "src/lib/**/*"],
"exclude": ["node_modules", "./scripts/build-registry.ts"] "exclude": ["node_modules", "./scripts/build-registry.ts"]
} }

View File

@ -0,0 +1,13 @@
{
"extends": "@vue/tsconfig/tsconfig.json",
"compilerOptions": {
"moduleResolution": "Node",
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"declaration": false
},
"include": ["src/lib/**/*"],
"exclude": ["node_modules", "src/lib/registry/**/example/**/*"]
}

View File

@ -1,6 +1,6 @@
{ {
"name": "shadcn-vue", "name": "shadcn-vue",
"version": "0.8.2", "version": "0.8.4",
"private": true, "private": true,
"packageManager": "pnpm@8.10.2", "packageManager": "pnpm@8.10.2",
"license": "MIT", "license": "MIT",

View File

@ -1,7 +1,7 @@
{ {
"name": "shadcn-vue", "name": "shadcn-vue",
"type": "module", "type": "module",
"version": "0.8.2", "version": "0.8.4",
"description": "Add components to your apps.", "description": "Add components to your apps.",
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
@ -63,7 +63,7 @@
"node-fetch": "^3.3.2", "node-fetch": "^3.3.2",
"ora": "^7.0.1", "ora": "^7.0.1",
"prompts": "^2.4.2", "prompts": "^2.4.2",
"radix-vue": "^1.1.0", "radix-vue": "^1.2.3",
"recast": "^0.23.4", "recast": "^0.23.4",
"rimraf": "^5.0.1", "rimraf": "^5.0.1",
"ts-morph": "^19.0.0", "ts-morph": "^19.0.0",

View File

@ -128,10 +128,20 @@ module.exports = {
from: { height: "var(--radix-accordion-content-height)" }, from: { height: "var(--radix-accordion-content-height)" },
to: { height: 0 }, to: { height: 0 },
}, },
"collapsible-down": {
from: { height: 0 },
to: { height: 'var(--radix-collapsible-content-height)' },
},
"collapsible-up": {
from: { height: 'var(--radix-collapsible-content-height)' },
to: { height: 0 },
},
}, },
animation: { animation: {
"accordion-down": "accordion-down 0.2s ease-out", "accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out", "accordion-up": "accordion-up 0.2s ease-out",
"collapsible-down": "collapsible-down 0.2s ease-in-out",
"collapsible-up": "collapsible-up 0.2s ease-in-out",
}, },
}, },
}, },

View File

@ -1,7 +1,7 @@
import type * as z from 'zod' import type * as z from 'zod'
import MagicString from 'magic-string' import MagicString from 'magic-string'
import type { SFCTemplateBlock } from '@vue/compiler-sfc' import type { SFCTemplateBlock } from '@vue/compiler-sfc'
import { parse, walk } from '@vue/compiler-sfc' import { parse } from '@vue/compiler-sfc'
import { SyntaxKind } from 'ts-morph' import { SyntaxKind } from 'ts-morph'
import type { registryBaseColorSchema } from '@/src/utils/registry/schema' import type { registryBaseColorSchema } from '@/src/utils/registry/schema'
import type { Transformer } from '@/src/utils/transformers' import type { Transformer } from '@/src/utils/transformers'

View File

@ -1,25 +1,25 @@
{ {
"compilerOptions": { "compilerOptions": {
"incremental": true,
"target": "es2017", "target": "es2017",
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true, "jsx": "preserve",
"checkJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext", "module": "esnext",
"moduleResolution": "node", "moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"noUncheckedIndexedAccess": true,
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"~/*": ["./src/*"] "~/*": ["./src/*"]
} },
"resolveJsonModule": true,
"allowJs": true,
"checkJs": true,
"noEmit": true,
"isolatedModules": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noUncheckedIndexedAccess": true,
"skipLibCheck": true
}, },
"include": [ "include": [
".eslintrc.cjs", ".eslintrc.cjs",

View File

@ -1,6 +1,5 @@
import { type ClassValue, clsx } from 'clsx' import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge' import { twMerge } from 'tailwind-merge'
import { camelize, getCurrentInstance, toHandlerKey } from 'vue'
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs)) return twMerge(clsx(inputs))

View File

@ -13,7 +13,7 @@
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"lucide-vue-next": "^0.276.0", "lucide-vue-next": "^0.276.0",
"radix-vue": "^1.1.0", "radix-vue": "^1.2.3",
"tailwind-merge": "^1.14.0", "tailwind-merge": "^1.14.0",
"tailwindcss-animate": "^1.0.7" "tailwindcss-animate": "^1.0.7"
}, },

View File

@ -124,10 +124,20 @@ export default {
from: { height: \\"var(--radix-accordion-content-height)\\" }, from: { height: \\"var(--radix-accordion-content-height)\\" },
to: { height: 0 }, to: { height: 0 },
}, },
\\"collapsible-down\\": {
from: { height: 0 },
to: { height: 'var(--radix-collapsible-content-height)' },
},
\\"collapsible-up\\": {
from: { height: 'var(--radix-collapsible-content-height)' },
to: { height: 0 },
},
}, },
animation: { animation: {
\\"accordion-down\\": \\"accordion-down 0.2s ease-out\\", \\"accordion-down\\": \\"accordion-down 0.2s ease-out\\",
\\"accordion-up\\": \\"accordion-up 0.2s ease-out\\", \\"accordion-up\\": \\"accordion-up 0.2s ease-out\\",
\\"collapsible-down\\": \\"collapsible-down 0.2s ease-in-out\\",
\\"collapsible-up\\": \\"collapsible-up 0.2s ease-in-out\\",
}, },
}, },
}, },

View File

@ -77,7 +77,8 @@ describe('transformSFC', () => {
`, `,
config: {}, config: {},
}) })
expect(result).toMatchSnapshot() // TODO: Ignore test until https://github.com/radix-vue/shadcn-vue/issues/187 is resolved
// expect(result).toMatchSnapshot()
}) })
test('defineEmits', async () => { test('defineEmits', async () => {

View File

@ -2,12 +2,12 @@
"$schema": "https://json.schemastore.org/tsconfig", "$schema": "https://json.schemastore.org/tsconfig",
"extends": "../../tsconfig.json", "extends": "../../tsconfig.json",
"compilerOptions": { "compilerOptions": {
"isolatedModules": false,
"baseUrl": ".",
"module": "ES2020", "module": "ES2020",
"baseUrl": ".",
"paths": { "paths": {
"@/*": ["./*"] "@/*": ["./*"]
} },
"isolatedModules": false
}, },
"include": ["src/**/*.ts"], "include": ["src/**/*.ts"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

56
packages/module/.gitignore vendored Normal file
View File

@ -0,0 +1,56 @@
# Dependencies
node_modules
# Logs
*.log*
# Temp directories
.temp
.tmp
.cache
# Yarn
**/.yarn/cache
**/.yarn/*state*
# Generated dirs
dist
# Nuxt
.nuxt
.output
.data
.vercel_build_output
.build-*
.netlify
# Env
.env
# Testing
reports
coverage
*.lcov
.nyc_output
# VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Intellij idea
*.iml
.idea
# OSX
.DS_Store
.AppleDouble
.LSOverride
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

1
packages/module/.npmrc Normal file
View File

@ -0,0 +1 @@
shamefully-hoist=true

104
packages/module/README.md Normal file
View File

@ -0,0 +1,104 @@
<!--
Get your module up and running quickly.
Find and replace all on all files (CMD+SHIFT+F):
- Name: Shadcn Nuxt
- Package name: shadcn-nuxt
- Description: My new Nuxt module
-->
# Shadcn Nuxt
[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![License][license-src]][license-href]
[![Nuxt][nuxt-src]][nuxt-href]
Shadcn Vue module for Nuxt.
- [✨ &nbsp;Release Notes](/CHANGELOG.md)
<!-- - [🏀 Online playground](https://stackblitz.com/github/radix-vue/shadcn-vue?file=playground%2Fapp.vue) -->
- [📖 &nbsp;Documentation](https://www.shadcn-vue.com/docs/installation/nuxt.html)
## Features
<!-- Highlight some of the features your module provide here -->
- ⛰ Auto-import correct and relevant components
- more to come...
## Quick Setup
1. Add `shadcn-nuxt` dependency to your project
```bash
# Using pnpm
pnpm add -D shadcn-nuxt
# Using yarn
yarn add --dev shadcn-nuxt
# Using npm
npm install --save-dev shadcn-nuxt
```
2. Add `shadcn-nuxt` to the `modules` section of `nuxt.config.ts`
```js
export default defineNuxtConfig({
modules: [
'shadcn-nuxt'
],
shadcn: {
/**
* Prefix for all the imported component
*/
prefix: '',
/**
* Directory that the component lives in.
* @default "./components/ui"
*/
componentDir: './components/ui'
}
})
```
That's it! You can now use Shadcn Nuxt in your Nuxt app ✨
## Development
```bash
# Install dependencies
npm install
# Generate type stubs
npm run dev:prepare
# Develop with the playground
npm run dev
# Build the playground
npm run dev:build
# Run ESLint
npm run lint
# Run Vitest
npm run test
npm run test:watch
# Release new version
npm run release
```
<!-- Badges -->
[npm-version-src]: https://img.shields.io/npm/v/shadcn-nuxt/latest.svg?style=flat&colorA=18181B&colorB=28CF8D
[npm-version-href]: https://npmjs.com/package/shadcn-nuxt
[npm-downloads-src]: https://img.shields.io/npm/dm/shadcn-nuxt.svg?style=flat&colorA=18181B&colorB=28CF8D
[npm-downloads-href]: https://npmjs.com/package/shadcn-nuxt
[license-src]: https://img.shields.io/npm/l/shadcn-nuxt.svg?style=flat&colorA=18181B&colorB=28CF8D
[license-href]: https://npmjs.com/package/shadcn-nuxt
[nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt.js
[nuxt-href]: https://nuxt.com

View File

@ -0,0 +1,51 @@
{
"name": "shadcn-nuxt",
"type": "module",
"version": "0.8.4",
"description": "Add shadcn-vue module to Nuxt",
"publishConfig": {
"access": "public"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/radix-vue/shadcn-vue.git",
"directory": "packages/module"
},
"exports": {
".": {
"types": "./dist/types.d.ts",
"import": "./dist/module.mjs",
"require": "./dist/module.cjs"
}
},
"main": "./dist/module.cjs",
"types": "./dist/types.d.ts",
"files": [
"dist"
],
"scripts": {
"prepack": "nuxt-module-build build",
"dev": "nuxi dev playground",
"dev:build": "nuxi build playground",
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
"lint": "eslint .",
"test": "vitest run",
"test:watch": "vitest watch",
"release": "pnpm run prepack && pnpm publish && git push --follow-tags"
},
"dependencies": {
"@nuxt/kit": "^3.8.2",
"ts-morph": "^19.0.0"
},
"devDependencies": {
"@nuxt/devtools": "latest",
"@nuxt/eslint-config": "^0.2.0",
"@nuxt/module-builder": "^0.5.4",
"@nuxt/schema": "^3.8.2",
"@nuxt/test-utils": "^3.8.1",
"@types/node": "^20.9.3",
"nuxt": "^3.8.2",
"vitest": "^0.33.0"
}
}

View File

@ -0,0 +1,11 @@
<script setup>
</script>
<template>
<div>
<UiButton :variant="'destructive'">
hi
</UiButton>
Nuxt module playground!
</div>
</template>

View File

@ -0,0 +1,15 @@
{
"style": "default",
"typescript": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "assets/css/tailwind.css",
"baseColor": "slate",
"cssVariables": true
},
"framework": "nuxt",
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}

View File

@ -0,0 +1,23 @@
<script setup lang="ts">
import { buttonVariants } from '.'
import { cn } from '@/lib/utils'
interface Props {
variant?: NonNullable<Parameters<typeof buttonVariants>[0]>['variant']
size?: NonNullable<Parameters<typeof buttonVariants>[0]>['size']
as?: string
}
withDefaults(defineProps<Props>(), {
as: 'button',
})
</script>
<template>
<component
:is="as"
:class="cn(buttonVariants({ variant, size }), $attrs.class ?? '')"
>
<slot />
</component>
</template>

View File

@ -0,0 +1,32 @@
import { cva } from 'class-variance-authority'
export { default as Button } from './Button.vue'
export const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive:
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline:
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
secondary:
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
},
)

View File

@ -0,0 +1,14 @@
<script setup lang="ts">
import { DropdownMenuRoot, type DropdownMenuRootEmits, type DropdownMenuRootProps, useForwardPropsEmits } from 'radix-vue'
const props = defineProps<DropdownMenuRootProps>()
const emits = defineEmits<DropdownMenuRootEmits>()
const forwarded = useForwardPropsEmits(props, emits)
</script>
<template>
<DropdownMenuRoot v-bind="forwarded">
<slot />
</DropdownMenuRoot>
</template>

View File

@ -0,0 +1,31 @@
<script setup lang="ts">
import {
DropdownMenuCheckboxItem,
type DropdownMenuCheckboxItemEmits,
type DropdownMenuCheckboxItemProps,
DropdownMenuItemIndicator,
useEmitAsProps,
} from 'radix-vue'
import { Check } from 'lucide-vue-next'
import { cn } from '@/lib/utils'
const props = defineProps<DropdownMenuCheckboxItemProps & { class?: string }>()
const emits = defineEmits<DropdownMenuCheckboxItemEmits>()
</script>
<template>
<DropdownMenuCheckboxItem
v-bind="{ ...props, ...useEmitAsProps(emits) }"
:class=" cn(
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
props.class,
)"
>
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuItemIndicator>
<Check class="w-4 h-4" />
</DropdownMenuItemIndicator>
</span>
<slot />
</DropdownMenuCheckboxItem>
</template>

View File

@ -0,0 +1,4 @@
export { DropdownMenuPortal } from 'radix-vue'
export { default as DropdownMenu } from './DropdownMenu.vue'
export { default as DropdownMenuCheckboxItem } from './DropdownMenuCheckboxItem.vue'

View File

@ -0,0 +1,6 @@
import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}

View File

@ -0,0 +1,7 @@
export default defineNuxtConfig({
modules: ['../src/module'],
shadcn: {
prefix: 'Ui',
},
devtools: { enabled: true },
})

View File

@ -0,0 +1,22 @@
{
"name": "my-module-playground",
"type": "module",
"private": true,
"scripts": {
"dev": "nuxi dev",
"build": "nuxi build",
"generate": "nuxi generate"
},
"dependencies": {
"@nuxtjs/tailwindcss": "^6.10.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"lucide-vue-next": "^0.276.0",
"radix-vue": "^1.2.3",
"tailwind-merge": "^2.0.0",
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
"nuxt": "latest"
}
}

View File

@ -0,0 +1,73 @@
const animate = require('tailwindcss-animate')
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ['class'],
theme: {
container: {
center: true,
padding: '2rem',
screens: {
'2xl': '1400px',
},
},
extend: {
colors: {
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))',
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))',
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))',
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))',
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))',
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))',
},
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))',
},
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)',
},
keyframes: {
'accordion-down': {
from: { height: 0 },
to: { height: 'var(--radix-accordion-content-height)' },
},
'accordion-up': {
from: { height: 'var(--radix-accordion-content-height)' },
to: { height: 0 },
},
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
},
},
},
plugins: [animate],
}

View File

@ -0,0 +1,3 @@
{
"extends": "./.nuxt/tsconfig.json"
}

View File

@ -0,0 +1,65 @@
import { readdirSync } from 'node:fs'
import { addComponent, createResolver, defineNuxtModule } from '@nuxt/kit'
import { Project } from 'ts-morph'
// Module options TypeScript interface definition
export interface ModuleOptions {
/**
* Prefix for all the imported component
*/
prefix?: string
/**
* Directory that the component lives in.
* @default "./components/ui"
*/
componentDir?: string
}
export default defineNuxtModule<ModuleOptions>({
meta: {
name: 'shadcn',
configKey: 'shadcn',
},
defaults: {
prefix: '',
componentDir: './components/ui',
},
async setup(options, nuxt) {
const IGNORE_DIR = '**/components/ui'
const COMPONENT_DIR_PATH = options.componentDir!
const ROOT_DIR_PATH = nuxt.options.rootDir
const { resolve } = createResolver(ROOT_DIR_PATH)
nuxt.options.ignore.push(IGNORE_DIR)
nuxt._ignore?.add(IGNORE_DIR)
nuxt._ignorePatterns?.push(IGNORE_DIR)
try {
readdirSync(resolve(COMPONENT_DIR_PATH))
.forEach(async (dir) => {
const filePath = resolve(COMPONENT_DIR_PATH, dir, 'index.ts')
const project = new Project()
project.addSourceFileAtPath(filePath)
const sourceFile = project.getSourceFileOrThrow(filePath)
const exportedDeclarations = sourceFile.getExportedDeclarations()
// Filter out non-component export
const exportedKeys = Array.from(exportedDeclarations.keys()).filter(key => /^[A-Z]/.test(key))
exportedKeys.forEach((key) => {
addComponent({
name: `${options.prefix}${key}`, // name of the component to be used in vue templates
export: key, // (optional) if the component is a named (rather than default) export
filePath: resolve(filePath),
})
})
})
}
catch (err) {
if (err instanceof Error)
console.warn(err.message)
}
},
})

View File

@ -0,0 +1,15 @@
import { fileURLToPath } from 'node:url'
import { describe, expect, it } from 'vitest'
import { $fetch, setup } from '@nuxt/test-utils'
describe('ssr', async () => {
await setup({
rootDir: fileURLToPath(new URL('./fixtures/basic', import.meta.url)),
})
it('renders the index page', async () => {
// Get response to a server-rendered page with `$fetch`.
const html = await $fetch('/')
expect(html).toContain('<div>basic</div>')
})
})

View File

@ -0,0 +1,6 @@
<script setup>
</script>
<template>
<div>basic</div>
</template>

View File

@ -0,0 +1,7 @@
import MyModule from '../../../src/module'
export default defineNuxtConfig({
modules: [
MyModule,
],
})

View File

@ -0,0 +1,5 @@
{
"name": "basic",
"type": "module",
"private": true
}

View File

@ -0,0 +1,3 @@
{
"extends": "./.nuxt/tsconfig.json"
}

File diff suppressed because it is too large Load Diff