Carousel
Basic
package carousel
import "github.com/gsxhq/gsxui/ui"
var slides = []int{1, 2, 3, 4, 5}
// Basic mirrors shadcn's own carousel-demo.tsx: single item per view (no
// per-item basis override), five numbered slides each in a bordered
// aspect-square Card, the docs' own baseline look.
component Basic() {
<ui.Carousel orientation="" class="mx-auto w-full max-w-xs">
<ui.CarouselContent orientation="">
{ for _, n := range slides {
<ui.CarouselItem orientation="">
<div class="p-1">
<ui.Card>
<ui.CardContent class="flex aspect-square items-center justify-center p-6">
<span class="text-4xl font-semibold">{ n }</span>
</ui.CardContent>
</ui.Card>
</div>
</ui.CarouselItem>
} }
</ui.CarouselContent>
<ui.CarouselPrevious orientation=""/>
<ui.CarouselNext orientation=""/>
</ui.Carousel>
}
Sizes
package carousel
import "github.com/gsxhq/gsxui/ui"
var sizeSlides = []int{1, 2, 3, 4, 5}
// Sizes mirrors shadcn's own carousel-size.tsx combined with
// carousel-spacing.tsx (the map's own demo-inventory note picks one of the
// two near-duplicates; this folds both features into a single example):
// multi-per-view responsive basis (two per view at md, three at lg) plus a
// tightened gap (CarouselContent's own -ml-1 override, each item's own
// pl-1) proving the gap is entirely caller-controlled via class merge, no
// separate spacing prop.
component Sizes() {
<ui.Carousel orientation="" class="mx-auto w-full max-w-sm">
<ui.CarouselContent orientation="" class="-ml-1">
{ for _, n := range sizeSlides {
<ui.CarouselItem orientation="" class="pl-1 -scroll-ml-1 md:basis-1/2 lg:basis-1/3">
<div class="p-1">
<ui.Card>
<ui.CardContent class="flex aspect-square items-center justify-center p-6">
<span class="text-2xl font-semibold">{ n }</span>
</ui.CardContent>
</ui.Card>
</div>
</ui.CarouselItem>
} }
</ui.CarouselContent>
<ui.CarouselPrevious orientation=""/>
<ui.CarouselNext orientation=""/>
</ui.Carousel>
}
Vertical
package carousel
import "github.com/gsxhq/gsxui/ui"
var verticalSlides = []int{1, 2, 3, 4, 5}
// Vertical mirrors shadcn's vertical carousel example. It is also the
// production proof that every Carousel part reflects the vertical
// orientation declared by the public style contract.
component Vertical() {
<ui.Carousel orientation="vertical" class="mx-auto w-full max-w-xs">
<ui.CarouselContent orientation="vertical" class="-mt-1 h-[200px]">
{ for _, n := range verticalSlides {
<ui.CarouselItem orientation="vertical" class="pt-1 -scroll-mt-1 md:basis-1/2">
<div class="p-1">
<ui.Card>
<ui.CardContent class="flex items-center justify-center p-6">
<span class="text-3xl font-semibold">{ n }</span>
</ui.CardContent>
</ui.Card>
</div>
</ui.CarouselItem>
} }
</ui.CarouselContent>
<ui.CarouselPrevious orientation="vertical"/>
<ui.CarouselNext orientation="vertical"/>
</ui.Carousel>
}
API
package carousel
import "github.com/gsxhq/gsxui/ui"
var apiSlides = []int{1, 2, 3, 4, 5}
// Api mirrors shadcn's own carousel-api.tsx: a "Slide X of Y" indicator
// below the carousel. shadcn drives it from embla's own setApi/api.on
// ("select") client state; this port has no equivalent client API object,
// so the indicator listens for the gsxui:carousel-select CustomEvent
// carousel.js emits on the carousel root instead — the direct proof case
// for that event's {index, count} contract (index is 0-based, matching
// gsxuiCarousel.scrollTo's own indexing, hence the +1 below). The initial
// text reads "Slide 1 of 5" without any JS having run yet, matching the
// same scrollLeft-starts-at-0 invariant CarouselPrevious's own initial
// disabled state relies on (see ui/carousel.gsx's package doc comment).
component Api() {
<div class="mx-auto max-w-xs">
<ui.Carousel id="api-carousel" orientation="" class="w-full max-w-xs">
<ui.CarouselContent orientation="">
{ for _, n := range apiSlides {
<ui.CarouselItem orientation="">
<ui.Card>
<ui.CardContent class="flex aspect-square items-center justify-center p-6">
<span class="text-4xl font-semibold">{ n }</span>
</ui.CardContent>
</ui.Card>
</ui.CarouselItem>
} }
</ui.CarouselContent>
<ui.CarouselPrevious orientation=""/>
<ui.CarouselNext orientation=""/>
</ui.Carousel>
<output id="api-indicator" class="block py-2 text-center text-sm text-muted-foreground">Slide 1 of 5</output>
<script>
document.addEventListener("gsxui:carousel-select", (e) => {
if (e.target.id !== "api-carousel") return;
document.getElementById("api-indicator").textContent =
"Slide " + (e.detail.index + 1) + " of " + e.detail.count;
});
</script>
</div>
}
RTL
package carousel
import "github.com/gsxhq/gsxui/ui"
var rtlSlides = []string{"١", "٢", "٣", "٤", "٥"}
// Rtl mirrors shadcn's own carousel-rtl demo: five slides numbered with
// Arabic-Indic numerals, wrapped in dir="rtl". gsxui's Carousel has no
// direction option to mirror shadcn's embla opts.direction (see
// ui/carousel.gsx); the dir="rtl" wrapper alone is what Basic's own
// pattern (and pagination/rtl.gsx) relies on for logical-property flip.
component Rtl() {
<div dir="rtl" lang="ar" class="mx-auto w-full max-w-xs">
<ui.Carousel orientation="" class="mx-auto w-full max-w-xs">
<ui.CarouselContent orientation="">
{ for _, n := range rtlSlides {
<ui.CarouselItem orientation="">
<div class="p-1">
<ui.Card>
<ui.CardContent class="flex aspect-square items-center justify-center p-6">
<span class="text-4xl font-semibold">{ n }</span>
</ui.CardContent>
</ui.Card>
</div>
</ui.CarouselItem>
} }
</ui.CarouselContent>
<ui.CarouselPrevious orientation=""/>
<ui.CarouselNext orientation=""/>
</ui.Carousel>
</div>
}