aboutsummaryrefslogtreecommitdiffstats
path: root/sdc-workflow-designer-ui/src/app/paletx/util
diff options
context:
space:
mode:
Diffstat (limited to 'sdc-workflow-designer-ui/src/app/paletx/util')
-rw-r--r--sdc-workflow-designer-ui/src/app/paletx/util/popup.ts59
-rw-r--r--sdc-workflow-designer-ui/src/app/paletx/util/positioning.ts425
-rw-r--r--sdc-workflow-designer-ui/src/app/paletx/util/triggers.ts66
-rw-r--r--sdc-workflow-designer-ui/src/app/paletx/util/util.ts65
4 files changed, 615 insertions, 0 deletions
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;
+}
+