Page MenuHomec4science

interacted.directive.ts
No OneTemporary

File Metadata

Created
Mon, Jun 3, 13:19

interacted.directive.ts

import { Directive, HostListener, Input, ElementRef, OnInit} from '@angular/core';
import { PageViewService } from './page-view.service';
import { Interactable, Word, Line } from './models';
export interface XYOffset {
x: number;
y: number;
}
/**
* This directive informs the {@link /injectables/PageViewService.html|PageViewService} about
* mouse events on interactable objects and scrolls interactable objects in view if they are
* invisible.
**/
@Directive({
selector: '[interactedObject]'
})
export class InteractedDirective implements OnInit {
/**
* the object of this rect
**/
@Input('interactedObject') interactedObject: Interactable;
/**
* the identification string of this Interactable's textfield (e.g. 'first textfield' or 'second textfield')
**/
@Input() identity: string = 'first textfield';
/**
* the scrollable HTML-container of this Interactable's textfield.
**/
@Input() container: HTMLElement;
/**
* The time (in milliseconds) the timer should wait before
* the element is scrolled in view.
**/
delay: number = 500;
/**
* The ID of the timeout set by {@link /directives/InteractedDirective.html#timeoutScroll|timeoutScroll}.
**/
timeoutID: number = -1;
/**
* Whether or not the element should scroll into view when the timer expires.
**/
doScroll: boolean = false;
xyOffset: XYOffset = { x: 0, y: 0 };
constructor(private pageViewService: PageViewService, private el: ElementRef) {}
/**
* Subscribe to on/offHovered and onClicked methods of the {@link /injectables/PageViewService.html|PageViewService}
* and scroll hovered object in view if it is invisible.
**/
ngOnInit(){
if (this.container != null && this.container != undefined){
let containerRect: DOMRect = <DOMRect>this.container.getBoundingClientRect();
this.xyOffset = { x: containerRect.left, y: containerRect.top };
}
this.interactedObject.textfield_identity = this.identity;
this.pageViewService.onClickedWord.subscribe(
(clickedWord: Word) => { this.scrollIntoViewIfNeeded(clickedWord, 'Word', 0)
});
this.pageViewService.onHoveredWord.subscribe(
(hoveredWord: Word) => { this.scrollIntoViewIfNeeded(hoveredWord, 'Word')
});
this.pageViewService.offHoveredWord.subscribe(
(hoveredWord: Word) => {
this.clearTimeout()
});
this.pageViewService.offHoveredLine.subscribe(
(hoveredLine: Line) => { this.clearTimeout()
});
this.pageViewService.onHoveredLine.subscribe(
(hoveredLine: Line) => { this.scrollIntoViewIfNeeded(hoveredLine, 'Line')
});
this.pageViewService.onClickedLine.subscribe(
(clickedLine: Line) => { this.scrollIntoViewIfNeeded(clickedLine, 'Line', 0)
});
}
/**
* Clear timeout and prevent element from scrolling into view.
**/
private clearTimeout(){
if(this.timeoutID != -1){
this.doScroll = false;
clearTimeout(this.timeoutID);
this.timeoutID = -1;
}
}
/**
* Scroll interactable object in view if it is invisible.
* @param hoveredItem interactable object that is hovered
* @param hoveredType string representation of object's type (i.e. 'Word' | 'Line')
**/
private scrollIntoViewIfNeeded(hoveredItem: Interactable, hoveredType: String, delay: number= this.delay){
if (hoveredType == 'Word' && this.interactedObject.datatype == 'Word' && this.identity != hoveredItem.textfield_identity){
let hoveredWord = <Word>hoveredItem
let currentWord = <Word>this.interactedObject
if (currentWord.id == hoveredWord.id && currentWord.is_top_object && this.isElementInvisible()){
this.timeoutScroll(delay);
}
} else if (hoveredType =='Line' && this.interactedObject.datatype == 'Line'){
let hoveredLine = <Line>hoveredItem
let currentLine = <Line>this.interactedObject
if (currentLine !== hoveredLine && currentLine.id == hoveredLine.id && this.isElementInvisible()){
this.timeoutScroll(delay)
}
}
}
/**
* Scroll element in view if timeout has not been canceled during its countdown.
**/
private timeoutScroll(delay: number) {
let behavior = (delay == 0) ? "instant" : "smooth";
this.doScroll = true;
this.timeoutID = window.setTimeout(()=>{
if (this.doScroll){
this.el.nativeElement.scrollIntoView({ 'behavior': behavior});
}
}, delay);
}
/**
* Return whether interactable object is invisible, i.e. whether it is outside of
* its scrollable container's viewport.
**/
private isElementInvisible(): boolean {
if (this.container == null || this.container == undefined || this.container.getAttribute('class') == 'inline'){
return false;
}
let myRect: DOMRect = <DOMRect>this.el.nativeElement.getBoundingClientRect();
let containerRect: DOMRect = <DOMRect>this.container.getBoundingClientRect();
return myRect.top < containerRect.top
|| myRect.bottom > containerRect.bottom
|| myRect.left < containerRect.left
|| myRect.right > containerRect.right;
}
/**
* informs the {@link /injectables/PageViewService.html|PageViewService} about
* click events on {@link #interactedObject|interactedObject}.
**/
@HostListener('click', ['$event']) onMouseClick( e: MouseEvent) {
this.pageViewService.onClickService(this.interactedObject, { visible: true, layerX: e.layerX, layerY: e.layerY, clientX: e.clientX, clientY: e.clientY });
}
/**
* informs the {@link /injectables/PageViewService.html|PageViewService} about
* mouse enter events on {@link #interactedObject|interactedObject}.
**/
@HostListener('mouseenter', ['$event']) onMouseEnter( e: MouseEvent) {
this.pageViewService.onHoverService(this.interactedObject, { visible: true, layerX: e.layerX, layerY: e.layerY, clientX: e.clientX, clientY: e.clientY });
}
/**
* informs the {@link /injectables/PageViewService.html|PageViewService} about
* mouse leave events on {@link #interactedObject|interactedObject}.
**/
@HostListener('mouseleave') onMouseLeave() {
this.pageViewService.offHoverService(this.interactedObject);
}
}

Event Timeline