Popup menus with Bootstrap popover (#17)

pull/5742/merge
Aleksander Machniak 8 years ago
parent b997d87f75
commit 73d77511ce

@ -38,6 +38,7 @@ div.popover-content > .popupmenu {
div.popover-content {
padding: 0;
overflow-x: hidden;
}
div.popover > h3 {

@ -127,7 +127,7 @@
<li role="menuitem"><label><input type="checkbox" name="s_mods[]" value="email" id="s_mod_email" /> <span><roundcube:label name="email" /></span></label></li>
<li role="menuitem"><label><input type="checkbox" name="s_mods[]" value="*" id="s_mod_all" /> <span><roundcube:label name="allfields" /></span></label></li>
</ul>
<div class="buttons"><button class="btn btn-primary icon search" href="#" onclick="if (rcmail.command('search')) UI.show_popup('searchmenu',false)"><roundcube:label name="search" /></button></div>
<div class="buttons"><button class="btn btn-primary icon search" href="#" onclick="if (rcmail.command('search')) UI.menu_hide('search-menu')"><roundcube:label name="search" /></button></div>
</div>
<div id="dragcontact-menu" class="popupmenu">

@ -50,7 +50,7 @@
</ul>
</div>
<div id="search-filter" class="popupmenu">
<div id="search-filter" class="popupmenu" data-editable="true">
<roundcube:object name="folderfilter" id="folderlist-filter" class="listing" noheader="true" />
</div>

@ -64,8 +64,8 @@
<roundcube:button type="link-menuitem" command="download" label="emlsave" class="download" classAct="download active" />
<roundcube:button type="link-menuitem" command="edit" prop="new" label="editasnew" class="edit asnew" classAct="edit asnew active" />
<roundcube:button type="link-menuitem" command="viewsource" label="viewsource" class="source" classAct="source active" />
<roundcube:button type="link-menuitem" command="move" label="moveto" class="move" classAct="move active" innerclass="folder-selector-link" />
<roundcube:button type="link-menuitem" command="copy" label="copyto" class="copy" classAct="copy active" innerclass="folder-selector-link" />
<roundcube:button type="link-menuitem" command="move" label="moveto" class="move" classAct="move active" innerclass="folder-selector-link" aria-haspopup="true" />
<roundcube:button type="link-menuitem" command="copy" label="copyto" class="copy" classAct="copy active" innerclass="folder-selector-link" aria-haspopup="true" />
<roundcube:button type="link-menuitem" command="open" label="openinextwin" target="_blank" class="extwin" classAct="extwin active" />
<roundcube:container name="messagemenu" id="message-menu" />
</ul>

@ -119,7 +119,7 @@
<li role="menuitem"><label><input type="radio" name="s_scope" value="sub" id="s_scope_sub" /> <span><roundcube:label name="subfolders" /></span></label></li>
<li role="menuitem"><label><input type="radio" name="s_scope" value="all" id="s_scope_all" /> <span><roundcube:label name="allfolders" /></span></label></li>
</ul>
<div class="buttons"><button class="btn btn-primary icon search" href="#" onclick="if (rcmail.command('search')) UI.show_popup('searchmenu',false)"><roundcube:label name="search" /></button></div>
<div class="buttons"><button class="btn btn-primary icon search" href="#" onclick="if (rcmail.command('search')) UI.menu_hide('search-menu')"><roundcube:label name="search" /></button></div>
</div>
<div id="dragmessage-menu" class="popupmenu">

@ -29,6 +29,7 @@ function rcube_elastic_ui()
compose_extwin: false
}
},
menus = {},
content_buttons = [],
layout = {
menu: $('#layout > .menu'),
@ -45,6 +46,7 @@ function rcube_elastic_ui()
// Public methods
this.register_frame_buttons = register_frame_buttons;
this.menu_hide = menu_hide;
this.about_dialog = about_dialog;
this.spellmenu = spellmenu;
this.searchmenu = searchmenu;
@ -367,9 +369,8 @@ function rcube_elastic_ui()
});
};
// TODO: Fix unwanted popups closing on click inside a popup
$(document).on('click', close_all_popups);
rcube_webmail.set_iframe_events({mousedown: close_all_popups});
$(document).on('click', popups_close);
rcube_webmail.set_iframe_events({mousedown: popups_close});
};
/**
@ -681,6 +682,33 @@ function rcube_elastic_ui()
$(object).addClass(classes);
}
/**
* Set UI dialogs size/style depending on screen size
*/
function dialog_open(dialog)
{
var me = $(dialog.uiDialog),
width = me.width(),
height = me.height(),
maxWidth = $(window).width(),
maxHeight = $(window).height();
if (maxWidth <= 480) {
me.css({width: '100%', height: '100%'});
}
else {
if (height > maxHeight) {
me.css('height', '100%');
}
if (width > maxWidth) {
me.css('width', '100%');
}
}
// TODO: style buttons/forms
bootstrap_style(dialog.uiDialog);
};
/**
* Initializes searchbar widget
*/
@ -797,69 +825,84 @@ function rcube_elastic_ui()
{
var popup_id = $(item).data('popup'),
popup = $('#' + popup_id)[0],
title = $(item).attr('title'),
popup_position = $(item).data('popup-pos') || 'bottom';
title = $(item).attr('title');
$(item).attr({
'aria-haspopup': 'true',
'aria-expanded': 'false',
'aria-owns': popup_id
'aria-owns': popup_id,
})
.popover({
trigger: 'click',
container: 'body',
trigger: $(item).data('popup-trigger') || 'click',
content: popup,
placement: popup_position,
placement: $(item).data('popup-pos') || 'bottom',
animation: true,
html: true
})
.on('show.bs.popover', function(event) {
.on('show.bs.popover', function(event, el) {
var init_func = $(popup).data('popup-init');
$(popup).attr('aria-hidden', false);
if (init_func && ref[init_func]) {
ref[init_func](popup, item, event);
}
else if (init_func && window[init_func]) {
window[init_func](popup, item, event);
}
$(popup).attr('aria-hidden', false)
// Set popup height so it is less than the window height
.css('max-height', Math.min(500, $(window).height() - 5));
})
.on('hide.bs.popover', function() {
$(popup).attr('aria-hidden', true);
.on('shown.bs.popover', function(event, el) {
if (popup_id && menus[popup_id]) {
menus[popup_id].transitioning = false;
}
})
.on('hidden.bs.popover', function() {
$(popup).attr('aria-hidden', true)
// Bootstrap will detach the popup element from
// the DOM (https://github.com/twbs/bootstrap/issues/20219)
// making our menus to not update buttons state.
// Work around this by attaching it back to the body.
.appendTo(document.body);
if (popup_id && menus[popup_id]) {
menus[popup_id].transitioning = false;
}
})
.attr('title', title); // re-add title attribute removed by bootstrap
.on('keypress', function(event) {
// Close the popup on ESC key
if (event.originalEvent.keyCode == 27) {
$(item).popover('hide');
}
});
// re-add title attribute removed by bootstrap popover
if (title) {
$(item).attr('title', title);
}
$(popup).attr('aria-hidden', 'true').data('button', item);
// TODO: Fix popup positioning
// TODO: Set popup height so it is less than the window height
$(popup).attr('aria-hidden', 'true')
.data('button', item);
// stop propagation to e.g. do not hide the popup when
// clicking inside on form elements
if ($(popup).data('editable')) {
$(popup).on('click mousedown', function(e) { e.stopPropagation(); });
}
};
/**
* Set UI dialogs size/style depending on screen size
* Closes all popups (for use as event handler)
*/
function dialog_open(dialog)
function popups_close(e)
{
var me = $(dialog.uiDialog),
width = me.width(),
height = me.height(),
maxWidth = $(window).width(),
maxHeight = $(window).height();
$('.popover-content:visible').each(function() {
var popup = $(this),
button = popup.children().first().data('button');
if (maxWidth <= 480) {
me.css({width: '100%', height: '100%'});
}
else {
if (height > maxHeight) {
me.css('height', '100%');
}
if (width > maxWidth) {
me.css('width', '100%');
if (button && e.target != button && !$(button).find(e.target).length) {
$(button).popover('hide');
}
}
// TODO: style buttons/forms
bootstrap_style(dialog.uiDialog);
});
};
/**
@ -870,12 +913,68 @@ function rcube_elastic_ui()
if (p && p.name == 'messagelistmenu') {
menu_messagelist(p);
}
else if (p && p.name == 'folder-selector') {
$('ul:first', p.obj).addClass('listing folderlist');
// $(p.obj).addClass('popupmenu');
else if (p && p.name) {
var target = p.originalEvent.target;
if ($(target).is('span')) {
target = $(target).parents('a,li')[0];
}
if (p.event == 'menu-open') {
var fn, content = $('ul:first', p.obj);
if (p.name == 'folder-selector') {
content.addClass('listing folderlist');
}
else if (content.hasClass('toolbarmenu')) {
content.addClass('listing');
}
// Popover menus use animation. Sometimes the same menu is
// immediately hidden and shown (e.g. folder-selector for copy and move action)
// we have to way until the previous menu hides before we can open it again
fn = function() {
if (menus[p.name] && menus[p.name].transitioning) {
return setTimeout(fn, 50);
}
if (!$(target).data('popup')) {
$(target).data({popup: p.name, 'popup-pos': 'right', 'popup-trigger': 'manual'});
popup_init(target);
}
menus[p.name] = {target: target, transitioning: true};
$(target).popover('show');
}
fn();
}
else {
menu_hide(p.name);
}
// Stop propagation so multi-level menus work properly
p.originalEvent.stopPropagation();
}
};
/**
* Close menu_hide by name
*/
function menu_hide(name)
{
var target;
if (menus[name]) {
menus[name].transitioning = true;
target = menus[name].target;
}
else {
target = $('#' + name).data('button');
}
$(target).popover('hide');
};
/**
* Messages list options dialog
*/

Loading…
Cancel
Save