Skip to content
IzzyBlazor 0.1.0-alpha.20

Enter all results ↑ ↓ move Esc close

Calendar

Since 0.1.0-alpha.20
A month on the page, with the browser's own date input posting the day.

Static server-side rendering: no circuit, no WebAssembly.

A month on the page.

Usage

razor
<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

March 2027
SunMonTueWedThuFriSat

In March 2027.

March 2027
SunMonTueWedThuFriSat

From March 8 to 19.

Code

Limits.razor
@using 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>

@code {
    [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

March 2027
SunMonTueWedThuFriSat

On a weekday, within 90 days.

March 2027
SunMonTueWedThuFriSat

Paydays are outlined.

Code

Days.razor
<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>

@code {
    [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

2027

Code

Periods.razor
<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>

@code {
    [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

Januar 2027
WkMoDiMiDoFrSaSo
53
1
2
3
4
5
January 2027
MonTueWedThuFriSatSun

Code

Culture.razor
@using 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>

@code {
    [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

May 2027
SunMonTueWedThuFriSat
May 2027
SunMonTueWedThuFriSat
May 2027
SunMonTueWedThuFriSat

Read-only: posted, but can't be changed.

May 2027
SunMonTueWedThuFriSat

Disabled: not posted.

Code

States.razor
<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>

@code {
    [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.

razor
<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.

razor
<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

What works in each mode, Calendar
FeatureStaticInteractive
A month grid that follows the page's culture, with the chosen day markedYesYes
The browser's date input posts the value, with Min and Max checked before posting and on the serverYesYes
Binds DateOnly, on the Gregorian calendarYesYes

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 grid named 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 has aria-selected, and today aria-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

Calendar keys, In the days
Press this keyTo do this
Left or RightMove a day (reversed in right-to-left)
Up or DownMove a week
Home or EndGo to the start or end of the week
Page Up or Page DownMove a month, keeping the day number or the month's last day
Shift+Page Up or Shift+Page DownMove a year
Enter or SpaceChoose the date

In a page of months or years

Calendar keys, In a page of months or years
Press this keyTo do this
Left or RightMove one (reversed in right-to-left)
Up or DownMove a row
Home or EndGo to the start or end of the row
Page Up or Page DownMove twelve: a year of months, or twelve years
Enter or SpaceChoose it

Parameters

Calendar parameters
PropTypeDefault
ReadOnlyMakes the field read-only: it can be focused, and it still posts.boolfalse
TypeA date, a month or a year.DateFieldType?computed
MinThe earliest value.TValuecomputed
MaxThe latest value.TValuecomputed
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.boolfalse
ShowAdjacentDaysWhether to show the days of the months before and after, muted, in the first and last weeks.booltrue
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?

See also

  • DateField: a date to type or choose from a calendar, or a date and time.
  • TimeField: a time of day, in the browser's own input, with a list of times to choose from.
  • Form: posts on static pages, validates live on interactive ones.

Connection lost

Rejoining the server…