From eccfb73839eef82532357fa28df8a2abf9150988 Mon Sep 17 00:00:00 2001 From: Shamim Rezaie Date: Mon, 11 Mar 2024 02:47:12 +1100 Subject: [PATCH] MDL-80426 qtype_ordering: Remove the unintuitive keyboard handling code Users can now rearrange options using the move buttons that each option have, therefore there is no need for the current unintuitive keyboard interaction implementation in the ordering question type plugin. --- .../ordering/amd/build/drag_reorder.min.js | 2 +- .../amd/build/drag_reorder.min.js.map | 2 +- .../type/ordering/amd/src/drag_reorder.js | 78 +------------------ question/type/ordering/styles.css | 4 - 4 files changed, 3 insertions(+), 83 deletions(-) diff --git a/question/type/ordering/amd/build/drag_reorder.min.js b/question/type/ordering/amd/build/drag_reorder.min.js index 4db721a7b5f..4edaec785ec 100644 --- a/question/type/ordering/amd/build/drag_reorder.min.js +++ b/question/type/ordering/amd/build/drag_reorder.min.js @@ -1,3 +1,3 @@ -define("qtype_ordering/drag_reorder",["exports","jquery","core/dragdrop","core/key_codes","core/templates","core/notification"],(function(_exports,_jquery,_dragdrop,_key_codes,_templates,_notification){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),_dragdrop=_interopRequireDefault(_dragdrop),_key_codes=_interopRequireDefault(_key_codes),_templates=_interopRequireDefault(_templates),_notification=_interopRequireDefault(_notification);class DragReorder{constructor(config){_defineProperty(this,"config",{reorderStart:void 0,reorderEnd:void 0}),_defineProperty(this,"dragStart",null),_defineProperty(this,"originalOrder",null),_defineProperty(this,"orderList",null),_defineProperty(this,"itemDragging",null),_defineProperty(this,"proxy",null),this.config=config,this.orderList=document.querySelector(this.config.list),this.startListeners(),(0,_jquery.default)(this.combineSelectors(config.list,config.item)).attr("tabindex","0")}startListeners(){const pointerHandle=e=>{if(e.target.closest(this.config.item)&&!e.target.closest(this.config.actionButton)){this.itemDragging=(0,_jquery.default)(e.target.closest(this.config.item));const details=_dragdrop.default.prepare(e);details.start&&this.startDrag(e,details)}};this.orderList.addEventListener("mousedown",pointerHandle),this.orderList.addEventListener("touchstart",pointerHandle),this.orderList.addEventListener("keydown",this.itemMovedByKeyboard.bind(this)),this.orderList.addEventListener("click",this.itemMovedByClick.bind(this))}startDrag(e,details){this.dragStart={time:(new Date).getTime(),x:details.x,y:details.y},void 0!==this.config.reorderStart&&this.config.reorderStart(this.itemDragging.closest(this.config.list),this.itemDragging),this.originalOrder=this.getCurrentOrder(),_templates.default.renderForPromise("qtype_ordering/proxyhtml",{itemHtml:this.itemDragging.html(),itemClassName:this.itemDragging.attr("class"),listClassName:this.orderList.classList.toString(),proxyStyles:["width: ".concat(this.itemDragging.outerWidth(),"px;"),"height: ".concat(this.itemDragging.outerHeight(),"px;")].join(" ")}).then((_ref=>{let{html:html,js:js}=_ref;this.proxy=(0,_jquery.default)(_templates.default.appendNodeContents(document.body,html,js)[0]),this.proxy.css(this.itemDragging.offset()),this.itemDragging.addClass(this.config.itemMovingClass),this.updateProxy(),_dragdrop.default.start(e,this.proxy,this.dragMove.bind(this),this.dragEnd.bind(this))})).catch(_notification.default.exception)}dragMove(){let closestItem=null,closestDistance=null;if(this.orderList.querySelectorAll(this.config.item).forEach((element=>{const distance=this.distanceBetweenElements(element);(null===closestItem||distance{inner.split(",").forEach((secondSelector=>{combined.push(firstSelector.trim()+" "+secondSelector.trim())}))})),combined.join(", ")}midX(node){return node.offset().left+node.outerWidth()/2}midY(node){return node.offset().top+node.outerHeight()/2}distanceBetweenElements(element){const[e1,e2]=[(0,_jquery.default)(element),(0,_jquery.default)(this.proxy)],[dx,dy]=[this.midX(e1)-this.midX(e2),this.midY(e1)-this.midY(e2)];return Math.sqrt(dx*dx+dy*dy)}getCurrentOrder(){return this.itemDragging.closest(this.config.list).find(this.config.item).map(((index,item)=>this.config.idGetter(item))).get()}arrayEquals(a1,a2){return a1.length===a2.length&&a1.every(((v,i)=>v===a2[i]))}static init(sortableid,responseid){new DragReorder({actionButton:"[data-action]",list:"ul#"+sortableid,item:"li.sortableitem",itemMovingClass:"current-drop",idGetter:item=>item.id,reorderDone:(list,item,newOrder)=>{(0,_jquery.default)("input#"+responseid)[0].value=newOrder.join(",")}})}}return _exports.default=DragReorder,_exports.default})); +define("qtype_ordering/drag_reorder",["exports","jquery","core/dragdrop","core/templates","core/notification"],(function(_exports,_jquery,_dragdrop,_templates,_notification){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),_dragdrop=_interopRequireDefault(_dragdrop),_templates=_interopRequireDefault(_templates),_notification=_interopRequireDefault(_notification);class DragReorder{constructor(config){_defineProperty(this,"config",{reorderStart:void 0,reorderEnd:void 0}),_defineProperty(this,"dragStart",null),_defineProperty(this,"originalOrder",null),_defineProperty(this,"orderList",null),_defineProperty(this,"itemDragging",null),_defineProperty(this,"proxy",null),this.config=config,this.orderList=document.querySelector(this.config.list),this.startListeners()}startListeners(){const pointerHandle=e=>{if(e.target.closest(this.config.item)&&!e.target.closest(this.config.actionButton)){this.itemDragging=(0,_jquery.default)(e.target.closest(this.config.item));const details=_dragdrop.default.prepare(e);details.start&&this.startDrag(e,details)}};this.orderList.addEventListener("mousedown",pointerHandle),this.orderList.addEventListener("touchstart",pointerHandle),this.orderList.addEventListener("click",this.itemMovedByClick.bind(this))}startDrag(e,details){this.dragStart={time:(new Date).getTime(),x:details.x,y:details.y},void 0!==this.config.reorderStart&&this.config.reorderStart(this.itemDragging.closest(this.config.list),this.itemDragging),this.originalOrder=this.getCurrentOrder(),_templates.default.renderForPromise("qtype_ordering/proxyhtml",{itemHtml:this.itemDragging.html(),itemClassName:this.itemDragging.attr("class"),listClassName:this.orderList.classList.toString(),proxyStyles:["width: ".concat(this.itemDragging.outerWidth(),"px;"),"height: ".concat(this.itemDragging.outerHeight(),"px;")].join(" ")}).then((_ref=>{let{html:html,js:js}=_ref;this.proxy=(0,_jquery.default)(_templates.default.appendNodeContents(document.body,html,js)[0]),this.proxy.css(this.itemDragging.offset()),this.itemDragging.addClass(this.config.itemMovingClass),this.updateProxy(),_dragdrop.default.start(e,this.proxy,this.dragMove.bind(this),this.dragEnd.bind(this))})).catch(_notification.default.exception)}dragMove(){let closestItem=null,closestDistance=null;if(this.orderList.querySelectorAll(this.config.item).forEach((element=>{const distance=this.distanceBetweenElements(element);(null===closestItem||distancethis.config.idGetter(item))).get()}arrayEquals(a1,a2){return a1.length===a2.length&&a1.every(((v,i)=>v===a2[i]))}static init(sortableid,responseid){new DragReorder({actionButton:"[data-action]",list:"ul#"+sortableid,item:"li.sortableitem",itemMovingClass:"current-drop",idGetter:item=>item.id,reorderDone:(list,item,newOrder)=>{(0,_jquery.default)("input#"+responseid)[0].value=newOrder.join(",")}})}}return _exports.default=DragReorder,_exports.default})); //# sourceMappingURL=drag_reorder.min.js.map \ No newline at end of file diff --git a/question/type/ordering/amd/build/drag_reorder.min.js.map b/question/type/ordering/amd/build/drag_reorder.min.js.map index ceaa2c9bc30..c95f8f33737 100644 --- a/question/type/ordering/amd/build/drag_reorder.min.js.map +++ b/question/type/ordering/amd/build/drag_reorder.min.js.map @@ -1 +1 @@ -{"version":3,"file":"drag_reorder.min.js","sources":["../src/drag_reorder.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/*\n * To make a set of things draggable, create a new instance of this object passing the\n * necessary config, as explained in the comment on the constructor.\n *\n * @package qtype_ordering\\drag_reorder\n * @copyright 2018 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n'use strict';\n\nimport $ from 'jquery';\nimport drag from 'core/dragdrop';\nimport keys from 'core/key_codes';\nimport Templates from 'core/templates';\nimport Notification from 'core/notification';\n\nexport default class DragReorder {\n\n // Class variables handling state.\n config = {reorderStart: undefined, reorderEnd: undefined}; // Config object with some basic definitions.\n dragStart = null; // Information about when and where the drag started.\n originalOrder = null; // Array of ids that's used to compare the state after the drag event finishes.\n\n // DOM Nodes and jQuery representations.\n orderList = null; // Order list (HTMLElement).\n itemDragging = null; // Item being moved by dragging (jQuery object).\n proxy = null; // Drag proxy (jQuery object).\n\n /**\n * Constructor.\n *\n * To make a list draggable, create a new instance of this object, passing the necessary config.\n * For example:\n * {\n * // Selector for the list (or lists) to be reordered.\n * list: 'ul.my-list',\n *\n * // Selector, relative to the list selector, for the items that can be moved.\n * item: '> li',\n *\n * // While the proxy is being dragged, this class is added to the item being moved.\n * // You can probably use \"osep-itemmoving\" here.\n * itemMovingClass: \"osep-itemmoving\",\n *\n * // This is a callback which, when called with the DOM node for an item,\n * // returns the string that uniquely identifies each item.\n * // Therefore, the result of the drag action will be represented by the array\n * // obtained by calling this method on each item in the list in order.\n * idGetter: function(item) { return node.id; },\n *\n * // Function that will be called when a re-order starts (optional, can be not set).\n * // Useful if you need to save information about the initial state.\n * // This function should have two parameters. The first will be a\n * // jQuery object for the list that was reordered, the second will\n * // be the jQuery object for the item moved - which will not yet have been moved.\n * // Note, it is quite possible for reorderStart to be called with no\n * // subsequent call to reorderDone.\n * reorderStart: function($list, $item) { ... }\n *\n * // Function that will be called when a drag has finished, and the list\n * // has been reordered. This function should have three parameters. The first will be\n * // a jQuery object for the list that was reordered, the second will be the jQuery\n * // object for the item moved, and the third will be the new order, which is\n * // an array of ids obtained by calling idGetter on each item in the list in order.\n * // This callback will only be called in the new order is actually different from the old order.\n * reorderDone: function($list, $item, newOrder) { ... }\n *\n * // Function that is always called when a re-order ends (optional, can be not set)\n * // whether the order has changed. Useful if you need to undo changes made\n * // in reorderStart, since reorderDone is only called if the new order is different\n * // from the original order.\n * reorderEnd: function($list, $item) { ... }\n * }\n *\n * There is a subtlety, If you have items in your list that do not have a drag handle,\n * they are considered to be placeholders in otherwise empty containers.\n *\n * @param {Object} config As above.\n */\n constructor(config) {\n // Bring in the config to our state.\n this.config = config;\n\n // Get the list we'll be working with this time.\n this.orderList = document.querySelector(this.config.list);\n\n this.startListeners();\n\n // Make the items tabbable.\n // TODO: This can be removed once we move to templates and add the tabindex there.\n $(this.combineSelectors(config.list, config.item)).attr('tabindex', '0');\n }\n\n /**\n * Start the listeners for the list.\n */\n startListeners() {\n /**\n * Handle mousedown or touchstart events on the list.\n *\n * @param {Event} e The event.\n */\n const pointerHandle = e => {\n if (e.target.closest(this.config.item) && !e.target.closest(this.config.actionButton)) {\n this.itemDragging = $(e.target.closest(this.config.item));\n const details = drag.prepare(e);\n if (details.start) {\n this.startDrag(e, details);\n }\n }\n };\n // Set up the list listeners for moving list items around.\n this.orderList.addEventListener('mousedown', pointerHandle);\n this.orderList.addEventListener('touchstart', pointerHandle);\n this.orderList.addEventListener('keydown', this.itemMovedByKeyboard.bind(this));\n this.orderList.addEventListener('click', this.itemMovedByClick.bind(this));\n }\n\n /**\n * Start dragging.\n *\n * @param {Event} e The event which is either mousedown or touchstart.\n * @param {Object} details Object with start (boolean flag) and x, y (only if flag true) values\n */\n startDrag(e, details) {\n this.dragStart = {\n time: new Date().getTime(),\n x: details.x,\n y: details.y\n };\n\n if (typeof this.config.reorderStart !== 'undefined') {\n this.config.reorderStart(this.itemDragging.closest(this.config.list), this.itemDragging);\n }\n\n this.originalOrder = this.getCurrentOrder();\n\n Templates.renderForPromise('qtype_ordering/proxyhtml', {\n itemHtml: this.itemDragging.html(),\n itemClassName: this.itemDragging.attr('class'),\n listClassName: this.orderList.classList.toString(),\n proxyStyles: [\n `width: ${this.itemDragging.outerWidth()}px;`,\n `height: ${this.itemDragging.outerHeight()}px;`,\n ].join(' '),\n }).then(({html, js}) => {\n this.proxy = $(Templates.appendNodeContents(document.body, html, js)[0]);\n this.proxy.css(this.itemDragging.offset());\n\n this.itemDragging.addClass(this.config.itemMovingClass);\n\n this.updateProxy();\n // Start drag.\n drag.start(e, this.proxy, this.dragMove.bind(this), this.dragEnd.bind(this));\n }).catch(Notification.exception);\n }\n\n /**\n * Move the proxy to the current mouse position.\n */\n dragMove() {\n let closestItem = null;\n let closestDistance = null;\n this.orderList.querySelectorAll(this.config.item).forEach(element => {\n const distance = this.distanceBetweenElements(element);\n if (closestItem === null || distance < closestDistance) {\n closestItem = $(element);\n closestDistance = distance;\n }\n });\n\n if (closestItem[0] === this.itemDragging[0]) {\n return;\n }\n\n // Set offset depending on if item is being dragged downwards/upwards.\n const offsetValue = this.midY(this.proxy) < this.midY(closestItem) ? 20 : -20;\n if (this.midY(this.proxy) + offsetValue < this.midY(closestItem)) {\n this.itemDragging.insertBefore(closestItem);\n } else {\n this.itemDragging.insertAfter(closestItem);\n }\n this.updateProxy();\n }\n\n /**\n * Update proxy's position.\n */\n updateProxy() {\n const items = [...this.orderList.querySelectorAll(this.config.item)];\n for (let i = 0; i < items.length; ++i) {\n if (this.itemDragging[0] === items[i]) {\n this.proxy.find('li').attr('value', i + 1);\n break;\n }\n }\n }\n\n /**\n * End dragging.\n *\n * @param {number} x X co-ordinate\n * @param {number} y Y co-ordinate\n */\n dragEnd(x, y) {\n if (typeof this.config.reorderEnd !== 'undefined') {\n this.config.reorderEnd(this.itemDragging.closest(this.config.list), this.itemDragging);\n }\n\n if (!this.arrayEquals(this.originalOrder, this.getCurrentOrder())) {\n // Order has changed, call the callback.\n this.config.reorderDone(this.itemDragging.closest(this.config.list), this.itemDragging, this.getCurrentOrder());\n\n } else if (new Date().getTime() - this.dragStart.time < 500 &&\n Math.abs(this.dragStart.x - x) < 10 && Math.abs(this.dragStart.y - y) < 10) {\n // This was really a click. Set the focus on the current item.\n this.itemDragging[0].focus();\n }\n\n // Clean up after the drag is finished.\n this.proxy.remove();\n this.proxy = null;\n this.itemDragging.removeClass(this.config.itemMovingClass);\n this.itemDragging = null;\n this.dragStart = null;\n }\n\n /**\n * Items can be moved and placed using certain keys.\n * Tab for tabbing though and choose the item to be moved\n * space, arrow-right arrow-down for moving current element forwards.\n * arrow-right arrow-down for moving the current element backwards.\n *\n * @param {Event} e The keyboard event.\n */\n itemMovedByKeyboard(e) {\n if (e.target.closest(this.config.item)) {\n this.itemDragging = $(e.target.closest(this.config.item));\n\n // Store the current state of the list.\n this.originalOrder = this.getCurrentOrder();\n\n switch (e.keyCode) {\n case keys.arrowRight:\n case keys.arrowDown:\n e.preventDefault();\n e.stopPropagation();\n if (this.itemDragging.next().length) {\n this.itemDragging.next().insertBefore(this.itemDragging);\n }\n break;\n\n case keys.arrowLeft:\n case keys.arrowUp:\n e.preventDefault();\n e.stopPropagation();\n if (this.itemDragging.prev().length) {\n this.itemDragging.prev().insertAfter(this.itemDragging);\n }\n break;\n }\n\n // After we have potentially moved the item, we need to check if the order has changed.\n if (!this.arrayEquals(this.originalOrder, this.getCurrentOrder())) {\n // Order has changed, call the callback.\n this.config.reorderDone(this.itemDragging.closest(this.config.list), this.itemDragging, this.getCurrentOrder());\n }\n }\n }\n\n /**\n * Handles the movement of an item by click.\n *\n * @param {MouseEvent} e The pointer event.\n */\n itemMovedByClick(e) {\n const actionButton = e.target.closest(this.config.actionButton);\n if (actionButton) {\n this.itemDragging = $(e.target.closest(this.config.item));\n\n // Store the current state of the list.\n this.originalOrder = this.getCurrentOrder();\n\n switch (actionButton.dataset.action) {\n case 'move-backward':\n e.preventDefault();\n e.stopPropagation();\n if (this.itemDragging.prev().length) {\n this.itemDragging.prev().insertAfter(this.itemDragging);\n }\n break;\n case 'move-forward':\n e.preventDefault();\n e.stopPropagation();\n if (this.itemDragging.next().length) {\n this.itemDragging.next().insertBefore(this.itemDragging);\n }\n break;\n }\n\n // After we have potentially moved the item, we need to check if the order has changed.\n if (!this.arrayEquals(this.originalOrder, this.getCurrentOrder())) {\n // Order has changed, call the callback.\n this.config.reorderDone(this.itemDragging.closest(this.config.list), this.itemDragging, this.getCurrentOrder());\n\n // When moving an item to the first or last position, the button that was clicked will be hidden.\n // In this case, we need to focus the other button.\n if (!this.itemDragging.prev().length) {\n // Focus the 'next' action button.\n this.itemDragging.find('[data-action=\"move-forward\"]').focus();\n } else if (!this.itemDragging.next().length) {\n // Focus the 'previous' action button.\n this.itemDragging.find('[data-action=\"move-backward\"]').focus();\n }\n }\n }\n }\n\n /**\n * TODO: Once the tabindex is added to the template, this can be removed.\n * Our outer and inner are two CSS selectors, which may contain commas.\n * We want to combine them safely. So for instance combineSelectors('a, b', 'c, d')\n * gives 'a c, a d, b c, b d'.\n *\n * @param {String} outer The selector for the outer element.\n * @param {String} inner The selector for the inner element.\n * @returns {String} The combined selector used to listen to the list item.\n */\n combineSelectors(outer, inner) {\n let combined = [];\n outer.split(',').forEach(firstSelector => {\n inner.split(',').forEach(secondSelector => {\n combined.push(firstSelector.trim() + ' ' + secondSelector.trim());\n });\n });\n return combined.join(', ');\n }\n\n /**\n * Get the x-position of the middle of the DOM node represented by the given jQuery object.\n *\n * @param {jQuery} node jQuery wrapping a DOM node.\n * @returns {number} Number the x-coordinate of the middle (left plus half outerWidth).\n */\n midX(node) {\n return node.offset().left + node.outerWidth() / 2;\n }\n\n /**\n * Get the y-position of the middle of the DOM node represented by the given jQuery object.\n *\n * @param {jQuery} node jQuery wrapped DOM node.\n * @returns {number} Number the y-coordinate of the middle (top plus half outerHeight).\n */\n midY(node) {\n return node.offset().top + node.outerHeight() / 2;\n }\n\n /**\n * Calculate the distance between the centres of two elements.\n *\n * @param {HTMLLIElement} element DOM node of a list item.\n * @return {number} number the distance in pixels.\n */\n distanceBetweenElements(element) {\n const [e1, e2] = [$(element), $(this.proxy)];\n const [dx, dy] = [this.midX(e1) - this.midX(e2), this.midY(e1) - this.midY(e2)];\n return Math.sqrt(dx * dx + dy * dy);\n }\n\n /**\n * Get the current order of the list containing itemDragging.\n *\n * @returns {Array} Array of strings, the id of each element in order.\n */\n getCurrentOrder() {\n return this.itemDragging.closest(this.config.list).find(this.config.item).map(\n (index, item) => {\n return this.config.idGetter(item);\n }).get();\n }\n\n /**\n * Compare two arrays which contain primitive types to see if they are equal.\n * @param {Array} a1 first array.\n * @param {Array} a2 second array.\n * @return {Boolean} boolean true if they both contain the same elements in the same order, else false.\n */\n arrayEquals(a1, a2) {\n return a1.length === a2.length &&\n a1.every((v, i) => {\n return v === a2[i];\n });\n }\n\n /**\n * Initialise one ordering question.\n *\n * @param {String} sortableid id of ul for this question.\n * @param {String} responseid id of hidden field for this question.\n */\n static init(sortableid, responseid) {\n new DragReorder({\n actionButton: '[data-action]',\n list: 'ul#' + sortableid,\n item: 'li.sortableitem',\n itemMovingClass: \"current-drop\",\n idGetter: item => {\n return item.id;\n },\n reorderDone: (list, item, newOrder) => {\n $('input#' + responseid)[0].value = newOrder.join(',');\n }\n });\n }\n}\n"],"names":["DragReorder","constructor","config","reorderStart","undefined","reorderEnd","orderList","document","querySelector","this","list","startListeners","combineSelectors","item","attr","pointerHandle","e","target","closest","actionButton","itemDragging","details","drag","prepare","start","startDrag","addEventListener","itemMovedByKeyboard","bind","itemMovedByClick","dragStart","time","Date","getTime","x","y","originalOrder","getCurrentOrder","renderForPromise","itemHtml","html","itemClassName","listClassName","classList","toString","proxyStyles","outerWidth","outerHeight","join","then","_ref","js","proxy","Templates","appendNodeContents","body","css","offset","addClass","itemMovingClass","updateProxy","dragMove","dragEnd","catch","Notification","exception","closestItem","closestDistance","querySelectorAll","forEach","element","distance","distanceBetweenElements","offsetValue","midY","insertBefore","insertAfter","items","i","length","find","arrayEquals","Math","abs","focus","reorderDone","remove","removeClass","keyCode","keys","arrowRight","arrowDown","preventDefault","stopPropagation","next","arrowLeft","arrowUp","prev","dataset","action","outer","inner","combined","split","firstSelector","secondSelector","push","trim","midX","node","left","top","e1","e2","dx","dy","sqrt","map","index","idGetter","get","a1","a2","every","v","sortableid","responseid","id","newOrder","value"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"drag_reorder.min.js","sources":["../src/drag_reorder.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/*\n * To make a set of things draggable, create a new instance of this object passing the\n * necessary config, as explained in the comment on the constructor.\n *\n * @package qtype_ordering\\drag_reorder\n * @copyright 2018 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n'use strict';\n\nimport $ from 'jquery';\nimport drag from 'core/dragdrop';\nimport Templates from 'core/templates';\nimport Notification from 'core/notification';\n\nexport default class DragReorder {\n\n // Class variables handling state.\n config = {reorderStart: undefined, reorderEnd: undefined}; // Config object with some basic definitions.\n dragStart = null; // Information about when and where the drag started.\n originalOrder = null; // Array of ids that's used to compare the state after the drag event finishes.\n\n // DOM Nodes and jQuery representations.\n orderList = null; // Order list (HTMLElement).\n itemDragging = null; // Item being moved by dragging (jQuery object).\n proxy = null; // Drag proxy (jQuery object).\n\n /**\n * Constructor.\n *\n * To make a list draggable, create a new instance of this object, passing the necessary config.\n * For example:\n * {\n * // Selector for the list (or lists) to be reordered.\n * list: 'ul.my-list',\n *\n * // Selector, relative to the list selector, for the items that can be moved.\n * item: '> li',\n *\n * // While the proxy is being dragged, this class is added to the item being moved.\n * // You can probably use \"osep-itemmoving\" here.\n * itemMovingClass: \"osep-itemmoving\",\n *\n * // This is a callback which, when called with the DOM node for an item,\n * // returns the string that uniquely identifies each item.\n * // Therefore, the result of the drag action will be represented by the array\n * // obtained by calling this method on each item in the list in order.\n * idGetter: function(item) { return node.id; },\n *\n * // Function that will be called when a re-order starts (optional, can be not set).\n * // Useful if you need to save information about the initial state.\n * // This function should have two parameters. The first will be a\n * // jQuery object for the list that was reordered, the second will\n * // be the jQuery object for the item moved - which will not yet have been moved.\n * // Note, it is quite possible for reorderStart to be called with no\n * // subsequent call to reorderDone.\n * reorderStart: function($list, $item) { ... }\n *\n * // Function that will be called when a drag has finished, and the list\n * // has been reordered. This function should have three parameters. The first will be\n * // a jQuery object for the list that was reordered, the second will be the jQuery\n * // object for the item moved, and the third will be the new order, which is\n * // an array of ids obtained by calling idGetter on each item in the list in order.\n * // This callback will only be called in the new order is actually different from the old order.\n * reorderDone: function($list, $item, newOrder) { ... }\n *\n * // Function that is always called when a re-order ends (optional, can be not set)\n * // whether the order has changed. Useful if you need to undo changes made\n * // in reorderStart, since reorderDone is only called if the new order is different\n * // from the original order.\n * reorderEnd: function($list, $item) { ... }\n * }\n *\n * There is a subtlety, If you have items in your list that do not have a drag handle,\n * they are considered to be placeholders in otherwise empty containers.\n *\n * @param {Object} config As above.\n */\n constructor(config) {\n // Bring in the config to our state.\n this.config = config;\n\n // Get the list we'll be working with this time.\n this.orderList = document.querySelector(this.config.list);\n\n this.startListeners();\n }\n\n /**\n * Start the listeners for the list.\n */\n startListeners() {\n /**\n * Handle mousedown or touchstart events on the list.\n *\n * @param {Event} e The event.\n */\n const pointerHandle = e => {\n if (e.target.closest(this.config.item) && !e.target.closest(this.config.actionButton)) {\n this.itemDragging = $(e.target.closest(this.config.item));\n const details = drag.prepare(e);\n if (details.start) {\n this.startDrag(e, details);\n }\n }\n };\n // Set up the list listeners for moving list items around.\n this.orderList.addEventListener('mousedown', pointerHandle);\n this.orderList.addEventListener('touchstart', pointerHandle);\n this.orderList.addEventListener('click', this.itemMovedByClick.bind(this));\n }\n\n /**\n * Start dragging.\n *\n * @param {Event} e The event which is either mousedown or touchstart.\n * @param {Object} details Object with start (boolean flag) and x, y (only if flag true) values\n */\n startDrag(e, details) {\n this.dragStart = {\n time: new Date().getTime(),\n x: details.x,\n y: details.y\n };\n\n if (typeof this.config.reorderStart !== 'undefined') {\n this.config.reorderStart(this.itemDragging.closest(this.config.list), this.itemDragging);\n }\n\n this.originalOrder = this.getCurrentOrder();\n\n Templates.renderForPromise('qtype_ordering/proxyhtml', {\n itemHtml: this.itemDragging.html(),\n itemClassName: this.itemDragging.attr('class'),\n listClassName: this.orderList.classList.toString(),\n proxyStyles: [\n `width: ${this.itemDragging.outerWidth()}px;`,\n `height: ${this.itemDragging.outerHeight()}px;`,\n ].join(' '),\n }).then(({html, js}) => {\n this.proxy = $(Templates.appendNodeContents(document.body, html, js)[0]);\n this.proxy.css(this.itemDragging.offset());\n\n this.itemDragging.addClass(this.config.itemMovingClass);\n\n this.updateProxy();\n // Start drag.\n drag.start(e, this.proxy, this.dragMove.bind(this), this.dragEnd.bind(this));\n }).catch(Notification.exception);\n }\n\n /**\n * Move the proxy to the current mouse position.\n */\n dragMove() {\n let closestItem = null;\n let closestDistance = null;\n this.orderList.querySelectorAll(this.config.item).forEach(element => {\n const distance = this.distanceBetweenElements(element);\n if (closestItem === null || distance < closestDistance) {\n closestItem = $(element);\n closestDistance = distance;\n }\n });\n\n if (closestItem[0] === this.itemDragging[0]) {\n return;\n }\n\n // Set offset depending on if item is being dragged downwards/upwards.\n const offsetValue = this.midY(this.proxy) < this.midY(closestItem) ? 20 : -20;\n if (this.midY(this.proxy) + offsetValue < this.midY(closestItem)) {\n this.itemDragging.insertBefore(closestItem);\n } else {\n this.itemDragging.insertAfter(closestItem);\n }\n this.updateProxy();\n }\n\n /**\n * Update proxy's position.\n */\n updateProxy() {\n const items = [...this.orderList.querySelectorAll(this.config.item)];\n for (let i = 0; i < items.length; ++i) {\n if (this.itemDragging[0] === items[i]) {\n this.proxy.find('li').attr('value', i + 1);\n break;\n }\n }\n }\n\n /**\n * End dragging.\n */\n dragEnd() {\n if (typeof this.config.reorderEnd !== 'undefined') {\n this.config.reorderEnd(this.itemDragging.closest(this.config.list), this.itemDragging);\n }\n\n if (!this.arrayEquals(this.originalOrder, this.getCurrentOrder())) {\n // Order has changed, call the callback.\n this.config.reorderDone(this.itemDragging.closest(this.config.list), this.itemDragging, this.getCurrentOrder());\n\n }\n\n // Clean up after the drag is finished.\n this.proxy.remove();\n this.proxy = null;\n this.itemDragging.removeClass(this.config.itemMovingClass);\n this.itemDragging = null;\n this.dragStart = null;\n }\n\n /**\n * Handles the movement of an item by click.\n *\n * @param {MouseEvent} e The pointer event.\n */\n itemMovedByClick(e) {\n const actionButton = e.target.closest(this.config.actionButton);\n if (actionButton) {\n this.itemDragging = $(e.target.closest(this.config.item));\n\n // Store the current state of the list.\n this.originalOrder = this.getCurrentOrder();\n\n switch (actionButton.dataset.action) {\n case 'move-backward':\n e.preventDefault();\n e.stopPropagation();\n if (this.itemDragging.prev().length) {\n this.itemDragging.prev().insertAfter(this.itemDragging);\n }\n break;\n case 'move-forward':\n e.preventDefault();\n e.stopPropagation();\n if (this.itemDragging.next().length) {\n this.itemDragging.next().insertBefore(this.itemDragging);\n }\n break;\n }\n\n // After we have potentially moved the item, we need to check if the order has changed.\n if (!this.arrayEquals(this.originalOrder, this.getCurrentOrder())) {\n // Order has changed, call the callback.\n this.config.reorderDone(this.itemDragging.closest(this.config.list), this.itemDragging, this.getCurrentOrder());\n\n // When moving an item to the first or last position, the button that was clicked will be hidden.\n // In this case, we need to focus the other button.\n if (!this.itemDragging.prev().length) {\n // Focus the 'next' action button.\n this.itemDragging.find('[data-action=\"move-forward\"]').focus();\n } else if (!this.itemDragging.next().length) {\n // Focus the 'previous' action button.\n this.itemDragging.find('[data-action=\"move-backward\"]').focus();\n }\n }\n }\n }\n\n /**\n * Get the x-position of the middle of the DOM node represented by the given jQuery object.\n *\n * @param {jQuery} node jQuery wrapping a DOM node.\n * @returns {number} Number the x-coordinate of the middle (left plus half outerWidth).\n */\n midX(node) {\n return node.offset().left + node.outerWidth() / 2;\n }\n\n /**\n * Get the y-position of the middle of the DOM node represented by the given jQuery object.\n *\n * @param {jQuery} node jQuery wrapped DOM node.\n * @returns {number} Number the y-coordinate of the middle (top plus half outerHeight).\n */\n midY(node) {\n return node.offset().top + node.outerHeight() / 2;\n }\n\n /**\n * Calculate the distance between the centres of two elements.\n *\n * @param {HTMLLIElement} element DOM node of a list item.\n * @return {number} number the distance in pixels.\n */\n distanceBetweenElements(element) {\n const [e1, e2] = [$(element), $(this.proxy)];\n const [dx, dy] = [this.midX(e1) - this.midX(e2), this.midY(e1) - this.midY(e2)];\n return Math.sqrt(dx * dx + dy * dy);\n }\n\n /**\n * Get the current order of the list containing itemDragging.\n *\n * @returns {Array} Array of strings, the id of each element in order.\n */\n getCurrentOrder() {\n return this.itemDragging.closest(this.config.list).find(this.config.item).map(\n (index, item) => {\n return this.config.idGetter(item);\n }).get();\n }\n\n /**\n * Compare two arrays which contain primitive types to see if they are equal.\n * @param {Array} a1 first array.\n * @param {Array} a2 second array.\n * @return {Boolean} boolean true if they both contain the same elements in the same order, else false.\n */\n arrayEquals(a1, a2) {\n return a1.length === a2.length &&\n a1.every((v, i) => {\n return v === a2[i];\n });\n }\n\n /**\n * Initialise one ordering question.\n *\n * @param {String} sortableid id of ul for this question.\n * @param {String} responseid id of hidden field for this question.\n */\n static init(sortableid, responseid) {\n new DragReorder({\n actionButton: '[data-action]',\n list: 'ul#' + sortableid,\n item: 'li.sortableitem',\n itemMovingClass: \"current-drop\",\n idGetter: item => {\n return item.id;\n },\n reorderDone: (list, item, newOrder) => {\n $('input#' + responseid)[0].value = newOrder.join(',');\n }\n });\n }\n}\n"],"names":["DragReorder","constructor","config","reorderStart","undefined","reorderEnd","orderList","document","querySelector","this","list","startListeners","pointerHandle","e","target","closest","item","actionButton","itemDragging","details","drag","prepare","start","startDrag","addEventListener","itemMovedByClick","bind","dragStart","time","Date","getTime","x","y","originalOrder","getCurrentOrder","renderForPromise","itemHtml","html","itemClassName","attr","listClassName","classList","toString","proxyStyles","outerWidth","outerHeight","join","then","_ref","js","proxy","Templates","appendNodeContents","body","css","offset","addClass","itemMovingClass","updateProxy","dragMove","dragEnd","catch","Notification","exception","closestItem","closestDistance","querySelectorAll","forEach","element","distance","distanceBetweenElements","offsetValue","midY","insertBefore","insertAfter","items","i","length","find","arrayEquals","reorderDone","remove","removeClass","dataset","action","preventDefault","stopPropagation","prev","next","focus","midX","node","left","top","e1","e2","dx","dy","Math","sqrt","map","index","idGetter","get","a1","a2","every","v","sortableid","responseid","id","newOrder","value"],"mappings":"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"} \ No newline at end of file diff --git a/question/type/ordering/amd/src/drag_reorder.js b/question/type/ordering/amd/src/drag_reorder.js index b2a39dae261..d4ab6278fd2 100644 --- a/question/type/ordering/amd/src/drag_reorder.js +++ b/question/type/ordering/amd/src/drag_reorder.js @@ -26,7 +26,6 @@ import $ from 'jquery'; import drag from 'core/dragdrop'; -import keys from 'core/key_codes'; import Templates from 'core/templates'; import Notification from 'core/notification'; @@ -101,10 +100,6 @@ export default class DragReorder { this.orderList = document.querySelector(this.config.list); this.startListeners(); - - // Make the items tabbable. - // TODO: This can be removed once we move to templates and add the tabindex there. - $(this.combineSelectors(config.list, config.item)).attr('tabindex', '0'); } /** @@ -128,7 +123,6 @@ export default class DragReorder { // Set up the list listeners for moving list items around. this.orderList.addEventListener('mousedown', pointerHandle); this.orderList.addEventListener('touchstart', pointerHandle); - this.orderList.addEventListener('keydown', this.itemMovedByKeyboard.bind(this)); this.orderList.addEventListener('click', this.itemMovedByClick.bind(this)); } @@ -214,11 +208,8 @@ export default class DragReorder { /** * End dragging. - * - * @param {number} x X co-ordinate - * @param {number} y Y co-ordinate */ - dragEnd(x, y) { + dragEnd() { if (typeof this.config.reorderEnd !== 'undefined') { this.config.reorderEnd(this.itemDragging.closest(this.config.list), this.itemDragging); } @@ -227,10 +218,6 @@ export default class DragReorder { // Order has changed, call the callback. this.config.reorderDone(this.itemDragging.closest(this.config.list), this.itemDragging, this.getCurrentOrder()); - } else if (new Date().getTime() - this.dragStart.time < 500 && - Math.abs(this.dragStart.x - x) < 10 && Math.abs(this.dragStart.y - y) < 10) { - // This was really a click. Set the focus on the current item. - this.itemDragging[0].focus(); } // Clean up after the drag is finished. @@ -241,49 +228,6 @@ export default class DragReorder { this.dragStart = null; } - /** - * Items can be moved and placed using certain keys. - * Tab for tabbing though and choose the item to be moved - * space, arrow-right arrow-down for moving current element forwards. - * arrow-right arrow-down for moving the current element backwards. - * - * @param {Event} e The keyboard event. - */ - itemMovedByKeyboard(e) { - if (e.target.closest(this.config.item)) { - this.itemDragging = $(e.target.closest(this.config.item)); - - // Store the current state of the list. - this.originalOrder = this.getCurrentOrder(); - - switch (e.keyCode) { - case keys.arrowRight: - case keys.arrowDown: - e.preventDefault(); - e.stopPropagation(); - if (this.itemDragging.next().length) { - this.itemDragging.next().insertBefore(this.itemDragging); - } - break; - - case keys.arrowLeft: - case keys.arrowUp: - e.preventDefault(); - e.stopPropagation(); - if (this.itemDragging.prev().length) { - this.itemDragging.prev().insertAfter(this.itemDragging); - } - break; - } - - // After we have potentially moved the item, we need to check if the order has changed. - if (!this.arrayEquals(this.originalOrder, this.getCurrentOrder())) { - // Order has changed, call the callback. - this.config.reorderDone(this.itemDragging.closest(this.config.list), this.itemDragging, this.getCurrentOrder()); - } - } - } - /** * Handles the movement of an item by click. * @@ -332,26 +276,6 @@ export default class DragReorder { } } - /** - * TODO: Once the tabindex is added to the template, this can be removed. - * Our outer and inner are two CSS selectors, which may contain commas. - * We want to combine them safely. So for instance combineSelectors('a, b', 'c, d') - * gives 'a c, a d, b c, b d'. - * - * @param {String} outer The selector for the outer element. - * @param {String} inner The selector for the inner element. - * @returns {String} The combined selector used to listen to the list item. - */ - combineSelectors(outer, inner) { - let combined = []; - outer.split(',').forEach(firstSelector => { - inner.split(',').forEach(secondSelector => { - combined.push(firstSelector.trim() + ' ' + secondSelector.trim()); - }); - }); - return combined.join(', '); - } - /** * Get the x-position of the middle of the DOM node represented by the given jQuery object. * diff --git a/question/type/ordering/styles.css b/question/type/ordering/styles.css index d1f157a83b7..f44a8267d86 100644 --- a/question/type/ordering/styles.css +++ b/question/type/ordering/styles.css @@ -32,10 +32,6 @@ cursor: move; margin-left: 26px; /* The margin is needed for the list-style-type in numberingxxx classes */ } -.que.ordering .sortablelist li.sortableitem:focus { - border-color: #0a0; - box-shadow: 0 0 5px 5px rgba(255, 255, 150, 1); -} .que.ordering .sortablelist li.sortableitem:first-of-type [data-action="move-backward"], .que.ordering .sortablelist li.sortableitem:last-of-type [data-action="move-forward"] {