Jelajahi Sumber

Sensor view ui refresh + graph width responsiveness

A-Konig 3 minggu lalu
induk
melakukan
4e9abb6223

+ 41 - 25
src/app/sensor/components/sensor.component.html

@@ -2,9 +2,12 @@
 
 <div class="container graph">
 
-  <button pButton type="button" label="Back to Dashboard" class="p-button-primary" icon="pi pi-backward" [routerLink]="['/dashboard']" style="margin-bottom: 15px;"></button>
+    <div class="button-row">
+        <button pButton type="button" label="Back to Dashboard" class="p-button-primary top-button" icon="pi pi-backward" [routerLink]="['/dashboard']" style="margin-bottom: 15px;"></button>
+        <button pButton type="button" label="Back to Map" class="p-button-primary top-button" icon="pi pi-backward" [routerLink]="['/dashboard']" [queryParams]="{ tab: 'map' }" style="margin-bottom: 15px;"></button>
+    </div>
 
-  <div class="row graph-information">
+  <div class="graph-information">
     <div class="graph-desc">
       <div>
         <span class="graph-attr-heading">Unit name: </span>
@@ -27,32 +30,45 @@
         {{sensor?.phenomenon?.phenomenonName}} ({{sensor?.phenomenon?.unit}})
       </div>
     </div>
-    <div class="graph-range-dates">
-      <div class="input-group form-group">
-        <div class="input-group-prepend">
-          <span class="input-group-text text-color-date background-date-color"><i class="fa fa-calendar-alt" aria-hidden="false"></i></span>
-        </div>
-        <p-calendar id="from" [(ngModel)]="from" [showTime]="true" (onSelect)="onDateChanged()" [maxDate]="today" showButtonBar="true"></p-calendar>
-      </div>
-      <div class="graph-range-dates-separator">
-        <div></div>
+      <div class="graph-range-dates">
+          <div class="input-group form-group">
+              <p-calendar
+                      id="from"
+                      [style]="{'width':'100%'}"
+                      [inputStyle]="{'width':'100%'}"
+                      [(ngModel)]="from"
+                      [showIcon]=true
+                      [showTime]=true
+                      (onSelect)="onDateChanged()"
+                      [maxDate]="today"
+                      showButtonBar=true>
+              </p-calendar>
+          </div>
+          <div class="graph-range-dates-separator">
+              <div></div>
+          </div>
+          <div class="input-group form-group">
+              <p-calendar
+                      id="to"
+                      [style]="{'width':'100%'}"
+                      [inputStyle]="{'width':'100%'}"
+                      [(ngModel)]="to"
+                      [showIcon]=true
+                      [showTime]=true
+                      (onSelect)="onDateChanged()"
+                      [maxDate]="today"
+                      showButtonBar=true>
+              </p-calendar>
+          </div>
+          <div>
+              <button pButton type="button" *ngIf="dateChanged" label="Load data" class="p-button-primary" icon="pi pi-chart-line" (click)="showGraph()"></button>
+          </div>
       </div>
-      <div class="input-group form-group">
-        <div class="input-group-prepend">
-          <span class="input-group-text text-color-date background-date-color"><i class="fa fa-calendar-alt" aria-hidden="false"></i></span>
-        </div>
-        <p-calendar id="to" [(ngModel)]="to" [showTime]="true" (onSelect)="onDateChanged()" [maxDate]="today" showButtonBar="true"></p-calendar>
-      </div>      
-      <!-- <p-listbox *ngIf="showAggregation" [options]="aggregationFunction" [(ngModel)]="selectedAggregationFunction" optionLabel="name" optionValue="code"></p-listbox> -->
-      <div>
-        <button pButton label="Load data" *ngIf="dateChanged" class="p-button-primary" icon="pi pi-chart-line" (click)="showGraph()"></button>
+      <div *ngIf="showIntervalError" class="alert alert-danger interval-alert" role="alert">
+          Select a valid interval - interval should be smaller than <b>6 months</b> and the "from" date has to <b>precede</b> the "to" date!
       </div>
-    </div>
-    <div *ngIf="showIntervalError" class="alert alert-danger" role="alert">
-      Select data from interval smaller than <b>6 months</b>!
-    </div>
   </div>
   <div class="graph-view-wrapper">
-    <div id="view"></div>
+    <div id="view" style="width:100%; height:auto;"></div>
   </div>
 </div>

+ 26 - 0
src/app/sensor/components/sensor.component.scss

