Skip to main content
added 4835 characters in body
Source Link
Keith C
  • 137.3k
  • 29
  • 218
  • 458

PS

It turned out to be easier than I expected to write a native LWC that displays multiple date pickers using the standard SLDS formatting. The current template is below, with the JavaScript controller building the data that this template iterates over. Surprisingly fast too: with 40 calendars rendered (as a pathological test case), moving them all by a month so they all change is pretty much instantaneous.

So cleaner to leverage the platform's own framework than to embed a big chunk of JavaScript.

<template>
    
    <!-- One or more consecutive month SLDS date pickers -->
    <div class="slds-grid slds-wrap">
        <template for:each={pickers} for:item="picker">

            <!-- Responsive grid -->
            <div key={picker.key} class="slds-col slds-size_1-of-1 slds-medium-size_1-of-2 slds-large-size_1-of-3">

                <!-- Date picker per month -->
                <div class="slds-form-element slds-p-around_small">

                    <!-- Want the dropdown look but relative layout -->
                    <div class="slds-datepicker slds-dropdown" style="position: relative;">

                        <!-- Selection area -->
                        <div class="slds-datepicker__filter slds-grid">
                            <div class="slds-datepicker__filter_month slds-grid slds-grid_align-spread slds-grow">
                                <div class="slds-align-middle">
                                    <lightning-button-icon
                                        icon-name="utility:left"
                                        variant="bare"
                                        data-relative-month={picker.relativeMonth}
                                        data-month-offset={picker.monthOffset}
                                        onclick={handleLeftClick}
                                    ></lightning-button-icon>
                                </div>
                                <div class="slds-align-middle">
                                    {picker.monthYear}
                                </div>
                                <div class="slds-align-middle">
                                    <lightning-button-icon
                                        icon-name="utility:right"
                                        variant="bare"
                                        data-relative-month={picker.relativeMonth}
                                        data-month-offset={picker.monthOffset}
                                        onclick={handleRightClick}
                                    ></lightning-button-icon>
                                </div>
                            </div>
                        </div>

                        <!-- Month of dates table area -->
                        <table>
                            <!-- Headings -->
                            <tr>
                                <template for:each={picker.headings} for:item="heading">
                                    <th key={heading.key}>{heading.name}</th>
                                </template>
                            </tr>
                            <!-- Rows -->
                            <template for:each={picker.weeks} for:item="week">
                                <tr key={week.key}>
                                    <template for:each={week.days} for:item="day">
                                        <!-- onclick on td didn't appear to work -->
                                        <td key={day.key} class={day.classes}>
                                            <span class="slds-day" data-day-number={day.dayNumber} onclick={handleDayClick}>{day.dayOfMonth}</span>
                                        </td>
                                    </template>
                                </tr>
                            </template>
                        </table>

                        <!-- Jump to today area -->
                        <button
                            class="slds-button slds-align_absolute-center slds-text-link"
                            data-relative-month={picker.relativeMonth}
                            data-month-offset={picker.monthOffset}
                            onclick={handleTodayClick}
                        >Today</button>
                    </div>     
                </div>
            </div>
        </template>
    </div>
    
</template>

PS

It turned out to be easier than I expected to write a native LWC that displays multiple date pickers using the standard SLDS formatting. The current template is below, with the JavaScript controller building the data that this template iterates over. Surprisingly fast too: with 40 calendars rendered (as a pathological test case), moving them all by a month so they all change is pretty much instantaneous.

So cleaner to leverage the platform's own framework than to embed a big chunk of JavaScript.

