Refactors dialogs

This commit is contained in:
Aaron Louie 2020-01-28 16:43:51 -05:00
parent 35ec329f9e
commit 5d53350adc
22 changed files with 314 additions and 73 deletions

View File

@ -0,0 +1,21 @@
import {FileType} from 'sartography-workflow-lib';
export interface FileMetaDialogData {
fileName: string;
fileType: FileType;
}
export interface NewFileDialogData {
fileType: FileType;
}
export interface OpenFileDialogData {
file: File;
}
export interface WorkflowSpecDialogData {
id: string;
name: string;
display_name: string;
description: string;
}

View File

@ -1,6 +0,0 @@
import {FileType} from 'sartography-workflow-lib';
export interface FileMetaDialogData {
fileName: string;
fileType: FileType;
}

View File

@ -1,6 +0,0 @@
export interface WorkflowSpecDialogData {
id: string;
name: string;
display_name: string;
description: string;
}

View File

@ -6,10 +6,10 @@ describe('GetIconCodePipe', () => {
beforeEach(() => { beforeEach(() => {
pipe = new GetIconCodePipe(); pipe = new GetIconCodePipe();
}) });
it('create an instance', () => { it('create an instance', () => {
expect(pipe).toBeTruthy() expect(pipe).toBeTruthy();
}); });
it('should get an icon code for each file type', () => { it('should get an icon code for each file type', () => {

View File

@ -8,8 +8,7 @@ import {WorkflowSpecListComponent} from './workflow-spec-list/workflow-spec-list
const appRoutes: Routes = [ const appRoutes: Routes = [
{ path: 'modeler/:workflowSpecId', component: ModelerComponent }, { path: 'modeler/:workflowSpecId', component: ModelerComponent },
{ path: 'modeler/:workflowSpecId/:fileMetaId', component: ModelerComponent }, { path: 'modeler/:workflowSpecId/:fileMetaId', component: ModelerComponent },
{ path: 'workflow-specs', component: WorkflowSpecListComponent }, { path: '', component: WorkflowSpecListComponent },
{ path: '', redirectTo: '/workflow-specs', pathMatch: 'full' },
]; ];
@NgModule({ @NgModule({

View File

@ -29,6 +29,8 @@ import {ModelerComponent} from './modeler/modeler.component';
import {WorkflowSpecDialogComponent} from './workflow-spec-dialog/workflow-spec-dialog.component'; import {WorkflowSpecDialogComponent} from './workflow-spec-dialog/workflow-spec-dialog.component';
import {WorkflowSpecListComponent} from './workflow-spec-list/workflow-spec-list.component'; import {WorkflowSpecListComponent} from './workflow-spec-list/workflow-spec-list.component';
import { GetIconCodePipe } from './_pipes/get-icon-code.pipe'; import { GetIconCodePipe } from './_pipes/get-icon-code.pipe';
import { NewFileDialogComponent } from './new-file-dialog/new-file-dialog.component';
import { OpenFileDialogComponent } from './open-file-dialog/open-file-dialog.component';
export class ThisEnvironment implements AppEnvironment { export class ThisEnvironment implements AppEnvironment {
production = environment.production; production = environment.production;
@ -47,6 +49,8 @@ export class ThisEnvironment implements AppEnvironment {
FileListComponent, FileListComponent,
WorkflowSpecDialogComponent, WorkflowSpecDialogComponent,
GetIconCodePipe, GetIconCodePipe,
NewFileDialogComponent,
OpenFileDialogComponent,
], ],
imports: [ imports: [
BrowserAnimationsModule, BrowserAnimationsModule,
@ -78,7 +82,9 @@ export class ThisEnvironment implements AppEnvironment {
bootstrap: [AppComponent], bootstrap: [AppComponent],
entryComponents: [ entryComponents: [
FileMetaDialogComponent, FileMetaDialogComponent,
WorkflowSpecDialogComponent NewFileDialogComponent,
OpenFileDialogComponent,
WorkflowSpecDialogComponent,
], ],
providers: [{provide: 'APP_ENVIRONMENT', useClass: ThisEnvironment}] providers: [{provide: 'APP_ENVIRONMENT', useClass: ThisEnvironment}]
}) })

View File

@ -21,7 +21,7 @@ export class DiagramComponent implements ControlValueAccessor, AfterViewInit {
@ViewChild('containerRef', {static: true}) containerRef: ElementRef; @ViewChild('containerRef', {static: true}) containerRef: ElementRef;
@ViewChild('propertiesRef', {static: true}) propertiesRef: ElementRef; @ViewChild('propertiesRef', {static: true}) propertiesRef: ElementRef;
@Output() private importDone: EventEmitter<ImportEvent> = new EventEmitter(); @Output() private importDone: EventEmitter<ImportEvent> = new EventEmitter();
private diagramType: FileType = FileType.BPMN; private diagramType: FileType;
private modeler: BpmnModeler | DmnModeler; private modeler: BpmnModeler | DmnModeler;
private xml = ''; private xml = '';
private disabled = false; private disabled = false;

View File

@ -5,8 +5,7 @@ import {MatFormFieldModule} from '@angular/material/form-field';
import {MatInputModule} from '@angular/material/input'; import {MatInputModule} from '@angular/material/input';
import {BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations'; import {BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations';
import {FileType} from 'sartography-workflow-lib'; import {FileType} from 'sartography-workflow-lib';
import {FileMetaDialogData} from '../_interfaces/file-meta-dialog-data'; import {FileMetaDialogData} from '../_interfaces/dialog-data';
import {FileMetaDialogComponent} from './file-meta-dialog.component'; import {FileMetaDialogComponent} from './file-meta-dialog.component';
describe('EditFileMetaDialogComponent', () => { describe('EditFileMetaDialogComponent', () => {

View File

@ -1,7 +1,7 @@
import {Component, Inject} from '@angular/core'; import {Component, Inject} from '@angular/core';
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
import {FileMetaDialogData} from '../_interfaces/file-meta-dialog-data'; import {FileMetaDialogData} from '../_interfaces/dialog-data';
import {cleanUpFilename, toSnakeCase, trimString} from '../_util/string-clean'; import {cleanUpFilename} from '../_util/string-clean';
@Component({ @Component({
selector: 'app-new-file-dialog', selector: 'app-new-file-dialog',

View File

@ -8,7 +8,7 @@
({{workflowSpec.name}}) ({{workflowSpec.name}})
</mat-toolbar-row> </mat-toolbar-row>
<mat-toolbar-row> <mat-toolbar-row>
<button mat-button [matMenuTriggerFor]="newMenu" title="Open diagram"> <button #newMenuTrigger="matMenuTrigger" mat-button [matMenuTriggerFor]="newMenu" title="Open diagram">
<mat-icon>insert_drive_file</mat-icon> <mat-icon>insert_drive_file</mat-icon>
<mat-icon>arrow_drop_down</mat-icon> <mat-icon>arrow_drop_down</mat-icon>
</button> </button>
@ -23,7 +23,7 @@
</button> </button>
</mat-menu> </mat-menu>
<button mat-button [matMenuTriggerFor]="importMenu" title="Open diagram"> <button #importMenuTrigger="matMenuTrigger" mat-button [matMenuTriggerFor]="importMenu" title="Open diagram">
<mat-icon>folder</mat-icon> <mat-icon>folder</mat-icon>
<mat-icon>arrow_drop_down</mat-icon> <mat-icon>arrow_drop_down</mat-icon>
</button> </button>
@ -36,7 +36,7 @@
<button <button
mat-menu-item mat-menu-item
*ngFor="let bf of bpmnFiles" *ngFor="let bf of bpmnFiles"
(click)="loadDbFile(bf)" [routerLink]="['/modeler', workflowSpec.id, bf.id]"
[matTooltip]="getFileMetaTooltipText(bf)" [matTooltip]="getFileMetaTooltipText(bf)"
matTooltipClass="tooltip-text" matTooltipClass="tooltip-text"
matTooltipPosition="right" matTooltipPosition="right"

View File

@ -27,7 +27,7 @@ import {
} from 'sartography-workflow-lib'; } from 'sartography-workflow-lib';
import {BPMN_DIAGRAM, BPMN_DIAGRAM_WITH_WARNINGS} from '../../testing/mocks/diagram.mocks'; import {BPMN_DIAGRAM, BPMN_DIAGRAM_WITH_WARNINGS} from '../../testing/mocks/diagram.mocks';
import {BpmnWarning} from '../_interfaces/bpmn-warning'; import {BpmnWarning} from '../_interfaces/bpmn-warning';
import {FileMetaDialogData} from '../_interfaces/file-meta-dialog-data'; import {DialogData} from '../_interfaces/dialog-data';
import {GetIconCodePipe} from '../_pipes/get-icon-code.pipe'; import {GetIconCodePipe} from '../_pipes/get-icon-code.pipe';
import {DiagramComponent} from '../diagram/diagram.component'; import {DiagramComponent} from '../diagram/diagram.component';
import {FileMetaDialogComponent} from '../file-meta-dialog/file-meta-dialog.component'; import {FileMetaDialogComponent} from '../file-meta-dialog/file-meta-dialog.component';
@ -268,7 +268,7 @@ describe('ModelerComponent', () => {
}); });
it('should open file metadata dialog', () => { it('should open file metadata dialog', () => {
const data: FileMetaDialogData = { const data: DialogData = {
fileName: 'after', fileName: 'after',
fileType: FileType.BPMN, fileType: FileType.BPMN,
}; };
@ -283,7 +283,7 @@ describe('ModelerComponent', () => {
it('should update file metadata for existing file', () => { it('should update file metadata for existing file', () => {
const newXml = '<xml>New Value</xml>'; const newXml = '<xml>New Value</xml>';
const data: FileMetaDialogData = { const data: DialogData = {
fileName: mockFileMeta0.name, fileName: mockFileMeta0.name,
fileType: FileType.BPMN, fileType: FileType.BPMN,
}; };
@ -315,7 +315,7 @@ describe('ModelerComponent', () => {
it('should create new file metadata for new file', () => { it('should create new file metadata for new file', () => {
const newXml = '<xml>New Value</xml>'; const newXml = '<xml>New Value</xml>';
const data: FileMetaDialogData = { const data: DialogData = {
fileName: mockFileMeta0.name, fileName: mockFileMeta0.name,
fileType: FileType.BPMN, fileType: FileType.BPMN,
}; };

View File

@ -2,14 +2,16 @@ import {DatePipe} from '@angular/common';
import {AfterViewInit, Component, ViewChild} from '@angular/core'; import {AfterViewInit, Component, ViewChild} from '@angular/core';
import {MatDialog} from '@angular/material/dialog'; import {MatDialog} from '@angular/material/dialog';
import {MatSnackBar} from '@angular/material/snack-bar'; import {MatSnackBar} from '@angular/material/snack-bar';
import {ActivatedRoute} from '@angular/router'; import {ActivatedRoute, Router} from '@angular/router';
import {ApiService, FileMeta, FileType, WorkflowSpec} from 'sartography-workflow-lib'; import {ApiService, FileMeta, FileType, WorkflowSpec} from 'sartography-workflow-lib';
import {BpmnWarning} from '../_interfaces/bpmn-warning'; import {BpmnWarning} from '../_interfaces/bpmn-warning';
import {FileMetaDialogData} from '../_interfaces/file-meta-dialog-data'; import {FileMetaDialogData, NewFileDialogData, OpenFileDialogData} from '../_interfaces/dialog-data';
import {ImportEvent} from '../_interfaces/import-event'; import {ImportEvent} from '../_interfaces/import-event';
import {getDiagramTypeFromXml} from '../_util/diagram-type'; import {getDiagramTypeFromXml} from '../_util/diagram-type';
import {DiagramComponent} from '../diagram/diagram.component'; import {DiagramComponent} from '../diagram/diagram.component';
import {FileMetaDialogComponent} from '../file-meta-dialog/file-meta-dialog.component'; import {FileMetaDialogComponent} from '../file-meta-dialog/file-meta-dialog.component';
import {NewFileDialogComponent} from '../new-file-dialog/new-file-dialog.component';
import {OpenFileDialogComponent} from '../open-file-dialog/open-file-dialog.component';
@Component({ @Component({
selector: 'app-modeler', selector: 'app-modeler',
@ -42,21 +44,19 @@ export class ModelerComponent implements AfterViewInit {
private snackBar: MatSnackBar, private snackBar: MatSnackBar,
public dialog: MatDialog, public dialog: MatDialog,
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router,
) { ) {
this.route.queryParams.subscribe(q => { this.route.queryParams.subscribe(q => {
if (q && q.action) { if (q && q.action) {
if (q.action === 'openFile') { if (q.action === 'openFile') {
this.expandToolbar = true; this.openFileDialog();
this.openMethod = 'file';
} else if (q.action === 'newFile') { } else if (q.action === 'newFile') {
this.newFileDialog();
} }
} }
}) });
this.route.paramMap.subscribe(paramMap => { this.route.paramMap.subscribe(paramMap => {
console.log('paramMap', paramMap);
this.workflowSpecId = paramMap.get('workflowSpecId'); this.workflowSpecId = paramMap.get('workflowSpecId');
this.fileMetaId = parseInt(paramMap.get('fileMetaId'), 10); this.fileMetaId = parseInt(paramMap.get('fileMetaId'), 10);
this.loadFilesFromDb(); this.loadFilesFromDb();
@ -163,14 +163,35 @@ export class ModelerComponent implements AfterViewInit {
this.diagramComponent.openDiagram(undefined, diagramType); this.diagramComponent.openDiagram(undefined, diagramType);
} }
openFileDialog() {
const dialogRef = this.dialog.open(OpenFileDialogComponent, {
});
dialogRef.afterClosed().subscribe((data: OpenFileDialogData) => {
if (data && data.file) {
this.diagramFile = data.file;
this.onSubmitFileToOpen();
}
});
}
newFileDialog() {
const dialogRef = this.dialog.open(NewFileDialogComponent, {
});
dialogRef.afterClosed().subscribe((data: NewFileDialogData) => {
if (data && data.fileType) {
this.diagramType = data.fileType;
this.newDiagram(data.fileType);
}
});
}
editFileMeta() { editFileMeta() {
// Open new filename/workflow spec dialog
const dialogRef = this.dialog.open(FileMetaDialogComponent, { const dialogRef = this.dialog.open(FileMetaDialogComponent, {
height: '400px',
width: '400px',
data: { data: {
fileName: this.diagramFile ? this.diagramFile.name : this.fileName || '', fileName: this.diagramFile ? this.diagramFile.name : this.fileName || '',
fileType: getDiagramTypeFromXml(this.xml), fileType: this.diagramType || getDiagramTypeFromXml(this.xml),
}, },
}); });
@ -245,9 +266,6 @@ export class ModelerComponent implements AfterViewInit {
private _upsertFileMeta(data: FileMetaDialogData) { private _upsertFileMeta(data: FileMetaDialogData) {
if (data.fileName) { if (data.fileName) {
this.xml = this.draftXml; this.xml = this.draftXml;
// Save old workflow spec id, if user wants to change it
const specId = this.workflowSpec ? this.workflowSpec.id : undefined;
const fileMetaId = this.diagramFileMeta ? this.diagramFileMeta.id : undefined; const fileMetaId = this.diagramFileMeta ? this.diagramFileMeta.id : undefined;
const fileType = this.diagramFileMeta ? this.diagramFileMeta.type : FileType.BPMN; const fileType = this.diagramFileMeta ? this.diagramFileMeta.type : FileType.BPMN;
this.diagramFileMeta = { this.diagramFileMeta = {
@ -259,17 +277,17 @@ export class ModelerComponent implements AfterViewInit {
workflow_spec_id: this.workflowSpec.id, workflow_spec_id: this.workflowSpec.id,
}; };
if (specId && fileMetaId) { if (this.workflowSpec && fileMetaId) {
// Update existing file meta // Update existing file meta
this.api.updateFileMeta(specId, this.diagramFileMeta).subscribe(fileMeta => { this.api.updateFileMeta(this.workflowSpec.id, this.diagramFileMeta).subscribe(fileMeta => {
this.loadFilesFromDb(); this.loadFilesFromDb();
this.snackBar.open('Saved changes to workflow spec and file.', 'Ok', {duration: 5000}); this.snackBar.open(`Saved changes to file ${fileMeta.name}.`, 'Ok', {duration: 5000});
}); });
} else { } else {
// Add new file meta // Add new file meta
this.api.addFileMeta(specId, this.diagramFileMeta).subscribe(fileMeta => { this.api.addFileMeta(this.workflowSpec.id, this.diagramFileMeta).subscribe(fileMeta => {
this.loadFilesFromDb(); this.router.navigate(['/modeler', this.workflowSpec.id, fileMeta.id]);
this.snackBar.open('Saved new workflow spec and file.', 'Ok', {duration: 5000}); this.snackBar.open(`Saved new file ${fileMeta.name} to workflow spec ${this.workflowSpec.name}.`, 'Ok', {duration: 5000});
}); });
} }
} }
@ -287,7 +305,7 @@ export class ModelerComponent implements AfterViewInit {
this.xml = this.draftXml; this.xml = this.draftXml;
this.diagramFileMeta.file = new File([this.xml], this.diagramFileMeta.name, {type: 'text/xml'}); this.diagramFileMeta.file = new File([this.xml], this.diagramFileMeta.name, {type: 'text/xml'});
this.api.updateFileMeta(this.workflowSpec.id, this.diagramFileMeta).subscribe(() => { this.api.updateFileMeta(this.workflowSpec.id, this.diagramFileMeta).subscribe(() => {
this.snackBar.open('Saved changes to file.', 'Ok', {duration: 5000}); this.snackBar.open(`Saved changes to file ${this.diagramFileMeta.name}.`, 'Ok', {duration: 5000});
}); });
} }
} }

View File

@ -0,0 +1,15 @@
<div mat-dialog-title>
<button mat-icon-button mat-dialog-close=""><mat-icon>close</mat-icon></button>
</div>
<div mat-dialog-content>
<div *ngIf="!mode" class="select-mode" fxLayoutAlign="center center">
<button (click)="onSubmit(fileType.DMN)" color="primary" mat-flat-button>
<mat-icon>{{fileType.DMN | getIconCode}}</mat-icon>
New DMN Diagram
</button>
<button (click)="onSubmit(fileType.BPMN)" color="primary" mat-flat-button>
<mat-icon>{{fileType.BPMN | getIconCode}}</mat-icon>
New BPMN Diagram
</button>
</div>
</div>

View File

@ -0,0 +1,9 @@
.select-mode {
width: 100%;
height: 100%;
button {
padding: 1em;
margin: 1em;
}
}

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NewFileDialogComponent } from './new-file-dialog.component';
describe('NewFileDialogComponent', () => {
let component: NewFileDialogComponent;
let fixture: ComponentFixture<NewFileDialogComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ NewFileDialogComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(NewFileDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,29 @@
import {Component} from '@angular/core';
import {MatDialogRef} from '@angular/material/dialog';
import {FileType} from 'sartography-workflow-lib';
import {NewFileDialogData} from '../_interfaces/dialog-data';
@Component({
selector: 'app-new-file-dialog',
templateUrl: './new-file-dialog.component.html',
styleUrls: ['./new-file-dialog.component.scss']
})
export class NewFileDialogComponent {
mode: string;
fileType = FileType;
constructor(
public dialogRef: MatDialogRef<NewFileDialogComponent>
) {
}
onNoClick() {
this.dialogRef.close();
}
onSubmit(fileType: FileType) {
const data: NewFileDialogData = {fileType: fileType};
this.dialogRef.close(data);
}
}

View File

@ -0,0 +1,33 @@
<div mat-dialog-content>
<div *ngIf="!mode" class="select-mode" fxLayoutAlign="center center">
<button (click)="mode = 'local'" color="primary" mat-flat-button>
<mat-icon>code</mat-icon>
Upload a local BPMN/DMN file
</button>
<button (click)="mode = 'remote'" color="primary" mat-flat-button>
<mat-icon>link</mat-icon>
Open a BPMN/DMN file from URL
</button>
</div>
<div *ngIf="mode === 'local'">
<h1>Upload a local file</h1>
<mat-form-field (click)="fileInput.click()">
<mat-icon matPrefix>folder_open</mat-icon>
<input [value]="getFileName()" disabled matInput type="text">
</mat-form-field>
<input #fileInput (change)="onFileSelected($event)" accept=".bpmn,.dmn,.xml,application/xml,text/xml" hidden id="file"
type="file">
<button (click)="onSubmit()" [disabled]="!diagramFile" color="primary" mat-flat-button>Upload File</button>
<button (click)="mode = undefined" mat-flat-button>Cancel</button>
</div>
<div *ngIf="mode === 'remote'">
<h1>Open a file from a URL</h1>
<mat-form-field>
<input [(ngModel)]="url" matInput placeholder="BPMN/DMN File URL" type="url">
</mat-form-field>
<button (click)="onSubmitUrl()" [disabled]="!isValidUrl()" color="primary" mat-flat-button>Fetch File</button>
<button (click)="mode = undefined" mat-flat-button>Cancel</button>
</div>
</div>

View File

@ -0,0 +1,17 @@
.select-mode {
width: 100%;
height: 100%;
button {
padding: 1rem;
margin: 1rem;
}
}
.mat-dialog-content {
padding: 2rem;
}
mat-form-field {
width: 100%;
}

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { OpenFileDialogComponent } from './open-file-dialog.component';
describe('OpenFileDialogComponent', () => {
let component: OpenFileDialogComponent;
let fixture: ComponentFixture<OpenFileDialogComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ OpenFileDialogComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(OpenFileDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,57 @@
import { Component, OnInit } from '@angular/core';
import {MatDialogRef} from '@angular/material/dialog';
import {ApiService} from 'sartography-workflow-lib';
import {getDiagramTypeFromXml} from '../_util/diagram-type';
import {cleanUpFilename} from '../_util/string-clean';
@Component({
selector: 'app-open-file-dialog',
templateUrl: './open-file-dialog.component.html',
styleUrls: ['./open-file-dialog.component.scss']
})
export class OpenFileDialogComponent {
mode: string;
diagramFile: File;
url: string;
constructor(
public dialogRef: MatDialogRef<OpenFileDialogComponent>,
private api: ApiService
) {
}
onNoClick() {
this.dialogRef.close();
}
onSubmit() {
this.dialogRef.close({file: this.diagramFile});
}
onFileSelected($event: Event) {
this.diagramFile = ($event.target as HTMLFormElement).files[0];
}
getFileName() {
return this.diagramFile ? this.diagramFile.name : 'No file selected';
}
onSubmitUrl() {
if (this.url) {
this.api.getStringFromUrl(this.url).subscribe(s => {
const fileArray = this.url.split('/');
const fileName = fileArray[fileArray.length - 1];
const fileType = getDiagramTypeFromXml(s);
const name = cleanUpFilename(fileName, fileType);
this.diagramFile = new File([s], name, {type: 'text/xml'});
this.onSubmit();
});
}
}
isValidUrl() {
// tslint:disable-next-line:max-line-length
const re = /^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/i;
return re.test(this.url);
}
}

View File

@ -3,7 +3,7 @@ import {FormGroup} from '@angular/forms';
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
import {FormlyFieldConfig, FormlyFormOptions} from '@ngx-formly/core'; import {FormlyFieldConfig, FormlyFormOptions} from '@ngx-formly/core';
import {v4 as uuidv4} from 'uuid'; import {v4 as uuidv4} from 'uuid';
import {WorkflowSpecDialogData} from '../_interfaces/workflow-spec-dialog-data'; import {WorkflowSpecDialogData} from '../_interfaces/dialog-data';
import {toSnakeCase} from '../_util/string-clean'; import {toSnakeCase} from '../_util/string-clean';
@Component({ @Component({

View File

@ -2,7 +2,7 @@ import {Component, OnInit} from '@angular/core';
import {MatDialog} from '@angular/material/dialog'; import {MatDialog} from '@angular/material/dialog';
import {MatSnackBar} from '@angular/material/snack-bar'; import {MatSnackBar} from '@angular/material/snack-bar';
import {ApiService, WorkflowSpec} from 'sartography-workflow-lib'; import {ApiService, WorkflowSpec} from 'sartography-workflow-lib';
import {WorkflowSpecDialogData} from '../_interfaces/workflow-spec-dialog-data'; import {WorkflowSpecDialogData} from '../_interfaces/dialog-data';
import {WorkflowSpecDialogComponent} from '../workflow-spec-dialog/workflow-spec-dialog.component'; import {WorkflowSpecDialogComponent} from '../workflow-spec-dialog/workflow-spec-dialog.component';
@Component({ @Component({