@@ -0,0 +1,26 @@
+::ng-deep .p-button {
+  background: #174B97 !important;
+  border-color: #174B97 !important;
+}
+
+
+::ng-deep .top-button {
+  width: 220px !important;
+}
+
+/* Round only the right side of the calendar button */
+::ng-deep .p-calendar .p-datepicker-trigger {
+  color: white !important;
+  border-top-right-radius: 6px;
+  border-bottom-right-radius: 6px;
+  border-left: none;
+}
+
+/* Add space to the right of every button except the last one */
+::ng-deep .button-row .p-button:not(:last-child) {
+  margin-right: 1rem;
+}
+
+::ng-deep .interval-alert {
+  margin: 5px !important;
+}

+ 45 - 11
src/app/sensor/components/sensor.component.ts

@@ -1,4 +1,4 @@
-import { Component, OnDestroy, OnInit } from '@angular/core';
+import { Component, OnDestroy, OnInit, HostListener } from '@angular/core';
 import { ActivatedRoute } from '@angular/router';
 import * as moment from 'moment-timezone';
 import { GraphLoader } from '../../shared/graph-loading/graphloader';
@@ -9,6 +9,7 @@ import { ToastService } from '../../shared/services/toast.service';
 import { Sensor } from '../../shared/api/endpoints/models/sensor';
 import { SensorsService } from '../../shared/api/endpoints/services/sensors.service';
 import { Subscription } from 'rxjs';
