@times-components/ts-components 1.145.1-82bc6796129e892c1eb22c6257c5e3809c159767.3 → 1.145.1-cfea81c4084e6f91221ea00fec9fc730d5b933cb.4

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.
Files changed (156) hide show
  1. package/dist/components/carousel-component/CarouselComponent.stories.js +146 -0
  2. package/dist/components/carousel-component/CarouselItem.d.ts +3 -0
  3. package/dist/components/carousel-component/CarouselItem.js +11 -0
  4. package/dist/components/carousel-component/DefaultNavigationArrow.d.ts +8 -0
  5. package/dist/components/carousel-component/DefaultNavigationArrow.js +6 -0
  6. package/dist/components/carousel-component/DefaultPageDot.d.ts +8 -0
  7. package/dist/components/carousel-component/DefaultPageDot.js +4 -0
  8. package/dist/components/carousel-component/__tests__/CarouselComponent.test.js +163 -0
  9. package/dist/components/carousel-component/__tests__/CarouselItem.test.js +80 -0
  10. package/dist/components/carousel-component/__tests__/DefaultNavigationArrow.test.js +62 -0
  11. package/dist/components/{opta/football/opta-match-stats/matchday-live/__tests__/OptaMatchStatsMatchdayLive.test.d.ts → carousel-component/__tests__/DefaultPageDot.test.d.ts} +0 -1
  12. package/dist/components/carousel-component/__tests__/DefaultPageDot.test.js +68 -0
  13. package/dist/components/carousel-component/hooks/__tests__/useCarousel.test.d.ts +1 -0
  14. package/dist/components/carousel-component/hooks/__tests__/useCarousel.test.js +314 -0
  15. package/dist/components/carousel-component/hooks/useCarousel.d.ts +2 -0
  16. package/dist/components/carousel-component/hooks/useCarousel.js +140 -0
  17. package/dist/components/carousel-component/index.d.ts +4 -0
  18. package/dist/components/carousel-component/index.js +20 -0
  19. package/dist/components/carousel-component/styles.d.ts +30 -0
  20. package/dist/components/carousel-component/styles.js +120 -0
  21. package/dist/components/carousel-component/types.d.ts +46 -0
  22. package/dist/components/carousel-component/types.js +2 -0
  23. package/dist/components/opta/football/opta-match-stats/shared/styles.js +1 -8
  24. package/dist/components/opta/football/opta-match-stats/summary/OptaMatchStatsSummary.js +2 -2
  25. package/dist/components/opta/football/opta-match-stats/summary/OptaMatchStatsSummary.stories.js +1 -1
  26. package/dist/components/opta/football/opta-match-stats/summary/WidgetContainer.js +4 -13
  27. package/dist/components/trip-cards/SkeletonCard.d.ts +2 -0
  28. package/dist/components/trip-cards/SkeletonCard.js +21 -0
  29. package/dist/components/trip-cards/TripCard.d.ts +3 -0
  30. package/dist/components/trip-cards/TripCard.js +47 -0
  31. package/dist/components/trip-cards/TripCards.stories.d.ts +1 -0
  32. package/dist/components/trip-cards/TripCards.stories.js +40 -0
  33. package/dist/components/trip-cards/TripCardsLayout.d.ts +3 -0
  34. package/dist/components/trip-cards/TripCardsLayout.js +26 -0
  35. package/dist/components/trip-cards/__tests__/SkeletonCard.test.d.ts +1 -0
  36. package/dist/components/trip-cards/__tests__/SkeletonCard.test.js +139 -0
  37. package/dist/components/trip-cards/__tests__/TripCard.test.d.ts +1 -0
  38. package/dist/components/trip-cards/__tests__/TripCard.test.js +95 -0
  39. package/dist/components/trip-cards/__tests__/TripCardsLayout.test.d.ts +1 -0
  40. package/dist/components/trip-cards/__tests__/TripCardsLayout.test.js +277 -0
  41. package/dist/components/trip-cards/__tests__/assets.test.d.ts +1 -0
  42. package/dist/components/trip-cards/__tests__/assets.test.js +165 -0
  43. package/dist/components/trip-cards/__tests__/helpers.test.d.ts +1 -0
  44. package/dist/components/trip-cards/__tests__/helpers.test.js +135 -0
  45. package/dist/components/trip-cards/__tests__/index.test.d.ts +1 -0
  46. package/dist/components/trip-cards/__tests__/index.test.js +437 -0
  47. package/dist/components/trip-cards/__tests__/mockData.test.d.ts +1 -0
  48. package/dist/components/trip-cards/__tests__/mockData.test.js +57 -0
  49. package/dist/components/trip-cards/__tests__/skeletonStyles.test.d.ts +1 -0
  50. package/dist/components/trip-cards/__tests__/skeletonStyles.test.js +194 -0
  51. package/dist/components/trip-cards/assets/BoatIcon.d.ts +1 -0
  52. package/dist/components/trip-cards/assets/BoatIcon.js +4 -0
  53. package/dist/components/trip-cards/assets/CalendarIcon.d.ts +1 -0
  54. package/dist/components/trip-cards/assets/CalendarIcon.js +4 -0
  55. package/dist/components/trip-cards/assets/ChevronRightIcon.d.ts +1 -0
  56. package/dist/components/trip-cards/assets/ChevronRightIcon.js +4 -0
  57. package/dist/components/trip-cards/assets/LocationIcon.d.ts +1 -0
  58. package/dist/components/trip-cards/assets/LocationIcon.js +4 -0
  59. package/dist/components/trip-cards/assets/MoonIcon.d.ts +1 -0
  60. package/dist/components/trip-cards/assets/MoonIcon.js +4 -0
  61. package/dist/components/trip-cards/assets/index.d.ts +6 -0
  62. package/dist/components/trip-cards/assets/index.js +7 -0
  63. package/dist/components/trip-cards/helpers.d.ts +4 -0
  64. package/dist/components/trip-cards/helpers.js +74 -0
  65. package/dist/components/trip-cards/index.d.ts +4 -0
  66. package/dist/components/trip-cards/index.js +70 -0
  67. package/dist/components/trip-cards/mockData.d.ts +3 -0
  68. package/dist/components/trip-cards/mockData.js +323 -0
  69. package/dist/components/trip-cards/skeletonStyles.d.ts +9 -0
  70. package/dist/components/trip-cards/skeletonStyles.js +37 -0
  71. package/dist/components/trip-cards/styles.d.ts +39 -0
  72. package/dist/components/trip-cards/styles.js +387 -0
  73. package/dist/components/trip-cards/types.d.ts +87 -0
  74. package/dist/components/trip-cards/types.js +2 -0
  75. package/dist/index.d.ts +1 -1
  76. package/dist/index.js +2 -2
  77. package/package.json +3 -3
  78. package/rnw.js +1 -1
  79. package/src/components/carousel-component/CarouselComponent.stories.tsx +220 -0
  80. package/src/components/carousel-component/CarouselItem.tsx +20 -0
  81. package/src/components/carousel-component/DefaultNavigationArrow.tsx +37 -0
  82. package/src/components/carousel-component/DefaultPageDot.tsx +20 -0
  83. package/src/components/carousel-component/__tests__/CarouselComponent.test.tsx +259 -0
  84. package/src/components/carousel-component/__tests__/CarouselItem.test.tsx +140 -0
  85. package/src/components/carousel-component/__tests__/DefaultNavigationArrow.test.tsx +153 -0
  86. package/src/components/carousel-component/__tests__/DefaultPageDot.test.tsx +105 -0
  87. package/src/components/carousel-component/hooks/__tests__/useCarousel.test.ts +438 -0
  88. package/src/components/carousel-component/hooks/useCarousel.ts +187 -0
  89. package/src/components/carousel-component/index.tsx +88 -0
  90. package/src/components/carousel-component/styles.ts +140 -0
  91. package/src/components/carousel-component/types.ts +51 -0
  92. package/src/components/opta/football/opta-match-stats/commentary/__tests__/__snapshots__/OptaMatchStatsCommentary.test.tsx.snap +1 -1
  93. package/src/components/opta/football/opta-match-stats/shared/styles.ts +0 -8
  94. package/src/components/opta/football/opta-match-stats/stats-graphs/__tests__/__snapshots__/OptaMatchStatsGraphs.test.tsx.snap +1 -1
  95. package/src/components/opta/football/opta-match-stats/summary/OptaMatchStatsSummary.stories.tsx +1 -1
  96. package/src/components/opta/football/opta-match-stats/summary/OptaMatchStatsSummary.tsx +1 -1
  97. package/src/components/opta/football/opta-match-stats/summary/WidgetContainer.tsx +3 -12
  98. package/src/components/opta/football/opta-match-stats/summary/__tests__/__snapshots__/OptaMatchStatsSummary.test.tsx.snap +1 -1
  99. package/src/components/trip-cards/SkeletonCard.tsx +54 -0
  100. package/src/components/trip-cards/TripCard.tsx +135 -0
  101. package/src/components/trip-cards/TripCards.stories.tsx +67 -0
  102. package/src/components/trip-cards/TripCardsLayout.tsx +75 -0
  103. package/src/components/trip-cards/__tests__/SkeletonCard.test.tsx +169 -0
  104. package/src/components/trip-cards/__tests__/TripCard.test.tsx +120 -0
  105. package/src/components/trip-cards/__tests__/TripCardsLayout.test.tsx +532 -0
  106. package/src/components/trip-cards/__tests__/assets.test.tsx +206 -0
  107. package/src/components/trip-cards/__tests__/helpers.test.ts +165 -0
  108. package/src/components/trip-cards/__tests__/index.test.tsx +499 -0
  109. package/src/components/trip-cards/__tests__/mockData.test.ts +67 -0
  110. package/src/components/trip-cards/__tests__/skeletonStyles.test.tsx +256 -0
  111. package/src/components/trip-cards/assets/BoatIcon.tsx +17 -0
  112. package/src/components/trip-cards/assets/CalendarIcon.tsx +17 -0
  113. package/src/components/trip-cards/assets/ChevronRightIcon.tsx +20 -0
  114. package/src/components/trip-cards/assets/LocationIcon.tsx +17 -0
  115. package/src/components/trip-cards/assets/MoonIcon.tsx +17 -0
  116. package/src/components/trip-cards/assets/index.ts +7 -0
  117. package/src/components/trip-cards/helpers.ts +99 -0
  118. package/src/components/trip-cards/index.tsx +104 -0
  119. package/src/components/trip-cards/mockData.ts +351 -0
  120. package/src/components/trip-cards/skeletonStyles.ts +46 -0
  121. package/src/components/trip-cards/styles.ts +426 -0
  122. package/src/components/trip-cards/types.ts +91 -0
  123. package/src/index.ts +2 -3
  124. package/dist/components/opta/football/opta-match-stats/matchday-live/DesktopWidget.d.ts +0 -10
  125. package/dist/components/opta/football/opta-match-stats/matchday-live/DesktopWidget.js +0 -69
  126. package/dist/components/opta/football/opta-match-stats/matchday-live/MobileWidget.d.ts +0 -12
  127. package/dist/components/opta/football/opta-match-stats/matchday-live/MobileWidget.js +0 -90
  128. package/dist/components/opta/football/opta-match-stats/matchday-live/OptaMatchStatsMatchdayLive.d.ts +0 -12
  129. package/dist/components/opta/football/opta-match-stats/matchday-live/OptaMatchStatsMatchdayLive.js +0 -10
  130. package/dist/components/opta/football/opta-match-stats/matchday-live/OptaMatchStatsMatchdayLive.stories.js +0 -24
  131. package/dist/components/opta/football/opta-match-stats/matchday-live/__tests__/MobileWidget.test.js +0 -57
  132. package/dist/components/opta/football/opta-match-stats/matchday-live/__tests__/OptaMatchStatsMatchdayLive.test.js +0 -48
  133. package/dist/components/opta/football/opta-match-stats/matchday-live/styles/MatchdayLiveController.d.ts +0 -1
  134. package/dist/components/opta/football/opta-match-stats/matchday-live/styles/MatchdayLiveController.js +0 -19
  135. package/dist/components/opta/football/opta-match-stats/matchday-live/styles/NavigationWrapper.d.ts +0 -12
  136. package/dist/components/opta/football/opta-match-stats/matchday-live/styles/NavigationWrapper.js +0 -67
  137. package/dist/components/opta/football/opta-match-stats/matchday-live/styles/WidgetContainer.d.ts +0 -6
  138. package/dist/components/opta/football/opta-match-stats/matchday-live/styles/WidgetContainer.js +0 -730
  139. package/dist/components/opta/football/opta-match-stats/matchday-live/styles/__tests__/NavigationWrapper.test.js +0 -33
  140. package/dist/components/opta/football/opta-match-stats/matchday-live/styles/__tests__/WidgetContainer.test.js +0 -36
  141. package/src/components/opta/football/opta-match-stats/matchday-live/DesktopWidget.tsx +0 -108
  142. package/src/components/opta/football/opta-match-stats/matchday-live/MobileWidget.tsx +0 -158
  143. package/src/components/opta/football/opta-match-stats/matchday-live/OptaMatchStatsMatchdayLive.stories.tsx +0 -38
  144. package/src/components/opta/football/opta-match-stats/matchday-live/OptaMatchStatsMatchdayLive.tsx +0 -23
  145. package/src/components/opta/football/opta-match-stats/matchday-live/__tests__/MobileWidget.test.tsx +0 -69
  146. package/src/components/opta/football/opta-match-stats/matchday-live/__tests__/OptaMatchStatsMatchdayLive.test.tsx +0 -61
  147. package/src/components/opta/football/opta-match-stats/matchday-live/__tests__/__snapshots__/OptaMatchStatsMatchdayLive.test.tsx.snap +0 -61
  148. package/src/components/opta/football/opta-match-stats/matchday-live/styles/MatchdayLiveController.tsx +0 -19
  149. package/src/components/opta/football/opta-match-stats/matchday-live/styles/NavigationWrapper.tsx +0 -81
  150. package/src/components/opta/football/opta-match-stats/matchday-live/styles/WidgetContainer.tsx +0 -761
  151. package/src/components/opta/football/opta-match-stats/matchday-live/styles/__tests__/NavigationWrapper.test.tsx +0 -67
  152. package/src/components/opta/football/opta-match-stats/matchday-live/styles/__tests__/WidgetContainer.test.tsx +0 -64
  153. /package/dist/components/{opta/football/opta-match-stats/matchday-live/OptaMatchStatsMatchdayLive.stories.d.ts → carousel-component/CarouselComponent.stories.d.ts} +0 -0
  154. /package/dist/components/{opta/football/opta-match-stats/matchday-live/__tests__/MobileWidget.test.d.ts → carousel-component/__tests__/CarouselComponent.test.d.ts} +0 -0
  155. /package/dist/components/{opta/football/opta-match-stats/matchday-live/styles/__tests__/NavigationWrapper.test.d.ts → carousel-component/__tests__/CarouselItem.test.d.ts} +0 -0
  156. /package/dist/components/{opta/football/opta-match-stats/matchday-live/styles/__tests__/WidgetContainer.test.d.ts → carousel-component/__tests__/DefaultNavigationArrow.test.d.ts} +0 -0
