Use Bootstrap 4, forget about Semantic UI

pull/5742/merge
Aleksander Machniak 8 years ago
parent 8ee2b96a3d
commit 5929dc2ac8

@ -57,10 +57,3 @@ RULES:
- Minimum supported screen width is 240px (note that even if the device screen - Minimum supported screen width is 240px (note that even if the device screen
resolution is e.g.320x372 changing the text size in device settings will reduce resolution is e.g.320x372 changing the text size in device settings will reduce
the resolution) the resolution)
IDEAS/TODOs:
------------
- Planned use of a css framework, e.g. semantic-ui, bootstrap (to be decided)
- build custom semantic-ui style/js, we don't really want 800kB (already minified) file
- use semantic-ui/loader when loading content to content-frames (on phones only)?

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -4,7 +4,8 @@
<roundcube:object name="message" id="messagestack" /> <roundcube:object name="message" id="messagestack" />
<script src="/semantic-ui/semantic.min.js"></script> <script src="/bootstrap/tether.min.js"></script>
<script src="/bootstrap/bootstrap.min.js"></script>
<script src="/ui.js"></script> <script src="/ui.js"></script>
</body> </body>

@ -2,9 +2,9 @@
<html> <html>
<head> <head>
<title><roundcube:object name="pagetitle" /></title> <title><roundcube:object name="pagetitle" /></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" id="viewport" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no" id="viewport" />
<link rel="shortcut icon" href="/images/favicon.ico"/> <link rel="shortcut icon" href="/images/favicon.ico"/>
<link rel="stylesheet" href="/semantic-ui/semantic.min.css"> <link rel="stylesheet" href="/bootstrap/bootstrap.min.css">
<roundcube:if condition="config:devel_mode" /> <roundcube:if condition="config:devel_mode" />
<link rel="stylesheet/less" type="text/css" href="/styles/styles.less" /> <link rel="stylesheet/less" type="text/css" href="/styles/styles.less" />
<script src="/styles/less.min.js" data-env="development"></script> <script src="/styles/less.min.js" data-env="development"></script>

@ -25,7 +25,7 @@
<roundcube:if condition="template:name == 'message'" /> <roundcube:if condition="template:name == 'message'" />
<roundcube:button command="move" type="link" <roundcube:button command="move" type="link"
class="button move disabled" classAct="button move" class="button move disabled" classAct="button move"
label="move" title="moveto" data-menu-pos="bottom" innerclass="inner" /> label="move" title="moveto" innerclass="inner" />
<roundcube:button command="print" type="link" <roundcube:button command="print" type="link"
class="button print disabled" classAct="button print" class="button print disabled" classAct="button print"
label="print" title="printmessage" innerclass="inner" /> label="print" title="printmessage" innerclass="inner" />
@ -33,10 +33,10 @@
<roundcube:container name="toolbar" id="mailtoolbar" /> <roundcube:container name="toolbar" id="mailtoolbar" />
<roundcube:button name="markmenulink" id="markmessagemenulink" type="link" <roundcube:button name="markmenulink" id="markmessagemenulink" type="link"
class="button markmessage" label="mark" title="markmessages" class="button markmessage" label="mark" title="markmessages"
data-popup="markmessage-menu" data-popup-pos="bottom right" innerclass="inner" /> data-popup="markmessage-menu" innerclass="inner" />
<roundcube:button name="messagemenulink" id="messagemenulink" type="link" <roundcube:button name="messagemenulink" id="messagemenulink" type="link"
class="button more" label="more" title="moreactions" class="button more" label="more" title="moreactions"
data-popup="message-menu" data-popup-pos="bottom right" innerclass="inner" /> data-popup="message-menu" innerclass="inner" />
</div> </div>
<div id="forward-menu" class="ui popup"> <div id="forward-menu" class="ui popup">

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

@ -1,3 +1,14 @@
@font-face {
font-family: 'Icons';
src: url("../fonts/fontawesome.woff2") format('woff2'),
url("../fonts/fontawesome.woff") format('woff');
font-style: normal;
font-weight: normal;
font-variant: normal;
text-decoration: inherit;
text-transform: none;
}
/* roboto-regular - greek-ext_greek_latin_cyrillic-ext_latin-ext_cyrillic */ /* roboto-regular - greek-ext_greek_latin_cyrillic-ext_latin-ext_cyrillic */
@font-face { @font-face {
font-family: 'Roboto'; font-family: 'Roboto';
@ -38,7 +49,7 @@
url('../fonts/roboto-v15-greek-ext_greek_latin_cyrillic-ext_latin-ext_cyrillic-700italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ url('../fonts/roboto-v15-greek-ext_greek_latin_cyrillic-ext_latin-ext_cyrillic-700italic.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
} }
/* Define font-family. h1-h5 is needed here to overwrite semantic-ui font */ /* Define font-family. h1-h5 is needed here to overwrite bootstrap font */
body,h1,h2,h3,h4,h5 { body,button,input,optgroup,select,textarea {
font-family: Roboto, sans-serif; font-family: Roboto, sans-serif;
} }

@ -1,8 +1,10 @@
/* font-icons */ /* font-icons */
button.ui.button.icon:before, button.btn.icon:before,
a.btn.icon:before,
a.button.icon:before, a.button.icon:before,
.toolbar a.button:before, .toolbar a.button:before,
.ui.alert > i.icon:before,
.toolbarmenu li a:before, .toolbarmenu li a:before,
.folderlist li a:before, .folderlist li a:before,
.listing.iconized tr td:before, .listing.iconized tr td:before,
@ -39,8 +41,31 @@ a.rcmaddcontact:before,
backface-visibility: hidden; backface-visibility: hidden;
} }
button.ui.button.icon:before {
height: auto; @-webkit-keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
button.btn.icon:before {
font-size: 1em;
} }
.toolbar a.button:before { .toolbar a.button:before {
@ -104,6 +129,21 @@ button.ui.button.icon:before {
content: @fa-var-edit; content: @fa-var-edit;
} }
.ui.alert > i.icon:before {
content: @fa-var-info-circle;
}
.ui.alert.loading > i.icon:before {
content: @fa-var-circle-o-notch;
-webkit-animation: fa-spin 2s infinite linear;
animation: fa-spin 2s infinite linear;
}
.ui.alert.alert-warning > i.icon:before {
content: @fa-var-warning;
}
.ui.alert.alert-danger > i.icon:before {
content: @fa-var-exclamation-circle;
}
.listing.iconized li.preferences > a:before { .listing.iconized li.preferences > a:before {
content: @fa-var-sliders; content: @fa-var-sliders;
} }

@ -1,6 +1,6 @@
@import "fonts.less"; @import "fonts.less";
@page-font-size: 1em; @page-font-size: 14px;
@page-min-width: 240px; @page-min-width: 240px;
@taskmenu-width: 65px; @taskmenu-width: 65px;
@ -30,9 +30,16 @@ NOTE: below 1024px we do some UI elements bigger, as this is good (imho)
should we rather feature detect tables/phones? should we rather feature detect tables/phones?
*/ */
html {
height: 100%;
overflow: hidden;
}
body { body {
font-size: @page-font-size; font-size: @page-font-size;
min-width: @page-min-width; /* overwrite semantic-ui's limit of 320px */ line-height: 1;
min-width: @page-min-width;
height: 100%;
} }
body > #layout { body > #layout {
@ -123,7 +130,7 @@ body > #layout > div > .header > a.menu-button {
padding: 0 0.5em; padding: 0 0.5em;
margin: 0; margin: 0;
position: relative; /* for absolute positioning of searchbar */ position: relative; /* for absolute positioning of searchbar */
/* overflow: hidden; breaks semantic-ui popups */ overflow: hidden;
} }
#layout > div > .header { #layout > div > .header {
border-bottom: 1px solid #ddd; border-bottom: 1px solid #ddd;
@ -212,7 +219,7 @@ body > #layout > div > .header > a.menu-button {
body > #layout > div.menu { body > #layout > div.menu {
display: none; display: none;
position: absolute; position: absolute;
z-index: 2; z-index: 100;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;

@ -18,12 +18,3 @@
width: 95%; width: 95%;
max-width: 320px; max-width: 320px;
} }
#login-form table {
display: none;
}
#login-form div.ui.input {
width: 100%;
margin-bottom: 1em;
}

