Skip to content

Commit

Permalink
Improve RadzenScheduler rendering algorithm to handle more cases.
Browse files Browse the repository at this point in the history
  • Loading branch information
akorchev committed Jun 19, 2023
1 parent bfaa5a9 commit 650f2ca
Showing 1 changed file with 62 additions and 75 deletions.
137 changes: 62 additions & 75 deletions Radzen.Blazor/Rendering/DaySlotEvents.razor
Original file line number Diff line number Diff line change
@@ -1,45 +1,13 @@
@using Radzen.Blazor

<div class="rz-events">
@{
var eventGroups = AppointmentGroups();
var lefts = new Dictionary<AppointmentData, double>();
}
@for (var date = StartDate; date < EndDate; date = date.AddMinutes(MinutesPerSlot))
{
var start = date;
var end = start.AddMinutes(MinutesPerSlot);

var appointments = AppointmentsInSlot(start, end);
var existingLefts = ExistingLefts(lefts, appointments);

@foreach (var item in appointments)
@foreach (var kvp in Layout())
{
var width = 90.0 / appointments.Max(data => eventGroups[Appointments.IndexOf(data)]);

if (!lefts.TryGetValue(item, out var left))
{
left = DetermineLeft(existingLefts, width);
lefts.Add(item, left);
existingLefts.Add(left);
}

var eventStart = item.Start < StartDate ? StartDate : item.Start;
var eventEnd = item.End > EndDate ? EndDate : item.End;
var length = eventStart.Subtract(StartDate).TotalMinutes / MinutesPerSlot;
var top = 1.5 * length;
var height = Math.Max(1.5, 1.5 * eventEnd.Subtract(eventStart).TotalHours * (60 / MinutesPerSlot));

@if (item.Start >= start && item.Start <= end)
{
<Appointment Data=@item Top=@top Left=@left Width=@width Height=@height Click=@OnAppointmentSelect />
}
else if (date == StartDate)
{
<Appointment Data=@item Top=@top Left=@left Width=@width Height=@height Click=@OnAppointmentSelect />
}
var appointment = kvp.Key;
var rectangle = kvp.Value;
<Appointment Data=@appointment Top=@rectangle.Top Left=@rectangle.Left Width=@rectangle.Width
Height=@rectangle.Height Click=@OnAppointmentSelect />
}
}
</div>

@code {
Expand All @@ -63,69 +31,88 @@
await Scheduler.SelectAppointment(data);
}

private AppointmentData[] AppointmentsInSlot(DateTime start, DateTime end)
private IList<AppointmentData> Overlapping(IList<AppointmentData> appointments)
{
if (Appointments == null)
if (appointments.Count <= 1)
{
return Array.Empty<AppointmentData>();
return appointments;
}

return Appointments.Where(item => Scheduler.IsAppointmentInRange(item, start, end)).OrderBy(item => item.Start).ThenByDescending(item => item.End).ToArray();
return appointments.Where(a => appointments.Any(b => a != b && a.Start < b.End && a.End > b.Start)).ToList();
}

double DetermineLeft(HashSet<double> existingLefts, double width)
private List<IList<AppointmentData>> PartitionAppointments()
{
double left = 0;

while (existingLefts.Contains(left))
{
left += width;
}

return left;
}
var appointments = Scheduler.GetAppointmentsInRange(StartDate, EndDate)
.OrderBy(e => e.Start)
.ThenByDescending(e => e.End - e.Start)
.ToArray();

HashSet<double> ExistingLefts(IDictionary<AppointmentData, double> lefts, IEnumerable<AppointmentData> appointments)
{
var existingLefts = new HashSet<double>();
var partitions = new List<List<AppointmentData>>();

foreach (var appointment in appointments)
{
if (lefts.TryGetValue(appointment, out var existingLeft))
var slotsOccupied = (int)Math.Ceiling((appointment.End - appointment.Start).TotalMinutes / MinutesPerSlot);

for (var i = 0; i < slotsOccupied; i++)
{
existingLefts.Add(existingLeft);
var partitionIndex = (int)(appointment.Start - appointments[0].Start).TotalMinutes / MinutesPerSlot + i;

while (partitions.Count <= partitionIndex)
{
partitions.Add(new List<AppointmentData>());
}

partitions[partitionIndex].Add(appointment);
}
}

return existingLefts;
return partitions.Select(Overlapping).ToList();
}
private IDictionary<int, int> AppointmentGroups()

private IDictionary<AppointmentData, Rect> Layout()
{
var groups = new Dictionary<int, int>();
var partitions = PartitionAppointments();

for (var index = 0; index < Appointments.Count(); index++)
{
groups[index] = 0;
}
var rectangles = new Dictionary<AppointmentData, Rect>();

for (var date = StartDate; date < EndDate; date = date.AddMinutes(MinutesPerSlot))
foreach (var appointments in partitions)
{
var start = date;
var end = start.AddMinutes(MinutesPerSlot);
var previous = new Rect();

var appointments = AppointmentsInSlot(start, end);

foreach (var item in appointments)
foreach (var appointment in appointments)
{
var index = Appointments.IndexOf(item);

var count = groups[index];

groups[index] = Math.Max(appointments.Length, count);
if (!rectangles.TryGetValue(appointment, out var rectangle))
{
var width = GetAppointmentWidth(partitions, appointment);
var start = appointment.Start < StartDate ? StartDate : appointment.Start;
var eventEnd = appointment.End > EndDate ? EndDate : appointment.End;
var length = start.Subtract(StartDate).TotalMinutes / MinutesPerSlot;
var top = 1.5 * length;
var height = Math.Max(1.5, 1.5 * eventEnd.Subtract(start).TotalHours * (60 / MinutesPerSlot));

rectangle = new Rect();

rectangle.Left = previous.Left + previous.Width;
rectangle.Width = previous.Width == 0 ? width : previous.Width;
rectangle.Top = top;
rectangle.Height = height;

rectangles[appointment] = rectangle;
}

previous = rectangle;
}
}

return groups;
return rectangles;
}

private static double GetAppointmentWidth(IList<IList<AppointmentData>> partitions, AppointmentData appointment)
{
var count = partitions.Where(appointments => appointments.Contains(appointment))
.Max(appointments => appointments.Count());

return 90 / Math.Max(count, 1);
}
}

0 comments on commit 650f2ca

Please sign in to comment.