Change indentation to 4 spaces (because why was it 2 anyway?)
Signed-off-by: trivernis <trivernis@protonmail.com>pull/4/head
parent
6348f10981
commit
e694645a78
@ -1,13 +1,13 @@
|
|||||||
import {NgModule} from '@angular/core';
|
import {NgModule} from "@angular/core";
|
||||||
import {RouterModule, Routes} from '@angular/router';
|
import {RouterModule, Routes} from "@angular/router";
|
||||||
import {CoreComponent} from "./components/core/core.component";
|
import {CoreComponent} from "./components/core/core.component";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{path: "", component: CoreComponent}];
|
{path: "", component: CoreComponent}];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forRoot(routes)],
|
imports: [RouterModule.forRoot(routes)],
|
||||||
exports: [RouterModule]
|
exports: [RouterModule]
|
||||||
})
|
})
|
||||||
export class AppRoutingModule {
|
export class AppRoutingModule {
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
::ng-deep .mat-button-wrapper > ng-icon {
|
::ng-deep .mat-button-wrapper > ng-icon {
|
||||||
font-size: 26px;
|
font-size: 26px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::ng-deep ng-icon {
|
::ng-deep ng-icon {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
|
@ -1,36 +1,36 @@
|
|||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from "@angular/core/testing";
|
||||||
import {RouterTestingModule} from '@angular/router/testing';
|
import {RouterTestingModule} from "@angular/router/testing";
|
||||||
import {AppComponent} from './app.component';
|
import {AppComponent} from "./app.component";
|
||||||
|
|
||||||
describe('AppComponent', () => {
|
describe("AppComponent", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
RouterTestingModule
|
RouterTestingModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent
|
AppComponent
|
||||||
],
|
],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create the app', () => {
|
it("should create the app", () => {
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
const app = fixture.componentInstance;
|
const app = fixture.componentInstance;
|
||||||
expect(app).toBeTruthy();
|
expect(app).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should have as title 'mediarepo-ui'`, () => {
|
it("should have as title 'mediarepo-ui'", () => {
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
const app = fixture.componentInstance;
|
const app = fixture.componentInstance;
|
||||||
expect(app.title).toEqual('mediarepo-ui');
|
expect(app.title).toEqual("mediarepo-ui");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render title', () => {
|
it("should render title", () => {
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
const compiled = fixture.nativeElement as HTMLElement;
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
expect(compiled.querySelector('.content span')?.textContent)
|
expect(compiled.querySelector(".content span")?.textContent)
|
||||||
.toContain('mediarepo-ui app is running!');
|
.toContain("mediarepo-ui app is running!");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,43 +1,43 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from "@angular/core";
|
||||||
import {Router} from "@angular/router";
|
import {Router} from "@angular/router";
|
||||||
import {RepositoryService} from "./services/repository/repository.service";
|
import {RepositoryService} from "./services/repository/repository.service";
|
||||||
import {MatSnackBar} from "@angular/material/snack-bar";
|
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||||
import {ErrorBrokerService} from "./services/error-broker/error-broker.service";
|
import {ErrorBrokerService} from "./services/error-broker/error-broker.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: "app-root",
|
||||||
templateUrl: './app.component.html',
|
templateUrl: "./app.component.html",
|
||||||
styleUrls: ['./app.component.scss']
|
styleUrls: ["./app.component.scss"]
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnInit {
|
export class AppComponent implements OnInit {
|
||||||
title = 'mediarepo-ui';
|
title = "mediarepo-ui";
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private snackBar: MatSnackBar,
|
private snackBar: MatSnackBar,
|
||||||
private errorBroker: ErrorBrokerService,
|
private errorBroker: ErrorBrokerService,
|
||||||
private repoService: RepositoryService,
|
private repoService: RepositoryService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.errorBroker.errorCb = (err: { message: string }) => this.showError(
|
this.errorBroker.errorCb = (err: { message: string }) => this.showError(
|
||||||
err);
|
err);
|
||||||
this.errorBroker.infoCb = (info: string) => this.showInfo(info);
|
this.errorBroker.infoCb = (info: string) => this.showInfo(info);
|
||||||
await this.repoService.loadRepositories();
|
await this.repoService.loadRepositories();
|
||||||
}
|
}
|
||||||
|
|
||||||
private showError(err: { message: string }) {
|
private showError(err: { message: string }) {
|
||||||
this.snackBar.open(err.message, undefined, {
|
this.snackBar.open(err.message, undefined, {
|
||||||
panelClass: "warn",
|
panelClass: "warn",
|
||||||
duration: 2000,
|
duration: 2000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private showInfo(info: string) {
|
private showInfo(info: string) {
|
||||||
this.snackBar.open(info, undefined, {
|
this.snackBar.open(info, undefined, {
|
||||||
panelClass: "primary",
|
panelClass: "primary",
|
||||||
duration: 2000,
|
duration: 2000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
import {NgModule} from '@angular/core';
|
import {NgModule} from "@angular/core";
|
||||||
import {BrowserModule} from '@angular/platform-browser';
|
import {BrowserModule} from "@angular/platform-browser";
|
||||||
import {AppComponent} from './app.component';
|
import {AppComponent} from "./app.component";
|
||||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
|
||||||
import {CoreModule} from "./components/core/core.module";
|
import {CoreModule} from "./components/core/core.module";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
CoreModule,
|
CoreModule,
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
export class AppModule {
|
export class AppModule {
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
<div id="content">
|
<div id="content">
|
||||||
<mat-tab-group #tabGroup (selectedTabChange)="this.onTabSelectionChange($event)">
|
<mat-tab-group #tabGroup (selectedTabChange)="this.onTabSelectionChange($event)">
|
||||||
<mat-tab label="Repositories">
|
<mat-tab label="Repositories">
|
||||||
<app-repositories-tab></app-repositories-tab>
|
<app-repositories-tab></app-repositories-tab>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
<mat-tab *ngIf="this.selectedRepository" label="Files">
|
<mat-tab *ngIf="this.selectedRepository" label="Files">
|
||||||
<app-files-tab></app-files-tab>
|
<app-files-tab></app-files-tab>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
<mat-tab *ngIf="this.selectedRepository" label="Import">
|
<mat-tab *ngIf="this.selectedRepository" label="Import">
|
||||||
<app-import-tab></app-import-tab>
|
<app-import-tab></app-import-tab>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
</mat-tab-group>
|
</mat-tab-group>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
#content {
|
#content {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
overflow: hidden
|
overflow: hidden
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-tab-group {
|
mat-tab-group {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
::ng-deep .mat-tab-body-wrapper {
|
::ng-deep .mat-tab-body-wrapper {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {CoreComponent} from './core.component';
|
import {CoreComponent} from "./core.component";
|
||||||
|
|
||||||
describe('HomeComponent', () => {
|
describe("HomeComponent", () => {
|
||||||
let component: CoreComponent;
|
let component: CoreComponent;
|
||||||
let fixture: ComponentFixture<CoreComponent>;
|
let fixture: ComponentFixture<CoreComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [CoreComponent]
|
declarations: [CoreComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(CoreComponent);
|
fixture = TestBed.createComponent(CoreComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,41 +1,43 @@
|
|||||||
<div class="sidebar-inner">
|
<div class="sidebar-inner">
|
||||||
<mat-tab-group headerPosition="below">
|
<mat-tab-group headerPosition="below">
|
||||||
<mat-tab label="Search">
|
<mat-tab label="Search">
|
||||||
<div class="search-tab-inner" fxLayout="column">
|
<div class="search-tab-inner" fxLayout="column">
|
||||||
<div id="file-search-input">
|
<div id="file-search-input">
|
||||||
<app-file-search #filesearch (searchEndEvent)="this.searchEndEvent.emit()"
|
<app-file-search #filesearch (searchEndEvent)="this.searchEndEvent.emit()"
|
||||||
(searchStartEvent)="this.searchStartEvent.emit()"
|
(searchStartEvent)="this.searchStartEvent.emit()"
|
||||||
[availableTags]="this.allTags"></app-file-search>
|
[availableTags]="this.allTags"></app-file-search>
|
||||||
</div>
|
</div>
|
||||||
<mat-divider fxFlex="1em"></mat-divider>
|
<mat-divider fxFlex="1em"></mat-divider>
|
||||||
<div class="tag-list-header" fxFlex="40px">
|
<div class="tag-list-header" fxFlex="40px">
|
||||||
<h2>Tags</h2>
|
<h2>Tags</h2>
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
</div>
|
</div>
|
||||||
<div class="file-tag-list" fxFlex fxFlexAlign="start" fxFlexFill>
|
<div class="file-tag-list" fxFlex fxFlexAlign="start" fxFlexFill>
|
||||||
<cdk-virtual-scroll-viewport itemSize="50" maxBufferPx="4000" minBufferPx="500">
|
<cdk-virtual-scroll-viewport itemSize="50" maxBufferPx="4000" minBufferPx="500">
|
||||||
<div (click)="addSearchTag(tag)" (contextmenu)="contextMenuTag = tag; contextMenu.onContextMenu($event)" *cdkVirtualFor="let tag of tags" class="selectable-tag"
|
<div (click)="addSearchTag(tag)"
|
||||||
matRipple>
|
(contextmenu)="contextMenuTag = tag; contextMenu.onContextMenu($event)"
|
||||||
<app-tag-item [tag]="tag"></app-tag-item>
|
*cdkVirtualFor="let tag of tags" class="selectable-tag"
|
||||||
|
matRipple>
|
||||||
|
<app-tag-item [tag]="tag"></app-tag-item>
|
||||||
|
</div>
|
||||||
|
</cdk-virtual-scroll-viewport>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</cdk-virtual-scroll-viewport>
|
</mat-tab>
|
||||||
</div>
|
<mat-tab *ngIf="this.selectedFiles.length > 0" label="Edit">
|
||||||
</div>
|
<app-file-edit #fileedit [files]="this.selectedFiles"></app-file-edit>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
<mat-tab *ngIf="this.selectedFiles.length > 0" label="Edit">
|
</mat-tab-group>
|
||||||
<app-file-edit #fileedit [files]="this.selectedFiles"></app-file-edit>
|
|
||||||
</mat-tab>
|
|
||||||
</mat-tab-group>
|
|
||||||
</div>
|
</div>
|
||||||
<app-context-menu #contextMenu>
|
<app-context-menu #contextMenu>
|
||||||
<button (click)="this.copyToClipboard(this.contextMenuTag!.getNormalizedOutput())" *ngIf="this.contextMenuTag"
|
<button (click)="this.copyToClipboard(this.contextMenuTag!.getNormalizedOutput())" *ngIf="this.contextMenuTag"
|
||||||
mat-menu-item>Copy
|
mat-menu-item>Copy
|
||||||
"{{contextMenuTag!.getNormalizedOutput()}}"
|
"{{contextMenuTag!.getNormalizedOutput()}}"
|
||||||
</button>
|
</button>
|
||||||
<button (click)="this.copyToClipboard(this.contextMenuTag!.name)" *ngIf="this.contextMenuTag?.namespace"
|
<button (click)="this.copyToClipboard(this.contextMenuTag!.name)" *ngIf="this.contextMenuTag?.namespace"
|
||||||
mat-menu-item>Copy "{{this.contextMenuTag!.name}}"
|
mat-menu-item>Copy "{{this.contextMenuTag!.name}}"
|
||||||
</button>
|
</button>
|
||||||
<button (click)="this.copyToClipboard(this.contextMenuTag!.namespace!)" *ngIf="this.contextMenuTag?.namespace"
|
<button (click)="this.copyToClipboard(this.contextMenuTag!.namespace!)" *ngIf="this.contextMenuTag?.namespace"
|
||||||
mat-menu-item>Copy "{{this.contextMenuTag!.namespace!}}"
|
mat-menu-item>Copy "{{this.contextMenuTag!.namespace!}}"
|
||||||
</button>
|
</button>
|
||||||
</app-context-menu>
|
</app-context-menu>
|
||||||
|
@ -1,70 +1,70 @@
|
|||||||
app-file-search {
|
app-file-search {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#file-search-input {
|
#file-search-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-tab-group, mat-tab, .file-tag-list, app-file-edit {
|
mat-tab-group, mat-tab, .file-tag-list, app-file-edit {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-selection-list {
|
mat-selection-list {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-inner, .search-tab-inner {
|
.sidebar-inner, .search-tab-inner {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.selectable-tag {
|
.selectable-tag {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
display: flex;
|
display: flex;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition-duration: 0.1s;
|
transition-duration: 0.1s;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
app-tag-item {
|
app-tag-item {
|
||||||
margin: auto auto auto 0.25em;
|
margin: auto auto auto 0.25em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.selectable-tag:hover {
|
.selectable-tag:hover {
|
||||||
background-color: darken(dimgrey, 10);
|
background-color: darken(dimgrey, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
.selectable-tag:active {
|
.selectable-tag:active {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
cdk-virtual-scroll-viewport {
|
cdk-virtual-scroll-viewport {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
::ng-deep .cdk-virtual-scroll-content-wrapper {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
overflow-y: auto;
|
||||||
|
|
||||||
|
::ng-deep .cdk-virtual-scroll-content-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-divider {
|
mat-divider {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag-list-header {
|
.tag-list-header {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {FilesTabSidebarComponent} from './files-tab-sidebar.component';
|
import {FilesTabSidebarComponent} from "./files-tab-sidebar.component";
|
||||||
|
|
||||||
describe('FilesTabSidebarComponent', () => {
|
describe("FilesTabSidebarComponent", () => {
|
||||||
let component: FilesTabSidebarComponent;
|
let component: FilesTabSidebarComponent;
|
||||||
let fixture: ComponentFixture<FilesTabSidebarComponent>;
|
let fixture: ComponentFixture<FilesTabSidebarComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [FilesTabSidebarComponent]
|
declarations: [FilesTabSidebarComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(FilesTabSidebarComponent);
|
fixture = TestBed.createComponent(FilesTabSidebarComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
<mat-drawer-container autosize>
|
<mat-drawer-container autosize>
|
||||||
<mat-drawer disableClose mode="side" opened>
|
<mat-drawer disableClose mode="side" opened>
|
||||||
<app-files-tab-sidebar (searchEndEvent)="this.contentLoading = false"
|
<app-files-tab-sidebar (searchEndEvent)="this.contentLoading = false"
|
||||||
(searchStartEvent)="this.contentLoading = true"
|
(searchStartEvent)="this.contentLoading = true"
|
||||||
[selectedFiles]="this.selectedFiles"></app-files-tab-sidebar>
|
[selectedFiles]="this.selectedFiles"></app-files-tab-sidebar>
|
||||||
</mat-drawer>
|
</mat-drawer>
|
||||||
<mat-drawer-content>
|
<mat-drawer-content>
|
||||||
<app-busy-indicator [blurBackground]="true" [busy]="contentLoading">
|
<app-busy-indicator [blurBackground]="true" [busy]="contentLoading">
|
||||||
<app-file-multiview (fileSelectEvent)="this.onFileSelect($event)" [files]="this.files"></app-file-multiview>
|
<app-file-multiview (fileSelectEvent)="this.onFileSelect($event)" [files]="this.files"></app-file-multiview>
|
||||||
</app-busy-indicator>
|
</app-busy-indicator>
|
||||||
</mat-drawer-content>
|
</mat-drawer-content>
|
||||||
</mat-drawer-container>
|
</mat-drawer-container>
|
||||||
|
@ -1,45 +1,45 @@
|
|||||||
mat-selection-list {
|
mat-selection-list {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-drawer {
|
mat-drawer {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 25%;
|
width: 25%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-drawer-content {
|
mat-drawer-content {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-drawer-container {
|
mat-drawer-container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
app-file-multiview {
|
app-file-multiview {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spinner-overlay {
|
.spinner-overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
z-index: 998;
|
z-index: 998;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
backdrop-filter: blur(5px);
|
backdrop-filter: blur(5px);
|
||||||
|
|
||||||
mat-progress-spinner {
|
mat-progress-spinner {
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,24 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {FilesTabComponent} from './files-tab.component';
|
import {FilesTabComponent} from "./files-tab.component";
|
||||||
|
|
||||||
describe('SearchPageComponent', () => {
|
describe("SearchPageComponent", () => {
|
||||||
let component: FilesTabComponent;
|
let component: FilesTabComponent;
|
||||||
let fixture: ComponentFixture<FilesTabComponent>;
|
let fixture: ComponentFixture<FilesTabComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [FilesTabComponent]
|
declarations: [FilesTabComponent]
|
||||||
})
|
}).compileComponents();
|
||||||
.compileComponents();
|
});
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(FilesTabComponent);
|
fixture = TestBed.createComponent(FilesTabComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,34 +1,34 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from "@angular/core";
|
||||||
import {File} from "../../../models/File";
|
import {File} from "../../../models/File";
|
||||||
import {ErrorBrokerService} from "../../../services/error-broker/error-broker.service";
|
import {ErrorBrokerService} from "../../../services/error-broker/error-broker.service";
|
||||||
import {FileService} from "../../../services/file/file.service";
|
import {FileService} from "../../../services/file/file.service";
|
||||||
import {RepositoryService} from "../../../services/repository/repository.service";
|
import {RepositoryService} from "../../../services/repository/repository.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-files-tab',
|
selector: "app-files-tab",
|
||||||
templateUrl: './files-tab.component.html',
|
templateUrl: "./files-tab.component.html",
|
||||||
styleUrls: ['./files-tab.component.scss']
|
styleUrls: ["./files-tab.component.scss"]
|
||||||
})
|
})
|
||||||
export class FilesTabComponent implements OnInit {
|
export class FilesTabComponent implements OnInit {
|
||||||
|
|
||||||
|
|
||||||
files: File[] = [];
|
files: File[] = [];
|
||||||
contentLoading = false;
|
contentLoading = false;
|
||||||
selectedFiles: File[] = [];
|
selectedFiles: File[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private errorBroker: ErrorBrokerService,
|
private errorBroker: ErrorBrokerService,
|
||||||
private repoService: RepositoryService,
|
private repoService: RepositoryService,
|
||||||
private fileService: FileService,) {
|
private fileService: FileService,) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.fileService.displayedFiles.subscribe(async (files) => {
|
this.fileService.displayedFiles.subscribe(async (files) => {
|
||||||
this.files = files;
|
this.files = files;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async onFileSelect(files: File[]) {
|
async onFileSelect(files: File[]) {
|
||||||
this.selectedFiles = files;
|
this.selectedFiles = files;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,35 @@
|
|||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-label>Selection Type</mat-label>
|
<mat-label>Selection Type</mat-label>
|
||||||
<mat-select #selectionType value="files">
|
<mat-select #selectionType value="files">
|
||||||
<mat-option value="folders">Folders</mat-option>
|
<mat-option value="folders">Folders</mat-option>
|
||||||
<mat-option value="files">Files</mat-option>
|
<mat-option value="files">Files</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<app-native-file-select (fileSelect)="this.setSelectedPaths($event)" [filters]="this.filters"
|
<app-native-file-select (fileSelect)="this.setSelectedPaths($event)" [filters]="this.filters"
|
||||||
[mode]="selectionType.value"></app-native-file-select>
|
[mode]="selectionType.value"></app-native-file-select>
|
||||||
<button mat-flat-button>
|
<button mat-flat-button>
|
||||||
{{resolving ? "Searching for files..." : this.fileCount + " files found"}}
|
{{resolving ? "Searching for files..." : this.fileCount + " files found"}}
|
||||||
<mat-progress-bar *ngIf="resolving" color="primary" mode="indeterminate"></mat-progress-bar>
|
<mat-progress-bar *ngIf="resolving" color="primary" mode="indeterminate"></mat-progress-bar>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
<section class="binary-import-options">
|
<section class="binary-import-options">
|
||||||
<mat-checkbox (change)="this.importOptions.read_tags_from_txt = $event.checked"
|
<mat-checkbox (change)="this.importOptions.read_tags_from_txt = $event.checked"
|
||||||
[checked]="this.importOptions.read_tags_from_txt">Import tags from
|
[checked]="this.importOptions.read_tags_from_txt">Import tags from
|
||||||
adjacent .txt tag files
|
adjacent .txt tag files
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
<mat-checkbox (change)="this.importOptions.delete_after_import = $event.checked"
|
<mat-checkbox (change)="this.importOptions.delete_after_import = $event.checked"
|
||||||
[checked]="this.importOptions.delete_after_import" color="warn">
|
[checked]="this.importOptions.delete_after_import" color="warn">
|
||||||
Delete files from original location after import
|
Delete files from original location after import
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
<button (click)="import()" [disabled]="importing || this.fileCount === 0" class="import-button" color="primary"
|
<button (click)="import()" [disabled]="importing || this.fileCount === 0" class="import-button" color="primary"
|
||||||
mat-flat-button>
|
mat-flat-button>
|
||||||
{{importing ? "Importing..." : "Import"}}
|
{{importing ? "Importing..." : "Import"}}
|
||||||
</button>
|
</button>
|
||||||
<mat-progress-bar *ngIf="importing" [value]="this.importingProgress" color="primary"
|
<mat-progress-bar *ngIf="importing" [value]="this.importingProgress" color="primary"
|
||||||
mode="determinate"></mat-progress-bar>
|
mode="determinate"></mat-progress-bar>
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
app-native-file-select {
|
app-native-file-select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-divider {
|
mat-divider {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
margin: 1em 0;
|
margin: 1em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filled-button {
|
.filled-button {
|
||||||
background-color: #5c5c5c;
|
background-color: #5c5c5c;
|
||||||
}
|
}
|
||||||
|
|
||||||
.binary-import-options {
|
.binary-import-options {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
|
|
||||||
mat-checkbox {
|
mat-checkbox {
|
||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {FilesystemImportComponent} from './filesystem-import.component';
|
import {FilesystemImportComponent} from "./filesystem-import.component";
|
||||||
|
|
||||||
describe('FilesystemImportComponent', () => {
|
describe("FilesystemImportComponent", () => {
|
||||||
let component: FilesystemImportComponent;
|
let component: FilesystemImportComponent;
|
||||||
let fixture: ComponentFixture<FilesystemImportComponent>;
|
let fixture: ComponentFixture<FilesystemImportComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [FilesystemImportComponent]
|
declarations: [FilesystemImportComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(FilesystemImportComponent);
|
fixture = TestBed.createComponent(FilesystemImportComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
<div class="import-tab-inner">
|
<div class="import-tab-inner">
|
||||||
<mat-tab-group headerPosition="below">
|
<mat-tab-group headerPosition="below">
|
||||||
<mat-tab label="Import">
|
<mat-tab label="Import">
|
||||||
<div class="import-sidebar-tab-inner" fxLayout="column">
|
<div class="import-sidebar-tab-inner" fxLayout="column">
|
||||||
<div class="import-type-select-wrapper" fxFlex="6em">
|
<div class="import-type-select-wrapper" fxFlex="6em">
|
||||||
<mat-form-field class="import-type-select">
|
<mat-form-field class="import-type-select">
|
||||||
<mat-label>Import Type</mat-label>
|
<mat-label>Import Type</mat-label>
|
||||||
<mat-select value="filesystem">
|
<mat-select value="filesystem">
|
||||||
<mat-option value="filesystem">Filesystem</mat-option>
|
<mat-option value="filesystem">Filesystem</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
</div>
|
</div>
|
||||||
<div class="import-configuration" fxFlex fxFlexFill>
|
<div class="import-configuration" fxFlex fxFlexFill>
|
||||||
<app-filesystem-import (fileImported)="this.fileImported.emit($event)"
|
<app-filesystem-import (fileImported)="this.fileImported.emit($event)"
|
||||||
(importFinished)="importFinished.emit()"></app-filesystem-import>
|
(importFinished)="importFinished.emit()"></app-filesystem-import>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
</mat-tab-group>
|
</mat-tab-group>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,35 +1,35 @@
|
|||||||
mat-tab-group, mat-tab {
|
mat-tab-group, mat-tab {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.import-tab-inner {
|
.import-tab-inner {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.import-type-select-wrapper {
|
.import-type-select-wrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.import-type-select {
|
.import-type-select {
|
||||||
width: calc(100% - 2em);
|
width: calc(100% - 2em);
|
||||||
height: calc(100% - 2em);
|
height: calc(100% - 2em);
|
||||||
margin: 1em;
|
margin: 1em;
|
||||||
|
|
||||||
mat-select {
|
mat-select {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.import-sidebar-tab-inner {
|
.import-sidebar-tab-inner {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.import-configuration {
|
.import-configuration {
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {ImportTabSidebarComponent} from './import-tab-sidebar.component';
|
import {ImportTabSidebarComponent} from "./import-tab-sidebar.component";
|
||||||
|
|
||||||
describe('ImportTabSidebarComponent', () => {
|
describe("ImportTabSidebarComponent", () => {
|
||||||
let component: ImportTabSidebarComponent;
|
let component: ImportTabSidebarComponent;
|
||||||
let fixture: ComponentFixture<ImportTabSidebarComponent>;
|
let fixture: ComponentFixture<ImportTabSidebarComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [ImportTabSidebarComponent]
|
declarations: [ImportTabSidebarComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ImportTabSidebarComponent);
|
fixture = TestBed.createComponent(ImportTabSidebarComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import {Component, EventEmitter, Output} from '@angular/core';
|
import {Component, EventEmitter, Output} from "@angular/core";
|
||||||
import {File} from "../../../../models/File";
|
import {File} from "../../../../models/File";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-import-tab-sidebar',
|
selector: "app-import-tab-sidebar",
|
||||||
templateUrl: './import-tab-sidebar.component.html',
|
templateUrl: "./import-tab-sidebar.component.html",
|
||||||
styleUrls: ['./import-tab-sidebar.component.scss']
|
styleUrls: ["./import-tab-sidebar.component.scss"]
|
||||||
})
|
})
|
||||||
export class ImportTabSidebarComponent {
|
export class ImportTabSidebarComponent {
|
||||||
|
|
||||||
@Output() fileImported = new EventEmitter<File>();
|
@Output() fileImported = new EventEmitter<File>();
|
||||||
@Output() importFinished = new EventEmitter<void>();
|
@Output() importFinished = new EventEmitter<void>();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<mat-drawer-container autosize>
|
<mat-drawer-container autosize>
|
||||||
<mat-drawer disableClose="true" mode="side" opened>
|
<mat-drawer disableClose="true" mode="side" opened>
|
||||||
<app-import-tab-sidebar (fileImported)="this.addFileFromImport($event)"
|
<app-import-tab-sidebar (fileImported)="this.addFileFromImport($event)"
|
||||||
(importFinished)="this.refreshFileView()"></app-import-tab-sidebar>
|
(importFinished)="this.refreshFileView()"></app-import-tab-sidebar>
|
||||||
</mat-drawer>
|
</mat-drawer>
|
||||||
<mat-drawer-content>
|
<mat-drawer-content>
|
||||||
<app-file-multiview [files]="this.files"></app-file-multiview>
|
<app-file-multiview [files]="this.files"></app-file-multiview>
|
||||||
</mat-drawer-content>
|
</mat-drawer-content>
|
||||||
</mat-drawer-container>
|
</mat-drawer-container>
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
mat-drawer-container {
|
mat-drawer-container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-drawer-content {
|
mat-drawer-content {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-drawer {
|
mat-drawer {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 25%;
|
width: 25%;
|
||||||
}
|
}
|
||||||
|
|
||||||
app-import-tab-sidebar, app-file-multiview {
|
app-import-tab-sidebar, app-file-multiview {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
app-file-multiview {
|
app-file-multiview {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {ImportTabComponent} from './import-tab.component';
|
import {ImportTabComponent} from "./import-tab.component";
|
||||||
|
|
||||||
describe('ImportTabComponent', () => {
|
describe("ImportTabComponent", () => {
|
||||||
let component: ImportTabComponent;
|
let component: ImportTabComponent;
|
||||||
let fixture: ComponentFixture<ImportTabComponent>;
|
let fixture: ComponentFixture<ImportTabComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [ImportTabComponent]
|
declarations: [ImportTabComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ImportTabComponent);
|
fixture = TestBed.createComponent(ImportTabComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,35 +1,35 @@
|
|||||||
import {Component} from '@angular/core';
|
import {Component} from "@angular/core";
|
||||||
import {File} from "../../../models/File";
|
import {File} from "../../../models/File";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-import-tab',
|
selector: "app-import-tab",
|
||||||
templateUrl: './import-tab.component.html',
|
templateUrl: "./import-tab.component.html",
|
||||||
styleUrls: ['./import-tab.component.scss']
|
styleUrls: ["./import-tab.component.scss"]
|
||||||
})
|
})
|
||||||
export class ImportTabComponent {
|
export class ImportTabComponent {
|
||||||
|
|
||||||
public files: File[] = [];
|
public files: File[] = [];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an imported file to the list of imported files
|
* Adds an imported file to the list of imported files
|
||||||
* @param {File} file
|
* @param {File} file
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
public async addFileFromImport(file: File) {
|
public async addFileFromImport(file: File) {
|
||||||
this.files.push(file);
|
this.files.push(file);
|
||||||
if (this.files.length % 50 === 0) { // refresh every 50 pictures
|
if (this.files.length % 50 === 0) { // refresh every 50 pictures
|
||||||
this.refreshFileView();
|
this.refreshFileView();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refreshes the file view
|
* Refreshes the file view
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
public refreshFileView() {
|
public refreshFileView() {
|
||||||
this.files = [...this.files];
|
this.files = [...this.files];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,54 +1,56 @@
|
|||||||
<h1 mat-dialog-title>Add a Repository</h1>
|
<h1 mat-dialog-title>Add a Repository</h1>
|
||||||
<div mat-dialog-content>
|
<div mat-dialog-content>
|
||||||
<form [formGroup]="formGroup">
|
<form [formGroup]="formGroup">
|
||||||
|
|
||||||
<mat-form-field matTooltip="A unique name for the repository" matTooltipShowDelay="1000">
|
<mat-form-field matTooltip="A unique name for the repository" matTooltipShowDelay="1000">
|
||||||
<mat-label>Name</mat-label>
|
<mat-label>Name</mat-label>
|
||||||
<input (blur)="validateName()" (change)="validateName()" (input)="validateName()" formControlName="name" matInput>
|
<input (blur)="validateName()" (change)="validateName()" (input)="validateName()" formControlName="name"
|
||||||
</mat-form-field>
|
matInput>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field matTooltip="Type of repository if it's on the local system or somewhere else"
|
<mat-form-field matTooltip="Type of repository if it's on the local system or somewhere else"
|
||||||
matTooltipShowDelay="1000">
|
matTooltipShowDelay="1000">
|
||||||
<mat-label>Type</mat-label>
|
<mat-label>Type</mat-label>
|
||||||
<mat-select #repoTypeSelect (selectionChange)="onTypeChange(repoTypeSelect.value)"
|
<mat-select #repoTypeSelect (selectionChange)="onTypeChange(repoTypeSelect.value)"
|
||||||
formControlName="repositoryType">
|
formControlName="repositoryType">
|
||||||
<mat-option value="local">Local</mat-option>
|
<mat-option value="local">Local</mat-option>
|
||||||
<mat-option value="remote">Remote</mat-option>
|
<mat-option value="remote">Remote</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field *ngIf="repoTypeSelect.value === 'local'"
|
<mat-form-field *ngIf="repoTypeSelect.value === 'local'"
|
||||||
matTooltip="Path where the repository is located or should be created"
|
matTooltip="Path where the repository is located or should be created"
|
||||||
matTooltipShowDelay="1000">
|
matTooltipShowDelay="1000">
|
||||||
<button (click)="openFolderDialog()" class="button-folder-select" mat-button>
|
<button (click)="openFolderDialog()" class="button-folder-select" mat-button>
|
||||||
<ng-icon name="mat-folder"></ng-icon>
|
<ng-icon name="mat-folder"></ng-icon>
|
||||||
</button>
|
</button>
|
||||||
<mat-label>Path</mat-label>
|
<mat-label>Path</mat-label>
|
||||||
<input (change)="this.checkLocalRepoExists()" formControlName="path" matInput>
|
<input (change)="this.checkLocalRepoExists()" formControlName="path" matInput>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field *ngIf="repoTypeSelect.value === 'remote'" matTooltip="IP address and port of the remote repository"
|
<mat-form-field *ngIf="repoTypeSelect.value === 'remote'"
|
||||||
matTooltipShowDelay="1000">
|
matTooltip="IP address and port of the remote repository"
|
||||||
<mat-label>Address</mat-label>
|
matTooltipShowDelay="1000">
|
||||||
<input formControlName="address" matInput>
|
<mat-label>Address</mat-label>
|
||||||
</mat-form-field>
|
<input formControlName="address" matInput>
|
||||||
</form>
|
</mat-form-field>
|
||||||
<div *ngIf="repoTypeSelect.value === 'remote'" class="connection-state">
|
</form>
|
||||||
<span>Status:</span> <span>{{this.onlineStatus}}</span>
|
<div *ngIf="repoTypeSelect.value === 'remote'" class="connection-state">
|
||||||
</div>
|
<span>Status:</span> <span>{{this.onlineStatus}}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-buttons" mat-dialog-actions>
|
<div class="dialog-buttons" mat-dialog-actions>
|
||||||
<button (click)="closeDialog()" color="accent" mat-stroked-button>Cancel</button>
|
<button (click)="closeDialog()" color="accent" mat-stroked-button>Cancel</button>
|
||||||
<button (click)="addRepository()" *ngIf="repoTypeSelect.value === 'remote' || this.localRepoExists"
|
<button (click)="addRepository()" *ngIf="repoTypeSelect.value === 'remote' || this.localRepoExists"
|
||||||
[disabled]="!formGroup.valid" color="primary" mat-flat-button
|
[disabled]="!formGroup.valid" color="primary" mat-flat-button
|
||||||
matTooltip="Add the existing repository">Add
|
matTooltip="Add the existing repository">Add
|
||||||
</button>
|
</button>
|
||||||
<button (click)="this.initLocalRepository()" *ngIf="repoTypeSelect.value === 'local' && !this.localRepoExists"
|
<button (click)="this.initLocalRepository()" *ngIf="repoTypeSelect.value === 'local' && !this.localRepoExists"
|
||||||
[disabled]="!formGroup.valid"
|
[disabled]="!formGroup.valid"
|
||||||
color="accent" mat-flat-button
|
color="accent" mat-flat-button
|
||||||
matTooltip="Initialize the repository in the specified path">Init
|
matTooltip="Initialize the repository in the specified path">Init
|
||||||
</button>
|
</button>
|
||||||
<button (click)="checkRepositoryStatus()" *ngIf="repoTypeSelect.value === 'remote'" [disabled]="!formGroup.valid"
|
<button (click)="checkRepositoryStatus()" *ngIf="repoTypeSelect.value === 'remote'" [disabled]="!formGroup.valid"
|
||||||
class="check-connection-button" mat-stroked-button>Check Connection
|
class="check-connection-button" mat-stroked-button>Check Connection
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
form {
|
form {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-buttons {
|
.dialog-buttons {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
button {
|
button {
|
||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
float: right
|
float: right
|
||||||
}
|
}
|
||||||
|
|
||||||
.check-connection-button {
|
.check-connection-button {
|
||||||
justify-self: right;
|
justify-self: right;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-folder-select {
|
.button-folder-select {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -10px;
|
top: -10px;
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {AddRepositoryDialogComponent} from './add-repository-dialog.component';
|
import {AddRepositoryDialogComponent} from "./add-repository-dialog.component";
|
||||||
|
|
||||||
describe('AddRepositoryDialogComponent', () => {
|
describe("AddRepositoryDialogComponent", () => {
|
||||||
let component: AddRepositoryDialogComponent;
|
let component: AddRepositoryDialogComponent;
|
||||||
let fixture: ComponentFixture<AddRepositoryDialogComponent>;
|
let fixture: ComponentFixture<AddRepositoryDialogComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [AddRepositoryDialogComponent]
|
declarations: [AddRepositoryDialogComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(AddRepositoryDialogComponent);
|
fixture = TestBed.createComponent(AddRepositoryDialogComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<div class="repo-page-content">
|
<div class="repo-page-content">
|
||||||
<div class="add-repo-tools">
|
<div class="add-repo-tools">
|
||||||
<button (click)="openAddRepositoryDialog()" color="primary" mat-flat-button>Add Repository</button>
|
<button (click)="openAddRepositoryDialog()" color="primary" mat-flat-button>Add Repository</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="repository-list">
|
<div class="repository-list">
|
||||||
<div *ngFor="let repository of repositories" class="repository-container">
|
<div *ngFor="let repository of repositories" class="repository-container">
|
||||||
<app-repository-card [repository]="repository"></app-repository-card>
|
<app-repository-card [repository]="repository"></app-repository-card>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {RepositoriesTabComponent} from './repositories-tab.component';
|
import {RepositoriesTabComponent} from "./repositories-tab.component";
|
||||||
|
|
||||||
describe('RepositoriesComponent', () => {
|
describe("RepositoriesComponent", () => {
|
||||||
let component: RepositoriesTabComponent;
|
let component: RepositoriesTabComponent;
|
||||||
let fixture: ComponentFixture<RepositoriesTabComponent>;
|
let fixture: ComponentFixture<RepositoriesTabComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [RepositoriesTabComponent]
|
declarations: [RepositoriesTabComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(RepositoriesTabComponent);
|
fixture = TestBed.createComponent(RepositoriesTabComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,37 +1,37 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from "@angular/core";
|
||||||
import {Repository} from "../../../models/Repository";
|
import {Repository} from "../../../models/Repository";
|
||||||
import {RepositoryService} from "../../../services/repository/repository.service";
|
import {RepositoryService} from "../../../services/repository/repository.service";
|
||||||
import {MatDialog} from "@angular/material/dialog";
|
import {MatDialog} from "@angular/material/dialog";
|
||||||
import {AddRepositoryDialogComponent} from "./add-repository-dialog/add-repository-dialog.component";
|
import {AddRepositoryDialogComponent} from "./add-repository-dialog/add-repository-dialog.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-repositories-tab',
|
selector: "app-repositories-tab",
|
||||||
templateUrl: './repositories-tab.component.html',
|
templateUrl: "./repositories-tab.component.html",
|
||||||
styleUrls: ['./repositories-tab.component.scss']
|
styleUrls: ["./repositories-tab.component.scss"]
|
||||||
})
|
})
|
||||||
export class RepositoriesTabComponent implements OnInit {
|
export class RepositoriesTabComponent implements OnInit {
|
||||||
|
|
||||||
repositories: Repository[] = [];
|
repositories: Repository[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private repoService: RepositoryService,
|
private repoService: RepositoryService,
|
||||||
public dialog: MatDialog
|
public dialog: MatDialog
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.repoService.repositories.subscribe({
|
this.repoService.repositories.subscribe({
|
||||||
next: (repos) => {
|
next: (repos) => {
|
||||||
this.repositories = repos;
|
this.repositories = repos;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public openAddRepositoryDialog() {
|
public openAddRepositoryDialog() {
|
||||||
this.dialog.open(AddRepositoryDialogComponent, {
|
this.dialog.open(AddRepositoryDialogComponent, {
|
||||||
disableClose: true,
|
disableClose: true,
|
||||||
minWidth: "30%",
|
minWidth: "30%",
|
||||||
minHeight: "30%",
|
minHeight: "30%",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,37 @@
|
|||||||
<app-busy-indicator [darkenBackground]="true">
|
<app-busy-indicator [darkenBackground]="true">
|
||||||
<mat-card>
|
<mat-card>
|
||||||
<mat-card-title>{{repository.name}}</mat-card-title>
|
<mat-card-title>{{repository.name}}</mat-card-title>
|
||||||
<div [class]="'repository-status ' + this.getDaemonStatusClass()">
|
<div [class]="'repository-status ' + this.getDaemonStatusClass()">
|
||||||
<p>{{this.getDaemonStatusText()}}</p>
|
<p>{{this.getDaemonStatusText()}}</p>
|
||||||
</div>
|
</div>
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<p *ngIf="repository.local" class="repository-path">{{repository.path!}}</p>
|
<p *ngIf="repository.local" class="repository-path">{{repository.path!}}</p>
|
||||||
<p *ngIf="!repository.local" class="repository-address">{{repository.address}}</p>
|
<p *ngIf="!repository.local" class="repository-address">{{repository.address}}</p>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
<mat-action-list>
|
<mat-action-list>
|
||||||
<button (click)="startDaemonAndSelectRepository()" *ngIf="!this.isSelectedRepository() && repository.local"
|
<button (click)="startDaemonAndSelectRepository()" *ngIf="!this.isSelectedRepository() && repository.local"
|
||||||
color="primary"
|
color="primary"
|
||||||
mat-flat-button>Open
|
mat-flat-button>Open
|
||||||
</button>
|
</button>
|
||||||
<button (click)="selectRepository()" *ngIf="!this.isSelectedRepository() && !repository.local"
|
<button (click)="selectRepository()" *ngIf="!this.isSelectedRepository() && !repository.local"
|
||||||
[disabled]="!this.daemonRunning"
|
[disabled]="!this.daemonRunning"
|
||||||
color="primary" mat-flat-button>Connect
|
color="primary" mat-flat-button>Connect
|
||||||
</button>
|
</button>
|
||||||
<button (click)="this.repoService.closeSelectedRepository()"
|
<button (click)="this.repoService.closeSelectedRepository()"
|
||||||
*ngIf="this.isSelectedRepository() && repository.local" color="primary"
|
*ngIf="this.isSelectedRepository() && repository.local" color="primary"
|
||||||
mat-flat-button>Close
|
mat-flat-button>Close
|
||||||
</button>
|
</button>
|
||||||
<button (click)="this.repoService.disconnectSelectedRepository()"
|
<button (click)="this.repoService.disconnectSelectedRepository()"
|
||||||
*ngIf="this.isSelectedRepository() && !repository.local" color="primary"
|
*ngIf="this.isSelectedRepository() && !repository.local" color="primary"
|
||||||
mat-flat-button>Disconnect
|
mat-flat-button>Disconnect
|
||||||
</button>
|
</button>
|
||||||
<button [mat-menu-trigger-for]="menu" class="menu-button" mat-button>
|
<button [mat-menu-trigger-for]="menu" class="menu-button" mat-button>
|
||||||
<ng-icon name="mat-more-vert"></ng-icon>
|
<ng-icon name="mat-more-vert"></ng-icon>
|
||||||
</button>
|
</button>
|
||||||
<mat-menu #menu="matMenu">
|
<mat-menu #menu="matMenu">
|
||||||
<button (click)="removeRepository()" *ngIf="repository.local" mat-menu-item>Delete</button>
|
<button (click)="removeRepository()" *ngIf="repository.local" mat-menu-item>Delete</button>
|
||||||
<button (click)="removeRepository()" *ngIf="!repository.local" mat-menu-item>Remove</button>
|
<button (click)="removeRepository()" *ngIf="!repository.local" mat-menu-item>Remove</button>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
</mat-action-list>
|
</mat-action-list>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
</app-busy-indicator>
|
</app-busy-indicator>
|
||||||
|
@ -1,40 +1,40 @@
|
|||||||
@import "../../../../../styles";
|
@import "../../../../../styles";
|
||||||
|
|
||||||
.repository-path {
|
.repository-path {
|
||||||
color: lightgray;
|
color: lightgray;
|
||||||
}
|
}
|
||||||
|
|
||||||
.repository-address {
|
.repository-address {
|
||||||
color: lightgray;
|
color: lightgray;
|
||||||
font-family: "Fira Code Light", Monospaced, Consolas, monospace;
|
font-family: "Fira Code Light", Monospaced, Consolas, monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.repository-status {
|
.repository-status {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
width: 3em;
|
width: 3em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-bottom-left-radius: 1em;
|
border-bottom-left-radius: 1em;
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-local {
|
.status-local {
|
||||||
background-color: #2e237a;
|
background-color: #2e237a;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-offline {
|
.status-offline {
|
||||||
background-color: #8e1e2a;
|
background-color: #8e1e2a;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-online {
|
.status-online {
|
||||||
background-color: #1b651b;
|
background-color: #1b651b;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.menu-button {
|
button.menu-button {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {RepositoryCardComponent} from './repository-card.component';
|
import {RepositoryCardComponent} from "./repository-card.component";
|
||||||
|
|
||||||
describe('RepositoryCardComponent', () => {
|
describe("RepositoryCardComponent", () => {
|
||||||
let component: RepositoryCardComponent;
|
let component: RepositoryCardComponent;
|
||||||
let fixture: ComponentFixture<RepositoryCardComponent>;
|
let fixture: ComponentFixture<RepositoryCardComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [RepositoryCardComponent]
|
declarations: [RepositoryCardComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(RepositoryCardComponent);
|
fixture = TestBed.createComponent(RepositoryCardComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
<div *ngIf="this.busy" [class.blur]="this.blurBackground" [class.darken]="this.darkenBackground"
|
<div *ngIf="this.busy" [class.blur]="this.blurBackground" [class.darken]="this.darkenBackground"
|
||||||
class="busy-indicator-overlay">
|
class="busy-indicator-overlay">
|
||||||
<mat-progress-spinner [mode]="mode" [value]="value" color="primary"></mat-progress-spinner>
|
<mat-progress-spinner [mode]="mode" [value]="value" color="primary"></mat-progress-spinner>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,31 +1,31 @@
|
|||||||
.busy-indicator-overlay {
|
.busy-indicator-overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
z-index: 998;
|
z-index: 998;
|
||||||
|
|
||||||
mat-progress-spinner {
|
mat-progress-spinner {
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.busy-indicator-overlay.blur {
|
.busy-indicator-overlay.blur {
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
backdrop-filter: blur(5px);
|
backdrop-filter: blur(5px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.busy-indicator-overlay.darken {
|
.busy-indicator-overlay.darken {
|
||||||
background-color: rgba(0, 0, 0, 0.2);
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
::ng-deep app-busy-indicator {
|
::ng-deep app-busy-indicator {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {BusyIndicatorComponent} from './busy-indicator.component';
|
import {BusyIndicatorComponent} from "./busy-indicator.component";
|
||||||
|
|
||||||
describe('BusyIndicatorComponent', () => {
|
describe("BusyIndicatorComponent", () => {
|
||||||
let component: BusyIndicatorComponent;
|
let component: BusyIndicatorComponent;
|
||||||
let fixture: ComponentFixture<BusyIndicatorComponent>;
|
let fixture: ComponentFixture<BusyIndicatorComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [BusyIndicatorComponent]
|
declarations: [BusyIndicatorComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(BusyIndicatorComponent);
|
fixture = TestBed.createComponent(BusyIndicatorComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,49 +1,49 @@
|
|||||||
import {Component, Input} from '@angular/core';
|
import {Component, Input} from "@angular/core";
|
||||||
import {ProgressSpinnerMode} from "@angular/material/progress-spinner";
|
import {ProgressSpinnerMode} from "@angular/material/progress-spinner";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-busy-indicator',
|
selector: "app-busy-indicator",
|
||||||
templateUrl: './busy-indicator.component.html',
|
templateUrl: "./busy-indicator.component.html",
|
||||||
styleUrls: ['./busy-indicator.component.scss']
|
styleUrls: ["./busy-indicator.component.scss"]
|
||||||
})
|
})
|
||||||
export class BusyIndicatorComponent {
|
export class BusyIndicatorComponent {
|
||||||
|
|
||||||
@Input() busy: boolean = false;
|
@Input() busy: boolean = false;
|
||||||
@Input() blurBackground: boolean = false;
|
@Input() blurBackground: boolean = false;
|
||||||
@Input() darkenBackground: boolean = false;
|
@Input() darkenBackground: boolean = false;
|
||||||
@Input() mode: ProgressSpinnerMode = "indeterminate";
|
@Input() mode: ProgressSpinnerMode = "indeterminate";
|
||||||
@Input() value: number | undefined;
|
@Input() value: number | undefined;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public setBusy(busy: boolean) {
|
public setBusy(busy: boolean) {
|
||||||
this.busy = busy;
|
this.busy = busy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public wrapOperation<T>(operation: Function): T | undefined {
|
public wrapOperation<T>(operation: Function): T | undefined {
|
||||||
this.setBusy(true)
|
this.setBusy(true)
|
||||||
try {
|
try {
|
||||||
const result = operation();
|
const result = operation();
|
||||||
this.setBusy(false);
|
this.setBusy(false);
|
||||||
return result;
|
return result;
|
||||||
} catch {
|
} catch {
|
||||||
return undefined;
|
return undefined;
|
||||||
} finally {
|
} finally {
|
||||||
this.setBusy(false);
|
this.setBusy(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public async wrapAsyncOperation<T>(operation: Function): Promise<T | undefined> {
|
public async wrapAsyncOperation<T>(operation: Function): Promise<T | undefined> {
|
||||||
this.setBusy(true)
|
this.setBusy(true)
|
||||||
try {
|
try {
|
||||||
const result = await operation();
|
const result = await operation();
|
||||||
this.setBusy(false);
|
this.setBusy(false);
|
||||||
return result;
|
return result;
|
||||||
} catch {
|
} catch {
|
||||||
return undefined;
|
return undefined;
|
||||||
} finally {
|
} finally {
|
||||||
this.setBusy(false);
|
this.setBusy(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<h1 mat-dialog-title>
|
<h1 mat-dialog-title>
|
||||||
{{title}}
|
{{title}}
|
||||||
</h1>
|
</h1>
|
||||||
<div mat-dialog-content>
|
<div mat-dialog-content>
|
||||||
{{message}}
|
{{message}}
|
||||||
</div>
|
</div>
|
||||||
<div class="confirm-dialog-actions" mat-dialog-actions>
|
<div class="confirm-dialog-actions" mat-dialog-actions>
|
||||||
<button (click)="closeDialog(false)" [color]="this.denyColor" mat-stroked-button>{{denyAction}}</button>
|
<button (click)="closeDialog(false)" [color]="this.denyColor" mat-stroked-button>{{denyAction}}</button>
|
||||||
<button (click)="closeDialog(true)" [color]="this.confirmColor" mat-flat-button>{{confirmAction}}</button>
|
<button (click)="closeDialog(true)" [color]="this.confirmColor" mat-flat-button>{{confirmAction}}</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
.confirm-dialog-actions {
|
.confirm-dialog-actions {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
button {
|
button {
|
||||||
float: right;
|
float: right;
|
||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {ConfirmDialogComponent} from './confirm-dialog.component';
|
import {ConfirmDialogComponent} from "./confirm-dialog.component";
|
||||||
|
|
||||||
describe('ConfirmDialogComponent', () => {
|
describe("ConfirmDialogComponent", () => {
|
||||||
let component: ConfirmDialogComponent;
|
let component: ConfirmDialogComponent;
|
||||||
let fixture: ComponentFixture<ConfirmDialogComponent>;
|
let fixture: ComponentFixture<ConfirmDialogComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [ConfirmDialogComponent]
|
declarations: [ConfirmDialogComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ConfirmDialogComponent);
|
fixture = TestBed.createComponent(ConfirmDialogComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,35 +1,35 @@
|
|||||||
import {Component, Inject} from '@angular/core';
|
import {Component, Inject} from "@angular/core";
|
||||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||||
import {ThemePalette} from "@angular/material/core";
|
import {ThemePalette} from "@angular/material/core";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-confirm-dialog',
|
selector: "app-confirm-dialog",
|
||||||
templateUrl: './confirm-dialog.component.html',
|
templateUrl: "./confirm-dialog.component.html",
|
||||||
styleUrls: ['./confirm-dialog.component.scss']
|
styleUrls: ["./confirm-dialog.component.scss"]
|
||||||
})
|
})
|
||||||
export class ConfirmDialogComponent {
|
export class ConfirmDialogComponent {
|
||||||
|
|
||||||
title = "";
|
title = "";
|
||||||
message = "";
|
message = "";
|
||||||
confirmAction = "";
|
confirmAction = "";
|
||||||
confirmColor: ThemePalette = "primary";
|
confirmColor: ThemePalette = "primary";
|
||||||
denyAction = "Cancel";
|
denyAction = "Cancel";
|
||||||
denyColor: ThemePalette = "accent";
|
denyColor: ThemePalette = "accent";
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public dialogRef: MatDialogRef<ConfirmDialogComponent>,
|
public dialogRef: MatDialogRef<ConfirmDialogComponent>,
|
||||||
@Inject(
|
@Inject(
|
||||||
MAT_DIALOG_DATA) data: { title: string, message: string, confirmAction: string, denyAction?: string, confirmColor?: ThemePalette, denyColor?: ThemePalette }
|
MAT_DIALOG_DATA) data: { title: string, message: string, confirmAction: string, denyAction?: string, confirmColor?: ThemePalette, denyColor?: ThemePalette }
|
||||||
) {
|
) {
|
||||||
this.title = data.title;
|
this.title = data.title;
|
||||||
this.message = data.message;
|
this.message = data.message;
|
||||||
this.confirmAction = data.confirmAction;
|
this.confirmAction = data.confirmAction;
|
||||||
this.denyAction = data.denyAction ?? this.denyAction;
|
this.denyAction = data.denyAction ?? this.denyAction;
|
||||||
this.confirmColor = data.confirmColor ?? this.confirmColor;
|
this.confirmColor = data.confirmColor ?? this.confirmColor;
|
||||||
this.denyColor = data.denyColor ?? this.denyColor;
|
this.denyColor = data.denyColor ?? this.denyColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public closeDialog(result: boolean) {
|
public closeDialog(result: boolean) {
|
||||||
this.dialogRef.close(result);
|
this.dialogRef.close(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div #imageContainer (window:resize)="this.adjustSize(image, imageContainer)" class="image-container">
|
<div #imageContainer (window:resize)="this.adjustSize(image, imageContainer)" class="image-container">
|
||||||
<img #image (load)="this.adjustSize(image, imageContainer)" [class.scale-height]="(!scaleWidth) && maximizeHeight"
|
<img #image (load)="this.adjustSize(image, imageContainer)" [class.scale-height]="(!scaleWidth) && maximizeHeight"
|
||||||
[class.scale-width]="scaleWidth && maximizeWidth" [src]="this.imageSrc"
|
[class.scale-width]="scaleWidth && maximizeWidth" [src]="this.imageSrc"
|
||||||
[style]="{borderRadius: this.borderRadius}" alt="">
|
[style]="{borderRadius: this.borderRadius}" alt="">
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
.image-container {
|
.image-container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
img.scale-height {
|
img.scale-height {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
img.scale-width {
|
img.scale-width {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {ContentAwareImageComponent} from './content-aware-image.component';
|
import {ContentAwareImageComponent} from "./content-aware-image.component";
|
||||||
|
|
||||||
describe('ContentAwareImageComponent', () => {
|
describe("ContentAwareImageComponent", () => {
|
||||||
let component: ContentAwareImageComponent;
|
let component: ContentAwareImageComponent;
|
||||||
let fixture: ComponentFixture<ContentAwareImageComponent>;
|
let fixture: ComponentFixture<ContentAwareImageComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [ContentAwareImageComponent]
|
declarations: [ContentAwareImageComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ContentAwareImageComponent);
|
fixture = TestBed.createComponent(ContentAwareImageComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,42 +1,42 @@
|
|||||||
import {Component, ElementRef, Input, OnInit, ViewChild} from '@angular/core';
|
import {Component, ElementRef, Input, OnInit, ViewChild} from "@angular/core";
|
||||||
import {SafeResourceUrl} from "@angular/platform-browser";
|
import {SafeResourceUrl} from "@angular/platform-browser";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-content-aware-image',
|
selector: "app-content-aware-image",
|
||||||
templateUrl: './content-aware-image.component.html',
|
templateUrl: "./content-aware-image.component.html",
|
||||||
styleUrls: ['./content-aware-image.component.scss']
|
styleUrls: ["./content-aware-image.component.scss"]
|
||||||
})
|
})
|
||||||
export class ContentAwareImageComponent implements OnInit {
|
export class ContentAwareImageComponent implements OnInit {
|
||||||
|
|
||||||
@Input() imageSrc!: string | SafeResourceUrl;
|
@Input() imageSrc!: string | SafeResourceUrl;
|
||||||
@Input() maximizeHeight: boolean = true;
|
@Input() maximizeHeight: boolean = true;
|
||||||
@Input() maximizeWidth: boolean = true;
|
@Input() maximizeWidth: boolean = true;
|
||||||
@Input() borderRadius: string | undefined;
|
@Input() borderRadius: string | undefined;
|
||||||
@Input() decoding: "async" | "sync" | "auto" = "auto";
|
@Input() decoding: "async" | "sync" | "auto" = "auto";
|
||||||
|
|
||||||
@ViewChild("image") image: ElementRef<HTMLImageElement> | undefined;
|
@ViewChild("image") image: ElementRef<HTMLImageElement> | undefined;
|
||||||
|
|
||||||
scaleWidth = false;
|
scaleWidth = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
if (this.image) {
|
if (this.image) {
|
||||||
this.image.nativeElement.decoding = this.decoding;
|
this.image.nativeElement.decoding = this.decoding;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fits the image into the container
|
* Fits the image into the container
|
||||||
* @param {HTMLImageElement} image
|
* @param {HTMLImageElement} image
|
||||||
* @param {HTMLDivElement} imageContainer
|
* @param {HTMLDivElement} imageContainer
|
||||||
*/
|
*/
|
||||||
public adjustSize(image: HTMLImageElement, imageContainer: HTMLDivElement): void {
|
public adjustSize(image: HTMLImageElement, imageContainer: HTMLDivElement): void {
|
||||||
const containerHeight = Math.abs(imageContainer.clientHeight);
|
const containerHeight = Math.abs(imageContainer.clientHeight);
|
||||||
const containerWidth = Math.abs(imageContainer.clientWidth);
|
const containerWidth = Math.abs(imageContainer.clientWidth);
|
||||||
const imageRelativeHeight = image.naturalHeight / containerHeight;
|
const imageRelativeHeight = image.naturalHeight / containerHeight;
|
||||||
const imageRelativeWidth = image.naturalWidth / containerWidth;
|
const imageRelativeWidth = image.naturalWidth / containerWidth;
|
||||||
this.scaleWidth = imageRelativeWidth > imageRelativeHeight;
|
this.scaleWidth = imageRelativeWidth > imageRelativeHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<div [matMenuTriggerFor]="contextMenu" [style.left]="x" [style.top]="y" class="menu-anchor"></div>
|
<div [matMenuTriggerFor]="contextMenu" [style.left]="x" [style.top]="y" class="menu-anchor"></div>
|
||||||
<mat-menu #contextMenu="matMenu">
|
<mat-menu #contextMenu="matMenu">
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
.menu-anchor {
|
.menu-anchor {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {ContextMenuComponent} from './context-menu.component';
|
import {ContextMenuComponent} from "./context-menu.component";
|
||||||
|
|
||||||
describe('ContextMenuComponent', () => {
|
describe("ContextMenuComponent", () => {
|
||||||
let component: ContextMenuComponent;
|
let component: ContextMenuComponent;
|
||||||
let fixture: ComponentFixture<ContextMenuComponent>;
|
let fixture: ComponentFixture<ContextMenuComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [ContextMenuComponent]
|
declarations: [ContextMenuComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ContextMenuComponent);
|
fixture = TestBed.createComponent(ContextMenuComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
import {Component, ViewChild,} from '@angular/core';
|
import {Component, ViewChild,} from "@angular/core";
|
||||||
import {MatMenuTrigger} from "@angular/material/menu";
|
import {MatMenuTrigger} from "@angular/material/menu";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-context-menu',
|
selector: "app-context-menu",
|
||||||
templateUrl: './context-menu.component.html',
|
templateUrl: "./context-menu.component.html",
|
||||||
styleUrls: ['./context-menu.component.scss']
|
styleUrls: ["./context-menu.component.scss"]
|
||||||
})
|
})
|
||||||
export class ContextMenuComponent {
|
export class ContextMenuComponent {
|
||||||
|
|
||||||
public x: string = "0";
|
public x: string = "0";
|
||||||
public y: string = "0";
|
public y: string = "0";
|
||||||
|
|
||||||
@ViewChild(MatMenuTrigger) menuTrigger!: MatMenuTrigger
|
@ViewChild(MatMenuTrigger) menuTrigger!: MatMenuTrigger
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public onContextMenu(event: MouseEvent) {
|
public onContextMenu(event: MouseEvent) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.x = event.clientX + "px";
|
this.x = event.clientX + "px";
|
||||||
this.y = event.clientY + "px";
|
this.y = event.clientY + "px";
|
||||||
this.menuTrigger.menu.focusFirstItem("mouse");
|
this.menuTrigger.menu.focusFirstItem("mouse");
|
||||||
this.menuTrigger.openMenu();
|
this.menuTrigger.openMenu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<app-context-menu #contextMenu>
|
<app-context-menu #contextMenu>
|
||||||
<button (click)="this.copyFileHash()" mat-menu-item>Copy Hash</button>
|
<button (click)="this.copyFileHash()" mat-menu-item>Copy Hash</button>
|
||||||
<button (click)="this.exportFile()" mat-menu-item>Save As...</button>
|
<button (click)="this.exportFile()" mat-menu-item>Save As...</button>
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
</app-context-menu>
|
</app-context-menu>
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {FileContextMenuComponent} from './file-context-menu.component';
|
import {FileContextMenuComponent} from "./file-context-menu.component";
|
||||||
|
|
||||||
describe('FileContextMenuComponent', () => {
|
describe("FileContextMenuComponent", () => {
|
||||||
let component: FileContextMenuComponent;
|
let component: FileContextMenuComponent;
|
||||||
let fixture: ComponentFixture<FileContextMenuComponent>;
|
let fixture: ComponentFixture<FileContextMenuComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [FileContextMenuComponent]
|
declarations: [FileContextMenuComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(FileContextMenuComponent);
|
fixture = TestBed.createComponent(FileContextMenuComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,39 +1,39 @@
|
|||||||
<div class="file-edit-inner" fxLayout="column">
|
<div class="file-edit-inner" fxLayout="column">
|
||||||
<div class="file-metadata" fxFlex="150px">
|
<div class="file-metadata" fxFlex="150px">
|
||||||
<h1>Edit File</h1>
|
<h1>Edit File</h1>
|
||||||
<mat-form-field *ngIf="this.files.length === 1" appearance="fill">
|
<mat-form-field *ngIf="this.files.length === 1" appearance="fill">
|
||||||
<mat-label>Name</mat-label>
|
<mat-label>Name</mat-label>
|
||||||
<input #fileNameInput (focusout)="this.changeFileName(fileNameInput.value)" matInput>
|
<input #fileNameInput (focusout)="this.changeFileName(fileNameInput.value)" matInput>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div class="tag-edit-list" fxFlex fxFlexAlign="start" fxFlexFill>
|
<div class="tag-edit-list" fxFlex fxFlexAlign="start" fxFlexFill>
|
||||||
<cdk-virtual-scroll-viewport #tagScroll itemSize="50" maxBufferPx="2000" minBufferPx="1000">
|
<cdk-virtual-scroll-viewport #tagScroll itemSize="50" maxBufferPx="2000" minBufferPx="1000">
|
||||||
<div *cdkVirtualFor="let tag of tags" class="editable-tag">
|
<div *cdkVirtualFor="let tag of tags" class="editable-tag">
|
||||||
<app-tag-item [tag]="tag"></app-tag-item>
|
<app-tag-item [tag]="tag"></app-tag-item>
|
||||||
<button (click)="removeTag(tag)" class="tag-remove-button" mat-icon-button>
|
<button (click)="removeTag(tag)" class="tag-remove-button" mat-icon-button>
|
||||||
<ng-icon name="mat-remove"></ng-icon>
|
<ng-icon name="mat-remove"></ng-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</cdk-virtual-scroll-viewport>
|
</cdk-virtual-scroll-viewport>
|
||||||
</div>
|
</div>
|
||||||
<mat-divider fxFlex="1em"></mat-divider>
|
<mat-divider fxFlex="1em"></mat-divider>
|
||||||
<div class="tag-input" fxFlex="200px">
|
<div class="tag-input" fxFlex="200px">
|
||||||
<div class="tag-input-field">
|
<div class="tag-input-field">
|
||||||
<app-tag-input (tagAdded)="this.editTag($event)" [allowInvalid]="true"
|
<app-tag-input (tagAdded)="this.editTag($event)" [allowInvalid]="true"
|
||||||
[availableTags]="this.allTags"></app-tag-input>
|
[availableTags]="this.allTags"></app-tag-input>
|
||||||
<button class="add-tag-button" mat-icon-button>
|
<button class="add-tag-button" mat-icon-button>
|
||||||
<ng-icon *ngIf="editMode === 'Toggle'" name="mat-change-circle"></ng-icon>
|
<ng-icon *ngIf="editMode === 'Toggle'" name="mat-change-circle"></ng-icon>
|
||||||
<ng-icon *ngIf="editMode === 'Add'" name="mat-add-circle"></ng-icon>
|
<ng-icon *ngIf="editMode === 'Add'" name="mat-add-circle"></ng-icon>
|
||||||
<ng-icon *ngIf="editMode === 'Remove'" name="mat-remove-circle"></ng-icon>
|
<ng-icon *ngIf="editMode === 'Remove'" name="mat-remove-circle"></ng-icon>
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
|
<mat-form-field appearance="fill" class="form-field-mode">
|
||||||
|
<mat-label>Mode</mat-label>
|
||||||
|
<mat-select [(value)]="editMode">
|
||||||
|
<mat-option value="Toggle">Toggle</mat-option>
|
||||||
|
<mat-option value="Add">Add</mat-option>
|
||||||
|
<mat-option value="Remove">Remove</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<mat-form-field appearance="fill" class="form-field-mode">
|
|
||||||
<mat-label>Mode</mat-label>
|
|
||||||
<mat-select [(value)]="editMode">
|
|
||||||
<mat-option value="Toggle">Toggle</mat-option>
|
|
||||||
<mat-option value="Add">Add</mat-option>
|
|
||||||
<mat-option value="Remove">Remove</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,69 +1,69 @@
|
|||||||
.file-metadata, .tag-input {
|
.file-metadata, .tag-input {
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
mat-form-field, app-tag-input {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
|
||||||
|
|
||||||
mat-form-field.form-field-mode {
|
mat-form-field, app-tag-input {
|
||||||
width: 10em;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mat-form-field.form-field-mode {
|
||||||
|
width: 10em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-edit-inner {
|
.file-edit-inner {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag-edit-list {
|
.tag-edit-list {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
cdk-virtual-scroll-viewport {
|
cdk-virtual-scroll-viewport {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
::ng-deep .cdk-virtual-scroll-content-wrapper {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
overflow-y: auto;
|
||||||
|
|
||||||
|
::ng-deep .cdk-virtual-scroll-content-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.editable-tag {
|
.editable-tag {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
transition-duration: 0.1s;
|
transition-duration: 0.1s;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
|
|
||||||
app-tag-item {
|
app-tag-item {
|
||||||
margin: auto auto auto 0.25em;
|
margin: auto auto auto 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag-remove-button {
|
.tag-remove-button {
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
width: 50px;
|
width: 50px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag-input-field {
|
.tag-input-field {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
||||||
.add-tag-button {
|
.add-tag-button {
|
||||||
width: 65px;
|
width: 65px;
|
||||||
height: 65px;
|
height: 65px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-divider {
|
mat-divider {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {FileEditComponent} from './file-edit.component';
|
import {FileEditComponent} from "./file-edit.component";
|
||||||
|
|
||||||
describe('FileEditComponent', () => {
|
describe("FileEditComponent", () => {
|
||||||
let component: FileEditComponent;
|
let component: FileEditComponent;
|
||||||
let fixture: ComponentFixture<FileEditComponent>;
|
let fixture: ComponentFixture<FileEditComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [FileEditComponent]
|
declarations: [FileEditComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(FileEditComponent);
|
fixture = TestBed.createComponent(FileEditComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<div class="audio-container">
|
<div class="audio-container">
|
||||||
<audio [src]="this.blobUrl" controls>
|
<audio [src]="this.blobUrl" controls>
|
||||||
</audio>
|
</audio>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
.audio-container {
|
.audio-container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
audio {
|
audio {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {AudioViewerComponent} from './audio-viewer.component';
|
import {AudioViewerComponent} from "./audio-viewer.component";
|
||||||
|
|
||||||
describe('AudioViewerComponent', () => {
|
describe("AudioViewerComponent", () => {
|
||||||
let component: AudioViewerComponent;
|
let component: AudioViewerComponent;
|
||||||
let fixture: ComponentFixture<AudioViewerComponent>;
|
let fixture: ComponentFixture<AudioViewerComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [AudioViewerComponent]
|
declarations: [AudioViewerComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(AudioViewerComponent);
|
fixture = TestBed.createComponent(AudioViewerComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import {Component, Input} from '@angular/core';
|
import {Component, Input} from "@angular/core";
|
||||||
import {SafeResourceUrl} from "@angular/platform-browser";
|
import {SafeResourceUrl} from "@angular/platform-browser";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-audio-viewer',
|
selector: "app-audio-viewer",
|
||||||
templateUrl: './audio-viewer.component.html',
|
templateUrl: "./audio-viewer.component.html",
|
||||||
styleUrls: ['./audio-viewer.component.scss']
|
styleUrls: ["./audio-viewer.component.scss"]
|
||||||
})
|
})
|
||||||
export class AudioViewerComponent {
|
export class AudioViewerComponent {
|
||||||
|
|
||||||
@Input() blobUrl!: SafeResourceUrl;
|
@Input() blobUrl!: SafeResourceUrl;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
app-image-viewer, app-video-viewer, app-audio-viewer {
|
app-image-viewer, app-video-viewer, app-audio-viewer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.download-prompt {
|
.download-prompt {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
button {
|
button {
|
||||||
margin: 1em 0 auto;
|
margin: 1em 0 auto;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
margin: auto 0 0;
|
margin: auto 0 0;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {ContentViewerComponent} from './content-viewer.component';
|
import {ContentViewerComponent} from "./content-viewer.component";
|
||||||
|
|
||||||
describe('ContentViewerComponent', () => {
|
describe("ContentViewerComponent", () => {
|
||||||
let component: ContentViewerComponent;
|
let component: ContentViewerComponent;
|
||||||
let fixture: ComponentFixture<ContentViewerComponent>;
|
let fixture: ComponentFixture<ContentViewerComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [ContentViewerComponent]
|
declarations: [ContentViewerComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ContentViewerComponent);
|
fixture = TestBed.createComponent(ContentViewerComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
<div (mouseenter)="this.mouseInImageView = true" (mouseleave)="this.mouseInImageView = false"
|
<div (mouseenter)="this.mouseInImageView = true" (mouseleave)="this.mouseInImageView = false"
|
||||||
class="image-full-view-inner">
|
class="image-full-view-inner">
|
||||||
<div class="zoom-slider">
|
<div class="zoom-slider">
|
||||||
<mat-slider (input)="this.imageZoom=$event.value ?? 1" [value]="this.imageZoom" max="4"
|
<mat-slider (input)="this.imageZoom=$event.value ?? 1" [value]="this.imageZoom" max="4"
|
||||||
min="0.5" step="0.1" vertical></mat-slider>
|
min="0.5" step="0.1" vertical></mat-slider>
|
||||||
<button (click)="this.resetImage()" mat-icon-button>
|
<button (click)="this.resetImage()" mat-icon-button>
|
||||||
<ng-icon name="mat-refresh"></ng-icon>
|
<ng-icon name="mat-refresh"></ng-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div (cdkDragMoved)="this.onDragMoved($event)"
|
<div (cdkDragMoved)="this.onDragMoved($event)"
|
||||||
[cdkDragFreeDragPosition]="this.imagePosition" cdkDrag class="image-drag-container">
|
[cdkDragFreeDragPosition]="this.imagePosition" cdkDrag class="image-drag-container">
|
||||||
<div [style]="{scale: this.imageZoom}" class="image-scale-container">
|
<div [style]="{scale: this.imageZoom}" class="image-scale-container">
|
||||||
<app-content-aware-image [imageSrc]="this.imageUrl" decoding="sync"></app-content-aware-image>
|
<app-content-aware-image [imageSrc]="this.imageUrl" decoding="sync"></app-content-aware-image>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,35 +1,35 @@
|
|||||||
|
|
||||||
.image-drag-container, .image-scale-container {
|
.image-drag-container, .image-scale-container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-scale-container {
|
.image-scale-container {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoom-slider {
|
.zoom-slider {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
right: 1em;
|
right: 1em;
|
||||||
bottom: 1em;
|
bottom: 1em;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
padding: 1em 0.5em;
|
padding: 1em 0.5em;
|
||||||
transition-duration: 0.2s;
|
transition-duration: 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoom-slider:hover {
|
.zoom-slider:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
background-color: rgba(0, 0, 0, 0.3);
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
border-radius: 1em;
|
border-radius: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.image-full-view-inner {
|
.image-full-view-inner {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {ImageViewerComponent} from './image-viewer.component';
|
import {ImageViewerComponent} from "./image-viewer.component";
|
||||||
|
|
||||||
describe('ImageViewerComponent', () => {
|
describe("ImageViewerComponent", () => {
|
||||||
let component: ImageViewerComponent;
|
let component: ImageViewerComponent;
|
||||||
let fixture: ComponentFixture<ImageViewerComponent>;
|
let fixture: ComponentFixture<ImageViewerComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [ImageViewerComponent]
|
declarations: [ImageViewerComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ImageViewerComponent);
|
fixture = TestBed.createComponent(ImageViewerComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,69 +1,69 @@
|
|||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
HostListener,
|
HostListener,
|
||||||
Input,
|
Input,
|
||||||
OnChanges,
|
OnChanges,
|
||||||
SimpleChanges
|
SimpleChanges
|
||||||
} from '@angular/core';
|
} from "@angular/core";
|
||||||
import {CdkDragMove} from "@angular/cdk/drag-drop";
|
import {CdkDragMove} from "@angular/cdk/drag-drop";
|
||||||
import {SafeResourceUrl} from "@angular/platform-browser";
|
import {SafeResourceUrl} from "@angular/platform-browser";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-image-viewer',
|
selector: "app-image-viewer",
|
||||||
templateUrl: './image-viewer.component.html',
|
templateUrl: "./image-viewer.component.html",
|
||||||
styleUrls: ['./image-viewer.component.scss']
|
styleUrls: ["./image-viewer.component.scss"]
|
||||||
})
|
})
|
||||||
export class ImageViewerComponent implements OnChanges {
|
export class ImageViewerComponent implements OnChanges {
|
||||||
@Input() imageUrl!: SafeResourceUrl | string;
|
@Input() imageUrl!: SafeResourceUrl | string;
|
||||||
public imageZoom = 1;
|
public imageZoom = 1;
|
||||||
public imagePosition = {x: 0, y: 0};
|
public imagePosition = {x: 0, y: 0};
|
||||||
public mouseInImageView = false;
|
public mouseInImageView = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnChanges(changes: SimpleChanges): void {
|
public ngOnChanges(changes: SimpleChanges): void {
|
||||||
if (changes["imageUrl"]) {
|
if (changes["imageUrl"]) {
|
||||||
this.resetImage();
|
this.resetImage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public resetImage() {
|
public resetImage() {
|
||||||
this.imageZoom = 1;
|
this.imageZoom = 1;
|
||||||
this.imagePosition = {x: 0, y: 0};
|
this.imagePosition = {x: 0, y: 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
public onDragMoved($event: CdkDragMove<HTMLDivElement>): void {
|
public onDragMoved($event: CdkDragMove<HTMLDivElement>): void {
|
||||||
this.imagePosition.x += $event.delta.x;
|
this.imagePosition.x += $event.delta.x;
|
||||||
this.imagePosition.y += $event.delta.y;
|
this.imagePosition.y += $event.delta.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener("window:keydown", ["$event"])
|
@HostListener("window:keydown", ["$event"])
|
||||||
private async handleKeydownEvent(event: KeyboardEvent) {
|
private async handleKeydownEvent(event: KeyboardEvent) {
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
case "Escape":
|
case "Escape":
|
||||||
this.resetImage();
|
this.resetImage();
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@HostListener("mousewheel", ["$event"])
|
@HostListener("mousewheel", ["$event"])
|
||||||
private handleScroll(event: any) {
|
private handleScroll(event: any) {
|
||||||
if (this.mouseInImageView) {
|
if (this.mouseInImageView) {
|
||||||
const delta = event.wheelDelta ?? event.detail;
|
const delta = event.wheelDelta ?? event.detail;
|
||||||
|
|
||||||
if (delta > 0) {
|
if (delta > 0) {
|
||||||
this.imageZoom += 0.2
|
this.imageZoom += 0.2
|
||||||
if (this.imageZoom > 4) {
|
if (this.imageZoom > 4) {
|
||||||
this.imageZoom = 4;
|
this.imageZoom = 4;
|
||||||
}
|
}
|
||||||
} else if (delta < 0) {
|
} else if (delta < 0) {
|
||||||
this.imageZoom -= 0.2
|
this.imageZoom -= 0.2
|
||||||
if (this.imageZoom < 0.5) {
|
if (this.imageZoom < 0.5) {
|
||||||
this.imageZoom = 0.5;
|
this.imageZoom = 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
<video [src]="this.blobUrl" controls>
|
<video [src]="this.blobUrl" controls>
|
||||||
Unsupported video type
|
Unsupported video type
|
||||||
</video>
|
</video>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
video {
|
video {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {VideoViewerComponent} from './video-viewer.component';
|
import {VideoViewerComponent} from "./video-viewer.component";
|
||||||
|
|
||||||
describe('VideoViewerComponent', () => {
|
describe("VideoViewerComponent", () => {
|
||||||
let component: VideoViewerComponent;
|
let component: VideoViewerComponent;
|
||||||
let fixture: ComponentFixture<VideoViewerComponent>;
|
let fixture: ComponentFixture<VideoViewerComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [VideoViewerComponent]
|
declarations: [VideoViewerComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(VideoViewerComponent);
|
fixture = TestBed.createComponent(VideoViewerComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import {Component, Input,} from '@angular/core';
|
import {Component, Input,} from "@angular/core";
|
||||||
import {SafeResourceUrl} from "@angular/platform-browser";
|
import {SafeResourceUrl} from "@angular/platform-browser";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-video-viewer',
|
selector: "app-video-viewer",
|
||||||
templateUrl: './video-viewer.component.html',
|
templateUrl: "./video-viewer.component.html",
|
||||||
styleUrls: ['./video-viewer.component.scss']
|
styleUrls: ["./video-viewer.component.scss"]
|
||||||
})
|
})
|
||||||
export class VideoViewerComponent {
|
export class VideoViewerComponent {
|
||||||
@Input() blobUrl!: SafeResourceUrl;
|
@Input() blobUrl!: SafeResourceUrl;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<div (click)="fileSelectEvent.emit(this.file)" [class.selected]="this.file.selected" class="image-wrapper">
|
<div (click)="fileSelectEvent.emit(this.file)" [class.selected]="this.file.selected" class="image-wrapper">
|
||||||
<mat-progress-spinner *ngIf="!contentUrl"></mat-progress-spinner>
|
<mat-progress-spinner *ngIf="!contentUrl"></mat-progress-spinner>
|
||||||
<app-file-thumbnail [file]="file.data"></app-file-thumbnail>
|
<app-file-thumbnail [file]="file.data"></app-file-thumbnail>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
app-file-thumbnail {
|
app-file-thumbnail {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-wrapper {
|
.image-wrapper {
|
||||||
width: calc(100% - 20px);
|
width: calc(100% - 20px);
|
||||||
height: calc(100% - 20px);
|
height: calc(100% - 20px);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: darken(dimgrey, 15);
|
background-color: darken(dimgrey, 15);
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {FileGalleryEntryComponent} from './file-gallery-entry.component';
|
import {FileGalleryEntryComponent} from "./file-gallery-entry.component";
|
||||||
|
|
||||||
describe('FileGalleryEntryComponent', () => {
|
describe("FileGalleryEntryComponent", () => {
|
||||||
let component: FileGalleryEntryComponent;
|
let component: FileGalleryEntryComponent;
|
||||||
let fixture: ComponentFixture<FileGalleryEntryComponent>;
|
let fixture: ComponentFixture<FileGalleryEntryComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [FileGalleryEntryComponent]
|
declarations: [FileGalleryEntryComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(FileGalleryEntryComponent);
|
fixture = TestBed.createComponent(FileGalleryEntryComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,21 +1,23 @@
|
|||||||
<div class="gallery-container" fxLayout="column">
|
<div class="gallery-container" fxLayout="column">
|
||||||
<button (click)="this.closeEvent.emit(this)" class="close-button" mat-icon-button>
|
<button (click)="this.closeEvent.emit(this)" class="close-button" mat-icon-button>
|
||||||
<ng-icon name="mat-close"></ng-icon>
|
<ng-icon name="mat-close"></ng-icon>
|
||||||
</button>
|
</button>
|
||||||
<div (dblclick)="this.selectedFile? this.fileDblClickEvent.emit(this.selectedFile.data) : null" class="file-full-view"
|
<div (dblclick)="this.selectedFile? this.fileDblClickEvent.emit(this.selectedFile.data) : null"
|
||||||
fxFlex="80%">
|
class="file-full-view"
|
||||||
<app-content-viewer
|
fxFlex="80%">
|
||||||
(contextmenu)="this.selectedFile && fileContextMenu.onContextMenu($event, this.selectedFile!.data)"
|
<app-content-viewer
|
||||||
[file]="this.selectedFile!.data"></app-content-viewer>
|
(contextmenu)="this.selectedFile && fileContextMenu.onContextMenu($event, this.selectedFile!.data)"
|
||||||
</div>
|
[file]="this.selectedFile!.data"></app-content-viewer>
|
||||||
<mat-divider fxFlex></mat-divider>
|
</div>
|
||||||
<div class="file-scroll-view" fxFlex="20%">
|
<mat-divider fxFlex></mat-divider>
|
||||||
<cdk-virtual-scroll-viewport #virtualScroll class="file-scroll-viewport" itemSize="260" maxBufferPx="3000"
|
<div class="file-scroll-view" fxFlex="20%">
|
||||||
minBufferPx="1000" orientation="horizontal">
|
<cdk-virtual-scroll-viewport #virtualScroll class="file-scroll-viewport" itemSize="260" maxBufferPx="3000"
|
||||||
<div *cdkVirtualFor="let entry of entries" class="file-item">
|
minBufferPx="1000" orientation="horizontal">
|
||||||
<app-file-gallery-entry (fileSelectEvent)="onEntrySelect($event)" [file]="entry"></app-file-gallery-entry>
|
<div *cdkVirtualFor="let entry of entries" class="file-item">
|
||||||
</div>
|
<app-file-gallery-entry (fileSelectEvent)="onEntrySelect($event)"
|
||||||
</cdk-virtual-scroll-viewport>
|
[file]="entry"></app-file-gallery-entry>
|
||||||
</div>
|
</div>
|
||||||
|
</cdk-virtual-scroll-viewport>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<app-file-context-menu #fileContextMenu></app-file-context-menu>
|
<app-file-context-menu #fileContextMenu></app-file-context-menu>
|
||||||
|
@ -1,62 +1,62 @@
|
|||||||
.file-scroll-viewport {
|
.file-scroll-viewport {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
::ng-deep .file-scroll-viewport > .cdk-virtual-scroll-content-wrapper {
|
::ng-deep .file-scroll-viewport > .cdk-virtual-scroll-content-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gallery-container {
|
.gallery-container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
app-file-gallery-entry, .file-item {
|
app-file-gallery-entry, .file-item {
|
||||||
width: 250px;
|
width: 250px;
|
||||||
height: calc(100% - 10px);
|
height: calc(100% - 10px);
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
app-file-gallery-entry {
|
app-file-gallery-entry {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-full-view {
|
.file-full-view {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
app-content-viewer {
|
app-content-viewer {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.close-button {
|
.close-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
width: 3em;
|
width: 3em;
|
||||||
height: 3em;
|
height: 3em;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.url-loading-backdrop {
|
.url-loading-backdrop {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
mat-progress-spinner {
|
mat-progress-spinner {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {FileGalleryComponent} from './file-gallery.component';
|
import {FileGalleryComponent} from "./file-gallery.component";
|
||||||
|
|
||||||
describe('FileGalleryComponent', () => {
|
describe("FileGalleryComponent", () => {
|
||||||
let component: FileGalleryComponent;
|
let component: FileGalleryComponent;
|
||||||
let fixture: ComponentFixture<FileGalleryComponent>;
|
let fixture: ComponentFixture<FileGalleryComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [FileGalleryComponent]
|
declarations: [FileGalleryComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(FileGalleryComponent);
|
fixture = TestBed.createComponent(FileGalleryComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {File} from "../../../../../models/File";
|
import {File} from "../../../../../models/File";
|
||||||
|
|
||||||
export type GridEntry = {
|
export type GridEntry = {
|
||||||
file: File,
|
file: File,
|
||||||
selected: boolean,
|
selected: boolean,
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<mat-card #card (click)="clickEvent.emit(this)" (dblclick)="dblClickEvent.emit(this)"
|
<mat-card #card (click)="clickEvent.emit(this)" (dblclick)="dblClickEvent.emit(this)"
|
||||||
[ngClass]="{'selected': gridEntry.selected}">
|
[ngClass]="{'selected': gridEntry.selected}">
|
||||||
<mat-card-content *ngIf="contentUrl !== undefined">
|
<mat-card-content *ngIf="contentUrl !== undefined">
|
||||||
<app-file-thumbnail [file]="this.gridEntry.file" class=".entry-image"></app-file-thumbnail>
|
<app-file-thumbnail [file]="this.gridEntry.file" class=".entry-image"></app-file-thumbnail>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
mat-card {
|
mat-card {
|
||||||
height: calc(100% - 32px);
|
height: calc(100% - 32px);
|
||||||
width: calc(100% - 32px);
|
width: calc(100% - 32px);
|
||||||
user-select: none;
|
user-select: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-card-content {
|
mat-card-content {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entry-image {
|
.entry-image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from "@angular/core/testing";
|
||||||
|
|
||||||
import {FileGridEntryComponent} from './file-grid-entry.component';
|
import {FileGridEntryComponent} from "./file-grid-entry.component";
|
||||||
|
|
||||||
describe('FileGridEntryComponent', () => {
|
describe("FileGridEntryComponent", () => {
|
||||||
let component: FileGridEntryComponent;
|
let component: FileGridEntryComponent;
|
||||||
let fixture: ComponentFixture<FileGridEntryComponent>;
|
let fixture: ComponentFixture<FileGridEntryComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [FileGridEntryComponent]
|
declarations: [FileGridEntryComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(FileGridEntryComponent);
|
fixture = TestBed.createComponent(FileGridEntryComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it("should create", () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,57 +1,56 @@
|
|||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
ElementRef,
|
ElementRef,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
Input,
|
Input,
|
||||||
OnChanges,
|
OnChanges,
|
||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
SimpleChanges,
|
SimpleChanges,
|
||||||
ViewChild
|
ViewChild
|
||||||
} from '@angular/core';
|
} from "@angular/core";
|
||||||
import {File} from "../../../../../models/File";
|
import {File} from "../../../../../models/File";
|
||||||
import {FileService} from "../../../../../services/file/file.service";
|
import {FileService} from "../../../../../services/file/file.service";
|
||||||
import {ErrorBrokerService} from "../../../../../services/error-broker/error-broker.service";
|
|
||||||
import {SafeResourceUrl} from "@angular/platform-browser";
|
import {SafeResourceUrl} from "@angular/platform-browser";
|
||||||
import {GridEntry} from "./GridEntry";
|
import {GridEntry} from "./GridEntry";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-file-grid-entry',
|
selector: "app-file-grid-entry",
|
||||||
templateUrl: './file-grid-entry.component.html',
|
templateUrl: "./file-grid-entry.component.html",
|
||||||
styleUrls: ['./file-grid-entry.component.scss']
|
styleUrls: ["./file-grid-entry.component.scss"]
|
||||||
})
|
})
|
||||||
export class FileGridEntryComponent implements OnInit, OnChanges {
|
export class FileGridEntryComponent implements OnInit, OnChanges {
|
||||||
|
|
||||||
@ViewChild("card") card!: ElementRef;
|
@ViewChild("card") card!: ElementRef;
|
||||||
@Input() public gridEntry!: GridEntry;
|
@Input() public gridEntry!: GridEntry;
|
||||||
@Output() clickEvent = new EventEmitter<FileGridEntryComponent>();
|
@Output() clickEvent = new EventEmitter<FileGridEntryComponent>();
|
||||||
@Output() dblClickEvent = new EventEmitter<FileGridEntryComponent>();
|
@Output() dblClickEvent = new EventEmitter<FileGridEntryComponent>();
|
||||||
|
|
||||||
contentUrl: SafeResourceUrl | undefined;
|
contentUrl: SafeResourceUrl | undefined;
|
||||||
private cachedFile: File | undefined;
|
private cachedFile: File | undefined;
|
||||||
private urlSetTimeout: number | undefined;
|
private urlSetTimeout: number | undefined;
|
||||||
|
|
||||||
constructor(private fileService: FileService) {
|
constructor(private fileService: FileService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.cachedFile = this.gridEntry.file;
|
this.cachedFile = this.gridEntry.file;
|
||||||
this.setImageDelayed();
|
this.setImageDelayed();
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnChanges(changes: SimpleChanges) {
|
async ngOnChanges(changes: SimpleChanges) {
|
||||||
if (changes["file"] && (!this.cachedFile || this.gridEntry.file.hash !== this.cachedFile.hash)) {
|
if (changes["file"] && (!this.cachedFile || this.gridEntry.file.hash !== this.cachedFile.hash)) {
|
||||||
this.cachedFile = this.gridEntry.file;
|
this.cachedFile = this.gridEntry.file;
|
||||||
this.setImageDelayed();
|
this.setImageDelayed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private setImageDelayed() {
|
private setImageDelayed() {
|
||||||
this.contentUrl = undefined;
|
this.contentUrl = undefined;
|
||||||
clearTimeout(this.urlSetTimeout);
|
clearTimeout(this.urlSetTimeout);
|
||||||
this.urlSetTimeout = setTimeout(
|
this.urlSetTimeout = setTimeout(
|
||||||
() => this.contentUrl = this.fileService.buildThumbnailUrl(
|
() => this.contentUrl = this.fileService.buildThumbnailUrl(
|
||||||
this.gridEntry.file,
|
this.gridEntry.file,
|
||||||
250, 250), 200);
|
250, 250), 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
<div class="file-gallery-inner">
|
<div class="file-gallery-inner">
|
||||||
<cdk-virtual-scroll-viewport #virtualScrollGrid class="file-scroll" itemSize="250" maxBufferPx="2000"
|
<cdk-virtual-scroll-viewport #virtualScrollGrid class="file-scroll" itemSize="250" maxBufferPx="2000"
|
||||||
minBufferPx="500">
|
minBufferPx="500">
|
||||||
<div *cdkVirtualFor="let rowEntry of partitionedGridEntries">
|
<div *cdkVirtualFor="let rowEntry of partitionedGridEntries">
|
||||||
<div class="file-row">
|
<div class="file-row">
|
||||||
<app-file-grid-entry
|
<app-file-grid-entry
|
||||||
(clickEvent)="setSelectedFile($event.gridEntry)" (contextmenu)="fileContextMenu.onContextMenu($event, gridEntry.file)"
|
(clickEvent)="setSelectedFile($event.gridEntry)"
|
||||||
(dblClickEvent)="fileOpenEvent.emit($event.gridEntry.file)"
|
(contextmenu)="fileContextMenu.onContextMenu($event, gridEntry.file)"
|
||||||
*ngFor="let gridEntry of rowEntry"
|
(dblClickEvent)="fileOpenEvent.emit($event.gridEntry.file)"
|
||||||
[gridEntry]="gridEntry"></app-file-grid-entry>
|
*ngFor="let gridEntry of rowEntry"
|
||||||
</div>
|
[gridEntry]="gridEntry"></app-file-grid-entry>
|
||||||
</div>
|
</div>
|
||||||
</cdk-virtual-scroll-viewport>
|
</div>
|
||||||
|
</cdk-virtual-scroll-viewport>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<app-file-context-menu #fileContextMenu>
|
<app-file-context-menu #fileContextMenu>
|
||||||
<button (click)="this.regenerateThumbnail(fileContextMenu.file)" mat-menu-item>Regenerate thumbnail</button>
|
<button (click)="this.regenerateThumbnail(fileContextMenu.file)" mat-menu-item>Regenerate thumbnail</button>
|
||||||
</app-file-context-menu>
|
</app-file-context-menu>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue