Skip to content

Commit c12ea1e

Browse files
Merge pull request #113 from linked-planet/dev
Dev
2 parents e84456b + f21f1f9 commit c12ea1e

File tree

11 files changed

+471
-455
lines changed

11 files changed

+471
-455
lines changed
Lines changed: 143 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,58 @@
1-
import { useMemo } from "react"
1+
import { type ComponentType, useMemo } from "react"
22
import type { Dayjs } from "dayjs/esm"
3-
import { twMerge } from "tailwind-merge"
4-
import type { TimeType } from "../utils"
3+
import type { DateType, TimeType } from "../utils/DateUtils"
4+
import { DateUtils } from "../utils"
5+
import dayjs from "dayjs/esm"
56

6-
export interface EventListObject {
7+
export type EventListItem = {
78
key: string
89
title?: string
910
subtitle?: string
1011
startDate: Dayjs | undefined
1112
endDate: Dayjs | undefined
1213
}
1314

14-
interface EventWrapper<T extends EventListObject> {
15-
booking: T
16-
renderStartDate: Dayjs
17-
renderEndDate: Dayjs
15+
export type EventListItemComponentProps<T extends EventListItem> = {
16+
event: T
17+
onEventClick?: (event: T) => void
18+
overrideStartDate?: Dayjs
19+
overrideEndDate?: Dayjs
20+
style?: React.CSSProperties
21+
className?: string
1822
}
1923

20-
export interface EventListProps<T extends EventListObject> {
24+
export type EventListProps<T extends EventListItem> = {
2125
items: T[]
22-
minStartTime: Dayjs
23-
maxEndTime: Dayjs
26+
minStartDateTime: Dayjs
27+
maxEndDateTime: Dayjs
28+
HeaderComponent?: ComponentType<{ date: Dayjs }>
29+
ItemComponent: ComponentType<EventListItemComponentProps<T>>
30+
onEventClick?: (event: T) => void
2431
dayStart: TimeType
2532
dayEnd: TimeType
26-
renderEvent?: (
27-
event: T,
28-
startDate?: Dayjs,
29-
endDate?: Dayjs,
30-
) => React.JSX.Element
31-
renderTimeHeader?: (date: Dayjs) => React.JSX.Element
32-
className?: string
33-
style?: React.CSSProperties
3433
}
3534

36-
function useOrderByDateBookings<T extends EventListObject>(
35+
/**
36+
* This also filters out when there is no place or room booked
37+
* @param items
38+
* @param minStartTime
39+
* @param maxEndTime
40+
* @param dayStart
41+
* @param dayEnd
42+
* @returns
43+
*/
44+
function useOrderByDate<T extends EventListItem>(
3745
items: T[],
38-
_minStartTime: Dayjs,
46+
minStartTime: Dayjs,
3947
maxEndTime: Dayjs,
4048
dayStart: TimeType,
4149
dayEnd: TimeType,
4250
) {
4351
return useMemo(() => {
4452
const dayStartTime = dayStart.split(":").map(Number)
4553
const dayEndTime = dayEnd.split(":").map(Number)
46-
const minStartTime = _minStartTime
47-
.set("hour", dayStartTime[0])
48-
.set("minute", dayStartTime[1])
4954

50-
const withinTimeRange = (items as T[]).filter((it) => {
55+
const withinTimeRange = items.filter((it) => {
5156
if (!it.endDate) return true
5257
if (it.startDate?.isAfter(maxEndTime)) return false
5358
if (it.endDate?.isBefore(minStartTime)) return false
@@ -61,58 +66,98 @@ function useOrderByDateBookings<T extends EventListObject>(
6166
)
6267
})
6368

64-
const datesMap: Map<Dayjs, EventWrapper<T>[]> = new Map()
65-
let currentStartDate = minStartTime
69+
const datesMap: {
70+
[date: DateType]: {
71+
event: T
72+
renderStartDate: Dayjs
73+
renderEndDate: Dayjs
74+
}[]
75+
} = {}
6676
for (const it of sortedItems) {
6777
let startDate = it.startDate ?? minStartTime
6878
const endDate = it.endDate ?? maxEndTime
69-
if (startDate.isBefore(minStartTime)) {
70-
startDate = minStartTime
71-
}
72-
73-
const timelineStartDay = startDate
74-
.hour(dayStartTime[0])
75-
.minute(dayStartTime[1])
7679

7780
while (startDate.isBefore(endDate)) {
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-
}
85-
const bookingOfThisDay = {
86-
booking: it,
81+
const dt = DateUtils.toDateType(startDate)
82+
const eventOfThisDay = {
83+
event: it,
8784
renderStartDate: startDate,
8885
renderEndDate: endDate,
8986
}
87+
// out of the days time range
88+
if (
89+
startDate.hour() > dayEndTime[0] ||
90+
(startDate.hour() === dayEndTime[0] &&
91+
startDate.minute() > dayEndTime[1])
92+
) {
93+
startDate = startDate
94+
.startOf("day")
95+
.add(1, "day")
96+
.add(dayStartTime[0], "hour")
97+
.add(dayStartTime[1], "minute")
98+
continue
99+
}
90100

91-
if (it.startDate?.isBefore(timelineStartDay)) {
92-
bookingOfThisDay.renderStartDate = startDate
93-
.hour(dayStartTime[0])
94-
.minute(dayStartTime[1])
95-
} else if (!it.startDate || it.startDate.isBefore(startDate)) {
96-
bookingOfThisDay.renderStartDate = startDate
101+
if (!it.startDate || it.startDate.isBefore(startDate)) {
102+
eventOfThisDay.renderStartDate = startDate
97103
.hour(dayStartTime[0])
98104
.minute(dayStartTime[1])
99105
}
106+
if (
107+
eventOfThisDay.renderStartDate.hour() < dayStartTime[0] ||
108+
(eventOfThisDay.renderStartDate.hour() ===
109+
dayStartTime[0] &&
110+
eventOfThisDay.renderStartDate.minute() <
111+
dayStartTime[1])
112+
) {
113+
eventOfThisDay.renderStartDate =
114+
eventOfThisDay.renderStartDate
115+
.hour(dayStartTime[0])
116+
.minute(dayStartTime[1])
117+
}
100118

101-
const currEndDate = startDate
119+
let currEndDate = startDate
102120
.hour(dayEndTime[0])
103121
.minute(dayEndTime[1])
122+
if (dayEnd === "00:00") {
123+
currEndDate = currEndDate.add(1, "day")
124+
} else if (
125+
currEndDate.hour() > dayEndTime[0] ||
126+
(currEndDate.hour() === dayEndTime[0] &&
127+
currEndDate.minute() > dayEndTime[1])
128+
) {
129+
currEndDate = currEndDate
130+
.hour(dayEndTime[0])
131+
.minute(dayEndTime[1])
132+
}
133+
104134
if (!it.endDate || currEndDate.isBefore(it.endDate)) {
105-
bookingOfThisDay.renderEndDate = currEndDate
135+
eventOfThisDay.renderEndDate = currEndDate
136+
}
137+
if (
138+
eventOfThisDay.renderStartDate.isAfter(
139+
eventOfThisDay.renderEndDate,
140+
)
141+
) {
142+
console.log(
143+
"BookingList - render start date is after end date",
144+
it,
145+
eventOfThisDay,
146+
)
147+
eventOfThisDay.renderStartDate =
148+
eventOfThisDay.renderEndDate
106149
}
107-
datesMap.set(currentStartDate, [
108-
...(datesMap.get(currentStartDate) ?? []),
109-
bookingOfThisDay,
110-
])
111-
startDate = startDate.add(1, "day")
150+
151+
datesMap[dt] = [...(datesMap[dt] ?? []), eventOfThisDay]
152+
startDate = startDate
153+
.startOf("day")
154+
.add(1, "day")
155+
.add(dayStartTime[0], "hour")
156+
.add(dayStartTime[1], "minute")
112157
}
113158
}
114159
return datesMap
115-
}, [items, dayEnd, dayStart, maxEndTime, _minStartTime])
160+
}, [items, dayEnd, dayStart, maxEndTime, minStartTime])
116161
}
117162

118163
const dateFormat = Intl.DateTimeFormat(undefined, {
@@ -122,82 +167,54 @@ const dateFormat = Intl.DateTimeFormat(undefined, {
122167
year: "numeric",
123168
})
124169

125-
function defaultRenderEvent<T extends EventListObject>(
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-
152-
export function EventList<T extends EventListObject>({
170+
export function EventList<T extends EventListItem>({
153171
items,
154-
renderEvent = defaultRenderEvent,
155-
renderTimeHeader,
156-
minStartTime,
157-
maxEndTime,
172+
HeaderComponent,
173+
ItemComponent,
174+
minStartDateTime,
175+
maxEndDateTime,
176+
onEventClick,
158177
dayStart,
159178
dayEnd,
160-
className,
161-
style,
162179
}: EventListProps<T>) {
163-
const datesMap: Map<Dayjs, EventWrapper<T>[]> = useOrderByDateBookings(
180+
const datesMap = useOrderByDate(
164181
items,
165-
minStartTime,
166-
maxEndTime,
167-
dayStart || "00:00",
168-
dayEnd || "00:00",
182+
minStartDateTime,
183+
maxEndDateTime,
184+
dayStart,
185+
dayEnd,
169186
)
170187

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">
178-
{renderTimeHeader ? renderTimeHeader(date) : dateStr}
179-
</div>
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+
const list = useMemo(
189+
() =>
190+
Object.entries(datesMap).map(([date, bookings]) => {
191+
const hdate = DateUtils.dateFromString(date, true)
192+
const header = HeaderComponent ? (
193+
<HeaderComponent date={dayjs(hdate)} />
194+
) : (
195+
dateFormat.format(hdate)
196+
)
197+
return (
198+
<div key={date}>
199+
{header}
200+
<div className="flex flex-1 flex-col gap-1">
201+
{bookings.map((it, index) => {
202+
return (
203+
<ItemComponent
204+
event={it.event}
205+
key={it.event.key}
206+
overrideStartDate={it.renderStartDate}
207+
overrideEndDate={it.renderEndDate}
208+
onEventClick={onEventClick}
209+
/>
210+
)
211+
})}
212+
</div>
188213
</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}
201-
</div>
214+
)
215+
}),
216+
[datesMap, ItemComponent, onEventClick, HeaderComponent],
202217
)
218+
219+
return <div className="min-h-0 overflow-auto">{list}</div>
203220
}

library/src/components/Tabs.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ const TabList = forwardRef(
139139
)
140140
}
141141
if (child.type !== Tab && !allowNonTabComponents) {
142-
console.warn(
142+
console.info(
143143
"Only Tab components are allowed as children of TabList, but was",
144144
child.type,
145145
"(use allowNonTabComponents prop to allow other components)",

0 commit comments

Comments
 (0)