diff --git a/sharedComponents/slider-utils/slider-column.component.ts b/sharedComponents/slider-utils/slider-column.component.ts new file mode 100644 index 00000000..d3bb22a4 --- /dev/null +++ b/sharedComponents/slider-utils/slider-column.component.ts @@ -0,0 +1,78 @@ +import {AfterContentInit, Component, ContentChildren, Input, OnDestroy, QueryList} from '@angular/core'; +import {SliderItemComponent} from "./slider-item.component"; +import {SliderNavItemComponent} from "./slider-nav-item.component"; +import {SliderContainerComponent} from "./slider-container.component"; +import {LayoutService} from "../../dashboard/sharedComponents/sidebar/layout.service"; +import {Subscriber} from "rxjs"; + +@Component({ + selector: 'slider-column', + template: ` +
+ +
+ ` +}) +export class SliderColumnComponent implements AfterContentInit, OnDestroy { + @Input() + type: 'slider' | 'nav' = null; + @Input() + animation = 'uk-animation-fade'; + @ContentChildren(SliderItemComponent) items: QueryList; + @ContentChildren(SliderNavItemComponent) navItems: QueryList; + public isMobile: boolean; + private subscriptions: any[] = []; + + constructor(private layoutService: LayoutService) { + } + + ngOnDestroy() { + this.subscriptions.forEach(value => { + if (value instanceof Subscriber) { + value.unsubscribe(); + } + }); + } + + ngAfterContentInit() { + this.slides.forEach(slide => { + slide.init(this.animation); + }); + this.navItems.forEach(slide => { + slide.init(this.animation); + }); + this.subscriptions.push(this.layoutService.isMobile.subscribe(isMobile => { + this.isMobile = isMobile; + })); + } + + change(time: number) { + if (this.type === 'slider') { + let slides = this.slides; + for (let i = 0; i < slides.length; i++) { + slides[i].setActive(slides[i].start <= time && (!slides[i + 1] || slides[i + 1].start > time)); + } + } + if (this.type === 'nav') { + let slides = this.navItems; + for (let i = 0; i < slides.length; i++) { + if (this.isMobile) { + slides.get(i).setActive(slides.get(i).start <= time && (!slides.get(i + 1) || slides.get(i + 1).start > time)); + } + slides.get(i).active = slides.get(i).start <= time && (!slides.get(i + 1) || slides.get(i + 1).start > time); + } + } + } + + setContainer(container: SliderContainerComponent) { + if (this.type === 'nav') { + this.navItems.forEach(item => { + item.container = container; + }); + } + } + + get slides(): SliderItemComponent[] { + return this.items.filter(item => item.type === 'slide'); + } +} diff --git a/sharedComponents/slider-utils/slider-container.component.ts b/sharedComponents/slider-utils/slider-container.component.ts new file mode 100644 index 00000000..856eeb2f --- /dev/null +++ b/sharedComponents/slider-utils/slider-container.component.ts @@ -0,0 +1,128 @@ +import { + AfterContentInit, + ChangeDetectorRef, + Component, + ContentChildren, + ElementRef, + Input, OnDestroy, + OnInit, + QueryList +} from "@angular/core"; +import {SliderColumnComponent} from "./slider-column.component"; + +export class Stage { + value: number; + max: number; +} + +@Component({ + selector: 'slider-container', + template: ` +
+ +
+
+
+
+ +
+
+
+ ` +}) +export class SliderContainerComponent implements OnInit, OnDestroy, AfterContentInit { + private static INTERVAL = 10; + private static ANIMATION_DURATION = 600; // UIKit progress animation duration + + @ContentChildren(SliderColumnComponent) sliders: QueryList; + @Input() + navigation: 'progress' | null = null; + @Input() + total: number = 0; + @Input() + period: number = 3000; // in ms (>= 1000ms) + @Input() + infinite: boolean = false; + @Input() + parent: HTMLDivElement; + stages: Stage[] = []; + time: number = 0; + interval: any; + observer: IntersectionObserver; + initialized: boolean = false; + stopped: boolean = true; + + constructor(private cdr: ChangeDetectorRef, private element: ElementRef) { + } + + ngOnInit() { + this.period = this.period - SliderContainerComponent.ANIMATION_DURATION; + } + + ngOnDestroy() { + if(this.observer) { + this.observer.disconnect(); + } + } + + ngAfterContentInit() { + this.setObserver(); + this.sliders.forEach(slider => { + slider.setContainer(this); + slider.navItems.forEach(item => { + if(this.parent) { + item.background = getComputedStyle(this.parent).backgroundColor; + } + }); + }); + } + + setObserver() { + let options = { + root: null, + rootMargin: '0px', + threshold: 0.1 + }; + this.observer = new IntersectionObserver(entries => { + entries.forEach(entry => { + if (entry.isIntersecting && !this.initialized) { + this.stopped = false; + this.start(0); + this.initialized = true; + } else { + this.initialized = false; + this.stopped = true; + } + }); + }, options); + this.observer.observe(this.element.nativeElement); + } + + start(time: number) { + this.stages = []; + for(let i = 0; i < this.total; i++) { + this.stages.push({value: (time > i?this.period:(time - i)), max: this.period}); + } + if(this.interval) { + clearInterval(this.interval); + } + this.cdr.detectChanges(); + this.interval = setInterval(() => { + let current = Math.floor(time); + this.stages[current].value += SliderContainerComponent.INTERVAL; + this.time = current + this.stages[current].value/this.stages[current].max; + this.sliders.forEach(slider => { + slider.change(this.time); + }); + if(this.stages[current].value >= this.stages[current].max) { + clearInterval(this.interval); + let next = (current + 1 > this.total - 1)?0:current + 1; + if(!this.stopped && (this.infinite || next > current)) { + setTimeout(() => { + this.start(next); + }, SliderContainerComponent.ANIMATION_DURATION); + } + } + }, SliderContainerComponent.INTERVAL); + } +} diff --git a/sharedComponents/slider-utils/slider-item.component.ts b/sharedComponents/slider-utils/slider-item.component.ts new file mode 100644 index 00000000..49202132 --- /dev/null +++ b/sharedComponents/slider-utils/slider-item.component.ts @@ -0,0 +1,29 @@ +import {Component, ElementRef, Input} from "@angular/core"; + +@Component({ + selector: 'slider-item', + template: ` + + ` +}) +export class SliderItemComponent { + @Input() + type: 'slide' | 'static' = 'slide'; + @Input() + start: number; + + constructor(private element: ElementRef) { + } + + init(animation: string) { + this.element.nativeElement.classList.add(animation); + } + + setActive(active: boolean) { + if(active) { + this.element.nativeElement.classList.remove('uk-hidden'); + } else { + this.element.nativeElement.classList.add('uk-hidden'); + } + } +} diff --git a/sharedComponents/slider-utils/slider-nav-item.component.ts b/sharedComponents/slider-utils/slider-nav-item.component.ts new file mode 100644 index 00000000..1a2764e8 --- /dev/null +++ b/sharedComponents/slider-utils/slider-nav-item.component.ts @@ -0,0 +1,51 @@ +import {AfterViewInit, Component, ElementRef, Input, ViewChild} from "@angular/core"; +import {ActivatedRoute} from "@angular/router"; +import {SliderContainerComponent} from "./slider-container.component"; +import {SliderItemComponent} from "./slider-item.component"; + +export interface Link { + routerLink?: { + commands: string[] | string, + queryParams?: Object, + fragment?: string, + relativeTo?: ActivatedRoute + } + href?: string + external?: boolean +} + +@Component({ + selector: 'slider-nav-item', + template: ` +
+
+ +
+ +
+ ` +}) +export class SliderNavItemComponent extends SliderItemComponent implements AfterViewInit { + @Input() + link: Link = null; + @Input() + start: number; + active: boolean = false; + container: SliderContainerComponent; + background: string; + @ViewChild('linkElement') linkElement: ElementRef; + + ngAfterViewInit() { + if(this.linkElement) { + this.linkElement.nativeElement.style.background = this.background; + } + } +} diff --git a/sharedComponents/slider-utils/slider-utils.module.ts b/sharedComponents/slider-utils/slider-utils.module.ts index da0d1181..ca8ded90 100644 --- a/sharedComponents/slider-utils/slider-utils.module.ts +++ b/sharedComponents/slider-utils/slider-utils.module.ts @@ -2,10 +2,15 @@ import {NgModule} from "@angular/core"; import {CommonModule} from "@angular/common"; import {SliderArrowComponent} from "./slider-arrow.component"; import {IconsModule} from "../../utils/icons/icons.module"; +import {SliderNavItemComponent} from "./slider-nav-item.component"; +import {SliderItemComponent} from "./slider-item.component"; +import {RouterLink} from "@angular/router"; +import {SliderColumnComponent} from "./slider-column.component"; +import {SliderContainerComponent} from "./slider-container.component"; @NgModule({ - imports: [CommonModule, IconsModule], - declarations: [SliderArrowComponent], - exports: [SliderArrowComponent] + imports: [CommonModule, IconsModule, RouterLink], + declarations: [SliderContainerComponent, SliderArrowComponent, SliderNavItemComponent, SliderItemComponent, SliderColumnComponent], + exports: [SliderContainerComponent, SliderArrowComponent, SliderNavItemComponent, SliderItemComponent, SliderColumnComponent], }) -export class SliderUtilsModule {} \ No newline at end of file +export class SliderUtilsModule {} diff --git a/utils/gif-slider/gif-slider.component.ts b/utils/gif-slider/gif-slider.component.ts deleted file mode 100644 index 4f194e7a..00000000 --- a/utils/gif-slider/gif-slider.component.ts +++ /dev/null @@ -1,47 +0,0 @@ -import {Component, Input} from '@angular/core'; - -@Component({ - selector: 'gif-slider', - template: ` -
-
-
    -
  • -
    -
    - -
    -
    -
    -
    {{gif.header}}
    -
    {{gif.text}}
    - -
    -
    -
    -
  • -
-
    -
    -
    ` -}) - -export class GifSliderComponent { - @Input() gifs: {"gif": string, "header": string, "text"}[] = []; - @Input() link = false; - @Input() route = null; - @Input() linkTitle = null; - @Input() gifRight = false; - @Input() velocity = 0; - @Input() autoplay = true; - @Input() autoplayInterval = 4000; - @Input() pauseOnHover = false; - @Input() center = true; - -} diff --git a/utils/gif-slider/gif-slider.module.ts b/utils/gif-slider/gif-slider.module.ts deleted file mode 100644 index 39f466b4..00000000 --- a/utils/gif-slider/gif-slider.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { NgModule} from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { RouterModule } from '@angular/router'; -import {GifSliderComponent} from "./gif-slider.component"; - -@NgModule({ - imports: [ - CommonModule, RouterModule - ], - declarations: [ - GifSliderComponent - ], - providers:[ - ], - exports: [ - GifSliderComponent - ] -}) -export class GifSliderModule { } diff --git a/utils/theme/theme.component.html b/utils/theme/theme.component.html index c3192d3d..660bfd7d 100644 --- a/utils/theme/theme.component.html +++ b/utils/theme/theme.component.html @@ -44,7 +44,6 @@
    -
    @@ -292,10 +291,10 @@
    -
    +
    -
    +
    @@ -468,6 +467,41 @@
    + + +
    + + + + ipad + + + ipad + + + ipad + + + + +

    + OpenAIRE Connect +

    +
    + +

    + OpenAIRE Explore +

    +
    + +

    + OpenAIRE Monitor +

    +
    +
    +
    +
    +
    diff --git a/utils/theme/theme.component.ts b/utils/theme/theme.component.ts index 37bba193..1b4854b7 100644 --- a/utils/theme/theme.component.ts +++ b/utils/theme/theme.component.ts @@ -3,6 +3,7 @@ import {ValidatorFn, Validators} from "@angular/forms"; import {StringUtils} from "../string-utils.class"; import {ActivatedRoute} from "@angular/router"; import {Subscriber} from "rxjs"; +import {OpenaireEntities} from "../properties/searchFields"; @Component({ @@ -36,4 +37,6 @@ export class ThemeComponent implements OnInit, OnDestroy { } }); } + + protected readonly OpenaireEntities = OpenaireEntities; } diff --git a/utils/theme/theme.module.ts b/utils/theme/theme.module.ts index cdb56ed8..a516a84f 100644 --- a/utils/theme/theme.module.ts +++ b/utils/theme/theme.module.ts @@ -5,11 +5,12 @@ import {ThemeRoutingModule} from "./theme-routing.module"; import {ThemeComponent} from "./theme.component"; import {IconsModule} from "../icons/icons.module"; import {InputModule} from "../../sharedComponents/input/input.module"; +import {SliderUtilsModule} from "../../sharedComponents/slider-utils/slider-utils.module"; @NgModule({ imports: [ CommonModule, RouterModule, - ThemeRoutingModule, IconsModule, InputModule + ThemeRoutingModule, IconsModule, InputModule, SliderUtilsModule ], declarations: [ ThemeComponent