Skip to content

Commit

Permalink
Merge pull request #76 from UST-QuAntiL/feature/ui-improvements
Browse files Browse the repository at this point in the history
Feature/UI improvements
  • Loading branch information
buehlefs authored Jul 26, 2024
2 parents 68fd312 + 49972dc commit 5ba8b1e
Show file tree
Hide file tree
Showing 16 changed files with 132 additions and 50 deletions.
20 changes: 10 additions & 10 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@

import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatBadgeModule } from '@angular/material/badge';
import { MatButtonModule } from '@angular/material/button';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatChipsModule } from '@angular/material/chips';
Expand All @@ -37,6 +36,7 @@ import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatRadioModule } from '@angular/material/radio';
import { MatSelectModule } from '@angular/material/select';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatTabsModule } from '@angular/material/tabs';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatTooltipModule } from '@angular/material/tooltip';
Expand All @@ -48,16 +48,20 @@ import { IframePreviewComponent } from './components-small/iframe-preview/iframe
import { ImagePreviewComponent } from './components-small/image-preview/image-preview.component';
import { ImportExperimentComponent } from './components-small/import-experiment/import-experiment.component';
import { MarkdownPreviewComponent } from './components-small/markdown-preview/markdown-preview.component';
import { PluginFilterEditorComponent } from './components-small/plugin-filter-editor/plugin-filter-editor.component';
import { PluginFilterNodeComponent } from './components-small/plugin-filter-node/plugin-filter-node.component';
import { PluginLastUsedComponent } from './components-small/plugin-last-used/plugin-last-used.component';
import { PluginListItemComponent } from './components-small/plugin-list-item/plugin-list-item.component';
import { PluginPreviewComponent } from './components-small/plugin-preview/plugin-preview.component';
import { QueryParamPreviewComponent } from './components-small/query-param-preview/query-param-preview.component';
import { RawTextPreviewComponent } from './components-small/raw-text-preview/raw-text-preview.component';
import { StepStatusComponent } from './components-small/step-status/step-status.component';
import { TemplateDetailsComponent } from './components-small/template-details/template-details.component';
import { DataDetailComponent } from './components/data-detail/data-detail.component';
import { DataPreviewComponent } from './components/data-preview/data-preview.component';
import { ExperimentDataComponent } from './components/experiment-data/experiment-data.component';
import { ExperimentTimelineComponent } from './components/experiment-timeline/experiment-timeline.component';
import { ExperimentWorkspaceDetailComponent } from './components/experiment-workspace-detail/experiment-workspace-detail.component';
import { ExperimentWorkspaceComponent } from './components/experiment-workspace/experiment-workspace.component';
import { ExperimentComponent } from './components/experiment/experiment.component';
import { ExperimentsPageComponent } from './components/experiments-page/experiments-page.component';
Expand All @@ -70,23 +74,18 @@ import { PluginUiframeComponent } from './components/plugin-uiframe/plugin-uifra
import { PreviewListComponent } from './components/preview-list/preview-list.component';
import { SettingsPageComponent } from './components/settings-page/settings-page.component';
import { SubstepsDetailsComponent } from './components/substeps-details/substeps-details.component';
import { TabGroupListComponent } from './components/tab-group-list/tab-group-list.component';
import { TimelineStepNavComponent } from './components/timeline-step-nav/timeline-step-nav.component';
import { TimelineStepComponent } from './components/timeline-step/timeline-step.component';
import { TimelineSubstepsComponent } from './components/timeline-substeps/timeline-substeps.component';
import { ChangeUiTemplateDialog } from './dialogs/change-ui-template/change-ui-template.dialog';
import { ChooseDataDialog } from './dialogs/choose-data/choose-data.dialog';
import { ChoosePluginDialog } from './dialogs/choose-plugin/choose-plugin.dialog';
import { ChooseTemplateDialog } from './dialogs/choose-template/choose-template.dialog';
import { CreateExperimentDialog } from './dialogs/create-experiment/create-experiment.dialog';
import { DeleteDialog } from './dialogs/delete-dialog/delete-dialog.dialog';
import { ExportExperimentDialog } from './dialogs/export-experiment/export-experiment.dialog';
import { MarkdownHelpDialog } from './dialogs/markdown-help/markdown-help.dialog';
import { ChangeUiTemplateDialog } from './dialogs/change-ui-template/change-ui-template.dialog';
import { ReactiveFormsModule } from '@angular/forms';
import { TemplateDetailsComponent } from './components-small/template-details/template-details.component';
import { ExperimentWorkspaceDetailComponent } from './components/experiment-workspace-detail/experiment-workspace-detail.component';
import { PluginFilterNodeComponent } from './components-small/plugin-filter-node/plugin-filter-node.component';
import { PluginFilterEditorComponent } from './components-small/plugin-filter-editor/plugin-filter-editor.component';
import { TabGroupListComponent } from './components/tab-group-list/tab-group-list.component';
import { ChooseTemplateDialog } from './dialogs/choose-template/choose-template.dialog';

