diff --git a/angular.json b/angular.json
index d0942847..1955cca0 100644
--- a/angular.json
+++ b/angular.json
@@ -49,6 +49,10 @@
"vendorChunk": false,
"buildOptimizer": true
},
+ "ec": {
+ "sourceMap": true,
+ "extractCss": true
+ },
"hmr": {
"fileReplacements": [
{
@@ -71,6 +75,9 @@
"hmr": {
"hmr": true,
"browserTarget": "fuse:build:hmr"
+ },
+ "ec": {
+ "browserTarget": "fuse:build:ec"
}
}
},
diff --git a/package-lock.json b/package-lock.json
index 52ff6dfe..f3df7b0c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "fuse",
- "version": "6.1.2",
+ "version": "6.2.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -129,9 +129,9 @@
}
},
"@angular/cdk": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-6.3.1.tgz",
- "integrity": "sha512-TEf1pstDmoLk4C0H45CxpdkBLunNYRQS1pCyWf7hWOHC0M0m130CYZ+rMt4uVDLe4NxiFUk3XwHITB3AuSlCOQ==",
+ "version": "6.3.3",
+ "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-6.3.3.tgz",
+ "integrity": "sha512-cfUwvnGGByZy/poA75/vsELBq68eDUYJe3qi0WvSbtFzbQlsxeqaBiaxVYqmHJkPlSF5nsUhg5KvDowED3a4sA==",
"requires": {
"tslib": "^1.7.1"
}
@@ -373,17 +373,26 @@
"dev": true
},
"@angular/material": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/@angular/material/-/material-6.3.1.tgz",
- "integrity": "sha512-2M5be3Re1EqTl+i5KuS54P/s+i2AQYlv6swR8ukwXR2arwXy8ewtuwiPjUQulpuEaUkKTQ3f6WndXAgP40Ou7A==",
+ "version": "6.3.3",
+ "resolved": "https://registry.npmjs.org/@angular/material/-/material-6.3.3.tgz",
+ "integrity": "sha512-3qTZ8+pjc8P1D+TLr9ETGfFyMYO+BAQlFiVs3oV8rw5y0Wzkz6G1JHfKQ2oOd8/npXP6rJQldssUM4IBbSOxIQ==",
"requires": {
+ "parse5": "^5.0.0",
"tslib": "^1.7.1"
+ },
+ "dependencies": {
+ "parse5": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.0.0.tgz",
+ "integrity": "sha512-0ywuiUOnpWWeil5grH2rxjyTJoeQVwyBuO2si6QIU9dWtj2npjuyK1HaY1RbLnVfDhEbhyAPNUBKRK0Xj2xE0w==",
+ "optional": true
+ }
}
},
"@angular/material-moment-adapter": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-6.3.1.tgz",
- "integrity": "sha512-rttEepHc/eVEUEErz8eg6063KC79vVN6/rATuoabfgtIqtoSEuft18SkwCt7gQz6lWtuJA1oFc3HIXhN8gxKBw==",
+ "version": "6.3.3",
+ "resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-6.3.3.tgz",
+ "integrity": "sha512-YFi1iED8gcHhCwkWGMkyJtHX/tWAsQWnRNrh1AwbWl4JW1739vPAM/OimdIijeGAZ5dVVZX4A4EscCBb74b4dA==",
"requires": {
"tslib": "^1.7.1"
}
@@ -7168,9 +7177,9 @@
}
},
"ngx-color-picker": {
- "version": "6.3.3",
- "resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-6.3.3.tgz",
- "integrity": "sha512-0uoxt+3BtanTTbQj9elpsZe/6a/BWAl8jdl0m6OyrCXlCiO3q4zGZjWee4905RF0NXGxQvDWT27coqmDeXGsvw=="
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-6.4.0.tgz",
+ "integrity": "sha512-u4UeeSdZoaGmHJCSlFQGe0xhOLB63rms35IjI7HhHz9MS4VeCQa4UiFMK9Dgjg1FuGt2nEyj/4Fs6tpQfgXEXQ=="
},
"ngx-cookie-service": {
"version": "1.0.10",
diff --git a/package.json b/package.json
index a96a888e..afe1f78f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "fuse",
- "version": "6.1.2",
+ "version": "6.2.0",
"license": "https://themeforest.net/licenses/terms/regular",
"scripts": {
"ng": "ng",
@@ -20,15 +20,15 @@
"dependencies": {
"@agm/core": "1.0.0-beta.3",
"@angular/animations": "6.0.7",
- "@angular/cdk": "6.3.1",
+ "@angular/cdk": "6.3.3",
"@angular/common": "6.0.7",
"@angular/compiler": "6.0.7",
"@angular/core": "6.0.7",
"@angular/flex-layout": "6.0.0-beta.16",
"@angular/forms": "6.0.7",
"@angular/http": "6.0.7",
- "@angular/material": "6.3.1",
- "@angular/material-moment-adapter": "6.3.1",
+ "@angular/material": "6.3.3",
+ "@angular/material-moment-adapter": "6.3.3",
"@angular/platform-browser": "6.0.7",
"@angular/platform-browser-dynamic": "6.0.7",
"@angular/router": "6.0.7",
@@ -52,7 +52,7 @@
"moment": "2.22.2",
"ng2-charts": "1.6.0",
"ngrx-store-freeze": "0.2.4",
- "ngx-color-picker": "6.3.3",
+ "ngx-color-picker": "6.4.0",
"ngx-cookie-service": "1.0.10",
"perfect-scrollbar": "1.4.0",
"prismjs": "1.15.0",
diff --git a/src/@fuse/components/sidebar/sidebar.component.scss b/src/@fuse/components/sidebar/sidebar.component.scss
index 0005a506..63a7775a 100644
--- a/src/@fuse/components/sidebar/sidebar.component.scss
+++ b/src/@fuse/components/sidebar/sidebar.component.scss
@@ -37,12 +37,6 @@ fuse-sidebar {
position: absolute !important;
top: 0;
bottom: 0;
-
- &:not(.unfolded) {
- width: 64px;
- min-width: 64px;
- max-width: 64px;
- }
}
&.animations-enabled {
diff --git a/src/@fuse/components/sidebar/sidebar.component.ts b/src/@fuse/components/sidebar/sidebar.component.ts
index a4695d4c..67b5efe2 100644
--- a/src/@fuse/components/sidebar/sidebar.component.ts
+++ b/src/@fuse/components/sidebar/sidebar.component.ts
@@ -2,7 +2,7 @@ import { ChangeDetectorRef, Component, ElementRef, EventEmitter, HostBinding, Ho
import { animate, AnimationBuilder, AnimationPlayer, style } from '@angular/animations';
import { ObservableMedia } from '@angular/flex-layout';
import { Subject } from 'rxjs';
-import { takeUntil } from 'rxjs/internal/operators';
+import { takeUntil } from 'rxjs/operators';
import { FuseSidebarService } from './sidebar.service';
import { FuseMatchMediaService } from '@fuse/services/match-media.service';
@@ -40,6 +40,14 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
@HostBinding('class.locked-open')
isLockedOpen: boolean;
+ // Folded width
+ @Input()
+ foldedWidth: number;
+
+ // Folded auto trigger on hover
+ @Input()
+ foldedAutoTriggerOnHover: boolean;
+
// Folded unfolded
@HostBinding('class.unfolded')
unfolded: boolean;
@@ -92,6 +100,8 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
)
{
// Set the defaults
+ this.foldedAutoTriggerOnHover = true;
+ this.foldedWidth = 64;
this.foldedChanged = new EventEmitter();
this.openedChanged = new EventEmitter();
this.opened = false;
@@ -108,7 +118,11 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
// @ Accessors
// -----------------------------------------------------------------------------------------------------
- // Folded
+ /**
+ * Folded
+ *
+ * @param {boolean} value
+ */
@Input()
set folded(value: boolean)
{
@@ -121,23 +135,23 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
return;
}
- // Programmatically add/remove margin to the element
+ // Programmatically add/remove padding to the element
// that comes after or before based on the position
let sibling,
styleRule;
- const styleValue = '64px';
+ const styleValue = this.foldedWidth + 'px';
// Get the sibling and set the style rule
if ( this.position === 'left' )
{
sibling = this._elementRef.nativeElement.nextElementSibling;
- styleRule = 'margin-left';
+ styleRule = 'padding-left';
}
else
{
sibling = this._elementRef.nativeElement.previousElementSibling;
- styleRule = 'margin-right';
+ styleRule = 'padding-right';
}
// If there is no sibling, return...
@@ -152,6 +166,11 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
// Fold the sidebar
this.fold();
+ // Set the folded width
+ this._renderer.setStyle(this._elementRef.nativeElement, 'width', styleValue);
+ this._renderer.setStyle(this._elementRef.nativeElement, 'min-width', styleValue);
+ this._renderer.setStyle(this._elementRef.nativeElement, 'max-width', styleValue);
+
// Set the style and class
this._renderer.setStyle(sibling, styleRule, styleValue, RendererStyleFlags2.Important + RendererStyleFlags2.DashCase);
this._renderer.addClass(this._elementRef.nativeElement, 'folded');
@@ -162,6 +181,11 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
// Unfold the sidebar
this.unfold();
+ // Remove the folded width
+ this._renderer.removeStyle(this._elementRef.nativeElement, 'width');
+ this._renderer.removeStyle(this._elementRef.nativeElement, 'min-width');
+ this._renderer.removeStyle(this._elementRef.nativeElement, 'max-width');
+
// Remove the style and class
this._renderer.removeStyle(sibling, styleRule);
this._renderer.removeClass(this._elementRef.nativeElement, 'folded');
@@ -375,23 +399,23 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
return;
}
- // Programmatically add/remove margin to the element
+ // Programmatically add/remove padding to the element
// that comes after or before based on the position
let sibling,
styleRule;
- const styleValue = '64px';
+ const styleValue = this.foldedWidth + 'px';
// Get the sibling and set the style rule
if ( this.position === 'left' )
{
sibling = this._elementRef.nativeElement.nextElementSibling;
- styleRule = 'margin-left';
+ styleRule = 'padding-left';
}
else
{
sibling = this._elementRef.nativeElement.previousElementSibling;
- styleRule = 'margin-right';
+ styleRule = 'padding-right';
}
// If there is no sibling, return...
@@ -403,6 +427,11 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
// Fold the sidebar
this.fold();
+ // Set the folded width
+ this._renderer.setStyle(this._elementRef.nativeElement, 'width', styleValue);
+ this._renderer.setStyle(this._elementRef.nativeElement, 'min-width', styleValue);
+ this._renderer.setStyle(this._elementRef.nativeElement, 'max-width', styleValue);
+
// Set the style and class
this._renderer.setStyle(sibling, styleRule, styleValue, RendererStyleFlags2.Important + RendererStyleFlags2.DashCase);
this._renderer.addClass(this._elementRef.nativeElement, 'folded');
@@ -633,20 +662,13 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
@HostListener('mouseenter')
onMouseEnter(): void
{
- // Only work if the sidebar is folded
- if ( !this.folded )
+ // Only work if the auto trigger is enabled
+ if ( !this.foldedAutoTriggerOnHover )
{
return;
}
- // Enable the animations
- this._enableAnimations();
-
- // Unfold the sidebar temporarily
- this.unfolded = true;
-
- // Mark for check
- this._changeDetectorRef.markForCheck();
+ this.unfoldTemporarily();
}
/**
@@ -655,20 +677,13 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
@HostListener('mouseleave')
onMouseLeave(): void
{
- // Only work if the sidebar is folded
- if ( !this.folded )
+ // Only work if the auto trigger is enabled
+ if ( !this.foldedAutoTriggerOnHover )
{
return;
}
- // Enable the animations
- this._enableAnimations();
-
- // Fold the sidebar back
- this.unfolded = false;
-
- // Mark for check
- this._changeDetectorRef.markForCheck();
+ this.foldTemporarily();
}
/**
@@ -727,4 +742,58 @@ export class FuseSidebarComponent implements OnInit, OnDestroy
this.fold();
}
}
+
+ /**
+ * Fold the temporarily unfolded sidebar back
+ */
+ foldTemporarily(): void
+ {
+ // Only work if the sidebar is folded
+ if ( !this.folded )
+ {
+ return;
+ }
+
+ // Enable the animations
+ this._enableAnimations();
+
+ // Fold the sidebar back
+ this.unfolded = false;
+
+ // Set the folded width
+ const styleValue = this.foldedWidth + 'px';
+
+ this._renderer.setStyle(this._elementRef.nativeElement, 'width', styleValue);
+ this._renderer.setStyle(this._elementRef.nativeElement, 'min-width', styleValue);
+ this._renderer.setStyle(this._elementRef.nativeElement, 'max-width', styleValue);
+
+ // Mark for check
+ this._changeDetectorRef.markForCheck();
+ }
+
+ /**
+ * Unfold the sidebar temporarily
+ */
+ unfoldTemporarily(): void
+ {
+ // Only work if the sidebar is folded
+ if ( !this.folded )
+ {
+ return;
+ }
+
+ // Enable the animations
+ this._enableAnimations();
+
+ // Unfold the sidebar temporarily
+ this.unfolded = true;
+
+ // Remove the folded width
+ this._renderer.removeStyle(this._elementRef.nativeElement, 'width');
+ this._renderer.removeStyle(this._elementRef.nativeElement, 'min-width');
+ this._renderer.removeStyle(this._elementRef.nativeElement, 'max-width');
+
+ // Mark for check
+ this._changeDetectorRef.markForCheck();
+ }
}
diff --git a/src/@fuse/components/theme-options/theme-options.component.html b/src/@fuse/components/theme-options/theme-options.component.html
index c6a6cc75..00a10741 100644
--- a/src/@fuse/components/theme-options/theme-options.component.html
+++ b/src/@fuse/components/theme-options/theme-options.component.html
@@ -73,6 +73,17 @@
Right
+
Variant:
+
+ Style 1
+ Style 2
+
+
+ Color:
+
+
+
@@ -91,6 +102,11 @@
Below Fixed
+ Color:
+
+
+
@@ -109,6 +125,11 @@
Below Fixed
+ Color:
+
+
+
@@ -146,6 +167,17 @@
Right
+ Variant:
+
+ Style 1
+ Style 2
+
+
+ Color:
+
+
+
@@ -164,6 +196,11 @@
Below
+ Color:
+
+
+
@@ -182,6 +219,11 @@
Below
+ Color:
+
+
+
@@ -219,6 +261,17 @@
Right
+ Variant:
+
+ Style 1
+ Style 2
+
+
+ Color:
+
+
+
@@ -236,6 +289,11 @@
Above Fixed
+ Color:
+
+
+
@@ -253,6 +311,11 @@
Above Fixed
+ Color:
+
+
+
@@ -285,6 +348,17 @@
Top
+ Variant (Vertical):
+
+ Style 1
+ Style 2
+
+
+ Color:
+
+
+
@@ -302,6 +376,11 @@
Below
+ Color:
+
+
+
@@ -319,6 +398,11 @@
Above Static
+ Color:
+
+
+
@@ -338,35 +422,6 @@
-
-
-
-
Colors
-
-
-
-
-
Toolbar Color
-
-
-
-
-
Navbar Color
-
-
-
-
-
Footer Color
-
-
-
-
-
-
-
diff --git a/src/@fuse/components/theme-options/theme-options.component.scss b/src/@fuse/components/theme-options/theme-options.component.scss
index 3024ff25..8199e635 100644
--- a/src/@fuse/components/theme-options/theme-options.component.scss
+++ b/src/@fuse/components/theme-options/theme-options.component.scss
@@ -73,11 +73,6 @@
}
}
}
-
- .colors {
- display: block !important;
- width: 100%;
- }
}
}
}
diff --git a/src/@fuse/components/theme-options/theme-options.component.ts b/src/@fuse/components/theme-options/theme-options.component.ts
index ebfbcac5..2243e1ea 100644
--- a/src/@fuse/components/theme-options/theme-options.component.ts
+++ b/src/@fuse/components/theme-options/theme-options.component.ts
@@ -65,20 +65,21 @@ export class FuseThemeOptionsComponent implements OnInit, OnDestroy
style : new FormControl(),
width : new FormControl(),
navbar : this._formBuilder.group({
+ background: new FormControl(),
+ folded : new FormControl(),
hidden : new FormControl(),
position : new FormControl(),
- folded : new FormControl(),
- background: new FormControl()
+ variant : new FormControl()
}),
toolbar: this._formBuilder.group({
+ background: new FormControl(),
hidden : new FormControl(),
- position : new FormControl(),
- background: new FormControl()
+ position : new FormControl()
}),
footer : this._formBuilder.group({
+ background: new FormControl(),
hidden : new FormControl(),
- position : new FormControl(),
- background: new FormControl()
+ position : new FormControl()
})
}),
customScrollbars: new FormControl()
@@ -174,20 +175,21 @@ export class FuseThemeOptionsComponent implements OnInit, OnDestroy
layout: {
width : 'fullwidth',
navbar : {
+ background: 'mat-fuse-dark-700-bg',
+ folded : false,
hidden : false,
position : 'left',
- folded : false,
- background: 'mat-fuse-dark-700-bg'
+ variant : 'vertical-style-1'
},
toolbar: {
+ background: 'mat-white-500-bg',
hidden : false,
- position : 'below-static',
- background: 'mat-white-500-bg'
+ position : 'below-static'
},
footer : {
+ background: 'mat-fuse-dark-900-bg',
hidden : false,
- position : 'below-static',
- background: 'mat-fuse-dark-900-bg'
+ position : 'below-static'
}
}
});
@@ -202,20 +204,21 @@ export class FuseThemeOptionsComponent implements OnInit, OnDestroy
layout: {
width : 'fullwidth',
navbar : {
+ background: 'mat-fuse-dark-700-bg',
+ folded : false,
hidden : false,
position : 'left',
- folded : false,
- background: 'mat-fuse-dark-700-bg'
+ variant : 'vertical-style-1'
},
toolbar: {
+ background: 'mat-white-500-bg',
hidden : false,
- position : 'below',
- background: 'mat-white-500-bg'
+ position : 'below'
},
footer : {
+ background: 'mat-fuse-dark-900-bg',
hidden : false,
- position : 'below',
- background: 'mat-fuse-dark-900-bg'
+ position : 'below'
}
}
});
@@ -230,20 +233,21 @@ export class FuseThemeOptionsComponent implements OnInit, OnDestroy
layout: {
width : 'fullwidth',
navbar : {
+ background: 'mat-fuse-dark-700-bg',
+ folded : false,
hidden : false,
position : 'left',
- folded : false,
- background: 'mat-fuse-dark-700-bg'
+ layout : 'vertical-style-1'
},
toolbar: {
+ background: 'mat-white-500-bg',
hidden : false,
- position : 'above-static',
- background: 'mat-white-500-bg'
+ position : 'above-static'
},
footer : {
+ background: 'mat-fuse-dark-900-bg',
hidden : false,
- position : 'above-static',
- background: 'mat-fuse-dark-900-bg'
+ position : 'above-static'
}
}
});
@@ -258,20 +262,21 @@ export class FuseThemeOptionsComponent implements OnInit, OnDestroy
layout: {
width : 'fullwidth',
navbar : {
+ background: 'mat-fuse-dark-700-bg',
+ folded : false,
hidden : false,
position : 'top',
- folded : false,
- background: 'mat-fuse-dark-700-bg'
+ variant : 'vertical-style-1'
},
toolbar: {
+ background: 'mat-white-500-bg',
hidden : false,
- position : 'above',
- background: 'mat-white-500-bg'
+ position : 'above'
},
footer : {
+ background: 'mat-fuse-dark-900-bg',
hidden : false,
- position : 'above-fixed',
- background: 'mat-fuse-dark-900-bg'
+ position : 'above-fixed'
}
}
});
diff --git a/src/@fuse/scss/partials/_colors.scss b/src/@fuse/scss/partials/_colors.scss
index 7b98456d..cc864cde 100644
--- a/src/@fuse/scss/partials/_colors.scss
+++ b/src/@fuse/scss/partials/_colors.scss
@@ -14,7 +14,7 @@ i {
}
// Material colors map
-$matColorsMap: (
+$matPalettes: (
primary: $primary,
accent: $accent,
warn: $warn,
@@ -43,75 +43,91 @@ $matColorsMap: (
);
// Material color hues list
-$matColorHues: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400, A700;
+$matHues: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400, A700;
// Text color levels generator mixin
-@mixin generateTextColorLevels($baseTextColor) {
+@mixin generateTextColorLevels($classes, $contrast) {
- // If the base text color is black...
- @if (rgba(black, 1) == rgba($baseTextColor, 1)) {
+ // If the contrast is dark...
+ @if ($contrast == 'dark') {
- i,
- .icon {
- color: rgba(0, 0, 0, 0.54);
- }
+ // Put down the color classes
+ #{$classes} {
- &.secondary-text,
- .secondary-text {
- color: rgba(0, 0, 0, 0.54) !important;
- }
+ i,
+ .icon {
+ color: rgba(0, 0, 0, 0.54);
+ }
- &.hint-text,
- .hint-text,
- &.disabled-text,
- .disabled-text {
- color: rgba(0, 0, 0, 0.38) !important;
- }
+ &.secondary-text,
+ .secondary-text {
+ color: rgba(0, 0, 0, 0.54) !important;
+ }
- &.divider,
- .divider {
- color: rgba(0, 0, 0, 0.12) !important;
- }
+ &.hint-text,
+ .hint-text,
+ &.disabled-text,
+ .disabled-text {
+ color: rgba(0, 0, 0, 0.38) !important;
+ }
- .mat-ripple-element {
- background: rgba(0, 0, 0, 0.1);
+ &.divider,
+ .divider {
+ color: rgba(0, 0, 0, 0.12) !important;
+ }
+
+ .mat-ripple-element {
+ background: rgba(0, 0, 0, 0.1);
+ }
+
+ .adaptive-border-color {
+ border-color: rgba(0, 0, 0, 0.12);
+ }
}
}
// If the base text color is white...
@else {
- i,
- .icon {
- color: rgba(255, 255, 255, 1);
- }
+ // Put down the color classes
+ #{$classes} {
- &.secondary-text,
- .secondary-text {
- color: rgba(255, 255, 255, 0.70) !important;
- }
+ i,
+ .icon {
+ color: rgba(255, 255, 255, 1);
+ }
- &.hint-text,
- .hint-text,
- &.disabled-text,
- .disabled-text {
- color: rgba(255, 255, 255, 0.50) !important;
- }
+ &.secondary-text,
+ .secondary-text {
+ color: rgba(255, 255, 255, 0.70) !important;
+ }
- &.divider,
- .divider {
- color: rgba(255, 255, 255, 0.12) !important;
- }
+ &.hint-text,
+ .hint-text,
+ &.disabled-text,
+ .disabled-text {
+ color: rgba(255, 255, 255, 0.50) !important;
+ }
- .mat-ripple-element {
- background: rgba(255, 255, 255, 0.1);
+ &.divider,
+ .divider {
+ color: rgba(255, 255, 255, 0.12) !important;
+ }
+
+ .mat-ripple-element {
+ background: rgba(255, 255, 255, 0.1);
+ }
+
+ .adaptive-border-color {
+ border-color: rgba(255, 255, 255, 0.12);
+ }
}
}
}
-@mixin generateMaterialElementColors($contrastColor) {
+@mixin generateMaterialElementColors($classes, $contrast) {
- // If the contrast color is white...
+ // If the contrast color is light...
$fuseForeground: (
base: white,
text: white,
@@ -119,8 +135,8 @@ $matColorHues: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400
divider: rgba(white, 0.12),
);
- // If the contrast color is black...
- @if (rgba(black, 1) == rgba($contrastColor, 1)) {
+ // If the contrast color is dark...
+ @if ($contrast == 'dark') {
$fuseForeground: (
base: black,
@@ -129,43 +145,47 @@ $matColorHues: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400
);
}
- // Native Input
- input[type="text"] {
- color: map_get($fuseForeground, base);
- }
+ // Put down the color classes
+ #{$classes} {
- // Input
- .mat-form-field-label {
- color: map_get($fuseForeground, hint-text);
- }
+ // Native Input
+ input[type="text"] {
+ color: map_get($fuseForeground, base);
+ }
- .mat-form-field-underline {
- background-color: map_get($fuseForeground, divider);
- }
+ // Input
+ .mat-form-field-label {
+ color: map_get($fuseForeground, hint-text);
+ }
- // Select
- .mat-select-trigger,
- .mat-select-arrow {
- color: map_get($fuseForeground, hint-text);
- }
+ .mat-form-field-underline {
+ background-color: map_get($fuseForeground, divider);
+ }
- .mat-select-underline {
- background-color: map_get($fuseForeground, divider);
- }
+ // Select
+ .mat-select-trigger,
+ .mat-select-arrow {
+ color: map_get($fuseForeground, hint-text);
+ }
- .mat-select-disabled .mat-select-value,
- .mat-select-arrow,
- .mat-select-trigger {
- color: map_get($fuseForeground, hint-text);
- }
+ .mat-select-underline {
+ background-color: map_get($fuseForeground, divider);
+ }
- .mat-select-content,
- .mat-select-panel-done-animating {
- background: map_get($background, card);
- }
+ .mat-select-disabled .mat-select-value,
+ .mat-select-arrow,
+ .mat-select-trigger {
+ color: map_get($fuseForeground, hint-text);
+ }
- .mat-select-value {
- color: map_get($fuseForeground, text);
+ .mat-select-content,
+ .mat-select-panel-done-animating {
+ background: map_get($background, card);
+ }
+
+ .mat-select-value {
+ color: map_get($fuseForeground, text);
+ }
}
}
@@ -180,14 +200,6 @@ $matColorHues: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400
background-color: $color !important;
color: $contrastColor !important;
- // Generate text color levels
- // based on current contrast color
- @include generateTextColorLevels($contrastColor);
-
- // Generate material element colors
- // based on current contrast color
- @include generateMaterialElementColors($contrastColor);
-
&[disabled] {
background-color: rgba($color, .12) !important;
color: rgba($contrastColor, .26) !important;
@@ -196,14 +208,6 @@ $matColorHues: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400
.#{$colorName}#{$hue}-fg {
color: $color !important;
-
- // Generate text color levels
- // based on current contrast color
- @include generateTextColorLevels($color);
-
- // Generate material element colors
- // based on current contrast color
- @include generateMaterialElementColors($color);
}
.#{$colorName}#{$hue}-border {
@@ -229,49 +233,126 @@ $matColorHues: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400
@mixin generateFuseColorClasses($primary, $accent, $warn) {
- $colorMap: (
+ $palettes: (
primary: $primary,
accent: $accent,
warn: $warn
);
+ // Define contrast lists
+ $light-contrasting-classes: ();
+ $dark-contrasting-classes: ();
+
// Generate the color classes...
- @each $name, $map in $colorMap {
+ @each $paletteName, $palette in $palettes {
- @each $hue in $matColorHues {
+ // Get the contrasts map
+ $contrasts: map-get($palette, 'contrast');
- $color: map-get($map, $hue);
- $contrastColor: map-get(map-get($map, 'contrast'), $hue);
+ @each $hue in $matHues {
- @if ($color != null and $contrastColor != null) {
+ // Get the color and the contrasting color
+ $color: map-get($palette, $hue);
+ $contrast: map-get($contrasts, $hue);
- @include generateColorClasses($name, $color, $contrastColor, '-#{$hue}');
+ @if ($color != null and $contrast != null) {
+
+ // Generate color classes
+ @include generateColorClasses($paletteName, $color, $contrast, '-#{$hue}');
+
+ // If the contrast color is dark
+ @if (rgba(black, 1) == rgba($contrast, 1)) {
+ $dark-contrasting-classes: append($dark-contrasting-classes, unquote('.mat-#{$paletteName}-#{$hue}-bg'), 'comma');
+ }
+ // if the contrast color is light
+ @else {
+ $light-contrasting-classes: append($light-contrasting-classes, unquote('.mat-#{$paletteName}-#{$hue}-bg'), 'comma');
+ }
// Run the generator one more time for default values (500)
@if ($hue == 500) {
- @include generateColorClasses($name, $color, $contrastColor, '');
+
+ // Generate color classes
+ @include generateColorClasses($paletteName, $color, $contrast, '');
+
+ // Add color to the correct list depending on the contrasting color
+
+ // If the contrast color is dark
+ @if (rgba(black, 1) == rgba($contrast, 1)) {
+ $dark-contrasting-classes: append($dark-contrasting-classes, unquote('.mat-#{$paletteName}-bg'), 'comma');
+ }
+ // if the contrast color is light
+ @else {
+ $light-contrasting-classes: append($light-contrasting-classes, unquote('.mat-#{$paletteName}-bg'), 'comma');
+ }
+ }
+ }
+ }
+ }
+
+ // Generate contrasting colors
+ @include generateTextColorLevels($dark-contrasting-classes, 'dark');
+ @include generateTextColorLevels($light-contrasting-classes, 'light');
+ @include generateMaterialElementColors($dark-contrasting-classes, 'dark');
+ @include generateMaterialElementColors($light-contrasting-classes, 'light');
+}
+
+// Generate the color classes...
+
+// Define contrast lists
+$light-contrasting-classes: ();
+$dark-contrasting-classes: ();
+
+@each $paletteName, $palette in $matPalettes {
+
+ // Get the contrasts map
+ $contrasts: map-get($palette, 'contrast');
+
+ @each $hue in $matHues {
+
+ // Get the color and the contrasting color
+ $color: map-get($palette, $hue);
+ $contrast: map-get($contrasts, $hue);
+
+ @if ($color != null and $contrast != null) {
+
+ // Generate color classes
+ @include generateColorClasses($paletteName, $color, $contrast, '-#{$hue}');
+
+ // Add color to the correct list depending on the contrasting color
+
+ // If the contrast color is dark
+ @if (rgba(black, 1) == rgba($contrast, 1)) {
+ $dark-contrasting-classes: append($dark-contrasting-classes, unquote('.mat-#{$paletteName}-#{$hue}-bg'), 'comma');
+ }
+ // if the contrast color is light
+ @else {
+ $light-contrasting-classes: append($light-contrasting-classes, unquote('.mat-#{$paletteName}-#{$hue}-bg'), 'comma');
+ }
+
+ // Run the generator one more time for default values (500)
+ @if ($hue == 500) {
+
+ // Generate color classes
+ @include generateColorClasses($paletteName, $color, $contrast, '');
+
+ // Add color to the correct list depending on the contrasting color
+
+ // If the contrast color is dark
+ @if (rgba(black, 1) == rgba($contrast, 1)) {
+ $dark-contrasting-classes: append($dark-contrasting-classes, unquote('.mat-#{$paletteName}-bg'), 'comma');
+ }
+ // if the contrast color is light
+ @else {
+ $light-contrasting-classes: append($light-contrasting-classes, unquote('.mat-#{$paletteName}-bg'), 'comma');
}
}
}
}
}
-// Generate the color classes...
-@each $colorName, $colorMap in $matColorsMap {
-
- @each $hue in $matColorHues {
-
- $color: map-get($colorMap, $hue);
- $contrastColor: map-get(map-get($colorMap, 'contrast'), $hue);
-
- @if ($color != null and $contrastColor != null) {
-
- @include generateColorClasses($colorName, $color, $contrastColor, '-#{$hue}');
-
- // Run the generator one more time for default values (500)
- @if ($hue == 500) {
- @include generateColorClasses($colorName, $color, $contrastColor, '');
- }
- }
- }
-}
+// Generate contrasting colors
+@include generateTextColorLevels($dark-contrasting-classes, 'dark');
+@include generateTextColorLevels($light-contrasting-classes, 'light');
+@include generateMaterialElementColors($dark-contrasting-classes, 'dark');
+@include generateMaterialElementColors($light-contrasting-classes, 'light');
diff --git a/src/@fuse/scss/partials/_navigation.scss b/src/@fuse/scss/partials/_navigation.scss
index df7b1be2..f9fb868d 100644
--- a/src/@fuse/scss/partials/_navigation.scss
+++ b/src/@fuse/scss/partials/_navigation.scss
@@ -219,4 +219,26 @@
}
}
}
+
+ // Material style
+ &.material {
+
+ .nav-subheader {
+ border-top: 1px solid rgba(0, 0, 0, 0.12);
+
+ &:first-child {
+ border-top: none;
+ }
+ }
+
+ .nav-item {
+
+ .nav-link {
+ height: 40px;
+ padding: 0 16px;
+ margin: 4px 8px;
+ border-radius: 4px;
+ }
+ }
+ }
}
diff --git a/src/@fuse/types/fuse-config.ts b/src/@fuse/types/fuse-config.ts
index b0d511e2..6ad44ba4 100644
--- a/src/@fuse/types/fuse-config.ts
+++ b/src/@fuse/types/fuse-config.ts
@@ -4,20 +4,21 @@ export interface FuseConfig
style: string,
width: 'fullwidth' | 'boxed',
navbar: {
+ background: string,
hidden: boolean,
folded: boolean,
position: 'left' | 'right' | 'top',
- background: string
+ variant: string
},
toolbar: {
+ background: string,
hidden: boolean,
- position: 'above' | 'above-static' | 'above-fixed' | 'below' | 'below-static' | 'below-fixed',
- background: string
+ position: 'above' | 'above-static' | 'above-fixed' | 'below' | 'below-static' | 'below-fixed'
}
footer: {
+ background: string,
hidden: boolean,
- position: 'above' | 'above-static' | 'above-fixed' | 'below' | 'below-static' | 'below-fixed',
- background: string
+ position: 'above' | 'above-static' | 'above-fixed' | 'below' | 'below-static' | 'below-fixed'
}
};
customScrollbars: boolean;
diff --git a/src/app/app.component.scss b/src/app/app.component.scss
index 42a78d3d..e04a412f 100644
--- a/src/app/app.component.scss
+++ b/src/app/app.component.scss
@@ -1,3 +1,5 @@
+@import "src/@fuse/scss/fuse";
+
:host {
position: relative;
display: flex;
@@ -9,7 +11,7 @@
.theme-options-button {
position: absolute;
top: 160px;
- right: 0;
+ right: 70px;
width: 48px;
height: 48px;
line-height: 48px;
@@ -18,9 +20,13 @@
border-radius: 0;
margin: 0;
pointer-events: auto;
- opacity: .75;
+ opacity: .90;
z-index: 998;
+ @include media-breakpoint-down('md') {
+ right: 0;
+ }
+
mat-icon {
animation: rotating 3s linear infinite;
}
diff --git a/src/app/fake-db/chat-panel.ts b/src/app/fake-db/chat-panel.ts
new file mode 100644
index 00000000..ec738bef
--- /dev/null
+++ b/src/app/fake-db/chat-panel.ts
@@ -0,0 +1,343 @@
+export class ChatPanelFakeDb
+{
+ public static contacts = [
+ {
+ 'id' : '5725a680b3249760ea21de52',
+ 'name' : 'Alice Freeman',
+ 'avatar': 'assets/images/avatars/alice.jpg',
+ 'status': 'online',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...',
+ 'unread': '2'
+ },
+ {
+ 'id' : '5725a680606588342058356d',
+ 'name' : 'Arnold',
+ 'avatar': 'assets/images/avatars/Arnold.jpg',
+ 'status': 'do-not-disturb',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...'
+ },
+ {
+ 'id' : '5725a68009e20d0a9e9acf2a',
+ 'name' : 'Barrera',
+ 'avatar': 'assets/images/avatars/Barrera.jpg',
+ 'status': 'do-not-disturb',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...'
+ },
+ {
+ 'id' : '5725a6809fdd915739187ed5',
+ 'name' : 'Blair',
+ 'avatar': 'assets/images/avatars/Blair.jpg',
+ 'status': 'offline',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...',
+ 'unread': '3'
+ },
+ {
+ 'id' : '5725a68007920cf75051da64',
+ 'name' : 'Boyle',
+ 'avatar': 'assets/images/avatars/Boyle.jpg',
+ 'status': 'offline',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...',
+ 'unread': '1'
+ },
+ {
+ 'id' : '5725a68031fdbb1db2c1af47',
+ 'name' : 'Christy',
+ 'avatar': 'assets/images/avatars/Christy.jpg',
+ 'status': 'offline',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...'
+ },
+ {
+ 'id' : '5725a680bc670af746c435e2',
+ 'name' : 'Copeland',
+ 'avatar': 'assets/images/avatars/Copeland.jpg',
+ 'status': 'online',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...'
+ },
+ {
+ 'id' : '5725a680e7eb988a58ddf303',
+ 'name' : 'Estes',
+ 'avatar': 'assets/images/avatars/Estes.jpg',
+ 'status': 'away',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...'
+ },
+ {
+ 'id' : '5725a680dcb077889f758961',
+ 'name' : 'Harper',
+ 'avatar': 'assets/images/avatars/Harper.jpg',
+ 'status': 'offline',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...'
+ },
+ {
+ 'id' : '5725a6806acf030f9341e925',
+ 'name' : 'Helen',
+ 'avatar': 'assets/images/avatars/Helen.jpg',
+ 'status': 'away',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...'
+ },
+ {
+ 'id' : '5725a680ae1ae9a3c960d487',
+ 'name' : 'Henderson',
+ 'avatar': 'assets/images/avatars/Henderson.jpg',
+ 'status': 'offline',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...'
+ },
+ {
+ 'id' : '5725a680b8d240c011dd224b',
+ 'name' : 'Josefina',
+ 'avatar': 'assets/images/avatars/Josefina.jpg',
+ 'status': 'online',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...'
+ },
+ {
+ 'id' : '5725a68034cb3968e1f79eac',
+ 'name' : 'Katina',
+ 'avatar': 'assets/images/avatars/Katina.jpg',
+ 'status': 'away',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...'
+ },
+ {
+ 'id' : '5725a6801146cce777df2a08',
+ 'name' : 'Lily',
+ 'avatar': 'assets/images/avatars/Lily.jpg',
+ 'status': 'do-not-disturb',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...',
+ 'unread': '10'
+ },
+ {
+ 'id' : '5725a6808a178bfd034d6ecf',
+ 'name' : 'Mai',
+ 'avatar': 'assets/images/avatars/Mai.jpg',
+ 'status': 'away',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...'
+ },
+ {
+ 'id' : '5725a680653c265f5c79b5a9',
+ 'name' : 'Nancy',
+ 'avatar': 'assets/images/avatars/Nancy.jpg',
+ 'status': 'do-not-disturb',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...'
+ },
+ {
+ 'id' : '5725a680bbcec3cc32a8488a',
+ 'name' : 'Nora',
+ 'avatar': 'assets/images/avatars/Nora.jpg',
+ 'status': 'do-not-disturb',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...',
+ 'unread': '7'
+ },
+ {
+ 'id' : '5725a6803d87f1b77e17b62b',
+ 'name' : 'Odessa',
+ 'avatar': 'assets/images/avatars/Odessa.jpg',
+ 'status': 'away',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...',
+ 'unread': '1'
+ },
+ {
+ 'id' : '5725a680e87cb319bd9bd673',
+ 'name' : 'Reyna',
+ 'avatar': 'assets/images/avatars/Reyna.jpg',
+ 'status': 'offline',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...'
+ },
+ {
+ 'id' : '5725a6802d10e277a0f35775',
+ 'name' : 'Shauna',
+ 'avatar': 'assets/images/avatars/Shauna.jpg',
+ 'status': 'online',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...'
+ },
+ {
+ 'id' : '5725a680aef1e5cf26dd3d1f',
+ 'name' : 'Shepard',
+ 'avatar': 'assets/images/avatars/Shepard.jpg',
+ 'status': 'online',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...'
+ },
+ {
+ 'id' : '5725a680cd7efa56a45aea5d',
+ 'name' : 'Tillman',
+ 'avatar': 'assets/images/avatars/Tillman.jpg',
+ 'status': 'do-not-disturb',
+ 'mood' : '',
+ 'unread': '99+'
+ },
+ {
+ 'id' : '5725a680fb65c91a82cb35e2',
+ 'name' : 'Trevino',
+ 'avatar': 'assets/images/avatars/Trevino.jpg',
+ 'status': 'away',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...'
+ },
+ {
+ 'id' : '5725a68018c663044be49cbf',
+ 'name' : 'Tyson',
+ 'avatar': 'assets/images/avatars/Tyson.jpg',
+ 'status': 'do-not-disturb',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...'
+ },
+ {
+ 'id' : '5725a6809413bf8a0a5272b1',
+ 'name' : 'Velazquez',
+ 'avatar': 'assets/images/avatars/Velazquez.jpg',
+ 'status': 'online',
+ 'mood' : 'Lorem ipsum dolor sit amet, consectetur adipiscing elit...'
+ }
+ ];
+
+ public static chats = [
+ {
+ 'id' : '1725a680b3249760ea21de52',
+ 'dialog': [
+ {
+ 'who' : '5725a680b3249760ea21de52',
+ 'message': 'Quickly come to the meeting room 1B, we have a big server issue',
+ 'time' : '2017-03-22T08:54:28.299Z'
+ },
+ {
+ 'who' : '5725a6802d10e277a0f35724',
+ 'message': 'I’m having breakfast right now, can’t you wait for 10 minutes?',
+ 'time' : '2017-03-22T08:55:28.299Z'
+ },
+ {
+ 'who' : '5725a6802d10e277a0f35724',
+ 'message': 'I’m having breakfast right now, can’t you wait for 10 minutes?',
+ 'time' : '2017-03-22T08:55:28.299Z'
+ },
+ {
+ 'who' : '5725a680b3249760ea21de52',
+ 'message': 'We are losing money! Quick!',
+ 'time' : '2017-03-22T09:00:28.299Z'
+ },
+ {
+ 'who' : '5725a6802d10e277a0f35724',
+ 'message': 'It’s not my money, you know. I will eat my breakfast and then I will come to the meeting room.',
+ 'time' : '2017-03-22T09:02:28.299Z'
+ },
+ {
+ 'who' : '5725a680b3249760ea21de52',
+ 'message': 'You are the worst!',
+ 'time' : '2017-03-22T09:05:28.299Z'
+ },
+ {
+ 'who' : '5725a680b3249760ea21de52',
+ 'message': 'We are losing money! Quick!',
+ 'time' : '2017-03-22T09:15:28.299Z'
+ },
+ {
+ 'who' : '5725a680b3249760ea21de52',
+ 'message': 'You are the worst!',
+ 'time' : '2017-03-22T09:05:28.299Z'
+ },
+ {
+ 'who' : '5725a680b3249760ea21de52',
+ 'message': 'We are losing money! Quick!',
+ 'time' : '2017-03-22T09:15:28.299Z'
+ },
+ {
+ 'who' : '5725a6802d10e277a0f35724',
+ 'message': 'It’s not my money, you know. I will eat my breakfast and then I will come to the meeting room.',
+ 'time' : '2017-03-22T09:20:28.299Z'
+ },
+ {
+ 'who' : '5725a680b3249760ea21de52',
+ 'message': 'You are the worst!',
+ 'time' : '2017-03-22T09:22:28.299Z'
+ },
+ {
+ 'who' : '5725a680b3249760ea21de52',
+ 'message': 'We are losing money! Quick!',
+ 'time' : '2017-03-22T09:25:28.299Z'
+ },
+ {
+ 'who' : '5725a6802d10e277a0f35724',
+ 'message': 'It’s not my money, you know. I will eat my breakfast and then I will come to the meeting room.',
+ 'time' : '2017-03-22T09:27:28.299Z'
+ },
+ {
+ 'who' : '5725a680b3249760ea21de52',
+ 'message': 'You are the worst!',
+ 'time' : '2017-03-22T09:33:28.299Z'
+ },
+ {
+ 'who' : '5725a680b3249760ea21de52',
+ 'message': 'You are the worst!',
+ 'time' : '2017-03-22T09:33:28.299Z'
+ },
+ {
+ 'who' : '5725a680b3249760ea21de52',
+ 'message': 'We are losing money! Quick!',
+ 'time' : '2017-03-22T09:35:28.299Z'
+ },
+ {
+ 'who' : '5725a6802d10e277a0f35724',
+ 'message': 'It’s not my money, you know. I will eat my breakfast and then I will come to the meeting room.',
+ 'time' : '2017-03-22T09:45:28.299Z'
+ },
+ {
+ 'who' : '5725a680b3249760ea21de52',
+ 'message': 'You are the worst!',
+ 'time' : '2017-03-22T10:00:28.299Z'
+ }
+ ]
+ },
+ {
+ 'id' : '2725a680b8d240c011dd2243',
+ 'dialog': [
+ {
+ 'who' : '5725a680606588342058356d',
+ 'message': 'Quickly come to the meeting room 1B, we have a big server issue',
+ 'time' : '2017-04-22T01:00:00.299Z'
+ },
+ {
+ 'who' : '5725a6802d10e277a0f35724',
+ 'message': 'I’m having breakfast right now, can’t you wait for 10 minutes?',
+ 'time' : '2017-04-22T01:05:00.299Z'
+ },
+ {
+ 'who' : '5725a680606588342058356d',
+ 'message': 'We are losing money! Quick!',
+ 'time' : '2017-04-22T01:10:00.299Z'
+ }
+ ]
+ },
+ {
+ 'id' : '3725a6809413bf8a0a5272b4',
+ 'dialog': [
+ {
+ 'who' : '5725a68009e20d0a9e9acf2a',
+ 'message': 'Quickly come to the meeting room 1B, we have a big server issue',
+ 'time' : '2017-04-22T02:10:00.299Z'
+ }
+ ]
+ }
+ ];
+
+ public static user = [
+ {
+ 'id' : '5725a6802d10e277a0f35724',
+ 'name' : 'John Doe',
+ 'avatar' : 'assets/images/avatars/profile.jpg',
+ 'status' : 'online',
+ 'mood' : '',
+ 'chatList': [
+ {
+ 'chatId' : '1725a680b3249760ea21de52',
+ 'contactId' : '5725a680b3249760ea21de52',
+ 'lastMessageTime': '2017-06-12T02:10:18.931Z'
+ },
+ {
+ 'chatId' : '2725a680b8d240c011dd2243',
+ 'contactId' : '5725a680606588342058356d',
+ 'lastMessageTime': '2017-02-18T10:30:18.931Z'
+ },
+ {
+ 'chatId' : '3725a6809413bf8a0a5272b4',
+ 'contactId' : '5725a68009e20d0a9e9acf2a',
+ 'lastMessageTime': '2017-03-18T12:30:18.931Z'
+ }
+ ]
+ }
+ ];
+
+}
diff --git a/src/app/fuse-config/index.ts b/src/app/fuse-config/index.ts
index 4b9e9adc..aac07997 100644
--- a/src/app/fuse-config/index.ts
+++ b/src/app/fuse-config/index.ts
@@ -10,23 +10,24 @@ import { FuseConfig } from '@fuse/types';
export const fuseConfig: FuseConfig = {
layout : {
- style : 'vertical-layout-1',
- width : 'fullwidth',
- navbar : {
+ style : 'vertical-layout-1',
+ width : 'fullwidth',
+ navbar : {
+ background: 'mat-fuse-dark-700-bg',
+ folded : false,
hidden : false,
position : 'left',
- folded : false,
- background: 'mat-fuse-dark-700-bg'
+ variant : 'vertical-style-1'
},
- toolbar : {
+ toolbar: {
+ background: 'mat-white-500-bg',
hidden : false,
- position : 'below-static',
- background: 'mat-white-500-bg'
+ position : 'below-static'
},
- footer : {
+ footer : {
+ background: 'mat-fuse-dark-900-bg',
hidden : false,
- position : 'below-static',
- background: 'mat-fuse-dark-900-bg'
+ position : 'below-fixed'
}
},
customScrollbars: true
diff --git a/src/app/layout/components/chat-panel/chat-panel.component.html b/src/app/layout/components/chat-panel/chat-panel.component.html
new file mode 100644
index 00000000..585f2a8c
--- /dev/null
+++ b/src/app/layout/components/chat-panel/chat-panel.component.html
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+ {{contact.unread}}
+
+
+
+
+
+
+
+
+
+
+
+
+
0">
+
+
+
+
+
+
+
{{message.message}}
+
{{message.time | date:'short'}}
+
+
+
+
+
+
+
+
+
+ chat
+
+
+
+ Start a conversation by typing your message below.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/layout/components/chat-panel/chat-panel.component.scss b/src/app/layout/components/chat-panel/chat-panel.component.scss
new file mode 100644
index 00000000..93b0d321
--- /dev/null
+++ b/src/app/layout/components/chat-panel/chat-panel.component.scss
@@ -0,0 +1,374 @@
+@import "src/@fuse/scss/fuse";
+
+chat-panel {
+ display: flex;
+ flex-direction: column;
+ flex: 1 1 auto;
+ width: 360px;
+ min-width: 360px;
+ max-width: 360px;
+ z-index: 99;
+ overflow: hidden;
+
+ .header {
+ height: 64px;
+ max-height: 64px;
+ min-height: 64px;
+
+ .title {
+
+ mat-icon {
+ margin-left: 4px;
+ }
+
+ h3 {
+ max-width: 120px;
+ transition: opacity 300ms ease-in-out;
+ }
+ }
+ }
+
+ #contacts-list {
+ padding: 8px 0;
+ overflow: auto;
+ width: 72px;
+ min-width: 72px;
+ max-width: 72px;
+
+ // Perfect scrollbar
+ .ps__rail-y {
+ width: 3px !important;
+
+ .ps__thumb-y {
+ width: 3px !important;
+ }
+ }
+
+ .mat-list-item {
+ cursor: pointer;
+ position: relative;
+
+ &.active {
+ background-color: mat-color(mat-palette($mat-grey, 300));
+ @include mat-elevation(2);
+ }
+
+ &.offline {
+
+ .mat-list-item-content {
+
+ img {
+ filter: grayscale(100%);
+ opacity: 0.7;
+ }
+
+ h3 {
+ opacity: 0.7;
+ }
+ }
+ }
+
+ .mat-list-item-content {
+
+ .unread-count {
+ position: absolute;
+ min-width: 18px;
+ height: 18px;
+ top: 4px;
+ left: 10px;
+ border-radius: 9px;
+ padding: 0 5px;
+ font-size: 11px;
+ text-align: center;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: mat-color(mat-palette($mat-indigo));
+ color: white;
+ box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.35);
+ }
+
+ .status-icon {
+ position: absolute;
+ width: 12px;
+ height: 12px;
+ bottom: 3px;
+ left: 44px;
+ border: 2px solid white;
+ border-radius: 50%;
+
+ &.online {
+ background-color: #4CAF50;
+ }
+
+ &.do-not-disturb {
+ background-color: #F44336;
+ }
+
+ &.away {
+ background-color: #FFC107;
+ }
+
+ &.offline {
+ background-color: #646464;
+ }
+ }
+ }
+ }
+ }
+
+ #chat {
+ background-color: mat-color(mat-palette($mat-grey, 300));
+
+ .messages {
+ position: relative;
+ overflow: auto;
+ padding: 16px 0 40px 40px;
+
+ .message-row {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: flex-end;
+ padding: 0 16px 4px 16px;
+
+ .avatar {
+ position: absolute;
+ left: -32px;
+ margin: 0;
+ }
+
+ .bubble {
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 12px;
+
+ .message {
+ white-space: pre-wrap;
+ line-height: 1.2;
+ }
+
+ .time {
+ position: absolute;
+ display: none;
+ width: 100%;
+ font-size: 11px;
+ margin-top: 8px;
+ top: 100%;
+ color: $black-87-opacity;
+ white-space: nowrap;
+ }
+ }
+
+ &.contact {
+
+ .bubble {
+ background-color: mat-color(mat-palette($mat-indigo));
+ color: white;
+
+ border-top-left-radius: 5px;
+ border-bottom-left-radius: 5px;
+
+ border-top-right-radius: 20px;
+ border-bottom-right-radius: 20px;
+
+ .time {
+ margin-left: 12px;
+ }
+ }
+
+ &.first-of-group {
+
+ .bubble {
+ border-top-left-radius: 20px;
+ }
+ }
+
+ &.last-of-group {
+
+ .bubble {
+ border-bottom-left-radius: 20px;
+ }
+ }
+ }
+
+ &.me {
+ padding-left: 40px;
+
+ .avatar {
+ order: 2;
+ margin: 0 0 0 16px;
+ }
+
+ .bubble {
+ margin-left: auto;
+ background-color: #FFFFFF;
+ color: $black-87-opacity;
+
+ border-top-left-radius: 20px;
+ border-bottom-left-radius: 20px;
+
+ border-top-right-radius: 5px;
+ border-bottom-right-radius: 5px;
+
+ .time {
+ justify-content: flex-end;
+ right: 0;
+ margin-right: 12px;
+ }
+ }
+
+ &.first-of-group {
+
+ .bubble {
+ border-top-right-radius: 20px;
+ }
+ }
+
+ &.last-of-group {
+
+ .bubble {
+ border-bottom-right-radius: 20px;
+ }
+ }
+ }
+
+ &.contact + .me,
+ &.me + .contact {
+ padding-top: 20px;
+ margin-top: 20px;
+ }
+
+ &.first-of-group {
+
+ .bubble {
+ border-top-left-radius: 20px;
+ padding-top: 13px;
+ }
+ }
+
+ &.last-of-group {
+
+ .bubble {
+ border-bottom-left-radius: 20px;
+ padding-bottom: 13px;
+
+ .time {
+ display: flex;
+ }
+ }
+ }
+ }
+
+ .no-messages-icon {
+ position: absolute;
+ top: 50%;
+ right: 0;
+ left: 0;
+ padding: 0 24px;
+ margin-top: -64px;
+ text-align: center;
+
+ mat-icon {
+ color: rgba(0, 0, 0, 0.06);
+ }
+ }
+
+ .no-messages {
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ padding: 0 16px 24px 16px;
+ text-align: center;
+ }
+ }
+
+ .reply-form {
+ position: relative;
+
+ .message-text {
+ padding: 16px 8px;
+
+ .mat-form-field-wrapper {
+ padding: 0;
+
+ .mat-form-field-flex {
+ padding: 0;
+
+ .mat-form-field-infix {
+ padding: 0;
+ border: none;
+ background: white;
+ border-radius: 20px;
+ @include mat-elevation(2);
+
+ textarea {
+ overflow: hidden;
+ margin: 16px 48px 16px 16px;
+ width: calc(100% - 64px);
+ padding: 0;
+ }
+ }
+ }
+
+ .mat-form-field-underline {
+ display: none !important;
+ }
+ }
+ }
+
+ .send-message-button {
+ position: absolute;
+ right: 16px;
+ bottom: 21px;
+ }
+ }
+ }
+}
+
+fuse-sidebar {
+
+ &.chat-panel {
+ width: 360px;
+ min-width: 360px;
+ max-width: 360px;
+
+ // Folded
+ &.folded {
+
+ chat-panel {
+
+ .header {
+
+ .title {
+
+ h3 {
+ opacity: 0;
+ }
+ }
+ }
+ }
+
+ // Folded unfolded
+ &.unfolded {
+
+ chat-panel {
+
+ .header {
+
+ .title {
+
+ h3 {
+ opacity: 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/app/layout/components/chat-panel/chat-panel.component.ts b/src/app/layout/components/chat-panel/chat-panel.component.ts
new file mode 100644
index 00000000..cf7c04f9
--- /dev/null
+++ b/src/app/layout/components/chat-panel/chat-panel.component.ts
@@ -0,0 +1,269 @@
+import { AfterViewInit, Component, ElementRef, OnDestroy, OnInit, QueryList, ViewChild, ViewChildren, ViewEncapsulation } from '@angular/core';
+import { NgForm } from '@angular/forms';
+import { HttpClient } from '@angular/common/http';
+
+import { Subject } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
+
+import { FuseSidebarService } from '@fuse/components/sidebar/sidebar.service';
+import { FusePerfectScrollbarDirective } from '@fuse/directives/fuse-perfect-scrollbar/fuse-perfect-scrollbar.directive';
+import { ChatPanelService } from 'app/layout/components/chat-panel/chat-panel.service';
+
+@Component({
+ selector : 'chat-panel',
+ templateUrl : './chat-panel.component.html',
+ styleUrls : ['./chat-panel.component.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+export class ChatPanelComponent implements OnInit, AfterViewInit, OnDestroy
+{
+ contacts: any[];
+ chat: any;
+ selectedContact: any;
+ sidebarFolded: boolean;
+ user: any;
+
+ @ViewChild('replyForm')
+ set replyForm(content: NgForm)
+ {
+ this._replyForm = content;
+ }
+
+ @ViewChild('replyInput')
+ set replyInput(content: ElementRef)
+ {
+ this._replyInput = content;
+ }
+
+ @ViewChildren(FusePerfectScrollbarDirective)
+ private _fusePerfectScrollbarDirectives: QueryList;
+
+ // Private
+ private _chatViewScrollbar: FusePerfectScrollbarDirective;
+ private _replyForm: NgForm;
+ private _replyInput: ElementRef;
+ private _unsubscribeAll: Subject;
+
+ /**
+ * Constructor
+ *
+ * @param {ChatPanelService} _chatPanelService
+ * @param {HttpClient} _httpClient
+ * @param {FuseSidebarService} _fuseSidebarService
+ */
+ constructor(
+ private _chatPanelService: ChatPanelService,
+ private _httpClient: HttpClient,
+ private _fuseSidebarService: FuseSidebarService
+ )
+ {
+ // Set the defaults
+ this.selectedContact = null;
+ this.sidebarFolded = true;
+
+ // Set the private defaults
+ this._unsubscribeAll = new Subject();
+ }
+
+ // -----------------------------------------------------------------------------------------------------
+ // @ Lifecycle hooks
+ // -----------------------------------------------------------------------------------------------------
+
+ /**
+ * On init
+ */
+ ngOnInit(): void
+ {
+ // Load the contacts
+ this._chatPanelService.loadContacts().then(() => {
+
+ this.contacts = this._chatPanelService.contacts;
+ this.user = this._chatPanelService.user;
+ });
+
+ // Subscribe to the foldedChanged observable
+ this._fuseSidebarService.getSidebar('chatPanel').foldedChanged
+ .pipe(takeUntil(this._unsubscribeAll))
+ .subscribe((folded) => {
+ this.sidebarFolded = folded;
+ });
+ }
+
+ /**
+ * After view init
+ */
+ ngAfterViewInit(): void
+ {
+ this._chatViewScrollbar = this._fusePerfectScrollbarDirectives.find((directive) => {
+ return directive.elementRef.nativeElement.id === 'messages';
+ });
+ }
+
+ /**
+ * On destroy
+ */
+ ngOnDestroy(): void
+ {
+ // Unsubscribe from all subscriptions
+ this._unsubscribeAll.next();
+ this._unsubscribeAll.complete();
+ }
+
+ // -----------------------------------------------------------------------------------------------------
+ // @ Private methods
+ // -----------------------------------------------------------------------------------------------------
+
+ /**
+ * Prepare the chat for the replies
+ */
+ private _prepareChatForReplies(): void
+ {
+ setTimeout(() => {
+
+ // Reset the reply form
+ this._replyForm.reset();
+
+ // Focus to the reply input
+ this._replyInput.nativeElement.focus();
+
+ // Scroll to the bottom of the messages list
+ if ( this._chatViewScrollbar )
+ {
+ this._chatViewScrollbar.update();
+
+ setTimeout(() => {
+ this._chatViewScrollbar.scrollToBottom(0);
+ });
+ }
+ });
+ }
+
+ // -----------------------------------------------------------------------------------------------------
+ // @ Public methods
+ // -----------------------------------------------------------------------------------------------------
+
+ /**
+ * Toggle sidebar folded status
+ */
+ toggleSidebarFolded(): void
+ {
+ this._fuseSidebarService.getSidebar('chatPanel').toggleFold();
+ }
+
+ /**
+ * Fold the temporarily unfolded sidebar back
+ */
+ foldSidebarTemporarily(): void
+ {
+ this._fuseSidebarService.getSidebar('chatPanel').foldTemporarily();
+ }
+
+ /**
+ * Unfold the sidebar temporarily
+ */
+ unfoldSidebarTemporarily(): void
+ {
+ this._fuseSidebarService.getSidebar('chatPanel').unfoldTemporarily();
+ }
+
+ /**
+ * Toggle sidebar opened status
+ */
+ toggleSidebarOpen(): void
+ {
+ this._fuseSidebarService.getSidebar('chatPanel').toggleOpen();
+ }
+
+ /**
+ * Decide whether to show or not the contact's avatar in the message row
+ *
+ * @param message
+ * @param i
+ * @returns {boolean}
+ */
+ shouldShowContactAvatar(message, i): boolean
+ {
+ return (
+ message.who === this.selectedContact.id &&
+ ((this.chat.dialog[i + 1] && this.chat.dialog[i + 1].who !== this.selectedContact.id) || !this.chat.dialog[i + 1])
+ );
+ }
+
+ /**
+ * Check if the given message is the first message of a group
+ *
+ * @param message
+ * @param i
+ * @returns {boolean}
+ */
+ isFirstMessageOfGroup(message, i): boolean
+ {
+ return (i === 0 || this.chat.dialog[i - 1] && this.chat.dialog[i - 1].who !== message.who);
+ }
+
+ /**
+ * Check if the given message is the last message of a group
+ *
+ * @param message
+ * @param i
+ * @returns {boolean}
+ */
+ isLastMessageOfGroup(message, i): boolean
+ {
+ return (i === this.chat.dialog.length - 1 || this.chat.dialog[i + 1] && this.chat.dialog[i + 1].who !== message.who);
+ }
+
+ /**
+ * Go to chat with the contact
+ *
+ * @param contact
+ */
+ goToChat(contact): void
+ {
+ // Unfold the sidebar temporarily
+ this.unfoldSidebarTemporarily();
+
+ // Set the selected contact
+ this.selectedContact = contact;
+
+ // Load the chat
+ this._chatPanelService.getChat(contact.id).then((chat) => {
+
+ // Set the chat
+ this.chat = chat;
+
+ // Prepare the chat for the replies
+ this._prepareChatForReplies();
+ });
+ }
+
+ /**
+ * Reply
+ */
+ reply(event): void
+ {
+ event.preventDefault();
+
+ if ( !this._replyForm.form.value.message )
+ {
+ return;
+ }
+
+ // Message
+ const message = {
+ who : this.user.id,
+ message: this._replyForm.form.value.message,
+ time : new Date().toISOString()
+ };
+
+ // Add the message to the chat
+ this.chat.dialog.push(message);
+
+ // Update the server
+ this._chatPanelService.updateChat(this.chat.id, this.chat.dialog).then(response => {
+
+ // Prepare the chat for the replies
+ this._prepareChatForReplies();
+ });
+ }
+}
diff --git a/src/app/layout/components/chat-panel/chat-panel.module.ts b/src/app/layout/components/chat-panel/chat-panel.module.ts
new file mode 100644
index 00000000..5d9eb29b
--- /dev/null
+++ b/src/app/layout/components/chat-panel/chat-panel.module.ts
@@ -0,0 +1,34 @@
+import { NgModule } from '@angular/core';
+import { MatButtonModule, MatFormFieldModule, MatIconModule, MatInputModule, MatListModule, MatRippleModule, MatTabsModule, MatTooltipModule } from '@angular/material';
+
+import { FuseSharedModule } from '@fuse/shared.module';
+
+import { ChatPanelComponent } from 'app/layout/components/chat-panel/chat-panel.component';
+import { ChatPanelService } from 'app/layout/components/chat-panel/chat-panel.service';
+
+@NgModule({
+ declarations: [
+ ChatPanelComponent
+ ],
+ providers : [
+ ChatPanelService
+ ],
+ imports : [
+ MatButtonModule,
+ MatFormFieldModule,
+ MatIconModule,
+ MatInputModule,
+ MatListModule,
+ MatTabsModule,
+ MatTooltipModule,
+ MatRippleModule,
+
+ FuseSharedModule
+ ],
+ exports : [
+ ChatPanelComponent
+ ]
+})
+export class ChatPanelModule
+{
+}
diff --git a/src/app/layout/components/chat-panel/chat-panel.service.ts b/src/app/layout/components/chat-panel/chat-panel.service.ts
new file mode 100644
index 00000000..8aa1b952
--- /dev/null
+++ b/src/app/layout/components/chat-panel/chat-panel.service.ts
@@ -0,0 +1,182 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+
+import { FuseUtils } from '@fuse/utils';
+
+@Injectable()
+export class ChatPanelService
+{
+ contacts: any[];
+ chats: any[];
+ user: any;
+
+ /**
+ * Constructor
+ *
+ * @param {HttpClient} _httpClient
+ */
+ constructor(
+ private _httpClient: HttpClient
+ )
+ {
+ }
+
+ /**
+ * Loader
+ *
+ * @returns {Promise | any}
+ */
+ loadContacts(): Promise | any
+ {
+ return new Promise((resolve, reject) => {
+ Promise.all([
+ this.getContacts(),
+ this.getUser()
+ ]).then(
+ ([contacts, user]) => {
+ this.contacts = contacts;
+ this.user = user;
+ resolve();
+ },
+ reject
+ );
+ });
+ }
+
+ /**
+ * Get chat
+ *
+ * @param contactId
+ * @returns {Promise}
+ */
+ getChat(contactId): Promise
+ {
+ const chatItem = this.user.chatList.find((item) => {
+ return item.contactId === contactId;
+ });
+
+ // Get the chat
+ return new Promise((resolve, reject) => {
+
+ // If there is a chat with this user, return that.
+ if ( chatItem )
+ {
+ this._httpClient.get('api/chat-panel-chats/' + chatItem.chatId)
+ .subscribe((chat) => {
+
+ // Resolve the promise
+ resolve(chat);
+
+ }, reject);
+ }
+ // If there is no chat with this user, create one...
+ else
+ {
+ this.createNewChat(contactId).then(() => {
+
+ // and then recall the getChat method
+ this.getChat(contactId).then((chat) => {
+ resolve(chat);
+ });
+ });
+ }
+ });
+ }
+
+ /**
+ * Create new chat
+ *
+ * @param contactId
+ * @returns {Promise}
+ */
+ createNewChat(contactId): Promise
+ {
+ return new Promise((resolve, reject) => {
+
+ // Generate a new id
+ const chatId = FuseUtils.generateGUID();
+
+ // Prepare the chat object
+ const chat = {
+ id : chatId,
+ dialog: []
+ };
+
+ // Prepare the chat list entry
+ const chatListItem = {
+ chatId : chatId,
+ contactId : contactId,
+ lastMessageTime: '2017-02-18T10:30:18.931Z'
+ };
+
+ // Add new chat list item to the user's chat list
+ this.user.chatList.push(chatListItem);
+
+ // Post the created chat to the server
+ this._httpClient.post('api/chat-panel-chats', {...chat})
+ .subscribe(() => {
+
+ // Post the updated user data to the server
+ this._httpClient.post('api/chat-panel-user/' + this.user.id, this.user)
+ .subscribe(() => {
+
+ // Resolve the promise
+ resolve();
+ });
+ }, reject);
+ });
+ }
+
+ /**
+ * Update the chat
+ *
+ * @param chatId
+ * @param dialog
+ * @returns {Promise}
+ */
+ updateChat(chatId, dialog): Promise
+ {
+ return new Promise((resolve, reject) => {
+
+ const newData = {
+ id : chatId,
+ dialog: dialog
+ };
+
+ this._httpClient.post('api/chat-panel-chats/' + chatId, newData)
+ .subscribe(updatedChat => {
+ resolve(updatedChat);
+ }, reject);
+ });
+ }
+
+ /**
+ * Get contacts
+ *
+ * @returns {Promise}
+ */
+ getContacts(): Promise
+ {
+ return new Promise((resolve, reject) => {
+ this._httpClient.get('api/chat-panel-contacts')
+ .subscribe((response: any) => {
+ resolve(response);
+ }, reject);
+ });
+ }
+
+ /**
+ * Get user
+ *
+ * @returns {Promise}
+ */
+ getUser(): Promise
+ {
+ return new Promise((resolve, reject) => {
+ this._httpClient.get('api/chat-panel-user')
+ .subscribe((response: any) => {
+ resolve(response[0]);
+ }, reject);
+ });
+ }
+}
diff --git a/src/app/layout/components/footer/footer.component.html b/src/app/layout/components/footer/footer.component.html
index 9c23c155..3d6e909e 100644
--- a/src/app/layout/components/footer/footer.component.html
+++ b/src/app/layout/components/footer/footer.component.html
@@ -2,7 +2,7 @@
-
diff --git a/src/app/layout/components/navbar/horizontal/style-1/style-1.component.html b/src/app/layout/components/navbar/horizontal/style-1/style-1.component.html
new file mode 100644
index 00000000..5e6bb93d
--- /dev/null
+++ b/src/app/layout/components/navbar/horizontal/style-1/style-1.component.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/app/layout/components/navbar/horizontal/style-1/style-1.component.scss b/src/app/layout/components/navbar/horizontal/style-1/style-1.component.scss
new file mode 100644
index 00000000..9a5630e2
--- /dev/null
+++ b/src/app/layout/components/navbar/horizontal/style-1/style-1.component.scss
@@ -0,0 +1,3 @@
+navbar-horizontal-style-1 {
+
+}
diff --git a/src/app/layout/components/navbar/horizontal/style-1/style-1.component.ts b/src/app/layout/components/navbar/horizontal/style-1/style-1.component.ts
new file mode 100644
index 00000000..df2ecf03
--- /dev/null
+++ b/src/app/layout/components/navbar/horizontal/style-1/style-1.component.ts
@@ -0,0 +1,65 @@
+import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
+import { Subject } from 'rxjs';
+import { filter, takeUntil } from 'rxjs/operators';
+
+import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
+import { FuseSidebarService } from '@fuse/components/sidebar/sidebar.service';
+
+@Component({
+ selector : 'navbar-horizontal-style-1',
+ templateUrl : './style-1.component.html',
+ styleUrls : ['./style-1.component.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+export class NavbarHorizontalStyle1Component implements OnInit, OnDestroy
+{
+ navigation: any;
+
+ // Private
+ private _unsubscribeAll: Subject;
+
+ /**
+ * Constructor
+ *
+ * @param {FuseNavigationService} _fuseNavigationService
+ * @param {FuseSidebarService} _fuseSidebarService
+ */
+ constructor(
+ private _fuseNavigationService: FuseNavigationService,
+ private _fuseSidebarService: FuseSidebarService
+ )
+ {
+ // Set the private defaults
+ this._unsubscribeAll = new Subject();
+ }
+
+ // -----------------------------------------------------------------------------------------------------
+ // @ Lifecycle hooks
+ // -----------------------------------------------------------------------------------------------------
+
+ /**
+ * On init
+ */
+ ngOnInit(): void
+ {
+ // Get current navigation
+ this._fuseNavigationService.onNavigationChanged
+ .pipe(
+ filter(value => value !== null),
+ takeUntil(this._unsubscribeAll)
+ )
+ .subscribe(() => {
+ this.navigation = this._fuseNavigationService.getCurrentNavigation();
+ });
+ }
+
+ /**
+ * On destroy
+ */
+ ngOnDestroy(): void
+ {
+ // Unsubscribe from all subscriptions
+ this._unsubscribeAll.next();
+ this._unsubscribeAll.complete();
+ }
+}
diff --git a/src/app/layout/components/navbar/horizontal/style-1/style-1.module.ts b/src/app/layout/components/navbar/horizontal/style-1/style-1.module.ts
new file mode 100644
index 00000000..9f32f8a1
--- /dev/null
+++ b/src/app/layout/components/navbar/horizontal/style-1/style-1.module.ts
@@ -0,0 +1,26 @@
+import { NgModule } from '@angular/core';
+import { MatButtonModule, MatIconModule } from '@angular/material';
+
+import { FuseNavigationModule } from '@fuse/components';
+import { FuseSharedModule } from '@fuse/shared.module';
+
+import { NavbarHorizontalStyle1Component } from 'app/layout/components/navbar/horizontal/style-1/style-1.component';
+
+@NgModule({
+ declarations: [
+ NavbarHorizontalStyle1Component
+ ],
+ imports : [
+ MatButtonModule,
+ MatIconModule,
+
+ FuseSharedModule,
+ FuseNavigationModule
+ ],
+ exports : [
+ NavbarHorizontalStyle1Component
+ ]
+})
+export class NavbarHorizontalStyle1Module
+{
+}
diff --git a/src/app/layout/components/navbar/navbar.component.html b/src/app/layout/components/navbar/navbar.component.html
index 115b1902..158a127c 100644
--- a/src/app/layout/components/navbar/navbar.component.html
+++ b/src/app/layout/components/navbar/navbar.component.html
@@ -1,39 +1,11 @@
-
-
-
-
+
+
-
+
+
+
-
-
-
-
-
\ No newline at end of file
+
+
+
diff --git a/src/app/layout/components/navbar/navbar.component.scss b/src/app/layout/components/navbar/navbar.component.scss
index fc111989..e7408888 100644
--- a/src/app/layout/components/navbar/navbar.component.scss
+++ b/src/app/layout/components/navbar/navbar.component.scss
@@ -1,81 +1,5 @@
-@import "src/@fuse/scss/fuse";
-
-fuse-sidebar {
-
- &.folded:not(.unfolded) {
-
- .navbar-vertical {
-
- .navbar-header {
- padding: 0 13px;
-
- .logo {
-
- .logo-text {
- opacity: 0;
- transition: opacity 200ms ease;
- }
- }
- }
- }
- }
-}
-
navbar {
-
- &:not(.top-navbar) {
- height: 100%;
- overflow: hidden;
- }
-
- .navbar-vertical {
- display: flex;
- flex-direction: column;
- width: 100%;
- height: 100%;
-
- .navbar-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- height: 64px;
- min-height: 64px;
- padding: 0 16px 0 24px;
- transition: padding 200ms ease;
- background-color: rgba(255, 255, 255, .05);
- @include mat-elevation(1);
-
- .logo {
- display: flex;
- align-items: center;
-
- .logo-icon {
- width: 38px;
- height: 38px;
- }
-
- .logo-text {
- margin-left: 8px;
- font-size: 20px;
- font-weight: 300;
- letter-spacing: 0.4px;
- }
- }
- }
-
- .navbar-content {
- flex: 1 1 auto;
- overflow-y: auto;
- }
- }
-
- &.right-navbar {
-
- .toggle-sidebar-opened {
-
- mat-icon {
- transform: rotate(180deg);
- }
- }
- }
+ display: flex;
+ flex-direction: column;
+ width: 100%;
}
diff --git a/src/app/layout/components/navbar/navbar.component.ts b/src/app/layout/components/navbar/navbar.component.ts
index e4424f8a..c9544269 100644
--- a/src/app/layout/components/navbar/navbar.component.ts
+++ b/src/app/layout/components/navbar/navbar.component.ts
@@ -1,11 +1,4 @@
-import { Component, Input, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
-import { NavigationEnd, Router } from '@angular/router';
-import { Subject } from 'rxjs';
-import { filter, take, takeUntil } from 'rxjs/operators';
-
-import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
-import { FusePerfectScrollbarDirective } from '@fuse/directives/fuse-perfect-scrollbar/fuse-perfect-scrollbar.directive';
-import { FuseSidebarService } from '@fuse/components/sidebar/sidebar.service';
+import { Component, Input, ViewEncapsulation } from '@angular/core';
@Component({
selector : 'navbar',
@@ -13,148 +6,18 @@ import { FuseSidebarService } from '@fuse/components/sidebar/sidebar.service';
styleUrls : ['./navbar.component.scss'],
encapsulation: ViewEncapsulation.None
})
-export class NavbarComponent implements OnInit, OnDestroy
+export class NavbarComponent
{
- // Layout
+ // Variant
@Input()
- layout;
-
- fusePerfectScrollbarUpdateTimeout: any;
- navigation: any;
-
- // Private
- private _fusePerfectScrollbar: FusePerfectScrollbarDirective;
- private _unsubscribeAll: Subject;
+ variant;
/**
* Constructor
- *
- * @param {FuseNavigationService} _fuseNavigationService
- * @param {FuseSidebarService} _fuseSidebarService
- * @param {Router} _router
*/
- constructor(
- private _fuseNavigationService: FuseNavigationService,
- private _fuseSidebarService: FuseSidebarService,
- private _router: Router
- )
+ constructor()
{
// Set the defaults
- this.layout = 'vertical';
-
- // Set the private defaults
- this._unsubscribeAll = new Subject();
- }
-
- // -----------------------------------------------------------------------------------------------------
- // @ Accessors
- // -----------------------------------------------------------------------------------------------------
-
- // Directive
- @ViewChild(FusePerfectScrollbarDirective)
- set directive(theDirective: FusePerfectScrollbarDirective)
- {
- if ( !theDirective )
- {
- return;
- }
-
- this._fusePerfectScrollbar = theDirective;
-
- // Update the scrollbar on collapsable item toggle
- this._fuseNavigationService.onItemCollapseToggled
- .pipe(takeUntil(this._unsubscribeAll))
- .subscribe(() => {
- this.fusePerfectScrollbarUpdateTimeout = setTimeout(() => {
- this._fusePerfectScrollbar.update();
- }, 310);
- });
-
- // Scroll to the active item position
- this._router.events
- .pipe(
- filter((event) => event instanceof NavigationEnd),
- take(1)
- )
- .subscribe(() => {
- setTimeout(() => {
- const activeNavItem: any = document.querySelector('navbar .nav-link.active');
-
- if ( activeNavItem )
- {
- const activeItemOffsetTop = activeNavItem.offsetTop,
- activeItemOffsetParentTop = activeNavItem.offsetParent.offsetTop,
- scrollDistance = activeItemOffsetTop - activeItemOffsetParentTop - (48 * 3);
-
- this._fusePerfectScrollbar.scrollToTop(scrollDistance);
- }
- });
- }
- );
- }
-
- // -----------------------------------------------------------------------------------------------------
- // @ Lifecycle hooks
- // -----------------------------------------------------------------------------------------------------
-
- /**
- * On init
- */
- ngOnInit(): void
- {
- this._router.events
- .pipe(
- filter((event) => event instanceof NavigationEnd),
- takeUntil(this._unsubscribeAll)
- )
- .subscribe(() => {
- if ( this._fuseSidebarService.getSidebar('navbar') )
- {
- this._fuseSidebarService.getSidebar('navbar').close();
- }
- }
- );
-
- // Get current navigation
- this._fuseNavigationService.onNavigationChanged
- .pipe(filter(value => value !== null))
- .subscribe(() => {
- this.navigation = this._fuseNavigationService.getCurrentNavigation();
- });
- }
-
- /**
- * On destroy
- */
- ngOnDestroy(): void
- {
- if ( this.fusePerfectScrollbarUpdateTimeout )
- {
- clearTimeout(this.fusePerfectScrollbarUpdateTimeout);
- }
-
- // Unsubscribe from all subscriptions
- this._unsubscribeAll.next();
- this._unsubscribeAll.complete();
- }
-
- // -----------------------------------------------------------------------------------------------------
- // @ Public methods
- // -----------------------------------------------------------------------------------------------------
-
- /**
- * Toggle sidebar opened status
- */
- toggleSidebarOpened(): void
- {
- this._fuseSidebarService.getSidebar('navbar').toggleOpen();
- }
-
- /**
- * Toggle sidebar folded status
- */
- toggleSidebarFolded(): void
- {
- this._fuseSidebarService.getSidebar('navbar').toggleFold();
+ this.variant = 'vertical-style-1';
}
}
diff --git a/src/app/layout/components/navbar/navbar.module.ts b/src/app/layout/components/navbar/navbar.module.ts
index acb91738..67005f0f 100644
--- a/src/app/layout/components/navbar/navbar.module.ts
+++ b/src/app/layout/components/navbar/navbar.module.ts
@@ -1,21 +1,22 @@
import { NgModule } from '@angular/core';
-import { MatButtonModule, MatIconModule } from '@angular/material';
-import { FuseNavigationModule } from '@fuse/components';
import { FuseSharedModule } from '@fuse/shared.module';
import { NavbarComponent } from 'app/layout/components/navbar/navbar.component';
+import { NavbarHorizontalStyle1Module } from 'app/layout/components/navbar/horizontal/style-1/style-1.module';
+import { NavbarVerticalStyle1Module } from 'app/layout/components/navbar/vertical/style-1/style-1.module';
+import { NavbarVerticalStyle2Module } from 'app/layout/components/navbar/vertical/style-2/style-2.module';
@NgModule({
declarations: [
NavbarComponent
],
imports : [
- MatButtonModule,
- MatIconModule,
-
FuseSharedModule,
- FuseNavigationModule
+
+ NavbarHorizontalStyle1Module,
+ NavbarVerticalStyle1Module,
+ NavbarVerticalStyle2Module
],
exports : [
NavbarComponent
diff --git a/src/app/layout/components/navbar/vertical/style-1/style-1.component.html b/src/app/layout/components/navbar/vertical/style-1/style-1.component.html
new file mode 100644
index 00000000..a8299367
--- /dev/null
+++ b/src/app/layout/components/navbar/vertical/style-1/style-1.component.html
@@ -0,0 +1,41 @@
+
+
+
+
+
FUSE
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/layout/components/navbar/vertical/style-1/style-1.component.scss b/src/app/layout/components/navbar/vertical/style-1/style-1.component.scss
new file mode 100644
index 00000000..4099e475
--- /dev/null
+++ b/src/app/layout/components/navbar/vertical/style-1/style-1.component.scss
@@ -0,0 +1,159 @@
+@import "src/@fuse/scss/fuse";
+
+fuse-sidebar {
+ overflow: hidden;
+
+ &.folded:not(.unfolded) {
+
+ navbar {
+
+ navbar-vertical-style-1 {
+
+ .navbar-top {
+ padding: 12px 0;
+ justify-content: center;
+
+ .buttons {
+ display: none;
+ }
+
+ .logo {
+
+ .logo-icon {
+ width: 32px;
+ height: 32px;
+ }
+
+ .logo-text {
+ display: none;
+ }
+ }
+ }
+
+ .navbar-scroll-container {
+
+ .user {
+ padding: 12px 0;
+
+ .avatar-container {
+ position: relative;
+ top: auto;
+ padding: 0;
+
+ .avatar {
+ width: 40px;
+ height: 40px;
+ }
+ }
+
+ .username,
+ .email {
+ display: none;
+ }
+ }
+
+ .navbar-content {
+ margin-top: 0;
+ }
+ }
+ }
+ }
+ }
+}
+
+navbar {
+
+ navbar-vertical-style-1 {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ height: 100%;
+
+ .navbar-top {
+ display: flex;
+ flex-direction: row;
+ flex: 1 0 auto;
+ align-items: center;
+ justify-content: space-between;
+ min-height: 64px;
+ height: 64px;
+ padding: 12px 12px 12px 20px;
+
+ @include media-breakpoint('xs') {
+ min-height: 56px;
+ height: 56px;
+ }
+
+ .logo {
+ display: flex;
+ align-items: center;
+
+ .logo-icon {
+ width: 24px;
+ height: 24px;
+ }
+
+ .logo-text {
+ margin-left: 12px;
+ font-size: 16px;
+ font-weight: 300;
+ letter-spacing: 0.4px;
+ line-height: normal;
+ }
+ }
+
+ .buttons {
+ display: flex;
+ align-items: center;
+ }
+ }
+
+ .navbar-scroll-container {
+ overflow-y: auto;
+
+ background: linear-gradient(rgba(0, 0, 0, 0) 30%, rgba(0, 0, 0, 0) 30%),
+ linear-gradient(rgba(0, 0, 0, 0.25) 0, rgba(0, 0, 0, 0) 40%);
+
+ background-repeat: no-repeat;
+ background-size: 100% 40px, 100% 10px;
+ background-attachment: local, scroll;
+
+ .user {
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: flex-start;
+ width: 100%;
+ padding: 24px 0 64px 0;
+
+ .avatar-container {
+ position: absolute;
+ top: 92px;
+ border-radius: 50%;
+ padding: 8px;
+
+ .avatar {
+ width: 72px;
+ height: 72px;
+ margin: 0;
+ }
+ }
+ }
+
+ .navbar-content {
+ flex: 1 1 auto;
+ margin-top: 32px;
+ }
+ }
+ }
+
+ &.right-navbar {
+
+ .toggle-sidebar-opened {
+
+ mat-icon {
+ transform: rotate(180deg);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/app/layout/components/navbar/vertical/style-1/style-1.component.ts b/src/app/layout/components/navbar/vertical/style-1/style-1.component.ts
new file mode 100644
index 00000000..acdc3238
--- /dev/null
+++ b/src/app/layout/components/navbar/vertical/style-1/style-1.component.ts
@@ -0,0 +1,167 @@
+import { Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
+import { NavigationEnd, Router } from '@angular/router';
+import { Subject } from 'rxjs';
+import { filter, take, takeUntil } from 'rxjs/operators';
+
+import { FuseConfigService } from '@fuse/services/config.service';
+import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
+import { FusePerfectScrollbarDirective } from '@fuse/directives/fuse-perfect-scrollbar/fuse-perfect-scrollbar.directive';
+import { FuseSidebarService } from '@fuse/components/sidebar/sidebar.service';
+
+@Component({
+ selector : 'navbar-vertical-style-1',
+ templateUrl : './style-1.component.html',
+ styleUrls : ['./style-1.component.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+export class NavbarVerticalStyle1Component implements OnInit, OnDestroy
+{
+ fuseConfig: any;
+ fusePerfectScrollbarUpdateTimeout: any;
+ navigation: any;
+
+ // Private
+ private _fusePerfectScrollbar: FusePerfectScrollbarDirective;
+ private _unsubscribeAll: Subject;
+
+ /**
+ * Constructor
+ *
+ * @param {FuseConfigService} _fuseConfigService
+ * @param {FuseNavigationService} _fuseNavigationService
+ * @param {FuseSidebarService} _fuseSidebarService
+ * @param {Router} _router
+ */
+ constructor(
+ private _fuseConfigService: FuseConfigService,
+ private _fuseNavigationService: FuseNavigationService,
+ private _fuseSidebarService: FuseSidebarService,
+ private _router: Router
+ )
+ {
+ // Set the private defaults
+ this._unsubscribeAll = new Subject();
+ }
+
+ // -----------------------------------------------------------------------------------------------------
+ // @ Accessors
+ // -----------------------------------------------------------------------------------------------------
+
+ // Directive
+ @ViewChild(FusePerfectScrollbarDirective)
+ set directive(theDirective: FusePerfectScrollbarDirective)
+ {
+ if ( !theDirective )
+ {
+ return;
+ }
+
+ this._fusePerfectScrollbar = theDirective;
+
+ // Update the scrollbar on collapsable item toggle
+ this._fuseNavigationService.onItemCollapseToggled
+ .pipe(takeUntil(this._unsubscribeAll))
+ .subscribe(() => {
+ this.fusePerfectScrollbarUpdateTimeout = setTimeout(() => {
+ this._fusePerfectScrollbar.update();
+ }, 310);
+ });
+
+ // Scroll to the active item position
+ this._router.events
+ .pipe(
+ filter((event) => event instanceof NavigationEnd),
+ take(1)
+ )
+ .subscribe(() => {
+ setTimeout(() => {
+ const activeNavItem: any = document.querySelector('navbar .nav-link.active');
+
+ if ( activeNavItem )
+ {
+ const activeItemOffsetTop = activeNavItem.offsetTop,
+ activeItemOffsetParentTop = activeNavItem.offsetParent.offsetTop,
+ scrollDistance = activeItemOffsetTop - activeItemOffsetParentTop - (48 * 3) - 168;
+
+ this._fusePerfectScrollbar.scrollToTop(scrollDistance);
+ }
+ });
+ }
+ );
+ }
+
+ // -----------------------------------------------------------------------------------------------------
+ // @ Lifecycle hooks
+ // -----------------------------------------------------------------------------------------------------
+
+ /**
+ * On init
+ */
+ ngOnInit(): void
+ {
+ this._router.events
+ .pipe(
+ filter((event) => event instanceof NavigationEnd),
+ takeUntil(this._unsubscribeAll)
+ )
+ .subscribe(() => {
+ if ( this._fuseSidebarService.getSidebar('navbar') )
+ {
+ this._fuseSidebarService.getSidebar('navbar').close();
+ }
+ }
+ );
+
+ // Subscribe to the config changes
+ this._fuseConfigService.config
+ .pipe(takeUntil(this._unsubscribeAll))
+ .subscribe((config) => {
+ this.fuseConfig = config;
+ });
+
+ // Get current navigation
+ this._fuseNavigationService.onNavigationChanged
+ .pipe(
+ filter(value => value !== null),
+ takeUntil(this._unsubscribeAll)
+ )
+ .subscribe(() => {
+ this.navigation = this._fuseNavigationService.getCurrentNavigation();
+ });
+ }
+
+ /**
+ * On destroy
+ */
+ ngOnDestroy(): void
+ {
+ if ( this.fusePerfectScrollbarUpdateTimeout )
+ {
+ clearTimeout(this.fusePerfectScrollbarUpdateTimeout);
+ }
+
+ // Unsubscribe from all subscriptions
+ this._unsubscribeAll.next();
+ this._unsubscribeAll.complete();
+ }
+
+ // -----------------------------------------------------------------------------------------------------
+ // @ Public methods
+ // -----------------------------------------------------------------------------------------------------
+
+ /**
+ * Toggle sidebar opened status
+ */
+ toggleSidebarOpened(): void
+ {
+ this._fuseSidebarService.getSidebar('navbar').toggleOpen();
+ }
+
+ /**
+ * Toggle sidebar folded status
+ */
+ toggleSidebarFolded(): void
+ {
+ this._fuseSidebarService.getSidebar('navbar').toggleFold();
+ }
+}
diff --git a/src/app/layout/components/navbar/vertical/style-1/style-1.module.ts b/src/app/layout/components/navbar/vertical/style-1/style-1.module.ts
new file mode 100644
index 00000000..f452e93f
--- /dev/null
+++ b/src/app/layout/components/navbar/vertical/style-1/style-1.module.ts
@@ -0,0 +1,26 @@
+import { NgModule } from '@angular/core';
+import { MatButtonModule, MatIconModule } from '@angular/material';
+
+import { FuseNavigationModule } from '@fuse/components';
+import { FuseSharedModule } from '@fuse/shared.module';
+
+import { NavbarVerticalStyle1Component } from 'app/layout/components/navbar/vertical/style-1/style-1.component';
+
+@NgModule({
+ declarations: [
+ NavbarVerticalStyle1Component
+ ],
+ imports : [
+ MatButtonModule,
+ MatIconModule,
+
+ FuseSharedModule,
+ FuseNavigationModule
+ ],
+ exports : [
+ NavbarVerticalStyle1Component
+ ]
+})
+export class NavbarVerticalStyle1Module
+{
+}
diff --git a/src/app/layout/components/navbar/vertical/style-2/style-2.component.html b/src/app/layout/components/navbar/vertical/style-2/style-2.component.html
new file mode 100644
index 00000000..601617f3
--- /dev/null
+++ b/src/app/layout/components/navbar/vertical/style-2/style-2.component.html
@@ -0,0 +1,23 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/layout/components/navbar/vertical/style-2/style-2.component.scss b/src/app/layout/components/navbar/vertical/style-2/style-2.component.scss
new file mode 100644
index 00000000..d150731e
--- /dev/null
+++ b/src/app/layout/components/navbar/vertical/style-2/style-2.component.scss
@@ -0,0 +1,81 @@
+@import "src/@fuse/scss/fuse";
+
+fuse-sidebar {
+ overflow: hidden;
+
+ &.folded:not(.unfolded) {
+
+ navbar {
+
+ navbar-vertical-style-2 {
+
+ .navbar-header {
+ padding: 0 13px;
+
+ .logo {
+
+ .logo-text {
+ opacity: 0;
+ transition: opacity 200ms ease;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+navbar {
+
+ navbar-vertical-style-2 {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ height: 100%;
+
+ .navbar-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ height: 64px;
+ min-height: 64px;
+ padding: 0 16px 0 24px;
+ transition: padding 200ms ease;
+ background-color: rgba(255, 255, 255, .05);
+ @include mat-elevation(1);
+
+ .logo {
+ display: flex;
+ align-items: center;
+
+ .logo-icon {
+ width: 38px;
+ height: 38px;
+ }
+
+ .logo-text {
+ margin-left: 8px;
+ font-size: 20px;
+ font-weight: 300;
+ letter-spacing: 0.4px;
+ }
+ }
+ }
+
+ .navbar-content {
+ flex: 1 1 auto;
+ overflow-y: auto;
+ }
+
+ }
+
+ &.right-navbar {
+
+ .toggle-sidebar-opened {
+
+ mat-icon {
+ transform: rotate(180deg);
+ }
+ }
+ }
+}
diff --git a/src/app/layout/components/navbar/vertical/style-2/style-2.component.ts b/src/app/layout/components/navbar/vertical/style-2/style-2.component.ts
new file mode 100644
index 00000000..29e4f440
--- /dev/null
+++ b/src/app/layout/components/navbar/vertical/style-2/style-2.component.ts
@@ -0,0 +1,156 @@
+import { Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
+import { NavigationEnd, Router } from '@angular/router';
+import { Subject } from 'rxjs';
+import { filter, take, takeUntil } from 'rxjs/operators';
+
+import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
+import { FusePerfectScrollbarDirective } from '@fuse/directives/fuse-perfect-scrollbar/fuse-perfect-scrollbar.directive';
+import { FuseSidebarService } from '@fuse/components/sidebar/sidebar.service';
+
+@Component({
+ selector : 'navbar-vertical-style-2',
+ templateUrl : './style-2.component.html',
+ styleUrls : ['./style-2.component.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+export class NavbarVerticalStyle2Component implements OnInit, OnDestroy
+{
+ fusePerfectScrollbarUpdateTimeout: any;
+ navigation: any;
+
+ // Private
+ private _fusePerfectScrollbar: FusePerfectScrollbarDirective;
+ private _unsubscribeAll: Subject;
+
+ /**
+ * Constructor
+ *
+ * @param {FuseNavigationService} _fuseNavigationService
+ * @param {FuseSidebarService} _fuseSidebarService
+ * @param {Router} _router
+ */
+ constructor(
+ private _fuseNavigationService: FuseNavigationService,
+ private _fuseSidebarService: FuseSidebarService,
+ private _router: Router
+ )
+ {
+ // Set the private defaults
+ this._unsubscribeAll = new Subject();
+ }
+
+ // -----------------------------------------------------------------------------------------------------
+ // @ Accessors
+ // -----------------------------------------------------------------------------------------------------
+
+ // Directive
+ @ViewChild(FusePerfectScrollbarDirective)
+ set directive(theDirective: FusePerfectScrollbarDirective)
+ {
+ if ( !theDirective )
+ {
+ return;
+ }
+
+ this._fusePerfectScrollbar = theDirective;
+
+ // Update the scrollbar on collapsable item toggle
+ this._fuseNavigationService.onItemCollapseToggled
+ .pipe(takeUntil(this._unsubscribeAll))
+ .subscribe(() => {
+ this.fusePerfectScrollbarUpdateTimeout = setTimeout(() => {
+ this._fusePerfectScrollbar.update();
+ }, 310);
+ });
+
+ // Scroll to the active item position
+ this._router.events
+ .pipe(
+ filter((event) => event instanceof NavigationEnd),
+ take(1)
+ )
+ .subscribe(() => {
+ setTimeout(() => {
+ const activeNavItem: any = document.querySelector('navbar .nav-link.active');
+
+ if ( activeNavItem )
+ {
+ const activeItemOffsetTop = activeNavItem.offsetTop,
+ activeItemOffsetParentTop = activeNavItem.offsetParent.offsetTop,
+ scrollDistance = activeItemOffsetTop - activeItemOffsetParentTop - (48 * 3);
+
+ this._fusePerfectScrollbar.scrollToTop(scrollDistance);
+ }
+ });
+ }
+ );
+ }
+
+ // -----------------------------------------------------------------------------------------------------
+ // @ Lifecycle hooks
+ // -----------------------------------------------------------------------------------------------------
+
+ /**
+ * On init
+ */
+ ngOnInit(): void
+ {
+ this._router.events
+ .pipe(
+ filter((event) => event instanceof NavigationEnd),
+ takeUntil(this._unsubscribeAll)
+ )
+ .subscribe(() => {
+ if ( this._fuseSidebarService.getSidebar('navbar') )
+ {
+ this._fuseSidebarService.getSidebar('navbar').close();
+ }
+ }
+ );
+
+ // Get current navigation
+ this._fuseNavigationService.onNavigationChanged
+ .pipe(
+ filter(value => value !== null),
+ takeUntil(this._unsubscribeAll)
+ )
+ .subscribe(() => {
+ this.navigation = this._fuseNavigationService.getCurrentNavigation();
+ });
+ }
+
+ /**
+ * On destroy
+ */
+ ngOnDestroy(): void
+ {
+ if ( this.fusePerfectScrollbarUpdateTimeout )
+ {
+ clearTimeout(this.fusePerfectScrollbarUpdateTimeout);
+ }
+
+ // Unsubscribe from all subscriptions
+ this._unsubscribeAll.next();
+ this._unsubscribeAll.complete();
+ }
+
+ // -----------------------------------------------------------------------------------------------------
+ // @ Public methods
+ // -----------------------------------------------------------------------------------------------------
+
+ /**
+ * Toggle sidebar opened status
+ */
+ toggleSidebarOpened(): void
+ {
+ this._fuseSidebarService.getSidebar('navbar').toggleOpen();
+ }
+
+ /**
+ * Toggle sidebar folded status
+ */
+ toggleSidebarFolded(): void
+ {
+ this._fuseSidebarService.getSidebar('navbar').toggleFold();
+ }
+}
diff --git a/src/app/layout/components/navbar/vertical/style-2/style-2.module.ts b/src/app/layout/components/navbar/vertical/style-2/style-2.module.ts
new file mode 100644
index 00000000..4968641b
--- /dev/null
+++ b/src/app/layout/components/navbar/vertical/style-2/style-2.module.ts
@@ -0,0 +1,26 @@
+import { NgModule } from '@angular/core';
+import { MatButtonModule, MatIconModule } from '@angular/material';
+
+import { FuseNavigationModule } from '@fuse/components';
+import { FuseSharedModule } from '@fuse/shared.module';
+
+import { NavbarVerticalStyle2Component } from 'app/layout/components/navbar/vertical/style-2/style-2.component';
+
+@NgModule({
+ declarations: [
+ NavbarVerticalStyle2Component
+ ],
+ imports : [
+ MatButtonModule,
+ MatIconModule,
+
+ FuseSharedModule,
+ FuseNavigationModule
+ ],
+ exports : [
+ NavbarVerticalStyle2Component
+ ]
+})
+export class NavbarVerticalStyle2Module
+{
+}
diff --git a/src/app/layout/components/toolbar/toolbar.component.html b/src/app/layout/components/toolbar/toolbar.component.html
index 14d2e6f5..60d0e50f 100644
--- a/src/app/layout/components/toolbar/toolbar.component.html
+++ b/src/app/layout/components/toolbar/toolbar.component.html
@@ -30,8 +30,8 @@
@@ -83,6 +83,15 @@
+
+
+
+