Browse Source

Angular: some improvements to dragging & zooming; layouted chart card

Christian Kahlau 3 years ago
parent
commit
2a48aec692

+ 28 - 8
ng/src/app/components/server-data-chart/server-data-chart.component.html

@@ -1,16 +1,36 @@
-<div class="card mb-2" (mousewheel)="zoom($event)" (dragstart)="dragStart($event)" (drag)="drag($event)" (dragend)="dragEnd($event)" draggable="true">
-  <div class="card-header d-flex flex-row">
-    <span class="flex-fill">
+<div class="card mb-2">
+  <div class="card-header d-flex flex-column">
+    <span>
       <fa-icon [icon]="parent ?? type | faType" class="pe-2"></fa-icon>
       <span class="text-uppercase">{{ parent ?? type }}</span>
       <ng-container *ngIf="parent"> : {{ type }} </ng-container>
     </span>
-    <span>
-      <span class="badge bd-blue-100">{{ start | date: 'YYYY-MM-dd HH:mm:ss' }}</span> -
-      <span class="badge bd-blue-100">{{ end | date: 'YYYY-MM-dd HH:mm:ss' }}</span>
-    </span>
   </div>
   <div class="card-body">
-    <canvas baseChart type="line" [data]="data" [options]="options"></canvas>
+    <span class="d-flex flex-row">
+      <span class="badge bd-gray-100">
+        <fa-icon [icon]="fa.locationDot" class="pe-1"></fa-icon><fa-icon [icon]="fa.ellipsis" class="pe-1"></fa-icon>
+        {{ start | date: 'YYYY-MM-dd HH:mm:ss' }}
+      </span>
+      <span class="flex-fill">&nbsp;</span>
+      <span class="badge bd-gray-100">
+        {{ end | date: 'YYYY-MM-dd HH:mm:ss' }}
+        <fa-icon [icon]="fa.ellipsis" class="ps-1"></fa-icon><fa-icon [icon]="fa.locationDot" class="ps-1"></fa-icon>
+      </span>
+    </span>
+    <canvas
+      baseChart
+      type="line"
+      [data]="data"
+      [class.zoom]="zoomMode"
+      [options]="options"
+      (mousewheel)="zoom($event)"
+      (dragstart)="dragStart($event)"
+      (drag)="drag($event)"
+      (dragend)="dragEnd($event)"
+      draggable="true"></canvas>
   </div>
 </div>
+
+<!-- 1x1 px transparent gif for hiding the default ghost image while dragging elements -->
+<img #dragImage src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="d-none" />

+ 7 - 0
ng/src/app/components/server-data-chart/server-data-chart.component.scss

@@ -0,0 +1,7 @@
+.card > .card-body > canvas {
+  cursor: grab;
+
+  &.zoom {
+    cursor: zoom-in;
+  }
+}

+ 26 - 31
ng/src/app/components/server-data-chart/server-data-chart.component.ts

@@ -1,4 +1,5 @@
-import { Component, Input, OnInit } from '@angular/core';
+import { Component, ElementRef, HostListener, Input, OnInit, ViewChild } from '@angular/core';
+import { faLocationDot, faEllipsis } from '@fortawesome/free-solid-svg-icons';
 import { ChartData, ChartOptions, TooltipItem } from 'chart.js';
 import { debounceTime, throttleTime, Subject } from 'rxjs';
 
