From 3ef3dcd590165ea82504884253af72a4fa5b2a4b Mon Sep 17 00:00:00 2001 From: "konstantina.galouni" Date: Wed, 30 Nov 2022 12:24:00 +0200 Subject: [PATCH] [Validator]: Added fair guideline | Updated how results are displayed | Added RuleInfo class with structured response | Added icon and modal files in shared/utils | Added commented code for file uploading. --- src/app/app.module.ts | 6 +- src/app/pages/entities/RuleInfo.ts | 15 ++ .../single-record-validator.component.html | 72 +++++- .../single-record-validator.component.ts | 79 +++++- .../single-record-validator.service.ts | 10 + .../icons-preview/icons-preview.component.ts | 97 ++++++++ .../icons-preview/icons-preview.module.ts | 32 +++ src/app/shared/utils/icons/icons.component.ts | 192 +++++++++++++++ src/app/shared/utils/icons/icons.module.ts | 10 + src/app/shared/utils/icons/icons.service.ts | 18 ++ src/app/shared/utils/icons/icons.ts | 226 ++++++++++++++++++ src/app/shared/utils/modal/alert.ts | 191 +++++++++++++++ .../shared/utils/modal/alertModal.module.ts | 18 ++ .../full-screen-modal.component.ts | 144 +++++++++++ .../full-screen-modal.module.ts | 12 + .../shared/utils/modal/loading.component.ts | 61 +++++ .../shared/utils/modal/loadingModal.module.ts | 19 ++ src/app/shared/utils/modal/modal.module.ts | 15 ++ src/app/shared/utils/modal/open.component.ts | 57 +++++ src/assets/common-assets | 2 +- src/assets/openaire-theme | 2 +- 21 files changed, 1270 insertions(+), 8 deletions(-) create mode 100644 src/app/pages/entities/RuleInfo.ts create mode 100644 src/app/shared/utils/icons/icons-preview/icons-preview.component.ts create mode 100644 src/app/shared/utils/icons/icons-preview/icons-preview.module.ts create mode 100644 src/app/shared/utils/icons/icons.component.ts create mode 100644 src/app/shared/utils/icons/icons.module.ts create mode 100644 src/app/shared/utils/icons/icons.service.ts create mode 100644 src/app/shared/utils/icons/icons.ts create mode 100644 src/app/shared/utils/modal/alert.ts create mode 100644 src/app/shared/utils/modal/alertModal.module.ts create mode 100644 src/app/shared/utils/modal/full-screen-modal/full-screen-modal.component.ts create mode 100644 src/app/shared/utils/modal/full-screen-modal/full-screen-modal.module.ts create mode 100644 src/app/shared/utils/modal/loading.component.ts create mode 100644 src/app/shared/utils/modal/loadingModal.module.ts create mode 100644 src/app/shared/utils/modal/modal.module.ts create mode 100644 src/app/shared/utils/modal/open.component.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 3a97107..d857f1a 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -9,6 +9,8 @@ import { SingleRecordValidatorComponent } from './pages/single-record-validator/ import {FormsModule, ReactiveFormsModule} from "@angular/forms"; import {HttpClient, HttpClientModule} from "@angular/common/http"; import {InputModule} from "./shared/utils/input/input.module"; +import {AlertModalModule} from "./shared/utils/modal/alertModal.module"; +import {IconsModule} from "./shared/utils/icons/icons.module"; @NgModule({ declarations: [ @@ -23,7 +25,9 @@ import {InputModule} from "./shared/utils/input/input.module"; FormsModule, ReactiveFormsModule, HttpClientModule, - InputModule + InputModule, + AlertModalModule, + IconsModule ], providers: [], bootstrap: [AppComponent] diff --git a/src/app/pages/entities/RuleInfo.ts b/src/app/pages/entities/RuleInfo.ts new file mode 100644 index 0000000..57f605d --- /dev/null +++ b/src/app/pages/entities/RuleInfo.ts @@ -0,0 +1,15 @@ +export class RuleInfo { + name: string; + description: string; + warnings: string[]; + errors: string[]; + internalError: string; + status: Status; + score: number; +} + +export enum Status { + SUCCESS, + FAILURE, + ERROR +} diff --git a/src/app/pages/single-record-validator/single-record-validator.component.html b/src/app/pages/single-record-validator/single-record-validator.component.html index 175d97b..38ed17b 100644 --- a/src/app/pages/single-record-validator/single-record-validator.component.html +++ b/src/app/pages/single-record-validator/single-record-validator.component.html @@ -8,6 +8,19 @@
2. Paste metadata record (*)
+ + + + + + + + + + + + +
diff --git a/src/app/pages/single-record-validator/single-record-validator.component.ts b/src/app/pages/single-record-validator/single-record-validator.component.ts index ec77763..2afea70 100644 --- a/src/app/pages/single-record-validator/single-record-validator.component.ts +++ b/src/app/pages/single-record-validator/single-record-validator.component.ts @@ -1,7 +1,8 @@ -import { Component, OnInit } from '@angular/core'; +import {Component, OnInit, ViewChild} from '@angular/core'; import {UntypedFormBuilder, UntypedFormGroup, Validators} from "@angular/forms"; import {SingleRecordValidatorService} from "./single-record-validator.service"; import {Option} from "../../shared/utils/input/input.component"; +import {RuleInfo, Status} from "../entities/RuleInfo"; @Component({ selector: 'app-single-record-validator', @@ -12,10 +13,14 @@ export class SingleRecordValidatorComponent implements OnInit { public options: Option[] = [ {label: 'Data Archive Guidelines V2 Profile', value: 'dataArchiveGuidelinesV2Profile'}, {label: 'Literature Guidelines V3 Profile', value: 'literatureGuidelinesV3Profile'}, - {label: 'Literature Guidelines V4 Profile', value: 'literatureGuidelinesV4Profile'} + {label: 'Literature Guidelines V4 Profile', value: 'literatureGuidelinesV4Profile'}, + {label: 'FAIR Data Guidelines Profile', value: 'fairDataGuidelinesProfile'} ]; public form: UntypedFormGroup; public result: any; + public modalOpen: boolean = false; + public currentRule: RuleInfo; + @ViewChild('modal') modal; constructor(private fb: UntypedFormBuilder, private validator: SingleRecordValidatorService) { this.form = this.fb.group({ @@ -33,4 +38,74 @@ export class SingleRecordValidatorComponent implements OnInit { } ) } + + public openMessagesModal(rule: RuleInfo) { + this.modalOpen = true; + this.currentRule = rule; + this.modal.cancelButton = false; + this.modal.okButton = false; + this.modal.alertTitle = "Warnings & Errors"; + this.modal.open(); + } + + // fileChangeEvent(fileInput: any, dropped: boolean = false) { + // if(this.form.value.value) { + // this.onRemove(false); + // } + // + // if(dropped) { + // this.filesToUpload = fileInput.addedFiles; + // } else { + // this.filesToUpload = fileInput.target.files; + // } + // + // + // let messages: string[] = []; + // if (this.filesToUpload.length == 0) { + // messages.push(this.language.instant('DATASET-WIZARD.MESSAGES.NO-FILES-SELECTED')); + // return; + // } else { + // let fileToUpload = this.filesToUpload[0]; + // if (this.form.get("data") && this.form.get("data").value.types + // && this.form.get("data").value.types.map(type => type.value).includes(fileToUpload.type) + // && this.form.get("data").value.maxFileSizeInMB + // && this.form.get("data").value.maxFileSizeInMB*1048576 >= fileToUpload.size) { + // this.upload(); + // } else { + // this.filesToUpload = null; + // messages.push(this.language.instant('DATASET-WIZARD.MESSAGES.LARGE-FILE-OR-UNACCEPTED-TYPE')); + // messages.push(this.language.instant('DATASET-WIZARD.MESSAGES.MAX-FILE-SIZE', {'maxfilesize': this.form.get("data").value.maxFileSizeInMB})); + // messages.push(this.language.instant('DATASET-WIZARD.MESSAGES.ACCEPTED-FILE-TYPES')+ this.form.get("data").value.types.map(type => type.value).join(", ")); + // } + // + // if(messages && messages.length > 0) { + // this.dialog.open(FormValidationErrorsDialogComponent, { + // data: { + // errorMessages: messages + // } + // }) + // } + // } + // } + + // onRemove(makeFilesNull: boolean = true) { + // // delete from tmp folder - subscribe call + // this.fileService.deleteFromTempFolder(this.form.value.value.id).subscribe(res => { + // if(makeFilesNull) { + // this.makeFilesNull(); + // } + // this.cdr.detectChanges(); + // }, error => { + // if(makeFilesNull) { + // this.makeFilesNull(); + // } + // }) + // } + // + // makeFilesNull() { + // this.filesToUpload = null; + // this.form.value.value = null; + // this.form.get("value").patchValue(null); + // } + } diff --git a/src/app/pages/single-record-validator/single-record-validator.service.ts b/src/app/pages/single-record-validator/single-record-validator.service.ts index e68cecc..f157745 100644 --- a/src/app/pages/single-record-validator/single-record-validator.service.ts +++ b/src/app/pages/single-record-validator/single-record-validator.service.ts @@ -15,4 +15,14 @@ export class SingleRecordValidatorService { let headers = new HttpHeaders({'Content-Type': 'application/json', 'accept': 'application/json'}); return this.http.post(url, xml, {headers: headers}); } + + uploadAndValidateFile(file: FileList, guidelinesName: string): Observable { + let url = environment.validatorAPI + "validate-file"; + + let headers = new HttpHeaders({'Content-Type': 'application/json', 'accept': 'application/json'}); + const formData = new FormData(); + formData.append('file', file[0]); + formData.append('guidelines', guidelinesName); + return this.http.post(url, formData, { headers: headers }); + } } diff --git a/src/app/shared/utils/icons/icons-preview/icons-preview.component.ts b/src/app/shared/utils/icons/icons-preview/icons-preview.component.ts new file mode 100644 index 0000000..deb26a9 --- /dev/null +++ b/src/app/shared/utils/icons/icons-preview/icons-preview.component.ts @@ -0,0 +1,97 @@ +import {Component} from "@angular/core"; + +@Component({ + selector: 'icons-preview', + template: ` +
+

Icons

+
+
+ +
book
+
+
+ +
database
+
+
+ +
cog
+
+
+ +
earth
+
+
+ +
incognito
+
+
+ +
restricted
+
+
+ +
graph
+
+
+ +
orcid_add
+
+
+ +
orcid_bin
+
+
+ +
link
+
+
+ +
quotes
+
+
+ +
mining
+
+
+
+

Usage

+
    +
  • 1. Import IconsModule
  • +
  • 2. Add this to your module with these icons you will need
    +
    +
    constructor(private iconsService: IconsService) {{ "{" }}
    +   this.iconsService.registerIcons([edit]);
    +{{ "}" }}
    +
    +
  • +
  • 3. Use an icon with icon tag. Add parameter ratio to scale this icon. Default size 20x20 (ratio: 1)
    +
    +
    {{'<'}}icon name="edit"{{'><'}}/icon{{'>'}}
    +
    +
  • +
  • 4. Add a color class in parent of icon to give your icon a color.
    +
    +
    {{'<'}}div class="uk-text-secondary"{{'>'}}
    +  {{'<'}}icon name="edit"{{'><'}}/icon{{'>'}}
    +{{'<'}}/div{{'>'}}
    +
    +
  • +
+
+
+

Add a new icon

+
    +
  • 1. Go to Material Icons
  • +
  • 2. Find your icon and download it as svg.
  • +
  • 3. Open svg file with an editor and change width and height to 20
  • +
  • 4. Create an entry on icons file with your new icon.
  • +
+
+
+ ` +}) +export class IconsPreviewComponent { + +} diff --git a/src/app/shared/utils/icons/icons-preview/icons-preview.module.ts b/src/app/shared/utils/icons/icons-preview/icons-preview.module.ts new file mode 100644 index 0000000..5ce06c3 --- /dev/null +++ b/src/app/shared/utils/icons/icons-preview/icons-preview.module.ts @@ -0,0 +1,32 @@ +import {NgModule} from "@angular/core"; +import {CommonModule} from "@angular/common"; +import {RouterModule} from "@angular/router"; +import {IconsPreviewComponent} from "./icons-preview.component"; +import {IconsService} from "../icons.service"; +import {IconsModule} from "../icons.module"; +import { + book, + cog, + database, + earth, + filters, + graph, + incognito, link, mining, + orcid_add, + orcid_bin, + quotes, + restricted +} from "../icons"; + +@NgModule({ + imports: [CommonModule, RouterModule.forChild([ + {path: '', component: IconsPreviewComponent} + ]), IconsModule], + declarations: [IconsPreviewComponent], + exports: [IconsPreviewComponent] +}) +export class IconsPreviewModule { + constructor(private iconsService: IconsService) { + this.iconsService.registerIcons([book, database, cog, earth, incognito, restricted, graph, filters, orcid_add, orcid_bin, link, quotes, mining]) + } +} diff --git a/src/app/shared/utils/icons/icons.component.ts b/src/app/shared/utils/icons/icons.component.ts new file mode 100644 index 0000000..e44a146 --- /dev/null +++ b/src/app/shared/utils/icons/icons.component.ts @@ -0,0 +1,192 @@ +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + Input, + OnChanges, + SimpleChanges, + ViewChild +} from "@angular/core"; +import {IconsService} from "./icons.service"; + +export interface StopRule { + class: string, + offset: number +} + +/** + * By default, this component uses Material Icons Library to render an icon with + * a specific @name. For custom icons you should: + * + * - Add your icon in icons.ts and register it to Icon registry, by adding this to your component Module. + * + * e.g export class ExampleModule { + * constructor(private iconsService: IconsService) { + * this.iconsService.registerIcons([arrow_right]) + * } + * } + * + * If the name of your icon is the same with a Material Icon name, yours will be used instead of the default. + * + * - Custom SVG Icon. Define a variable in your component with an SVG and give it as Input @svg. + * + * In case of SVGs, there is an opportunity to add gradient color by define at least a start and end stop-color rules + * and an id to support them. This option is enabled by giving id as input in @gradient (Optional @degrees for gradient direction) + * + * e.g #gradient .start { + * stop-color: red; + * } + * + * e.g #gradient .end { + * stop-color: orange; + * } + * + * */ +@Component({ + selector: 'icon', + template: ` + + + {{iconName}} + + {{visuallyHidden}} + ` +}) +export class IconsComponent implements AfterViewInit, OnChanges { + private static DEFAULT_ICON_SIZE = 20; + public iconName: string; + public style; + /** + * Custom icon as SVG + */ + @Input() + public svg; + @Input() + public defaultSize = false; + /** + * True if this icon should have display flex (Optional, Default: false) + * + * */ + @Input() + public flex = false; + /** + * + * Add custom class(es)(Optional) + * */ + @Input() + public customClass = ''; + /** + * Color of svg (Optional) + * */ + @Input() + public fill; + /** + * Color of svg stroke (Optional) + * */ + @Input() + public stroke; + /** + * Size of icon (Default: 1) + * + * Disabled if defaultSize = true + * */ + @Input() + public ratio = 1; + /** + * In case of Material icon only. Type of icon (Optional) + * */ + @Input() + public type: "outlined" | "round" | "sharp" | "two-tone" | null = null; + /** + * Name of icon in registry (Required) + * */ + @Input() + set name(iconName: string) { + this.iconName = iconName; + this.svg = this.iconsService.getIcon(iconName); + } + /** + * Set visually hidden name for accessibility + * */ + @Input() + public visuallyHidden: string = null; + /** + * Gradient rules + * Available only for SVG! + * + * Define your css rules for stop-colors + * */ + @Input() + public gradient: string = null; + @Input() + public degrees: number = 0; + @Input() + public stopRules: StopRule[]= [{class: 'start', offset: 0}, {class: 'end', offset: 100}]; + @ViewChild("icon") + public icon: ElementRef; + + constructor(private iconsService: IconsService, + private cdr: ChangeDetectorRef) {} + + ngAfterViewInit() { + this.initIcon(); + } + + ngOnChanges(changes: SimpleChanges) { + this.initIcon(); + } + + initIcon() { + if(this.svg && this.icon) { + this.cdr.detectChanges(); + let svg: Element = this.icon.nativeElement.getElementsByTagName('svg').item(0); + if(!this.defaultSize && svg) { + svg.setAttribute("width", (this.ratio * IconsComponent.DEFAULT_ICON_SIZE).toString()); + svg.setAttribute("height", (this.ratio * IconsComponent.DEFAULT_ICON_SIZE).toString()); + } + if(this.gradient && svg) { + this.addGradient(svg); + } else { + this.style = { + fill: this.fill, + stroke: this.stroke + }; + } + } else { + this.style = { + "font-size.px": this.ratio*IconsComponent.DEFAULT_ICON_SIZE + }; + } + this.cdr.detectChanges(); + } + + addGradient(svg: Element) { + if(svg.children.length > 0 && typeof document !== "undefined") { + let gradientDefinition = document.createElementNS('http://www.w3.org/2000/svg', 'linearGradient'); + gradientDefinition.setAttribute('id', this.gradient); + if(this.degrees !== 0) { + let angle = (this.degrees) * (Math.PI / 180); + gradientDefinition.setAttribute('x1', Math.round(50 + Math.sin(angle + Math.PI) * 50) + '%'); + gradientDefinition.setAttribute('y1', Math.round(50 + Math.cos(angle + Math.PI) * 50) + '%'); + gradientDefinition.setAttribute('x2', Math.round(50 + Math.sin(angle) * 50) + '%'); + gradientDefinition.setAttribute('y2', Math.round(50 + Math.cos(angle) * 50) + '%'); + } + for(let rule of this.stopRules) { + let item = document.createElementNS('http://www.w3.org/2000/svg','stop'); + item.setAttribute('class', rule.class); + item.setAttribute('offset', rule.offset + '%'); + gradientDefinition.appendChild(item); + } + let defs = document.createElementNS('http://www.w3.org/2000/svg','defs'); + defs.appendChild(gradientDefinition); + for(let i = 0; i < svg.children.length; i++) { + let item = svg.children.item(i); + if(!item.hasAttribute('fill')) { + item.setAttribute('fill', "url('#" + this.gradient + "')"); + } + } + svg.insertBefore(defs, svg.childNodes[0]); + } + } +} diff --git a/src/app/shared/utils/icons/icons.module.ts b/src/app/shared/utils/icons/icons.module.ts new file mode 100644 index 0000000..bdbd84f --- /dev/null +++ b/src/app/shared/utils/icons/icons.module.ts @@ -0,0 +1,10 @@ +import {NgModule} from "@angular/core"; +import {CommonModule} from "@angular/common"; +import {IconsComponent} from "./icons.component"; + +@NgModule({ + imports: [CommonModule], + declarations: [IconsComponent], + exports: [IconsComponent] +}) +export class IconsModule {} diff --git a/src/app/shared/utils/icons/icons.service.ts b/src/app/shared/utils/icons/icons.service.ts new file mode 100644 index 0000000..151fc64 --- /dev/null +++ b/src/app/shared/utils/icons/icons.service.ts @@ -0,0 +1,18 @@ +import {Injectable} from "@angular/core"; +import {Icon} from "./icons"; + +@Injectable({ + providedIn: 'root' +}) +export class IconsService { + + private registry = new Map(); + + public registerIcons(icons: any[]): void { + icons.forEach((icon: Icon) => this.registry.set(icon.name, icon.data)); + } + + public getIcon(iconName: string): string | undefined { + return this.registry.get(iconName); + } +} diff --git a/src/app/shared/utils/icons/icons.ts b/src/app/shared/utils/icons/icons.ts new file mode 100644 index 0000000..1a6447d --- /dev/null +++ b/src/app/shared/utils/icons/icons.ts @@ -0,0 +1,226 @@ +export interface Icon { + name: string, + data: string +} + +/** @deprecated */ +export const arrow_left: Icon = { + name: 'arrow_left', + data: '' +} + +/** @deprecated */ +export const arrow_right: Icon = { + name: 'arrow_right', + data: '' +}; + +/** @deprecated */ +export const arrow_up: Icon = { + name: 'arrow_up', + data: '' +} + +/** @deprecated */ +export const arrow_down: Icon = { + name: 'arrow_down', + data: '' +} + +export const book: Icon = { + name: 'book', + data: '' +}; + +export const database: Icon = { + name: 'database', + data: '' +}; + +export const cog: Icon = { + name: 'cog', + data: '' +}; + +export const earth: Icon = { + name: 'earth', + data: '' +} + +/** @deprecated */ +export const edit: Icon = { + name: 'edit', + data: '' +} + +/** @deprecated + * + * Use delete + * */ +export const remove: Icon = { + name: 'remove', + data: '' +} + +/** @deprecated + * Use visibility + * */ +export const preview: Icon = { + name: 'preview', + data: '' +} + +/** @deprecated */ +export const bullet: Icon = { + name: 'bullet', + data: '' +} + +/** @deprecated */ +export const remove_circle = { + name: 'remove_circle', + data: '' +} + +/** @deprecated */ +export const remove_circle_outline = { + name: 'remove_circle_outline', + data: '' +} + +/** @deprecated */ +export const person_add = { + name: 'person_add', + data: '' +} + +/** @deprecated */ +export const cloud_upload = { + name: 'cloud_upload', + data: '' +} + +/** @deprecated */ +export const add = { + name: 'add', + data: '' +} + +/** @deprecated */ +export const group = { + name: 'group', + data: '' +} + +/** @deprecated */ +export const lock = { + name: 'lock', + data: '' +} + +/** @deprecated */ +export const search = { + name: 'search', + data: '' +} + +/** @deprecated */ +export const refresh = { + name: 'refresh', + data: '' +} + +/** @deprecated */ +export const close = { + name: 'close', + data: '' +} + +/** @deprecated */ +export const done = { + name: 'done', + data: '' +} + +/** @deprecated */ +export const mail = { + name: 'mail', + data: '' +} + +/** @deprecated */ +export const photo = { + name: 'photo', + data: '' +} + +/** @deprecated */ +export const check_circle_outlined = { + name: 'check_circle_outlined', + data: '' +} + +/** @deprecated */ +export const reset = { + name: 'reset', + data: '' +} + +/** @deprecated */ +export const send = { + name: 'send', + data: '' +} + +/** @deprecated */ +export const print = { + name: 'print', + data: '' +} + +export const incognito = { + name: 'incognito', + data: '' +} + +export const restricted = { + name: 'restricted', + data: '' +} + +export const graph = { + name: 'graph', + data: '' +} + +export const filters = { + name: 'filters', + data: '' +} + +export const orcid_add = { + name: 'orcid_add', + data: '' +} + +export const orcid_bin = { + name: 'orcid_bin', + data: '' +} + +export const link = { + name: 'link', + data: '' +} + +export const quotes = { + name: 'quotes', + data: '' +} + +export const mining = { + name: 'mining', + data: '' +} + +/** Add new icon under this line to be sure that it will be added on preview */ diff --git a/src/app/shared/utils/modal/alert.ts b/src/app/shared/utils/modal/alert.ts new file mode 100644 index 0000000..f9599ca --- /dev/null +++ b/src/app/shared/utils/modal/alert.ts @@ -0,0 +1,191 @@ +import {Component, ElementRef, EventEmitter, Input, Output, ViewChild, ViewEncapsulation} from '@angular/core'; + +declare var UIkit: any; + +@Component({ + selector: 'modal-alert', + template: ` +
+
+
+
+
{{alertTitle}}
+
+ +
+
+
+ +
+ +
+
+ `, + encapsulation: ViewEncapsulation.None, +}) +/** + * API to an open alert window. + */ +export class AlertModal { + private static MODAL_COUNTER: number = 0; + + id: string = "modal"; + @Input() classTitle: string = "uk-background-primary-opacity"; + @Input() classBody: string = ""; + @Input() large: boolean = false; + @Input() overflowBody: boolean = true; + /** + * Caption for the title. + */ + public alertTitle: string; + /** + * Describes if the alert contains Ok Button. + * The default Ok button will close the alert and emit the callback. + * Defaults to true. + */ + public okButton: boolean = true; + /** + * Caption for the OK button. + * Default: Ok + */ + public okButtonText: string = 'OK'; + /** + * Describes if the alert contains cancel Button. + * The default Cancelbutton will close the alert. + * Defaults to true. + */ + public cancelButton: boolean = true; + /** + * Caption for the Cancel button. + * Default: Cancel + */ + public cancelButtonText: string = 'Cancel'; + /** + * if the alertMessage is true it will show the contentString inside alert body. + */ + public alertMessage: boolean = true; + /** + * Some message/content can be set in message which will be shown in alert body. + */ + public message: string; + /** + * if the value is true alert footer will be visible or else it will be hidden. + */ + public alertFooter: boolean = true; + /** + * shows alert header if the value is true. + */ + public alertHeader: boolean = true; + /** + * if the value is true ok button align on the left, else on the right + */ + public okButtonLeft: boolean = true; + + /** + * if the value is true ok button is disabled + */ + @Input() + public okDisabled: boolean = false; + + /** + * If the value is true, a checkbox option will be appeared on the right side of footer + */ + @Input() + public choice: boolean = false; + + /** + * if the value is true then on ok clicked, modal will stay open. + */ + public stayOpen: boolean = false; + + /** + * Value will be emitted if @choice is true + */ + public select: boolean = true; + /** + * Emitted when ok button was clicked + * or when Ok method is called. + */ + @Output() public alertOutput: EventEmitter = new EventEmitter(); + /** + * Emitted when cancel button was clicked + * or when cancel method is called. + */ + @Output() public cancelOutput: EventEmitter = new EventEmitter(); + + @ViewChild('element') element: ElementRef; + + constructor() { + } + + ngOnInit() { + AlertModal.MODAL_COUNTER++; + this.id = 'modal-' + AlertModal.MODAL_COUNTER; + } + + ngOnDestroy() { + if(typeof document !== "undefined") { + const element = document.getElementById("modal-container"); + for (let i = element.childNodes.length - 1; i >= 0; --i) { + let child: ChildNode = element.childNodes[i]; + if (child['id'] == this.id) { + child.remove(); + } + } + } + } + + /** + * Opens an alert window creating backdrop. + */ + open() { + UIkit.modal(this.element.nativeElement).show(); + } + + /** + * ok method closes the modal and emits modalOutput. + */ + ok() { + if (!this.stayOpen) { + UIkit.modal(this.element.nativeElement).hide(); + } + if (!this.choice) { + this.alertOutput.emit(true); + } else { + this.alertOutput.emit({ + value: true, + choice: this.select + }); + } + } + + /** + * cancel method closes the modal. + */ + cancel() { + UIkit.modal(this.element.nativeElement).hide(); + this.cancelOutput.emit(true); + } +} diff --git a/src/app/shared/utils/modal/alertModal.module.ts b/src/app/shared/utils/modal/alertModal.module.ts new file mode 100644 index 0000000..bf0d941 --- /dev/null +++ b/src/app/shared/utils/modal/alertModal.module.ts @@ -0,0 +1,18 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; + +import {AlertModal} from './alert'; +import {ModalModule} from './modal.module'; +import {IconsModule} from "../icons/icons.module"; + +@NgModule({ + imports: [CommonModule, FormsModule, ModalModule, IconsModule], + declarations: [ + AlertModal + ], + exports: [ + AlertModal + ] +}) +export class AlertModalModule { } diff --git a/src/app/shared/utils/modal/full-screen-modal/full-screen-modal.component.ts b/src/app/shared/utils/modal/full-screen-modal/full-screen-modal.component.ts new file mode 100644 index 0000000..750286a --- /dev/null +++ b/src/app/shared/utils/modal/full-screen-modal/full-screen-modal.component.ts @@ -0,0 +1,144 @@ +import { + AfterViewInit, ChangeDetectorRef, + Component, + ElementRef, + EventEmitter, HostListener, + Input, + OnDestroy, + OnInit, + Output, + ViewChild +} from "@angular/core"; +import {fromEvent, Subscription} from 'rxjs'; +import {delay} from "rxjs/operators"; +import {HelperFunctions} from "../../HelperFunctions.class"; + +declare var UIkit; +declare var ResizeObserver; + +@Component({ + selector: 'fs-modal', + template: ` +
+
+
+
+ +
+
+

{{title}}

+
+
+ + +
+
+
+
+ +
+
+
+
+ ` +}) +export class FullScreenModalComponent implements AfterViewInit, OnDestroy { + private static FS_MODAL_COUNTER: number = 0; + + id: string = "fs-modal"; + @Input() classTitle: string = "uk-background-primary uk-light"; + @Input() classBody: string = 'uk-container-large'; + back: boolean = false; + title: string; + okButton: boolean = false; + okButtonText = 'OK'; + @Input() + okButtonDisabled = false; + @Output() + okEmitter: EventEmitter = new EventEmitter(); + @Output() + cancelEmitter: EventEmitter = new EventEmitter(); + @ViewChild('modal') modal: ElementRef; + @ViewChild('header') header: ElementRef; + @ViewChild('body') body: ElementRef; + observer: any; + headerHeight: number; + bodyHeight: number; + + constructor(private cdr: ChangeDetectorRef) { + } + + @HostListener('window:resize', ['$event']) + onResize() { + this.changeHeight(); + } + + ngOnInit() { + FullScreenModalComponent.FS_MODAL_COUNTER++; + this.id = 'fs-modal-' + FullScreenModalComponent.FS_MODAL_COUNTER; + } + + ngAfterViewInit() { + if(typeof window !== "undefined") { + this.observer = new ResizeObserver(entries => { + for (let entry of entries) { + this.changeHeight(); + } + }); + this.observer.observe(this.header.nativeElement); + } + } + + /* Height = Viewport - header - (Body padding) */ + changeHeight() { + if(typeof window !== "undefined" && this.header) { + this.bodyHeight = window.innerHeight - this.header.nativeElement.clientHeight - 80; + this.cdr.detectChanges(); + } + } + + ngOnDestroy() { + if(this.observer && this.observer instanceof ResizeObserver) { + this.observer.disconnect(); + } + if(typeof document !== "undefined") { + const element = document.getElementById("modal-container"); + for (let i = element.childNodes.length - 1; i >= 0; --i) { + let child: ChildNode = element.childNodes[i]; + if (child['id'] == this.id) { + child.remove(); + } + } + } + + } + + get isOpen() { + return this.modal && UIkit.modal(this.modal.nativeElement).isToggled(); + } + + open() { + UIkit.modal(this.modal.nativeElement).show(); + HelperFunctions.scroll(); + } + + cancel() { + UIkit.modal(this.modal.nativeElement).hide(); + HelperFunctions.scroll(); + this.cancelEmitter.emit(); + } + + ok() { + UIkit.modal(this.modal.nativeElement).hide(); + HelperFunctions.scroll(); + this.okEmitter.emit(true); + } +} diff --git a/src/app/shared/utils/modal/full-screen-modal/full-screen-modal.module.ts b/src/app/shared/utils/modal/full-screen-modal/full-screen-modal.module.ts new file mode 100644 index 0000000..ab5537d --- /dev/null +++ b/src/app/shared/utils/modal/full-screen-modal/full-screen-modal.module.ts @@ -0,0 +1,12 @@ +import {NgModule} from "@angular/core"; +import {CommonModule} from "@angular/common"; +import {FullScreenModalComponent} from "./full-screen-modal.component"; +import {IconsModule} from "../../icons/icons.module"; + +@NgModule({ + imports: [CommonModule, IconsModule], + declarations: [FullScreenModalComponent], + exports: [FullScreenModalComponent] +}) +export class FullScreenModalModule { +} diff --git a/src/app/shared/utils/modal/loading.component.ts b/src/app/shared/utils/modal/loading.component.ts new file mode 100644 index 0000000..81c84c2 --- /dev/null +++ b/src/app/shared/utils/modal/loading.component.ts @@ -0,0 +1,61 @@ +import {Component, ViewEncapsulation, ComponentRef, ElementRef, Input, EventEmitter, Output} from '@angular/core'; + +@Component({ + selector: 'modal-loading', + template: ` + +
+
+ + +
+
+

{{message}}

+ + + + +
+
+ +
+ +
+ + `, + encapsulation: ViewEncapsulation.None, +}) +/** + * API to an open alert window. + */ +export class ModalLoading{ + +@Input() public message:string ="Loading"; + + /** + * if the value is true alert will be visible or else it will be hidden. + */ + public isOpen:boolean=false; + /** + * Emitted when a ok button was clicked + * or when Ok method is called. + */ + @Output() public alertOutput:EventEmitter = new EventEmitter(); + constructor( public _elementRef: ElementRef){} + /** + * Opens a alert window creating backdrop. + */ + open(){ + this.isOpen= true; + } + + close(){ + this.isOpen = false; + } +} diff --git a/src/app/shared/utils/modal/loadingModal.module.ts b/src/app/shared/utils/modal/loadingModal.module.ts new file mode 100644 index 0000000..6edf5c4 --- /dev/null +++ b/src/app/shared/utils/modal/loadingModal.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; + + import {ModalLoading} from './loading.component'; +import {ModalModule} from './modal.module'; + +//helpers + +@NgModule({ + imports: [ CommonModule, FormsModule,ModalModule ], + declarations: [ + ModalLoading + ], + exports: [ + ModalLoading + ] +}) +export class LoadingModalModule { } diff --git a/src/app/shared/utils/modal/modal.module.ts b/src/app/shared/utils/modal/modal.module.ts new file mode 100644 index 0000000..6b76d1a --- /dev/null +++ b/src/app/shared/utils/modal/modal.module.ts @@ -0,0 +1,15 @@ +/* common components of modal components */ +import {NgModule} from '@angular/core'; +import {Open} from './open.component'; + +@NgModule({ + imports: [], + declarations: [ + Open + ], + exports: [ + Open + ] +}) +export class ModalModule { +} diff --git a/src/app/shared/utils/modal/open.component.ts b/src/app/shared/utils/modal/open.component.ts new file mode 100644 index 0000000..fb9f011 --- /dev/null +++ b/src/app/shared/utils/modal/open.component.ts @@ -0,0 +1,57 @@ +import {Directive, Input, HostBinding} from '@angular/core'; + +// todo: add animate +// todo: add init and on change +@Directive({selector: '[open]'}) +export class Open { + @HostBinding('style.display') + public display:string; + @HostBinding('class.in') + @HostBinding('attr.aria-expanded') + public isExpanded:boolean = true; + + @Input() + public set open(value:boolean) { + this.isExpanded = value; + this.toggle(); + } + + public get open():boolean { + return this.isExpanded; + } + + constructor() { + } + init() { + this.isExpanded = false; + this.display = 'none'; + } + toggle() { + if (this.isExpanded) { + this.hide(); + } else { + this.show(); + } + } + + hide() { + this.isExpanded = false; + this.display = 'none'; + if (typeof document !== 'undefined') { + let backDrop = document.getElementsByClassName("modal-backdrop"); + if(backDrop.length>0){ + document.body.removeChild(backDrop[0]); + } + } + } + + show() { + if (typeof document !== 'undefined') { + // let backDrop = document.createElement('div'); + // backDrop.className="modal-backdrop fade in"; + // document.body.appendChild(backDrop); + } + this.isExpanded = true; + this.display = 'block'; + } +} diff --git a/src/assets/common-assets b/src/assets/common-assets index 936fac2..658d017 160000 --- a/src/assets/common-assets +++ b/src/assets/common-assets @@ -1 +1 @@ -Subproject commit 936fac297322fa252af930cab3c0e69efe57c1a5 +Subproject commit 658d017b02df4786e9b221f51eb39ef9a42c6d88 diff --git a/src/assets/openaire-theme b/src/assets/openaire-theme index 8bb758b..fb1d714 160000 --- a/src/assets/openaire-theme +++ b/src/assets/openaire-theme @@ -1 +1 @@ -Subproject commit 8bb758b340d05b50b65da48863ff7ed69fdc122b +Subproject commit fb1d7149654415dd6b848b86ab5187401f50975e