diff --git a/package-lock.json b/package-lock.json index 6534f6d5..14b70d87 100644 --- a/package-lock.json +++ b/package-lock.json @@ -363,18 +363,17 @@ "integrity": "sha512-zTCgrIAA9FYPMbqqpQnoNltiLR58q0FMfzP2t96q/1tjyVy/Y/IaNgVQ7eL0HeQ0nG6IAzQ1HVx8Xeneg4Yj5Q==" }, "@angular/cdk": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-8.0.0.tgz", - "integrity": "sha512-2vsRWEHNARe0iRmqgzvM67gwfRy+aKvdef4Qu9L+ndSsTrrZT3tSgG8SMn1v9SfBHnx5G8mo4d1AMquXG69AuQ==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-9.1.0.tgz", + "integrity": "sha512-qKpAudJx9z0MD+ADptS0OZViJBTA49+JCKym0hPQUkcB9Po4Al6gu6oZ1VSXV5Ln3T84z9aAw/AhUGP/YCFNSQ==", "requires": { - "parse5": "^5.0.0", - "tslib": "^1.7.1" + "parse5": "^5.0.0" }, "dependencies": { "parse5": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", - "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", "optional": true } } @@ -857,6 +856,11 @@ "integrity": "sha512-Q/kFQV4mjZ/Mpx6GksriM5lingjX73EwtVc79AfVMA76Pv5XqfYQZuti6tk7DvYQD89sv1Z/iN2di+ZLKsSTnw==", "dev": true }, + "@angular/material": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-9.1.0.tgz", + "integrity": "sha512-KjA1ARoZ1vBg4jUH1y39muynxAFvkD8QZFGKH8Nh4zXW5pOP/7NyjokxXsNx5qSBCq/6ac2xBxCrCYJN7HmoDw==" + }, "@angular/platform-browser": { "version": "9.0.4", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-9.0.4.tgz", diff --git a/package.json b/package.json index bb87c8b7..a28aa428 100644 --- a/package.json +++ b/package.json @@ -31,12 +31,13 @@ }, "dependencies": { "@angular/animations": "^9.0.4", - "@angular/cdk": "^8.0.0", + "@angular/cdk": "^9.1.0", "@angular/common": "^9.0.4", "@angular/compiler": "^9.0.4", "@angular/core": "^9.0.4", "@angular/forms": "^9.0.4", "@angular/google-maps": "^9.1.0", + "@angular/material": "^9.1.0", "@angular/platform-browser": "^9.0.4", "@angular/platform-browser-dynamic": "^9.0.4", "@angular/router": "^9.0.4", diff --git a/src/app/@core/core.module.ts b/src/app/@core/core.module.ts index 1135104a..26bf0743 100644 --- a/src/app/@core/core.module.ts +++ b/src/app/@core/core.module.ts @@ -1,5 +1,6 @@ import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { MAT_RIPPLE_GLOBAL_OPTIONS } from '@angular/material/core'; import { NbAuthModule, NbDummyAuthStrategy } from '@nebular/auth'; import { NbSecurityModule, NbRoleProvider } from '@nebular/security'; import { of as observableOf } from 'rxjs'; @@ -51,6 +52,7 @@ import { CountryOrderService } from './mock/country-order.service'; import { StatsProgressBarService } from './mock/stats-progress-bar.service'; import { VisitorsAnalyticsService } from './mock/visitors-analytics.service'; import { SecurityCamerasService } from './mock/security-cameras.service'; +import { RippleService } from './utils/ripple.service'; import { MockDataModule } from './mock/mock-data.module'; const socialLinks = [ @@ -91,6 +93,7 @@ const DATA_SERVICES = [ { provide: StatsProgressBarData, useClass: StatsProgressBarService }, { provide: VisitorsAnalyticsData, useClass: VisitorsAnalyticsService }, { provide: SecurityCamerasData, useClass: SecurityCamerasService }, + {provide: MAT_RIPPLE_GLOBAL_OPTIONS, useExisting: RippleService}, ]; export class NbSimpleRoleProvider extends NbRoleProvider { diff --git a/src/app/@core/utils/ripple.service.ts b/src/app/@core/utils/ripple.service.ts new file mode 100644 index 00000000..5efb7a3c --- /dev/null +++ b/src/app/@core/utils/ripple.service.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@angular/core'; +import { RippleGlobalOptions } from '@angular/material/core'; + +@Injectable({providedIn: 'root'}) +export class RippleService implements RippleGlobalOptions { + public disabled: boolean = false; + + public toggle(enabled: boolean): void { + this.disabled = !enabled; + } +} diff --git a/src/app/@theme/components/header/header.component.ts b/src/app/@theme/components/header/header.component.ts index 7dff1543..a556db90 100644 --- a/src/app/@theme/components/header/header.component.ts +++ b/src/app/@theme/components/header/header.component.ts @@ -2,8 +2,10 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { NbMediaBreakpointsService, NbMenuService, NbSidebarService, NbThemeService } from '@nebular/theme'; import { UserData } from '../../../@core/data/users'; +import { LayoutService } from '../../../@core/utils'; import { map, takeUntil } from 'rxjs/operators'; import { Subject } from 'rxjs'; +import { RippleService } from '../../../@core/utils/ripple.service'; @Component({ selector: 'ngx-header', @@ -51,7 +53,9 @@ export class HeaderComponent implements OnInit, OnDestroy { private menuService: NbMenuService, private themeService: NbThemeService, private userService: UserData, - private breakpointService: NbMediaBreakpointsService) { + private layoutService: LayoutService, + private breakpointService: NbMediaBreakpointsService, + private rippleService: RippleService) { } ngOnInit() { @@ -74,7 +78,10 @@ export class HeaderComponent implements OnInit, OnDestroy { map(({ name }) => name), takeUntil(this.destroy$), ) - .subscribe(themeName => this.currentTheme = themeName); + .subscribe(themeName => { + this.currentTheme = themeName; + this.rippleService.toggle(themeName?.startsWith('material')); + }); } ngOnDestroy() { @@ -88,6 +95,7 @@ export class HeaderComponent implements OnInit, OnDestroy { toggleSidebar(): boolean { this.sidebarService.toggle(true, 'menu-sidebar'); + this.layoutService.changeLayoutSize(); return false; } diff --git a/src/app/@theme/styles/_ripple.scss b/src/app/@theme/styles/_ripple.scss new file mode 100644 index 00000000..f77ad29b --- /dev/null +++ b/src/app/@theme/styles/_ripple.scss @@ -0,0 +1,21 @@ +@mixin ngx-ripple() { + .mat-ripple { + overflow: hidden; + position: relative; + + &:not(:empty) { + transform: translateZ(0); + } + } + + .mat-ripple-element { + position: absolute; + border-radius: 50%; + pointer-events: none; + transition: opacity, transform 0ms cubic-bezier(0, 0, 0.2, 1); + transform: scale(0); + // switch ripple color depending on selected theme + background-color: nb-theme(background-alternative-color-4); + opacity: .1; + } +} \ No newline at end of file diff --git a/src/app/@theme/styles/styles.scss b/src/app/@theme/styles/styles.scss index 3f5a8927..15c2de14 100644 --- a/src/app/@theme/styles/styles.scss +++ b/src/app/@theme/styles/styles.scss @@ -18,6 +18,7 @@ @import './layout'; @import './overrides'; +@import './ripple'; // install the framework and custom global styles @include nb-install() { @@ -31,4 +32,5 @@ @include ngx-pace-theme(); @include nb-overrides(); + @include ngx-ripple(); }; diff --git a/src/app/@theme/theme.module.ts b/src/app/@theme/theme.module.ts index 9bb44420..eb38e16f 100644 --- a/src/app/@theme/theme.module.ts +++ b/src/app/@theme/theme.module.ts @@ -1,5 +1,6 @@ import { ModuleWithProviders, NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { MatRippleModule } from '@angular/material/core'; import { NbActionsModule, NbLayoutModule, @@ -71,8 +72,8 @@ const PIPES = [ ]; @NgModule({ - imports: [CommonModule, ...NB_MODULES], - exports: [CommonModule, ...PIPES, ...COMPONENTS], + imports: [CommonModule, MatRippleModule, ...NB_MODULES], + exports: [CommonModule, MatRippleModule, ...PIPES, ...COMPONENTS], declarations: [...COMPONENTS, ...PIPES], }) export class ThemeModule { diff --git a/src/app/pages/dashboard/dashboard.component.ts b/src/app/pages/dashboard/dashboard.component.ts index 6ae4216c..083d3bdd 100644 --- a/src/app/pages/dashboard/dashboard.component.ts +++ b/src/app/pages/dashboard/dashboard.component.ts @@ -1,8 +1,103 @@ -import { Component } from '@angular/core'; +import {Component, OnDestroy} from '@angular/core'; +import { NbThemeService } from '@nebular/theme'; +import { takeWhile } from 'rxjs/operators' ; +import { SolarData } from '../../@core/data/solar'; + +interface CardSettings { + title: string; + iconClass: string; + type: string; +} @Component({ selector: 'ngx-dashboard', + styleUrls: ['./dashboard.component.scss'], templateUrl: './dashboard.component.html', }) -export class DashboardComponent { +export class DashboardComponent implements OnDestroy { + + private alive = true; + + solarValue: number; + lightCard: CardSettings = { + title: 'Light', + iconClass: 'nb-lightbulb', + type: 'primary', + }; + rollerShadesCard: CardSettings = { + title: 'Roller Shades', + iconClass: 'nb-roller-shades', + type: 'success', + }; + wirelessAudioCard: CardSettings = { + title: 'Wireless Audio', + iconClass: 'nb-audio', + type: 'info', + }; + coffeeMakerCard: CardSettings = { + title: 'Coffee Maker', + iconClass: 'nb-coffee-maker', + type: 'warning', + }; + + statusCards: string; + + commonStatusCardsSet: CardSettings[] = [ + this.lightCard, + this.rollerShadesCard, + this.wirelessAudioCard, + this.coffeeMakerCard, + ]; + + statusCardsByThemes: { + default: CardSettings[]; + cosmic: CardSettings[]; + corporate: CardSettings[]; + dark: CardSettings[]; + 'material-dark': CardSettings[]; + 'material-light': CardSettings[]; + } = { + default: this.commonStatusCardsSet, + cosmic: this.commonStatusCardsSet, + corporate: [ + { + ...this.lightCard, + type: 'warning', + }, + { + ...this.rollerShadesCard, + type: 'primary', + }, + { + ...this.wirelessAudioCard, + type: 'danger', + }, + { + ...this.coffeeMakerCard, + type: 'info', + }, + ], + dark: this.commonStatusCardsSet, + 'material-dark': this.commonStatusCardsSet, + 'material-light': this.commonStatusCardsSet, + }; + + constructor(private themeService: NbThemeService, + private solarService: SolarData) { + this.themeService.getJsTheme() + .pipe(takeWhile(() => this.alive)) + .subscribe(theme => { + this.statusCards = this.statusCardsByThemes[theme.name]; + }); + + this.solarService.getSolarData() + .pipe(takeWhile(() => this.alive)) + .subscribe((data) => { + this.solarValue = data; + }); + } + + ngOnDestroy() { + this.alive = false; + } } diff --git a/src/app/pages/dashboard/status-card/status-card.component.scss b/src/app/pages/dashboard/status-card/status-card.component.scss new file mode 100644 index 00000000..261f13cf --- /dev/null +++ b/src/app/pages/dashboard/status-card/status-card.component.scss @@ -0,0 +1,78 @@ +@import '../../../@theme/styles/themes'; + +@include nb-install-component() { + nb-card { + flex-direction: row; + align-items: center; + height: 6rem; + + .icon-container { + height: 100%; + padding: 0.625rem; + } + + .icon { + display: flex; + align-items: center; + justify-content: center; + width: 5.75rem; + height: 4.75rem; + font-size: 3.75rem; + border-radius: nb-theme(card-border-radius); + transition: width 0.4s ease; + transform: translate3d(0, 0, 0); + -webkit-transform-style: preserve-3d; + -webkit-backface-visibility: hidden; + color: nb-theme(text-control-color); + + @each $status in nb-get-statuses() { + &.status-#{$status} { + $left-color: nb-theme(button-hero-#{$status}-left-background-color); + $right-color: nb-theme(button-hero-#{$status}-right-background-color); + background-image: linear-gradient(to right, $left-color, $right-color); + + &:hover { + $left-hover-color: nb-theme(button-hero-#{$status}-hover-left-background-color); + $right-hover-color: nb-theme(button-hero-#{$status}-hover-right-background-color); + background-image: linear-gradient(to right, $left-hover-color, $right-hover-color); + } + } + } + } + + &.off { + color: nb-theme(text-hint-color); + + .status, + .title, + .icon { + color: nb-theme(text-hint-color); + } + + @each $status in nb-get-statuses() { + .icon.status-#{$status} { + box-shadow: none; + background-image: linear-gradient(to right, transparent, transparent); + } + } + } + + .details { + display: flex; + flex-direction: column; + justify-content: center; + height: 100%; + @include nb-ltr(padding, 0 0.5rem 0 0.75rem); + @include nb-rtl(padding, 0 0.75rem 0 0.5rem); + border-left: 1px solid transparent; + } + + .title { + margin: 0; + } + + .status { + text-transform: uppercase; + } + } +} diff --git a/src/app/pages/dashboard/status-card/status-card.component.ts b/src/app/pages/dashboard/status-card/status-card.component.ts new file mode 100644 index 00000000..3ffd6ad2 --- /dev/null +++ b/src/app/pages/dashboard/status-card/status-card.component.ts @@ -0,0 +1,26 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'ngx-status-card', + styleUrls: ['./status-card.component.scss'], + template: ` + + + + + + + + + {{ title }} + {{ on ? 'ON' : 'OFF' }} + + + `, +}) +export class StatusCardComponent { + + @Input() title: string; + @Input() type: string; + @Input() on = true; +} diff --git a/src/app/pages/dashboard/temperature/temperature.component.html b/src/app/pages/dashboard/temperature/temperature.component.html new file mode 100644 index 00000000..9f6c4345 --- /dev/null +++ b/src/app/pages/dashboard/temperature/temperature.component.html @@ -0,0 +1,69 @@ + + + + + + + + + + + {{ temperatureOff ? '--' : (temperature | ngxRound) }} + + + Celsius + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ humidityOff ? '--' : (humidity | ngxRound) }} + + + + + + + + + + + + + + + + + + + + + +