bryntum / support

An issues-only repository for the Bryntum project management component suite which includes powerful Grid, Scheduler, Calendar, Kanban Task Board and Gantt chart components all built in pure JS / CSS / TypeScript
https://www.bryntum.com
53 stars 6 forks source link

`alwaysShowCurrentDate` setting needed for AgendaView #9547

Closed ExtAnimal closed 1 month ago

ExtAnimal commented 2 months ago

Forum post

To allow user interaction to create events.

Suggested code in thread

ExtAnimal commented 2 months ago

It should always show the generated, empty cell for the current date:

Screenshot 2024-07-09 at 16 42 12

Code :

    onMonthChange() {
        super.onMonthChange(...arguments);

        // No cells, and there's a date change with showAtLeastOneCell set
        // We must ensure that the store is repopulated with the generated
        // cell for the new date.
        // If the date change is to a date range with data, populateStore will
        // be called immediately which cancels the populateStoreSoon queue.
        if (!this._cellMap?.size && this.showAtLeastOneCell) {
            this.populateStoreSoon();
        }
    }

    populateStore() {
        this._cellMap?.clear();

        const
            me = this,
            {
                store,
                eventStore,
                rowManager
            }             = me,
            { rowHeight } = rowManager,
            rowCount      = rowManager.rows?.length,
            eventHeight   = isNaN(me.eventHeight) ? 25 : me.eventHeight;

        me._eventCount = 0;
        me.populateStoreSoon.cancel();

        if (!me.date) {
            // Avoid recursion into populateStore
            me.isConfiguring = true;
            me.date = eventStore.map(r => r.startDate).sort((lhs, rhs) => lhs.valueOf() - rhs.valueOf())[0];
            me.isConfiguring = false;
        }

        const
            {
                cellMap,
                firstVisibleDate
            }              = me,
            endDate        = DH.add(me.endDate, me.range ? 0 : 1, 'd'),
            cellMapEntries = [...cellMap.values()],
            sortedEntries  = [];

        me.dateIndex = {};

        // If configured to do so, ensure there is always a cell for the start date if the range turns out to be empty,
        // so that user has something to interact with
        if (me.showAtLeastOneCell && !cellMapEntries.length) {
            cellMapEntries.push(me.createCellData(me.date));
        }

        for (let i = 0, { length } = cellMapEntries; i < length; i++) {
            const
                cellData         = cellMapEntries[i],
                { events, date } = cellData;

            // Recurring event continuation blocks which are past a midnight boundary are inserted into
            // the cellMap even if they are beyond the end date. Eliminate these from the view.
            if (date >= firstVisibleDate && date < endDate) {
                // Count unique events
                for (let j = 0, { length } = events; j < length; j++) {
                    const event = events[j];

                    if (!me.isAllDayEvent(event) || !i || DH.clearTime(event.startDate).valueOf() === date.valueOf()) {
                        me._eventCount++;
                    }
                }

                // Build Store data array in ascending YYYY-MM-DD order.
                // The ordered insertion is needed because generated occurrences are
                // added to the Map in a second pass, and it will not be in order.
                // Other views iterate their date range and pull cell info using the key.
                // This view is a grid which renders rows in the store's order.
                const
                    cellInfo = store.createRecord(cellData),
                    idx      = ArrayHelper.findInsertionIndex(cellInfo, sortedEntries, compareDate);

                sortedEntries.splice(idx, 0, cellInfo);
                me.dateIndex[cellData.id] = cellInfo;
            }
        }
        const avgEventsPerCell = me._eventCount ? sortedEntries.map(e => e.events.length).reduce((a, b) => a + b, 0) / cellMapEntries.length : 0;

        store.suspendEvents();
        store.loadData(sortedEntries);
        store.resumeEvents();

        // Give RowManager a clue so that it can calculate an appropriate rowCount.
        // If the rows are tall, we do not need many to cover the viewport.
        rowManager._rowHeight = 20;
        // RowManager#set rowHeight does not tolerate no rows.
        if (store.count) {
            rowManager.rowHeight = Math.max(avgEventsPerCell * (eventHeight + me.eventSpacing), 70);
        }

        // Setting the rowHeight does a refresh if there are existing rows and the height actually changed.
        // Otherwise, we explicitly refresh now.
        if (!rowCount || !store.count || rowManager.rowHeight === rowHeight) {
            rowManager.calculateRowCount();
            rowManager.estimateTotalHeight(true);
        }

        me.refreshCount = (me.refreshCount || 0) + 1;

        /**
         * Fires when this AgendaView refreshes.
         * @param {Calendar.widget.AgendaView} source The triggering instance.
         * @event refresh
         */
        me.trigger('refresh');

        // The owning Calendar's UI may need to sync with the new state
        me.calendar?.syncUIWithActiveView(me);

        me.columns.forEach(c => c.constructor.exposeProperties?.());

        // Evaluate this late so that it doesn't change the order of date config evaluation
        // Ensure that the menu stays aligned if scrollbar causes button movement.
        me.settings?._menu?.realign();
    }