-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2d3fc68
commit 6112e9b
Showing
17 changed files
with
924 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"name": "carousel", | ||
"dependencies": [ | ||
"embla-carousel-solid" | ||
], | ||
"files": [ | ||
{ | ||
"name": "carousel.tsx", | ||
"content": "import type { Accessor, Component, ComponentProps, VoidProps } from \"solid-js\"\nimport {\n createContext,\n createEffect,\n createMemo,\n createSignal,\n mergeProps,\n splitProps,\n useContext\n} from \"solid-js\"\n\nimport type { CreateEmblaCarouselType } from \"embla-carousel-solid\"\nimport createEmblaCarousel from \"embla-carousel-solid\"\nimport { TbArrowLeft, TbArrowRight } from \"solid-icons/tb\"\n\nimport { cn } from \"~/lib/utils\"\nimport { Button, ButtonProps } from \"~/registry/ui/button\"\n\nexport type CarouselApi = CreateEmblaCarouselType[1]\n\ntype UseCarouselParameters = Parameters<typeof createEmblaCarousel>\ntype CarouselOptions = NonNullable<UseCarouselParameters[0]>\ntype CarouselPlugin = NonNullable<UseCarouselParameters[1]>\n\ntype CarouselProps = {\n opts?: ReturnType<CarouselOptions>\n plugins?: ReturnType<CarouselPlugin>\n orientation?: \"horizontal\" | \"vertical\"\n setApi?: (api: CarouselApi) => void\n}\n\ntype CarouselContextProps = {\n carouselRef: ReturnType<typeof createEmblaCarousel>[0]\n api: ReturnType<typeof createEmblaCarousel>[1]\n scrollPrev: () => void\n scrollNext: () => void\n canScrollPrev: Accessor<boolean>\n canScrollNext: Accessor<boolean>\n} & CarouselProps\n\nconst CarouselContext = createContext<Accessor<CarouselContextProps> | null>(null)\n\nconst useCarousel = () => {\n const context = useContext(CarouselContext)\n\n if (!context) {\n throw new Error(\"useCarousel must be used within a <Carousel />\")\n }\n\n return context()\n}\n\nconst Carousel: Component<CarouselProps & ComponentProps<\"div\">> = (rawProps) => {\n const props = mergeProps<(CarouselProps & ComponentProps<\"div\">)[]>(\n { orientation: \"horizontal\" },\n rawProps\n )\n\n const [, rest] = splitProps(props, [\n \"orientation\",\n \"opts\",\n \"setApi\",\n \"plugins\",\n \"class\",\n \"children\"\n ])\n\n const [carouselRef, api] = createEmblaCarousel(\n () => ({\n ...props.opts,\n axis: props.orientation === \"horizontal\" ? \"x\" : \"y\"\n }),\n () => (props.plugins === undefined ? [] : props.plugins)\n )\n const [canScrollPrev, setCanScrollPrev] = createSignal(false)\n const [canScrollNext, setCanScrollNext] = createSignal(false)\n\n const onSelect = (api: NonNullable<ReturnType<CarouselApi>>) => {\n setCanScrollPrev(api.canScrollPrev())\n setCanScrollNext(api.canScrollNext())\n }\n\n const scrollPrev = () => {\n api()?.scrollPrev()\n }\n\n const scrollNext = () => {\n api()?.scrollNext()\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"ArrowLeft\") {\n event.preventDefault()\n scrollPrev()\n } else if (event.key === \"ArrowRight\") {\n event.preventDefault()\n scrollNext()\n }\n }\n\n createEffect(() => {\n if (!api() || !props.setApi) {\n return\n }\n\n props.setApi(api)\n })\n\n createEffect(() => {\n if (!api()) {\n return\n }\n\n onSelect(api()!)\n api()!.on(\"reInit\", onSelect)\n api()!.on(\"select\", onSelect)\n\n return () => {\n api()?.off(\"select\", onSelect)\n }\n })\n\n const value = createMemo(\n () =>\n ({\n carouselRef,\n api,\n opts: props.opts,\n orientation: props.orientation || (props.opts?.axis === \"y\" ? \"vertical\" : \"horizontal\"),\n scrollPrev,\n scrollNext,\n canScrollPrev,\n canScrollNext\n }) satisfies CarouselContextProps\n )\n\n return (\n <CarouselContext.Provider value={value}>\n <div\n onKeyDown={handleKeyDown}\n class={cn(\"relative\", props.class)}\n role=\"region\"\n aria-roledescription=\"carousel\"\n {...rest}\n >\n {props.children}\n </div>\n </CarouselContext.Provider>\n )\n}\n\nconst CarouselContent: Component<ComponentProps<\"div\">> = (props) => {\n const [, rest] = splitProps(props, [\"class\"])\n const { carouselRef, orientation } = useCarousel()\n\n return (\n <div ref={carouselRef} class=\"overflow-hidden\">\n <div\n class={cn(\"flex\", orientation === \"horizontal\" ? \"-ml-4\" : \"-mt-4 flex-col\", props.class)}\n {...rest}\n />\n </div>\n )\n}\n\nconst CarouselItem: Component<ComponentProps<\"div\">> = (props) => {\n const [, rest] = splitProps(props, [\"class\"])\n const { orientation } = useCarousel()\n\n return (\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 {...rest}\n />\n )\n}\n\ntype CarouselButtonProps = VoidProps<ButtonProps>\n\nconst CarouselPrevious: Component<CarouselButtonProps> = (rawProps) => {\n const props = mergeProps<CarouselButtonProps[]>({ variant: \"outline\", size: \"icon\" }, rawProps)\n const [, rest] = splitProps(props, [\"class\", \"variant\", \"size\"])\n const { orientation, scrollPrev, canScrollPrev } = useCarousel()\n\n return (\n <Button\n variant={props.variant}\n size={props.size}\n class={cn(\n \"absolute size-8 rounded-full\",\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 disabled={!canScrollPrev()}\n onClick={scrollPrev}\n {...rest}\n >\n <TbArrowLeft class=\"size-4\" />\n <span class=\"sr-only\">Previous slide</span>\n </Button>\n )\n}\n\nconst CarouselNext: Component<CarouselButtonProps> = (rawProps) => {\n const props = mergeProps<CarouselButtonProps[]>({ variant: \"outline\", size: \"icon\" }, rawProps)\n const [, rest] = splitProps(props, [\"class\", \"variant\", \"size\"])\n const { orientation, scrollNext, canScrollNext } = useCarousel()\n\n return (\n <Button\n variant={props.variant}\n size={props.size}\n class={cn(\n \"absolute size-8 rounded-full\",\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 disabled={!canScrollNext()}\n onClick={scrollNext}\n {...rest}\n >\n <TbArrowRight class=\"size-4\" />\n <span class=\"sr-only\">Next slide</span>\n </Button>\n )\n}\n\nexport { Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext }\n" | ||
} | ||
], | ||
"type": "ui" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { createEffect, createSignal, Index } from "solid-js" | ||
|
||
import { Card, CardContent } from "~/registry/ui/card" | ||
import { | ||
Carousel, | ||
CarouselContent, | ||
CarouselItem, | ||
CarouselNext, | ||
CarouselPrevious, | ||
type CarouselApi | ||
} from "~/registry/ui/carousel" | ||
|
||
export default function CarouselApiDemo() { | ||
const [api, setApi] = createSignal<ReturnType<CarouselApi>>() | ||
const [current, setCurrent] = createSignal(0) | ||
const [count, setCount] = createSignal(0) | ||
|
||
const onSelect = () => { | ||
setCurrent(api()!.selectedScrollSnap() + 1) | ||
} | ||
|
||
createEffect(() => { | ||
if (!api()) { | ||
return | ||
} | ||
|
||
setCount(api()!.scrollSnapList().length) | ||
setCurrent(api()!.selectedScrollSnap() + 1) | ||
|
||
api()!.on("select", onSelect) | ||
}) | ||
|
||
return ( | ||
<div> | ||
<Carousel setApi={setApi} class="w-full max-w-xs"> | ||
<CarouselContent> | ||
<Index each={Array.from({ length: 5 })}> | ||
{(_, index) => ( | ||
<CarouselItem> | ||
<Card> | ||
<CardContent class="flex aspect-square items-center justify-center p-6"> | ||
<span class="text-4xl font-semibold">{index + 1}</span> | ||
</CardContent> | ||
</Card> | ||
</CarouselItem> | ||
)} | ||
</Index> | ||
</CarouselContent> | ||
<CarouselPrevious /> | ||
<CarouselNext /> | ||
</Carousel> | ||
<div class="py-2 text-center text-sm text-muted-foreground"> | ||
Slide {current()} of {count()} | ||
</div> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { Index } from "solid-js" | ||
|
||
import { Card, CardContent } from "~/registry/ui/card" | ||
import { | ||
Carousel, | ||
CarouselContent, | ||
CarouselItem, | ||
CarouselNext, | ||
CarouselPrevious | ||
} from "~/registry/ui/carousel" | ||
|
||
export default function CarouselDemo() { | ||
return ( | ||
<Carousel class="w-full max-w-xs"> | ||
<CarouselContent> | ||
<Index each={Array.from({ length: 5 })}> | ||
{(_, index) => ( | ||
<CarouselItem> | ||
<div class="p-1"> | ||
<Card> | ||
<CardContent class="flex aspect-square items-center justify-center p-6"> | ||
<span class="text-4xl font-semibold">{index + 1}</span> | ||
</CardContent> | ||
</Card> | ||
</div> | ||
</CarouselItem> | ||
)} | ||
</Index> | ||
</CarouselContent> | ||
<CarouselPrevious /> | ||
<CarouselNext /> | ||
</Carousel> | ||
) | ||
} |
40 changes: 40 additions & 0 deletions
40
apps/docs/src/registry/example/carousel-orientation-demo.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { Index } from "solid-js" | ||
|
||
import { Card, CardContent } from "~/registry/ui/card" | ||
import { | ||
Carousel, | ||
CarouselContent, | ||
CarouselItem, | ||
CarouselNext, | ||
CarouselPrevious | ||
} from "~/registry/ui/carousel" | ||
|
||
export default function CarouselOrientationDemo() { | ||
return ( | ||
<Carousel | ||
opts={{ | ||
align: "start" | ||
}} | ||
orientation="vertical" | ||
class="w-full max-w-xs" | ||
> | ||
<CarouselContent class="-mt-1 h-[200px]"> | ||
<Index each={Array.from({ length: 5 })}> | ||
{(_, index) => ( | ||
<CarouselItem class="pt-1 md:basis-1/2"> | ||
<div class="p-1"> | ||
<Card> | ||
<CardContent class="flex items-center justify-center p-6"> | ||
<span class="text-3xl font-semibold">{index + 1}</span> | ||
</CardContent> | ||
</Card> | ||
</div> | ||
</CarouselItem> | ||
)} | ||
</Index> | ||
</CarouselContent> | ||
<CarouselPrevious /> | ||
<CarouselNext /> | ||
</Carousel> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { Index } from "solid-js" | ||
|
||
import Autoplay from "embla-carousel-autoplay" | ||
|
||
import { Card, CardContent } from "~/registry/ui/card" | ||
import { | ||
Carousel, | ||
CarouselContent, | ||
CarouselItem, | ||
CarouselNext, | ||
CarouselPrevious | ||
} from "~/registry/ui/carousel" | ||
|
||
export default function CarouselPluginDemo() { | ||
let plugin = Autoplay({ delay: 2000, stopOnInteraction: true }) | ||
|
||
return ( | ||
<Carousel | ||
plugins={[plugin]} | ||
class="w-full max-w-xs" | ||
onMouseEnter={plugin.stop} | ||
onMouseLeave={() => plugin.play(false)} | ||
> | ||
<CarouselContent> | ||
<Index each={Array.from({ length: 5 })}> | ||
{(_, index) => ( | ||
<CarouselItem> | ||
<div class="p-1"> | ||
<Card> | ||
<CardContent class="flex aspect-square items-center justify-center p-6"> | ||
<span class="text-4xl font-semibold">{index + 1}</span> | ||
</CardContent> | ||
</Card> | ||
</div> | ||
</CarouselItem> | ||
)} | ||
</Index> | ||
</CarouselContent> | ||
<CarouselPrevious /> | ||
<CarouselNext /> | ||
</Carousel> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { Index } from "solid-js" | ||
|
||
import { Card, CardContent } from "~/registry/ui/card" | ||
import { | ||
Carousel, | ||
CarouselContent, | ||
CarouselItem, | ||
CarouselNext, | ||
CarouselPrevious | ||
} from "~/registry/ui/carousel" | ||
|
||
export default function CarouselSizeDemo() { | ||
return ( | ||
<Carousel | ||
opts={{ | ||
align: "start" | ||
}} | ||
class="w-full max-w-sm" | ||
> | ||
<CarouselContent> | ||
<Index each={Array.from({ length: 5 })}> | ||
{(_, index) => ( | ||
<CarouselItem class="md:basis-1/2 lg:basis-1/3"> | ||
<div class="p-1"> | ||
<Card> | ||
<CardContent class="flex aspect-square items-center justify-center p-6"> | ||
<span class="text-3xl font-semibold">{index + 1}</span> | ||
</CardContent> | ||
</Card> | ||
</div> | ||
</CarouselItem> | ||
)} | ||
</Index> | ||
</CarouselContent> | ||
<CarouselPrevious /> | ||
<CarouselNext /> | ||
</Carousel> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { Index } from "solid-js" | ||
|
||
import { Card, CardContent } from "~/registry/ui/card" | ||
import { | ||
Carousel, | ||
CarouselContent, | ||
CarouselItem, | ||
CarouselNext, | ||
CarouselPrevious | ||
} from "~/registry/ui/carousel" | ||
|
||
export default function CarouselSpacingDemo() { | ||
return ( | ||
<Carousel class="w-full max-w-sm"> | ||
<CarouselContent class="-ml-1"> | ||
<Index each={Array.from({ length: 5 })}> | ||
{(_, index) => ( | ||
<CarouselItem class="pl-1 md:basis-1/2 lg:basis-1/3"> | ||
<div class="p-1"> | ||
<Card> | ||
<CardContent class="flex aspect-square items-center justify-center p-6"> | ||
<span class="text-2xl font-semibold">{index + 1}</span> | ||
</CardContent> | ||
</Card> | ||
</div> | ||
</CarouselItem> | ||
)} | ||
</Index> | ||
</CarouselContent> | ||
<CarouselPrevious /> | ||
<CarouselNext /> | ||
</Carousel> | ||
) | ||
} |
Oops, something went wrong.