From e182f196446bf986cff768a5a3b5236e8c2ce6d6 Mon Sep 17 00:00:00 2001 From: Sercan Yemen Date: Mon, 2 Jul 2018 19:48:48 +0300 Subject: [PATCH 01/29] Fix scss import path --- src/app/layout/vertical/layout-3/layout-3.component.scss | 2 +- .../example-viewer/example-viewer.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/layout/vertical/layout-3/layout-3.component.scss b/src/app/layout/vertical/layout-3/layout-3.component.scss index 295c20d7..686cb918 100644 --- a/src/app/layout/vertical/layout-3/layout-3.component.scss +++ b/src/app/layout/vertical/layout-3/layout-3.component.scss @@ -1,4 +1,4 @@ -@import "../../../../@fuse/scss/fuse"; +@import "src/@fuse/scss/fuse"; vertical-layout-3 { display: flex; diff --git a/src/app/main/angular-material-elements/example-viewer/example-viewer.scss b/src/app/main/angular-material-elements/example-viewer/example-viewer.scss index 096de499..244aed9c 100644 --- a/src/app/main/angular-material-elements/example-viewer/example-viewer.scss +++ b/src/app/main/angular-material-elements/example-viewer/example-viewer.scss @@ -1,4 +1,4 @@ -@import "../../../../@fuse/scss/fuse"; +@import "src/@fuse/scss/fuse"; example-viewer { display: block; From 51d7c6fd6fdd8f3697043c229d8dcaada3318985 Mon Sep 17 00:00:00 2001 From: Sercan Yemen Date: Mon, 2 Jul 2018 19:51:37 +0300 Subject: [PATCH 02/29] (Contacts) Make the "Add Contact" button sticky on mobile devices --- .../apps/contacts/contact-list/contact-list.component.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/app/main/apps/contacts/contact-list/contact-list.component.scss b/src/app/main/apps/contacts/contact-list/contact-list.component.scss index a8b26b96..f00b223f 100644 --- a/src/app/main/apps/contacts/contact-list/contact-list.component.scss +++ b/src/app/main/apps/contacts/contact-list/contact-list.component.scss @@ -50,4 +50,10 @@ contacts-contact-list { right: 12px; padding: 0; z-index: 99; + + @include media-breakpoint-down('xs') { + position: sticky; + top: calc(100vh - 72px); + bottom: auto; + } } \ No newline at end of file From 89f71735a8335b525690c06fbc7b22f8b3d6bca5 Mon Sep 17 00:00:00 2001 From: Sercan Yemen Date: Tue, 3 Jul 2018 13:51:31 +0300 Subject: [PATCH 03/29] (Footer) Make the footer fixed for the Demo so the links can be seen at all times --- src/app/fuse-config/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/fuse-config/index.ts b/src/app/fuse-config/index.ts index 4b9e9adc..cf924d74 100644 --- a/src/app/fuse-config/index.ts +++ b/src/app/fuse-config/index.ts @@ -25,7 +25,7 @@ export const fuseConfig: FuseConfig = { }, footer : { hidden : false, - position : 'below-static', + position : 'below-fixed', background: 'mat-fuse-dark-900-bg' } }, From d781e5992815573ecb8469e609aaf017bf1fbc57 Mon Sep 17 00:00:00 2001 From: Sercan Yemen Date: Tue, 3 Jul 2018 13:54:19 +0300 Subject: [PATCH 04/29] (Sidebar) Added "foldedWidth" input for controlling the width of the folded sidebar (Sidebar) Replaced the margin with padding on the folded sidebar's sibling (Docs) Updated Sidebar docs --- .../components/sidebar/sidebar.component.scss | 6 --- .../components/sidebar/sidebar.component.ts | 54 +++++++++++++++---- .../components/sidebar/sidebar.component.html | 7 +++ 3 files changed, 52 insertions(+), 15 deletions(-) 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..4f80c255 100644 --- a/src/@fuse/components/sidebar/sidebar.component.ts +++ b/src/@fuse/components/sidebar/sidebar.component.ts @@ -40,6 +40,10 @@ export class FuseSidebarComponent implements OnInit, OnDestroy @HostBinding('class.locked-open') isLockedOpen: boolean; + // Folded width + @Input() + foldedWidth: number; + // Folded unfolded @HostBinding('class.unfolded') unfolded: boolean; @@ -92,6 +96,7 @@ export class FuseSidebarComponent implements OnInit, OnDestroy ) { // Set the defaults + this.foldedWidth = 64; this.foldedChanged = new EventEmitter(); this.openedChanged = new EventEmitter(); this.opened = false; @@ -108,7 +113,11 @@ export class FuseSidebarComponent implements OnInit, OnDestroy // @ Accessors // ----------------------------------------------------------------------------------------------------- - // Folded + /** + * Folded + * + * @param {boolean} value + */ @Input() set folded(value: boolean) { @@ -121,23 +130,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 +161,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 +176,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 +394,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 +422,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'); @@ -645,6 +669,11 @@ export class FuseSidebarComponent implements OnInit, OnDestroy // 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(); } @@ -667,6 +696,13 @@ export class FuseSidebarComponent implements OnInit, OnDestroy // 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(); } diff --git a/src/app/main/documentation/components/sidebar/sidebar.component.html b/src/app/main/documentation/components/sidebar/sidebar.component.html index 57e15554..2e1a5ef9 100644 --- a/src/app/main/documentation/components/sidebar/sidebar.component.html +++ b/src/app/main/documentation/components/sidebar/sidebar.component.html @@ -47,6 +47,13 @@

+
+

[foldedWidth]

+

+ Controls the width of the sidebar when it's folded. +

+
+

[position]

