diff options
Diffstat (limited to 'frontend/src/app/_elements/folder')
4 files changed, 589 insertions, 0 deletions
diff --git a/frontend/src/app/_elements/folder/folder.component.css b/frontend/src/app/_elements/folder/folder.component.css new file mode 100644 index 00000000..137a9643 --- /dev/null +++ b/frontend/src/app/_elements/folder/folder.component.css @@ -0,0 +1,189 @@ +#folder { + /*position: absolute; + left: 50%; + transform: translateX(-50%);*/ +} + +#tabs { + display: flex; + flex-direction: row; + align-items: flex-end; + height: 3.1rem; +} + +#tabs>.folder-tab:not(:first-child) { + margin-left: -5px; +} + +.folder-tab { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + position: relative; + height: 2.5rem; + background-color: var(--ns-bg-dark-100); + border-color: var(--ns-primary); + color: var(--ns-primary); + border-style: solid; + border-width: 1px 1px 0 1px; +} + +.folder-tab:not(:first-child) { + margin-block-start: auto; +} + +.selected-tab { + height: 3rem; + background-color: var(--ns-primary); + color: var(--offwhite); +} + +.hover-tab { + height: 3.2rem; +} + +.selected-tab, +.hover-tab { + width: fit-content !important; +} + +.tab-link { + color: var(--offwhite) !important; + text-decoration: none !important; + cursor: pointer; + padding: 0.5rem; +} + +.tab-link:active { + text-decoration: underline !important; +} + +.selected-tab { + background-color: var(--ns-primary); +} + +#searchbar { + height: 2.5rem; + background-color: var(--ns-bg-dark-100); + border-bottom: 1px solid var(--ns-primary); + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + width: 100%; +} + +.collapse-horizontal { + white-space: nowrap; + height: 2.5rem; + overflow-x: hidden; +} + +#search-options { + margin-left: auto; + display: flex; + flex-direction: row; + align-items: center; + height: 100%; +} + +#selected-content { + background-color: var(--ns-bg-dark-50); + width: 100%; + /*backdrop-filter: blur(2px);*/ + border-color: var(--ns-primary); + border-style: solid; + border-width: 1px 1px 1px 1px; + border-top-right-radius: 4px; +} + +#footer { + display: flex; + flex-direction: row; + justify-content: center; +} + +.bottom-button { + font-size: large; + position: relative; + background-color: var(--ns-primary); + width: 10rem; + height: 2.3rem; + border-color: var(--ns-primary); + border-style: solid; + border-width: 0px 1px 1px 1px; +} + +.rounded-bottom { + border-top-right-radius: 0; + border-top-left-radius: 0; +} + +.separator { + border-left-color: var(--ns-primary); + border-left-width: 1px; + border-left-style: solid; +} + +.list-view { + height: 100%; + overflow-y: auto; +} + +.list-item { + height: 3rem; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid var(--ns-primary); +} + +.list-item:hover { + background-color: var(--ns-bg-dark-100); + box-shadow: 0px 3px 3px var(--ns-primary); +} + +.list-item:hover>.hover-hide { + display: none; +} + +.folder-inside { + width: 100%; + height: 40rem; + overflow-y: auto; +} + +.file-content { + width: 100%; + height: 92%; + position: relative; +} + +.file-bottom-buttons { + position: absolute; + bottom: 15px; + right: 15px; + display: flex; + flex-direction: row-reverse; +} + +.file-button { + position: relative; + color: var(--offwhite); + border-radius: 4px; + border: 1px solid var(--ns-primary); + margin: 5px; + padding: 5px; + cursor: pointer; + z-index: 1001; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; +} + +.file-button:hover { + background-color: var(--ns-primary); +}
\ No newline at end of file diff --git a/frontend/src/app/_elements/folder/folder.component.html b/frontend/src/app/_elements/folder/folder.component.html new file mode 100644 index 00000000..36f70c97 --- /dev/null +++ b/frontend/src/app/_elements/folder/folder.component.html @@ -0,0 +1,99 @@ +<div id="folder"> + <div id="tabs"> + <div id="new-file-tab" class="folder-tab p-1 rounded-top" [style]="'z-index:' + (selectedTab == TabType.NewFile ? 11 : 10) + ' ;'" [ngClass]="{'selected-tab' : selectedTab == TabType.NewFile, 'hover-tab' : hoverTab == TabType.NewFile}"> + <mat-icon class="text-offwhite">add</mat-icon> + <a class="stretched-link tab-link" (click)="selectTab(TabType.NewFile)" (mouseenter)="hoverOverTab(TabType.NewFile)" (mouseleave)="hoverOverTab(TabType.None)"> + {{tabTitles[TabType.NewFile]}} + </a> + </div> + <!--<div class="folder-tab p-1 rounded-top" *ngFor="let file of filteredFiles; let i = index" [style]="'z-index:' + calcZIndex(i) + ' ;'" [ngClass]="{'selected-tab' : selectedFileIndex == i, 'hover-tab' : hoveringOverFileIndex == i}"> + <a class="m-1 stretched-link tab-link" (click)="selectFile(i)" (mouseenter)="hoverOverFile(i)" (mouseleave)="hoverOverFile(-1)">{{file.name}}</a> + </div>--> + <div class="folder-tab p-1 rounded-top" *ngFor="let tab of tabsToShow; let i = index" [style]="'z-index:' + (selectedTab == tab ? 11 : (tabsToShow.length - i)) + ' ;'" [ngClass]="{'selected-tab' : selectedTab == tab, 'hover-tab' : hoverTab == tab}"> + <a class="m-1 stretched-link tab-link" (click)="selectTab(tab)" (mouseenter)="hoverOverTab(tab)" (mouseleave)="hoverOverTab(TabType.None)">{{tabTitles[tab]}}</a> + </div> + </div> + <div id="selected-content" class="rounded-bottom text-offwhite"> + <div id="searchbar" *ngIf="listView"> + <!-- <div id="path" class="ps-2">{{folderName}} + </div> + <mat-icon>keyboard_arrow_right</mat-icon> --> + <div id="search" class="text-offwhite mx-1"> + <mat-form-field> + <button matPrefix class="btn-clear input-icon"><mat-icon>search</mat-icon></button> + <input type="search" matInput name="search" [(ngModel)]="searchTerm" (input)="searchTermsChanged()"> + <button matSuffix class="btn-clear input-icon" (click)="clearSearchTerm()"><mat-icon>clear</mat-icon></button> + </mat-form-field> + </div> + <div id="search-options"> + <div id="collapseFilters" class="collapse collapse-horizontal"> + <mat-icon class="text-offwhite ">timeline</mat-icon> + Regresioni + <mat-icon class="text-offwhite ">looks_two</mat-icon> + Binarni klasifikacioni + <mat-icon class="text-offwhite ">auto_awesome_motion</mat-icon> + Multiklasifikacioni + </div> + <button class="btn-clear icon-toggle" data-bs-toggle="collapse" data-bs-target="#collapseFilters" aria-expanded="false" aria-controls="collapseFilters"> + <mat-icon>filter_alt</mat-icon> + </button> + <div id="collapseSort" class="collapse collapse-horizontal"> + [sort options here TODO] + </div> + <button class="btn-clear icon-toggle" data-bs-toggle="collapse" data-bs-target="#collapseSort" aria-expanded="false" aria-controls="collapseSort"> + <mat-icon>sort</mat-icon> + </button> + <!-- <button class="btn-clear icon-toggle separator" [ngClass]="{'icon-toggle-on': listView}" (click)="toggleListView()"> + <mat-icon>view_list</mat-icon> + </button> --> + </div> + </div> + <!--{{fileToDisplay ? fileToDisplay.name : 'No file selected.'}} {{selectedFileIndex}} {{hoveringOverFileIndex}}--> + <div [ngSwitch]="listView" class="folder-inside bg-blur"> + <div class="file-content" [ngSwitch]="type" *ngSwitchCase="false"> + <div class="file-bottom-buttons"> + <button class="btn-clear file-button" (click)="deleteFile()"> + <mat-icon>delete</mat-icon> + </button> + <button class="btn-clear file-button"> + <mat-icon>link</mat-icon> + </button> + <button class="btn-clear file-button"> + <mat-icon>zoom_out_map</mat-icon> + </button> + </div> + <app-form-model [forExperiment]="forExperiment" [model]="fileToDisplay" *ngSwitchCase="FolderType.Model"></app-form-model> + <app-form-dataset *ngSwitchCase="FolderType.Dataset" ></app-form-dataset> + </div> + <div *ngSwitchCase="true" class="list-view"> + <div *ngFor="let file of filteredFiles; let i = index" class="list-item"> + <div class="mx-2"> + <a class="force-link" (click)="selectFile(i)">{{file.name}}</a> + </div> + <div class="mx-2 hover-hide"> + {{file.lastUpdated | date}} + </div> + </div> + </div> + </div> + </div> + <div id="footer" [ngSwitch]="newFileSelected"> + <button mat-button (click)="saveNewFile()" class="bottom-button text-offwhite rounded-bottom" *ngSwitchCase="true"> + <div class="f-row"> + <div>Sačuvaj</div> + <div class="pt-1"> + <mat-icon>check</mat-icon> + </div> + </div> + </button> + <button mat-button (click)="ok()" class="bottom-button text-offwhite rounded-bottom" *ngSwitchCase="false"> + <div class="f-row"> + <div>Ok</div> + <div class="icon-double pt-1"> + <mat-icon>check</mat-icon> + <mat-icon>check</mat-icon> + </div> + </div> + </button> + </div> +</div>
\ No newline at end of file diff --git a/frontend/src/app/_elements/folder/folder.component.spec.ts b/frontend/src/app/_elements/folder/folder.component.spec.ts new file mode 100644 index 00000000..33a573a7 --- /dev/null +++ b/frontend/src/app/_elements/folder/folder.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FolderComponent } from './folder.component'; + +describe('FolderComponent', () => { + let component: FolderComponent; + let fixture: ComponentFixture<FolderComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ FolderComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(FolderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_elements/folder/folder.component.ts b/frontend/src/app/_elements/folder/folder.component.ts new file mode 100644 index 00000000..06b4d893 --- /dev/null +++ b/frontend/src/app/_elements/folder/folder.component.ts @@ -0,0 +1,276 @@ +import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; +import Dataset from 'src/app/_data/Dataset'; +import { FolderFile, FolderType } from 'src/app/_data/FolderFile'; +import Model from 'src/app/_data/Model'; +import { DatasetsService } from 'src/app/_services/datasets.service'; +import shared from 'src/app/Shared'; +import { ModelsService } from 'src/app/_services/models.service'; +import { FormDatasetComponent } from '../form-dataset/form-dataset.component'; +import Experiment from 'src/app/_data/Experiment'; +import { ExperimentsService } from 'src/app/_services/experiments.service'; +import { PredictorsService } from 'src/app/_services/predictors.service'; + +@Component({ + selector: 'app-folder', + templateUrl: './folder.component.html', + styleUrls: ['./folder.component.css'] +}) +export class FolderComponent implements OnInit { + + @ViewChild(FormDatasetComponent) formDataset?: FormDatasetComponent; + + @Input() folderName: string = 'Moji podaci'; + + @Input() files!: FolderFile[] + + newFile!: Dataset | Model; + + @Input() type: FolderType = FolderType.Dataset; + + @Input() forExperiment?: Experiment; + + newFileSelected: boolean = true; + + selectedFileIndex: number = -1; + selectedFile?: FolderFile; + hoveringOverFileIndex: number = -1; + + fileToDisplay?: FolderFile; + + @Output() selectedFileChanged: EventEmitter<FolderFile> = new EventEmitter(); + @Output() okPressed: EventEmitter<string> = new EventEmitter(); + + searchTerm: string = ''; + + constructor(private datasetsService: DatasetsService, private experimentsService: ExperimentsService, private modelsService: ModelsService, private predictorsService: PredictorsService) { + //PLACEHOLDER + this.forExperiment = new Experiment(); + this.forExperiment.inputColumns = ['kolona1', 'kol2', '???', 'test']; + + this.folders[TabType.File] = []; + this.folders[TabType.NewFile] = []; + + this.refreshFiles(); + + + } + + ngOnInit(): void { + if (this.files.length > 0) + this.selectFile(0); + else { + this.selectNewFile(); + } + } + + displayFile(){ + if(this.type == FolderType.Dataset) + this.formDataset!.dataset = <Dataset>this.fileToDisplay; + } + + hoverOverFile(i: number) { + this.hoveringOverFileIndex = i; + if (i != -1) { + this.fileToDisplay = this.files[i]; + } else { + if (this.newFileSelected) { + this.fileToDisplay = this.newFile; + } else { + this.fileToDisplay = this.files[this.selectedFileIndex]; + } + } + this.displayFile(); + } + + selectNewFile() { + if (!this.newFile) { + this.createNewFile(); + } + this.fileToDisplay = this.newFile; + this.selectedFile = this.newFile; + this.newFileSelected = true; + this.listView = false; + this.selectedFileChanged.emit(this.newFile); + this.displayFile(); + } + + selectFile(index: number) { + this.selectedFile = this.filteredFiles[index]; + this.fileToDisplay = this.filteredFiles[index]; + this.newFileSelected = false; + this.listView = false; + this.selectedFileChanged.emit(this.selectedFile); + this.displayFile(); + } + + createNewFile() { + if (this.type == FolderType.Dataset) { + this.newFile = new Dataset(); + } else if (this.type == FolderType.Model) { + this.newFile = new Model(); + } + } + + ok() { + this.okPressed.emit(); + } + + refreshFiles(){ + this.datasetsService.getMyDatasets().subscribe((datasets) => { + this.folders[TabType.MyDatasets] = datasets; + }); + + this.datasetsService.getPublicDatasets().subscribe((datasets) => { + this.folders[TabType.PublicDatasets] = datasets; + }); + + this.modelsService.getMyModels().subscribe((models) => { + this.folders[TabType.MyModels] = models; + }); + + /*this.modelsService.getMyModels().subscribe((models) => { + this.folders[TabType.PublicModels] = models; + });*/ + this.folders[TabType.PublicModels] = []; + + this.experimentsService.getMyExperiments().subscribe((experiments) => { + this.folders[TabType.MyExperiments] = experiments; + }); + + this.files = []; + + this.filteredFiles.length = 0; + this.filteredFiles.push(...this.files); + + this.searchTermsChanged(); + + } + + saveNewFile() { + if(this.type == FolderType.Dataset) + this.formDataset!.uploadDataset(); + } + + /*calcZIndex(i: number) { + let zIndex = (this.files.length - i - 1) + if (this.selectedFileIndex == i) + zIndex = this.files.length + 2; + if (this.hoveringOverFileIndex == i) + zIndex = this.files.length + 3; + return zIndex; + } + + newFileZIndex() { + return (this.files.length + 1); + }*/ + + clearSearchTerm() { + this.searchTerm = ''; + this.searchTermsChanged(); + } + + filteredFiles: FolderFile[] = []; + + searchTermsChanged() { + this.filteredFiles.length = 0; + this.filteredFiles.push(...this.files.filter((file) => file.name.toLowerCase().includes(this.searchTerm.toLowerCase()))); + if (this.selectedFile) { + if (!this.filteredFiles.includes(this.selectedFile)) { + this.selectFile(-1); + } else { + this.selectedFileIndex = this.filteredFiles.indexOf(this.selectedFile); + } + } + } + + listView: boolean = false; + + toggleListView() { + this.listView = !this.listView; + } + + deleteFile() { + console.log('delete'); + } + + folders: { [tab: number]: FolderFile[] } = {}; + + tabTitles: { [tab: number]: string } = { + [TabType.File]: 'Fajl', + [TabType.NewFile]: 'Novi fajl', + [TabType.MyDatasets]: 'Moji izvori podataka', + [TabType.PublicDatasets]: 'Javni izvori podataka', + [TabType.MyModels]: 'Moje konfiguracije neuronske mreže', + [TabType.PublicModels]: 'Javne konfiguracije neuronske mreže', + [TabType.MyExperiments]: 'Eksperimenti', + }; + + FolderType = FolderType; + + TabType = TabType; + + @Input() tabsToShow: TabType[] = [ + TabType.MyDatasets, + TabType.PublicDatasets, + TabType.MyModels, + TabType.PublicModels, + TabType.MyExperiments, + TabType.File + ] + + @Input() selectedTab: TabType = TabType.NewFile; + hoverTab: TabType = TabType.None; + + selectTab(tab: TabType) { + this.checkListView(tab); + this.selectedTab = tab; + this.files = this.folders[tab]; + + this.searchTermsChanged(); + } + + checkListView(tab: TabType) { + switch (tab) { + case TabType.File: + case TabType.NewFile: + case TabType.None: + this.listView = false; + break; + case TabType.MyExperiments: + case TabType.MyDatasets: + case TabType.MyModels: + case TabType.PublicDatasets: + case TabType.PublicModels: + this.listView = true; + break; + } + } + + hoverOverTab(tab: TabType) { + this.checkListView(tab); + this.hoverTab = tab; + if (tab == TabType.None) { + this.checkListView(this.selectedTab); + this.files = this.folders[this.selectedTab]; + } else { + this.files = this.folders[tab]; + } + this.searchTermsChanged(); + } +} + +export enum Privacy { + Private, + Public +} + +export enum TabType { + NewFile, + File, + MyDatasets, + PublicDatasets, + MyModels, + PublicModels, + MyExperiments, + None +}
\ No newline at end of file |