Skip to content

Commit

Permalink
feat: add indicators and control position as options
Browse files Browse the repository at this point in the history
  • Loading branch information
aleksandar-r committed Apr 4, 2024
1 parent 23c8c85 commit 33608b7
Show file tree
Hide file tree
Showing 4 changed files with 281 additions and 39 deletions.
153 changes: 117 additions & 36 deletions src/core/Slider.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,78 @@
import React, { useState, useEffect, useRef, ReactNode } from "react";
import Icon from "../core/Icon.tsx";
import "./component.css";
import "./Slider/component.css";

interface SliderProps {
children: ReactNode[];
interval?: number;
options?: {
interval?: number;
controlPosition?: "inline" | "floating";
intervalIndicator?: boolean;
mqEnableThreshold?: () => boolean;
};
}

interface SliderIndicatorProps {
numSlides: number;
activeIndex: number;
interval: number;
intervalIndicator?: boolean;
isInline?: boolean;
}

const SlideIndicator = ({
numSlides,
activeIndex,
interval,
intervalIndicator,
isInline,
}: SliderIndicatorProps) => {
return (
<div className="flex gap-4 absolute -bottom-40 left-1/2 transform -translate-x-1/2">
{Array.from({ length: numSlides }, (_, i) => (
<div
key={i}
className={"relative w-40 h-4 mx-1 rounded-full bg-neutral-500"}
>
{i === activeIndex && (
<div
className="absolute inset-0 rounded-full bg-active-orange animate-[fillAnimation]"
style={{
animationDuration: `${interval}ms`,
animationTimingFunction: "linear",
}}
></div>
)}
</div>
))}
</div>
<ul
className={`flex gap-4 left-1/2 ${
isInline ? "bottom-0" : "absolute -bottom-40 transform -translate-x-1/2"
}`}
>
{Array.from({ length: numSlides }, (_, i) =>
intervalIndicator ? (
<li
key={i}
className={"relative w-40 h-4 mx-1 rounded-full bg-neutral-500"}
>
{i === activeIndex && (
<li
className={`absolute inset-0 rounded-full bg-active-orange`}
style={{
animation: `fillAnimation ${interval}ms linear`,
}}
></li>
)}
</li>
) : (
<li key={i}>
<span
className={`ui-slider-marker ${
i === activeIndex ? "text-active-orange" : "text-cool-black"
}`}
data-id="slider-marker"
>
&#x2b24;
</span>
</li>
)
)}
</ul>
);
};

const Slider = ({ children, interval = 15000 }: SliderProps) => {
const Slider = ({ children, options }: SliderProps) => {
const [activeIndex, setActiveIndex] = useState(0);
const [touchStartX, setTouchStartX] = useState(0);
const [touchEndX, setTouchEndX] = useState(0);
const timerRef = useRef<NodeJS.Timeout | null>(null);

const isInline = options?.controlPosition === "inline";

const next = () => {
setActiveIndex((prevIndex) => (prevIndex + 1) % children.length);
resetInterval();
Expand All @@ -58,18 +87,40 @@ const Slider = ({ children, interval = 15000 }: SliderProps) => {

const resetInterval = () => {
if (timerRef.current) clearInterval(timerRef.current);
timerRef.current = setInterval(next, interval);
timerRef.current = setInterval(next, options?.interval ?? 10000);
};

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, interval]);
}, [children.length, options?.interval]);

