aa1d030137b224b594663f44bd5062c00430b1c6
[moodle.git] / lib / table / amd / src / dynamic.js
1 // This file is part of Moodle - http://moodle.org/
2 //
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.
7 //
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.
12 //
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/>.
16 /**
17  * Module to handle dynamic table features.
18  *
19  * @module     core_table/dynamic
20  * @package    core_table
21  * @copyright  2020 Simey Lameze <simey@moodle.com>
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
24 import {fetch as fetchTableData} from 'core_table/local/dynamic/repository';
25 import * as Selectors from 'core_table/local/dynamic/selectors';
26 import Events from './local/dynamic/events';
28 let watching = false;
30 /**
31  * Ensure that a table is a dynamic table.
32  *
33  * @param {HTMLElement} tableRoot
34  * @returns {Bool}
35  */
36 const checkTableIsDynamic = tableRoot => {
37     if (!tableRoot) {
38         // The table is not a dynamic table.
39         throw new Error("The table specified is not a dynamic table and cannot be updated");
40     }
42     if (!tableRoot.matches(Selectors.main.region)) {
43         // The table is not a dynamic table.
44         throw new Error("The table specified is not a dynamic table and cannot be updated");
45     }
47     return true;
48 };
50 /**
51  * Get the filterset data from a known dynamic table.
52  *
53  * @param {HTMLElement} tableRoot
54  * @returns {Object}
55  */
56 const getFiltersetFromTable = tableRoot => {
57     return JSON.parse(tableRoot.dataset.tableFilters);
58 };
60 /**
61  * Update the specified table based on its current values.
62  *
63  * @param {HTMLElement} tableRoot
64  * @returns {Promise}
65  */
66 export const refreshTableContent = tableRoot => {
67     const filterset = getFiltersetFromTable(tableRoot);
69     return fetchTableData(
70         tableRoot.dataset.tableComponent,
71         tableRoot.dataset.tableHandler,
72         tableRoot.dataset.tableUniqueid,
73         {
74             sortBy: tableRoot.dataset.tableSortBy,
75             sortOrder: tableRoot.dataset.tableSortOrder,
76             joinType: filterset.jointype,
77             filters: filterset.filters,
78             firstinitial: tableRoot.dataset.tableFirstInitial,
79             lastinitial: tableRoot.dataset.tableLastInitial,
80             pageNumber: tableRoot.dataset.tablePageNumber,
81             pageSize: tableRoot.dataset.tablePageSize,
82             hiddenColumns: JSON.parse(tableRoot.dataset.tableHiddenColumns),
83         }
84     )
85     .then(data => {
86         const placeholder = document.createElement('div');
87         placeholder.innerHTML = data.html;
88         tableRoot.replaceWith(...placeholder.childNodes);
90         // Update the tableRoot.
91         return getTableFromId(tableRoot.dataset.tableUniqueid);
92     }).then(tableRoot => {
93         tableRoot.dispatchEvent(new CustomEvent(Events.tableContentRefreshed, {
94             bubbles: true,
95         }));
97         return tableRoot;
98     });
99 };
101 export const updateTable = (tableRoot, {
102     sortBy = null,
103     sortOrder = null,
104     filters = null,
105     firstInitial = null,
106     lastInitial = null,
107     pageNumber = null,
108     pageSize = null,
109     hiddenColumns = null,
110 } = {}, refreshContent = true) => {
111     checkTableIsDynamic(tableRoot);
113     // Update sort fields.
114     if (sortBy && sortOrder) {
115         tableRoot.dataset.tableSortBy = sortBy;
116         tableRoot.dataset.tableSortOrder = sortOrder;
117     }
119     // Update initials.
120     if (firstInitial !== null) {
121         tableRoot.dataset.tableFirstInitial = firstInitial;
122     }
124     if (lastInitial !== null) {
125         tableRoot.dataset.tableLastInitial = lastInitial;
126     }
128     if (pageNumber !== null) {
129         tableRoot.dataset.tablePageNumber = pageNumber;
130     }
132     if (pageSize !== null) {
133         tableRoot.dataset.tablePageSize = pageSize;
134     }
136     // Update filters.
137     if (filters) {
138         tableRoot.dataset.tableFilters = JSON.stringify(filters);
139     }
141     // Update hidden columns.
142     if (hiddenColumns) {
143         tableRoot.dataset.tableHiddenColumns = JSON.stringify(hiddenColumns);
144     }
146     // Refresh.
147     if (refreshContent) {
148         return refreshTableContent(tableRoot);
149     } else {
150         return Promise.resolve(tableRoot);
151     }
152 };
154 /**
155  * Update the specified table using the new filters.
156  *
157  * @param {HTMLElement} tableRoot
158  * @param {Object} filters
159  * @param {Bool} refreshContent
160  * @returns {Promise}
161  */
162 export const setFilters = (tableRoot, filters, refreshContent = true) =>
163     updateTable(tableRoot, {filters}, refreshContent);
165 /**
166  * Update the sort order.
167  *
168  * @param {HTMLElement} tableRoot
169  * @param {String} sortBy
170  * @param {Number} sortOrder
171  * @param {Bool} refreshContent
172  * @returns {Promise}
173  */
174 export const setSortOrder = (tableRoot, sortBy, sortOrder, refreshContent = true) =>
175     updateTable(tableRoot, {sortBy, sortOrder}, refreshContent);
177 /**
178  * Set the page number.
179  *
180  * @param {HTMLElement} tableRoot
181  * @param {String} pageNumber
182  * @param {Bool} refreshContent
183  * @returns {Promise}
184  */
185 export const setPageNumber = (tableRoot, pageNumber, refreshContent = true) =>
186     updateTable(tableRoot, {pageNumber}, refreshContent);
188 /**
189  * Set the page size.
190  *
191  * @param {HTMLElement} tableRoot
192  * @param {Number} pageSize
193  * @param {Bool} refreshContent
194  * @returns {Promise}
195  */
196 export const setPageSize = (tableRoot, pageSize, refreshContent = true) =>
197     updateTable(tableRoot, {pageSize, pageNumber: 0}, refreshContent);
199 /**
200  * Update the first initial to show.
201  *
202  * @param {HTMLElement} tableRoot
203  * @param {String} firstInitial
204  * @param {Bool} refreshContent
205  * @returns {Promise}
206  */
207 export const setFirstInitial = (tableRoot, firstInitial, refreshContent = true) =>
208     updateTable(tableRoot, {firstInitial}, refreshContent);
210 /**
211  * Update the last initial to show.
212  *
213  * @param {HTMLElement} tableRoot
214  * @param {String} lastInitial
215  * @param {Bool} refreshContent
216  * @returns {Promise}
217  */
218 export const setLastInitial = (tableRoot, lastInitial, refreshContent = true) =>
219     updateTable(tableRoot, {lastInitial}, refreshContent);
221 /**
222  * Hide a column in the participants table.
223  *
224  * @param {HTMLElement} tableRoot
225  * @param {String} columnToHide
226  * @param {Bool} refreshContent
227  */
228 export const hideColumn = (tableRoot, columnToHide, refreshContent = true) => {
229     const hiddenColumns = JSON.parse(tableRoot.dataset.tableHiddenColumns);
230     hiddenColumns.push(columnToHide);
232     updateTable(tableRoot, {hiddenColumns}, refreshContent);
233 };
235 /**
236  * Make a hidden column visible in the participants table.
237  *
238  * @param {HTMLElement} tableRoot
239  * @param {String} columnToShow
240  * @param {Bool} refreshContent
241  */
242 export const showColumn = (tableRoot, columnToShow, refreshContent = true) => {
243     let hiddenColumns = JSON.parse(tableRoot.dataset.tableHiddenColumns);
244     hiddenColumns = hiddenColumns.filter(columnName => columnName !== columnToShow);
246     updateTable(tableRoot, {hiddenColumns}, refreshContent);
247 };
249 /**
250  * Set up listeners to handle table updates.
251  */
252 export const init = () => {
253     if (watching) {
254         // Already watching.
255         return;
256     }
257     watching = true;
259     document.addEventListener('click', e => {
260         const tableRoot = e.target.closest(Selectors.main.region);
262         if (!tableRoot) {
263             return;
264         }
266         const sortableLink = e.target.closest(Selectors.table.links.sortableColumn);
267         if (sortableLink) {
268             e.preventDefault();
270             setSortOrder(tableRoot, sortableLink.dataset.sortby, sortableLink.dataset.sortorder);
271         }
273         const firstInitialLink = e.target.closest(Selectors.initialsBar.links.firstInitial);
274         if (firstInitialLink !== null) {
275             e.preventDefault();
277             setFirstInitial(tableRoot, firstInitialLink.dataset.initial);
278         }
280         const lastInitialLink = e.target.closest(Selectors.initialsBar.links.lastInitial);
281         if (lastInitialLink !== null) {
282             e.preventDefault();
284             setLastInitial(tableRoot, lastInitialLink.dataset.initial);
285         }
287         const pageItem = e.target.closest(Selectors.paginationBar.links.pageItem);
288         if (pageItem) {
289             e.preventDefault();
291             setPageNumber(tableRoot, pageItem.dataset.pageNumber);
292         }
294         const hide = e.target.closest(Selectors.table.links.hide);
295         if (hide) {
296             e.preventDefault();
298             hideColumn(tableRoot, hide.dataset.column);
299         }
301         const show = e.target.closest(Selectors.table.links.show);
302         if (show) {
303             e.preventDefault();
305             showColumn(tableRoot, show.dataset.column);
306         }
308     });
309 };
311 /**
312  * Fetch the table via its table region id.
313  *
314  * @param {String} tableRegionId
315  * @returns {HTMLElement}
316  */
317 export const getTableFromId = tableRegionId => {
318     const tableRoot = document.querySelector(Selectors.main.fromRegionId(tableRegionId));
321     if (!tableRoot) {
322         // The table is not a dynamic table.
323         throw new Error("The table specified is not a dynamic table and cannot be updated");
324     }
326     return tableRoot;
327 };
329 export {
330     Events
331 };