From d075c81496c06c3fb8b0003083767c6b6f288cad Mon Sep 17 00:00:00 2001
From: Xiao Gui <xgui3783@gmail.com>
Date: Tue, 6 Sep 2022 13:56:58 +0200
Subject: [PATCH] feat: sane plugin window placements

---
 src/widget/constants.ts                       |  8 ++-
 src/widget/widget.module.ts                   |  6 ++-
 .../widgetPortal/widgetPortal.component.ts    | 52 +++++++++++++++++--
 .../widgetPortal/widgetPortal.style.css       | 36 +++++--------
 .../widgetPortal/widgetPortal.template.html   | 48 +++++++++++++++--
 src/widget/widgetStateIcon.pipe.ts            | 26 ++++++++++
 6 files changed, 142 insertions(+), 34 deletions(-)
 create mode 100644 src/widget/widgetStateIcon.pipe.ts

diff --git a/src/widget/constants.ts b/src/widget/constants.ts
index a779b079a..53cc6568a 100644
--- a/src/widget/constants.ts
+++ b/src/widget/constants.ts
@@ -22,4 +22,10 @@ export type TypeActionToWidget<T> = (type: EnumActionToWidget, obj: T, option: I
 
 export const WIDGET_PORTAL_TOKEN = new InjectionToken<Record<string, unknown>>("WIDGET_PORTAL_TOKEN")
 
-export const RM_WIDGET = new InjectionToken('RM_WIDGET')
\ No newline at end of file
+export const RM_WIDGET = new InjectionToken('RM_WIDGET')
+
+export enum EnumWidgetState {
+  MINIMIZED,
+  NORMAL,
+  MAXIMIZED,
+}
diff --git a/src/widget/widget.module.ts b/src/widget/widget.module.ts
index 138ed6648..62b9d65ba 100644
--- a/src/widget/widget.module.ts
+++ b/src/widget/widget.module.ts
@@ -2,17 +2,20 @@ import { NgModule } from "@angular/core";
 import { CommonModule } from "@angular/common";
 import { ComponentsModule } from "src/components";
 import { WidgetCanvas } from "./widgetCanvas.directive";
-import { WidgetPortal } from "./widgetPortal/widgetPortal.component";
+import { WidgetPortal } from "./widgetPortal/widgetPortal.component"
 import { MatCardModule } from "@angular/material/card";
 import { DragDropModule } from "@angular/cdk/drag-drop";
 import { MatButtonModule } from "@angular/material/button";
 import { PortalModule } from "@angular/cdk/portal";
+import { MatTooltipModule } from "@angular/material/tooltip";
+import { WidgetStateIconPipe } from "./widgetStateIcon.pipe";
 
 @NgModule({
   imports:[
     MatCardModule,
     DragDropModule,
     MatButtonModule,
+    MatTooltipModule,
     PortalModule,
     CommonModule,
     ComponentsModule,
@@ -20,6 +23,7 @@ import { PortalModule } from "@angular/cdk/portal";
   declarations: [
     WidgetCanvas,
     WidgetPortal,
+    WidgetStateIconPipe,
   ],
   providers: [],
   exports: [
diff --git a/src/widget/widgetPortal/widgetPortal.component.ts b/src/widget/widgetPortal/widgetPortal.component.ts
index 5e5cc05fa..cf7f0ea9a 100644
--- a/src/widget/widgetPortal/widgetPortal.component.ts
+++ b/src/widget/widgetPortal/widgetPortal.component.ts
@@ -1,6 +1,26 @@
 import { ComponentPortal } from "@angular/cdk/portal";
-import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Optional } from "@angular/core";
-import { RM_WIDGET } from "../constants";
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding, Inject, Optional } from "@angular/core";
+import { RM_WIDGET, EnumWidgetState } from "../constants";
+
+type TWidgetCss = {
+  transform: string
+  width: string
+}
+
+const widgetStateToTransform: Record<EnumWidgetState, TWidgetCss> = {
+  [EnumWidgetState.MINIMIZED]: {
+    transform: `translate(100vw, 5vh)`,
+    width: '24rem'
+  },
+  [EnumWidgetState.NORMAL]: {
+    transform: `translate(calc(100vw - 24rem), 5vh)`,
+    width: '24rem'
+  },
+  [EnumWidgetState.MAXIMIZED]: {
+    transform: `translate(5vw, 5vh)`,
+    width: '90vw'
+  },
+}
 
 @Component({
   selector: 'sxplr-widget-portal',
@@ -13,6 +33,8 @@ import { RM_WIDGET } from "../constants";
 
 export class WidgetPortal<T>{
 
+  EnumWidgetState = EnumWidgetState
+
   portal: ComponentPortal<T>
   
   private _name: string
@@ -24,10 +46,30 @@ export class WidgetPortal<T>{
     this.cdr.markForCheck()
   }
 
-  defaultPosition = {
-    x: 200,
-    y: 200,
+  minimizeReturnState: EnumWidgetState.NORMAL | EnumWidgetState.MAXIMIZED
+
+  private _state: EnumWidgetState = EnumWidgetState.NORMAL
+  get state() {
+    return this._state
   }
+  set state(val: EnumWidgetState) {
+    if (val === EnumWidgetState.MINIMIZED) {
+      this.minimizeReturnState = this._state !== EnumWidgetState.MINIMIZED
+        ? this._state
+        : EnumWidgetState.NORMAL
+    }
+    this._state = val
+    this.transform = widgetStateToTransform[this._state]?.transform || widgetStateToTransform[EnumWidgetState.NORMAL].transform
+    this.width = widgetStateToTransform[this._state]?.width || widgetStateToTransform[EnumWidgetState.NORMAL].width
+
+    this.cdr.markForCheck()
+  }
+
+  @HostBinding('style.transform')
+  transform = widgetStateToTransform[ EnumWidgetState.NORMAL ].transform
+
+  @HostBinding('style.width')
+  width = widgetStateToTransform[ EnumWidgetState.NORMAL ].width
 
   constructor(
     private cdr: ChangeDetectorRef,
diff --git a/src/widget/widgetPortal/widgetPortal.style.css b/src/widget/widgetPortal/widgetPortal.style.css
index 12e9c8096..3b374eee2 100644
--- a/src/widget/widgetPortal/widgetPortal.style.css
+++ b/src/widget/widgetPortal/widgetPortal.style.css
@@ -2,15 +2,18 @@
 {
   pointer-events: none;
   display: block;
-  max-width: 24rem;
+  
+  width: 24rem;
+  height: 90vh;
+
+  transition: all 160ms cubic-bezier(0.35, 0, 0.25, 1);
 }
 
 mat-card
 {
   pointer-events: all;
-  max-width: 36vw;
-  height: 36rem;
-  max-height: 90vh;
+  width: 100%;
+  height: 100%;
 }
 
 mat-card-content
@@ -24,7 +27,7 @@ mat-card-content
 .widget-portal-header
 {
   display: flex;
-  justify-content: space-between;
+  justify-content: flex-end;
   align-items: center;
 }
 
@@ -33,24 +36,13 @@ mat-card-content
   flex-grow: 1;
 }
 
-.hover-grab
-{
-  opacity: 0.5;
-  transition: opacity 200ms ease-in-out;
-  cursor: move;
-}
-
-.hover-grab:hover
-{
-  opacity: 1.0;
-}
-
-.widget-grab-handle
-{
-  margin-right:1rem;
-}
-
 .widget-name
 {
   flex-grow: 1;
 }
+
+.when-minimized-nub
+{
+  position: absolute;
+  transform: translate(-5rem, 5rem);
+}
\ No newline at end of file
diff --git a/src/widget/widgetPortal/widgetPortal.template.html b/src/widget/widgetPortal/widgetPortal.template.html
index 7aff890d9..5e4e495fa 100644
--- a/src/widget/widgetPortal/widgetPortal.template.html
+++ b/src/widget/widgetPortal/widgetPortal.template.html
@@ -1,13 +1,41 @@
-<mat-card cdkDrag [cdkDragFreeDragPosition]="defaultPosition">
+<div *ngIf="state === EnumWidgetState.MINIMIZED"
+  class="when-minimized-nub">
+
+  <button mat-mini-fab
+    [matTooltip]="name"
+    color="primary"
+    class="sxplr-pe-all"
+    (click)="state = minimizeReturnState">
+    <i [class]="minimizeReturnState | widgetStateIcon"></i>
+  </button>
+</div>
+
+<mat-card>
   <mat-card-content>
-    <div class="widget-portal-header" cdkDragHandle>
-      <span class="hover-grab widget-grab-handle">
-        <i class="fas fa-grip-vertical"></i>
-      </span>
+    <div class="widget-portal-header">
 
       <span *ngIf="name" class="widget-name">
         {{ name }}
       </span>
+      
+      <!-- state changer -->
+      <ng-template [ngTemplateOutlet]="stateBtnTmpl"
+        [ngTemplateOutletContext]="{
+          $implicit: EnumWidgetState.MINIMIZED
+        }">
+      </ng-template>
+
+      <ng-template [ngTemplateOutlet]="stateBtnTmpl"
+        [ngTemplateOutletContext]="{
+          $implicit: EnumWidgetState.NORMAL
+        }">
+      </ng-template>
+
+      <ng-template [ngTemplateOutlet]="stateBtnTmpl"
+        [ngTemplateOutletContext]="{
+          $implicit: EnumWidgetState.MAXIMIZED
+        }">
+      </ng-template>
 
       <button mat-icon-button (click)="exit()">
         <i class="fas fa-times"></i>
@@ -20,3 +48,13 @@
     </div>
   </mat-card-content>
 </mat-card>
+
+<!-- template for plugin state -->
+<ng-template #stateBtnTmpl let-btnstate>
+  <button
+    *ngIf="state !== btnstate"
+    (click)="state = btnstate"
+    mat-icon-button>
+    <i [class]="btnstate | widgetStateIcon"></i>
+  </button>
+</ng-template>
diff --git a/src/widget/widgetStateIcon.pipe.ts b/src/widget/widgetStateIcon.pipe.ts
new file mode 100644
index 000000000..b7af88175
--- /dev/null
+++ b/src/widget/widgetStateIcon.pipe.ts
@@ -0,0 +1,26 @@
+import { Pipe, PipeTransform } from "@angular/core";
+import { EnumWidgetState } from "./constants"
+
+@Pipe({
+  name: 'widgetStateIcon',
+  pure: true
+})
+
+export class WidgetStateIconPipe implements PipeTransform{
+  public transform(state: EnumWidgetState): string {
+    switch (state) {
+    case EnumWidgetState.MINIMIZED: {
+      return 'fas fa-window-minimize'
+    }
+    case EnumWidgetState.NORMAL: {
+      return 'fas fa-window-restore'
+    }
+    case EnumWidgetState.MAXIMIZED: {
+      return 'fas fa-window-maximize'
+    }
+    default: {
+      return 'fas fa-window-restore'
+    }
+    }
+  }
+}
-- 
GitLab