From fb003bc96f4473872d2bb6a05f9537ac5205566b Mon Sep 17 00:00:00 2001 From: Sercan Yemen Date: Tue, 3 Jul 2018 18:06:33 +0300 Subject: [PATCH 05/29] (Scrumboard) Removed an extra semi-colon --- .../main/apps/scrumboard/board/list/card/card.component.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/main/apps/scrumboard/board/list/card/card.component.scss b/src/app/main/apps/scrumboard/board/list/card/card.component.scss index 10fe2b1b..50731afd 100644 --- a/src/app/main/apps/scrumboard/board/list/card/card.component.scss +++ b/src/app/main/apps/scrumboard/board/list/card/card.component.scss @@ -58,7 +58,7 @@ } &.due-date { - background-color: mat-color(mat-palette($mat-green));; + background-color: mat-color(mat-palette($mat-green)); &.overdue { background-color: mat-color(mat-palette($mat-red)); From 595b16275bd63f333b5eb3a68994022e839dde99 Mon Sep 17 00:00:00 2001 From: Sercan Yemen Date: Tue, 3 Jul 2018 18:23:51 +0300 Subject: [PATCH 06/29] (Chat Panel) Added a chat panel --- src/app/app.component.scss | 4 +- src/app/fake-db/chat-panel.ts | 323 ++++++++++++++++++ src/app/fake-db/fake-db.service.ts | 6 + .../chat-panel/chat-panel.component.html | 105 ++++++ .../chat-panel/chat-panel.component.scss | 218 ++++++++++++ .../chat-panel/chat-panel.component.ts | 217 ++++++++++++ .../chat-panel/chat-panel.module.ts | 33 ++ .../chat-panel/chat-panel.service.ts | 182 ++++++++++ .../components/toolbar/toolbar.component.html | 11 +- .../components/toolbar/toolbar.component.scss | 1 + .../components/toolbar/toolbar.component.ts | 23 ++ .../layout-1/layout-1.component.html | 8 + .../layout-1/layout-1.component.scss | 1 + .../horizontal/layout-1/layout-1.module.ts | 2 + .../vertical/layout-1/layout-1.component.html | 8 + .../vertical/layout-1/layout-1.component.scss | 1 + .../vertical/layout-1/layout-1.module.ts | 2 + .../vertical/layout-2/layout-2.component.html | 8 + .../vertical/layout-2/layout-2.component.scss | 1 + .../vertical/layout-2/layout-2.module.ts | 2 + .../vertical/layout-3/layout-3.component.html | 8 + .../vertical/layout-3/layout-3.component.scss | 1 + .../vertical/layout-3/layout-3.module.ts | 2 + 23 files changed, 1164 insertions(+), 3 deletions(-) create mode 100644 src/app/fake-db/chat-panel.ts create mode 100644 src/app/layout/components/chat-panel/chat-panel.component.html create mode 100644 src/app/layout/components/chat-panel/chat-panel.component.scss create mode 100644 src/app/layout/components/chat-panel/chat-panel.component.ts create mode 100644 src/app/layout/components/chat-panel/chat-panel.module.ts create mode 100644 src/app/layout/components/chat-panel/chat-panel.service.ts diff --git a/src/app/app.component.scss b/src/app/app.component.scss index 42a78d3d..6e7a0eff 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -9,7 +9,7 @@ .theme-options-button { position: absolute; top: 160px; - right: 0; + right: 70px; width: 48px; height: 48px; line-height: 48px; @@ -18,7 +18,7 @@ border-radius: 0; margin: 0; pointer-events: auto; - opacity: .75; + opacity: .90; z-index: 998; mat-icon { diff --git a/src/app/fake-db/chat-panel.ts b/src/app/fake-db/chat-panel.ts new file mode 100644 index 00000000..878c69a1 --- /dev/null +++ b/src/app/fake-db/chat-panel.ts @@ -0,0 +1,323 @@ +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' : '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' : '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': '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' : '5725a680b8d240c011dd224b', + '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' : '5725a680b8d240c011dd224b', + 'message': 'We are losing money! Quick!', + 'time' : '2017-04-22T01:10:00.299Z' + } + ] + }, + { + 'id' : '3725a6809413bf8a0a5272b4', + 'dialog': [ + { + 'who' : '5725a6809413bf8a0a5272b1', + '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/fake-db/fake-db.service.ts b/src/app/fake-db/fake-db.service.ts index 32027ba6..de47b4b6 100644 --- a/src/app/fake-db/fake-db.service.ts +++ b/src/app/fake-db/fake-db.service.ts @@ -17,6 +17,7 @@ import { SearchFakeDb } from 'app/fake-db/search'; import { FaqFakeDb } from 'app/fake-db/faq'; import { KnowledgeBaseFakeDb } from 'app/fake-db/knowledge-base'; import { IconsFakeDb } from 'app/fake-db/icons'; +import { ChatPanelFakeDb } from 'app/fake-db/chat-panel'; import { QuickPanelFakeDb } from 'app/fake-db/quick-panel'; export class FakeDbService implements InMemoryDbService @@ -89,6 +90,11 @@ export class FakeDbService implements InMemoryDbService // Icons 'icons': IconsFakeDb.icons, + // Chat Panel + 'chat-panel-contacts' : ChatPanelFakeDb.contacts, + 'chat-panel-chats': ChatPanelFakeDb.chats, + 'chat-panel-user': ChatPanelFakeDb.user, + // Quick Panel 'quick-panel-notes' : QuickPanelFakeDb.notes, 'quick-panel-events': QuickPanelFakeDb.events 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..5bc074af --- /dev/null +++ b/src/app/layout/components/chat-panel/chat-panel.component.html @@ -0,0 +1,105 @@ +

+ + + +
+ account_circle +

Team Members

+
+ +
+ + + +
+ + +

{{contact.name}}

+
+ +
+ + + + + +
+ + + + + + + + + +

+ {{contact.name}} +

+ +
{{contact.unread}}
+
+ +
+ +
+ +
+ + + +
+ +
+ +
+ + + + + +
+
{{message.message}}
+
{{message.time | date:'medium'}}
+
+ +
+ +
+ +
+ +
+ + + + + + + +
+ +
+ +
+ +
\ 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..51f951c2 --- /dev/null +++ b/src/app/layout/components/chat-panel/chat-panel.component.scss @@ -0,0 +1,218 @@ +@import "src/@fuse/scss/fuse"; + +chat-panel { + display: flex; + flex-direction: column; + flex: 1 1 auto; + width: 280px; + min-width: 280px; + max-width: 280px; + 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; + + .mat-list-item { + cursor: pointer; + position: relative; + + &.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, 200)); + + .messages { + overflow: auto; + + .message-row { + padding: 16px; + + .bubble { + position: relative; + padding: 6px 7px 8px 9px; + background-color: #FFF; + box-shadow: 0 1px .5px rgba(0, 0, 0, .13); + border-radius: 6px; + + &:before { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAmCAMAAADp2asXAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAADGUExURQAAAP////b29vn5+f///wAAAP///wAAAAAAAP///9ra2v////j4+PHx8fv7++Hh4fHx8f////////////////39/QAAAP////////z8/P////39/f39/fz8/P////////////z8/P////////////z8/P////////////v7+/Hx8f///9bW1vz8/K2trf////39/f39/WJiYgAAAExMTFtbWwAAAN3d3cjIyPr6+vX19QAAAO7u7vz8/NTU1Ofn5zMzM////zGPlXsAAABBdFJOUwAcm/kREh4CCDWL1SneR6TfAQffhMYK/A5nRrLWfRc5DW2ih5f+19Kn+9v4g/1LCJuXHwQUKgahcXS6DNnlDMMKKzPoTgAAAKBJREFUKM+V08USwmAQA+C/0NIWd3d3d8/7vxTMcIPkQK7f7CG7s8bQAOY/SCuwFYQU1P+eiCqIK2gpWCmoCrAgoKQgJ8CHgIqAMjg0MxxSQ3DogEMWFBZtUPAHYGB1CyDQWE6AH7BrfXzlAxGAQhECTGAmwN1Okz0Gb/LW4fEItIfrOfNELMh3tck7u+PhcT2zQ7l77/K8iY8yJwV3BeYFqpc/uSyPGdAAAAAASUVORK5CYII=); + content: ''; + position: absolute; + left: -11px; + bottom: 3px; + width: 12px; + height: 19px; + background-position: 50% 50%; + background-repeat: no-repeat; + background-size: contain; + } + + .message { + white-space: pre-wrap; + } + + .time { + font-size: 11px; + margin-top: 8px; + text-align: right; + } + } + + &.contact { + + .avatar { + margin: 0 16px 0 0; + } + } + + &.user { + align-items: flex-end; + + .avatar { + order: 2; + margin: 0 0 0 16px; + } + + .bubble { + margin-left: auto; + background-color: #E8F5E9; + border: 1px solid #DFEBE0; + order: 1; + &:before { + right: -11px; + left: auto; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAmCAMAAADp2asXAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAD2UExURQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRsXAAAANzwzNPmxNrtyau5oIWRedDkwNntyczgwdfpyJ+/n97wzsLWtNjsytvwzczfvtPmxau6nNjqxtrtyio1KtzwzNjryAAAANzwzgAAANzwzK7Aor/Us9Lnw8vevAAAAMzevtbpxrvMrX+IdwAAAEROOi45Lr3MrZGjf9LoxX+MctnqydLkwhgYGMzfv9vuyQAAANzwzNvuy9zxy7vMu7XGqNvtzKKykwAAANruzKq6nLnMriQkGMXXuL3PsNjsySgzKAAAANLkw83fvd3vy9z4xtzwzRpFmIEAAABQdFJOUwAXChEGBAMBAgwhDvJ7k0YqMc0Zmwj6apf2kjU0+dkw/swh/CP9j2Wr2gndvaYeBRoxQg6gUPt/FaHJGdTj9A9k7XQLeE6iFcN12xkSt9r4NKizowAAAMFJREFUKM+V0sdywlAMBVDbMX7PQCihQ+iQ0HsJvfem/P/PwBIzugu0PXNnNNJVyPmhsIPhhoB2COwIGuLdhAcl3AhCBoBoHUC6BCBbA0C/EkBFB5D/FjxQwQYg1RI8UKINgDoSAPUlAPqUAMgfAEBfXsEDBV0+Hogi4Zhg4THj9YwHoqEBYOrgYTI3GVgMNn8r+Qq94k9yZNosW/3Hy9VuTjWfHkOX6367bGZUU7de66ieHZrO1OGg8Z1WTgYAFLgD5S1PCkzo1B0AAAAASUVORK5CYII=); + } + } + } + } + } + + .reply-form { + position: relative; + @include mat-elevation(8); + } + } +} + +fuse-sidebar { + + &.chat-panel { + + // 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..e3a6cfe8 --- /dev/null +++ b/src/app/layout/components/chat-panel/chat-panel.component.ts @@ -0,0 +1,217 @@ +import { Component, ElementRef, OnDestroy, OnInit, ViewChild, 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, OnDestroy +{ + contacts: any[]; + contact: any; + chat: any; + sidebarFolded: boolean; + user: any; + view: string; + + @ViewChild(FusePerfectScrollbarDirective) + set fusePerfectScrollbarDirective(content: FusePerfectScrollbarDirective) + { + this._fusePerfectScrollbarDirective = content; + } + + @ViewChild('replyForm') + set replyForm(content: NgForm) + { + this._replyForm = content; + } + + @ViewChild('replyInput') + set replyInput(content: ElementRef) + { + this._replyInput = content; + } + + // Private + private _fusePerfectScrollbarDirective: 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.contact = null; + this.sidebarFolded = true; + this.view = 'contacts'; + + // 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; + }); + } + + /** + * 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._fusePerfectScrollbarDirective ) + { + this._fusePerfectScrollbarDirective.update(); + + setTimeout(() => { + this._fusePerfectScrollbarDirective.scrollToBottom(0); + }); + } + }); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Toggle sidebar folded status + */ + toggleSidebarFolded(): void + { + this._fuseSidebarService.getSidebar('chatPanel').toggleFold(); + } + + /** + * Toggle sidebar opened status + */ + toggleSidebarOpen(): void + { + this._fuseSidebarService.getSidebar('chatPanel').toggleOpen(); + } + + /** + * Go to chat with the contact + * + * @param contact + */ + goToChat(contact): void + { + // Change the view + this.view = 'chat'; + + // Set the current contact + this.contact = 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(); + }); + } + + /** + * Go to contact view + */ + goToContacts(): void + { + // Change the view + this.view = 'contacts'; + + // Set the current contact as null + this.contact = null; + + // Clear the chat data + this.chat = null; + } + + /** + * Reply + */ + reply(): void + { + // 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..8963e64d --- /dev/null +++ b/src/app/layout/components/chat-panel/chat-panel.module.ts @@ -0,0 +1,33 @@ +import { NgModule } from '@angular/core'; +import { MatButtonModule, MatFormFieldModule, MatIconModule, MatInputModule, MatListModule, MatRippleModule, MatTabsModule } 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, + 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/toolbar/toolbar.component.html b/src/app/layout/components/toolbar/toolbar.component.html index 7ac75bcf..1607df5e 100644 --- a/src/app/layout/components/toolbar/toolbar.component.html +++ b/src/app/layout/components/toolbar/toolbar.component.html @@ -30,7 +30,7 @@ + +
+
+ + + + + + diff --git a/src/app/layout/horizontal/layout-1/layout-1.component.scss b/src/app/layout/horizontal/layout-1/layout-1.component.scss index b92cdd75..2b0d322d 100644 --- a/src/app/layout/horizontal/layout-1/layout-1.component.scss +++ b/src/app/layout/horizontal/layout-1/layout-1.component.scss @@ -14,6 +14,7 @@ horizontal-layout-1 { width: 100%; height: 100%; z-index: 1; + min-width: 0; // Boxed &.boxed { diff --git a/src/app/layout/horizontal/layout-1/layout-1.module.ts b/src/app/layout/horizontal/layout-1/layout-1.module.ts index ee92e4b6..20388cfb 100644 --- a/src/app/layout/horizontal/layout-1/layout-1.module.ts +++ b/src/app/layout/horizontal/layout-1/layout-1.module.ts @@ -4,6 +4,7 @@ import { MatSidenavModule } from '@angular/material'; import { FuseSidebarModule, FuseThemeOptionsModule } from '@fuse/components'; import { FuseSharedModule } from '@fuse/shared.module'; +import { ChatPanelModule } from 'app/layout/components/chat-panel/chat-panel.module'; import { ContentModule } from 'app/layout/components/content/content.module'; import { FooterModule } from 'app/layout/components/footer/footer.module'; import { NavbarModule } from 'app/layout/components/navbar/navbar.module'; @@ -23,6 +24,7 @@ import { HorizontalLayout1Component } from 'app/layout/horizontal/layout-1/layou FuseSidebarModule, FuseThemeOptionsModule, + ChatPanelModule, ContentModule, FooterModule, NavbarModule, diff --git a/src/app/layout/vertical/layout-1/layout-1.component.html b/src/app/layout/vertical/layout-1/layout-1.component.html index 2d6dd0a9..b8a5e19a 100644 --- a/src/app/layout/vertical/layout-1/layout-1.component.html +++ b/src/app/layout/vertical/layout-1/layout-1.component.html @@ -67,6 +67,14 @@ + + + + + + diff --git a/src/app/layout/vertical/layout-1/layout-1.component.scss b/src/app/layout/vertical/layout-1/layout-1.component.scss index e9d12711..104739f9 100644 --- a/src/app/layout/vertical/layout-1/layout-1.component.scss +++ b/src/app/layout/vertical/layout-1/layout-1.component.scss @@ -14,6 +14,7 @@ vertical-layout-1 { width: 100%; height: 100%; z-index: 1; + min-width: 0; // Boxed &.boxed { diff --git a/src/app/layout/vertical/layout-1/layout-1.module.ts b/src/app/layout/vertical/layout-1/layout-1.module.ts index a4d70f06..8d62f2c5 100644 --- a/src/app/layout/vertical/layout-1/layout-1.module.ts +++ b/src/app/layout/vertical/layout-1/layout-1.module.ts @@ -4,6 +4,7 @@ import { RouterModule } from '@angular/router'; import { FuseSidebarModule } from '@fuse/components'; import { FuseSharedModule } from '@fuse/shared.module'; +import { ChatPanelModule } from 'app/layout/components/chat-panel/chat-panel.module'; import { ContentModule } from 'app/layout/components/content/content.module'; import { FooterModule } from 'app/layout/components/footer/footer.module'; import { NavbarModule } from 'app/layout/components/navbar/navbar.module'; @@ -22,6 +23,7 @@ import { VerticalLayout1Component } from 'app/layout/vertical/layout-1/layout-1. FuseSharedModule, FuseSidebarModule, + ChatPanelModule, ContentModule, FooterModule, NavbarModule, diff --git a/src/app/layout/vertical/layout-2/layout-2.component.html b/src/app/layout/vertical/layout-2/layout-2.component.html index 8e03dc7e..9f57af55 100644 --- a/src/app/layout/vertical/layout-2/layout-2.component.html +++ b/src/app/layout/vertical/layout-2/layout-2.component.html @@ -67,6 +67,14 @@ + + + + + + diff --git a/src/app/layout/vertical/layout-2/layout-2.component.scss b/src/app/layout/vertical/layout-2/layout-2.component.scss index 1ade45b0..e1e6cecb 100644 --- a/src/app/layout/vertical/layout-2/layout-2.component.scss +++ b/src/app/layout/vertical/layout-2/layout-2.component.scss @@ -14,6 +14,7 @@ vertical-layout-2 { width: 100%; height: 100%; z-index: 1; + min-width: 0; // Boxed &.boxed { diff --git a/src/app/layout/vertical/layout-2/layout-2.module.ts b/src/app/layout/vertical/layout-2/layout-2.module.ts index 64c7540f..be9edaa5 100644 --- a/src/app/layout/vertical/layout-2/layout-2.module.ts +++ b/src/app/layout/vertical/layout-2/layout-2.module.ts @@ -4,6 +4,7 @@ import { RouterModule } from '@angular/router'; import { FuseSidebarModule } from '@fuse/components'; import { FuseSharedModule } from '@fuse/shared.module'; +import { ChatPanelModule } from 'app/layout/components/chat-panel/chat-panel.module'; import { ContentModule } from 'app/layout/components/content/content.module'; import { FooterModule } from 'app/layout/components/footer/footer.module'; import { NavbarModule } from 'app/layout/components/navbar/navbar.module'; @@ -22,6 +23,7 @@ import { VerticalLayout2Component } from 'app/layout/vertical/layout-2/layout-2. FuseSharedModule, FuseSidebarModule, + ChatPanelModule, ContentModule, FooterModule, NavbarModule, diff --git a/src/app/layout/vertical/layout-3/layout-3.component.html b/src/app/layout/vertical/layout-3/layout-3.component.html index c7e8c0de..3b31bd0d 100644 --- a/src/app/layout/vertical/layout-3/layout-3.component.html +++ b/src/app/layout/vertical/layout-3/layout-3.component.html @@ -53,6 +53,14 @@ + + + + + + diff --git a/src/app/layout/vertical/layout-3/layout-3.component.scss b/src/app/layout/vertical/layout-3/layout-3.component.scss index 686cb918..1351e9b5 100644 --- a/src/app/layout/vertical/layout-3/layout-3.component.scss +++ b/src/app/layout/vertical/layout-3/layout-3.component.scss @@ -14,6 +14,7 @@ vertical-layout-3 { width: 100%; height: 100%; z-index: 1; + min-width: 0; // Boxed &.boxed { diff --git a/src/app/layout/vertical/layout-3/layout-3.module.ts b/src/app/layout/vertical/layout-3/layout-3.module.ts index 145b01cf..7975ca44 100644 --- a/src/app/layout/vertical/layout-3/layout-3.module.ts +++ b/src/app/layout/vertical/layout-3/layout-3.module.ts @@ -4,6 +4,7 @@ import { RouterModule } from '@angular/router'; import { FuseSidebarModule } from '@fuse/components/index'; import { FuseSharedModule } from '@fuse/shared.module'; +import { ChatPanelModule } from 'app/layout/components/chat-panel/chat-panel.module'; import { ContentModule } from 'app/layout/components/content/content.module'; import { FooterModule } from 'app/layout/components/footer/footer.module'; import { NavbarModule } from 'app/layout/components/navbar/navbar.module'; @@ -22,6 +23,7 @@ import { VerticalLayout3Component } from 'app/layout/vertical/layout-3/layout-3. FuseSharedModule, FuseSidebarModule, + ChatPanelModule, ContentModule, FooterModule, NavbarModule, From 49c8e32dce0c4ebff90167d3dce5588cb22b5916 Mon Sep 17 00:00:00 2001 From: Sercan Yemen Date: Tue, 3 Jul 2018 21:05:52 +0300 Subject: [PATCH 07/29] (Chat Panel) Styling adjustments --- src/app/fake-db/chat-panel.ts | 26 +++- .../chat-panel/chat-panel.component.html | 19 ++- .../chat-panel/chat-panel.component.scss | 138 ++++++++++++++---- .../chat-panel/chat-panel.component.ts | 48 +++++- 4 files changed, 188 insertions(+), 43 deletions(-) diff --git a/src/app/fake-db/chat-panel.ts b/src/app/fake-db/chat-panel.ts index 878c69a1..ec738bef 100644 --- a/src/app/fake-db/chat-panel.ts +++ b/src/app/fake-db/chat-panel.ts @@ -199,6 +199,11 @@ export class ChatPanelFakeDb '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!', @@ -219,6 +224,16 @@ export class ChatPanelFakeDb '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.', @@ -244,6 +259,11 @@ export class ChatPanelFakeDb '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!', @@ -265,7 +285,7 @@ export class ChatPanelFakeDb 'id' : '2725a680b8d240c011dd2243', 'dialog': [ { - 'who' : '5725a680b8d240c011dd224b', + 'who' : '5725a680606588342058356d', 'message': 'Quickly come to the meeting room 1B, we have a big server issue', 'time' : '2017-04-22T01:00:00.299Z' }, @@ -275,7 +295,7 @@ export class ChatPanelFakeDb 'time' : '2017-04-22T01:05:00.299Z' }, { - 'who' : '5725a680b8d240c011dd224b', + 'who' : '5725a680606588342058356d', 'message': 'We are losing money! Quick!', 'time' : '2017-04-22T01:10:00.299Z' } @@ -285,7 +305,7 @@ export class ChatPanelFakeDb 'id' : '3725a6809413bf8a0a5272b4', 'dialog': [ { - 'who' : '5725a6809413bf8a0a5272b1', + 'who' : '5725a68009e20d0a9e9acf2a', 'message': 'Quickly come to the meeting room 1B, we have a big server issue', 'time' : '2017-04-22T02:10:00.299Z' } diff --git a/src/app/layout/components/chat-panel/chat-panel.component.html b/src/app/layout/components/chat-panel/chat-panel.component.html index 5bc074af..fae3c12b 100644 --- a/src/app/layout/components/chat-panel/chat-panel.component.html +++ b/src/app/layout/components/chat-panel/chat-panel.component.html @@ -60,18 +60,21 @@
-
+
- - -
{{message.message}}
-
{{message.time | date:'medium'}}
+
{{message.time | date:'short'}}
@@ -81,8 +84,8 @@
diff --git a/src/app/layout/components/chat-panel/chat-panel.component.scss b/src/app/layout/components/chat-panel/chat-panel.component.scss index 51f951c2..ab6237f2 100644 --- a/src/app/layout/components/chat-panel/chat-panel.component.scss +++ b/src/app/layout/components/chat-panel/chat-panel.component.scss @@ -101,54 +101,84 @@ chat-panel { } #chat { - background-color: mat-color(mat-palette($mat-grey, 200)); + background-color: mat-color(mat-palette($mat-grey, 300)); .messages { overflow: auto; + padding: 16px 0 40px 40px; .message-row { - padding: 16px; + 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; - padding: 6px 7px 8px 9px; - background-color: #FFF; - box-shadow: 0 1px .5px rgba(0, 0, 0, .13); - border-radius: 6px; - - &:before { - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAmCAMAAADp2asXAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAADGUExURQAAAP////b29vn5+f///wAAAP///wAAAAAAAP///9ra2v////j4+PHx8fv7++Hh4fHx8f////////////////39/QAAAP////////z8/P////39/f39/fz8/P////////////z8/P////////////z8/P////////////v7+/Hx8f///9bW1vz8/K2trf////39/f39/WJiYgAAAExMTFtbWwAAAN3d3cjIyPr6+vX19QAAAO7u7vz8/NTU1Ofn5zMzM////zGPlXsAAABBdFJOUwAcm/kREh4CCDWL1SneR6TfAQffhMYK/A5nRrLWfRc5DW2ih5f+19Kn+9v4g/1LCJuXHwQUKgahcXS6DNnlDMMKKzPoTgAAAKBJREFUKM+V08USwmAQA+C/0NIWd3d3d8/7vxTMcIPkQK7f7CG7s8bQAOY/SCuwFYQU1P+eiCqIK2gpWCmoCrAgoKQgJ8CHgIqAMjg0MxxSQ3DogEMWFBZtUPAHYGB1CyDQWE6AH7BrfXzlAxGAQhECTGAmwN1Okz0Gb/LW4fEItIfrOfNELMh3tck7u+PhcT2zQ7l77/K8iY8yJwV3BeYFqpc/uSyPGdAAAAAASUVORK5CYII=); - content: ''; - position: absolute; - left: -11px; - bottom: 3px; - width: 12px; - height: 19px; - background-position: 50% 50%; - background-repeat: no-repeat; - background-size: contain; - } + 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; - text-align: right; + top: 100%; + color: $black-87-opacity; + white-space: nowrap; } } &.contact { - .avatar { - margin: 0 16px 0 0; + .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; + } } } - &.user { - align-items: flex-end; + &.me { + padding-left: 40px; .avatar { order: 2; @@ -157,13 +187,59 @@ chat-panel { .bubble { margin-left: auto; - background-color: #E8F5E9; - border: 1px solid #DFEBE0; - order: 1; - &:before { - right: -11px; - left: auto; - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAmCAMAAADp2asXAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAD2UExURQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRsXAAAANzwzNPmxNrtyau5oIWRedDkwNntyczgwdfpyJ+/n97wzsLWtNjsytvwzczfvtPmxau6nNjqxtrtyio1KtzwzNjryAAAANzwzgAAANzwzK7Aor/Us9Lnw8vevAAAAMzevtbpxrvMrX+IdwAAAEROOi45Lr3MrZGjf9LoxX+MctnqydLkwhgYGMzfv9vuyQAAANzwzNvuy9zxy7vMu7XGqNvtzKKykwAAANruzKq6nLnMriQkGMXXuL3PsNjsySgzKAAAANLkw83fvd3vy9z4xtzwzRpFmIEAAABQdFJOUwAXChEGBAMBAgwhDvJ7k0YqMc0Zmwj6apf2kjU0+dkw/swh/CP9j2Wr2gndvaYeBRoxQg6gUPt/FaHJGdTj9A9k7XQLeE6iFcN12xkSt9r4NKizowAAAMFJREFUKM+V0sdywlAMBVDbMX7PQCihQ+iQ0HsJvfem/P/PwBIzugu0PXNnNNJVyPmhsIPhhoB2COwIGuLdhAcl3AhCBoBoHUC6BCBbA0C/EkBFB5D/FjxQwQYg1RI8UKINgDoSAPUlAPqUAMgfAEBfXsEDBV0+Hogi4Zhg4THj9YwHoqEBYOrgYTI3GVgMNn8r+Qq94k9yZNosW/3Hy9VuTjWfHkOX6367bGZUU7de66ieHZrO1OGg8Z1WTgYAFLgD5S1PCkzo1B0AAAAASUVORK5CYII=); + 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; } } } diff --git a/src/app/layout/components/chat-panel/chat-panel.component.ts b/src/app/layout/components/chat-panel/chat-panel.component.ts index e3a6cfe8..2eba8086 100644 --- a/src/app/layout/components/chat-panel/chat-panel.component.ts +++ b/src/app/layout/components/chat-panel/chat-panel.component.ts @@ -153,6 +153,45 @@ export class ChatPanelComponent implements OnInit, OnDestroy 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.contact.id && + ((this.chat.dialog[i + 1] && this.chat.dialog[i + 1].who !== this.contact.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 * @@ -195,8 +234,15 @@ export class ChatPanelComponent implements OnInit, OnDestroy /** * Reply */ - reply(): void + reply(event): void { + event.preventDefault(); + + if ( !this._replyForm.form.value.message ) + { + return; + } + // Message const message = { who : this.user.id, From dbb925334ae4eb311ecb5629866e94941c79665e Mon Sep 17 00:00:00 2001 From: Sercan Yemen Date: Wed, 4 Jul 2018 17:21:51 +0300 Subject: [PATCH 08/29] (Chat Panel) Styling adjustments --- .../chat-panel/chat-panel.component.html | 23 +++++-- .../chat-panel/chat-panel.component.scss | 62 ++++++++++++++++++- 2 files changed, 78 insertions(+), 7 deletions(-) diff --git a/src/app/layout/components/chat-panel/chat-panel.component.html b/src/app/layout/components/chat-panel/chat-panel.component.html index fae3c12b..1da2ea29 100644 --- a/src/app/layout/components/chat-panel/chat-panel.component.html +++ b/src/app/layout/components/chat-panel/chat-panel.component.html @@ -79,6 +79,17 @@
+ + +
+ chat +
+ +
+ Start a conversation by typing your message below. +
+
+
@@ -86,17 +97,17 @@ - - + + - diff --git a/src/app/layout/components/chat-panel/chat-panel.component.scss b/src/app/layout/components/chat-panel/chat-panel.component.scss index ab6237f2..fbd52c1f 100644 --- a/src/app/layout/components/chat-panel/chat-panel.component.scss +++ b/src/app/layout/components/chat-panel/chat-panel.component.scss @@ -104,6 +104,7 @@ chat-panel { background-color: mat-color(mat-palette($mat-grey, 300)); .messages { + position: relative; overflow: auto; padding: 16px 0 40px 40px; @@ -244,11 +245,70 @@ chat-panel { } } } + + .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; - @include mat-elevation(8); + + .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; + } } } } From 8355e8a17c3df2b2fb40d79bad5cc694d0ed8b8d Mon Sep 17 00:00:00 2001 From: Sercan Yemen Date: Wed, 4 Jul 2018 17:26:25 +0300 Subject: [PATCH 09/29] Updated changelog --- src/app/main/documentation/changelog/changelog.component.html | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/main/documentation/changelog/changelog.component.html b/src/app/main/documentation/changelog/changelog.component.html index ab7f5ac6..30c849f0 100644 --- a/src/app/main/documentation/changelog/changelog.component.html +++ b/src/app/main/documentation/changelog/changelog.component.html @@ -38,6 +38,7 @@
  • Updated Angular Material to 6.3.1
  • Updated various other packages
  • Updated Angular material examples
  • +
  • Chat Panel sidebar
  • Added 'foldedChanged' & 'openedChanged' outputs to the FuseSidebar
  • Ability to add custom classes to the navigation items
  • FuseLoadingBarService for showing/hiding the loading bar easily
  • From 42d9748b10abe85f4890f557748839c344560483 Mon Sep 17 00:00:00 2001 From: Sercan Yemen Date: Wed, 4 Jul 2018 17:29:44 +0300 Subject: [PATCH 10/29] (Chat Panel) Changed the chat panel icon and title --- .../components/chat-panel/chat-panel.component.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/layout/components/chat-panel/chat-panel.component.html b/src/app/layout/components/chat-panel/chat-panel.component.html index 1da2ea29..8fd59600 100644 --- a/src/app/layout/components/chat-panel/chat-panel.component.html +++ b/src/app/layout/components/chat-panel/chat-panel.component.html @@ -3,8 +3,8 @@
    - account_circle -

    Team Members

    + chat +

    Team Chat

    @@ -22,8 +22,8 @@
    @@ -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/_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 6e7a0eff..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; @@ -21,6 +23,10 @@ opacity: .90; z-index: 998; + @include media-breakpoint-down('md') { + right: 0; + } + mat-icon { animation: rotating 3s linear infinite; } diff --git a/src/app/fuse-config/index.ts b/src/app/fuse-config/index.ts index cf924d74..1d6f9692 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-2' }, - 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-fixed', - background: 'mat-fuse-dark-900-bg' + position : 'below-fixed' } }, customScrollbars: true 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..601617f3 --- /dev/null +++ b/src/app/layout/components/navbar/vertical/style-1/style-1.component.html @@ -0,0 +1,23 @@ + + + \ No newline at end of file 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..348a7bad --- /dev/null +++ b/src/app/layout/components/navbar/vertical/style-1/style-1.component.scss @@ -0,0 +1,78 @@ +@import "src/@fuse/scss/fuse"; + +fuse-sidebar { + overflow: hidden; + + &.folded:not(.unfolded) { + + .navbar-vertical { + + .navbar-header { + padding: 0 13px; + + .logo { + + .logo-text { + opacity: 0; + transition: opacity 200ms ease; + } + } + } + } + } +} + +navbar { + + navbar-vertical-style-1 { + 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-1/style-1.component.ts b/src/app/layout/components/navbar/vertical/style-1/style-1.component.ts new file mode 100644 index 00000000..805102a8 --- /dev/null +++ b/src/app/layout/components/navbar/vertical/style-1/style-1.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-1', + templateUrl : './style-1.component.html', + styleUrls : ['./style-1.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class NavbarVerticalStyle1Component 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-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..5bd4611a --- /dev/null +++ b/src/app/layout/components/navbar/vertical/style-2/style-2.component.html @@ -0,0 +1,35 @@ + + +Style 2 + + \ 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..f829924f --- /dev/null +++ b/src/app/layout/components/navbar/vertical/style-2/style-2.component.scss @@ -0,0 +1,86 @@ +@import "src/@fuse/scss/fuse"; + +fuse-sidebar { + overflow: hidden; + + &.folded:not(.unfolded) { + + .navbar-vertical { + + .navbar-header { + + .navbar-header-top { + 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 { + + .navbar-header-top { + display: flex; + align-items: center; + justify-content: space-between; + height: 64px; + min-height: 64px; + padding: 0 16px 0 24px; + transition: padding 200ms ease; + border-bottom: 1px solid; + + .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-header-user { + + } + } + + .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/horizontal/layout-1/layout-1.component.html b/src/app/layout/horizontal/layout-1/layout-1.component.html index 5bef4f42..7b80c4d4 100644 --- a/src/app/layout/horizontal/layout-1/layout-1.component.html +++ b/src/app/layout/horizontal/layout-1/layout-1.component.html @@ -89,7 +89,7 @@ - @@ -102,7 +102,8 @@ - + diff --git a/src/app/layout/vertical/layout-1/layout-1.component.html b/src/app/layout/vertical/layout-1/layout-1.component.html index b8a5e19a..9a7ff897 100644 --- a/src/app/layout/vertical/layout-1/layout-1.component.html +++ b/src/app/layout/vertical/layout-1/layout-1.component.html @@ -105,7 +105,8 @@ [folded]="fuseConfig.layout.navbar.folded" lockedOpen="gt-md" *ngIf="!fuseConfig.layout.navbar.hidden"> - +
    @@ -116,7 +117,8 @@ [folded]="fuseConfig.layout.navbar.folded" lockedOpen="gt-md" *ngIf="!fuseConfig.layout.navbar.hidden"> - +
    \ No newline at end of file diff --git a/src/app/layout/vertical/layout-2/layout-2.component.html b/src/app/layout/vertical/layout-2/layout-2.component.html index 9f57af55..3b9aa221 100644 --- a/src/app/layout/vertical/layout-2/layout-2.component.html +++ b/src/app/layout/vertical/layout-2/layout-2.component.html @@ -105,7 +105,8 @@ [folded]="fuseConfig.layout.navbar.folded" lockedOpen="gt-md" *ngIf="!fuseConfig.layout.navbar.hidden"> - +
    @@ -116,7 +117,8 @@ [folded]="fuseConfig.layout.navbar.folded" lockedOpen="gt-md" *ngIf="!fuseConfig.layout.navbar.hidden"> - +
    \ No newline at end of file diff --git a/src/app/layout/vertical/layout-3/layout-3.component.html b/src/app/layout/vertical/layout-3/layout-3.component.html index 3b31bd0d..fe908e44 100644 --- a/src/app/layout/vertical/layout-3/layout-3.component.html +++ b/src/app/layout/vertical/layout-3/layout-3.component.html @@ -91,7 +91,8 @@ [folded]="fuseConfig.layout.navbar.folded" lockedOpen="gt-md" *ngIf="!fuseConfig.layout.navbar.hidden"> - + @@ -102,7 +103,8 @@ [folded]="fuseConfig.layout.navbar.folded" lockedOpen="gt-md" *ngIf="!fuseConfig.layout.navbar.hidden"> - + \ No newline at end of file From a6c91dd7447973b147edf757c2c4eef9f38d662c Mon Sep 17 00:00:00 2001 From: Sercan Yemen Date: Mon, 9 Jul 2018 14:31:11 +0300 Subject: [PATCH 17/29] (AngularCLI) Added a configuration to serve with extractCss and sourceMap enabled --- angular.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/angular.json b/angular.json index aef36c43..3c054356 100644 --- a/angular.json +++ b/angular.json @@ -50,6 +50,10 @@ "vendorChunk": false, "buildOptimizer": true }, + "ec": { + "sourceMap": true, + "extractCss": true + }, "hmr": { "fileReplacements": [ { @@ -72,6 +76,9 @@ "hmr": { "hmr": true, "browserTarget": "fuse:build:hmr" + }, + "ec": { + "browserTarget": "fuse:build:ec" } } }, From 59d53ba0b92e976268f3efea875101a83e2e0b1f Mon Sep 17 00:00:00 2001 From: Sercan Yemen Date: Mon, 9 Jul 2018 16:29:46 +0300 Subject: [PATCH 18/29] (Colors) Improved the color generators --- src/@fuse/scss/partials/_colors.scss | 317 ++++++++++++++++----------- 1 file changed, 195 insertions(+), 122 deletions(-) diff --git a/src/@fuse/scss/partials/_colors.scss b/src/@fuse/scss/partials/_colors.scss index 7b98456d..ac43fe67 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,83 @@ $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); + } } } // 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); + } } } } -@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 +127,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 +137,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 +192,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 +200,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 +225,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'); From 20d5a68bf38bf9a12157cbb4ea4e2bf17b769352 Mon Sep 17 00:00:00 2001 From: Sercan Yemen Date: Mon, 9 Jul 2018 16:37:00 +0300 Subject: [PATCH 19/29] (Colors) Further improved the color generators + added adaptive-border-color class --- src/@fuse/scss/partials/_colors.scss | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/@fuse/scss/partials/_colors.scss b/src/@fuse/scss/partials/_colors.scss index ac43fe67..cc864cde 100644 --- a/src/@fuse/scss/partials/_colors.scss +++ b/src/@fuse/scss/partials/_colors.scss @@ -79,6 +79,10 @@ $matHues: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400, A70 .mat-ripple-element { background: rgba(0, 0, 0, 0.1); } + + .adaptive-border-color { + border-color: rgba(0, 0, 0, 0.12); + } } } @@ -113,6 +117,10 @@ $matHues: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400, A70 .mat-ripple-element { background: rgba(255, 255, 255, 0.1); } + + .adaptive-border-color { + border-color: rgba(255, 255, 255, 0.12); + } } } } From 18009c927561cc3f454d146aca9f0a214e0995b4 Mon Sep 17 00:00:00 2001 From: Sercan Yemen Date: Mon, 9 Jul 2018 21:29:15 +0300 Subject: [PATCH 20/29] (Navbar) Finished navbar variants --- src/app/fuse-config/index.ts | 2 +- .../vertical/style-1/style-1.component.html | 46 +++++--- .../vertical/style-1/style-1.component.scss | 109 ++++++++++++++---- .../vertical/style-2/style-2.component.html | 34 ++---- .../vertical/style-2/style-2.component.scss | 51 ++++---- 5 files changed, 153 insertions(+), 89 deletions(-) diff --git a/src/app/fuse-config/index.ts b/src/app/fuse-config/index.ts index 1d6f9692..aac07997 100644 --- a/src/app/fuse-config/index.ts +++ b/src/app/fuse-config/index.ts @@ -17,7 +17,7 @@ export const fuseConfig: FuseConfig = { folded : false, hidden : false, position : 'left', - variant : 'vertical-style-2' + variant : 'vertical-style-1' }, toolbar: { background: 'mat-white-500-bg', 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 index 601617f3..3383b780 100644 --- 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 @@ -1,23 +1,41 @@ -