@@ -101,6 +101,19 @@ function getSessionsByDay(
101101 }
102102}
103103
104+ const timeFormat = new Intl . DateTimeFormat ( undefined , {
105+ hour : "2-digit" ,
106+ minute : "2-digit" ,
107+ } )
108+ const formatBlockTime = ( start : string , end ?: string ) => {
109+ const startDate = parseISO ( start )
110+ if ( end ) {
111+ const endDate = parseISO ( end )
112+ return timeFormat . formatRange ( startDate , endDate )
113+ }
114+ return timeFormat . format ( startDate )
115+ }
116+
104117export interface ScheduleListProps {
105118 showFilter ?: boolean
106119 scheduleData : ScheduleSession [ ]
@@ -191,36 +204,64 @@ export function ScheduleList({
191204 { format ( parseISO ( date ) , "EEEE, MMMM d" ) }
192205 </ h3 >
193206 { Object . entries ( concurrentSessionsGroup ) . map (
194- ( [ sessionDate , sessions ] ) => (
195- < div
196- key = { `concurrent sessions on ${ sessionDate } ` }
197- className = "lg:mt-px"
198- >
199- < div className = "mr-px flex flex-col max-lg:ml-px lg:flex-row" >
200- < div className = "relative border-neu-50 bg-neu-50 dark:bg-neu-0 max-lg:-mx-px max-lg:my-px max-lg:border-x lg:mr-px" >
201- < span className = "typography-body-sm mt-3 inline-block w-20 whitespace-nowrap pb-0.5 pl-4 lg:mr-6 lg:w-28 lg:pb-4 lg:pl-0" >
202- { parseISO ( sessionDate ) . toLocaleTimeString (
203- undefined ,
204- {
205- hour : "2-digit" ,
206- minute : "2-digit" ,
207- } ,
208- ) }
209- </ span >
207+ ( [ sessionDate , sessions ] , i , blocks ) => {
208+ const blockEnd = sessions [ 0 ] ?. event_end
209+ const nextBlockStart = blocks [ i + 1 ] ?. [ 0 ]
210+
211+ const isBreak =
212+ sessions [ 0 ] ?. event_type
213+ ?. toLowerCase ( )
214+ . includes ( "break" ) ||
215+ blocks [ i + 1 ] ?. [ 1 ] ?. [ 0 ] ?. event_type
216+ ?. toLowerCase ( )
217+ . includes ( "break" )
218+ const hasDashedBorder =
219+ blockEnd && blockEnd === nextBlockStart && ! isBreak
220+
221+ return (
222+ < div
223+ key = { `concurrent sessions on ${ sessionDate } ` }
224+ className = "relative lg:mt-px [&_div:has(a:hover)]:z-[1]"
225+ >
226+ < div className = "mr-px flex flex-col max-lg:ml-px lg:flex-row" >
227+ < div className = "relative border-neu-50 bg-neu-50 dark:bg-neu-0 max-lg:-mx-px max-lg:my-px max-lg:border-x lg:mr-px" >
228+ < span className = "typography-body-sm mt-3 inline-block w-20 whitespace-nowrap pb-0.5 pl-4 lg:mr-6 lg:w-28 lg:pb-4 lg:pl-0" >
229+ { formatBlockTime ( sessionDate , blockEnd ) }
230+ </ span >
231+ </ div >
232+ < div className = "relative flex w-full flex-col items-end gap-px lg:flex-row lg:items-start" >
233+ { sessions . map ( session => (
234+ < ScheduleSessionCard
235+ key = { session . id }
236+ session = { session }
237+ year = { year }
238+ eventsColors = { eventsColors }
239+ />
240+ ) ) }
241+ </ div >
210242 </ div >
211- < div className = "relative flex w-full flex-col items-end gap-px lg:flex-row lg:items-start" >
212- { sessions . map ( session => (
213- < ScheduleSessionCard
214- key = { session . id }
215- session = { session }
216- year = { year }
217- eventsColors = { eventsColors }
243+ { hasDashedBorder && (
244+ < svg
245+ className = "absolute -bottom-px left-0 h-px w-full text-neu-50"
246+ viewBox = "0 0 100 1"
247+ preserveAspectRatio = "none"
248+ >
249+ < line
250+ x1 = "0"
251+ y1 = "0.5"
252+ x2 = "100"
253+ y2 = "0.5"
254+ stroke = "currentColor"
255+ strokeWidth = "1"
256+ strokeDasharray = "4,4"
257+ strokeDashoffset = "4"
258+ vectorEffect = "non-scaling-stroke"
218259 />
219- ) ) }
220- </ div >
260+ </ svg >
261+ ) }
221262 </ div >
222- </ div >
223- ) ,
263+ )
264+ } ,
224265 ) }
225266 </ div >
226267 ) ,
@@ -237,7 +278,7 @@ function BookmarkOnSched({ year }: { year: `202${number}` }) {
237278 href = { `https://graphqlconf${ year } .sched.com` }
238279 target = "_blank"
239280 rel = "noreferrer"
240- className = "typography-link mb-8 block w-fit decoration-neu-400"
281+ className = "typography-link mb-8 block w-fit decoration-neu-400 max-lg:hidden "
241282 >
242283 Bookmark sessions & plan your days on Sched
243284 < svg
0 commit comments