Add multiple tabs of one type

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/4/head
trivernis 3 years ago
parent 481114052e
commit 20d527f195

@ -1,13 +1,33 @@
<div id="content">
<mat-tab-group #tabGroup (selectedTabChange)="this.onTabSelectionChange($event)">
<mat-tab-group #tabGroup (selectedTabChange)="this.onTabSelectionChange($event)" class="main-tab-group">
<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 *ngFor="let tab of tabs">
<ng-template mat-tab-label>
<div class="tab-label-div" (click)="this.onMouseClickTabLabel(tab, $event)">
{{tab.category}}
<button class="close-tab-button" mat-icon-button (click)="this.closeTab(tab)">
<ng-icon name="mat-close"></ng-icon>
</button>
</div>
</ng-template>
<app-files-tab *ngIf="tab.category === 'Files'" [state]="tab"></app-files-tab>
<app-import-tab *ngIf="tab.category === 'Import'" [state]="tab"></app-import-tab>
</mat-tab>
<mat-tab *ngIf="this.selectedRepository" label="Import">
<app-import-tab></app-import-tab>
<mat-tab *ngIf="this.newTab" label="New Tab">
<div class="new-tab-content">
Select the tab type
<button mat-flat-button (click)="this.addFilesTab()" color="primary">Files</button>
<button mat-flat-button (click)="this.addImportTab()" color="primary">Import</button>
</div>
</mat-tab>
<mat-tab disabled>
<ng-template mat-tab-label>
<button class="new-tab-button" mat-icon-button (click)="this.addTab()">
<ng-icon name="mat-plus"></ng-icon>
</button>
</ng-template>
</mat-tab>
</mat-tab-group>
</div>

@ -17,3 +17,43 @@ mat-tab-group {
height: 100%;
width: 100%;
}
::ng-deep .main-tab-group .mat-tab-label.mat-tab-disabled {
width: 2em;
min-width: 0;
color: white;
}
::ng-deep .main-tab-group .mat-tab-label {
ng-icon {
margin-top: -0.5em;
--ng-icon__size: 1em;
}
.close-tab-button {
float: right;
position: absolute;
right: 0;
top: 4px;
ng-icon {
font-size: 1.5em;
margin-top: -1.1em;
--ng-icon__size: 0.4em;
}
}
}
.new-tab-content {
display: block;
width: 100%;
text-align: center;
button {
display: flex;
margin: 1em auto auto;
}
}
.tab-label-div {
display: flex;
}

@ -4,6 +4,8 @@ import {RepositoryService} from "../../services/repository/repository.service";
import {MatTabChangeEvent, MatTabGroup} from "@angular/material/tabs";
import {TagService} from "../../services/tag/tag.service";
import {TabService} from "../../services/tab/tab.service";
import {TabCategory} from "../../models/TabCategory";
import {TabState} from "../../models/TabState.rs";
@Component({
selector: "app-core",
@ -13,9 +15,10 @@ import {TabService} from "../../services/tab/tab.service";
export class CoreComponent implements OnInit {
public selectedRepository: Repository | undefined;
public tabs: TabState[] = [];
@ViewChild("tabGroup") tabGroup!: MatTabGroup;
public newTab = false;
constructor(
private tabService: TabService,
@ -27,20 +30,11 @@ export class CoreComponent implements OnInit {
this.selectedRepository = this.repoService.selectedRepository.getValue();
this.repoService.selectedRepository.subscribe(async (selected) => {
this.selectedRepository = selected;
this.updateSelectedTab();
await this.loadRepoData();
});
}
public updateSelectedTab() {
if (!this.tabGroup) {
return;
}
if (!this.selectedRepository) {
this.tabGroup.selectedIndex = 0;
} else if (this.tabGroup.selectedIndex === 0) {
this.tabGroup.selectedIndex = 1;
}
this.tabService.tabs.subscribe(tabs => {
this.tabs = tabs;
});
}
async loadRepoData() {
@ -50,4 +44,39 @@ export class CoreComponent implements OnInit {
public onTabSelectionChange(event: MatTabChangeEvent): void {
this.tabService.setSelectedTab(event.index);
}
public addFilesTab(): void {
this.tabService.addTab(TabCategory.Files);
this.tabGroup.selectedIndex = this.tabs.length;
this.newTab = false;
}
public addImportTab(): void {
this.tabService.addTab(TabCategory.Import);
this.tabGroup.selectedIndex = this.tabs.length;
this.newTab = false;
}
public addTab(): void {
this.newTab = true;
this.tabGroup.selectedIndex = this.tabs.length + 1;
}
public closeTab(tab: TabState): void {
const previousIndex = this.tabGroup.selectedIndex;
this.tabService.closeTab(tab.uuid);
if (previousIndex) {
this.tabGroup.selectedIndex = previousIndex - 1;
} else {
this.tabGroup.selectedIndex = 0;
}
}
public onMouseClickTabLabel(tab: TabState, event: MouseEvent): void {
console.log(event);
if (event.button === 1) { // middle mouse button
this.closeTab(tab);
}
}
}

@ -1,9 +1,9 @@
<div class="sidebar-inner">
<mat-tab-group headerPosition="below">
<mat-tab label="Search">
<app-file-search [availableTags]="this.allTags" [contextTags]="this.tags"
<app-file-search [state]="this.state" [availableTags]="this.allTags" [contextTags]="this.tags"
(searchStartEvent)="this.searchStartEvent.emit($event)"
(searchEndEvent)="this.searchEndEvent.emit($event)"></app-file-search>
(searchEndEvent)="this.onDisplayedFilesChange(); this.searchEndEvent.emit($event);"></app-file-search>
</mat-tab>
<mat-tab *ngIf="this.selectedFiles.length > 0" label="Edit Tags">
<app-tag-edit #fileedit [files]="this.selectedFiles"></app-tag-edit>

@ -10,12 +10,17 @@ import {
} from "@angular/core";
import {Tag} from "../../../../models/Tag";
import {TagService} from "../../../../services/tag/tag.service";
import {FileService} from "../../../../services/file/file.service";
import {File} from "../../../../models/File";
import {FileSearchComponent} from "../../../shared/sidebar/file-search/file-search.component";
import {RepositoryService} from "../../../../services/repository/repository.service";
import {TagEditComponent} from "../../../shared/sidebar/tag-edit/tag-edit.component";
import {clipboard} from "@tauri-apps/api";
import {
FileSearchComponent
} from "../../../shared/sidebar/file-search/file-search.component";
import {
RepositoryService
} from "../../../../services/repository/repository.service";
import {
TagEditComponent
} from "../../../shared/sidebar/tag-edit/tag-edit.component";
import {TabState} from "../../../../models/TabState.rs";
@Component({
selector: "app-files-tab-sidebar",
@ -24,6 +29,7 @@ import {clipboard} from "@tauri-apps/api";
})
export class FilesTabSidebarComponent implements OnInit, OnChanges {
@Input() state!: TabState;
@Input() selectedFiles: File[] = [];
@Output() searchStartEvent = new EventEmitter<void>();
@Output() searchEndEvent = new EventEmitter<void>();
@ -37,19 +43,20 @@ export class FilesTabSidebarComponent implements OnInit, OnChanges {
public files: File[] = [];
public tagsOfSelection: Tag[] = [];
constructor(private repoService: RepositoryService, private tagService: TagService, private fileService: FileService) {
this.fileService.displayedFiles.subscribe(async files => {
this.files = files;
await this.loadTagsForDisplayedFiles();
await this.refreshFileSelection();
});
constructor(private repoService: RepositoryService, private tagService: TagService) {
this.repoService.selectedRepository.subscribe(
async (repo) => repo && this.fileSearch && await this.fileSearch.searchForFiles());
this.tagService.tags.subscribe(t => this.allTags = t);
}
async ngOnInit() {
this.fileSearch && await this.fileSearch.searchForFiles();
this.state.files.subscribe(async (files) => {
this.files = files;
await this.onDisplayedFilesChange();
})
if (this.fileSearch) {
await this.fileSearch.searchForFiles();
}
if (this.tags.length === 0) {
this.tags = this.tagsOfFiles;
}
@ -62,6 +69,11 @@ export class FilesTabSidebarComponent implements OnInit, OnChanges {
}
}
public async onDisplayedFilesChange() {
await this.loadTagsForDisplayedFiles();
await this.refreshFileSelection();
}
async loadTagsForDisplayedFiles() {
this.tagsOfFiles = await this.tagService.getTagsForFiles(
this.files.map(f => f.hash));

@ -1,7 +1,7 @@
<mat-drawer-container autosize>
<mat-drawer disableClose mode="side" opened>
<app-files-tab-sidebar (searchEndEvent)="this.contentLoading = false"
(searchStartEvent)="this.contentLoading = true"
<app-files-tab-sidebar [state]="this.state" (searchEndEvent)="this.contentLoading = false;"
(searchStartEvent)="this.contentLoading = true;"
[selectedFiles]="this.selectedFiles"></app-files-tab-sidebar>
</mat-drawer>
<mat-drawer-content>

@ -1,8 +1,6 @@
import {Component, OnInit} from "@angular/core";
import {Component, Input, 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";
import {TabState} from "../../../models/TabState.rs";
@Component({
selector: "app-files-tab",
@ -11,21 +9,17 @@ import {RepositoryService} from "../../../services/repository/repository.service
})
export class FilesTabComponent implements OnInit {
@Input() state!: TabState;
files: File[] = [];
contentLoading = false;
selectedFiles: File[] = [];
constructor(
private errorBroker: ErrorBrokerService,
private repoService: RepositoryService,
private fileService: FileService,) {
constructor() {
}
async ngOnInit() {
this.fileService.displayedFiles.subscribe(async (files) => {
this.files = files;
});
this.state.files.subscribe(files => this.files = files);
}
async onFileSelect(files: File[]) {

@ -1,5 +1,6 @@
import {Component} from "@angular/core";
import {Component, Input} from "@angular/core";
import {File} from "../../../models/File";
import {TabState} from "../../../models/TabState.rs";
@Component({
selector: "app-import-tab",
@ -8,6 +9,8 @@ import {File} from "../../../models/File";
})
export class ImportTabComponent {
@Input() state!: TabState;
public files: File[] = [];
public selectedFiles: File[] = [];

@ -3,17 +3,18 @@ import {
Component,
ElementRef,
EventEmitter,
Input, OnChanges,
Input,
OnInit,
Output, SimpleChanges,
Output,
ViewChild
} from "@angular/core";
import {FileService} from "../../../../services/file/file.service";
import {TagQuery} from "../../../../models/TagQuery";
import {SortKey} from "../../../../models/SortKey";
import {MatDialog} from "@angular/material/dialog";
import {SortDialogComponent} from "./sort-dialog/sort-dialog.component";
import {ErrorBrokerService} from "../../../../services/error-broker/error-broker.service";
import {
ErrorBrokerService
} from "../../../../services/error-broker/error-broker.service";
import {
FilterExpression,
SingleFilterExpression
@ -21,6 +22,7 @@ import {
import {FilterDialogComponent} from "./filter-dialog/filter-dialog.component";
import {Tag} from "../../../../models/Tag";
import {clipboard} from "@tauri-apps/api";
import {TabState} from "../../../../models/TabState.rs";
@Component({
@ -35,6 +37,8 @@ export class FileSearchComponent implements AfterViewChecked, OnInit {
@Input() availableTags: Tag[] = [];
@Input() contextTags: Tag[] = [];
@Input() state!: TabState;
@Output() searchStartEvent = new EventEmitter<void>();
@Output() searchEndEvent = new EventEmitter<void>();
@ -45,7 +49,6 @@ export class FileSearchComponent implements AfterViewChecked, OnInit {
constructor(
private errorBroker: ErrorBrokerService,
private fileService: FileService,
public dialog: MatDialog
) {
}
@ -61,7 +64,7 @@ export class FileSearchComponent implements AfterViewChecked, OnInit {
public async searchForFiles() {
this.searchStartEvent.emit();
try {
await this.fileService.findFiles(this.filters, this.sortExpression);
await this.state.findFiles(this.filters, this.sortExpression);
} catch (err) {
this.errorBroker.showError(err);
}

@ -0,0 +1,4 @@
export enum TabCategory {
Files = "Files",
Import = "Import",
}

@ -0,0 +1,30 @@
import {BehaviorSubject} from "rxjs";
import {TabCategory} from "./TabCategory";
import {FileService} from "../services/file/file.service";
import {File} from "./File";
import {FilterExpression} from "./FilterExpression";
import {SortKey} from "./SortKey";
export class TabState {
public uuid: number;
public category: TabCategory;
public files = new BehaviorSubject<File[]>([]);
private fileService: FileService;
constructor(uuid: number, category: TabCategory, fileService: FileService) {
this.category = category;
this.uuid = uuid;
this.fileService = fileService;
}
public async loadAllFiles() {
const files = await this.fileService.getAllFiles();
this.files.next(files);
}
public async findFiles(filters: FilterExpression[], sortBy: SortKey[]) {
const files = await this.fileService.findFiles(filters, sortBy);
this.files.next(files);
}
}

@ -1,5 +1,4 @@
import {Inject, Injectable} from "@angular/core";
import {BehaviorSubject} from "rxjs";
import {File} from "../../models/File";
import {invoke} from "@tauri-apps/api/tauri";
import {DomSanitizer, SafeResourceUrl} from "@angular/platform-browser";
@ -14,7 +13,6 @@ import {FilterExpression} from "../../models/FilterExpression";
})
export class FileService {
displayedFiles = new BehaviorSubject<File[]>([]);
thumbnailCache: { [key: number]: Thumbnail[] } = {};
constructor(
@ -28,17 +26,18 @@ export class FileService {
this.thumbnailCache = {};
}
public async getFiles() {
let all_files = await invoke<File[]>("plugin:mediarepo|get_all_files");
this.displayedFiles.next(all_files);
public async getAllFiles(): Promise<File[]> {
return await invoke<File[]>("plugin:mediarepo|get_all_files");
}
public async findFiles(filters: FilterExpression[], sortBy: SortKey[]) {
public async findFiles(filters: FilterExpression[], sortBy: SortKey[]): Promise<File[]> {
console.log(filters);
let backendFilters = filters.map(f => f.toBackendType());
let files = await invoke<File[]>("plugin:mediarepo|find_files",
{filters: backendFilters, sortBy: sortBy.map(k => k.toBackendType())});
this.displayedFiles.next(files);
return await invoke<File[]>("plugin:mediarepo|find_files",
{
filters: backendFilters,
sortBy: sortBy.map(k => k.toBackendType())
});
}
public async updateFileName(file: File, name: string): Promise<File> {

@ -1,17 +1,35 @@
import {Injectable} from "@angular/core";
import {BehaviorSubject} from "rxjs";
import {TabState} from "../../models/TabState.rs";
import {TabCategory} from "../../models/TabCategory";
import {FileService} from "../file/file.service";
@Injectable({
providedIn: "root"
})
export class TabService {
private tabIdCounter = 0;
public selectedTab = new BehaviorSubject<number>(0);
public tabs = new BehaviorSubject<TabState[]>([]);
constructor() {
constructor(private fileService: FileService) {
}
public setSelectedTab(index: number) {
this.selectedTab.next(index);
}
public addTab(category: TabCategory): TabState {
const state = new TabState(this.tabIdCounter++, category, this.fileService);
this.tabs.next([...this.tabs.value, state]);
return state;
}
public closeTab(uuid: number) {
const index = this.tabs.value.findIndex(t => t.uuid === uuid);
const tabs = this.tabs.value;
tabs.splice(index, 1)
this.tabs.next(tabs);
}
}

Loading…
Cancel
Save