@NgModule({
declarations: [
Expand Down Expand Up @@ -165,6 +164,7 @@ import { ChooseTemplateDialog } from './dialogs/choose-template/choose-template.
MatRadioModule,
MatMenuModule,
MatBadgeModule,
MatRadioModule,
],
providers: [
{ provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: { appearance: "outline" } }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
Filter
</button>
</div>
<mat-card appearance="outlined" *ngIf="!isEmpty" class="filter-node mat-elevation-z0" [ngClass]="{'filter-leaf' : type !== 'and' && type !== 'or'}">
<mat-card appearance="outlined" *ngIf="!isEmpty" class="filter-node mat-elevation-z0"
[ngClass]="{'filter-leaf' : type !== 'and' && type !== 'or'}">
<mat-card-header>
<ng-container *ngIf="children != null">
<div class="config-header">
Expand All @@ -22,11 +23,13 @@
<mat-button-toggle value="or">OR</mat-button-toggle>
</mat-button-toggle-group>
<div class="config-header-buttons">
<button mat-raised-button *ngIf="depth < 2" type="button" (click)="addFilter('and')" color="primary">
<button mat-raised-button *ngIf="depth < 2" type="button" (click)="addFilter('and')"
color="primary">
<mat-icon>add</mat-icon>
AND
</button>
<button mat-raised-button *ngIf="depth < 2" type="button" (click)="addFilter('or')" color="primary">
<button mat-raised-button *ngIf="depth < 2" type="button" (click)="addFilter('or')"
color="primary">
<mat-icon>add</mat-icon>
OR
</button>
Expand All @@ -45,7 +48,8 @@
<ng-container *ngIf="children != null">
<span *ngIf="inverted" class="t-subheading">Not:</span>
<qhana-plugin-filter-node *ngFor="let child of children; let i = index" [filterIn]="children[i]"
[depth]="depth + 1" (filterOut)="updateChild($event, i)" (delete)="deleteChild(i)"></qhana-plugin-filter-node>
[depth]="depth + 1" (filterOut)="updateChild($event, i)"
(delete)="deleteChild(i)"></qhana-plugin-filter-node>
</ng-container>
<div class="config-filter" *ngIf="value != null">
<span *ngIf="inverted" class="t-subheading">Not:</span>
Expand All @@ -69,12 +73,20 @@
<option value="dataloader"></option>
<option value="interaction"></option>
</datalist>
<mat-hint *ngIf="type === 'version'">
Examples: ">= 0.1.0", "== 1.0.2", "< 2.0.0", ">= 1.0.0, < 2.0.0" , "!= 1.0.14"
</mat-hint>
<mat-hint *ngIf="type === 'type'">
Examples: "processing", "conversion", "dataloader", "visualization", "interaction"
</mat-hint>
</mat-form-field>
<button mat-raised-button type="button" (click)="delete.emit()">
<mat-icon>delete</mat-icon>
</button>
</div>
<mat-checkbox [checked]="inverted" (change)="invertFilter($event)">Invert - <i>Negate this filter</i></mat-checkbox>
<mat-checkbox [checked]="inverted" (change)="invertFilter($event)">
Invert - <i>Negate this filter</i>
</mat-checkbox>
</mat-card-content>
</mat-card>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
<h2>Template Tab</h2>
<form [formGroup]="templateForm" (ngSubmit)="onSubmit()">
<mat-form-field class="form-field">
<mat-label>Location</mat-label>
<mat-select formControlName="location">
<mat-option *ngFor="let location of tabGroupNameOverrides | keyvalue" [value]="location.key">
<div class="location-chooser">
<p style="margin-bottom: 0;">Show tab in:</p>
<mat-radio-group aria-label="Select the location of the template tab" formControlName="location"
[required]="true">
<mat-radio-button [value]="location.key" *ngFor="let location of tabGroupNameOverrides">
{{location.value}}
</mat-option>
</mat-select>
</mat-form-field>
</mat-radio-button>
</mat-radio-group>
</div>
<mat-form-field class="form-field">
<mat-label>Name:</mat-label>
<input matInput formControlName="name">
Expand All @@ -21,7 +22,8 @@ <h2>Template Tab</h2>
<mat-label>Sort Key:</mat-label>
<input matInput type="number" formControlName="sortKey">
</mat-form-field>
<qhana-plugin-filter-editor [tabLink]="tabLink" (filterEmitter)="filterString = $event"></qhana-plugin-filter-editor>
<qhana-plugin-filter-editor [tabLink]="tabLink"
(filterEmitter)="filterString = $event"></qhana-plugin-filter-editor>
<br>
<button mat-raised-button type="submit" color="primary">{{templateLink ? 'Create' : 'Update'}} Tab</button>
</form>
</form>
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
resize: none
overflow: hidden

.location-chooser
margin-block-end: 1rem

details
margin-left: 2rem

Expand All @@ -19,4 +22,4 @@ dd
font-size: 0.9rem

dd
margin-bottom: 1em
margin-bottom: 1em
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { Component, Input, OnInit } from '@angular/core';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { ApiLink, ApiResponse } from 'src/app/services/api-data-types';
import { PluginRegistryBaseService } from 'src/app/services/registry.service';
import { TemplateApiObject, TemplateTabApiObject } from 'src/app/services/templates.service';
import { TAB_GROUP_NAME_OVERRIDES } from 'src/app/services/templates.service';
import { TAB_GROUP_NAME_OVERRIDES, TemplateApiObject, TemplateTabApiObject } from 'src/app/services/templates.service';

export function isInSetValidator(validValues: any[]): Validators {
return (control: FormControl): { [key: string]: any } | null => {
Expand All @@ -25,7 +24,18 @@ export class TemplateDetailsComponent implements OnInit {

filterString: string = "{}";

tabGroupNameOverrides = { ...TAB_GROUP_NAME_OVERRIDES };
tabGroupNameOverrides = Object.entries(TAB_GROUP_NAME_OVERRIDES)
.map(entry => { return { key: entry[0], value: entry[1] }; })
.sort((a, b) => {
if (a.key === "workspace") {
return -1;
}
else if (b.key === "workspace") {
return 1;
}
return a.value.localeCompare(b.value)
});


private initialValues = {
name: "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ <h1 class="t-page-headline">Experiment Workspace</h1>
[hidden]="!expandedPluginDescription">expand_more</mat-icon>
<span class="t-title">{{activePlugin.title}}
(&#64;{{activePlugin.version}})</span>
<span class="t-title plugin-identifier">{{activePlugin.identifier}}</span>
<div class="spacer"></div>
<qhana-plugin-last-used class="plugin-status" [plugin]="activePlugin" [spinner]="20">
</qhana-plugin-last-used>
</h2>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
justify-content: space-between
align-items: center

.plugin-identifier
font-weight: 200

.spacer
flex-grow: 1

.card-list
margin-inline: -16px

Expand Down
12 changes: 6 additions & 6 deletions src/app/components/navbar/navbar.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,25 @@
</a>

<ng-container *ngIf="(currentExperiment|async) != null">
<a class="navigation-link" mat-button [routerLink]="['/experiments', (experimentId|async), 'info']" routerLinkActive="active">
<a class="navigation-link" mat-button [routerLink]="['/experiments', (experimentId|async), 'info']"
routerLinkActive="active">
Info
</a>
<a class="navigation-link" mat-button [routerLink]="['/experiments', (experimentId|async), 'workspace']"
queryParamsHandling="merge" routerLinkActive="active">
Workspace
</a>
<a class="navigation-link" mat-button [routerLink]="['/experiments', (experimentId|async), 'data']"
queryParamsHandling="merge" routerLinkActive="active">
queryParamsHandling="merge" routerLinkActive="active">
Data
</a>
<a class="navigation-link" mat-button [routerLink]="['/experiments', (experimentId|async), 'timeline']"
queryParamsHandling="merge" routerLinkActive="active">
queryParamsHandling="merge" routerLinkActive="active">
Timeline
</a>
<a class="navigation-link" mat-button
[routerLink]="['/experiments', (experimentId|async), 'extra', tab.resourceKey?.uiTemplateTabId]"
queryParamsHandling="merge" routerLinkActive="active"
*ngFor="let tab of experimentExtraTabs">
queryParamsHandling="merge" routerLinkActive="active" *ngFor="let tab of experimentExtraTabs">
{{tab.name}}
</a>
</ng-container>
Expand All @@ -36,7 +36,7 @@

<div class="toolbar-spacer"></div>

<div [hidden]="(currentExperiment|async) == null">
<div class="experiment-switcher" [hidden]="(currentExperiment|async) == null">
<span>{{currentExperiment|async}}</span>
<a class="navigation-link" mat-icon-button [hidden]="(currentExperiment|async) == null" [routerLink]="['/']"
aria-label="change experiment">
Expand Down
4 changes: 4 additions & 0 deletions src/app/components/navbar/navbar.component.sass
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
.toolbar-spacer
flex-grow: 1

.experiment-switcher
display: flex
align-items: center

.settings-link
display: inline-flex
align-items: center
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,13 @@ export class PluginSidebarComponent implements OnInit, OnDestroy {
});

this.registry.resolveRecursiveRels([["plugin", "collection"]]).then((apiLink) => {
const pluginTypes = new Map<string, string>([["dataloader", "Dataloader Plugins"], ["processing", "Processing Plugins"], ["conversion", "Conversion Plugins"], ["visualization", "Visualization Plugins"]]);
const pluginTypes = new Map<string, string>([
["dataloader", "Dataloader Plugins"],
["processing", "Processing Plugins"],
["conversion", "Conversion Plugins"],
["visualization", "Visualization Plugins"],
["interaction", "Interaction Plugins"]
]);
pluginTypes.forEach((name, pluginType) => {
const query = new URLSearchParams();
query.set("type", pluginType)
Expand Down
3 changes: 2 additions & 1 deletion src/app/components/plugin-tab/plugin-tab.component.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<nav mat-tab-nav-bar class="big-nav-tabs" *ngIf="plugins.length > 1">
<a mat-tab-link
[routerLink]="['/experiments', currentExperimentId, 'extra', currentTabId, 'plugin', plugin.resourceKey?.pluginId]"
[active]="plugin.resourceKey?.pluginId === currentPluginId" *ngFor="let plugin of plugins">
[queryParamsHandling]="'preserve'" [active]="plugin.resourceKey?.pluginId === currentPluginId"
*ngFor="let plugin of plugins">
{{plugin.name}}
</a>
</nav>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<mat-icon *ngIf="!buttonsLeft" inline="false" aria-hidden="true">chevron_left</mat-icon>
</button>
<mat-divider [vertical]="true"></mat-divider>
<button mat-icon-button (click)="autofillLatest()" [disabled]="autofillData == null">
<button mat-icon-button style="text-align: left;" (click)="autofillLatest()" [disabled]="autofillData == null">
<mat-icon inline="true" aria-hidden="true">rocket_launch</mat-icon>
</button>
<button mat-icon-button (click)="fullscreen=!fullscreen">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
.floating-buttons
display: none
position: absolute
height: 2.5rem
height: 3rem
top: calc(-2.5rem - 3px)
right: 0.3rem
color: var(--text-card)
Expand Down
32 changes: 31 additions & 1 deletion src/app/components/plugin-uiframe/plugin-uiframe.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,22 @@ export class PluginUiframeComponent implements OnChanges, OnDestroy {
return;
}

private calculateMaxHeight(): number | undefined {
const maxHeight = window.visualViewport?.height;
if (maxHeight == null) {
return undefined;
}
let offsetTop = 4; // start with 4px offset to avoid scrollbar
let currentElement: HTMLElement | Element | null = this.uiframe?.nativeElement ?? null;

while (currentElement != null && currentElement instanceof HTMLElement) {
offsetTop += currentElement.offsetTop ?? 0; // add up all ofets until root layout
currentElement = currentElement.offsetParent ?? null;
}

return maxHeight - offsetTop;
}

private selectPlugin(request: PluginUrlRequest) {
if (this.dialogActive) {
return; // only ever show one dialog at a time
Expand Down Expand Up @@ -471,7 +487,21 @@ export class PluginUiframeComponent implements OnChanges, OnDestroy {
}
} else { // assume object message
if (data?.type === "ui-resize") {
this.frontendHeight = Math.max(data.height ?? 100, 20);
let newHeight = Math.max(data.height ?? 100, 20);
if (data.targetHeight != null && Number.isFinite(data.targetHeight) && data.targetHeight > 20) {
// directly use target height (if it is the higher value)
newHeight = Math.max(newHeight, data.targetHeight);
// use target height * 2 as the maximum height (to allow for some slack)
newHeight = Math.min(newHeight, 2 * data.targetHeight);
}
if (data.targetHeight === "full") {
// if target height is full set iframe to max height to fill the current screen
const maxHeight = this.calculateMaxHeight();
if (maxHeight != null) {
newHeight = maxHeight;
}
}
this.frontendHeight = newHeight;
}
if (data?.type === "form-submit") {
if (!isFormSubmitData(data)) {
Expand Down
Loading

0 comments on commit 5ba8b1e

Please sign in to comment.