Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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/>.
15
 
16
/**
17
 * Module to handle dynamic table features.
18
 *
19
 * @module     core_table/dynamic
20
 * @copyright  2020 Simey Lameze <simey@moodle.com>
21
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22
 */
23
import * as Selectors from 'core_table/local/dynamic/selectors';
24
import Events from './local/dynamic/events';
25
import Pending from 'core/pending';
26
import {addIconToContainer} from 'core/loadingicon';
27
import {fetch as fetchTableData} from 'core_table/local/dynamic/repository';
28
import Notification from 'core/notification';
1441 ariadna 29
import {replaceNode} from 'core/templates';
1 efrain 30
 
31
let watching = false;
32
 
33
/**
34
 * Ensure that a table is a dynamic table.
35
 *
36
 * @param {HTMLElement} tableRoot
37
 * @returns {Bool}
38
 */
39
const checkTableIsDynamic = tableRoot => {
40
    if (!tableRoot) {
41
        // The table is not a dynamic table.
42
        throw new Error("The table specified is not a dynamic table and cannot be updated");
43
    }
44
 
45
    if (!tableRoot.matches(Selectors.main.region)) {
46
        // The table is not a dynamic table.
47
        throw new Error("The table specified is not a dynamic table and cannot be updated");
48
    }
49
 
50
    return true;
51
};
52
 
53
/**
54
 * Get the filterset data from a known dynamic table.
55
 *
56
 * @param {HTMLElement} tableRoot
57
 * @returns {Object}
58
 */
59
const getFiltersetFromTable = tableRoot => {
60
    return JSON.parse(tableRoot.dataset.tableFilters);
61
};
62
 
63
/**
64
 * Update the specified table based on its current values.
65
 *
66
 * @param {HTMLElement} tableRoot
67
 * @param {Bool} resetContent
68
 * @returns {Promise}
69
 */
70
export const refreshTableContent = (tableRoot, resetContent = false) => {
71
    const filterset = getFiltersetFromTable(tableRoot);
72
    addIconToContainer(tableRoot);
73
 
74
    const pendingPromise = new Pending('core_table/dynamic:refreshTableContent');
75
 
76
    return fetchTableData(
77
        tableRoot.dataset.tableComponent,
78
        tableRoot.dataset.tableHandler,
79
        tableRoot.dataset.tableUniqueid,
80
        {
81
            sortData: JSON.parse(tableRoot.dataset.tableSortData),
82
            joinType: filterset.jointype,
83
            filters: filterset.filters,
84
            firstinitial: tableRoot.dataset.tableFirstInitial,
85
            lastinitial: tableRoot.dataset.tableLastInitial,
86
            pageNumber: tableRoot.dataset.tablePageNumber,
87
            pageSize: tableRoot.dataset.tablePageSize,
88
            hiddenColumns: JSON.parse(tableRoot.dataset.tableHiddenColumns),
89
        },
90
        resetContent,
91
    )
92
    .then(data => {
1441 ariadna 93
        const tableRootReplacement = replaceNode(tableRoot, data.html, '');
1 efrain 94
 
95
        // Update the tableRoot.
1441 ariadna 96
        return tableRootReplacement[0];
1 efrain 97
    }).then(tableRoot => {
98
        tableRoot.dispatchEvent(new CustomEvent(Events.tableContentRefreshed, {
99
            bubbles: true,
100
        }));
101
 
102
        return tableRoot;
103
    })
104
    .then(tableRoot => {
105
        pendingPromise.resolve();
106
 
107
        return tableRoot;
108
    });
109
};
110
 
