MDL-51961 cbe: UX improvements to manage competencies
1. The competencies tree should open with all nodes initially collapsed
(unless there is less than 20 results, in which case they should all be expanded)
2. When the tree is refreshed, all previously selected nodes should be remembered
and the tree expanded to that point.
3. While the tree is reloading - there should be a "spinner"
This commit is contained in:
committed by
Frederic Massart
parent
91f3ad48d9
commit
6bf250f84e
+1
-1
@@ -1 +1 @@
|
||||
define(["core/ajax","core/notification","core/templates","tool_lp/tree","jquery"],function(a,b,c,d,e){var f=[],g=0,h="",i="",j=function(a,b){var c=0,d=!1;for(a.haschildren=!1,a.children=[],c=0;c<b.length;c++)d=b[c],d.parentid==a.id&&(a.haschildren=!0,a.children.push(d),j(d,b))},k=function(b){var k=e.Deferred(),l=a.call([{methodname:"tool_lp_search_competencies",args:{searchtext:b,competencyframeworkid:g,includerelated:!0}}]);return l[0].done(function(a){f=[];var b=0;for(b=0;b<a.length;b++)f[a[b].id]=a[b];var g=[],l=!1;for(b=0;b<a.length;b++)l=a[b],0===parseInt(l.parentid,10)&&(g.push(l),j(l,a));var m={shortname:h,competencies:g};c.render("tool_lp/competencies_tree_root",m).done(function(a,b){c.replaceNodeContents(e(i),e(a).html(),b),new d(i),k.resolve(f)}).fail(k.reject)}).fail(k.reject),k.promise()};return{init:function(a,c,d,e){g=a,h=c,i=e,k(d).fail(b.exception)},on:function(a,b){e(i).on(a,b)},getCompetencyFrameworkId:function(){return g},getCompetency:function(a){return f[a]},getCompetencyLevel:function(a){var b=this.getCompetency(a),c=b.path.replace(/^\/|\/$/g,"").split("/").length;return c},reloadCompetencies:function(){return k("").fail(b.exception)},listCompetencies:function(){return f}}});
|
||||
define(["core/ajax","core/notification","core/templates","tool_lp/tree","jquery"],function(a,b,c,d,e){var f=[],g=0,h="",i="",j="",k=function(a,b){var c=0,d=!1;for(a.haschildren=!1,a.children=[],c=0;c<b.length;c++)d=b[c],d.parentid==a.id&&(a.haschildren=!0,a.children.push(d),k(d,b))},l=function(b){var l=e.Deferred();return c.render("tool_lp/loading",{}).done(function(m,n){c.replaceNodeContents(e(i),m,n);var o=a.call([{methodname:"tool_lp_search_competencies",args:{searchtext:b,competencyframeworkid:g,includerelated:!0}}]);o[0].done(function(a){f=[];var b=0;for(b=0;b<a.length;b++)f[a[b].id]=a[b];var g=[],m=!1;for(b=0;b<a.length;b++)m=a[b],0===parseInt(m.parentid,10)&&(g.push(m),k(m,a));var n={shortname:h,competencies:g};c.render("tool_lp/competencies_tree_root",n).done(function(a,b){c.replaceNodeContents(e(i),e(a).html(),b);var g=new d(i);if(j){var h=e(i).find("[data-id="+j+"]");h.length&&g.updateFocus(h)}l.resolve(f)}).fail(l.reject)}).fail(l.reject)}),l.promise()},m=function(a,b){j=b.attr("data-id")};return{init:function(a,c,d,e){g=a,h=c,i=e,l(d).fail(b.exception),this.on("selectionchanged",m)},on:function(a,b){e(i).on(a,b)},getCompetencyFrameworkId:function(){return g},getCompetency:function(a){return f[a]},getCompetencyLevel:function(a){var b=this.getCompetency(a),c=b.path.replace(/^\/|\/$/g,"").split("/").length;return c},reloadCompetencies:function(){return l("").fail(b.exception)},listCompetencies:function(){return f}}});
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core/url","core/log"],function(a,b,c){var d=a('<img alt="" src="'+b.imageUrl("t/expanded")+'"/>'),e=a('<img alt="" src="'+b.imageUrl("t/collapsed")+'"/>'),f=function(b){this.treeRoot=a(b),this.items=this.treeRoot.find("li"),this.parents=this.treeRoot.find("li:has(ul)"),this.visibleItems=null,this.activeItem=null,this.keys={tab:9,enter:13,space:32,pageup:33,pagedown:34,end:35,home:36,left:37,up:38,right:39,down:40,asterisk:106},this.init(),this.bindEventHandlers()};return f.prototype.init=function(){this.parents.attr("aria-expanded","true"),this.parents.prepend(d.clone()),this.items.attr("role","tree-item"),this.items.attr("tabindex","-1"),this.parents.attr("role","group"),this.treeRoot.attr("role","tree"),this.visibleItems=this.treeRoot.find("li")},f.prototype.expandGroup=function(a){var b=a.children("ul");b.show().attr("aria-hidden","false"),a.attr("aria-expanded","true"),a.children("img").attr("src",d.attr("src")),this.visibleItems=this.treeRoot.find("li:visible")},f.prototype.collapseGroup=function(a){var b=a.children("ul");b.hide().attr("aria-hidden","true"),a.attr("aria-expanded","false"),a.children("img").attr("src",e.attr("src")),this.visibleItems=this.treeRoot.find("li:visible")},f.prototype.toggleGroup=function(a){"true"==a.attr("aria-expanded")?this.collapseGroup(a):this.expandGroup(a)},f.prototype.updateFocus=function(a){this.items.attr("aria-selected","false").attr("tabindex","-1"),a.attr("aria-selected","true").attr("tabindex",0),this.treeRoot.trigger("selectionchanged",[a])},f.prototype.handleKeyDown=function(b,c){var d=this.visibleItems.index(b);if(c.altKey||c.ctrlKey||c.shiftKey&&c.keyCode!=this.keys.tab)return!0;switch(c.keyCode){case this.keys.home:return this.activeItem=this.parents.first(),this.activeItem.focus(),c.stopPropagation(),!1;case this.keys.end:return this.activeItem=this.visibleItems.last(),this.activeItem.focus(),c.stopPropagation(),!1;case this.keys.enter:case this.keys.space:return b.has("ul")&&this.toggleGroup(b,!0),c.stopPropagation(),!1;case this.keys.left:if(b.has("ul")&&"true"==b.attr("aria-expanded"))this.collapseGroup(b);else{var e=b.parent(),f=e.parent();this.activeItem=f,this.activeItem.focus()}return c.stopPropagation(),!1;case this.keys.right:return b.has("ul")&&"false"==b.attr("aria-expanded")?this.expandGroup(b):(this.activeItem=b.children("ul").children("li").first(),this.activeItem.focus()),c.stopPropagation(),!1;case this.keys.up:if(d>0){var g=this.visibleItems.eq(d-1);this.activeItem=g,g.focus()}return c.stopPropagation(),!1;case this.keys.down:if(d<this.visibleItems.length-1){var h=this.visibleItems.eq(d+1);this.activeItem=h,h.focus()}return c.stopPropagation(),!1;case this.keys.asterisk:var i=this;return this.parents.each(function(){i.expandGroup(a(this))}),c.stopPropagation(),!1}return!0},f.prototype.handleKeyPress=function(a,b){if(b.altKey||b.ctrlKey||b.shiftKey)return!0;switch(b.keyCode){case this.keys.tab:return!0;case this.keys.enter:case this.keys.home:case this.keys.end:case this.keys.left:case this.keys.right:case this.keys.up:case this.keys.down:return b.stopPropagation(),!1;default:var c=String.fromCharCode(b.which),d=!1,e=this.visibleItems.index(a),f=this.visibleItems.length,g=e+1;for(g==f&&(g=0);g!=e;){var h=this.visibleItems.eq(g),i=h.text().charAt(0);if(h.has("ul")&&(i=h.find("span").text().charAt(0)),i.toLowerCase()==c){d=!0;break}g+=1,g==f&&(g=0)}return d===!0&&(this.activeItem=this.visibleItems.eq(g),this.activeItem.focus()),b.stopPropagation(),!1}return!0},f.prototype.on=function(a,b){"selectionchanged"!==a?c.warning('Invalid custom event name for tree. Only "selectionchanged" is supported.'):this.treeRoot.on(a,b)},f.prototype.handleDblClick=function(a,b){return b.altKey||b.ctrlKey||b.shiftKey?!0:(this.activeItem=a,this.updateFocus(a),this.toggleGroup(a),b.stopPropagation(),!1)},f.prototype.handleClick=function(a,b){return b.altKey||b.ctrlKey||b.shiftKey?!0:(this.activeItem=a,this.updateFocus(a),b.stopPropagation(),!1)},f.prototype.handleBlur=function(){return!0},f.prototype.handleFocus=function(a){return null===this.activeItem&&(this.activeItem=a),this.updateFocus(this.activeItem),!0},f.prototype.bindEventHandlers=function(){var b=this;this.parents.dblclick(function(c){return b.handleDblClick(a(this),c)}),this.items.click(function(c){return b.handleClick(a(this),c)}),this.items.keydown(function(c){return b.handleKeyDown(a(this),c)}),this.items.keypress(function(c){return b.handleKeyPress(a(this),c)}),this.items.focus(function(c){return b.handleFocus(a(this),c)}),this.items.blur(function(c){return b.handleBlur(a(this),c)})},f});
|
||||
define(["jquery","core/url","core/log"],function(a,b,c){var d=a('<img alt="" src="'+b.imageUrl("t/expanded")+'"/>'),e=a('<img alt="" src="'+b.imageUrl("t/collapsed")+'"/>'),f=function(b){this.treeRoot=a(b),this.items=this.treeRoot.find("li"),this.expandAll=this.items.length<20,this.parents=this.treeRoot.find("li:has(ul)"),this.visibleItems=null,this.activeItem=null,this.keys={tab:9,enter:13,space:32,pageup:33,pagedown:34,end:35,home:36,left:37,up:38,right:39,down:40,asterisk:106},this.init(),this.bindEventHandlers()};return f.prototype.init=function(){this.parents.attr("aria-expanded","true"),this.parents.prepend(d.clone()),this.items.attr("role","tree-item"),this.items.attr("tabindex","-1"),this.parents.attr("role","group"),this.treeRoot.attr("role","tree"),this.visibleItems=this.treeRoot.find("li");var b=this;this.expandAll||(this.parents.each(function(){b.collapseGroup(a(this))}),this.expandGroup(this.parents.first()))},f.prototype.expandGroup=function(a){var b=a.children("ul");b.show().attr("aria-hidden","false"),a.attr("aria-expanded","true"),a.children("img").attr("src",d.attr("src")),this.visibleItems=this.treeRoot.find("li:visible")},f.prototype.collapseGroup=function(a){var b=a.children("ul");b.hide().attr("aria-hidden","true"),a.attr("aria-expanded","false"),a.children("img").attr("src",e.attr("src")),this.visibleItems=this.treeRoot.find("li:visible")},f.prototype.toggleGroup=function(a){"true"==a.attr("aria-expanded")?this.collapseGroup(a):this.expandGroup(a)},f.prototype.updateFocus=function(a){for(var b=a.parent();"tree"!=b.attr("role");)b=b.parent(),"false"==b.attr("aria-expanded")&&this.expandGroup(b),b=b.parent();this.items.attr("aria-selected","false").attr("tabindex","-1"),a.attr("aria-selected","true").attr("tabindex",0),this.treeRoot.trigger("selectionchanged",[a])},f.prototype.handleKeyDown=function(b,c){var d=this.visibleItems.index(b);if(c.altKey||c.ctrlKey||c.shiftKey&&c.keyCode!=this.keys.tab)return!0;switch(c.keyCode){case this.keys.home:return this.activeItem=this.parents.first(),this.activeItem.focus(),c.stopPropagation(),!1;case this.keys.end:return this.activeItem=this.visibleItems.last(),this.activeItem.focus(),c.stopPropagation(),!1;case this.keys.enter:case this.keys.space:return b.has("ul")&&this.toggleGroup(b,!0),c.stopPropagation(),!1;case this.keys.left:if(b.has("ul")&&"true"==b.attr("aria-expanded"))this.collapseGroup(b);else{var e=b.parent(),f=e.parent();this.activeItem=f,this.activeItem.focus()}return c.stopPropagation(),!1;case this.keys.right:return b.has("ul")&&"false"==b.attr("aria-expanded")?this.expandGroup(b):(this.activeItem=b.children("ul").children("li").first(),this.activeItem.focus()),c.stopPropagation(),!1;case this.keys.up:if(d>0){var g=this.visibleItems.eq(d-1);this.activeItem=g,g.focus()}return c.stopPropagation(),!1;case this.keys.down:if(d<this.visibleItems.length-1){var h=this.visibleItems.eq(d+1);this.activeItem=h,h.focus()}return c.stopPropagation(),!1;case this.keys.asterisk:var i=this;return this.parents.each(function(){i.expandGroup(a(this))}),c.stopPropagation(),!1}return!0},f.prototype.handleKeyPress=function(a,b){if(b.altKey||b.ctrlKey||b.shiftKey)return!0;switch(b.keyCode){case this.keys.tab:return!0;case this.keys.enter:case this.keys.home:case this.keys.end:case this.keys.left:case this.keys.right:case this.keys.up:case this.keys.down:return b.stopPropagation(),!1;default:var c=String.fromCharCode(b.which),d=!1,e=this.visibleItems.index(a),f=this.visibleItems.length,g=e+1;for(g==f&&(g=0);g!=e;){var h=this.visibleItems.eq(g),i=h.text().charAt(0);if(h.has("ul")&&(i=h.find("span").text().charAt(0)),i.toLowerCase()==c){d=!0;break}g+=1,g==f&&(g=0)}return d===!0&&(this.activeItem=this.visibleItems.eq(g),this.activeItem.focus()),b.stopPropagation(),!1}return!0},f.prototype.on=function(a,b){"selectionchanged"!==a?c.warning('Invalid custom event name for tree. Only "selectionchanged" is supported.'):this.treeRoot.on(a,b)},f.prototype.handleDblClick=function(a,b){return b.altKey||b.ctrlKey||b.shiftKey?!0:(this.activeItem=a,this.updateFocus(a),this.toggleGroup(a),b.stopPropagation(),!1)},f.prototype.handleClick=function(a,b){return b.altKey||b.ctrlKey||b.shiftKey?!0:(this.activeItem=a,this.updateFocus(a),b.stopPropagation(),!1)},f.prototype.handleBlur=function(){return!0},f.prototype.handleFocus=function(a){return null===this.activeItem&&(this.activeItem=a),this.updateFocus(this.activeItem),!0},f.prototype.bindEventHandlers=function(){var b=this;this.parents.dblclick(function(c){return b.handleDblClick(a(this),c)}),this.items.click(function(c){return b.handleClick(a(this),c)}),this.items.keydown(function(c){return b.handleKeyDown(a(this),c)}),this.items.keypress(function(c){return b.handleKeyPress(a(this),c)}),this.items.focus(function(c){return b.handleFocus(a(this),c)}),this.items.blur(function(c){return b.handleBlur(a(this),c)})},f});
|
||||
@@ -37,6 +37,9 @@ define(['core/ajax', 'core/notification', 'core/templates', 'tool_lp/tree', 'jqu
|
||||
/** @var {String} treeSelector - The selector for the root of the tree. */
|
||||
var treeSelector = '';
|
||||
|
||||
/** @var {String} currentNodeId - The data-id of the current node in the tree. */
|
||||
var currentNodeId = '';
|
||||
|
||||
/**
|
||||
* Build a tree from the flat list of competencies.
|
||||
* @param {Object} parent The parent competency.
|
||||
@@ -64,45 +67,65 @@ define(['core/ajax', 'core/notification', 'core/templates', 'tool_lp/tree', 'jqu
|
||||
*/
|
||||
var loadCompetencies = function(searchtext) {
|
||||
var deferred = $.Deferred();
|
||||
var promises = ajax.call([{
|
||||
methodname: 'tool_lp_search_competencies',
|
||||
args: {
|
||||
searchtext: searchtext,
|
||||
competencyframeworkid: competencyFrameworkId,
|
||||
includerelated: true
|
||||
}
|
||||
}]);
|
||||
promises[0].done(function(result) {
|
||||
competencies = [];
|
||||
var i = 0;
|
||||
for (i = 0; i < result.length; i++) {
|
||||
competencies[result[i].id] = result[i];
|
||||
}
|
||||
|
||||
var children = [];
|
||||
var competency = false;
|
||||
for (i = 0; i < result.length; i++) {
|
||||
competency = result[i];
|
||||
if (parseInt(competency.parentid, 10) === 0) {
|
||||
children.push(competency);
|
||||
addChildren(competency, result);
|
||||
templates.render('tool_lp/loading', {}).done(function(loadinghtml, loadingjs) {
|
||||
templates.replaceNodeContents($(treeSelector), loadinghtml, loadingjs);
|
||||
|
||||
var promises = ajax.call([{
|
||||
methodname: 'tool_lp_search_competencies',
|
||||
args: {
|
||||
searchtext: searchtext,
|
||||
competencyframeworkid: competencyFrameworkId,
|
||||
includerelated: true
|
||||
}
|
||||
}]);
|
||||
promises[0].done(function(result) {
|
||||
competencies = [];
|
||||
var i = 0;
|
||||
for (i = 0; i < result.length; i++) {
|
||||
competencies[result[i].id] = result[i];
|
||||
}
|
||||
}
|
||||
var context = {
|
||||
shortname: competencyFrameworkShortName,
|
||||
competencies: children
|
||||
};
|
||||
templates.render('tool_lp/competencies_tree_root', context).done(function(html, js) {
|
||||
templates.replaceNodeContents($(treeSelector), $(html).html(), js);
|
||||
new Ariatree(treeSelector);
|
||||
deferred.resolve(competencies);
|
||||
}).fail(deferred.reject);
|
||||
|
||||
}).fail(deferred.reject);
|
||||
var children = [];
|
||||
var competency = false;
|
||||
for (i = 0; i < result.length; i++) {
|
||||
competency = result[i];
|
||||
if (parseInt(competency.parentid, 10) === 0) {
|
||||
children.push(competency);
|
||||
addChildren(competency, result);
|
||||
}
|
||||
}
|
||||
var context = {
|
||||
shortname: competencyFrameworkShortName,
|
||||
competencies: children
|
||||
};
|
||||
templates.render('tool_lp/competencies_tree_root', context).done(function(html, js) {
|
||||
templates.replaceNodeContents($(treeSelector), $(html).html(), js);
|
||||
var tree = new Ariatree(treeSelector);
|
||||
|
||||
if (currentNodeId) {
|
||||
var node = $(treeSelector).find('[data-id=' + currentNodeId + ']');
|
||||
if (node.length) {
|
||||
tree.updateFocus(node);
|
||||
}
|
||||
}
|
||||
deferred.resolve(competencies);
|
||||
}).fail(deferred.reject);
|
||||
}).fail(deferred.reject);
|
||||
});
|
||||
|
||||
return deferred.promise();
|
||||
};
|
||||
|
||||
/**
|
||||
* Whenever the current item in the tree is changed - remember the "id".
|
||||
* @param {Event} evt
|
||||
* @param {Node} The active node in the tree.
|
||||
*/
|
||||
var rememberCurrent = function(evt, node) {
|
||||
currentNodeId = node.attr('data-id');
|
||||
};
|
||||
|
||||
|
||||
return /** @alias module:tool_lp/competencytree */ {
|
||||
// Public variables and functions.
|
||||
@@ -119,6 +142,8 @@ define(['core/ajax', 'core/notification', 'core/templates', 'tool_lp/tree', 'jqu
|
||||
competencyFrameworkShortName = shortname;
|
||||
treeSelector = selector;
|
||||
loadCompetencies(search).fail(notification.exception);
|
||||
|
||||
this.on('selectionchanged', rememberCurrent);
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,6 +42,7 @@ define(['jquery', 'core/url', 'core/log'], function($, url, log) {
|
||||
this.treeRoot = $(selector);
|
||||
|
||||
this.items = this.treeRoot.find('li');
|
||||
this.expandAll = this.items.length < 20;
|
||||
this.parents = this.treeRoot.find('li:has(ul)');
|
||||
|
||||
this.visibleItems = null;
|
||||
@@ -82,6 +83,14 @@ define(['jquery', 'core/url', 'core/log'], function($, url, log) {
|
||||
this.treeRoot.attr('role', 'tree');
|
||||
|
||||
this.visibleItems = this.treeRoot.find('li');
|
||||
|
||||
var thisObj = this;
|
||||
if (!this.expandAll) {
|
||||
this.parents.each(function() {
|
||||
thisObj.collapseGroup($(this));
|
||||
});
|
||||
this.expandGroup(this.parents.first());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -146,6 +155,15 @@ define(['jquery', 'core/url', 'core/log'], function($, url, log) {
|
||||
* @param {Object} item is the jquery id of the parent item of the group
|
||||
*/
|
||||
Tree.prototype.updateFocus = function(item) {
|
||||
// Expand all nodes up the tree.
|
||||
var walk = item.parent();
|
||||
while (walk.attr('role') != 'tree') {
|
||||
walk = walk.parent();
|
||||
if (walk.attr('aria-expanded') == 'false') {
|
||||
this.expandGroup(walk);
|
||||
}
|
||||
walk = walk.parent();
|
||||
}
|
||||
this.items.attr('aria-selected', 'false').attr('tabindex', '-1');
|
||||
item.attr('aria-selected', 'true').attr('tabindex', 0);
|
||||
this.treeRoot.trigger('selectionchanged', [item]);
|
||||
|
||||
@@ -80,6 +80,7 @@ $string['linktemplatecompetencies'] = 'Link template competencies';
|
||||
$string['listcompetencyframeworkscaption'] = 'List of competency frameworks';
|
||||
$string['listplanscaption'] = 'List of learning plans';
|
||||
$string['listtemplatescaption'] = 'List of learning plan templates';
|
||||
$string['loading'] = 'Loading...';
|
||||
$string['locatecompetency'] = 'Locate competency';
|
||||
$string['lp:competencymanage'] = 'Manage competency frameworks';
|
||||
$string['lp:competencyread'] = 'View competency frameworks';
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{{#pix}}y/loading, core, {{#str}}loading, tool_lp{{/str}}{{/pix}}
|
||||
@@ -42,6 +42,7 @@
|
||||
</form>
|
||||
</p>
|
||||
<ul data-enhance="tree">
|
||||
{{> tool_lp/loading }}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user