diff --git a/Heron.MudCalendar.Docs/Pages/Calendar/CalendarPage.razor b/Heron.MudCalendar.Docs/Pages/Calendar/CalendarPage.razor index 34e32a8..cef7d3c 100644 --- a/Heron.MudCalendar.Docs/Pages/Calendar/CalendarPage.razor +++ b/Heron.MudCalendar.Docs/Pages/Calendar/CalendarPage.razor @@ -76,6 +76,7 @@ The CellClicked event will be raised when a calendar cell is clicked. This can be used, for example, to add new events to the calendar. + The ItemClicked event will be raised when an item in the calendar is clicked. diff --git a/Heron.MudCalendar.Docs/Pages/Calendar/Examples/EventsCalendarExample.razor b/Heron.MudCalendar.Docs/Pages/Calendar/Examples/EventsCalendarExample.razor index 0b3117b..74e0137 100644 --- a/Heron.MudCalendar.Docs/Pages/Calendar/Examples/EventsCalendarExample.razor +++ b/Heron.MudCalendar.Docs/Pages/Calendar/Examples/EventsCalendarExample.razor @@ -1,6 +1,6 @@ @namespace Heron.MudCalendar.Docs.Examples - + @code { @@ -9,7 +9,27 @@ private Task CellClicked(DateTime dateTime) { - return DialogService.ShowMessageBox("Click", dateTime.ToString(Thread.CurrentThread.CurrentCulture)); + return DialogService.ShowMessageBox("Cell Clicked", dateTime.ToString(Thread.CurrentThread.CurrentCulture)); } + private Task ItemClicked(CalendarItem item) + { + return DialogService.ShowMessageBox("Item Clicked", item.Text); + } + + private List _events = new() + { + new CalendarItem + { + Start = DateTime.Today.AddHours(10), + End = DateTime.Today.AddHours(11), + Text = "Event today" + }, + new CalendarItem + { + Start = DateTime.Today.AddDays(1).AddHours(11), + End = DateTime.Today.AddDays(1).AddHours(12.5), + Text = "Event tomorrow" + } + }; } \ No newline at end of file diff --git a/Heron.MudCalendar.UnitTests.Viewer/TestComponents/Calendar/CalendarItemClickTest.razor b/Heron.MudCalendar.UnitTests.Viewer/TestComponents/Calendar/CalendarItemClickTest.razor new file mode 100644 index 0000000..b08c333 --- /dev/null +++ b/Heron.MudCalendar.UnitTests.Viewer/TestComponents/Calendar/CalendarItemClickTest.razor @@ -0,0 +1,25 @@ + + + +@code { + public static string __description__ = "Calendar Item Click Test"; + + private string _value = string.Empty; + + private MudCalendar _mudCalendar; + + private void TestClick(CalendarItem item) + { + _value = string.Concat(item.Text, "_", _mudCalendar.View.ToString()); + } + + private List _events = new() + { + new CalendarItem + { + Start = DateTime.Today.AddHours(10), + End = DateTime.Today.AddHours(11), + Text = "Event" + } + }; +} \ No newline at end of file diff --git a/Heron.MudCalendar.UnitTests/Components/CalendarTests.cs b/Heron.MudCalendar.UnitTests/Components/CalendarTests.cs index 0e6303a..7afaef2 100644 --- a/Heron.MudCalendar.UnitTests/Components/CalendarTests.cs +++ b/Heron.MudCalendar.UnitTests/Components/CalendarTests.cs @@ -136,6 +136,28 @@ public void CellClick() comp.Find("div.mud-cal-week-layer a").Click(); textField.Instance.Text.Should().Be("8"); } + + [Test] + public void ItemsClick() + { + var cut = Context.RenderComponent(); + var comp = cut.FindComponent(); + var textField = cut.FindComponent>(); + + // Month View + comp.Find("div.mud-cal-cell-template").Click(); + textField.Instance.Text.Should().Be("Event_Month"); + + // Week View + comp.SetParam(x => x.View, CalendarView.Week); + comp.Find("div.mud-cal-cell-template").Click(); + textField.Instance.Text.Should().Be("Event_Week"); + + // Day View + comp.SetParam(x => x.View, CalendarView.Day); + comp.Find("div.mud-cal-cell-template").Click(); + textField.Instance.Text.Should().Be("Event_Day"); + } [Test] public void EnsureAllDays() diff --git a/Heron.MudCalendar/Base/DayWeekViewBase.razor b/Heron.MudCalendar/Base/DayWeekViewBase.razor index 6857fc6..870198e 100644 --- a/Heron.MudCalendar/Base/DayWeekViewBase.razor +++ b/Heron.MudCalendar/Base/DayWeekViewBase.razor @@ -111,7 +111,7 @@
@if (cell.Today && Calendar.ShowCurrentTime) { -
+
}
@@ -170,9 +170,19 @@ var positions = CalcPositions(cell.Items, DateOnly.FromDateTime(cell.Date)); foreach (var position in positions) { -
- @CellTemplate?.Invoke(position.Item) -
+ @if (Calendar.ItemClicked.HasDelegate) + { +
+ @CellTemplate?.Invoke(position.Item) +
+ } + else + { +
+ @CellTemplate?.Invoke(position.Item) +
+ } } }; + } \ No newline at end of file diff --git a/Heron.MudCalendar/Base/DayWeekViewBase.razor.cs b/Heron.MudCalendar/Base/DayWeekViewBase.razor.cs index dad520a..92a497e 100644 --- a/Heron.MudCalendar/Base/DayWeekViewBase.razor.cs +++ b/Heron.MudCalendar/Base/DayWeekViewBase.razor.cs @@ -9,7 +9,7 @@ public abstract partial class DayWeekViewBase : CalendarViewBase, IAsyncDisposab { private ElementReference _scrollDiv; private JsService? _jsService; - + private const int MinutesInDay = 24 * 60; private int PixelsInCell => Calendar.DayCellHeight; @@ -34,7 +34,8 @@ protected override async Task OnAfterRenderAsync(bool firstRender) protected virtual string DayStyle(CalendarCell calendarCell) { return new StyleBuilder() - .AddStyle("border", $"1px solid var(--mud-palette-{Calendar.Color.ToDescriptionString()})", calendarCell.Today && Calendar.HighlightToday) + .AddStyle("border", $"1px solid var(--mud-palette-{Calendar.Color.ToDescriptionString()})", + calendarCell.Today && Calendar.HighlightToday) .Build(); } @@ -45,8 +46,10 @@ protected virtual string EventStyle(ItemPosition position) .AddStyle("top", $"{CalcTop(position)}px") .AddStyle("height", $"{CalcHeight(position)}px") .AddStyle("overflow", "hidden") - .AddStyle("left", (((position.Position / (double)position.Total) - (1.0 / position.Total)) * 100).ToInvariantString() + "%") - .AddStyle("width", (100 / position.Total) + "%" ) + .AddStyle("left", + (((position.Position / (double)position.Total) - (1.0 / position.Total)) * 100).ToInvariantString() + + "%") + .AddStyle("width", (100 / position.Total) + "%") .Build(); } @@ -63,7 +66,8 @@ protected virtual string TimelineStyle() .AddStyle("position", "absolute") .AddStyle("width", "100%") .AddStyle("border", "1px solid var(--mud-palette-grey-default)") - .AddStyle("top", $"{(int)((DateTime.Now.Subtract(DateTime.Today).TotalMinutes / MinutesInDay) * PixelsInDay)}px") + .AddStyle("top", + $"{(int)((DateTime.Now.Subtract(DateTime.Today).TotalMinutes / MinutesInDay) * PixelsInDay)}px") .Build(); } @@ -79,6 +83,16 @@ protected virtual Task OnCellLinkClicked(CalendarCell cell, int row) return Calendar.CellClicked.InvokeAsync(date); } + /// + /// Method invoked when the user clicks on the calendar item. + /// + /// The calendar item that was clicked. + /// + protected virtual Task OnItemClicked(CalendarItem item) + { + return Calendar.ItemClicked.InvokeAsync(item); + } + private int CalcTop(ItemPosition position) { double minutes = 0; @@ -86,6 +100,7 @@ private int CalcTop(ItemPosition position) { minutes = position.Item.Start.Hour * 60 + position.Item.Start.Minute; } + var percent = minutes / MinutesInDay; var top = PixelsInDay * percent; @@ -150,8 +165,9 @@ private static IEnumerable CalcPositions(IEnumerable position.Position = i; } } + if (position.Position == 0) position.Position = overlaps.Count + 1; - + overlaps.Add(position); var maxPosition = overlaps.Max(o => o.Position); foreach (var overlap in overlaps) @@ -159,12 +175,13 @@ private static IEnumerable CalcPositions(IEnumerable overlap.Total = maxPosition; } } - + // Calculate the total overlapping events foreach (var position in positions) { - var max = positions.Where(p => p.Item.Start < (position.Item.End ?? position.Item.Start.AddHours(1)) - && (p.Item.End ?? p.Item.Start.AddHours(1)) > position.Item.Start).Max(p => p.Total); + var max = positions.Where(p => p.Item.Start < (position.Item.End ?? position.Item.Start.AddHours(1)) + && (p.Item.End ?? p.Item.Start.AddHours(1)) > position.Item.Start) + .Max(p => p.Total); position.Total = max; } @@ -178,11 +195,11 @@ protected class ItemPosition public int Total { get; set; } public DateOnly Date { get; set; } } - + public async ValueTask DisposeAsync() { GC.SuppressFinalize(this); - + if (_jsService != null) { await _jsService.DisposeAsync(); diff --git a/Heron.MudCalendar/Components/MonthView.razor b/Heron.MudCalendar/Components/MonthView.razor index c82c23a..ad881a0 100644 --- a/Heron.MudCalendar/Components/MonthView.razor +++ b/Heron.MudCalendar/Components/MonthView.razor @@ -91,7 +91,18 @@ @
@foreach (var item in cell.Items) { - @CellTemplate?.Invoke(item) + @if (Calendar.ItemClicked.HasDelegate) + { +
+ @CellTemplate?.Invoke(item) +
+ } + else + { +
+ @CellTemplate?.Invoke(item) +
+ } }
; diff --git a/Heron.MudCalendar/Components/MonthView.razor.cs b/Heron.MudCalendar/Components/MonthView.razor.cs index 5f4433e..93286a1 100644 --- a/Heron.MudCalendar/Components/MonthView.razor.cs +++ b/Heron.MudCalendar/Components/MonthView.razor.cs @@ -61,6 +61,16 @@ protected virtual Task OnCellLinkClicked(CalendarCell cell) return Calendar.CellClicked.InvokeAsync(cell.Date); } + /// + /// Method invoked when the user clicks on the calendar item. + /// + /// The calendar item that was clicked. + /// + protected virtual Task OnItemClicked(CalendarItem item) + { + return Calendar.ItemClicked.InvokeAsync(item); + } + protected override List BuildCells() { var cells = new List(); diff --git a/Heron.MudCalendar/Components/MudCalendar.razor.cs b/Heron.MudCalendar/Components/MudCalendar.razor.cs index 2bc7fbc..74160d1 100644 --- a/Heron.MudCalendar/Components/MudCalendar.razor.cs +++ b/Heron.MudCalendar/Components/MudCalendar.razor.cs @@ -186,6 +186,12 @@ public partial class MudCalendar : MudComponentBase /// [Parameter] public EventCallback CellClicked { get; set; } + + /// + /// Called when a CalendarItem is clicked. + /// + [Parameter] + public EventCallback ItemClicked { get; set; } private DateTime? PickerDate { diff --git a/Heron.MudCalendar/Styles/_cellTemplate.scss b/Heron.MudCalendar/Styles/_cellTemplate.scss index df08708..1cbf7fe 100644 --- a/Heron.MudCalendar/Styles/_cellTemplate.scss +++ b/Heron.MudCalendar/Styles/_cellTemplate.scss @@ -16,3 +16,11 @@ width: 100%; margin: 0; } + +.mud-cal-clickable { + cursor: pointer; + + .mud-chip { + cursor: inherit; + } +} \ No newline at end of file