Merge branch 'radix-vue:dev' into 199-bug-whitespace-nowrap
This commit is contained in:
commit
a4b13d7a91
|
|
@ -359,6 +359,13 @@ export const Index = {
|
|||
component: () => import('../src/lib/registry/default/example/DatePickerWithRange.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/DatePickerWithRange.vue'],
|
||||
},
|
||||
DateTimePickerDemo: {
|
||||
name: 'DateTimePickerDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['utils', 'button', 'calendar', 'popover'],
|
||||
component: () => import('../src/lib/registry/default/example/DateTimePickerDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/default/example/DateTimePickerDemo.vue'],
|
||||
},
|
||||
DialogCustomCloseButton: {
|
||||
name: 'DialogCustomCloseButton',
|
||||
type: 'components:example',
|
||||
|
|
@ -1194,6 +1201,13 @@ export const Index = {
|
|||
component: () => import('../src/lib/registry/new-york/example/DatePickerWithRange.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/DatePickerWithRange.vue'],
|
||||
},
|
||||
DateTimePickerDemo: {
|
||||
name: 'DateTimePickerDemo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['utils', 'button', 'calendar', 'popover'],
|
||||
component: () => import('../src/lib/registry/new-york/example/DateTimePickerDemo.vue').then(m => m.default),
|
||||
files: ['../src/lib/registry/new-york/example/DateTimePickerDemo.vue'],
|
||||
},
|
||||
DialogCustomCloseButton: {
|
||||
name: 'DialogCustomCloseButton',
|
||||
type: 'components:example',
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "www",
|
||||
"type": "module",
|
||||
"version": "0.8.6",
|
||||
"version": "0.8.7",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -64,6 +64,10 @@ const date = ref<Date>()
|
|||
|
||||
<ComponentPreview name="DatePickerWithRange" />
|
||||
|
||||
### Date Time Picker
|
||||
|
||||
<ComponentPreview name="DateTimePickerDemo" />
|
||||
|
||||
### With Presets
|
||||
|
||||
<ComponentPreview name="DatePickerWithPresets" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
<script setup lang="ts">
|
||||
import { format } from 'date-fns'
|
||||
import { Calendar as CalendarIcon } from 'lucide-vue-next'
|
||||
|
||||
import { ref } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Button } from '@/lib/registry/default/ui/button'
|
||||
import { Calendar } from '@/lib/registry/default/ui/calendar'
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from '@/lib/registry/default/ui/popover'
|
||||
|
||||
const date = ref<Date>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Popover>
|
||||
<PopoverTrigger as-child>
|
||||
<Button
|
||||
:variant="'outline'"
|
||||
:class="cn(
|
||||
'w-[280px] justify-start text-left font-normal',
|
||||
!date && 'text-muted-foreground',
|
||||
)"
|
||||
>
|
||||
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||
<span>{{ date ? format(date, 'PPP - hh:mm') : "Pick a date" }}</span>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-auto p-0">
|
||||
<Calendar v-model="date" mode="datetime" />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</template>
|
||||
|
|
@ -236,4 +236,49 @@ onMounted(async () => {
|
|||
-webkit-transform: translateY(calc(-1 * var(--vc-slide-translate)));
|
||||
transform: translateY(calc(-1 * var(--vc-slide-translate)));
|
||||
}
|
||||
/**
|
||||
* Timepicker styles
|
||||
*/
|
||||
.vc-time-picker {
|
||||
@apply flex flex-col items-center p-2;
|
||||
}
|
||||
.vc-time-picker.vc-invalid {
|
||||
@apply pointer-events-none opacity-50;
|
||||
}
|
||||
.vc-time-picker.vc-attached {
|
||||
@apply border-t border-solid border-secondary mt-2;
|
||||
}
|
||||
.vc-time-picker > * + * {
|
||||
@apply mt-1;
|
||||
}
|
||||
.vc-time-header {
|
||||
@apply flex items-center text-sm font-semibold uppercase mt-1 px-1 leading-6;
|
||||
}
|
||||
.vc-time-select-group {
|
||||
@apply inline-flex items-center px-1 rounded-md bg-primary-foreground border border-solid border-secondary;
|
||||
}
|
||||
.vc-time-select-group .vc-base-icon {
|
||||
@apply mr-1 text-primary stroke-primary;
|
||||
}
|
||||
.vc-time-select-group select {
|
||||
@apply bg-primary-foreground p-1 appearance-none outline-none text-center;
|
||||
}
|
||||
.vc-time-weekday {
|
||||
@apply text-muted-foreground tracking-wide;
|
||||
}
|
||||
.vc-time-month {
|
||||
@apply text-primary ml-2;
|
||||
}
|
||||
.vc-time-day {
|
||||
@apply text-primary ml-1;
|
||||
}
|
||||
.vc-time-year {
|
||||
@apply text-muted-foreground ml-2;
|
||||
}
|
||||
.vc-time-colon {
|
||||
@apply mb-0.5;
|
||||
}
|
||||
.vc-time-decimal {
|
||||
@apply ml-0.5;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ const forwarded = useForwardPropsEmits(props, emits)
|
|||
v-bind="{ ...forwarded, ...$attrs }"
|
||||
:class="
|
||||
cn(
|
||||
'relative z-50 min-w-[10rem] overflow-hidden rounded-md bg-background border border-border text-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
'relative z-50 min-w-[10rem] overflow-hidden rounded-md bg-background border border-border text-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 max-h-[--radix-popper-available-height]',
|
||||
position === 'popper'
|
||||
&& 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
||||
props.class,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
<script setup lang="ts">
|
||||
import { format } from 'date-fns'
|
||||
import { ref } from 'vue'
|
||||
import { CalendarIcon } from '@radix-icons/vue'
|
||||
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||
import { Calendar } from '@/lib/registry/new-york/ui/calendar'
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from '@/lib/registry/new-york/ui/popover'
|
||||
|
||||
const date = ref<Date>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Popover>
|
||||
<PopoverTrigger as-child>
|
||||
<Button
|
||||
:variant="'outline'"
|
||||
:class="cn(
|
||||
'w-[280px] justify-start text-left font-normal',
|
||||
!date && 'text-muted-foreground',
|
||||
)"
|
||||
>
|
||||
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||
<span>{{ date ? format(date, 'PPP - hh:mm') : "Pick a date" }}</span>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-auto p-0">
|
||||
<Calendar v-model="date" mode="datetime" />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</template>
|
||||
|
|
@ -242,4 +242,49 @@ onMounted(async () => {
|
|||
-webkit-transform: translateY(calc(-1 * var(--vc-slide-translate)));
|
||||
transform: translateY(calc(-1 * var(--vc-slide-translate)));
|
||||
}
|
||||
/**
|
||||
* Timepicker styles
|
||||
*/
|
||||
.vc-time-picker {
|
||||
@apply flex flex-col items-center p-2;
|
||||
}
|
||||
.vc-time-picker.vc-invalid {
|
||||
@apply pointer-events-none opacity-50;
|
||||
}
|
||||
.vc-time-picker.vc-attached {
|
||||
@apply border-t border-solid border-secondary mt-2;
|
||||
}
|
||||
.vc-time-picker > * + * {
|
||||
@apply mt-1;
|
||||
}
|
||||
.vc-time-header {
|
||||
@apply flex items-center text-sm font-semibold uppercase mt-1 px-1 leading-6;
|
||||
}
|
||||
.vc-time-select-group {
|
||||
@apply inline-flex items-center px-1 rounded-md bg-primary-foreground border border-solid border-secondary;
|
||||
}
|
||||
.vc-time-select-group .vc-base-icon {
|
||||
@apply mr-1 text-primary stroke-primary;
|
||||
}
|
||||
.vc-time-select-group select {
|
||||
@apply bg-primary-foreground p-1 appearance-none outline-none text-center;
|
||||
}
|
||||
.vc-time-weekday {
|
||||
@apply text-muted-foreground tracking-wide;
|
||||
}
|
||||
.vc-time-month {
|
||||
@apply text-primary ml-2;
|
||||
}
|
||||
.vc-time-day {
|
||||
@apply text-primary ml-1;
|
||||
}
|
||||
.vc-time-year {
|
||||
@apply text-muted-foreground ml-2;
|
||||
}
|
||||
.vc-time-colon {
|
||||
@apply mb-0.5;
|
||||
}
|
||||
.vc-time-decimal {
|
||||
@apply ml-0.5;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ const forwarded = useForwardPropsEmits(props, emits)
|
|||
v-bind="{ ...forwarded, ...$attrs }"
|
||||
:class="
|
||||
cn(
|
||||
'relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
'relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 max-h-[--radix-popper-available-height]',
|
||||
position === 'popper'
|
||||
&& 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
||||
props.class,
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -11,7 +11,7 @@
|
|||
},
|
||||
{
|
||||
"name": "SelectContent.vue",
|
||||
"content": "<script setup lang=\"ts\">\nimport {\n SelectContent,\n type SelectContentEmits,\n type SelectContentProps,\n SelectPortal,\n SelectViewport,\n useForwardPropsEmits,\n} from 'radix-vue'\nimport { cn } from '@/lib/utils'\n\nconst props = withDefaults(\n defineProps<SelectContentProps & { class?: string }>(), {\n position: 'popper',\n sideOffset: 4,\n },\n)\nconst emits = defineEmits<SelectContentEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n <SelectPortal>\n <SelectContent\n v-bind=\"{ ...forwarded, ...$attrs }\"\n :class=\"\n cn(\n 'relative z-50 min-w-[10rem] overflow-hidden rounded-md bg-background border border-border text-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',\n position === 'popper'\n && 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',\n props.class,\n )\n \"\n >\n <SelectViewport\n :class=\"\n cn('p-1',\n position === 'popper'\n && 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]')\"\n >\n <slot />\n </SelectViewport>\n </SelectContent>\n </SelectPortal>\n</template>\n"
|
||||
"content": "<script setup lang=\"ts\">\nimport {\n SelectContent,\n type SelectContentEmits,\n type SelectContentProps,\n SelectPortal,\n SelectViewport,\n useForwardPropsEmits,\n} from 'radix-vue'\nimport { cn } from '@/lib/utils'\n\nconst props = withDefaults(\n defineProps<SelectContentProps & { class?: string }>(), {\n position: 'popper',\n sideOffset: 4,\n },\n)\nconst emits = defineEmits<SelectContentEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n <SelectPortal>\n <SelectContent\n v-bind=\"{ ...forwarded, ...$attrs }\"\n :class=\"\n cn(\n 'relative z-50 min-w-[10rem] overflow-hidden rounded-md bg-background border border-border text-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 max-h-[--radix-popper-available-height]',\n position === 'popper'\n && 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',\n props.class,\n )\n \"\n >\n <SelectViewport\n :class=\"\n cn('p-1',\n position === 'popper'\n && 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]')\"\n >\n <slot />\n </SelectViewport>\n </SelectContent>\n </SelectPortal>\n</template>\n"
|
||||
},
|
||||
{
|
||||
"name": "SelectGroup.vue",
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -11,7 +11,7 @@
|
|||
},
|
||||
{
|
||||
"name": "SelectContent.vue",
|
||||
"content": "<script setup lang=\"ts\">\nimport {\n SelectContent,\n type SelectContentEmits,\n type SelectContentProps,\n SelectPortal,\n SelectViewport,\n useForwardPropsEmits,\n} from 'radix-vue'\nimport { cn } from '@/lib/utils'\n\nconst props = withDefaults(\n defineProps<SelectContentProps & { class?: string }>(), {\n position: 'popper',\n sideOffset: 4,\n },\n)\nconst emits = defineEmits<SelectContentEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n <SelectPortal>\n <SelectContent\n v-bind=\"{ ...forwarded, ...$attrs }\"\n :class=\"\n cn(\n 'relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',\n position === 'popper'\n && 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',\n props.class,\n )\n \"\n >\n <SelectViewport\n :class=\"\n cn('p-0',\n position === 'popper'\n && 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]')\"\n >\n <slot />\n </SelectViewport>\n </SelectContent>\n </SelectPortal>\n</template>\n"
|
||||
"content": "<script setup lang=\"ts\">\nimport {\n SelectContent,\n type SelectContentEmits,\n type SelectContentProps,\n SelectPortal,\n SelectViewport,\n useForwardPropsEmits,\n} from 'radix-vue'\nimport { cn } from '@/lib/utils'\n\nconst props = withDefaults(\n defineProps<SelectContentProps & { class?: string }>(), {\n position: 'popper',\n sideOffset: 4,\n },\n)\nconst emits = defineEmits<SelectContentEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n <SelectPortal>\n <SelectContent\n v-bind=\"{ ...forwarded, ...$attrs }\"\n :class=\"\n cn(\n 'relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 max-h-[--radix-popper-available-height]',\n position === 'popper'\n && 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',\n props.class,\n )\n \"\n >\n <SelectViewport\n :class=\"\n cn('p-0',\n position === 'popper'\n && 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]')\"\n >\n <slot />\n </SelectViewport>\n </SelectContent>\n </SelectPortal>\n</template>\n"
|
||||
},
|
||||
{
|
||||
"name": "SelectGroup.vue",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "shadcn-vue",
|
||||
"version": "0.8.6",
|
||||
"version": "0.8.7",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@8.10.2",
|
||||
"license": "MIT",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "shadcn-vue",
|
||||
"type": "module",
|
||||
"version": "0.8.6",
|
||||
"version": "0.8.7",
|
||||
"description": "Add components to your apps.",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "shadcn-nuxt",
|
||||
"type": "module",
|
||||
"version": "0.8.6",
|
||||
"version": "0.8.7",
|
||||
"description": "Add shadcn-vue module to Nuxt",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
|
@ -36,8 +36,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@nuxt/kit": "^3.8.2",
|
||||
"recast": "^0.23.4",
|
||||
"ts-morph": "^19.0.0"
|
||||
"oxc-parser": "^0.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/devtools": "latest",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
<script setup lang="ts">
|
||||
import emblaCarouselVue from 'embla-carousel-vue'
|
||||
import { useProvideCarousel } from './useCarousel'
|
||||
import type { CarouselEmits, CarouselProps, WithClassAsProps } from './interface'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = withDefaults(defineProps<CarouselProps & WithClassAsProps>(), {
|
||||
orientation: 'horizontal',
|
||||
})
|
||||
|
||||
const emits = defineEmits<CarouselEmits>()
|
||||
|
||||
const carouselArgs = useProvideCarousel(props, emits)
|
||||
|
||||
defineExpose(carouselArgs)
|
||||
|
||||
function onKeyDown(event: KeyboardEvent) {
|
||||
const prevKey = props.orientation === 'vertical' ? 'ArrowUp' : 'ArrowLeft'
|
||||
const nextKey = props.orientation === 'vertical' ? 'ArrowDown' : 'ArrowRight'
|
||||
|
||||
if (event.key === prevKey) {
|
||||
event.preventDefault()
|
||||
carouselArgs.scrollPrev()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (event.key === nextKey) {
|
||||
event.preventDefault()
|
||||
carouselArgs.scrollNext()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="cn('relative', props.class)"
|
||||
role="region"
|
||||
aria-roledescription="carousel"
|
||||
tabindex="0"
|
||||
@keydown="onKeyDown"
|
||||
>
|
||||
<slot v-bind="carouselArgs" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<script setup lang="ts">
|
||||
import type { WithClassAsProps } from './interface'
|
||||
import { useCarousel } from './useCarousel'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
|
||||
const props = defineProps<WithClassAsProps>()
|
||||
|
||||
const { carouselRef, orientation } = useCarousel()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="carouselRef" class="overflow-hidden">
|
||||
<div
|
||||
:class="
|
||||
cn(
|
||||
'flex',
|
||||
orientation === 'horizontal' ? '-ml-4' : '-mt-4 flex-col',
|
||||
props.class,
|
||||
)"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<script setup lang="ts">
|
||||
import type { WithClassAsProps } from './interface'
|
||||
import { useCarousel } from './useCarousel'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<WithClassAsProps>()
|
||||
|
||||
const { orientation } = useCarousel()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
role="group"
|
||||
aria-roledescription="slide"
|
||||
:class="cn(
|
||||
'min-w-0 shrink-0 grow-0 basis-full',
|
||||
orientation === 'horizontal' ? 'pl-4' : 'pt-4',
|
||||
props.class,
|
||||
)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<script setup lang="ts">
|
||||
import { ChevronRight } from 'lucide-vue-next'
|
||||
import { useCarousel } from './useCarousel'
|
||||
import type { WithClassAsProps } from './interface'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
const props = defineProps<WithClassAsProps>()
|
||||
|
||||
const { orientation, canScrollNext, scrollNext } = useCarousel()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Button
|
||||
:disabled="!canScrollNext"
|
||||
:class="cn(
|
||||
'absolute h-10 w-10 rounded-full p-0',
|
||||
orientation === 'horizontal'
|
||||
? '-right-12 top-1/2 -translate-y-1/2'
|
||||
: '-bottom-12 left-1/2 -translate-x-1/2 rotate-90',
|
||||
props.class,
|
||||
)"
|
||||
variant="outline"
|
||||
@click="scrollNext"
|
||||
>
|
||||
<slot>
|
||||
<ChevronRight class="h-4 w-4 text-current" />
|
||||
</slot>
|
||||
</Button>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<script setup lang="ts">
|
||||
import { ChevronLeft } from 'lucide-vue-next'
|
||||
import { useCarousel } from './useCarousel'
|
||||
import type { WithClassAsProps } from './interface'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
const props = defineProps<WithClassAsProps>()
|
||||
|
||||
const { orientation, canScrollPrev, scrollPrev } = useCarousel()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Button
|
||||
:disabled="!canScrollPrev"
|
||||
:class="cn(
|
||||
'absolute h-10 w-10 rounded-full p-0',
|
||||
orientation === 'horizontal'
|
||||
? '-left-12 top-1/2 -translate-y-1/2'
|
||||
: '-top-12 left-1/2 -translate-x-1/2 rotate-90',
|
||||
props.class,
|
||||
)"
|
||||
variant="outline"
|
||||
@click="scrollPrev"
|
||||
>
|
||||
<slot>
|
||||
<ChevronLeft class="h-4 w-4 text-current" />
|
||||
</slot>
|
||||
</Button>
|
||||
</template>
|
||||
10
packages/module/playground/components/ui/carousel/index.ts
Normal file
10
packages/module/playground/components/ui/carousel/index.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
export { default as Carousel } from './Carousel.vue'
|
||||
export { default as CarouselContent } from './CarouselContent.vue'
|
||||
export { default as CarouselItem } from './CarouselItem.vue'
|
||||
export { default as CarouselPrevious } from './CarouselPrevious.vue'
|
||||
export { default as CarouselNext } from './CarouselNext.vue'
|
||||
export { useCarousel } from './useCarousel'
|
||||
|
||||
export type {
|
||||
EmblaCarouselType as CarouselApi,
|
||||
} from 'embla-carousel'
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
import type {
|
||||
EmblaCarouselType as CarouselApi,
|
||||
EmblaOptionsType as CarouselOptions,
|
||||
EmblaPluginType as CarouselPlugin,
|
||||
} from 'embla-carousel'
|
||||
import type { HTMLAttributes, Ref } from 'vue'
|
||||
|
||||
export interface CarouselProps {
|
||||
opts?: CarouselOptions | Ref<CarouselOptions>
|
||||
plugins?: CarouselPlugin[] | Ref<CarouselPlugin[]>
|
||||
orientation?: 'horizontal' | 'vertical'
|
||||
}
|
||||
|
||||
export interface CarouselEmits {
|
||||
(e: 'init-api', payload: CarouselApi): void
|
||||
}
|
||||
|
||||
export interface WithClassAsProps {
|
||||
class?: HTMLAttributes['class']
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
import { createInjectionState } from '@vueuse/core'
|
||||
import emblaCarouselVue from 'embla-carousel-vue'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import type {
|
||||
EmblaCarouselType as CarouselApi,
|
||||
} from 'embla-carousel'
|
||||
import type { CarouselEmits, CarouselProps } from './interface'
|
||||
|
||||
const [useProvideCarousel, useInjectCarousel] = createInjectionState(
|
||||
({
|
||||
opts, orientation, plugins,
|
||||
}: CarouselProps, emits: CarouselEmits) => {
|
||||
const [emblaNode, emblaApi] = emblaCarouselVue({
|
||||
...opts,
|
||||
axis: orientation === 'horizontal' ? 'x' : 'y',
|
||||
}, plugins)
|
||||
|
||||
function scrollPrev() {
|
||||
emblaApi.value?.scrollPrev()
|
||||
}
|
||||
function scrollNext() {
|
||||
emblaApi.value?.scrollNext()
|
||||
}
|
||||
|
||||
const canScrollNext = ref(true)
|
||||
const canScrollPrev = ref(true)
|
||||
|
||||
function onSelect(api: CarouselApi) {
|
||||
canScrollNext.value = api.canScrollNext()
|
||||
canScrollPrev.value = api.canScrollPrev()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!emblaApi.value)
|
||||
return
|
||||
|
||||
emblaApi.value?.on('init', onSelect)
|
||||
emblaApi.value?.on('reInit', onSelect)
|
||||
emblaApi.value?.on('select', onSelect)
|
||||
|
||||
emits('init-api', emblaApi.value)
|
||||
})
|
||||
|
||||
return { carouselRef: emblaNode, carouselApi: emblaApi, canScrollPrev, canScrollNext, scrollPrev, scrollNext, orientation }
|
||||
},
|
||||
)
|
||||
|
||||
function useCarousel() {
|
||||
const carouselState = useInjectCarousel()
|
||||
|
||||
if (!carouselState)
|
||||
throw new Error('useCarousel must be used within a <Carousel />')
|
||||
|
||||
return carouselState
|
||||
}
|
||||
|
||||
export { useCarousel, useProvideCarousel }
|
||||
|
|
@ -11,6 +11,8 @@
|
|||
"@nuxtjs/tailwindcss": "^6.10.1",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"embla-carousel": "8.0.0-rc19",
|
||||
"embla-carousel-vue": "8.0.0-rc19",
|
||||
"lucide-vue-next": "^0.276.0",
|
||||
"radix-vue": "^1.3.0",
|
||||
"tailwind-merge": "^2.0.0",
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { readFileSync, readdirSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import { addComponent, createResolver, defineNuxtModule } from '@nuxt/kit'
|
||||
import { parse } from 'recast'
|
||||
import oxc from 'oxc-parser'
|
||||
|
||||
// TODO: add test to make sure all registry is being parse correctly
|
||||
// Module options TypeScript interface definition
|
||||
export interface ModuleOptions {
|
||||
/**
|
||||
|
|
@ -40,24 +41,34 @@ export default defineNuxtModule<ModuleOptions>({
|
|||
try {
|
||||
readdirSync(resolve(COMPONENT_DIR_PATH))
|
||||
.forEach(async (dir) => {
|
||||
const filePath = await resolvePath(join(COMPONENT_DIR_PATH, dir, 'index'), { extensions: ['.ts', '.js'] })
|
||||
const content = readFileSync(filePath, { encoding: 'utf8' })
|
||||
const ast = parse(content)
|
||||
|
||||
const exportedKeys: string[] = ast.program.body
|
||||
// @ts-expect-error parse return any
|
||||
.filter(node => node.type === 'ExportNamedDeclaration')
|
||||
// @ts-expect-error parse return any
|
||||
.flatMap(node => node.specifiers.map(specifier => specifier.exported.name))
|
||||
.filter((key: string) => /^[A-Z]/.test(key))
|
||||
|
||||
exportedKeys.forEach((key) => {
|
||||
addComponent({
|
||||
name: `${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),
|
||||
try {
|
||||
const filePath = await resolvePath(join(COMPONENT_DIR_PATH, dir, 'index'), { extensions: ['.ts', '.js'] })
|
||||
const content = readFileSync(filePath, { encoding: 'utf8' })
|
||||
const ast = oxc.parseSync(content, {
|
||||
sourceType: 'module',
|
||||
sourceFilename: filePath,
|
||||
})
|
||||
})
|
||||
const program = JSON.parse(ast.program)
|
||||
|
||||
const exportedKeys: string[] = program.body
|
||||
// @ts-expect-error parse return any
|
||||
.filter(node => node.type === 'ExportNamedDeclaration')
|
||||
// @ts-expect-error parse return any
|
||||
.flatMap(node => node.specifiers.map(specifier => specifier.exported.name))
|
||||
.filter((key: string) => /^[A-Z]/.test(key))
|
||||
|
||||
exportedKeys.forEach((key) => {
|
||||
addComponent({
|
||||
name: `${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('Module error: ', err.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
catch (err) {
|
||||
|
|
|
|||
587
pnpm-lock.yaml
587
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user