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