mirror of
https://github.com/clearml/clearml-web
synced 2025-05-08 14:04:38 +00:00
Added pipeline parameters component and integrated with pipeline dialog.
This commit is contained in:
parent
6f37b4ee0d
commit
ca102ecd2c
@ -1,6 +1,7 @@
|
|||||||
import {ProjectsGetAllResponseSingleSubProjects} from '~/business-logic/model/projects/projectsGetAllResponseSingleSubProjects';
|
import {ProjectsGetAllResponseSingleSubProjects} from '~/business-logic/model/projects/projectsGetAllResponseSingleSubProjects';
|
||||||
import {Stats} from '~/business-logic/model/projects/stats';
|
import {Stats} from '~/business-logic/model/projects/stats';
|
||||||
import {ProjectsGetAllResponseSingleDatasetStats} from '~/business-logic/model/projects/projectsGetAllResponseSingleDatasetStats';
|
import {ProjectsGetAllResponseSingleDatasetStats} from '~/business-logic/model/projects/projectsGetAllResponseSingleDatasetStats';
|
||||||
|
import { PipelinesParameter } from './pipelinesParameter';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pipelines
|
* pipelines
|
||||||
@ -69,5 +70,5 @@ export interface Pipeline {
|
|||||||
own_models?: number;
|
own_models?: number;
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
|
|
||||||
parameters?: Array<object>
|
parameters?: Array<PipelinesParameter>
|
||||||
}
|
}
|
||||||
|
21
src/app/business-logic/model/pipelines/pipelinesParameter.ts
Normal file
21
src/app/business-logic/model/pipelines/pipelinesParameter.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
export interface PipelinesParameter {
|
||||||
|
id?: string;
|
||||||
|
/**
|
||||||
|
* Name of the parameter. The combination of section and name should be unique
|
||||||
|
*/
|
||||||
|
name?: string;
|
||||||
|
/**
|
||||||
|
* Value of the parameter
|
||||||
|
*/
|
||||||
|
value?: string;
|
||||||
|
/**
|
||||||
|
* Type of the parameter. Optional
|
||||||
|
*/
|
||||||
|
type?: string;
|
||||||
|
/**
|
||||||
|
* The parameter description. Optional
|
||||||
|
*/
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
default?:string;
|
||||||
|
}
|
@ -52,6 +52,34 @@
|
|||||||
<textarea class="pipeline-description" name="description" matInput [(ngModel)]="pipeline.description"
|
<textarea class="pipeline-description" name="description" matInput [(ngModel)]="pipeline.description"
|
||||||
#description="ngModel"></textarea>
|
#description="ngModel"></textarea>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
<div class="parameters">
|
||||||
|
<div class="search">
|
||||||
|
<mat-label>Parameters</mat-label>
|
||||||
|
<sm-search search-button
|
||||||
|
#search
|
||||||
|
class="table-search"
|
||||||
|
[value]="searchedText"
|
||||||
|
[enableNavigation]="true"
|
||||||
|
[minimumChars]="1"
|
||||||
|
[debounceTime]="0"
|
||||||
|
[expandOnHover]="true"
|
||||||
|
[searchResultsCount]="searchResultsCount"
|
||||||
|
[searchCounterIndex]="pipelineParamsForm.matchIndex"
|
||||||
|
(valueChanged)="searchTable($event)"
|
||||||
|
></sm-search>
|
||||||
|
</div>
|
||||||
|
<sm-pipeline-parameters
|
||||||
|
#pipelineParamsForm
|
||||||
|
class="form-section"
|
||||||
|
[section]=""
|
||||||
|
[formData]="pipeline?.parameters"
|
||||||
|
(formDataChanged)="onFormValuesChanged($event)"
|
||||||
|
(searchCounterChanged)="searchCounterChanged($event)"
|
||||||
|
(resetSearch)="search.clear(false)"
|
||||||
|
(scrollToResultCounterReset)="scrollIndexCounterReset()"
|
||||||
|
></sm-pipeline-parameters>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="w-100 create-pipeline-button">
|
<div class="w-100 create-pipeline-button">
|
||||||
<button class="btn btn-dark-fill center" data-id="Create Pipeline" [disabled]="pipelineForm.invalid"
|
<button class="btn btn-dark-fill center" data-id="Create Pipeline" [disabled]="pipelineForm.invalid"
|
||||||
(click)="send()">CREATE PIPELINE
|
(click)="send()">CREATE PIPELINE
|
||||||
|
@ -1,7 +1,37 @@
|
|||||||
|
@import "variables";
|
||||||
|
|
||||||
:host {
|
:host {
|
||||||
.create-report-button {
|
.create-report-button {
|
||||||
padding: 32px 12px 0;
|
padding: 32px 12px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.parameters {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
margin-bottom: 50px;
|
||||||
|
|
||||||
|
sm-pipeline-parameters {
|
||||||
|
display: block;
|
||||||
|
height: 124px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.search {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
// padding-right: 4px;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
sm-search.table-search {
|
||||||
|
border-radius: 4px;
|
||||||
|
color: $blue-280 !important;
|
||||||
|
background-color: $blue-600 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
mat-form-field {
|
mat-form-field {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
Component,
|
Component,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
Input,
|
Input,
|
||||||
@ -16,6 +17,9 @@ import {rootProjectsPageSize} from '@common/constants';
|
|||||||
import {
|
import {
|
||||||
IOption
|
IOption
|
||||||
} from '@common/shared/ui-components/inputs/select-autocomplete-with-chips/select-autocomplete-with-chips.component';
|
} from '@common/shared/ui-components/inputs/select-autocomplete-with-chips/select-autocomplete-with-chips.component';
|
||||||
|
import { PipelinesParameter } from '~/business-logic/model/pipelines/pipelinesParameter';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
import { PipelineParametersComponent } from '@common/pipelines/pipeline-parameters/pipeline-parameters.component';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -37,16 +41,29 @@ export class CreateNewPipelineFormComponent implements OnChanges, OnDestroy {
|
|||||||
|
|
||||||
public pipelinesNames: Array<string>;
|
public pipelinesNames: Array<string>;
|
||||||
public projectsNames: Array<string>;
|
public projectsNames: Array<string>;
|
||||||
public pipeline: { name: string; description: string; project: { label: string; value: string }, parameters: Array<object>, tags: Array<string> } = {
|
public pipeline: { name: string; description: string; project: { label: string; value: string }, parameters: Array<PipelinesParameter>, tags: Array<string> } = {
|
||||||
name: null,
|
name: null,
|
||||||
description: '',
|
description: '',
|
||||||
project: null,
|
project: null,
|
||||||
parameters: [],
|
parameters: [{
|
||||||
|
name: "Paramter1",
|
||||||
|
value: ""
|
||||||
|
}, {
|
||||||
|
name: "Parameter2",
|
||||||
|
value: ""
|
||||||
|
}],
|
||||||
tags: [],
|
tags: [],
|
||||||
};
|
};
|
||||||
filterText: string = '';
|
filterText: string = '';
|
||||||
isAutoCompleteOpen: boolean;
|
isAutoCompleteOpen: boolean;
|
||||||
|
|
||||||
|
// for parameters
|
||||||
|
@ViewChild('pipelineParamsForm', {static: false}) pipelineParamsForm: PipelineParametersComponent;
|
||||||
|
public searchedText: string;
|
||||||
|
public searchResultsCount: number;
|
||||||
|
public scrollIndexCounter: number;
|
||||||
|
public size$: Observable<number>;
|
||||||
|
|
||||||
@Input() readOnlyProjectsNames: string[];
|
@Input() readOnlyProjectsNames: string[];
|
||||||
@Input() defaultProjectId: string;
|
@Input() defaultProjectId: string;
|
||||||
public loading: boolean;
|
public loading: boolean;
|
||||||
@ -66,6 +83,24 @@ export class CreateNewPipelineFormComponent implements OnChanges, OnDestroy {
|
|||||||
this.projectsNames = this.projectsOptions.map(project => project.label);
|
this.projectsNames = this.projectsOptions.map(project => project.label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constructor(/* private store: Store, protected router: Router, */ private cdr: ChangeDetectorRef) {
|
||||||
|
// this.selectedSectionHyperParams$ = this.store.select(selectExperimentHyperParamsSelectedSectionParams);
|
||||||
|
// this.editable$ = this.store.select(selectIsExperimentEditable);
|
||||||
|
// this.selectedSection$ = this.store.select(selectExperimentHyperParamsSelectedSectionFromRoute);
|
||||||
|
// this.isInDev$ = this.store.select(selectIsSelectedExperimentInDev);
|
||||||
|
// this.saving$ = this.store.select(selectIsExperimentSaving);
|
||||||
|
// this.backdropActive$ = this.store.select(selectBackdropActive);
|
||||||
|
// this.routerConfig$ = this.store.select(selectRouterConfig);
|
||||||
|
// this.selectedExperiment$ = this.store.select(selectSelectedExperiment);
|
||||||
|
// this.size$ = this.store.select(selectSplitSize);
|
||||||
|
|
||||||
|
// this.store.dispatch(setExperimentFormErrors({errors: null}));
|
||||||
|
// this.selectedSectionSubscription = this.selectedSection$.subscribe(section => {
|
||||||
|
// this.selectedSection = section;
|
||||||
|
// this.propSection = section === 'properties';
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
get projects() {
|
get projects() {
|
||||||
return this._projects;
|
return this._projects;
|
||||||
}
|
}
|
||||||
@ -116,6 +151,9 @@ export class CreateNewPipelineFormComponent implements OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
send() {
|
send() {
|
||||||
|
if (this.pipelineParamsForm.formData.length > 0) {
|
||||||
|
this.pipeline.parameters = cloneDeep(this.pipelineParamsForm.formData);
|
||||||
|
}
|
||||||
this.pipelineCreated.emit(this.pipeline);
|
this.pipelineCreated.emit(this.pipeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,5 +172,35 @@ export class CreateNewPipelineFormComponent implements OnChanges, OnDestroy {
|
|||||||
isFocused(locationRef: HTMLInputElement) {
|
isFocused(locationRef: HTMLInputElement) {
|
||||||
return document.activeElement === locationRef;
|
return document.activeElement === locationRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
searchTable(value: string) {
|
||||||
|
// const searchBackward = value === null;
|
||||||
|
// if (this.searchedText !== value && !searchBackward) {
|
||||||
|
// this.searchedText = value;
|
||||||
|
// this.scrollIndexCounter = -1;
|
||||||
|
// this.searchResultsCount = 0;
|
||||||
|
// // this.executionParamsForm.resetIndex();
|
||||||
|
// this.cdr.detectChanges();
|
||||||
|
// }
|
||||||
|
// // this.executionParamsForm.jumpToNextResult(!searchBackward);
|
||||||
|
}
|
||||||
|
|
||||||
|
searchCounterChanged(count: number) {
|
||||||
|
this.searchResultsCount = count;
|
||||||
|
this.cdr.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollIndexCounterReset() {
|
||||||
|
this.scrollIndexCounter = -1;
|
||||||
|
this.cdr.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
onFormValuesChanged(event: { field: string; value: any }) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(event);
|
||||||
|
// this.store.dispatch(updateExperimentAtPath({path: ('hyperparams.' + event.field), value: event.value}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
:host{
|
:host{
|
||||||
width: 640px;
|
width: 690px;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,115 @@
|
|||||||
|
<form [class.editable]="editable" #hyperParameters="ngForm">
|
||||||
|
<cdk-virtual-scroll-viewport #formContainer class="form-container" itemSize="58" minBufferPx="280" >
|
||||||
|
<ng-container *ngIf="editable">
|
||||||
|
<div *ngFor="let parameter of formData; let index= index" class="w-100 d-flex">
|
||||||
|
<mat-form-field
|
||||||
|
[hintLabel]="parameterKey.invalid && parameterKey?.errors?.required? '*Required': ''"
|
||||||
|
appearance="outline"
|
||||||
|
class="strength"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
#parameterKey="ngModel"
|
||||||
|
#row="matInput"
|
||||||
|
[(ngModel)]="parameter['name']"
|
||||||
|
(keydown.enter)="nextRow($event, index)"
|
||||||
|
placeholder="Parameter"
|
||||||
|
name="parameterKey-{{parameter.name}}-{{index}}"
|
||||||
|
matInput
|
||||||
|
smUniqueNameValidator
|
||||||
|
[class.highlight-text]="(searchedText?.length > 0) && parameter['name'].includes(searchedText)"
|
||||||
|
[class.current-match]="searchIndexList[matchIndex]?.index===index && searchIndexList[matchIndex]?.col==='name'"
|
||||||
|
[existingNames]="formNames(parameter.name)"
|
||||||
|
required/>
|
||||||
|
<mat-error *ngIf="parameterKey.invalid && parameterKey?.errors?.required">
|
||||||
|
Required
|
||||||
|
</mat-error>
|
||||||
|
<mat-error *ngIf="parameterKey.invalid && parameterKey?.errors?.smNotAllowedStringsValidator">
|
||||||
|
.(dot) $(dollar) and space are not allowed in parameter key.
|
||||||
|
</mat-error>
|
||||||
|
<mat-error *ngIf="!parameterKey?.errors?.required && parameterKey.invalid && parameterKey?.errors?.uniqueName">
|
||||||
|
key already exists
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="outline"
|
||||||
|
class="strength">
|
||||||
|
<input
|
||||||
|
#parameterValue="ngModel"
|
||||||
|
[(ngModel)]="parameter['value']"
|
||||||
|
(keydown.enter)="nextRow($event, index)"
|
||||||
|
name="parameterValue-{{parameter.name}}-{{index}}"
|
||||||
|
placeholder="Value"
|
||||||
|
[class.highlight-text]="(searchedText?.length > 0) && parameter['value'].includes(searchedText)"
|
||||||
|
[class.current-match]="searchIndexList[matchIndex]?.index===index && searchIndexList[matchIndex]?.col==='value'"
|
||||||
|
matInput/>
|
||||||
|
</mat-form-field>
|
||||||
|
<button class="remove-button btn" (click)="removeRow(index)">
|
||||||
|
<i class="al-icon al-ico-trash al-color blue-400 sm-md pointer flashing-icon"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</cdk-virtual-scroll-viewport>
|
||||||
|
<button *ngIf="editable" class="btn btn-dark-fill add-parameter" (click)="addRow()"><i class="fas fa-plus" data-id="AddParamterButton"></i> ADD PARAMETER</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- <div class="table-container" *ngIf="!editable">
|
||||||
|
<sm-table
|
||||||
|
[columns]="cols"
|
||||||
|
columnResizeMode="fit"
|
||||||
|
[tableData]="formData"
|
||||||
|
[enableTableSearch]="true"
|
||||||
|
[globalFilterFields]="['name', 'value','description']"
|
||||||
|
[noHeader]="true"
|
||||||
|
[simple]="true"
|
||||||
|
[scrollable]="true"
|
||||||
|
[virtualScrollOptions]="{ trackBy: trackByIndex, appendOnly: true, delay: 0, orientation: 'vertical', items: formData, itemSize: 32}"
|
||||||
|
[virtualScroll]="true"
|
||||||
|
[rowHeight]="32"
|
||||||
|
(rowClicked)="rowActivated($event)"
|
||||||
|
>
|
||||||
|
<ng-template pTemplate="body"
|
||||||
|
let-col
|
||||||
|
let-i="rowIndex"
|
||||||
|
let-row="rowData">
|
||||||
|
<ng-container [ngSwitch]="col.id">
|
||||||
|
<ng-container *ngSwitchCase="'description'">
|
||||||
|
<span *ngIf="row.type!=='legacy' && (row.description || row.type)"
|
||||||
|
class="allow-multi-space pointer"
|
||||||
|
customClass="hyper-parameters-tooltip parameter-tooltip"
|
||||||
|
smTooltip="{{(row.type ? ('Type: '+ row.type+'\n') : '') + (row?.description|| '')}}"
|
||||||
|
matTooltipPosition="before"
|
||||||
|
><i class="al-icon al-ico-description"></i></span>
|
||||||
|
</ng-container>
|
||||||
|
<span
|
||||||
|
*ngSwitchCase="'name'"
|
||||||
|
class="ellipsis name"
|
||||||
|
smShowTooltipIfEllipsis
|
||||||
|
matTooltipPosition="before"
|
||||||
|
[matTooltipShowDelay]="250"
|
||||||
|
[smTooltip]="row.name"
|
||||||
|
[smSearchText]="searchedText"
|
||||||
|
highlightClass="highlight-text"
|
||||||
|
[class.current-match]="searchIndexList[matchIndex]?.index===i && searchIndexList[matchIndex]?.col==='name'"
|
||||||
|
>{{row.name}}</span>
|
||||||
|
<ng-container *ngSwitchCase="'value'">
|
||||||
|
<a *ngIf="row.section === 'Datasets' && row.value.length === 32; else: noLink"
|
||||||
|
(click)="$event.preventDefault(); navigateToDataset(row.value)"
|
||||||
|
href="" target="_blank"
|
||||||
|
>{{row.value}}</a>
|
||||||
|
<ng-template #noLink>
|
||||||
|
<span
|
||||||
|
class="ellipsis"
|
||||||
|
smShowTooltipIfEllipsis
|
||||||
|
matTooltipPosition="before"
|
||||||
|
[matTooltipShowDelay]="250"
|
||||||
|
[smTooltip]="row.value"
|
||||||
|
[smSearchText]="searchedText"
|
||||||
|
highlightClass="highlight-text"
|
||||||
|
[class.current-match]="searchIndexList[matchIndex]?.index===i && searchIndexList[matchIndex]?.col==='value'"
|
||||||
|
>{{row.value}}</span>
|
||||||
|
</ng-template>
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
|
</ng-template>
|
||||||
|
</sm-table>
|
||||||
|
</div> -->
|
||||||
|
|
@ -0,0 +1,80 @@
|
|||||||
|
@import "variables";
|
||||||
|
@import "mixins/link";
|
||||||
|
|
||||||
|
:host {
|
||||||
|
@include link();
|
||||||
|
form.editable, .table-container {
|
||||||
|
height: 100%;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.editable {
|
||||||
|
padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-container {
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
input.highlight-text {
|
||||||
|
background: $neon-yellow;
|
||||||
|
&.current-match {
|
||||||
|
background: #f5d655;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-row {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-form-field {
|
||||||
|
flex: 1;
|
||||||
|
padding-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep tr:hover {
|
||||||
|
cursor: auto !important;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
width: 100%;
|
||||||
|
//border-bottom: solid 1px #dee1e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-button {
|
||||||
|
margin-bottom: 22px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-row {
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-match .highlight-text{
|
||||||
|
background-color: #f5d655;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.allow-multi-space {
|
||||||
|
white-space: pre;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-parameter {
|
||||||
|
border: 1px solid $grey-purple;
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: $grey-purple;
|
||||||
|
transition: all ease-in-out 0.15s, border ease-in-out 0.15s;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,193 @@
|
|||||||
|
import {
|
||||||
|
AfterViewInit,
|
||||||
|
Component,
|
||||||
|
EventEmitter,
|
||||||
|
Input, OnChanges,
|
||||||
|
OnDestroy,
|
||||||
|
Output,
|
||||||
|
QueryList, SimpleChanges,
|
||||||
|
ViewChild,
|
||||||
|
ViewChildren
|
||||||
|
} from '@angular/core';
|
||||||
|
import {IExperimentInfoFormComponent} from '~/features/experiments/shared/experiment-info.model';
|
||||||
|
import {cloneDeep} from 'lodash-es';
|
||||||
|
import {v4 as uuidV4} from 'uuid';
|
||||||
|
import {NgForm} from '@angular/forms';
|
||||||
|
import {ParamsItem} from '~/business-logic/model/tasks/paramsItem';
|
||||||
|
import {ISmCol} from '@common/shared/ui-components/data/table/table.consts';
|
||||||
|
import {CdkVirtualScrollViewport} from '@angular/cdk/scrolling';
|
||||||
|
import {Subscription} from 'rxjs';
|
||||||
|
// import {TableComponent} from '@common/shared/ui-components/data/table/table.component';
|
||||||
|
import {isEqual} from 'lodash-es';
|
||||||
|
import {MatInput} from '@angular/material/input';
|
||||||
|
import {Store} from '@ngrx/store';
|
||||||
|
import { navigateToDataset } from '@common/experiments/actions/common-experiments-info.actions';
|
||||||
|
import {trackByIndex} from '@common/shared/utils/forms-track-by';
|
||||||
|
import { PipelinesParameter } from '~/business-logic/model/pipelines/pipelinesParameter';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'sm-pipeline-parameters',
|
||||||
|
templateUrl: './pipeline-parameters.component.html',
|
||||||
|
styleUrls: ['./pipeline-parameters.component.scss']
|
||||||
|
})
|
||||||
|
export class PipelineParametersComponent implements IExperimentInfoFormComponent, OnDestroy, AfterViewInit, OnChanges {
|
||||||
|
private _formData = [] as PipelinesParameter[];
|
||||||
|
private formContainersSub: Subscription;
|
||||||
|
private _editable: boolean = true;
|
||||||
|
private formContainer: CdkVirtualScrollViewport;
|
||||||
|
private clickedRow: number;
|
||||||
|
|
||||||
|
public search = '';
|
||||||
|
public searchIndexList: {index: number; col: string}[] = [];
|
||||||
|
public matchIndex = -1;
|
||||||
|
public cols = [
|
||||||
|
{id: 'name', style: {width: '300px'}},
|
||||||
|
{id: 'value', style: {width: '300px'}},
|
||||||
|
{id: 'description', style: {width: '48px'}}
|
||||||
|
] as ISmCol[];
|
||||||
|
|
||||||
|
|
||||||
|
@ViewChild('hyperParameters') hyperParameters: NgForm;
|
||||||
|
// @ViewChild(TableComponent) executionParametersTable: TableComponent<PipelinesParameter>;
|
||||||
|
|
||||||
|
@ViewChildren('formContainer') formContainers: QueryList<CdkVirtualScrollViewport>;
|
||||||
|
@ViewChildren('row') rows: QueryList<MatInput>;
|
||||||
|
@ViewChild(CdkVirtualScrollViewport) viewPort: CdkVirtualScrollViewport;
|
||||||
|
@Output() formDataChanged = new EventEmitter<{ field: string; value: ParamsItem[] }>();
|
||||||
|
@Output() searchCounterChanged = new EventEmitter<number>();
|
||||||
|
@Output() resetSearch = new EventEmitter();
|
||||||
|
@Output() scrollToResultCounterReset = new EventEmitter();
|
||||||
|
@Input() section;
|
||||||
|
private _originalData: PipelineParametersComponent['formData'];
|
||||||
|
|
||||||
|
// @Input() set size(size: number) {
|
||||||
|
// this.executionParametersTable?.resize();
|
||||||
|
// }
|
||||||
|
|
||||||
|
@Input() set formData(formData) {
|
||||||
|
this._originalData = formData;
|
||||||
|
this._formData = cloneDeep(formData).map((row: ParamsItem) => ({...row, id: uuidV4()}));
|
||||||
|
// console.log(this._formData);
|
||||||
|
}
|
||||||
|
|
||||||
|
get formData() {
|
||||||
|
return this._formData;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// @Input() set editable(editable: boolean) {
|
||||||
|
// this._editable = editable;
|
||||||
|
// this.executionParametersTable?.resize();
|
||||||
|
// editable && window.setTimeout(() => this.rows.first?.focus());
|
||||||
|
// }
|
||||||
|
get editable() {
|
||||||
|
return this._editable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input() searchedText: string;
|
||||||
|
|
||||||
|
constructor(private store: Store) {}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
this.formContainersSub = this.formContainers.changes
|
||||||
|
.subscribe((list: QueryList<CdkVirtualScrollViewport>) => {
|
||||||
|
this.formContainer = list.first;
|
||||||
|
if (this.formContainer && this.clickedRow !== null) {
|
||||||
|
this.formContainer.scrollToIndex(this.clickedRow, 'smooth');
|
||||||
|
this.clickedRow = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//this.executionParametersTable.resize();
|
||||||
|
}
|
||||||
|
|
||||||
|
formNames(name) {
|
||||||
|
return this.formData.filter(parameter => parameter.name !== name).map(parameter => parameter.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
addRow() {
|
||||||
|
this.formData.push({
|
||||||
|
id: uuidV4(),
|
||||||
|
name: '',
|
||||||
|
value: '',
|
||||||
|
description: '',
|
||||||
|
type: ''
|
||||||
|
});
|
||||||
|
window.setTimeout(() => {
|
||||||
|
const height = this.viewPort.elementRef.nativeElement.scrollHeight;
|
||||||
|
this.viewPort.scrollToIndex(height, 'smooth');
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeRow(index) {
|
||||||
|
this.formData.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
rowActivated({data}: { data: PipelinesParameter; e: MouseEvent }) {
|
||||||
|
this.clickedRow = this.formData.findIndex(row => row.name === data.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.formContainersSub?.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
resetIndex() {
|
||||||
|
this.matchIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
jumpToNextResult(forward: boolean) {
|
||||||
|
this.matchIndex = forward ? this.matchIndex + 1 : this.matchIndex - 1;
|
||||||
|
if (this.editable) {
|
||||||
|
this.viewPort.scrollToIndex(this.searchIndexList[this.matchIndex]?.index, 'smooth');
|
||||||
|
} else {
|
||||||
|
// this.executionParametersTable.scrollToIndex(this.searchIndexList[this.matchIndex]?.index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
if (changes?.formData && (!changes.formData?.firstChange) && !isEqual(changes.formData.currentValue, changes.formData.previousValue)) {
|
||||||
|
this.matchIndex = -1;
|
||||||
|
this.searchIndexList = [];
|
||||||
|
this.resetSearch.emit();
|
||||||
|
}
|
||||||
|
if (changes?.searchedText) {
|
||||||
|
let searchResultsCounter = 0;
|
||||||
|
const searchedIndexList = [];
|
||||||
|
if (changes?.searchedText?.currentValue) {
|
||||||
|
this.formData.forEach((parameter, index) => {
|
||||||
|
if (parameter?.name.includes(changes.searchedText.currentValue)) {
|
||||||
|
searchResultsCounter++;
|
||||||
|
searchedIndexList.push({index, col: 'name'});
|
||||||
|
}
|
||||||
|
if (parameter?.value.includes(changes.searchedText.currentValue)) {
|
||||||
|
searchResultsCounter++;
|
||||||
|
searchedIndexList.push({index, col: 'value'});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.searchCounterChanged.emit(searchResultsCounter);
|
||||||
|
this.scrollToResultCounterReset.emit();
|
||||||
|
this.searchIndexList = searchedIndexList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel() {
|
||||||
|
this._formData = cloneDeep(this._originalData).map((row: ParamsItem) => ({...row, id: uuidV4()}));
|
||||||
|
}
|
||||||
|
|
||||||
|
nextRow(event: Event, index: number) {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
if (this.formData.length === index + 1) {
|
||||||
|
this.addRow();
|
||||||
|
}
|
||||||
|
window.setTimeout(()=> this.rows.get(index + 1)?.focus());
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateToDataset(datasetId: string) {
|
||||||
|
this.store.dispatch(navigateToDataset({datasetId}));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected readonly trackByIndex = trackByIndex;
|
||||||
|
}
|
@ -138,7 +138,7 @@ if __name__ == '__main__':
|
|||||||
this.dialog.open(PipelineDialogComponent, {
|
this.dialog.open(PipelineDialogComponent, {
|
||||||
data: {defaultProjectId: this.projectId},
|
data: {defaultProjectId: this.projectId},
|
||||||
panelClass: 'light-theme',
|
panelClass: 'light-theme',
|
||||||
width: '640px'
|
width: '690px'
|
||||||
})
|
})
|
||||||
.afterClosed()
|
.afterClosed()
|
||||||
.subscribe(pipeline => {
|
.subscribe(pipeline => {
|
||||||
|
@ -53,6 +53,8 @@ import { createUserPrefFeatureReducer } from "@common/core/meta-reducers/user-pr
|
|||||||
import { PIPELINES_PREFIX } from "./pipelines.actions";
|
import { PIPELINES_PREFIX } from "./pipelines.actions";
|
||||||
import { PipelineAddStepDialogComponent } from "./pipeline-add-step-dialog/pipeline-add-step-dialog.component";
|
import { PipelineAddStepDialogComponent } from "./pipeline-add-step-dialog/pipeline-add-step-dialog.component";
|
||||||
import { PipelineAddStepFormComponent } from "./pipeline-add-step-dialog/pipeline-add-step-form/pipeline-add-step-form.component";
|
import { PipelineAddStepFormComponent } from "./pipeline-add-step-dialog/pipeline-add-step-form/pipeline-add-step-form.component";
|
||||||
|
import {SortPipe} from '@common/shared/pipes/sort.pipe';
|
||||||
|
import { PipelineParametersComponent } from "./pipeline-parameters/pipeline-parameters.component";
|
||||||
|
|
||||||
export const pipelinesSyncedKeys = ["projects.showPipelineExamples"];
|
export const pipelinesSyncedKeys = ["projects.showPipelineExamples"];
|
||||||
const pipelinesSyncedKeys2 = ['orderBy', 'sortOrder'];
|
const pipelinesSyncedKeys2 = ['orderBy', 'sortOrder'];
|
||||||
@ -103,6 +105,7 @@ const getInitState = (userPreferences: UserPreferences) => ({
|
|||||||
PipelineAddStepFormComponent,
|
PipelineAddStepFormComponent,
|
||||||
EditPipelinePageComponent,
|
EditPipelinePageComponent,
|
||||||
EditPipelineHeaderComponent,
|
EditPipelineHeaderComponent,
|
||||||
|
PipelineParametersComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@ -145,6 +148,7 @@ const getInitState = (userPreferences: UserPreferences) => ({
|
|||||||
ExperimentSharedModule,
|
ExperimentSharedModule,
|
||||||
RefreshButtonComponent,
|
RefreshButtonComponent,
|
||||||
LabeledFormFieldDirective,
|
LabeledFormFieldDirective,
|
||||||
|
SortPipe,
|
||||||
],
|
],
|
||||||
exports: [PipelinesPageComponent, EditPipelinePageComponent],
|
exports: [PipelinesPageComponent, EditPipelinePageComponent],
|
||||||
providers: [
|
providers: [
|
||||||
|
Loading…
Reference in New Issue
Block a user