@@ -0,0 +1,187 @@
1
+ import { useState, useEffect, useRef, useCallback } from 'react';
2
+ import { UseCarouselOptions, UseCarouselReturn } from '../types';
3
+
4
+ export const useCarousel = (
5
+ totalItems: number,
6
+ options: UseCarouselOptions = {}
7
+ ): UseCarouselReturn => {
8
+ const { itemsPerPage = 2, onPageChange } = options;
9
+
10
+ const [currentPage, setCurrentPage] = useState(0);
11
+ const carouselRef = useRef<HTMLDivElement>(null);
12
+ const isScrollingRef = useRef(false);
13
+ const isDraggingRef = useRef(false);
14
+ const startXRef = useRef(0);
15
+
16
+ const totalPages = Math.ceil(totalItems / itemsPerPage);
17
+
18
+ const scrollToPage = useCallback(
19
+ (page: number) => {
20
+ if (carouselRef.current) {
21
+ const cardElements = carouselRef.current.children;
22
+
23
+ if (cardElements.length > 0) {
24
+ isScrollingRef.current = true;
25
+
26
+ const container = carouselRef.current;
27
+ const targetCardIndex = page * itemsPerPage;
28
+ const targetCard = cardElements[targetCardIndex] as HTMLElement;
29
+
30
+ if (targetCard) {
31
+ const targetScroll = targetCard.offsetLeft;
32
+
33
+ container.scrollTo({
34
+ left: targetScroll,
35
+ behavior: 'smooth'
36
+ });
37
+ setCurrentPage(page);
38
+
39
+ if (onPageChange) {
40
+ onPageChange(page);
41
+ }
42
+
43
+ setTimeout(() => {
44
+ isScrollingRef.current = false;
45
+ }, 800);
46
+ }
47
+ }
48
+ }
49
+ },
50
+ [itemsPerPage, onPageChange]
51
+ );
52
+
53
+ const handlePrevious = useCallback(
54
+ () => {
55
+ const newPage = Math.max(0, currentPage - 1);
56
+ scrollToPage(newPage);
57
+ },
58
+ [currentPage, scrollToPage]
59
+ );
60
+
61
+ const handleNext = useCallback(
62
+ () => {
63
+ const newPage = Math.min(totalPages - 1, currentPage + 1);
64
+ scrollToPage(newPage);
65
+ },
66
+ [currentPage, totalPages, scrollToPage]
67
+ );
68
+
69
+ const handleMouseDown = useCallback((e: React.MouseEvent) => {
70
+ if (!carouselRef.current) {
71
+ return;
72
+ }
73
+ e.preventDefault();
74
+ isDraggingRef.current = true;
75
+ startXRef.current = e.pageX;
76
+ carouselRef.current.style.cursor = 'grabbing';
77
+ }, []);
78
+
79
+ const handleMouseMove = useCallback((e: React.MouseEvent) => {
80
+ if (!isDraggingRef.current) {
81
+ return;
82
+ }
83
+ e.preventDefault();
84
+ }, []);
85
+
86
+ const handleMouseUp = useCallback(
87
+ (e: React.MouseEvent) => {
88
+ if (!isDraggingRef.current) {
89
+ return;
90
+ }
91
+ isDraggingRef.current = false;
92
+
93
+ if (carouselRef.current) {
94
+ carouselRef.current.style.cursor = 'grab';
95
+
96
+ const endX = e.pageX;
97
+ const diff = startXRef.current - endX;
98
+
99
+ if (Math.abs(diff) > 30) {
100
+ if (diff > 0) {
101
+ handleNext();
102
+ } else {
103
+ handlePrevious();
104
+ }
105
+ }
106
+ }
107
+ },
108
+ [handleNext, handlePrevious]
109
+ );
110
+
111
+ const handleMouseLeave = useCallback(() => {
112
+ if (!isDraggingRef.current) {
113
+ return;
114
+ }
115
+ isDraggingRef.current = false;
116
+ if (carouselRef.current) {
117
+ carouselRef.current.style.cursor = 'grab';
118
+ }
119
+ }, []);
120
+
121
+ const handleScroll = useCallback(
122
+ () => {
123
+ if (isScrollingRef.current) {
124
+ return;
125
+ }
126
+
127
+ if (carouselRef.current) {
128
+ const container = carouselRef.current;
129
+ const children = container.children;
130
+ if (children.length === 0) {
131
+ return;
132
+ }
133
+
134
+ const scrollLeft = container.scrollLeft;
135
+
136
+ let closestPage = 0;
137
+ let closestDistance = Infinity;
138
+
139
+ for (let page = 0; page < totalPages; page++) {
140
+ const cardIndex = page * itemsPerPage;
141
+ if (cardIndex < children.length) {
142
+ const card = children[cardIndex] as HTMLElement;
143
+ const distance = Math.abs(card.offsetLeft - scrollLeft);
144
+ if (distance < closestDistance) {
145
+ closestDistance = distance;
146
+ closestPage = page;
147
+ }
148
+ }
149
+ }
150
+
151
+ if (closestPage !== currentPage) {
152
+ setCurrentPage(closestPage);
153
+ if (onPageChange) {
154
+ onPageChange(closestPage);
155
+ }
156
+ }
157
+ }
158
+ },
159
+ [currentPage, totalPages, itemsPerPage, onPageChange]
160
+ );
161
+
162
+ useEffect(
163
+ () => {
164
+ const carousel = carouselRef.current;
165
+ if (carousel) {
166
+ carousel.addEventListener('scroll', handleScroll);
167
+ return () => carousel.removeEventListener('scroll', handleScroll);
168
+ }
169
+ return undefined;
170
+ },
171
+ [handleScroll]
172
+ );
173
+
174
+ return {
175
+ currentPage,
176
+ totalPages,
177
+ carouselRef,
178
+ isScrolling: isScrollingRef.current,
179
+ handlePrevious,
180
+ handleNext,
181
+ scrollToPage,
182
+ handleMouseDown,
183
+ handleMouseMove,
184
+ handleMouseUp,
185
+ handleMouseLeave
186
+ };
187
+ };
@@ -0,0 +1,88 @@
1
+ import React from 'react';
2
+ import { useCarousel } from './hooks/useCarousel';
3
+ import {
4
+ CarouselContainer,
5
+ CarouselWrapper,
6
+ CarouselContent,
7
+ CardsRow,
8
+ PageControl
9
+ } from './styles';
10
+ import { DefaultNavigationArrow } from './DefaultNavigationArrow';
11
+ import { DefaultPageDot } from './DefaultPageDot';
12
+ import { CarouselComponentProps } from './types';
13
+
14
+ export const CarouselComponent: React.FC<CarouselComponentProps> = ({
15
+ items,
16
+ options,
17
+ showArrows = true,
18
+ showDots = true,
19
+ arrowComponent: CustomArrow,
20
+ dotComponent: CustomDot,
21
+ className
22
+ }) => {
23
+ const {
24
+ currentPage,
25
+ totalPages,
26
+ carouselRef,
27
+ handlePrevious,
28
+ handleNext,
29
+ scrollToPage,
30
+ handleMouseDown,
31
+ handleMouseMove,
32
+ handleMouseUp,
33
+ handleMouseLeave
34
+ } = useCarousel(items.length, options);
35
+
36
+ const ArrowComponent = CustomArrow || DefaultNavigationArrow;
37
+ const DotComponent = CustomDot || DefaultPageDot;
38
+
39
+ return (
40
+ <CarouselContainer className={className}>
41
+ <CarouselWrapper>
42
+ {showArrows && (
43
+ <ArrowComponent
44
+ direction="left"
45
+ onClick={handlePrevious}
46
+ disabled={currentPage === 0}
47
+ />
48
+ )}
49
+
50
+ <CarouselContent>
51
+ <CardsRow
52
+ ref={carouselRef}
53
+ onMouseDown={handleMouseDown}
54
+ onMouseMove={handleMouseMove}
55
+ onMouseUp={handleMouseUp}
56
+ onMouseLeave={handleMouseLeave}
57
+ >
58
+ {items}
59
+ </CardsRow>
60
+ </CarouselContent>
61
+
62
+ {showArrows && (
63
+ <ArrowComponent
64
+ direction="right"
65
+ onClick={handleNext}
66
+ disabled={currentPage >= totalPages - 1}
67
+ />
68
+ )}
69
+ </CarouselWrapper>
70
+
71
+ {showDots &&
72
+ totalPages > 1 && (
73
+ <PageControl>
74
+ {Array.from({ length: totalPages }).map((_, pageIndex) => (
75
+ <DotComponent
76
+ key={pageIndex}
77
+ active={pageIndex === currentPage}
78
+ onClick={() => scrollToPage(pageIndex)}
79
+ index={pageIndex}
80
+ />
81
+ ))}
82
+ </PageControl>
83
+ )}
84
+ </CarouselContainer>
85
+ );
86
+ };
87
+
88
+ export { CarouselItem } from './CarouselItem';
@@ -0,0 +1,140 @@
1
+ import styled from 'styled-components';
2
+
3
+ export const breakpoints = {
4
+ sm: '390px',
5
+ md: '768px',
6
+ lg: '1024px',
7
+ xl: '1320px'
8
+ };
9
+
10
+ export const colors = {
11
+ primary: '#005C8A',
12
+ white: '#FFFFFF'
13
+ };
14
+
15
+ export const ItemContainer = styled.div<{
16
+ widthConfig: {
17
+ mobile?: string;
18
+ tablet?: string;
19
+ desktop?: string;
20
+ xl?: string;
21
+ };
22
+ }>`
23
+ flex: 0 0 ${({ widthConfig }) => widthConfig.mobile};
24
+ scroll-snap-align: start;
25
+ border-radius: 8px;
26
+ transition: filter 0.3s ease;
27
+
28
+ @media (min-width: ${breakpoints.md}) {
29
+ flex: 0 0 ${({ widthConfig }) => widthConfig.tablet};
30
+ }
31
+
32
+ @media (min-width: ${breakpoints.lg}) {
33
+ flex: 0 0 ${({ widthConfig }) => widthConfig.desktop};
34
+ }
35
+
36
+ @media (min-width: ${breakpoints.xl}) {
37
+ flex: 0 0 ${({ widthConfig }) => widthConfig.xl};
38
+ }
39
+ `;
40
+
41
+ export const CarouselContainer = styled.div`
42
+ width: 100%;
43
+ `;
44
+
45
+ export const CarouselWrapper = styled.div`
46
+ position: relative;
47
+ width: 100%;
48
+ `;
49
+
50
+ export const CarouselContent = styled.div`
51
+ overflow: hidden;
52
+ `;
53
+
54
+ export const CardsRow = styled.div`
55
+ display: flex;
56
+ gap: 20px;
57
+ overflow-x: auto;
58
+ scroll-snap-type: x mandatory;
59
+ scrollbar-width: none;
60
+ -ms-overflow-style: none;
61
+ padding: 0;
62
+ scroll-behavior: smooth;
63
+ cursor: grab;
64
+ user-select: none;
65
+
66
+ &::-webkit-scrollbar {
67
+ display: none;
68
+ }
69
+
70
+ &:active {
71
+ cursor: grabbing;
72
+ }
73
+
74
+ @media (max-width: ${breakpoints.md}) {
75
+ padding-left: 10px;
76
+ }
77
+ `;
78
+
79
+ export const PageControl = styled.div`
80
+ display: flex;
81
+ justify-content: center;
82
+ align-items: center;
83
+ gap: 10px;
84
+ padding: 12px;
85
+ background: ${colors.white};
86
+ `;
87
+
88
+ export const NavigationArrowButton = styled.button<{
89
+ direction: 'left' | 'right';
90
+ disabled?: boolean;
91
+ }>`
92
+ display: none;
93
+ position: absolute;
94
+ top: 50%;
95
+ transform: translateY(-50%);
96
+ ${({ direction }) =>
97
+ direction === 'left' ? 'left: -60px;' : 'right: -60px;'} z-index: 10;
98
+ width: 48px;
99
+ height: 48px;
100
+ border: none;
101
+ border-radius: 50%;
102
+ background: ${({ disabled }) => (disabled ? '#CCCCCC' : colors.primary)};
103
+ cursor: ${({ disabled }) => (disabled ? 'default' : 'pointer')};
104
+ align-items: center;
105
+ justify-content: center;
106
+ transition: background 0.2s ease;
107
+ pointer-events: ${({ disabled }) => (disabled ? 'none' : 'auto')};
108
+
109
+ svg {
110
+ width: 24px;
111
+ height: 24px;
112
+ }
113
+
114
+ svg path {
115
+ stroke: ${colors.white};
116
+ }
117
+
118
+ &:hover:not(:disabled) {
119
+ background: #004a6e;
120
+ }
121
+
122
+ @media (min-width: ${breakpoints.lg}) {
123
+ display: flex;
124
+ }
125
+ `;
126
+
127
+ export const PageDotButton = styled.button<{ active: boolean }>`
128
+ width: 8px;
129
+ height: 8px;
130
+ border-radius: 1000px;
131
+ border: none;
132
+ padding: 0;
133
+ background: ${({ active }) => (active ? '#01000D' : '#AAAAAA')};
134
+ cursor: pointer;
135
+ transition: background 0.2s ease;
136
+
137
+ &:hover {
138
+ background: #01000d;
139
+ }
140
+ `;
@@ -0,0 +1,51 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ export interface CarouselComponentProps {
4
+ items: ReactNode[];
5
+ options?: UseCarouselOptions;
6
+ showArrows?: boolean;
7
+ showDots?: boolean;
8
+ arrowComponent?: React.ComponentType<{
9
+ direction: 'left' | 'right';
10
+ onClick: () => void;
11
+ disabled: boolean;
12
+ }>;
13
+ dotComponent?: React.ComponentType<{
14
+ active: boolean;
15
+ onClick: () => void;
16
+ index: number;
17
+ }>;
18
+ className?: string;
19
+ }
20
+
21
+ export interface CarouselItemProps {
22
+ children: ReactNode;
23
+ widthConfig?: {
24
+ xs?: string;
25
+ mobile?: string;
26
+ tablet?: string;
27
+ desktop?: string;
28
+ xl?: string;
29
+ };
30
+ className?: string;
31
+ }
32
+
33
+ // Carousel Hook Types
34
+ export interface UseCarouselOptions {
35
+ itemsPerPage?: number;
36
+ onPageChange?: (page: number) => void;
37
+ }
38
+
39
+ export interface UseCarouselReturn {
40
+ currentPage: number;
41
+ totalPages: number;
42
+ carouselRef: React.RefObject<HTMLDivElement>;
43
+ isScrolling: boolean;
44
+ handlePrevious: () => void;
45
+ handleNext: () => void;
46
+ scrollToPage: (page: number) => void;
47
+ handleMouseDown: (e: React.MouseEvent) => void;
48
+ handleMouseMove: (e: React.MouseEvent) => void;
49
+ handleMouseUp: (e: React.MouseEvent) => void;
50
+ handleMouseLeave: () => void;
51
+ }
@@ -3,7 +3,7 @@
3
3
  exports[`OptaMatchStatsCommentary renders and initialises widget 1`] = `
4
4
  <DocumentFragment>
5
5
  <div
6
- class="sc-bxivhb eKHCQT"
6
+ class="sc-bxivhb hlADP"
7
7
  >
8
8
  <h3>
9
9
  Commentary
@@ -54,13 +54,5 @@ export const WidgetWrapper = styled.div<{
54
54
  @media (max-width: ${maxMDBreakpoint}px) {
55
55
  display: none;
56
56
  }
57
-
58
- ${props =>
59
- props.isApp &&
60
- `
61
- @media(prefers-color-scheme: dark) {
62
- color: ${darkTextColor} !important;
63
- }
64
- `}
65
57
  }
66
58
  `;
