Compare View
Commits (26)
-
Skin structure - Core default theme error
-
Skin structure - Folder organization + Validation See merge request !43
-
Search - new UI field See merge request !45
-
Export comment fix button See merge request !44
-
Added css rules that increased margin and padding on specific elements, horizontal rules with text in the middle and new translations. Also added checkbox for keep logged in. Signed-off-by: Carlos Coêlho <carlospecter@gmail.com> Signed-off-by: Sabryna Sousa <sabryna.sousa1323@gmail.com>
-
Login modal and block improvements - Renamed labels and inputs from login form on login block and modal - Added css rules that increased margin and padding on specific elements, horizontal rules with text in the middle and new translations. - Also added checkbox for keep logged in. See merge request !38
-
Fix edit mode state See merge request !46
-
Search ui - Fix CSS :hover See merge request !49
-
- Added support only to 'rightbar' and 'default' @TODO - Develop all layouts Signed-off-by: Tallys Martins <tallysmartins@gmail.com>
Showing
63 changed files
Show diff stats
README.md
@@ -53,18 +53,31 @@ See some important folders bellow: | @@ -53,18 +53,31 @@ See some important folders bellow: | ||
53 | 53 | ||
54 | ## Change skin | 54 | ## Change skin |
55 | 55 | ||
56 | -- Create a any scss file into your theme folder structure | 56 | +- Create an any scss file into: `app/layout/skins/` |
57 | + > **Suggestion:** Create a `sass` file partial. Something like: **`_mycustom.scss`**. | ||
58 | + | ||
57 | - Extend your skin css class from `%skin-base` scss placeholder selector. Something like this: | 59 | - Extend your skin css class from `%skin-base` scss placeholder selector. Something like this: |
60 | + > **Suggestion:** Use the prefix **`skin-`** to the css class | ||
58 | 61 | ||
59 | ```sass | 62 | ```sass |
60 | -.skin-custom { | 63 | +.skin-mycustom { |
61 | @extend %skin-base | 64 | @extend %skin-base |
62 | } | 65 | } |
63 | ``` | 66 | ``` |
64 | - Configure application to use the new theme, e.g.: | 67 | - Configure application to use the new theme, e.g.: |
65 | -`npm config set angular-theme:skin custom-skin` | 68 | +`npm config set angular-theme:skin skin-mycustom` |
69 | + | ||
70 | +**N.B.** | ||
71 | + | ||
72 | +1. The full name of the scss class should be used as the parameter for the command `npm config set angular-theme:`, like in _skin-mycustom_. DO NOT use the file name of the skin as the parameter. | ||
73 | + | ||
74 | +2. The skin's file should be the named as the scss class without the word `skin`, preceded by an underline. Otherwise, it will raise an error during `npm install`. | ||
75 | + | ||
76 | +Example: _mycustom. | ||
77 | + | ||
66 | 78 | ||
67 | -- Start the application with `npm start` scripts ou make a build | 79 | +- Start the application with `npm start` scripts or make a build |
80 | + > **PS:** If the configured skin is invalid, an error message is showed in the terminal. | ||
68 | 81 | ||
69 | ## Development environment | 82 | ## Development environment |
70 | 83 |
bower.json
@@ -38,7 +38,8 @@ | @@ -38,7 +38,8 @@ | ||
38 | "angular-bind-html-compile": "^1.2.1", | 38 | "angular-bind-html-compile": "^1.2.1", |
39 | "angular-click-outside": "^2.7.1", | 39 | "angular-click-outside": "^2.7.1", |
40 | "ng-ckeditor": "^0.2.1", | 40 | "ng-ckeditor": "^0.2.1", |
41 | - "angular-bootstrap-toggle-switch": "^0.5.6" | 41 | + "angular-bootstrap-toggle-switch": "^0.5.6", |
42 | + "angular-tag-cloud": "^0.3.0" | ||
42 | }, | 43 | }, |
43 | "devDependencies": { | 44 | "devDependencies": { |
44 | "angular-mocks": "~1.5.0" | 45 | "angular-mocks": "~1.5.0" |
gulp/conf.js
@@ -9,6 +9,7 @@ | @@ -9,6 +9,7 @@ | ||
9 | var argv = require('minimist')(process.argv.slice(2)); | 9 | var argv = require('minimist')(process.argv.slice(2)); |
10 | var gutil = require('gulp-util'); | 10 | var gutil = require('gulp-util'); |
11 | var path = require('path'); | 11 | var path = require('path'); |
12 | +var fs = require('fs'); | ||
12 | 13 | ||
13 | /** | 14 | /** |
14 | * The main paths of your project handle these with care | 15 | * The main paths of your project handle these with care |
@@ -23,12 +24,70 @@ exports.paths = { | @@ -23,12 +24,70 @@ exports.paths = { | ||
23 | themes: 'themes', | 24 | themes: 'themes', |
24 | languages: 'languages' | 25 | languages: 'languages' |
25 | }; | 26 | }; |
27 | + | ||
28 | +/** | ||
29 | +* Check if theme folder exists on "themes" directory | ||
30 | +* | ||
31 | +* @param path The string relative path of the theme | ||
32 | +*/ | ||
33 | +exports.themeExists = function (path) { | ||
34 | + try { | ||
35 | + fs.statSync(path); | ||
36 | + } catch (e) { | ||
37 | + throw new Error('The theme "'+exports.paths.theme+ ' on path "'+path+'" was not found'); | ||
38 | + } | ||
39 | +}; | ||
40 | + | ||
41 | +/** | ||
42 | +* Check if skin file exists on "{theme}/app/layout/skins" directory | ||
43 | +* | ||
44 | +* @param skin The skin name passed by arg to gulp task | ||
45 | +*/ | ||
46 | +exports.skinExists = function (skin) { | ||
47 | + | ||
48 | + var skinPath, prefixPath = ''; | ||
49 | + var skinFile = skin+'.scss'; | ||
50 | + if (/skin-/.test(skin)) { | ||
51 | + skinFile = skin.replace('skin-','_')+'.scss'; | ||
52 | + } | ||
53 | + | ||
54 | + if (/-default$/.test(exports.paths.theme)) { | ||
55 | + prefixPath = exports.paths.src; | ||
56 | + }else { | ||
57 | + prefixPath = path.join(exports.paths.themes, exports.paths.theme); | ||
58 | + } | ||
59 | + | ||
60 | + skinPath = path.join(prefixPath, '/app/layout/scss/skins/', skinFile); | ||
61 | + | ||
62 | + try { | ||
63 | + fs.statSync(skinPath); | ||
64 | + } catch(e) { | ||
65 | + throw new Error('The skin file "'+skinPath+'" was not found'); | ||
66 | + } | ||
67 | + | ||
68 | + var content = fs.readFileSync(skinPath, {encoding: 'utf8'}); | ||
69 | + if(content.search(skin) == -1) { | ||
70 | + throw new Error('The skin css selector ".'+skin+'" was not found in "'+skinPath+'" file'); | ||
71 | + }else if (content.search('@extend %skin-base') == -1) { | ||
72 | + throw new Error('The skin css selector ".'+skin+'" needs inherit from %skin-base sass placeholder'); | ||
73 | + } | ||
74 | + | ||
75 | +}; | ||
76 | + | ||
26 | exports.configTheme = function(theme) { | 77 | exports.configTheme = function(theme) { |
78 | + | ||
27 | exports.paths.theme = theme || "angular-default"; | 79 | exports.paths.theme = theme || "angular-default"; |
28 | - exports.paths.allSources = [exports.paths.src, path.join(exports.paths.themes, exports.paths.theme)]; | 80 | + var themePath = path.join(exports.paths.themes, exports.paths.theme); |
81 | + | ||
82 | + exports.paths.allSources = [exports.paths.src, themePath]; | ||
83 | + | ||
84 | + | ||
85 | + exports.themeExists(themePath); | ||
29 | exports.paths.dist = path.join("dist", exports.paths.theme); | 86 | exports.paths.dist = path.join("dist", exports.paths.theme); |
30 | 87 | ||
31 | if(argv.skin) { | 88 | if(argv.skin) { |
89 | + exports.skinExists(argv.skin); | ||
90 | + | ||
32 | exports.paths.skin = argv.skin; | 91 | exports.paths.skin = argv.skin; |
33 | } | 92 | } |
34 | } | 93 | } |
gulp/styles.js
@@ -10,6 +10,7 @@ var $ = require('gulp-load-plugins')(); | @@ -10,6 +10,7 @@ var $ = require('gulp-load-plugins')(); | ||
10 | 10 | ||
11 | var wiredep = require('wiredep').stream; | 11 | var wiredep = require('wiredep').stream; |
12 | var _ = require('lodash'); | 12 | var _ = require('lodash'); |
13 | +var importCss = require('gulp-import-css'); | ||
13 | 14 | ||
14 | gulp.task('styles-reload', ['styles'], function() { | 15 | gulp.task('styles-reload', ['styles'], function() { |
15 | return buildStyles() | 16 | return buildStyles() |
@@ -55,5 +56,6 @@ var buildStyles = function() { | @@ -55,5 +56,6 @@ var buildStyles = function() { | ||
55 | .pipe($.sass(sassOptions)).on('error', conf.errorHandler('Sass')) | 56 | .pipe($.sass(sassOptions)).on('error', conf.errorHandler('Sass')) |
56 | .pipe($.autoprefixer()).on('error', conf.errorHandler('Autoprefixer')) | 57 | .pipe($.autoprefixer()).on('error', conf.errorHandler('Autoprefixer')) |
57 | .pipe($.sourcemaps.write()) | 58 | .pipe($.sourcemaps.write()) |
59 | + .pipe(importCss()) | ||
58 | .pipe(gulp.dest(path.join(conf.paths.tmp, '/serve/app/'))); | 60 | .pipe(gulp.dest(path.join(conf.paths.tmp, '/serve/app/'))); |
59 | }; | 61 | }; |
package.json
@@ -47,6 +47,7 @@ | @@ -47,6 +47,7 @@ | ||
47 | "gulp-eslint": "~1.0.0", | 47 | "gulp-eslint": "~1.0.0", |
48 | "gulp-filter": "~3.0.1", | 48 | "gulp-filter": "~3.0.1", |
49 | "gulp-flatten": "~0.2.0", | 49 | "gulp-flatten": "~0.2.0", |
50 | + "gulp-import-css": "^0.1.2", | ||
50 | "gulp-inject": "~3.0.0", | 51 | "gulp-inject": "~3.0.0", |
51 | "gulp-insert": "^0.5.0", | 52 | "gulp-insert": "^0.5.0", |
52 | "gulp-load-plugins": "~0.10.0", | 53 | "gulp-load-plugins": "~0.10.0", |
src/app/admin/layout-edit/designMode.service.spec.ts
1 | import {DesignModeService} from './designMode.service'; | 1 | import {DesignModeService} from './designMode.service'; |
2 | +import {INoosferoLocalStorage} from "./../../shared/models/interfaces"; | ||
2 | 3 | ||
3 | describe('DesignMode Service', () => { | 4 | describe('DesignMode Service', () => { |
4 | let service: DesignModeService; | 5 | let service: DesignModeService; |
5 | - | 6 | + let $localStorage = <INoosferoLocalStorage>{ currentUser: null, settings: { designMode: false } }; |
6 | beforeEach(() => { | 7 | beforeEach(() => { |
7 | - service = new DesignModeService(); | 8 | + service = new DesignModeService($localStorage); |
8 | }); | 9 | }); |
9 | 10 | ||
10 | it('has the designModeOn equals false as default', () => { | 11 | it('has the designModeOn equals false as default', () => { |
@@ -18,14 +19,16 @@ describe('DesignMode Service', () => { | @@ -18,14 +19,16 @@ describe('DesignMode Service', () => { | ||
18 | }); | 19 | }); |
19 | 20 | ||
20 | it('emits the onToggle event when changing the designModeOn property', () => { | 21 | it('emits the onToggle event when changing the designModeOn property', () => { |
22 | + service.setInDesignMode(false); | ||
21 | spyOn(service.onToggle, 'next').and.stub(); | 23 | spyOn(service.onToggle, 'next').and.stub(); |
22 | service.setInDesignMode(true); | 24 | service.setInDesignMode(true); |
23 | expect(service.onToggle.next).toHaveBeenCalled(); | 25 | expect(service.onToggle.next).toHaveBeenCalled(); |
24 | }); | 26 | }); |
25 | 27 | ||
26 | it('does not emit onToggle event when there is no change on designModeOn property', () => { | 28 | it('does not emit onToggle event when there is no change on designModeOn property', () => { |
29 | + service.setInDesignMode(false); | ||
27 | spyOn(service.onToggle, 'next').and.stub(); | 30 | spyOn(service.onToggle, 'next').and.stub(); |
28 | service.setInDesignMode(false); | 31 | service.setInDesignMode(false); |
29 | expect(service.onToggle.next).not.toHaveBeenCalled(); | 32 | expect(service.onToggle.next).not.toHaveBeenCalled(); |
30 | }); | 33 | }); |
31 | -}); | ||
32 | \ No newline at end of file | 34 | \ No newline at end of file |
35 | +}); |
src/app/admin/layout-edit/designMode.service.ts
1 | -import {Injectable, Output, EventEmitter} from 'ng-forward'; | 1 | +import {Injectable, Output, EventEmitter, Inject} from 'ng-forward'; |
2 | +import {INoosferoLocalStorage} from "./../../shared/models/interfaces"; | ||
2 | 3 | ||
3 | @Injectable() | 4 | @Injectable() |
5 | +@Inject("$localStorage") | ||
4 | export class DesignModeService { | 6 | export class DesignModeService { |
5 | - @Output() onToggle: EventEmitter<boolean> = new EventEmitter<boolean>(); | ||
6 | 7 | ||
7 | - private designModeOn: boolean = false; | 8 | + @Output() onToggle: EventEmitter<boolean> = new EventEmitter<boolean>(); |
8 | 9 | ||
9 | isInDesignMode(): boolean { | 10 | isInDesignMode(): boolean { |
10 | - return this.designModeOn; | 11 | + return this.$localStorage.settings.designModeOn; |
12 | + } | ||
13 | + | ||
14 | + destroy() { | ||
15 | + delete this.$localStorage.settings; | ||
16 | + this.$localStorage.settings = {}; | ||
11 | } | 17 | } |
12 | 18 | ||
13 | setInDesignMode(value: boolean) { | 19 | setInDesignMode(value: boolean) { |
14 | - if (this.designModeOn !== value) { | ||
15 | - this.designModeOn = value; | ||
16 | - this.onToggle.next(this.designModeOn); | 20 | + if (this.$localStorage.settings.designModeOn !== value) { |
21 | + this.$localStorage.settings.designModeOn = value; | ||
22 | + this.onToggle.next(value); | ||
17 | } | 23 | } |
18 | } | 24 | } |
19 | 25 | ||
20 | - constructor() { | 26 | + constructor(private $localStorage: INoosferoLocalStorage) { |
27 | + if (!this.$localStorage.settings) { | ||
28 | + this.$localStorage.settings = {}; | ||
29 | + } | ||
21 | } | 30 | } |
22 | } | 31 | } |
src/app/admin/layout-edit/designModeToggler.component.spec.ts
@@ -2,6 +2,7 @@ import {ComponentTestHelper, createClass} from '../../../spec/component-test-hel | @@ -2,6 +2,7 @@ import {ComponentTestHelper, createClass} from '../../../spec/component-test-hel | ||
2 | import * as helpers from '../../../spec/helpers'; | 2 | import * as helpers from '../../../spec/helpers'; |
3 | import {DesignModeTogglerComponent} from './designModeToggler.component'; | 3 | import {DesignModeTogglerComponent} from './designModeToggler.component'; |
4 | import {DesignModeService} from './designMode.service'; | 4 | import {DesignModeService} from './designMode.service'; |
5 | +import {INoosferoLocalStorage} from "./../../shared/models/interfaces"; | ||
5 | 6 | ||
6 | describe('DesignModeToggler Component', () => { | 7 | describe('DesignModeToggler Component', () => { |
7 | const htmlTemplate: string = '<noosfero-design-toggler></noosfero-design-toggler>'; | 8 | const htmlTemplate: string = '<noosfero-design-toggler></noosfero-design-toggler>'; |
@@ -14,13 +15,15 @@ describe('DesignModeToggler Component', () => { | @@ -14,13 +15,15 @@ describe('DesignModeToggler Component', () => { | ||
14 | }); | 15 | }); |
15 | 16 | ||
16 | let designModeService: DesignModeService; | 17 | let designModeService: DesignModeService; |
18 | + let $localStorage = <INoosferoLocalStorage>{ currentUser: null, settings: { designMode: false } }; | ||
17 | beforeEach((done) => { | 19 | beforeEach((done) => { |
18 | - designModeService = new DesignModeService(); | 20 | + designModeService = new DesignModeService($localStorage); |
19 | let cls = createClass({ | 21 | let cls = createClass({ |
20 | template: htmlTemplate, | 22 | template: htmlTemplate, |
21 | directives: [DesignModeTogglerComponent], | 23 | directives: [DesignModeTogglerComponent], |
22 | providers: [ | 24 | providers: [ |
23 | - helpers.createProviderToValue('DesignModeService', designModeService) | 25 | + helpers.createProviderToValue('DesignModeService', designModeService), |
26 | + helpers.createProviderToValue('AuthService', helpers.mocks.authService), | ||
24 | ] | 27 | ] |
25 | }); | 28 | }); |
26 | helper = new ComponentTestHelper<DesignModeTogglerComponent>(cls, done); | 29 | helper = new ComponentTestHelper<DesignModeTogglerComponent>(cls, done); |
@@ -36,6 +39,7 @@ describe('DesignModeToggler Component', () => { | @@ -36,6 +39,7 @@ describe('DesignModeToggler Component', () => { | ||
36 | }); | 39 | }); |
37 | 40 | ||
38 | it('emits event with value "true" when changing inDesignMode to On', (done) => { | 41 | it('emits event with value "true" when changing inDesignMode to On', (done) => { |
42 | + designModeService.setInDesignMode(false); | ||
39 | designModeService.onToggle.subscribe((designModeOn: boolean) => { | 43 | designModeService.onToggle.subscribe((designModeOn: boolean) => { |
40 | expect(designModeOn).toBeTruthy(); | 44 | expect(designModeOn).toBeTruthy(); |
41 | done(); | 45 | done(); |
src/app/admin/layout-edit/designModeToggler.component.ts
1 | import {Component, Inject} from 'ng-forward'; | 1 | import {Component, Inject} from 'ng-forward'; |
2 | import {DesignModeService} from './designMode.service'; | 2 | import {DesignModeService} from './designMode.service'; |
3 | +import {AuthService, AuthEvents} from '../../login'; | ||
4 | + | ||
3 | @Component({ | 5 | @Component({ |
4 | selector: 'noosfero-design-toggler', | 6 | selector: 'noosfero-design-toggler', |
5 | templateUrl: 'app/admin/layout-edit/designModeToggler.html' | 7 | templateUrl: 'app/admin/layout-edit/designModeToggler.html' |
6 | }) | 8 | }) |
7 | -@Inject(DesignModeService) | 9 | +@Inject(DesignModeService, AuthService) |
8 | export class DesignModeTogglerComponent { | 10 | export class DesignModeTogglerComponent { |
9 | 11 | ||
10 | icon: string = " <i class='glyphicon glyphicon-wrench'></i> "; | 12 | icon: string = " <i class='glyphicon glyphicon-wrench'></i> "; |
11 | 13 | ||
12 | - constructor(private designModeService: DesignModeService) { | 14 | + constructor(private designModeService: DesignModeService, private authService: AuthService) { |
15 | + this.authService.subscribe(AuthEvents[AuthEvents.logoutSuccess], () => { | ||
16 | + this.designModeService.destroy(); | ||
17 | + }); | ||
13 | } | 18 | } |
14 | 19 | ||
15 | private _inDesignMode: boolean = false; | 20 | private _inDesignMode: boolean = false; |
@@ -21,4 +26,4 @@ export class DesignModeTogglerComponent { | @@ -21,4 +26,4 @@ export class DesignModeTogglerComponent { | ||
21 | set inDesignMode(value: boolean) { | 26 | set inDesignMode(value: boolean) { |
22 | this.designModeService.setInDesignMode(value); | 27 | this.designModeService.setInDesignMode(value); |
23 | }; | 28 | }; |
24 | -} | ||
25 | \ No newline at end of file | 29 | \ No newline at end of file |
30 | +} |
src/app/environment/environment.html
1 | <div class="environment-container"> | 1 | <div class="environment-container"> |
2 | <div class="row"> | 2 | <div class="row"> |
3 | - <noosfero-boxes ng-if="vm.boxes" [boxes]="vm.boxes" [owner]="vm.environment"></noosfero-boxes> | 3 | + <noosfero-boxes ng-if="vm.boxes" |
4 | + [layout]="vm.environment.layout_template" | ||
5 | + [boxes]="vm.boxes" | ||
6 | + [owner]="vm.environment"> | ||
7 | + </noosfero-boxes> | ||
4 | </div> | 8 | </div> |
5 | </div> | 9 | </div> |
src/app/index.scss
@@ -82,3 +82,4 @@ h1, h2, h3, h4, h5 { | @@ -82,3 +82,4 @@ h1, h2, h3, h4, h5 { | ||
82 | @import "layout/scss/layout"; | 82 | @import "layout/scss/layout"; |
83 | @import "layout/scss/sidebar"; | 83 | @import "layout/scss/sidebar"; |
84 | @import "layout/scss/tables"; | 84 | @import "layout/scss/tables"; |
85 | +@import "layout/scss/forms"; |
src/app/layout/blocks/block.component.spec.ts
@@ -2,6 +2,7 @@ import {Component} from 'ng-forward'; | @@ -2,6 +2,7 @@ import {Component} from 'ng-forward'; | ||
2 | import {BlockComponent} from './block.component'; | 2 | import {BlockComponent} from './block.component'; |
3 | import * as helpers from "../../../spec/helpers"; | 3 | import * as helpers from "../../../spec/helpers"; |
4 | import {ComponentTestHelper, createClass} from '../../../spec/component-test-helper'; | 4 | import {ComponentTestHelper, createClass} from '../../../spec/component-test-helper'; |
5 | +import {DesignModeService} from '../../admin/layout-edit/designMode.service'; | ||
5 | 6 | ||
6 | const htmlTemplate: string = '<noosfero-block [block]="ctrl.block" [owner]="ctrl.profile"></noosfero-block>'; | 7 | const htmlTemplate: string = '<noosfero-block [block]="ctrl.block" [owner]="ctrl.profile"></noosfero-block>'; |
7 | 8 | ||
@@ -32,12 +33,12 @@ describe("Boxes Component", () => { | @@ -32,12 +33,12 @@ describe("Boxes Component", () => { | ||
32 | helpers.createProviderToValue('TranslatorService', translatorService), | 33 | helpers.createProviderToValue('TranslatorService', translatorService), |
33 | helpers.createProviderToValue('$uibModal', helpers.mocks.$modal), | 34 | helpers.createProviderToValue('$uibModal', helpers.mocks.$modal), |
34 | helpers.createProviderToValue('BlockService', blockService), | 35 | helpers.createProviderToValue('BlockService', blockService), |
35 | - helpers.createProviderToValue('NotificationService', helpers.mocks.notificationService) | 36 | + helpers.createProviderToValue('NotificationService', helpers.mocks.notificationService), |
37 | + helpers.createProviderToValue('DesignModeService', helpers.mocks.designModeService) | ||
36 | ] | 38 | ] |
37 | }); | 39 | }); |
38 | helper = new ComponentTestHelper<BlockComponent>(cls, done); | 40 | helper = new ComponentTestHelper<BlockComponent>(cls, done); |
39 | }); | 41 | }); |
40 | - | ||
41 | let translatorService = jasmine.createSpyObj("translatorService", ["currentLanguage"]); | 42 | let translatorService = jasmine.createSpyObj("translatorService", ["currentLanguage"]); |
42 | let blockService = jasmine.createSpyObj("blockService", ["update"]); | 43 | let blockService = jasmine.createSpyObj("blockService", ["update"]); |
43 | let state = jasmine.createSpyObj("state", ["current"]); | 44 | let state = jasmine.createSpyObj("state", ["current"]); |
src/app/layout/blocks/block.component.ts
@@ -11,7 +11,8 @@ import { DesignModeService } from "../../admin/layout-edit/designMode.service"; | @@ -11,7 +11,8 @@ import { DesignModeService } from "../../admin/layout-edit/designMode.service"; | ||
11 | templateUrl: 'app/layout/blocks/block.html', | 11 | templateUrl: 'app/layout/blocks/block.html', |
12 | directives: [BlockEditionComponent] | 12 | directives: [BlockEditionComponent] |
13 | }) | 13 | }) |
14 | -@Inject("$uibModal", "$scope", "$state", "$rootScope", BlockService, NotificationService, AuthService, SessionService, TranslatorService, DesignModeService) | 14 | +@Inject("$uibModal", "$scope", "$state", "$rootScope", BlockService, NotificationService, |
15 | + AuthService, SessionService, TranslatorService, DesignModeService) | ||
15 | export class BlockComponent { | 16 | export class BlockComponent { |
16 | 17 | ||
17 | @Input() block: noosfero.Block; | 18 | @Input() block: noosfero.Block; |
src/app/layout/blocks/block.html
1 | -<div ng-show="ctrl.canDisplay() || ctrl.editionMode || ctrl.designMode" ng-class="{'invisible-block': !ctrl.canDisplay()}" class="noosfero-block" ng-mouseover="displayActions = true" ng-mouseleave="displayActions = false"> | 1 | +<div ng-show="ctrl.canDisplay() || ctrl.inEditMode() || ctrl.designMode" ng-class="{'invisible-block': !ctrl.canDisplay()}" class="noosfero-block" ng-mouseover="displayActions = true" ng-mouseleave="displayActions = false"> |
2 | <div ng-show="displayActions" class="actions block-actions" permission="ctrl.block.permissions" permission-action="allow_edit"> | 2 | <div ng-show="displayActions" class="actions block-actions" permission="ctrl.block.permissions" permission-action="allow_edit"> |
3 | <button type="submit" class="btn btn-xs btn-default" ng-click="ctrl.openEdit()"><i class="fa fa-edit fa-fw"></i></button> | 3 | <button type="submit" class="btn btn-xs btn-default" ng-click="ctrl.openEdit()"><i class="fa fa-edit fa-fw"></i></button> |
4 | </div> | 4 | </div> |
src/app/layout/blocks/login-block/login-block.html
@@ -15,13 +15,21 @@ | @@ -15,13 +15,21 @@ | ||
15 | <div class="logged-user-info" ng-show="!ctrl.currentUser"> | 15 | <div class="logged-user-info" ng-show="!ctrl.currentUser"> |
16 | <form> | 16 | <form> |
17 | <div class="form-group"> | 17 | <div class="form-group"> |
18 | - <label for="exampleInputEmail1">{{"auth.form.login" | translate}}</label> | ||
19 | - <input type="text" class="form-control" id="exampleInputEmail1" placeholder="Login / Email" ng-model="ctrl.credentials.username"> | 18 | + <label for="email">{{"auth.form.login" | translate}}</label> |
19 | + <input type="text" class="form-control" id="email" placeholder="{{'auth.form.login' | translate}}" ng-model="ctrl.credentials.username"> | ||
20 | </div> | 20 | </div> |
21 | <div class="form-group"> | 21 | <div class="form-group"> |
22 | - <label for="exampleInputPassword1">{{"auth.form.password" | translate}}</label> | ||
23 | - <input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password" ng-model="ctrl.credentials.password"> | 22 | + <label for="passwd">{{"auth.form.password" | translate}}</label> |
23 | + <input type="password" class="form-control" id="passwd" placeholder="{{'auth.form.password' | translate}}" ng-model="ctrl.credentials.password"> | ||
24 | + </div> | ||
25 | + <div class="form-inline"> | ||
26 | + <div class="checkbox-nice"> | ||
27 | + <input type="checkbox" id="keep-logged"> | ||
28 | + <label for="keep-logged"> | ||
29 | + {{"auth.form.keepLoggedIn" | translate}} | ||
30 | + </label> | ||
31 | + </div> | ||
24 | </div> | 32 | </div> |
25 | <button type="submit" class="btn btn-default" ng-click="ctrl.login()">{{"auth.form.login_button" | translate}}</button> | 33 | <button type="submit" class="btn btn-default" ng-click="ctrl.login()">{{"auth.form.login_button" | translate}}</button> |
26 | </form> | 34 | </form> |
27 | -</div> | ||
28 | \ No newline at end of file | 35 | \ No newline at end of file |
36 | +</div> |
@@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
1 | +export * from "./tags-block.component"; |
@@ -0,0 +1,55 @@ | @@ -0,0 +1,55 @@ | ||
1 | +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | ||
2 | +import {Provider, Input, provide, Component} from 'ng-forward'; | ||
3 | +import {provideFilters} from '../../../../spec/helpers'; | ||
4 | +import {TagsBlockComponent} from './tags-block.component'; | ||
5 | + | ||
6 | +const htmlTemplate: string = '<noosfero-tags-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-tags-block>'; | ||
7 | + | ||
8 | +const tcb = new TestComponentBuilder(); | ||
9 | + | ||
10 | +describe("Components", () => { | ||
11 | + describe("Tags Block Component", () => { | ||
12 | + | ||
13 | + let settingsObj = {}; | ||
14 | + let mockedEnvironmentService = { | ||
15 | + getTags: (): any => { | ||
16 | + return Promise.resolve({ foo: 10, bar: 20 }); | ||
17 | + } | ||
18 | + }; | ||
19 | + let profile = { name: 'profile-name' }; | ||
20 | + beforeEach(angular.mock.module("templates")); | ||
21 | + | ||
22 | + let state = jasmine.createSpyObj("state", ["go"]); | ||
23 | + | ||
24 | + | ||
25 | + function getProviders() { | ||
26 | + return [ | ||
27 | + new Provider('$state', { useValue: state }), | ||
28 | + new Provider('EnvironmentService', { | ||
29 | + useValue: mockedEnvironmentService | ||
30 | + }), | ||
31 | + ].concat(provideFilters("truncateFilter", "stripTagsFilter")); | ||
32 | + } | ||
33 | + let componentClass: any = null; | ||
34 | + | ||
35 | + function getComponent() { | ||
36 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [TagsBlockComponent], providers: getProviders() }) | ||
37 | + class BlockContainerComponent { | ||
38 | + block = { type: 'Block', settings: settingsObj }; | ||
39 | + owner = profile; | ||
40 | + constructor() { | ||
41 | + } | ||
42 | + } | ||
43 | + return BlockContainerComponent; | ||
44 | + } | ||
45 | + | ||
46 | + | ||
47 | + it("get tags from the environment service", done => { | ||
48 | + tcb.createAsync(getComponent()).then(fixture => { | ||
49 | + let tagsBlock: TagsBlockComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | ||
50 | + expect(tagsBlock.tags).toEqual([{ text: "foo", weight: '10', link: '/tag/foo' }, { text: "bar", weight: '20', link: '/tag/bar' }]); | ||
51 | + done(); | ||
52 | + }); | ||
53 | + }); | ||
54 | + }); | ||
55 | +}); |
@@ -0,0 +1,45 @@ | @@ -0,0 +1,45 @@ | ||
1 | +import {Component, Inject, Input} from "ng-forward"; | ||
2 | +import {EnvironmentService} from "../../../../lib/ng-noosfero-api/http/environment.service"; | ||
3 | + | ||
4 | +@Component({ | ||
5 | + selector: "noosfero-tags-block", | ||
6 | + templateUrl: 'app/layout/blocks/tags/tags-block.html' | ||
7 | +}) | ||
8 | +@Inject(EnvironmentService, "$state") | ||
9 | +export class TagsBlockComponent { | ||
10 | + | ||
11 | + @Input() block: any; | ||
12 | + @Input() owner: any; | ||
13 | + | ||
14 | + profile: any; | ||
15 | + tags: any; | ||
16 | + tagsLoaded: boolean = false; | ||
17 | + | ||
18 | + constructor(private environmentService: EnvironmentService, private $state: any) { | ||
19 | + this.loadTags(); | ||
20 | + } | ||
21 | + | ||
22 | + loadTags() { | ||
23 | + this.tags = []; | ||
24 | + let tag = ''; | ||
25 | + let tags: any = []; | ||
26 | + let that = this; | ||
27 | + | ||
28 | + this.environmentService.getTags() | ||
29 | + .then((result: any) => { | ||
30 | + for (tag in result) { | ||
31 | + if (result.hasOwnProperty(tag)) { | ||
32 | + let size: number = result[tag]; | ||
33 | + tags.push({ text: tag.toString(), weight: size.toString(), link: '/tag/' + tag }); | ||
34 | + } | ||
35 | + } | ||
36 | + | ||
37 | + that.tagsLoaded = true; | ||
38 | + that.tags = tags.slice(); | ||
39 | + }); | ||
40 | + } | ||
41 | + | ||
42 | + ngOnInit() { | ||
43 | + this.profile = this.owner; | ||
44 | + } | ||
45 | +} |
@@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
1 | +<ng-tag-cloud cloud-width="200" cloud-height="150" cloud-data="ctrl.tags"></ng-tag-cloud> |
src/app/layout/boxes/box.html
1 | -<div ng-class="{'col-md-2-5': box.position!=1, 'col-md-7': box.position==1}"> | ||
2 | - <noosfero-block ng-repeat="block in box.blocks | orderBy: 'position'" [block]="block" [owner]="ctrl.owner"></noosfero-block> | 1 | +<div ng-class="box.position | setBoxLayout:ctrl.layout"> |
2 | + | ||
3 | + <div ng-repeat="block in box.blocks | orderBy: 'position'" class="panel panel-default block {{block.type | lowercase}}" > | ||
4 | + <div class="panel-heading" ng-show="block.title"> | ||
5 | + <h3 class="panel-title">{{block.title}}</h3> | ||
6 | + </div> | ||
7 | + <div class="panel-body {{block.type | lowercase}}" > | ||
8 | + <noosfero-block [block]="block" [owner]="ctrl.owner"></noosfero-block> | ||
9 | + </div> | ||
10 | + </div> | ||
3 | </div> | 11 | </div> |
src/app/layout/boxes/boxes.component.spec.ts
@@ -45,12 +45,7 @@ describe("Boxes Component", () => { | @@ -45,12 +45,7 @@ describe("Boxes Component", () => { | ||
45 | state.current = { name: "" }; | 45 | state.current = { name: "" }; |
46 | 46 | ||
47 | it("renders boxes into a container", () => { | 47 | it("renders boxes into a container", () => { |
48 | - expect(helper.find('div.col-md-7').length).toEqual(1); | ||
49 | - expect(helper.find('div.col-md-2-5').length).toEqual(1); | ||
50 | - }); | ||
51 | - | ||
52 | - it("check the boxes order", () => { | ||
53 | - expect(helper.component.boxesOrder(properties['boxes'][0])).toEqual(1); | ||
54 | - expect(helper.component.boxesOrder(properties['boxes'][1])).toEqual(0); | 48 | + expect(helper.find('div.col-md-6').length).toEqual(1); |
49 | + expect(helper.find('div.col-md-3').length).toEqual(1); | ||
55 | }); | 50 | }); |
56 | }); | 51 | }); |
src/app/layout/boxes/boxes.component.ts
1 | -import {Input, Component} from 'ng-forward'; | 1 | +import {Input, Inject, Component} from 'ng-forward'; |
2 | +import {DisplayBoxes} from "./display-boxes.filter"; | ||
3 | +import {SetBoxLayout} from "./set-box-layout.filter"; | ||
2 | 4 | ||
3 | @Component({ | 5 | @Component({ |
4 | selector: "noosfero-boxes", | 6 | selector: "noosfero-boxes", |
5 | - templateUrl: "app/layout/boxes/boxes.html" | 7 | + templateUrl: "app/layout/boxes/boxes.html", |
8 | + directives: [DisplayBoxes, SetBoxLayout] | ||
6 | }) | 9 | }) |
7 | export class BoxesComponent { | 10 | export class BoxesComponent { |
8 | 11 | ||
9 | @Input() boxes: noosfero.Box[]; | 12 | @Input() boxes: noosfero.Box[]; |
10 | @Input() owner: noosfero.Profile | noosfero.Environment; | 13 | @Input() owner: noosfero.Profile | noosfero.Environment; |
14 | + @Input() layout: string; | ||
11 | 15 | ||
12 | - boxesOrder(box: noosfero.Box) { | ||
13 | - if (box.position === 2) return 0; | ||
14 | - return box.position; | ||
15 | - } | ||
16 | } | 16 | } |
src/app/layout/boxes/boxes.html
@@ -0,0 +1,51 @@ | @@ -0,0 +1,51 @@ | ||
1 | +import {DisplayBoxes} from './display-boxes.filter'; | ||
2 | + | ||
3 | +describe("Boxes Filters", () => { | ||
4 | + describe("Display Boxes Filter", () => { | ||
5 | + | ||
6 | + let boxes: noosfero.Box[] = [ | ||
7 | + {id: 1, position: 1 }, | ||
8 | + {id: 2, position: 2 }, | ||
9 | + {id: 3, position: 3 }, | ||
10 | + {id: 4, position: 4 } | ||
11 | + ]; | ||
12 | + | ||
13 | + let expected_on_default: noosfero.Box[] = [ | ||
14 | + {id: 1, position: 1 }, | ||
15 | + {id: 2, position: 2 }, | ||
16 | + {id: 3, position: 3 }, | ||
17 | + ]; | ||
18 | + | ||
19 | + let expected_on_rightbar: noosfero.Box[] = [ | ||
20 | + {id: 1, position: 1 }, | ||
21 | + {id: 3, position: 3 }, | ||
22 | + ]; | ||
23 | + | ||
24 | + it("filter boxes when layout is set to default", done => { | ||
25 | + let filter = new DisplayBoxes(); | ||
26 | + | ||
27 | + let filtered_boxes: noosfero.Box[] = filter.transform(boxes, "default"); | ||
28 | + expect(filtered_boxes.length).toEqual(3); | ||
29 | + expect(filtered_boxes).toEqual(expected_on_default); | ||
30 | + done(); | ||
31 | + }); | ||
32 | + | ||
33 | + it("filter boxes when layout is set to rightbar", done => { | ||
34 | + let filter = new DisplayBoxes(); | ||
35 | + | ||
36 | + let filtered_boxes: noosfero.Box[] = filter.transform(boxes, "rightbar"); | ||
37 | + expect(filtered_boxes.length).toEqual(2); | ||
38 | + expect(filtered_boxes).toEqual(expected_on_rightbar); | ||
39 | + done(); | ||
40 | + }); | ||
41 | + | ||
42 | + it("filter boxes with default layout when invalid layout is given", done => { | ||
43 | + let filter = new DisplayBoxes(); | ||
44 | + | ||
45 | + let filtered_boxes: noosfero.Box[] = filter.transform(boxes, ""); | ||
46 | + expect(filtered_boxes.length).toEqual(3); | ||
47 | + expect(filtered_boxes).toEqual(expected_on_default); | ||
48 | + done(); | ||
49 | + }); | ||
50 | + }); | ||
51 | +}); |
@@ -0,0 +1,37 @@ | @@ -0,0 +1,37 @@ | ||
1 | +import {Pipe, Inject} from "ng-forward"; | ||
2 | +import {TranslatorService} from "../../shared/services/translator.service"; | ||
3 | + | ||
4 | +@Pipe("displayBoxes") | ||
5 | +@Inject(TranslatorService) | ||
6 | +export class DisplayBoxes { | ||
7 | + | ||
8 | + transform(boxes: noosfero.Box[], layout: string) { | ||
9 | + let function_str: string = "visible_on" + layout; | ||
10 | + let valid_boxes: number[] = []; | ||
11 | + let selected: noosfero.Box[] = []; | ||
12 | + boxes = boxes || []; | ||
13 | + | ||
14 | + if (layout === "rightbar") { | ||
15 | + valid_boxes = this.visible_on_right_bar(); | ||
16 | + }else { | ||
17 | + valid_boxes = this.visible_on_default(); | ||
18 | + } | ||
19 | + | ||
20 | + for (let box of boxes) { | ||
21 | + if (valid_boxes.indexOf(box.position) !== -1) { | ||
22 | + selected.push(box); | ||
23 | + } | ||
24 | + } | ||
25 | + return selected; | ||
26 | + } | ||
27 | + | ||
28 | + private visible_on_default() { | ||
29 | + return [1, 2, 3]; | ||
30 | + } | ||
31 | + | ||
32 | + private visible_on_right_bar() { | ||
33 | + return [1, 3]; | ||
34 | + } | ||
35 | + | ||
36 | +} | ||
37 | + |
@@ -0,0 +1,34 @@ | @@ -0,0 +1,34 @@ | ||
1 | +import {SetBoxLayout} from './set-box-layout.filter'; | ||
2 | + | ||
3 | +describe("Boxes Filters", () => { | ||
4 | + describe("Set Box Layout Filter", () => { | ||
5 | + let box_layout_filter = new SetBoxLayout(); | ||
6 | + describe("When layout is set to default", () => { | ||
7 | + it("return style when box position is 1 ", done => { | ||
8 | + expect(box_layout_filter.transform(1, "default")).toEqual("col-md-6 col-md-push-3"); | ||
9 | + done(); | ||
10 | + }); | ||
11 | + it("return style when box position is 2", done => { | ||
12 | + expect(box_layout_filter.transform(2, "default")).toEqual("col-md-3 col-md-pull-6"); | ||
13 | + done(); | ||
14 | + }); | ||
15 | + it("return style when any other position is given", done => { | ||
16 | + expect(box_layout_filter.transform(null, "default")).toEqual("col-md-3"); | ||
17 | + expect(box_layout_filter.transform(3, "default")).toEqual("col-md-3"); | ||
18 | + expect(box_layout_filter.transform(99, "default")).toEqual("col-md-3"); | ||
19 | + done(); | ||
20 | + }); | ||
21 | + }); | ||
22 | + | ||
23 | + describe("When layout is set to right_bar", () => { | ||
24 | + it("return style when box position is 1 ", done => { | ||
25 | + expect(box_layout_filter.transform(1, "rightbar")).toEqual("col-sm-6 col-sm-push-2"); | ||
26 | + done(); | ||
27 | + }); | ||
28 | + it("return style when box other position is given", done => { | ||
29 | + expect(box_layout_filter.transform(2, "rightbar")).toEqual("col-sm-3 col-sm-push-2"); | ||
30 | + done(); | ||
31 | + }); | ||
32 | + }); | ||
33 | + }); | ||
34 | +}); |
@@ -0,0 +1,34 @@ | @@ -0,0 +1,34 @@ | ||
1 | +import {Pipe, Inject} from "ng-forward"; | ||
2 | +import {TranslatorService} from "../../shared/services/translator.service"; | ||
3 | + | ||
4 | +@Pipe("setBoxLayout") | ||
5 | +@Inject(TranslatorService) | ||
6 | +export class SetBoxLayout { | ||
7 | + | ||
8 | + transform(pos: number, layout: string) { | ||
9 | + if (layout === "rightbar") { | ||
10 | + return this.right_bar(pos); | ||
11 | + }else { | ||
12 | + return this.default(pos); | ||
13 | + } | ||
14 | + } | ||
15 | + | ||
16 | + private default(position: number) { | ||
17 | + if (position === 1) { | ||
18 | + return "col-md-6 col-md-push-3"; | ||
19 | + }else if (position === 2) { | ||
20 | + return "col-md-3 col-md-pull-6"; | ||
21 | + }else { | ||
22 | + return "col-md-3"; | ||
23 | + } | ||
24 | + } | ||
25 | + | ||
26 | + private right_bar(position: number) { | ||
27 | + if (position === 1) { | ||
28 | + return "col-sm-6 col-sm-push-2"; | ||
29 | + }else { | ||
30 | + return "col-sm-3 col-sm-push-2"; | ||
31 | + } | ||
32 | + } | ||
33 | + | ||
34 | +} |
src/app/layout/language-selector/language-selector.html
1 | -<li class="dropdown profile-menu" uib-dropdown> | 1 | +<li class="dropdown profile-menu btn-nav" uib-dropdown> |
2 | <a href="#" class="dropdown-toggle" aria-expanded="false" uib-dropdown-toggle> | 2 | <a href="#" class="dropdown-toggle" aria-expanded="false" uib-dropdown-toggle> |
3 | <span>{{"language.selector" | translate}}</span> <b class="caret"></b> | 3 | <span>{{"language.selector" | translate}}</span> <b class="caret"></b> |
4 | </a> | 4 | </a> |
src/app/layout/navbar/navbar.html
@@ -16,7 +16,7 @@ | @@ -16,7 +16,7 @@ | ||
16 | </ul> | 16 | </ul> |
17 | 17 | ||
18 | <ul class="nav navbar-nav navbar-right"> | 18 | <ul class="nav navbar-nav navbar-right"> |
19 | - <li ng-show="!ctrl.currentUser"> | 19 | + <li class="btn-nav" ng-show="!ctrl.currentUser"> |
20 | <a ng-href="#" ng-click="ctrl.openLogin()">{{"navbar.login" | translate}}</a> | 20 | <a ng-href="#" ng-click="ctrl.openLogin()">{{"navbar.login" | translate}}</a> |
21 | </li> | 21 | </li> |
22 | 22 | ||
@@ -51,4 +51,4 @@ | @@ -51,4 +51,4 @@ | ||
51 | </div> | 51 | </div> |
52 | </nav> | 52 | </nav> |
53 | <div ui-view="toolbar"> | 53 | <div ui-view="toolbar"> |
54 | -</div> | ||
55 | \ No newline at end of file | 54 | \ No newline at end of file |
55 | +</div> |
src/app/layout/navbar/navbar.scss
src/app/layout/navbar/navbar.spec.ts
@@ -9,6 +9,8 @@ import {SessionService, AuthService, AuthController, AuthEvents} from "./../../l | @@ -9,6 +9,8 @@ import {SessionService, AuthService, AuthController, AuthEvents} from "./../../l | ||
9 | 9 | ||
10 | import events from 'ng-forward/cjs/events/events'; | 10 | import events from 'ng-forward/cjs/events/events'; |
11 | 11 | ||
12 | +import {DesignModeService} from '../../admin/layout-edit/designMode.service'; | ||
13 | + | ||
12 | describe("Components", () => { | 14 | describe("Components", () => { |
13 | 15 | ||
14 | describe("Navbar Component", () => { | 16 | describe("Navbar Component", () => { |
@@ -22,6 +24,7 @@ describe("Components", () => { | @@ -22,6 +24,7 @@ describe("Components", () => { | ||
22 | let authService: any; | 24 | let authService: any; |
23 | let stateService: any; | 25 | let stateService: any; |
24 | let sessionService: SessionService; | 26 | let sessionService: SessionService; |
27 | + let designModeService: DesignModeService; | ||
25 | 28 | ||
26 | let provideFunc = provide; | 29 | let provideFunc = provide; |
27 | 30 | ||
@@ -37,6 +40,7 @@ describe("Components", () => { | @@ -37,6 +40,7 @@ describe("Components", () => { | ||
37 | authService = helpers.mocks.authService; | 40 | authService = helpers.mocks.authService; |
38 | stateService = jasmine.createSpyObj("$state", ["go"]); | 41 | stateService = jasmine.createSpyObj("$state", ["go"]); |
39 | sessionService = <any>helpers.mocks.sessionWithCurrentUser(user); | 42 | sessionService = <any>helpers.mocks.sessionWithCurrentUser(user); |
43 | + designModeService = helpers.mocks.designModeService; | ||
40 | }); | 44 | }); |
41 | 45 | ||
42 | 46 | ||
@@ -76,6 +80,9 @@ describe("Components", () => { | @@ -76,6 +80,9 @@ describe("Components", () => { | ||
76 | }), | 80 | }), |
77 | provide('TranslatorService', { | 81 | provide('TranslatorService', { |
78 | useValue: helpers.mocks.translatorService | 82 | useValue: helpers.mocks.translatorService |
83 | + }), | ||
84 | + provide('DesignModeService', { | ||
85 | + useValue: helpers.mocks.designModeService | ||
79 | }) | 86 | }) |
80 | ].concat(helpers.provideFilters("translateFilter")), | 87 | ].concat(helpers.provideFilters("translateFilter")), |
81 | directives: [Navbar], | 88 | directives: [Navbar], |
@@ -0,0 +1,36 @@ | @@ -0,0 +1,36 @@ | ||
1 | +label { | ||
2 | + cursor: pointer; | ||
3 | +} | ||
4 | + | ||
5 | +.checkbox-nice { | ||
6 | + position: relative; | ||
7 | + padding-left: 15px; | ||
8 | + | ||
9 | + input[type=checkbox] { | ||
10 | + visibility: hidden; | ||
11 | + } | ||
12 | + label { | ||
13 | + padding-top: 3px; | ||
14 | + } | ||
15 | + &.checkbox-inline > label { | ||
16 | + margin-left: 16px; | ||
17 | + } | ||
18 | + label:before { | ||
19 | + @include checkbox-mark(22px, 22px, 1px, 1px, #ffffff, 2px solid $main-bg-color); | ||
20 | + @include border-radius(3px); | ||
21 | + } | ||
22 | + label:after { | ||
23 | + @include checkbox-mark(12px, 7px, 7px, 6px, transparent, 3px solid #000); | ||
24 | + @include opacity(0); | ||
25 | + | ||
26 | + border-top: none; | ||
27 | + border-right: none; | ||
28 | + transform: rotate(-45deg); | ||
29 | + } | ||
30 | + label:hover::after { | ||
31 | + @include opacity(0.3); | ||
32 | + } | ||
33 | + input[type=checkbox]:checked + label:after { | ||
34 | + @include opacity(1); | ||
35 | + } | ||
36 | +} |
src/app/layout/scss/_mixins.scss
@@ -3,3 +3,48 @@ | @@ -3,3 +3,48 @@ | ||
3 | border-radius: $radius; | 3 | border-radius: $radius; |
4 | background-clip: padding-box; /* stops bg color from leaking outside the border: */ | 4 | background-clip: padding-box; /* stops bg color from leaking outside the border: */ |
5 | } | 5 | } |
6 | + | ||
7 | +@mixin checkbox-mark($width, $height, $top, $left, $bg, $border, $content: "", $cursor: pointer, $position: absolute) { | ||
8 | + width: $width; | ||
9 | + height: $height; | ||
10 | + cursor: $cursor; | ||
11 | + position: $position; | ||
12 | + left: $left; | ||
13 | + top: $top; | ||
14 | + background: $bg; | ||
15 | + content: $content; | ||
16 | + border: $border; | ||
17 | +} | ||
18 | + | ||
19 | +@mixin opacity($opacity) { | ||
20 | + /* Netscape */ | ||
21 | + -moz-opacity: $opacity; | ||
22 | + | ||
23 | + /* Safari 1.x */ | ||
24 | + -khtml-opacity: $opacity; | ||
25 | + | ||
26 | + /* Good browsers */ | ||
27 | + opacity: $opacity; | ||
28 | +} | ||
29 | + | ||
30 | +@mixin transition($args...) { | ||
31 | + | ||
32 | + $duration: 300ms; | ||
33 | + $keys: keywords($args); | ||
34 | + @if map-has-key($keys, duration) { | ||
35 | + $duration: map-get($keys, duration); | ||
36 | + } | ||
37 | + | ||
38 | + -webkit-transition: $args; | ||
39 | + -o-transition: $args; | ||
40 | + transition: $args; | ||
41 | + -webkit-transition-duration: $duration; | ||
42 | + transition-duration: $duration; | ||
43 | +} | ||
44 | + | ||
45 | +@mixin outline($style: none, $width: 0px) { | ||
46 | + &:focus { | ||
47 | + outline-style: $style; | ||
48 | + outline-width: $width; | ||
49 | + } | ||
50 | +} |
src/app/layout/scss/skins/_whbl.scss
@@ -7,6 +7,15 @@ $whbl-font-color: #16191c; | @@ -7,6 +7,15 @@ $whbl-font-color: #16191c; | ||
7 | #header-navbar { | 7 | #header-navbar { |
8 | background-color: $whbl-primary-color; | 8 | background-color: $whbl-primary-color; |
9 | } | 9 | } |
10 | + | ||
11 | + .navbar { | ||
12 | + .btn-nav { | ||
13 | + &:hover { | ||
14 | + background-color: #1b86ba; | ||
15 | + } | ||
16 | + } | ||
17 | + } | ||
18 | + | ||
10 | .navbar > .container .navbar-brand { | 19 | .navbar > .container .navbar-brand { |
11 | background-color: transparent; | 20 | background-color: transparent; |
12 | width: 221px; | 21 | width: 221px; |
@@ -306,6 +315,13 @@ $whbl-font-color: #16191c; | @@ -306,6 +315,13 @@ $whbl-font-color: #16191c; | ||
306 | color: white; | 315 | color: white; |
307 | } | 316 | } |
308 | 317 | ||
318 | + /* Form overrides */ | ||
319 | + .checkbox-nice { | ||
320 | + label:after { | ||
321 | + border-color: $whbl-primary-color; | ||
322 | + } | ||
323 | + } | ||
324 | + | ||
309 | } | 325 | } |
310 | .rtl.skin-whbl #content-wrapper { | 326 | .rtl.skin-whbl #content-wrapper { |
311 | border-left: 0; | 327 | border-left: 0; |
src/app/layout/services/body-state-classes.service.spec.ts
@@ -5,11 +5,13 @@ import {AuthEvents} from "./../../login/auth-events"; | @@ -5,11 +5,13 @@ import {AuthEvents} from "./../../login/auth-events"; | ||
5 | 5 | ||
6 | import {EventEmitter} from 'ng-forward'; | 6 | import {EventEmitter} from 'ng-forward'; |
7 | import {DesignModeService} from './../../admin/layout-edit/designMode.service'; | 7 | import {DesignModeService} from './../../admin/layout-edit/designMode.service'; |
8 | +import {INoosferoLocalStorage} from "./../../shared/models/interfaces"; | ||
8 | 9 | ||
9 | describe("BodyStateClasses Service", () => { | 10 | describe("BodyStateClasses Service", () => { |
10 | 11 | ||
11 | let currentStateName = "main"; | 12 | let currentStateName = "main"; |
12 | let bodyStateClasseService: BodyStateClassesService; | 13 | let bodyStateClasseService: BodyStateClassesService; |
14 | + let $localStorage = <INoosferoLocalStorage>{ currentUser: null, settings: { designMode: false } }; | ||
13 | let $rootScope: ng.IRootScopeService = <any>{}, | 15 | let $rootScope: ng.IRootScopeService = <any>{}, |
14 | $document: ng.IDocumentService = <any>{}, | 16 | $document: ng.IDocumentService = <any>{}, |
15 | $state: ng.ui.IStateService = <any>{ | 17 | $state: ng.ui.IStateService = <any>{ |
@@ -20,7 +22,7 @@ describe("BodyStateClasses Service", () => { | @@ -20,7 +22,7 @@ describe("BodyStateClasses Service", () => { | ||
20 | authService: any = helpers.mocks.authService, | 22 | authService: any = helpers.mocks.authService, |
21 | bodyEl: { className: string }, | 23 | bodyEl: { className: string }, |
22 | bodyElJq: any, | 24 | bodyElJq: any, |
23 | - designModeService = new DesignModeService(); | 25 | + designModeService = new DesignModeService($localStorage); |
24 | 26 | ||
25 | 27 | ||
26 | 28 | ||
@@ -174,7 +176,7 @@ describe("BodyStateClasses Service", () => { | @@ -174,7 +176,7 @@ describe("BodyStateClasses Service", () => { | ||
174 | 176 | ||
175 | it("should add the class noosfero-design-on when designMode is changed to true", () => { | 177 | it("should add the class noosfero-design-on when designMode is changed to true", () => { |
176 | let fnOnToggle: Function = null; | 178 | let fnOnToggle: Function = null; |
177 | - designModeService.onToggle = <any> { | 179 | + designModeService.onToggle = <any>{ |
178 | subscribe: (fn: Function) => { | 180 | subscribe: (fn: Function) => { |
179 | fnOnToggle = fn; | 181 | fnOnToggle = fn; |
180 | }, | 182 | }, |
src/app/login/login.html
@@ -4,13 +4,21 @@ | @@ -4,13 +4,21 @@ | ||
4 | <div class="modal-body"> | 4 | <div class="modal-body"> |
5 | <form> | 5 | <form> |
6 | <div class="form-group"> | 6 | <div class="form-group"> |
7 | - <label for="exampleInputEmail1">{{"auth.form.login" | translate}}</label> | ||
8 | - <input type="text" class="form-control" id="exampleInputEmail1" placeholder="Login / Email" ng-model="vm.credentials.username"> | 7 | + <label for="email">{{"auth.form.login" | translate}}</label> |
8 | + <input type="text" class="form-control" id="email" placeholder="{{'auth.form.login' | translate}}" ng-model="vm.credentials.username"> | ||
9 | </div> | 9 | </div> |
10 | <div class="form-group"> | 10 | <div class="form-group"> |
11 | - <label for="exampleInputPassword1">{{"auth.form.password" | translate}}</label> | ||
12 | - <input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password" ng-model="vm.credentials.password"> | 11 | + <label for="passwd">{{"auth.form.password" | translate}}</label> |
12 | + <input type="password" class="form-control" id="passwd" placeholder="{{'auth.form.password' | translate}}" ng-model="vm.credentials.password"> | ||
13 | </div> | 13 | </div> |
14 | - <button type="submit" class="btn btn-default" ng-click="vm.login()">{{"auth.form.login_button" | translate}}</button> | 14 | + <div class="form-inline"> |
15 | + <div class="checkbox-nice"> | ||
16 | + <input type="checkbox" id="keep-logged"> | ||
17 | + <label for="keep-logged"> | ||
18 | + {{"auth.form.keepLoggedIn" | translate}} | ||
19 | + </label> | ||
20 | + </div> | ||
21 | + </div> | ||
22 | + <button type="submit" class="btn btn-default btn-block" ng-click="vm.login()">{{"auth.form.login_button" | translate}}</button> | ||
15 | </form> | 23 | </form> |
16 | </div> | 24 | </div> |
@@ -0,0 +1,43 @@ | @@ -0,0 +1,43 @@ | ||
1 | +.modal-content { | ||
2 | + padding: 40px; | ||
3 | + .modal-header { | ||
4 | + border-bottom: 0; | ||
5 | + text-align: center; | ||
6 | + } | ||
7 | + form { | ||
8 | + margin-bottom: 30px; | ||
9 | + button { | ||
10 | + margin-top: 20px; | ||
11 | + text-transform: uppercase; | ||
12 | + } | ||
13 | + } | ||
14 | +} | ||
15 | + | ||
16 | +.strike { | ||
17 | + display: block; | ||
18 | + text-align: center; | ||
19 | + overflow: hidden; | ||
20 | + white-space: nowrap; | ||
21 | + padding: 25px 0; | ||
22 | + > span { | ||
23 | + position: relative; | ||
24 | + display: inline-block; | ||
25 | + text-transform: uppercase; | ||
26 | + } | ||
27 | + > span:before, > span:after { | ||
28 | + content: ""; | ||
29 | + position: absolute; | ||
30 | + top: 50%; | ||
31 | + width: 9999px; | ||
32 | + height: 1px; | ||
33 | + background: black; | ||
34 | + } | ||
35 | + > span:before { | ||
36 | + right: 100%; | ||
37 | + margin-right: 15px; | ||
38 | + } | ||
39 | + > span:after { | ||
40 | + left: 100%; | ||
41 | + margin-left: 15px; | ||
42 | + } | ||
43 | +} |
src/app/login/session.service.spec.ts
@@ -13,7 +13,7 @@ describe("Services", () => { | @@ -13,7 +13,7 @@ describe("Services", () => { | ||
13 | let $log: any; | 13 | let $log: any; |
14 | 14 | ||
15 | beforeEach(() => { | 15 | beforeEach(() => { |
16 | - $localStorage = <INoosferoLocalStorage>{ currentUser: null }; | 16 | + $localStorage = <INoosferoLocalStorage>{ currentUser: null, settings: null }; |
17 | $log = jasmine.createSpyObj('$log', ['debug']); | 17 | $log = jasmine.createSpyObj('$log', ['debug']); |
18 | }); | 18 | }); |
19 | 19 |
src/app/login/session.service.ts
@@ -16,6 +16,7 @@ export class SessionService { | @@ -16,6 +16,7 @@ export class SessionService { | ||
16 | 16 | ||
17 | destroy() { | 17 | destroy() { |
18 | delete this.$localStorage.currentUser; | 18 | delete this.$localStorage.currentUser; |
19 | + delete this.$localStorage.settings; | ||
19 | }; | 20 | }; |
20 | 21 | ||
21 | currentUser(): noosfero.User { | 22 | currentUser(): noosfero.User { |
src/app/main/main.component.ts
@@ -18,6 +18,7 @@ import {ProfileImageBlockComponent} from "../layout/blocks/profile-image/profile | @@ -18,6 +18,7 @@ import {ProfileImageBlockComponent} from "../layout/blocks/profile-image/profile | ||
18 | import {RawHTMLBlockComponent} from "../layout/blocks/raw-html/raw-html-block.component"; | 18 | import {RawHTMLBlockComponent} from "../layout/blocks/raw-html/raw-html-block.component"; |
19 | import {StatisticsBlockComponent} from "../layout/blocks/statistics/statistics-block.component"; | 19 | import {StatisticsBlockComponent} from "../layout/blocks/statistics/statistics-block.component"; |
20 | import {PersonTagsPluginInterestsBlockComponent} from "../layout/blocks/person-tags-plugin-interests/person-tags-plugin-interests-block.component"; | 20 | import {PersonTagsPluginInterestsBlockComponent} from "../layout/blocks/person-tags-plugin-interests/person-tags-plugin-interests-block.component"; |
21 | +import {TagsBlockComponent} from "../layout/blocks/tags/tags-block.component"; | ||
21 | import {CustomContentComponent} from "../profile/custom-content/custom-content.component"; | 22 | import {CustomContentComponent} from "../profile/custom-content/custom-content.component"; |
22 | 23 | ||
23 | import {MembersBlockComponent} from "../layout/blocks/members/members-block.component"; | 24 | import {MembersBlockComponent} from "../layout/blocks/members/members-block.component"; |
@@ -105,7 +106,7 @@ export class EnvironmentContent { | @@ -105,7 +106,7 @@ export class EnvironmentContent { | ||
105 | MainBlockComponent, RecentDocumentsBlockComponent, Navbar, SidebarComponent, ProfileImageBlockComponent, | 106 | MainBlockComponent, RecentDocumentsBlockComponent, Navbar, SidebarComponent, ProfileImageBlockComponent, |
106 | MembersBlockComponent, NoosferoTemplate, DateFormat, RawHTMLBlockComponent, StatisticsBlockComponent, | 107 | MembersBlockComponent, NoosferoTemplate, DateFormat, RawHTMLBlockComponent, StatisticsBlockComponent, |
107 | LoginBlockComponent, CustomContentComponent, PermissionDirective, SearchFormComponent, SearchComponent, | 108 | LoginBlockComponent, CustomContentComponent, PermissionDirective, SearchFormComponent, SearchComponent, |
108 | - PersonTagsPluginInterestsBlockComponent, BlockComponent | 109 | + PersonTagsPluginInterestsBlockComponent, TagsBlockComponent, BlockComponent |
109 | ].concat(plugins.mainComponents).concat(plugins.hotspots), | 110 | ].concat(plugins.mainComponents).concat(plugins.hotspots), |
110 | providers: [AuthService, SessionService, NotificationService, BodyStateClassesService, | 111 | providers: [AuthService, SessionService, NotificationService, BodyStateClassesService, |
111 | "ngAnimate", "ngCookies", "ngStorage", "ngTouch", | 112 | "ngAnimate", "ngCookies", "ngStorage", "ngTouch", |
@@ -114,7 +115,7 @@ export class EnvironmentContent { | @@ -114,7 +115,7 @@ export class EnvironmentContent { | ||
114 | "angular-bind-html-compile", "angularMoment", "angular.filter", "akoenig.deckgrid", | 115 | "angular-bind-html-compile", "angularMoment", "angular.filter", "akoenig.deckgrid", |
115 | "angular-timeline", "duScroll", "oitozero.ngSweetAlert", | 116 | "angular-timeline", "duScroll", "oitozero.ngSweetAlert", |
116 | "pascalprecht.translate", "tmh.dynamicLocale", "angularLoad", | 117 | "pascalprecht.translate", "tmh.dynamicLocale", "angularLoad", |
117 | - "angular-click-outside", "toggle-switch", "noosfero.init"] | 118 | + "angular-click-outside", "toggle-switch", "ngTagCloud", "noosfero.init"] |
118 | }) | 119 | }) |
119 | @StateConfig([ | 120 | @StateConfig([ |
120 | { | 121 | { |
src/app/profile/custom-content/custom-content.component.spec.ts
1 | import {CustomContentComponent} from './custom-content.component'; | 1 | import {CustomContentComponent} from './custom-content.component'; |
2 | import {ComponentTestHelper, createClass} from '../../../spec/component-test-helper'; | 2 | import {ComponentTestHelper, createClass} from '../../../spec/component-test-helper'; |
3 | import * as helpers from "../../../spec/helpers"; | 3 | import * as helpers from "../../../spec/helpers"; |
4 | +import {DesignModeService} from '../../admin/layout-edit/designMode.service'; | ||
4 | 5 | ||
5 | const htmlTemplate: string = '<custom-content [attribute]="\'custom_footer\'" [profile]="ctrl.profile"></custom-content>'; | 6 | const htmlTemplate: string = '<custom-content [attribute]="\'custom_footer\'" [profile]="ctrl.profile"></custom-content>'; |
6 | 7 | ||
@@ -14,6 +15,7 @@ describe("Components", () => { | @@ -14,6 +15,7 @@ describe("Components", () => { | ||
14 | beforeEach((done) => { | 15 | beforeEach((done) => { |
15 | let profileService = jasmine.createSpyObj("profileService", ["update"]); | 16 | let profileService = jasmine.createSpyObj("profileService", ["update"]); |
16 | let notificationService = jasmine.createSpyObj("notificationService", ["success"]); | 17 | let notificationService = jasmine.createSpyObj("notificationService", ["success"]); |
18 | + let designModeService = { isInDesignMode: () => { return true; }}; | ||
17 | let properties = { profile: { custom_footer: "footer" } }; | 19 | let properties = { profile: { custom_footer: "footer" } }; |
18 | let cls = createClass({ | 20 | let cls = createClass({ |
19 | template: htmlTemplate, | 21 | template: htmlTemplate, |
@@ -22,7 +24,8 @@ describe("Components", () => { | @@ -22,7 +24,8 @@ describe("Components", () => { | ||
22 | providers: [ | 24 | providers: [ |
23 | helpers.createProviderToValue("$uibModal", helpers.mocks.$modal), | 25 | helpers.createProviderToValue("$uibModal", helpers.mocks.$modal), |
24 | helpers.createProviderToValue("ProfileService", profileService), | 26 | helpers.createProviderToValue("ProfileService", profileService), |
25 | - helpers.createProviderToValue("NotificationService", notificationService) | 27 | + helpers.createProviderToValue("NotificationService", notificationService), |
28 | + helpers.createProviderToValue("DesignModeService", designModeService) | ||
26 | ] | 29 | ] |
27 | }); | 30 | }); |
28 | helper = new ComponentTestHelper<CustomContentComponent>(cls, done); | 31 | helper = new ComponentTestHelper<CustomContentComponent>(cls, done); |
src/app/profile/custom-content/custom-content.component.ts
@@ -12,7 +12,8 @@ import {DesignModeService} from '../../admin/layout-edit/designMode.service'; | @@ -12,7 +12,8 @@ import {DesignModeService} from '../../admin/layout-edit/designMode.service'; | ||
12 | @Inject("$uibModal", "$scope", ProfileService, NotificationService, DesignModeService) | 12 | @Inject("$uibModal", "$scope", ProfileService, NotificationService, DesignModeService) |
13 | export class CustomContentComponent { | 13 | export class CustomContentComponent { |
14 | 14 | ||
15 | - static $inject = ["DesignModeService"]; // @Inject doesn't works with uibModal.open | 15 | + // @Inject doesn't works with uibModal.open |
16 | + static $inject = ["DesignModeService"]; | ||
16 | 17 | ||
17 | @Input() attribute: string; | 18 | @Input() attribute: string; |
18 | @Input() profile: noosfero.Profile; | 19 | @Input() profile: noosfero.Profile; |
@@ -20,7 +21,6 @@ export class CustomContentComponent { | @@ -20,7 +21,6 @@ export class CustomContentComponent { | ||
20 | 21 | ||
21 | content: string; | 22 | content: string; |
22 | originalContent: string; | 23 | originalContent: string; |
23 | - editionMode = false; | ||
24 | private modalInstance: any = null; | 24 | private modalInstance: any = null; |
25 | 25 | ||
26 | constructor(private $uibModal: any, | 26 | constructor(private $uibModal: any, |
@@ -35,9 +35,10 @@ export class CustomContentComponent { | @@ -35,9 +35,10 @@ export class CustomContentComponent { | ||
35 | }, () => { | 35 | }, () => { |
36 | if (this.profile) this.content = (<any>this.profile)[this.attribute]; | 36 | if (this.profile) this.content = (<any>this.profile)[this.attribute]; |
37 | }); | 37 | }); |
38 | - this.designModeService.onToggle.subscribe((designModeOn: boolean) => { | ||
39 | - this.editionMode = designModeOn; | ||
40 | - }); | 38 | + } |
39 | + | ||
40 | + inEditMode() { | ||
41 | + return this.designModeService.isInDesignMode(); | ||
41 | } | 42 | } |
42 | 43 | ||
43 | openEdit() { | 44 | openEdit() { |
src/app/profile/custom-content/custom-content.html
1 | <div class="custom-content"> | 1 | <div class="custom-content"> |
2 | - <div class="actions" permission="ctrl.profile.permissions" permission-action="allow_edit" ng-show="ctrl.editionMode"> | 2 | + <div class="actions" permission="ctrl.profile.permissions" permission-action="allow_edit" ng-show="ctrl.inEditMode()"> |
3 | <button type="submit" class="btn btn-xs btn-default" ng-click="ctrl.openEdit()"><i class="fa fa-edit fa-fw"></i> {{ctrl.label | translate}}</button> | 3 | <button type="submit" class="btn btn-xs btn-default" ng-click="ctrl.openEdit()"><i class="fa fa-edit fa-fw"></i> {{ctrl.label | translate}}</button> |
4 | </div> | 4 | </div> |
5 | <div class="content" ng-bind-html="ctrl.content"></div> | 5 | <div class="content" ng-bind-html="ctrl.content"></div> |
src/app/profile/profile.html
1 | <div class="profile-container"> | 1 | <div class="profile-container"> |
2 | - <custom-content class="profile-header" [label]="'profile.custom_header.label'" [attribute]="'custom_header'" [profile]="vm.profile"></custom-content> | ||
3 | - <div class="row"> | ||
4 | - <noosfero-boxes ng-if="vm.boxes" [boxes]="vm.boxes" [owner]="vm.profile"></noosfero-boxes> | ||
5 | - </div> | 2 | + <custom-content class="profile-header" |
3 | + [label]="'profile.custom_header.label'" | ||
4 | + [attribute]="'custom_header'" | ||
5 | + [profile]="vm.profile"> | ||
6 | + </custom-content> | ||
7 | + <div class="row" ui-view="profile-info"></div> | ||
8 | + <noosfero-boxes ng-if="vm.boxes" | ||
9 | + [layout]="vm.profile.layout_template" | ||
10 | + [boxes]="vm.boxes" | ||
11 | + [owner]="vm.profile" class="row"> | ||
12 | + </noosfero-boxes> | ||
6 | <custom-content class="profile-footer" [label]="'profile.custom_footer.label'" [attribute]="'custom_footer'" [profile]="vm.profile"></custom-content> | 13 | <custom-content class="profile-footer" [label]="'profile.custom_footer.label'" [attribute]="'custom_footer'" [profile]="vm.profile"></custom-content> |
7 | </div> | 14 | </div> |
src/app/search/search-form/search-form.component.spec.ts
@@ -21,8 +21,8 @@ describe("Components", () => { | @@ -21,8 +21,8 @@ describe("Components", () => { | ||
21 | helper = new ComponentTestHelper<SearchFormComponent>(cls, done); | 21 | helper = new ComponentTestHelper<SearchFormComponent>(cls, done); |
22 | }); | 22 | }); |
23 | 23 | ||
24 | - it("render a input for search query", () => { | ||
25 | - expect(helper.find(".search-input").length).toEqual(1); | 24 | + it("render a button that open a search query field", () => { |
25 | + expect(helper.find(".btn-search-nav").length).toEqual(1); | ||
26 | }); | 26 | }); |
27 | 27 | ||
28 | it("go to search page when click on search button", () => { | 28 | it("go to search page when click on search button", () => { |
src/app/search/search-form/search-form.html
1 | -<form class="navbar-form search-form" role="search" ng-if="!ctrl.isSearchPage()"> | ||
2 | - <div class="input-group"> | ||
3 | - <input type="text" class="search-input form-control" placeholder="Search" name="q" ng-model="ctrl.query"> | ||
4 | - <div class="input-group-btn"> | ||
5 | - <button class="btn btn-default" type="submit" (click)="ctrl.search()"><i class="fa fa-search fa-fw"></i></button> | 1 | +<a class="btn btn-nav" ng-click="showSearch = !showSearch"> |
2 | + <i class="fa fa-search btn-search-nav" aria-hidden="true"></i> | ||
3 | +</a> | ||
4 | +<form class="navbar-form search-form" role="search"> | ||
5 | + <div class="ng-scope" ng-class="{'top-search-wrap': !showSearch, 'top-search-toggled': showSearch}"> | ||
6 | + <div class="tsw-inner"> | ||
7 | + <i id="top-search-close" class="fa fa-chevron-left" aria-hidden="true" ng-click="showSearch = false"></i> | ||
8 | + <input type="text" placeholder="{{ 'search.label' | translate }}" name="q" ng-model="ctrl.query"> | ||
9 | + <i class="fa fa-search btn-search" aria-hidden="true" (click)="ctrl.search()"></i> | ||
10 | + <button type="submit" (click)="ctrl.search()"></i> | ||
6 | </div> | 11 | </div> |
7 | </div> | 12 | </div> |
8 | </form> | 13 | </form> |
@@ -0,0 +1,107 @@ | @@ -0,0 +1,107 @@ | ||
1 | +/** | ||
2 | +* TODO: Remove the temporary reimport mixins | ||
3 | +* Maybe the cause for this is in gulp/styles.js | ||
4 | +* task on compile sass order. | ||
5 | +*/ | ||
6 | +@import "../../layout/scss/mixins"; | ||
7 | + | ||
8 | +@mixin search-wrap-btn { | ||
9 | + @include border-radius(2px 0px 0px 2px); | ||
10 | + @content; | ||
11 | + font-size: 23px; | ||
12 | + font-style: normal; | ||
13 | + width: 45px; | ||
14 | + text-align: center; | ||
15 | + cursor: pointer; | ||
16 | + height: 40px; | ||
17 | + padding-top: 9px; | ||
18 | + | ||
19 | + background-color: #e3e3e3; | ||
20 | + &:hover { | ||
21 | + background-color: #D1D1D1; | ||
22 | + } | ||
23 | + | ||
24 | + @include outline(none); | ||
25 | +} | ||
26 | + | ||
27 | +.top-search-wrap { | ||
28 | + @include opacity(0); | ||
29 | + @include transition(all); | ||
30 | + | ||
31 | + position: absolute; | ||
32 | + top: -65px; | ||
33 | + left: 0; | ||
34 | + width: 100%; | ||
35 | + height: 70px; | ||
36 | + background: #fff; | ||
37 | + z-index: 10; | ||
38 | + | ||
39 | + .tsw-inner { | ||
40 | + position: relative; | ||
41 | + padding: 15px; | ||
42 | + max-width: 700px; | ||
43 | + display: block; | ||
44 | + margin: 0 auto; | ||
45 | + } | ||
46 | + | ||
47 | + #top-search-close { | ||
48 | + @include border-radius(2px 0px 0px 2px); | ||
49 | + @include search-wrap-btn { | ||
50 | + position: absolute; | ||
51 | + top: 15px; | ||
52 | + left: 15px; | ||
53 | + } | ||
54 | + | ||
55 | + @include outline(none); | ||
56 | + } | ||
57 | + | ||
58 | + .btn-search { | ||
59 | + display: inline; | ||
60 | + float: right; | ||
61 | + | ||
62 | + @include search-wrap-btn { | ||
63 | + position: relative; | ||
64 | + top: -40px; | ||
65 | + left: 0px; | ||
66 | + } | ||
67 | + | ||
68 | + } | ||
69 | + | ||
70 | + input[type="text"] { | ||
71 | + @include border-radius(2px); | ||
72 | + border: 0; | ||
73 | + height: 40px; | ||
74 | + padding: 0 10px 0 55px; | ||
75 | + font-size: 18px; | ||
76 | + width: 500px; | ||
77 | + background-color: #efefef; | ||
78 | + width: 100%; | ||
79 | + | ||
80 | + @include outline(none); | ||
81 | + } | ||
82 | + | ||
83 | +} | ||
84 | + | ||
85 | +.top-search-toggled { | ||
86 | + @extend .top-search-wrap; | ||
87 | + @include opacity(1); | ||
88 | + position: fixed; | ||
89 | + top: 0; | ||
90 | + box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.3); | ||
91 | +} | ||
92 | + | ||
93 | +.btn-nav { | ||
94 | + .btn-search-nav { | ||
95 | + font-size: 25px; | ||
96 | + color: #FFF; | ||
97 | + cursor: pointer; | ||
98 | + padding: 1px; | ||
99 | + } | ||
100 | +} | ||
101 | + | ||
102 | + | ||
103 | +.search-form { | ||
104 | + button[type="submit"] { | ||
105 | + visibility: hidden; | ||
106 | + } | ||
107 | +} |
src/app/search/search.component.ts
@@ -23,7 +23,7 @@ export class SearchComponent { | @@ -23,7 +23,7 @@ export class SearchComponent { | ||
23 | } | 23 | } |
24 | 24 | ||
25 | search() { | 25 | search() { |
26 | - this.$state.go('main.environment.search', { query: this.query }); | 26 | + this.$state.go('main.environment.search', { query: this.query }); |
27 | } | 27 | } |
28 | 28 | ||
29 | loadPage() { | 29 | loadPage() { |
src/app/search/search.html
1 | <form ng-submit="ctrl.search()"> | 1 | <form ng-submit="ctrl.search()"> |
2 | <label for="query" ng-bind-html="'search.results.query.label' | translate"></label> | 2 | <label for="query" ng-bind-html="'search.results.query.label' | translate"></label> |
3 | -<input id="query" placeholder="{{'search.results.query.placeholder' | translate}}" type="search" class="search-box-title" ng-model="ctrl.query"> | 3 | +<h3 id="query" class="search-box-title">{{ctrl.query}}</h3> |
4 | </form> | 4 | </form> |
5 | <div class="search-results"> | 5 | <div class="search-results"> |
6 | <div class="summary"> | 6 | <div class="summary"> |
src/app/search/search.scss
@@ -2,7 +2,6 @@ | @@ -2,7 +2,6 @@ | ||
2 | .summary { | 2 | .summary { |
3 | color: #bbbbbb; | 3 | color: #bbbbbb; |
4 | font-size: 13px; | 4 | font-size: 13px; |
5 | - border-top: 1px solid #ececec; | ||
6 | } | 5 | } |
7 | .result { | 6 | .result { |
8 | margin: 25px 0; | 7 | margin: 25px 0; |
@@ -45,4 +44,4 @@ | @@ -45,4 +44,4 @@ | ||
45 | 44 | ||
46 | .search-box-title:focus { | 45 | .search-box-title:focus { |
47 | outline: none; | 46 | outline: none; |
48 | -} | ||
49 | \ No newline at end of file | 47 | \ No newline at end of file |
48 | +} |
src/app/shared/models/interfaces.ts
@@ -8,4 +8,5 @@ export interface UserResponse { | @@ -8,4 +8,5 @@ export interface UserResponse { | ||
8 | 8 | ||
9 | export interface INoosferoLocalStorage extends angular.storage.ILocalStorageService { | 9 | export interface INoosferoLocalStorage extends angular.storage.ILocalStorageService { |
10 | currentUser: noosfero.User; | 10 | currentUser: noosfero.User; |
11 | + settings: any; | ||
11 | } | 12 | } |
src/languages/en.json
@@ -21,9 +21,10 @@ | @@ -21,9 +21,10 @@ | ||
21 | "activities.create_article.description": "has published on", | 21 | "activities.create_article.description": "has published on", |
22 | "activities.add_member_in_community.description": "has joined the community", | 22 | "activities.add_member_in_community.description": "has joined the community", |
23 | "activities.new_friendship.description": "has made {friends, plural, one{one new friend} other{# new friends}}:", | 23 | "activities.new_friendship.description": "has made {friends, plural, one{one new friend} other{# new friends}}:", |
24 | - "auth.title": "Login", | ||
25 | - "auth.form.login": "Login / Email address", | 24 | + "auth.title": "Great to have you back!", |
25 | + "auth.form.login": "Username or Email address", | ||
26 | "auth.form.password": "Password", | 26 | "auth.form.password": "Password", |
27 | + "auth.form.keepLoggedIn": "Keep me logged in", | ||
27 | "auth.form.login_button": "Login", | 28 | "auth.form.login_button": "Login", |
28 | "navbar.content_viewer_actions.new_item": "New Item", | 29 | "navbar.content_viewer_actions.new_item": "New Item", |
29 | "navbar.profile_actions.new_item": "New Item", | 30 | "navbar.profile_actions.new_item": "New Item", |
@@ -80,8 +81,8 @@ | @@ -80,8 +81,8 @@ | ||
80 | "designMode.toggle.ON": "ON", | 81 | "designMode.toggle.ON": "ON", |
81 | "designMode.toggle.OFF": "OFF", | 82 | "designMode.toggle.OFF": "OFF", |
82 | "search.results.summary": "{results, plural, one{result} other{# results}}", | 83 | "search.results.summary": "{results, plural, one{result} other{# results}}", |
83 | - "search.results.query.label": "Search for:", | ||
84 | - "search.results.query.placeholder": "Search", | 84 | + "search.results.query.label": "Search therm:", |
85 | + "search.label": "Search", | ||
85 | "block.edit": "Edit", | 86 | "block.edit": "Edit", |
86 | "block.edition.title": "Edit Block", | 87 | "block.edition.title": "Edit Block", |
87 | "block.edition.success.title": "Good job!", | 88 | "block.edition.success.title": "Good job!", |
src/languages/pt.json
@@ -21,9 +21,10 @@ | @@ -21,9 +21,10 @@ | ||
21 | "activities.create_article.description": "publicou em", | 21 | "activities.create_article.description": "publicou em", |
22 | "activities.add_member_in_community.description": "entrou na comunidade", | 22 | "activities.add_member_in_community.description": "entrou na comunidade", |
23 | "activities.new_friendship.description": "fez {friends, plural, one{um novo amigo} other{# novos amigos}}:", | 23 | "activities.new_friendship.description": "fez {friends, plural, one{um novo amigo} other{# novos amigos}}:", |
24 | - "auth.title": "Login", | ||
25 | - "auth.form.login": "Login / Email", | 24 | + "auth.title": "Legal ter você de volta!", |
25 | + "auth.form.login": "Nome de usuário ou Email", | ||
26 | "auth.form.password": "Senha", | 26 | "auth.form.password": "Senha", |
27 | + "auth.form.keepLoggedIn": "Continuar logado", | ||
27 | "auth.form.login_button": "Login", | 28 | "auth.form.login_button": "Login", |
28 | "navbar.content_viewer_actions.new_item": "Novo Item", | 29 | "navbar.content_viewer_actions.new_item": "Novo Item", |
29 | "navbar.profile_actions.new_item": "Novo Item", | 30 | "navbar.profile_actions.new_item": "Novo Item", |
@@ -80,8 +81,8 @@ | @@ -80,8 +81,8 @@ | ||
80 | "designMode.toggle.ON": "Ligado", | 81 | "designMode.toggle.ON": "Ligado", |
81 | "designMode.toggle.OFF": "Desligado", | 82 | "designMode.toggle.OFF": "Desligado", |
82 | "search.results.summary": "{results, plural, one{# resultado} other{# resultados}}", | 83 | "search.results.summary": "{results, plural, one{# resultado} other{# resultados}}", |
83 | - "search.results.query.label": "Buscar:", | ||
84 | - "search.results.query.placeholder": "Informe aqui sua busca", | 84 | + "search.results.query.label": "Termo da busca:", |
85 | + "search.label": "Pesquisar", | ||
85 | "block.edit": "Editar", | 86 | "block.edit": "Editar", |
86 | "block.edition.title": "Editar Bloco", | 87 | "block.edition.title": "Editar Bloco", |
87 | "block.edition.success.title": "Bom trabalho!", | 88 | "block.edition.success.title": "Bom trabalho!", |
src/lib/ng-noosfero-api/http/environment.service.ts
@@ -68,6 +68,14 @@ export class EnvironmentService { | @@ -68,6 +68,14 @@ export class EnvironmentService { | ||
68 | return errorFunction; | 68 | return errorFunction; |
69 | } | 69 | } |
70 | 70 | ||
71 | + getTags(): ng.IPromise<{}> { | ||
72 | + let p = this.restangular.one('environment').customGET('tags'); | ||
73 | + let deferred = this.$q.defer<{}>(); | ||
74 | + p.then(this.getHandleSuccessFunction<{}>(deferred)); | ||
75 | + p.catch(this.getHandleErrorFunction<{}>(deferred)); | ||
76 | + return deferred.promise; | ||
77 | + } | ||
78 | + | ||
71 | /** | 79 | /** |
72 | * TODO - use restangular service as base class, and this will not be necessary here anymore | 80 | * TODO - use restangular service as base class, and this will not be necessary here anymore |
73 | */ | 81 | */ |
src/lib/ng-noosfero-api/interfaces/environment.ts
@@ -15,5 +15,13 @@ namespace noosfero { | @@ -15,5 +15,13 @@ namespace noosfero { | ||
15 | */ | 15 | */ |
16 | id: number; | 16 | id: number; |
17 | settings: any | 17 | settings: any |
18 | + | ||
19 | + /** | ||
20 | + * @ngdoc property | ||
21 | + * @name layout_template | ||
22 | + * @propertyOf noofero.Environment | ||
23 | + * @returns {string} The Environment layout (e.g. default, rightbar) | ||
24 | + */ | ||
25 | + layout_template: string; | ||
18 | } | 26 | } |
19 | -} | ||
20 | \ No newline at end of file | 27 | \ No newline at end of file |
28 | +} |
src/lib/ng-noosfero-api/interfaces/profile.ts
@@ -80,5 +80,13 @@ namespace noosfero { | @@ -80,5 +80,13 @@ namespace noosfero { | ||
80 | custom_footer: string; | 80 | custom_footer: string; |
81 | 81 | ||
82 | permissions: string[]; | 82 | permissions: string[]; |
83 | + | ||
84 | + /** | ||
85 | + * @ngdoc property | ||
86 | + * @name layout_template | ||
87 | + * @propertyOf noofero.Profile | ||
88 | + * @returns {string} The Profile layout template (e.g.: "rightbar", "default") | ||
89 | + */ | ||
90 | + layout_template: string; | ||
83 | } | 91 | } |
84 | } | 92 | } |
src/plugins/comment_paragraph/hotspot/export-comment-button.scss
0 → 100644
src/spec/mocks.ts
@@ -193,6 +193,17 @@ export var mocks: any = { | @@ -193,6 +193,17 @@ export var mocks: any = { | ||
193 | currentUser: () => { return user; } | 193 | currentUser: () => { return user; } |
194 | }; | 194 | }; |
195 | }, | 195 | }, |
196 | + designModeService: { | ||
197 | + modeFn: null, | ||
198 | + onToggle: { | ||
199 | + subscribe: (fn: Function) => { | ||
200 | + mocks.designModeService.modeFn = fn; | ||
201 | + }, | ||
202 | + next: (param: any) => { | ||
203 | + mocks.designModeService.modeFn(param); | ||
204 | + } | ||
205 | + } | ||
206 | + }, | ||
196 | $translate: { | 207 | $translate: { |
197 | use: (lang?: string) => { | 208 | use: (lang?: string) => { |
198 | return lang ? Promise.resolve(lang) : "en"; | 209 | return lang ? Promise.resolve(lang) : "en"; |
themes/angular-participa-consulta/app/layout/scss/skins/_yellow.scss
0 → 100644
@@ -0,0 +1,64 @@ | @@ -0,0 +1,64 @@ | ||
1 | +.skin-yellow { | ||
2 | + @extend %skin-base; | ||
3 | + | ||
4 | + .notifications-list .item-footer { | ||
5 | + background: #DAE1C4; | ||
6 | + color: #4F9CAC; | ||
7 | + } | ||
8 | + | ||
9 | + .navbar-nav .open > a, | ||
10 | + .navbar-nav .open > a:hover, | ||
11 | + .navbar-nav .open > a:focus { | ||
12 | + background-color: $selected-color; | ||
13 | + color: #000; | ||
14 | + } | ||
15 | + | ||
16 | + .nav .open > a, | ||
17 | + .nav .open > a:hover, | ||
18 | + .nav .open > a:focus { | ||
19 | + border: none; | ||
20 | + } | ||
21 | + | ||
22 | + .dropdown-menu { | ||
23 | + li > a:hover { | ||
24 | + color: #555; | ||
25 | + background-color: #F7F7F7; | ||
26 | + } | ||
27 | + | ||
28 | + .active > a, | ||
29 | + .active > a:hover, | ||
30 | + .active > a:focus { | ||
31 | + color: #000; | ||
32 | + background-color: #CCC; | ||
33 | + } | ||
34 | + } | ||
35 | + | ||
36 | + .nav .caret { | ||
37 | + border-bottom-color: #fff !important; | ||
38 | + border-top-color: #fff !important; | ||
39 | + } | ||
40 | + | ||
41 | + .nav .open .caret { | ||
42 | + border-bottom-color: #000 !important; | ||
43 | + border-top-color: #000 !important; | ||
44 | + } | ||
45 | + | ||
46 | + .navbar-inverse .navbar-toggle { | ||
47 | + border-color: #D49F18; | ||
48 | + } | ||
49 | + | ||
50 | + .container-fluid .navbar-header .navbar-toggle { | ||
51 | + &:hover, &:focus { | ||
52 | + background-color: #f5b025; | ||
53 | + } | ||
54 | + } | ||
55 | + | ||
56 | + .navbar { | ||
57 | + .navbar-nav .btn-nav { | ||
58 | + &:hover { | ||
59 | + background-color: $selected-color; | ||
60 | + } | ||
61 | + } | ||
62 | + } | ||
63 | + | ||
64 | +} |
themes/angular-participa-consulta/app/navbar.scss
1 | -.skin-yellow { | ||
2 | - .navbar-inverse .navbar-toggle { | ||
3 | - border-color: #D49F18; | ||
4 | - } | ||
5 | - | ||
6 | - .container-fluid .navbar-header .navbar-toggle { | ||
7 | - &:hover, &:focus { | ||
8 | - background-color: #f5b025; | ||
9 | - } | ||
10 | - } | ||
11 | -} | ||
12 | - | ||
13 | .navbar { | 1 | .navbar { |
14 | min-height: 123px; | 2 | min-height: 123px; |
15 | background-color: #f9c404; | 3 | background-color: #f9c404; |
themes/angular-participa-consulta/app/participa-consulta.scss
@@ -21,53 +21,6 @@ $selected-color: #f6c445; | @@ -21,53 +21,6 @@ $selected-color: #f6c445; | ||
21 | src: url('../assets/fonts/participa-consulta/Ubuntu-RI.ttf'); | 21 | src: url('../assets/fonts/participa-consulta/Ubuntu-RI.ttf'); |
22 | } | 22 | } |
23 | 23 | ||
24 | -.skin-yellow { | ||
25 | - @extend %skin-base; | ||
26 | - | ||
27 | - .notifications-list .item-footer { | ||
28 | - background: #DAE1C4; | ||
29 | - color: #4F9CAC; | ||
30 | - } | ||
31 | - | ||
32 | - .navbar-nav .open > a, | ||
33 | - .navbar-nav .open > a:hover, | ||
34 | - .navbar-nav .open > a:focus { | ||
35 | - background-color: $selected-color; | ||
36 | - color: #000; | ||
37 | - } | ||
38 | - | ||
39 | - .nav .open > a, | ||
40 | - .nav .open > a:hover, | ||
41 | - .nav .open > a:focus { | ||
42 | - border: none; | ||
43 | - } | ||
44 | - | ||
45 | - .dropdown-menu { | ||
46 | - li > a:hover { | ||
47 | - color: #555; | ||
48 | - background-color: #F7F7F7; | ||
49 | - } | ||
50 | - | ||
51 | - .active > a, | ||
52 | - .active > a:hover, | ||
53 | - .active > a:focus { | ||
54 | - color: #000; | ||
55 | - background-color: #CCC; | ||
56 | - } | ||
57 | - } | ||
58 | - | ||
59 | - .nav .caret { | ||
60 | - border-bottom-color: #fff !important; | ||
61 | - border-top-color: #fff !important; | ||
62 | - } | ||
63 | - | ||
64 | - .nav .open .caret { | ||
65 | - border-bottom-color: #000 !important; | ||
66 | - border-top-color: #000 !important; | ||
67 | - } | ||
68 | - | ||
69 | -} | ||
70 | - | ||
71 | .profile-header, .profile-footer{ | 24 | .profile-header, .profile-footer{ |
72 | text-align: center; | 25 | text-align: center; |
73 | } | 26 | } |