<template>
    
    <!-- One or more consecutive month SLDS date pickers -->
    <div class="slds-grid slds-wrap">
        <template for:each={pickers} for:item="picker">

            <!-- Responsive grid -->
            <div key={picker.key} class="slds-col slds-size_1-of-1 slds-medium-size_1-of-2 slds-large-size_1-of-3">

                <!-- Date picker per month -->
                <div class="slds-form-element slds-p-around_small">

                    <!-- Want the dropdown look but relative layout -->
                    <div class="slds-datepicker slds-dropdown" style="position: relative;">

                        <!-- Selection area -->
                        <div class="slds-datepicker__filter slds-grid">
                            <div class="slds-datepicker__filter_month slds-grid slds-grid_align-spread slds-grow">
                                <div class="slds-align-middle">
                                    <lightning-button-icon
                                        icon-name="utility:left"
                                        variant="bare"
                                        data-relative-month={picker.relativeMonth}
                                        data-month-offset={picker.monthOffset}
                                        onclick={handleLeftClick}
                                    ></lightning-button-icon>
                                </div>
                                <div class="slds-align-middle">
                                    {picker.monthYear}
                                </div>
                                <div class="slds-align-middle">
                                    <lightning-button-icon
                                        icon-name="utility:right"
                                        variant="bare"
                                        data-relative-month={picker.relativeMonth}
                                        data-month-offset={picker.monthOffset}
                                        onclick={handleRightClick}
                                    ></lightning-button-icon>
                                </div>
                            </div>
                        </div>

                        <!-- Month of dates table area -->
                        <table>
                            <!-- Headings -->
                            <tr>
                                <template for:each={picker.headings} for:item="heading">
                                    <th key={heading.key}>{heading.name}</th>
                                </template>
                            </tr>
                            <!-- Rows -->
                            <template for:each={picker.weeks} for:item="week">
                                <tr key={week.key}>
                                    <template for:each={week.days} for:item="day">
                                        <!-- onclick on td didn't appear to work -->
                                        <td key={day.key} class={day.classes}>
                                            <span class="slds-day" data-day-number={day.dayNumber} onclick={handleDayClick}>{day.dayOfMonth}</span>
                                        </td>
                                    </template>
                                </tr>
                            </template>
                        </table>

                        <!-- Jump to today area -->
                        <button
                            class="slds-button slds-align_absolute-center slds-text-link"
                            data-relative-month={picker.relativeMonth}
                            data-month-offset={picker.monthOffset}
                            onclick={handleTodayClick}
                        >Today</button>
                    </div>     
                </div>
            </div>
        </template>
    </div>
    
</template>
Source Link
Keith C
  • 137.3k
  • 29
  • 218
  • 458

It looks like to me that unless Mobiscroll removes the dependency on matches (perhaps just in the logic that identifies the DOM node to add children to), the calendar won't work inside an LWC.

But thanks to advice from Mobiscroll's Isti Halmen and with a small change that hopefully Mobiscroll will add to their main distribution, the calendar will work inside an Aura component... So the fallback from the ideal of hosting inside an LWC, is to host inside an Aura component that can inter-operate with LWCs.

Component:

<aura:component implements="forceCommunity:availableForAllPageTypes" access="global">
    
    <ltng:require styles="{!$Resource.mobiscroll + '/css/mobiscroll.javascript.min.css'}" />
    <ltng:require scripts="{!$Resource.mobiscroll + '/js/mobiscroll.javascript.min.js'}" afterScriptsLoaded="{!c.afterScriptsLoaded}" />

    <div style="height:500px">
        <p>Aura before input</p>
        <input aura:id="xyz" placeholder="Please select a date..."></input>
        <p>Aura after input</p>
    </div>
    
</aura:component>

Controller:

({
    afterScriptsLoaded : function(component, event, helper) {

        var m = mobiscroll || window.mobiscroll;

        var now = new Date();
        var week = [now, new Date(now.getFullYear(), now.getMonth(), now.getDate() + 6, 23, 59)];

        var el = component.find("xyz").getElement();
        
        m.range(el, {
            display: 'inline',
            months: 4,
            controls: ['calendar'],
            onInit: function (event, inst) {
                inst.setVal(week, true);
            }
        });
    }
})