From c43d3ffd88c0c4fadf945c482de26539ee5e8363 Mon Sep 17 00:00:00 2001 From: Aleksandar Ristic Date: Mon, 1 Apr 2024 15:26:56 +0200 Subject: [PATCH] feat: add new generic slider, remove old one --- src/core/Slider.tsx | 249 +++++++++++++++++++---------- src/core/Slider/Slider.stories.tsx | 113 +++++++++---- src/core/Slider/component.css | 32 ++++ 3 files changed, 280 insertions(+), 114 deletions(-) diff --git a/src/core/Slider.tsx b/src/core/Slider.tsx index 968a3f69..2a3d391f 100644 --- a/src/core/Slider.tsx +++ b/src/core/Slider.tsx @@ -1,108 +1,189 @@ -import React, { CSSProperties, ReactNode, useEffect, useRef } from "react"; - -import Icon from "./Icon"; -import SliderScripts from "./Slider/component.js"; +import React, { useState, useEffect, useRef, ReactNode } from "react"; +import Icon from "../core/Icon.tsx"; import "./Slider/component.css"; -type SliderProps = { - slides?: ReactNode[]; - classes?: string; - slideClasses?: string; - slideMinWidth?: string; - slideMaxWidth?: string; - mqEnableThreshold?: () => boolean; +interface SliderProps { + children: ReactNode[]; + options?: { + interval?: number; + controlPosition?: "inline" | "floating"; + intervalIndicator?: boolean; + }; +} + +interface SliderIndicatorProps { + numSlides: number; + activeIndex: number; + interval: number; + intervalIndicator?: boolean; + isInline?: boolean; +} - container?: HTMLDivElement | null; +const SlideIndicator = ({ + numSlides, + activeIndex, + interval, + intervalIndicator, + isInline, +}: SliderIndicatorProps) => { + return ( + + ); }; -const Slider = ({ - slides = [], - classes = "", - slideClasses = "", - slideMinWidth = "16.875rem", - slideMaxWidth = "1fr", - mqEnableThreshold = () => true, - ...props -}: SliderProps) => { - const containerRef = useRef(null); +const Slider = ({ children, options }: SliderProps) => { + const [activeIndex, setActiveIndex] = useState(0); + const [touchStartX, setTouchStartX] = useState(0); + const [touchEndX, setTouchEndX] = useState(0); + const timerRef = useRef(null); - useEffect(() => { - SliderScripts({ - container: containerRef.current, - mqEnableThreshold, - }); - }, []); + const isInline = options?.controlPosition === "inline"; + + const next = () => { + setActiveIndex((prevIndex) => (prevIndex + 1) % children.length); + resetInterval(); + }; + + const prev = () => { + setActiveIndex((prevIndex) => + prevIndex > 0 ? prevIndex - 1 : children.length - 1 + ); + resetInterval(); + }; + + const resetInterval = () => { + if (timerRef.current) clearInterval(timerRef.current); + timerRef.current = setInterval(next, options?.interval ?? 10000); + }; - if (slides.length === 0) return; + const handleTouchStart = (e) => { + setTouchStartX(e.touches[0].clientX); + }; + + const handleTouchMove = (e) => { + setTouchEndX(e.touches[0].clientX); + }; + + const handleTouchEnd = () => { + if (touchStartX - touchEndX > 50) { + next(); + } + if (touchStartX - touchEndX < -50) { + prev(); + } + }; + + useEffect(() => { + resetInterval(); + return () => { + if (timerRef.current) clearInterval(timerRef.current); + }; + }, [children.length, options?.interval]); return (
-
    - {slides.map((slide, i) => ( -
  1. - {slide} -
  2. - ))} -
+
+
+ {children.map((child, index) => ( +
+ {child} +
+ ))} +
+
-
    - {slides.map((_, i) => ( -
  • - - ⬤ - {" "} - {/* ⬤ */} -
  • - ))} -
+
diff --git a/src/core/Slider/Slider.stories.tsx b/src/core/Slider/Slider.stories.tsx index 34aeca1b..d883c442 100644 --- a/src/core/Slider/Slider.stories.tsx +++ b/src/core/Slider/Slider.stories.tsx @@ -1,45 +1,98 @@ -import React, { ReactNode } from "react"; -import Slider from "../Slider"; +import React from "react"; +import Slider from "../Slider.tsx"; +import Icon from "../Icon.tsx"; -export default { - title: "Components/Slider", - component: Slider, - parameters: { - layout: "fullscreen", - }, - tags: ["autodocs"], -}; +const Slide = ({ name }: { name: string }) => ( +
+
+
+

+ “Ably seamlessly absorbs sudden bursts in load during unexpected + client events. The integration was easy and we were live in under a + month.” +

+
+
+
+ test-image +
+
+

{name}

+

+ Co-Founder & Technical Leader +

+
+
-const Slide = ({ children }: { children: ReactNode }) => ( -
-

{children}

+
+
+ test-image +

Mentimeter

+
+
+ + Read case study + + +
+
+ +
+
); const slides = [ - - Powers live chat, updates, analytics, and composition for millions of users. - , - - Powers virtual venues for millions of event attendees around the world. - , - - Provides 5 million daily users with live financial commentary and stock - tickers. - , - Monitors live car performance data across the USA., + , + , + , + , ]; -export const SliderOnAllBreakpoints = { +export default { + title: "Components/Slider", + component: Slider, + args: { + children: slides, + options: { + interval: 10000, + intervalIndicator: true, + controlPosition: "floating", + }, + }, +}; + +export const FloatingControlPosition = { + args: { + children: slides, + options: { + interval: 10000, + intervalIndicator: true, + controlPosition: "floating", + }, + }, +}; + +export const InlineControlPosition = { args: { - slides, + options: { + interval: 10000, + intervalIndicator: true, + controlPosition: "inline", + }, }, }; -export const SliderOnSmallBreakpointOnly = { +export const WithoutIntervalIndicator = { args: { - slides, - classes: `sm:grid-cols-${slides.length / 2} md:grid-cols-${slides.length}`, - mqEnableThreshold: () => !window.matchMedia("(min-width: 48rem)").matches, + options: { + interval: 10000, + intervalIndicator: false, + controlPosition: "floating", + }, }, }; diff --git a/src/core/Slider/component.css b/src/core/Slider/component.css index 48ea9f48..a43fbab4 100644 --- a/src/core/Slider/component.css +++ b/src/core/Slider/component.css @@ -4,3 +4,35 @@ @apply leading-none px-4 relative; } + +@keyframes fillAnimation { + 0% { + width: 0%; + } + 100% { + width: 100%; + } +} + +.ui-icon-cta { + @apply cursor-pointer overflow-hidden; + @apply rounded border-2 border-mid-grey hover:border-active-orange; + transition: all 0.4s; +} + +@screen sm { + .ui-icon-cta-left:hover .ui-icon-cta-holder { + transform: translateX(-120%); + } + .ui-icon-cta-right .ui-icon-cta-holder { + transform: translateX(-120%); + } + .ui-icon-cta-right:hover .ui-icon-cta-holder { + transform: translateX(0%); + } +} + +.ui-icon-cta-holder { + @apply w-full h-full; + transition: all 0.4s; +}