aboutsummaryrefslogtreecommitdiffstats
path: root/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position
diff options
context:
space:
mode:
Diffstat (limited to 'deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position')
-rw-r--r--deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/connected-position-strategy.ts478
-rw-r--r--deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/connected-position.ts87
-rw-r--r--deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/fake-viewport-ruler.ts25
-rw-r--r--deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/free-position-strategy.ts83
-rw-r--r--deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/global-position-strategy.ts178
-rw-r--r--deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/overlay-position-builder.ts51
-rw-r--r--deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/position-strategy.ts17
-rw-r--r--deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/viewport-ruler.ts110
8 files changed, 1029 insertions, 0 deletions
diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/connected-position-strategy.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/connected-position-strategy.ts
new file mode 100644
index 00000000..d144c81f
--- /dev/null
+++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/connected-position-strategy.ts
@@ -0,0 +1,478 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+/* tslint:disable:array-type member-access variable-name typedef
+ only-arrow-functions directive-class-suffix component-class-suffix
+ component-selector no-unnecessary-type-assertion arrow-parens
+ no-unused-variable*/
+import {ElementRef} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import {Subject} from 'rxjs/Subject';
+
+import {Scrollable} from '../scroll/scrollable';
+
+import {ConnectedOverlayPositionChange, ConnectionPositionPair, OriginConnectionPosition, OverlayConnectionPosition, ScrollableViewProperties} from './connected-position';
+import {PositionStrategy} from './position-strategy';
+import {ViewportRuler} from './viewport-ruler';
+
+/**
+ * Container to hold the bounding positions of a particular element with respect
+ * to the viewport, where top and bottom are the y-axis coordinates of the
+ * bounding rectangle and left and right are the x-axis coordinates.
+ */
+interface ElementBoundingPositions {
+ top: number;
+ right: number;
+ bottom: number;
+ left: number;
+}
+
+/**
+ * A strategy for positioning overlays. Using this strategy, an overlay is given
+ * an implicit position relative some origin element. The relative position is
+ * defined in terms of a point on the origin element that is connected to a
+ * point on the overlay element. For example, a basic dropdown is connecting the
+ * bottom-left corner of the origin to the top-left corner of the overlay.
+ */
+export class ConnectedPositionStrategy implements PositionStrategy {
+ private _dir = 'ltr';
+
+ /** The offset in pixels for the overlay connection point on the x-axis */
+ private _offsetX = 0;
+
+ /** The offset in pixels for the overlay connection point on the y-axis */
+ private _offsetY = 0;
+
+ /** The Scrollable containers used to check scrollable view properties on
+ * position change. */
+ private scrollables: Scrollable[] = [];
+
+ /** Whether the we're dealing with an RTL context */
+ get _isRtl() {
+ return this._dir === 'rtl';
+ }
+
+ /** Ordered list of preferred positions, from most to least desirable. */
+ _preferredPositions: ConnectionPositionPair[] = [];
+
+ /** The origin element against which the overlay will be positioned. */
+ private _origin: HTMLElement;
+
+ /** The overlay pane element. */
+ private _pane: HTMLElement;
+
+ /** The last position to have been calculated as the best fit position. */
+ private _lastConnectedPosition: ConnectionPositionPair;
+
+ _onPositionChange: Subject<ConnectedOverlayPositionChange> =
+ new Subject<ConnectedOverlayPositionChange>();
+
+ /** Emits an event when the connection point changes. */
+ get onPositionChange(): Observable<ConnectedOverlayPositionChange> {
+ return this._onPositionChange.asObservable();
+ }
+
+ constructor(
+ private _connectedTo: ElementRef,
+ private _originPos: OriginConnectionPosition,
+ private _overlayPos: OverlayConnectionPosition,
+ private _viewportRuler: ViewportRuler) {
+ this._origin = this._connectedTo.nativeElement;
+ this.withFallbackPosition(this._originPos, this._overlayPos);
+ }
+
+ /** Ordered list of preferred positions, from most to least desirable. */
+ get positions() {
+ return this._preferredPositions;
+ }
+
+ /**
+ * To be used to for any cleanup after the element gets destroyed.
+ */
+ dispose() {
+ //
+ }
+
+ /**
+ * Updates the position of the overlay element, using whichever preferred
+ * position relative to the origin fits on-screen.
+ * @docs-private
+ *
+ * @param element Element to which to apply the CSS styles.
+ * @returns Resolves when the styles have been applied.
+ */
+ apply(element: HTMLElement): void {
+ // Cache the overlay pane element in case re-calculating position is
+ // necessary
+ this._pane = element;
+
+ // We need the bounding rects for the origin and the overlay to determine
+ // how to position the overlay relative to the origin.
+ const originRect = this._origin.getBoundingClientRect();
+ const overlayRect = element.getBoundingClientRect();
+
+ // We use the viewport rect to determine whether a position would go
+ // off-screen.
+ const viewportRect = this._viewportRuler.getViewportRect();
+
+ // Fallback point if none of the fallbacks fit into the viewport.
+ let fallbackPoint: OverlayPoint|undefined;
+ let fallbackPosition: ConnectionPositionPair|undefined;
+
+ // We want to place the overlay in the first of the preferred positions such
+ // that the overlay fits on-screen.
+ for (const pos of this._preferredPositions) {
+ // Get the (x, y) point of connection on the origin, and then use that to
+ // get the (top, left) coordinate for the overlay at `pos`.
+ const originPoint = this._getOriginConnectionPoint(originRect, pos);
+ const overlayPoint =
+ this._getOverlayPoint(originPoint, overlayRect, viewportRect, pos);
+
+ // If the overlay in the calculated position fits on-screen, put it there
+ // and we're done.
+ if (overlayPoint.fitsInViewport) {
+ this._setElementPosition(element, overlayRect, overlayPoint, pos);
+
+ // Save the last connected position in case the position needs to be
+ // re-calculated.
+ this._lastConnectedPosition = pos;
+
+ // Notify that the position has been changed along with its change
+ // properties.
+ const scrollableViewProperties =
+ this.getScrollableViewProperties(element);
+ const positionChange =
+ new ConnectedOverlayPositionChange(pos, scrollableViewProperties);
+ this._onPositionChange.next(positionChange);
+
+ return;
+ } else if (
+ !fallbackPoint ||
+ fallbackPoint.visibleArea < overlayPoint.visibleArea) {
+ fallbackPoint = overlayPoint;
+ fallbackPosition = pos;
+ }
+ }
+
+ // If none of the preferred positions were in the viewport, take the one
+ // with the largest visible area.
+ this._setElementPosition(
+ element, overlayRect, fallbackPoint, fallbackPosition);
+ }
+
+ /**
+ * This re-aligns the overlay element with the trigger in its last calculated
+ * position, even if a position higher in the "preferred positions" list would
+ * now fit. This allows one to re-align the panel without changing the
+ * orientation of the panel.
+ */
+ recalculateLastPosition(): void {
+ const originRect = this._origin.getBoundingClientRect();
+ const overlayRect = this._pane.getBoundingClientRect();
+ const viewportRect = this._viewportRuler.getViewportRect();
+ const lastPosition =
+ this._lastConnectedPosition || this._preferredPositions[0];
+
+ const originPoint =
+ this._getOriginConnectionPoint(originRect, lastPosition);
+ const overlayPoint = this._getOverlayPoint(
+ originPoint, overlayRect, viewportRect, lastPosition);
+ this._setElementPosition(
+ this._pane, overlayRect, overlayPoint, lastPosition);
+ }
+
+ /**
+ * Sets the list of Scrollable containers that host the origin element so that
+ * on reposition we can evaluate if it or the overlay has been clipped or
+ * outside view. Every Scrollable must be an ancestor element of the
+ * strategy's origin element.
+ */
+ withScrollableContainers(scrollables: Scrollable[]) {
+ this.scrollables = scrollables;
+ }
+
+ /**
+ * Adds a new preferred fallback position.
+ * @param originPos
+ * @param overlayPos
+ */
+ withFallbackPosition(
+ originPos: OriginConnectionPosition,
+ overlayPos: OverlayConnectionPosition): this {
+ this._preferredPositions.push(
+ new ConnectionPositionPair(originPos, overlayPos));
+ return this;
+ }
+
+ /**
+ * Sets the layout direction so the overlay's position can be adjusted to
+ * match.
+ * @param dir New layout direction.
+ */
+ withDirection(dir: 'ltr'|'rtl'): this {
+ this._dir = dir;
+ return this;
+ }
+
+ /**
+ * Sets an offset for the overlay's connection point on the x-axis
+ * @param offset New offset in the X axis.
+ */
+ withOffsetX(offset: number): this {
+ this._offsetX = offset;
+ return this;
+ }
+
+ /**
+ * Sets an offset for the overlay's connection point on the y-axis
+ * @param offset New offset in the Y axis.
+ */
+ withOffsetY(offset: number): this {
+ this._offsetY = offset;
+ return this;
+ }
+
+ /**
+ * Gets the horizontal (x) "start" dimension based on whether the overlay is
+ * in an RTL context.
+ * @param rect
+ */
+ private _getStartX(rect: ClientRect): number {
+ return this._isRtl ? rect.right : rect.left;
+ }
+
+ /**
+ * Gets the horizontal (x) "end" dimension based on whether the overlay is in
+ * an RTL context.
+ * @param rect
+ */
+ private _getEndX(rect: ClientRect): number {
+ return this._isRtl ? rect.left : rect.right;
+ }
+
+
+ /**
+ * Gets the (x, y) coordinate of a connection point on the origin based on a
+ * relative position.
+ * @param originRect
+ * @param pos
+ */
+ private _getOriginConnectionPoint(
+ originRect: ClientRect, pos: ConnectionPositionPair): Point {
+ const originStartX = this._getStartX(originRect);
+ const originEndX = this._getEndX(originRect);
+
+ let x: number;
+ if (pos.originX === 'center') {
+ x = originStartX + (originRect.width / 2);
+ } else {
+ x = pos.originX === 'start' ? originStartX : originEndX;
+ }
+
+ let y: number;
+ if (pos.originY === 'center') {
+ y = originRect.top + (originRect.height / 2);
+ } else {
+ y = pos.originY === 'top' ? originRect.top : originRect.bottom;
+ }
+
+ return {x, y};
+ }
+
+
+ /**
+ * Gets the (x, y) coordinate of the top-left corner of the overlay given a
+ * given position and origin point to which the overlay should be connected,
+ * as well as how much of the element would be inside the viewport at that
+ * position.
+ */
+ private _getOverlayPoint(
+ originPoint: Point, overlayRect: ClientRect, viewportRect: ClientRect,
+ pos: ConnectionPositionPair): OverlayPoint {
+ // Calculate the (overlayStartX, overlayStartY), the start of the potential
+ // overlay position relative to the origin point.
+ let overlayStartX: number;
+ if (pos.overlayX === 'center') {
+ overlayStartX = -overlayRect.width / 2;
+ } else if (pos.overlayX === 'start') {
+ overlayStartX = this._isRtl ? -overlayRect.width : 0;
+ } else {
+ overlayStartX = this._isRtl ? 0 : -overlayRect.width;
+ }
+
+ let overlayStartY: number;
+ if (pos.overlayY === 'center') {
+ overlayStartY = -overlayRect.height / 2;
+ } else {
+ overlayStartY = pos.overlayY === 'top' ? 0 : -overlayRect.height;
+ }
+
+ // The (x, y) coordinates of the overlay.
+ const x = originPoint.x + overlayStartX + this._offsetX;
+ const y = originPoint.y + overlayStartY + this._offsetY;
+
+ // How much the overlay would overflow at this position, on each side.
+ const leftOverflow = 0 - x;
+ const rightOverflow = (x + overlayRect.width) - viewportRect.width;
+ const topOverflow = 0 - y;
+ const bottomOverflow = (y + overlayRect.height) - viewportRect.height;
+
+ // Visible parts of the element on each axis.
+ const visibleWidth =
+ this._subtractOverflows(overlayRect.width, leftOverflow, rightOverflow);
+ const visibleHeight = this._subtractOverflows(
+ overlayRect.height, topOverflow, bottomOverflow);
+
+ // The area of the element that's within the viewport.
+ const visibleArea = visibleWidth * visibleHeight;
+ const fitsInViewport =
+ (overlayRect.width * overlayRect.height) === visibleArea;
+
+ return {x, y, fitsInViewport, visibleArea};
+ }
+
+ /**
+ * Gets the view properties of the trigger and overlay, including whether they
+ * are clipped or completely outside the view of any of the strategy's
+ * scrollables.
+ */
+ private getScrollableViewProperties(overlay: HTMLElement):
+ ScrollableViewProperties {
+ const originBounds = this._getElementBounds(this._origin);
+ const overlayBounds = this._getElementBounds(overlay);
+ const scrollContainerBounds =
+ this.scrollables.map((scrollable: Scrollable) => {
+ return this._getElementBounds(
+ scrollable.getElementRef().nativeElement);
+ });
+
+ return {
+ isOriginClipped:
+ this.isElementClipped(originBounds, scrollContainerBounds),
+ isOriginOutsideView:
+ this.isElementOutsideView(originBounds, scrollContainerBounds),
+ isOverlayClipped:
+ this.isElementClipped(overlayBounds, scrollContainerBounds),
+ isOverlayOutsideView:
+ this.isElementOutsideView(overlayBounds, scrollContainerBounds),
+ };
+ }
+
+ /** Whether the element is completely out of the view of any of the
+ * containers. */
+ private isElementOutsideView(
+ elementBounds: ElementBoundingPositions,
+ containersBounds: ElementBoundingPositions[]): boolean {
+ return containersBounds.some(
+ (containerBounds: ElementBoundingPositions) => {
+ const outsideAbove = elementBounds.bottom < containerBounds.top;
+ const outsideBelow = elementBounds.top > containerBounds.bottom;
+ const outsideLeft = elementBounds.right < containerBounds.left;
+ const outsideRight = elementBounds.left > containerBounds.right;
+
+ return outsideAbove || outsideBelow || outsideLeft || outsideRight;
+ });
+ }
+
+ /** Whether the element is clipped by any of the containers. */
+ private isElementClipped(
+ elementBounds: ElementBoundingPositions,
+ containersBounds: ElementBoundingPositions[]): boolean {
+ return containersBounds.some(
+ (containerBounds: ElementBoundingPositions) => {
+ const clippedAbove = elementBounds.top < containerBounds.top;
+ const clippedBelow = elementBounds.bottom > containerBounds.bottom;
+ const clippedLeft = elementBounds.left < containerBounds.left;
+ const clippedRight = elementBounds.right > containerBounds.right;
+
+ return clippedAbove || clippedBelow || clippedLeft || clippedRight;
+ });
+ }
+
+ /** Physically positions the overlay element to the given coordinate. */
+ private _setElementPosition(
+ element: HTMLElement, overlayRect: ClientRect, overlayPoint: Point,
+ pos: ConnectionPositionPair) {
+ // We want to set either `top` or `bottom` based on whether the overlay
+ // wants to appear above or below the origin and the direction in which the
+ // element will expand.
+ const verticalStyleProperty = pos.overlayY === 'bottom' ? 'bottom' : 'top';
+
+ // When using `bottom`, we adjust the y position such that it is the
+ // distance from the bottom of the viewport rather than the top.
+ const y = verticalStyleProperty === 'top' ?
+ overlayPoint.y :
+ document.documentElement.clientHeight -
+ (overlayPoint.y + overlayRect.height);
+
+ // We want to set either `left` or `right` based on whether the overlay
+ // wants to appear "before" or "after" the origin, which determines the
+ // direction in which the element will expand. For the horizontal axis, the
+ // meaning of "before" and "after" change based on whether the page is in
+ // RTL or LTR.
+ let horizontalStyleProperty: any;
+ if (this._dir === 'rtl') {
+ horizontalStyleProperty = pos.overlayX === 'end' ? 'left' : 'right';
+ } else {
+ horizontalStyleProperty = pos.overlayX === 'end' ? 'right' : 'left';
+ }
+
+ // When we're setting `right`, we adjust the x position such that it is the
+ // distance from the right edge of the viewport rather than the left edge.
+ const x = horizontalStyleProperty === 'left' ?
+ overlayPoint.x :
+ document.documentElement.clientWidth -
+ (overlayPoint.x + overlayRect.width);
+
+
+ // Reset any existing styles. This is necessary in case the preferred
+ // position has changed since the last `apply`.
+ ['top', 'bottom', 'left', 'right'].forEach(
+ (p: any) => element.style[p] = null);
+
+ element.style[verticalStyleProperty] = `${y}px`;
+ element.style[horizontalStyleProperty] = `${x}px`;
+ }
+
+ /** Returns the bounding positions of the provided element with respect to the
+ * viewport. */
+ private _getElementBounds(element: HTMLElement): ElementBoundingPositions {
+ const boundingClientRect = element.getBoundingClientRect();
+ return {
+ top: boundingClientRect.top,
+ right: boundingClientRect.left + boundingClientRect.width,
+ bottom: boundingClientRect.top + boundingClientRect.height,
+ left: boundingClientRect.left
+ };
+ }
+
+ /**
+ * Subtracts the amount that an element is overflowing on an axis from it's
+ * length.
+ */
+ private _subtractOverflows(length: number, ...overflows: number[]): number {
+ return overflows.reduce((currentValue: number, currentOverflow: number) => {
+ return currentValue - Math.max(currentOverflow, 0);
+ }, length);
+ }
+}
+
+/** A simple (x, y) coordinate. */
+interface Point {
+ x: number;
+ y: number;
+}
+
+/**
+ * Expands the simple (x, y) coordinate by adding info about whether the
+ * element would fit inside the viewport at that position, as well as
+ * how much of the element would be visible.
+ */
+interface OverlayPoint extends Point {
+ visibleArea: number;
+ fitsInViewport: boolean;
+}
diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/connected-position.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/connected-position.ts
new file mode 100644
index 00000000..dad3f04e
--- /dev/null
+++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/connected-position.ts
@@ -0,0 +1,87 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+/* tslint:disable:array-type member-access variable-name typedef
+ only-arrow-functions directive-class-suffix component-class-suffix
+ component-selector no-unnecessary-type-assertion arrow-parens*/
+/** Horizontal dimension of a connection point on the perimeter of the origin or
+ * overlay element. */
+import {Optional} from '@angular/core';
+export type HorizontalConnectionPos = 'start' | 'center' | 'end';
+
+/** Vertical dimension of a connection point on the perimeter of the origin or
+ * overlay element. */
+export type VerticalConnectionPos = 'top' | 'center' | 'bottom';
+
+
+/** A connection point on the origin element. */
+export interface OriginConnectionPosition {
+ originX: HorizontalConnectionPos;
+ originY: VerticalConnectionPos;
+}
+
+/** A connection point on the overlay element. */
+export interface OverlayConnectionPosition {
+ overlayX: HorizontalConnectionPos;
+ overlayY: VerticalConnectionPos;
+}
+
+/** The points of the origin element and the overlay element to connect. */
+export class ConnectionPositionPair {
+ originX: HorizontalConnectionPos;
+ originY: VerticalConnectionPos;
+ overlayX: HorizontalConnectionPos;
+ overlayY: VerticalConnectionPos;
+
+ constructor(
+ origin: OriginConnectionPosition, overlay: OverlayConnectionPosition) {
+ this.originX = origin.originX;
+ this.originY = origin.originY;
+ this.overlayX = overlay.overlayX;
+ this.overlayY = overlay.overlayY;
+ }
+}
+
+/**
+ * Set of properties regarding the position of the origin and overlay relative
+ * to the viewport with respect to the containing Scrollable elements.
+ *
+ * The overlay and origin are clipped if any part of their bounding client
+ * rectangle exceeds the bounds of any one of the strategy's Scrollable's
+ * bounding client rectangle.
+ *
+ * The overlay and origin are outside view if there is no overlap between their
+ * bounding client rectangle and any one of the strategy's Scrollable's bounding
+ * client rectangle.
+ *
+ * ----------- -----------
+ * | outside | | clipped |
+ * | view | --------------------------
+ * | | | | | |
+ * ---------- | ----------- |
+ * -------------------------- | |
+ * | | | Scrollable |
+ * | | | |
+ * | | --------------------------
+ * | Scrollable |
+ * | |
+ * --------------------------
+ */
+export class ScrollableViewProperties {
+ isOriginClipped: boolean;
+ isOriginOutsideView: boolean;
+ isOverlayClipped: boolean;
+ isOverlayOutsideView: boolean;
+}
+
+/** The change event emitted by the strategy when a fallback position is used.
+ */
+export class ConnectedOverlayPositionChange {
+ constructor(
+ public connectionPair: ConnectionPositionPair,
+ @Optional() public scrollableViewProperties: ScrollableViewProperties) {}
+}
diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/fake-viewport-ruler.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/fake-viewport-ruler.ts
new file mode 100644
index 00000000..698aed1d
--- /dev/null
+++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/fake-viewport-ruler.ts
@@ -0,0 +1,25 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+/** @docs-private */
+export class FakeViewportRuler {
+ getViewportRect() {
+ return {
+ left: 0,
+ top: 0,
+ width: 1014,
+ height: 686,
+ bottom: 686,
+ right: 1014
+ };
+ }
+
+ getViewportScrollPosition() {
+ return {top: 0, left: 0};
+ }
+}
diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/free-position-strategy.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/free-position-strategy.ts
new file mode 100644
index 00000000..bd33e404
--- /dev/null
+++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/free-position-strategy.ts
@@ -0,0 +1,83 @@
+/* tslint:disable:array-type member-access variable-name typedef
+ only-arrow-functions directive-class-suffix component-class-suffix
+ component-selector no-unnecessary-type-assertion arrow-parens*/
+import {PositionStrategy} from './position-strategy';
+
+/**
+ * Free position strategy for overlay without origin
+ * @author lingyi.zcs
+ */
+export class FreePositionStrategy implements PositionStrategy {
+ private _wrapper: HTMLElement;
+ // private _cssPosition: string = '';
+ // private _top: string = '';
+ // private _left: string = '';
+ // private _width: string = '';
+ // private _height: string = '';
+
+ // cssPosition(value: string) {
+ // this._cssPosition = value;
+ // return this;
+ // }
+
+ // top(value: number | string): this {
+ // this._top = this._toCssValue(value);
+ // return this;
+ // }
+
+ // left(value: number | string): this {
+ // this._left = this._toCssValue(value);
+ // return this;
+ // }
+
+ // width(value: number | string): this {
+ // this._width = this._toCssValue(value);
+ // return this;
+ // }
+
+ // height(value: number | string): this {
+ // this._height = this._toCssValue(value);
+ // return this;
+ // }
+
+ /**
+ * Apply the position to the element. (NOTE: normally will triggered by
+ * scrolling)
+ * @docs-private
+ *
+ * @param element Element to which to apply the CSS.
+ * @returns Resolved when the styles have been applied.
+ */
+ apply(element: HTMLElement): void {
+ if (!this._wrapper) {
+ this._wrapper = document.createElement('div');
+ this._wrapper.classList.add('cdk-free-overlay-wrapper');
+ element.parentNode.insertBefore(this._wrapper, element);
+ this._wrapper.appendChild(element);
+
+ // // Initialized style once
+ // const style = element.style;
+ // style.position = this._cssPosition;
+ // style.top = this._top;
+ // style.left = this._left;
+ // style.width = this._width;
+ // style.height = this._height;
+ }
+
+ // TODO: do somethings while triggered (eg. by scrolling)
+ }
+
+ /**
+ * Removes the wrapper element from the DOM.
+ */
+ dispose(): void {
+ if (this._wrapper && this._wrapper.parentNode) {
+ this._wrapper.parentNode.removeChild(this._wrapper);
+ this._wrapper = null;
+ }
+ }
+
+ // private _toCssValue(value: number | string) {
+ // return typeof value === 'number' ? value + 'px' : value;
+ // }
+}
diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/global-position-strategy.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/global-position-strategy.ts
new file mode 100644
index 00000000..8e3204a9
--- /dev/null
+++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/global-position-strategy.ts
@@ -0,0 +1,178 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+/* tslint:disable:array-type member-access variable-name typedef
+ only-arrow-functions directive-class-suffix component-class-suffix
+ component-selector no-unnecessary-type-assertion arrow-parens*/
+import {PositionStrategy} from './position-strategy';
+
+
+/**
+ * A strategy for positioning overlays. Using this strategy, an overlay is given
+ * an explicit position relative to the browser's viewport. We use flexbox,
+ * instead of transforms, in order to avoid issues with subpixel rendering which
+ * can cause the element to become blurry.
+ */
+export class GlobalPositionStrategy implements PositionStrategy {
+ private _cssPosition = 'static';
+ private _topOffset = '';
+ private _bottomOffset = '';
+ private _leftOffset = '';
+ private _rightOffset = '';
+ private _alignItems = '';
+ private _justifyContent = '';
+ private _width = '';
+ private _height = '';
+
+ /* A lazily-created wrapper for the overlay element that is used as a flex
+ * container. */
+ private _wrapper: HTMLElement|null = null;
+
+ /**
+ * Sets the top position of the overlay. Clears any previously set vertical
+ * position.
+ * @param value New top offset.
+ */
+ top(value = ''): this {
+ this._bottomOffset = '';
+ this._topOffset = value;
+ this._alignItems = 'flex-start';
+ return this;
+ }
+
+ /**
+ * Sets the left position of the overlay. Clears any previously set horizontal
+ * position.
+ * @param value New left offset.
+ */
+ left(value = ''): this {
+ this._rightOffset = '';
+ this._leftOffset = value;
+ this._justifyContent = 'flex-start';
+ return this;
+ }
+
+ /**
+ * Sets the bottom position of the overlay. Clears any previously set vertical
+ * position.
+ * @param value New bottom offset.
+ */
+ bottom(value = ''): this {
+ this._topOffset = '';
+ this._bottomOffset = value;
+ this._alignItems = 'flex-end';
+ return this;
+ }
+
+ /**
+ * Sets the right position of the overlay. Clears any previously set
+ * horizontal position.
+ * @param value New right offset.
+ */
+ right(value = ''): this {
+ this._leftOffset = '';
+ this._rightOffset = value;
+ this._justifyContent = 'flex-end';
+ return this;
+ }
+
+ /**
+ * Sets the overlay width and clears any previously set width.
+ * @param value New width for the overlay
+ */
+ width(value = ''): this {
+ this._width = value;
+
+ // When the width is 100%, we should reset the `left` and the offset,
+ // in order to ensure that the element is flush against the viewport edge.
+ if (value === '100%') {
+ this.left('0px');
+ }
+
+ return this;
+ }
+
+ /**
+ * Sets the overlay height and clears any previously set height.
+ * @param value New height for the overlay
+ */
+ height(value = ''): this {
+ this._height = value;
+
+ // When the height is 100%, we should reset the `top` and the offset,
+ // in order to ensure that the element is flush against the viewport edge.
+ if (value === '100%') {
+ this.top('0px');
+ }
+
+ return this;
+ }
+
+ /**
+ * Centers the overlay horizontally with an optional offset.
+ * Clears any previously set horizontal position.
+ *
+ * @param offset Overlay offset from the horizontal center.
+ */
+ centerHorizontally(offset = ''): this {
+ this.left(offset);
+ this._justifyContent = 'center';
+ return this;
+ }
+
+ /**
+ * Centers the overlay vertically with an optional offset.
+ * Clears any previously set vertical position.
+ *
+ * @param offset Overlay offset from the vertical center.
+ */
+ centerVertically(offset = ''): this {
+ this.top(offset);
+ this._alignItems = 'center';
+ return this;
+ }
+
+ /**
+ * Apply the position to the element.
+ * @docs-private
+ *
+ * @param element Element to which to apply the CSS.
+ * @returns Resolved when the styles have been applied.
+ */
+ apply(element: HTMLElement): void {
+ if (!this._wrapper && element.parentNode) {
+ this._wrapper = document.createElement('div');
+ this._wrapper.classList.add('cdk-global-overlay-wrapper');
+ element.parentNode.insertBefore(this._wrapper, element);
+ this._wrapper.appendChild(element);
+ }
+
+ const styles = element.style;
+ const parentStyles = (element.parentNode as HTMLElement).style;
+
+ styles.position = this._cssPosition;
+ styles.marginTop = this._topOffset;
+ styles.marginLeft = this._leftOffset;
+ styles.marginBottom = this._bottomOffset;
+ styles.marginRight = this._rightOffset;
+ styles.width = this._width;
+ styles.height = this._height;
+
+ parentStyles.justifyContent = this._justifyContent;
+ parentStyles.alignItems = this._alignItems;
+ }
+
+ /**
+ * Removes the wrapper element from the DOM.
+ */
+ dispose(): void {
+ if (this._wrapper && this._wrapper.parentNode) {
+ this._wrapper.parentNode.removeChild(this._wrapper);
+ this._wrapper = null;
+ }
+ }
+}
diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/overlay-position-builder.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/overlay-position-builder.ts
new file mode 100644
index 00000000..0f6735eb
--- /dev/null
+++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/overlay-position-builder.ts
@@ -0,0 +1,51 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+/* tslint:disable:array-type member-access variable-name typedef
+ only-arrow-functions directive-class-suffix component-class-suffix
+ component-selector no-unnecessary-type-assertion arrow-parens*/
+import {ElementRef, Injectable} from '@angular/core';
+
+import {OriginConnectionPosition, OverlayConnectionPosition} from './connected-position';
+import {ConnectedPositionStrategy} from './connected-position-strategy';
+import {FreePositionStrategy} from './free-position-strategy';
+import {GlobalPositionStrategy} from './global-position-strategy';
+import {ViewportRuler} from './viewport-ruler';
+
+
+/** Builder for overlay position strategy. */
+@Injectable()
+export class OverlayPositionBuilder {
+ constructor(private _viewportRuler: ViewportRuler) {}
+
+ /**
+ * Creates a free position strategy
+ */
+ free(): FreePositionStrategy {
+ return new FreePositionStrategy();
+ }
+
+ /**
+ * Creates a global position strategy.
+ */
+ global(): GlobalPositionStrategy {
+ return new GlobalPositionStrategy();
+ }
+
+ /**
+ * Creates a relative position strategy.
+ * @param elementRef
+ * @param originPos
+ * @param overlayPos
+ */
+ connectedTo(
+ elementRef: ElementRef, originPos: OriginConnectionPosition,
+ overlayPos: OverlayConnectionPosition): ConnectedPositionStrategy {
+ return new ConnectedPositionStrategy(
+ elementRef, originPos, overlayPos, this._viewportRuler);
+ }
+}
diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/position-strategy.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/position-strategy.ts
new file mode 100644
index 00000000..21bd0fa0
--- /dev/null
+++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/position-strategy.ts
@@ -0,0 +1,17 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+/** Strategy for setting the position on an overlay. */
+export interface PositionStrategy {
+ /** Updates the position of the overlay element. */
+ apply(element: Element): void;
+
+ /** Cleans up any DOM modifications made by the position strategy, if
+ * necessary. */
+ dispose(): void;
+}
diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/viewport-ruler.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/viewport-ruler.ts
new file mode 100644
index 00000000..298cd642
--- /dev/null
+++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/position/viewport-ruler.ts
@@ -0,0 +1,110 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+/* tslint:disable:array-type member-access variable-name typedef
+ only-arrow-functions directive-class-suffix component-class-suffix
+ component-selector no-unnecessary-type-assertion arrow-parens*/
+import {Injectable, Optional, SkipSelf} from '@angular/core';
+import {ScrollDispatcher} from '../scroll/scroll-dispatcher';
+
+
+/**
+ * Simple utility for getting the bounds of the browser viewport.
+ * @docs-private
+ */
+@Injectable()
+export class ViewportRuler {
+ /** Cached document client rectangle. */
+ private _documentRect?: ClientRect;
+
+ constructor(scrollDispatcher: ScrollDispatcher) {
+ // Subscribe to scroll and resize events and update the document rectangle
+ // on changes.
+ scrollDispatcher.scrolled(0, () => this._cacheViewportGeometry());
+ }
+
+ /** Gets a ClientRect for the viewport's bounds. */
+ getViewportRect(documentRect = this._documentRect): ClientRect {
+ // Cache the document bounding rect so that we don't recompute it for
+ // multiple calls.
+ if (!documentRect) {
+ this._cacheViewportGeometry();
+ documentRect = this._documentRect;
+ }
+
+ // Use the document element's bounding rect rather than the window scroll
+ // properties (e.g. pageYOffset, scrollY) due to in issue in Chrome and IE
+ // where window scroll properties and client coordinates
+ // (boundingClientRect, clientX/Y, etc.) are in different conceptual
+ // viewports. Under most circumstances these viewports are equivalent, but
+ // they can disagree when the page is pinch-zoomed (on devices that support
+ // touch). See
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=489206#c4 We use
+ // the documentElement instead of the body because, by default (without a
+ // css reset) browsers typically give the document body an 8px margin, which
+ // is not included in getBoundingClientRect().
+ const scrollPosition = this.getViewportScrollPosition(documentRect);
+ const height = window.innerHeight;
+ const width = window.innerWidth;
+
+ return {
+ top: scrollPosition.top,
+ left: scrollPosition.left,
+ bottom: scrollPosition.top + height,
+ right: scrollPosition.left + width,
+ height,
+ width,
+ };
+ }
+
+
+ /**
+ * Gets the (top, left) scroll position of the viewport.
+ * @param documentRect
+ */
+ getViewportScrollPosition(documentRect = this._documentRect) {
+ // Cache the document bounding rect so that we don't recompute it for
+ // multiple calls.
+ if (!documentRect) {
+ this._cacheViewportGeometry();
+ documentRect = this._documentRect;
+ }
+
+ // The top-left-corner of the viewport is determined by the scroll position
+ // of the document body, normally just (scrollLeft, scrollTop). However,
+ // Chrome and Firefox disagree about whether `document.body` or
+ // `document.documentElement` is the scrolled element, so reading
+ // `scrollTop` and `scrollLeft` is inconsistent. However, using the bounding
+ // rect of `document.documentElement` works consistently, where the `top`
+ // and `left` values will equal negative the scroll position.
+ const top = -documentRect.top || document.body.scrollTop ||
+ window.scrollY || document.documentElement.scrollTop || 0;
+
+ const left = -documentRect.left || document.body.scrollLeft ||
+ window.scrollX || document.documentElement.scrollLeft || 0;
+
+ return {top, left};
+ }
+
+ /** Caches the latest client rectangle of the document element. */
+ _cacheViewportGeometry() {
+ this._documentRect = document.documentElement.getBoundingClientRect();
+ }
+}
+
+export function VIEWPORT_RULER_PROVIDER_FACTORY(
+ parentRuler: ViewportRuler, scrollDispatcher: ScrollDispatcher) {
+ return parentRuler || new ViewportRuler(scrollDispatcher);
+}
+
+export const VIEWPORT_RULER_PROVIDER = {
+ // If there is already a ViewportRuler available, use that. Otherwise, provide
+ // a new one.
+ provide: ViewportRuler,
+ deps: [[new Optional(), new SkipSelf(), ViewportRuler], ScrollDispatcher],
+ useFactory: VIEWPORT_RULER_PROVIDER_FACTORY
+};