@@ -3,7 +3,7 @@
3
3
  exports[`OptaMatchStatsSummary should render correctly 1`] = `
4
4
  <DocumentFragment>
5
5
  <div
6
- class="sc-bxivhb dwwqvg"
6
+ class="sc-bxivhb JyPwY"
7
7
  >
8
8
  <h3>
9
9
  Match Stats
@@ -26,7 +26,7 @@ const showcase = {
26
26
  <OptaMatchStatsSummary
27
27
  season="2025"
28
28
  competition="8"
29
- match="2562044"
29
+ match="2561901"
30
30
  isApp
31
31
  />
32
32
  </>
@@ -72,7 +72,7 @@ export const OptaMatchStatsSummary: React.FC<{
72
72
  show_competition_name: true,
73
73
  competition_naming: 'full',
74
74
  team_naming: 'full',
75
- player_naming: 'last_name',
75
+ player_naming: 'initial',
76
76
  show_live: false,
77
77
  show_logo: false,
78
78
  show_title: false
@@ -316,23 +316,14 @@ export const WidgetContainer = styled.div<{
316
316
  display: flex;
317
317
  text-align: right;
318
318
  }
319
-
320
- &.Opta-Home {
321
- .Opta-Event-Player {
322
- text-align: left;
323
- }
324
- }
325
-
326
- &.Opta-Away {
327
- .Opta-Event-Player {
328
- text-align: right;
329
- }
330
- }
331
319
  }
332
320
 
333
321
  .Opta-Event-Text {
334
322
  display: flex;
335
323
  align-items: center;
324
+ .Opta-Event-Player {
325
+ text-align: left;
326
+ }
336
327
 
337
328
  &.Opta-Home {
338
329
  li {
@@ -3,7 +3,7 @@
3
3
  exports[`OptaMatchStatsSummary should render correctly 1`] = `
