diff --git a/src/app/layout/navbar/navbar.ts b/src/app/layout/navbar/navbar.ts index 6d03903..2966448 100644 --- a/src/app/layout/navbar/navbar.ts +++ b/src/app/layout/navbar/navbar.ts @@ -2,6 +2,7 @@ import {Component, Inject, EventEmitter, Input} from "ng-forward"; import {LanguageSelectorComponent} from "../language-selector/language-selector.component"; import {SessionService, AuthService, AuthController, AuthEvents} from "./../../login"; import {SidebarNotificationService} from "../sidebar/sidebar.notification.service"; +import {BodyStateClassesService} from '../services/body-state-classes.service'; @Component({ selector: "acme-navbar", @@ -9,7 +10,7 @@ import {SidebarNotificationService} from "../sidebar/sidebar.notification.servic directives: [LanguageSelectorComponent], providers: [AuthService, SessionService, SidebarNotificationService] }) -@Inject("$uibModal", AuthService, "SessionService", "$state", SidebarNotificationService) +@Inject("$uibModal", AuthService, "SessionService", "$state", SidebarNotificationService, BodyStateClassesService) export class Navbar { private currentUser: noosfero.User; @@ -25,11 +26,13 @@ export class Navbar { public authService: AuthService, private session: SessionService, private $state: ng.ui.IStateService, - private sidebarNotificationService: SidebarNotificationService + private sidebarNotificationService: SidebarNotificationService, + private bodyStateService: BodyStateClassesService ) { this.currentUser = this.session.currentUser(); this.showHamburguer = this.authService.isAuthenticated(); + this.bodyStateService.addContentClass(!this.sidebarNotificationService.sidebarVisible); this.authService.subscribe(AuthEvents[AuthEvents.loginSuccess], () => { if (this.modalInstance) { @@ -51,6 +54,8 @@ export class Navbar { public toggleCollapse() { this.sidebarNotificationService.alternateVisibility(); + + this.bodyStateService.addContentClass(!this.sidebarNotificationService.sidebarVisible); } openLogin() { diff --git a/src/app/layout/scss/_sidebar.scss b/src/app/layout/scss/_sidebar.scss index 179f65e..05e6106 100644 --- a/src/app/layout/scss/_sidebar.scss +++ b/src/app/layout/scss/_sidebar.scss @@ -21,6 +21,8 @@ position: absolute; background: #2c3e50; width: 220px; + border-right: 2px solid #e7ebee; + height: 200%; @media (max-width: $break-sm-max) { position: relative; @@ -289,3 +291,17 @@ color: #fff; border-left-color: $primary-color; } + +#user-left-box { + .profile-image-wrap { + float:left; + width: 70px; + height: 70px; + } + i.profile-image { + border-radius: 18%; + border: 3px solid #fff; + background-color: #fff; + text-align: center; + } +} diff --git a/src/app/layout/scss/skins/_whbl.scss b/src/app/layout/scss/skins/_whbl.scss new file mode 100644 index 0000000..146307f --- /dev/null +++ b/src/app/layout/scss/skins/_whbl.scss @@ -0,0 +1,310 @@ +$whbl-nav-col-bg: #ffffff; //f3f5f6 +$whbl-sidebar-linkColor: #484848; +$whbl-primary-color: #1E96D0; //blue +$whbl-font-color: #16191c; + +.skin-whbl { + #header-navbar { + background-color: $whbl-primary-color; + } + .navbar > .container .navbar-brand { + background-color: transparent; + width: 221px; + } + #nav-col, + #page-wrapper { + background-color: $whbl-nav-col-bg; + } + #sidebar-nav .nav > li > a { + color: $whbl-sidebar-linkColor; + /* border-bottom: 1px solid #e7ebee; */ + } + #sidebar-nav .nav > .open > .submenu > li > .submenu, + #sidebar-nav .nav > .active > .submenu > li > .submenu, + #sidebar-nav .nav li .submenu > li.open a, + #nav-col-submenu .submenu > li > .submenu, + #nav-col-submenu li .submenu > li.open > a { + background-color: darken($whbl-nav-col-bg, 8%); + } + .nav-pills > li.active > a, + .nav-pills > li.active > a:hover, + .nav-pills > li.active > a:focus, + #sidebar-nav .nav-pills > li.active > a, + #sidebar-nav .nav-pills > li.active > a:hover, + #sidebar-nav .nav-pills > li.active > a:focus, + .nav-pills > li.open > a, + .nav-pills > li.open > a:hover, + .nav-pills > li.open > a:focus, + #sidebar-nav .nav-pills > li.open > a, + #sidebar-nav .nav-pills > li.open > a:hover, + #sidebar-nav .nav-pills > li.open > a:focus, + #sidebar-nav .nav-pills > li > a:focus { + background-color: darken($whbl-nav-col-bg, 4%); + border-color: $whbl-primary-color; + border-bottom-color: #e7ebee; + color: $whbl-sidebar-linkColor; + } + #sidebar-nav .nav-pills > li.active > a > i { + color: #2980b9; + } + #sidebar-nav .nav > li > a:hover { + background-color: darken($whbl-nav-col-bg, 4%); + border-color: $whbl-primary-color; + border-bottom-color: #e7ebee; + color: $whbl-sidebar-linkColor; + } + + #sidebar-nav .nav > li .submenu li > a:hover { + color: $whbl-font-color; + font-weight: bold; + } + #header-navbar .nav > li > a { + color: #fff; + } + #header-navbar .nav > li > a:hover, + #header-navbar .nav > li > a:focus, + #header-navbar .nav .open > a, + #header-navbar .nav .open > a:hover, + #header-navbar .nav .open > a:focus { + background-color: #2980b9; + } + #sidebar-nav .nav li .submenu, + #nav-col-submenu .submenu { + background-color: darken($whbl-nav-col-bg, 4%); + } + #sidebar-nav .nav li .submenu > li > a, + .nav-small #nav-col-submenu .submenu > li > a { + color: $whbl-font-color; + } + #sidebar-nav .nav > .open > .submenu > .open > a, + #sidebar-nav .nav > .active > .submenu > .open > a, + #sidebar-nav .nav > .active > .submenu > .active > a, + #nav-col-submenu .submenu > .open > a, + #nav-col-submenu .submenu > .active > a { + border-bottom-color: transparent; + box-shadow: 0 -1px 0 transparent inset; + } + #sidebar-nav .nav > .open > .submenu > .open > a { + border-bottom-color: #dcdfe6; + box-shadow: none; + } + #sidebar-nav .nav li.open > a.dropdown-toggle > .drop-icon, + #sidebar-nav .nav li.active > a.dropdown-toggle > .drop-icon { + color: $whbl-font-color; + } + #sidebar-nav .nav li .submenu > li > a:hover, + #sidebar-nav .nav li .submenu > li > a.active, + #sidebar-nav .nav li .submenu > li.active > a { + background-color: darken($whbl-nav-col-bg, 8%); + } + .navbar > .container .navbar-brand { + color: #fff; + } + .navbar-toggle { + color: #fff; + } + .graph-box { + background-color: $whbl-primary-color !important; + } + .content-wrapper { + background-color: #f9f9f9; + height: 100%; + margin-top: 0; + margin-bottom: 0; + position: relative; + min-height: 1200px; + padding: 15px 15px 35px 15px; + margin-left: 220px; + // border-left: 2px solid #e7ebee; + } + #user-left-box { + + .user-box { + color: $whbl-font-color; + > a { + color: $whbl-font-color; + } + > .name { + > a { + color: $whbl-font-color; + + &:hover, &:focus { + color: $whbl-font-color; + } + } + } + } + .user-box a:hover, + .user-box a:focus { + color: darken($whbl-font-color, 20%); + } + } + #sidebar-nav .nav > li.nav-header { + border-top-color: #e7ebee; + color: darken($whbl-nav-col-bg, 35%); + } + .nav-tabs { + background-color: #f9f9f9; + } + h1 { + color: $whbl-primary-color; + } + #header-navbar .nav > li > a:hover, + #header-navbar .nav > li > a:focus, + #header-navbar .nav .open > a, + #header-navbar .nav .open > a:hover, + #header-navbar .nav .open > a:focus, + .navbar-toggle:hover, + .navbar-toggle:focus, + .mobile-search.active > .btn { + background-color: #2980b9; + } + .main-box { + border: 1px solid $main-bg-color; + } + a, + .fc-state-default, + .jvectormap-zoomin, + .jvectormap-zoomout, + #user-profile .profile-details ul > li > span { + color: $whbl-primary-color; + } + a:hover, + a:focus, + .widget-users li > .details > .name > a:hover, + .widget-todo .actions > a:hover { + color: $whbl-primary-color; + } + .table a.table-link:hover { + color: #2980b9; + } + .pagination { + > li { + > a, + > span, + > a:hover, + > span:hover, + > a:focus, + > span:focus, + > a:active, + > span:active { + color: $whbl-primary-color; + } + } + > .active { + > a, + > span, + > a:hover, + > span:hover, + > a:focus, + > span:focus { + background-color: $whbl-primary-color; + border-color: $whbl-primary-color; + color: #fff; + } + } + } + .notifications-list { + .item-footer { + background-color: #272d33; + + a:hover { + background-color: #0f1114; + } + } + } + .btn-primary, + .btn-default, + .btn-info, + .btn-success, + .btn-warning, + .btn-danger, + .btn-primary:hover, + .btn-default:hover, + .btn-info:hover, + .btn-success:hover, + .btn-warning:hover, + .btn-danger:hover { + color: #fff; + } + .btn-primary { + background-color: $whbl-primary-color; + border-color: #2980b9; + } + .btn-primary:hover, + .btn-primary:focus, + .btn-primary:active, + .btn-primary.active, + .open .dropdown-toggle.btn-primary { + background-color: #2980b9; + border-color: #216897; + } + .btn-success { + background-color: $whbl-primary-color; + border-color: #2980b9; + } + .btn-success:hover, + .btn-success:focus, + .btn-success:active, + .btn-success.active, + .open .dropdown-toggle.btn-success { + background-color: #2980b9; + border-color: #1c5c87; + } + h1 { + color: $whbl-primary-color; + } + .widget-users li > .details > .time { + color: $whbl-primary-color; + } + blockquote, + blockquote.pull-right { + border-color: $whbl-primary-color; + } + a.list-group-item.active, + a.list-group-item.active:hover, + a.list-group-item.active:focus { + background-color: $whbl-primary-color; + border-color: $whbl-primary-color; + } + .nav .caret { + border-bottom-color: $whbl-primary-color; + border-top-color: $whbl-primary-color; + } + .panel-default > .panel-heading, + .notifications-list .item-footer { + background-color: $whbl-primary-color; + } + .notifications-list .item-footer a:hover { + background-color: #2980b9; + } + #invoice-companies .invoice-dates .invoice-number > span, + .notifications-list .item a .time { + color: $whbl-primary-color; + } + .table thead > tr > th > a:hover span { + color: $whbl-primary-color; + border-color: $whbl-primary-color; + } + .pace .pace-progress { + background-color: #fff; + } +} +.rtl.skin-whbl #content-wrapper { + border-left: 0; + border-right: 2px solid #e7ebee; +} + +@media (max-width: $break-sm-max) { + .skin-whbl { + #logo.navbar-brand > img.normal-logo.logo-white { + display: block; + } + #logo.navbar-brand > img.normal-logo.logo-black { + display: none; + } + .navbar > .container .navbar-brand { + background-color: $whbl-primary-color; + } + } +} diff --git a/src/app/layout/services/body-state-classes.service.spec.ts b/src/app/layout/services/body-state-classes.service.spec.ts index 1444509..703e8d5 100644 --- a/src/app/layout/services/body-state-classes.service.spec.ts +++ b/src/app/layout/services/body-state-classes.service.spec.ts @@ -19,7 +19,9 @@ describe("BodyStateClasses Service", () => { }, authService: any = helpers.mocks.authService, bodyEl: { className: string }, - bodyElJq: any; + bodyElJq: any, + contentWrapperEl: { className: string }, + contentWrapperElJq: any; let getService = (): BodyStateClassesService => { return new BodyStateClassesService($rootScope, $document, $state, authService); @@ -29,6 +31,9 @@ describe("BodyStateClasses Service", () => { authService.isAuthenticated = jasmine.createSpy("isAuthenticated").and.returnValue(true); bodyEl = { className: "" }; bodyElJq = [bodyEl]; + + contentWrapperEl = { className: "" }; + contentWrapperElJq = [contentWrapperEl]; }); it("should add the class noosfero-user-logged to the body element if the user is authenticated", () => { @@ -133,4 +138,43 @@ describe("BodyStateClasses Service", () => { // and check now if the bodyEl has a class indicating the new state route expect(bodyEl.className).toEqual(BodyStateClassesService.ROUTE_STATE_CLASSNAME_PREFIX + "new-route"); }); + + it("add a css class theme skin to body element", () => { + let service = getService(); + let skinClass: string = 'skin-test'; + + bodyElJq.addClass = jasmine.createSpy("addClass"); + bodyElJq.removeClass = jasmine.createSpy("removeClass"); + service["bodyElement"] = bodyElJq; + + service.start({ + skin: skinClass + }); + + expect(bodyElJq.addClass).toHaveBeenCalledWith(skinClass); + }); + + it("add a css class to content wrapper element", () => { + let service = getService(); + + contentWrapperElJq.addClass = jasmine.createSpy("addClass"); + contentWrapperElJq.removeClass = jasmine.createSpy("removeClass"); + + service["contentWrapperElement"] = contentWrapperElJq; + service.addContentClass(true); + + expect(contentWrapperElJq.addClass).toHaveBeenCalledWith(BodyStateClassesService.CONTENT_WRAPPER_FULL); + }); + + it("remove a css class from content wrapper element", () => { + let service = getService(); + + contentWrapperElJq.addClass = jasmine.createSpy("addClass"); + contentWrapperElJq.removeClass = jasmine.createSpy("removeClass"); + + service["contentWrapperElement"] = contentWrapperElJq; + service.addContentClass(false); + + expect(contentWrapperElJq.removeClass).toHaveBeenCalledWith(BodyStateClassesService.CONTENT_WRAPPER_FULL); + }); }); diff --git a/src/app/layout/services/body-state-classes.service.ts b/src/app/layout/services/body-state-classes.service.ts index 3cf35d2..f24db47 100644 --- a/src/app/layout/services/body-state-classes.service.ts +++ b/src/app/layout/services/body-state-classes.service.ts @@ -2,6 +2,11 @@ import {Directive, Inject, Injectable} from "ng-forward"; import {AuthEvents} from "./../../login/auth-events"; import {AuthService} from "./../../login/auth.service"; import {HtmlUtils} from "../html-utils"; +import {INgForwardJQuery} from 'ng-forward/cjs/util/jqlite-extensions'; + +export interface StartParams { + skin?: string; +} /** * This is a service which adds classes to the body element @@ -18,11 +23,14 @@ import {HtmlUtils} from "../html-utils"; export class BodyStateClassesService { private started: boolean = false; + private skin: string; public static get USER_LOGGED_CLASSNAME(): string { return "noosfero-user-logged"; } public static get ROUTE_STATE_CLASSNAME_PREFIX(): string { return "noosfero-route-"; } + public static get CONTENT_WRAPPER_FULL(): string { return "full-content"; } private bodyElement: ng.IAugmentedJQuery = null; + private contentWrapperElement: INgForwardJQuery = null; constructor( private $rootScope: ng.IRootScopeService, @@ -33,14 +41,37 @@ export class BodyStateClassesService { } - start() { + start(config?: StartParams) { if (!this.started) { this.setupUserLoggedClassToggle(); this.setupStateClassToggle(); + + if (config) { + this.setThemeSkin(config.skin); + } this.started = true; } } + setThemeSkin(skin: string) { + this.getBodyElement().addClass(skin); + } + + addContentClass(addClass: boolean, className?: string): BodyStateClassesService { + + let fullContentClass: string = className || BodyStateClassesService.CONTENT_WRAPPER_FULL; + let contentWrapper = this.getContentWrapper(); + + if (contentWrapper) { + if (addClass) { + contentWrapper.addClass(fullContentClass); + } else { + contentWrapper.removeClass(fullContentClass); + } + } + return this; + } + private getStateChangeSuccessHandlerFunction(bodyElement: ng.IAugmentedJQuery): (event: ng.IAngularEvent, toState: ng.ui.IState) => void { let self = this; return (event: ng.IAngularEvent, toState: ng.ui.IState) => { @@ -95,4 +126,15 @@ export class BodyStateClassesService { } return this.bodyElement; } + + private getContentWrapper(selector?: string): INgForwardJQuery { + + if (this.contentWrapperElement === null) { + + let doc = angular.element(this.$document); + this.contentWrapperElement = doc.query(selector || '.content-wrapper'); + } + + return this.contentWrapperElement; + } } diff --git a/src/app/layout/sidebar/sidebar.component.spec.ts b/src/app/layout/sidebar/sidebar.component.spec.ts index 74c4db5..a889cac 100644 --- a/src/app/layout/sidebar/sidebar.component.spec.ts +++ b/src/app/layout/sidebar/sidebar.component.spec.ts @@ -2,6 +2,7 @@ import {provide} from 'ng-forward'; import {ComponentTestHelper, createClass} from '../../../spec/component-test-helper'; import {providers} from 'ng-forward/cjs/testing/providers'; import {SidebarComponent} from './sidebar.component'; +import * as helpers from '../../../spec/helpers'; const htmlTemplate: string = ''; diff --git a/src/app/layout/sidebar/sidebar.html b/src/app/layout/sidebar/sidebar.html index 3f9338d..53c5824 100644 --- a/src/app/layout/sidebar/sidebar.html +++ b/src/app/layout/sidebar/sidebar.html @@ -1,12 +1,12 @@ -