From: Yoav Schneiderman Date: Wed, 15 Jan 2020 13:32:10 +0000 (+0200) Subject: Move onap UI loader and icons to VID X-Git-Tag: 6.0.2~12^2 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=3279721d72b3e5adadf5431c58383e71f6b080d7;p=vid.git Move onap UI loader and icons to VID Issue-ID: VID-748 Change-Id: If79180e55651ad29bd2771a35855209419d1a0f5 Signed-off-by: Yoav Schneiderman --- diff --git a/vid-automation/src/main/java/org/onap/sdc/ci/tests/utilities/GeneralUIUtils.java b/vid-automation/src/main/java/org/onap/sdc/ci/tests/utilities/GeneralUIUtils.java index dd632d201..56fd2d30d 100644 --- a/vid-automation/src/main/java/org/onap/sdc/ci/tests/utilities/GeneralUIUtils.java +++ b/vid-automation/src/main/java/org/onap/sdc/ci/tests/utilities/GeneralUIUtils.java @@ -374,7 +374,7 @@ public final class GeneralUIUtils { } public static void waitForLoader(int timeOut) { - newWait(timeOut).until(ExpectedConditions.invisibilityOfElementLocated(By.className("sdc-loader-background"))); + newWait(timeOut).until(ExpectedConditions.invisibilityOfElementLocated(By.className("custom-loader-background"))); } public static void findComponentAndClick(String resourceName) throws Exception { diff --git a/vid-webpack-master/cypress/integration/shared/spinner.e2e.ts b/vid-webpack-master/cypress/integration/shared/spinner.e2e.ts index 51e084b32..a64644087 100644 --- a/vid-webpack-master/cypress/integration/shared/spinner.e2e.ts +++ b/vid-webpack-master/cypress/integration/shared/spinner.e2e.ts @@ -32,7 +32,7 @@ describe('Spinner', function () { "error 500 asyncInstantiation"); cy.openIframe('app/ui/#/instantiationStatus'); - cy.get('.sdc-loader') + cy.get('.custom-loader') .and('be.visible'); }); diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/available-models-tree/available-models-tree.component.html b/vid-webpack-master/src/app/drawingBoard/service-planning/available-models-tree/available-models-tree.component.html index 7864808fc..d309f2107 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/available-models-tree/available-models-tree.component.html +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/available-models-tree/available-models-tree.component.html @@ -29,10 +29,10 @@ {{node.data.getNodeCount(node, this.serviceModelId)}} - - + diff --git a/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-header/drawing-board-header.component.html b/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-header/drawing-board-header.component.html index 7a18bce89..2c046785d 100644 --- a/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-header/drawing-board-header.component.html +++ b/vid-webpack-master/src/app/drawingBoard/service-planning/drawing-board-header/drawing-board-header.component.html @@ -2,12 +2,12 @@
- - +
@@ -59,12 +59,12 @@
- -
+
Show Audit Info
{{node?.data?.typeName}}
- - +
@@ -78,12 +78,12 @@ [enabled]="isEnabled(currentNode, serviceModelId, contextMenuOption.methodName)">
- -
+
{{getcontextMenuOptionLabel(contextMenuOption)}}
@@ -95,12 +95,12 @@ tooltipPlacement="left" [attr.data-tests-id]="'node-'+ node.data.modelId + '-' + node.data.modelName +'-alert-icon'" class="icon-alert"> - - +
diff --git a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.html b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.html index 16b8c0132..dcc32a5e4 100644 --- a/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.html +++ b/vid-webpack-master/src/app/instantiationStatus/instantiationStatus.component.html @@ -65,13 +65,13 @@ - - + diff --git a/vid-webpack-master/src/app/shared/components/customIcon/custom-icon.component.html b/vid-webpack-master/src/app/shared/components/customIcon/custom-icon.component.html new file mode 100644 index 000000000..0ac56d2d2 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/customIcon/custom-icon.component.html @@ -0,0 +1,2 @@ +
diff --git a/vid-webpack-master/src/app/shared/components/customIcon/custom-icon.component.scss b/vid-webpack-master/src/app/shared/components/customIcon/custom-icon.component.scss new file mode 100644 index 000000000..54b375702 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/customIcon/custom-icon.component.scss @@ -0,0 +1,297 @@ +@import 'node_modules/onap-ui-common/lib/scss/variables.scss'; + +:host { + display: inline-flex; +} + +@mixin color-icon($primary-color) { + color: $primary-color; + fill: $primary-color; +} + +@mixin color-icon-hover($secondary-color) { + &.clickable { + cursor: pointer; + &:not([disabled]):hover, &:active, &:focus { + @include color-icon($secondary-color); + } + } +} + +@mixin color-icon-label($primary-color) { + @include color-icon($primary-color); + + .custom-icon { + @include color-icon($primary-color); + } +} + +@mixin color-icon-label-hover($secondary-color) { + &.clickable { + &:not([disabled]):hover, &:active, &:focus { + @include color-icon-label($secondary-color); + } + } +} + +/deep/ .custom-icon { + display: inline-flex; + width: 24px; + height: 24px; + box-sizing: content-box; + + & > svg { + width: 100%; + height: 100%; + } + + &[disabled] { + opacity: 0.7; + } + + &.mode-primary { + @include color-icon($blue); + @include color-icon-hover($light-blue); + } + + &.mode-primary2 { + @include color-icon($dark-gray); + @include color-icon-hover($light-blue); + } + + &.mode-secondary { + @include color-icon($gray); + @include color-icon-hover($dark-gray); + } + + &.mode-success { + @include color-icon($green); + } + + &.mode-error { + @include color-icon($red); + } + + &.mode-warning { + @include color-icon($yellow); + } + + &.mode-info { + @include color-icon($text-black); + @include color-icon-hover($dark-blue); + } + + &.mode-white { + @include color-icon($white); + @include color-icon-hover($light-gray); + } + + &.size-x_small { + width: 8px; + height: 8px; + } + + &.size-small { + width: 12px; + height: 12px; + } + + &.size-medium { + width: 16px; + height: 16px; + } + + &.size-large { + width: 24px; + height: 24px; + } + + &.size-x_large { + width: 36px; + height: 36px; + } + + &.size-x_x_large { + width: 48px; + height: 48px; + } + + &.bg-type-circle { + border-radius: 50%; + padding: 6px; + } + + &.bg-type-rectangle { + padding: 6px; + } + + &.bg-color-purple { + background-color: $purple; + } + + &.bg-color-light-blue { + background-color: $light-blue; + } + + &.bg-color-green { + background-color: $green; + } + + &.bg-color-red { + background-color: $red; + } + + &.bg-color-yellow { + background-color: $yellow; + } + + &.bg-color-blue { + background-color: $blue; + } + + &.bg-color-lightBlue { + background-color: $light-blue; + } + + &.bg-color-darkBlue { + background-color: $dark-blue; + } + + &.bg-color-darkBlue2 { + background-color: $dark-blue2; + } + + &.bg-color-disabledBlue { + background-color: $disabled-blue; + } + + &.bg-color-gray { + background-color: $gray; + } + + &.bg-color-white { + background-color: $white; + } + + &.bg-color-transparent { + background-color:transparent; + } + &.bg-color-silver { + background-color: $light-silver; + } +} + +.custom-icon-wrapper { + display: inline-flex; + justify-content: center; + align-items: center; + + &.custom-icon-label { + } + + &.custom-icon { + } + + &[disabled] { + opacity: 0.7; + } + + &.label-placement-bottom { + flex-direction: column; + .custom-icon-label { + margin-top: 0.25em; + } + } + + &.label-placement-right { + .custom-icon-label { + margin-left: 0.25em; + } + } + + &.label-placement-top { + flex-direction: column-reverse; + .custom-icon-label { + margin-bottom: 0.25em; + } + } + + &.label-placement-left { + flex-direction: row-reverse; + .custom-icon-label { + margin-right: 0.25em; + } + } + + &.mode-primary { + @include color-icon-label($blue); + @include color-icon-label-hover($light-blue); + } + + &.mode-secondary { + @include color-icon-label($gray); + @include color-icon-label-hover($light-blue); + } + + &.mode-success { + @include color-icon-label($green); + } + + &.mode-error { + @include color-icon-label($red); + } + + &.mode-warning { + @include color-icon-label($yellow); + } + + &.mode-info { + @include color-icon-label($text-black); + @include color-icon-label-hover($light-blue); + } + + &.size-x_small { + font-size: 8px; + line-height: 10px; + + .custom-icon { + @extend .custom-icon.size-x_small; + } + } + + &.size-small { + font-size: 12px; + line-height: 14px; + + .custom-icon { + @extend .custom-icon.size-small; + } + } + + &.size-medium { + font-size: 16px; + line-height: 20px; + + .custom-icon { + @extend .custom-icon.size-medium; + } + } + + &.size-large { + font-size: 24px; + line-height: 28px; + + .custom-icon { + @extend .custom-icon.size-large; + } + } + + &.size-x_large { + font-size: 34px; + line-height: 40px; + + .custom-icon { + @extend .custom-icon.size-x_large; + } + } +} diff --git a/vid-webpack-master/src/app/shared/components/customIcon/custom-icon.component.ts b/vid-webpack-master/src/app/shared/components/customIcon/custom-icon.component.ts new file mode 100644 index 000000000..c59bc5945 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/customIcon/custom-icon.component.ts @@ -0,0 +1,85 @@ +import {Component, Input, OnChanges, SimpleChanges} from "@angular/core"; +import {Mode} from "../customButton/models/mode.model"; +import {Size} from "./models/icon-size.model"; +import {BackgroundShape} from "./models/background-shape.model"; +import {BackgroundColor} from "./models/background-color.model"; +import {DomSanitizer, SafeHtml} from "@angular/platform-browser"; +import {iconsMap} from 'onap-ui-common'; + +@Component({ + selector: 'custom-icon', + templateUrl: './custom-icon.component.html', + styleUrls: ['./custom-icon.component.scss'], +}) +export class SvgIconComponent implements OnChanges { + + @Input() public name: string; + @Input() public type: string; + @Input() public mode: Mode; + @Input() public size: Size; + @Input() public backgroundShape: BackgroundShape; + @Input() public backgroundColor: BackgroundColor; + @Input() public disabled: boolean; + @Input() public clickable: boolean; + @Input() public className: any; + @Input() public testId: string; + + public svgIconContent: string; + public svgIconContentSafeHtml: SafeHtml; + public svgIconCustomClassName: string; + public classes: string; + + constructor(protected domSanitizer: DomSanitizer) { + this.size = Size.medium; + this.disabled = false; + this.type = this.type || "common"; + } + + static Icons(): { [key: string]: string } { + return iconsMap; + } + + public ngOnChanges(changes: SimpleChanges) { + this.updateSvgIconByName(); + this.buildClasses(); + } + + protected updateSvgIconByName() { + this.svgIconContent = iconsMap[this.type][this.name] || null; + if (this.svgIconContent) { + this.svgIconContentSafeHtml = this.domSanitizer.bypassSecurityTrustHtml(this.svgIconContent); + this.svgIconCustomClassName = '__' + this.name.replace(/\s+/g, '_'); + } else { + this.svgIconContentSafeHtml = null; + this.svgIconCustomClassName = 'missing'; + } + } + + private buildClasses = (): void => { + const _classes = ['svg-icon']; + if (this.mode) { + _classes.push('mode-' + this.mode); + } + if (this.size) { + _classes.push('size-' + this.size); + } + if (this.clickable) { + !this.disabled && _classes.push('clickable'); + } + if (this.svgIconCustomClassName) { + _classes.push(this.svgIconCustomClassName); + } + if (this.className) { + _classes.push(this.className); + } + if (this.backgroundShape) { + _classes.push('bg-type-' + this.backgroundShape); + } + if (this.backgroundShape && this.backgroundColor) { + _classes.push('bg-color-' + this.backgroundColor); + } else if (this.backgroundShape && !this.backgroundColor) { + _classes.push('bg-color-primary'); + } + this.classes = _classes.join(" "); + } +} diff --git a/vid-webpack-master/src/app/shared/components/customIcon/models/background-color.model.ts b/vid-webpack-master/src/app/shared/components/customIcon/models/background-color.model.ts new file mode 100644 index 000000000..324f51ba4 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/customIcon/models/background-color.model.ts @@ -0,0 +1,15 @@ +export enum BackgroundColor { + gray = 'gray', + purple = 'purple', + blue = 'blue', + lightBlue = 'light-blue', + darkBlue = 'dark-blue', + darkBlue2 = 'dark-blue2', + disabledBlue = 'disabled-blue', + white = 'white', + transparent = 'transparent', + green = 'green', + red = 'red', + yellow = 'yellow', + silver ='silver' +} diff --git a/vid-webpack-master/src/app/shared/components/customIcon/models/background-shape.model.ts b/vid-webpack-master/src/app/shared/components/customIcon/models/background-shape.model.ts new file mode 100644 index 000000000..924ce3da1 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/customIcon/models/background-shape.model.ts @@ -0,0 +1,4 @@ +export enum BackgroundShape { + circle = 'circle', + rectangle = 'rectangle' +} diff --git a/vid-webpack-master/src/app/shared/components/customIcon/models/icon-size.model.ts b/vid-webpack-master/src/app/shared/components/customIcon/models/icon-size.model.ts new file mode 100644 index 000000000..a6cd9f537 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/customIcon/models/icon-size.model.ts @@ -0,0 +1,7 @@ +export enum Size { + x_large = 'x_large', + large = 'large', + medium = 'medium', + small = 'small', + x_small = 'x_small' +} diff --git a/vid-webpack-master/src/app/shared/components/customLoader/custom-loader.component.html b/vid-webpack-master/src/app/shared/components/customLoader/custom-loader.component.html new file mode 100644 index 000000000..999d5d3f9 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/customLoader/custom-loader.component.html @@ -0,0 +1,15 @@ +
+
+
+
+
+ +
+
+
+
+
+
+
diff --git a/vid-webpack-master/src/app/shared/components/customLoader/custom-loader.component.scss b/vid-webpack-master/src/app/shared/components/customLoader/custom-loader.component.scss new file mode 100644 index 000000000..7572c6e11 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/customLoader/custom-loader.component.scss @@ -0,0 +1,222 @@ +.custom-loader-background { + background-color: #000000; + position: absolute; + z-index: 9999; + opacity: 0.5; + display: flex; + justify-content: center; + align-items: center; } + +.sdc-loader-wrapper-absolute { + position: absolute; + top: 0; } + +.custom-loader { + z-index: 10002; } + +.custom-loader-global-wrapper { + position: fixed; + width: 100%; + height: 100%; } + +.loader-fixed { + display: block; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; } +@keyframes fadein { + from { + opacity: 0; } + to { + opacity: 0.8; } } + +/* Firefox < 16 */ +@-moz-keyframes fadein { + from { + opacity: 0; } + to { + opacity: 0.8; } } + +/* Safari, Chrome and Opera > 12.1 */ +@-webkit-keyframes fadein { + from { + opacity: 0; } + to { + opacity: 0.8; } } + +/* Internet Explorer */ +@-ms-keyframes fadein { + from { + opacity: 0; } + to { + opacity: 0.8; } } + +/* Opera < 12.1 */ +@-o-keyframes fadein { + from { + opacity: 0; } + to { + opacity: 0.8; } } + +@keyframes fadeout { + from { + opacity: 0.8; } + to { + opacity: 0; } } + +/* Firefox < 16 */ +@-moz-keyframes fadeout { + from { + opacity: 0.8; } + to { + opacity: 0; } } + +/* Safari, Chrome and Opera > 12.1 */ +@-webkit-keyframes fadeout { + from { + opacity: 0.8; } + to { + opacity: 0; } } + +/* Internet Explorer */ +@-ms-keyframes fadeout { + from { + opacity: 0.8; } + to { + opacity: 0; } } + +/* Opera < 12.1 */ +@-o-keyframes fadeout { + from { + opacity: 0.8; } + to { + opacity: 0; } } + +.custom-loader { + height: 63px; + width: 63px; + position: absolute; } + +.custom-loader.small { + transform: scale(0.26); } + +.custom-loader.medium { + transform: scale(0.5); } + +.custom-loader.large { + transform: scale(1); } + +.custom-loader::before { + background-color: #eaeaea; + border-radius: 50%; + box-shadow: 21px 21px 0px 0px #eaeaea, 0px 42px 0px 0px #eaeaea, -21px 21px 0px 0px #eaeaea; + content: ''; + display: block; + height: 21px; + width: 21px; + position: absolute; + left: 50%; + margin-left: -10.5px; } + +.custom-loader::after { + border-radius: 50%; + content: ''; + display: block; + position: absolute; + height: 21px; + width: 21px; + animation: dot-move-2 4.5s infinite ease-in; } + +@keyframes dot-move { + 0% { + background-color: #1eb9f3; + left: 21px; + top: 0; } + 25% { + background-color: #ffb81c; + left: 42px; + top: 21px; } + 50% { + background-color: #caa2dd; + left: 21px; + top: 42px; } + 75% { + background-color: #f6c632; + left: 0; + top: 21px; } + 100% { + background-color: #1eb9f3; + left: 21px; + top: 0; } } + +@keyframes dot-move-2 { + 0% { + background-color: #1eb9f3; + left: 21px; + top: 0; } + 6.25% { + background-color: #1eb9f3; + left: 42px; + top: 21px; } + 12.5% { + background-color: #1eb9f3; + left: 21px; + top: 42px; } + 18.75% { + background-color: #1eb9f3; + left: 0; + top: 21px; } + 25% { + background-color: #ffb81c; + left: 21px; + top: 0; } + 31.25% { + background-color: #ffb81c; + left: 42px; + top: 21px; } + 37.5% { + background-color: #ffb81c; + left: 21px; + top: 42px; } + 43.75% { + background-color: #ffb81c; + left: 0; + top: 21px; } + 50% { + background-color: #caa2dd; + left: 21px; + top: 0; } + 56.25% { + background-color: #caa2dd; + left: 42px; + top: 21px; } + 62.5% { + background-color: #caa2dd; + left: 21px; + top: 42px; } + 68.75% { + background-color: #caa2dd; + left: 0; + top: 21px; } + 75% { + background-color: #f6c632; + left: 21px; + top: 0; } + 81.25% { + background-color: #f6c632; + left: 42px; + top: 21px; } + 87.5% { + background-color: #f6c632; + left: 21px; + top: 42px; } + 93.75% { + background-color: #f6c632; + left: 0; + top: 21px; } + 100% { + background-color: #1eb9f3; + left: 21px; + top: 0; } } diff --git a/vid-webpack-master/src/app/shared/components/customLoader/custom-loader.component.ts b/vid-webpack-master/src/app/shared/components/customLoader/custom-loader.component.ts new file mode 100644 index 000000000..98bcce1a1 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/customLoader/custom-loader.component.ts @@ -0,0 +1,76 @@ +import {Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewContainerRef} from "@angular/core"; +import {LoaderSize} from "./models/loader-size.model"; +import {LoaderService} from "./custom-loader.service"; + +@Component({ + selector: "custom-loader", + templateUrl: './custom-loader.component.html', + styleUrls: ['./custom-loader.component.scss'] + +}) + +export class LoaderComponent implements OnInit, OnDestroy { + @Input() active: number; + @Input() size?: LoaderSize; + @Input() global?: boolean; + @Input() name?: string; + @Input() testId: string; + @Input() relative: boolean; + @Output() activeChange: EventEmitter = new EventEmitter(); + private offset : { + top: string; + left: string; + width: string; + height: string; + }; + + constructor(private loaderService: LoaderService, private viewContainerRef: ViewContainerRef) { + this.active = 0; + this.size = LoaderSize.large; + this.global = false; + } + + public ngOnInit(): void { + if (this.name !== undefined) { + this.loaderService.register(this.name, this); + } + this.setLoaderPlace(); + } + + public ngOnDestroy(): void { + if (this.name !== undefined) { + this.loaderService.unregister(this.name); + } + } + + public activate() { + this.active++; + this.activeChange.emit(this.active); + } + + public deactivate() { + if (this.active > 0) { + this.active--; + this.activeChange.emit(this.active); + } + } + public setLoaderPlace = () => { + if (this.relative === true) { + let parentElement = this.viewContainerRef.element.nativeElement.parentElement; + this.offset = { + left: (parentElement.offsetLeft !== undefined) ? parentElement.offsetLeft + "px" : undefined, + top: (parentElement.offsetTop !== undefined) ? parentElement.offsetTop + "px" : undefined, + width: (parentElement.offsetWidth !== undefined) ? parentElement.offsetWidth + "px" : undefined, + height: (parentElement.offsetHeight !== undefined) ? parentElement.offsetHeight + "px" : undefined + }; + } else { + this.offset = { + left: '0px', + top: '0px', + width: '100%', + height: '100%' + } + } + } + +} diff --git a/vid-webpack-master/src/app/shared/components/customLoader/custom-loader.service.ts b/vid-webpack-master/src/app/shared/components/customLoader/custom-loader.service.ts new file mode 100644 index 000000000..398eac233 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/customLoader/custom-loader.service.ts @@ -0,0 +1,34 @@ +import {Injectable} from "@angular/core"; +import {LoaderComponent} from "./custom-loader.component"; + +@Injectable() +export class LoaderService { + + private mainLoaderName = 'general'; + public registeredLoaders = {}; + + register(name: string, loader: LoaderComponent) { + if (!this.registeredLoaders[name]) { + this.registeredLoaders[name] = loader; + } + } + + unregister(name: string) { + if (this.registeredLoaders[name]) { + delete this.registeredLoaders[name]; + } + } + + activate(name: string = this.mainLoaderName) { + if (this.registeredLoaders[name]) { + this.registeredLoaders[name].activate(); + } + } + + deactivate(name: string = this.mainLoaderName) { + if (this.registeredLoaders[name]) { + this.registeredLoaders[name].deactivate(); + } + } + +} diff --git a/vid-webpack-master/src/app/shared/components/customLoader/models/loader-size.model.ts b/vid-webpack-master/src/app/shared/components/customLoader/models/loader-size.model.ts new file mode 100644 index 000000000..d5400efb5 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/customLoader/models/loader-size.model.ts @@ -0,0 +1,5 @@ +export enum LoaderSize { + large = 'large', + medium = 'medium', + small = 'small', +} diff --git a/vid-webpack-master/src/app/shared/components/customModal/components/modalButton/modal-button.component.html b/vid-webpack-master/src/app/shared/components/customModal/components/modalButton/modal-button.component.html index 753a3923b..a50522a67 100644 --- a/vid-webpack-master/src/app/shared/components/customModal/components/modalButton/modal-button.component.html +++ b/vid-webpack-master/src/app/shared/components/customModal/components/modalButton/modal-button.component.html @@ -2,15 +2,15 @@ - + diff --git a/vid-webpack-master/src/app/shared/components/customModal/components/modalCloseButton/modal-close-button.component.ts b/vid-webpack-master/src/app/shared/components/customModal/components/modalCloseButton/modal-close-button.component.ts index a3e741a32..9528f778d 100644 --- a/vid-webpack-master/src/app/shared/components/customModal/components/modalCloseButton/modal-close-button.component.ts +++ b/vid-webpack-master/src/app/shared/components/customModal/components/modalCloseButton/modal-close-button.component.ts @@ -14,7 +14,7 @@ import {RippleAnimationAction} from "../../directives/ripple-click.animation.dir [attr.data-tests-id]="testId" (click)="!disabled && closeModal('close')" > - + ` }) diff --git a/vid-webpack-master/src/app/shared/components/customModal/modal.component.html b/vid-webpack-master/src/app/shared/components/customModal/modal.component.html index 11455bc32..059eb8a3f 100644 --- a/vid-webpack-master/src/app/shared/components/customModal/modal.component.html +++ b/vid-webpack-master/src/app/shared/components/customModal/modal.component.html @@ -8,12 +8,12 @@
{{ title }} - - +
diff --git a/vid-webpack-master/src/app/shared/components/customTooltip/custom-tooltip.component.ts b/vid-webpack-master/src/app/shared/components/customTooltip/custom-tooltip.component.ts new file mode 100644 index 000000000..a8163a6d8 --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/customTooltip/custom-tooltip.component.ts @@ -0,0 +1,20 @@ +import {AfterViewInit, Component, ViewChild, ViewContainerRef} from "@angular/core"; +import {BehaviorSubject} from "rxjs"; + +@Component({ + selector: 'tooltip-template', + template: ` +
+ +
` +}) + +export class TooltipTemplateComponent implements AfterViewInit { + @ViewChild('templateContainer', {read: ViewContainerRef, static: true}) public container: ViewContainerRef; + + public viewReady: BehaviorSubject = new BehaviorSubject(false); + + ngAfterViewInit() : void { + this.viewReady.next(true); + } +} diff --git a/vid-webpack-master/src/app/shared/components/customTooltip/tooltip.directive.ts b/vid-webpack-master/src/app/shared/components/customTooltip/tooltip.directive.ts new file mode 100644 index 000000000..4c3b8bd3d --- /dev/null +++ b/vid-webpack-master/src/app/shared/components/customTooltip/tooltip.directive.ts @@ -0,0 +1,461 @@ +import {Directive, ElementRef, HostListener, Input, OnInit, Renderer, TemplateRef} from "@angular/core"; +import {CreateDynamicComponentService} from "../customModal/services/create-dynamic-component.service"; +import {TooltipTemplateComponent} from "./custom-tooltip.component"; + +const pixel = 'px'; +const leftStyle = 'left'; +const topStyle = 'top'; +const showSuffix = 'show'; +const rightBottomSuffix = 'right__bottom'; +const centerMiddleSuffix = 'center__middle'; + +@Directive({ + selector: '[custom-tooltip]' +}) +export class TooltipDirective implements OnInit { + @Input('tooltip-text') public text; + @Input('tooltip-placement') public placement: TooltipPlacement = TooltipPlacement.Top; + @Input('tooltip-css-class') public customCssClass: string; + @Input('tooltip-template') public template: TemplateRef; + @Input('tooltip-arrow-offset') public arrowOffset: number = 10; + @Input('tooltip-arrow-placement') public arrowPlacement: ArrowPlacement = ArrowPlacement.LeftTop; + @Input('tooltip-offset') public tooltipOffset: number = 3; + + private cssClass: string = 'sdc-tooltip'; // default css class + private tooltip: any; // tooltip html element + private elemPosition: any; + private tooltipTemplateContainer: any; + + private scrollEventHandler = () => {}; + + constructor( + private elementRef: ElementRef, + private service: CreateDynamicComponentService, + private renderer: Renderer) { + + this.elementRef.nativeElement.title = ""; + } + + @HostListener('mouseenter') + public onMouseEnter() { + this.show(); + this.activateScrollEvent(); + } + + @HostListener('mouseleave') + public onMouseLeave() { + this.hide(); + this.deactivateScrollEvent(); + } + + ngOnInit(): void { + this.initScrollEvent(); + } + + private get ScreenWidth() { + return document.documentElement.clientWidth; + } + + private get ScreenHeight() { + return document.documentElement.clientHeight; + } + + private create() { + this.tooltipTemplateContainer = this.service.createComponentDynamically(TooltipTemplateComponent, document.body); + + /** + * Creating a view (injecting our template) from template in our component. + */ + this.tooltip = this.tooltipTemplateContainer.location.nativeElement.querySelector( + '.sdc-tooltip-template-container'); + + if (this.template) { + this.tooltipTemplateContainer.instance.container.createEmbeddedView(this.template); + } else if(this.text) { + this.tooltip.textContent = this.text; + } else { + this.tooltip = undefined; + } + + this.setCssClass(true); + } + + private destroy() { + this.tooltipTemplateContainer.destroy(); + this.tooltip = null; + } + + private show() { + this.create(); + + /** + * View is ready (AfterViewInit event in template component) + */ + this.tooltipTemplateContainer.instance.viewReady.subscribe((isReady) => { + if (isReady) { + this.setPosition(); + this.toggleShowCssClass(true); // add css class + } + }); + } + + private hide() { + this.toggleShowCssClass(false); // remove css class + + this.destroy(); + } + + private toggleShowCssClass(isAdd: boolean) { + if (this.tooltip) { + this.setCssClass(isAdd, '-' + showSuffix); + } + } + + /** + * Adds placement css class and sets tooltip position in style + */ + private setPosition() { + const tooltipPos: IPlacementData = this.getPlacementData(); + + const placementSuffix: string = TooltipPlacement[tooltipPos.placement].toLowerCase(); + + this.setCssClass(true, '-' + placementSuffix); + + this.setAdditionalCssClass(placementSuffix); + + this.renderer.setElementStyle(this.tooltip, topStyle, tooltipPos.top + pixel); + this.renderer.setElementStyle(this.tooltip, leftStyle, tooltipPos.left + pixel); + } + + private setAdditionalCssClass(placementSuffix: string) { + if (this.arrowPlacement === ArrowPlacement.RightBottom) { + this.setCssClass(true, '-' + placementSuffix + '-' + rightBottomSuffix); + } else if (this.arrowPlacement === ArrowPlacement.CenterMiddle) { + this.setCssClass(true, '-' + placementSuffix + '-' + centerMiddleSuffix); + } + } + + private setCssClass(isAdd: boolean, suffix: string = '') { + this.renderer.setElementClass(this.tooltip, this.cssClass + suffix, isAdd); + + if (this.customCssClass) { + this.renderer.setElementClass(this.tooltip, this.customCssClass + suffix, isAdd); + } + } + + /** + * Checks the specified placement (first element in array), if it is not valid - checks other placements + * @returns {IPlacementData} + */ + private getPlacementData(): IPlacementData { + const placement: TooltipPlacement = this.placement; + let tooltipPos: IPlacementData; + + const tooltipPosWithPlacement = this.getPlacement.bind(this, placement); + + // TODO add comments - done + switch (placement) { + case TooltipPlacement.Left: + tooltipPos = tooltipPosWithPlacement( + TooltipPlacement.Right, + TooltipPlacement.Top, + TooltipPlacement.Bottom); + break; + + case TooltipPlacement.Right: + tooltipPos = tooltipPosWithPlacement( + TooltipPlacement.Left, + TooltipPlacement.Top, + TooltipPlacement.Bottom); + break; + + case TooltipPlacement.Top: + tooltipPos = tooltipPosWithPlacement( + TooltipPlacement.Bottom, + TooltipPlacement.Left, + TooltipPlacement.Right); + break; + + case TooltipPlacement.Bottom: + tooltipPos = tooltipPosWithPlacement( + TooltipPlacement.Top, + TooltipPlacement.Left, + TooltipPlacement.Right); + break; + } + + return tooltipPos; + } + + /** + * Returns valid tooltip position data + * @param {TooltipPlacement} placement + * @param {TooltipPlacement} additionalPlacements + * @returns {IPlacementData} + */ + private getPlacement(placement: TooltipPlacement, + ...additionalPlacements: TooltipPlacement[] + ): IPlacementData { + const placements: TooltipPlacement[] = [placement, ...additionalPlacements]; + const filterPlacements = placements + .map((pl) => this.getPosition(pl)) + .filter((item) => this.validatePosition(item)); + return filterPlacements.length > 0 ? filterPlacements[0] : this.getPosition(placement); + } + + /** + * Returns input data for getPosition method + * @returns {ITooltipPositionParams} + */ + private getPlacementInputParams(): ITooltipPositionParams { + this.elemPosition = this.elementRef.nativeElement.getBoundingClientRect(); + + return { + elemHeight: this.elementRef.nativeElement.offsetHeight, + elemLeft: this.elemPosition.left, + elemTop: this.elemPosition.top, + elemWidth: this.elementRef.nativeElement.offsetWidth, + pageYOffset: window.pageYOffset, + tooltipHeight: this.tooltip.offsetHeight, // .clientHeight, + tooltipOffset: this.tooltipOffset, + tooltipWidth: this.tooltip.offsetWidth, + arrowOffset: this.arrowOffset + }; + } + + /** + * Returns tooltip position data + * @param {TooltipPlacement} placement (left, top, right, bottom) + * @returns {IPlacementData} + */ + private getPosition(placement: TooltipPlacement): IPlacementData { + switch(this.arrowPlacement) { + case ArrowPlacement.LeftTop: + return this.getLeftTopPosition(placement); + + case ArrowPlacement.RightBottom: + return this.getRightBottomPosition(placement); + } + + return this.getCenterMiddlePosition(placement); + } + + /** + * Returns tooltip position data (center / middle arrow) + * @param {TooltipPlacement} placement (left, top, right, bottom) + * @returns {IPlacementData} + */ + private getCenterMiddlePosition(placement: TooltipPlacement): IPlacementData { + let left = 0; + let top = 0; + + const inputPos: ITooltipPositionParams = this.getPlacementInputParams(); + switch (placement) { + case TooltipPlacement.Left: + left = inputPos.elemLeft - inputPos.tooltipWidth - inputPos.tooltipOffset; + top = inputPos.elemTop + inputPos.pageYOffset + inputPos.elemHeight / 2 - inputPos.tooltipHeight / 2; + break; + + case TooltipPlacement.Right: + left = inputPos.elemLeft + inputPos.elemWidth + inputPos.tooltipOffset; + top = inputPos.elemTop + inputPos.pageYOffset + inputPos.elemHeight / 2 - inputPos.tooltipHeight / 2; + break; + + case TooltipPlacement.Top: + left = inputPos.elemLeft + inputPos.elemWidth / 2 - inputPos.tooltipWidth / 2; + top = inputPos.elemTop + inputPos.pageYOffset - inputPos.tooltipHeight - inputPos.tooltipOffset; + break; + + case TooltipPlacement.Bottom: + left = inputPos.elemLeft + inputPos.elemWidth / 2 - inputPos.tooltipWidth / 2; + top = inputPos.elemTop + inputPos.pageYOffset + inputPos.elemHeight + inputPos.tooltipOffset; + break; + } + + return { + height: inputPos.tooltipHeight, + left, + placement, + top, + width: inputPos.tooltipWidth, + pageYOffset: inputPos.pageYOffset + } as IPlacementData; + } + + /** + * Returns tooltip position data (left / top arrow) + * @param {TooltipPlacement} placement (left, top, right, bottom) + * @returns {IPlacementData} + */ + private getLeftTopPosition(placement: TooltipPlacement): IPlacementData { + let left = 0; + let top = 0; + + const inputPos: ITooltipPositionParams = this.getPlacementInputParams(); + switch (placement) { + case TooltipPlacement.Left: + left = inputPos.elemLeft - inputPos.tooltipWidth - inputPos.tooltipOffset; + top = inputPos.elemTop + inputPos.pageYOffset + inputPos.elemHeight / 2 - inputPos.arrowOffset; + break; + + case TooltipPlacement.Right: + left = inputPos.elemLeft + inputPos.elemWidth + inputPos.tooltipOffset; + top = inputPos.elemTop + inputPos.pageYOffset + inputPos.elemHeight / 2 - inputPos.arrowOffset; + break; + + case TooltipPlacement.Top: + left = inputPos.elemLeft + inputPos.elemWidth / 2 - inputPos.arrowOffset; + top = inputPos.elemTop + inputPos.pageYOffset - inputPos.tooltipHeight - inputPos.tooltipOffset; + break; + + case TooltipPlacement.Bottom: + left = inputPos.elemLeft + inputPos.elemWidth / 2 - inputPos.arrowOffset; + top = inputPos.elemTop + inputPos.pageYOffset + inputPos.elemHeight + inputPos.tooltipOffset; + break; + } + + return { + height: inputPos.tooltipHeight, + left, + placement, + top, + width: inputPos.tooltipWidth, + pageYOffset: inputPos.pageYOffset + } as IPlacementData; + } + + /** + * Returns tooltip position data (right / bottom arrow) + * @param {TooltipPlacement} placement (left, top, right, bottom) + * @returns {IPlacementData} + */ + private getRightBottomPosition(placement: TooltipPlacement): IPlacementData { + let left = 0; + let top = 0; + + const inputPos: ITooltipPositionParams = this.getPlacementInputParams(); + switch (placement) { + case TooltipPlacement.Left: + left = inputPos.elemLeft - inputPos.tooltipWidth - inputPos.tooltipOffset; + top = inputPos.elemTop + inputPos.pageYOffset + inputPos.elemHeight / 2 - inputPos.tooltipHeight + inputPos.arrowOffset; + break; + + case TooltipPlacement.Right: + left = inputPos.elemLeft + inputPos.elemWidth + inputPos.tooltipOffset; + top = inputPos.elemTop + inputPos.pageYOffset + inputPos.elemHeight / 2 - inputPos.tooltipHeight + inputPos.arrowOffset; + break; + + case TooltipPlacement.Top: + left = inputPos.elemLeft + inputPos.elemWidth / 2 - inputPos.tooltipWidth + inputPos.arrowOffset; + top = inputPos.elemTop + inputPos.pageYOffset - inputPos.tooltipHeight - inputPos.tooltipOffset; + break; + + case TooltipPlacement.Bottom: + left = inputPos.elemLeft + inputPos.elemWidth / 2 - inputPos.tooltipWidth + inputPos.arrowOffset; + top = inputPos.elemTop + inputPos.pageYOffset + inputPos.elemHeight + inputPos.tooltipOffset; + break; + } + + return { + height: inputPos.tooltipHeight, + left, + placement, + top, + width: inputPos.tooltipWidth, + pageYOffset: inputPos.pageYOffset + } as IPlacementData; + } + + /** + * Checks if tooltip position is valid + * @param {IPlacementData} pos + * @returns {boolean} + */ + private validatePosition(pos: IPlacementData): boolean { + if (pos.left < 0 || pos.left + pos.width - 1 > this.ScreenWidth) { + return false; + } + + if (pos.top - pos.pageYOffset < 0 || pos.top - pos.pageYOffset + pos.height - 1 > this.ScreenHeight) { + return false; + } + + return true; + } + + /** + * Scrolling + */ + + private debounce(func: Function, wait: number, immediate?: boolean) { + let timeout; + return function() { + const context = this; + const args = arguments; + const later = () => { + timeout = null; + if (!immediate) { + func.apply(context, args); + } + }; + const callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) { + func.apply(context, args); + } + }; + } + + private initScrollEvent() { + this.scrollEventHandler = this.debounce(() => { + try { + this.setPosition(); + } catch (e) { + + } + }, 10); + } + + private activateScrollEvent() { + window.addEventListener('scroll', this.scrollEventHandler , true); + } + + private deactivateScrollEvent() { + window.removeEventListener('scroll', this.scrollEventHandler , true); + } +} + +export enum TooltipPlacement { + Left, + Right, + Top, + Bottom +} + +export enum ArrowPlacement { + CenterMiddle, + LeftTop, + RightBottom +} + +interface ITooltipPositionParams { + elemLeft: number; + elemTop: number; + elemWidth: number; + elemHeight: number; + tooltipWidth: number; + tooltipHeight: number; + tooltipOffset: number; + pageYOffset: number; + arrowOffset: number; +} + +interface IPlacementData { + left: number; + top: number; + width: number; + height: number; + pageYOffset: number; + placement?: TooltipPlacement; +} diff --git a/vid-webpack-master/src/app/shared/components/ellipsis/ellipsis.component.ts b/vid-webpack-master/src/app/shared/components/ellipsis/ellipsis.component.ts index a7b8ac744..9828645bf 100644 --- a/vid-webpack-master/src/app/shared/components/ellipsis/ellipsis.component.ts +++ b/vid-webpack-master/src/app/shared/components/ellipsis/ellipsis.component.ts @@ -6,7 +6,7 @@ import * as _ from 'lodash'; selector: 'custom-ellipsis', template: ` + [(active)] = "show"> diff --git a/vid-webpack-master/src/app/shared/components/svg/svg-component.ts b/vid-webpack-master/src/app/shared/components/svg/svg-component.ts index 2e4642432..2b5727bb3 100644 --- a/vid-webpack-master/src/app/shared/components/svg/svg-component.ts +++ b/vid-webpack-master/src/app/shared/components/svg/svg-component.ts @@ -10,14 +10,14 @@ import * as _ from 'lodash'; @Component({ selector : 'vid-svg-icon', - template: ` - - + `, diff --git a/vid-webpack-master/src/app/shared/shared.module.ts b/vid-webpack-master/src/app/shared/shared.module.ts index 98db41d41..08b7fe520 100644 --- a/vid-webpack-master/src/app/shared/shared.module.ts +++ b/vid-webpack-master/src/app/shared/shared.module.ts @@ -49,7 +49,6 @@ import {ServicePopupService} from "./components/genericFormPopup/genericFormServ import {GenericFormPopupService} from "./components/genericFormPopup/generic-form-popup.service"; import {FormGeneralErrorsService} from "./components/formGeneralErrors/formGeneralErrors.service"; import {VnfPopupService} from "./components/genericFormPopup/genericFormServices/vnf/vnf.popup.service"; -import {SdcUiComponentsModule, SdcUiServices} from "onap-ui-angular"; import {SafePipe} from "./pipes/safe/safe.pipe"; import {ViewEditResolver} from "./resolvers/viewEdit/viewEdit.resolver"; import {FlagsResolve} from "./resolvers/flag/flag.resolver"; @@ -88,6 +87,12 @@ import {ModalCloseButtonComponent} from './components/customModal/components/mod import {CustomButtonComponent} from "./components/customButton/custom-button.component"; import {CustomModalButtonComponent} from "./components/customModal/components/modalButton/modal-button.component"; import {CustomRippleClickAnimationDirective} from "./components/customModal/directives/ripple-click.animation.directive"; +import {LoaderComponent} from "./components/customLoader/custom-loader.component"; +import {LoaderService} from "./components/customLoader/custom-loader.service"; +import {SvgIconComponent} from "./components/customIcon/custom-icon.component"; +import {TooltipTemplateComponent} from "./components/customTooltip/custom-tooltip.component"; +import {TooltipDirective} from "./components/customTooltip/tooltip.directive"; +import {SdcUiComponentsModule} from "onap-ui-angular"; @@ -101,8 +106,8 @@ import {CustomRippleClickAnimationDirective} from "./components/customModal/dire FeatureFlagModule.forRoot(), FormsModule, ReactiveFormsModule, - TooltipModule, SdcUiComponentsModule, + TooltipModule, AngularMultiSelectModule, BootstrapModalModule, DataTableModule, @@ -119,6 +124,7 @@ import {CustomRippleClickAnimationDirective} from "./components/customModal/dire NumberFormControlComponent, InputPreventionPatternDirective, ClickOutsideDirective, + TooltipDirective, CustomRippleClickAnimationDirective, FormGeneralErrorsComponent, SpinnerComponent, @@ -147,7 +153,10 @@ import {CustomRippleClickAnimationDirective} from "./components/customModal/dire ModalComponent, ModalCloseButtonComponent, CustomButtonComponent, - CustomModalButtonComponent + CustomModalButtonComponent, + LoaderComponent, + SvgIconComponent, + TooltipTemplateComponent ], exports: [ PopoverComponent, @@ -158,6 +167,7 @@ import {CustomRippleClickAnimationDirective} from "./components/customModal/dire InputPreventionPatternDirective, CustomRippleClickAnimationDirective, ClickOutsideDirective, + TooltipDirective, FormGeneralErrorsComponent, SpinnerComponent, NoContentMessageAndIconComponent, @@ -184,7 +194,10 @@ import {CustomRippleClickAnimationDirective} from "./components/customModal/dire ModalComponent, ModalCloseButtonComponent, CustomButtonComponent, - CustomModalButtonComponent + CustomModalButtonComponent, + LoaderComponent, + SvgIconComponent, + TooltipTemplateComponent ], entryComponents : [ GenericFormPopupComponent, @@ -198,7 +211,7 @@ import {CustomRippleClickAnimationDirective} from "./components/customModal/dire MessageBoxService, CreateDynamicComponentService, ModalService, - SdcUiServices.LoaderService, + LoaderService, HttpInterceptorService, IframeService, DefaultDataGeneratorService, @@ -234,7 +247,8 @@ import {CustomRippleClickAnimationDirective} from "./components/customModal/dire SearchFilterPipe, ModelInformationService, MultiselectFormControlService, - InstantiationTemplatesModalService + InstantiationTemplatesModalService, + LoaderService ] }) export class SharedModule {