import { AfterViewInit, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import {
Component,
ComponentFactoryResolver,
Directive,
ElementRef,
HostBinding,
HostListener,
Inject,
Input,
ViewChild,
ViewContainerRef
} from '@angular/core';
import {
FC_NODE_COMPONENT_CONFIG,
FcCallbacks,
FcConnector,
FcNode,
FcNodeComponentConfig,
FcNodeRectInfo,
FlowchartConstants,
UserNodeCallbacks
} from './ngx-flowchart.models';
import { FcModelService } from './model.service';
@Component({
selector: 'fc-node',
template: '',
styleUrls: ['./node.component.scss']
})
export class FcNodeContainerComponent implements OnInit, AfterViewInit, OnChanges {
@Input()
callbacks: FcCallbacks;
@Input()
userNodeCallbacks: UserNodeCallbacks;
@Input()
node: FcNode;
@Input()
selected: boolean;
@Input()
edit: boolean;
@Input()
underMouse: boolean;
@Input()
mouseOverConnector: FcConnector;
@Input()
modelservice: FcModelService;
@Input()
dragging: boolean;
@HostBinding('attr.id')
get nodeId(): string {
return this.node.id;
}
@HostBinding('style.top')
get top(): string {
return this.node.y + 'px';
}
@HostBinding('style.left')
get left(): string {
return this.node.x + 'px';
}
nodeComponent: FcNodeComponent;
@ViewChild('nodeContent', {read: ViewContainerRef, static: true}) nodeContentContainer: ViewContainerRef;
constructor(@Inject(FC_NODE_COMPONENT_CONFIG) private nodeComponentConfig: FcNodeComponentConfig,
private elementRef: ElementRef,
private componentFactoryResolver: ComponentFactoryResolver) {
}
ngOnInit(): void {
if (!this.userNodeCallbacks) {
this.userNodeCallbacks = {};
}
this.userNodeCallbacks.nodeEdit = this.userNodeCallbacks.nodeEdit || (() => {});
this.userNodeCallbacks.doubleClick = this.userNodeCallbacks.doubleClick || (() => {});
this.userNodeCallbacks.mouseDown = this.userNodeCallbacks.mouseDown || (() => {});
this.userNodeCallbacks.mouseEnter = this.userNodeCallbacks.mouseEnter || (() => {});
this.userNodeCallbacks.mouseLeave = this.userNodeCallbacks.mouseLeave || (() => {});
const element = $(this.elementRef.nativeElement);
element.addClass(FlowchartConstants.nodeClass);
if (!this.node.readonly) {
element.attr('draggable', 'true');
}
this.updateNodeClass();
this.modelservice.nodes.setHtmlElement(this.node.id, element[0]);
this.nodeContentContainer.clear();
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.nodeComponentConfig.nodeComponentType);
const componentRef = this.nodeContentContainer.createComponent(componentFactory);
this.nodeComponent = componentRef.instance;
this.nodeComponent.callbacks = this.callbacks;
this.nodeComponent.userNodeCallbacks = this.userNodeCallbacks;
this.nodeComponent.node = this.node;
this.nodeComponent.modelservice = this.modelservice;
this.updateNodeComponent();
this.nodeComponent.width = this.elementRef.nativeElement.offsetWidth;
this.nodeComponent.height = this.elementRef.nativeElement.offsetHeight;
}
ngAfterViewInit(): void {
this.nodeComponent.width = this.elementRef.nativeElement.offsetWidth;
this.nodeComponent.height = this.elementRef.nativeElement.offsetHeight;
}
ngOnChanges(changes: SimpleChanges): void {
let updateNode = false;
for (const propName of Object.keys(changes)) {
const change = changes[propName];
if (!change.firstChange && change.currentValue !== change.previousValue) {
if (['selected', 'edit', 'underMouse', 'mouseOverConnector', 'dragging'].includes(propName)) {
updateNode = true;
}
}
}
if (updateNode) {
this.updateNodeClass();
this.updateNodeComponent();
}
}
private updateNodeClass() {
const element = $(this.elementRef.nativeElement);
this.toggleClass(element, FlowchartConstants.selectedClass, this.selected);
this.toggleClass(element, FlowchartConstants.editClass, this.edit);
this.toggleClass(element, FlowchartConstants.hoverClass, this.underMouse);
this.toggleClass(element, FlowchartConstants.draggingClass, this.dragging);
}
private updateNodeComponent() {
this.nodeComponent.selected = this.selected;
this.nodeComponent.edit = this.edit;
this.nodeComponent.underMouse = this.underMouse;
this.nodeComponent.mouseOverConnector = this.mouseOverConnector;
this.nodeComponent.dragging = this.dragging;
}
private toggleClass(element: JQuery, clazz: string, set: boolean) {
if (set) {
element.addClass(clazz);
} else {
element.removeClass(clazz);
}
}
@HostListener('mousedown', ['$event'])
mousedown(event: MouseEvent) {
event.stopPropagation();
}
@HostListener('dragstart', ['$event'])
dragstart(event: Event | any) {
if (!this.node.readonly) {
this.callbacks.nodeDragstart(event, this.node);
}
}
@HostListener('dragend', ['$event'])
dragend(event: Event | any) {
if (!this.node.readonly) {
this.callbacks.nodeDragend(event);
}
}
@HostListener('click', ['$event'])
click(event: MouseEvent) {
if (!this.node.readonly) {
this.callbacks.nodeClicked(event, this.node);
}
}
@HostListener('mouseover', ['$event'])
mouseover(event: MouseEvent) {
if (!this.node.readonly) {
this.callbacks.nodeMouseOver(event, this.node);
}
}
@HostListener('mouseout', ['$event'])
mouseout(event: MouseEvent) {
if (!this.node.readonly) {
this.callbacks.nodeMouseOut(event, this.node);
}
}
}
@Directive()
export abstract class FcNodeComponent implements OnInit {
@Input()
callbacks: FcCallbacks;
@Input()
userNodeCallbacks: UserNodeCallbacks;
@Input()
node: FcNode;
@Input()
selected: boolean;
@Input()
edit: boolean;
@Input()
underMouse: boolean;
@Input()
mouseOverConnector: FcConnector;
@Input()
modelservice: FcModelService;
@Input()
dragging: boolean;
flowchartConstants = FlowchartConstants;
width: number;
height: number;
nodeRectInfo: FcNodeRectInfo = {
top: () => {
return this.node.y;
},
left: () => {
return this.node.x;
},
bottom: () => {
return this.node.y + this.height;
},
right: () => {
return this.node.x + this.width;
},
width: () => {
return this.width;
},
height: () => {
return this.height;
}
};
ngOnInit(): void {
}
}