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
 * Manage the timeline view navigation for the timeline block.
18
 *
19
 * @copyright  2018 Ryan Wyllie <ryan@moodle.com>
20
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
21
 */
22
 
23
import $ from 'jquery';
24
import * as CustomEvents from 'core/custom_interaction_events';
25
import * as View from 'block_timeline/view';
26
import * as Notification from 'core/notification';
27
import * as Utils from 'core/utils';
28
import * as UserRepository from 'core_user/repository';
1441 ariadna 29
import {getFirst} from 'core/normalise';
1 efrain 30
 
31
const SELECTORS = {
32
    TIMELINE_DAY_FILTER: '[data-region="day-filter"]',
33
    TIMELINE_DAY_FILTER_OPTION: '[data-from]',
34
    TIMELINE_VIEW_SELECTOR: '[data-region="view-selector"]',
35
    DATA_DAYS_OFFSET: '[data-days-offset]',
36
    DATA_DAYS_LIMIT: '[data-days-limit]',
37
    TIMELINE_SEARCH_INPUT: '[data-action="search"]',
38
    TIMELINE_SEARCH_CLEAR_ICON: '[data-action="clearsearch"]',
39
    NO_COURSES_EMPTY_MESSAGE: '[data-region="no-courses-empty-message"]',
40
};
41
 
42
/**
43
 * Event listener for the day selector ("Next 7 days", "Next 30 days", etc).
44
 *
45
 * @param {object} root The root element for the timeline block
46
 * @param {object} timelineViewRoot The root element for the timeline view
47
 */
48
const registerTimelineDaySelector = function(root, timelineViewRoot) {
49
    const timelineDaySelectorContainer = root.find(SELECTORS.TIMELINE_DAY_FILTER);
50
 
51
    CustomEvents.define(timelineDaySelectorContainer, [CustomEvents.events.activate]);
52
    timelineDaySelectorContainer.on(
53
        CustomEvents.events.activate,
54
        SELECTORS.TIMELINE_DAY_FILTER_OPTION,
55
        function(e, data) {
56
            // Update the user preference
57
            var filtername = $(e.currentTarget).data('filtername');
58
            var type = 'block_timeline_user_filter_preference';
59
            UserRepository.setUserPreference(type, filtername)
60
                .catch(Notification.exception);
61
 
62
            var option = $(e.target).closest(SELECTORS.TIMELINE_DAY_FILTER_OPTION);
63
 
64
            if (option.attr('aria-current') == 'true') {
65
                // If it's already active then we don't need to do anything.
66
                return;
67
            }
68
 
69
            var daysOffset = option.attr('data-from');
70
            var daysLimit = option.attr('data-to');
71
            var elementsWithDaysOffset = root.find(SELECTORS.DATA_DAYS_OFFSET);
72
 
73
            elementsWithDaysOffset.attr('data-days-offset', daysOffset);
74
 
75
            if (daysLimit != undefined) {
76
                elementsWithDaysOffset.attr('data-days-limit', daysLimit);
77
            } else {
78
                elementsWithDaysOffset.removeAttr('data-days-limit');
79
            }
80
 
81
            if (option.attr('data-filtername') === 'overdue') {
82
                elementsWithDaysOffset.attr('data-filter-overdue', true);
83
            } else {
84
                elementsWithDaysOffset.removeAttr('data-filter-overdue');
85
            }
86
 
87
            // Reset the views to reinitialise the event lists now that we've
88
            // updated the day limits.
89
            View.reset(timelineViewRoot);
90
 
91
            data.originalEvent.preventDefault();
92
        }
93
    );
94
};
95
 
96
/**
97
 * Event listener for the "sort" button in the timeline navigation that allows for
98
 * changing between the timeline dates and courses views.
99
 *
100
 * On a view change we tell the timeline view module that the view has been shown
101
 * so that it can handle how to display the appropriate view.
102
 *
103
 * @param {object} root The root element for the timeline block
104
 * @param {object} timelineViewRoot The root element for the timeline view
105
 */
106
const registerViewSelector = function(root, timelineViewRoot) {
107
    const viewSelector = root.find(SELECTORS.TIMELINE_VIEW_SELECTOR);
108
 
109
    // Listen for when the user changes tab so that we can show the first set of courses
110
    // and load their events when they request the sort by courses view for the first time.
1441 ariadna 111
    getFirst(viewSelector).querySelectorAll('[data-bs-toggle="tab"]').forEach((tab) => {
112
        tab.addEventListener('shown.bs.tab', (e) => {
113
            View.shown(timelineViewRoot);
114
            $(e.target).removeClass('active');
115
        });
1 efrain 116
    });
117
 
118
 
119
    // Event selector for user_sort
120
    CustomEvents.define(viewSelector, [CustomEvents.events.activate]);
1441 ariadna 121
    viewSelector.on(CustomEvents.events.activate, "[data-bs-toggle='tab']", function(e) {
1 efrain 122
        var filtername = $(e.currentTarget).data('filtername');
123
        var type = 'block_timeline_user_sort_preference';
124
        UserRepository.setUserPreference(type, filtername)
125
            .catch(Notification.exception);
126
    });
127
};
128
 
129
/**
130
 * Event listener for the "search" input field in the timeline navigation that allows for
131
 * searching the activity name, course name and activity type.
132
 *
133
 * @param {object} root The root element for the timeline block
134
 * @param {object} timelineViewRoot The root element for the timeline view
135
 */
136
const registerSearch = (root, timelineViewRoot) => {
137
    const searchInput = root.find(SELECTORS.TIMELINE_SEARCH_INPUT);
138
    const clearSearchIcon = root.find(SELECTORS.TIMELINE_SEARCH_CLEAR_ICON);
139
    searchInput.on('input', Utils.debounce(() => {
140
        if (searchInput.val() !== '') {
141
            activeSearchState(clearSearchIcon, timelineViewRoot);
142
        } else {
143
            clearSearchState(clearSearchIcon, timelineViewRoot);
144
        }
145
    }, 1000));
146
    clearSearchIcon.on('click', () => {
147
        searchInput.val('');
148
        clearSearchState(clearSearchIcon, timelineViewRoot);
149
        searchInput.focus();
150
    });
151
};
152
 
153
/**
154
 * Show the clear search icon.
155
 *
156
 * @param {object} clearSearchIcon Clear search icon element.
157
 * @param {object} timelineViewRoot The root element for the timeline view
158
 */
159
const activeSearchState = (clearSearchIcon, timelineViewRoot) => {
160
    clearSearchIcon.removeClass('d-none');
161
    View.reset(timelineViewRoot);
162
};
163
 
164
/**
165
 * Hide the clear search icon.
166
 *
167
 * @param {object} clearSearchIcon Clear search icon element.
168
 * @param {object} timelineViewRoot The root element for the timeline view
169
 */
170
const clearSearchState = (clearSearchIcon, timelineViewRoot) => {
171
    clearSearchIcon.addClass('d-none');
172
    View.reset(timelineViewRoot);
173
};
174
 
175
/**
176
 * Initialise the timeline view navigation by adding event listeners to
177
 * the navigation elements.
178
 *
179
 * @param {jQuery|HTMLElement} root The root element for the timeline block
180
 * @param {object} timelineViewRoot The root element for the timeline view
181
 */
182
export const init = function(root, timelineViewRoot) {
183
    root = $(root);
184
 
185
    registerViewSelector(root, timelineViewRoot);
186
 
187
    // Only need to handle filtering if the user is actively enrolled in a course.
188
    if (!root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).length) {
189
        registerTimelineDaySelector(root, timelineViewRoot);
190
        registerSearch(root, timelineViewRoot);
191
    }
192
};