4
4
  <DocumentFragment>
5
5
  <div
6
- class="sc-ifAKCX hkuAFt"
6
+ class="sc-ifAKCX eIVfcL"
7
7
  />
8
8
  <div
9
9
  class="sc-bwzfXH eSqyJ"
@@ -0,0 +1,54 @@
1
+ import React, { FC } from 'react';
2
+ import { Placeholder } from '@times-components/image';
3
+ import {
4
+ CardContainer,
5
+ ImageContainer,
6
+ CardContent,
7
+ TopContainer,
8
+ BottomContainer,
9
+ DataPointsList,
10
+ DataPoint,
11
+ PriceSection,
12
+ PriceContainer
13
+ } from './styles';
14
+ import {
15
+ SkeletonLine,
16
+ SkeletonHeadline,
17
+ SkeletonPrice,
18
+ SkeletonButton
19
+ } from './skeletonStyles';
20
+
21
+ export const SkeletonCard: FC = () => {
22
+ return (
23
+ <CardContainer data-testid="skeleton-card-container">
24
+ <ImageContainer data-testid="skeleton-image-container">
25
+ <Placeholder />
26
+ </ImageContainer>
27
+ <CardContent data-testid="skeleton-card-content">
28
+ <TopContainer data-testid="skeleton-top-container">
29
+ <SkeletonHeadline data-testid="skeleton-headline" />
30
+ <DataPointsList data-testid="skeleton-data-points-list">
31
+ {[1, 2, 3, 4].map(i => (
32
+ <DataPoint key={i} data-testid={`skeleton-data-point-${i}`}>
33
+ <SkeletonLine width="80px" data-testid={`skeleton-line-${i}`} />
34
+ </DataPoint>
35
+ ))}
36
+ </DataPointsList>
37
+ </TopContainer>
38
+ <BottomContainer data-testid="skeleton-bottom-container">
39
+ <PriceSection data-testid="skeleton-price-section">
40
+ <PriceContainer data-testid="skeleton-price-container">
41
+ <SkeletonLine
42
+ width="40px"
43
+ marginBottom="4px"
44
+ data-testid="skeleton-price-label"
45
+ />
46
+ <SkeletonPrice data-testid="skeleton-price" />
47
+ </PriceContainer>
48
+ </PriceSection>
49
+ <SkeletonButton as="div" data-testid="skeleton-button" />
50
+ </BottomContainer>
51
+ </CardContent>
52
+ </CardContainer>
53
+ );
54
+ };