Skip to content

Commit

Permalink
feat: add responsive support
Browse files Browse the repository at this point in the history
  • Loading branch information
pstachula-dev committed Jul 7, 2024
1 parent c58153b commit b1735e0
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 21 deletions.
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
20 changes: 12 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -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:

```
Expand All @@ -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
1 change: 0 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ function App() {
<div className="mx-auto max-w-3xl pt-10">
<CarouselProvider
autoPlayDelay={2000}
width={500}
autoPlay={false}
slidesVisible={1}
infinite
Expand Down
27 changes: 18 additions & 9 deletions src/lib/simple-headless-carousel/components/Carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { getSlideClientX } from '../services/getSliderClientX';
import type { EventMap, SlideEvent } from '../services/types';
import { manageEvents } from '../services/manageEvents';
import { clsx } from '../services/clsx';
import { useResizeObserver } from '../hooks/useResizeObserver';

const threashold = 0.25;

Expand Down Expand Up @@ -42,10 +43,11 @@ export const Carousel = memo(
moveRight: true,
});

const { total, width, slidesVisible, currentIndex, infinite } = state;
const { refWidth } = useResizeObserver(imgRef);
const { total, slidesVisible, currentIndex, infinite } = state;
const totalWidth = (100 * total) / slidesVisible;
const totalWidthPercent = `${totalWidth}%`;

const width = (refWidth || 0) / total;
const cancelWrongTarget = (event: SlideEvent) =>
event.target !== imgRef.current;

Expand All @@ -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;
Expand Down Expand Up @@ -155,9 +165,8 @@ export const Carousel = memo(

return (
<div
style={{ width }}
className={clsx(
'relative z-10 cursor-pointer overflow-hidden',
'relative z-10 w-full max-w-full cursor-pointer overflow-hidden',
wrapperClassName,
)}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 0 additions & 2 deletions src/lib/simple-headless-carousel/hooks/useCarouselReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export type DispatchOpts = {
export type CarouselState = {
currentIndex: number;
total: number;
width: number;
// optional
slidesVisible: number;
step: number;
Expand All @@ -25,7 +24,6 @@ export type CarouselReduceDispatch = Dispatch<DispatchOpts>;

export const stateDefaults: CarouselState = {
currentIndex: 0,
width: 0,
total: 0,
step: 1,
autoPlayDelay: 2000,
Expand Down
41 changes: 41 additions & 0 deletions src/lib/simple-headless-carousel/hooks/useResizeObserver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useEffect, useState, RefObject } from 'react';

interface Size {
width: number;
height: number;
}

type UseResizeObserverProps<T> = {
ref: RefObject<T>;
refWidth: number | undefined;
};

export const useResizeObserver = <T extends HTMLElement>(
ref: RefObject<T>,
): UseResizeObserverProps<T> => {
const [size, setSize] = useState<Size>();

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 };
};

0 comments on commit b1735e0

Please sign in to comment.