MDL-85504 mod_feedback: Enhance the question edit form via JS
The drag handle and actions menu should not be within the accessible labels of the question items. In order to enhance the question edit form with these, they need to be rendered via JS outside of container of the questions' accessible name.
This commit is contained in:
+1
-1
@@ -1,3 +1,3 @@
|
||||
define("mod_feedback/edit",["exports","core/loadingicon","core/notification","core/pending","core/prefetch","core/sortable_list","core/str","core/toast","mod_feedback/local/repository"],(function(_exports,_loadingicon,_notification,_pending,_prefetch,_sortable_list,_str,_toast,_repository){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_sortable_list=_interopRequireDefault(_sortable_list);const Selectors_deleteQuestionButton='[data-action="delete"]',Selectors_sortableListRegion='[data-region="questions-sortable-list"]',Selectors_sortableElement='[data-region="questions-sortable-list"] .feedback_itemlist[id]',Selectors_sortableElementTitle='[data-region="item-title"]',getItemOrder=element=>{const sortableList=element.closest(Selectors_sortableListRegion);let itemOrder=[];return sortableList.querySelectorAll(Selectors_sortableElement).forEach((item=>{var id;itemOrder.push((id=item.id,Number(id.replace(/^.*feedback_item_/i,""))))})),itemOrder.toString()};let initialized=!1,moduleId=null;_exports.init=cmId=>{if(moduleId=cmId,initialized)return;(0,_prefetch.prefetchStrings)("core",["yes","no"]),(0,_prefetch.prefetchStrings)("admin",["confirmation"]),(0,_prefetch.prefetchStrings)("mod_feedback",["confirmdeleteitem","questionmoved"]),document.addEventListener("click",(async event=>{const deleteButton=event.target.closest(Selectors_deleteQuestionButton);if(deleteButton){event.preventDefault();const confirmationStrings=await(0,_str.getStrings)([{key:"confirmation",component:"admin"},{key:"confirmdeleteitem",component:"mod_feedback"},{key:"yes",component:"core"},{key:"no",component:"core"}]);_notification.default.confirm(...confirmationStrings,(()=>{window.location=deleteButton.getAttribute("href")}))}else;}));new _sortable_list.default(document.querySelector(Selectors_sortableListRegion)).getElementName=element=>{var _element$0$querySelec;return Promise.resolve(null===(_element$0$querySelec=element[0].querySelector(Selectors_sortableElementTitle))||void 0===_element$0$querySelec?void 0:_element$0$querySelec.textContent)},document.addEventListener(_sortable_list.default.EVENTS.elementDrop,(event=>{if(!event.detail.positionChanged)return;const pendingPromise=new _pending.default("mod_feedback/questions:reorder"),itemOrder=getItemOrder(event.detail.element[0]);(0,_loadingicon.addIconToContainerRemoveOnCompletion)(event.detail.element[0],pendingPromise),(0,_repository.reorderQuestions)(moduleId,itemOrder).then((()=>(0,_str.getString)("questionmoved","mod_feedback"))).then(_toast.add).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)})),initialized=!0}}));
|
||||
define("mod_feedback/edit",["exports","core/loadingicon","core/notification","core/pending","core/prefetch","core/sortable_list","core/str","core/toast","mod_feedback/local/repository","core/templates"],(function(_exports,_loadingicon,_notification,_pending,_prefetch,_sortable_list,_str,_toast,_repository,_templates){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_sortable_list=_interopRequireDefault(_sortable_list),_templates=_interopRequireDefault(_templates);const Selectors_deleteQuestionButton='[data-action="delete"]',Selectors_sortableListRegion='[data-region="questions-sortable-list"]',Selectors_sortableElement='[data-region="questions-sortable-list"] .feedback_itemlist[id]',Selectors_sortableElementTitle='[data-region="item-title"] span',Selectors_questionLabel='[data-region="questions-sortable-list"] .col-form-label',Selectors_actionsMenuData="[data-item-actions-menu]",getItemOrder=element=>{const sortableList=element.closest(Selectors_sortableListRegion);let itemOrder=[];return sortableList.querySelectorAll(Selectors_sortableElement).forEach((item=>{var id;itemOrder.push((id=item.id,Number(id.replace(/^.*feedback_item_/i,""))))})),itemOrder.toString()};let initialized=!1,moduleId=null;_exports.init=async cmId=>{if(moduleId=cmId,initialized)return;(0,_prefetch.prefetchStrings)("core",["yes","no"]),(0,_prefetch.prefetchStrings)("admin",["confirmation"]),(0,_prefetch.prefetchStrings)("mod_feedback",["confirmdeleteitem","questionmoved","move_item"]),await enhanceEditForm(),document.addEventListener("click",(async event=>{const deleteButton=event.target.closest(Selectors_deleteQuestionButton);if(deleteButton){event.preventDefault();const confirmationStrings=await(0,_str.getStrings)([{key:"confirmation",component:"admin"},{key:"confirmdeleteitem",component:"mod_feedback"},{key:"yes",component:"core"},{key:"no",component:"core"}]);_notification.default.confirm(...confirmationStrings,(()=>{window.location=deleteButton.getAttribute("href")}))}else;}));new _sortable_list.default(document.querySelector(Selectors_sortableListRegion)).getElementName=element=>{var _element$0$querySelec;return Promise.resolve(null===(_element$0$querySelec=element[0].querySelector(Selectors_sortableElementTitle))||void 0===_element$0$querySelec?void 0:_element$0$querySelec.textContent)},document.addEventListener(_sortable_list.default.EVENTS.elementDrop,(event=>{if(!event.detail.positionChanged)return;const pendingPromise=new _pending.default("mod_feedback/questions:reorder"),itemOrder=getItemOrder(event.detail.element[0]);(0,_loadingicon.addIconToContainerRemoveOnCompletion)(event.detail.element[0],pendingPromise),(0,_repository.reorderQuestions)(moduleId,itemOrder).then((()=>(0,_str.getString)("questionmoved","mod_feedback"))).then(_toast.add).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)})),initialized=!0};const enhanceEditForm=async()=>{const questionLabels=document.querySelectorAll(Selectors_questionLabel),movetitle=await(0,_str.getString)("move_item","mod_feedback"),updates=Array.from(questionLabels).map((async container=>{const label=container.querySelector(Selectors_actionsMenuData);if(label)try{const contextData={movetitle:movetitle,label:label.parentElement.outerHTML,actionsmenu:JSON.parse(label.dataset.itemActionsMenu||"{}")};container.innerHTML=await _templates.default.render("mod_feedback/item_edit_enhanced_title",contextData)}catch(error){await _notification.default.exception(error)}}));await Promise.all(updates)}}));
|
||||
|
||||
//# sourceMappingURL=edit.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -31,12 +31,15 @@ import SortableList from 'core/sortable_list';
|
||||
import {getString, getStrings} from 'core/str';
|
||||
import {add as addToast} from 'core/toast';
|
||||
import {reorderQuestions} from 'mod_feedback/local/repository';
|
||||
import Templates from 'core/templates';
|
||||
|
||||
const Selectors = {
|
||||
deleteQuestionButton: '[data-action="delete"]',
|
||||
sortableListRegion: '[data-region="questions-sortable-list"]',
|
||||
sortableElement: '[data-region="questions-sortable-list"] .feedback_itemlist[id]',
|
||||
sortableElementTitle: '[data-region="item-title"]',
|
||||
sortableElementTitle: '[data-region="item-title"] span',
|
||||
questionLabel: '[data-region="questions-sortable-list"] .col-form-label',
|
||||
actionsMenuData: '[data-item-actions-menu]',
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -72,7 +75,7 @@ let moduleId = null;
|
||||
*
|
||||
* @param {Integer} cmId
|
||||
*/
|
||||
export const init = (cmId) => {
|
||||
export const init = async(cmId) => {
|
||||
|
||||
moduleId = cmId;
|
||||
|
||||
@@ -91,8 +94,11 @@ export const init = (cmId) => {
|
||||
prefetchStrings('mod_feedback', [
|
||||
'confirmdeleteitem',
|
||||
'questionmoved',
|
||||
'move_item',
|
||||
]);
|
||||
|
||||
await enhanceEditForm();
|
||||
|
||||
// Add event listeners.
|
||||
document.addEventListener('click', async event => {
|
||||
|
||||
@@ -133,3 +139,32 @@ export const init = (cmId) => {
|
||||
|
||||
initialized = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enhance the edit form by adding a move item button and an action menu to each question.
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const enhanceEditForm = async() => {
|
||||
const questionLabels = document.querySelectorAll(Selectors.questionLabel);
|
||||
const movetitle = await getString('move_item', 'mod_feedback');
|
||||
|
||||
const updates = Array.from(questionLabels).map(async(container) => {
|
||||
const label = container.querySelector(Selectors.actionsMenuData);
|
||||
if (!label) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const contextData = {
|
||||
movetitle,
|
||||
label: label.parentElement.outerHTML,
|
||||
actionsmenu: JSON.parse(label.dataset.itemActionsMenu || '{}'),
|
||||
};
|
||||
container.innerHTML = await Templates.render('mod_feedback/item_edit_enhanced_title', contextData);
|
||||
} catch (error) {
|
||||
await Notification.exception(error);
|
||||
}
|
||||
});
|
||||
await Promise.all(updates);
|
||||
};
|
||||
|
||||
@@ -472,14 +472,10 @@ class mod_feedback_complete_form extends moodleform {
|
||||
foreach ($actions as $action) {
|
||||
$menu->add($action);
|
||||
}
|
||||
$editmenu = $OUTPUT->render($menu);
|
||||
$draghandle = $OUTPUT->render_from_template('core/drag_handle',
|
||||
['movetitle' => get_string('move_item', 'mod_feedback')]);
|
||||
|
||||
$name = html_writer::div($draghandle, 'itemhandle', ['data-drag-type' => 'move']) .
|
||||
html_writer::div($element->getLabel(), 'itemname', ['data-region' => 'item-title']) .
|
||||
html_writer::div($editmenu, 'itemactions');
|
||||
$element->setLabel(html_writer::div($name, 'itemtitle d-flex mb-3'));
|
||||
$menudata = $menu->export_for_template($OUTPUT);
|
||||
$element->setLabel(html_writer::span($element->getLabel(), '', [
|
||||
'data-item-actions-menu' => json_encode($menudata),
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template mod_feedback/item_edit_enhanced_title
|
||||
|
||||
The enhanced item title template when editing questions in the feedback activity.
|
||||
|
||||
Context variables required for this template:
|
||||
* movetitle - The string for the title attribute of the move button.
|
||||
* label - The question text.
|
||||
* actionsmenu - Context data for the actions menu.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"movetitle": "Move item",
|
||||
"label": "Hello world!",
|
||||
"actionsmenu": {}
|
||||
}
|
||||
}}
|
||||
<div class="itemtitle d-flex mb-3">
|
||||
<div class="itemhandle" data-drag-type="move">
|
||||
{{> core/drag_handle}}
|
||||
</div>
|
||||
<div class="itemname" data-region="item-title">
|
||||
{{{label}}}
|
||||
</div>
|
||||
{{#actionsmenu}}
|
||||
<div class="itemactions">
|
||||
{{> core/action_menu}}
|
||||
</div>
|
||||
{{/actionsmenu}}
|
||||
</div>
|
||||
Reference in New Issue
Block a user