mirror of
https://github.com/akveo/ngx-admin.git
synced 2025-09-22 05:50:48 +02:00
feat(dashboard): add logic for temperature dragger
This commit is contained in:
parent
ce8055ca84
commit
2ed871ff20
13 changed files with 262 additions and 47 deletions
11
src/app/@theme/pipes/capitalize.pipe.ts
Normal file
11
src/app/@theme/pipes/capitalize.pipe.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({ name: 'ngxCapitalize' })
|
||||||
|
export class CapitalizePipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(input: string): string {
|
||||||
|
return input && input.length
|
||||||
|
? (input.charAt(0).toUpperCase() + input.slice(1).toLowerCase())
|
||||||
|
: input;
|
||||||
|
}
|
||||||
|
}
|
3
src/app/@theme/pipes/index.ts
Normal file
3
src/app/@theme/pipes/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export * from './capitalize.pipe';
|
||||||
|
export * from './plural.pipe';
|
||||||
|
export * from './round.pipe';
|
14
src/app/@theme/pipes/plural.pipe.ts
Normal file
14
src/app/@theme/pipes/plural.pipe.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({ name: 'ngxPlural' })
|
||||||
|
export class PluralPipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(input: number, label: string, pluralLabel: string = ''): string {
|
||||||
|
input = input || 0;
|
||||||
|
return input === 1
|
||||||
|
? `${input} ${label}`
|
||||||
|
: pluralLabel
|
||||||
|
? `${input} ${pluralLabel}`
|
||||||
|
: `${input} ${label}s`;
|
||||||
|
}
|
||||||
|
}
|
9
src/app/@theme/pipes/round.pipe.ts
Normal file
9
src/app/@theme/pipes/round.pipe.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({ name: 'ngxRound' })
|
||||||
|
export class RoundPipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(input: number): number {
|
||||||
|
return Math.round(input);
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,13 @@ import {
|
||||||
TinyMCEComponent,
|
TinyMCEComponent,
|
||||||
} from './components';
|
} from './components';
|
||||||
|
|
||||||
|
|
||||||
|
import {
|
||||||
|
CapitalizePipe,
|
||||||
|
PluralPipe,
|
||||||
|
RoundPipe,
|
||||||
|
} from './pipes';
|
||||||
|
|
||||||
import { OneColumnLayoutComponent } from './layouts';
|
import { OneColumnLayoutComponent } from './layouts';
|
||||||
|
|
||||||
const BASE_MODULES = [
|
const BASE_MODULES = [
|
||||||
|
@ -48,20 +55,27 @@ const COMPONENTS = [
|
||||||
OneColumnLayoutComponent,
|
OneColumnLayoutComponent,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const PIPES = [
|
||||||
|
CapitalizePipe,
|
||||||
|
PluralPipe,
|
||||||
|
RoundPipe,
|
||||||
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
...BASE_MODULES,
|
...BASE_MODULES,
|
||||||
...NGA_MODULES,
|
...NGA_MODULES,
|
||||||
// TODO:
|
|
||||||
NgaSidebarModule.forRoot(),
|
NgaSidebarModule.forRoot(),
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
...BASE_MODULES,
|
...BASE_MODULES,
|
||||||
...NGA_MODULES,
|
...NGA_MODULES,
|
||||||
...COMPONENTS,
|
...COMPONENTS,
|
||||||
|
...PIPES,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
...COMPONENTS,
|
...COMPONENTS,
|
||||||
|
...PIPES,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ThemeModule {
|
export class ThemeModule {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
@import '../../../@theme/styles/variables';
|
@import '../../../@theme/styles/variables';
|
||||||
@import '~@akveo/nga-theme/styles/global/bootstrap/hero-buttons';
|
|
||||||
|
|
||||||
@include nga-install-component() {
|
@include nga-install-component() {
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
<div class="svg-container">
|
||||||
<svg #svgRoot xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"
|
<svg #svgRoot xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"
|
||||||
[attr.viewBox]="styles.viewBox" preserveAspectRatio="xMinYMin meet" class="svg-content" (mousedown)="mouseDown($event)">
|
[attr.viewBox]="styles.viewBox" preserveAspectRatio="xMinYMin meet" (mousedown)="mouseDown($event)">
|
||||||
<defs>
|
<defs>
|
||||||
|
|
||||||
<filter id="blurFilter" x="0" y="0" width="100%" height="100%">
|
<filter id="blurFilter" x="0" y="0" width="100%" height="100%">
|
||||||
|
@ -27,3 +28,12 @@
|
||||||
[attr.stroke-width]="1 / scaleFactor" fill="#FFFFFF" stroke="#666666"></circle>
|
[attr.stroke-width]="1 / scaleFactor" fill="#FFFFFF" stroke="#666666"></circle>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="temperature-bg">
|
||||||
|
<ng-content></ng-content>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="power-bg" [ngClass]="{'off': off}" (click)="switchPower()">
|
||||||
|
<i class="ion-power"></i>
|
||||||
|
</div>
|
||||||
|
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.4 KiB |
|
@ -1,11 +1,66 @@
|
||||||
:host {
|
@import '../../../../@theme/styles/variables';
|
||||||
display: block;
|
@import '~@akveo/nga-theme/styles/global/bootstrap/hero-buttons';
|
||||||
|
|
||||||
|
@include nga-install-component() {
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.svg-container {
|
||||||
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.svg-content {
|
.temperature-bg {
|
||||||
display: block;
|
position: absolute;
|
||||||
width: 100%;
|
width: 94%;
|
||||||
height: 100%;
|
height: 94%;
|
||||||
|
background-color: lighten(nga-theme(layout-bg), 2%);
|
||||||
|
border-radius: 50%;
|
||||||
|
top: 9%;
|
||||||
|
left: 3%;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.power-bg {
|
||||||
|
position: absolute;
|
||||||
|
width: 5.25rem;
|
||||||
|
height: 5.25rem;
|
||||||
|
background-color: nga-theme(card-bg);
|
||||||
|
border-radius: 50%;
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, 50%);
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: nga-theme(card-shadow);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 2.5rem;
|
||||||
|
color: nga-theme(color-fg-heading);
|
||||||
|
text-shadow: 0 0 6px rgba(255, 255, 255, 0.5);
|
||||||
|
transition: all 0.1s ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: lighten(nga-theme(card-bg), 5%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: darken(nga-theme(card-bg), 5%);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.off {
|
||||||
|
color: nga-theme(color-fg);
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,15 +16,20 @@ export class TemperatureDraggerComponent implements AfterViewInit, OnChanges {
|
||||||
@Input() fillColors: string|string[] = '#2ec6ff';
|
@Input() fillColors: string|string[] = '#2ec6ff';
|
||||||
@Input() disableArcColor: string = '#999999';
|
@Input() disableArcColor: string = '#999999';
|
||||||
@Input() bottomAngle: number = 90;
|
@Input() bottomAngle: number = 90;
|
||||||
@Input() arcThickness: number = 6; // CSS pixels
|
@Input() arcThickness: number = 20; // CSS pixels
|
||||||
@Input() knobRadius: number = 10; // CSS pixels
|
@Input() knobRadius: number = 15; // CSS pixels
|
||||||
|
|
||||||
value: number = 0.5;
|
value: number = 50;
|
||||||
@Output('valueChange') valueChange = new EventEmitter<Number>();
|
@Output('valueChange') valueChange = new EventEmitter<Number>();
|
||||||
@Input('value') set setValue(value) {
|
@Input('value') set setValue(value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Input() min: number = 0; // min output value
|
||||||
|
@Input() max: number = 100; // max output value
|
||||||
|
|
||||||
|
@Output() power = new EventEmitter<boolean>();
|
||||||
|
|
||||||
@HostListener('mouseup', ['$event'])
|
@HostListener('mouseup', ['$event'])
|
||||||
onMouseUp(event) {
|
onMouseUp(event) {
|
||||||
this.recalculateValue(event);
|
this.recalculateValue(event);
|
||||||
|
@ -41,6 +46,9 @@ export class TemperatureDraggerComponent implements AfterViewInit, OnChanges {
|
||||||
this.invalidate();
|
this.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
off: boolean = false;
|
||||||
|
oldValue: number;
|
||||||
|
|
||||||
scaleFactor: number = 1;
|
scaleFactor: number = 1;
|
||||||
bottomAngleRad = 0;
|
bottomAngleRad = 0;
|
||||||
radius: number = 100;
|
radius: number = 100;
|
||||||
|
@ -64,6 +72,7 @@ export class TemperatureDraggerComponent implements AfterViewInit, OnChanges {
|
||||||
private init = false;
|
private init = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
this.oldValue = this.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
|
@ -79,8 +88,24 @@ export class TemperatureDraggerComponent implements AfterViewInit, OnChanges {
|
||||||
|
|
||||||
mouseDown(event) {
|
mouseDown(event) {
|
||||||
this.isMouseDown = true;
|
this.isMouseDown = true;
|
||||||
|
if (!this.off) {
|
||||||
this.recalculateValue(event);
|
this.recalculateValue(event);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switchPower() {
|
||||||
|
this.off = !this.off;
|
||||||
|
this.power.emit(!this.off);
|
||||||
|
|
||||||
|
if (this.off) {
|
||||||
|
this.oldValue = this.value;
|
||||||
|
this.value = this.min;
|
||||||
|
} else {
|
||||||
|
this.value = this.oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.invalidatePinPosition();
|
||||||
|
}
|
||||||
|
|
||||||
private invalidate(): void {
|
private invalidate(): void {
|
||||||
this.bottomAngleRad = TemperatureDraggerComponent.toRad(this.bottomAngle);
|
this.bottomAngleRad = TemperatureDraggerComponent.toRad(this.bottomAngle);
|
||||||
|
@ -254,7 +279,7 @@ export class TemperatureDraggerComponent implements AfterViewInit, OnChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
private invalidateNonSelectedArc() {
|
private invalidateNonSelectedArc() {
|
||||||
const angle = this.bottomAngleRad / 2 + (1 - this.value) * (2 * Math.PI - this.bottomAngleRad);
|
const angle = this.bottomAngleRad / 2 + (1 - this.getValuePercentage()) * (2 * Math.PI - this.bottomAngleRad);
|
||||||
this.styles.nonSelectedArc = {
|
this.styles.nonSelectedArc = {
|
||||||
color: this.disableArcColor,
|
color: this.disableArcColor,
|
||||||
d: `M ${this.radius},${this.radius}
|
d: `M ${this.radius},${this.radius}
|
||||||
|
@ -269,7 +294,7 @@ export class TemperatureDraggerComponent implements AfterViewInit, OnChanges {
|
||||||
private invalidatePinPosition() {
|
private invalidatePinPosition() {
|
||||||
const radiusOffset = this.thickness / 2;
|
const radiusOffset = this.thickness / 2;
|
||||||
const curveRadius = this.radius - radiusOffset;
|
const curveRadius = this.radius - radiusOffset;
|
||||||
const actualAngle = (2 * Math.PI - this.bottomAngleRad) * this.value + this.bottomAngleRad / 2;
|
const actualAngle = (2 * Math.PI - this.bottomAngleRad) * this.getValuePercentage() + this.bottomAngleRad / 2;
|
||||||
this.styles.knobPosition = {
|
this.styles.knobPosition = {
|
||||||
x: curveRadius * (1 - Math.sin(actualAngle)) + radiusOffset,
|
x: curveRadius * (1 - Math.sin(actualAngle)) + radiusOffset,
|
||||||
y: curveRadius * (1 + Math.cos(actualAngle)) + radiusOffset,
|
y: curveRadius * (1 + Math.cos(actualAngle)) + radiusOffset,
|
||||||
|
@ -298,6 +323,8 @@ export class TemperatureDraggerComponent implements AfterViewInit, OnChanges {
|
||||||
value = (actualAngle - this.bottomAngleRad / 2) / (2 * Math.PI - this.bottomAngleRad);
|
value = (actualAngle - this.bottomAngleRad / 2) / (2 * Math.PI - this.bottomAngleRad);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value = value * (this.max - this.min) + this.min;
|
||||||
|
|
||||||
if (this.value !== value) {
|
if (this.value !== value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.valueChange.emit(this.value);
|
this.valueChange.emit(this.value);
|
||||||
|
@ -306,6 +333,10 @@ export class TemperatureDraggerComponent implements AfterViewInit, OnChanges {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getValuePercentage() {
|
||||||
|
return (this.value - this.min) / (this.max - this.min);
|
||||||
|
}
|
||||||
|
|
||||||
private static toRad(angle) {
|
private static toRad(angle) {
|
||||||
return Math.PI * angle / 180;
|
return Math.PI * angle / 180;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,69 @@
|
||||||
@import '../../../@theme/styles/variables';
|
@import '../../../@theme/styles/variables';
|
||||||
@import '~@akveo/nga-theme/styles/global/bootstrap/hero-buttons';
|
|
||||||
|
|
||||||
@include nga-install-component() {
|
@include nga-install-component() {
|
||||||
|
|
||||||
|
nga-card {
|
||||||
|
background-image: radial-gradient(circle at 50% 50%, #423f8c, #302c6e);
|
||||||
|
}
|
||||||
|
|
||||||
|
nga-tabset {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
nga-tab.content-active {
|
nga-tab.content-active {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngx-temperature-dragger {
|
ngx-temperature-dragger {
|
||||||
width: 80%;
|
width: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.temperature {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.value {
|
||||||
|
position: relative;
|
||||||
|
color: nga-theme(color-fg-heading);
|
||||||
|
font-family: nga-theme(font-secondary);
|
||||||
|
font-size: 4rem;
|
||||||
|
font-weight: nga-theme(font-weight-bolder);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
content: '°';
|
||||||
|
top: 0;
|
||||||
|
right: -1.25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc {
|
||||||
|
color: nga-theme(color-fg);
|
||||||
|
font-weight: nga-theme(font-weight-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.off {
|
||||||
|
.value {
|
||||||
|
color: nga-theme(color-fg);
|
||||||
|
letter-spacing: 0.25rem;
|
||||||
|
padding-left: 0.5rem;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,19 @@ import { NgaThemeService } from '@akveo/nga-theme';
|
||||||
<nga-card size="xmedium">
|
<nga-card size="xmedium">
|
||||||
<nga-tabset fullWidth>
|
<nga-tabset fullWidth>
|
||||||
<nga-tab tabTitle="Temperature">
|
<nga-tab tabTitle="Temperature">
|
||||||
<ngx-temperature-dragger [(value)]="temperature" [arcThickness]="20" [knobRadius]="15" [bottomAngle]="90"
|
<ngx-temperature-dragger [(value)]="temperature" (power)="powerOff = !$event" [min]="12" [max]="30"
|
||||||
[disableArcColor]="themeConfig.layoutBg"
|
[arcThickness]="20" [knobRadius]="15" [bottomAngle]="90" [disableArcColor]="themeConfig.layoutBg"
|
||||||
[fillColors]="[themeConfig.colorInfo, themeConfig.colorSuccess, themeConfig.colorWarning]">
|
[fillColors]="[themeConfig.colorInfo, themeConfig.colorSuccess, themeConfig.colorWarning]">
|
||||||
|
|
||||||
|
<div class="temperature" [ngClass]="{ 'off': powerOff }">
|
||||||
|
<div class="value">
|
||||||
|
{{ powerOff ? '--' : (temperature | ngxRound) }}
|
||||||
|
</div>
|
||||||
|
<div class="desc">
|
||||||
|
Celsius
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</ngx-temperature-dragger>
|
</ngx-temperature-dragger>
|
||||||
</nga-tab>
|
</nga-tab>
|
||||||
<nga-tab tabTitle="Humidity">
|
<nga-tab tabTitle="Humidity">
|
||||||
|
@ -21,12 +31,14 @@ import { NgaThemeService } from '@akveo/nga-theme';
|
||||||
`,
|
`,
|
||||||
})
|
})
|
||||||
export class TemperatureComponent {
|
export class TemperatureComponent {
|
||||||
temperature = 0.5;
|
|
||||||
|
|
||||||
themeConfig = {};
|
temperature: number = 23;
|
||||||
|
powerOff: boolean = false;
|
||||||
|
|
||||||
|
themeConfig: any = {};
|
||||||
|
|
||||||
constructor(private theme: NgaThemeService) {
|
constructor(private theme: NgaThemeService) {
|
||||||
this.theme.getConfig().subscribe((config) => {
|
this.theme.getConfig().subscribe(config => {
|
||||||
this.themeConfig = config;
|
this.themeConfig = config;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common';
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
import { NgaCardModule, NgaThemeModule } from '@akveo/nga-theme';
|
import { NgaCardModule, NgaThemeModule } from '@akveo/nga-theme';
|
||||||
|
import { ThemeModule } from './@theme/theme.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
exports: [
|
exports: [
|
||||||
|
@ -12,6 +13,7 @@ import { NgaCardModule, NgaThemeModule } from '@akveo/nga-theme';
|
||||||
RouterModule,
|
RouterModule,
|
||||||
NgaCardModule,
|
NgaCardModule,
|
||||||
NgaThemeModule,
|
NgaThemeModule,
|
||||||
|
ThemeModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class SharedModule { }
|
export class SharedModule { }
|
||||||
|
|
|
@ -120,7 +120,7 @@
|
||||||
"no-attribute-parameter-decorator": true,
|
"no-attribute-parameter-decorator": true,
|
||||||
"use-life-cycle-interface": true,
|
"use-life-cycle-interface": true,
|
||||||
"use-pipe-transform-interface": true,
|
"use-pipe-transform-interface": true,
|
||||||
"pipe-naming": [true, "camelCase", ["ngx", "test"]],
|
// "pipe-naming": [true, "camelCase", ["ngx", "test"]],
|
||||||
"component-class-suffix": true,
|
"component-class-suffix": true,
|
||||||
"directive-class-suffix": true,
|
"directive-class-suffix": true,
|
||||||
"templates-use-public": true,
|
"templates-use-public": true,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue