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

dblClick to create after 23:00 in DayView results in a day-crossing event #9443

Closed ExtAnimal closed 18 hours ago

ExtAnimal commented 4 weeks ago

The default event duration of 1 hour means that if the event starts after 23:00, it crosses midnight, and therefore ends up in the all day row.

This is likely not the user's intention. DayView should minimize the event duration of autoCreate events to extend no further than <endTime> (day start and end are definable) of the clicked day.

Screenshot 2024-06-26 at 08 53 44
ExtAnimal commented 2 weeks ago

CalendarMixin.js needs:

    async doCreateEvent(date, resourceRecord, editingView = this, isAutoCreate) {

        resourceRecord = resourceRecord ?? this.defaultCalendar;

        const
            me                = this,
            { isDayView }     = (me.viewType || me),
            // We are either a mode or a CalendarRow
            calendar          = me.calendar || me.owner?.calendar,
            // Some views may be created with a chained EventStore.
            // We must use the Calendar's EventStore.
            eventStore        = calendar?.eventStore || me.eventStore,
            autoCreate        = editingView.autoCreate || editingView.changeAutoCreate(true),
            { modelClass }    = eventStore,
            { newName }       = autoCreate;

        // Default date to current date and inject the autoCreate's start time
        if (date == null || !isDayView && isAutoCreate) {
            const
                { dayStartHour, dayEndHour } = me.activeView,
                // Here we need to pick startHour which should be between active view dayStartHour and dayEndHour
                // 1. Pick autoCreate.startHour as startHour if it is greater then me.activeView.dayStartHour
                // and less than me.activeView.dayEndHour
                // 2. Pick me.activeView.dayStartHour as startHour if autoCreate.startHour is not within the current
                // active view range.
                startHour                    = Math.min(Math.max(autoCreate.startHour, dayStartHour),  dayEndHour),
                pickedValue                  = startHour === dayEndHour ? dayStartHour : startHour,
                startHourMS                  = (isNaN(pickedValue) ? isNaN(autoCreate.startHour) ? DH.getTimeOfDay(DH.parse(autoCreate.startHour, 'HH:mm:ss')) : autoCreate.startHour * 3600000 : pickedValue * 3600000);

            date = DH.add(date || me.date, startHourMS);
        }

        const
            d                 = DH.parseDuration(autoCreate.duration),
            dateStart         = new Date(date.getTime() + (me.dayStartShift || 0)),

            // If this view has high definition time granularity (isa DayView), then round the precise date
            // passed to this view's autoCreate.step and snapType.
            // Otherwise default to dateStart which is adjusted at the start of method.
            startDate         = isDayView ? DH[autoCreate.snapType](date, autoCreate.step) : dateStart,

            // In a DayView dblclick create, constrain the ending so that it does not cross our day end.
            // Otherwise a dblclick after 11:08 (depending on autoCreate.step and snapType) will create an event of 1 hour
            // starting at 23:15 which will jump into the all day row
            duration          = isDayView && isAutoCreate? {
                magnitude : Math.min(d.magnitude, DH.diff(startDate, DH.add(DH.startOf(date), me.dayTime.timeEnd), d.unit)),
                unit      : d.unit
            } : d,
            endDate           = DH.add(startDate, duration.magnitude, duration.unit),
            name              = me.resolveCallback(newName, me, false) ? me.callback(newName, me, [me, startDate, resourceRecord]) : newName,
            recordData        = {
                [modelClass.getFieldDataSource('name')]         : name,
                [modelClass.getFieldDataSource('startDate')]    : startDate,
                [modelClass.getFieldDataSource('endDate')]      : endDate,
                [modelClass.getFieldDataSource('duration')]     : duration.magnitude,
                [modelClass.getFieldDataSource('durationUnit')] : duration.unit,

                // If the view's settings resulted in a midnight to midnight event, flag it as allDay
                allDay : DH.diff(startDate, endDate, 'day') === 1
            },
            // For EventLists, when we are using a startDate->endDate range as opposed to a fixed
            // range, the dates are *inclusive*, so pick the correct date containment function.
            dateContainmentFn = editingView.isEventList && !editingView.range ? 'betweenLesserEqual' : 'betweenLesser',
            newRecord = eventStore.createRecord(recordData);

        // If an editor is available, mark the event as non-persistable while it is being edited
        if (calendar?.features.eventEdit && !calendar.features.eventEdit.disabled) {
            newRecord.isCreating = true;
        }

        if (resourceRecord) {
            eventStore.assignmentStore.assignEventToResource(newRecord, resourceRecord);
        }

        await eventStore.addAsync(newRecord);

        // If the date we are being asked to create at is in view, edit the event when
        // the view has rendered it.
        if (DH[dateContainmentFn](startDate, editingView.startDate, editingView.endDate)) {
            if (editingView.getEventElement(newRecord, startDate)) {
                editingView.editAutoCreatedEvent(newRecord);
            }
            else {
                editingView.ion({
                    refresh({ source }) {
                        // Conditionally call. May have been destroyed
                        source.editAutoCreatedEvent?.(newRecord);
                    },
                    once    : true,
                    prio    : -10000,
                    buffer  : 100,
                    expires : 500
                });
            }
        }
    }