简体   繁体   中英

Hide rows without dates in current month (Custom Calendar Tag Helper)

Following this article: https://cpratt.co/bootstrap-4-responsive-calendar-asp-net-core-taghelper/ I was able to implement a custom Calendar tag helper which just takes a month and a year as parameters and displays the calendar for that month.

It displays a standard calendar with some days from the previous months and days in the future. What I want is to hide any rows that do not have days in the current month so essentially , some months may only have 5 rows instead of 6 as in a standard calendar.

I have attempted to hide certain cells using css , but this still gives me 6 rows with blank cells.

see the sample codepen as explained in article:

https://codepen.io/chrisdpratt/pen/OOybam As you can see there is a row with dates not in that month. I only want to display rows where there are some inclusive days in the current month.

And here is the code in ac# tag helper that renders the html.

[HtmlTargetElement("calendar", TagStructure = TagStructure.NormalOrSelfClosing)]
public class CalendarTagHelper : TagHelper  
{
    public int Month { get; set; }

    public int Year { get; set; }

    public List<CalendarEvent> Events { get; set; }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        output.TagName = "section";
        output.Attributes.Add("class", "calendar");
        output.Content.SetHtmlContent(GetHtml());
        output.TagMode = TagMode.StartTagAndEndTag;
    }

    private string GetHtml()
    {
        var monthStart = new DateTime(Year, Month, 1);
        var events = Events?.GroupBy(e => e.Date);

        var html = new XDocument(
            new XElement("div",
                new XAttribute("class", "container-fluid"),
                new XElement("header",
                    new XElement("h4",
                        new XAttribute("class", "display-4 mb-2 text-center"),
                        monthStart.ToString("MMMM yyyy")
                    ),
                    new XElement("div",
                        new XAttribute("class", "row d-none d-lg-flex p-1 bg-dark text-white"),
                        Enum.GetValues(typeof(DayOfWeek)).Cast<DayOfWeek>().Select(d =>
                            new XElement("h5",
                                new XAttribute("class", "col-lg p-1 text-center"),
                                d.ToString()
                            )
                        )
                    )
                ),
                new XElement("div",
                    new XAttribute("class", "row border border-right-0 border-bottom-0"),
                    GetDatesHtml()
                )
            )
        );

        return html.ToString();

        IEnumerable<XElement> GetDatesHtml()
        {
            var startDate = monthStart.AddDays(-(int)monthStart.DayOfWeek);
            var dates = Enumerable.Range(0, 42).Select(i => startDate.AddDays(i));

            foreach (var d in dates)
            {
                if (d.DayOfWeek == DayOfWeek.Sunday && d != startDate)
                {
                    yield return new XElement("div",
                        new XAttribute("class", "w-100"),
                        String.Empty
                    );
                }

                var mutedClasses = "d-none d-lg-inline-block bg-light text-muted";
                yield return new XElement("div",
                    new XAttribute("class", $"day col-lg p-2 border border-left-0 border-top-0 text-truncate {(d.Month != monthStart.Month ? mutedClasses : null)}"),
                    new XElement("h5",
                        new XAttribute("class", "row align-items-center"),
                        new XElement("span",
                            new XAttribute("class", "date col-1"),
                            d.Day
                        ),
                        new XElement("small",
                            new XAttribute("class", "col d-lg-none text-center text-muted"),
                            d.DayOfWeek.ToString()
                        ),
                        new XElement("span",
                            new XAttribute("class", "col-1"),
                            String.Empty
                        )
                    ),
                    GetEventHtml(d)
                );
            }
        }

        IEnumerable<XElement> GetEventHtml(DateTime d)
        {
            return events?.SingleOrDefault(e => e.Key == d)?.Select(e =>
                new XElement("a",
                    new XAttribute("class", $"event d-block p-1 pl-2 pr-2 mb-1 rounded text-truncate small bg-{e.Type} text-white"),
                    new XAttribute("title", e.Title),
                    e.Title
                )
            ) ?? new[] {
                new XElement("p",
                    new XAttribute("class", "d-lg-none"),
                    "No events"
                )
            };
        }
    }
}

I know I need to tweak this line var dates = Enumerable.Range(0, 42).Select(i => startDate.AddDays(i)); to get the right range so that rows without days in current month do not get rendered at all but I can't seem to wrap my head around it.

You just need to determine how many days to show based on the number of needed weeks, ie 42 (days) is for 6 weeks. I just went with a standard 42, because I wanted the calendar to always maintain the same visual height.

Anyways, to calculate the number of weeks to show, you can use something like:

var weeks = (int)Math.Ceiling((DateTime.DaysInMonth(Year, Month) + (int)monthStart.DayOfWeek) / 7f);

Then, just replace the dates initialization with:

var dates = Enumerable.Range(0, weeks * 7).Select(i => startDate.AddDays(i));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM