Components

Carousel

A carousel with motion and swipe built using Embla-style semantics.

Overview

视口 overflow-hidden + flex track 平移,两侧圆形 outline 按钮(起始位 prev 禁用),点按发射 Slide X of N。

GPUI · Live previewGPUI WASM

Installation

Carousel 是单个 Rust 文件(src/carousel.rs)—— track/viewport/位移全自绘,导航按钮复用 button.rs。

Cargo.toml
[dependencies]gpui-kit = "0.6.6"# carousel.rs composes button.rs for the nav controls —# both ship in source.zip.

Download runnable example

Terminal
# Download the runnable example crate, then:cargo run --locked -- --carousel# Other demos:cargo run --locked -- --carousel sizescargo run --locked -- --carousel spacingcargo run --locked -- --carousel verticalcargo run --locked -- --carousel loop

Usage

Carousel 是 entity:cx.new(|cx| Carousel::new(cx)) 拿到 focus handle 后配置 slide 数量、每张占比(per_view)、间距与轴向。滚动状态由组件自持,选中变化通过 CarouselSelectEvent 订阅。

Example.rs
use justdo_command::carousel::{Carousel, CarouselSelectEvent};let carousel = cx.new(|cx| {    Carousel::new(cx)        .dark(dark)        .count(5)            // demo slides 1..=5        .per_view(1)         // basis-full — one slide per viewport        .gap(16.)        .slide_px(336.)      // extent along the scroll axis        .cross_px(200.)      // cross-axis extent        .loop_(false)});// Current-slide API — emitted by nav buttons and arrow keys.cx.subscribe(&carousel, |_, _, e: &CarouselSelectEvent, _| {    println!("slide {} of {}", e.index + 1, e.count);}).detach();

网页预览与桌面端运行同一份 Rust / GPUI 组件。

Composition

组合结构对齐 shadcn/ui 的 Carousel / CarouselContent / CarouselItem / CarouselPrevious / CarouselNext(Embla)。

Structure
Carousel              root — relative, focusable, arrow-key scroll├── viewport          overflow-hidden clip box│   └── track         flex-row (or flex-col) + gap, offset by -scroll px│       ├── slide     flex_none, slide × cross, rounded card│       └── …         rendered twice when loop_(true)├── prev              circular outline button, -left-44px, disabled at 0└── next              circular outline button, -right-44px, disabled at end

Notes

Scroll & loop. 位移是 track 上的单个动画 px offset(负 margin),ease-out cubic 320ms。loop_(true) 会把 slide 列表渲染两份:回绕时动画先滚进复制 lap,落地后 offset 按一圈取模归位 —— 视觉上连续无缝。

Example.rs
// Scroll model: one animated px offset on the flex track// (negative margin), eased cubic-out over 320ms like Embla.// loop_(true) renders the slide list twice; a wrap-around// animates into the duplicated lap then snaps the offset// back by one lap — visually continuous, no blank space.car.go(-1, cx);  // previous slidecar.go(1, cx);   // next slide (clamped or wrapped)

Keyboard. 根容器可聚焦(点击视口聚焦):横排响应 ←/→,竖排(.vertical(true))响应 ↑/↓,与 Embla 的键盘映射一致。

Boundaries. 非 loop 模式首尾按钮禁用(can_prev / can_next);per_view > 1 时末位自动停在最后完整视口(count - per_view)。

API Reference

Carousel 实体与事件。

APISignatureNotes
Carousel::new(cx)-> CarouselEntity; needs cx for the focus handle.
.count(..)usizeSlide count (demo slides 1..=n).
.per_view(..)usizeSlides per viewport — Embla basis-1/n.
.gap(..)f32Slide gutter — Embla pl-*.
.slide_px(..) / .cross_px(..)f32Slide extent on the scroll axis / cross axis.
.vertical(..)boolVertical axis: column track + top/bottom buttons.
.loop_(..)boolEmbla opts.loop — seamless wrap, buttons never disable.
.dark(..)boolDark theme.
CarouselSelectEvent{ index, count }Emitted on every slide change — the current-slide API.

Source & inspiration

结构与交互参考 shadcn/ui Carousel (React Aria / Embla) 。本页交互由 Rust / GPUI 绘制,基础样式来自 gpui-kit 。Autoplay/插件体系与 RTL 未移植。