111
export const updateTable = (tableRoot, {
112
    sortBy = null,
113
    sortOrder = null,
114
    filters = null,
115
    firstInitial = null,
116
    lastInitial = null,
117
    pageNumber = null,
118
    pageSize = null,
119
    hiddenColumns = null,
120
} = {}, refreshContent = true) => {
121
    checkTableIsDynamic(tableRoot);
122
 
123
    const pendingPromise = new Pending('core_table/dynamic:updateTable');
124
    let tableConfigChanged = false;
125
 
126
    // Update sort fields.
127
    if (sortBy && sortOrder) {
128
        // Always update the table if requested and there were sort fields.
129
        // These fields are only ever normalised in the backend.
130
        tableConfigChanged = true;
131
 
132
        const sortData = JSON.parse(tableRoot.dataset.tableSortData);
133
        sortData.unshift({
134
            sortby: sortBy,
135
            sortorder: parseInt(sortOrder, 10),
136
        });
137
        tableRoot.dataset.tableSortData = JSON.stringify(sortData);
138
    }
139
 
140
    // Update initials.
141
    if (firstInitial !== null) {
142
        if (tableRoot.dataset.tableFirstInitial !== firstInitial) {
143
            tableConfigChanged = true;
144
        }
145
 
146
        tableRoot.dataset.tableFirstInitial = firstInitial;
147
    }
148
 
149
    if (lastInitial !== null) {
150
        if (tableRoot.dataset.tableLastInitial !== lastInitial) {
151
            tableConfigChanged = true;
152
        }
153
 
154
        tableRoot.dataset.tableLastInitial = lastInitial;
155
    }
156
 
157
    if (pageSize !== null) {
158
        if (tableRoot.dataset.tablePageSize != pageSize) {
159
            tableConfigChanged = true;
160
        }
161
 
162
        tableRoot.dataset.tablePageSize = pageSize;
163
    }
164
 
165
    // Update filters.
166
    if (filters) {
167
        const filterJson = JSON.stringify(filters);
168
 
169
        if (tableRoot.dataset.tableFilters !== filterJson) {
170
            tableConfigChanged = true;
171
        }
172
 
173
        tableRoot.dataset.tableFilters = filterJson;
174
    }
175
 
176
    // Reset to page 1 when table content is being altered by filtering or sorting.
177
    // This ensures the table page being loaded always exists, and gives a consistent experience.
178
    if (tableConfigChanged) {
179
        pageNumber = 1;
180
    }
181
 
182
    // Update hidden columns.
183
    if (hiddenColumns) {
184
        const columnJson = JSON.stringify(hiddenColumns);
185
 
186
        if (tableRoot.dataset.tableHiddenColumns !== columnJson) {
187
            tableConfigChanged = true;
188
        }
189
 
190
        tableRoot.dataset.tableHiddenColumns = columnJson;
191
    }
192
 
193
    if (pageNumber !== null) {
194
        if (tableRoot.dataset.tablePageNumber != pageNumber) {
195
            tableConfigChanged = true;
196
        }
197
 
198
        tableRoot.dataset.tablePageNumber = pageNumber;
199
    }
200
 
201
    // Refresh.
202
    if (refreshContent && tableConfigChanged) {
203
        return refreshTableContent(tableRoot)
204
        .then(tableRoot => {
205
            pendingPromise.resolve();
206
            return tableRoot;
207
        });
208
    } else {
209
        pendingPromise.resolve();
210
        return Promise.resolve(tableRoot);
211
    }
212
};
213
 
214
/**
215
 * Get the table dataset for the specified tableRoot, ensuring that the provided table is a dynamic table.
216
 *
217
 * @param {HTMLElement} tableRoot
218
 * @returns {DOMStringMap}
219
 */
220
const getTableData = tableRoot => {
221
    checkTableIsDynamic(tableRoot);
222
 
223
    return tableRoot.dataset;
224
};
225
 
226
/**
227
 * Update the specified table using the new filters.
228
 *
229
 * @param {HTMLElement} tableRoot
230
 * @param {Object} filters
231
 * @param {Bool} refreshContent
232
 * @returns {Promise}
233
 */
234
export const setFilters = (tableRoot, filters, refreshContent = true) =>
235
    updateTable(tableRoot, {filters}, refreshContent);
236
 
237
/**
238
 * Get the filter data for the specified table.
239
 *
240
 * @param {HTMLElement} tableRoot
241
 * @returns {Object}
242
 */
243
export const getFilters = tableRoot => {
244
    checkTableIsDynamic(tableRoot);
245
 
246
    return getFiltersetFromTable(tableRoot);
247
};
248
 
249
/**
250
 * Update the sort order.
251
 *
252
 * @param {HTMLElement} tableRoot
253
 * @param {String} sortBy
254
 * @param {Number} sortOrder
255
 * @param {Bool} refreshContent
256
 * @returns {Promise}
257
 */
258
export const setSortOrder = (tableRoot, sortBy, sortOrder, refreshContent = true) =>
259
    updateTable(tableRoot, {sortBy, sortOrder}, refreshContent);
260
 
261
/**
262
 * Set the page number.
263
 *
264
 * @param {HTMLElement} tableRoot
265
 * @param {String} pageNumber
266
 * @param {Bool} refreshContent
267
 * @returns {Promise}
268
 */
269
export const setPageNumber = (tableRoot, pageNumber, refreshContent = true) =>
270
    updateTable(tableRoot, {pageNumber}, refreshContent);
271
 
272
/**
273
 * Get the current page number.
274
 *
275
 * @param {HTMLElement} tableRoot
276
 * @returns {Number}
277
 */
278
export const getPageNumber = tableRoot => getTableData(tableRoot).tablePageNumber;
279
 
280
/**
281
 * Set the page size.
282
 *
283
 * @param {HTMLElement} tableRoot
284
 * @param {Number} pageSize
285
 * @param {Bool} refreshContent
286
 * @returns {Promise}
287
 */
288
export const setPageSize = (tableRoot, pageSize, refreshContent = true) =>
289
    updateTable(tableRoot, {pageSize, pageNumber: 1}, refreshContent);
290
 
291
/**
292
 * Get the current page size.
293
 *
294
 * @param {HTMLElement} tableRoot
295
 * @returns {Number}
296
 */
297
export const getPageSize = tableRoot => getTableData(tableRoot).tablePageSize;
298
 
299
/**
300
 * Update the first initial to show.
301
 *
302
 * @param {HTMLElement} tableRoot
303
 * @param {String} firstInitial
304
 * @param {Bool} refreshContent
305
 * @returns {Promise}
306
 */
307
export const setFirstInitial = (tableRoot, firstInitial, refreshContent = true) =>
308
    updateTable(tableRoot, {firstInitial}, refreshContent);
309
 
310
/**
311
 * Get the current first initial filter.
312
 *
313
 * @param {HTMLElement} tableRoot
314
 * @returns {String}
315
 */
316
export const getFirstInitial = tableRoot => getTableData(tableRoot).tableFirstInitial;
317
 
318
/**
319
 * Update the last initial to show.
320
 *
321
 * @param {HTMLElement} tableRoot
322
 * @param {String} lastInitial
323
 * @param {Bool} refreshContent
324
 * @returns {Promise}
325
 */
326
export const setLastInitial = (tableRoot, lastInitial, refreshContent = true) =>
327
    updateTable(tableRoot, {lastInitial}, refreshContent);
328
 
329
/**
330
 * Get the current last initial filter.
331
 *
332
 * @param {HTMLElement} tableRoot
333
 * @returns {String}
334
 */
335
export const getLastInitial = tableRoot => getTableData(tableRoot).tableLastInitial;
336
 
337
/**
338
 * Hide a column in the participants table.
339
 *
340
 * @param {HTMLElement} tableRoot
341
 * @param {String} columnToHide
342
 * @param {Bool} refreshContent
343
 * @returns {Promise}
344
 */
345
export const hideColumn = (tableRoot, columnToHide, refreshContent = true) => {
346
    const hiddenColumns = JSON.parse(tableRoot.dataset.tableHiddenColumns);
347
    hiddenColumns.push(columnToHide);
348
 
349
    return updateTable(tableRoot, {hiddenColumns}, refreshContent);
350
};
351
 
352
/**
353
 * Make a hidden column visible in the participants table.
354
 *
355
 * @param {HTMLElement} tableRoot
356
 * @param {String} columnToShow
357
 * @param {Bool} refreshContent
358
 * @returns {Promise}
359
 */
