MDL-54165 mod_assign: Collapsible review and grade panel
authorRyan Wyllie <ryan@moodle.com>
Fri, 20 May 2016 08:15:20 +0000 (08:15 +0000)
committerRyan Wyllie <ryan@moodle.com>
Wed, 6 Jul 2016 02:50:24 +0000 (02:50 +0000)
12 files changed:
mod/assign/amd/build/grading_actions.min.js
mod/assign/amd/build/grading_events.min.js [new file with mode: 0644]
mod/assign/amd/build/grading_panel.min.js
mod/assign/amd/build/grading_review_panel.min.js
mod/assign/amd/src/grading_actions.js
mod/assign/amd/src/grading_events.js [new file with mode: 0644]
mod/assign/amd/src/grading_panel.js
mod/assign/amd/src/grading_review_panel.js
mod/assign/lang/en/assign.php
mod/assign/styles.css
mod/assign/templates/grading_actions.mustache
mod/assign/templates/grading_app.mustache

index a68cc23..b5d6827 100644 (file)
Binary files a/mod/assign/amd/build/grading_actions.min.js and b/mod/assign/amd/build/grading_actions.min.js differ
diff --git a/mod/assign/amd/build/grading_events.min.js b/mod/assign/amd/build/grading_events.min.js
new file mode 100644 (file)
index 0000000..6152079
Binary files /dev/null and b/mod/assign/amd/build/grading_events.min.js differ
index 83022c4..2b81723 100644 (file)
Binary files a/mod/assign/amd/build/grading_panel.min.js and b/mod/assign/amd/build/grading_panel.min.js differ
index 084992e..8ad6a6e 100644 (file)
Binary files a/mod/assign/amd/build/grading_review_panel.min.js and b/mod/assign/amd/build/grading_review_panel.min.js differ
index 753db51..692c77d 100644 (file)
@@ -23,7 +23,7 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  * @since      3.1
  */
