1 // This file is part of Moodle - http://moodle.org/
3 // Moodle is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
8 // Moodle is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17 * This module will tie together all of the different calls the gradable module will make.
19 * @module mod_forum/local/grades/local/grader/user_picker
21 * @copyright 2019 Mathew May <mathew.solutions>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 import Templates from 'core/templates';
26 import Selectors from './user_picker/selectors';
27 import {addIconToContainerWithPromise} from 'core/loadingicon';
29 const templatePath = 'mod_forum/local/grades/local/grader';
34 * Constructor for the User Picker.
36 * @param {Array} userList List of users
37 * @param {Function} showUserCallback The callback used to display the user
38 * @param {Function} preChangeUserCallback The callback to use before changing user
40 constructor(userList, showUserCallback, preChangeUserCallback) {
41 this.userList = userList;
42 this.showUserCallback = showUserCallback;
43 this.preChangeUserCallback = preChangeUserCallback;
44 this.currentUserIndex = 0;
46 // Ensure that render is bound correctly.
47 this.render = this.render.bind(this);
48 this.setUserId = this.setUserId.bind(this);
52 * Set the current userid without rendering the change.
53 * To show the user, call showUser too.
55 * @param {Number} userId
58 // Determine the current index based on the user ID.
59 const userIndex = this.userList.findIndex(user => {
60 return user.id === parseInt(userId);
63 if (userIndex === -1) {
64 throw Error(`User with id ${userId} not found`);
67 this.currentUserIndex = userIndex;
71 * Render the user picker.
74 // Create the root node.
75 this.root = document.createElement('div');
77 const {html, js} = await this.renderNavigator();
78 Templates.replaceNodeContents(this.root, html, js);
80 // Call the showUser function to show the first user immediately.
81 await this.showUser(this.currentUser);
83 // Ensure that the event listeners are all bound.
84 this.registerEventListeners();
88 * Render the navigator itself.
93 return Templates.renderForPromise(`${templatePath}/user_picker`, {});
97 * Render the current user details for the picker.
99 * @param {Object} context The data used to render the user picker.
102 renderUserChange(context) {
103 return Templates.renderForPromise(`${templatePath}/user_picker/user`, context);
107 * Show the specified user in the picker.
109 * @param {Object} user
111 async showUser(user) {
112 const [{html, js}] = await Promise.all([this.renderUserChange(user), this.showUserCallback(user)]);
113 const userRegion = this.root.querySelector(Selectors.regions.userRegion);
114 Templates.replaceNodeContents(userRegion, html, js);
118 * Register the event listeners for the user picker.
120 registerEventListeners() {
121 this.root.addEventListener('click', async e => {
122 const button = e.target.closest(Selectors.actions.changeUser);
124 const whole = document.querySelector('[data-region="unified-grader"]');
125 const spinner = addIconToContainerWithPromise(whole);
127 await this.preChangeUserCallback(this.currentUser);
128 this.updateIndex(parseInt(button.dataset.direction));
129 await this.showUser(this.currentUser);
137 * Update the current user index.
139 * @param {Number} direction
142 updateIndex(direction) {
143 this.currentUserIndex += direction;
145 // Loop around the edges.
146 if (this.currentUserIndex < 0) {
147 this.currentUserIndex = this.userList.length - 1;
148 } else if (this.currentUserIndex > this.userList.length - 1) {
149 this.currentUserIndex = 0;
152 return this.currentUserIndex;
156 * Get the details of the user currently shown with the total number of users, and the 1-indexed count of the
163 ...this.userList[this.currentUserIndex],
164 total: this.userList.length,
165 displayIndex: this.currentUserIndex + 1,
170 * Get the root node for the User Picker.
172 * @returns {HTMLElement}
180 * Create a new user picker.
182 * @param {Array} users The list of users
183 * @param {Function} showUserCallback The function to call to show a specific user
184 * @param {Function} preChangeUserCallback The fucntion to call to save the grade for the current user
185 * @param {Number} [currentUserID] The userid of the current user
186 * @returns {UserPicker}
188 export default async(users, showUserCallback, preChangeUserCallback, {
189 initialUserId = null,
192 const userPicker = new UserPicker(users, showUserCallback, preChangeUserCallback);
194 userPicker.setUserId(initialUserId);
196 await userPicker.render();