diff --git a/common/util.js b/common/util.js index 4029c6744e2ca274e4a4be75e9dab633a17c904f..a9fd9d6c0c939b5bb05ef281543f74d56b105a29 100644 --- a/common/util.js +++ b/common/util.js @@ -204,4 +204,26 @@ const { labelIndex, ngId } = deserialiseParcRegionId(labelIndexId) } + const getPad = ({ length, pad }) => { + if (pad.length !== 1) throw new Error(`pad needs to be precisely 1 character`) + return input => { + const padNum = Math.max(input.toString().length - length, 0) + const padString = Array(padNum).fill(pad).join('') + return `${padString}${input}` + } + } + + exports.getDateString = () => { + const d = new Date() + const pad2 = getPad({ pad: '0', length: 2 }) + + const year = d.getFullYear() + const month = d.getMonth() + 1 + const date = d.getDate() + + const hr = d.getHours() + const min = d.getMinutes() + return `${year}${pad2(month)}${pad2(date)}_${pad2(hr)}${pad2(min)}` + } + })(typeof exports === 'undefined' ? module.exports : exports) diff --git a/docs/releases/v2.4.0.md b/docs/releases/v2.4.0.md index 9e047ec46c57a81a3e1bf0a306bd4cedf95a8914..bd1bb94a609d2a92ad4fc44b5d1abd8da1c0f7c7 100644 --- a/docs/releases/v2.4.0.md +++ b/docs/releases/v2.4.0.md @@ -7,3 +7,5 @@ ## Under the hood stuff - refactored code, added additional test coverage +- reworked screenshot component, removed html2canvas dependency +- deprecated `getToastHandler` and `geModalHandler` in plugin API \ No newline at end of file diff --git a/package.json b/package.json index e62868a629bca945df2f56c4db4dbb9b2bb47a67..461fde5d6a38a9176f6a7c387ae2e1f8d2b317bb 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,6 @@ "glob": "^7.1.6", "hammerjs": "^2.0.8", "html-webpack-plugin": "^3.2.0", - "html2canvas": "^1.0.0-rc.1", "jasmine": "^3.1.0", "jasmine-core": "^3.5.0", "jasmine-marbles": "^0.6.0", diff --git a/src/atlasViewer/atlasViewer.apiService.service.ts b/src/atlasViewer/atlasViewer.apiService.service.ts index 75c5e5a231f25784773cb95a2941ed4692b2c834..05eba0de7e6c56770513349a68046df851987d77 100644 --- a/src/atlasViewer/atlasViewer.apiService.service.ts +++ b/src/atlasViewer/atlasViewer.apiService.service.ts @@ -15,8 +15,6 @@ import { } from "src/services/stateStore.service"; import { FRAGMENT_EMIT_RED } from "src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component"; import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util"; -import { ModalHandler } from "../util/pluginHandlerClasses/modalHandler"; -import { ToastHandler } from "../util/pluginHandlerClasses/toastHandler"; import { IPluginManifest, PluginServices } from "./pluginUnit"; declare let window @@ -35,7 +33,6 @@ interface IGetUserSelectRegionPr{ } export const CANCELLABLE_DIALOG = 'CANCELLABLE_DIALOG' -export const GET_TOAST_HANDLER_TOKEN = 'GET_TOAST_HANDLER_TOKEN' export interface ILoadMesh { type: 'VTK' @@ -161,7 +158,6 @@ export class AtlasViewerAPIServices implements OnDestroy{ private zone: NgZone, private pluginService: PluginServices, @Optional() @Inject(CANCELLABLE_DIALOG) openCancellableDialog: (message: string, options: any) => () => void, - @Optional() @Inject(GET_TOAST_HANDLER_TOKEN) private getToastHandler: Function, @Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR) clickInterceptor: ClickInterceptor ) { if (clickInterceptor) { @@ -255,39 +251,12 @@ export class AtlasViewerAPIServices implements OnDestroy{ }, uiHandle : { getModalHandler : () => { - const handler = new ModalHandler() - let modalRef - handler.show = () => { - /** - * TODO enable - * temporarily disabled - */ - // modalRef = this.modalService.show(ModalUnit, { - // initialState : { - // title : handler.title, - // body : handler.body - // ? handler.body - // : 'handler.body has yet been defined ...', - // footer : handler.footer - // }, - // class : this.darktheme ? 'darktheme' : 'not-darktheme', - // backdrop : handler.dismissable ? true : 'static', - // keyboard : handler.dismissable - // }) - } - handler.hide = () => { - if (modalRef) { - modalRef.hide() - modalRef = null - } - } - return handler + throw new Error(`uihandle.getModalHandler has been deprecated`) }, /* to be overwritten by atlasViewer.component.ts */ getToastHandler : () => { - if (this.getToastHandler) return this.getToastHandler() - else throw new Error('getToast Handler not overwritten by atlasViewer.component.ts') + throw new Error('uiHandle.getToastHandler has been deprecated') }, /** @@ -451,8 +420,8 @@ export interface IInteractiveViewerInterface { } uiHandle: { - getModalHandler: () => ModalHandler - getToastHandler: () => ToastHandler + getModalHandler: () => void + getToastHandler: () => void launchNewWidget: (manifest: IPluginManifest) => Promise<any> getUserInput: (config: IGetUserInputConfig) => Promise<string> getUserConfirmation: (config: IGetUserConfirmation) => Promise<any> diff --git a/src/atlasViewer/atlasViewer.workerService.service.ts b/src/atlasViewer/atlasViewer.workerService.service.ts index 17204cb05224dc78887f74cdae74ab9699ba6ec8..98dea256450eaddbcd4a0849176185317a5fe9bc 100644 --- a/src/atlasViewer/atlasViewer.workerService.service.ts +++ b/src/atlasViewer/atlasViewer.workerService.service.ts @@ -1,6 +1,7 @@ import { Injectable } from "@angular/core"; import { fromEvent } from "rxjs"; import { filter, take } from "rxjs/operators"; +import { getUuid } from "src/util/fn"; /* telling webpack to pack the worker file */ import '../util/worker.js' @@ -24,7 +25,7 @@ export class AtlasWorkerService { async sendMessage(data: IWorkerMessage){ - const newUuid = crypto.getRandomValues(new Uint32Array(1))[0].toString(16) + const newUuid = getUuid() this.worker.postMessage({ id: newUuid, ...data diff --git a/src/atlasViewer/modalUnit/modalUnit.component.ts b/src/atlasViewer/modalUnit/modalUnit.component.ts deleted file mode 100644 index eb62a94bb383b8a17e0a3d9d145872e23b0cede0..0000000000000000000000000000000000000000 --- a/src/atlasViewer/modalUnit/modalUnit.component.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Component, Input, TemplateRef, ViewChild, ViewContainerRef } from '@angular/core' - -@Component({ - templateUrl : './modalUnit.template.html', - styleUrls : [ - './modalUnit.style.css', - ], -}) - -export class ModalUnit { - @Input() public title: string - @Input() public body: string = 'Modal Body Text' - @Input() public template: TemplateRef<any> - @Input() public footer: string - - @ViewChild('templateContainer', {read: ViewContainerRef}) public templateContainer: ViewContainerRef - - constructor(public viewContainerRef: ViewContainerRef) { - - } - - public ngAfterViewInit() { - if (this.templateContainer) { - this.templateContainer.createEmbeddedView(this.template) - } - } -} diff --git a/src/atlasViewer/modalUnit/modalUnit.style.css b/src/atlasViewer/modalUnit/modalUnit.style.css deleted file mode 100644 index 619233185073476c359eb2b6188f1a5280a166c2..0000000000000000000000000000000000000000 --- a/src/atlasViewer/modalUnit/modalUnit.style.css +++ /dev/null @@ -1,8 +0,0 @@ -:host-context(.darktheme) div.modal-header, -:host-context(.darktheme) div.modal-body, -:host-context(.darktheme) div.modal-footer -{ - background-color:rgba(50,50,50,1.0); - border-color: rgba(0,0,0,0.2); - color:white; -} \ No newline at end of file diff --git a/src/atlasViewer/modalUnit/modalUnit.template.html b/src/atlasViewer/modalUnit/modalUnit.template.html deleted file mode 100644 index a3aba585c53b1d35e044ec5aa92ece92c94a05a2..0000000000000000000000000000000000000000 --- a/src/atlasViewer/modalUnit/modalUnit.template.html +++ /dev/null @@ -1,19 +0,0 @@ -<div *ngIf = "title" class = "modal-header"> - <h4 class = "modal-title pull-left"> - {{ title }} - </h4> -</div> - -<div *ngIf = "!template" class = "modal-body"> - {{ body }} -</div> - -<div *ngIf = "template" class="modal-body"> - <ng-template #templateContainer> - - </ng-template> -</div> - -<div *ngIf = "footer" class = "modal-footer"> - {{ footer }} -</div> \ No newline at end of file diff --git a/src/main.module.ts b/src/main.module.ts index a87d7e6d8fa662263b04876717cbcd98a0c480b0..11a705ee245213a857e933cd3c82dce5010beb9b 100644 --- a/src/main.module.ts +++ b/src/main.module.ts @@ -1,6 +1,6 @@ import { DragDropModule } from '@angular/cdk/drag-drop' import { CommonModule } from "@angular/common"; -import { CUSTOM_ELEMENTS_SCHEMA, InjectionToken, NgModule } from "@angular/core"; +import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; import { StoreModule, ActionReducer } from "@ngrx/store"; import { AngularMaterialModule } from 'src/ui/sharedModules/angularMaterial.module' @@ -14,9 +14,8 @@ import { GetNamesPipe } from "./util/pipes/getNames.pipe"; import { HttpClientModule } from "@angular/common/http"; import { EffectsModule } from "@ngrx/effects"; -import { AtlasViewerAPIServices, CANCELLABLE_DIALOG, GET_TOAST_HANDLER_TOKEN, API_SERVICE_SET_VIEWER_HANDLE_TOKEN, setViewerHandleFactory, LOAD_MESH_TOKEN, ILoadMesh } from "./atlasViewer/atlasViewer.apiService.service"; +import { AtlasViewerAPIServices, CANCELLABLE_DIALOG, API_SERVICE_SET_VIEWER_HANDLE_TOKEN, setViewerHandleFactory, LOAD_MESH_TOKEN, ILoadMesh } from "./atlasViewer/atlasViewer.apiService.service"; import { AtlasWorkerService } from "./atlasViewer/atlasViewer.workerService.service"; -import { ModalUnit } from "./atlasViewer/modalUnit/modalUnit.component"; import { TransformOnhoverSegmentPipe } from "./atlasViewer/onhoverSegment.pipe"; import { ConfirmDialogComponent } from "./components/confirmDialog/confirmDialog.component"; import { DialogComponent } from "./components/dialog/dialog.component"; @@ -121,7 +120,6 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> { ], declarations : [ AtlasViewer, - ModalUnit, TryMeComponent, /* directives */ @@ -140,7 +138,6 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> { MouseOverIconPipe, ], entryComponents : [ - ModalUnit, DialogComponent, ConfirmDialogComponent, ], @@ -153,13 +150,6 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> { UIService, TemplateCoordinatesTransformation, ClickInterceptorService, - { - provide: GET_TOAST_HANDLER_TOKEN, - useFactory: (uiService: UIService) => { - return () => uiService.getToastHandler() - }, - deps: [ UIService ] - }, { provide: OVERRIDE_IAV_DATASET_PREVIEW_DATASET_FN, useFactory: (glue: IDatasetPreviewGlue) => glue.displayDatasetPreview.bind(glue), diff --git a/src/plugin_examples/migrationGuide.md b/src/plugin_examples/migrationGuide.md index 1489d5b848ab70382b63575190309020b0f8c148..9cd95c535efd5699d03ed3d04e2d57afc5ad554a 100644 --- a/src/plugin_examples/migrationGuide.md +++ b/src/plugin_examples/migrationGuide.md @@ -49,16 +49,4 @@ Plugin APIs have changed drastically from v0.1.0 to v0.2.0. Here is a list of pl - ~~*onParcellationSelection(callback)* : Function that attach a callback function to user selecting a different parcellation~~ removed. use **window.interactiveViewer.metadata.selectedParcellationBSubject** instead. - ~~*afterParcellationSelection(callback)* : Function that attach a callback function to be called after the parcellation selection process is complete and *selectedParcellation* is updated.~~ removed - *modalControl* - - *getModalHandler()* : Function returning a handler to change/show/hide/listen to a Modal. - - *modalHander* methods: - - *hide()* : Dynamically hides the modal - - *show()* : Shows the modal - - *onHide(callback(reason)=>void)* : Attaches an onHide callback. - - *onHidden(callback(reason)=>void)* : Attaches an onHidden callback. - - *onShow(callback(reason)=>void)* : Attaches an onShow callback. - - *onShown(callback(reason)=>void)* : Attaches an onShown callback. - - *modalHandler* properties: - - title : title of the modal (String) - - body : body of the modal shown (JSON, Array, String) - - footer : footer of the modal (String) - - config : config of the modal \ No newline at end of file + - ~~*getModalHandler()* : Function returning a handler to change/show/hide/listen to a Modal.~~ removed \ No newline at end of file diff --git a/src/plugin_examples/plugin_api.md b/src/plugin_examples/plugin_api.md index a8b29b30f48e239a0e2a1294c845eaa24abc0e8d..d9663435dfd2d544adf1ca7c6dbc5a0d9c3ef995 100644 --- a/src/plugin_examples/plugin_api.md +++ b/src/plugin_examples/plugin_api.md @@ -222,34 +222,6 @@ whether the modal is dismissable on click backdrop/esc key (Boolean) *n.b. if true, users will not be able to interact with the viewer unless you specifically call `handler.hide()`* -#### getToastHandler() - -returns a toastHandler objectm, which has the following methods/properties: - -##### show() - -Show the toast - -##### hide() - -Dynamically hides the toast - -##### message - -message on the toast - -##### htmlMessage - -HTML message. If used to display user content, beware of script injection. Angular strips `style` attribute, so use `class` and bootstrap for styling. - -##### dismissable - -allow user dismiss the toast via x - -##### timeout - -auto hide (in ms). set to 0 for not auto hide. - #### launchNewWidget(manifest) returns a Promise. expects a JSON object, with the same key value as a plugin manifest. the *name* key must be unique, or the promise will be rejected. diff --git a/src/services/uiService.service.ts b/src/services/uiService.service.ts index dc6f4dda7ea8c427536f2b567e94ef2b24e726ab..cc8774728fb1742e36722135960341c35f7c0599 100644 --- a/src/services/uiService.service.ts +++ b/src/services/uiService.service.ts @@ -1,5 +1,4 @@ import { Injectable } from "@angular/core"; -import { ToastHandler } from "src/util/pluginHandlerClasses/toastHandler"; import {MatSnackBar, MatSnackBarConfig} from "@angular/material/snack-bar"; import { MatDialog } from "@angular/material/dialog"; import { ActionDialog } from "src/ui/actionDialog/actionDialog.component"; @@ -15,22 +14,6 @@ export class UIService { ) { } - public getToastHandler = () => { - const toasthandler = new ToastHandler() - let handle - toasthandler.show = () => { - handle = this.showMessage(toasthandler.message, null, { - duration: toasthandler.timeout, - }) - } - - toasthandler.hide = () => { - if (handle) { handle.dismiss() } - handle = null - } - return toasthandler - } - public showMessage(message: string, actionBtnTxt: string = 'Dismiss', config?: Partial<MatSnackBarConfig>) { return this.snackbar.open(message, actionBtnTxt, config) } diff --git a/src/theme.scss b/src/theme.scss index b2b1c43ca50117e48bfc37e5b27ddbf507686d6b..85ecba40b9d80b41f5692c7b9a4ea88bd195d075 100644 --- a/src/theme.scss +++ b/src/theme.scss @@ -1,4 +1,5 @@ @import '~@angular/material/theming'; +@import '~@angular/cdk/overlay-prebuilt.css'; @include mat-core(); diff --git a/src/ui/screenshot/index.ts b/src/ui/screenshot/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..9bb724742f69501cfd9d0b330b56b32294f38e4e --- /dev/null +++ b/src/ui/screenshot/index.ts @@ -0,0 +1,5 @@ +export { ScreenshotModule } from './module' +export { + HANDLE_SCREENSHOT_PROMISE, + TypeHandleScrnShotPromise, +} from './util' diff --git a/src/ui/screenshot/module.ts b/src/ui/screenshot/module.ts new file mode 100644 index 0000000000000000000000000000000000000000..68ca809f2ca6d93d53263f25d5ffa20430318c8b --- /dev/null +++ b/src/ui/screenshot/module.ts @@ -0,0 +1,28 @@ +import { FullscreenOverlayContainer, OverlayContainer } from "@angular/cdk/overlay"; +import { CommonModule } from "@angular/common"; +import { NgModule } from "@angular/core"; +import { UtilModule } from "src/util"; +import { AngularMaterialModule } from "../sharedModules/angularMaterial.module"; +import { ScreenshotCmp } from "./screenshotCmp/screenshot.component"; +import { ScreenshotSwitch } from "./screenshotSwitch.directive"; + +@NgModule({ + imports: [ + CommonModule, + AngularMaterialModule, + UtilModule, + ], + declarations:[ + ScreenshotSwitch, + ScreenshotCmp, + ], + exports: [ + ScreenshotSwitch, + ], + providers:[{ + provide: OverlayContainer, + useClass: FullscreenOverlayContainer + }] +}) + +export class ScreenshotModule{} diff --git a/src/ui/screenshot/screenshotCmp/screenshot.component.ts b/src/ui/screenshot/screenshotCmp/screenshot.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..9af9d6b90a7d60af3438b2784906918b6f74aab0 --- /dev/null +++ b/src/ui/screenshot/screenshotCmp/screenshot.component.ts @@ -0,0 +1,200 @@ +import { Component, HostListener, Inject, OnDestroy, Output, EventEmitter, Optional, ViewChild, TemplateRef } from "@angular/core"; +import { MatDialog } from "@angular/material/dialog"; +import { MatSnackBar } from "@angular/material/snack-bar"; +import { combineLatest, Observable, Subject, Subscription } from "rxjs"; +import { distinctUntilChanged, map, mapTo, shareReplay, startWith, switchMap, switchMapTo, tap } from "rxjs/operators"; +import { HANDLE_SCREENSHOT_PROMISE, TypeHandleScrnShotPromise } from "../util"; +import { getDateString } from 'common/util' +import { getUuid } from "src/util/fn"; + +const BORDER_WIDTH = 10000 + +@Component({ + templateUrl: './screenshot.template.html', + styleUrls: [ + './screenshot.style.css' + ] +}) + +export class ScreenshotCmp implements OnDestroy{ + + public borderStyle = `${BORDER_WIDTH}px rgba(0, 0, 0, 0.5) solid` + private mousedown$ = new Subject<MouseEvent>() + private mouseup$ = new Subject<MouseEvent>() + private mousemove$ = new Subject<MouseEvent>() + private subscriptions: Subscription[] = [] + + public isDragging$: Observable<boolean> + public transformString$: Observable<string> + public widthString$: Observable<string> + public heightString$: Observable<string> + + private flipRect$: Observable<{ x: boolean, y: boolean }> + + constructor( + private dialog: MatDialog, + private snackbar: MatSnackBar, + @Optional() @Inject(HANDLE_SCREENSHOT_PROMISE) private handleScreenshotPromise: TypeHandleScrnShotPromise + ){ + this.isDragging$ = this.mousedown$.pipe( + switchMapTo(this.mouseup$.pipe( + mapTo(false), + startWith(true) + )), + distinctUntilChanged(), + ) + + this.flipRect$ = this.mousedown$.pipe( + switchMap(mousedownEv => this.mousemove$.pipe( + map(mousemoveEv => { + return { + x: mousemoveEv.clientX < mousedownEv.clientX, + y: mousemoveEv.clientY < mousedownEv.clientY + } + }) + )), + distinctUntilChanged(({ x: oldX, y: oldY }, {x, y}) => oldX === x && oldY === y), + ) + + this.transformString$ = combineLatest([ + this.mousedown$, + this.flipRect$, + ]).pipe( + map(([ev, { x, y }]) => { + /** + * scale will retroactively change the effect of previous translations + */ + const xFactor = x ? -1 : 1 + const yFactor = y ? -1 : 1 + return `translate(${-BORDER_WIDTH * xFactor}px, ${-BORDER_WIDTH * yFactor}px) scale(${xFactor}, ${yFactor}) translate(${(ev.clientX) * (xFactor)}px, ${(ev.clientY) * (yFactor)}px)` + }), + shareReplay(1), + ) + + this.subscriptions.push( + this.transformString$.subscribe() + ) + + const width$ = this.mousedown$.pipe( + switchMap(ev => this.mousemove$.pipe( + map(moveEv => Math.abs(moveEv.clientX - ev.clientX)), + startWith(0), + )), + shareReplay(1), + ) + + this.widthString$ = width$.pipe( + map(width => `${width}px`), + shareReplay(1), + ) + + this.subscriptions.push( + this.widthString$.subscribe() + ) + + const height$ = this.mousedown$.pipe( + switchMap(ev => this.mousemove$.pipe( + map(moveEv => Math.abs(moveEv.clientY - ev.clientY)), + startWith(0), + )), + shareReplay(1), + ) + + this.heightString$ = height$.pipe( + map(height => `${height}px`), + shareReplay(1), + ) + + this.subscriptions.push( + this.heightString$.subscribe() + ) + + this.subscriptions.push( + combineLatest([ + this.mousedown$, + this.flipRect$, + width$, + height$, + ]).pipe( + switchMap(arg => this.mouseup$.pipe( + mapTo(arg) + )) + ).subscribe(([ startEv, flipRect, width, height ]) => { + const startX = startEv.clientX + const startY = startEv.clientY + if (!handleScreenshotPromise) { + console.warn(`HANDLE_SCREENSHOT_PROMISE not provided`) + return + } + + if (width < 5 || height < 5) { + snackbar.open('width and height needs to be a bit larger', null, { + duration: 1000 + }) + return + } + + this.captureScreenshot({ + x: flipRect.x ? startX - width : startX, + y: flipRect.y ? startY - height : startY, + width, + height + }) + }) + ) + } + + captureScreenshot(param?){ + + this.handleScreenshotPromise(param) + .then(({ revoke, url }) => { + this.dialog.open( + this.previewTmpl, + { + data: { + url, + download: `${getDateString()}_${getUuid()}.png` + } + } + ).afterClosed().subscribe( + reason => { + /** + * if user clicks outside, or clicks cancel, emit destroy signal + */ + if (!reason || reason === 'cancel') { + this.destroy.emit() + } + revoke() + } + ) + }) + .catch(e => { + this.snackbar.open(e, 'Dismiss') + }) + } + + ngOnDestroy(){ + while(this.subscriptions.length > 0) this.subscriptions.pop().unsubscribe() + } + + @HostListener('mousedown', ['$event']) + mousedown(ev: MouseEvent){ + this.mousedown$.next(ev) + } + + @HostListener('mouseup', ['$event']) + mouseup(ev: MouseEvent){ + this.mouseup$.next(ev) + } + + @HostListener('mousemove', ['$event']) + mousemove(ev: MouseEvent){ + this.mousemove$.next(ev) + } + + @Output() + destroy = new EventEmitter() + + @ViewChild('previewTmpl', { read: TemplateRef }) + private previewTmpl: TemplateRef<any> +} \ No newline at end of file diff --git a/src/ui/screenshot/screenshotCmp/screenshot.style.css b/src/ui/screenshot/screenshotCmp/screenshot.style.css new file mode 100644 index 0000000000000000000000000000000000000000..052e938e4b62e7c5eb2ab8a1fac99dca19be16b8 --- /dev/null +++ b/src/ui/screenshot/screenshotCmp/screenshot.style.css @@ -0,0 +1,30 @@ +:host +{ + display: block; + width:100%; +} + +.cover +{ + background-color: rgba(0,0,0, 0.5); +} + +.box +{ + /* background: none; */ + box-sizing: content-box; + width: 0; + height: 0; + transform-origin: top left; +} + +.inner-box +{ + border: 1px white solid; +} + +img +{ + max-width: 50vw; + min-width: 20vw; +} \ No newline at end of file diff --git a/src/ui/screenshot/screenshotCmp/screenshot.template.html b/src/ui/screenshot/screenshotCmp/screenshot.template.html new file mode 100644 index 0000000000000000000000000000000000000000..ad7143b67c6c49b75f8341450b70633e997149eb --- /dev/null +++ b/src/ui/screenshot/screenshotCmp/screenshot.template.html @@ -0,0 +1,74 @@ + +<ng-template #placeholderTmpl> + + <div class="d-flex align-items-center justify-content-center w-100 h-100 cover"> + <span class="iv-custom-comp text"> + <h2 class="mat-h2 text-center"> + <span> + Drag a box to take a screenshot or + </span> + + <button mat-stroked-button iav-stop="mousedown mouseup" + (click)="captureScreenshot()"> + <i class="fas fa-camera"></i> + <span class="ml-1"> + capture whole screen + </span> + </button> + </h2> + + <h3 class="mat-h3 text-center"> + <span> + cancel with Esc or + </span> + <button iav-stop="mousedown mouseup" + (click)="destroy.emit()" + mat-button> + click here + </button> + </h3> + </span> + </div> + +</ng-template> + +<ng-template [ngIf]="isDragging$ | async" [ngIfElse]="placeholderTmpl"> + <div [style.border]="borderStyle" + [style.transform]="(transformString$ | async)" + [style.width]="widthString$ | async" + [style.height]="heightString$ | async" + class="box"> + <div class="inner-box h-100 w-100"> + </div> + </div> +</ng-template> + + +<ng-template #previewTmpl let-data> + + <mat-dialog-content class="d-flex justify-content-center"> + <img [src]="data.url | safeResource"> + </mat-dialog-content> + + <mat-dialog-actions align="end"> + <a [href]="data.url | safeResource" + [download]="data.download" + mat-raised-button color="primary"> + <i class="fas fa-save"></i> + <span class="ml-1">Save</span> + </a> + + <button mat-stroked-button + color="default" + mat-dialog-close="try again"> + <i class="fas fa-camera"></i> + <span class="ml-1">Try again</span> + </button> + + <button mat-button + color="default" + mat-dialog-close="cancel"> + Cancel + </button> + </mat-dialog-actions> +</ng-template> \ No newline at end of file diff --git a/src/ui/screenshot/screenshotSwitch.directive.ts b/src/ui/screenshot/screenshotSwitch.directive.ts new file mode 100644 index 0000000000000000000000000000000000000000..e80be541b86313b80cd4c13e15eb7e5d31164dc8 --- /dev/null +++ b/src/ui/screenshot/screenshotSwitch.directive.ts @@ -0,0 +1,67 @@ +import { Overlay, OverlayRef } from "@angular/cdk/overlay"; +import { ComponentPortal } from "@angular/cdk/portal"; +import { ComponentRef, Directive, HostListener } from "@angular/core"; +import { take } from "rxjs/operators"; +import { ScreenshotCmp } from "./screenshotCmp/screenshot.component"; + +@Directive({ + selector: '[screenshot-switch]' +}) + +export class ScreenshotSwitch{ + public takingScreenshot = false + + private overlayRef: OverlayRef + private cmpRef: ComponentRef<ScreenshotCmp> + + @HostListener('window:keydown', ['$event']) + keyListener(ev: KeyboardEvent){ + if (ev.key === 'Escape') { + if (this.overlayRef) this.dispose() + } + } + + @HostListener('click') + onClick(){ + /** + * button should act as a toggle? + */ + + this.overlayRef = this.overlay.create({ + hasBackdrop: false, + positionStrategy: this.overlay.position().global().centerVertically(), + panelClass: ['w-100', 'h-100'], + }) + + this.cmpRef = this.overlayRef.attach( + new ComponentPortal(ScreenshotCmp) + ) + + this.cmpRef.instance.destroy.pipe( + take(1) + ).subscribe( + () => { + this.dispose() + } + ) + + + } + + dispose(){ + this.cmpRef = null + if (this.overlayRef) this.overlayRef.dispose() + this.overlayRef = null + } + + reset(){ + this.takingScreenshot = true + } + + clear(){ + this.takingScreenshot = false + } + + constructor(private overlay: Overlay){} + +} \ No newline at end of file diff --git a/src/ui/screenshot/util.ts b/src/ui/screenshot/util.ts new file mode 100644 index 0000000000000000000000000000000000000000..5a84dc7f1b76e9a420c3d311d1b7dfdd34e42439 --- /dev/null +++ b/src/ui/screenshot/util.ts @@ -0,0 +1,14 @@ +import { InjectionToken } from "@angular/core" + +interface IScrnShot{ + x: number + y: number + width: number + height: number +} + +/** + * if param is not provided, screenshot entire screen + */ +export type TypeHandleScrnShotPromise = (param?: IScrnShot) => Promise<{ revoke: Function, url: string }> +export const HANDLE_SCREENSHOT_PROMISE = new InjectionToken('HANDLE_SCREENSHOT_PROMISE') \ No newline at end of file diff --git a/src/ui/signinBanner/signinBanner.components.ts b/src/ui/signinBanner/signinBanner.components.ts index 9c823d35fbd4ee77f8ff656b43f2b87e16ff2816..ef5c4e442357fcd684657bb9fb503888d11d5e30 100644 --- a/src/ui/signinBanner/signinBanner.components.ts +++ b/src/ui/signinBanner/signinBanner.components.ts @@ -2,10 +2,8 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, - ElementRef, Input, TemplateRef, - ViewChild } from "@angular/core"; import { select, Store } from "@ngrx/store"; import { Observable } from "rxjs"; @@ -47,15 +45,12 @@ export class SigninBanner { @Input() public darktheme: boolean @Input() public parcellationIsSelected: boolean - @ViewChild('takeScreenshotElement', {read: ElementRef}) takeScreenshotElement: ElementRef - public user$: Observable<any> public userBtnTooltip$: Observable<string> public favDataEntries$: Observable<Partial<IDataEntry>[]> public pluginTooltipText: string = `Plugins and Tools` public screenshotTooltipText: string = 'Take screenshot' - public takingScreenshot: boolean = false constructor( private store$: Store<IavRootStoreInterface>, @@ -92,17 +87,6 @@ export class SigninBanner { } } - resetScreenshotTaking() { - this.takingScreenshot = true - //ToDo find out better way to detect changes - setTimeout(() => this.changeDetectionRef.detectChanges()) - } - - disableScreenshotTaking() { - this.takingScreenshot = false - this.changeDetectionRef.detectChanges() - } - private keyListenerConfigBase = { type: 'keydown', stop: true, diff --git a/src/ui/signinBanner/signinBanner.style.css b/src/ui/signinBanner/signinBanner.style.css index 787a6e42e83b544a36a33d1f634480724c5baeda..d1021184f2955c2dd4721ee4eb86731ff32f57ee 100644 --- a/src/ui/signinBanner/signinBanner.style.css +++ b/src/ui/signinBanner/signinBanner.style.css @@ -10,11 +10,6 @@ pointer-events: all; } -take-screenshot -{ - z-index: 1509; -} - .btnWrapper { margin: 0.5rem 0; diff --git a/src/ui/signinBanner/signinBanner.template.html b/src/ui/signinBanner/signinBanner.template.html index ae9fe7663c001f81f708fc4f296fdc5a691a42d4..c4b925620a6ec51a92bb4293bda290706bd8f6c5 100644 --- a/src/ui/signinBanner/signinBanner.template.html +++ b/src/ui/signinBanner/signinBanner.template.html @@ -139,11 +139,13 @@ [aria-label]="'Tools and plugins menu'"> <button mat-menu-item [disabled]="!parcellationIsSelected" - (click)="takingScreenshot = true;" + screenshot-switch [matTooltip]="screenshotTooltipText"> <mat-icon fontSet="fas" fontIcon="fa-camera"> </mat-icon> - Screenshot + <span> + Screenshot + </span> </button> <plugin-banner></plugin-banner> </mat-menu> @@ -254,11 +256,3 @@ </mat-list-item> </mat-list> </ng-template> - -<take-screenshot - #takeScreenshotElement - *ngIf="takingScreenshot" - class="position-fixed fixed-top" - (screenshotTaking)="disableScreenshotTaking()" - (resetScreenshot)="$event? resetScreenshotTaking() : disableScreenshotTaking()"> -</take-screenshot> diff --git a/src/ui/takeScreenshot/takeScreenshot.component.ts b/src/ui/takeScreenshot/takeScreenshot.component.ts deleted file mode 100644 index b10f94a06cee887ece6d331b661ec137af58a15d..0000000000000000000000000000000000000000 --- a/src/ui/takeScreenshot/takeScreenshot.component.ts +++ /dev/null @@ -1,285 +0,0 @@ -import {DOCUMENT} from "@angular/common"; -import { - ChangeDetectorRef, - Component, - ElementRef, EventEmitter, - HostListener, - Inject, OnDestroy, - OnInit, Output, - Renderer2, - TemplateRef, - ViewChild, -} from "@angular/core"; -import {MatDialog, MatDialogRef} from "@angular/material/dialog"; -import html2canvas from "html2canvas"; - -@Component({ - selector: 'take-screenshot', - templateUrl: './takeScreenshot.template.html', - styleUrls: ['./takeScreenshot.style.css'], -}) - -export class TakeScreenshotComponent implements OnInit, OnDestroy { - - ngOnDestroy(): void { - if (this.resettingScreenshotTaking) this.resetScreenshot.emit(true) - this.resettingScreenshotTaking = false - } - - @ViewChild('screenshotPreviewCard', {read: ElementRef}) public screenshotPreviewCard: ElementRef - @ViewChild('previewImageDialog', {read: TemplateRef}) public previewImageDialogTemplateRef: TemplateRef<any> - - @Output() screenshotTaking: EventEmitter<boolean> = new EventEmitter<boolean>() - @Output() resetScreenshot: EventEmitter<boolean> = new EventEmitter<boolean>() - - private resettingScreenshotTaking: boolean = false - - private dialogRef: MatDialogRef<any> - - public takingScreenshot: boolean = false - public previewingScreenshot: boolean = false - public loadingScreenshot: boolean = false - - public screenshotName: string = `screenshot.png` - private croppedCanvas = null - - public mouseIsDown = false - public isDragging = false - - // Used to calculate where to start showing the dragging area - private startX: number = 0 - private startY: number = 0 - private endX: number = 0 - private endY: number = 0 - - public borderWidth: string = '' - // The box that contains the border and all required numbers. - public boxTop: number = 0 - public boxLeft: number = 0 - public boxEndWidth: number = 0 - public boxEndHeight: number = 0 - - private windowHeight: number = 0 - private windowWidth: number = 0 - - private screenshotStartX: number = 0 - private screenshotStartY: number = 0 - - public imageUrl: string - - constructor( - private renderer: Renderer2, - @Inject(DOCUMENT) private document: any, - private matDialog: MatDialog, - private cdr: ChangeDetectorRef, - ) {} - - public ngOnInit(): void { - this.windowWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth - this.windowHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight - - this.startScreenshot() - - } - - @HostListener('window:resize', ['$event']) - public onResize() { - this.windowWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth - this.windowHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight - } - - @HostListener('window:keyup', ['$event']) - public keyEvent(event: KeyboardEvent) { - if (this.takingScreenshot && event.key === 'Escape') { - this.cancelTakingScreenshot() - } - } - - public startScreenshot() { - this.previewingScreenshot = false - this.croppedCanvas = null - this.loadingScreenshot = false - this.takingScreenshot = true - } - - public move(e: MouseEvent) { - if (this.mouseIsDown) { - this.isDragging = true - - this.endY = e.clientY - this.endX = e.clientX - - if (this.endX >= this.startX && this.endY >= this.startY) { - // III quadrant - this.borderWidth = this.startY + 'px ' - + (this.windowWidth - this.endX) + 'px ' - + (this.windowHeight - this.endY) + 'px ' - + this.startX + 'px' - this.boxTop = this.startY - this.boxLeft = this.startX - this.boxEndWidth = this.endX - this.startX - this.boxEndHeight = this.endY - this.startY - - this.screenshotStartX = this.startX - this.screenshotStartY = this.startY - - } else if (this.endX <= this.startX && this.endY >= this.startY) { - // IV quadrant - - this.borderWidth = this.startY + 'px ' - + (this.windowWidth - this.startX) + 'px ' - + (this.windowHeight - this.endY) + 'px ' - + this.endX + 'px' - - this.boxLeft = this.endX - this.boxTop = this.startY - this.boxEndWidth = this.startX - this.endX - this.boxEndHeight = this.endY - this.startY - - this.screenshotStartX = this.endX - this.screenshotStartY = this.startY - - } else if (this.endX >= this.startX && this.endY <= this.startY) { - - // II quadrant - - this.borderWidth = this.endY + 'px ' - + (this.windowWidth - this.endX) + 'px ' - + (this.windowHeight - this.startY) + 'px ' - + this.startX + 'px' - - this.boxLeft = this.startX - this.boxTop = this.endY - this.boxEndWidth = this.endX - this.startX - this.boxEndHeight = this.startY - this.endY - - this.screenshotStartX = this.startX - this.screenshotStartY = this.endY - - } else if (this.endX <= this.startX && this.endY <= this.startY) { - // I quadrant - - this.boxLeft = this.endX - this.boxTop = this.endY - this.boxEndWidth = this.startX - this.endX - this.boxEndHeight = this.startY - this.endY - - this.borderWidth = this.endY + 'px ' - + (this.windowWidth - this.startX) + 'px ' - + (this.windowHeight - this.startY) + 'px ' - + this.endX + 'px' - - this.screenshotStartX = this.endX - this.screenshotStartY = this.endY - - } else { - this.isDragging = false - } - - } - } - - public mouseDown(event: MouseEvent) { - this.borderWidth = this.windowWidth + 'px ' + this.windowHeight + 'px' - - this.startX = event.clientX - this.startY = event.clientY - - this.mouseIsDown = true - } - - public mouseUp(_event: MouseEvent) { - (window as any).viewer.display.update() - this.borderWidth = '0' - - this.isDragging = false - this.mouseIsDown = false - - this.takingScreenshot = false - - if (this.boxEndWidth * window.devicePixelRatio <= 1 && this.boxEndHeight * window.devicePixelRatio <= 1) { - this.cancelTakingScreenshot() - } else { - this.loadScreenshot() - } - - } - - public loadScreenshot() { - - this.loadingScreenshot = true - this.dialogRef = this.matDialog.open(this.previewImageDialogTemplateRef, { - autoFocus: false, - }) - this.dialogRef.afterClosed().toPromise() - .then(result => { - switch (result) { - case 'again': { - this.restartScreenshot() - this.startScreenshot() - this.cdr.markForCheck() - break - } - case 'cancel': { - this.cancelTakingScreenshot() - break - } - default: this.cancelTakingScreenshot() - } - }) - - html2canvas(this.document.querySelector('#neuroglancer-container canvas')).then(canvas => { - this.croppedCanvas = null - this.croppedCanvas = this.renderer.createElement('canvas') - - this.croppedCanvas.width = this.boxEndWidth * window.devicePixelRatio - this.croppedCanvas.height = this.boxEndHeight * window.devicePixelRatio - - this.croppedCanvas.getContext('2d') - .drawImage(canvas, - this.screenshotStartX * window.devicePixelRatio, this.screenshotStartY * window.devicePixelRatio, - this.boxEndWidth * window.devicePixelRatio, this.boxEndHeight * window.devicePixelRatio, - 0, 0, - this.boxEndWidth * window.devicePixelRatio, this.boxEndHeight * window.devicePixelRatio) - }).then(() => { - - const d = new Date() - const n = `${d.getFullYear()}_${d.getMonth() + 1}_${d.getDate()}_${d.getHours()}_${d.getMinutes()}_${d.getSeconds()}` - this.screenshotName = `${n}_IAV.png` - - this.loadingScreenshot = false - this.imageUrl = this.croppedCanvas.toDataURL('image/png') - this.previewingScreenshot = true - this.clearStateAfterScreenshot() - - this.cdr.markForCheck() - }) - } - - public restartScreenshot() { - this.resettingScreenshotTaking = true - this.resetScreenshot.emit(false) - } - - public cancelTakingScreenshot() { - this.screenshotTaking.emit(false) - } - - public clearStateAfterScreenshot() { - this.mouseIsDown = false - this.isDragging = false - this.startX = 0 - this.startY = 0 - this.endX = 0 - this.endY = 0 - this.borderWidth = '' - this.boxTop = 0 - this.boxLeft = 0 - this.boxEndWidth = 0 - this.boxEndHeight = 0 - this.windowHeight = 0 - this.windowWidth = 0 - this.screenshotStartX = 0 - this.screenshotStartY = 0 - } -} diff --git a/src/ui/takeScreenshot/takeScreenshot.style.css b/src/ui/takeScreenshot/takeScreenshot.style.css deleted file mode 100644 index f104726c32b3c6a08edb1bc04f005a7fa25ccb4a..0000000000000000000000000000000000000000 --- a/src/ui/takeScreenshot/takeScreenshot.style.css +++ /dev/null @@ -1,37 +0,0 @@ -.overlay, -.tooltip, -.borderedBox { - user-select: none; -} - -.overlay { - background-color: rgba(0, 0, 0, 0.5); -} - -.overlay.highlighting { - background: none; - border-color: rgba(0, 0, 0, 0.5); - border-style: solid; -} - -.screenshotContainer { - clear: both; - background-repeat: no-repeat; - background-size: cover; - - transition: opacity ease-in-out 200ms; -} - -.smallSizeWindow { - height: 40px; -} - -.cancelTimesPosition { - top: 5px; - right: -10px; -} - -.screenshotPreviewCard { - z-index: 10520 !important; - background:none; -} \ No newline at end of file diff --git a/src/ui/takeScreenshot/takeScreenshot.template.html b/src/ui/takeScreenshot/takeScreenshot.template.html deleted file mode 100644 index edbdcfa45775b870299274dc5f205b9902116a97..0000000000000000000000000000000000000000 --- a/src/ui/takeScreenshot/takeScreenshot.template.html +++ /dev/null @@ -1,62 +0,0 @@ -<div class="screenshotContainer overflow-hidden w-50 h-50" - (mousemove)="move($event)" - (mousedown)="mouseDown($event)" - (mouseup)="mouseUp($event)" - [ngClass]="{'pe-none o-0':!takingScreenshot, 'o-1': takingScreenshot}" - [ngStyle]="{'cursor':takingScreenshot? 'crosshair' : 'auto'}"> - - <div class="overlay position-fixed fixed-top w-100 h-100 d-flex align-items-center justify-content-center" - [ngClass]="{ 'highlighting' : mouseIsDown }" - [ngStyle]="{ borderWidth: borderWidth }"> - - <!-- instruction text --> - <mat-card *ngIf="!isDragging" class="screenshotPreviewCard pe-none"> - <mat-card-title> - Drag a box to take a screenshot - </mat-card-title> - <mat-card-subtitle class="text-muted d-flex justify-content-center"> - cancel with Esc - </mat-card-subtitle> - </mat-card> - </div> - <div class="position-absolute border border-light" - *ngIf="isDragging" - [ngStyle]="{ left: boxLeft + 'px', top: boxTop + 'px', width: boxEndWidth + 'px', height: boxEndHeight + 'px' }"> - </div> -</div> - -<ng-template #previewImageDialog> - <mat-dialog-content> - - <div class="d-flex w-100 h-100 justify-content-center align-items-center" *ngIf="loadingScreenshot"> - <span class="text-nowrap">Generating screenshot </span> <i class="fas fa-spinner fa-pulse ml-1"></i> - </div> - <ng-template [ngIf]="!loadingScreenshot"> - <img [src]="imageUrl" class="w-100 h-100"> - </ng-template> - </mat-dialog-content> - <mat-dialog-actions align="end"> - - <a *ngIf="imageUrl" - [href]="imageUrl" - [download]="screenshotName"> - - <button mat-raised-button - color="primary" - class="mr-2"> - <i class="fas fa-save"></i> Save - </button> - </a> - <button mat-stroked-button - color="default" - class="mr-2" - mat-dialog-close="again"> - <i class="fas fa-camera"></i> Try again - </button> - <button mat-button - color="default" - mat-dialog-close="cancel"> - Cancel - </button> - </mat-dialog-actions> -</ng-template> \ No newline at end of file diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts index b45831e8f461f2ca53a9a51c17cdc0b65340016d..e58bd77ba1c134fad099b3c439e73b425f7cc1c4 100644 --- a/src/ui/ui.module.ts +++ b/src/ui/ui.module.ts @@ -56,7 +56,6 @@ import { ReorderPanelIndexPipe } from "./nehubaContainer/reorderPanelIndex.pipe" import { TouchSideClass } from "./nehubaContainer/touchSideClass.directive"; import { BinSavedRegionsSelectionPipe, SavedRegionsSelectionBtnDisabledPipe } from "./viewerStateController/viewerState.pipes"; -import { TakeScreenshotComponent } from "src/ui/takeScreenshot/takeScreenshot.component"; import { FixedMouseContextualContainerDirective } from "src/util/directives/FixedMouseContextualContainerDirective.directive"; import { RegionHierarchy } from './viewerStateController/regionHierachy/regionHierarchy.component' import { RegionTextSearchAutocomplete } from "./viewerStateController/regionSearch/regionSearch.component"; @@ -86,6 +85,7 @@ import { HelpOnePager } from "./helpOnePager/helpOnePager.component"; import { RegionalFeaturesModule } from "./regionalFeatures"; import { Landmark2DModule } from "./nehubaContainer/2dLandmarks/module"; import { PluginCspCtrlCmp } from "./config/pluginCsp/pluginCsp.component"; +import { ScreenshotModule, HANDLE_SCREENSHOT_PROMISE, TypeHandleScrnShotPromise } from "./screenshot"; @NgModule({ imports : [ @@ -106,6 +106,7 @@ import { PluginCspCtrlCmp } from "./config/pluginCsp/pluginCsp.component"; FabSpeedDialModule, RegionalFeaturesModule, Landmark2DModule, + ScreenshotModule, ], declarations : [ NehubaContainer, @@ -138,7 +139,6 @@ import { PluginCspCtrlCmp } from "./config/pluginCsp/pluginCsp.component"; RegionHierarchy, MaximmisePanelButton, RegionTextSearchAutocomplete, - TakeScreenshotComponent, RegionMenuComponent, ConnectivityBrowserComponent, SimpleRegionComponent, @@ -189,6 +189,60 @@ import { PluginCspCtrlCmp } from "./config/pluginCsp/pluginCsp.component"; provide: APPEND_SCRIPT_TOKEN, useFactory: appendScriptFactory, deps: [ DOCUMENT ] + }, + { + provide: HANDLE_SCREENSHOT_PROMISE, + useValue: ((param) => { + const canvas: HTMLCanvasElement = document.querySelector('#neuroglancer-container canvas') + if (!canvas) return Promise.reject(`element '#neuroglancer-container canvas' not found`) + const _ = (window as any).viewer.display.draw() + if (!param) { + return new Promise(rs => { + canvas.toBlob(blob => { + const url = URL.createObjectURL(blob) + rs({ + url, + revoke: () => URL.revokeObjectURL(url) + }) + }, 'image/png') + }) + } + const { x, y, width, height } = param + const { devicePixelRatio: dpr } = window + return new Promise(rs => { + const subCanvas = document.createElement('canvas') + subCanvas.width = width * dpr + subCanvas.height = height * dpr + const context = subCanvas.getContext('2d') + context.drawImage( + canvas, + + /** + * from + */ + x * dpr, + y * dpr, + width * dpr, + height * dpr, + + /** + * to + */ + 0, + 0, + width * dpr, + height * dpr + ) + + subCanvas.toBlob(blob => { + const url = URL.createObjectURL(blob) + rs({ + url, + revoke: () => URL.revokeObjectURL(url) + }) + }, 'image/png') + }) + }) as TypeHandleScrnShotPromise } ], entryComponents : [ diff --git a/src/util/fn.ts b/src/util/fn.ts index 9307ae0930cc410525a9bfebb05b0a5fe1196d22..827aec4f0e872df3433aacc123066f6707ce2f72 100644 --- a/src/util/fn.ts +++ b/src/util/fn.ts @@ -51,3 +51,7 @@ export function recursiveFindRegionWithLabelIndexId({ regions, labelIndexId, inh if (found) { return found } return null } + +export function getUuid(){ + return crypto.getRandomValues(new Uint32Array(1))[0].toString(16) +} diff --git a/src/util/pluginHandlerClasses/modalHandler.ts b/src/util/pluginHandlerClasses/modalHandler.ts deleted file mode 100644 index 2afa36774891ced56a784d685f02dd159570ec58..0000000000000000000000000000000000000000 --- a/src/util/pluginHandlerClasses/modalHandler.ts +++ /dev/null @@ -1,14 +0,0 @@ -export class ModalHandler { - - public hide: () => void - public show: () => void - // onHide : (callback: () => void) => void - // onHidden : (callback : () => void) => void - // onShow : (callback : () => void) => void - // onShown : (callback : () => void) => void - public title: string - public body: string - public footer: string - - public dismissable = true -} diff --git a/src/util/pluginHandlerClasses/toastHandler.ts b/src/util/pluginHandlerClasses/toastHandler.ts deleted file mode 100644 index 9bba88be2bd00bc936ba0da7609054ddc5ea769d..0000000000000000000000000000000000000000 --- a/src/util/pluginHandlerClasses/toastHandler.ts +++ /dev/null @@ -1,8 +0,0 @@ -export class ToastHandler { - public message = 'Toast message' - public timeout = 3000 - public dismissable = true - public show: () => void - public hide: () => void - public htmlMessage: string -}