MDL-39814 actionmenu: tweaked the action menu post integration

The following changes have been made:
 * course_section_cm_edit_actions now allows the caller more options for the
   display of the action menu.
 * The site menu block now disables the JS enhancement of the actionmenu so
   that it displays as it did before. After more testing this seemed like the
   only option that worked in the limited space of the block when it had
   several modules.
 * draganddrop of resources now triggers the action menu enhancement as well.
 * Fixed display of title when the menu is not being enhanced by JS.
 * Fixed the alignment of the completion icon in the bootstrapbase theme.
 * Tweaked the CSS for the standard theme.
This commit is contained in:
Sam Hemelryk 2013-08-01 13:01:04 +12:00
parent 4a4d57ae1f
commit f803ce267f
13 changed files with 206 additions and 71 deletions

View File

@ -91,8 +91,9 @@ class block_site_main_menu extends block_list {
if (!$ismoving) {
$actions = course_get_cm_edit_actions($mod, -1);
$editbuttons = html_writer::tag('div',
$courserenderer->course_section_cm_edit_actions($actions, $mod),
array('class' => 'buttons'));
$courserenderer->course_section_cm_edit_actions($actions, $mod, array('donotenhance' => true)),
array('class' => 'buttons')
);
} else {
$editbuttons = '';
}

View File

@ -1053,8 +1053,12 @@ M.course_dndupload = {
* @param sectionnumber the number of the selected course section
*/
add_editing: function(elementid) {
var node = Y.one('#' + elementid);
YUI().use('moodle-course-coursebase', function(Y) {
M.course.coursebase.invoke_function('setup_for_resource', '#' + elementid);
M.course.register_new_module(node);
});
if (M.core.actionmenu && M.core.actionmenu.newDOMNode) {
M.core.actionmenu.newDOMNode(node);
}
}
};

View File

