Skip to content

Commit d4fce7a

Browse files
committed
refactored event list
1 parent 556c281 commit d4fce7a

File tree

6 files changed

+301
-130
lines changed

6 files changed

+301
-130
lines changed
Lines changed: 95 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { useMemo } from "react"
22
import type { Dayjs } from "dayjs"
3-
import { type DateType, DateUtils, type TimeType } from "../utils"
3+
import { twMerge } from "tailwind-merge"
4+
import type { TimeType } from "../utils"
45

56
export interface EventObject {
67
key: string
8+
title?: string
9+
subtitle?: string
710
startDate: Dayjs | undefined
811
endDate: Dayjs | undefined
912
}
@@ -20,24 +23,29 @@ export interface EventListProps<T extends EventObject> {
2023
maxEndTime: Dayjs
2124
dayStart: TimeType
2225
dayEnd: TimeType
23-
renderEvent: (
26+
renderEvent?: (
2427
event: T,
2528
startDate?: Dayjs,
2629
endDate?: Dayjs,
2730
) => React.JSX.Element
28-
renderTimeHeader?: (dateString: string) => React.JSX.Element
31+
renderTimeHeader?: (date: Dayjs) => React.JSX.Element
32+
className?: string
33+
style?: React.CSSProperties
2934
}
3035

3136
function useOrderByDateBookings<T extends EventObject>(
3237
items: T[],
33-
minStartTime: Dayjs,
38+
_minStartTime: Dayjs,
3439
maxEndTime: Dayjs,
3540
dayStart: TimeType,
3641
dayEnd: TimeType,
3742
) {
3843
return useMemo(() => {
3944
const dayStartTime = dayStart.split(":").map(Number)
4045
const dayEndTime = dayEnd.split(":").map(Number)
46+
const minStartTime = _minStartTime
47+
.set("hour", dayStartTime[0])
48+
.set("minute", dayStartTime[1])
4149

4250
const withinTimeRange = (items as T[]).filter((it) => {
4351
if (!it.endDate) return true
@@ -53,26 +61,34 @@ function useOrderByDateBookings<T extends EventObject>(
5361
)
5462
})
5563

56-
const datesMap: {
57-
[date: DateType]: EventWrapper<T>[]
58-
} = {}
64+
const datesMap: Map<Dayjs, EventWrapper<T>[]> = new Map()
65+
let currentStartDate = minStartTime
5966
for (const it of sortedItems) {
6067
let startDate = it.startDate ?? minStartTime
6168
const endDate = it.endDate ?? maxEndTime
69+
if (startDate.isBefore(minStartTime)) {
70+
startDate = minStartTime
71+
}
6272

6373
const timelineStartDay = startDate
6474
.hour(dayStartTime[0])
6575
.minute(dayStartTime[1])
6676

6777
while (startDate.isBefore(endDate)) {
68-
const dt = DateUtils.toDateType(startDate)
78+
//const dt = DateUtils.toDateType(startDate)
79+
const startOfDay = startDate
80+
.hour(dayStartTime[0])
81+
.minute(dayStartTime[1])
82+
if (!startOfDay.isSame(currentStartDate)) {
83+
currentStartDate = startDate.add(1, "day")
84+
}
6985
const bookingOfThisDay = {
7086
booking: it,
7187
renderStartDate: startDate,
7288
renderEndDate: endDate,
7389
}
7490

75-
if (it.startDate?.isBefore(timelineStartDay) === true) {
91+
if (it.startDate?.isBefore(timelineStartDay)) {
7692
bookingOfThisDay.renderStartDate = startDate
7793
.hour(dayStartTime[0])
7894
.minute(dayStartTime[1])
@@ -88,12 +104,15 @@ function useOrderByDateBookings<T extends EventObject>(
88104
if (!it.endDate || currEndDate.isBefore(it.endDate)) {
89105
bookingOfThisDay.renderEndDate = currEndDate
90106
}
91-
datesMap[dt] = [...(datesMap[dt] ?? []), bookingOfThisDay]
107+
datesMap.set(currentStartDate, [
108+
...(datesMap.get(currentStartDate) ?? []),
109+
bookingOfThisDay,
110+
])
92111
startDate = startDate.add(1, "day")
93112
}
94113
}
95114
return datesMap
96-
}, [items, dayEnd, dayStart, maxEndTime, minStartTime])
115+
}, [items, dayEnd, dayStart, maxEndTime, _minStartTime])
97116
}
98117

99118
const dateFormat = Intl.DateTimeFormat(undefined, {
@@ -103,47 +122,82 @@ const dateFormat = Intl.DateTimeFormat(undefined, {
103122
year: "numeric",
104123
})
105124

125+
function defaultRenderEvent<T extends EventObject>(
126+
booking: T,
127+
startDate: Dayjs | undefined,
128+
endDate: Dayjs | undefined,
129+
) {
130+
return (
131+
<div
132+
data-id={booking.key}
133+
className="flex justify-between py-1 cursor-pointer border-solid border-l-8 border-l-border-bold overflow-hidden bg-surface-sunken"
134+
>
135+
<div className="flex pl-2.5 flex-col overflow-hidden">
136+
<div className="text-text-subtle text-xl flex-0 truncate">
137+
<span>{booking.title ?? "no title"}</span>
138+
</div>
139+
{booking.subtitle && (
140+
<div className="text-text-subtle text-sm flex-0 truncate">
141+
<span>{booking.subtitle}</span>
142+
</div>
143+
)}
144+
<div className="text-text">
145+
{startDate?.format("HH:mm")} - {endDate?.format("HH:mm")}
146+
</div>
147+
</div>
148+
</div>
149+
)
150+
}
151+
106152
export function EventList<T extends EventObject>({
107153
items,
108-
renderEvent,
154+
renderEvent = defaultRenderEvent,
109155
renderTimeHeader,
110156
minStartTime,
111157
maxEndTime,
112158
dayStart,
113159
dayEnd,
160+
className,
161+
style,
114162
}: EventListProps<T>) {
115-
const datesMap: { [p: DateType]: EventWrapper<T>[] } =
116-
useOrderByDateBookings(
117-
items,
118-
minStartTime,
119-
maxEndTime,
120-
dayStart || "00:00",
121-
dayEnd || "00:00",
122-
)
163+
const datesMap: Map<Dayjs, EventWrapper<T>[]> = useOrderByDateBookings(
164+
items,
165+
minStartTime,
166+
maxEndTime,
167+
dayStart || "00:00",
168+
dayEnd || "00:00",
169+
)
123170

124-
return (
125-
<div className="min-h-0 overflow-auto">
126-
{Object.entries(datesMap).map(([date, eventObjects]) => {
127-
const dateStr = dateFormat.format(
128-
DateUtils.dateFromString(date, true),
129-
)
130-
return (
131-
<div key={date}>
171+
const content = useMemo(() => {
172+
const content: JSX.Element[] = []
173+
for (const [date, eventObjects] of datesMap) {
174+
const dateStr = dateFormat.format(date.toDate())
175+
content.push(
176+
<div key={dateStr} className="mt-4 first:mt-0">
177+
<div className="text-text-subtle text-sm flex items-center font-bold mt-4">
132178
{renderTimeHeader ? renderTimeHeader(date) : dateStr}
133-
<div className="flex flex-1 flex-col gap-1">
134-
{eventObjects.map(
135-
(eventObject: EventWrapper<T>) => {
136-
return renderEvent(
137-
eventObject.booking,
138-
eventObject.renderStartDate,
139-
eventObject.renderEndDate,
140-
)
141-
},
142-
)}
143-
</div>
144179
</div>
145-
)
146-
})}
180+
<div className="flex flex-1 flex-col gap-1">
181+
{eventObjects.map((eventObject: EventWrapper<T>) => {
182+
return renderEvent(
183+
eventObject.booking,
184+
eventObject.renderStartDate,
185+
eventObject.renderEndDate,
186+
)
187+
})}
188+
</div>
189+
</div>,
190+
)
191+
}
192+
return content
193+
}, [datesMap, renderTimeHeader, renderEvent])
194+
195+
return (
196+
<div
197+
className={twMerge("min-h-0 overflow-auto", className)}
198+
style={style}
199+
>
200+
{content}
147201
</div>
148202
)
149203
}

0 commit comments

Comments
 (0)