Calendar
Since 0.1.0-alpha.20Static server-side rendering: no circuit, no WebAssembly.
A month on the page.
Usage
<Calendar @bind-Value="Model.Day" />
<Calendar @bind-Value="Model.Day" Min="today" Max="lastDay" ShowWeekNumbers />Limits
Min and Max come from [Range], or set them on the field. Days outside them are marked.
Preview
Code
System.ComponentModel.DataAnnotations
<Form Model="Model" FormName="calendar-limits" OnValidSubmit="Save">
<Calendar @bind-Value="Model!.Delivery" Hint="In March 2027." />
<Calendar @bind-Value="Model!.Pickup" Min="(new DateOnly(2027, 3, 8))" Max="(new DateOnly(2027, 3, 19))" Hint="From March 8 to 19." />
<Row><Button Submit>Book</Button></Row>
if (_saved is { } saved)
{
<Alert Status="Status.Success">Booked: delivery saved.Delivery, pickup saved.Pickup.</Alert>
}
</Form>
{
[SupplyParameterFromForm(FormName = "calendar-limits")]
private Booking? Model { get; set; }
// What was saved, not the fields: on an interactive page they follow typing.
private (DateOnly? Delivery, DateOnly Pickup)? _saved;
protected override void OnInitialized() => Model ??= new();
private void Save() => _saved = (Model!.Delivery, Model.Pickup);
public sealed class Booking
{
[Range(typeof(DateOnly), "2027-03-01", "2027-03-31", ErrorMessage = "Pick a day in March 2027.")]
public DateOnly? Delivery { get; set; } = new(2027, 3, 3);
public DateOnly Pickup { get; set; } = new(2027, 3, 10);
}
}Static: The server turns away a date outside the limits, whatever the browser let through.
Disabled days and classes
Use DateDisabled to turn days off and DayClass to give days a class of your own. The server asks about every day in a window, so keep them cheap; DateDisabledWindow sets how far.
Preview
Code
<style>
.pay-day {
font-weight: 600;
box-shadow: inset 0 0 0 1px currentColor;
}
</style>
<Form Model="Model" FormName="calendar-days" OnValidSubmit="Save" Columns="2">
<Calendar @bind-Value="Model!.Visit" DateDisabled="IsWeekend" DateDisabledWindow="TimeSpan.FromDays(90)" Hint="On a weekday, within 90 days." />
<Calendar @bind-Value="Model!.Transfer" DayClass="(day => day.Day == 15 ? "pay-day" : null)" Hint="Paydays are outlined." />
<Row ColSpan="GridSpan.Full"><Button Submit>Save dates</Button></Row>
if (_saved is { } saved)
{
<Alert ColSpan="GridSpan.Full" Status="Status.Success">Saved: visit on saved.</Alert>
}
</Form>
{
[SupplyParameterFromForm(FormName = "calendar-days")]
private Dates? Model { get; set; }
// What was saved, not the field: on an interactive page it follows the choice.
private DateOnly? _saved;
protected override void OnInitialized() => Model ??= new();
private void Save() => _saved = Model!.Visit;
private static bool IsWeekend(DateOnly day) => day.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday;
public sealed class Dates
{
public DateOnly Visit { get; set; } = new(2027, 3, 10);
public DateOnly Transfer { get; set; } = new(2027, 3, 10);
}
}Static: The server turns away a day that's off, whatever the browser let through.
A month or a year
Type shows a page of months, holding the 1st of one, or of years, holding an int.
Preview
Code
<Form Model="Model" FormName="calendar-periods" OnValidSubmit="Save" Grow>
<Calendar @bind-Value="Model!.Starts" Type="DateFieldType.Month" />
<Row><Button Submit>Save</Button></Row>
if (_saved is { } saved)
{
<Alert Status="Status.Success">Starts saved.ToString("MMMM yyyy").</Alert>
}
</Form>
{
[SupplyParameterFromForm(FormName = "calendar-periods")]
private Plan? Model { get; set; }
private DateOnly? _saved;
protected override void OnInitialized() => Model ??= new();
private void Save() => _saved = Model!.Starts;
public sealed class Plan
{
public DateOnly? Starts { get; set; } = new DateOnly(2027, 3, 1);
}
}Culture and weeks
Use Culture for the names and the first day, FirstDayOfWeek for the first day alone, ShowWeekNumbers for a column of weeks and ShowAdjacentDays to leave out other months' days.
Preview
Code
System.Globalization
<Form Model="Model" FormName="calendar-culture" Columns="2">
<Calendar @bind-Value="Model!.Day" Label="German, with week numbers" Culture="(new CultureInfo("de-DE"))" ShowWeekNumbers />
<Calendar @bind-Value="Model!.Other" Label="Monday first, this month only" FirstDayOfWeek="DayOfWeek.Monday" ShowAdjacentDays="false" />
</Form>
{
[SupplyParameterFromForm(FormName = "calendar-culture")]
private Dates? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
public sealed class Dates
{
public DateOnly Day { get; set; } = new(2027, 1, 14);
public DateOnly Other { get; set; } = new(2027, 1, 14);
}
}Sizes, read-only and disabled
Use Size for the field and the month together.
Preview
Code
<Form Model="Model" FormName="calendar-states" Columns="2">
<Calendar @bind-Value="Model!.Small" Label="Small" Size="ControlSize.Sm" />
<Calendar @bind-Value="Model!.Large" Label="Large" Size="ControlSize.Lg" />
<Calendar @bind-Value="Model!.Signed" Label="Signed on" ReadOnly Hint="Read-only: posted, but can't be changed." />
<Calendar @bind-Value="Model!.Renews" Label="Renews on" Disabled Hint="Disabled: not posted." />
</Form>
{
[SupplyParameterFromForm(FormName = "calendar-states")]
private Contract? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
public sealed class Contract
{
public DateOnly Small { get; set; } = new(2027, 5, 12);
public DateOnly Large { get; set; } = new(2027, 5, 12);
public DateOnly Signed { get; set; } = new(2027, 5, 12);
public DateOnly Renews { get; set; } = new(2027, 5, 12);
}
}Details
Type="DateFieldType.Month" is a page of months, holding the 1st of one, and Type="DateFieldType.Year" a page of years, holding an int. DateDisabled, DayClass, ShowWeekNumbers and FirstDayOfWeek throw with these, as they're about days.
<Calendar @bind-Value="Model.Starts" Type="DateFieldType.Month" />Names and the first day of the week follow the page's culture, or Culture. It's always the Gregorian calendar, which is what a date input posts. A browser's date input carries the value, so it posts with the form on a static page and binds on an interactive one. Min and Max stop the month and its days, and are checked again on the server.
DateDisabled turns days off, with a line through them, and is checked again on the server. DayClass gives days a class of your own.
<Calendar @bind-Value="Model.Day" DateDisabled="d => d.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday" />
<Calendar @bind-Value="Model.Day" DayClass="d => holidays.Contains(d) ? "holiday" : null" />The script can't call C#, so the server calls these for every day in a window and writes down the answers, on every render, so keep them cheap. The window is Min to Max, with DateDisabledWindow (about a year) each way from the value or today for an end not set: 730 calls by default. Past the window the server hasn't said what's allowed, so with DateDisabled the month stops there, and with DayClass alone the days have no class.
Mode support
| Feature | Static | Interactive |
|---|---|---|
| A month grid that follows the page's culture, with the chosen day marked | Yes | Yes |
| The browser's date input posts the value, with Min and Max checked before posting and on the server | Yes | Yes |
| Binds DateOnly, on the Gregorian calendar | Yes | Yes |
Static: static server-side rendering, with no circuit and no WebAssembly. Interactive: Server, WebAssembly or Auto.
Without JavaScript
Every page starts as plain HTML: before IzzyBlazor's script loads on a static page, while an interactive page prerenders, and with JavaScript off. With JavaScript off, the date is typed or picked in the browser's own input, above a month that shows it and can't change.
Accessibility
- The month is a
gridnamed by the field's label and the month, with full day names on its columns and the full date on each day. The chosen day hasaria-selected, and todayaria-current. - One day is a tab stop, and arrow keys move it. A day that can't be chosen can take focus so the keys move evenly, and has
aria-disabled. - A polite announcement gives the month when it changes.
- Hint and errors are linked to the grid. The label and an error's link focus the day.
- With JavaScript off, the browser's date input takes the value.
Keyboard navigation
In the days
| Press this key | To do this |
|---|---|
| Left or Right | Move a day (reversed in right-to-left) |
| Up or Down | Move a week |
| Home or End | Go to the start or end of the week |
| Page Up or Page Down | Move a month, keeping the day number or the month's last day |
| Shift+Page Up or Shift+Page Down | Move a year |
| Enter or Space | Choose the date |
In a page of months or years
| Press this key | To do this |
|---|---|
| Left or Right | Move one (reversed in right-to-left) |
| Up or Down | Move a row |
| Home or End | Go to the start or end of the row |
| Page Up or Page Down | Move twelve: a year of months, or twelve years |
| Enter or Space | Choose it |
Parameters
| Prop | Type | Default |
|---|---|---|
ReadOnlyMakes the field read-only: it can be focused, and it still posts. | bool | false |
TypeA date, a month or a year. | DateFieldType? | computed |
MinThe earliest value. | TValue | computed |
MaxThe latest value. | TValue | computed |
FirstDayOfWeekThe first column. | DayOfWeek? | computed |
CultureThe culture for the month and day names, the first day and the week numbers. | CultureInfo? | computed |
ShowWeekNumbersAdds a first column with the week of the year, counted as the culture counts them. | bool | false |
ShowAdjacentDaysWhether to show the days of the months before and after, muted, in the first and last weeks. | bool | true |
DateDisabledWhether a day can't be chosen. | Func<DateOnly, bool>? | |
DateDisabledWindowHow far each way from the value or today to call DateDisabled and DayClass, in whole days, for an end the limits don't set. | TimeSpan? | |
DayClassA class for a day's button, or null. | Func<DateOnly, string?>? | |
@bind-ValueThe value, bound with @bind-Value. | TValue | |
DisplayNameThe field's name in validation messages from the input itself, such as a value that can't be parsed. | string? |
Plus 26 field props.
Full API