Move tag list to search sidebar

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/4/head
trivernis 3 years ago
parent 556e718f2a
commit 0563c928d1

@ -1,43 +1,12 @@
<div class="sidebar-inner"> <div class="sidebar-inner">
<mat-tab-group headerPosition="below"> <mat-tab-group headerPosition="below">
<mat-tab label="Search"> <mat-tab label="Search">
<div class="search-tab-inner" fxLayout="column"> <app-file-search [availableTags]="this.allTags" [contextTags]="this.tags"
<div id="file-search-input"> (searchStartEvent)="this.searchStartEvent.emit($event)"
<app-file-search #filesearch (searchEndEvent)="this.searchEndEvent.emit()" (searchEndEvent)="this.searchEndEvent.emit($event)"></app-file-search>
(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>
</mat-tab> </mat-tab>
<mat-tab *ngIf="this.selectedFiles.length > 0" label="Edit Tags"> <mat-tab *ngIf="this.selectedFiles.length > 0" label="Edit Tags">
<app-tag-edit #fileedit [files]="this.selectedFiles"></app-tag-edit> <app-tag-edit #fileedit [files]="this.selectedFiles"></app-tag-edit>
</mat-tab> </mat-tab>
</mat-tab-group> </mat-tab-group>
</div> </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>
</app-context-menu>

@ -1,70 +1,10 @@
app-file-search { mat-tab-group, mat-tab, .file-tag-list, app-tag-edit, app-file-search {
display: block;
width: 100%;
}
#file-search-input {
width: 100%;
overflow: hidden;
}
mat-tab-group, mat-tab, .file-tag-list, app-file-edit {
height: 100%; height: 100%;
width: 100%; width: 100%;
} }
mat-selection-list { .sidebar-inner {
height: 100%;
user-select: none;
}
.sidebar-inner, .search-tab-inner {
height: 100%; height: 100%;
width: 100%; width: 100%;
display: block; display: block;
} }
.selectable-tag {
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;
}
}
.selectable-tag:hover {
background-color: darken(dimgrey, 10);
}
.selectable-tag:active {
cursor: pointer;
}
cdk-virtual-scroll-viewport {
height: 100%;
width: 100%;
overflow-y: auto;
::ng-deep .cdk-virtual-scroll-content-wrapper {
width: 100%;
}
}
mat-divider {
width: 100%;
}
.tag-list-header {
width: 100%;
display: flex;
flex-direction: column;
h2 {
margin: auto;
}
}