return (
<div className="relative">
<div
className="relative"
onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove}
onTouchEnd={handleTouchEnd}
>
<div className="overflow-hidden w-full py-40">
<div
className="flex items-center transition-transform ease-in-out duration-500"
Expand All @@ -78,34 +129,64 @@ const Slider = ({ children, interval = 15000 }: SliderProps) => {
{children.map((child, index) => (
<div
key={index}
className="w-full flex-shrink-0 flex justify-center sm:px-60"
className="w-full flex-shrink-0 flex justify-center sm:px-60 transition-opacity ease-in delay-500 duration-500"
style={{
opacity: activeIndex === index ? 1 : 0.1,
}}
>
{child}
</div>
))}
</div>
</div>

<div className="hidden sm:absolute inset-0 sm:flex items-center justify-between pointer-events-none">
<div
className={`flex items-center pointer-events-none ${
isInline
? "ui-standard-container justify-center gap-16"
: "sm:flex sm:absolute inset-0 justify-between"
}`}
>
<button
className="rounded border border-gray-500 p-16 pointer-events-auto"
className={`${
!isInline && "hidden sm:flex"
} pointer-events-auto w-48 h-48 rounded border border-mid-grey hover:border-active-orange flex justify-center items-center ui-icon-cta ui-icon-cta-left`}
onClick={prev}
>
<Icon name="icon-gui-arrow-left" size="1.5rem" />
<div className="ui-icon-cta-holder flex">
<div className="w-full h-full flex-shrink-0 flex items-center justify-center">
<Icon name="icon-gui-arrow-left" size="1.5rem" />
</div>
<div className="w-full h-full flex-shrink-0 flex items-center justify-center">
<Icon name="icon-gui-arrow-left" size="1.5rem" />
</div>
</div>
</button>

<SlideIndicator
numSlides={children.length}
activeIndex={activeIndex}
interval={options?.interval ?? 10000}
intervalIndicator={options?.intervalIndicator}
isInline={isInline}
/>

<button
className="rounded border border-gray-500 p-16 pointer-events-auto"
className={`${
!isInline && "hidden sm:flex"
} pointer-events-auto w-48 h-48 rounded border border-mid-grey hover:border-active-orange flex justify-center items-center ui-icon-cta ui-icon-cta-right`}
onClick={next}
>
<Icon name="icon-gui-arrow-right" size="1.5rem" />
<div className="ui-icon-cta-holder flex">
<div className="w-full h-full flex-shrink-0 flex items-center justify-center">
<Icon name="icon-gui-arrow-right" size="1.5rem" />
</div>
<div className="w-full h-full flex-shrink-0 flex items-center justify-center">
<Icon name="icon-gui-arrow-right" size="1.5rem" />
</div>
</div>
</button>
</div>

<SlideIndicator
numSlides={children.length}
activeIndex={activeIndex}
interval={interval}
/>
</div>
);
};
Expand Down
30 changes: 27 additions & 3 deletions src/core/Slider/Slider.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const Slide = ({ name }: { name: string }) => (
<div className="flex flex-col sm:flex-row gap-32">
<div className="flex gap-8">
<div className="static self-center sm:absolute sm:-bottom-48 sm:-right-[56px] rounded-full bg-gradient-to-l from-neutral-200 to-50% to-neutral-500 w-[48px] h-[48px] sm:w-[201px] sm:h-[201px] md:w-[257px] md:h-[257px] lg:w-[280px] lg:h-[280px] overflow-hidden flex items-center justify-center sm:border-[16px] border-neutral-200">
{/* <GatsbyImage /> */}
<img src="https://picsum.photos/id/64/200" alt="test-image" />
</div>
<div className="sm:py-16">
<p className="ui-text-p1 text-neutral-1300">{name}</p>
Expand All @@ -26,7 +26,7 @@ const Slide = ({ name }: { name: string }) => (

<div className="w-[80px] h-1 sm:w-1 sm:h-full bg-neutral-500"></div>
<div className="flex items-center gap-4">
{/* <GatsbyImage /> */}
<img src="https://picsum.photos/id/145/40" alt="test-image" />
<p className="ui-text-h4 font-bold">Mentimeter</p>
</div>
</div>
Expand Down Expand Up @@ -58,7 +58,31 @@ export default {
component: Slider,
args: {
children: slides,
interval: 15000,
options: {
interval: 10000,
intervalIndicator: true,
controlPosition: "floating",
},
},
};

export const InlineControlPosition = {
args: {
options: {
interval: 10000,
intervalIndicator: true,
controlPosition: "inline",
},
},
};

export const WithoutIntervalIndicator = {
args: {
options: {
interval: 10000,
intervalIndicator: false,
controlPosition: "floating",
},
},
};

Expand Down
30 changes: 30 additions & 0 deletions src/core/Slider/component.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
.ui-slider-marker {
font-size: 0.5rem;
top: -1px;

@apply leading-none px-4 relative;
}

@keyframes fillAnimation {
0% {
width: 0%;
Expand All @@ -6,3 +13,26 @@
width: 100%;
}
}

.ui-icon-cta {
@apply w-48 h-48 cursor-pointer overflow-hidden;
@apply rounded border-2 border-mid-grey hover:border-active-orange;
transition: all 0.4s;
}

@screen md {
.ui-icon-cta-left:hover .ui-icon-cta-holder {
transform: translateX(-100%);
}
.ui-icon-cta-right .ui-icon-cta-holder {
transform: translateX(-100%);
}
.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;
}
Loading

0 comments on commit 33608b7

Please sign in to comment.