MDL-59713 calendar: clicking day in monthly view opens new event modal
authorRyan Wyllie <ryan@moodle.com>
Mon, 21 Aug 2017 06:27:47 +0000 (06:27 +0000)
committerRyan Wyllie <ryan@moodle.com>
Fri, 25 Aug 2017 01:37:20 +0000 (01:37 +0000)
13 files changed:
calendar/amd/build/calendar.min.js
calendar/amd/build/modal_event_form.min.js
calendar/amd/src/calendar.js
calendar/amd/src/modal_event_form.js
calendar/classes/external/day_exporter.php
calendar/classes/local/event/forms/create.php
calendar/lib.php
calendar/templates/month_detailed.mustache
theme/boost/scss/moodle/calendar.scss
theme/boost/scss/moodle/core.scss
theme/bootstrapbase/less/moodle/calendar.less
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/style/moodle.css

index b0336c4..c797403 100644 (file)
Binary files a/calendar/amd/build/calendar.min.js and b/calendar/amd/build/calendar.min.js differ
index 78b804a..f29703f 100644 (file)
Binary files a/calendar/amd/build/modal_event_form.min.js and b/calendar/amd/build/modal_event_form.min.js differ
index 86fd106..e75671c 100644 (file)
@@ -58,10 +58,13 @@ define([
 
     var SELECTORS = {
         ROOT: "[data-region='calendar']",
+        DAY: "[data-region='day']",
+        EVENT_ITEM: "[data-region='event-item']",
         EVENT_LINK: "[data-action='view-event']",
         NEW_EVENT_BUTTON: "[data-action='new-event-button']",
         DAY_CONTENT: "[data-region='day-content']",
         LOADING_ICON: '.loading-icon',
+        VIEW_DAY_LINK: "[data-action='view-day-link']"
     };
 
     /**
@@ -284,14 +287,42 @@ define([
         var root = $(SELECTORS.ROOT);
 
         // Bind click events to event links.
-        root.on('click', SELECTORS.EVENT_LINK, function(e) {
+        root.on('click', SELECTORS.EVENT_ITEM, function(e) {
             e.preventDefault();
-            var eventId = $(this).attr('data-event-id');
+            // We've handled the event so stop it from bubbling
+            // and causing the day click handler to fire.
+            e.stopPropagation();
+
+            var target = $(e.target);
+            var eventId = null;
+
+            if (target.is(SELECTORS.EVENT_LINK)) {
+                eventId = target.attr('data-event-id');
+            } else {
+                eventId = target.find(SELECTORS.EVENT_LINK).attr('data-event-id');
+            }
+
             renderEventSummaryModal(eventId);
         });
 
         var eventFormPromise = registerEventFormModal(root);
         registerCalendarEventListeners(root, eventFormPromise);
+
+        // Bind click events to calendar days.
+        root.on('click', SELECTORS.DAY, function(e) {
+            var target = $(e.target);
+
+            if (!target.is(SELECTORS.VIEW_DAY_LINK)) {
+                var startTime = $(this).attr('data-new-event-timestamp');
+                eventFormPromise.then(function(modal) {
+                    modal.setStartTime(startTime);
+                    modal.show();
+                    return;
+                });
+
+                e.preventDefault();
+            }
+        });
     };
 
     return {
index bad5ca4..cee039a 100644 (file)
@@ -66,6 +66,7 @@ define([
     var ModalEventForm = function(root) {
         Modal.call(this, root);
         this.eventId = null;
+        this.startTime = null;
         this.reloadingBody = false;
         this.reloadingTitle = false;
         this.saveButton = this.getFooter().find(SELECTORS.SAVE_BUTTON);
@@ -106,6 +107,36 @@ define([
         return this.eventId !== null;
     };
 
+    /**
+     * Set the start time to the given value.
+     *
+     * @method setStartTime
+     * @param {int} time The start time
+     */
+    ModalEventForm.prototype.setStartTime = function(time) {
+        this.startTime = time;
+    };
+
+    /**
+     * Retrieve the current start time, if any.
+     *
+     * @method getStartTime
+     * @return {int|null} The start time
+     */
+    ModalEventForm.prototype.getStartTime = function() {
+        return this.startTime;
+    };
+
+    /**
+     * Check if the modal has start time.
+     *
+     * @method hasStartTime
+     * @return {bool}
+     */
+    ModalEventForm.prototype.hasStartTime = function() {
+        return this.startTime !== null;
+    };
+
     /**
      * Get the form element from the modal.
      *
@@ -244,6 +275,10 @@ define([
             args.eventid = this.getEventId();
         }
 
+        if (this.hasStartTime()) {
+            args.starttime = this.getStartTime();
+        }
+
         if (typeof formData !== 'undefined') {
             args.formdata = formData;
         }
@@ -305,6 +340,7 @@ define([
     ModalEventForm.prototype.hide = function() {
         Modal.prototype.hide.call(this);
         this.setEventId(null);
+        this.setStartTime(null);
     };
 
     /**
index 5cac152..4a713bc 100644 (file)
@@ -82,6 +82,9 @@ class day_exporter extends exporter {
             'timestamp' => [
                 'type' => PARAM_INT,
             ],
+            'neweventtimestamp' => [
+                'type' => PARAM_INT,
+            ],
             'istoday' => [
                 'type' => PARAM_BOOL,
                 'default' => false,
@@ -108,13 +111,26 @@ class day_exporter extends exporter {
      * @return array Keys are the property names, values are their values.
      */
     protected function get_other_values(renderer_base $output) {
+        $timestamp = $this->data[0];
+        // Need to account for user's timezone.
+        $usernow = usergetdate(time());
+        $today = new \DateTimeImmutable();
+        // The start time should use the day's date but the current
+        // time of the day (adjusted for user's timezone).
+        $neweventstarttime = $today->setTimestamp($timestamp)->setTime(
+            $usernow['hours'],
+            $usernow['minutes'],
+            $usernow['seconds']
+        );
+
         $return = [
-            'timestamp' => $this->data[0],
+            'timestamp' => $timestamp,
+            'neweventtimestamp' => $neweventstarttime->getTimestamp()
         ];
 
         $url = new moodle_url('/calendar/view.php', [
                 'view' => 'day',
-                'time' => $this->data[0],
+                'time' => $timestamp,
             ]);
         $return['viewdaylink'] = $url->out(false);
 
index 3e8ed78..4a4172d 100644 (file)
@@ -42,6 +42,7 @@ class create extends \moodleform {
 
         $mform = $this->_form;
         $haserror = !empty($this->_customdata['haserror']);
+        $starttime = isset($this->_customdata['starttime']) ? $this->_customdata['starttime'] : 0;
         $eventtypes = calendar_get_all_allowed_types();
 
         $mform->setDisableShortforms();
@@ -58,7 +59,7 @@ class create extends \moodleform {
         $mform->setType('name', PARAM_TEXT);
 
         // Event time start field.
-        $mform->addElement('date_time_selector', 'timestart', get_string('date'));
+        $mform->addElement('date_time_selector', 'timestart', get_string('date'), ['defaulttime' => $starttime]);
 
         // Add the select elements for the available event types.
         $this->add_event_type_elements($mform, $eventtypes);
index 30cbaa4..b088e71 100644 (file)
@@ -3534,6 +3534,7 @@ function calendar_output_fragment_event_form($args) {
     $html = '';
     $data = null;
     $eventid = isset($args['eventid']) ? clean_param($args['eventid'], PARAM_INT) : null;
+    $starttime = isset($args['starttime']) ? clean_param($args['starttime'], PARAM_INT) : null;
     $event = null;
     $hasformdata = isset($args['formdata']) && !empty($args['formdata']);
     $formoptions = [];
@@ -3546,6 +3547,10 @@ function calendar_output_fragment_event_form($args) {
         $formoptions['haserror'] = clean_param($args['haserror'], PARAM_BOOL);
     }
 
+    if ($starttime) {
+        $formoptions['starttime'] = $starttime;
+    }
+
     if (is_null($eventid)) {
         $mform = new \core_calendar\local\event\forms\create(
             null,
index 19b12ff..9d17086 100644 (file)
@@ -31,7 +31,7 @@
     {
     }
 }}
-<span id="month-detailed-{{uniqid}}" class="calendarwrapper" data-courseid="{{courseid}}" data-current-time="{{time}}">
+<div id="month-detailed-{{uniqid}}" class="calendarwrapper" data-courseid="{{courseid}}" data-current-time="{{time}}">
     {{> core_calendar/month_header }}
     {{> core_calendar/month_navigation }}
     <table class="calendarmonth calendartable card-deck m-b-0">
                     <td class="dayblank">&nbsp;</td>
                 {{/prepadding}}
                 {{#days}}
-                    <td class="day text-sm-center text-md-left{{!
+                    <td class="clickable day text-sm-center text-md-left{{!
                             }}{{#istoday}} today{{/istoday}}{{!
                             }}{{#isweekend}} weekend{{/isweekend}}{{!
                             }}{{#durationevents.0}} duration{{/durationevents.0}}{{!
                             }}{{#durationevents}} duration_{{.}}{{/durationevents}}{{!
                         }}"
                         data-day-timestamp="{{timestamp}}"
-                        data-drop-zone="true">
+                        data-drop-zone="true"
+                        data-region="day"
+                        data-new-event-timestamp="{{neweventtimestamp}}">
                         <div class="hidden-sm-down text-xs-center">
                             {{#events.0}}
-                                <a href="{{viewdaylink}}" class="day" title="{{viewdaylinktitle}}">{{mday}}</a>
+                                <a data-action="view-day-link" href="{{viewdaylink}}" class="day" title="{{viewdaylinktitle}}">{{mday}}</a>
                             {{/events.0}}
                             {{^events.0}}
                                 {{mday}}
@@ -75,6 +77,7 @@
                                         {{/underway}}
                                         {{^underway}}
                                             <li class="calendar_event_{{eventtype}}"
+                                                data-region="event-item"
                                                 {{#canedit}}
                                                     draggable="true"
                                                     data-drag-type="move"
     {{/weeks}}
         </tbody>
     </table>
-</span>
+</div>
 {{#js}}
 require(['jquery', 'core_calendar/drag_drop'], function($, DragDrop) {
     var root = $('#month-detailed-{{uniqid}}');
index 47632cd..df6829e 100644 (file)
@@ -100,6 +100,10 @@ $calendarEventUserColor: #dce7ec !default; // Pale blue.
             td {
                 height: 5em;
             }
+
+            .clickable:hover {
+                box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12);
+            }
         }
 
         .calendar-controls {
@@ -122,8 +126,12 @@ $calendarEventUserColor: #dce7ec !default; // Pale blue.
         .calendar_event_site,
         .calendar_event_group,
         .calendar_event_user {
-            border-width: 1px 1px 1px 12px;
-            border-style: solid;
+            &:hover {
+                a {
+                    color: $link-hover-color;
+                    text-decoration: $link-hover-decoration;
+                }
+            }
         }
 
         .calendar_event_course {
index 966d885..f7a36ef 100644 (file)
@@ -2155,3 +2155,7 @@ $footer-link-color: $bg-inverse-link-color !default;
 [data-drag-type="move"] {
     cursor: move;
 }
+
+.clickable {
+    cursor: pointer;
+}
index 59fdeb9..d89dd37 100644 (file)
             td {
                 height: 5em;
             }
+
+            .clickable:hover {
+                box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12);
+            }
         }
         .calendar-controls {
             .previous,
         .calendar_event_site,
         .calendar_event_group,
         .calendar_event_user {
-            border-width: 1px 1px 1px 12px;
-            border-style: solid;
+            &:hover {
+                a {
+                    color: @linkColorHover;
+                    text-decoration: underline;
+                }
+            }
         }
         .calendar_event_course {
             border-color: @calendarEventCourseColor;
index 1a47442..85e2608 100644 (file)
@@ -2381,3 +2381,7 @@ h3.sectionname .inplaceeditable.inplaceeditingon .editinstructions {
         }
     }
 }
+
+.clickable {
+    cursor: pointer;
+}
index 0772ef9..70102d3 100644 (file)
@@ -4774,6 +4774,9 @@ h3.sectionname .inplaceeditable.inplaceeditingon .editinstructions {
   visibility: visible;
   transition: opacity 0.15s 0.5s, visibility 0.15s 0.5s;
 }
+.clickable {
+  cursor: pointer;
+}
 /* admin.less */
 .formtable tbody th {
   font-weight: normal;
@@ -5618,6 +5621,9 @@ img.iconsmall {
 .path-calendar .maincalendar .calendarmonth td {
   height: 5em;
 }
+.path-calendar .maincalendar .calendarmonth .clickable:hover {
+  box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
+}
 .path-calendar .maincalendar .calendar-controls .previous,
 .path-calendar .maincalendar .calendar-controls .next {
   width: 30%;
@@ -5629,12 +5635,12 @@ img.iconsmall {
   width: 98%;
   margin: 10px auto;
 }
-.path-calendar .maincalendar .calendar_event_course,
-.path-calendar .maincalendar .calendar_event_site,
-.path-calendar .maincalendar .calendar_event_group,
-.path-calendar .maincalendar .calendar_event_user {
-  border-width: 1px 1px 1px 12px;
-  border-style: solid;
+.path-calendar .maincalendar .calendar_event_course:hover a,
+.path-calendar .maincalendar .calendar_event_site:hover a,
+.path-calendar .maincalendar .calendar_event_group:hover a,
+.path-calendar .maincalendar .calendar_event_user:hover a {
+  color: #003d5c;
+  text-decoration: underline;
 }
 .path-calendar .maincalendar .calendar_event_course {
   border-color: #ffd3bd;