@devrongx/games 0.3.0 → 0.3.2
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/package.json +1 -1
- package/src/matches/MatchCalendar.tsx +209 -165
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// @devrongx/games — matches/MatchCalendar.tsx
|
|
2
2
|
"use client";
|
|
3
3
|
|
|
4
|
-
import { useRef, useMemo } from "react";
|
|
4
|
+
import { useRef, useMemo, useEffect } from "react";
|
|
5
5
|
import { motion } from "framer-motion";
|
|
6
|
-
import { Calendar, MapPin, Loader2, Zap,
|
|
6
|
+
import { Calendar, MapPin, Loader2, Trophy, Zap, Target, Users } from "lucide-react";
|
|
7
7
|
import { useTDMatches } from "./useTDMatches";
|
|
8
8
|
import type { ITDMatch } from "./types";
|
|
9
9
|
|
|
@@ -14,6 +14,8 @@ const MATCH_STATUS = {
|
|
|
14
14
|
COMPLETED: 5,
|
|
15
15
|
} as const;
|
|
16
16
|
|
|
17
|
+
const OUTFIT = { fontFamily: "Outfit, sans-serif" };
|
|
18
|
+
|
|
17
19
|
/* ── Date helpers ── */
|
|
18
20
|
|
|
19
21
|
function toLocalDate(iso: string): Date {
|
|
@@ -57,13 +59,19 @@ function getTeamShortName(match: ITDMatch, team: "a" | "b"): string {
|
|
|
57
59
|
return t.short_name || t.name.slice(0, 3).toUpperCase();
|
|
58
60
|
}
|
|
59
61
|
|
|
62
|
+
/** Map popularity 0-10 to border shine opacity (0 = no shine, 10 = full intensity) */
|
|
63
|
+
function getShineOpacity(popularity: number): number {
|
|
64
|
+
if (popularity <= 0) return 0;
|
|
65
|
+
return Math.min(popularity / 10, 1);
|
|
66
|
+
}
|
|
67
|
+
|
|
60
68
|
/* ── Grouping: by date ── */
|
|
61
69
|
|
|
62
70
|
interface IMatchDay {
|
|
63
|
-
dateKey: string;
|
|
71
|
+
dateKey: string;
|
|
64
72
|
date: Date;
|
|
65
|
-
label: string;
|
|
66
|
-
dayMonth: string;
|
|
73
|
+
label: string;
|
|
74
|
+
dayMonth: string;
|
|
67
75
|
matches: ITDMatch[];
|
|
68
76
|
}
|
|
69
77
|
|
|
@@ -93,91 +101,148 @@ function groupByDate(matches: ITDMatch[]): IMatchDay[] {
|
|
|
93
101
|
}));
|
|
94
102
|
}
|
|
95
103
|
|
|
96
|
-
/* ──
|
|
104
|
+
/* ── Match card with border shine animation ── */
|
|
97
105
|
|
|
98
|
-
function
|
|
106
|
+
function MatchCard({ match }: { match: ITDMatch }) {
|
|
99
107
|
const isLive = match.status === MATCH_STATUS.LIVE;
|
|
100
108
|
const isCompleted = match.status === MATCH_STATUS.COMPLETED;
|
|
101
109
|
const time = match.scheduled_start_at ? formatTime(toLocalDate(match.scheduled_start_at)) : "";
|
|
102
|
-
const venue = match.venue?.city || match.venue?.name;
|
|
110
|
+
const venue = match.venue?.city?.trim() || match.venue?.name;
|
|
111
|
+
const shineOpacity = getShineOpacity(match.rating_popularity);
|
|
112
|
+
const showShine = shineOpacity > 0 && !isCompleted;
|
|
103
113
|
|
|
104
114
|
return (
|
|
105
|
-
<div
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
: isCompleted
|
|
111
|
-
? "rgba(255,255,255,0.02)"
|
|
112
|
-
: "rgba(255,255,255,0.03)",
|
|
113
|
-
border: isLive
|
|
114
|
-
? "1px solid rgba(248,60,197,0.2)"
|
|
115
|
-
: "1px solid rgba(255,255,255,0.04)",
|
|
116
|
-
}}
|
|
117
|
-
>
|
|
118
|
-
{/* Time + status row */}
|
|
119
|
-
<div className="flex items-center justify-between">
|
|
120
|
-
<span
|
|
121
|
-
className="text-[10px]"
|
|
115
|
+
<div className="relative w-full rounded-xl p-[1px] overflow-hidden">
|
|
116
|
+
{/* Spinning border shine — intensity based on popularity */}
|
|
117
|
+
{showShine && (
|
|
118
|
+
<div
|
|
119
|
+
className="absolute inset-[-50%] pointer-events-none"
|
|
122
120
|
style={{
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
background: isLive
|
|
122
|
+
? `conic-gradient(from 0deg, transparent 0%, transparent 65%, rgba(248,60,197,${shineOpacity * 0.6}) 75%, rgba(255,255,255,${shineOpacity * 0.7}) 80%, rgba(248,60,197,${shineOpacity * 0.6}) 85%, transparent 95%, transparent 100%)`
|
|
123
|
+
: `conic-gradient(from 0deg, transparent 0%, transparent 70%, rgba(34,227,232,${shineOpacity * 0.5}) 78%, rgba(255,255,255,${shineOpacity * 0.6}) 80%, rgba(34,227,232,${shineOpacity * 0.5}) 82%, transparent 90%, transparent 100%)`,
|
|
124
|
+
animation: `borderShine ${6 - shineOpacity * 2}s linear infinite`,
|
|
125
125
|
}}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
126
|
+
/>
|
|
127
|
+
)}
|
|
128
|
+
|
|
129
|
+
{/* Static border for no-shine / completed */}
|
|
130
|
+
{!showShine && (
|
|
131
|
+
<div
|
|
132
|
+
className="absolute inset-0 rounded-xl"
|
|
133
|
+
style={{
|
|
134
|
+
border: isCompleted
|
|
135
|
+
? "1px solid rgba(255,255,255,0.06)"
|
|
136
|
+
: "1px solid rgba(255,255,255,0.08)",
|
|
137
|
+
}}
|
|
138
|
+
/>
|
|
139
|
+
)}
|
|
140
|
+
|
|
141
|
+
<div
|
|
142
|
+
className="relative flex flex-col gap-1.5 rounded-xl px-3 py-2.5"
|
|
143
|
+
style={{
|
|
144
|
+
background: isLive ? "rgba(10,10,18,0.95)" : "rgba(10,10,18,0.98)",
|
|
145
|
+
}}
|
|
146
|
+
>
|
|
147
|
+
{/* Time + live indicator */}
|
|
148
|
+
<div className="flex items-center justify-between">
|
|
141
149
|
<span
|
|
142
|
-
className="text-[
|
|
143
|
-
style={{
|
|
150
|
+
className="text-[10px]"
|
|
151
|
+
style={{
|
|
152
|
+
...OUTFIT,
|
|
153
|
+
color: isLive ? "#f83cc5" : isCompleted ? "rgba(255,255,255,0.35)" : "#fff",
|
|
154
|
+
}}
|
|
144
155
|
>
|
|
145
|
-
{
|
|
156
|
+
{time}
|
|
146
157
|
</span>
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
>
|
|
165
|
-
{getTeamShortName(match, "b")}
|
|
166
|
-
</span>
|
|
167
|
-
</div>
|
|
158
|
+
{isLive && (
|
|
159
|
+
<div className="flex items-center gap-1">
|
|
160
|
+
<span className="relative flex h-1.5 w-1.5">
|
|
161
|
+
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-[#f83cc5] opacity-75" />
|
|
162
|
+
<span className="relative inline-flex rounded-full h-1.5 w-1.5 bg-[#f83cc5]" />
|
|
163
|
+
</span>
|
|
164
|
+
<span className="text-[8px] barlowcondensedBold tracking-widest" style={{ color: "#f83cc5" }}>
|
|
165
|
+
LIVE
|
|
166
|
+
</span>
|
|
167
|
+
</div>
|
|
168
|
+
)}
|
|
169
|
+
{isCompleted && match.winner && (
|
|
170
|
+
<span className="text-[8px]" style={{ ...OUTFIT, color: "rgba(255,255,255,0.35)" }}>
|
|
171
|
+
{match.winner.short_name || match.winner.name} won
|
|
172
|
+
</span>
|
|
173
|
+
)}
|
|
174
|
+
</div>
|
|
168
175
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
<div className="flex items-center gap-1 justify-center">
|
|
172
|
-
<MapPin size={7} style={{ color: "rgba(255,255,255,0.15)" }} />
|
|
176
|
+
{/* Teams */}
|
|
177
|
+
<div className="flex items-center gap-1.5 justify-center">
|
|
173
178
|
<span
|
|
174
|
-
className="text-[
|
|
175
|
-
style={{
|
|
179
|
+
className="barlowcondensedBold text-[20px] leading-none"
|
|
180
|
+
style={{ color: isCompleted ? "rgba(255,255,255,0.35)" : "#fff" }}
|
|
176
181
|
>
|
|
177
|
-
{
|
|
182
|
+
{getTeamShortName(match, "a")}
|
|
183
|
+
</span>
|
|
184
|
+
<span className="text-[9px]" style={{ color: "rgba(255,255,255,0.3)" }}>
|
|
185
|
+
v
|
|
186
|
+
</span>
|
|
187
|
+
<span
|
|
188
|
+
className="barlowcondensedBold text-[20px] leading-none"
|
|
189
|
+
style={{ color: isCompleted ? "rgba(255,255,255,0.35)" : "#fff" }}
|
|
190
|
+
>
|
|
191
|
+
{getTeamShortName(match, "b")}
|
|
178
192
|
</span>
|
|
179
193
|
</div>
|
|
180
|
-
|
|
194
|
+
|
|
195
|
+
{/* Venue */}
|
|
196
|
+
{venue && (
|
|
197
|
+
<div className="flex items-center gap-1 justify-center">
|
|
198
|
+
<MapPin size={8} style={{ color: "rgba(255,255,255,0.3)" }} />
|
|
199
|
+
<span
|
|
200
|
+
className="text-[8px] truncate max-w-[100px]"
|
|
201
|
+
style={{ ...OUTFIT, color: "rgba(255,255,255,0.3)" }}
|
|
202
|
+
>
|
|
203
|
+
{venue}
|
|
204
|
+
</span>
|
|
205
|
+
</div>
|
|
206
|
+
)}
|
|
207
|
+
|
|
208
|
+
{/* Popularity indicator */}
|
|
209
|
+
{match.rating_popularity > 0 && !isCompleted && (
|
|
210
|
+
<div className="flex items-center gap-0.5 justify-center mt-0.5">
|
|
211
|
+
{Array.from({ length: Math.min(Math.ceil(match.rating_popularity / 2), 5) }).map((_, i) => (
|
|
212
|
+
<Zap
|
|
213
|
+
key={i}
|
|
214
|
+
size={7}
|
|
215
|
+
fill={isLive ? "#f83cc5" : "#22E3E8"}
|
|
216
|
+
style={{ color: isLive ? "#f83cc5" : "#22E3E8", opacity: 0.4 + i * 0.15 }}
|
|
217
|
+
/>
|
|
218
|
+
))}
|
|
219
|
+
</div>
|
|
220
|
+
)}
|
|
221
|
+
|
|
222
|
+
{/* Game modes */}
|
|
223
|
+
{!isCompleted && (
|
|
224
|
+
<div className="flex flex-col gap-1 mt-1 pt-1.5" style={{ borderTop: "1px solid rgba(255,255,255,0.06)" }}>
|
|
225
|
+
<div
|
|
226
|
+
className="flex items-center gap-1.5 px-2 py-1 rounded-lg"
|
|
227
|
+
style={{ background: "rgba(34,227,232,0.06)" }}
|
|
228
|
+
>
|
|
229
|
+
<Target size={9} style={{ color: "#22E3E8" }} />
|
|
230
|
+
<span className="text-[8px] barlowcondensedBold tracking-wide text-white">
|
|
231
|
+
Pre-Match Bets
|
|
232
|
+
</span>
|
|
233
|
+
</div>
|
|
234
|
+
<div
|
|
235
|
+
className="flex items-center gap-1.5 px-2 py-1 rounded-lg"
|
|
236
|
+
style={{ background: "rgba(153,69,255,0.06)" }}
|
|
237
|
+
>
|
|
238
|
+
<Users size={9} style={{ color: "#9945FF" }} />
|
|
239
|
+
<span className="text-[8px] barlowcondensedBold tracking-wide text-white">
|
|
240
|
+
Fantasy 11
|
|
241
|
+
</span>
|
|
242
|
+
</div>
|
|
243
|
+
</div>
|
|
244
|
+
)}
|
|
245
|
+
</div>
|
|
181
246
|
</div>
|
|
182
247
|
);
|
|
183
248
|
}
|
|
@@ -195,55 +260,38 @@ function DayColumn({ day, index }: { day: IMatchDay; index: number }) {
|
|
|
195
260
|
animate={{ opacity: 1, y: 0 }}
|
|
196
261
|
transition={{ duration: 0.3, delay: index * 0.03 }}
|
|
197
262
|
className="shrink-0 flex flex-col gap-1.5"
|
|
198
|
-
style={{ scrollSnapAlign: "start", width: "
|
|
263
|
+
style={{ scrollSnapAlign: "start", width: "140px" }}
|
|
199
264
|
>
|
|
200
265
|
{/* Day header */}
|
|
201
266
|
<div className="flex flex-col items-center gap-0.5 pb-1">
|
|
202
267
|
<span
|
|
203
268
|
className="text-[10px] barlowcondensedBold tracking-widest"
|
|
204
269
|
style={{
|
|
205
|
-
color: hasLive
|
|
206
|
-
? "#f83cc5"
|
|
207
|
-
: hasToday
|
|
208
|
-
? "#22E3E8"
|
|
209
|
-
: allCompleted
|
|
210
|
-
? "rgba(255,255,255,0.15)"
|
|
211
|
-
: "rgba(255,255,255,0.4)",
|
|
270
|
+
color: hasLive ? "#f83cc5" : hasToday ? "#22E3E8" : allCompleted ? "rgba(255,255,255,0.25)" : "#fff",
|
|
212
271
|
}}
|
|
213
272
|
>
|
|
214
273
|
{day.label}
|
|
215
274
|
</span>
|
|
216
275
|
<span
|
|
217
|
-
className="text-[
|
|
276
|
+
className="text-[12px] font-semibold"
|
|
218
277
|
style={{
|
|
219
|
-
|
|
220
|
-
color: hasLive
|
|
221
|
-
? "rgba(248,60,197,0.8)"
|
|
222
|
-
: hasToday
|
|
223
|
-
? "rgba(34,227,232,0.7)"
|
|
224
|
-
: allCompleted
|
|
225
|
-
? "rgba(255,255,255,0.15)"
|
|
226
|
-
: "rgba(255,255,255,0.5)",
|
|
278
|
+
...OUTFIT,
|
|
279
|
+
color: hasLive ? "#f83cc5" : hasToday ? "#22E3E8" : allCompleted ? "rgba(255,255,255,0.25)" : "#fff",
|
|
227
280
|
}}
|
|
228
281
|
>
|
|
229
282
|
{day.dayMonth}
|
|
230
283
|
</span>
|
|
231
|
-
{/* Indicator dot */}
|
|
232
284
|
{(hasToday || hasLive) && (
|
|
233
285
|
<div
|
|
234
286
|
className="w-5 h-[2px] rounded-full mt-0.5"
|
|
235
|
-
style={{
|
|
236
|
-
background: hasLive
|
|
237
|
-
? "#f83cc5"
|
|
238
|
-
: "#22E3E8",
|
|
239
|
-
}}
|
|
287
|
+
style={{ background: hasLive ? "#f83cc5" : "#22E3E8" }}
|
|
240
288
|
/>
|
|
241
289
|
)}
|
|
242
290
|
</div>
|
|
243
291
|
|
|
244
292
|
{/* Match cards for the day */}
|
|
245
293
|
{day.matches.map((match) => (
|
|
246
|
-
<
|
|
294
|
+
<MatchCard key={match.id} match={match} />
|
|
247
295
|
))}
|
|
248
296
|
</motion.div>
|
|
249
297
|
);
|
|
@@ -264,48 +312,43 @@ export function MatchCalendar({ tournamentId }: MatchCalendarProps) {
|
|
|
264
312
|
|
|
265
313
|
const days = useMemo(() => groupByDate(matches), [matches]);
|
|
266
314
|
|
|
267
|
-
//
|
|
268
|
-
const
|
|
269
|
-
const now = new Date();
|
|
270
|
-
// Find today
|
|
315
|
+
// Auto-scroll to today or first upcoming day
|
|
316
|
+
const scrollTargetIndex = useMemo(() => {
|
|
271
317
|
const tIdx = days.findIndex((d) => isToday(d.date));
|
|
272
318
|
if (tIdx >= 0) return tIdx;
|
|
273
|
-
|
|
319
|
+
const now = new Date();
|
|
274
320
|
const uIdx = days.findIndex((d) => d.date >= now);
|
|
275
|
-
if (uIdx >= 0) return Math.max(0, uIdx
|
|
321
|
+
if (uIdx >= 0) return Math.max(0, uIdx);
|
|
276
322
|
return 0;
|
|
277
323
|
}, [days]);
|
|
278
324
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
if (!stages.has(m.stage.id)) {
|
|
286
|
-
stages.set(m.stage.id, { name: m.stage.name, orderNo: m.stage.order_no, days: [] });
|
|
287
|
-
}
|
|
288
|
-
}
|
|
325
|
+
useEffect(() => {
|
|
326
|
+
if (scrollRef.current && scrollTargetIndex > 0) {
|
|
327
|
+
const container = scrollRef.current;
|
|
328
|
+
const target = container.children[scrollTargetIndex] as HTMLElement;
|
|
329
|
+
if (target) {
|
|
330
|
+
container.scrollTo({ left: target.offsetLeft - 20, behavior: "smooth" });
|
|
289
331
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
332
|
+
}
|
|
333
|
+
}, [scrollTargetIndex, days]);
|
|
334
|
+
|
|
335
|
+
// Stage groups
|
|
336
|
+
const stageGroups = useMemo(() => {
|
|
337
|
+
const stages = new Map<number, { name: string; orderNo: number }>();
|
|
338
|
+
for (const m of matches) {
|
|
339
|
+
if (m.stage && !stages.has(m.stage.id)) {
|
|
340
|
+
stages.set(m.stage.id, { name: m.stage.name, orderNo: m.stage.order_no });
|
|
297
341
|
}
|
|
298
342
|
}
|
|
299
343
|
return Array.from(stages.values()).sort((a, b) => a.orderNo - b.orderNo);
|
|
300
|
-
}, [
|
|
344
|
+
}, [matches]);
|
|
301
345
|
|
|
302
|
-
// Tournament date range from first match
|
|
303
346
|
const tournament = matches[0]?.tournament;
|
|
304
347
|
|
|
305
348
|
if (isLoading) {
|
|
306
349
|
return (
|
|
307
|
-
<div className="flex items-center justify-center py-
|
|
308
|
-
<Loader2 size={16} className="animate-spin" style={{ color: "rgba(255,255,255,0.
|
|
350
|
+
<div className="flex items-center justify-center py-6">
|
|
351
|
+
<Loader2 size={16} className="animate-spin" style={{ color: "rgba(255,255,255,0.3)" }} />
|
|
309
352
|
</div>
|
|
310
353
|
);
|
|
311
354
|
}
|
|
@@ -313,54 +356,55 @@ export function MatchCalendar({ tournamentId }: MatchCalendarProps) {
|
|
|
313
356
|
if (error || matches.length === 0) return null;
|
|
314
357
|
|
|
315
358
|
return (
|
|
316
|
-
<div className="relative z-30 w-full
|
|
359
|
+
<div className="relative z-30 w-full mt-6 mb-4">
|
|
360
|
+
{/* Tournament info header */}
|
|
361
|
+
{tournament && (
|
|
362
|
+
<div className="px-5 mb-3">
|
|
363
|
+
<div className="flex items-center gap-2 mb-1">
|
|
364
|
+
<Trophy size={14} style={{ color: "#22E3E8" }} />
|
|
365
|
+
<span className="barlowcondensedBold text-[16px] tracking-wide text-white">
|
|
366
|
+
{tournament.name}
|
|
367
|
+
</span>
|
|
368
|
+
</div>
|
|
369
|
+
<div className="flex items-center gap-3">
|
|
370
|
+
{tournament.start_date && tournament.end_date && (
|
|
371
|
+
<span className="text-[11px] text-white" style={OUTFIT}>
|
|
372
|
+
{formatDayMonth(toLocalDate(tournament.start_date))} – {formatDayMonth(toLocalDate(tournament.end_date))}
|
|
373
|
+
</span>
|
|
374
|
+
)}
|
|
375
|
+
<span className="text-[11px] text-white" style={OUTFIT}>
|
|
376
|
+
{matches.length} matches
|
|
377
|
+
</span>
|
|
378
|
+
{stageGroups.length > 0 && (
|
|
379
|
+
<span
|
|
380
|
+
className="text-[9px] barlowcondensedBold tracking-widest px-2 py-0.5 rounded-full"
|
|
381
|
+
style={{
|
|
382
|
+
color: "#22E3E8",
|
|
383
|
+
background: "rgba(34,227,232,0.08)",
|
|
384
|
+
border: "1px solid rgba(34,227,232,0.15)",
|
|
385
|
+
}}
|
|
386
|
+
>
|
|
387
|
+
{stageGroups[0].name.toUpperCase()}
|
|
388
|
+
</span>
|
|
389
|
+
)}
|
|
390
|
+
</div>
|
|
391
|
+
</div>
|
|
392
|
+
)}
|
|
393
|
+
|
|
317
394
|
{/* Section header */}
|
|
318
|
-
<div className="flex items-center gap-2 px-5 mb-
|
|
319
|
-
<Calendar size={12} style={{ color: "
|
|
320
|
-
<span className="barlowCondensedSemiBold text-[13px] tracking-wider
|
|
395
|
+
<div className="flex items-center gap-2 px-5 mb-2.5">
|
|
396
|
+
<Calendar size={12} style={{ color: "#22E3E8" }} />
|
|
397
|
+
<span className="barlowCondensedSemiBold text-[13px] tracking-wider text-white">
|
|
321
398
|
SCHEDULE
|
|
322
399
|
</span>
|
|
323
|
-
{
|
|
324
|
-
{tournament?.start_date && tournament?.end_date && (
|
|
325
|
-
<>
|
|
326
|
-
<ChevronRight size={10} style={{ color: "rgba(255,255,255,0.15)" }} />
|
|
327
|
-
<span className="text-[10px]" style={{ fontFamily: "Outfit, sans-serif", color: "rgba(255,255,255,0.2)" }}>
|
|
328
|
-
{formatDayMonth(toLocalDate(tournament.start_date))} – {formatDayMonth(toLocalDate(tournament.end_date))}
|
|
329
|
-
</span>
|
|
330
|
-
</>
|
|
331
|
-
)}
|
|
332
|
-
<div className="flex-1" />
|
|
333
|
-
<span className="text-[10px]" style={{ fontFamily: "Outfit, sans-serif", color: "rgba(255,255,255,0.2)" }}>
|
|
334
|
-
{matches.length} matches
|
|
335
|
-
</span>
|
|
400
|
+
<div className="flex-1 h-px" style={{ background: "rgba(255,255,255,0.06)" }} />
|
|
336
401
|
</div>
|
|
337
402
|
|
|
338
|
-
{/* Stage label if exists */}
|
|
339
|
-
{stageGroups.length > 0 && stageGroups[0].name && (
|
|
340
|
-
<div className="px-5 mb-2">
|
|
341
|
-
<span
|
|
342
|
-
className="text-[9px] barlowcondensedBold tracking-widest px-2 py-0.5 rounded-full"
|
|
343
|
-
style={{
|
|
344
|
-
color: "rgba(34,227,232,0.5)",
|
|
345
|
-
background: "rgba(34,227,232,0.06)",
|
|
346
|
-
border: "1px solid rgba(34,227,232,0.1)",
|
|
347
|
-
}}
|
|
348
|
-
>
|
|
349
|
-
{stageGroups[0].name.toUpperCase()}
|
|
350
|
-
</span>
|
|
351
|
-
</div>
|
|
352
|
-
)}
|
|
353
|
-
|
|
354
403
|
{/* Scrollable day columns */}
|
|
355
404
|
<div
|
|
356
405
|
ref={scrollRef}
|
|
357
|
-
className="flex gap-2 overflow-x-auto px-5 pb-
|
|
358
|
-
style={{
|
|
359
|
-
scrollSnapType: "x mandatory",
|
|
360
|
-
WebkitOverflowScrolling: "touch",
|
|
361
|
-
// Auto-scroll to today/upcoming
|
|
362
|
-
...(todayIndex > 0 ? {} : {}),
|
|
363
|
-
}}
|
|
406
|
+
className="flex gap-2.5 overflow-x-auto px-5 pb-3 scrollbar-hide"
|
|
407
|
+
style={{ scrollSnapType: "x mandatory", WebkitOverflowScrolling: "touch" }}
|
|
364
408
|
>
|
|
365
409
|
{days.map((day, i) => (
|
|
366
410
|
<DayColumn key={day.dateKey} day={day} index={i} />
|