@@ -27,27 +28,23 @@ const typeBgColors: { [k: string]: string } = {
 export class ServerDataChartComponent implements OnInit {
   private zoomEvent$ = new Subject<void>();
   private dragEvent$ = new Subject<DragEvent>();
-  private _type!: string;
+
+  @ViewChild('dragImage') dragImage!: ElementRef<HTMLImageElement>;
 
   @Input() parent?: string;
   @Input() server!: ServerConfig;
-  @Input() set type(type: string) {
-    this._type = type;
-  }
-
-  public get type(): string {
-    return this._type;
-  }
+  @Input() type!: string;
 
   public start!: Date;
   public end!: Date;
   public data?: ChartData<'line', ServerData[], Date>;
   public options!: ChartOptions<'line'> & any;
+  public fa = { locationDot: faLocationDot, ellipsis: faEllipsis };
 
   constructor(private apiService: ServerApiService, private bytePipe: BytePipe) {
     this.defaults();
     this.zoomEvent$.pipe(debounceTime(400)).subscribe({ next: this.updateData.bind(this) });
-    this.dragEvent$.pipe(throttleTime(400)).subscribe({ next: this.dragDebounced.bind(this) });
+    this.dragEvent$.pipe(throttleTime(80)).subscribe({ next: this.dragDebounced.bind(this) });
   }
 
   ngOnInit(): void {
@@ -110,7 +107,8 @@ export class ServerDataChartComponent implements OnInit {
           type: 'linear',
           ticks: {
             callback: (val: string | number) => (typeof val === 'number' && this.type !== 'cpu' ? this.bytePipe.transform(val) : `${val} %`)
-          }
+          },
+          max: this.type === 'cpu' ? 100 : undefined
         }
       }
     };
@@ -190,19 +188,30 @@ export class ServerDataChartComponent implements OnInit {
     rangeEnd: 0
   };
 
+  public zoomMode = false;
+
+  @HostListener('window:keydown', ['$event'])
+  hotKeydown(event: KeyboardEvent) {
+    if (event.shiftKey) this.zoomMode = true;
+  }
+  @HostListener('window:keyup', ['$event'])
+  hotKeyup(event: KeyboardEvent) {
+    if (event.key.toLowerCase() === 'shift') this.zoomMode = false;
+  }
+
   dragStart(ev: Event) {
     const event = ev as DragEvent;
 
-    console.log('[DRAGSTART]', {
-      offsetX: event.offsetX,
-      clientWidth: (event.target as HTMLCanvasElement).clientWidth,
-      xRel: ((event.offsetX / (event.target as HTMLCanvasElement).clientWidth) * 100).toFixed(2)
-    });
     this.dragStartParams = {
       offsetX: event.offsetX,
       rangeStart: this.start.getTime(),
       rangeEnd: this.end.getTime()
     };
+    if (event.dataTransfer) {
+      event.dataTransfer.setDragImage(this.dragImage.nativeElement, -10, -10);
+      event.dataTransfer.effectAllowed = 'all';
+    }
+    (event.target as HTMLCanvasElement).style.cursor = 'grabbing';
 
     event.stopPropagation();
     // event.preventDefault();
@@ -215,7 +224,6 @@ export class ServerDataChartComponent implements OnInit {
     event.stopPropagation();
     event.cancelBubble = true;
 
-    console.log('[DRAG]');
     this.dragEvent$.next(event);
   }
 
@@ -231,14 +239,6 @@ export class ServerDataChartComponent implements OnInit {
       this.start = new Date(newStartMs);
       this.end = new Date(newEndMs);
 
-      console.log('[DRAGDEBOUNCE]', {
-        offsetX: event.offsetX,
-        clientWidth: (event.target as HTMLCanvasElement).clientWidth,
-        xDiffRel,
-        newStart: this.start,
-        newEnd: this.end
-      });
-
       this.updateOptions();
     }
   }
@@ -246,12 +246,7 @@ export class ServerDataChartComponent implements OnInit {
   dragEnd(ev: Event) {
     const event = ev as DragEvent;
 
-    console.log('[DRAGEND]', {
-      offsetX: event.offsetX,
-      clientWidth: (event.target as HTMLCanvasElement).clientWidth,
-      xRel: (((event.offsetX - this.dragStartParams.offsetX) / (event.target as HTMLCanvasElement).clientWidth) * 100).toFixed(2)
-    });
-
+    (event.target as HTMLCanvasElement).style.removeProperty('cursor');
     this.updateOptions();
     this.zoomEvent$.next();
   }