-define(['jquery'], function($) {
+define(['jquery', 'mod_assign/grading_events'], function($, GradingEvents) {
 
     /**
      * GradingActions class.
@@ -35,13 +35,7 @@ define(['jquery'], function($) {
         this._regionSelector = selector;
         this._region = $(selector);
 
-        $(document).on('user-changed', this._showActionsForm.bind(this));
-
-        this._region.find('[name="savechanges"]').on('click', this._trigger.bind(this, 'save-changes'));
-        this._region.find('[name="resetbutton"]').on('click', this._trigger.bind(this, 'reset'));
-        this._region.find('form').on('submit', function(e) {
-          e.preventDefault();
-        });
+        this.registerEventListeners();
     };
 
     /** @type {String} Selector for the page region containing the user navigation. */
@@ -86,5 +80,165 @@ define(['jquery'], function($) {
         $(document).trigger(action);
     };
 
+    /**
+     * Get the review panel element.
+     *
+     * @method getReviewPanelElement
+     * @return {jQuery}
+     */
+    GradingActions.prototype.getReviewPanelElement = function() {
+        return $('[data-region="review-panel"]');
+    };
+
+    /**
+     * Check if the page has a review panel.
+     *
+     * @method hasReviewPanelElement
+     * @return {bool}
+     */
+    GradingActions.prototype.hasReviewPanelElement = function() {
+        return this.getReviewPanelElement().length > 0;
+    };
+
+    /**
+     * Get the collapse grade panel button.
+     *
+     * @method getCollapseGradePanelButton
+     * @return {jQuery}
+     */
+    GradingActions.prototype.getCollapseGradePanelButton = function() {
+        return $('[data-region="grade-actions"] .collapse-grade-panel');
+    };
+
+    /**
+     * Get the collapse review panel button.
+     *
+     * @method getCollapseReviewPanelButton
+     * @return {jQuery}
+     */
+    GradingActions.prototype.getCollapseReviewPanelButton = function() {
+        return $('[data-region="grade-actions"] .collapse-review-panel');
+    };
+
+    /**
+     * Get the expand all panels button.
+     *
+     * @method getExpandAllPanelsButton
+     * @return {jQuery}
+     */
+    GradingActions.prototype.getExpandAllPanelsButton = function() {
+        return $('[data-region="grade-actions"] .collapse-none');
+    };
+
+    /**
+     * Remove the active state from all layout buttons.
+     *
+     * @method resetLayoutButtons
+     */
+    GradingActions.prototype.resetLayoutButtons = function() {
+        this.getCollapseGradePanelButton().removeClass('active');
+        this.getCollapseReviewPanelButton().removeClass('active');
+        this.getExpandAllPanelsButton().removeClass('active');
+    };
+
+    /**
+     * Hide the review panel.
+     *
+     * @method collapseReviewPanel
+     */
+    GradingActions.prototype.collapseReviewPanel = function() {
+        $(document).trigger(GradingEvents.COLLAPSE_REVIEW_PANEL);
+        $(document).trigger(GradingEvents.EXPAND_GRADE_PANEL);
+        this.resetLayoutButtons();
+        this.getCollapseReviewPanelButton().addClass('active');
+    };
+
+    /**
+     * Show/Hide the grade panel.
+     *
+     * @method collapseGradePanel
+     */
+    GradingActions.prototype.collapseGradePanel = function() {
+        $(document).trigger(GradingEvents.COLLAPSE_GRADE_PANEL);
+        $(document).trigger(GradingEvents.EXPAND_REVIEW_PANEL);
+        this.resetLayoutButtons();
+        this.getCollapseGradePanelButton().addClass('active');
+    };
+
+    /**
+     * Return the layout to default.
+     *
+     * @method expandAllPanels
+     */
+    GradingActions.prototype.expandAllPanels = function() {
+        $(document).trigger(GradingEvents.EXPAND_GRADE_PANEL);
+        $(document).trigger(GradingEvents.EXPAND_REVIEW_PANEL);
+        this.resetLayoutButtons();
+        this.getExpandAllPanelsButton().addClass('active');
+    };
+
+    /**
+     * Register event listeners for the grade panel.
+     *
+     * @method registerEventListeners
+     */
+    GradingActions.prototype.registerEventListeners = function() {
+        // Don't need layout controls if there is no review panel.
+        if (this.hasReviewPanelElement()) {
+            var collapseReviewPanelButton = this.getCollapseReviewPanelButton();
+            collapseReviewPanelButton.click(function(e) {
+                this.collapseReviewPanel();
+                e.preventDefault();
+            }.bind(this));
+
+            collapseReviewPanelButton.keydown(function(e) {
+                if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {
+                    if (e.keyCode === 13 || e.keyCode === 32) {
+                        this.collapseReviewPanel();
+                        e.preventDefault();
+                    }
+                }
+            }.bind(this));
+
+            var collapseGradePanelButton = this.getCollapseGradePanelButton();
+            collapseGradePanelButton.click(function(e) {
+                this.collapseGradePanel();
+                e.preventDefault();
+            }.bind(this));
+
+            collapseGradePanelButton.keydown(function(e) {
+                if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {
+                    if (e.keyCode === 13 || e.keyCode === 32) {
+                        this.collapseGradePanel();
+                        e.preventDefault();
+                    }
+                }
+            }.bind(this));
+
+            var expandAllPanelsButton = this.getExpandAllPanelsButton();
+            expandAllPanelsButton.click(function(e) {
+                this.expandAllPanels();
+                e.preventDefault();
+            }.bind(this));
+
+            expandAllPanelsButton.keydown(function(e) {
+                if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {
+                    if (e.keyCode === 13 || e.keyCode === 32) {
+                        this.expandAllPanels();
+                        e.preventDefault();
+                    }
+                }
+            }.bind(this));
+        }
+
+        $(document).on('user-changed', this._showActionsForm.bind(this));
+
+        this._region.find('[name="savechanges"]').on('click', this._trigger.bind(this, 'save-changes'));
+        this._region.find('[name="resetbutton"]').on('click', this._trigger.bind(this, 'reset'));
+        this._region.find('form').on('submit', function(e) {
+            e.preventDefault();
+        });
+    };
+
     return GradingActions;
 });
diff --git a/mod/assign/amd/src/grading_events.js b/mod/assign/amd/src/grading_events.js
new file mode 100644 (file)
index 0000000..a2336a8
--- /dev/null
@@ -0,0 +1,32 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Javascript controller for the "Actions" panel at the bottom of the page.
+ *
+ * @module     mod_assign/grading_events
+ * @package    mod_assign
+ * @copyright  2016 Ryan Wyllie <ryan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      3.1
+ */
+define(function() {
+    return {
+        COLLAPSE_REVIEW_PANEL: 'grading:collapse-review-panel',
+        EXPAND_REVIEW_PANEL: 'grading:expand-review-panel',
+        COLLAPSE_GRADE_PANEL: 'grading:collapse-grade-panel',
+        EXPAND_GRADE_PANEL: 'grading:expand-grade-panel',
+    };
+});
index 818d263..9d9e328 100644 (file)
@@ -24,8 +24,9 @@
  * @since      3.1
  */
 define(['jquery', 'core/notification', 'core/templates', 'core/fragment',
-        'core/ajax', 'core/str', 'mod_assign/grading_form_change_checker'],
-       function($, notification, templates, fragment, ajax, str, checker) {
+        'core/ajax', 'core/str', 'mod_assign/grading_form_change_checker',
+        'mod_assign/grading_events'],
+       function($, notification, templates, fragment, ajax, str, checker, GradingEvents) {
 
     /**
      * GradingPanel class.
@@ -38,11 +39,7 @@ define(['jquery', 'core/notification', 'core/templates', 'core/fragment',
         this._region = $(selector);
         this._userCache = [];
 
-        $(document).on('user-changed', this._refreshGradingPanel.bind(this));
-        $(document).on('save-changes', this._submitForm.bind(this));
-        $(document).on('reset', this._resetForm.bind(this));
-
-        $(document).on('save-form-state', this._saveFormState.bind(this));
+        this.registerEventListeners();
     };
 
     /** @type {String} Selector for the page region containing the user navigation. */
@@ -307,5 +304,61 @@ define(['jquery', 'core/notification', 'core/templates', 'core/fragment',
         }.bind(this)).fail(notification.exception);
     };
 
+    /**
+     * Get the grade panel element.
+     *
+     * @method getPanelElement
+     * @return {jQuery}
+     */
+    GradingPanel.prototype.getPanelElement = function() {
+        return $('[data-region="grade-panel"]');
+    };
+
+    /**
+     * Hide the grade panel.
+     *
+     * @method collapsePanel
+     */
+    GradingPanel.prototype.collapsePanel = function() {
+        this.getPanelElement().addClass('collapsed');
+    };
+
+    /**
+     * Show the grade panel.
+     *
+     * @method expandPanel
+     */
+    GradingPanel.prototype.expandPanel = function() {
+        this.getPanelElement().removeClass('collapsed');
+    };
+
+    /**
+     * Register event listeners for the grade panel.
+     *
+     * @method registerEventListeners
+     */
+    GradingPanel.prototype.registerEventListeners = function() {
+        var docElement = $(document);
+
+        docElement.on('user-changed', this._refreshGradingPanel.bind(this));
+        docElement.on('save-changes', this._submitForm.bind(this));
+        docElement.on('reset', this._resetForm.bind(this));
+
+        docElement.on('save-form-state', this._saveFormState.bind(this));
+
+        docElement.on(GradingEvents.COLLAPSE_GRADE_PANEL, function() {
+            this.collapsePanel();
+        }.bind(this));
+
+        // We should expand if the review panel is collapsed.
+        docElement.on(GradingEvents.COLLAPSE_REVIEW_PANEL, function() {
+            this.expandPanel();
+        }.bind(this));
+
+        docElement.on(GradingEvents.EXPAND_GRADE_PANEL, function() {
+            this.expandPanel();
+        }.bind(this));
+    };
+
     return GradingPanel;
 });
index fffe2e4..39a02d2 100644 (file)
@@ -23,7 +23,7 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  * @since      3.1
  */
-define(['jquery'], function($) {
+define(['jquery', 'mod_assign/grading_events'], function($, GradingEvents) {
 
     /**
      * GradingReviewPanel class.
@@ -32,7 +32,8 @@ define(['jquery'], function($) {
      * @param {String} selector The selector for the page region containing the user navigation.
      */
     var GradingReviewPanel = function() {
-        this._region = $('[data-region="review-panel"]');
+        this._region = $('[data-region="review-panel-content"]');
+        this.registerEventListeners();
     };
 
     /** @type {JQuery} JQuery node for the page region containing the user navigation. */
@@ -58,5 +59,109 @@ define(['jquery'], function($) {
         return false;
     };
 
+    /**
+     * Get the toggle review panel button.
+     *
+     * @method getTogglePanelButton
+     * @return {jQuery}
+     */
+    GradingReviewPanel.prototype.getTogglePanelButton = function() {
+        return this.getPanelElement().find('[data-region="review-panel-toggle"]');
+    };
+
+    /**
+     * Get the review panel element.
+     *
+     * @method getPanelElement
+     * @return {jQuery}
+     */
+    GradingReviewPanel.prototype.getPanelElement = function() {
+        return $('[data-region="review-panel"]');
+    };
+
+    /**
+     * Get the review panel content element.
+     *
+     * @method getPanelContentElement
+     * @return {jQuery}
+     */
+    GradingReviewPanel.prototype.getPanelContentElement = function() {
+        return $('[data-region="review-panel-content"]');
+    };
+
+    /**
+     * Show/Hide the review panel.
+     *
+     * @method togglePanel
+     */
+    GradingReviewPanel.prototype.togglePanel = function() {
+        if (this.getPanelElement().hasClass('collapsed')) {
+            $(document).trigger(GradingEvents.EXPAND_REVIEW_PANEL);
+        } else {
+            $(document).trigger(GradingEvents.COLLAPSE_REVIEW_PANEL);
+        }
+    };
+
+    /**
+     * Hide the review panel.
+     *
+     * @method collapsePanel
+     */
+    GradingReviewPanel.prototype.collapsePanel = function() {
+        this.getPanelElement().addClass('collapsed').removeClass('grade-panel-collapsed');
+        this.getPanelContentElement().attr('aria-hidden', true);
+    };
+
+    /**
+     * Show the review panel.
+     *
+     * @method expandPanel
+     */
+    GradingReviewPanel.prototype.expandPanel = function() {
+        this.getPanelElement().removeClass('collapsed');
+        this.getPanelContentElement().removeAttr('aria-hidden');
+    };
+
+    /**
+     * Register event listeners for the review panel.
+     *
+     * @method registerEventListeners
+     */
+    GradingReviewPanel.prototype.registerEventListeners = function() {
+        var toggleReviewPanelButton = this.getTogglePanelButton();
+        toggleReviewPanelButton.click(function(e) {
+            this.togglePanel();
+            e.preventDefault();
+        }.bind(this));
+
+        toggleReviewPanelButton.keydown(function(e) {
+            if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {
+                if (e.keyCode === 13 || e.keyCode === 32) {
+                    this.togglePanel();
+                    e.preventDefault();
+                }
+            }
+        }.bind(this));
+
+        var docElement = $(document);
+        docElement.on(GradingEvents.COLLAPSE_REVIEW_PANEL, function() {
+            this.collapsePanel();
+        }.bind(this));
+
+        // Need special styling when grade panel is collapsed.
+        docElement.on(GradingEvents.COLLAPSE_GRADE_PANEL, function() {
+            this.expandPanel();
+            this.getPanelElement().addClass('grade-panel-collapsed');
+        }.bind(this));
+
+        docElement.on(GradingEvents.EXPAND_REVIEW_PANEL, function() {
+            this.expandPanel();
+        }.bind(this));
+
+        docElement.on(GradingEvents.EXPAND_GRADE_PANEL, function() {
+            this.getPanelElement().removeClass('grade-panel-collapsed');
+        }.bind(this));
+    };
+
     return GradingReviewPanel;
 });
index 419e7c7..637b71e 100644 (file)
@@ -108,6 +108,9 @@ $string['changegradewarning'] = 'This assignment has graded submissions and chan
 $string['choosegradingaction'] = 'Grading action';
 $string['choosemarker'] = 'Choose...';
 $string['chooseoperation'] = 'Choose operation';
+$string['clickexpandreviewpanel'] = 'Click to expand review panel';
+$string['collapsegradepanel'] = 'Collapse grade panel';
+$string['collapsereviewpanel'] = 'Collapse review panel';
 $string['comment'] = 'Comment';
 $string['completionsubmit'] = 'Student must submit to this activity to complete it';
 $string['conversionexception'] = 'Could not convert assignment. Exception was: {$a}.';
@@ -127,6 +130,7 @@ $string['cutoffdatecolon'] = 'Cut-off date: {$a}';
 $string['cutoffdate_help'] = 'If set, the assignment will not accept submissions after this date without an extension.';
 $string['cutoffdatevalidation'] = 'The cut-off date cannot be earlier than the due date.';
 $string['cutoffdatefromdatevalidation'] = 'Cut-off date must be after the allow submissions from date.';
+$string['defaultlayout'] = 'Restore default layout';
 $string['defaultsettings'] = 'Default assignment settings';
 $string['defaultsettings_help'] = 'These settings define the defaults for all new assignments.';
 $string['defaultteam'] = 'Default group';
@@ -174,6 +178,7 @@ $string['eventsubmissionunlocked'] = 'The submissions have been unlocked for a u
 $string['eventsubmissionupdated'] = 'Submission updated.';
 $string['eventsubmissionviewed'] = 'Submission viewed.';
 $string['eventworkflowstateupdated'] = 'The state of the workflow has been updated.';
+$string['expandreviewpanel'] = 'Expand review panel';
 $string['extensionduedate'] = 'Extension due date';
 $string['extensionnotafterduedate'] = 'Extension date must be after the due date';
 $string['extensionnotafterfromdate'] = 'Extension date must be after the allow submissions from date';
index c3a8505..c838143 100644 (file)
     top: 85px;
     bottom: 60px;
     left: 0;
-    width: 70%;
+    right: 30%;
+    width: auto;
     box-sizing: border-box;
+    -webkit-transition: right 0.5s, left 0.5s;
+       -moz-transition: right 0.5s, left 0.5s;
+            transition: right 0.5s, left 0.5s;
+}
+
+.path-mod-assign [data-region="review-panel"].grade-panel-collapsed {
+    right: 30px;
+}
+
+.path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] {
+    display: none;
+    height: 30px;
+    width: 30px;
+    position: absolute;
+    top: 0;
+    right: 0;
+    left: auto;
+    box-sizing: border-box;
+    border-radius: 0 0 0 4px;
+    border: 1px solid #cccccc;
+    border-top: none;
+    background-color: #fff;
+    z-index: 99999;
+}
+
+.path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] .expand-icon,
+.path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] .collapse-icon {
+    width: 100%;
+    height: 100%;
+    text-align: center;
+    line-height: 30px;
+}
+
+.path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] .expand-icon .toggle-text,
+.path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] .collapse-icon .toggle-text {
+    visibility: hidden;
+    opacity: 0;
+    height: 0;
+    overflow: hidden;
+}
+
+.path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] img {
+    height: 100%;
+    float: right;
+}
+
+.path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] .expand-icon {
+    display: none;
+}
+
+.path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] .collapse-icon {
+    display: block;
+}
+
+.path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] .collapse-icon.full-width {
+    display: none;
 }
 
 .path-mod-assign [data-region="review-panel"] .pageheader {
     border-right: 1px solid #ddd;
 }
