63 lines
8.3 KiB
JSON
63 lines
8.3 KiB
JSON
{
|
|
"name": "carousel",
|
|
"type": "registry:ui",
|
|
"dependencies": [
|
|
"embla-carousel-vue",
|
|
"@vueuse/core"
|
|
],
|
|
"registryDependencies": [
|
|
"utils",
|
|
"button"
|
|
],
|
|
"files": [
|
|
{
|
|
"path": "ui/carousel/Carousel.vue",
|
|
"content": "<script setup lang=\"ts\">\nimport type { CarouselEmits, CarouselProps, WithClassAsProps } from './interface'\nimport { cn } from '@/lib/utils'\nimport { useProvideCarousel } from './useCarousel'\n\nconst props = withDefaults(defineProps<CarouselProps & WithClassAsProps>(), {\n orientation: 'horizontal',\n})\n\nconst emits = defineEmits<CarouselEmits>()\n\nconst { canScrollNext, canScrollPrev, carouselApi, carouselRef, orientation, scrollNext, scrollPrev } = useProvideCarousel(props, emits)\n\ndefineExpose({\n canScrollNext,\n canScrollPrev,\n carouselApi,\n carouselRef,\n orientation,\n scrollNext,\n scrollPrev,\n})\n\nfunction onKeyDown(event: KeyboardEvent) {\n const prevKey = props.orientation === 'vertical' ? 'ArrowUp' : 'ArrowLeft'\n const nextKey = props.orientation === 'vertical' ? 'ArrowDown' : 'ArrowRight'\n\n if (event.key === prevKey) {\n event.preventDefault()\n scrollPrev()\n\n return\n }\n\n if (event.key === nextKey) {\n event.preventDefault()\n scrollNext()\n }\n}\n</script>\n\n<template>\n <div\n :class=\"cn('relative', props.class)\"\n role=\"region\"\n aria-roledescription=\"carousel\"\n tabindex=\"0\"\n @keydown=\"onKeyDown\"\n >\n <slot :can-scroll-next :can-scroll-prev :carousel-api :carousel-ref :orientation :scroll-next :scroll-prev />\n </div>\n</template>\n",
|
|
"type": "registry:ui",
|
|
"target": "carousel/Carousel.vue"
|
|
},
|
|
{
|
|
"path": "ui/carousel/CarouselContent.vue",
|
|
"content": "<script setup lang=\"ts\">\nimport type { WithClassAsProps } from './interface'\nimport { cn } from '@/lib/utils'\nimport { useCarousel } from './useCarousel'\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst props = defineProps<WithClassAsProps>()\n\nconst { carouselRef, orientation } = useCarousel()\n</script>\n\n<template>\n <div ref=\"carouselRef\" class=\"overflow-hidden\">\n <div\n :class=\"\n cn(\n 'flex',\n orientation === 'horizontal' ? '-ml-4' : '-mt-4 flex-col',\n props.class,\n )\"\n v-bind=\"$attrs\"\n >\n <slot />\n </div>\n </div>\n</template>\n",
|
|
"type": "registry:ui",
|
|
"target": "carousel/CarouselContent.vue"
|
|
},
|
|
{
|
|
"path": "ui/carousel/CarouselItem.vue",
|
|
"content": "<script setup lang=\"ts\">\nimport type { WithClassAsProps } from './interface'\nimport { cn } from '@/lib/utils'\nimport { useCarousel } from './useCarousel'\n\nconst props = defineProps<WithClassAsProps>()\n\nconst { orientation } = useCarousel()\n</script>\n\n<template>\n <div\n role=\"group\"\n aria-roledescription=\"slide\"\n :class=\"cn(\n 'min-w-0 shrink-0 grow-0 basis-full',\n orientation === 'horizontal' ? 'pl-4' : 'pt-4',\n props.class,\n )\"\n >\n <slot />\n </div>\n</template>\n",
|
|
"type": "registry:ui",
|
|
"target": "carousel/CarouselItem.vue"
|
|
},
|
|
{
|
|
"path": "ui/carousel/CarouselNext.vue",
|
|
"content": "<script setup lang=\"ts\">\nimport type { WithClassAsProps } from './interface'\nimport { Button } from '@/registry/default/ui/button'\nimport { cn } from '@/lib/utils'\nimport { ArrowRight } from 'lucide-vue-next'\nimport { useCarousel } from './useCarousel'\n\nconst props = defineProps<WithClassAsProps>()\n\nconst { orientation, canScrollNext, scrollNext } = useCarousel()\n</script>\n\n<template>\n <Button\n :disabled=\"!canScrollNext\"\n :class=\"cn(\n 'touch-manipulation absolute h-8 w-8 rounded-full p-0',\n orientation === 'horizontal'\n ? '-right-12 top-1/2 -translate-y-1/2'\n : '-bottom-12 left-1/2 -translate-x-1/2 rotate-90',\n props.class,\n )\"\n variant=\"outline\"\n @click=\"scrollNext\"\n >\n <slot>\n <ArrowRight class=\"h-4 w-4 text-current\" />\n <span class=\"sr-only\">Next Slide</span>\n </slot>\n </Button>\n</template>\n",
|
|
"type": "registry:ui",
|
|
"target": "carousel/CarouselNext.vue"
|
|
},
|
|
{
|
|
"path": "ui/carousel/CarouselPrevious.vue",
|
|
"content": "<script setup lang=\"ts\">\nimport type { WithClassAsProps } from './interface'\nimport { Button } from '@/registry/default/ui/button'\nimport { cn } from '@/lib/utils'\nimport { ArrowLeft } from 'lucide-vue-next'\nimport { useCarousel } from './useCarousel'\n\nconst props = defineProps<WithClassAsProps>()\n\nconst { orientation, canScrollPrev, scrollPrev } = useCarousel()\n</script>\n\n<template>\n <Button\n :disabled=\"!canScrollPrev\"\n :class=\"cn(\n 'touch-manipulation absolute h-8 w-8 rounded-full p-0',\n orientation === 'horizontal'\n ? '-left-12 top-1/2 -translate-y-1/2'\n : '-top-12 left-1/2 -translate-x-1/2 rotate-90',\n props.class,\n )\"\n variant=\"outline\"\n @click=\"scrollPrev\"\n >\n <slot>\n <ArrowLeft class=\"h-4 w-4 text-current\" />\n <span class=\"sr-only\">Previous Slide</span>\n </slot>\n </Button>\n</template>\n",
|
|
"type": "registry:ui",
|
|
"target": "carousel/CarouselPrevious.vue"
|
|
},
|
|
{
|
|
"path": "ui/carousel/index.ts",
|
|
"content": "export { default as Carousel } from './Carousel.vue'\nexport { default as CarouselContent } from './CarouselContent.vue'\nexport { default as CarouselItem } from './CarouselItem.vue'\nexport { default as CarouselNext } from './CarouselNext.vue'\nexport { default as CarouselPrevious } from './CarouselPrevious.vue'\nexport type {\n UnwrapRefCarouselApi as CarouselApi,\n} from './interface'\n\nexport { useCarousel } from './useCarousel'\n",
|
|
"type": "registry:ui",
|
|
"target": "carousel/index.ts"
|
|
},
|
|
{
|
|
"path": "ui/carousel/interface.ts",
|
|
"content": "import type useEmblaCarousel from 'embla-carousel-vue'\nimport type {\n EmblaCarouselVueType,\n} from 'embla-carousel-vue'\nimport type { HTMLAttributes, UnwrapRef } from 'vue'\n\ntype CarouselApi = EmblaCarouselVueType[1]\ntype UseCarouselParameters = Parameters<typeof useEmblaCarousel>\ntype CarouselOptions = UseCarouselParameters[0]\ntype CarouselPlugin = UseCarouselParameters[1]\n\nexport type UnwrapRefCarouselApi = UnwrapRef<CarouselApi>\n\nexport interface CarouselProps {\n opts?: CarouselOptions\n plugins?: CarouselPlugin\n orientation?: 'horizontal' | 'vertical'\n}\n\nexport interface CarouselEmits {\n (e: 'init-api', payload: UnwrapRefCarouselApi): void\n}\n\nexport interface WithClassAsProps {\n class?: HTMLAttributes['class']\n}\n",
|
|
"type": "registry:ui",
|
|
"target": "carousel/interface.ts"
|
|
},
|
|
{
|
|
"path": "ui/carousel/useCarousel.ts",
|
|
"content": "import type { UnwrapRefCarouselApi as CarouselApi, CarouselEmits, CarouselProps } from './interface'\nimport { createInjectionState } from '@vueuse/core'\nimport emblaCarouselVue from 'embla-carousel-vue'\nimport { onMounted, ref } from 'vue'\n\nconst [useProvideCarousel, useInjectCarousel] = createInjectionState(\n ({\n opts,\n orientation,\n plugins,\n }: CarouselProps, emits: CarouselEmits) => {\n const [emblaNode, emblaApi] = emblaCarouselVue({\n ...opts,\n axis: orientation === 'horizontal' ? 'x' : 'y',\n }, plugins)\n\n function scrollPrev() {\n emblaApi.value?.scrollPrev()\n }\n function scrollNext() {\n emblaApi.value?.scrollNext()\n }\n\n const canScrollNext = ref(false)\n const canScrollPrev = ref(false)\n\n function onSelect(api: CarouselApi) {\n canScrollNext.value = api?.canScrollNext() || false\n canScrollPrev.value = api?.canScrollPrev() || false\n }\n\n onMounted(() => {\n if (!emblaApi.value)\n return\n\n emblaApi.value?.on('init', onSelect)\n emblaApi.value?.on('reInit', onSelect)\n emblaApi.value?.on('select', onSelect)\n\n emits('init-api', emblaApi.value)\n })\n\n return { carouselRef: emblaNode, carouselApi: emblaApi, canScrollPrev, canScrollNext, scrollPrev, scrollNext, orientation }\n },\n)\n\nfunction useCarousel() {\n const carouselState = useInjectCarousel()\n\n if (!carouselState)\n throw new Error('useCarousel must be used within a <Carousel />')\n\n return carouselState\n}\n\nexport { useCarousel, useProvideCarousel }\n",
|
|
"type": "registry:ui",
|
|
"target": "carousel/useCarousel.ts"
|
|
}
|
|
]
|
|
}
|