diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 433d9d8..e2ff00f 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -51,6 +51,7 @@ module.exports = { 'react/jsx-props-no-spreading': 'off', 'react/require-default-props': 'off', 'react/prop-types': 'off', + 'consistent-return': 'off', '@typescript-eslint/no-unsafe-enum-comparison': 'off', 'react/function-component-definition': 'off', 'react/react-in-jsx-scope': 'off', diff --git a/README.md b/README.md index 008af69..04eebc2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,13 @@ # Simple Headless Carousel React +- Extremely small package size: 2kB gzipped +- 0 external dependencies +- Full typescript support +- Built in with Tailwind, but you can always customize the styles +- Handling touch/mouse events +- Lazy image loading (WIP) +- Responsive support (WIP) + ### How to use: ``` @@ -25,15 +33,11 @@ ### TODO: - [x] Add beta implementation -- [ ] Add extra props +- [ ] Add extra option props - [ ] Add stable version -- [ ] Setup CI -- [ ] DX improvments prettier/eslint +- [x] Setup CI +- [x] DX improvments prettier/eslint - [ ] Add docs - [ ] Add unit tests - [ ] Add examples -- [ ] Add more options - ---- - -- [ ] Publish on NPM +- [x] Publish on NPM diff --git a/src/App.tsx b/src/App.tsx index 820cef7..3235bc5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -19,7 +19,6 @@ function App() {
event.target !== imgRef.current; @@ -56,11 +58,19 @@ export const Carousel = memo( [dispatch], ); - const setTranslateX = useCallback((x: number) => { - animationRef.current = requestAnimationFrame(() => { - imgRef.current?.style.setProperty('transform', `translateX(${x}px)`); - }); - }, []); + const setTranslateX = useCallback( + (x: number) => { + animationRef.current = requestAnimationFrame(() => { + const percent = (x * 100) / width / total; + + imgRef.current?.style.setProperty( + 'transform', + `translateX(${percent}%)`, + ); + }); + }, + [width, total], + ); const onMoveStart = useCallback((event: SlideEvent) => { if (cancelWrongTarget(event)) return; @@ -155,9 +165,8 @@ export const Carousel = memo( return (
diff --git a/src/lib/simple-headless-carousel/context/CarouselProvider.tsx b/src/lib/simple-headless-carousel/context/CarouselProvider.tsx index 2cb39cb..9aa86ea 100644 --- a/src/lib/simple-headless-carousel/context/CarouselProvider.tsx +++ b/src/lib/simple-headless-carousel/context/CarouselProvider.tsx @@ -12,7 +12,6 @@ import { useMergeConfig } from '../hooks/useMergeConfig'; * * @param {ReactNode} children - The child components to be wrapped by the Carousel context. * @param {number} total - The total number of slides in the carousel. - * @param {number} width - The width of each slide in the carousel. * @param {boolean} autoPlay - Whether the carousel should automatically play. * @param {number} autoPlayDelay - The delay between each slide transition in auto play mode. * @param {number} slidesVisible - The number of slides visible at a time. diff --git a/src/lib/simple-headless-carousel/hooks/useCarouselReducer.ts b/src/lib/simple-headless-carousel/hooks/useCarouselReducer.ts index bf45e00..e622729 100644 --- a/src/lib/simple-headless-carousel/hooks/useCarouselReducer.ts +++ b/src/lib/simple-headless-carousel/hooks/useCarouselReducer.ts @@ -12,7 +12,6 @@ export type DispatchOpts = { export type CarouselState = { currentIndex: number; total: number; - width: number; // optional slidesVisible: number; step: number; @@ -25,7 +24,6 @@ export type CarouselReduceDispatch = Dispatch; export const stateDefaults: CarouselState = { currentIndex: 0, - width: 0, total: 0, step: 1, autoPlayDelay: 2000, diff --git a/src/lib/simple-headless-carousel/hooks/useResizeObserver.ts b/src/lib/simple-headless-carousel/hooks/useResizeObserver.ts new file mode 100644 index 0000000..b79aa94 --- /dev/null +++ b/src/lib/simple-headless-carousel/hooks/useResizeObserver.ts @@ -0,0 +1,41 @@ +import { useEffect, useState, RefObject } from 'react'; + +interface Size { + width: number; + height: number; +} + +type UseResizeObserverProps = { + ref: RefObject; + refWidth: number | undefined; +}; + +export const useResizeObserver = ( + ref: RefObject, +): UseResizeObserverProps => { + const [size, setSize] = useState(); + + useEffect(() => { + if (!ref.current) return; + + const handleResize = (entries: ResizeObserverEntry[]) => { + for (const entry of entries) { + if (entry.target === ref.current) { + setSize({ + width: entry.contentRect.width, + height: entry.contentRect.height, + }); + } + } + }; + + const resizeObserver = new ResizeObserver(handleResize); + resizeObserver.observe(ref.current); + + return () => { + resizeObserver.disconnect(); + }; + }, [ref]); + + return { ref, refWidth: size?.width }; +};