+.path-mod-assign [data-region="review-panel"] + [data-region="grade-panel"] [data-region="grade"] {
+    margin-left: auto;
+    margin-right: auto;
+    max-width: 100%;
+    -webkit-transition: max-width 0.5s;
+       -moz-transition: max-width 0.5s;
+            transition: max-width 0.5s;
+}
 
 .path-mod-assign [data-region="review-panel"] .drawingregion {
     left: 0;
     border-color: #ddd;
 }
 
-.path-mod-assign [data-region="grade-panel"].fullwidth {
+.path-mod-assign [data-region="review-panel"].collapsed {
+    left: calc(30px - 70%);
+    right: calc(100% - 30px);
+    -webkit-transition: right 0.5s, left 0.5s;
+       -moz-transition: right 0.5s, left 0.5s;
+            transition: right 0.5s, left 0.5s;
+}
+
+.path-mod-assign [data-region="review-panel"].collapsed [data-region="review-panel-content"] {
+    visibility: hidden;
+    -webkit-transition: visibility 0s 0.5s;
+       -moz-transition: visibility 0s 0.5s;
+            transition: visibility 0s 0.5s;
+}
+
+.path-mod-assign [data-region="review-panel"].collapsed [data-region="review-panel-toggle"] .expand-icon {
+    display: block;
+}
+
+.path-mod-assign [data-region="review-panel"].collapsed [data-region="review-panel-toggle"] .collapse-icon {
+    display: none;
+}
+
+.path-mod-assign [data-region="review-panel"].collapsed + [data-region="grade-panel"] {
     position: absolute;
-    top: 7em;
-    left: 0;
+    left: 30px;
     right: 0;
-    width: 99%;
+    width: calc(100% - 30px);
     overflow: auto;
-    bottom: 7em;
+    -webkit-transition: width 0.5s, right 0.5s, left 0.5s;
+       -moz-transition: width 0.5s, right 0.5s, left 0.5s;
+            transition: width 0.5s, right 0.5s, left 0.5s;
 }
 
+.path-mod-assign [data-region="review-panel"].collapsed + [data-region="grade-panel"] [data-region="grade"],
 .path-mod-assign [data-region="grade-panel"].fullwidth [data-region="grade"] {
     max-width: 800px;
     margin-left: auto;
     top: 85px;
     bottom: 60px;
     right: 0;
+    left: 70%;
     width: 30%;
     overflow: auto;
     box-sizing: border-box;
     background-color: #f5f5f5;
     padding: 15px;
     padding-top: 0px;
+    -webkit-transition: width 0.5s, right 0.5s, left 0.5s;
+       -moz-transition: width 0.5s, right 0.5s, left 0.5s;
+            transition: width 0.5s, right 0.5s, left 0.5s;
+}
+
+.path-mod-assign [data-region="grade-panel"].collapsed {
+    left: calc(100% - 30px);
+    right: calc(30px - 100%);
+    visibility: hidden;
+    -webkit-transition: right 0.5s, left 0.5s, visibility 0s 0.5s;
+       -moz-transition: right 0.5s, left 0.5s, visibility 0s 0.5s;
+            transition: right 0.5s, left 0.5s, visibility 0s 0.5s;
+}
+
+.path-mod-assign [data-region="grade-panel"].fullwidth {
+    left: 0;
+    width: 100%;
+    overflow: auto;
 }
 
 .path-mod-assign [data-region="grade-panel"] h3 {
     height: 60px;
 }
 
+.path-mod-assign [data-region="grade-actions-panel"] [data-region="grade-actions"] .collapse-buttons {
+    position: absolute;
+    top: 0;
+    left: auto;
+    right: 15px;
+    margin: 0;
+    height: 100%;
+    line-height: 60px;
+    direction: ltr;
+}
+
 .path-mod-assign [data-region="grade-actions"] {
     padding: 1em;
     text-align: center;
     .path-mod-assign .page-context-header .page-header-headings {
         margin-top: 13px;
     }
+    .path-mod-assign [data-region="grade-actions-panel"] [data-region="grade-actions"] .collapse-buttons {
+        display: none;
+    }
     .path-mod-assign [data-region="grading-navigation-panel"],
     .path-mod-assign [data-region="review-panel"],
     .path-mod-assign [data-region="grade-panel"],
+    .path-mod-assign [data-region="review-panel"].collapsed + [data-region="grade-panel"],
     .path-mod-assign [data-region="grade-actions-panel"] {
         position: inherit;
         width: 100%;
         top: 0;
         left: 0;
+        right: auto;
         overflow: auto;
         height: auto;
         margin-bottom: 1em;
     }
+    .path-mod-assign [data-region="grade-panel"].collapsed {
+        visibility: visible;
+    }
     .path-mod-assign [data-region="grading-navigation"] {
         padding: 0;
         text-align: center;
     .path-mod-assign [data-region="grade-panel"] [data-region="popout-button"] {
         display: none;
     }
+    .path-mod-assign [data-region="review-panel"] {
+        position: relative;
+        max-height: 2000px;
+        -webkit-transition: max-height 0.25s linear;
+           -moz-transition: max-height 0.25s linear;
+                transition: max-height 0.25s linear;
+    }
     .path-mod-assign [data-region="review-panel"] .pageheader {
         border-right: none;
+        padding-right: 20px;
+        padding-left: 40px;
+    }
+    .path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] {
+        display: block;
+        height: 20px;
+        width: 20px;
+        position: absolute;
+        top: 0;
+        left: 0;
+        right: auto;
+        box-sizing: border-box;
+        border: 1px solid #cccccc;
+        border-radius: 0 0 4px 0;
+        background-color: #fff;
+        z-index: 99999;
+    }
+    .path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] .expand-icon,
+    .path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] .collapse-icon {
+        width: 100%;
+        height: 100%;
+        text-align: center;
+        line-height: 30px;
+    }
+    .path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] .ltr-icon,
+    .path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] .rtl-icon {
+        height: 100%;
+    }
+    .path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] .ltr-icon {
+        float: left;
+    }
+    .path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] .rtl-icon {
+        float: right;
+    }
+    .path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] .expand-icon .toggle-text,
+    .path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] .collapse-icon .toggle-text {
+        line-height: 20px;
+        visibility: visible;
+        opacity: 1;
+        height: auto;
+        -webkit-transition: visibility 0s 0.25s, opacity 0s 0.25s;
+           -moz-transition: visibility 0s 0.25s, opacity 0s 0.25s;
+                transition: visibility 0s 0.25s, opacity 0s 0.25s;
+    }
+    .path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] .expand-icon .rtl-icon {
+        display: none;
+    }
+    .path-mod-assign [data-region="review-panel"].collapsed {
+        max-height: 20px;
+        top: 0;
+        left: 0;
+        overflow: hidden;
+        -webkit-transition: max-height 0.25s linear;
+           -moz-transition: max-height 0.25s linear;
+                transition: max-height 0.25s linear;
+    }
+    .path-mod-assign [data-region="review-panel"].collapsed [data-region="review-panel-toggle"] {
+        width: 100%;
+        border-radius: 0 0 0 0;
+        -webkit-transition: all 0s 0.25s;
+           -moz-transition: all 0s 0.25s;
+                transition: all 0s 0.25s;
+    }
+    .path-mod-assign [data-region="review-panel"].collapsed [data-region="review-panel-toggle"] img {
+        height: 100%;
+    }
+    .path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] .collapse-icon {
+        display: block;
+    }
+    .path-mod-assign [data-region="review-panel"].collapsed [data-region="review-panel-toggle"] .collapse-icon {
+        display: none;
     }
     .path-mod-assign.pagelayout-popup {
         overflow: inherit;
         float: none;
         margin: 0 auto 10px;
     }
