Add new slider-tabs component. Add slider tabs in terminology page and make content with class help texts

This commit is contained in:
Konstantinos Triantafyllou 2022-10-21 17:06:44 +03:00
parent ada4017fe5
commit 1e8ec15451
5 changed files with 263 additions and 253 deletions

View File

@ -11,6 +11,8 @@ import {IconsModule} from "../../utils/icons/icons.module";
import {IconsService} from "../../utils/icons/icons.service";
import {graph} from "../../utils/icons/icons";
import {BreadcrumbsModule} from "../../utils/breadcrumbs/breadcrumbs.module";
import {SliderTabsModule} from "../../sharedComponents/tabs/slider-tabs.module";
import {HelperModule} from "../../utils/helper/helper.module";
@NgModule({
declarations: [TerminologyComponent, SeeHowItWorksComponent],
@ -30,7 +32,7 @@ import {BreadcrumbsModule} from "../../utils/breadcrumbs/breadcrumbs.module";
component: SeeHowItWorksComponent,
canDeactivate: [PreviousRouteRecorder]
},
]), PageContentModule, HowModule, TabsModule, IconsModule, BreadcrumbsModule],
]), PageContentModule, HowModule, SliderTabsModule, IconsModule, BreadcrumbsModule, SliderTabsModule, HelperModule],
exports: [TerminologyComponent, SeeHowItWorksComponent]
})
export class MethodologyModule {

View File

@ -1,4 +1,12 @@
import {ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild} from "@angular/core";
import {
AfterContentChecked,
AfterViewInit,
ChangeDetectorRef,
Component,
OnDestroy,
OnInit,
ViewChild
} from "@angular/core";
import {Subscription} from "rxjs";
import {Meta, Title} from "@angular/platform-browser";
import {ActivatedRoute, Router} from "@angular/router";
@ -6,7 +14,8 @@ import {OpenaireEntities} from "../../utils/properties/searchFields";
import {SEOService} from "../../sharedComponents/SEO/SEO.service";
import {properties} from "../../../../environments/environment";
import {Breadcrumb} from "../../utils/breadcrumbs/breadcrumbs.component";
import Timeout = NodeJS.Timeout;
import {HelperService} from "../../utils/helper/helper.service";
declare var ResizeObserver;
@ -31,217 +40,37 @@ declare var ResizeObserver;
</div>
</div>
</div>
<div class="uk-section" uk-scrollspy="target: [uk-scrollspy-class]; cls: uk-animation-fade; delay: 250">
<div *ngIf="divContents" class="uk-section" uk-scrollspy="target: [uk-scrollspy-class]; cls: uk-animation-fade; delay: 250">
<div class="uk-section uk-container uk-container-large" uk-scrollspy-class>
<div id="parentContainer" class="uk-grid uk-grid-large" uk-grid>
<div class="uk-width-1-4@m uk-visible@m">
<div class="uk-sticky" uk-sticky="bottom: !#parentContainer; offset: 100;">
<ul class="uk-tab uk-tab-left">
<li class="uk-margin-small-bottom" [class.uk-active]="activeSection === 'entities'">
<a routerLink="./" fragment="entities">1. Entities</a>
</li>
<li class="uk-margin-small-bottom" [class.uk-active]="activeSection === 'inherited-and-inferred-attributes'">
<a routerLink="./" fragment="inherited-and-inferred-attributes">2. Inherited and Inferred Attributes</a>
</li>
<li class="uk-margin-small-bottom" [class.uk-active]="activeSection === 'constructed-attributes'">
<a routerLink="./" fragment="constructed-attributes">3. Constructed Attributes</a>
</li>
</ul>
<slider-tabs type="scrollable" position="left">
<slider-tab tabId="entities" tabTitle="1. Entities"></slider-tab>
<slider-tab tabId="inherited-and-inferred-attributes" tabTitle="2. Inherited and Inferred Attributes"></slider-tab>
<slider-tab tabId="constructed-attributes" tabTitle="3. Constructed Attributes"></slider-tab>
</slider-tabs>
</div>
</div>
<div class="uk-width-1-1 uk-hidden@m">
<div class="uk-sticky uk-background-default" uk-sticky>
<ul class="uk-tab">
<li [class.uk-active]="activeSection === 'entities'">
<a routerLink="./" fragment="entities">1. Entities</a>
</li>
<li [class.uk-active]="activeSection === 'inherited-and-inferred-attributes'">
<a routerLink="./" fragment="inherited-and-inferred-attributes">2. Inherited and Inferred Attributes</a>
</li>
<li [class.uk-active]="activeSection === 'constructed-attributes'">
<a routerLink="./" fragment="constructed-attributes">3. Constructed Attributes</a>
</li>
</ul>
<slider-tabs type="scrollable" position="horizontal">
<slider-tab tabId="entities" tabTitle="1. Entities"></slider-tab>
<slider-tab tabId="inherited-and-inferred-attributes" tabTitle="2. Inherited and Inferred Attributes"></slider-tab>
<slider-tab tabId="constructed-attributes" tabTitle="3. Constructed Attributes"></slider-tab>
</slider-tabs>
</div>
</div>
<div class="uk-width-expand@m uk-margin-left">
<div class="uk-width-expand@m">
<div id="entities" class="uk-margin-large-bottom">
<!-- Helptext below here -->
<div>
<h4>1. Entities</h4>
<h6 class="uk-text-primary">Research Products</h6>
<div>
There are four different types of research products in the
OpenAIRE Research Graph:
</div>
<ul class="uk-list uk-list-bullet uk-list-primary">
<li>Publications</li>
<li>Research data</li>
<li>Research software</li>
<li>Other research products.</li>
</ul>
<div class="uk-margin-small-top">
We deduplicate (merge) different records of research products and keep the metadata of all instances.
</div>
<h6>Publication</h6>
<div>
Research products intended for human reading (published articles, pre-prints, conference
papers, presentations, technical reports, etc.)
</div>
<h6>Research data</h6>
<div>
The sources from which the description of the research data has been collected reflect and support their own granularity, we do not define it.
</div>
<h6>Research software</h6>
<div>
Source code or software package developed and/or used in a research context
</div>
<h6>Other research product</h6>
<div>
Anything that does not fall in the previous categories (e.g. workflow, methods, protocols)
</div>
</div>
<helper [texts]="divContents.entities"></helper>
</div>
<div id="inherited-and-inferred-attributes" class="uk-margin-large-bottom">
<!-- Helptext below here -->
<div>
<h4>2. Inherited and Inferred Attributes</h4>
<div class="uk-margin-medium-top">
We either inherit the attributes of entities via entries in the harvested metadata records or automatically generate them using our inference system (text and data mining algorithms).
</div>
<h6>Organization</h6>
<div>
<p><span class="uk-text-bold">For research products,</span> this refers to the
affiliated organizations of its authors</p>
<p><span class="uk-text-bold">For projects:</span>
the organizations participating in
the project
(i.e. beneficiaries of the grant)</p>
<p>
We are improving the organization database with the use of our <a href="https://orgs.openaire.eu/" target="_blank">OpenOrgs</a> tool. It allows curators to disambiguate organizations (merge different names of the same organization) and identify parent-child relationships (schools, departments, etc.).
</p>
</div>
<h6>Country</h6>
<div>
<p>The country of the organization.</p>
<p>
<span class="uk-text-bold">Country code mapping: </span>
<a href="https://api.openaire.eu/vocabularies/dnet:countries" target="_blank">
https://api.openaire.eu/vocabularies/dnet:countries</a>
</p>
</div>
<h6>Funder</h6>
<div>
<p>Funders that have joined OpenAIRE, i.e. their project data have
gone through a validation process.</p>
<p>You can visit <a class="https://explore.openaire.eu/search/find" target="_blank">https://explore.openaire.eu/search/find</a>
if you would like to explore the research products
and projects of all funders in OpenAIRE (the list of funders can be
seen under the "Funder" Filter shown on the left side of the page).</p>
<p><span class="uk-text-bold">For funder who want to join OpenAIRE: </span><a
href="https://www.openaire.eu/funders-how-to-join-guide" target="_blank">https://www.openaire.eu/funders-how-to-join-guide</a>
</p>
</div>
<h6>Type</h6>
<div>
<p>The sub-type of a research outcome (e.g.,
a publication can be a pre-print, conference proceeding,
article,
etc.)</p>
<p><span class="uk-text-bold">Resource type mapping: </span>
<a href="https://api.openaire.eu/vocabularies/dnet:result_typologies" target="_blank">https://api.openaire.eu/vocabularies/dnet:result_typologies</a>
(click on the code to see the specific types for each result type)
</p>
</div>
<h6>Access mode or access rights</h6>
<div>
<p>The best available (across all instances) access rights of
a research product</p>
<p>Types (by best available):</p>
<p><span class="uk-text-bold">Open:</span> Open Access</p>
<p><span class="uk-text-bold">Embargo:</span> Closed for a specific period of time, then open.</p>
<p><span class="uk-text-bold">Restricted:</span> Definition of restricted may vary by data source, it may refer to access rights being given to registered users, potentially behind a paywall.</p>
<p><span class="uk-text-bold">Closed:</span> Closed access</p>
</div>
<h6>CC license</h6>
<div>
<p>A Creative Commons copyright license <a href="(https://creativecommons.org/)" target="_blank">(https://creativecommons.org/)</a>
</p>
</div>
<h6>PID (persistent identifier)</h6>
<div>
<p>A long-lasting reference to a resource</p>
<p><span class="uk-text-bold">Types: </span> <a
href="http://api.openaire.eu/vocabularies/dnet:pid_types" target="_blank">http://api.openaire.eu/vocabularies/dnet:pid_types</a>
</p>
</div>
<h6>Context</h6>
<div>
<p>Related research community, initiative or infrastructure.</p>
</div>
<h6>Journal</h6>
<div>
<p>The scientific journal an article is published in.</p>
</div>
<h6>Publisher</h6>
<div>
<p>The publisher of the venue (journal, book, etc.) of a research product.</p>
</div>
<h6>Data sources (content providers)</h6>
<div>
<p>The different data sources ingested in the OpenAIRE Research Graph.</p>
<div class="uk-text-bold">Data Source Types:</div>
<ul class="uk-list uk-list-disc">
<li>Repositories</li>
<li>Open Access Publishers & Journals</li>
<li>Aggregators</li>
<li>Entity Registries</li>
<li>Journal Aggregators</li>
<li>CRIS (Current Research Information System)</li>
</ul>
</div>
<h6>Repositories</h6>
<div>
<p>Information systems where scientists upload the bibliographic metadata and payloads of their
research products (e.g. PDFs of their scientific articles, CSVs of their data,
archive with their
software), due to obligations from their organizations, their
funders, or due to community practices
(e.g. ArXiv, Europe PMC, Zenodo).</p>
</div>
<h6>Open Access Publishers & Journals</h6>
<div>
<p>Information systems of open access publishers or relative journals, which offer bibliographic
metadata and PDFs of their published articles.</p>
</div>
<h6>Aggregators</h6>
<div>
<p>Information systems that collect descriptive metadata about research products
from multiple sources
in order to enable cross-data source discovery of given research products (e,g,
DataCite,
BASE, DOAJ).</p>
</div>
<h6>Entity Registries</h6>
<div>
<p>Information systems created with the intent of maintaining authoritative registries of given
entities in the scholarly communication, such as OpenDOAR for the institutional repositories, re3data
for the data repositories, CORDA and other funder databases
for projects and funding information.</p>
</div>
<h6>CRIS (Current Research Information System)</h6>
<div>
<p>Information systems adopted by research and academic organizations to
keep track of their research
administration records and relative results; examples of CRIS content are articles
or research data funded
by projects, their principal investigators, facilities acquired
thanks to funding, etc.</p>
</div>
</div>
<helper [texts]="divContents['inherited-and-inferred-attributes']"></helper>
</div>
<div id="constructed-attributes" class="uk-margin-large-bottom">
<!-- Helptext below here -->
<helper [texts]="divContents['constructed-attributes']"></helper>
<div>
<h4>3. Constructed Attributes</h4>
<div class="uk-margin-medium-top">
@ -403,25 +232,26 @@ declare var ResizeObserver;
</div>
`
})
export class TerminologyComponent implements OnInit, OnDestroy {
export class TerminologyComponent implements OnInit, OnDestroy, AfterViewInit, AfterContentChecked {
public tab: 'entities' | 'attributes' = 'entities';
private subscriptions: any[] = [];
public openaireEntities = OpenaireEntities;
public breadcrumbs: Breadcrumb[] = [{name: 'home', route: '/'}, {name: 'Resources - Terminology and construction', keepFormat: true}];
public graph_offset: number = 0;
public graph_height: number = 0;
public graph_offset: number = 0;
public graph_height: number = 0;
@ViewChild("graph_element") graph_element;
public contentSections: string[] = ['entities', 'inherited-and-inferred-attributes', 'constructed-attributes'];
public activeSection: string;
private observer: IntersectionObserver;
private timeout: Timeout;
public contentSections: string[] = ['entities', 'inherited-and-inferred-attributes', 'constructed-attributes'];
public activeSection: string;
public properties = properties;
public divContents: any;
constructor(private seoService: SEOService,
private meta: Meta,
private router: Router,
private route: ActivatedRoute,
private title: Title,
private cdr: ChangeDetectorRef) {
private cdr: ChangeDetectorRef,
private helper: HelperService) {
}
ngOnInit() {
@ -432,27 +262,25 @@ export class TerminologyComponent implements OnInit, OnDestroy {
this.breadcrumbs[0].route = '/' + (params['stakeholder']?params['stakeholder']:'');
this.breadcrumbs[0].name = (params['stakeholder']?'dashboard':'home');
}));
this.subscriptions.push(this.route.fragment.subscribe(fragment => {
if(fragment) {
this.activeSection = fragment;
} else {
this.activeSection = 'entities';
}
}));
this.subscriptions.push(this.route.fragment.subscribe(fragment => {
if(fragment) {
this.activeSection = fragment;
} else {
this.activeSection = 'entities';
}
}));
this.getDivContents();
}
ngAfterViewInit() {
ngAfterViewInit() {
if (typeof document !== 'undefined') {
if(this.graph_element) {
this.observeGraphElement();
}
setTimeout(() => {
this.setObserver();
});
}
}
ngAfterContentChecked() {
ngAfterContentChecked() {
if(this.graph_element && typeof document !== 'undefined') {
this.graph_offset = this.calcGraphOffset(this.graph_element.nativeElement);
}
@ -464,37 +292,16 @@ export class TerminologyComponent implements OnInit, OnDestroy {
subscription.unsubscribe();
}
});
if(this.observer) {
this.observer.disconnect();
}
}
private setObserver() {
if(this.observer) {
this.observer.disconnect();
}
this.observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if(entry.isIntersecting) {
if(this.timeout) {
clearTimeout(this.timeout);
}
this.timeout = setTimeout(() => {
this.router.navigate(['./'], {fragment: entry.target.id, relativeTo: this.route, state: {disableScroll: true}});
}, 200);
}
});
}, {threshold: 0.25});
this.contentSections.forEach(section => {
let element = document.getElementById(section);
if(element) {
this.observer.observe(element);
}
});
}
public observeGraphElement() {
let resizeObs = new ResizeObserver(entries => {
private getDivContents() {
this.subscriptions.push(this.helper.getDivHelpContents(this.properties, 'monitor', this.router.url).subscribe(contents => {
this.divContents = contents;
}));
}
public observeGraphElement() {
let resizeObs = new ResizeObserver(entries => {
entries.forEach(entry => {
setTimeout(() => {
this.graph_offset = this.calcGraphOffset(entry.target);
@ -504,9 +311,9 @@ export class TerminologyComponent implements OnInit, OnDestroy {
});
this.subscriptions.push(resizeObs);
resizeObs.observe(this.graph_element.nativeElement);
}
calcGraphOffset(element) {
}
calcGraphOffset(element) {
this.graph_height = element.offsetHeight;
return window.innerHeight-this.graph_height;
}

View File

@ -0,0 +1,16 @@
import {Component, Input} from "@angular/core";
@Component({
selector: 'slider-tab',
template: ``
})
export class SliderTabComponent {
@Input("tabTitle")
public title: string;
@Input("tabId")
public id: string;
@Input()
public active: boolean = false;
@Input()
public disabled: boolean = false;
}

View File

@ -0,0 +1,172 @@
import {
AfterViewInit,
ChangeDetectorRef,
Component,
ContentChildren,
ElementRef, EventEmitter,
Input, OnDestroy, Output,
QueryList,
ViewChild
} from "@angular/core";
import {SliderTabComponent} from "./slider-tab.component";
import {ActivatedRoute, Router} from "@angular/router";
import {Subscription} from "rxjs";
import Timeout = NodeJS.Timeout;
declare var UIkit;
@Component({
selector: 'slider-tabs',
template: `
<div #tabsElement class="uk-position-relative" [class.uk-slider]="position === 'horizontal'"
[ngClass]="background">
<div [class.uk-slider-container-tabs]="position === 'horizontal'">
<ul class="uk-tab" [class.uk-flex-nowrap]="position === 'horizontal'"
[class.uk-slider-items]="position === 'horizontal'"
[class.uk-tab-left]="position === 'left'" [class.uk-tab-right]="position === 'right'"
[attr.uk-tab]="type === 'static'?('connect: .' + connect):null">
<ng-container *ngIf="type === 'scrollable'">
<li *ngFor="let tab of tabs.toArray()" class="uk-text-capitalize" [class.uk-active]="tab.active">
<a routerLink="./" [fragment]="tab.id">{{tab.title}}</a>
</li>
</ng-container>
</ul>
</div>
<a *ngIf="position === 'horizontal'" class="uk-position-center-left uk-blur-background" uk-slider-item="previous"><span
uk-icon="chevron-left"></span></a>
<a *ngIf="position === 'horizontal'" class="uk-position-center-right uk-blur-background" uk-slider-item="next"><span
uk-icon="chevron-right"></span></a>
</div>
`,
})
export class SliderTabsComponent implements AfterViewInit, OnDestroy {
//TODO now it works only for scrollable, to be extended
/**
* Type of tabs: Static = Uikit tabs with @connect class, Dynamic = Active is defined by tabComponent.active input,
* Scrollable = Active is defined by the active fragment of URL and position of scroll
* */
@Input()
public type: 'static' | 'dynamic' | 'scrollable' = 'static';
/**
* Connect class in static type. Default: uk-switcher
* */
@Input()
public connect = 'uk-switcher';
/**
* Threshold between 0.0 to 1.0 for Intersection Observer
* */
@Input()
public scrollThreshold = 0.1;
/**
* Tabs view: Horizontal is the default.
* */
@Input()
public position: 'horizontal' | 'left' | 'right' = 'horizontal';
/**
* Tabs background
* */
@Input()
public background: string;
@ContentChildren(SliderTabComponent) tabs: QueryList<SliderTabComponent>;
@ViewChild('tabsElement') tabsElement: ElementRef;
/**
* Notify regarding new active element
* */
@Output() activeEmitter: EventEmitter<string> = new EventEmitter<string>();
public init: boolean = false;
private subscriptions: any[] = [];
private observer: IntersectionObserver;
private timeout: Timeout;
constructor(private route: ActivatedRoute,
private router: Router,
private cdr: ChangeDetectorRef) {
}
ngAfterViewInit() {
if (typeof document !== 'undefined' && this.tabs.length > 0) {
setTimeout(() => {
if(this.position === 'horizontal') {
let slider = UIkit.slider(this.tabsElement.nativeElement, {finite: true});
slider.clsActive = 'uk-slider-active';
slider.updateActiveClasses();
this.init = true;
slider.slides.forEach(item => {
item.classList.remove('uk-active');
});
if (this.type === 'scrollable') {
this.scrollable(slider);
}
} else {
this.scrollable();
}
});
}
}
private scrollable(slider = null) {
this.activeFragment(this.route.snapshot.fragment, slider);
this.subscriptions.push(this.route.fragment.subscribe(fragment => {
this.activeFragment(fragment,slider);
}));
this.setObserver();
}
private setObserver() {
if (this.observer) {
this.observer.disconnect();
}
this.observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
if (this.timeout) {
clearTimeout(this.timeout);
}
this.timeout = setTimeout(() => {
this.router.navigate(['./'], {
fragment: entry.target.id,
relativeTo: this.route,
state: {disableScroll: true}
});
}, 200);
}
});
}, {threshold: 0.1});
this.tabs.forEach(tab => {
let element = document.getElementById(tab.id);
if (element) {
this.observer.observe(element);
}
});
}
private activeFragment(fragment, slider) {
let index = 0;
if (fragment) {
index = this.tabs.toArray().findIndex(item => item.id == fragment);
}
if(slider) {
slider.show(index);
}
this.tabs.forEach((tab, i) => {
if (index === i) {
tab.active = true;
this.activeEmitter.emit(tab.id);
} else {
tab.active = false;
}
});
this.cdr.detectChanges();
}
ngOnDestroy() {
this.subscriptions.forEach(subscription => {
if (subscription instanceof Subscription) {
subscription.unsubscribe();
}
});
if (this.observer) {
this.observer.disconnect();
}
}
}

View File

@ -0,0 +1,13 @@
import {NgModule} from "@angular/core";
import {CommonModule} from "@angular/common";
import {SliderTabsComponent} from "./slider-tabs.component";
import {SliderTabComponent} from "./slider-tab.component";
import {RouterModule} from "@angular/router";
@NgModule({
imports: [CommonModule, RouterModule],
declarations: [SliderTabsComponent, SliderTabComponent],
exports: [SliderTabsComponent, SliderTabComponent]
})
export class SliderTabsModule {}