diff options
author | 2018-03-27 15:50:00 +0800 | |
---|---|---|
committer | 2018-03-27 15:50:00 +0800 | |
commit | 309680feb63ec6a49163387cb1e116b721254847 (patch) | |
tree | 0d58a1e12acad73b1421236412f6e41ca08196d3 /sdc-workflow-designer-ui/src/app/paletx | |
parent | 5b3cc39ad139793fe689683a4a7344ddb3c8361f (diff) |
Add paletx common & util
Add paletx common & util
Issue-ID: SDC-1130,SDC-1131
Change-Id: Ibbdd8d4fabf45637be7d44400c18d34efc15d46e
Signed-off-by: YuanHu <yuan.hu1@zte.com.cn>
Diffstat (limited to 'sdc-workflow-designer-ui/src/app/paletx')
5 files changed, 898 insertions, 0 deletions
diff --git a/sdc-workflow-designer-ui/src/app/paletx/common/shared.ts b/sdc-workflow-designer-ui/src/app/paletx/common/shared.ts new file mode 100644 index 00000000..e0d9bdb5 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/common/shared.ts @@ -0,0 +1,283 @@ +import {CommonModule} from '@angular/common'; +import {AfterContentInit, ContentChild, ContentChildren, Directive, EmbeddedViewRef, EventEmitter, Input, NgModule} from '@angular/core'; +import {OnDestroy, OnInit, Output, QueryList, TemplateRef, ViewContainerRef} from '@angular/core'; +import {Component} from '@angular/core'; + +@Component({selector: 'plx-header', template: '<ng-content></ng-content>'}) +export class PlxHeaderComponent { +} + +@Component({selector: 'plx-footer', template: '<ng-content></ng-content>'}) +export class PlxFooterComponent { +} + +@Directive({selector: '[pTemplate]'}) +export class PlxPrimeTemplateDirective { + @Input() public type: string; + + @Input() public pxTemplate: string; + + constructor(public template: TemplateRef<any>) {} + + getType(): string { + if (this.type) { + console.log( + 'Defining a pTemplate with type property is deprecated use pTemplate="type" instead.'); + return this.type; + } else { + return this.pxTemplate; + } + } +} + +@Directive({selector: '[pxTemplateWrapper]'}) +export class PlxTemplateWrapperDirective implements OnInit, OnDestroy { + @Input() public item: any; + + @Input() public index: number; + + @Input() public pxTemplateWrapper: TemplateRef<any>; + + view: EmbeddedViewRef<any>; + + constructor(public viewContainer: ViewContainerRef) {} + + ngOnInit() { + this.view = this.viewContainer.createEmbeddedView( + this.pxTemplateWrapper, {'\$implicit': this.item, 'index': this.index}); + } + + ngOnDestroy() { + this.view.destroy(); + } +} + +@Component({selector: 'plx-column', template: ``}) +export class PlxColumnComponent implements AfterContentInit { + @Input() public field: string; + @Input() public sortField: string; + @Input() public header: string; + @Input() public footer: string; + @Input() public sortable: any; + @Input() public editable: boolean; + @Input() public filter: boolean; + @Input() public filterMatchMode: string; + @Input() public rowspan: number; + @Input() public colspan: number; + @Input() public style: any; + @Input() public styleClass: string; + @Input() public hidden: boolean; + @Input() public expander: boolean; + @Input() public selectionMode: string; + @Input() public filterPlaceholder: string; + @Input() public frozen: boolean; + @Output() sortFunction: EventEmitter<any> = new EventEmitter(); + @ContentChildren(PlxPrimeTemplateDirective) templates: QueryList<any>; + @ContentChild(TemplateRef) template: TemplateRef<any>; + + headerTemplate: TemplateRef<any>; + bodyTemplate: TemplateRef<any>; + footerTemplate: TemplateRef<any>; + filterTemplate: TemplateRef<any>; + editorTemplate: TemplateRef<any>; + + ngAfterContentInit(): void { + this.templates.forEach((item) => { + switch (item.getType()) { + case 'header': + this.headerTemplate = item.template; + break; + + case 'body': + this.bodyTemplate = item.template; + break; + + case 'footer': + this.footerTemplate = item.template; + break; + + case 'filter': + this.filterTemplate = item.template; + break; + + case 'editor': + this.editorTemplate = item.template; + break; + + default: + this.bodyTemplate = item.template; + } + }); + } +} + +@Component({selector: 'plx-row', template: ``}) +export class PlxRowComponent { + @ContentChildren(PlxColumnComponent) columns: QueryList<PlxColumnComponent>; +} + +@Component({selector: 'plx-header-column-group', template: ``}) +export class PlxHeaderColumnGroupComponent { + @ContentChildren(PlxRowComponent) rows: QueryList<any>; +} + +@Component({selector: 'plx-footer-column-group', template: ``}) +export class PlxFooterColumnGroupComponent { + @ContentChildren(PlxRowComponent) rows: QueryList<any>; +} + +@Component({selector: 'plx-column-body-template-loader', template: ``}) +export class PlxColumnBodyTemplateLoaderComponent implements OnInit, OnDestroy { + @Input() public column: any; + + @Input() public rowData: any; + + @Input() public rowIndex: number; + + view: EmbeddedViewRef<any>; + + constructor(public viewContainer: ViewContainerRef) {} + + ngOnInit() { + this.view = + this.viewContainer.createEmbeddedView(this.column.bodyTemplate, { + '\$implicit': this.column, + 'rowData': this.rowData, + 'rowIndex': this.rowIndex + }); + } + + ngOnDestroy() { + this.view.destroy(); + } +} + +@Component({selector: 'plx-column-header-template-loader', template: ``}) +export class PlxColumnHeaderTemplateLoaderComponent implements OnInit, + OnDestroy { + @Input() public column: any; + + view: EmbeddedViewRef<any>; + + constructor(public viewContainer: ViewContainerRef) {} + + ngOnInit() { + this.view = this.viewContainer.createEmbeddedView( + this.column.headerTemplate, {'\$implicit': this.column}); + } + + ngOnDestroy() { + this.view.destroy(); + } +} + +@Component({selector: 'plx-column--footer-template-loader', template: ``}) +export class PlxColumnFooterTemplateLoaderComponent implements OnInit, + OnDestroy { + @Input() public column: any; + + view: EmbeddedViewRef<any>; + + constructor(public viewContainer: ViewContainerRef) {} + + ngOnInit() { + this.view = this.viewContainer.createEmbeddedView( + this.column.footerTemplate, {'\$implicit': this.column}); + } + + ngOnDestroy() { + this.view.destroy(); + } +} + +@Component({selector: 'plx-column-filter-template-loader', template: ``}) +export class PlxColumnFilterTemplateLoaderComponent implements OnInit, + OnDestroy { + @Input() public column: any; + + view: EmbeddedViewRef<any>; + + constructor(public viewContainer: ViewContainerRef) {} + + ngOnInit() { + this.view = this.viewContainer.createEmbeddedView( + this.column.filterTemplate, {'\$implicit': this.column}); + } + + ngOnDestroy() { + this.view.destroy(); + } +} + +@Component({selector: 'plx-column-editor-template-loader', template: ``}) +export class PlxColumnEditorTemplateLoaderComponent implements OnInit, + OnDestroy { + @Input() public column: any; + + @Input() public rowData: any; + + @Input() public rowIndex: any; + + view: EmbeddedViewRef<any>; + + constructor(public viewContainer: ViewContainerRef) {} + + ngOnInit() { + this.view = + this.viewContainer.createEmbeddedView(this.column.editorTemplate, { + '\$implicit': this.column, + 'rowData': this.rowData, + 'rowIndex': this.rowIndex + }); + } + + ngOnDestroy() { + this.view.destroy(); + } +} + +@Component({selector: 'plx-template-loader', template: ``}) +export class PlxTemplateLoaderComponent implements OnInit, OnDestroy { + @Input() public template: TemplateRef<any>; + + @Input() public data: any; + + view: EmbeddedViewRef<any>; + + constructor(public viewContainer: ViewContainerRef) {} + + ngOnInit() { + if (this.template) { + this.view = this.viewContainer.createEmbeddedView( + this.template, {'\$implicit': this.data}); + } + } + + ngOnDestroy() { + if (this.view) { + this.view.destroy(); + } + } +} + +@NgModule({ + imports: [CommonModule], + exports: [ + PlxHeaderComponent, PlxFooterComponent, PlxColumnComponent, + PlxTemplateWrapperDirective, PlxColumnHeaderTemplateLoaderComponent, + PlxColumnBodyTemplateLoaderComponent, PlxColumnFooterTemplateLoaderComponent, + PlxColumnFilterTemplateLoaderComponent, PlxPrimeTemplateDirective, + PlxTemplateLoaderComponent, PlxRowComponent, PlxHeaderColumnGroupComponent, + PlxFooterColumnGroupComponent, PlxColumnEditorTemplateLoaderComponent + ], + declarations: [ + PlxHeaderComponent, PlxFooterComponent, PlxColumnComponent, + PlxTemplateWrapperDirective, PlxColumnHeaderTemplateLoaderComponent, + PlxColumnBodyTemplateLoaderComponent, PlxColumnFooterTemplateLoaderComponent, + PlxColumnFilterTemplateLoaderComponent, PlxPrimeTemplateDirective, + PlxTemplateLoaderComponent, PlxRowComponent, PlxHeaderColumnGroupComponent, + PlxFooterColumnGroupComponent, PlxColumnEditorTemplateLoaderComponent + ] +}) +export class PlxSharedModule { +}
\ No newline at end of file diff --git a/sdc-workflow-designer-ui/src/app/paletx/util/popup.ts b/sdc-workflow-designer-ui/src/app/paletx/util/popup.ts new file mode 100644 index 00000000..dacbc0b9 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/util/popup.ts @@ -0,0 +1,59 @@ +import { + Injector, + TemplateRef, + ViewRef, + ViewContainerRef, + Renderer, + ComponentRef, + ComponentFactory, + ComponentFactoryResolver +} from '@angular/core'; + +export class ContentRef { + constructor(public nodes: any[], public viewRef?: ViewRef, public componentRef?: ComponentRef<any>) { + } +} + +export class PopupService<T> { + private _windowFactory: ComponentFactory<T>; + private _windowRef: ComponentRef<T>; + private _contentRef: ContentRef; + + constructor(type: any, private _injector: Injector, private _viewContainerRef: ViewContainerRef, private _renderer: Renderer, + componentFactoryResolver: ComponentFactoryResolver) { + this._windowFactory = componentFactoryResolver.resolveComponentFactory<T>(type); + } + + public open(content?: string | TemplateRef<any>, context?: any): ComponentRef<T> { + if (!this._windowRef) { + this._contentRef = this._getContentRef(content, context); + this._windowRef = + this._viewContainerRef.createComponent(this._windowFactory, 0, this._injector, this._contentRef.nodes); + } + + return this._windowRef; + } + + public close() { + if (this._windowRef) { + this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._windowRef.hostView)); + this._windowRef = null; + + if (this._contentRef.viewRef) { + this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._contentRef.viewRef)); + this._contentRef = null; + } + } + } + + private _getContentRef(content: string | TemplateRef<any>, context?: any): ContentRef { + if (!content) { + return new ContentRef([]); + } else if (content instanceof TemplateRef) { + const viewRef = this._viewContainerRef.createEmbeddedView(<TemplateRef<T>>content, context); + return new ContentRef([viewRef.rootNodes], viewRef); + } else { + return new ContentRef([[this._renderer.createText(null, `${content}`)]]); + } + } +} diff --git a/sdc-workflow-designer-ui/src/app/paletx/util/positioning.ts b/sdc-workflow-designer-ui/src/app/paletx/util/positioning.ts new file mode 100644 index 00000000..79399d6d --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/util/positioning.ts @@ -0,0 +1,425 @@ +// previous version: +// https://github.com/angular-ui/bootstrap/blob/07c31d0731f7cb068a1932b8e01d2312b796b4ec/src/position/position.js +export class Positioning { + private getStyle(element: HTMLElement, prop: string): string { + return window.getComputedStyle(element)[prop]; + } + + private isStaticPositioned(element: HTMLElement): boolean { + return (this.getStyle(element, 'position') || 'static') === 'static'; + } + + private offsetParent(element: HTMLElement): HTMLElement { + let offsetParentEl = <HTMLElement>element.offsetParent || document.documentElement; + + while (offsetParentEl && offsetParentEl !== document.documentElement && this.isStaticPositioned(offsetParentEl)) { + offsetParentEl = <HTMLElement>offsetParentEl.offsetParent; + } + + return offsetParentEl || document.documentElement; + } + + public position(element: HTMLElement, round = true): ClientRect { + let elPosition: ClientRect; + let parentOffset: ClientRect = {width: 0, height: 0, top: 0, bottom: 0, left: 0, right: 0}; + + if (this.getStyle(element, 'position') === 'fixed') { + elPosition = element.getBoundingClientRect(); + } else { + const offsetParentEl = this.offsetParent(element); + + elPosition = this.offset(element, false); + + if (offsetParentEl !== document.documentElement) { + parentOffset = this.offset(offsetParentEl, false); + } + + parentOffset.top += offsetParentEl.clientTop; + parentOffset.left += offsetParentEl.clientLeft; + } + + elPosition.top -= parentOffset.top; + elPosition.bottom -= parentOffset.top; + elPosition.left -= parentOffset.left; + elPosition.right -= parentOffset.left; + + if (round) { + elPosition.top = Math.round(elPosition.top); + elPosition.bottom = Math.round(elPosition.bottom); + elPosition.left = Math.round(elPosition.left); + elPosition.right = Math.round(elPosition.right); + } + + return elPosition; + } + + public offset(element: HTMLElement, round = true): ClientRect { + const elBcr = element.getBoundingClientRect(); + const viewportOffset = { + top: window.pageYOffset - document.documentElement.clientTop, + left: window.pageXOffset - document.documentElement.clientLeft + }; + + let elOffset = { + height: elBcr.height || element.offsetHeight, + width: elBcr.width || element.offsetWidth, + top: elBcr.top + viewportOffset.top, + bottom: elBcr.bottom + viewportOffset.top, + left: elBcr.left + viewportOffset.left, + right: elBcr.right + viewportOffset.left + }; + + if (round) { + elOffset.height = Math.round(elOffset.height); + elOffset.width = Math.round(elOffset.width); + elOffset.top = Math.round(elOffset.top); + elOffset.bottom = Math.round(elOffset.bottom); + elOffset.left = Math.round(elOffset.left); + elOffset.right = Math.round(elOffset.right); + } + + return elOffset; + } + + + public getPlacementPrimary(hostElement: HTMLElement, targetElement: HTMLElement, placement: string): string { + + // let placementPrimaryArray = ['right', 'bottom', 'left', 'top']; + // placementPrimaryArray.splice(placementPrimaryArray.indexOf(placementPrimary), 1); + // placementPrimaryArray.splice(0, 0, placementPrimary); + + let placementPrimaryArray = this.getTotalPlacementArr(placement); + let placementPrimary; + let placementSecondary; + let rect; + + let result = placementPrimaryArray.find(place => { + placementPrimary = place.split('-')[0] || 'top'; + placementSecondary = place.split('-')[1] || 'center'; + rect = this.getBoundingClientRect(placementPrimary, placementSecondary, hostElement, targetElement); + return this.canDisplay(rect); + }); + + if (!result) { + return placement; + } else { + return result; + } + } + + private getTotalPlacementArr(placement: string): any { + let placementPrimary = placement.split('-')[0] || 'top'; + + let placementBasic = ['right', 'bottom', 'left', 'top']; + placementBasic.splice(placementBasic.indexOf(placementPrimary), 1); + placementBasic.splice(0, 0, placementPrimary); + let placeTotal = { + right: [ + 'right', + 'right-top', + 'right-bottom' + ], + bottom: [ + 'bottom', + 'bottom-left', + 'bottom-right' + ], + left: [ + 'left', + 'left-top', + 'left-bottom' + ], + top: [ + 'top', + 'top-left', + 'top-right' + ] + } + let placeArr = []; + placeArr.push(placement); + placementBasic.forEach(placePri => { + placeTotal[placePri].forEach(palce => { + if (placement !== palce) { + placeArr.push(palce); + } + }); + }); + return placeArr; + } + + private canDisplay(rect): boolean { + if(this.isSheltered(rect, window)) { + return false; + } else { + var pElement = this.getParentElement(window); + if(pElement) { + const shelter = this.getShelter(pElement, window.parent, rect); + if(shelter) { + return false; + } else { + return true; + } + } else { + return true; + } + + } + } + + /** + * 判断当前位置在对应的窗口中是否会被遮挡 + * 用于iframe嵌套的场景 + * @param position + * @param currentWindow + */ + private isSheltered(position, currentWindow) { + if(position.left < 0 || position.top < 0 + || position.right > currentWindow.document.documentElement.clientWidth + || position.bottom > currentWindow.document.documentElement.clientHeight) { + return true; + } else { + return false; + } + } + + /** + * 递归判断当前元素是否会被视口遮挡 + * @param element + * @param currentWindow + * @param position + */ + public getShelter(element, currentWindow, position) { + var rect = element.getBoundingClientRect(); + position.left += rect.left; + position.right += rect.left; + position.top += rect.top; + position.bottom += rect.top; + + if(this.isSheltered(position, currentWindow)) { + return element; + } else { + if(currentWindow.parent != currentWindow) { // 判断是否到达最顶级容器 + var pElement = this.getParentElement(currentWindow); + return this.getShelter(pElement, currentWindow.parent, position); + } else { + return null; + } + } + } + + /** + * 查找当前容器在父窗口中的dom元素 + * 如:iframe场景中,则为在子页面中查找父页面中的iframe节点 + * @param currentWindow + */ + public getParentElement(currentWindow) { + if(currentWindow.parent !== currentWindow) { + var parentWindow = currentWindow.parent; + var frames = parentWindow.document.getElementsByTagName("iframe"); + for(var i=0; i<frames.length; i++) { + if(frames[i].contentWindow === currentWindow) { + return frames[i]; + } + } + } + } + + private getBoundingClientRect(placementPrimary, placementSecondary, hostElement, targetElement) { + const hostBcr = hostElement.getBoundingClientRect(); + + // const shiftWidth = { + // left: hostBcr.left, + // center: hostBcr.left + hostElement.offsetWidth/2 - targetElement.offsetWidth/2, + // right: hostBcr.left + hostElement.offsetWidth, + // }; + // const shiftHeight = { + // top: hostBcr.top, + // center: hostBcr.top + hostElement.offsetHeight/2 - targetElement.offsetHeight/2, + // bottom: hostBcr.top + hostElement.offsetHeight, + // }; + + const shiftWidth: any = { + left: hostBcr.left, + center: hostBcr.left + hostBcr.width / 2 - targetElement.offsetWidth / 2, + right: hostBcr.right - targetElement.offsetWidth + }; + const shiftHeight: any = { + top: hostBcr.top, + center: hostBcr.top + hostBcr.height / 2 - targetElement.offsetHeight / 2, + bottom: hostBcr.bottom - targetElement.offsetHeight + }; + switch (placementPrimary) { + case 'top': + return { + top: hostBcr.top - targetElement.offsetHeight, + bottom: hostBcr.top, + left: shiftWidth[placementSecondary], + right: shiftWidth[placementSecondary] + targetElement.offsetWidth + }; + case 'bottom': + return { + top: hostBcr.bottom, + bottom: hostBcr.bottom + targetElement.offsetHeight, + left: shiftWidth[placementSecondary], + right: shiftWidth[placementSecondary] + targetElement.offsetWidth + }; + case 'left': + return { + top: shiftHeight[placementSecondary], + bottom: shiftHeight[placementSecondary] + targetElement.offsetHeight, + left: hostBcr.left - targetElement.offsetWidth, + right: hostBcr.left + }; + case 'right': + return { + top: shiftHeight[placementSecondary], + bottom: shiftHeight[placementSecondary] + targetElement.offsetHeight, + left: hostBcr.right, + right: hostBcr.right + targetElement.offsetWidth, + }; + } + } + + public positionElements(hostElement: HTMLElement, targetElement: HTMLElement, placement: string, appendToBody?: boolean): ClientRect { + const hostElPosition = appendToBody ? this.offset(hostElement, false) : this.position(hostElement, false); + const shiftWidthPri: any = { + left: hostElPosition.left, + right: hostElPosition.left + hostElPosition.width + }; + const shiftHeightPri: any = { + top: hostElPosition.top, + bottom: hostElPosition.top + hostElPosition.height + }; + const targetElBCR = targetElement.getBoundingClientRect(); + placement = this.getPlacementPrimary(hostElement, targetElement, placement); + let placementPrimary = placement.split('-')[0] || 'top'; + const placementSecondary = placement.split('-')[1] || 'center'; + + let targetElPosition: ClientRect = { + height: targetElBCR.height || targetElement.offsetHeight, + width: targetElBCR.width || targetElement.offsetWidth, + top: 0, + bottom: targetElBCR.height || targetElement.offsetHeight, + left: 0, + right: targetElBCR.width || targetElement.offsetWidth + }; + const shiftWidthSec: any = { + left: hostElPosition.left, + center: hostElPosition.left + hostElPosition.width / 2 - targetElement.offsetWidth / 2, + right: hostElPosition.right - targetElement.offsetWidth + }; + const shiftHeightSec: any = { + top: hostElPosition.top, + center: hostElPosition.top + hostElPosition.height / 2 - targetElement.offsetHeight / 2, + bottom: hostElPosition.bottom - targetElement.offsetHeight + }; + + switch (placementPrimary) { + case 'top': + targetElPosition.top = hostElPosition.top - targetElement.offsetHeight; + targetElPosition.bottom += hostElPosition.top - targetElement.offsetHeight; + targetElPosition.left = shiftWidthSec[placementSecondary]; + targetElPosition.right += shiftWidthSec[placementSecondary]; + break; + case 'bottom': + targetElPosition.top = shiftHeightPri[placementPrimary]; + targetElPosition.bottom += shiftHeightPri[placementPrimary]; + targetElPosition.left = shiftWidthSec[placementSecondary]; + targetElPosition.right += shiftWidthSec[placementSecondary]; + break; + case 'left': + targetElPosition.top = shiftHeightSec[placementSecondary]; + targetElPosition.bottom += shiftHeightSec[placementSecondary]; + targetElPosition.left = hostElPosition.left - targetElement.offsetWidth; + targetElPosition.right += hostElPosition.left - targetElement.offsetWidth; + break; + case 'right': + targetElPosition.top = shiftHeightSec[placementSecondary]; + targetElPosition.bottom += shiftHeightSec[placementSecondary]; + targetElPosition.left = shiftWidthPri[placementPrimary]; + targetElPosition.right += shiftWidthPri[placementPrimary]; + break; + } + + targetElPosition.top = Math.round(targetElPosition.top); + targetElPosition.bottom = Math.round(targetElPosition.bottom); + targetElPosition.left = Math.round(targetElPosition.left); + targetElPosition.right = Math.round(targetElPosition.right); + + return targetElPosition; + } + + public positionElements_bak(hostElement: HTMLElement, targetElement: HTMLElement, placement: string, appendToBody?: boolean): ClientRect { + const hostElPosition = appendToBody ? this.offset(hostElement, false) : this.position(hostElement, false); + const shiftWidth: any = { + left: hostElPosition.left, + center: hostElPosition.left + hostElPosition.width / 2 - targetElement.offsetWidth / 2, + right: hostElPosition.left + hostElPosition.width + }; + const shiftHeight: any = { + top: hostElPosition.top, + center: hostElPosition.top + hostElPosition.height / 2 - targetElement.offsetHeight / 2, + bottom: hostElPosition.top + hostElPosition.height + }; + const targetElBCR = targetElement.getBoundingClientRect(); + const placementPrimary = placement.split('-')[0] || 'top'; + const placementSecondary = placement.split('-')[1] || 'center'; + + let targetElPosition: ClientRect = { + height: targetElBCR.height || targetElement.offsetHeight, + width: targetElBCR.width || targetElement.offsetWidth, + top: 0, + bottom: targetElBCR.height || targetElement.offsetHeight, + left: 0, + right: targetElBCR.width || targetElement.offsetWidth + }; + + switch (placementPrimary) { + case 'top': + targetElPosition.top = hostElPosition.top - targetElement.offsetHeight; + targetElPosition.bottom += hostElPosition.top - targetElement.offsetHeight; + targetElPosition.left = shiftWidth[placementSecondary]; + targetElPosition.right += shiftWidth[placementSecondary]; + break; + case 'bottom': + targetElPosition.top = shiftHeight[placementPrimary]; + targetElPosition.bottom += shiftHeight[placementPrimary]; + targetElPosition.left = shiftWidth[placementSecondary]; + targetElPosition.right += shiftWidth[placementSecondary]; + break; + case 'left': + targetElPosition.top = shiftHeight[placementSecondary]; + targetElPosition.bottom += shiftHeight[placementSecondary]; + targetElPosition.left = hostElPosition.left - targetElement.offsetWidth; + targetElPosition.right += hostElPosition.left - targetElement.offsetWidth; + break; + case 'right': + targetElPosition.top = shiftHeight[placementSecondary]; + targetElPosition.bottom += shiftHeight[placementSecondary]; + targetElPosition.left = shiftWidth[placementPrimary]; + targetElPosition.right += shiftWidth[placementPrimary]; + break; + } + + targetElPosition.top = Math.round(targetElPosition.top); + targetElPosition.bottom = Math.round(targetElPosition.bottom); + targetElPosition.left = Math.round(targetElPosition.left); + targetElPosition.right = Math.round(targetElPosition.right); + + return targetElPosition; + } +} + +const positionService = new Positioning(); +export function positionElements(hostElement: HTMLElement, targetElement: HTMLElement, placement: string, appendToBody?: boolean): void { + const pos = positionService.positionElements(hostElement, targetElement, placement, appendToBody); + + targetElement.style.top = `${pos.top}px`; + targetElement.style.left = `${pos.left}px`; +} + +export function getPlacement(hostElement: HTMLElement, targetElement: HTMLElement, placement: string): any { + const placementPrimary = positionService.getPlacementPrimary(hostElement, targetElement, placement); + console.log(placementPrimary); + return placementPrimary; +} diff --git a/sdc-workflow-designer-ui/src/app/paletx/util/triggers.ts b/sdc-workflow-designer-ui/src/app/paletx/util/triggers.ts new file mode 100644 index 00000000..708790a4 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/util/triggers.ts @@ -0,0 +1,66 @@ +export class Trigger { + constructor(public open: string, public close?: string) { + if (!close) { + this.close = open; + } + } + + public isManual() { + return this.open === 'manual' || this.close === 'manual'; + } +} + +const DEFAULT_ALIASES = { + hover: ['mouseenter', 'mouseleave'] +}; + +export function parseTriggers(triggers: string, aliases = DEFAULT_ALIASES): Trigger[] { + const trimmedTriggers = (triggers || '').trim(); + + if (trimmedTriggers.length === 0) { + return []; + } + + const parsedTriggers = trimmedTriggers.split(/\s+/).map(trigger => trigger.split(':')).map((triggerPair) => { + let alias = aliases[triggerPair[0]] || triggerPair; + return new Trigger(alias[0], alias[1]); + }); + + const manualTriggers = parsedTriggers.filter(triggerPair => triggerPair.isManual()); + + if (manualTriggers.length > 1) { + throw 'Triggers parse error: only one manual trigger is allowed'; + } + + if (manualTriggers.length === 1 && parsedTriggers.length > 1) { + throw 'Triggers parse error: manual trigger can\'t be mixed with other triggers'; + } + + return parsedTriggers; +} + +const noopFn = () => { + // TO DO +}; + +export function listenToTriggers(renderer: any, nativeElement: any, triggers: string, openFn, closeFn, toggleFn) { + const parsedTriggers = parseTriggers(triggers); + const listeners = []; + + if (parsedTriggers.length === 1 && parsedTriggers[0].isManual()) { + return noopFn; + } + + parsedTriggers.forEach((trigger: Trigger) => { + if (trigger.open === trigger.close) { + listeners.push(renderer.listen(nativeElement, trigger.open, toggleFn)); + } else { + listeners.push( + renderer.listen(nativeElement, trigger.open, openFn), renderer.listen(nativeElement, trigger.close, closeFn)); + } + }); + + return () => { + listeners.forEach(unsubscribeFn => unsubscribeFn()); + }; +} diff --git a/sdc-workflow-designer-ui/src/app/paletx/util/util.ts b/sdc-workflow-designer-ui/src/app/paletx/util/util.ts new file mode 100644 index 00000000..5df0381e --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/paletx/util/util.ts @@ -0,0 +1,65 @@ +export function toInteger(value: any): number { + return parseInt(`${value}`, 10); +} + +export function toString(value: any): string { + return (value !== undefined && value !== null) ? `${value}` : ''; +} + +export function getValueInRange(value: number, max: number, min = 0): number { + return Math.max(Math.min(value, max), min); +} + +export function isString(value: any): boolean { + return typeof value === 'string'; +} + +export function isNumber(value: any): boolean { + return !isNaN(toInteger(value)); +} + +export function isInteger(value: any): boolean { + return typeof value === 'number' && isFinite(value) && Math.floor(value) === value; +} + +export function isDefined(value: any): boolean { + return value !== undefined && value !== null; +} + +export function padNumber(value: number) { + if (isNumber(value)) { + return `0${value}`.slice(-2); + } else { + return ''; + } +} + +export function regExpEscape(text) { + return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); +} + + +export function parseDate(date: Date, format) { + let o = { + 'M+': date.getMonth() + 1, // month + 'd+': date.getDate(), // day + 'h+': date.getHours(), // hour + 'm+': date.getMinutes(), // minute + 's+': date.getSeconds(), // second + 'q+': Math.floor((date.getMonth() + 3) / 3), // quarter + 'S': date.getMilliseconds() // millisecond + }; + if (/(y+)/.test(format)) { + format = format.replace(RegExp.$1, + (date.getFullYear() + '').substr(4 - RegExp.$1.length)); + } + for (let k in o) { + if (new RegExp('(' + k + ')').test(format)) { + format = format.replace(RegExp.$1, + RegExp.$1.length === 1 ? o[k] : + ('00' + o[k]).substr(('' + o[k]).length)); + } + } + return format; +} + |