Moved the navigation.model.ts into its own folder

+ Added support for translations in nav items
+ Added badge support for collapsable nav items
+ Initialize the navigation from app.component rather then navigation.service
This commit is contained in:
Sercan Yemen 2017-12-06 14:10:48 +03:00
parent db7a00440c
commit 2a10f3e443
14 changed files with 195 additions and 90 deletions

View File

@ -1,6 +1,12 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { FuseSplashScreenService } from './core/services/splash-screen.service'; import { FuseSplashScreenService } from './core/services/splash-screen.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { FuseTranslationLoaderService } from './core/services/translation-loader.service';
import { FuseNavigationService } from './core/components/navigation/navigation.service';
import { FuseNavigationModel } from './navigation/navigation.model';
import { locale as navigationEnglish } from './navigation/i18n/en';
import { locale as navigationTurkish } from './navigation/i18n/tr';
@Component({ @Component({
selector : 'fuse-root', selector : 'fuse-root',
@ -10,8 +16,10 @@ import { TranslateService } from '@ngx-translate/core';
export class AppComponent export class AppComponent
{ {
constructor( constructor(
private fuseNavigationService: FuseNavigationService,
private fuseSplashScreen: FuseSplashScreenService, private fuseSplashScreen: FuseSplashScreenService,
private translate: TranslateService private translate: TranslateService,
private translationLoader: FuseTranslationLoaderService
) )
{ {
// Add languages // Add languages
@ -22,5 +30,11 @@ export class AppComponent
// Use a language // Use a language
this.translate.use('en'); this.translate.use('en');
// Set the navigation model
this.fuseNavigationService.setNavigationModel(new FuseNavigationModel());
// Set the navigation translations
this.translationLoader.loadTranslations(navigationEnglish, navigationTurkish);
} }
} }

View File

@ -73,16 +73,12 @@ const appRoutes: Routes = [
SharedModule, SharedModule,
MarkdownModule.forRoot(), MarkdownModule.forRoot(),
TranslateModule.forRoot(), TranslateModule.forRoot(),
InMemoryWebApiModule.forRoot(FuseFakeDbService, { InMemoryWebApiModule.forRoot(FuseFakeDbService, {
delay : 0, delay : 0,
passThruUnknownUrl: true passThruUnknownUrl: true
}), }),
FuseMainModule, FuseMainModule,
ProjectModule, ProjectModule,
PagesModule, PagesModule,
UIModule, UIModule,
ServicesModule, ServicesModule,

View File

@ -1,6 +1,10 @@
<a class="nav-link" matRipple> <a class="nav-link" matRipple>
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon> <mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
<span class="nav-link-title">{{item.title}}</span> <span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
<span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
{{item.badge.title}}
</span>
<mat-icon class="collapse-arrow">keyboard_arrow_right</mat-icon> <mat-icon class="collapse-arrow">keyboard_arrow_right</mat-icon>
</a> </a>

View File

@ -1,8 +1,8 @@
<a class="nav-link" *ngIf="item.url" [routerLink]="[item.url]" routerLinkActive="active" <a class="nav-link" *ngIf="item.url" [routerLink]="[item.url]" routerLinkActive="active"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}" matRipple> [routerLinkActiveOptions]="{exact: item.exactMatch || false}" matRipple>
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon> <mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
<span class="nav-link-title">{{item.title}}</span> <span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
<span class="nav-link-badge" *ngIf="item.badge" <span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}"> [ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
{{item.badge.title}} {{item.badge.title}}
</span> </span>
@ -10,8 +10,8 @@
<span class="nav-link" *ngIf="item.function" (click)="item.function()" matRipple> <span class="nav-link" *ngIf="item.function" (click)="item.function()" matRipple>
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon> <mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
<span class="nav-link-title">{{item.title}}</span> <span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
<span class="nav-link-badge" *ngIf="item.badge" <span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}"> [ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
{{item.badge.title}} {{item.badge.title}}
</span> </span>

View File

@ -0,0 +1,5 @@
export interface FuseNavigationModelInterface
{
model: any[];
}

View File

@ -1,6 +1,6 @@
import { EventEmitter, Injectable } from '@angular/core'; import { EventEmitter, Injectable } from '@angular/core';
import { NavigationModel } from '../../../navigation.model';
import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { FuseNavigationModelInterface } from './navigation.model';
@Injectable() @Injectable()
export class FuseNavigationService export class FuseNavigationService
@ -8,13 +8,11 @@ export class FuseNavigationService
onNavCollapseToggle = new EventEmitter<any>(); onNavCollapseToggle = new EventEmitter<any>();
onNavCollapseToggled = new EventEmitter<any>(); onNavCollapseToggled = new EventEmitter<any>();
onNavigationModelChange: BehaviorSubject<any> = new BehaviorSubject({}); onNavigationModelChange: BehaviorSubject<any> = new BehaviorSubject({});
navigationModel: NavigationModel; navigationModel: FuseNavigationModelInterface;
flatNavigation: any[] = []; flatNavigation: any[] = [];
constructor() constructor()
{ {
this.navigationModel = new NavigationModel();
this.onNavigationModelChange.next(this.navigationModel.model);
} }
/** /**

View File

@ -1,8 +1,13 @@
<a class="nav-link" matRipple (click)="toggleOpen($event)"> <a class="nav-link" matRipple (click)="toggleOpen($event)">
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon> <mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
<span class="nav-link-title">{{item.title}}</span> <span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
<span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
{{item.badge.title}}
</span>
<mat-icon class="collapse-arrow">keyboard_arrow_right</mat-icon> <mat-icon class="collapse-arrow">keyboard_arrow_right</mat-icon>
</a> </a>
<div class="children" [@slideInOut]="isOpen"> <div class="children" [@slideInOut]="isOpen">
<ng-container *ngFor="let item of item.children"> <ng-container *ngFor="let item of item.children">
<fuse-nav-vertical-item *ngIf="item.type=='item'" [item]="item"></fuse-nav-vertical-item> <fuse-nav-vertical-item *ngIf="item.type=='item'" [item]="item"></fuse-nav-vertical-item>

View File

@ -1,6 +1,7 @@
<div class="group-title"> <div class="group-title">
<span class="hint-text">{{ item.title }}</span> <span class="hint-text" [translate]="item.translate">{{ item.title }}</span>
</div> </div>
<div class="group-items"> <div class="group-items">
<ng-container *ngFor="let item of item.children"> <ng-container *ngFor="let item of item.children">
<fuse-nav-vertical-group *ngIf="item.type=='group'" [item]="item"></fuse-nav-vertical-group> <fuse-nav-vertical-group *ngIf="item.type=='group'" [item]="item"></fuse-nav-vertical-group>

View File

@ -1,8 +1,8 @@
<a class="nav-link" *ngIf="item.url" [routerLink]="[item.url]" routerLinkActive="active" <a class="nav-link" *ngIf="item.url" [routerLink]="[item.url]" routerLinkActive="active"
[routerLinkActiveOptions]="{exact: item.exactMatch || false}" matRipple> [routerLinkActiveOptions]="{exact: item.exactMatch || false}" matRipple>
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon> <mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
<span class="nav-link-title">{{item.title}}</span> <span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
<span class="nav-link-badge" *ngIf="item.badge" <span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}"> [ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
{{item.badge.title}} {{item.badge.title}}
</span> </span>
@ -10,8 +10,8 @@
<span class="nav-link" *ngIf="item.function" (click)="item.function()" matRipple> <span class="nav-link" *ngIf="item.function" (click)="item.function()" matRipple>
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon> <mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
<span class="nav-link-title">{{item.title}}</span> <span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
<span class="nav-link-badge" *ngIf="item.badge" <span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}"> [ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
{{item.badge.title}} {{item.badge.title}}
</span> </span>

View File

@ -47,14 +47,20 @@
} }
.nav-link-badge { .nav-link-badge {
display: flex;
align-items: center;
min-width: 20px; min-width: 20px;
height: 20px; height: 20px;
line-height: 20px;
padding: 0 7px; padding: 0 7px;
font-size: 11px; font-size: 11px;
font-weight: 500; font-weight: 500;
border-radius: 20px; border-radius: 20px;
transition: opacity 0.2s ease-in-out 0.1s; transition: opacity 0.2s ease-in-out 0.1s;
margin-left: 8px;
+ .collapse-arrow {
margin-left: 8px;
}
} }
&:hover { &:hover {

View File

@ -18,7 +18,7 @@
<p> <p>
<code>fuse-navigation</code> is a custom built Fuse component allows you to create a multi-level collapsable <code>fuse-navigation</code> is a custom built Fuse component allows you to create a multi-level collapsable
navigation. navigation. It has built-in support for translations using <b>ngx-translate</b> module.
</p> </p>
<div class="my-48"> <div class="my-48">
@ -40,6 +40,11 @@
<b>Collapsable</b> and <b>Item</b>. These items can be mixed and matched to create unique and complex <b>Collapsable</b> and <b>Item</b>. These items can be mixed and matched to create unique and complex
navigation layouts. navigation layouts.
</p> </p>
<p class="py-8">
Navigation model can be found in <code>src/app/navigation</code> folder along with its translation
files. Navigation model and its translation files are set in <b>app.component.ts</b> file. Check that
file for more detailed usage example.
</p>
</div> </div>
<div class="my-48"> <div class="my-48">
@ -48,9 +53,10 @@
<fuse-hljs lang="json" class="source-code"> <fuse-hljs lang="json" class="source-code">
<textarea #source hidden="hidden"> <textarea #source hidden="hidden">
{ {
'title': 'COMPONENTS', 'title' : 'COMPONENTS',
'type' : 'group', 'translate': 'NAV.COMPONENTS',
'children': [ 'type' : 'group',
'children' : [
{ {
'title': 'ngx-datatable', 'title': 'ngx-datatable',
'type' : 'item', 'type' : 'item',
@ -69,10 +75,11 @@
<fuse-hljs lang="json" class="source-code"> <fuse-hljs lang="json" class="source-code">
<textarea #source hidden="hidden"> <textarea #source hidden="hidden">
{ {
'title' : 'Datatables', 'title' : 'Datatables',
'type' : 'collapse', 'translate': 'NAV.DATATABLES',
'icon' : 'border_all', 'type' : 'collapse',
'children': [ 'icon' : 'border_all',
'children' : [
{ {
'title': 'ngx-datatable', 'title': 'ngx-datatable',
'type' : 'nav-item', 'type' : 'nav-item',
@ -91,10 +98,11 @@
<fuse-hljs lang="json" class="source-code"> <fuse-hljs lang="json" class="source-code">
<textarea #source hidden="hidden"> <textarea #source hidden="hidden">
{ {
'title': 'Countdown', 'title' : 'Countdown',
'type' : 'item', 'translate': 'NAV.COUNTDOWN',
'icon' : 'settings_input_component', 'type' : 'item',
'url' : '/components/countdown' 'icon' : 'settings_input_component',
'url' : '/components/countdown'
}, },
</textarea> </textarea>
</fuse-hljs> </fuse-hljs>

View File

@ -0,0 +1,20 @@
export const locale = {
lang: 'en',
data: {
'NAV': {
'APPLICATIONS': 'Applications',
'DASHBOARDS' : 'Dashboards',
'CALENDAR' : 'Calendar',
'ECOMMERCE' : 'E-Commerce',
'MAIL' : {
'TITLE': 'Mail',
'BADGE': '25'
},
'CHAT' : 'Chat',
'FILE_MANAGER': 'File Manager',
'CONTACTS' : 'Contacts',
'TODO' : 'To-Do',
'SCRUMBOARD' : 'Scrumboard'
}
}
};

View File

@ -0,0 +1,20 @@
export const locale = {
lang: 'tr',
data: {
'NAV': {
'APPLICATIONS': 'Programlar',
'DASHBOARDS' : 'Kontrol Paneli',
'CALENDAR' : 'Takvim',
'ECOMMERCE' : 'E-Ticaret',
'MAIL' : {
'TITLE': 'Posta',
'BADGE': '15'
},
'CHAT' : 'Sohbet',
'FILE_MANAGER': 'Dosya Yöneticisi',
'CONTACTS' : 'Kişiler',
'TODO' : 'Yapılacaklar',
'SCRUMBOARD' : 'Proje'
}
}
};

View File

@ -1,4 +1,6 @@
export class NavigationModel import { FuseNavigationModelInterface } from '../core/components/navigation/navigation.model';
export class FuseNavigationModel implements FuseNavigationModelInterface
{ {
public model: any[]; public model: any[];
@ -6,17 +8,19 @@ export class NavigationModel
{ {
this.model = [ this.model = [
{ {
'id' : 'applications', 'id' : 'applications',
'title' : 'Applications', 'title' : 'Applications',
'type' : 'group', 'translate': 'NAV.APPLICATIONS',
'icon' : 'apps', 'type' : 'group',
'children': [ 'icon' : 'apps',
'children' : [
{ {
'id' : 'dashboards', 'id' : 'dashboards',
'title' : 'Dashboards', 'title' : 'Dashboards',
'type' : 'collapse', 'translate': 'NAV.DASHBOARDS',
'icon' : 'dashboard', 'type' : 'collapse',
'children': [ 'icon' : 'dashboard',
'children' : [
{ {
'id' : 'project', 'id' : 'project',
'title': 'Project', 'title': 'Project',
@ -26,18 +30,20 @@ export class NavigationModel
] ]
}, },
{ {
'id' : 'calendar', 'id' : 'calendar',
'title': 'Calendar', 'title' : 'Calendar',
'type' : 'item', 'translate': 'NAV.CALENDAR',
'icon' : 'today', 'type' : 'item',
'url' : '/apps/calendar' 'icon' : 'today',
'url' : '/apps/calendar'
}, },
{ {
'id' : 'e-commerce', 'id' : 'e-commerce',
'title' : 'E-Commerce', 'title' : 'E-Commerce',
'type' : 'collapse', 'translate': 'NAV.ECOMMERCE',
'icon' : 'shopping_cart', 'type' : 'collapse',
'children': [ 'icon' : 'shopping_cart',
'children' : [
{ {
'id' : 'dashboard', 'id' : 'dashboard',
'title': 'Dashboard', 'title': 'Dashboard',
@ -75,61 +81,68 @@ export class NavigationModel
] ]
}, },
{ {
'id' : 'mail', 'id' : 'mail',
'title': 'Mail', 'title' : 'Mail',
'type' : 'item', 'translate': 'NAV.MAIL.TITLE',
'icon' : 'email', 'type' : 'item',
'url' : '/apps/mail', 'icon' : 'email',
'badge': { 'url' : '/apps/mail',
'title': 25, 'badge' : {
'bg' : '#F44336', 'title' : 25,
'fg' : '#FFFFFF' 'translate': 'NAV.MAIL.BADGE',
'bg' : '#F44336',
'fg' : '#FFFFFF'
} }
}, },
{ {
'id' : 'chat', 'id' : 'chat',
'title': 'Chat', 'title' : 'Chat',
'type' : 'item', 'translate': 'NAV.CHAT',
'icon' : 'chat', 'type' : 'item',
'url' : '/apps/chat', 'icon' : 'chat',
'badge': { 'url' : '/apps/chat',
'badge' : {
'title': 13, 'title': 13,
'bg' : '#09d261', 'bg' : '#09d261',
'fg' : '#FFFFFF' 'fg' : '#FFFFFF'
} }
}, },
{ {
'id' : 'file-manager', 'id' : 'file-manager',
'title': 'File Manager', 'title' : 'File Manager',
'type' : 'item', 'translate': 'NAV.FILE_MANAGER',
'icon' : 'folder', 'type' : 'item',
'url' : '/apps/file-manager' 'icon' : 'folder',
'url' : '/apps/file-manager'
}, },
{ {
'id' : 'contacts', 'id' : 'contacts',
'title': 'Contacts', 'title' : 'Contacts',
'type' : 'item', 'translate': 'NAV.CONTACTS',
'icon' : 'account_box', 'type' : 'item',
'url' : '/apps/contacts' 'icon' : 'account_box',
'url' : '/apps/contacts'
}, },
{ {
'id' : 'to-do', 'id' : 'to-do',
'title': 'To-Do', 'title' : 'To-Do',
'type' : 'item', 'translate': 'NAV.TODO',
'icon' : 'check_box', 'type' : 'item',
'url' : '/apps/todo', 'icon' : 'check_box',
'badge': { 'url' : '/apps/todo',
'badge' : {
'title': 3, 'title': 3,
'bg' : '#FF6F00', 'bg' : '#FF6F00',
'fg' : '#FFFFFF' 'fg' : '#FFFFFF'
} }
}, },
{ {
'id' : 'scrumboard', 'id' : 'scrumboard',
'title': 'Scrumboard', 'title' : 'Scrumboard',
'type' : 'item', 'translate': 'NAV.SCRUMBOARD',
'icon' : 'assessment', 'type' : 'item',
'url' : '/apps/scrumboard' 'icon' : 'assessment',
'url' : '/apps/scrumboard'
} }
] ]
}, },
@ -144,6 +157,11 @@ export class NavigationModel
'title' : 'Authentication', 'title' : 'Authentication',
'type' : 'collapse', 'type' : 'collapse',
'icon' : 'lock', 'icon' : 'lock',
'badge' : {
'title': 10,
'bg' : '#525e8a',
'fg' : '#FFFFFF'
},
'children': [ 'children': [
{ {
'id' : 'login', 'id' : 'login',
@ -359,6 +377,11 @@ export class NavigationModel
'id' : 'carded', 'id' : 'carded',
'title' : 'Carded', 'title' : 'Carded',
'type' : 'collapse', 'type' : 'collapse',
'badge' : {
'title': 10,
'bg' : '#525e8a',
'fg' : '#FFFFFF'
},
'children': [ 'children': [
{ {
'id' : 'full-width', 'id' : 'full-width',
@ -426,6 +449,11 @@ export class NavigationModel
'id' : 'simple', 'id' : 'simple',
'title' : 'Simple', 'title' : 'Simple',
'type' : 'collapse', 'type' : 'collapse',
'badge' : {
'title': 8,
'bg' : '#525e8a',
'fg' : '#FFFFFF'
},
'children': [ 'children': [
{ {
'id' : 'full-width', 'id' : 'full-width',