diff --git a/modules/backend/ServiceProvider.php b/modules/backend/ServiceProvider.php index 34aea94e1..9f3dd231d 100644 --- a/modules/backend/ServiceProvider.php +++ b/modules/backend/ServiceProvider.php @@ -101,7 +101,8 @@ class ServiceProvider extends ModuleServiceProvider 'icon' => 'icon-user', 'url' => Backend::URL('backend/users/myaccount'), 'order' => 400, - 'context' => 'mysettings' + 'context' => 'mysettings', + 'keywords' => 'backend::lang.myaccount.menu_keywords', ], ]); }); diff --git a/modules/backend/assets/css/october.css b/modules/backend/assets/css/october.css index 25bb5b602..bc59da87b 100644 --- a/modules/backend/assets/css/october.css +++ b/modules/backend/assets/css/october.css @@ -8071,6 +8071,9 @@ label { transition: none; -webkit-box-shadow: none; box-shadow: none; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; } .form-control:focus { -webkit-box-shadow: none; @@ -8425,16 +8428,17 @@ label { border-radius: 2px; } .switch-field .field-switch { - padding-left: 105px; + padding-left: 75px; float: left; } .custom-switch { display: block; - width: 90px; - height: 25px; + width: 58px; + height: 26px; position: relative; text-transform: uppercase; - border: 1px solid #999999; + border: none; + cursor: pointer; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; @@ -8452,14 +8456,14 @@ label { z-index: 4; display: block; position: absolute; - right: 50%; - top: 0; - width: 50%; - height: 100%; + right: 34px; + top: 2px; + width: 22px; + height: 22px; background-color: #f6f6f6; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; + -webkit-border-radius: 20px; + -moz-border-radius: 20px; + border-radius: 20px; -webkit-transition: all 0.1s; transition: all 0.1s; } @@ -8481,16 +8485,18 @@ label { filter: alpha(opacity=0); } .custom-switch input:checked ~ .slide-button { - right: 0%; + right: 2px; } .custom-switch input:checked ~ span { background-color: #8da85e; } .custom-switch input:checked ~ span span:first-of-type { color: #FFFFFF; + display: block; } .custom-switch input:checked ~ span span:last-of-type { color: #666666; + display: none; } .custom-switch > span { display: block; @@ -8499,28 +8505,34 @@ label { left: 0; width: 100%; background-color: #cc3300; + font-size: 11px; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; + -webkit-border-radius: 20px; + -moz-border-radius: 20px; + border-radius: 20px; } .custom-switch > span span { z-index: 5; display: block; width: 50%; position: absolute; - top: 0; + top: 1px; left: 0; - text-align: center; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; } .custom-switch > span span:last-child { left: 50%; color: #FFFFFF; + display: block; } .custom-switch > span span:first-of-type { + padding-left: 9px; + display: none; color: #666666; } .custom-select .select2-choice { @@ -10186,6 +10198,7 @@ html.cssanimations .cursor-loading-indicator.hide { border: none; text-align: left; outline: none!important; + padding: 9px 18px; } .btn[class^="oc-icon-"]:before, .btn[class*=" oc-icon-"]:before { @@ -10856,12 +10869,14 @@ body.dropdown-open .dropdown-overlay { .control-tabs.primary > div > ul.nav-tabs, .control-tabs.primary > div > div > ul.nav-tabs { position: relative; + margin-left: -20px; + margin-right: -20px; } .control-tabs.primary > ul.nav-tabs:before, .control-tabs.primary > div > ul.nav-tabs:before, .control-tabs.primary > div > div > ul.nav-tabs:before { position: absolute; - top: 19px; + top: 26px; height: 1px; width: 100%; content: ' '; @@ -10873,16 +10888,64 @@ body.dropdown-open .dropdown-overlay { padding-right: 10px; padding-left: 11px; margin-right: 0; + margin-left: -30px; background: transparent; } +.control-tabs.primary > ul.nav-tabs > li:first-child, +.control-tabs.primary > div > ul.nav-tabs > li:first-child, +.control-tabs.primary > div > div > ul.nav-tabs > li:first-child { + margin-left: 0; + padding-left: 15px!important; +} .control-tabs.primary > ul.nav-tabs > li a, .control-tabs.primary > div > ul.nav-tabs > li a, .control-tabs.primary > div > div > ul.nav-tabs > li a { font-size: 12px; padding-bottom: 3px; + padding: 0 16px; + margin: 0; position: relative; z-index: 101; background: transparent; + overflow: visible; +} +.control-tabs.primary > ul.nav-tabs > li a > span.title, +.control-tabs.primary > div > ul.nav-tabs > li a > span.title, +.control-tabs.primary > div > div > ul.nav-tabs > li a > span.title { + position: relative; + display: inline-block; + padding: 5px 5px 9px 5px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + border-top: 1px solid #f0f0f0; + z-index: 100; +} +.control-tabs.primary > ul.nav-tabs > li a > span.title:before, +.control-tabs.primary > div > ul.nav-tabs > li a > span.title:before, +.control-tabs.primary > div > div > ul.nav-tabs > li a > span.title:before, +.control-tabs.primary > ul.nav-tabs > li a > span.title:after, +.control-tabs.primary > div > ul.nav-tabs > li a > span.title:after, +.control-tabs.primary > div > div > ul.nav-tabs > li a > span.title:after { + content: ' '; + position: absolute; + background: transparent url(../images/primary-tab-shape.png) no-repeat left -31px; + width: 16px; + height: 26px; + display: block; + top: -1px; + z-index: 100; +} +.control-tabs.primary > ul.nav-tabs > li a > span.title:before, +.control-tabs.primary > div > ul.nav-tabs > li a > span.title:before, +.control-tabs.primary > div > div > ul.nav-tabs > li a > span.title:before { + left: -16px; +} +.control-tabs.primary > ul.nav-tabs > li a > span.title:after, +.control-tabs.primary > div > ul.nav-tabs > li a > span.title:after, +.control-tabs.primary > div > div > ul.nav-tabs > li a > span.title:after { + right: -16px; + background-position: -61px -31px; } .control-tabs.primary > ul.nav-tabs > li:last-child, .control-tabs.primary > div > ul.nav-tabs > li:last-child, @@ -10899,12 +10962,36 @@ body.dropdown-open .dropdown-overlay { .control-tabs.primary > ul.nav-tabs > li.active a:before, .control-tabs.primary > div > ul.nav-tabs > li.active a:before, .control-tabs.primary > div > div > ul.nav-tabs > li.active a:before { - content: ' '; position: absolute; - width: 100%; + top: 26px; + height: 1px; + right: 2px; left: 0; - top: 19px; - border-bottom: 1px solid #ec8017; + content: ' '; + background-color: #fafafa; +} +.control-tabs.primary > ul.nav-tabs > li.active a, +.control-tabs.primary > div > ul.nav-tabs > li.active a, +.control-tabs.primary > div > div > ul.nav-tabs > li.active a { + z-index: 107; +} +.control-tabs.primary > ul.nav-tabs > li.active a > span.title, +.control-tabs.primary > div > ul.nav-tabs > li.active a > span.title, +.control-tabs.primary > div > div > ul.nav-tabs > li.active a > span.title { + z-index: 105; + border-top-color: #d6d6d6; +} +.control-tabs.primary > ul.nav-tabs > li.active a > span.title:before, +.control-tabs.primary > div > ul.nav-tabs > li.active a > span.title:before, +.control-tabs.primary > div > div > ul.nav-tabs > li.active a > span.title:before { + background-position: left 0; + z-index: 107; +} +.control-tabs.primary > ul.nav-tabs > li.active a > span.title:after, +.control-tabs.primary > div > ul.nav-tabs > li.active a > span.title:after, +.control-tabs.primary > div > div > ul.nav-tabs > li.active a > span.title:after { + background-position: -61px 0; + z-index: 107; } .control-tabs.secondary > ul.nav-tabs > li, .control-tabs.secondary > div > ul.nav-tabs > li, @@ -12862,3 +12949,112 @@ div[data-control="balloon-selector"]:not(.control-disabled) ul li:hover { .callout.no-subheader > .header i { margin-top: -5px; } +.sidenav-tree { + width: 280px; + background: #34495e; +} +.sidenav-tree .control-toolbar { + padding: 20px 0 20px 20px; +} +.sidenav-tree .control-toolbar input.form-control { + border: none; +} +.sidenav-tree ul { + padding: 0; + margin: 0; + list-style: none; +} +.sidenav-tree div.scrollbar-thumb { + background: #2b3e50!important; +} +.sidenav-tree ul.top-level > li[data-status=collapsed] > div.group h3:before { + -webkit-transform: rotate(0deg) translate(3px, 0); + -ms-transform: rotate(0deg) translate(3px, 0); + transform: rotate(0deg) translate(3px, 0); +} +.sidenav-tree ul.top-level > li[data-status=collapsed] ul { + display: none; +} +.sidenav-tree ul.top-level > li > div.group h3 { + background: #2b3e50; + color: #ecf0f1; + text-transform: uppercase; + font-size: 14px; + padding: 15px 15px 15px 33px; + margin: 0; + position: relative; + cursor: pointer; +} +.sidenav-tree ul.top-level > li > div.group h3:before { + width: 10px; + height: 10px; + display: block; + position: absolute; + top: 1px; +} +.sidenav-tree ul.top-level > li > div.group h3:before { + left: 13px; + top: 15px; + color: #cfcfcf; + font-family: FontAwesome; + font-weight: normal; + font-style: normal; + text-decoration: inherit; + -webkit-font-smoothing: antialiased; + *margin-right: .3em; + content: "\f0da"; + -webkit-transform: rotate(90deg) translate(5px, 0); + -ms-transform: rotate(90deg) translate(5px, 0); + transform: rotate(90deg) translate(5px, 0); + -webkit-transition: all 0.1s ease; + transition: all 0.1s ease; +} +.sidenav-tree ul.top-level > li > ul li a { + display: block; + position: relative; + padding: 15px 15px 15px 40px; + color: #808b93; + background: #3d5265; + margin-bottom: 1px; + text-decoration: none!important; +} +.sidenav-tree ul.top-level > li > ul li a:hover { + text-decoration: none; +} +.sidenav-tree ul.top-level > li > ul li a i { + position: absolute; + left: 15px; + top: 17px; + font-size: 16px; +} +.sidenav-tree ul.top-level > li > ul li a span { + display: block; + line-height: 150%; +} +.sidenav-tree ul.top-level > li > ul li a span.header { + font-size: 14px; + margin-bottom: 5px; +} +.sidenav-tree ul.top-level > li > ul li a span.description { + font-size: 12px; + font-weight: 100; +} +.sidenav-tree ul.top-level > li > ul li.active a { + background: #34495e; + color: #ecf0f1; +} +.sidenav-tree ul.top-level > li > ul li.active a:before { + content: ' '; + position: absolute; + width: 4px; + background: #e6802b; + left: 0; + top: 0; + height: 100%; +} +.sidenav-tree ul.top-level > li > ul li:last-child a { + margin-bottom: 0; +} +.sidenav-tree ul.top-level > li > ul li:hover a { + background-color: #34495e; +} diff --git a/modules/backend/assets/images/primary-tab-shape.png b/modules/backend/assets/images/primary-tab-shape.png new file mode 100644 index 000000000..8cec7349e Binary files /dev/null and b/modules/backend/assets/images/primary-tab-shape.png differ diff --git a/modules/backend/assets/js/october.scrollbar.js b/modules/backend/assets/js/october.scrollbar.js index 8b620bb13..c4c47f9c6 100644 --- a/modules/backend/assets/js/october.scrollbar.js +++ b/modules/backend/assets/js/october.scrollbar.js @@ -255,6 +255,57 @@ : this.$el.get(0).scrollWidth; } + Scrollbar.prototype.gotoElement = function(element, callback) { + var $el = $(element) + if (!$el.length) + return; + + var self = this, + offset = 0, + animated = false, + params = { + duration: 300, + queue: false, + complete: function(){ + if (callback !== undefined) + callback() + } + } + + if (!this.options.vertical) { + offset = $el.get(0).offsetLeft - this.$el.scrollLeft() + + if (offset < 0) { + this.$el.animate({'scrollLeft': $el.get(0).offsetLeft}, params) + animated = true + } else { + offset = $el.get(0).offsetLeft + $el.outerWidth() - (this.$el.scrollLeft() + this.$el.outerWidth()) + if (offset > 0) { + this.$el.animate({'scrollLeft': $el.get(0).offsetLeft + $el.outerWidth() - this.$el.outerWidth()}, params) + animated = true + } + } + } else { + offset = $el.get(0).offsetTop - this.$el.scrollTop() + + if (offset < 0) { + this.$el.animate({'scrollTop': $el.get(0).offsetTop}, params) + animated = true + } else { + offset = $el.get(0).offsetTop - (this.$el.scrollTop() + this.$el.outerHeight()) + if (offset > 0) { + this.$el.animate({'scrollTop': $el.get(0).offsetTop + $el.outerHeight() - this.$el.outerHeight()}, params) + animated = true + } + } + } + + if (!animated && callback !== undefined) + callback() + + return this + } + // SCROLLBAR PLUGIN DEFINITION // ============================ diff --git a/modules/backend/assets/js/october.sidenav-tree.js b/modules/backend/assets/js/october.sidenav-tree.js new file mode 100644 index 000000000..cb4e20db8 --- /dev/null +++ b/modules/backend/assets/js/october.sidenav-tree.js @@ -0,0 +1,254 @@ +/* + * Side navigation tree + * + * Data attributes: + * - data-control="sidenav-tree" - enables the plugin + * - data-tree-name - unique name of the tree control. The name is used for storing user configuration in the browser cookies. + * + * JavaScript API: + * $('#tree').sidenavTree() + * + * Dependences: + * - Null + */ + ++function ($) { "use strict"; + + // SIDENAVTREE CLASS DEFINITION + // ============================ + + var SidenavTree = function(element, options) { + this.options = options + this.$el = $(element) + + this.init(); + } + + SidenavTree.DEFAULTS = { + treeName: 'sidenav_tree' + } + + SidenavTree.prototype.init = function (){ + var self = this + + this.statusCookieName = this.options.treeName + 'groupStatus' + this.searchCookieName = this.options.treeName + 'search' + this.$searchInput = $(this.options.searchInput) + + this.$el.on('click', 'li > div.group', function() { + self.toggleGroup($(this).closest('li')) + + return false; + }); + + this.$searchInput.on('keyup', function(){ + self.handleSearchChange() + }) + + var searchTerm = $.cookie(this.searchCookieName) + if (searchTerm !== undefined && searchTerm.length > 0) { + this.$searchInput.val(searchTerm) + this.applySearch() + } + + var scrollbar = $('[data-control=scrollbar]', this.$el).data('oc.scrollbar'), + active = $('li.active', this.$el) + + if (active.length > 0) + scrollbar.gotoElement(active) + } + + SidenavTree.prototype.toggleGroup = function(group) { + var $group = $(group), + status = $group.attr('data-status') + + status === undefined || status == 'expanded' ? + this.collapseGroup($group) : + this.expandGroup($group) + } + + SidenavTree.prototype.collapseGroup = function(group) { + var + $list = $('> ul', group), + self = this; + + $list.css('overflow', 'hidden') + $list.animate({'height': 0}, { duration: 100, queue: false, complete: function() { + $list.css({ + 'overflow': 'visible', + 'display': 'none' + }) + $(group).attr('data-status', 'collapsed') + $(window).trigger('oc.updateUi') + self.saveGroupStatus($(group).data('group-code'), true) + } }) + } + + SidenavTree.prototype.expandGroup = function(group, duration) { + var + $list = $('> ul', group), + self = this + + duration = duration === undefined ? 100 : duration + + $list.css({ + 'overflow': 'hidden', + 'display': 'block', + 'height': 0 + }) + $list.animate({'height': $list[0].scrollHeight}, { duration: duration, queue: false, complete: function() { + $list.css({ + 'overflow': 'visible', + 'height': 'auto' + }) + $(group).attr('data-status', 'expanded') + $(window).trigger('oc.updateUi') + self.saveGroupStatus($(group).data('group-code'), false) + } }) + } + + SidenavTree.prototype.saveGroupStatus = function(groupCode, collapsed) { + var collapsedGroups = $.cookie(this.statusCookieName), + updatedGroups = [] + + if (collapsedGroups === undefined) + collapsedGroups = '' + + collapsedGroups = collapsedGroups.split('|') + $.each(collapsedGroups, function() { + if (groupCode != this) + updatedGroups.push(this) + }) + + if (collapsed) + updatedGroups.push(groupCode) + + $.cookie(this.statusCookieName, updatedGroups.join('|'), { expires: 30, path: '/' }) + } + + SidenavTree.prototype.handleSearchChange = function() { + var lastValue = this.$searchInput.data('oc.lastvalue'); + + if (lastValue !== undefined && lastValue == this.$searchInput.val()) + return + + this.$searchInput.data('oc.lastvalue', this.$searchInput.val()) + + if (this.dataTrackInputTimer !== undefined) + window.clearTimeout(this.dataTrackInputTimer); + + var self = this + this.dataTrackInputTimer = window.setTimeout(function(){ + self.applySearch() + }, 300); + + $.cookie(this.searchCookieName, $.trim(this.$searchInput.val()), { expires: 30, path: '/' }) + } + + SidenavTree.prototype.applySearch = function() { + var query = $.trim(this.$searchInput.val()), + words = query.toLowerCase().split(' '), + visibleGroups = [], + visibleItems = [], + self = this + + if (query.length == 0) { + $('li', this.$el).removeClass('hidden') + + return + } + + // Find visible groups and items + // + $('ul.top-level > li', this.$el).each(function() { + var $li = $(this) + + if (self.textContainsWords($('div.group h3', $li).text(), words)) { + visibleGroups.push($li.get(0)) + + $('ul li', $li).each(function(){ + visibleItems.push(this) + }) + } else { + $('ul li', $li).each(function(){ + if (self.textContainsWords($(this).text(), words) || self.textContainsWords($(this).data('keywords'), words)) { + visibleGroups.push($li.get(0)) + visibleItems.push(this) + } + }) + } + }) + + // Hide invisible groups and items + // + $('ul.top-level > li', this.$el).each(function() { + var $li = $(this), + groupIsVisible = $.inArray(this, visibleGroups) !== -1 + + $li.toggleClass('hidden', !groupIsVisible) + if (groupIsVisible) + self.expandGroup($li, 0) + + $('ul li', $li).each(function(){ + var $itemLi = $(this) + + $itemLi.toggleClass('hidden', $.inArray(this, visibleItems) == -1) + }) + }) + + return false + } + + SidenavTree.prototype.textContainsWords = function(text, words) { + text = text.toLowerCase() + + for (var i = 0; i < words.length; i++) { + if (text.indexOf(words[i]) === -1) + return false + } + + return true + } + + // SIDENAVTREE PLUGIN DEFINITION + // ============================ + + var old = $.fn.sidenavTree + + $.fn.sidenavTree = function (option) { + var args = arguments; + + return this.each(function () { + var $this = $(this) + var data = $this.data('oc.sidenavTree') + var options = $.extend({}, SidenavTree.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data) $this.data('oc.sidenavTree', (data = new SidenavTree(this, options))) + if (typeof option == 'string') { + var methodArgs = []; + for (var i=1; i 1 && !$.isFunction(value)) { + options = $.extend({}, config.defaults, options); + + if (typeof options.expires === 'number') { + var days = options.expires, t = options.expires = new Date(); + t.setTime(+t + days * 864e+5); + } + + return (document.cookie = [ + encode(key), '=', stringifyCookieValue(value), + options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE + options.path ? '; path=' + options.path : '', + options.domain ? '; domain=' + options.domain : '', + options.secure ? '; secure' : '' + ].join('')); + } + + // Read + + var result = key ? undefined : {}; + + // To prevent the for loop in the first place assign an empty array + // in case there are no cookies at all. Also prevents odd result when + // calling $.cookie(). + var cookies = document.cookie ? document.cookie.split('; ') : []; + + for (var i = 0, l = cookies.length; i < l; i++) { + var parts = cookies[i].split('='); + var name = decode(parts.shift()); + var cookie = parts.join('='); + + if (key && key === name) { + // If second argument (value) is a function it's a converter... + result = read(cookie, value); + break; + } + + // Prevent storing a cookie that we couldn't decode. + if (!key && (cookie = read(cookie)) !== undefined) { + result[name] = cookie; + } + } + + return result; + }; + + config.defaults = {}; + + $.removeCookie = function (key, options) { + if ($.cookie(key) === undefined) { + return false; + } + + // Must not alter options, thus extending a fresh object... + $.cookie(key, '', $.extend({}, options, { expires: -1 })); + return !$.cookie(key); + }; + +})); diff --git a/modules/backend/assets/less/controls/button.less b/modules/backend/assets/less/controls/button.less index c9c3028dd..344235418 100644 --- a/modules/backend/assets/less/controls/button.less +++ b/modules/backend/assets/less/controls/button.less @@ -7,6 +7,7 @@ border: none; text-align: left; outline: none!important; + padding: 9px 18px; &[class^="oc-icon-"], &[class*=" oc-icon-"] { diff --git a/modules/backend/assets/less/controls/forms.less b/modules/backend/assets/less/controls/forms.less index 4afe2ac04..262cf7516 100644 --- a/modules/backend/assets/less/controls/forms.less +++ b/modules/backend/assets/less/controls/forms.less @@ -13,6 +13,7 @@ label { .form-control { .transition(none); .box-shadow(none); + .border-radius(3px); &:focus { .box-shadow(none); } @@ -361,18 +362,19 @@ label { .switch-field { .field-switch { - padding-left: 105px; + padding-left: 75px; float: left; } } .custom-switch { display: block; - width: 90px; - height: 25px; + width: 58px; + height: 26px; position: relative; text-transform: uppercase; - border: 1px solid @color-custom-input-border; + border: none; + cursor: pointer; .border-radius(3px); * { .box-sizing(border-box); } @@ -381,12 +383,12 @@ label { z-index: 4; display: block; position: absolute; - right: 50%; - top: 0; - width: 50%; - height: 100%; + right: 34px; + top: 2px; + width: 22px; + height: 22px; background-color: @color-switch-input-bg; - .border-radius(3px); + .border-radius(20px); .transition(all 0.1s); } @@ -408,11 +410,19 @@ label { position: absolute; .opacity(0); &:checked { - ~ .slide-button { right: 0%; } + ~ .slide-button { + right: 2px; + } ~ span { background-color: @color-switch-input-on; } ~ span span { - &:first-of-type { color: #FFFFFF; } - &:last-of-type { color: #666666; } + &:first-of-type { + color: #FFFFFF; + display: block; + } + &:last-of-type { + color: #666666; + display: none; + } } } } @@ -424,19 +434,28 @@ label { left: 0; width: 100%; background-color: @color-switch-input-off; + font-size: 11px; .user-select(none); - .border-radius(3px); + .border-radius(20px); span { z-index: 5; display: block; width: 50%; position: absolute; - top: 0; + top: 1px; left: 0; - text-align: center; - &:last-child { left: 50%; color: #FFFFFF; } - &:first-of-type { color: #666666; } + .box-sizing(border-box); + &:last-child { + left: 50%; + color: #FFFFFF; + display: block; + } + &:first-of-type { + padding-left: 9px; + display: none; + color: #666666; + } } } } diff --git a/modules/backend/assets/less/controls/sidenav-tree.less b/modules/backend/assets/less/controls/sidenav-tree.less new file mode 100644 index 000000000..72eb5b3df --- /dev/null +++ b/modules/backend/assets/less/controls/sidenav-tree.less @@ -0,0 +1,127 @@ +.sidenav-tree { + width: 280px; + background: @color-sidebarnav-bg; + + .control-toolbar { + padding: 20px 0 20px 20px; + + input.form-control { + border: none; + } + } + + ul { + padding: 0; + margin: 0; + list-style: none; + } + + div.scrollbar-thumb { + background: #2b3e50!important; + } + + ul.top-level > li { + &[data-status=collapsed] { + > div.group h3:before { + .transform( ~'rotate(0deg) translate(3px, 0)' ); + } + + ul { + display: none; + } + } + + > div.group { + h3 { + background: @color-sidebarnav-tree-group-bg; + color: @color-sidebarnav-tree-group; + text-transform: uppercase; + font-size: 14px; + padding: 15px 15px 15px 33px; + margin: 0; + position: relative; + cursor: pointer; + + &:before { + width: 10px; + height: 10px; + display: block; + position: absolute; + top: 1px; + } + + &:before { + left: 13px; + top: 15px; + color: @color-list-arrow; + .icon(@caret-right); + .transform( ~'rotate(90deg) translate(5px, 0)' ); + .transition(all 0.1s ease); + } + } + } + + > ul { + li { + a { + display: block; + position: relative; + padding: 15px 15px 15px 40px; + color: @color-sidebarnav-tree-inactive-text; + background: @color-sidebarnav-tree-inactive-bg; + margin-bottom: 1px; + text-decoration: none!important; + + &:hover { + text-decoration: none; + } + + i { + position: absolute; + left: 15px; + top: 17px; + font-size: 16px; + } + + span { + display: block; + line-height: 150%; + + &.header { + font-size: 14px; + margin-bottom: 5px; + } + + &.description { + font-size: 12px; + font-weight: 100; + } + } + } + + &.active a { + background: @color-sidebarnav-tree-active-bg; + color: @color-sidebarnav-tree-active-text; + + &:before { + content: ' '; + position: absolute; + width: 4px; + background: @color-sidebarnav-tree-active-marker; + left: 0; + top: 0; + height: 100%; + } + } + + &:last-child a { + margin-bottom: 0; + } + + &:hover a { + background-color: @color-sidebarnav-tree-active-bg; + } + } + } + } +} \ No newline at end of file diff --git a/modules/backend/assets/less/controls/tab.less b/modules/backend/assets/less/controls/tab.less index 5dee7990f..679fe12bf 100644 --- a/modules/backend/assets/less/controls/tab.less +++ b/modules/backend/assets/less/controls/tab.less @@ -171,10 +171,12 @@ > ul.nav-tabs, > div > ul.nav-tabs, > div > div > ul.nav-tabs { position: relative; + margin-left: -20px; + margin-right: -20px; &:before { position: absolute; - top: 19px; + top: 26px; height: 1px; width: 100%; content: ' '; @@ -185,14 +187,52 @@ padding-right: 10px; padding-left: 11px; margin-right: 0; + margin-left: -30px; background: transparent; + + &:first-child { + margin-left: 0; + padding-left: 15px!important; + } a { font-size: 12px; padding-bottom: 3px; + padding: 0 16px; + margin: 0; position: relative; z-index: 101; background: transparent; + overflow: visible; + + > span.title { + position: relative; + display: inline-block; + padding: 5px 5px 9px 5px; + .box-sizing(border-box); + border-top: 1px solid #f0f0f0; + z-index: 100; + + &:before, &:after { + content: ' '; + position: absolute; + background: transparent url(../images/primary-tab-shape.png) no-repeat left -31px; + width: 16px; + height: 26px; + display: block; + top: -1px; + z-index: 100; + } + + &:before { + left: -16px; + } + + &:after { + right: -16px; + background-position: -61px -31px; + } + } } &:last-child { @@ -205,14 +245,32 @@ padding-left: 0; } - &.active a { - &:before { - content: ' '; - position: absolute; - width: 100%; - left: 0; - top: 19px; - border-bottom: 1px solid @color-tab-active-marker; + &.active a:before { + position: absolute; + top: 26px; + height: 1px; + right: 2px; + left: 0; + content: ' '; + background-color: @color-body-bg; + } + + &.active a{ + z-index: 107; + + > span.title { + z-index: 105; + border-top-color: #d6d6d6; + + &:before { + background-position: left 0; + z-index: 107; + } + + &:after { + background-position: -61px 0; + z-index: 107; + } } } } diff --git a/modules/backend/assets/less/core/variables.less b/modules/backend/assets/less/core/variables.less index 9479fd5ad..ed8ee8011 100644 --- a/modules/backend/assets/less/core/variables.less +++ b/modules/backend/assets/less/core/variables.less @@ -76,6 +76,14 @@ @color-sidebarnav-counter-bg: #d9350f; @color-sidebarnav-counter-text: #ffffff; +@color-sidebarnav-tree-group: #ecf0f1; +@color-sidebarnav-tree-group-bg: #2b3e50; +@color-sidebarnav-tree-inactive-text: #808b93; +@color-sidebarnav-tree-inactive-bg: #3d5265; +@color-sidebarnav-tree-active-bg: #34495e; +@color-sidebarnav-tree-active-text: #ecf0f1; +@color-sidebarnav-tree-active-marker: #e6802b; + @color-list-active: #dddddd; @color-list-hover: #dddddd; @color-list-active-border: #e67e22; diff --git a/modules/backend/assets/less/october.less b/modules/backend/assets/less/october.less index 2b7f57420..364de5238 100644 --- a/modules/backend/assets/less/october.less +++ b/modules/backend/assets/less/october.less @@ -42,3 +42,4 @@ @import "controls/reportwidgets.less"; @import "controls/treelist.less"; @import "controls/callout.less"; +@import "controls/sidenav-tree.less"; diff --git a/modules/backend/classes/NavigationManager.php b/modules/backend/classes/NavigationManager.php index fcd04cef7..e1a275bfa 100644 --- a/modules/backend/classes/NavigationManager.php +++ b/modules/backend/classes/NavigationManager.php @@ -24,6 +24,8 @@ class NavigationManager */ private $items; + private $contextSidenavPartials = []; + private $contextOwner; private $contextMainMenuItemCode; private $contextSideMenuItemCode; @@ -295,9 +297,9 @@ class NavigationManager /** * Sets the navigation context. * The function sets the navigation owner, main menu item code and the side menu item code. - * @param string @owner Specifies the navigation owner in the format Vendor/Module - * @param string @mainMenuItemCode Specifies the main menu item code - * @param string @sideMenuItemCode Specifies the side menu item code + * @param string $owner Specifies the navigation owner in the format Vendor/Module + * @param string $mainMenuItemCode Specifies the main menu item code + * @param string $sideMenuItemCode Specifies the side menu item code */ public function setContext($owner, $mainMenuItemCode, $sideMenuItemCode = null) { @@ -309,7 +311,7 @@ class NavigationManager /** * Sets the navigation context. * The function sets the navigation owner. - * @param string @owner Specifies the navigation owner in the format Vendor/Module + * @param string $owner Specifies the navigation owner in the format Vendor/Module */ public function setContextOwner($owner) { @@ -318,7 +320,7 @@ class NavigationManager /** * Specifies a code of the main menu item in the current navigation context. - * @param string @mainMenuItemCode Specifies the main menu item code + * @param string $mainMenuItemCode Specifies the main menu item code */ public function setContextMainMenu($mainMenuItemCode) { @@ -330,18 +332,20 @@ class NavigationManager * @return mixed Returns an object with the following fields: * - mainMenuCode * - sideMenuCode + * - owner */ public function getContext() { return (object)[ 'mainMenuCode' => $this->contextMainMenuItemCode, - 'sideMenuCode' => $this->contextSideMenuItemCode + 'sideMenuCode' => $this->contextSideMenuItemCode, + 'owner' => $this->contextOwner ]; } /** * Specifies a code of the side menu item in the current navigation context. - * @param string @sideMenuItemCode Specifies the side menu item code + * @param string $sideMenuItemCode Specifies the side menu item code */ public function setContextSideMenu($sideMenuItemCode) { @@ -350,7 +354,7 @@ class NavigationManager /** * Determines if a main menu item is active. - * @param mixed @item Specifies the item object. + * @param mixed $item Specifies the item object. * @return boolean Returns true if the menu item is active. */ public function isMainMenuItemActive($item) @@ -360,7 +364,7 @@ class NavigationManager /** * Determines if a side menu item is active. - * @param mixed @item Specifies the item object. + * @param mixed $item Specifies the item object. * @return boolean Returns true if the side item is active. */ public function isSideMenuItemActive($item) @@ -368,6 +372,33 @@ class NavigationManager return $this->contextOwner == $item->owner && $this->contextSideMenuItemCode == $item->code; } + /** + * Registers a special side navigation partial for a specific main menu. + * The sidenav partial replaces the standard side navigation. + * @param string $owner Specifies the navigation owner in the format Vendor/Module. + * @param string $mainMenuItemCode Specifies the main menu item code. + * @param string $partial Specifies the partial name. + */ + public function registerContextSidenavPartial($owner, $mainMenuItemCode, $partial) + { + $this->contextSidenavPartials[$owner.$mainMenuItemCode] = $partial; + } + + /** + * Returns the side navigation partial for a specific main menu previously registered with the registerContextSidenavPartial() method. + * @param string $owner Specifies the navigation owner in the format Vendor/Module. + * @param string $mainMenuItemCode Specifies the main menu item code. + * @return mixed Returns the partial name or null. + */ + public function getContextSidenavPartial($owner, $mainMenuItemCode) + { + $key = $owner.$mainMenuItemCode; + + return array_key_exists($key, $this->contextSidenavPartials) ? + $this->contextSidenavPartials[$key] : + null; + } + /** * Removes menu items from an array if the supplied user lacks permission. * @param User $user A user object diff --git a/modules/backend/controllers/EditorPreferences.php b/modules/backend/controllers/EditorPreferences.php index 51937200c..bc37ec44a 100644 --- a/modules/backend/controllers/EditorPreferences.php +++ b/modules/backend/controllers/EditorPreferences.php @@ -3,6 +3,7 @@ use Lang; use BackendMenu; use Backend\Classes\Controller; +use System\Classes\SettingsManager; use Backend\Models\EditorPreferences as EditorPreferencesModel; /** @@ -34,6 +35,7 @@ class EditorPreferences extends Controller $this->addJs('/modules/backend/assets/js/editorpreferences/editorpreferences.js', 'core'); BackendMenu::setContext('October.System', 'system', 'mysettings'); + SettingsManager::setContext('October.Backend', 'editor'); } public function index() diff --git a/modules/backend/controllers/Users.php b/modules/backend/controllers/Users.php index 6d8247f65..36a067a53 100644 --- a/modules/backend/controllers/Users.php +++ b/modules/backend/controllers/Users.php @@ -6,6 +6,7 @@ use Redirect; use BackendMenu; use BackendAuth; use Backend\Classes\Controller; +use System\Classes\SettingsManager; /** * Backend user controller @@ -36,6 +37,7 @@ class Users extends Controller $this->requiredPermissions = null; BackendMenu::setContext('October.System', 'system', 'users'); + SettingsManager::setContext('October.System', 'administrators'); } /** @@ -55,7 +57,8 @@ class Users extends Controller */ public function myaccount() { - BackendMenu::setContextSideMenu('mysettings'); + SettingsManager::setContext('October.Backend', 'myaccount'); + $this->pageTitle = Lang::get('backend::lang.myaccount.menu_label'); return $this->update($this->user->id, 'myaccount'); } diff --git a/modules/backend/controllers/editorpreferences/index.htm b/modules/backend/controllers/editorpreferences/index.htm index a66351741..26ec34f6e 100644 --- a/modules/backend/controllers/editorpreferences/index.htm +++ b/modules/backend/controllers/editorpreferences/index.htm @@ -1,10 +1,3 @@ - - - - fatalError): ?> 'layout-item stretch layout-column']) ?> diff --git a/modules/backend/controllers/users/myaccount.htm b/modules/backend/controllers/users/myaccount.htm index e046fa852..d9f646246 100644 --- a/modules/backend/controllers/users/myaccount.htm +++ b/modules/backend/controllers/users/myaccount.htm @@ -1,10 +1,3 @@ - - - - fatalError): ?> diff --git a/modules/backend/lang/en/lang.php b/modules/backend/lang/en/lang.php index 28f721349..13ec1161b 100644 --- a/modules/backend/lang/en/lang.php +++ b/modules/backend/lang/en/lang.php @@ -49,6 +49,7 @@ return [ 'user' => [ 'name' => 'Administrator', 'menu_label' => 'Administrators', + 'menu_description' => 'Manage back-end administrator users, groups and permissions.', 'list_title' => 'Manage Administrators', 'new' => 'New Administrator', 'login' => "Login", @@ -178,6 +179,7 @@ return [ 'myaccount' => [ 'menu_label' => 'My Account', 'menu_description' => 'Update your account details such as name, email address and password.', + 'menu_keywords' => 'security login' ], 'backend_preferences' => [ 'menu_label' => 'Backend Preferences', diff --git a/modules/backend/layouts/_head.htm b/modules/backend/layouts/_head.htm index 8dceb3af1..fc61b28d6 100644 --- a/modules/backend/layouts/_head.htm +++ b/modules/backend/layouts/_head.htm @@ -18,6 +18,8 @@ + + @@ -69,7 +71,7 @@ - +