diff --git a/mediarepo-ui/src/app/app.module.ts b/mediarepo-ui/src/app/app.module.ts
index ce93bac..f443a4e 100644
--- a/mediarepo-ui/src/app/app.module.ts
+++ b/mediarepo-ui/src/app/app.module.ts
@@ -48,6 +48,7 @@ import {ConfirmDialogComponent} from './components/confirm-dialog/confirm-dialog
import {FilesTabSidebarComponent} from './pages/home/files-tab/files-tab-sidebar/files-tab-sidebar.component';
import {MatExpansionModule} from "@angular/material/expansion";
import {TagItemComponent} from './components/tag-item/tag-item.component';
+import { FileEditComponent } from './components/file-edit/file-edit.component';
@NgModule({
declarations: [
@@ -67,6 +68,7 @@ import {TagItemComponent} from './components/tag-item/tag-item.component';
ConfirmDialogComponent,
FilesTabSidebarComponent,
TagItemComponent,
+ FileEditComponent,
],
imports: [
BrowserModule,
diff --git a/mediarepo-ui/src/app/components/file-edit/file-edit.component.html b/mediarepo-ui/src/app/components/file-edit/file-edit.component.html
new file mode 100644
index 0000000..37352c2
--- /dev/null
+++ b/mediarepo-ui/src/app/components/file-edit/file-edit.component.html
@@ -0,0 +1,48 @@
+
+
+
Edit File
+
+ Name
+
+
+
+
+
+
+
diff --git a/mediarepo-ui/src/app/components/file-edit/file-edit.component.scss b/mediarepo-ui/src/app/components/file-edit/file-edit.component.scss
new file mode 100644
index 0000000..92d6ccc
--- /dev/null
+++ b/mediarepo-ui/src/app/components/file-edit/file-edit.component.scss
@@ -0,0 +1,70 @@
+.file-metadata, .tag-input {
+ width: 100%;
+ mat-form-field {
+ width: 100%;
+ }
+
+ mat-form-field.form-field-mode {
+ width: 10em;
+ }
+
+ mat-form-field.form-field-tag-input {
+
+ }
+}
+
+.file-edit-inner {
+ height: 100%;
+ width: 100%;
+ display: block;
+}
+
+.tag-edit-list {
+ 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 {
+ 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;
+
+ app-tag-item {
+ margin: auto auto auto 0.25em;
+ }
+
+ .tag-remove-button {
+ margin-right: 1em;
+ height: 50px;
+ width: 50px;
+ }
+}
+
+.tag-input-field {
+ display: flex;
+ flex-direction: row;
+ .add-tag-button {
+ width: 65px;
+ height: 65px;
+ }
+}
+
+mat-divider {
+ width: 100%;
+}
diff --git a/mediarepo-ui/src/app/components/file-edit/file-edit.component.spec.ts b/mediarepo-ui/src/app/components/file-edit/file-edit.component.spec.ts
new file mode 100644
index 0000000..51ab4bb
--- /dev/null
+++ b/mediarepo-ui/src/app/components/file-edit/file-edit.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FileEditComponent } from './file-edit.component';
+
+describe('FileEditComponent', () => {
+ let component: FileEditComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ FileEditComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(FileEditComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/mediarepo-ui/src/app/components/file-edit/file-edit.component.ts b/mediarepo-ui/src/app/components/file-edit/file-edit.component.ts
new file mode 100644
index 0000000..b11e6bf
--- /dev/null
+++ b/mediarepo-ui/src/app/components/file-edit/file-edit.component.ts
@@ -0,0 +1,100 @@
+import {Component, Input, OnInit, ViewChild} from '@angular/core';
+import {FormControl} from "@angular/forms";
+import {File} from "../../models/File";
+import {Tag} from "../../models/Tag";
+import {CdkVirtualScrollViewport} from "@angular/cdk/scrolling";
+import {MatAutocompleteSelectedEvent} from "@angular/material/autocomplete";
+import {Observable} from "rxjs";
+import {map, startWith} from "rxjs/operators";
+import {TagService} from "../../services/tag/tag.service";
+
+@Component({
+ selector: 'app-file-edit',
+ templateUrl: './file-edit.component.html',
+ styleUrls: ['./file-edit.component.scss']
+})
+export class FileEditComponent implements OnInit {
+
+ @Input() files: File[] = [];
+ @Input() tags: Tag[] = [];
+
+ private allTags: Tag[] = [];
+
+ public suggestionTags: Observable;
+ public tagInputForm = new FormControl("");
+ public editMode: string = "Toggle";
+
+ @ViewChild("tagScroll") tagScroll!: CdkVirtualScrollViewport;
+
+ constructor(
+ private tagService: TagService,
+ ) {
+ this.suggestionTags = this.tagInputForm.valueChanges.pipe(startWith(null),
+ map(
+ (tag: string | null) => tag ? this.filterSuggestionTag(
+ tag) : this.allTags.slice(0, 20).map(t => t.getNormalizedOutput())));
+ }
+
+ async ngOnInit() {
+ this.tagService.tags.subscribe(tags => this.allTags = tags);
+ await this.tagService.loadTags();
+ }
+
+ public async editTagByAutocomplete($event: MatAutocompleteSelectedEvent) {
+ const tag = $event.option.value.trim();
+ await this.editTag(tag);
+ }
+
+ private async editTag(tag: string): Promise {
+ if (tag.length > 0) {
+ let tagInstance = this.allTags.find(t => t.getNormalizedOutput() === tag);
+
+ if (!tagInstance) {
+ // TODO: Create tag
+ tagInstance = new Tag(0, "", undefined);
+ }
+ switch (this.editMode) {
+ case "Toggle":
+ await this.toggleTag(tagInstance);
+ break;
+ case "Add":
+ await this.addTag(tagInstance);
+ break;
+ case "Remove":
+ await this.removeTag(tagInstance);
+ break;
+ }
+ this.tagInputForm.setValue("");
+ }
+ }
+
+ async toggleTag(tag: Tag) {
+
+ }
+
+ async addTag(tag: Tag) {
+ if (this.tags.findIndex(t => t.getNormalizedOutput() === tag.getNormalizedOutput()) < 0) {
+ this.tags.push(tag);
+ this.tags = this.tags.sort(
+ (a, b) => a.getNormalizedOutput().localeCompare(b.getNormalizedOutput()));
+ this.tags = [...this.tags]; // angular pls detect it wtf
+ }
+ const index = this.tags.indexOf(tag);
+ index >= 0 && this.tagScroll.scrollToIndex(index);
+ }
+
+ public async removeTag(tag: Tag) {
+ const index = this.tags.indexOf(tag);
+ if (index >= 0) {
+ this.tags.splice(index, 1);
+ this.tags = [...this.tags]; // so angular detects the change
+ }
+ }
+
+ private filterSuggestionTag(tag: string) {
+ const allTags = this.allTags.map(t => t.getNormalizedOutput());
+ return allTags.filter(
+ t => t.includes(tag))
+ .slice(0, 20);
+ }
+}
diff --git a/mediarepo-ui/src/app/components/file-search/file-search.component.ts b/mediarepo-ui/src/app/components/file-search/file-search.component.ts
index e58462a..19da0a3 100644
--- a/mediarepo-ui/src/app/components/file-search/file-search.component.ts
+++ b/mediarepo-ui/src/app/components/file-search/file-search.component.ts
@@ -135,7 +135,7 @@ export class FileSearchComponent implements AfterViewChecked, OnInit {
return this.validTags.filter(
t => t.includes(normalizedTag) && this.searchTags.findIndex(
- s => s.name === t) < 0)
+ s => s.getNormalizedTag() === t) < 0)
.map(t => negated ? "-" + t : t)
.slice(0, 20);
}
diff --git a/mediarepo-ui/src/app/components/tag-item/tag-item.component.scss b/mediarepo-ui/src/app/components/tag-item/tag-item.component.scss
index 5e91837..6911049 100644
--- a/mediarepo-ui/src/app/components/tag-item/tag-item.component.scss
+++ b/mediarepo-ui/src/app/components/tag-item/tag-item.component.scss
@@ -2,6 +2,8 @@
display: inline;
width: 100%;
padding: 0.25em;
+ word-break: break-all;
+ text-overflow: ellipsis;
}
.tag-item-namespace {
diff --git a/mediarepo-ui/src/app/pages/home/files-tab/files-tab-sidebar/files-tab-sidebar.component.html b/mediarepo-ui/src/app/pages/home/files-tab/files-tab-sidebar/files-tab-sidebar.component.html
index b5b3c49..a86bcb8 100644
--- a/mediarepo-ui/src/app/pages/home/files-tab/files-tab-sidebar/files-tab-sidebar.component.html
+++ b/mediarepo-ui/src/app/pages/home/files-tab/files-tab-sidebar/files-tab-sidebar.component.html
@@ -21,5 +21,8 @@
+ 0">
+
+
diff --git a/mediarepo-ui/src/app/pages/home/files-tab/files-tab-sidebar/files-tab-sidebar.component.scss b/mediarepo-ui/src/app/pages/home/files-tab/files-tab-sidebar/files-tab-sidebar.component.scss
index 30b9a87..fcdf3f8 100644
--- a/mediarepo-ui/src/app/pages/home/files-tab/files-tab-sidebar/files-tab-sidebar.component.scss
+++ b/mediarepo-ui/src/app/pages/home/files-tab/files-tab-sidebar/files-tab-sidebar.component.scss
@@ -8,7 +8,7 @@ app-file-search {
overflow: hidden;
}
-mat-tab-group, mat-tab, .file-tag-list {
+mat-tab-group, mat-tab, .file-tag-list, app-file-edit {
height: 100%;
width: 100%;
}
@@ -45,17 +45,13 @@ mat-selection-list {
cursor: pointer;
}
-.file-tag-list-inner {
- display: block;
- height: 100%;
- width: 100%;
- overflow: hidden;
-}
-
cdk-virtual-scroll-viewport {
height: 100%;
width: 100%;
overflow-y: auto;
+ ::ng-deep .cdk-virtual-scroll-content-wrapper {
+ width: 100%;
+ }
}
mat-divider {
diff --git a/mediarepo-ui/src/app/pages/home/files-tab/files-tab-sidebar/files-tab-sidebar.component.ts b/mediarepo-ui/src/app/pages/home/files-tab/files-tab-sidebar/files-tab-sidebar.component.ts
index 69b2706..062c8c2 100644
--- a/mediarepo-ui/src/app/pages/home/files-tab/files-tab-sidebar/files-tab-sidebar.component.ts
+++ b/mediarepo-ui/src/app/pages/home/files-tab/files-tab-sidebar/files-tab-sidebar.component.ts
@@ -14,6 +14,7 @@ import {FileService} from "../../../../services/file/file.service";
import {File} from "../../../../models/File";
import {FileSearchComponent} from "../../../../components/file-search/file-search.component";
import {RepositoryService} from "../../../../services/repository/repository.service";
+import {FileEditComponent} from "../../../../components/file-edit/file-edit.component";
@Component({
selector: 'app-files-tab-sidebar',
@@ -27,10 +28,12 @@ export class FilesTabSidebarComponent implements OnInit, OnChanges {
@Output() searchEndEvent = new EventEmitter();
@ViewChild('filesearch') fileSearch!: FileSearchComponent;
+ @ViewChild("fileedit") fileEdit: FileEditComponent | undefined;
public tagsOfFiles: Tag[] = [];
public tags: Tag[] = [];
public files: File[] = [];
+ public tagsOfSelection: Tag[] = [];
constructor(private repoService: RepositoryService, private tagService: TagService, private fileService: FileService) {
this.fileService.displayedFiles.subscribe(async files => {
@@ -72,9 +75,10 @@ export class FilesTabSidebarComponent implements OnInit, OnChanges {
}
async showFileDetails(files: File[]) {
- this.tags = await this.tagService.getTagsForFiles(files.map(f => f.hash))
- this.tags = this.tags.sort(
+ this.tagsOfSelection = await this.tagService.getTagsForFiles(files.map(f => f.hash))
+ this.tagsOfSelection = this.tagsOfSelection.sort(
(a, b) => a.getNormalizedOutput().localeCompare(b.getNormalizedOutput()));
+ this.tags = this.tagsOfSelection;
}
private async refreshFileSelection() {