diff --git a/angular-client/src/components/battery-level-indicator/battery-level-indicator.component.html b/angular-client/src/components/battery-level-indicator/battery-level-indicator.component.html index dda920a3f..0312cda5a 100644 --- a/angular-client/src/components/battery-level-indicator/battery-level-indicator.component.html +++ b/angular-client/src/components/battery-level-indicator/battery-level-indicator.component.html @@ -1,6 +1,6 @@
-
+
diff --git a/angular-client/src/components/battery-level-indicator/battery-level-indicator.component.ts b/angular-client/src/components/battery-level-indicator/battery-level-indicator.component.ts index 9df19f786..b5b57baf2 100644 --- a/angular-client/src/components/battery-level-indicator/battery-level-indicator.component.ts +++ b/angular-client/src/components/battery-level-indicator/battery-level-indicator.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnChanges } from '@angular/core'; +import { Component, computed, input } from '@angular/core'; import Theme from 'src/services/theme.service'; /** @@ -11,14 +11,9 @@ import Theme from 'src/services/theme.service'; styleUrl: './battery-level-indicator.component.css', standalone: true }) -export class BatteryLevelIndicatorComponent implements OnChanges { - @Input() percentage: number = 0; +export class BatteryLevelIndicatorComponent { + percentage = input(0); - fillHeight = '0%'; - fillColor = Theme.battteryHigh; - - ngOnChanges(): void { - this.fillHeight = Math.max(0, Math.min(100, this.percentage)) + '%'; - this.fillColor = this.percentage <= 20 ? Theme.battteryLow : Theme.battteryHigh; - } + fillHeight = computed(() => Math.max(0, Math.min(100, this.percentage())) + '%'); + fillColor = computed(() => (this.percentage() <= 20 ? Theme.battteryLow : Theme.battteryHigh)); } diff --git a/angular-client/src/components/battery-percentage/battery-percentage.component.html b/angular-client/src/components/battery-percentage/battery-percentage.component.html index 3ac9695c9..09d0900a2 100644 --- a/angular-client/src/components/battery-percentage/battery-percentage.component.html +++ b/angular-client/src/components/battery-percentage/battery-percentage.component.html @@ -24,8 +24,8 @@ >
(); + ringColor = input.required(); + percentage = input.required(); + spacing = input(0); //values needed for styling and scaling backgroundColor: string = Theme.infoBackground; @@ -31,10 +31,10 @@ export class CircularPercentageComponent implements OnInit { //assigns values needed for styling and scaling ngOnInit() { - this.innerCircleDimension = this.dimension * 0.87; - this.percentageFontSize = this.dimension * 0.39; - this.percentageSignFontSize = this.dimension * 0.17; - this.percentageSignOffset = this.dimension * 0.02; + this.innerCircleDimension = this.dimension() * 0.87; + this.percentageFontSize = this.dimension() * 0.39; + this.percentageSignFontSize = this.dimension() * 0.17; + this.percentageSignOffset = this.dimension() * 0.02; } getFilledAngle(percentage: number): number { diff --git a/angular-client/src/components/current-total-timer/current-total-timer.component.ts b/angular-client/src/components/current-total-timer/current-total-timer.component.ts index 26ed4897f..8b3206dad 100644 --- a/angular-client/src/components/current-total-timer/current-total-timer.component.ts +++ b/angular-client/src/components/current-total-timer/current-total-timer.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, input } from '@angular/core'; import { InfoBackgroundComponent } from '../info-background/info-background.component'; import TypographyComponent from '../typography/typography.component'; import HStackComponent from '../hstack/hstack.component'; @@ -12,15 +12,15 @@ import VStackComponent from '../vstack/vstack.component'; imports: [InfoBackgroundComponent, TypographyComponent, HStackComponent, VStackComponent] }) export default class CurrentTotalTimerComponent { - @Input() currentTime: number = 0; - @Input() totalTime: number = 0; + currentTime = input(0); + totalTime = input(0); getCurrentTime() { - return this.formatTime(this.currentTime); + return this.formatTime(this.currentTime()); } getTotalTime() { - return this.formatTime(this.totalTime); + return this.formatTime(this.totalTime()); } /** diff --git a/angular-client/src/components/double-line-graph/double-line-graph.component.html b/angular-client/src/components/double-line-graph/double-line-graph.component.html index 314db0362..cbc678654 100644 --- a/angular-client/src/components/double-line-graph/double-line-graph.component.html +++ b/angular-client/src/components/double-line-graph/double-line-graph.component.html @@ -1 +1 @@ -
+
diff --git a/angular-client/src/components/double-line-graph/double-line-graph.component.ts b/angular-client/src/components/double-line-graph/double-line-graph.component.ts index ee83c66d0..dc9033902 100644 --- a/angular-client/src/components/double-line-graph/double-line-graph.component.ts +++ b/angular-client/src/components/double-line-graph/double-line-graph.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnDestroy, OnInit, inject } from '@angular/core'; +import { Component, OnDestroy, OnInit, inject, input } from '@angular/core'; import ApexCharts from 'apexcharts'; import { ApexAxisChartSeries, @@ -41,15 +41,15 @@ type ChartOptions = { }) export class DoubleLineGraphComponent implements OnInit, OnDestroy { public dialogService = inject(DialogService); - @Input() data1!: GraphData[]; - @Input() color1!: string; // Must be hex - @Input() title1?: string; - @Input() data2!: GraphData[]; - @Input() color2!: string; // Must be hex - @Input() title2?: string; - @Input() header?: string; - @Input() graphContainerId!: string; - @Input({ required: false }) timeRangeSec!: number; + data1 = input.required(); + color1 = input.required(); // Must be hex + title1 = input(undefined); + data2 = input.required(); + color2 = input.required(); // Must be hex + title2 = input(undefined); + header = input(undefined); + graphContainerId = input.required(); + timeRangeSec = input(undefined); options!: ChartOptions; chart!: ApexCharts; series: ApexAxisChartSeries = []; @@ -59,11 +59,11 @@ export class DoubleLineGraphComponent implements OnInit, OnDestroy { timeOuts: NodeJS.Timeout[] = []; openDialog = () => { this.dialogService.open(GraphDialogComponent, { - header: this.header, + header: this.header(), data: { - data: this.data1, - color: this.color1, - title: this.title1 + data: this.data1(), + color: this.color1(), + title: this.title1() } }); }; @@ -76,18 +76,18 @@ export class DoubleLineGraphComponent implements OnInit, OnDestroy { updateChart = () => { this.series = [ { - name: this.title1, - data: Array.from(this.data1) + name: this.title1(), + data: Array.from(this.data1()) }, { - name: this.title2, - data: Array.from(this.data2) + name: this.title2(), + data: Array.from(this.data2()) } ]; // temp fix, for now just basing change on data1 length... // even though probably should check data1 also - if (!this.isSliding && this.data1.length > 2) { - this.timeDiffMs = this.data1[this.data1.length - 1].x - this.data1[0].x; + if (!this.isSliding && this.data1().length > 2) { + this.timeDiffMs = this.data1()[this.data1().length - 1].x - this.data1()[0].x; } if (!this.isSliding && this.timeDiffMs > this.timeRangeMs) { @@ -110,16 +110,16 @@ export class DoubleLineGraphComponent implements OnInit, OnDestroy { }; ngOnInit(): void { - this.timeRangeMs = (this.timeRangeSec ?? 120) * 1000; + this.timeRangeMs = (this.timeRangeSec() ?? 120) * 1000; this.series = [ { - name: this.title1, - data: this.data1 + name: this.title1(), + data: this.data1() }, { - name: this.title2, - data: this.data2 + name: this.title2(), + data: this.data2() } ]; @@ -143,7 +143,7 @@ export class DoubleLineGraphComponent implements OnInit, OnDestroy { } // background: '#5A5A5A' }, - colors: [this.color1, this.color2], // Set series colors here + colors: [this.color1(), this.color2()], // Set series colors here dataLabels: { enabled: false }, @@ -209,7 +209,7 @@ export class DoubleLineGraphComponent implements OnInit, OnDestroy { // Delay rendering to ensure the container is available setTimeout(() => { - const chartContainer = document.getElementById(this.graphContainerId); + const chartContainer = document.getElementById(this.graphContainerId()); if (!chartContainer) { return; } diff --git a/angular-client/src/components/error-page/error-page.component.html b/angular-client/src/components/error-page/error-page.component.html index 7d4b38264..f521a670f 100644 --- a/angular-client/src/components/error-page/error-page.component.html +++ b/angular-client/src/components/error-page/error-page.component.html @@ -1,3 +1,3 @@
- +
diff --git a/angular-client/src/components/error-page/error-page.component.ts b/angular-client/src/components/error-page/error-page.component.ts index bc337ab83..901e3a6db 100644 --- a/angular-client/src/components/error-page/error-page.component.ts +++ b/angular-client/src/components/error-page/error-page.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, input } from '@angular/core'; import TypographyComponent from '../typography/typography.component'; /** @@ -13,5 +13,5 @@ import TypographyComponent from '../typography/typography.component'; standalone: true }) export default class ErrorPageComponent { - @Input() errorMessage!: string; + errorMessage = input.required(); } diff --git a/angular-client/src/components/form-template/form-template.component.ts b/angular-client/src/components/form-template/form-template.component.ts index a9aff461f..4394908f2 100644 --- a/angular-client/src/components/form-template/form-template.component.ts +++ b/angular-client/src/components/form-template/form-template.component.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Component, EventEmitter, inject, input, OnChanges, OnInit, Output } from '@angular/core'; +import { Component, effect, inject, input, output, untracked } from '@angular/core'; import { FormBuilder, FormGroup, ValidatorFn, Validators, ReactiveFormsModule } from '@angular/forms'; import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; import { InputText } from 'primeng/inputtext'; @@ -31,7 +31,7 @@ export interface DynamicFormField { standalone: true, imports: [ReactiveFormsModule, InputText, NgClass, NgIf, ButtonDirective] }) -export class FormTemplateComponent implements OnInit, OnChanges { +export class FormTemplateComponent { public config = inject(DynamicDialogConfig); fields = input.required(); @@ -39,28 +39,24 @@ export class FormTemplateComponent implements OnInit, OnChanges { public ref = inject(DynamicDialogRef); formData = input>(new Map()); - @Output() submitForm: EventEmitter = new EventEmitter(); + submitForm = output(); form: FormGroup = this.fb.group({}); constructor(private fb: FormBuilder) { - // this.fields = this.config.data.fields; - } - - ngOnInit(): void { - this.buildForm(); - } - - ngOnChanges(): void { - this.buildForm(); + effect(() => { + const fields = this.fields(); + const formData = this.formData(); + untracked(() => this.buildForm(fields, formData)); + }); } - buildForm() { + buildForm(fields = this.fields(), formData = this.formData()) { const group: any = {}; - this.fields().forEach((field) => { + fields.forEach((field) => { const control = this.fb.control({ - value: this.formData() ? this.formData().get(field.name) : '', + value: formData ? formData.get(field.name) : '', disabled: field.disabled }); diff --git a/angular-client/src/components/glance-thermometer/glance-thermometer.component.html b/angular-client/src/components/glance-thermometer/glance-thermometer.component.html index 083590cd9..0bb5924e5 100644 --- a/angular-client/src/components/glance-thermometer/glance-thermometer.component.html +++ b/angular-client/src/components/glance-thermometer/glance-thermometer.component.html @@ -1,7 +1,7 @@
-
+
-
+
diff --git a/angular-client/src/components/glance-thermometer/glance-thermometer.component.ts b/angular-client/src/components/glance-thermometer/glance-thermometer.component.ts index 69fc03455..138e234c5 100644 --- a/angular-client/src/components/glance-thermometer/glance-thermometer.component.ts +++ b/angular-client/src/components/glance-thermometer/glance-thermometer.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, computed, input } from '@angular/core'; /** * Compact thermometer widget sized for the At A Glance bar. @@ -11,21 +11,21 @@ import { Component, Input } from '@angular/core'; standalone: true }) export class GlanceThermometerComponent { - @Input() temperature: number = 0; - @Input() min: number = 0; - @Input() max: number = 100; + temperature = input(0); + min = input(0); + max = input(100); - get mercuryHeight(): string { - const clamped = Math.max(this.min, Math.min(this.temperature, this.max)); - const pct = ((clamped - this.min) / (this.max - this.min)) * 100; - // Mercury fills 0–70% of the tube area (leaving room for glass top) + // Mercury fills 0–70% of the tube area (leaving room for glass top) + mercuryHeight = computed(() => { + const clamped = Math.max(this.min(), Math.min(this.temperature(), this.max())); + const pct = ((clamped - this.min()) / (this.max() - this.min())) * 100; return Math.max(5, pct * 0.7) + '%'; - } + }); - get mercuryColor(): string { - const range = this.max - this.min; - if (this.temperature < this.min + range / 2) return '#3b82f6'; - if (this.temperature < this.min + range / 1.5) return '#eab308'; + mercuryColor = computed(() => { + const range = this.max() - this.min(); + if (this.temperature() < this.min() + range / 2) return '#3b82f6'; + if (this.temperature() < this.min() + range / 1.5) return '#eab308'; return '#ef4444'; - } + }); } diff --git a/angular-client/src/components/graph-dialog/graph-dialog.component.ts b/angular-client/src/components/graph-dialog/graph-dialog.component.ts index 56ab20e93..6b1b1971e 100644 --- a/angular-client/src/components/graph-dialog/graph-dialog.component.ts +++ b/angular-client/src/components/graph-dialog/graph-dialog.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, inject } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { DialogService, DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; import { GraphData } from 'src/utils/types.utils'; import { GraphComponent } from '../graph/graph.component'; @@ -7,17 +7,16 @@ import { GraphComponent } from '../graph/graph.component'; selector: 'graph-dialog', templateUrl: './graph-dialog.component.html', providers: [DialogService], - standalone: true, imports: [GraphComponent] }) export class GraphDialogComponent { public dialogService = inject(DialogService); public config = inject(DynamicDialogConfig); - public ref = inject(DynamicDialogRef); // Inject the dialog reference for closing - @Input() data!: GraphData[]; - @Input() color!: string; - @Input() title!: string; - @Input() graphContainerId!: string; + public ref = inject(DynamicDialogRef); + data: GraphData[]; + color: string; + title: string; + graphContainerId: string; constructor() { this.data = this.config.data.data; diff --git a/angular-client/src/components/graph/graph.component.html b/angular-client/src/components/graph/graph.component.html index 1cf0777c5..61559aee4 100644 --- a/angular-client/src/components/graph/graph.component.html +++ b/angular-client/src/components/graph/graph.component.html @@ -1 +1 @@ -
+
diff --git a/angular-client/src/components/graph/graph.component.ts b/angular-client/src/components/graph/graph.component.ts index 54c62d9ff..7aea142c6 100644 --- a/angular-client/src/components/graph/graph.component.ts +++ b/angular-client/src/components/graph/graph.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnDestroy, OnInit, inject } from '@angular/core'; +import { Component, OnDestroy, OnInit, inject, input } from '@angular/core'; import ApexCharts from 'apexcharts'; import { ApexXAxis, ApexDataLabels, ApexChart, ApexMarkers, ApexGrid, ApexTooltip, ApexFill } from 'ng-apexcharts'; import { DialogService } from 'primeng/dynamicdialog'; @@ -26,11 +26,11 @@ type ChartOptions = { }) export class GraphComponent implements OnInit, OnDestroy { public dialogService = inject(DialogService); - @Input() data!: GraphData[]; - @Input() color!: string; // Must be hex - @Input() title?: string; - @Input() graphContainerId!: string; - @Input({ required: false }) timeRangeSec!: number; + data = input.required(); + color = input.required(); // Must be hex + title = input(undefined); + graphContainerId = input.required(); + timeRangeSec = input(undefined); options!: ChartOptions; chart!: ApexCharts; timeDiffMs: number = 0; @@ -39,11 +39,11 @@ export class GraphComponent implements OnInit, OnDestroy { timeOuts: NodeJS.Timeout[] = []; openDialog = () => { this.dialogService.open(GraphDialogComponent, { - header: this.title, + header: this.title(), data: { - data: this.data, - color: this.color, - title: this.title + data: this.data(), + color: this.color(), + title: this.title() } }); }; @@ -51,13 +51,13 @@ export class GraphComponent implements OnInit, OnDestroy { updateChart = () => { this.chart.updateSeries([ { - name: this.title, - data: Array.from(this.data) + name: this.title(), + data: Array.from(this.data()) } ]); - if (!this.isSliding && this.data.length > 2) { - this.timeDiffMs = this.data[this.data.length - 1].x - this.data[0].x; + if (!this.isSliding && this.data().length > 2) { + this.timeDiffMs = this.data()[this.data().length - 1].x - this.data()[0].x; } if (!this.isSliding && this.timeDiffMs > this.timeRangeMs) { @@ -79,7 +79,7 @@ export class GraphComponent implements OnInit, OnDestroy { }; ngOnInit(): void { - this.timeRangeMs = (this.timeRangeSec ?? 120) * 1000; + this.timeRangeMs = (this.timeRangeSec() ?? 120) * 1000; this.options = { chart: { @@ -105,7 +105,7 @@ export class GraphComponent implements OnInit, OnDestroy { }, stroke: { curve: 'straight', - colors: [this.color] + colors: [this.color()] }, markers: { size: 0 @@ -168,7 +168,7 @@ export class GraphComponent implements OnInit, OnDestroy { //Weird rendering stuff with apex charts, view link to see why https://github.com/apexcharts/react-apexcharts/issues/187 this.timeOuts.push( setTimeout(() => { - const chartContainer = document.getElementById(this.graphContainerId); + const chartContainer = document.getElementById(this.graphContainerId()); if (!chartContainer) { return; } diff --git a/angular-client/src/components/grid-layout/grid-layout.component.html b/angular-client/src/components/grid-layout/grid-layout.component.html index 0e15f7680..2e126b0c0 100644 --- a/angular-client/src/components/grid-layout/grid-layout.component.html +++ b/angular-client/src/components/grid-layout/grid-layout.component.html @@ -1,3 +1,3 @@ -
+
diff --git a/angular-client/src/components/grid-layout/grid-layout.component.ts b/angular-client/src/components/grid-layout/grid-layout.component.ts index 669f04d41..5cd74c658 100644 --- a/angular-client/src/components/grid-layout/grid-layout.component.ts +++ b/angular-client/src/components/grid-layout/grid-layout.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, computed, input } from '@angular/core'; import { NgStyle } from '@angular/common'; type RepeatMode = 'auto-fit' | 'auto-fill'; @@ -16,23 +16,22 @@ type RepeatMode = 'auto-fit' | 'auto-fill'; selector: 'grid-layout', templateUrl: './grid-layout.component.html', styleUrls: ['./grid-layout.component.css'], - standalone: true, imports: [NgStyle] }) export default class GridLayoutComponent { /** Minimum column width (e.g. '300px', '20rem') */ - @Input() minWidth: string = '300px'; + minWidth = input('300px'); /** Maximum column width (e.g. '1fr', '500px') */ - @Input() maxWidth: string = '1fr'; + maxWidth = input('1fr'); /** Grid repeat mode: 'auto-fit' collapses empty tracks, 'auto-fill' keeps them */ - @Input() repeatMode: RepeatMode = 'auto-fit'; + repeatMode = input('auto-fit'); /** Gap between grid items (e.g. '15px', '1rem') */ - @Input() gap: string = '15px'; + gap = input('15px'); - get gridTemplateColumns(): string { - return `repeat(${this.repeatMode}, minmax(${this.minWidth}, ${this.maxWidth}))`; - } + gridTemplateColumns = computed(() => { + return `repeat(${this.repeatMode()}, minmax(${this.minWidth()}, ${this.maxWidth()}))`; + }); } diff --git a/angular-client/src/components/hstack/hstack.component.html b/angular-client/src/components/hstack/hstack.component.html index 3a68ee5b9..1111e9cc1 100644 --- a/angular-client/src/components/hstack/hstack.component.html +++ b/angular-client/src/components/hstack/hstack.component.html @@ -1,3 +1,3 @@ -
+
diff --git a/angular-client/src/components/hstack/hstack.component.ts b/angular-client/src/components/hstack/hstack.component.ts index 93ed89534..52d30dbc8 100644 --- a/angular-client/src/components/hstack/hstack.component.ts +++ b/angular-client/src/components/hstack/hstack.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, input } from '@angular/core'; import { NgStyle } from '@angular/common'; type JusitfyContent = 'space-between' | 'space-around' | 'space-evenly' | 'center' | 'start' | 'end'; @@ -12,14 +12,8 @@ type AlignItems = 'center' | 'start' | 'end' | 'stretch'; standalone: true, imports: [NgStyle] }) -export default class HStackComponent implements OnInit { - @Input() spacing: string = '20px'; - @Input() justifyContent: JusitfyContent = 'center'; - @Input() alignItems: AlignItems = 'center'; - - alignment!: string; - - ngOnInit() { - this.alignment = `${this.justifyContent} ${this.alignItems}`; - } +export default class HStackComponent { + spacing = input('20px'); + justifyContent = input('center'); + alignItems = input('center'); } diff --git a/angular-client/src/components/info-background/info-background.component.html b/angular-client/src/components/info-background/info-background.component.html index 897c3c352..dd2901da0 100644 --- a/angular-client/src/components/info-background/info-background.component.html +++ b/angular-client/src/components/info-background/info-background.component.html @@ -18,16 +18,14 @@ }
- @if (button) { + @if (button(); as btn) {
- @if (button) { - - } +
} @if (this.selectorConfigs().length !== 0) { diff --git a/angular-client/src/components/info-background/info-background.component.ts b/angular-client/src/components/info-background/info-background.component.ts index 08264318f..34cab91da 100644 --- a/angular-client/src/components/info-background/info-background.component.ts +++ b/angular-client/src/components/info-background/info-background.component.ts @@ -1,4 +1,4 @@ -import { Component, input, Input } from '@angular/core'; +import { Component, input } from '@angular/core'; import Theme from 'src/services/theme.service'; import { SelectorConfig, SelectDropdownComponent } from '../select-dropdown/select-dropdown.component'; import { MatIcon } from '@angular/material/icon'; @@ -28,7 +28,7 @@ export class InfoBackgroundComponent { backgroundColor = input(Theme.infoBackground); title = input(); onClick = input<(() => void) | undefined>(undefined); - @Input() button?: ButtonInputs; + button = input(undefined); selectorConfigs = input([]); topRightInfo = input(undefined); topRightInfoSize = input('19px'); diff --git a/angular-client/src/components/info-graph/info-graph.component.html b/angular-client/src/components/info-graph/info-graph.component.html index e0bca76d4..cb3f37ad3 100644 --- a/angular-client/src/components/info-graph/info-graph.component.html +++ b/angular-client/src/components/info-graph/info-graph.component.html @@ -1,9 +1,9 @@ - + diff --git a/angular-client/src/components/info-graph/info-graph.component.ts b/angular-client/src/components/info-graph/info-graph.component.ts index 70f0acab2..12de1bc78 100644 --- a/angular-client/src/components/info-graph/info-graph.component.ts +++ b/angular-client/src/components/info-graph/info-graph.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, inject } from '@angular/core'; +import { Component, inject, input } from '@angular/core'; import { DialogService } from 'primeng/dynamicdialog'; import { GraphDialogComponent } from '../graph-dialog/graph-dialog.component'; import { GraphData } from 'src/utils/types.utils'; @@ -15,21 +15,21 @@ import { GraphComponent } from '../graph/graph.component'; }) export class InfoGraphComponent { public dialogService = inject(DialogService); - @Input() data!: GraphData[]; - @Input() icon!: string; - @Input() title!: string; - @Input() color!: string; - @Input() subTitle?: string; - @Input() graphContainerId!: string; + data = input.required(); + icon = input.required(); + title = input.required(); + color = input.required(); + subTitle = input(undefined); + graphContainerId = input.required(); openDialog = () => { this.dialogService.open(GraphDialogComponent, { - header: this.title, + header: this.title(), data: { - data: this.data, - color: this.color, - title: this.title, - subTitle: this.subTitle, - graphContainerId: this.graphContainerId + 'big' + data: this.data(), + color: this.color(), + title: this.title(), + subTitle: this.subTitle(), + graphContainerId: this.graphContainerId() + 'big' }, modal: true, // makes the dialog modal dismissableMask: true, // enables auto-close on outside click diff --git a/angular-client/src/components/info-value-dispaly/info-value-display.component.html b/angular-client/src/components/info-value-dispaly/info-value-display.component.html index d0af7da16..3732100c0 100644 --- a/angular-client/src/components/info-value-dispaly/info-value-display.component.html +++ b/angular-client/src/components/info-value-dispaly/info-value-display.component.html @@ -6,7 +6,7 @@ } @else { diff --git a/angular-client/src/components/info-value-dispaly/info-value-display.component.ts b/angular-client/src/components/info-value-dispaly/info-value-display.component.ts index c26463227..3a2c3fc51 100644 --- a/angular-client/src/components/info-value-dispaly/info-value-display.component.ts +++ b/angular-client/src/components/info-value-dispaly/info-value-display.component.ts @@ -1,4 +1,5 @@ -import { Component, input, OnChanges } from '@angular/core'; +import { Component, computed, input } from '@angular/core'; +import { formatDisplayValue } from 'src/utils/pipes.utils'; import { BatteryPercentageComponent } from '../battery-percentage/battery-percentage.component'; import { ConnectionDotWithMessageComponent } from '../connection-dot-with-message/connection-dot-with-message.component'; import TypographyComponent from '../typography/typography.component'; @@ -42,10 +43,7 @@ export type WidgetConfig = ThermometerConfig | BatteryConfig | ConnectionDotConf VStackComponent ] }) -export class InfoValueDisplayComponent implements OnChanges { - ngOnChanges(): void { - this.formattedValue = (this.value()?.toFixed(this.precision()) ?? '-') + (this.unit() === 'C' ? '°' : ''); - } +export class InfoValueDisplayComponent { containerStyle = input(''); valueUnitContainerStyle = input(''); value = input(); @@ -56,7 +54,8 @@ export class InfoValueDisplayComponent implements OnChanges { subtitleStyle = input(''); unit = input(''); unitStyle = input(''); - formattedValue = '-'; + + formattedValue = computed(() => formatDisplayValue(this.value(), this.precision(), this.unit())); // Consolidated widget input widget = input(); diff --git a/angular-client/src/components/latency-display/latency-display.html b/angular-client/src/components/latency-display/latency-display.html index f831ba57f..ec657476b 100644 --- a/angular-client/src/components/latency-display/latency-display.html +++ b/angular-client/src/components/latency-display/latency-display.html @@ -3,7 +3,7 @@
-
+
@@ -14,7 +14,7 @@
-
+
diff --git a/angular-client/src/components/latency-display/latency-display.ts b/angular-client/src/components/latency-display/latency-display.ts index 0949eb2c4..4e60b4407 100644 --- a/angular-client/src/components/latency-display/latency-display.ts +++ b/angular-client/src/components/latency-display/latency-display.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit, inject } from '@angular/core'; +import { Component, OnInit, inject, input } from '@angular/core'; import Storage from 'src/services/storage.service'; import { topics } from 'src/utils/topic.utils'; import { InfoBackgroundComponent } from '../info-background/info-background.component'; @@ -17,9 +17,9 @@ import HStackComponent from '../hstack/hstack.component'; }) export default class LatencyDisplayComponent implements OnInit { private storage = inject(Storage); - @Input() lowVal: number = 0; - @Input() medVal: number = 50; - @Input() highVal: number = 100; + lowVal = input(0); + medVal = input(50); + highVal = input(100); latency: number = 0; newLatency: number = 0; diff --git a/angular-client/src/components/pie-chart/pie-chart.component.ts b/angular-client/src/components/pie-chart/pie-chart.component.ts index 572a30f1a..edcf7c725 100644 --- a/angular-client/src/components/pie-chart/pie-chart.component.ts +++ b/angular-client/src/components/pie-chart/pie-chart.component.ts @@ -1,4 +1,4 @@ -import { Component, ElementRef, Input, Renderer2, OnInit, inject, input } from '@angular/core'; +import { Component, ElementRef, OnInit, Renderer2, inject, input } from '@angular/core'; import { ApexNonAxisChartSeries, ApexPlotOptions, ApexChart, ApexFill, NgApexchartsModule } from 'ng-apexcharts'; import Theme from 'src/services/theme.service'; @@ -24,7 +24,7 @@ export default class PieChartComponent implements OnInit { // eslint-disable-next-line @typescript-eslint/no-explicit-any public chartOptions!: Partial | any; data = input.required<{ value: number; name: string }[]>(); - @Input() backgroundColor: string = Theme.infoBackground; + backgroundColor = input(Theme.infoBackground); title = input('Pie Chart'); currentWidth: number = 0; @@ -61,7 +61,7 @@ export default class PieChartComponent implements OnInit { chart: { width: '100%', type: 'pie', - background: this.backgroundColor, + background: this.backgroundColor(), redrawOnParentResize: true, foreColor: '#ffffff', animations: { diff --git a/angular-client/src/components/raspberry-pi/raspberry-pi-desktop-content/raspberry-pi-desktop.component.html b/angular-client/src/components/raspberry-pi/raspberry-pi-desktop-content/raspberry-pi-desktop.component.html index a14949944..b50c6f2c1 100644 --- a/angular-client/src/components/raspberry-pi/raspberry-pi-desktop-content/raspberry-pi-desktop.component.html +++ b/angular-client/src/components/raspberry-pi/raspberry-pi-desktop-content/raspberry-pi-desktop.component.html @@ -1,6 +1,6 @@ - + @@ -8,9 +8,9 @@ - +
- +
@@ -19,7 +19,7 @@ - +
@@ -30,7 +30,7 @@
- +
@@ -44,7 +44,7 @@
- +
diff --git a/angular-client/src/components/raspberry-pi/raspberry-pi-desktop-content/raspberry-pi-desktop.component.ts b/angular-client/src/components/raspberry-pi/raspberry-pi-desktop-content/raspberry-pi-desktop.component.ts index 004fa2b9a..542f9d6fe 100644 --- a/angular-client/src/components/raspberry-pi/raspberry-pi-desktop-content/raspberry-pi-desktop.component.ts +++ b/angular-client/src/components/raspberry-pi/raspberry-pi-desktop-content/raspberry-pi-desktop.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, input } from '@angular/core'; import { CircularPercentageComponent } from '../../circular-percentage/circular-percentage.component'; import { DividerComponent } from '../../divider/divider'; import { MatIcon } from '@angular/material/icon'; @@ -23,11 +23,11 @@ import ThermometerComponent from 'src/components/thermometer/thermometer.compone ] }) export default class RaspberryPiDesktopComponent { - @Input() cpuUsage: number = 0; - @Input() cpuTemp: number = 0; - @Input() ramUsage: number = 0; - @Input() wifiRSSI: number = 0; - @Input() mcs: number = 0; + cpuUsage = input(0); + cpuTemp = input(0); + ramUsage = input(0); + wifiRSSI = input(0); + mcs = input(0); colorRed = '#FF0000'; colorPurple = '#800080'; diff --git a/angular-client/src/components/raspberry-pi/raspberry-pi-mobile-content/raspberry-pi-mobile.component.html b/angular-client/src/components/raspberry-pi/raspberry-pi-mobile-content/raspberry-pi-mobile.component.html index ddf2b6e44..b16538bc2 100644 --- a/angular-client/src/components/raspberry-pi/raspberry-pi-mobile-content/raspberry-pi-mobile.component.html +++ b/angular-client/src/components/raspberry-pi/raspberry-pi-mobile-content/raspberry-pi-mobile.component.html @@ -1,7 +1,7 @@ - + @@ -9,9 +9,9 @@ - +
- +
@@ -21,7 +21,7 @@ - + @@ -29,7 +29,7 @@ - +
@@ -39,7 +39,7 @@ - +
dBM diff --git a/angular-client/src/components/raspberry-pi/raspberry-pi-mobile-content/raspberry-pi-mobile.component.ts b/angular-client/src/components/raspberry-pi/raspberry-pi-mobile-content/raspberry-pi-mobile.component.ts index 2b1bd8ef3..bf96a0caa 100644 --- a/angular-client/src/components/raspberry-pi/raspberry-pi-mobile-content/raspberry-pi-mobile.component.ts +++ b/angular-client/src/components/raspberry-pi/raspberry-pi-mobile-content/raspberry-pi-mobile.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, input } from '@angular/core'; import { CircularPercentageComponent } from '../../circular-percentage/circular-percentage.component'; import { DividerComponent } from '../../divider/divider'; import { MatIcon } from '@angular/material/icon'; @@ -23,11 +23,11 @@ import ThermometerComponent from 'src/components/thermometer/thermometer.compone ] }) export default class RaspberryPiMobileComponent { - @Input() cpuUsage: number = 0; - @Input() cpuTemp: number = 0; - @Input() ramUsage: number = 0; - @Input() wifiRSSI: number = 0; - @Input() mcs: number = 0; + cpuUsage = input(0); + cpuTemp = input(0); + ramUsage = input(0); + wifiRSSI = input(0); + mcs = input(0); colorRed = '#FF0000'; colorPurple = '#800080'; diff --git a/angular-client/src/components/select-dropdown/select-dropdown.component.ts b/angular-client/src/components/select-dropdown/select-dropdown.component.ts index 425571ffe..9c21ab4ba 100644 --- a/angular-client/src/components/select-dropdown/select-dropdown.component.ts +++ b/angular-client/src/components/select-dropdown/select-dropdown.component.ts @@ -1,4 +1,4 @@ -import { Component, effect, input, ViewChild } from '@angular/core'; +import { Component, effect, input, viewChild } from '@angular/core'; import { SelectChangeEvent, Select } from 'primeng/select'; import { ReactiveFormsModule, FormsModule } from '@angular/forms'; @@ -34,7 +34,7 @@ export class SelectDropdownComponent { selectedOption: DropdownOption | undefined; // eslint-disable-next-line @typescript-eslint/no-explicit-any - @ViewChild('dropdownRef') dropdownRef: any; + dropdownRef = viewChild('dropdownRef'); constructor() { // The dropdown's selected display tracks `defaultValue`: diff --git a/angular-client/src/components/sidebar-chip/sidebar-chip.component.html b/angular-client/src/components/sidebar-chip/sidebar-chip.component.html index 54902aee0..21c9cc4c0 100644 --- a/angular-client/src/components/sidebar-chip/sidebar-chip.component.html +++ b/angular-client/src/components/sidebar-chip/sidebar-chip.component.html @@ -1,9 +1,9 @@ -
+
- +
diff --git a/angular-client/src/components/sidebar-chip/sidebar-chip.component.ts b/angular-client/src/components/sidebar-chip/sidebar-chip.component.ts index 9537e6dbb..846bc8d21 100644 --- a/angular-client/src/components/sidebar-chip/sidebar-chip.component.ts +++ b/angular-client/src/components/sidebar-chip/sidebar-chip.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, input } from '@angular/core'; import { MatIcon } from '@angular/material/icon'; import TypographyComponent from '../typography/typography.component'; @@ -10,8 +10,8 @@ import TypographyComponent from '../typography/typography.component'; imports: [MatIcon, TypographyComponent] }) export default class SidebarChipComponent { - @Input() icon: string = ''; - @Input() value: string = ''; - @Input() active: boolean = false; // New input to control active state - @Input() small: boolean = false; + icon = input(''); + value = input(''); + active = input(false); // New input to control active state + small = input(false); } diff --git a/angular-client/src/components/switch/switch.component.html b/angular-client/src/components/switch/switch.component.html index b5647513f..bb9804f64 100644 --- a/angular-client/src/components/switch/switch.component.html +++ b/angular-client/src/components/switch/switch.component.html @@ -1,5 +1,5 @@ -
-
+
+
- + diff --git a/angular-client/src/components/switch/switch.component.ts b/angular-client/src/components/switch/switch.component.ts index 32784506b..c530f5d3e 100644 --- a/angular-client/src/components/switch/switch.component.ts +++ b/angular-client/src/components/switch/switch.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core'; +import { Component, OnInit, computed, input, output, signal } from '@angular/core'; import TypographyComponent from '../typography/typography.component'; @Component({ @@ -9,24 +9,19 @@ import TypographyComponent from '../typography/typography.component'; imports: [TypographyComponent] }) export class SwitchComponent implements OnInit { - @Input() isOn: boolean = false; - @Input() offString: string = 'PAUSED'; - @Input() onString: string = 'ALLOWED'; - chargingString: string = this.offString; - @Output() toggleEmitter = new EventEmitter(); + isOn = input(false); + offString = input('PAUSED'); + onString = input('ALLOWED'); + currentState = signal(false); + chargingString = computed(() => (this.currentState() ? this.onString() : this.offString())); + toggleEmitter = output(); ngOnInit(): void { - // Set the initial value of chargingString based on isOn - this.chargingString = this.isOn ? this.onString : this.offString; + this.currentState.set(this.isOn()); } onToggle() { - this.isOn = !this.isOn; - if (this.isOn) { - this.chargingString = this.onString; - } else { - this.chargingString = this.offString; - } - this.toggleEmitter.emit(this.isOn); // Emit the new state + this.currentState.update((v) => !v); + this.toggleEmitter.emit(this.currentState()); } } diff --git a/angular-client/src/components/thermometer/thermometer.component.html b/angular-client/src/components/thermometer/thermometer.component.html index 333f7b844..44a607d22 100644 --- a/angular-client/src/components/thermometer/thermometer.component.html +++ b/angular-client/src/components/thermometer/thermometer.component.html @@ -1,4 +1,4 @@
-
-
+
+
diff --git a/angular-client/src/components/thermometer/thermometer.component.ts b/angular-client/src/components/thermometer/thermometer.component.ts index ef9f73028..f57c9a960 100644 --- a/angular-client/src/components/thermometer/thermometer.component.ts +++ b/angular-client/src/components/thermometer/thermometer.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, input } from '@angular/core'; @Component({ selector: 'thermometer', @@ -7,9 +7,9 @@ import { Component, Input } from '@angular/core'; standalone: true }) export default class ThermometerComponent { - @Input() temperature: number = 0; - @Input() min: number = 0; - @Input() max: number = 100; + temperature = input(0); + min = input(0); + max = input(100); mapColor = (value: number, min: number, max: number) => { const range = max - min; diff --git a/angular-client/src/components/vstack/vstack.component.html b/angular-client/src/components/vstack/vstack.component.html index f6e187409..bda265726 100644 --- a/angular-client/src/components/vstack/vstack.component.html +++ b/angular-client/src/components/vstack/vstack.component.html @@ -1,3 +1,3 @@ -
+
diff --git a/angular-client/src/components/vstack/vstack.component.ts b/angular-client/src/components/vstack/vstack.component.ts index e19071e7b..31bf88f7e 100644 --- a/angular-client/src/components/vstack/vstack.component.ts +++ b/angular-client/src/components/vstack/vstack.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, input } from '@angular/core'; import { NgStyle } from '@angular/common'; @Component({ @@ -9,6 +9,6 @@ import { NgStyle } from '@angular/common'; imports: [NgStyle] }) export default class VStackComponent { - @Input() spacing: string = '5px'; - @Input() align: string = 'center'; + spacing = input('5px'); + align = input('center'); } diff --git a/angular-client/src/pages/camera-page/camera-page.component.ts b/angular-client/src/pages/camera-page/camera-page.component.ts index 9f8a0df6f..251f6a7d5 100644 --- a/angular-client/src/pages/camera-page/camera-page.component.ts +++ b/angular-client/src/pages/camera-page/camera-page.component.ts @@ -1,4 +1,4 @@ -import { Component, ElementRef, inject, OnInit, ViewChild } from '@angular/core'; +import { Component, ElementRef, OnInit, inject, viewChild } from '@angular/core'; import { MessageService } from 'primeng/api'; import { SelectChangeEvent, Select } from 'primeng/select'; import { urls } from 'src/api/urls'; @@ -28,8 +28,8 @@ export class CameraPageComponent implements OnInit { videoUrls: string[] = ['Live Stream']; videoUrlsIsLoading: boolean = true; - @ViewChild('remoteVideo') remoteVideo?: ElementRef; - @ViewChild('playbackVideo', { static: false }) playbackVideoRef!: ElementRef; + remoteVideo = viewChild>('remoteVideo'); + playbackVideoRef = viewChild.required>('playbackVideo'); async ngOnInit(): Promise { this.urlAvailable = await this.checkConnection(); @@ -37,8 +37,9 @@ export class CameraPageComponent implements OnInit { url: new URL('whep', this.url), onError: console.log, onTrack: (e) => { - if (this.remoteVideo) { - [this.remoteVideo.nativeElement.srcObject] = e.streams; + const video = this.remoteVideo(); + if (video) { + [video.nativeElement.srcObject] = e.streams; } } }); @@ -66,7 +67,7 @@ export class CameraPageComponent implements OnInit { this.liveStream = false; // Wait for Angular to update the DOM setTimeout(() => { - const videoEl = this.playbackVideoRef.nativeElement; + const videoEl = this.playbackVideoRef().nativeElement; videoEl.load(); // This reloads the new inside the