@ -36,7 +36,6 @@ export class FilesTabSidebarComponent implements OnInit, OnChanges {
public allTags: Tag[] = []; public allTags: Tag[] = [];
public files: File[] = []; public files: File[] = [];
public tagsOfSelection: Tag[] = []; public tagsOfSelection: Tag[] = [];
public contextMenuTag: Tag | undefined;
constructor(private repoService: RepositoryService, private tagService: TagService, private fileService: FileService) { constructor(private repoService: RepositoryService, private tagService: TagService, private fileService: FileService) {
this.fileService.displayedFiles.subscribe(async files => { this.fileService.displayedFiles.subscribe(async files => {
@ -69,11 +68,6 @@ export class FilesTabSidebarComponent implements OnInit, OnChanges {
this.showAllTagsFallback(); this.showAllTagsFallback();
} }
async addSearchTag(tag: Tag) {
this.fileSearch.addSearchTag(tag.getNormalizedOutput());
await this.fileSearch.searchForFiles();
}
async showFileDetails(files: File[]) { async showFileDetails(files: File[]) {
this.tagsOfSelection = await this.tagService.getTagsForFiles( this.tagsOfSelection = await this.tagService.getTagsForFiles(
files.map(f => f.hash)) files.map(f => f.hash))
@ -83,10 +77,6 @@ export class FilesTabSidebarComponent implements OnInit, OnChanges {
this.tags = this.tagsOfSelection; this.tags = this.tagsOfSelection;
} }
public async copyToClipboard(text: string) {
await clipboard.writeText(text);
}
private async refreshFileSelection() { private async refreshFileSelection() {
const filteredSelection = this.selectedFiles.filter( const filteredSelection = this.selectedFiles.filter(
file => this.files.findIndex(f => f.id === file.id) >= 0); file => this.files.findIndex(f => f.id === file.id) >= 0);

@ -18,3 +18,4 @@
<button (click)="this.fileOpenEvent.emit(fileContextMenu.file)" mat-menu-item content-before>Open</button> <button (click)="this.fileOpenEvent.emit(fileContextMenu.file)" mat-menu-item content-before>Open</button>
<button (click)="this.regenerateThumbnail(fileContextMenu.file)" mat-menu-item>Regenerate thumbnail</button> <button (click)="this.regenerateThumbnail(fileContextMenu.file)" mat-menu-item>Regenerate thumbnail</button>
</app-file-context-menu> </app-file-context-menu>

@ -1,19 +1,58 @@
<div class="tag-input-list-and-actions"> <div class="search-tab-inner" fxLayout="column">
<div id="file-search-input">
<div class="tag-input-list-and-actions">
<div #tagInputList class="tag-input-list"> <div #tagInputList class="tag-input-list">
<div class="tag-input-list-inner"> <div class="tag-input-list-inner">
<div (click)="removeFilterExpression(filter)" *ngFor="let filter of filters" class="tag-input-item" <div (click)="removeFilterExpression(filter)" *ngFor="let filter of filters" class="tag-input-item"
mat-ripple>{{filter.getDisplayName()}}</div> mat-ripple>{{filter.getDisplayName()}}</div>
</div> </div>
</div> </div>
<button (click)="removeAllSearchTags()" id="delete-all-tags-button" mat-icon-button> <button (click)="removeAllSearchTags()" id="delete-all-tags-button" mat-icon-button>
<ng-icon name="mat-delete-sweep"></ng-icon> <ng-icon name="mat-delete-sweep"></ng-icon>
</button> </button>
</div> </div>
<app-tag-input (tagAdded)="addSearchTag($event); searchForFiles()" [allowNegation]="true"
<app-tag-input (tagAdded)="addSearchTag($event); searchForFiles()" [allowNegation]="true"
[availableTags]="getValidSearchTags()" [availableTags]="getValidSearchTags()"
class="full-width"> class="full-width">
<button (click)="openFilterDialog()" class="filter-dialog-button" mat-button> <button (click)="openFilterDialog()" class="filter-dialog-button" mat-button>
<ng-icon name="mat-filter-alt"></ng-icon> <ng-icon name="mat-filter-alt"></ng-icon>
</button> </button>
</app-tag-input> </app-tag-input>
<button (click)="openSortDialog()" id="sort-button" mat-flat-button>Sort: {{sortExpression.join(", ")}}</button>
<button (click)="openSortDialog()" id="sort-button" mat-flat-button>Sort: {{sortExpression.join(", ")}}</button>
</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)="addSearchTagAndSearch(tag.getNormalizedOutput())"
(contextmenu)="contextMenuTag = tag; contextMenu.onContextMenu($event)"
*cdkVirtualFor="let tag of contextTags" class="selectable-tag">
<app-tag-item [tag]="tag"></app-tag-item>
</div>
</cdk-virtual-scroll-viewport>
</div>
</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>
</app-context-menu>

@ -1,32 +1,32 @@
.full-width { .full-width {
min-width: 100%;
width: auto; width: auto;
min-width: 100%;
} }
.tag-input-list { .tag-input-list {
width: calc(100% - 64px);
height: 2.7em; height: 2.7em;
padding: 0.5em 0; padding: 0.5em 0;
width: calc(100% - 64px); box-shadow: 0 0 1em 0.1em rgba(0, 0, 0, 0.05) inset;
overflow-x: auto; overflow-x: auto;
overflow-y: hidden; overflow-y: hidden;
box-shadow: 0 0 1em 0.1em rgba(0, 0, 0, 0.05) inset;
} }
.tag-input-list-and-actions { .tag-input-list-and-actions {
position: relative;
display: block; display: block;
height: calc(3em + 10px);
width: 100%; width: 100%;
position: relative; height: calc(3em + 10px);
} }
#delete-all-tags-button { #delete-all-tags-button {
font-size: 0.5em; position: absolute;
top: 0;
right: 0;
width: 64px; width: 64px;
height: 100%; height: 100%;
padding: 0; padding: 0;
position: absolute; font-size: 0.5em;
right: 0;
top: 0;
} }
#sort-button { #sort-button {
@ -57,6 +57,65 @@
.filter-dialog-button { .filter-dialog-button {
position: absolute; position: absolute;
right: -13px;
top: -17px; top: -17px;
right: -13px;
}
#file-search-input {
width: 100%;
overflow: hidden;
}
.sidebar-inner, .search-tab-inner {
display: block;
width: 100%;
height: 100%;
}
.selectable-tag {
display: flex;
height: 50px;
cursor: pointer;
user-select: none;
font-size: 1.2em;
transition-duration: 0.1s;
width: 100%;
app-tag-item {
margin: auto auto auto 0.25em;
}
}
.selectable-tag:hover {
background-color: darken(dimgrey, 10);
}
.selectable-tag:active {
cursor: pointer;
background-color: darken(dimgrey, 5);
}
cdk-virtual-scroll-viewport {
width: 100%;
height: 100%;
overflow-y: auto;
::ng-deep .cdk-virtual-scroll-content-wrapper {
width: 100%;
}
}
mat-divider {
width: 100%;
}
.tag-list-header {
display: flex;
flex-direction: column;
width: 100%;
h2 {
margin: auto;
}
} }

@ -3,9 +3,9 @@ import {
Component, Component,
ElementRef, ElementRef,
EventEmitter, EventEmitter,
Input, Input, OnChanges,
OnInit, OnInit,
Output, Output, SimpleChanges,
ViewChild ViewChild
} from "@angular/core"; } from "@angular/core";
import {FileService} from "../../../../services/file/file.service"; import {FileService} from "../../../../services/file/file.service";
@ -20,6 +20,7 @@ import {
} from "../../../../models/FilterExpression"; } from "../../../../models/FilterExpression";
import {FilterDialogComponent} from "./filter-dialog/filter-dialog.component"; import {FilterDialogComponent} from "./filter-dialog/filter-dialog.component";
import {Tag} from "../../../../models/Tag"; import {Tag} from "../../../../models/Tag";
import {clipboard} from "@tauri-apps/api";
@Component({ @Component({
@ -33,12 +34,15 @@ export class FileSearchComponent implements AfterViewChecked, OnInit {
public filters: FilterExpression[] = []; public filters: FilterExpression[] = [];
@Input() availableTags: Tag[] = []; @Input() availableTags: Tag[] = [];
@Input() contextTags: Tag[] = [];
@Output() searchStartEvent = new EventEmitter<void>(); @Output() searchStartEvent = new EventEmitter<void>();
@Output() searchEndEvent = new EventEmitter<void>(); @Output() searchEndEvent = new EventEmitter<void>();
@ViewChild("tagInput") tagInput!: ElementRef<HTMLInputElement>; @ViewChild("tagInput") tagInput!: ElementRef<HTMLInputElement>;
@ViewChild("tagInputList") inputList!: ElementRef; @ViewChild("tagInputList") inputList!: ElementRef;
public contextMenuTag: Tag | undefined;
constructor( constructor(
private errorBroker: ErrorBrokerService, private errorBroker: ErrorBrokerService,
private fileService: FileService, private fileService: FileService,
@ -74,6 +78,11 @@ export class FileSearchComponent implements AfterViewChecked, OnInit {
} }
} }
public async addSearchTagAndSearch(tag: string) {
this.addSearchTag(tag);
await this.searchForFiles();
}
public getValidSearchTags(): Tag[] { public getValidSearchTags(): Tag[] {
return this.availableTags.filter(t => this.filters.findIndex( return this.availableTags.filter(t => this.filters.findIndex(
f => f.partiallyEq(t.getNormalizedOutput())) < 0); f => f.partiallyEq(t.getNormalizedOutput())) < 0);
@ -130,4 +139,8 @@ export class FileSearchComponent implements AfterViewChecked, OnInit {
} }
}); });
} }
public async copyToClipboard(text: string) {
await clipboard.writeText(text);
}
} }

@ -25,6 +25,7 @@ import { FileImportComponent } from "./file-import/file-import.component";
import {FilesystemImportComponent} from "./file-import/filesystem-import/filesystem-import.component"; import {FilesystemImportComponent} from "./file-import/filesystem-import/filesystem-import.component";
import {MatCheckboxModule} from "@angular/material/checkbox"; import {MatCheckboxModule} from "@angular/material/checkbox";
import {MatProgressBarModule} from "@angular/material/progress-bar"; import {MatProgressBarModule} from "@angular/material/progress-bar";
import {MatMenuModule} from "@angular/material/menu";
@NgModule({ @NgModule({
@ -68,6 +69,7 @@ import {MatProgressBarModule} from "@angular/material/progress-bar";
TagModule, TagModule,
MatCheckboxModule, MatCheckboxModule,
MatProgressBarModule, MatProgressBarModule,
MatMenuModule,
] ]
}) })
export class SidebarModule { export class SidebarModule {

Loading…
Cancel
Save