Proyectos de Subversion Moodle

Rev

Ir a la última revisión | | 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
 * Course section format component.
18
 *
19
 * @module     core_courseformat/local/content/section
20
 * @class      core_courseformat/local/content/section
21
 * @copyright  2021 Ferran Recio <ferran@moodle.com>
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
import Header from 'core_courseformat/local/content/section/header';
26
import DndSection from 'core_courseformat/local/courseeditor/dndsection';
27
import Templates from 'core/templates';
28
 
29
export default class extends DndSection {
30
 
31
    /**
32
     * Constructor hook.
33
     */
34
    create() {
35
        // Optional component name for debugging.
36
        this.name = 'content_section';
37
        // Default query selectors.
38
        this.selectors = {
39
            SECTION_ITEM: `[data-for='section_title']`,
40
            CM: `[data-for="cmitem"]`,
41
            SECTIONINFO: `[data-for="sectioninfo"]`,
42
            SECTIONBADGES: `[data-region="sectionbadges"]`,
43
            SHOWSECTION: `[data-action="sectionShow"]`,
44
            HIDESECTION: `[data-action="sectionHide"]`,
45
            ACTIONTEXT: `.menu-action-text`,
46
            ICON: `.icon`,
47
        };
48
        // Most classes will be loaded later by DndCmItem.
49
        this.classes = {
50
            LOCKED: 'editinprogress',
51
            HASDESCRIPTION: 'description',
52
            HIDE: 'd-none',
53
            HIDDEN: 'hidden',
54
            CURRENT: 'current',
55
        };
56
 
57
        // We need our id to watch specific events.
58
        this.id = this.element.dataset.id;
59
    }
60
 
61
    /**
62
     * Initial state ready method.
63
     *
64
     * @param {Object} state the initial state
65
     */
66
    stateReady(state) {
67
        this.configState(state);
68
        // Drag and drop is only available for components compatible course formats.
69
        if (this.reactive.isEditing && this.reactive.supportComponents) {
70
            // Section zero and other formats sections may not have a title to drag.
71
            const sectionItem = this.getElement(this.selectors.SECTION_ITEM);
72
            if (sectionItem) {
73
                // Init the inner dragable element.
74
                const headerComponent = new Header({
75
                    ...this,
76
                    element: sectionItem,
77
                    fullregion: this.element,
78
                });
79
                this.configDragDrop(headerComponent);
80
            }
81
        }
82
    }
83
 
84
    /**
85
     * Component watchers.
86
     *
87
     * @returns {Array} of watchers
88
     */
89
    getWatchers() {
90
        return [
91
            {watch: `section[${this.id}]:updated`, handler: this._refreshSection},
92
        ];
93
    }
94
 
95
    /**
96
     * Validate if the drop data can be dropped over the component.
97
     *
98
     * @param {Object} dropdata the exported drop data.
99
     * @returns {boolean}
100
     */
101
    validateDropData(dropdata) {
102
        // If the format uses one section per page sections dropping in the content is ignored.
103
       if (dropdata?.type === 'section' && this.reactive.sectionReturn !== null) {
104
            return false;
105
        }
106
        return super.validateDropData(dropdata);
107
    }
108
 
109
    /**
110
     * Get the last CM element of that section.
111
     *
112
     * @returns {element|null}
113
     */
114
    getLastCm() {
115
        const cms = this.getElements(this.selectors.CM);
116
        // DndUpload may add extra elements so :last-child selector cannot be used.
117
        if (!cms || cms.length === 0) {
118
            return null;
119
        }
120
        return cms[cms.length - 1];
121
    }
122
 
123
    /**
124
     * Update a content section using the state information.
125
     *
126
     * @param {object} param
127
     * @param {Object} param.element details the update details.
128
     */
129
    _refreshSection({element}) {
130
        // Update classes.
131
        this.element.classList.toggle(this.classes.DRAGGING, element.dragging ?? false);
132
        this.element.classList.toggle(this.classes.LOCKED, element.locked ?? false);
133
        this.element.classList.toggle(this.classes.HIDDEN, !element.visible ?? false);
134
        this.element.classList.toggle(this.classes.CURRENT, element.current ?? false);
135
        this.locked = element.locked;
136
        // The description box classes depends on the section state.
137
        const sectioninfo = this.getElement(this.selectors.SECTIONINFO);
138
        if (sectioninfo) {
139
            sectioninfo.classList.toggle(this.classes.HASDESCRIPTION, element.hasrestrictions);
140
        }
141
        // Update section badges and menus.
142
        this._updateBadges(element);
143
        this._updateActionsMenu(element);
144
    }
145
 
146
    /**
147
     * Update a section badges using the state information.
148
     *
149
     * @param {object} section the section state.
150
     */
151
    _updateBadges(section) {
152
        const current = this.getElement(`${this.selectors.SECTIONBADGES} [data-type='iscurrent']`);
153
        current?.classList.toggle(this.classes.HIDE, !section.current);
154
 
155
        const hiddenFromStudents = this.getElement(`${this.selectors.SECTIONBADGES} [data-type='hiddenfromstudents']`);
156
        hiddenFromStudents?.classList.toggle(this.classes.HIDE, section.visible);
157
    }
158
 
159
    /**
160
     * Update a section action menus.
161
     *
162
     * @param {object} section the section state.
163
     */
164
    async _updateActionsMenu(section) {
165
        let selector;
166
        let newAction;
167
        if (section.visible) {
168
            selector = this.selectors.SHOWSECTION;
169
            newAction = 'sectionHide';
170
        } else {
171
            selector = this.selectors.HIDESECTION;
172
            newAction = 'sectionShow';
173
        }
174
        // Find the affected action.
175
        const affectedAction = this.getElement(selector);
176
        if (!affectedAction) {
177
            return;
178
        }
179
        // Change action.
180
        affectedAction.dataset.action = newAction;
181
        // Change text.
182
        const actionText = affectedAction.querySelector(this.selectors.ACTIONTEXT);
183
        if (affectedAction.dataset?.swapname && actionText) {
184
            const oldText = actionText?.innerText;
185
            actionText.innerText = affectedAction.dataset.swapname;
186
            affectedAction.dataset.swapname = oldText;
187
        }
188
        // Change icon.
189
        const icon = affectedAction.querySelector(this.selectors.ICON);
190
        if (affectedAction.dataset?.swapicon && icon) {
191
            const newIcon = affectedAction.dataset.swapicon;
192
            if (newIcon) {
193
                const pixHtml = await Templates.renderPix(newIcon, 'core');
194
                Templates.replaceNode(icon, pixHtml, '');
195
            }
196
        }
197
    }
198
}