mirror of
https://github.com/radzenhq/radzen-blazor.git
synced 2026-02-04 05:35:44 +00:00
773 lines
28 KiB
C#
773 lines
28 KiB
C#
using Microsoft.AspNetCore.Components;
|
|
using Microsoft.JSInterop;
|
|
using Radzen.Blazor.Rendering;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Radzen.Blazor
|
|
{
|
|
/// <summary>
|
|
/// A scheduler component for displaying and managing calendar appointments in multiple view types (day, week, month, year).
|
|
/// RadzenScheduler provides a rich calendar interface with drag-and-drop, inline editing, recurring events, and customizable views.
|
|
/// Displays time-based events in various calendar views, ideal for appointment booking, event calendars, resource scheduling, or any time-based data visualization.
|
|
/// Features multiple views (Day, Week, Month, Year Planner, Year Timeline), drag & drop to move appointments between time slots, resize to adjust appointment duration by dragging edges,
|
|
/// inline editing to create and edit appointments directly in the calendar, tooltips for quick info on hover, customizable appointment templates,
|
|
/// support for all-day and multi-day events, and timezone-aware appointments.
|
|
/// Define data properties using StartProperty, EndProperty, and TextProperty. Add view components (RadzenDayView, RadzenWeekView, RadzenMonthView) as child content.
|
|
/// </summary>
|
|
/// <typeparam name="TItem">The type of appointment data items. Must have DateTime properties for start/end times and a string property for text.</typeparam>
|
|
/// <example>
|
|
/// Basic scheduler with month view:
|
|
/// <code>
|
|
/// <RadzenScheduler Data=@appointments TItem="Appointment" StartProperty="Start" EndProperty="End" TextProperty="Title">
|
|
/// <RadzenMonthView />
|
|
/// <RadzenWeekView />
|
|
/// <RadzenDayView />
|
|
/// </RadzenScheduler>
|
|
/// @code {
|
|
/// class Appointment
|
|
/// {
|
|
/// public DateTime Start { get; set; }
|
|
/// public DateTime End { get; set; }
|
|
/// public string Title { get; set; }
|
|
/// }
|
|
/// List<Appointment> appointments = new();
|
|
/// }
|
|
/// </code>
|
|
/// Scheduler with editing and custom template:
|
|
/// <code>
|
|
/// <RadzenScheduler Data=@appointments TItem="Appointment"
|
|
/// StartProperty="Start" EndProperty="End" TextProperty="Title"
|
|
/// SlotSelect=@OnSlotSelect AppointmentSelect=@OnAppointmentSelect>
|
|
/// <Template Context="appointment">
|
|
/// <strong>@appointment.Title</strong>
|
|
/// <div>@appointment.Description</div>
|
|
/// </Template>
|
|
/// <RadzenWeekView />
|
|
/// </RadzenScheduler>
|
|
/// </code>
|
|
/// </example>
|
|
public partial class RadzenScheduler<TItem> : RadzenComponent, IScheduler
|
|
{
|
|
/// <summary>
|
|
/// Gets or sets the child content of the scheduler. Use to specify what views to render.
|
|
/// </summary>
|
|
/// <value>The child content.</value>
|
|
[Parameter]
|
|
public RenderFragment? ChildContent { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the template used to render appointments.
|
|
/// </summary>
|
|
/// <example>
|
|
/// <code>
|
|
/// <RadzenScheduler Data="@data" TItem="DataItem" StartProperty="Start" EndProperty="End" TextProperty="Text">
|
|
/// <Template Context="data">
|
|
/// <strong>@data.Text</strong>
|
|
/// </Template>
|
|
/// <ChildContent>
|
|
/// <RadzenMonthView />
|
|
/// </ChildContent>
|
|
/// </RadzenScheduler>
|
|
/// </code>
|
|
/// </example>
|
|
/// <value>The template.</value>
|
|
[Parameter]
|
|
public RenderFragment<TItem>? Template { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the additional content to be rendered in place of the default navigation buttons in the scheduler.
|
|
/// This property allows for complete customization of the navigation controls, replacing the native date navigation buttons (such as year, month, and day) with user-defined content or buttons.
|
|
/// Use this to add custom controls or interactive elements that better suit your application's requirements.
|
|
/// This requires that the <c>ShowHeader</c> parameter to be set to true (enabled by default).
|
|
/// </summary>
|
|
/// <value>The custom navigation template to replace default navigation buttons.</value>
|
|
[Parameter]
|
|
public RenderFragment? NavigationTemplate { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
/// Gets or sets the data of RadzenScheduler. It will display an appointment for every item of the collection which is within the current view date range.
|
|
/// </summary>
|
|
/// <value>The data.</value>
|
|
[Parameter]
|
|
public IEnumerable<TItem>? Data { get; set; }
|
|
|
|
/// <summary>
|
|
/// Specifies the property of <typeparamref name="TItem" /> which will set <see cref="AppointmentData.Start" />.
|
|
/// </summary>
|
|
/// <value>The name of the property. Must be a <c>DateTime</c> property.</value>
|
|
[Parameter]
|
|
public string? StartProperty { get; set; }
|
|
|
|
/// <summary>
|
|
/// Specifies the property of <typeparamref name="TItem" /> which will set <see cref="AppointmentData.End" />.
|
|
/// </summary>
|
|
/// <value>The name of the property. Must be a <c>DateTime</c> property.</value>
|
|
[Parameter]
|
|
public string? EndProperty { get; set; }
|
|
|
|
private int selectedIndex { get; set; }
|
|
|
|
/// <summary>
|
|
/// Specifies the initially selected view.
|
|
/// </summary>
|
|
/// <value>The index of the selected.</value>
|
|
[Parameter]
|
|
public int SelectedIndex { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the text of the today button. Set to <c>Today</c> by default.
|
|
/// </summary>
|
|
/// <value>The today text.</value>
|
|
[Parameter]
|
|
public string TodayText { get; set; } = "Today";
|
|
|
|
/// <summary>
|
|
/// Gets or sets the text of the next button. Set to <c>Next</c> by default.
|
|
/// </summary>
|
|
/// <value>The next text.</value>
|
|
[Parameter]
|
|
public string NextText { get; set; } = "Next";
|
|
|
|
/// <summary>
|
|
/// Gets or sets the text of the previous button. Set to <c>Previous</c> by default.
|
|
/// </summary>
|
|
/// <value>The previous text.</value>
|
|
[Parameter]
|
|
public string PrevText { get; set; } = "Previous";
|
|
|
|
/// <summary>
|
|
/// Gets or sets the initial date displayed by the selected view. Set to <c>DateTime.Today</c> by default.
|
|
/// </summary>
|
|
/// <value>The date.</value>
|
|
[Parameter]
|
|
public DateTime Date { get; set; } = DateTime.Today;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the current date displayed by the selected view. Initially set to <see cref="Date" />. Changes during navigation.
|
|
/// </summary>
|
|
/// <value>The current date.</value>
|
|
public DateTime CurrentDate { get; set; }
|
|
|
|
/// <summary>
|
|
/// Specifies the property of <typeparamref name="TItem" /> which will set <see cref="AppointmentData.Text" />.
|
|
/// </summary>
|
|
/// <value>The name of the property. Must be a <c>string</c> property.</value>
|
|
[Parameter]
|
|
public string? TextProperty { get; set; }
|
|
|
|
/// <summary>
|
|
/// Specifies whether to Show or Hide the Scheduler Header. Defaults to true />.
|
|
/// </summary>
|
|
/// <value>Show / hide header</value>
|
|
[Parameter]
|
|
public bool ShowHeader { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// A callback that will be invoked when the user clicks a slot in the current view. Commonly used to add new appointments.
|
|
/// </summary>
|
|
/// <example>
|
|
/// <code>
|
|
/// <RadzenScheduler Data=@appointments SlotSelect=@OnSlotSelect>
|
|
/// </RadzenScheduler>
|
|
/// @code {
|
|
/// void OnSlotSelect(SchedulerSlotSelectEventArgs args)
|
|
/// {
|
|
/// }
|
|
/// }
|
|
/// </code>
|
|
/// </example>
|
|
[Parameter]
|
|
public EventCallback<SchedulerSlotSelectEventArgs> SlotSelect { get; set; }
|
|
|
|
/// <summary>
|
|
/// A callback that will be invoked when the user clicks the Today button.
|
|
/// </summary>
|
|
/// <example>
|
|
/// <code>
|
|
/// <RadzenScheduler Data=@appointments TodaySelect=@OnTodaySelect>
|
|
/// </RadzenScheduler>
|
|
/// @code {
|
|
/// void OnTodaySelect(SchedulerTodaySelectEventArgs args)
|
|
/// {
|
|
/// args.Today = DateTime.Today.AddDays(1);
|
|
/// }
|
|
/// }
|
|
/// </code>
|
|
/// </example>
|
|
[Parameter]
|
|
public EventCallback<SchedulerTodaySelectEventArgs> TodaySelect { get; set; }
|
|
|
|
/// <summary>
|
|
/// A callback that will be invoked when the user clicks a month header button.
|
|
/// </summary>
|
|
/// <example>
|
|
/// <code>
|
|
/// <RadzenScheduler Data=@appointments MonthSelect=@OnMonthSelect>
|
|
/// </RadzenScheduler>
|
|
/// @code {
|
|
/// void OnMonthSelect(SchedulerMonthSelectEventArgs args)
|
|
/// {
|
|
/// var selectedMonth = args.MonthStart.Month;
|
|
/// }
|
|
/// }
|
|
/// </code>
|
|
/// </example>
|
|
[Parameter]
|
|
public EventCallback<SchedulerMonthSelectEventArgs> MonthSelect { get; set; }
|
|
|
|
/// <summary>
|
|
/// A callback that will be invoked when the user clicks a day header button or the day number in a MonthView.
|
|
/// </summary>
|
|
/// <example>
|
|
/// <code>
|
|
/// <RadzenScheduler Data=@appointments DaySelect=@OnDaySelect>
|
|
/// </RadzenScheduler>
|
|
/// @code {
|
|
/// void OnDaySelect(SchedulerDaySelectEventArgs args)
|
|
/// {
|
|
/// var selectedDay = args.Day;
|
|
/// }
|
|
/// }
|
|
/// </code>
|
|
/// </example>
|
|
[Parameter]
|
|
public EventCallback<SchedulerDaySelectEventArgs> DaySelect { get; set; }
|
|
|
|
/// <summary>
|
|
/// A callback that will be invoked when the user clicks an appointment in the current view. Commonly used to edit existing appointments.
|
|
/// </summary>
|
|
/// <example>
|
|
/// <code>
|
|
/// <RadzenScheduler Data=@appointments AppointmentSelect=@OnAppointmentSelect>
|
|
/// </RadzenScheduler>
|
|
/// @code {
|
|
/// void OnAppointmentSelect(SchedulerAppointmentSelectEventArgs<TItem> args)
|
|
/// {
|
|
/// }
|
|
/// }
|
|
/// </code>
|
|
/// </example>
|
|
[Parameter]
|
|
public EventCallback<SchedulerAppointmentSelectEventArgs<TItem>> AppointmentSelect { get; set; }
|
|
|
|
/// <summary>
|
|
/// A callback that will be invoked when the user moves the mouse over an appointment in the current view.
|
|
/// </summary>
|
|
[Parameter]
|
|
public EventCallback<SchedulerAppointmentMouseEventArgs<TItem>> AppointmentMouseEnter { get; set; }
|
|
|
|
/// <summary>
|
|
/// A callback that will be invoked when the user moves the mouse out of an appointment in the current view.
|
|
/// </summary>
|
|
[Parameter]
|
|
public EventCallback<SchedulerAppointmentMouseEventArgs<TItem>> AppointmentMouseLeave { get; set; }
|
|
|
|
/// <summary>
|
|
/// A callback that will be invoked when the user clicks the more text in the current view. Commonly used to view additional appointments.
|
|
/// Invoke the <see cref="SchedulerMoreSelectEventArgs.PreventDefault"/> method to prevent the default action (showing the additional appointments).
|
|
/// </summary>
|
|
/// <example>
|
|
/// <code>
|
|
/// <RadzenScheduler Data=@appointments MoreSelect=@OnMoreSelect>
|
|
/// </RadzenScheduler>
|
|
/// @code {
|
|
/// void OnMoreSelect(SchedulerMoreSelectEventArgs args)
|
|
/// {
|
|
/// args.PreventDefault();
|
|
/// }
|
|
/// }
|
|
/// </code>
|
|
/// </example>
|
|
[Parameter]
|
|
public EventCallback<SchedulerMoreSelectEventArgs> MoreSelect { get; set; }
|
|
|
|
/// <summary>
|
|
/// An action that will be invoked when the current view renders an appointment. Never call <c>StateHasChanged</c> when handling AppointmentRender.
|
|
/// </summary>
|
|
/// <example>
|
|
/// <code>
|
|
/// <RadzenScheduler Data=@appointments AppointmentRender=@OnAppointmentRendert>
|
|
/// </RadzenScheduler>
|
|
/// @code {
|
|
/// void OnAppintmentRender(SchedulerAppointmentRenderEventArgs<TItem> args)
|
|
/// {
|
|
/// if (args.Data.Text == "Birthday")
|
|
/// {
|
|
/// args.Attributes["style"] = "color: red;"
|
|
/// }
|
|
///. }
|
|
/// }
|
|
/// </code>
|
|
/// </example>
|
|
[Parameter]
|
|
public Action<SchedulerAppointmentRenderEventArgs<TItem>>? AppointmentRender { get; set; }
|
|
|
|
/// <summary>
|
|
/// An action that will be invoked when the current view renders an slot. Never call <c>StateHasChanged</c> when handling SlotRender.
|
|
/// </summary>
|
|
/// <example>
|
|
/// <code>
|
|
/// <RadzenScheduler Data=@appointments SlotRender=@OnSlotRender>
|
|
/// </RadzenScheduler>
|
|
/// @code {
|
|
/// void OnSlotRender(SchedulerSlotRenderEventArgs args)
|
|
/// {
|
|
/// if (args.View.Text == "Month" && args.Start.Date == DateTime.Today)
|
|
/// {
|
|
/// args.Attributes["style"] = "background: red;";
|
|
/// }
|
|
/// }
|
|
/// }
|
|
/// </code>
|
|
/// </example>
|
|
[Parameter]
|
|
public Action<SchedulerSlotRenderEventArgs>? SlotRender { get; set; }
|
|
|
|
/// <summary>
|
|
/// A callback that will be invoked when the scheduler needs data for the current view. Commonly used to filter the
|
|
/// data assigned to <see cref="Data" />.
|
|
/// </summary>
|
|
[Parameter]
|
|
public EventCallback<SchedulerLoadDataEventArgs> LoadData { get; set; }
|
|
|
|
/// <summary>
|
|
/// A callback that will be invoked when an appointment is being dragged and then dropped on a different slot.
|
|
/// Commonly used to change it to a different timeslot.
|
|
/// </summary>
|
|
/// <example>
|
|
/// <code>
|
|
/// <RadzenScheduler Data=@appointments AppointmentMove=@OnAppointmentMove>
|
|
/// </RadzenScheduler>
|
|
/// @code {
|
|
/// async Task OnAppointmentMove(SchedulerAppointmentMoveEventArgs moved)
|
|
/// {
|
|
/// var draggedAppointment = appointments.SingleOrDefault(x => x == (Appointment)moved.Appointment.Data);
|
|
/// if (draggedAppointment != null)
|
|
/// {
|
|
/// draggedAppointment.Start = draggedAppointment.Start + moved.TimeSpan;
|
|
/// draggedAppointment.End = draggedAppointment.End + moved.TimeSpan;
|
|
/// await scheduler.Reload();
|
|
/// }
|
|
/// }
|
|
/// }
|
|
/// </code>
|
|
/// </example>
|
|
/// <value></value>
|
|
[Parameter]
|
|
public EventCallback<SchedulerAppointmentMoveEventArgs> AppointmentMove { get; set; }
|
|
|
|
IList<ISchedulerView> Views { get; set; } = new List<ISchedulerView>();
|
|
|
|
/// <summary>
|
|
/// Gets the SelectedView.
|
|
/// </summary>
|
|
public ISchedulerView? SelectedView
|
|
{
|
|
get
|
|
{
|
|
return Views.ElementAtOrDefault(selectedIndex);
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IDictionary<string, object> GetAppointmentAttributes(AppointmentData item)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(item);
|
|
|
|
var appointmentData = item.Data is TItem typedData ? typedData : default!;
|
|
var args = new SchedulerAppointmentRenderEventArgs<TItem> { Data = appointmentData, Start = item.Start, End = item.End };
|
|
|
|
AppointmentRender?.Invoke(args);
|
|
|
|
return args.Attributes;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IDictionary<string, object> GetSlotAttributes(DateTime start, DateTime end, Func<IEnumerable<AppointmentData>> getAppointments)
|
|
{
|
|
var args = new SchedulerSlotRenderEventArgs { Start = start, End = end, getAppointments = getAppointments, View = SelectedView };
|
|
|
|
SlotRender?.Invoke(args);
|
|
|
|
return args.Attributes;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public RenderFragment RenderAppointment(AppointmentData item)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(item);
|
|
|
|
if (Template != null)
|
|
{
|
|
var context = item.Data is TItem templateData ? templateData : default!;
|
|
return Template(context);
|
|
}
|
|
|
|
return builder => builder.AddContent(0, item.Text);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task SelectSlot(DateTime start, DateTime end)
|
|
{
|
|
await SlotSelect.InvokeAsync(new SchedulerSlotSelectEventArgs { Start = start, End = end, Appointments = Array.Empty<AppointmentData>(), View = SelectedView });
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<bool> SelectSlot(DateTime start, DateTime end, IEnumerable<AppointmentData> appointments)
|
|
{
|
|
var args = new SchedulerSlotSelectEventArgs { Start = start, End = end, Appointments = appointments, View = SelectedView };
|
|
await SlotSelect.InvokeAsync(args);
|
|
|
|
return args.IsDefaultPrevented;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task SelectMonth(DateTime monthStart, IEnumerable<AppointmentData> appointments)
|
|
{
|
|
await MonthSelect.InvokeAsync(new SchedulerMonthSelectEventArgs { MonthStart = monthStart, Appointments = appointments, View = SelectedView });
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task SelectDay(DateTime day, IEnumerable<AppointmentData> appointments)
|
|
{
|
|
await DaySelect.InvokeAsync(new SchedulerDaySelectEventArgs { Day = day, Appointments = appointments, View = SelectedView });
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<bool> SelectMore(DateTime start, DateTime end, IEnumerable<AppointmentData> appointments)
|
|
{
|
|
var args = new SchedulerMoreSelectEventArgs { Start = start, End = end, Appointments = appointments, View = SelectedView };
|
|
await MoreSelect.InvokeAsync(args);
|
|
|
|
return args.IsDefaultPrevented;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task SelectAppointment(AppointmentData data)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(data);
|
|
|
|
var appointmentData = data.Data is TItem typedData ? typedData : default!;
|
|
await AppointmentSelect.InvokeAsync(new SchedulerAppointmentSelectEventArgs<TItem> { Start = data.Start, End = data.End, Data = appointmentData });
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task AddView(ISchedulerView view)
|
|
{
|
|
if (!Views.Contains(view))
|
|
{
|
|
Views.Add(view);
|
|
|
|
if (SelectedView == view)
|
|
{
|
|
await InvokeLoadData();
|
|
}
|
|
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Selects the specified <see cref="ISchedulerView"/>. The view must already be present in this scheduler.
|
|
/// If the specified view is already selected, no action will be performed.
|
|
/// </summary>
|
|
/// <param name="view">The <see cref="ISchedulerView"/> to select</param>
|
|
public async Task SelectView(ISchedulerView view)
|
|
{
|
|
var viewIndex = Views.IndexOf(view);
|
|
if (viewIndex == -1)
|
|
return;
|
|
|
|
if (SelectedView == view)
|
|
return;
|
|
|
|
selectedIndex = viewIndex;
|
|
|
|
await InvokeLoadData();
|
|
|
|
StateHasChanged();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Causes the current scheduler view to render. Enumerates the items of <see cref="Data" /> and creates instances of <see cref="AppointmentData" /> to
|
|
/// display in the current view. Use it when <see cref="Data" /> has changed.
|
|
/// </summary>
|
|
public async Task Reload()
|
|
{
|
|
appointments = null;
|
|
|
|
await InvokeLoadData();
|
|
|
|
StateHasChanged();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool IsSelected(ISchedulerView view)
|
|
{
|
|
return selectedIndex == Views.IndexOf(view);
|
|
}
|
|
|
|
async Task OnChangeView(ISchedulerView view)
|
|
{
|
|
selectedIndex = Views.IndexOf(view);
|
|
|
|
await InvokeLoadData();
|
|
}
|
|
|
|
async Task OnPrev()
|
|
{
|
|
if (SelectedView != null)
|
|
{
|
|
CurrentDate = SelectedView.Prev();
|
|
await InvokeLoadData();
|
|
}
|
|
}
|
|
|
|
async Task OnToday()
|
|
{
|
|
var args = new SchedulerTodaySelectEventArgs { Today = DateTime.Now.Date };
|
|
|
|
await TodaySelect.InvokeAsync(args);
|
|
|
|
CurrentDate = args.Today;
|
|
|
|
await InvokeLoadData();
|
|
}
|
|
|
|
async Task OnNext()
|
|
{
|
|
if (SelectedView != null)
|
|
{
|
|
CurrentDate = SelectedView.Next();
|
|
await InvokeLoadData();
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void RemoveView(ISchedulerView view)
|
|
{
|
|
Views.Remove(view);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override void OnInitialized()
|
|
{
|
|
CurrentDate = Date;
|
|
selectedIndex = SelectedIndex;
|
|
|
|
double height = 0;
|
|
|
|
var style = CurrentStyle;
|
|
|
|
if (style.TryGetValue("height", out var pixelHeight))
|
|
{
|
|
if (pixelHeight.EndsWith("px", StringComparison.Ordinal))
|
|
{
|
|
height = Convert.ToDouble(pixelHeight.TrimEnd("px".ToCharArray()), CultureInfo.InvariantCulture);
|
|
}
|
|
}
|
|
|
|
if (height > 0)
|
|
{
|
|
heightIsSet = true;
|
|
|
|
Height = height;
|
|
}
|
|
}
|
|
|
|
IEnumerable<AppointmentData>? appointments;
|
|
DateTime rangeStart;
|
|
DateTime rangeEnd;
|
|
Func<TItem, DateTime>? startGetter;
|
|
Func<TItem, DateTime>? endGetter;
|
|
Func<TItem, string>? textGetter;
|
|
|
|
/// <inheritdoc />
|
|
public override async Task SetParametersAsync(ParameterView parameters)
|
|
{
|
|
var needsReload = false;
|
|
|
|
if (parameters.DidParameterChange(nameof(Date), Date))
|
|
{
|
|
CurrentDate = parameters.GetValueOrDefault<DateTime>(nameof(Date));
|
|
needsReload = true;
|
|
}
|
|
|
|
if (parameters.DidParameterChange(nameof(SelectedIndex), SelectedIndex))
|
|
{
|
|
selectedIndex = parameters.GetValueOrDefault<int>(nameof(SelectedIndex));
|
|
needsReload = true;
|
|
}
|
|
|
|
if (parameters.DidParameterChange(nameof(Data), Data))
|
|
{
|
|
appointments = null;
|
|
}
|
|
|
|
if (parameters.DidParameterChange(nameof(StartProperty), StartProperty))
|
|
{
|
|
startGetter = PropertyAccess.Getter<TItem, DateTime>(parameters.GetValueOrDefault<string>(nameof(StartProperty))!);
|
|
}
|
|
|
|
if (parameters.DidParameterChange(nameof(EndProperty), EndProperty))
|
|
{
|
|
endGetter = PropertyAccess.Getter<TItem, DateTime>(parameters.GetValueOrDefault<string>(nameof(EndProperty))!);
|
|
}
|
|
|
|
if (parameters.DidParameterChange(nameof(TextProperty), TextProperty))
|
|
{
|
|
textGetter = PropertyAccess.Getter<TItem, string>(parameters.GetValueOrDefault<string>(nameof(TextProperty))!);
|
|
}
|
|
|
|
await base.SetParametersAsync(parameters);
|
|
|
|
if (needsReload)
|
|
{
|
|
await InvokeLoadData();
|
|
}
|
|
}
|
|
|
|
private async Task InvokeLoadData()
|
|
{
|
|
if (SelectedView != null)
|
|
{
|
|
await LoadData.InvokeAsync(new SchedulerLoadDataEventArgs { Start = SelectedView.StartDate, End = SelectedView.EndDate, View = SelectedView });
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool IsAppointmentInRange(AppointmentData item, DateTime start, DateTime end)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(item);
|
|
|
|
if (item.Start == item.End && item.Start >= start && item.End < end)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return item.End > start && item.Start < end;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IEnumerable<AppointmentData> GetAppointmentsInRange(DateTime start, DateTime end)
|
|
{
|
|
if (Data == null)
|
|
{
|
|
return [];
|
|
}
|
|
|
|
if (start == rangeStart && end == rangeEnd && appointments != null)
|
|
{
|
|
return appointments;
|
|
}
|
|
|
|
rangeStart = start;
|
|
rangeEnd = end;
|
|
|
|
appointments = Data.AsQueryable()
|
|
.Where([
|
|
new FilterDescriptor { Property = EndProperty, FilterValue = start, FilterOperator = FilterOperator.GreaterThanOrEquals },
|
|
new FilterDescriptor { Property = StartProperty, FilterValue = end, FilterOperator = FilterOperator.LessThanOrEquals }
|
|
], LogicalFilterOperator.And, FilterCaseSensitivity.Default)
|
|
.ToList()
|
|
.Select(item => new AppointmentData { Start = startGetter!(item), End = endGetter!(item), Text = textGetter!(item), Data = item });
|
|
|
|
return appointments;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
{
|
|
await base.OnAfterRenderAsync(firstRender);
|
|
|
|
if (firstRender && JSRuntime != null)
|
|
{
|
|
var rect = await JSRuntime.InvokeAsync<Rect>("Radzen.createScheduler", Element, Reference);
|
|
|
|
if (!heightIsSet)
|
|
{
|
|
heightIsSet = true;
|
|
Resize(rect.Width, rect.Height);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invoked from client-side via interop when the scheduler size changes.
|
|
/// </summary>
|
|
/// <param name="width">The width.</param>
|
|
/// <param name="height">The height.</param>
|
|
[JSInvokable]
|
|
public void Resize(double width, double height)
|
|
{
|
|
var stateHasChanged = false;
|
|
|
|
if (height != Height)
|
|
{
|
|
Height = height;
|
|
stateHasChanged = true;
|
|
}
|
|
|
|
if (stateHasChanged)
|
|
{
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void Dispose()
|
|
{
|
|
base.Dispose();
|
|
|
|
if (IsJSRuntimeAvailable && JSRuntime != null)
|
|
{
|
|
JSRuntime.InvokeVoid("Radzen.destroyScheduler", Element);
|
|
}
|
|
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
private bool heightIsSet;
|
|
private double Height { get; set; } = 400; // Default height set from theme.
|
|
double IScheduler.Height
|
|
{
|
|
get
|
|
{
|
|
return Height;
|
|
}
|
|
}
|
|
/// <inheritdoc />
|
|
protected override string GetComponentCssClass()
|
|
{
|
|
return $"rz-scheduler";
|
|
}
|
|
|
|
async Task IScheduler.MouseEnterAppointment(ElementReference reference, AppointmentData data)
|
|
{
|
|
var argsData = data.Data is TItem typed ? typed : default!;
|
|
await AppointmentMouseEnter.InvokeAsync(new SchedulerAppointmentMouseEventArgs<TItem> { Element = reference, Data = argsData });
|
|
}
|
|
|
|
async Task IScheduler.MouseLeaveAppointment(ElementReference reference, AppointmentData data)
|
|
{
|
|
var argsData = data.Data is TItem typed ? typed : default!;
|
|
await AppointmentMouseLeave.InvokeAsync(new SchedulerAppointmentMouseEventArgs<TItem> { Element = reference, Data = argsData });
|
|
}
|
|
|
|
bool IScheduler.HasMouseEnterAppointmentDelegate()
|
|
{
|
|
return AppointmentMouseEnter.HasDelegate;
|
|
}
|
|
|
|
bool IScheduler.HasAppointmentMoveDelegate()
|
|
{
|
|
return AppointmentMove.HasDelegate;
|
|
}
|
|
}
|
|
}
|