Elastic: Pretty select dropdown

pull/6292/head
Aleksander Machniak 7 years ago
parent 2548a7c55e
commit 15f0a1e8e6

@ -47,6 +47,7 @@
.popover { .popover {
box-shadow: 3px 3px 5px @color-popover-shadow; box-shadow: 3px 3px 5px @color-popover-shadow;
border-color: @color-layout-border;
padding: 0; padding: 0;
.popover-header { .popover-header {
@ -67,7 +68,7 @@
html.layout-small, html.layout-small,
html.layout-phone { html.layout-phone {
.popover { .popover:not(.select-menu) {
margin: 0 !important; margin: 0 !important;
padding: 0; padding: 0;
right: 0; right: 0;
@ -148,6 +149,20 @@ html.touch .popover {
} }
} }
.select-menu {
max-width: initial;
margin: 0;
.listing li a {
padding-left: .25rem;
outline: 0; // for Android browser
}
.popover-header {
border-radius: .25rem .25rem 0 0 !important;
}
}
/** PGP Key search/import dialog **/ /** PGP Key search/import dialog **/
.pgpkeyimport { .pgpkeyimport {

@ -112,9 +112,11 @@
span.secondary { span.secondary {
color: @color-list-secondary; color: @color-list-secondary;
} }
}
// Focus indicator // Focus indicator
@media screen and (min-width: @screen-width-large) { html:not(.touch) {
.listing {
li > a, li > a,
tbody tr > td:first-child, tbody tr > td:first-child,
&:not(.withselection) tbody tr > td.selection + td { &:not(.withselection) tbody tr > td.selection + td {

@ -62,14 +62,14 @@
<div class="form-group row"> <div class="form-group row">
<label for="compose-priority" class="col-form-label col-6"><roundcube:label name="priority" /></label> <label for="compose-priority" class="col-form-label col-6"><roundcube:label name="priority" /></label>
<div class="col-6"> <div class="col-6">
<roundcube:object name="prioritySelector" id="compose-priority" noform="true" tabindex="2" class="form-control" /> <roundcube:object name="prioritySelector" id="compose-priority" noform="true" tabindex="2" />
</div> </div>
</div> </div>
<roundcube:if condition="!config:no_save_sent_messages" /> <roundcube:if condition="!config:no_save_sent_messages" />
<div class="form-group row"> <div class="form-group row">
<label for="compose-store-target" class="col-form-label col-6"><roundcube:label name="savesentmessagein" /></label> <label for="compose-store-target" class="col-form-label col-6"><roundcube:label name="savesentmessagein" /></label>
<div class="col-6"> <div class="col-6">
<roundcube:object name="storetarget" id="compose-store-target" noform="true" tabindex="2" class="form-control" /> <roundcube:object name="storetarget" id="compose-store-target" noform="true" tabindex="2" />
</div> </div>
</div> </div>
<roundcube:endif /> <roundcube:endif />
@ -78,7 +78,7 @@
<div class="form-group row hidden"> <div class="form-group row hidden">
<label for="editor-selector" class="col-form-label col-6"><roundcube:label name="editortype" /></label> <label for="editor-selector" class="col-form-label col-6"><roundcube:label name="editortype" /></label>
<div class="col-6"> <div class="col-6">
<roundcube:object name="editorSelector" id="editor-selector" editorid="composebody" noform="true" tabindex="2" class="form-control" /> <roundcube:object name="editorSelector" id="editor-selector" editorid="composebody" noform="true" tabindex="2" />
</div> </div>
</div> </div>
<roundcube:endif /> <roundcube:endif />

@ -145,7 +145,7 @@
<div class="form-group row"> <div class="form-group row">
<label for="listoptions-sortcol" class="col-form-label col-sm-4"><roundcube:label name="listsorting" /></label> <label for="listoptions-sortcol" class="col-form-label col-sm-4"><roundcube:label name="listsorting" /></label>
<div class="col-sm-8"> <div class="col-sm-8">
<select id="listoptions-sortcol" name="sort_col" class="form-control"> <select id="listoptions-sortcol" name="sort_col">
<option value=""><roundcube:label name="nonesort" /></option> <option value=""><roundcube:label name="nonesort" /></option>
<option value="arrival"><roundcube:label name="arrival" /></option> <option value="arrival"><roundcube:label name="arrival" /></option>
<option value="date"><roundcube:label name="sentdate" /></option> <option value="date"><roundcube:label name="sentdate" /></option>
@ -163,7 +163,7 @@
<div class="form-group row"> <div class="form-group row">
<label for="listoptions-sortord" class="col-form-label col-sm-4"><roundcube:label name="listorder" /></label> <label for="listoptions-sortord" class="col-form-label col-sm-4"><roundcube:label name="listorder" /></label>
<div class="col-sm-8"> <div class="col-sm-8">
<select id="listoptions-sortord" name="sort_ord" class="form-control"> <select id="listoptions-sortord" name="sort_ord">
<option value="ASC"><roundcube:label name="asc" /></option> <option value="ASC"><roundcube:label name="asc" /></option>
<option value="DESC"><roundcube:label name="desc" /></option> <option value="DESC"><roundcube:label name="desc" /></option>
</select> </select>
@ -174,7 +174,7 @@
<div class="form-group row"> <div class="form-group row">
<label for="listoptions-threads" class="col-form-label col-sm-4"><roundcube:label name="lmode" /></label> <label for="listoptions-threads" class="col-form-label col-sm-4"><roundcube:label name="lmode" /></label>
<div class="col-sm-8"> <div class="col-sm-8">
<select id="listoptions-threads" name="mode" class="form-control"> <select id="listoptions-threads" name="mode">
<option value="list"><roundcube:label name="list" /></option> <option value="list"><roundcube:label name="list" /></option>
<option value="threads"><roundcube:label name="threads" /></option> <option value="threads"><roundcube:label name="threads" /></option>
</select> </select>

@ -74,6 +74,7 @@ function rcube_elastic_ui()
this.switch_nav_list = switch_nav_list; this.switch_nav_list = switch_nav_list;
this.searchbar_init = searchbar_init; this.searchbar_init = searchbar_init;
this.pretty_checkbox = pretty_checkbox; this.pretty_checkbox = pretty_checkbox;
this.pretty_select = pretty_select;
// Initialize layout // Initialize layout
@ -109,16 +110,21 @@ function rcube_elastic_ui()
$('.header > .searchbar').each(function() { searchbar_init(this); }); $('.header > .searchbar').each(function() { searchbar_init(this); });
$('.header > .searchfilterbar').each(function() { searchfilterbar_init(this); }); $('.header > .searchfilterbar').each(function() { searchfilterbar_init(this); });
// Intercept jQuery-UI dialogs to re-style them // Intercept jQuery-UI dialogs...
if ($.ui) { $.ui && $.widget('ui.dialog', $.ui.dialog, {
$.widget('ui.dialog', $.ui.dialog, { open: function() {
open: function() { this._super();
this._super(); // ... to re-style them on dialog open
dialog_open(this); dialog_open(this);
return this; return this;
} },
}); close: function() {
} this._super();
// ... to close custom select dropdowns on dialog close
$('.select-menu:visible').remove();
return this;
}
});
// menu/sidebar/list button // menu/sidebar/list button
buttons.menu.on('click', function() { app_menu(true); return false; }); buttons.menu.on('click', function() { app_menu(true); return false; });
@ -644,6 +650,7 @@ function rcube_elastic_ui()
context = document; context = document;
} }
// Buttons
$('input.button,button', context).not('.btn').addClass('btn').not('.btn-primary,.primary,.mainaction').addClass('btn-secondary'); $('input.button,button', context).not('.btn').addClass('btn').not('.btn-primary,.primary,.mainaction').addClass('btn-secondary');
$('input.button.mainaction,button.primary,button.mainaction', context).addClass('btn-primary'); $('input.button.mainaction,button.primary,button.mainaction', context).addClass('btn-primary');
$('button.btn.delete,button.btn.discard', context).addClass('btn-danger'); $('button.btn.delete,button.btn.discard', context).addClass('btn-danger');
@ -898,6 +905,8 @@ function rcube_elastic_ui()
.parent().addClass('input-group'); .parent().addClass('input-group');
}); });
} }
$('select:not([multiple])', context).each(function() { pretty_select(this); });
}; };
/** /**
@ -1513,15 +1522,8 @@ function rcube_elastic_ui()
alert_style(p.object, p.type, true); alert_style(p.object, p.type, true);
$(p.object).attr('role', 'alert'); $(p.object).attr('role', 'alert');
/*
$('a', p.object).addClass('alert-link');
// show a popup dialog on errors // $('a', p.object).addClass('alert-link');
if (p.type == 'error' && rcmail.env.task != 'login') {
// hide original message object, we don't want both
rcmail.hide_message(p.object);
}
*/
}; };
/** /**
@ -2833,6 +2835,147 @@ function rcube_elastic_ui()
); );
}; };
/**
* Make select dropdowns pretty
* TODO: searching, optgroup, [multiple], iPhone/iPad
*/
function pretty_select(select)
{
// iPhone is not supported yet (problem with browser dropdown on focus)
if (bw.iphone || bw.ipad) {
return;
}
select = $(select);
var close_func = function() {
var open = $('.select-menu').length;
select.popover('dispose').focus();
return !open;
};
var open_func = function(e) {
var items = [],
dialog = select.parents('.ui-dialog')[0],
min_width = select.outerWidth(),
max_height = $(document.body).height() - 75,
max_width = $(document.body).width() - 20,
value = select.val();
if (!is_mobile()) {
max_height *= 0.5;
}
$('option', select).each(function() {
var link = $('<a href="#">').text($(this).text())
.data('value', this.value)
.addClass(this.disabled ? 'disabled' : 'active' + (this.value == value ? ' selected' : ''));
items.push($('<li>').append(link));
});
var list = $('<ul class="toolbarmenu listing selectable iconized">')
.append(items)
.data('button', select[0]) // needed for dropdown closing code
.on('click', 'a.active', function() {
select.val($(this).data('value')).change();
return close_func();
})
.on('keydown', 'a.active', function(e) {
var item, node, mode = 'next';
// Close popup on ESC key
switch (e.which) {
case 27: // ESC
case 9: // TAB
return close_func();
case 13: // ENTER
case 32: // SPACE
$(this).click();
return false; // for IE
case 38: // ARROW-UP
case 63232:
mode = 'previous';
case 40: // ARROW-DOWN
case 63233:
item = e.target.parentNode;
while (item = item[mode + 'Sibling']) {
if (node = $(item).children('.active')[0]) {
node.focus();
break;
}
}
break;
}
});
select.popover('dispose')
.popover({
// because of focus issues we can't always use body,
// if select is in a dialog, popover has to be a child of this dialog
container: dialog || 'body',
content: list[0],
placement: 'bottom',
trigger: 'manual',
boundary: 'viewport',
html: true,
offset: '0,2',
template: '<div class="popover select-menu" style="min-width: ' + min_width + 'px; max-width: ' + max_width + 'px">'
+ '<div class="popover-header"></div>'
+ '<div class="popover-body" style="max-height: ' + max_height + 'px"></div></div>'
})
.on('shown.bs.popover', function() {
// Set popup Close title
$('#' + select.attr('aria-describedby') + ' > .popover-header')
.empty()
.append($('<a class="button icon cancel">').text(rcmail.gettext('close'))
.on('click', function(e) {
e.stopPropagation();
return close_func();
})
);
// focus first active element on the list
if (rcube_event.is_keyboard(e)) {
list.find('a.active:first').focus();
}
})
.popover('show');
};
select.on('mousedown keydown', function(e) {
// Do nothing on disabled select or on TAB key
if (select.prop('disabled') || e.which == 9) {
return;
}
// Close popup on ESC key
if (e.which == 27) {
return close_func();
}
// Close popup on click if already open
if (e.type == 'mousedown' && $('.select-menu:visible .listing').data('button') == select[0]) {
return close_func();
}
select.focus();
// prevent displaying browser-default select dropdown
select.prop('disabled', true);
setTimeout(function() { select.prop('disabled', false); }, 0);
e.stopPropagation();
// display options in our way (on SPACE, ENTER, ARROW-DOWN or mousedown)
if (e.type == 'mousedown' || e.which == 13 || e.which == 32 || e.which == 40 || e.which == 63233) {
open_func(e);
}
return false;
});
};
/** /**
* HTML editor textarea wrapper with nice looking tabs-like switch * HTML editor textarea wrapper with nice looking tabs-like switch
*/ */

Loading…
Cancel
Save