@ -331,6 +331,9 @@ class core_course_renderer extends plugin_renderer_base {
* ownerselector => A JS/CSS selector that can be used to find an cm node.
* If specified the owning node will be given the class 'action-menu-shown' when the action
* menu is being displayed.
* constraintselector => A JS/CSS selector that can be used to find the parent node for which to constrain
* the action menu to when it is being displayed.
* donotenhance => If set to true the action menu that gets displayed won't be enhanced by JS.
* @return string
*/
public function course_section_cm_edit_actions($actions, cm_info $mod = null, $displayoptions = array()) {
@ -349,11 +352,17 @@ class core_course_renderer extends plugin_renderer_base {
$ownerselector = 'li.activity';
}
if (isset($displayoptions['constraintselector'])) {
$constraint = $displayoptions['constraintselector'];
} else {
$constraint = '.course-content';
}
$menu = new action_menu();
$menu->set_owner_selector($ownerselector);
$menu->set_contraint('.course-content');
$menu->set_contraint($constraint);
$menu->set_alignment(action_menu::TL, action_menu::TR);
if (isset($CFG->modeditingmenu) && !$CFG->modeditingmenu) {
if (isset($CFG->modeditingmenu) && !$CFG->modeditingmenu || !empty($displayoptions['donotenhance'])) {
$menu->do_not_enhance();
}
foreach ($actions as $action) {

View File

@ -896,15 +896,28 @@ YUI.add('moodle-course-toolboxes', function(Y) {
});
M.course = M.course || {};
M.course.resource_toolbox = null;
M.course.init_resource_toolbox = function(config) {
return new RESOURCETOOLBOX(config);
M.course.resource_toolbox = new RESOURCETOOLBOX(config);
return M.course.resource_toolbox;
};
M.course.init_section_toolbox = function(config) {
return new SECTIONTOOLBOX(config);
};
M.course.register_new_module = function(module) {
if (typeof module === 'string') {
module = Y.one(module);
}
if (M.course.resource_toolbox !== null) {
module.setData('toolbox', M.course.resource_toolbox);
module.all(SELECTOR.COMMANDSPAN+ ' ' + SELECTOR.ACTIVITYACTION).each(function(){
this.setData('activity', module);
});
}
}
},
'@VERSION@', {
requires : ['base', 'node', 'io', 'moodle-course-coursebase']

View File

@ -3175,6 +3175,9 @@ class action_menu implements renderable {
public function add_primary_action($action) {
if ($action instanceof action_link || $action instanceof pix_icon) {
$action->attributes['role'] = 'menuitem';
if ($action instanceof action_menu_link) {
$action->actionmenu = $this;
}
}
$this->primaryactions[] = $action;
}
@ -3187,6 +3190,9 @@ class action_menu implements renderable {
public function add_secondary_action($action) {
if ($action instanceof action_link || $action instanceof pix_icon) {
$action->attributes['role'] = 'menuitem';
if ($action instanceof action_menu_link) {
$action->actionmenu = $this;
}
}
$this->secondaryactions[] = $action;
}
@ -3296,6 +3302,15 @@ class action_menu implements renderable {
public function do_not_enhance() {
unset($this->attributes['data-enhance']);
}
/**
* Returns true if this action menu will be enhanced.
*
* @return bool
*/
public function will_be_enhanced() {
return isset($this->attributes['data-enhance']);
}
}
/**
@ -3314,6 +3329,12 @@ class action_menu_link extends action_link implements renderable {
*/
public $primary = true;
/**
* The action menu this link has been added to.
* @var action_menu
*/
public $actionmenu = null;
/**
* Constructs the object.
*

View File

@ -1114,10 +1114,10 @@ class core_renderer extends renderer_base {
$icon = '';
if ($action->icon) {
$icon = $action->icon;
if ($action->primary) {
if ($action->primary || !$action->actionmenu->will_be_enhanced()) {
$action->attributes['title'] = $action->text;
}
if ($icon->attributes['alt'] === $comparetoalt) {
if ($icon->attributes['alt'] === $comparetoalt && $action->actionmenu->will_be_enhanced()) {
$icon->attributes['alt'] = ' ';
}
$icon = $this->render($icon);

View File

@ -65,27 +65,34 @@ ACTIONMENU.prototype = {
*/
initializer : function() {
Y.log('Initialising action menu manager', 'note', ACTIONMENU.NAME);
var defaultalign = this.get('align').join('-');
Y.all(SELECTOR.MENU).each(function() {
var menucontent = this.one(SELECTOR.MENUCONTENT),
align;
if (!menucontent) {
return false;
}
align = menucontent.getData('align') || defaultalign;
this.one(SELECTOR.TOGGLE).set('aria-haspopup', true);
menucontent.set('aria-hidden', true);
if (!menucontent.hasClass('align-'+align)) {
menucontent.addClass('align-'+align);
}
if (menucontent.hasChildNodes()) {
this.setAttribute('data-enhanced', '1');
}
});
Y.all(SELECTOR.MENU).each(this.enhance, this);
BODY.delegate('click', this.toggleMenu, SELECTOR.MENU + ' ' + SELECTOR.TOGGLE, this);
BODY.delegate('key', this.toggleMenu, 'enter,space', SELECTOR.MENU + ' ' + SELECTOR.TOGGLE, this);
},
/**
* Enhances a menu adding aria attributes and flagging it as functional.
*
* @param {Node} menu
* @returns {boolean}
*/
enhance : function(menu) {
var menucontent = menu.one(SELECTOR.MENUCONTENT),
align;
if (!menucontent) {
return false;
}
align = menucontent.getData('align') || this.get('align').join('-');
menu.one(SELECTOR.TOGGLE).set('aria-haspopup', true);
menucontent.set('aria-hidden', true);
if (!menucontent.hasClass('align-'+align)) {
menucontent.addClass('align-'+align);
}
if (menucontent.hasChildNodes()) {
menu.setAttribute('data-enhanced', '1');
}
},
/**
* Hides the menu if it is visible.
* @method hideMenu
@ -153,7 +160,9 @@ ACTIONMENU.prototype = {
this.owner = (ownerselector) ? menu.ancestor(ownerselector) : null;
this.dialogue = menu;
menu.addClass('show');
this.owner.addClass(CSS.MENUSHOWN);
if (this.owner) {
this.owner.addClass(CSS.MENUSHOWN);
}
this.constrain(menucontent.set('aria-hidden', false));
return true;
},
@ -203,7 +212,7 @@ ACTIONMENU.prototype = {
if (nx < cx) {
// If nx is less than cx we need to move it right.
newleft = nx = cx;
} else if (nx + nwidth > cx + cwidth) {
} else if (nx + nwidth >= cx + cwidth) {
// The top right of the node is outside of the constraint, move it in.
newleft = cx + cwidth - nwidth;
}
@ -268,6 +277,14 @@ M.core = M.core || {};
*/
M.core.actionmenu = M.core.actionmenu || {};
/**
*
* @static
* @property instance
* @type {ACTIONMENU}
*/
M.core.actionmenu.instance = null;
/**
* Init function - will only ever create one instance of the actionmenu class.
* @method init
@ -278,4 +295,16 @@ M.core.actionmenu.init = M.core.actionmenu.init || function(params) {
M.core.actionmenu.instance = M.core.actionmenu.instance || new ACTIONMENU(params);
};
/**
* Registers a new DOM node with the action menu causing it to be enhanced if required.
* @param node
* @returns {boolean}
*/
M.core.actionmenu.newDOMNode = function(node) {
if (M.core.actionmenu.instance === null) {
return true;
}
node.all(SELECTOR.MENU).each(M.core.actionmenu.instance.enhance, M.core.actionmenu.instance);
};
}, '@VERSION@', {"requires": ["base", "event"]});

View File

@ -1 +1 @@
YUI.add("moodle-core-actionmenu",function(e,t){var n=e.one(e.config.doc.body),r={MENUSHOWN:"action-menu-shown"},i={MENU:".moodle-actionmenu[data-enhance=moodle-core-actionmenu]",MENUCONTENT:".menu[data-rel=menu-content]",TOGGLE:".toggle-display"},s,o={TL:"tl",TR:"tr",BL:"bl",BR:"br"};s=function(){s.superclass.constructor.apply(this,arguments)},s.prototype={dialogue:null,events:[],owner:null,initializer:function(){var t=this.get("align").join("-");e.all(i.MENU).each(function(){var e=this.one(i.MENUCONTENT),n;if(!e)return!1;n=e.getData("align")||t,this.one(i.TOGGLE).set("aria-haspopup",!0),e.set("aria-hidden",!0),e.hasClass("align-"+n)||e.addClass("align-"+n),e.hasChildNodes()&&this.setAttribute("data-enhanced","1")}),n.delegate("click",this.toggleMenu,i.MENU+" "+i.TOGGLE,this),n.delegate("key",this.toggleMenu,"enter,space",i.MENU+" "+i.TOGGLE,this)},hideMenu:function(){this.dialogue&&(this.dialogue.removeClass("show"),this.dialogue.one(i.MENUCONTENT).set("aria-hidden",!0),this.dialogue=null),this.owner&&(this.owner.removeClass(r.MENUSHOWN),this.owner=null);for(var e in this.events)this.events[e].detach&&this.events[e].detach();this.events=[]},toggleMenu:function(e){e.halt(!0),this.hideMenu(),this.showMenu(e.target.ancestor(i.MENU)),this.events.push(n.on("key",this.hideMenu,"esc",this)),this.events.push(n.on("click",this.hideIfOutside,this)),this.events.push(n.delegate("focus",this.hideIfOutside,"*",this))},hideIfOutside:function(e){!e.target.test(i.MENU)&&!e.target.ancestor(i.MENU)&&this.hideMenu()},showMenu:function(e){var t=e.getData("owner"),n=e.one(i.MENUCONTENT);return this.owner=t?e.ancestor(t):null,this.dialogue=e,e.addClass("show"),this.owner.addClass(r.MENUSHOWN),this.constrain(n.set("aria-hidden",!1)),!0},constrain:function(e){var t=e.getData("constraint"),n=e.getX(),r=e.getY(),i=e.get("offsetWidth"),s=e.get("offsetHeight"),o=0,u=0,a,f,l=null,c=null,h=null,p=null;t&&(t=e.ancestor(t)),t?(a=t.get("offsetWidth"),f=t.get("offsetHeight"),o=t.getX(),u=t.getY()):(a=e.get("docWidth"),f=e.get("docHeight")),i>a?(l=i=a,h=n=o):n<o?h=n=o:n+i>o+a&&(h=o+a-i),s>f?(c=s=f,p=r=u):r<u?p=r=u:r+s>u+f&&(p=u+f-s),h!==null&&e.setX(h),p!==null&&e.setY(p),l!==null&&e.setStyle("width",l.toString()+"px"),c!==null&&e.setStyle("height",c.toString()+"px")}},e.extend(s,e.Base,s.prototype,{NAME:"moodle-core-actionmenu",ATTRS:{align:{value:[o.TR,o.BR]}}}),M.core=M.core||{},M.core.actionmenu=M.core.actionmenu||{},M.core.actionmenu.init=M.core.actionmenu.init||function(e){M.core.actionmenu.instance=M.core.actionmenu.instance||new s(e)}},"@VERSION@",{requires:["base","event"]});
YUI.add("moodle-core-actionmenu",function(e,t){var n=e.one(e.config.doc.body),r={MENUSHOWN:"action-menu-shown"},i={MENU:".moodle-actionmenu[data-enhance=moodle-core-actionmenu]",MENUCONTENT:".menu[data-rel=menu-content]",TOGGLE:".toggle-display"},s,o={TL:"tl",TR:"tr",BL:"bl",BR:"br"};s=function(){s.superclass.constructor.apply(this,arguments)},s.prototype={dialogue:null,events:[],owner:null,initializer:function(){e.all(i.MENU).each(this.enhance,this),n.delegate("click",this.toggleMenu,i.MENU+" "+i.TOGGLE,this),n.delegate("key",this.toggleMenu,"enter,space",i.MENU+" "+i.TOGGLE,this)},enhance:function(e){var t=e.one(i.MENUCONTENT),n;if(!t)return!1;n=t.getData("align")||this.get("align").join("-"),e.one(i.TOGGLE).set("aria-haspopup",!0),t.set("aria-hidden",!0),t.hasClass("align-"+n)||t.addClass("align-"+n),t.hasChildNodes()&&e.setAttribute("data-enhanced","1")},hideMenu:function(){this.dialogue&&(this.dialogue.removeClass("show"),this.dialogue.one(i.MENUCONTENT).set("aria-hidden",!0),this.dialogue=null),this.owner&&(this.owner.removeClass(r.MENUSHOWN),this.owner=null);for(var e in this.events)this.events[e].detach&&this.events[e].detach();this.events=[]},toggleMenu:function(e){e.halt(!0),this.hideMenu(),this.showMenu(e.target.ancestor(i.MENU)),this.events.push(n.on("key",this.hideMenu,"esc",this)),this.events.push(n.on("click",this.hideIfOutside,this)),this.events.push(n.delegate("focus",this.hideIfOutside,"*",this))},hideIfOutside:function(e){!e.target.test(i.MENU)&&!e.target.ancestor(i.MENU)&&this.hideMenu()},showMenu:function(e){var t=e.getData("owner"),n=e.one(i.MENUCONTENT);return this.owner=t?e.ancestor(t):null,this.dialogue=e,e.addClass("show"),this.owner&&this.owner.addClass(r.MENUSHOWN),this.constrain(n.set("aria-hidden",!1)),!0},constrain:function(e){var t=e.getData("constraint"),n=e.getX(),r=e.getY(),i=e.get("offsetWidth"),s=e.get("offsetHeight"),o=0,u=0,a,f,l=null,c=null,h=null,p=null;t&&(t=e.ancestor(t)),t?(a=t.get("offsetWidth"),f=t.get("offsetHeight"),o=t.getX(),u=t.getY()):(a=e.get("docWidth"),f=e.get("docHeight")),i>a?(l=i=a,h=n=o):n<o?h=n=o:n+i>=o+a&&(h=o+a-i),s>f?(c=s=f,p=r=u):r<u?p=r=u:r+s>u+f&&(p=u+f-s),h!==null&&e.setX(h),p!==null&&e.setY(p),l!==null&&e.setStyle("width",l.toString()+"px"),c!==null&&e.setStyle("height",c.toString()+"px")}},e.extend(s,e.Base,s.prototype,{NAME:"moodle-core-actionmenu",ATTRS:{align:{value:[o.TR,o.BR]}}}),M.core=M.core||{},M.core.actionmenu=M.core.actionmenu||{},M.core.actionmenu.instance=null,M.core.actionmenu.init=M.core.actionmenu.init||function(e){M.core.actionmenu.instance=M.core.actionmenu.instance||new s(e)},M.core.actionmenu.newDOMNode=function(e){if(M.core.actionmenu.instance===null)return!0;e.all(i.MENU).each(M.core.actionmenu.instance.enhance,M.core.actionmenu.instance)}},"@VERSION@",{requires:["base","event"]});

View File

@ -64,27 +64,34 @@ ACTIONMENU.prototype = {
* @method initializer
*/
initializer : function() {
var defaultalign = this.get('align').join('-');
Y.all(SELECTOR.MENU).each(function() {
var menucontent = this.one(SELECTOR.MENUCONTENT),
align;
if (!menucontent) {
return false;
}
align = menucontent.getData('align') || defaultalign;
this.one(SELECTOR.TOGGLE).set('aria-haspopup', true);
menucontent.set('aria-hidden', true);
if (!menucontent.hasClass('align-'+align)) {
menucontent.addClass('align-'+align);
}
if (menucontent.hasChildNodes()) {
this.setAttribute('data-enhanced', '1');
}
});
Y.all(SELECTOR.MENU).each(this.enhance, this);
BODY.delegate('click', this.toggleMenu, SELECTOR.MENU + ' ' + SELECTOR.TOGGLE, this);
BODY.delegate('key', this.toggleMenu, 'enter,space', SELECTOR.MENU + ' ' + SELECTOR.TOGGLE, this);
},
/**
* Enhances a menu adding aria attributes and flagging it as functional.
*
* @param {Node} menu
* @returns {boolean}
*/
enhance : function(menu) {
var menucontent = menu.one(SELECTOR.MENUCONTENT),
align;
if (!menucontent) {
return false;
}
align = menucontent.getData('align') || this.get('align').join('-');
menu.one(SELECTOR.TOGGLE).set('aria-haspopup', true);
menucontent.set('aria-hidden', true);
if (!menucontent.hasClass('align-'+align)) {
menucontent.addClass('align-'+align);
}
if (menucontent.hasChildNodes()) {
menu.setAttribute('data-enhanced', '1');
}
},
/**
* Hides the menu if it is visible.
* @method hideMenu
@ -151,7 +158,9 @@ ACTIONMENU.prototype = {
this.owner = (ownerselector) ? menu.ancestor(ownerselector) : null;
this.dialogue = menu;
menu.addClass('show');
this.owner.addClass(CSS.MENUSHOWN);
if (this.owner) {
this.owner.addClass(CSS.MENUSHOWN);
}
this.constrain(menucontent.set('aria-hidden', false));
return true;
},
@ -201,7 +210,7 @@ ACTIONMENU.prototype = {
if (nx < cx) {
// If nx is less than cx we need to move it right.
newleft = nx = cx;
} else if (nx + nwidth > cx + cwidth) {
} else if (nx + nwidth >= cx + cwidth) {
// The top right of the node is outside of the constraint, move it in.
newleft = cx + cwidth - nwidth;
}
@ -266,6 +275,14 @@ M.core = M.core || {};
*/
M.core.actionmenu = M.core.actionmenu || {};
/**
*
* @static
* @property instance
* @type {ACTIONMENU}
*/
M.core.actionmenu.instance = null;
/**
* Init function - will only ever create one instance of the actionmenu class.
* @method init
@ -276,4 +293,16 @@ M.core.actionmenu.init = M.core.actionmenu.init || function(params) {
M.core.actionmenu.instance = M.core.actionmenu.instance || new ACTIONMENU(params);
};
/**
* Registers a new DOM node with the action menu causing it to be enhanced if required.
* @param node
* @returns {boolean}
*/
M.core.actionmenu.newDOMNode = function(node) {
if (M.core.actionmenu.instance === null) {
return true;
}
node.all(SELECTOR.MENU).each(M.core.actionmenu.instance.enhance, M.core.actionmenu.instance);
};
}, '@VERSION@', {"requires": ["base", "event"]});

View File

@ -63,27 +63,34 @@ ACTIONMENU.prototype = {
*/
initializer : function() {
Y.log('Initialising action menu manager', 'note', ACTIONMENU.NAME);
var defaultalign = this.get('align').join('-');
Y.all(SELECTOR.MENU).each(function() {
var menucontent = this.one(SELECTOR.MENUCONTENT),
align;
if (!menucontent) {
return false;
}
align = menucontent.getData('align') || defaultalign;
this.one(SELECTOR.TOGGLE).set('aria-haspopup', true);
menucontent.set('aria-hidden', true);
if (!menucontent.hasClass('align-'+align)) {
menucontent.addClass('align-'+align);
}
if (menucontent.hasChildNodes()) {
this.setAttribute('data-enhanced', '1');
}
});
Y.all(SELECTOR.MENU).each(this.enhance, this);
BODY.delegate('click', this.toggleMenu, SELECTOR.MENU + ' ' + SELECTOR.TOGGLE, this);
BODY.delegate('key', this.toggleMenu, 'enter,space', SELECTOR.MENU + ' ' + SELECTOR.TOGGLE, this);
},
/**
* Enhances a menu adding aria attributes and flagging it as functional.
*
* @param {Node} menu
* @returns {boolean}
*/
enhance : function(menu) {
var menucontent = menu.one(SELECTOR.MENUCONTENT),
align;
if (!menucontent) {
return false;
}
align = menucontent.getData('align') || this.get('align').join('-');
menu.one(SELECTOR.TOGGLE).set('aria-haspopup', true);
menucontent.set('aria-hidden', true);
if (!menucontent.hasClass('align-'+align)) {
menucontent.addClass('align-'+align);
}
if (menucontent.hasChildNodes()) {
menu.setAttribute('data-enhanced', '1');
}
},
/**
* Hides the menu if it is visible.
* @method hideMenu
@ -151,7 +158,9 @@ ACTIONMENU.prototype = {
this.owner = (ownerselector) ? menu.ancestor(ownerselector) : null;
this.dialogue = menu;
menu.addClass('show');
this.owner.addClass(CSS.MENUSHOWN);
if (this.owner) {
this.owner.addClass(CSS.MENUSHOWN);
}
this.constrain(menucontent.set('aria-hidden', false));
return true;
},
@ -201,7 +210,7 @@ ACTIONMENU.prototype = {
if (nx < cx) {
// If nx is less than cx we need to move it right.
newleft = nx = cx;
} else if (nx + nwidth > cx + cwidth) {
} else if (nx + nwidth >= cx + cwidth) {
// The top right of the node is outside of the constraint, move it in.
newleft = cx + cwidth - nwidth;
}
@ -266,6 +275,14 @@ M.core = M.core || {};
*/
M.core.actionmenu = M.core.actionmenu || {};
/**
*
* @static
* @property instance
* @type {ACTIONMENU}
*/
M.core.actionmenu.instance = null;
/**
* Init function - will only ever create one instance of the actionmenu class.
* @method init
@ -274,4 +291,16 @@ M.core.actionmenu = M.core.actionmenu || {};
*/
M.core.actionmenu.init = M.core.actionmenu.init || function(params) {
M.core.actionmenu.instance = M.core.actionmenu.instance || new ACTIONMENU(params);
};
/**
* Registers a new DOM node with the action menu causing it to be enhanced if required.
* @param node
* @returns {boolean}
*/
M.core.actionmenu.newDOMNode = function(node) {
if (M.core.actionmenu.instance === null) {
return true;
}
node.all(SELECTOR.MENU).each(M.core.actionmenu.instance.enhance, M.core.actionmenu.instance);
};

View File

@ -27,8 +27,9 @@
.dir-rtl .sitetopic .section .activity .activityinstance,
.dir-rtl .course-content .section .activity .activityinstance { padding-right: 0; padding-left: 3em;}
.sitetopic .section .activity .commands,
.course-content .section .activity .commands { white-space: nowrap; display: inline; }
.section .activity .moodle-actionmenu .iconsmall {vertical-align: baseline;width:16px;height:16px;width:1rem;height:1rem;padding:0 0.5em 0.3em;}
.course-content .section .activity .commands { white-space: nowrap; display: inline-block; }
.section .activity .moodle-actionmenu .menubar > li > * {display:inline-block;min-height:16px;padding: 0.2em;}
.section .activity .moodle-actionmenu .iconsmall {vertical-align: baseline;width:16px;height:16px;}
.sitetopic .section li.activity,
.course-content .section li.activity {padding: .2em;}

View File

@ -39,7 +39,6 @@
margin: 1em;
}
.section .activity img.activityicon {
vertical-align: text-bottom;
margin-right: 6px;
}
.dir-rtl .section .activity img.activityicon {

File diff suppressed because one or more lines are too long