Browse Source

Make Carousel Scroll Limits Based On Childrens

Francesco Baccetti 4 years ago
parent
commit
80568274a8

+ 1 - 1
packages/app/src/App.tsx

@@ -11,7 +11,7 @@ export default function App() {
 			<Provider store={store}>
 				<Layout>
 					<Hero />
-					<VideoGallery title="Continue Watching" />
+					<VideoGallery title="Continue Watching" log />
 					<VideoGallery title="Top Trending Videos" />
 					<SeriesGallery title="Top Trending Series" />
 					<VideoGallery title="Featured Videos" />

+ 3 - 2
packages/app/src/components/Gallery.tsx

@@ -6,6 +6,7 @@ import { Carousel, theme } from "@joystream/components";
 type VideoGalleryProps = {
 	title?: string;
 	children?: React.ReactNode;
+	log?: boolean;
 };
 
 const sectionStyles = css`
@@ -18,11 +19,11 @@ const sectionStyles = css`
 	}
 `;
 
-export default function Gallery({ title = "", children }: VideoGalleryProps) {
+export default function Gallery({ title = "", log = false, children }: VideoGalleryProps) {
 	return (
 		<section css={sectionStyles}>
 			<h4>{title}</h4>
-			<Carousel>{children}</Carousel>
+			<Carousel log={log}>{children}</Carousel>
 		</section>
 	);
 }

+ 3 - 2
packages/app/src/components/VideoGallery.tsx

@@ -6,6 +6,7 @@ import Gallery from "./Gallery";
 
 type VideoGalleryProps = {
 	title: string;
+	log?: boolean;
 };
 
 function Video() {
@@ -26,10 +27,10 @@ const articleStyles = css`
 	margin: auto ${theme.spacing.m};
 `;
 
-export default function VideoGallery({ title = "" }: VideoGalleryProps) {
+export default function VideoGallery({ title = "", log = false }: VideoGalleryProps) {
 	const items = Array.from({ length: 15 }, (_, i) => i);
 	return (
-		<Gallery title={title}>
+		<Gallery title={title} log={log}>
 			{items.map((item) => (
 				<article css={articleStyles} key={`${title}- ${item}`}>
 					<Video />

+ 3 - 0
packages/components/.babelrc

@@ -0,0 +1,3 @@
+{
+	"plugins": ["@babel/plugin-transform-runtime"]
+}

+ 2 - 3
packages/components/src/components/Avatar/index.tsx

@@ -1,3 +1,2 @@
-import { memo } from "react"
-import Avatar from "./Avatar"
-export default memo(Avatar)
+import Avatar from "./Avatar";
+export default Avatar;

+ 1 - 2
packages/components/src/components/Button/index.ts

@@ -1,3 +1,2 @@
-import { memo } from "react";
 import Button from "./Button";
-export default memo(Button);
+export default Button;

+ 68 - 16
packages/components/src/components/Carousel/Carousel.tsx

@@ -1,36 +1,88 @@
-import React, { useState, useRef } from "react";
-import { animated, useTrail, useSpring } from "react-spring";
+import React, { useState, useRef, useEffect, useCallback } from "react";
+import { animated, useSpring } from "react-spring";
 import { useCSS, CarouselStyleProps } from "./Carousel.style";
 import NavButton from "../NavButton";
 
 type CarouselProps = {
-	children: React.ReactNode;
+	children: React.ReactNode[];
 	scrollAmount?: number;
 	maxInView?: number;
 } & CarouselStyleProps;
 
-export default function Carousel({ children, scrollAmount = 200, maxInView = 4, ...styleProps }: CarouselProps) {
+export default function Carousel({ children, scrollAmount = 200, ...styleProps }: CarouselProps) {
 	let styles = useCSS(styleProps);
-	const container = useRef<HTMLDivElement>(null);
+	const containerRef = useRef<HTMLDivElement>(null);
+	const elementsRefs = useRef<(HTMLDivElement | null)[]>([]);
+	const [distance, setDistance] = useState(0);
+	const [maxDistance, setMaxDistance] = useState(Infinity);
+	const [props, set] = useSpring(() => ({
+		transform: `translateX(${distance}px)`,
+	}));
 
-	function onScroll(direction: "right" | "left") {
-		if (container.current != null) {
-			container.current.scrollBy({
-				left: direction === "left" ? -scrollAmount : scrollAmount,
-				behavior: "smooth",
-			});
+	useEffect(() => {
+		if (containerRef.current) {
+			elementsRefs.current = elementsRefs.current.slice(0, children.length);
+			const totalChildrensLength = elementsRefs.current.reduce(
+				(accWidth, el) => (el != null ? accWidth + el.clientWidth : accWidth),
+				0
+			);
+			const longestChildrenWidth = elementsRefs.current.reduce(
+				(longest, el) => (el != null && el.clientWidth > longest ? el.clientWidth : longest),
+				0
+			);
+			const containerWidth = containerRef.current.clientWidth;
+
+			setMaxDistance(totalChildrensLength - containerWidth + longestChildrenWidth);
 		}
-	}
+	}, [children.length]);
+
+	const handleScroll = useCallback(
+		function (direction: "right" | "left") {
+			let newDist = NaN;
+			const MIN_DISTANCE = 0;
+			const MAX_DISTANCE = maxDistance;
+			switch (direction) {
+				case "left": {
+					newDist = distance + scrollAmount <= MIN_DISTANCE ? distance + scrollAmount : distance;
+					break;
+				}
+				case "right": {
+					newDist = distance - scrollAmount >= -MAX_DISTANCE ? distance - scrollAmount : distance;
+					break;
+				}
+			}
+
+			setDistance(newDist);
+			set({
+				transform: `translateX(${newDist}px)`,
+			});
+
+			return newDist;
+		},
+		[maxDistance]
+	);
+
 	return (
 		<div css={styles.wrapper}>
-			<div css={styles.container} ref={container}>
-				{children}
+			<div css={styles.container} ref={containerRef}>
+				{children.map((item, idx) => (
+					<animated.div
+						style={props}
+						key={`Carousel-${idx}`}
+						ref={(el) => {
+							elementsRefs.current[idx] = el;
+							return el;
+						}}
+					>
+						{item}
+					</animated.div>
+				))}
 			</div>
 			<div css={styles.navLeft}>
-				<NavButton type="primary" direction="left" onClick={() => onScroll("left")} />
+				<NavButton type="primary" direction="left" onClick={() => handleScroll("left")} />
 			</div>
 			<div css={styles.navRight}>
-				<NavButton type="primary" direction="right" onClick={() => onScroll("right")} />
+				<NavButton type="primary" direction="right" onClick={() => handleScroll("right")} />
 			</div>
 		</div>
 	);

+ 2 - 3
packages/components/src/components/Carousel/index.ts

@@ -1,3 +1,2 @@
-import { memo } from "react"
-import Carousel from "./Carousel"
-export default memo(Carousel)
+import Carousel from "./Carousel";
+export default Carousel;

+ 2 - 3
packages/components/src/components/ChannelPreview/index.ts

@@ -1,3 +1,2 @@
-import ChannelPreview from "./ChannelPreview"
-
-export default ChannelPreview
+import ChannelPreview from "./ChannelPreview";
+export default ChannelPreview;

+ 2 - 3
packages/components/src/components/Dropdown/index.ts

@@ -1,3 +1,2 @@
-import { memo } from "react"
-import Dropdown from "./Dropdown"
-export default memo(Dropdown)
+import Dropdown from "./Dropdown";
+export default Dropdown;

+ 2 - 3
packages/components/src/components/Grid/index.ts

@@ -1,3 +1,2 @@
-import { memo } from "react"
-import Grid from "./Grid"
-export default memo(Grid)
+import Grid from "./Grid";
+export default Grid;

+ 2 - 3
packages/components/src/components/Header/index.ts

@@ -1,3 +1,2 @@
-import { memo } from "react"
-import Header from "./Header"
-export default memo(Header)
+import Header from "./Header";
+export default Header;

+ 2 - 3
packages/components/src/components/Link/index.ts

@@ -1,3 +1,2 @@
-import { memo } from "react"
-import Link from "./Link"
-export default memo(Link)
+import Link from "./Link";
+export default Link;

+ 2 - 3
packages/components/src/components/NavButton/index.ts

@@ -1,3 +1,2 @@
-import { memo } from "react"
-import NavButton from "./NavButton"
-export default memo(NavButton)
+import NavButton from "./NavButton";
+export default NavButton;

+ 2 - 3
packages/components/src/components/RadioButton/index.ts

@@ -1,3 +1,2 @@
-import { memo } from "react"
-import RadioButton from "./RadioButton"
-export default memo(RadioButton)
+import RadioButton from "./RadioButton";
+export default RadioButton;

+ 2 - 3
packages/components/src/components/SeriesPreview/index.ts

@@ -1,3 +1,2 @@
-import SeriesPreview from "./SeriesPreview"
-
-export default SeriesPreview
+import SeriesPreview from "./SeriesPreview";
+export default SeriesPreview;

+ 2 - 3
packages/components/src/components/Tabs/index.ts

@@ -1,3 +1,2 @@
-import { memo } from "react"
-import Tabs from "./Tabs"
-export default memo(Tabs)
+import Tabs from "./Tabs";
+export default Tabs;

+ 2 - 3
packages/components/src/components/Tag/index.ts

@@ -1,3 +1,2 @@
-import { memo } from "react"
-import Tag from "./Tag"
-export default memo(Tag)
+import Tag from "./Tag";
+export default Tag;

+ 2 - 3
packages/components/src/components/TagButton/index.ts

@@ -1,3 +1,2 @@
-import { memo } from "react"
-import TagButton from "./TagButton"
-export default memo(TagButton)
+import TagButton from "./TagButton";
+export default TagButton;

+ 2 - 3
packages/components/src/components/TextField/index.ts

@@ -1,3 +1,2 @@
-import { memo } from "react"
-import TextField from "./TextField"
-export default memo(TextField)
+import TextField from "./TextField";
+export default TextField;

+ 2 - 3
packages/components/src/components/Typography/index.ts

@@ -1,3 +1,2 @@
-import { memo } from "react"
-import Typography from "./Typography"
-export default memo(Typography)
+import Typography from "./Typography";
+export default Typography;

+ 2 - 3
packages/components/src/components/VideoPlayer/index.tsx

@@ -1,3 +1,2 @@
-import { memo } from "react"
-import VideoPlayer from "./VideoPlayer"
-export default memo(VideoPlayer)
+import VideoPlayer from "./VideoPlayer";
+export default VideoPlayer;

+ 2 - 3
packages/components/src/components/VideoPreview/index.tsx

@@ -1,3 +1,2 @@
-import { memo } from "react"
-import VideoPreview from "./VideoPreview"
-export default memo(VideoPreview)
+import VideoPreview from "./VideoPreview";
+export default VideoPreview;