+import type { View } from 'vega';
 
 @Component({
   selector: 'app-sensor',
@@ -22,16 +23,18 @@ export class SensorComponent implements OnInit, OnDestroy {
   data = [];
   time = [];
   from: Date = moment().hour(0).minutes(0).subtract(7, 'days').toDate();
-  to: Date = moment().toDate(); 
+  to: Date = moment().toDate();
   sensor: Sensor;
   dateChanged = false;
   unitDescription: string;
   today: Date = moment().toDate();
   subscription: Subscription[] = [];
-  showIntervalError: boolean = false;
+  showIntervalError = false;
+  private resizeObserver!: ResizeObserver; // Will watch the container size
+  graphView;
 
   constructor(
-    private activatedRoute: ActivatedRoute,    
+    private activatedRoute: ActivatedRoute,
     private observationService: ObservationService,
     private toastService: ToastService,
     private sensorsService: SensorsService,
@@ -46,6 +49,18 @@ export class SensorComponent implements OnInit, OnDestroy {
     }, err => this.toastService.showError(err.error.message));
   }
 
+  ngAfterViewInit() {
+    window.addEventListener('resize', () => console.log('raw'))
+  }
+
+  /*
+    Fires on every resize event
+   */
+  @HostListener('window:resize', ['$event'])
+  public onWindowResize(event: UIEvent): void {
+    this.onResize();
+  }
+
   /**
    * Sets up default data
    */
@@ -77,26 +92,35 @@ export class SensorComponent implements OnInit, OnDestroy {
       this.dateChanged = false;
       this.showIntervalError = true;
     }
+    else if (this.to < this.from) {
+      this.dateChanged = false;
+      this.showIntervalError = true;
+    }
     else{
       this.dateChanged = true;
       this.showIntervalError = false;
     }
-    
   }
 
   /**
    * Gets data based on selected time range
    */
-  showGraph() {   
+  showGraph() {
     const range: Date[] = [this.from, this.to];
     this.getObservations(range);
-  }  
+  }
 
   /**
    * Get data from observation endpoint
    * @param range from and to interval
    */
-  getObservations(range: Date[]) {
+  async getObservations(range: Date[]) {
+    const box = document.getElementById('view');
+    const boxWidth = box.getBoundingClientRect().width;
+    const boxHeight = box.getBoundingClientRect().height;
+
+    GraphLoader.setSize(boxWidth - 50, 300);
+
     this.observationService.getObservation$Response({
       unit_id: this.unitId,
       sensor_id: this.sensorId,
@@ -114,12 +138,22 @@ export class SensorComponent implements OnInit, OnDestroy {
         }
       })
     ).subscribe(
-      observations => {
+      async observations => {
         if (observations) {
-          GraphLoader.getGraph(this.sensorId, observations, this.sensor, '#view', false);
+          this.graphView = await GraphLoader.getGraph(this.sensorId, observations, this.sensor, '#view', false);
         } else {
-          GraphLoader.getGraph(null, null, null, '#view', null);
+          this.graphView = await GraphLoader.getGraph(null, null, null, '#view', null);
         }
       }, err => this.toastService.showError(err.error.message));
   }
+
+  private onResize(): void {
+    // resize graph width if window changes size
+    const box = document.getElementById('view');
+
+    if (this.graphView) {
+      const newWidth = box.getBoundingClientRect().width - 50;
+      this.graphView.width(newWidth).height(300).runAsync();
+    }
+  }
 }

+ 16 - 10
src/app/shared/graph-loading/graphloader.ts

@@ -11,6 +11,7 @@ export class GraphLoader {
   static customSize = -1;
   static width = 800;
   static height = 300;
+  static lastGraphView;
 
   static setSize(width : number, height : number) {
     this.width = width;
@@ -25,8 +26,8 @@ export class GraphLoader {
    * @param element name of html element for graph display
    * @param isAnalytics true/false analytics/observations
    */
-  static getGraph(sensors, data, legendInfo, element, isAnalytics) {
-    this.getGraphWithInterval(sensors, data, 1800000, legendInfo, element, isAnalytics);
+  static async getGraph(sensors, data, legendInfo, element, isAnalytics) {
+    return this.getGraphWithInterval(sensors, data, 1800000, legendInfo, element, isAnalytics);
   }
 
 
@@ -40,7 +41,7 @@ export class GraphLoader {
    * @param element name of html element for graph display
    * @param isAnalytics true/false analytics/observations
    */
-  static getGraphWithInterval(sensors, data, interval, legendInfo, element, isAnalytics) {
+  static async getGraphWithInterval(sensors, data, interval, legendInfo, element, isAnalytics) {
     // gets uses sensors array to get graph type
     // then gets configuration and specification from corresponding class
     let graph = this.getGraphType(sensors,data, interval, legendInfo, isAnalytics);
@@ -48,7 +49,7 @@ export class GraphLoader {
     let spec = graph.getSpec();
 
    // then displays the graph
-    this.showGraph(spec, config, element);
+    return this.showGraph(spec, config, element);
   }
 
 
@@ -62,12 +63,12 @@ export class GraphLoader {
    */
   static getGraphType(sensors, data, interval, legendInfo, isAnalytics): Graph {
     if (sensors == null) {
-      return new EmptyGraph("No sensors selected.");
+      return new EmptyGraph('No sensors selected.');
 
     }  else if (Array.isArray(sensors)) {
-      if (sensors.length == 0) {
-        return new EmptyGraph("No sensors selected.");
-      } else if (sensors.length == 1) {
+      if (sensors.length === 0) {
+        return new EmptyGraph('No sensors selected.');
+      } else if (sensors.length === 1) {
         return new SingleGraph(sensors[0], isAnalytics, data, legendInfo,interval);
       } else {
         return new MultiGraph(isAnalytics, data, legendInfo,interval);
@@ -79,13 +80,14 @@ export class GraphLoader {
   }
 
 
+
   /**
    * Displays the graph
    * @param spec vega specification
    * @param config vega configuration
    * @param element name of html element for graph display
    */
-  static showGraph(spec, config, element) {
+  static async showGraph(spec, config, element) {
     const vega = require('vega');
     const vegaTooltip = require('vega-tooltip');
     const handler = new vegaTooltip.Handler();
@@ -95,10 +97,14 @@ export class GraphLoader {
     config.width = this.width;
     config.height = this.height;
 
-    const view = new vega.View(vega.parse(spec, config))
+    // const view = await
+    return new vega.View(vega.parse(spec, config))
       .tooltip(handler.call)
       .initialize(element)
       .hover()
       .runAsync();
+
+    // this.lastGraphView = view;
+    // console.log("got awaited " + view);
   }
 }

+ 1 - 1
src/app/unit/components/unit.component.html

@@ -49,7 +49,7 @@
       </div>
     </div>
     <div *ngIf="showIntervalError" class="alert alert-danger interval-alert" role="alert">
-      Select a valid interval - interval should be smaller than <b>6 months</b> and the "from" date has to precede the "to" date!
+      Select a valid interval - interval should be smaller than <b>6 months</b> and the "from" date has to <b>precede</b> the "to" date!
     </div>
   </div>
 

+ 1 - 2
src/app/unit/components/unit.component.scss

@@ -21,7 +21,6 @@
     color: white !important;
 }
 
-
 /* Round only the right side of the calendar button */
 ::ng-deep .p-calendar .p-datepicker-trigger {
     border-top-right-radius: 6px;
@@ -31,7 +30,7 @@
 
 /* Add space to the right of every button except the last one */
 ::ng-deep .button-row .p-button:not(:last-child) {
-    margin-right: 1rem;   /* adjust the value to your liking */
+    margin-right: 1rem;
 }
 
 ::ng-deep .interval-alert {

+ 1 - 0
src/vega/config/config.json

@@ -5,6 +5,7 @@
     {"name":  "axeLegendPath", "value":  "phenomenon.phenomenonName"},
     {"name":  "axeUnitPath", "value":  "phenomenon.unit"}
   ],
+  "autosize": "fit-x",
   "width": 800,
   "height": 300
 }