@ably/ui 14.0.0-dev.99c9769 → 14.0.0-dev.bc1b6fe

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/core/.DS_Store CHANGED
Binary file
package/core/Accordion.js CHANGED
@@ -1 +1 @@
1
- import React,{useState}from"react";import Icon from"./Icon";const AccordionRow=({name,children,index,setActiveIndex,active,topBorder,bottomBorder,last,arrowIcon})=>{let iconActive,iconInactive;const handleSetIndex=()=>{setActiveIndex(index)};if(arrowIcon){iconActive=React.createElement(Icon,{name:"icon-gui-disclosure-arrow",color:"text-dark-grey",size:"1.5rem",additionalCSS:"-rotate-90"});iconInactive=React.createElement(Icon,{name:"icon-gui-disclosure-arrow",color:"text-dark-grey",size:"1.5rem",additionalCSS:"rotate-90"})}else{iconActive=React.createElement(Icon,{name:"icon-gui-minus",color:"text-dark-grey",size:"1.5rem"});iconInactive=React.createElement(Icon,{name:"icon-gui-plus",color:"text-dark-grey",size:"1.5rem"})}return React.createElement("div",{className:`border-mid-grey ${last&&!bottomBorder?"":"border-b"} ${topBorder?"border-t":""}`},React.createElement("button",{type:"button",onClick:handleSetIndex,className:`flex w-full px-0 focus:outline-none py-20`},React.createElement("span",{className:"ui-text-p1 font-bold text-left mr-8"},name),React.createElement("span",{className:"ml-auto"},active?iconActive:iconInactive)),React.createElement("section",{className:"ui-text-p2 transition-all overflow-hidden",style:{maxHeight:active?"500px":"0",paddingBottom:active?"1.5rem":"0"}},children))};const Accordion=({data,id="id-accordion",topBorder,bottomBorder,arrowIcon,autoClose})=>{const[activeIndexes,setActiveIndexes]=useState([]);const handleSetIndex=index=>{const currentIndexIsActive=activeIndexes.includes(index);if(autoClose){setActiveIndexes(currentIndexIsActive?[]:[index])}else{setActiveIndexes(currentIndexIsActive?activeIndexes.filter(i=>i!==index):[...activeIndexes,index])}};return React.createElement("div",{className:"ui-grid-mx max-w-screen-sm sm:mx-auto",id:id},data.map((item,currentIndex)=>{return React.createElement(AccordionRow,{key:item.name,name:item.name,arrowIcon:arrowIcon,index:currentIndex,last:data.length===currentIndex+1,topBorder:topBorder&&currentIndex===0,bottomBorder:bottomBorder&&data.length===currentIndex+1,active:activeIndexes.includes(currentIndex),setActiveIndex:handleSetIndex},item.content)}))};export default Accordion;
1
+ import React,{useState}from"react";import Icon from"./Icon";const AccordionRow=({name,children,index,setActiveIndex,active,topBorder,bottomBorder,last,arrowIcon})=>{let iconActive,iconInactive;const handleSetIndex=()=>{setActiveIndex(index)};if(arrowIcon){iconActive=React.createElement(Icon,{name:"icon-gui-disclosure-arrow",color:"text-dark-grey",size:"1.5rem",additionalCSS:"-rotate-90"});iconInactive=React.createElement(Icon,{name:"icon-gui-disclosure-arrow",color:"text-dark-grey",size:"1.5rem",additionalCSS:"rotate-90"})}else{iconActive=React.createElement(Icon,{name:"icon-gui-minus",color:"text-dark-grey",size:"1.5rem"});iconInactive=React.createElement(Icon,{name:"icon-gui-plus",color:"text-dark-grey",size:"1.5rem"})}return React.createElement("div",{className:`border-mid-grey ${last&&!bottomBorder?"":"border-b"} ${topBorder?"border-t":""}`},React.createElement("button",{type:"button",onClick:handleSetIndex,className:`flex w-full px-0 focus:outline-none py-20`},React.createElement("span",{className:"ui-text-p1 !font-bold text-left mr-8"},name),React.createElement("span",{className:"ml-auto"},active?iconActive:iconInactive)),React.createElement("section",{className:"ui-text-p2 transition-all overflow-hidden",style:{maxHeight:active?"500px":"0",paddingBottom:active?"1.5rem":"0"}},children))};const Accordion=({data,id="id-accordion",topBorder,bottomBorder,arrowIcon,autoClose})=>{const[activeIndexes,setActiveIndexes]=useState([]);const handleSetIndex=index=>{const currentIndexIsActive=activeIndexes.includes(index);if(autoClose){setActiveIndexes(currentIndexIsActive?[]:[index])}else{setActiveIndexes(currentIndexIsActive?activeIndexes.filter(i=>i!==index):[...activeIndexes,index])}};return React.createElement("div",{className:"ui-grid-mx max-w-screen-sm sm:mx-auto",id:id},data.map((item,currentIndex)=>{return React.createElement(AccordionRow,{key:item.name,name:item.name,arrowIcon:arrowIcon,index:currentIndex,last:data.length===currentIndex+1,topBorder:topBorder&&currentIndex===0,bottomBorder:bottomBorder&&data.length===currentIndex+1,active:activeIndexes.includes(currentIndex),setActiveIndex:handleSetIndex},item.content)}))};export default Accordion;
@@ -4,3 +4,35 @@
4
4
 
5
5
  @apply leading-none px-4 relative;
6
6
  }
7
+
8
+ @keyframes fillAnimation {
9
+ 0% {
10
+ width: 0%;
11
+ }
12
+ 100% {
13
+ width: 100%;
14
+ }
15
+ }
16
+
17
+ .ui-icon-cta {
18
+ @apply cursor-pointer overflow-hidden;
19
+ @apply rounded border-2 border-mid-grey hover:border-active-orange;
20
+ transition: all 0.4s;
21
+ }
22
+
23
+ @screen sm {
24
+ .ui-icon-cta-left:hover .ui-icon-cta-holder {
25
+ transform: translateX(-120%);
26
+ }
27
+ .ui-icon-cta-right .ui-icon-cta-holder {
28
+ transform: translateX(-120%);
29
+ }
30
+ .ui-icon-cta-right:hover .ui-icon-cta-holder {
31
+ transform: translateX(0%);
32
+ }
33
+ }
34
+
35
+ .ui-icon-cta-holder {
36
+ @apply w-full h-full;
37
+ transition: all 0.4s;
38
+ }
package/core/Slider.js CHANGED
@@ -1 +1 @@
1
- import React,{useEffect,useRef}from"react";import Icon from"./Icon";import SliderScripts from"./Slider/component.js";import"./Slider/component.css";const Slider=({slides=[],classes="",slideClasses="",slideMinWidth="16.875rem",slideMaxWidth="1fr",mqEnableThreshold=()=>true,...props})=>{const containerRef=useRef(null);useEffect(()=>{SliderScripts({container:containerRef.current,mqEnableThreshold})},[]);if(slides.length===0)return;return React.createElement("div",{className:"w-full overflow-x-hidden","data-id":"slider",style:{"--dynamic-grid-columns-count":slides.length,"--dynamic-grid-column-min-width":slideMinWidth,"--dynamic-grid-column-max-width":slideMaxWidth},ref:containerRef},React.createElement("ol",{className:`grid ui-grid-gap grid-cols-dynamic transform transition-transform ${classes}`,"data-id":"slider-strip",...props},slides.map((slide,i)=>React.createElement("li",{key:i,className:slideClasses,"data-id":"slider-slide"},slide))),React.createElement("div",{className:"justify-center items-center my-24 hidden","data-id":"slider-controls"},React.createElement("button",{type:"button",className:"p-0 w-24 h-24 flex items-center focus:outline-gui-focus","data-id":"slider-previous"},React.createElement(Icon,{name:"icon-gui-disclosure-arrow",size:"1.5rem",color:"text-cool-black",additionalCSS:"transform rotate-180","data-id":"meganav-control-mobile-dropdown-menu"})),React.createElement("ul",{className:"flex justify-center items-center mx-32 relative h-24"},slides.map((_,i)=>React.createElement("li",{key:i},React.createElement("span",{className:"ui-slider-marker text-cool-black","data-id":"slider-marker"},"")," "))),React.createElement("button",{type:"button",className:"p-0 w-24 h-24 flex items-center focus:outline-gui-focus","data-id":"slider-next"},React.createElement(Icon,{name:"icon-gui-disclosure-arrow",size:"1.5rem",color:"text-cool-black","data-id":"meganav-control-mobile-dropdown-menu"}))))};export default Slider;
1
+ import React,{useState,useEffect,useRef}from"react";import Icon from"./Icon";import"./Slider/component.css";const SlideIndicator=({numSlides,activeIndex,interval,intervalIndicator,isInline})=>{return React.createElement("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?React.createElement("li",{key:i,className:"relative w-40 h-4 mx-1 rounded-full bg-neutral-500"},i===activeIndex&&React.createElement("li",{className:"absolute inset-0 rounded-full bg-active-orange",style:{animation:`fillAnimation ${interval}ms linear`}})):React.createElement("li",{key:i},React.createElement("span",{className:`ui-slider-marker ${i===activeIndex?"text-active-orange":"text-cool-black"}`,"data-id":"slider-marker"},"⬤"))))};const Slider=({children,options})=>{const[activeIndex,setActiveIndex]=useState(0);const[touchStartX,setTouchStartX]=useState(0);const[touchEndX,setTouchEndX]=useState(0);const timerRef=useRef(null);const isInline=(options===null||options===void 0?void 0: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);var _options_interval;timerRef.current=setInterval(next,(_options_interval=options===null||options===void 0?void 0:options.interval)!==null&&_options_interval!==void 0?_options_interval:1e4)};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===null||options===void 0?void 0:options.interval]);var _options_interval;return React.createElement("div",{className:"relative",onTouchStart:handleTouchStart,onTouchMove:handleTouchMove,onTouchEnd:handleTouchEnd},React.createElement("div",{className:"overflow-hidden w-full py-40"},React.createElement("div",{className:"flex items-center transition-transform ease-in-out duration-500",style:{transform:`translateX(-${activeIndex*100}%)`}},children.map((child,index)=>React.createElement("div",{key:index,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:.1}},child)))),React.createElement("div",{className:`flex items-center pointer-events-none ${isInline?"ui-standard-container justify-center gap-24":"sm:flex sm:absolute inset-0 justify-between"}`},React.createElement("button",{className:`${isInline?"w-32 h-32":"hidden sm:flex w-48 h-48"} pointer-events-auto rounded border border-mid-grey hover:border-active-orange flex justify-center items-center ui-icon-cta ui-icon-cta-left`,onClick:prev},React.createElement("div",{className:"ui-icon-cta-holder flex gap-4"},React.createElement("div",{className:"w-full h-full flex-shrink-0 flex items-center justify-center"},React.createElement(Icon,{name:"icon-gui-arrow-left",size:"1.5rem"})),React.createElement("div",{className:"w-full h-full flex-shrink-0 flex items-center justify-center"},React.createElement(Icon,{name:"icon-gui-arrow-left",size:"1.5rem"})))),React.createElement(SlideIndicator,{numSlides:children.length,activeIndex:activeIndex,interval:(_options_interval=options===null||options===void 0?void 0:options.interval)!==null&&_options_interval!==void 0?_options_interval:1e4,intervalIndicator:options===null||options===void 0?void 0:options.intervalIndicator,isInline:isInline}),React.createElement("button",{className:`${isInline?"w-32 h-32":"hidden sm:flex w-48 h-48"} pointer-events-auto rounded border border-mid-grey hover:border-active-orange justify-center items-center ui-icon-cta ui-icon-cta-right`,onClick:next},React.createElement("div",{className:"ui-icon-cta-holder flex gap-4"},React.createElement("div",{className:"w-full h-full flex-shrink-0 flex items-center justify-center"},React.createElement(Icon,{name:"icon-gui-arrow-right",size:"1.5rem"})),React.createElement("div",{className:"w-full h-full flex-shrink-0 flex items-center justify-center"},React.createElement(Icon,{name:"icon-gui-arrow-right",size:"1.5rem"}))))))};export default Slider;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ably/ui",
3
- "version": "14.0.0-dev.99c9769",
3
+ "version": "14.0.0-dev.bc1b6fe",
4
4
  "description": "Home of the Ably design system library ([design.ably.com](https://design.ably.com)). It provides a showcase, development/test environment and a publishing pipeline for different distributables.",
5
5
  "repository": {
6
6
  "type": "git",
Binary file
@@ -7,14 +7,14 @@ type AccordionData = {
7
7
  };
8
8
 
9
9
  type AccordionRowProps = {
10
- bottomBorder: boolean;
11
- topBorder: boolean;
10
+ bottomBorder?: boolean;
11
+ topBorder?: boolean;
12
12
  active: boolean;
13
13
  last: boolean;
14
14
  name: string;
15
15
  index;
16
16
  children: ReactNode;
17
- arrowIcon: boolean;
17
+ arrowIcon?: boolean;
18
18
  setActiveIndex: (index: number) => void;
19
19
  };
20
20
 
@@ -80,7 +80,7 @@ const AccordionRow = ({
80
80
  onClick={handleSetIndex}
81
81
  className={`flex w-full px-0 focus:outline-none py-20`}
82
82
  >
83
- <span className="ui-text-p1 font-bold text-left mr-8">{name}</span>
83
+ <span className="ui-text-p1 !font-bold text-left mr-8">{name}</span>
84
84
  <span className="ml-auto">{active ? iconActive : iconInactive}</span>
85
85
  </button>
86
86
 
@@ -105,7 +105,7 @@ const Accordion = ({
105
105
  arrowIcon,
106
106
  autoClose,
107
107
  }: AccordionProps) => {
108
- const [activeIndexes, setActiveIndexes] = useState([]);
108
+ const [activeIndexes, setActiveIndexes] = useState<number[]>([]);
109
109
 
110
110
  const handleSetIndex = (index: number) => {
111
111
  const currentIndexIsActive = activeIndexes.includes(index);
@@ -1,45 +1,98 @@
1
- import React, { ReactNode } from "react";
1
+ import React from "react";
2
2
  import Slider from "../Slider";
3
+ import Icon from "../Icon";
3
4
 
4
- export default {
5
- title: "Components/Slider",
6
- component: Slider,
7
- parameters: {
8
- layout: "fullscreen",
9
- },
10
- tags: ["autodocs"],
11
- };
5
+ const Slide = ({ name }: { name: string }) => (
6
+ <div className="relative ">
7
+ <div className="relative w-full sm:w-[560px] md:w-[784px] lg:w-[960px] bg-white overflow-hidden flex gap-40 rounded-3xl shadow-container-subtle">
8
+ <div className="w-full md:w-2/3 flex flex-col gap-24 pr-40 md:pr-0 pt-40 pl-40 pb-40 sm:pb-[120px] md:pb-40">
9
+ <h2 className="ui-text-h2 font-medium text-neutral-1000">
10
+ “Ably seamlessly absorbs sudden bursts in load during unexpected
11
+ client events. The integration was easy and we were live in under a
12
+ month.”
13
+ </h2>
14
+ <div className="flex flex-col sm:flex-row gap-32">
15
+ <div className="flex gap-8">
16
+ <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">
17
+ <img src="https://picsum.photos/id/64/200" alt="test-image" />
18
+ </div>
19
+ <div className="sm:py-16">
20
+ <p className="ui-text-p1 text-neutral-1300">{name}</p>
21
+ <p className="ui-text-p3 text-neutral-800">
22
+ Co-Founder & Technical Leader
23
+ </p>
24
+ </div>
25
+ </div>
12
26
 
13
- const Slide = ({ children }: { children: ReactNode }) => (
14
- <div className="h-full p-24 bg-white rounded">
15
- <p className="ui-text-p2 text-center">{children}</p>
27
+ <div className="w-[80px] h-1 sm:w-1 sm:h-full bg-neutral-500"></div>
28
+ <div className="flex items-center gap-4">
29
+ <img src="https://picsum.photos/id/145/40" alt="test-image" />
30
+ <p className="ui-text-h4 font-bold">Mentimeter</p>
31
+ </div>
32
+ </div>
33
+ <a href="/case-study" className="ui-btn self-start">
34
+ Read case study
35
+ <Icon
36
+ name="icon-gui-arrow-right"
37
+ size="1.25rem"
38
+ additionalCSS="ml-4"
39
+ />
40
+ </a>
41
+ </div>
42
+ </div>
43
+
44
+ <div className="absolute h-256 -z-10 -bottom-48 -left-36 w-1/5 rounded-full blur-xl opacity-50 transform -rotate-45 bg-gradient-to-bl from-bg-glow-green to-bg-glow-teal"></div>
45
+ <div className="absolute h-256 -z-10 -top-48 -right-48 w-3/5 rounded-full blur-xl opacity-50 transform rotate-12 bg-gradient-to-br from-bg-glow-green to-bg-glow-teal"></div>
16
46
  </div>
17
47
  );
18
48
 
19
49
  const slides = [
20
- <Slide key="1">
21
- Powers live chat, updates, analytics, and composition for millions of users.
22
- </Slide>,
23
- <Slide key="2">
24
- Powers virtual venues for millions of event attendees around the world.
25
- </Slide>,
26
- <Slide key="3">
27
- Provides 5 million daily users with live financial commentary and stock
28
- tickers.
29
- </Slide>,
30
- <Slide key="4">Monitors live car performance data across the USA.</Slide>,
50
+ <Slide key="1" name={"Johan Bengtsson"} />,
51
+ <Slide key="2" name={"Mirko Bergman"} />,
52
+ <Slide key="3" name={"Stefania Lombardo"} />,
53
+ <Slide key="4" name={"Aleksandar Kostadinov"} />,
31
54
  ];
32
55
 
33
- export const SliderOnAllBreakpoints = {
56
+ export default {
57
+ title: "Components/Slider",
58
+ component: Slider,
59
+ args: {
60
+ children: slides,
61
+ options: {
62
+ interval: 10000,
63
+ intervalIndicator: true,
64
+ controlPosition: "floating",
65
+ },
66
+ },
67
+ };
68
+
69
+ export const FloatingControlPosition = {
70
+ args: {
71
+ children: slides,
72
+ options: {
73
+ interval: 10000,
74
+ intervalIndicator: true,
75
+ controlPosition: "floating",
76
+ },
77
+ },
78
+ };
79
+
80
+ export const InlineControlPosition = {
34
81
  args: {
35
- slides,
82
+ options: {
83
+ interval: 10000,
84
+ intervalIndicator: true,
85
+ controlPosition: "inline",
86
+ },
36
87
  },
37
88
  };
38
89
 
39
- export const SliderOnSmallBreakpointOnly = {
90
+ export const WithoutIntervalIndicator = {
40
91
  args: {
41
- slides,
42
- classes: `sm:grid-cols-${slides.length / 2} md:grid-cols-${slides.length}`,
43
- mqEnableThreshold: () => !window.matchMedia("(min-width: 48rem)").matches,
92
+ options: {
93
+ interval: 10000,
94
+ intervalIndicator: false,
95
+ controlPosition: "floating",
96
+ },
44
97
  },
45
98
  };
@@ -4,3 +4,35 @@
4
4
 
5
5
  @apply leading-none px-4 relative;
6
6
  }
7
+
8
+ @keyframes fillAnimation {
9
+ 0% {
10
+ width: 0%;
11
+ }
12
+ 100% {
13
+ width: 100%;
14
+ }
15
+ }
16
+
17
+ .ui-icon-cta {
18
+ @apply cursor-pointer overflow-hidden;
19
+ @apply rounded border-2 border-mid-grey hover:border-active-orange;
20
+ transition: all 0.4s;
21
+ }
22
+
23
+ @screen sm {
24
+ .ui-icon-cta-left:hover .ui-icon-cta-holder {
25
+ transform: translateX(-120%);
26
+ }
27
+ .ui-icon-cta-right .ui-icon-cta-holder {
28
+ transform: translateX(-120%);
29
+ }
30
+ .ui-icon-cta-right:hover .ui-icon-cta-holder {
31
+ transform: translateX(0%);
32
+ }
33
+ }
34
+
35
+ .ui-icon-cta-holder {
36
+ @apply w-full h-full;
37
+ transition: all 0.4s;
38
+ }
@@ -1,108 +1,189 @@
1
- import React, { CSSProperties, ReactNode, useEffect, useRef } from "react";
2
-
1
+ import React, { useState, useEffect, useRef, ReactNode } from "react";
3
2
  import Icon from "./Icon";
4
- import SliderScripts from "./Slider/component.js";
5
3
  import "./Slider/component.css";
6
4
 
7
- type SliderProps = {
8
- slides?: ReactNode[];
9
- classes?: string;
10
- slideClasses?: string;
11
- slideMinWidth?: string;
12
- slideMaxWidth?: string;
13
- mqEnableThreshold?: () => boolean;
5
+ interface SliderProps {
6
+ children: ReactNode[];
7
+ options?: {
8
+ interval?: number;
9
+ controlPosition?: "inline" | "floating";
10
+ intervalIndicator?: boolean;
11
+ };
12
+ }
13
+
14
+ interface SliderIndicatorProps {
15
+ numSlides: number;
16
+ activeIndex: number;
17
+ interval: number;
18
+ intervalIndicator?: boolean;
19
+ isInline?: boolean;
20
+ }
14
21
 
15
- container?: HTMLDivElement | null;
22
+ const SlideIndicator = ({
23
+ numSlides,
24
+ activeIndex,
25
+ interval,
26
+ intervalIndicator,
27
+ isInline,
28
+ }: SliderIndicatorProps) => {
29
+ return (
30
+ <ul
31
+ className={`flex gap-4 left-1/2 ${
32
+ isInline ? "bottom-0" : "absolute -bottom-40 transform -translate-x-1/2"
33
+ }`}
34
+ >
35
+ {Array.from({ length: numSlides }, (_, i) =>
36
+ intervalIndicator ? (
37
+ <li
38
+ key={i}
39
+ className="relative w-40 h-4 mx-1 rounded-full bg-neutral-500"
40
+ >
41
+ {i === activeIndex && (
42
+ <li
43
+ className="absolute inset-0 rounded-full bg-active-orange"
44
+ style={{
45
+ animation: `fillAnimation ${interval}ms linear`,
46
+ }}
47
+ ></li>
48
+ )}
49
+ </li>
50
+ ) : (
51
+ <li key={i}>
52
+ <span
53
+ className={`ui-slider-marker ${
54
+ i === activeIndex ? "text-active-orange" : "text-cool-black"
55
+ }`}
56
+ data-id="slider-marker"
57
+ >
58
+ &#x2b24;
59
+ </span>
60
+ </li>
61
+ )
62
+ )}
63
+ </ul>
64
+ );
16
65
  };
17
66
 
18
- const Slider = ({
19
- slides = [],
20
- classes = "",
21
- slideClasses = "",
22
- slideMinWidth = "16.875rem",
23
- slideMaxWidth = "1fr",
24
- mqEnableThreshold = () => true,
25
- ...props
26
- }: SliderProps) => {
27
- const containerRef = useRef<HTMLDivElement>(null);
67
+ const Slider = ({ children, options }: SliderProps) => {
68
+ const [activeIndex, setActiveIndex] = useState(0);
69
+ const [touchStartX, setTouchStartX] = useState(0);
70
+ const [touchEndX, setTouchEndX] = useState(0);
71
+ const timerRef = useRef<NodeJS.Timeout | null>(null);
28
72
 
29
- useEffect(() => {
30
- SliderScripts({
31
- container: containerRef.current,
32
- mqEnableThreshold,
33
- });
34
- }, []);
73
+ const isInline = options?.controlPosition === "inline";
74
+
75
+ const next = () => {
76
+ setActiveIndex((prevIndex) => (prevIndex + 1) % children.length);
77
+ resetInterval();
78
+ };
79
+
80
+ const prev = () => {
81
+ setActiveIndex((prevIndex) =>
82
+ prevIndex > 0 ? prevIndex - 1 : children.length - 1
83
+ );
84
+ resetInterval();
85
+ };
86
+
87
+ const resetInterval = () => {
88
+ if (timerRef.current) clearInterval(timerRef.current);
89
+ timerRef.current = setInterval(next, options?.interval ?? 10000);
90
+ };
35
91
 
36
- if (slides.length === 0) return;
92
+ const handleTouchStart = (e) => {
93
+ setTouchStartX(e.touches[0].clientX);
94
+ };
95
+
96
+ const handleTouchMove = (e) => {
97
+ setTouchEndX(e.touches[0].clientX);
98
+ };
99
+
100
+ const handleTouchEnd = () => {
101
+ if (touchStartX - touchEndX > 50) {
102
+ next();
103
+ }
104
+ if (touchStartX - touchEndX < -50) {
105
+ prev();
106
+ }
107
+ };
108
+
109
+ useEffect(() => {
110
+ resetInterval();
111
+ return () => {
112
+ if (timerRef.current) clearInterval(timerRef.current);
113
+ };
114
+ }, [children.length, options?.interval]);
37
115
 
38
116
  return (
39
117
  <div
40
- className="w-full overflow-x-hidden"
41
- data-id="slider"
42
- style={
43
- {
44
- "--dynamic-grid-columns-count": slides.length,
45
- "--dynamic-grid-column-min-width": slideMinWidth,
46
- "--dynamic-grid-column-max-width": slideMaxWidth,
47
- } as CSSProperties
48
- }
49
- ref={containerRef}
118
+ className="relative"
119
+ onTouchStart={handleTouchStart}
120
+ onTouchMove={handleTouchMove}
121
+ onTouchEnd={handleTouchEnd}
50
122
  >
51
- <ol
52
- className={`grid ui-grid-gap grid-cols-dynamic transform transition-transform ${classes}`}
53
- data-id="slider-strip"
54
- {...props}
55
- >
56
- {slides.map((slide, i) => (
57
- <li key={i} className={slideClasses} data-id="slider-slide">
58
- {slide}
59
- </li>
60
- ))}
61
- </ol>
123
+ <div className="overflow-hidden w-full py-40">
124
+ <div
125
+ className="flex items-center transition-transform ease-in-out duration-500"
126
+ style={{ transform: `translateX(-${activeIndex * 100}%)` }}
127
+ >
128
+ {children.map((child, index) => (
129
+ <div
130
+ key={index}
131
+ className="w-full flex-shrink-0 flex justify-center sm:px-60 transition-opacity ease-in delay-500 duration-500"
132
+ style={{
133
+ opacity: activeIndex === index ? 1 : 0.1,
134
+ }}
135
+ >
136
+ {child}
137
+ </div>
138
+ ))}
139
+ </div>
140
+ </div>
62
141
 
63
142
  <div
64
- className="justify-center items-center my-24 hidden"
65
- data-id="slider-controls"
143
+ className={`flex items-center pointer-events-none ${
144
+ isInline
145
+ ? "ui-standard-container justify-center gap-24"
146
+ : "sm:flex sm:absolute inset-0 justify-between"
147
+ }`}
66
148
  >
67
149
  <button
68
- type="button"
69
- className="p-0 w-24 h-24 flex items-center focus:outline-gui-focus"
70
- data-id="slider-previous"
150
+ className={`${
151
+ isInline ? "w-32 h-32" : "hidden sm:flex w-48 h-48"
152
+ } pointer-events-auto rounded border border-mid-grey hover:border-active-orange flex justify-center items-center ui-icon-cta ui-icon-cta-left`}
153
+ onClick={prev}
71
154
  >
72
- <Icon
73
- name="icon-gui-disclosure-arrow"
74
- size="1.5rem"
75
- color="text-cool-black"
76
- additionalCSS="transform rotate-180"
77
- data-id="meganav-control-mobile-dropdown-menu"
78
- />
155
+ <div className="ui-icon-cta-holder flex gap-4">
156
+ <div className="w-full h-full flex-shrink-0 flex items-center justify-center">
157
+ <Icon name="icon-gui-arrow-left" size="1.5rem" />
158
+ </div>
159
+ <div className="w-full h-full flex-shrink-0 flex items-center justify-center">
160
+ <Icon name="icon-gui-arrow-left" size="1.5rem" />
161
+ </div>
162
+ </div>
79
163
  </button>
80
164
 
81
- <ul className="flex justify-center items-center mx-32 relative h-24">
82
- {slides.map((_, i) => (
83
- <li key={i}>
84
- <span
85
- className="ui-slider-marker text-cool-black"
86
- data-id="slider-marker"
87
- >
88
- &#x2b24;
89
- </span>{" "}
90
- {/* ⬤ */}
91
- </li>
92
- ))}
93
- </ul>
165
+ <SlideIndicator
166
+ numSlides={children.length}
167
+ activeIndex={activeIndex}
168
+ interval={options?.interval ?? 10000}
169
+ intervalIndicator={options?.intervalIndicator}
170
+ isInline={isInline}
171
+ />
94
172
 
95
173
  <button
96
- type="button"
97
- className="p-0 w-24 h-24 flex items-center focus:outline-gui-focus"
98
- data-id="slider-next"
174
+ className={`${
175
+ isInline ? "w-32 h-32" : "hidden sm:flex w-48 h-48"
176
+ } pointer-events-auto rounded border border-mid-grey hover:border-active-orange justify-center items-center ui-icon-cta ui-icon-cta-right`}
177
+ onClick={next}
99
178
  >
100
- <Icon
101
- name="icon-gui-disclosure-arrow"
102
- size="1.5rem"
103
- color="text-cool-black"
104
- data-id="meganav-control-mobile-dropdown-menu"
105
- />
179
+ <div className="ui-icon-cta-holder flex gap-4">
180
+ <div className="w-full h-full flex-shrink-0 flex items-center justify-center">
181
+ <Icon name="icon-gui-arrow-right" size="1.5rem" />
182
+ </div>
183
+ <div className="w-full h-full flex-shrink-0 flex items-center justify-center">
184
+ <Icon name="icon-gui-arrow-right" size="1.5rem" />
185
+ </div>
186
+ </div>
106
187
  </button>
107
188
  </div>
108
189
  </div>