Improvements made to the user menu in the back-end

Clicking your avatar will display a popover with settings links from the mysettings context.
This commit is contained in:
Samuel Georges 2015-03-06 20:41:14 +11:00
parent 507bfab768
commit 690ec1e5db
8 changed files with 222 additions and 61 deletions

View File

@ -1,4 +1,5 @@
* **Build 217** (2015-03-06)
- Improvements made to the user menu in the back-end: clicking your avatar will display a popover with settings links from the `mysettings` context.
- Added new form field widget called `repeater` for repeatable fields (see Backend > Forms docs).
- Made some UI improvements to the Rich Editor.
- Form widget base class no longer takes a model as the 2nd argument, it should be passed as `model` in the configuration instead.

View File

@ -78,8 +78,8 @@ if(this.options.triggerAction===false)
throw new Error('Trigger action is not specified.')
this.triggerCondition=this.options.triggerCondition
if(this.options.triggerCondition.indexOf('value')==0){var match=this.options.triggerCondition.match(/[^[\]]+(?=])/g)
if(match){this.triggerConditionValue=match
this.triggerCondition='value'}}
this.triggerCondition='value'
this.triggerConditionValue=(match)?match:""}
if(this.triggerCondition=='checked'||this.triggerCondition=='value')
$(document).on('change',this.options.trigger,$.proxy(this.onConditionChanged,this))
var self=this
@ -223,7 +223,64 @@ methodArgs.push(args[i])
data[option].apply(data,methodArgs)}})}
$.fn.dragScroll.Constructor=DragScroll
$.fn.dragScroll.noConflict=function(){$.fn.dragScroll=old
return this}}(window.jQuery);+function($){"use strict";var Toolbar=function(element,options){var
return this}}(window.jQuery);+function($){"use strict";var DragValue=function(element,options){this.options=options
this.$el=$(element)
this.init()}
DragValue.DEFAULTS={dragClick:false}
DragValue.prototype.init=function(){this.$el.prop('draggable',true)
this.textValue=this.$el.data('textValue')
this.$el.on('dragstart',$.proxy(this.handleDragStart,this))
this.$el.on('drop',$.proxy(this.handleDrop,this))
this.$el.on('dragend',$.proxy(this.handleDragEnd,this))
if(this.options.dragClick){this.$el.on('click',$.proxy(this.handleClick,this))
this.$el.on('mouseover',$.proxy(this.handleMouseOver,this))}}
DragValue.prototype.handleDragStart=function(event){var e=event.originalEvent
e.dataTransfer.effectAllowed='all'
e.dataTransfer.setData('text/plain',this.textValue)
this.$el.css({opacity:0.5}).addClass('dragvalue-dragging')}
DragValue.prototype.handleDrop=function(event){event.stopPropagation()
return false}
DragValue.prototype.handleDragEnd=function(event){this.$el.css({opacity:1}).removeClass('dragvalue-dragging')}
DragValue.prototype.handleMouseOver=function(event){var el=document.activeElement
if(!el)return
if(el.isContentEditable||(el.tagName.toLowerCase()=='input'&&el.type=='text'||el.tagName.toLowerCase()=='textarea')){this.lastElement=el}}
DragValue.prototype.handleClick=function(event){if(!this.lastElement)return
var $el=$(this.lastElement)
if($el.hasClass('ace_text-input'))
return this.handleClickCodeEditor(event,$el)
if(this.lastElement.isContentEditable)
return this.handleClickContentEditable()
this.insertAtCaret(this.lastElement,this.textValue)}
DragValue.prototype.handleClickCodeEditor=function(event,$el){var $editorArea=$el.closest('[data-control=codeeditor]')
if(!$editorArea.length)return
$editorArea.codeEditor('getEditorObject').insert(this.textValue)}
DragValue.prototype.handleClickContentEditable=function(){var sel,range,html;if(window.getSelection){sel=window.getSelection();if(sel.getRangeAt&&sel.rangeCount){range=sel.getRangeAt(0);range.deleteContents();range.insertNode(document.createTextNode(this.textValue));}}
else if(document.selection&&document.selection.createRange){document.selection.createRange().text=this.textValue;}}
DragValue.prototype.insertAtCaret=function(el,insertValue){if(document.selection){el.focus()
sel=document.selection.createRange()
sel.text=insertValue
el.focus()}
else if(el.selectionStart||el.selectionStart=='0'){var startPos=el.selectionStart,endPos=el.selectionEnd,scrollTop=el.scrollTop
el.value=el.value.substring(0,startPos)+insertValue+el.value.substring(endPos,el.value.length)
el.focus()
el.selectionStart=startPos+insertValue.length
el.selectionEnd=startPos+insertValue.length
el.scrollTop=scrollTop}
else{el.value+=insertValue
el.focus()}}
var old=$.fn.dragValue
$.fn.dragValue=function(option){var args=Array.prototype.slice.call(arguments,1),result
this.each(function(){var $this=$(this)
var data=$this.data('oc.dragvalue')
var options=$.extend({},DragValue.DEFAULTS,$this.data(),typeof option=='object'&&option)
if(!data)$this.data('oc.dragvalue',(data=new DragValue(this,options)))
if(typeof option=='string')result=data[option].apply(data,args)
if(typeof result!='undefined')return false})
return result?result:this}
$.fn.dragValue.Constructor=DragValue
$.fn.dragValue.noConflict=function(){$.fn.dragValue=old
return this}
$(document).render(function(){$('[data-control="dragvalue"]').dragValue()});}(window.jQuery);+function($){"use strict";var Toolbar=function(element,options){var
$el=this.$el=$(element),$toolbar=$el.closest('.control-toolbar')
this.options=options||{};var scrollClassContainer=options.scrollClassContainer!==undefined?options.scrollClassContainer:$el.parent()
$el.dragScroll({scrollClassContainer:scrollClassContainer})
@ -697,6 +754,7 @@ if(!data)$this.data('oc.popup',(data=new Popup(this,options)))
else if(typeof option=='string')data[option].apply(data,args)
else data.reload()})}
$.fn.popup.Constructor=Popup
$.popup=function(option){return $('<a />').popup(option)}
$.fn.popup.noConflict=function(){$.fn.popup=old
return this}
$(document).on('click.oc.popup','[data-control="popup"]',function(){$(this).popup()
@ -1058,7 +1116,7 @@ if(!data)$this.data('oc.inputPreset',(data=new InputPreset(this,options)))})}
$.fn.inputPreset.Constructor=InputPreset
$.fn.inputPreset.noConflict=function(){$.fn.inputPreset=old
return this}
$(document).render(function(){$('[data-input-preset]').inputPreset()})}(window.jQuery);(function($){var OctoberLayout=function(){}
$(document).render(function(){$('[data-input-preset]').inputPreset()})}(window.jQuery);(function($){var OctoberLayout=function(){this.$accountMenuOverlay=null}
OctoberLayout.prototype.setPageTitle=function(title){var $title=$('title')
if(this.pageTitleTemplate===undefined)
this.pageTitleTemplate=$title.data('titleTemplate')
@ -1067,6 +1125,14 @@ OctoberLayout.prototype.updateLayout=function(title){$('.layout-cell.width-fix')
$el.data('oc.layoutMargin',margin)}
$(this).width($el.get(0).offsetWidth+margin)
$(this).trigger('oc.widthFixed')}})}
OctoberLayout.prototype.toggleAccountMenu=function(el){var self=this,$menu=$(el).next()
if($menu.hasClass('active')){self.$accountMenuOverlay.remove()
$menu.removeClass('active')}
else{self.$accountMenuOverlay=$('<div />').addClass('popover-overlay')
$(document.body).append(self.$accountMenuOverlay)
$menu.addClass('active')
self.$accountMenuOverlay.one('click',function(){self.$accountMenuOverlay.remove()
$menu.removeClass('active')})}}
if($.oc===undefined)
$.oc={}
$.oc.layout=new OctoberLayout()
@ -1121,20 +1187,15 @@ this.$el.css({left:this.sideNavWidth,top:this.mainNavHeight})
this.updatePanelPosition()
$(window).trigger('resize')}
SidePanelTab.prototype.hideSidePanel=function(){$(document.body).removeClass('display-side-panel')
if(this.$el.next('#layout-body').length==0)
$('#layout-body').before(this.$el)
if(this.$el.next('#layout-body').length==0){$('#layout-body').before(this.$el)}
this.panelVisible=false
this.updateActiveTab()}
SidePanelTab.prototype.updatePanelPosition=function(){if(!this.panelFixed()||Modernizr.touch)
this.$el.height($(document).height()-this.mainNavHeight)
else
this.$el.css('height','')
if(this.panelVisible&&$(window).width()>this.options.breakpoint&&this.panelFixed())
this.hideSidePanel()}
SidePanelTab.prototype.updateActiveTab=function(){if(!this.panelVisible&&($(window).width()<this.options.breakpoint||!this.panelFixed()))
this.$sideNavItems.removeClass('active')
SidePanelTab.prototype.updatePanelPosition=function(){if(!this.panelFixed()||Modernizr.touch){this.$el.height($(document).height()-this.mainNavHeight)}
else{this.$el.css('height','')}
if(this.panelVisible&&$(window).width()>this.options.breakpoint&&this.panelFixed()){this.hideSidePanel()}}
SidePanelTab.prototype.updateActiveTab=function(){if(!this.panelVisible&&($(window).width()<this.options.breakpoint||!this.panelFixed())){this.$sideNavItems.removeClass('active')}
else{this.$sideNavItems.filter('[data-menu-item='+this.visibleItemId+']').addClass('active')}}
SidePanelTab.prototype.panelFixed=function(){return!$(document.body).hasClass('side-panel-not-fixed')}
SidePanelTab.prototype.panelFixed=function(){return!($(window).width()<this.options.breakpoint)&&!$(document.body).hasClass('side-panel-not-fixed')}
SidePanelTab.prototype.fixPanel=function(){$(document.body).toggleClass('side-panel-not-fixed')
var self=this
window.setTimeout(function(){var fixed=self.panelFixed()

View File

@ -11,6 +11,7 @@
=require october.utils.js
=require october.triggerapi.js
=require october.dragscroll.js
=require october.dragvalue.js
=require october.toolbar.js
=require october.verticalmenu.js
=require october.navbar.js

View File

@ -1,5 +1,7 @@
(function($){
var OctoberLayout = function() {}
var OctoberLayout = function() {
this.$accountMenuOverlay = null
}
OctoberLayout.prototype.setPageTitle = function(title) {
var $title = $('title')
@ -26,6 +28,26 @@
})
}
OctoberLayout.prototype.toggleAccountMenu = function(el) {
var self = this,
$menu = $(el).next()
if ($menu.hasClass('active')) {
self.$accountMenuOverlay.remove()
$menu.removeClass('active')
}
else {
self.$accountMenuOverlay = $('<div />').addClass('popover-overlay')
$(document.body).append(self.$accountMenuOverlay)
$menu.addClass('active')
self.$accountMenuOverlay.one('click', function(){
self.$accountMenuOverlay.remove()
$menu.removeClass('active')
})
}
}
if ($.oc === undefined)
$.oc = {}

View File

@ -64,6 +64,9 @@
@color-mainmenu-active: #ffffff;
@color-mainmenu-collapsed: #333333;
@color-accountmenu-bg: #3d3d3d;
@color-accountmenu-divider: #343434;
@color-footer: rgba(255,255,255,.8);
@color-footer-border: #dfdfdf;
@color-footer-text: #666666;

View File

@ -36,7 +36,7 @@ nav#layout-mainmenu.navbar {
margin-right: 30px;
position: relative;
&:last-child {margin-right: 0;}
&:last-child { margin-right: 0; }
&.active {
&:after {
@ -50,19 +50,26 @@ nav#layout-mainmenu.navbar {
&.icon {
margin-right: 0;
i {margin-right: 0;}
a {padding: 14px;}
i { margin-right: 0; }
a { padding: 14px; }
}
&.power-off, &.preview {
i {font-size: 20px;}
a {padding: 22px 20px 20px 20px;}
i { font-size: 20px; }
a { padding: 22px 20px 20px 20px; }
}
&.account {
margin-right: 0;
line-height: 23px;
a {
padding-left: 35px;
padding-right: 5px;
position: relative;
z-index: 900;
}
img {
width: 25px;
margin-right: 7px;
@ -72,19 +79,78 @@ nav#layout-mainmenu.navbar {
}
}
&.nav {display: inline-block;}
&.nav { display: inline-block; }
}
.menu-toggle { display: none; }
.toolbar-item {
margin-right: 0;
&:before, &:after {margin-top: 0;}
&:before {left: -12px;}
&:after {right: -12px;}
&:before, &:after { margin-top: 0; }
&:before { left: -12px; }
&:after { right: -12px; }
&.scroll-active-before:before {color: @color-mainmenu-active;}
&.scroll-active-after:after {color: @color-mainmenu-active;}
&.scroll-active-before:before { color: @color-mainmenu-active; }
&.scroll-active-after:after { color: @color-mainmenu-active; }
}
}
nav#layout-mainmenu.navbar ul li .mainmenu-accountmenu {
position: fixed;
top: 63px;
right: 0;
width: 225px;
background: @color-accountmenu-bg;
z-index: 900;
display: none;
&.active {
display: block;
}
&:after {
.triangle(up, 22px, 12px, @color-accountmenu-bg);
right: 101px;
top: -12px;
position: absolute;
}
ul {
padding: 12px 30px;
float: none;
display: block;
}
li {
padding: 0;
font-weight: 200;
text-align: left;
display: block;
a {
display: block;
padding: 9px 0;
text-align: left;
opacity: .65;
&:hover {
opacity: 1;
}
}
}
li.divider {
height: 1px;
width: 100%;
margin: 9px 0;
overflow: hidden;
background-color: transparent;
border-top: 1px solid @color-accountmenu-divider;
}
@media (max-width: @screen-sm) {
&:after {
right: 71px;
}
}
}
@ -98,7 +164,7 @@ nav#layout-mainmenu .menu-toggle,
outline: none;
&:hover {
background-color: transparent!important;
background-color: transparent !important;
}
&:active, &:focus {
@ -123,7 +189,7 @@ nav#layout-mainmenu.navbar ul li:hover,
.mainmenu-collapsed li:hover {
a {
&:active, &:focus {
color: @color-mainmenu-active!important;
color: @color-mainmenu-active !important;
}
}
}
@ -133,10 +199,10 @@ nav#layout-mainmenu.navbar ul li:hover,
nav#layout-mainmenu.navbar ul li,
.mainmenu-collapsed li {
&.active, &.highlight {
color: @color-mainmenu-active!important;
color: @color-mainmenu-active !important;
font-weight: 600;
a {color: @color-mainmenu-active!important;}
a {color: @color-mainmenu-active !important;}
}
&:hover {
@ -148,7 +214,7 @@ nav#layout-mainmenu.navbar ul li,
body.drag {
nav#layout-mainmenu.navbar ul.nav,
.mainmenu-collapsed ul {
li:hover {color: @color-mainmenu-inactive;}
li:hover { color: @color-mainmenu-inactive; }
}
}

View File

@ -1,13 +1,14 @@
<?php
$activeItem = BackendMenu::getActiveMainMenuItem();
$mySettings = System\Classes\SettingsManager::instance()->listItems('mysettings');
?>
<nav class="navbar layout control-toolbar" id="layout-mainmenu" role="navigation">
<div class="layout-cell">
<div class="toolbar-item">
<div data-control="toolbar">
<a class="menu-toggle" href="javascript:;">
<i class="icon-bars"></i>
<?php
$activeItem = BackendMenu::getActiveMainMenuItem();
echo $activeItem ? e(trans($activeItem->label)) : 'CMS';
?>
<?= $activeItem ? e(trans($activeItem->label)) : 'CMS' ?>
</a>
<ul class="nav">
@ -24,11 +25,33 @@
</div>
<div class="layout-cell width-fix">
<ul>
<?php
/* @todo Context help system
<li class="icon"><a href="#"><i class="icon-question"></i></a></li>
*/
?>
<li class="highlight account">
<a href="javascript:;" onclick="$.oc.layout.toggleAccountMenu(this)">
<img src="<?= $this->user->getAvatarThumb() ?>">
<span class="hidden-xs">
<?= e($this->user->first_name.' '.$this->user->last_name) ?>
</span>
</a>
<div class="mainmenu-accountmenu">
<ul>
<li>
<a href="<?= Backend::url('backend/auth/signout') ?>">
<?= e(trans('backend::lang.account.sign_out')) ?>
</a>
</li>
<?php foreach ($mySettings as $category => $items): ?>
<li class="divider"></li>
<?php foreach ($items as $item): ?>
<li>
<a href="<?= $item->url ?>">
<?= e(trans($item->label)) ?>
</a>
</li>
<?php endforeach ?>
<?php endforeach ?>
</ul>
</div>
</li>
<li class="icon preview">
<a
href="<?= URL::to('/') ?>"
@ -39,23 +62,7 @@
<i class="icon-crosshairs"></i>
</a>
</li>
<li class="highlight account">
<a href="<?= Backend::url('system/settings/mysettings') ?>">
<img src="<?= $this->user->getAvatarThumb() ?>">
<span class="hidden-xs">
<?= e($this->user->first_name.' '.$this->user->last_name) ?>
</span>
</a>
</li>
<li class="icon power-off">
<a
data-toggle="tooltip"
data-placement="left"
href="<?= Backend::url('backend/auth/signout') ?>"
title="<?= e(trans('backend::lang.account.sign_out')) ?>">
<i class="icon-power-off"></i>
</a>
</li>
</ul>
</div>
</nav>

View File

@ -1,6 +1,6 @@
<?php if (!$this->fatalError): ?>
<?= Form::open(['class'=>'layout']) ?>
<?= Form::open(['class' => 'layout']) ?>
<div class="layout-row">
<?= $this->formRender() ?>