360
export const showColumn = (tableRoot, columnToShow, refreshContent = true) => {
361
    let hiddenColumns = JSON.parse(tableRoot.dataset.tableHiddenColumns);
362
    hiddenColumns = hiddenColumns.filter(columnName => columnName !== columnToShow);
363
 
364
    return updateTable(tableRoot, {hiddenColumns}, refreshContent);
365
};
366
 
367
/**
368
 * Reset table preferences.
369
 *
370
 * @param {HTMLElement} tableRoot
371
 * @returns {Promise}
372
 */
373
const resetTablePreferences = tableRoot => refreshTableContent(tableRoot, true);
374
 
375
/**
376
 * Set up listeners to handle table updates.
377
 */
378
export const init = () => {
379
    if (watching) {
380
        // Already watching.
381
        return;
382
    }
383
    watching = true;
384
 
385
    document.addEventListener('click', e => {
386
        const tableRoot = e.target.closest(Selectors.main.region);
387
 
388
        if (!tableRoot) {
389
            return;
390
        }
391
 
392
        const sortableLink = e.target.closest(Selectors.table.links.sortableColumn);
393
        if (sortableLink) {
394
            e.preventDefault();
395
 
396
            setSortOrder(tableRoot, sortableLink.dataset.sortby, sortableLink.dataset.sortorder)
397
            .catch(Notification.exception);
398
        }
399
 
400
        const firstInitialLink = e.target.closest(Selectors.initialsBar.links.firstInitial);
401
        if (firstInitialLink !== null) {
402
            e.preventDefault();
403
 
404
            setFirstInitial(tableRoot, firstInitialLink.dataset.initial).catch(Notification.exception);
405
        }
406
 
407
        const lastInitialLink = e.target.closest(Selectors.initialsBar.links.lastInitial);
408
        if (lastInitialLink !== null) {
409
            e.preventDefault();
410
 
411
            setLastInitial(tableRoot, lastInitialLink.dataset.initial).catch(Notification.exception);
412
        }
413
 
414
        const pageItem = e.target.closest(Selectors.paginationBar.links.pageItem);
415
        if (pageItem) {
416
            e.preventDefault();
417
 
418
            setPageNumber(tableRoot, pageItem.dataset.pageNumber).catch(Notification.exception);
419
        }
420
 
421
        const hide = e.target.closest(Selectors.table.links.hide);
422
        if (hide) {
423
            e.preventDefault();
424
 
425
            hideColumn(tableRoot, hide.dataset.column).catch(Notification.exception);
426
        }
427
 
428
        const show = e.target.closest(Selectors.table.links.show);
429
        if (show) {
430
            e.preventDefault();
431
 
432
            showColumn(tableRoot, show.dataset.column).catch(Notification.exception);
433
        }
434
 
435
        const resetTablePreferencesLink = e.target.closest('.resettable a');
436
        if (resetTablePreferencesLink) {
437
            e.preventDefault();
438
 
439
            resetTablePreferences(tableRoot).catch(Notification.exception);
440
        }
441
 
442
        const showCountLink = e.target.closest(Selectors.showCount.links.toggle);
443
        if (showCountLink) {
444
            e.preventDefault();
445
 
446
            setPageSize(tableRoot, showCountLink.dataset.targetPageSize).catch(Notification.exception);
447
        }
448
    });
449
};
450
 
451
/**
452
 * Fetch the table via its table region id.
453
 *
454
 * @param {String} tableRegionId
455
 * @returns {HTMLElement}
456
 */
457
export const getTableFromId = tableRegionId => {
458
    const tableRoot = document.querySelector(Selectors.main.fromRegionId(tableRegionId));
459
 
460
 
461
    if (!tableRoot) {
462
        // The table is not a dynamic table.
463
        throw new Error("The table specified is not a dynamic table and cannot be updated");
464
    }
465
 
466
    return tableRoot;
467
};
468
 
469
export {
470
    Events
471
};