Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/dev/src/components/ComponentsCheckPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ export default function ComponentsCheck() {
{ name: "Avatar", element: <Avatar size="m" /> },
{ name: "AvatarGroup", element: <AvatarGroup avatars={[{}, {}, {}]} /> },
{ name: "Media", element: <Media src="/images/cover-01.jpg" aspectRatio="16/9" radius="l" /> },
{ name: "Carousel", element: <Row height={32} fillWidth><Carousel items={[{ slide: "/images/demo.jpg" }]} /></Row> },
{ name: "Carousel", element: <Row height={32} fillWidth><Carousel items={[{ slide: "/images/demo.jpg" }, { slide: "/images/demo.jpg" }]} /></Row> },
{ name: "Logo", element: <Logo icon="/trademarks/icon-dark.svg" size="xs" /> },
{ name: "LogoCloud", element: <LogoCloud logos={[{ icon: "/trademarks/icon-dark.svg" }]} /> },
],
Expand Down
62 changes: 36 additions & 26 deletions packages/core/src/components/Carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ interface CarouselProps extends React.ComponentProps<typeof Flex> {
sizes?: string;
revealedByDefault?: boolean;
thumbnail?: ThumbnailItem;
play?: {auto?: boolean, interval?: number, controls?: boolean, progress?: boolean};
play?: { auto?: boolean, interval?: number, controls?: boolean, progress?: boolean };
}

const Carousel: React.FC<CarouselProps> = ({
Expand All @@ -41,15 +41,16 @@ const Carousel: React.FC<CarouselProps> = ({
sizes,
revealedByDefault = false,
thumbnail = { scaling: 1, height: "80", sizes: "120px" },
play = {auto: false, interval: 3000, controls: true},
play = { auto: false, interval: 3000, controls: true },
...rest
}) => {
const [activeIndex, setActiveIndex] = useState<number>(0);
const [hoverIndex, setHoverIndex] = useState<number|undefined>(0);
const [isTransitioning, setIsTransitioning] = useState(revealedByDefault);
const [initialTransition, setInitialTransition] = useState(revealedByDefault);
const [isPlaying, setIsPlaying] = useState<boolean>(play.auto || false);
const [progressPercent, setProgressPercent] = useState<number>(0);

// Initialize auto-play state when props change
useEffect(() => {
setIsPlaying(play.auto || false);
Expand Down Expand Up @@ -101,7 +102,7 @@ const Carousel: React.FC<CarouselProps> = ({
}, 300);
}
};

// Simple function to handle auto-play
const handleNextWithLoop = () => {
const nextIndex = activeIndex < items.length - 1 ? activeIndex + 1 : 0;
Expand All @@ -111,39 +112,39 @@ const Carousel: React.FC<CarouselProps> = ({
// Progress tracking for animation
useEffect(() => {
let progressTimer: NodeJS.Timeout | undefined;

if (isPlaying && play.progress && items.length > 1) {
// Reset progress when slide changes
setProgressPercent(0);

// Update progress every 50ms
const updateFrequency = 50; // ms
const interval = play.interval || 3000; // Default to 3000ms if undefined
const totalSteps = Math.floor(interval / updateFrequency);
let currentStep = 0;

progressTimer = setInterval(() => {
currentStep++;
const percent = Math.min((currentStep / totalSteps) * 100, 100);
setProgressPercent(percent);
}, updateFrequency);
}

return () => {
if (progressTimer) {
clearInterval(progressTimer);
}
};
}, [isPlaying, activeIndex, play.interval, play.progress, items.length]);

// Handle auto-play functionality
useEffect(() => {
// Clear any existing interval first
if (autoPlayIntervalRef.current) {
clearInterval(autoPlayIntervalRef.current);
autoPlayIntervalRef.current = undefined;
}

// Start auto-play if enabled
if (isPlaying && items.length > 1) {
autoPlayIntervalRef.current = setInterval(() => {
Expand Down Expand Up @@ -184,7 +185,7 @@ const Carousel: React.FC<CarouselProps> = ({
}

return (
<Column fillWidth fillHeight={fill} gap="12" {...rest} aspectRatio={undefined} style={{isolation: "isolate"}}>
<Column fillWidth fillHeight={fill} gap="0" {...rest} aspectRatio={undefined} style={{ isolation: "isolate" }}>
{items.length > 1 && play.controls && play.auto && (
<Flex
position="absolute"
Expand Down Expand Up @@ -372,35 +373,44 @@ const Carousel: React.FC<CarouselProps> = ({
zIndex={1}
>
<Row radius="full" background="neutral-alpha-weak" height="2" fillWidth>
<Row radius="full" solid="brand-strong" style={{width: `${progressPercent}%`, transition: `width 0.05s linear`}} fillHeight />
<Row radius="full" solid="brand-strong" style={{ width: `${progressPercent}%`, transition: `width 0.05s linear` }} fillHeight />
</Row>
</Row>
)}
</RevealFx>
{items.length > 1 && indicator !== false && (
<>
{indicator === "line" ? (
<Flex gap="4" paddingX="s" fillWidth horizontal="center">
<Flex gap="4" paddingX="s" marginTop="8" fillWidth horizontal="center">
{items.map((_, index) => (
<Flex
radius="full"
key={index}
onClick={() => handleControlClick(index)}
style={{
background:
activeIndex === index
? "var(--neutral-on-background-strong)"
: "var(--neutral-alpha-medium)",
transition: "background 0.3s ease",
}}
cursor="interactive"
onMouseEnter={() => setHoverIndex(index)}
onMouseLeave={() => setHoverIndex(undefined)}
cursor="pointer"
key={index}
fillWidth
height="2"
/>
height="8"
vertical="center"
>
<Flex
radius="full"
style={{
background:
activeIndex === index || hoverIndex === index
? "var(--neutral-on-background-strong)"
: "var(--neutral-alpha-medium)",
transition: "background 0.2s ease",
}}
fillWidth
height="4"
vertical="center"
/>
</Flex>
))}
</Flex>
) : (
<Scroller gap="4" onItemClick={handleControlClick}>
<Scroller gap="4" marginTop="12" onItemClick={handleControlClick}>
{items.map((item, index) => (
<Flex
key={index}
Expand Down