+    .dir-rtl.path-mod-assign [data-region="review-panel"] .pageheader {
+        padding-right: 40px;
+        padding-left: 20px;
+    }
+    .dir-rtl.path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] {
+        left: auto;
+        right: 0;
+        border-radius: 0 0 0 4px;
+    }
+    .dir-rtl.path-mod-assign [data-region="review-panel"].collapsed {
+        right: 0;
+        left: auto;
+    }
+    .dir-rtl.path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] .expand-icon .ltr-icon {
+        display: none;
+    }
+    .dir-rtl.path-mod-assign [data-region="review-panel"] [data-region="review-panel-toggle"] .expand-icon .rtl-icon {
+        display: block;
+    }
+    .dir-rtl.path-mod-assign [data-region="review-panel"].collapsed [data-region="review-panel-toggle"] .collapse-icon {
+        display: none;
+    }
+    .dir-rtl.path-mod-assign [data-region="review-panel"].collapsed [data-region="review-panel-toggle"] .expand-icon {
+        display: block;
+    }
 }
 
 /** Start of CSS to make forms vertical in the grading panel (taken from theme/bootstrapbase/less/moodle/forms.less). */
index ab4e365..28c89f1 100644 (file)
     <button type="submit" class="btn btn-primary" name="savechanges">{{#str}}savechanges{{/str}}</button>
     <button type="submit" class="btn" name="resetbutton">{{#str}}reset{{/str}}</button>
 </form>
+{{#showreview}}
+<div class="btn-toolbar collapse-buttons">
+    <div class="btn-group">
+        <button class="btn collapse-review-panel">{{#pix}} layout-expand-right, mod_assign, {{#str}} collapsereviewpanel, mod_assign {{/str}} {{/pix}}</button>
+        <button class="btn collapse-none active">{{#pix}} layout-default, mod_assign, {{#str}} defaultlayout, mod_assign {{/str}} {{/pix}}</button>
+        <button class="btn collapse-grade-panel">{{#pix}} layout-expand-left, mod_assign, {{#str}} collapsegradepanel, mod_assign {{/str}} {{/pix}}</button>
+    </div>
+</div>
+{{/showreview}}
 {{#js}}
 require(['mod_assign/grading_actions'], function(GradingActions) {
     new GradingActions('[data-region="grade-actions"]');
index 62221f6..23f192b 100644 (file)
 <div data-region="grading-navigation-panel" data-first-userid="{{userid}}" data-courseid="{{courseid}}" data-showuseridentity="{{showuseridentity}}">
 {{> mod_assign/grading_navigation }}
 </div>
+{{#showreview}}
+<div data-region="review-panel">
+    <a href="#" data-region="review-panel-toggle">
+        <div class="collapse-icon">{{#pix}} t/expanded, core, {{#str}} collapsereviewpanel, mod_assign {{/str}} {{/pix}}</div>
+        <div class="expand-icon">
+            <div class="ltr-icon">{{#pix}} t/collapsed, core, {{#str}} expandreviewpanel, mod_assign {{/str}} {{/pix}}</div>
+            <div class="rtl-icon">{{#pix}} t/collapsed_rtl, core, {{#str}} expandreviewpanel, mod_assign {{/str}} {{/pix}}</div>
+            <div class="toggle-text">{{#str}} clickexpandreviewpanel, mod_assign {{/str}}</div>
+        </div>
+    </a>
+    <div data-region="review-panel-content">
+        <div data-region="review">
+            {{> mod_assign/review_panel }}
+        </div>
+    </div>
+</div>
+{{/showreview}}
 <div data-region="grade-panel" {{^showreview}}class="fullwidth"{{/showreview}}>
 <div data-region="grade" data-contextid="{{contextid}}" data-assignmentid="{{assignmentid}}">
 {{> mod_assign/grading_panel }}
 </div>
 </div>
-{{#showreview}}
-<div data-region="review-panel">
-<div data-region="review">
-{{> mod_assign/review_panel }}
-</div>
-</div>
-{{/showreview}}
 <div data-region="grade-actions-panel">
 <div data-region="grade-actions">
 {{> mod_assign/grading_actions }}