@ -2,6 +2,7 @@
#uploadform, #uploadform,
.ui.modal, .ui.modal,
.ui.popup,
.voice { .voice {
display: none; display: none;
} }
@ -19,7 +20,7 @@ body.iframe .formbuttons {
bottom: 0.5em; bottom: 0.5em;
right: 0.5em; right: 0.5em;
z-index: 50000; z-index: 50000;
width: 300px; width: 320px;
height: auto; height: auto;
max-height: 85%; max-height: 85%;
overflow-y: auto; overflow-y: auto;
@ -30,10 +31,6 @@ body.iframe .formbuttons {
top: -1000px; top: -1000px;
} }
#messagestack div a {
color: #94c0da; /* TODO: less'ify this color */
}
#messagestack div i.icon { #messagestack div i.icon {
font-size: 2em !important; font-size: 2em !important;
} }
@ -43,8 +40,19 @@ body.iframe .formbuttons {
cursor: pointer; cursor: pointer;
} }
.ui.message { .ui.alert {
margin-top: 0.5em; margin: 0;
margin-bottom: 0.2em;
opacity: 0.95;
float: left;
width: 100%;
padding-left: 0.75em;
}
.ui.alert > i.icon:before {
height: 1em;
width: 1em;
line-height: 1em;
} }
.ui-dialog iframe { .ui-dialog iframe {
@ -63,6 +71,13 @@ body.iframe .formbuttons {
min-width: 180px; min-width: 180px;
} }
#taskmenu a,
.toolbar a,
a.listbutton,
a.rcmaddcontact {
text-decoration: none;
}
.button.disabled, .button.disabled,
.toolbarmenu li a { .toolbarmenu li a {
opacity: 0.5; opacity: 0.5;
@ -72,6 +87,14 @@ body.iframe .formbuttons {
opacity: 1; opacity: 1;
} }
.toolbarmenu li:last-child {
border-bottom: none;
}
a.button {
text-decoration: none;
}
.toolbar > .spacer { .toolbar > .spacer {
display: inline-block; display: inline-block;
width: 1.2em; width: 1.2em;
@ -125,6 +148,7 @@ body.iframe .formbuttons {
border: none; border: none;
border-bottom: 1px solid #ddd; border-bottom: 1px solid #ddd;
background: transparent; background: transparent;
line-height: 1;
} }
.searchbar > a.button.search.active { .searchbar > a.button.search.active {
@ -355,6 +379,14 @@ table.fixedcopy {
border: none; border: none;
} }
div.popover-content {
padding: 0;
}
div.popover > h3 {
display: none;
}
/* make some elements bigger on tablets/phones */ /* make some elements bigger on tablets/phones */
/* TODO: we should/could maybe do this more globally? */ /* TODO: we should/could maybe do this more globally? */

@ -121,7 +121,7 @@
<li role="menuitem"><label><input type="checkbox" name="s_mods[]" value="email" id="s_mod_email" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="email" /></span></label></li> <li role="menuitem"><label><input type="checkbox" name="s_mods[]" value="email" id="s_mod_email" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="email" /></span></label></li>
<li role="menuitem"><label><input type="checkbox" name="s_mods[]" value="*" id="s_mod_all" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="allfields" /></span></label></li> <li role="menuitem"><label><input type="checkbox" name="s_mods[]" value="*" id="s_mod_all" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="allfields" /></span></label></li>
</ul> </ul>
<div class="buttons"><button class="ui button 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.show_popup('searchmenu',false)"><roundcube:label name="search" /></button></div>
</div> </div>
<div id="dragcontact-menu" class="ui popup"> <div id="dragcontact-menu" class="ui popup">

@ -20,9 +20,9 @@
</div> </div>
<div class="formbuttons"> <div class="formbuttons">
<roundcube:button command="edit" class="ui button icon edit primary" label="edit" condition="!ENV:readonly" /> <roundcube:button command="edit" class="btn btn-primary icon edit" label="edit" condition="!ENV:readonly" />
<roundcube:if condition="env:qrcode" /> <roundcube:if condition="env:qrcode" />
<roundcube:button command="qrcode" class="ui button icon qrcode" label="qrcode" /> <roundcube:button command="qrcode" class="btn icon qrcode" label="qrcode" />
<roundcube:endif /> <roundcube:endif />
</div> </div>

@ -33,8 +33,8 @@
</form> </form>
<div class="formbuttons"> <div class="formbuttons">
<roundcube:button command="save" class="ui button primary icon save" label="save" /> <roundcube:button command="save" class="btn btn-primary icon save" label="save" />
<roundcube:button command="show" class="ui button cancel" label="cancel" condition="env:action == 'edit'" /> <roundcube:button command="show" class="btn cancel" label="cancel" condition="env:action == 'edit'" />
</div> </div>
<roundcube:object name="photoUploadForm" id="upload-form" mode="smart" /> <roundcube:object name="photoUploadForm" id="upload-form" mode="smart" />

@ -117,7 +117,7 @@
<li role="menuitem"><label><input type="radio" name="s_scope" value="sub" id="s_scope_sub" onclick="UI.set_searchscope(this)" /> <span><roundcube:label name="subfolders" /></span></label></li> <li role="menuitem"><label><input type="radio" name="s_scope" value="sub" id="s_scope_sub" onclick="UI.set_searchscope(this)" /> <span><roundcube:label name="subfolders" /></span></label></li>
<li role="menuitem"><label><input type="radio" name="s_scope" value="all" id="s_scope_all" onclick="UI.set_searchscope(this)" /> <span><roundcube:label name="allfolders" /></span></label></li> <li role="menuitem"><label><input type="radio" name="s_scope" value="all" id="s_scope_all" onclick="UI.set_searchscope(this)" /> <span><roundcube:label name="allfolders" /></span></label></li>
</ul> </ul>
<div class="buttons"><button class="ui button 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.show_popup('searchmenu',false)"><roundcube:label name="search" /></button></div>
</div> </div>
<div id="dragmessage-menu" class="ui popup"> <div id="dragmessage-menu" class="ui popup">

@ -77,7 +77,8 @@ function rcube_elastic_ui()
.addEventListener('afterlistsearch', list_handler) .addEventListener('afterlistsearch', list_handler)
.addEventListener('message', message_displayed) .addEventListener('message', message_displayed)
.addEventListener('menu-open', menu_toggle) .addEventListener('menu-open', menu_toggle)
.addEventListener('menu-close', menu_toggle); .addEventListener('menu-close', menu_toggle)
.addEventListener('init', init);
// menu/sidebar button // menu/sidebar button
buttons.menu.on('click', function() { show_menu(); return false; }); buttons.menu.on('click', function() { show_menu(); return false; });
@ -93,36 +94,8 @@ function rcube_elastic_ui()
}); });
// Semantic-UI style // Convert some elements to bootstrap style
$('input.button').addClass('ui'); bootstrap_style();
$('input.button.mainaction').addClass('primary');
// TODO: Most of this style-related code should not be needed
// We should implement some features in the core that would
// allow as to tell the engine to add additional html code/attribs
$('select').dropdown();
// Make forms pretty with semantic-ui's accordion widget
// TODO: Consider using tabs when the page width is big enough
$('form.propform,.tabbed').each(function() {
var form = $(this), fieldsets = form.children('fieldset');
if (fieldsets.length) {
$(this).addClass('ui styled fluid accordion');
fieldsets.each(function(i, fieldset) {
var title = $('<div>').attr('class', 'title' + (i ? '' : ' active'))
.html('<i class="dropdown icon"></i>') // TODO: replace <i> with css
.append($('<span>').text($('legend', fieldset).text()));
var content = $('<div>').attr('class', 'content' + (i ? '' : ' active'))
.append($(fieldset).children().not('legend'));
form.append(title).append(content);
$(fieldset).remove();
});
form.accordion({animateChildren: false});
}
});
// Initialize responsive toolbars (have to be before popups init) // Initialize responsive toolbars (have to be before popups init)
toolbar_init(); toolbar_init();
@ -132,11 +105,14 @@ function rcube_elastic_ui()
// close popups on click in an iframe on the page // close popups on click in an iframe on the page
var close_all_popups = function() { var close_all_popups = function() {
$('.ui.popup:visible').each(function() { $('.popover-content:visible').each(function() {
$($(this).data('button')).popup('hide'); console.log($(this).children('*:first').data('button'));
$($(this).children('*:first').data('button')).popover('hide');
}); });
}; };
// 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}); rcube_webmail.set_iframe_events({mousedown: close_all_popups});
// Move form buttons from the content frame into the frame header (on parent window) // Move form buttons from the content frame into the frame header (on parent window)
@ -195,24 +171,6 @@ function rcube_elastic_ui()
// Initialize search forms (in list headers) // Initialize search forms (in list headers)
$('.header > .searchbar').each(function() { searchbar_init(this); }); $('.header > .searchbar').each(function() { searchbar_init(this); });
// Make login form pretty
if (rcmail.env.task == 'login') {
var inputs = [],
icon_map = {user: 'user', pass: 'lock', host: 'home'},
table = $('#login-form table');
$('tr', table).each(function() {
var input = $('input', this).detach(),
input_name = input.attr('name').replace('_', ''),
icon = $('<i>').attr('class', 'icon ' + icon_map[input_name]);
input.attr('placeholder', $('label', this).text());
inputs.push($('<div>').attr('class', 'ui left icon input').append([input, icon]));
});
table.after(inputs);
}
// Intercept jQuery-UI dialogs to re-style them // Intercept jQuery-UI dialogs to re-style them
if ($.ui) { if ($.ui) {
$.widget('ui.dialog', $.ui.dialog, { $.widget('ui.dialog', $.ui.dialog, {
@ -224,15 +182,23 @@ function rcube_elastic_ui()
}); });
} }
// rcmail 'init' event handler
function init() {
// Add checkbox selection to list widgets // Add checkbox selection to list widgets
rcmail.addEventListener('init', function() {
$('table[data-list]').each(function() { $('table[data-list]').each(function() {
var list = $(this).data('list'); var list = $(this).data('list');
if (rcmail[list] && rcmail[list].multiselect) { if (rcmail[list] && rcmail[list].multiselect) {
rcmail[list].checkbox_selection = true; rcmail[list].checkbox_selection = true;
} }
}); });
}); };
function bootstrap_style(parent)
{
$('input.button,button', parent || window).addClass('btn');
$('input.button.mainaction,button.primary,button.mainaction', parent || window).addClass('btn-primary');
};
// window resize handler // window resize handler
function resize() function resize()
@ -417,24 +383,23 @@ function rcube_elastic_ui()
*/ */
function message_displayed(p) function message_displayed(p)
{ {
var icon, classes = 'ui icon message', var cl, classes = 'ui alert',
map = { map = {
information: ['success', 'info circle icon'], information: 'alert-success',
confirmation: ['success', 'info circle icon'], confirmation: 'alert-success',
notice: ['', 'info circle icon'], notice: 'alert-info',
error: ['negative', 'warning circle icon'], error: 'alert-danger',
warning: ['negative', 'warning sign icon'], warning: 'alert-warning',
loading: ['', 'notched circle loading icon'] loading: 'alert-info loading'
}; };
if (icon = map[p.type]) { if (cl = map[p.type]) {
if (icon[0]) classes += ' ' + cl;
classes += ' ' + icon[0]; $('<i>').attr('class', 'icon').prependTo(p.object);
$('<i>').attr('class', icon[1]).prependTo(p.object);
} }
$(p.object).addClass(classes); $(p.object).addClass(classes).attr('role', 'alert');
$('a', p.object).addClass('alert-link');
/* /*
var siblings = $(p.object).siblings('div'); var siblings = $(p.object).siblings('div');
if (siblings.length) if (siblings.length)
@ -455,6 +420,7 @@ function rcube_elastic_ui()
{ {
var input = $('input', bar), var input = $('input', bar),
button = $('a.button.search', bar), button = $('a.button.search', bar),
settings_button = $('a.button.settings', bar)[0],
form = $('form', bar), form = $('form', bar),
all_elements = $('form, a.button.options, a.button.reset', bar), all_elements = $('form, a.button.options, a.button.reset', bar),
is_search_pending = function() { is_search_pending = function() {
@ -543,7 +509,7 @@ function rcube_elastic_ui()
// append the new toolbar and menu button // append the new toolbar and menu button
if (items.length) { if (items.length) {
var menu_button = $('<a class="button icon toolbar-menu-button" href="#menu">') var menu_button = $('<a class="button icon toolbar-menu-button" href="#menu">')
.attr({'data-popup': 'toolbar-menu', 'data-popup-pos': 'bottom right'}); .attr({'data-popup': 'toolbar-menu'});
layout.content.children('.header') layout.content.children('.header')
// TODO: copy original toolbar attributes (class, role, aria-*) // TODO: copy original toolbar attributes (class, role, aria-*)
@ -562,22 +528,26 @@ function rcube_elastic_ui()
{ {
var popup_id = $(item).data('popup'), var popup_id = $(item).data('popup'),
popup = $('#' + popup_id)[0], popup = $('#' + popup_id)[0],
popup_position = $(item).data('popup-pos') || 'bottom left'; title = $(item).attr('title'),
popup_position = $(item).data('popup-pos') || 'bottom';
$(item).attr({ $(item).attr({
'aria-haspopup': 'true', 'aria-haspopup': 'true',
'aria-expanded': 'false', 'aria-expanded': 'false',
'aria-owns': popup_id 'aria-owns': popup_id
}) })
.popup({ .popover({
popup: popup, trigger: 'click',
exclusive: true, container: 'body',
on: 'click', content: popup,
position: popup_position, placement: popup_position,
lastResort: true html: true
}); })
.on('show.bs.popover', function() { $(popup).removeClass('popup').attr('aria-hidden', false); })
.on('hide.bs.popover', function() { $(popup).attr('aria-hidden', true); })
.attr('title', title); // re-add title attribute removed by bootstrap
// TODO: Set aria attributes on menu show/hide // TODO: Fix popup positioning
// TODO: Set popup height so it is less that window height // TODO: Set popup height so it is less that window height
$(popup).attr('aria-hidden', 'true') $(popup).attr('aria-hidden', 'true')
.data('button', item); .data('button', item);
@ -607,6 +577,7 @@ function rcube_elastic_ui()
} }
// TODO: style buttons/forms // TODO: style buttons/forms
bootstrap_style(dialog.uiDialog);
}; };
/** /**

Loading…
Cancel
Save