diff --git a/docs/api/autoplay.mdx b/docs/api/autoplay.mdx index 01286a8b..6680f1c6 100644 --- a/docs/api/autoplay.mdx +++ b/docs/api/autoplay.mdx @@ -25,7 +25,7 @@ For accessibility, the carousel will pause when the user is interacting with it. #### Code ```tsx - + diff --git a/docs/api/index.mdx b/docs/api/index.mdx index bd77cfb1..fc47765c 100644 --- a/docs/api/index.mdx +++ b/docs/api/index.mdx @@ -78,12 +78,12 @@ Feel free to mix React components and HTML elements as children. Nuka Carousel w :::caution -Nuka Carousel uses a flex container for its magic - -::: +### Nuka Carousel uses a flex container to hold its contents. In order for Nuka to measure your slides, they must have a width that can be calculated. +::: + ### Images If you're using images, Nuka will correctly calculate the width and height of the image after it has loaded. @@ -114,6 +114,12 @@ However, it's recommended to set the width and height of the image in the HTML t When using HTML block elements, such as `div`, you must set the min width in the HTML. +:::info + +Most of the examples use Tailwind classes for styling + +::: + ```jsx .demo-slide { min-width: 300px; @@ -145,5 +151,5 @@ function CarouselImage() { - +; ``` diff --git a/docs/api/initial-page.mdx b/docs/api/initial-page.mdx new file mode 100644 index 00000000..d678349c --- /dev/null +++ b/docs/api/initial-page.mdx @@ -0,0 +1,31 @@ +--- +sidebar_position: 4 +--- + +import { Carousel } from 'nuka-carousel'; + +# Initial Page + +The carousel can start on any index within bounds of its length. Anything out of bounds will default back to `0` for its index. This list is `0` indexed. + +| Prop Name | Type | Default Value | +| :------------ | :----- | :------------ | +| `initialPage` | number | `0` | + +### Example + + + + + + + +#### Code + +```tsx + + + + + +``` diff --git a/docs/v8-upgrade-guide.mdx b/docs/v8-upgrade-guide.mdx index 06d85214..dac6626c 100644 --- a/docs/v8-upgrade-guide.mdx +++ b/docs/v8-upgrade-guide.mdx @@ -45,7 +45,7 @@ The following props were removed becuase they are no longer valid or replaced by - `pauseOnHover` - Enabled by default. See the autoplay docs. - `renderTop{direction}Controls` - `scrollMode` - Defaults to `remainder`. -- `slideIndex` +- `slideIndex` - Use initialPage to start on a certain page, use goToPage to change indices on command. - `slidesToShow` - Now based on media queries and how large the slides are. - `speed` - Controlled by native browser settings. - `style` - See the style guide. diff --git a/packages/nuka/src/Carousel/Carousel.css b/packages/nuka/src/Carousel/Carousel.css index 0f8cabb6..7c0cb352 100644 --- a/packages/nuka/src/Carousel/Carousel.css +++ b/packages/nuka/src/Carousel/Carousel.css @@ -17,10 +17,15 @@ } .nuka-overflow { overflow: scroll; - scroll-behavior: smooth; -ms-overflow-style: none; /* IE and Edge */ scrollbar-width: none; /* Firefox */ } +.nuka-overflow.scroll-smooth { + scroll-behavior: smooth; +} +.nuka-overflow.scroll-auto { + scroll-behavior: auto; +} .nuka-overflow::-webkit-scrollbar { display: none; } diff --git a/packages/nuka/src/Carousel/Carousel.stories.tsx b/packages/nuka/src/Carousel/Carousel.stories.tsx index aeafa2e8..be2b5c62 100644 --- a/packages/nuka/src/Carousel/Carousel.stories.tsx +++ b/packages/nuka/src/Carousel/Carousel.stories.tsx @@ -169,6 +169,20 @@ export const GoToPage: Story = { }, }; +export const InitialPage: Story = { + args: { + initialPage: 2, + scrollDistance: 'slide', + children: ( + <> + {[...Array(10)].map((_, index) => ( + + ))} + + ), + }, +}; + export const BeforeSlide: Story = { args: { beforeSlide: (currentSlideIndex, endSlideIndex) => diff --git a/packages/nuka/src/Carousel/Carousel.tsx b/packages/nuka/src/Carousel/Carousel.tsx index db0fbb24..e78981f5 100644 --- a/packages/nuka/src/Carousel/Carousel.tsx +++ b/packages/nuka/src/Carousel/Carousel.tsx @@ -59,6 +59,7 @@ export const Carousel = forwardRef( swiping, title, wrapMode, + initialPage, } = options; const carouselRef = useRef(null); @@ -75,6 +76,7 @@ export const Carousel = forwardRef( const { currentPage, goBack, goForward, goToPage } = usePaging({ totalPages, wrapMode, + initialPage, }); // -- handle touch scroll events @@ -139,8 +141,12 @@ export const Carousel = forwardRef( containerRef.current.scrollLeft = scrollOffset[currentPage]; afterSlide && setTimeout(() => afterSlide(endSlideIndex), 0); previousPageRef.current = currentPage; + if (initialPage === undefined || currentPage === initialPage) { + containerRef.current.classList.remove('scroll-auto'); + containerRef.current.classList.add('scroll-smooth'); + } } - }, [currentPage, scrollOffset, beforeSlide, afterSlide]); + }, [currentPage, scrollOffset, beforeSlide, afterSlide, initialPage]); const containerClassName = cls( 'nuka-container', diff --git a/packages/nuka/src/hooks/use-paging.test.tsx b/packages/nuka/src/hooks/use-paging.test.tsx index e677f4bd..a8b9d9ec 100644 --- a/packages/nuka/src/hooks/use-paging.test.tsx +++ b/packages/nuka/src/hooks/use-paging.test.tsx @@ -94,4 +94,32 @@ describe('usePaging', () => { }); expect(result.current.currentPage).toBe(0); }); + + it('should start at index 0 if not given an initial page index', () => { + const { result } = renderHook(() => + usePaging({ totalPages: 5, wrapMode: 'wrap' }), + ); + expect(result.current.currentPage).toBe(0); + }); + + it('should start at the given initial page index', () => { + const { result } = renderHook(() => + usePaging({ totalPages: 5, wrapMode: 'wrap', initialPage: 2 }), + ); + expect(result.current.currentPage).toBe(2); + }); + + it('should start at in bound indices if initial page is out of bounds', () => { + const { result } = renderHook(() => + usePaging({ totalPages: 5, wrapMode: 'wrap', initialPage: 200 }), + ); + expect(result.current.currentPage).toBe(5); + }); + + it('should start at 0 indices if initial page is out of bounds', () => { + const { result } = renderHook(() => + usePaging({ totalPages: 5, wrapMode: 'wrap', initialPage: -2 }), + ); + expect(result.current.currentPage).toBe(0); + }); }); diff --git a/packages/nuka/src/hooks/use-paging.tsx b/packages/nuka/src/hooks/use-paging.tsx index facb6bf2..0f0b0fe4 100644 --- a/packages/nuka/src/hooks/use-paging.tsx +++ b/packages/nuka/src/hooks/use-paging.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { CarouselProps } from '../types'; @@ -12,14 +12,22 @@ type UsePagingReturnType = { type PagingProps = { totalPages: number; wrapMode: CarouselProps['wrapMode']; + initialPage?: number; }; export function usePaging({ totalPages, wrapMode, + initialPage, }: PagingProps): UsePagingReturnType { const [currentPage, setCurrentPage] = useState(0); + useEffect(() => { + if (initialPage) { + setCurrentPage(Math.max(0, Math.min(initialPage, totalPages))); + } + }, [initialPage, totalPages]); + const goToPage = (idx: number) => { if (idx < 0 || idx >= totalPages) return; setCurrentPage(idx); diff --git a/packages/nuka/src/types.ts b/packages/nuka/src/types.ts index 4bdc292d..135d8aac 100644 --- a/packages/nuka/src/types.ts +++ b/packages/nuka/src/types.ts @@ -25,6 +25,7 @@ export type CarouselProps = CarouselCallbacks & { swiping?: boolean; title?: string; wrapMode?: 'nowrap' | 'wrap'; + initialPage?: number; }; export type SlideHandle = {