react-hook-calendar / react-hook-calendar

React components and hooks to build beautiful calendar views
https://react-hook-calendar.netlify.app/
MIT License
30 stars 2 forks source link

How to handle overlaps? #7

Open isBatak opened 2 years ago

isBatak commented 2 years ago

What if two events starts at the same time? How to show them one beside another? Something like this https://virtual.2020.emnlp.org/schedule.html

Codesandbox https://codesandbox.io/s/chakra-ui-calendar-hook-bx4bem?file=/src/index.tsx

hendrikniemann commented 2 years ago

Hello 👋🏻

This is indeed a problem, that the library currently does not address. In fact, the whole problem goes against some design decisions of the library. If you want to have overlapping appointments you have to know about other appointments during rendering. This means, with the current design it would be the responsibility of the library user to handle this case. The cool thing though is that this library is very flexible and unopinionated.

I think it would be possible to do this while rendering the list of appointments. I have forked your sandbox to allow two appointments to overlap (you test this by changing the start date of the second apointment back end forth): https://codesandbox.io/s/chakra-ui-calendar-hook-forked-2798d9?file=/src/index.tsx

First, I allow outside styling of the appointment by forwarding BoxProps. This is a bit of a technicallity:

function Appointment({
  start,
  end,
  title,
  color,
  ...boxProps
}: {
  start: Date;
  end: Date;
  title: string;
  color: string;
} & BoxProps) {

Then, the interesting part is detecting if two appointments overlap. We can use areIntervalsOverlapping to find out if there are more appointments within the time and the position in this array. Then I apply conditional styling to the appointments.

{appointments.map((appointment) => {
  const overlapping = appointments.filter((other) =>
    areIntervalsOverlapping(other, appointment)
  );
  const index = overlapping.indexOf(appointment);
  return (
    <Appointment
      key={appointment.title}
      {...appointment}
      w={overlapping.length > 1 ? "50%" : "100%"}
      ml={index === 1 ? "50%" : "0"}
    />
  );
})}

Now, if more appointments could overlap at once it becomes much trickier. We will probably have to track the indent of all appointments in a variable while iterating over the appointments.