mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Merged PR 896: #2253 UiOverlayTrigger erstellt und UiTooltip angepasst
#2253 UiOverlayTrigger erstellt und UiTooltip angepasst Related work items: #2253
This commit is contained in:
committed by
Lorenz Hilpert
parent
d96729fb89
commit
2c3115dc47
@@ -59,9 +59,9 @@
|
||||
<ng-container *ngIf="!!(statusActions$ | async)?.length">
|
||||
<button
|
||||
class="cta-status-dropdown"
|
||||
[uiDropdownTrigger]="statusDropdown"
|
||||
[uiOverlayTrigger]="statusDropdown"
|
||||
[disabled]="changeStatusDisabled$ | async"
|
||||
#dropdown="uiDropdownTrigger"
|
||||
#dropdown="uiOverlayTrigger"
|
||||
>
|
||||
{{ orderItem?.processingStatus | processingStatus }}
|
||||
<ui-icon [rotate]="dropdown.opened ? '270deg' : '90deg'" icon="arrow_head"></ui-icon>
|
||||
@@ -116,8 +116,8 @@
|
||||
<div class="label">Abholfrist</div>
|
||||
<div *ngIf="!(changeDateLoader$ | async)" class="value">
|
||||
<button
|
||||
[uiDropdownTrigger]="deadlineDropdown"
|
||||
#deadlineDropdownTrigger="uiDropdownTrigger"
|
||||
[uiOverlayTrigger]="deadlineDropdown"
|
||||
#deadlineDropdownTrigger="uiOverlayTrigger"
|
||||
[disabled]="!!orderItem?.features?.paid || (changeDateDisabled$ | async)"
|
||||
class="cta-pickup-deadline"
|
||||
>
|
||||
@@ -141,8 +141,8 @@
|
||||
<button
|
||||
class="cta-datepicker"
|
||||
[disabled]="changeDateDisabled$ | async"
|
||||
[uiDatepickerTrigger]="uiDatepicker"
|
||||
#datepicker="uiDatepickerTrigger"
|
||||
[uiOverlayTrigger]="uiDatepicker"
|
||||
#datepicker="uiOverlayTrigger"
|
||||
>
|
||||
<span>
|
||||
{{ orderItem?.estimatedShippingDate | date: 'dd.MM.yy' }}
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
|
||||
<ng-template #vslLieferdatum>
|
||||
<ui-form-control class="datepicker" label="vsl. Lieferdatum" variant="inline">
|
||||
<button class="date-btn" type="button" [uiDatepickerTrigger]="uiDatepicker" #datepicker="uiDatepickerTrigger">
|
||||
<button class="date-btn" type="button" [uiOverlayTrigger]="uiDatepicker" #datepicker="uiOverlayTrigger">
|
||||
<strong>
|
||||
{{ items[i]?.estimatedShippingDate | date: 'dd.MM.yy' }}
|
||||
</strong>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
import { UiDatepickerModule } from '@ui/datepicker';
|
||||
import { UiDropdownModule } from '@ui/dropdown';
|
||||
import { UiFormControlModule } from '@ui/form-control';
|
||||
@@ -14,6 +15,7 @@ import { SharedGoodsInOutOrderEditComponent } from './goods-in-out-order-edit.co
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
UiCommonModule,
|
||||
UiFormControlModule,
|
||||
UiInputModule,
|
||||
UiSelectModule,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { BlobImageDirective } from './blob-image.directive';
|
||||
import { UiClickOutsideDirective } from './click-outside.directive';
|
||||
import { UiFocusDirective } from './focus';
|
||||
import { IsInViewportDirective } from './is-in-viewport.directive';
|
||||
import { UiOverlayTriggerDirective } from './overlay-trigger';
|
||||
import { GroupByPipe } from './pipes/group-by.pipe';
|
||||
import { ReplacePipe } from './pipes/replace.pipe';
|
||||
import { StripHtmlTagsPipe } from './pipes/strip-html-tags.pipe';
|
||||
@@ -11,7 +12,14 @@ import { SubstrPipe } from './pipes/substr.pipe';
|
||||
import { SkeletonLoaderComponent } from './skeleton-loader';
|
||||
|
||||
const components = [SkeletonLoaderComponent];
|
||||
const directives = [UiClickOutsideDirective, IsInViewportDirective, BlobImageDirective, UiAutofocusDirective, UiFocusDirective];
|
||||
const directives = [
|
||||
UiClickOutsideDirective,
|
||||
IsInViewportDirective,
|
||||
BlobImageDirective,
|
||||
UiAutofocusDirective,
|
||||
UiFocusDirective,
|
||||
UiOverlayTriggerDirective,
|
||||
];
|
||||
const pipes = [StripHtmlTagsPipe, SubstrPipe, GroupByPipe, ReplacePipe];
|
||||
@NgModule({
|
||||
imports: [],
|
||||
|
||||
@@ -6,4 +6,5 @@ export * from './date';
|
||||
export * from './skeleton-loader';
|
||||
export * from './defs';
|
||||
export * from './focus';
|
||||
export * from './overlay-trigger';
|
||||
// end:ng42.barrel
|
||||
|
||||
5
apps/ui/common/src/lib/overlay-trigger/index.ts
Normal file
5
apps/ui/common/src/lib/overlay-trigger/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
// start:ng42.barrel
|
||||
export * from './overlay-positions';
|
||||
export * from './overlay-trigger';
|
||||
export * from './overlay-trigger.directive';
|
||||
// end:ng42.barrel
|
||||
@@ -0,0 +1,3 @@
|
||||
export type UiOverlayPositionX = 'before' | 'after';
|
||||
|
||||
export type UiOverlayPositionY = 'above' | 'below';
|
||||
@@ -16,24 +16,23 @@ import {
|
||||
} from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { UiDropdownComponent } from './dropdown.component';
|
||||
import { UiOverlayTrigger } from './overlay-trigger';
|
||||
|
||||
@Directive({ selector: '[uiDropdownTrigger]', exportAs: 'uiDropdownTrigger' })
|
||||
export class UiDropdownTriggerDirective implements OnInit, OnDestroy, OnChanges {
|
||||
@Input('uiDropdownTrigger')
|
||||
dropdown: UiDropdownComponent;
|
||||
@Directive({ selector: '[uiOverlayTrigger]', exportAs: 'uiOverlayTrigger' })
|
||||
export class UiOverlayTriggerDirective implements OnInit, OnDestroy, OnChanges {
|
||||
@Input('uiOverlayTrigger')
|
||||
component: UiOverlayTrigger;
|
||||
|
||||
@Input()
|
||||
@HostBinding('disabled')
|
||||
disabled: boolean;
|
||||
|
||||
private overlayRef: OverlayRef;
|
||||
private dropdownRef: EmbeddedViewRef<any>;
|
||||
|
||||
private viewRef: EmbeddedViewRef<any>;
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
get opened() {
|
||||
return !!this.dropdownRef;
|
||||
return !!this.viewRef;
|
||||
}
|
||||
|
||||
constructor(
|
||||
@@ -62,7 +61,7 @@ export class UiDropdownTriggerDirective implements OnInit, OnDestroy, OnChanges
|
||||
|
||||
@HostListener('click')
|
||||
toggle() {
|
||||
if (this.dropdownRef) {
|
||||
if (this.viewRef) {
|
||||
this.close();
|
||||
} else {
|
||||
this.open();
|
||||
@@ -70,27 +69,24 @@ export class UiDropdownTriggerDirective implements OnInit, OnDestroy, OnChanges
|
||||
}
|
||||
|
||||
open() {
|
||||
const dropdownPortal = new TemplatePortal(this.dropdown.templateRef, this.viewContainerRef);
|
||||
const dropdownPortal = new TemplatePortal(this.component.templateRef, this.viewContainerRef);
|
||||
|
||||
if (!!this.dropdownRef) {
|
||||
if (!!this.viewRef) {
|
||||
this.close();
|
||||
}
|
||||
|
||||
this.updatePositionStrategy();
|
||||
|
||||
this.dropdownRef = this.overlayRef.attach(dropdownPortal);
|
||||
|
||||
this.dropdown.close = () => {
|
||||
this.close();
|
||||
};
|
||||
this.viewRef = this.overlayRef.attach(dropdownPortal);
|
||||
this.component.close = () => this.close();
|
||||
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
close() {
|
||||
this.dropdownRef?.destroy();
|
||||
this.viewRef?.destroy();
|
||||
this.overlayRef.detach();
|
||||
delete this.dropdownRef;
|
||||
delete this.viewRef;
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
@@ -111,15 +107,14 @@ export class UiDropdownTriggerDirective implements OnInit, OnDestroy, OnChanges
|
||||
}
|
||||
|
||||
getPositionStrategy() {
|
||||
let [originX, originFallbackX]: HorizontalConnectionPos[] = [this.dropdown.xPosition === 'before' ? 'start' : 'end', 'start'];
|
||||
let [originX, originFallbackX]: HorizontalConnectionPos[] = [this.component.xPosition === 'before' ? 'start' : 'end', 'start'];
|
||||
|
||||
let [originY, overlayFallbackY]: VerticalConnectionPos[] = [this.dropdown.yPosition === 'above' ? 'top' : 'bottom', 'top'];
|
||||
let [originY, overlayFallbackY]: VerticalConnectionPos[] = [this.component.yPosition === 'above' ? 'top' : 'bottom', 'top'];
|
||||
|
||||
let [overlayY, originFallbackY]: VerticalConnectionPos[] = [originY === 'bottom' ? 'top' : 'bottom', overlayFallbackY];
|
||||
let [overlayX, overlayFallbackX] = [originX, originFallbackX];
|
||||
|
||||
let offsetY = this.dropdown.yOffset ?? 0;
|
||||
let offsetX = this.dropdown.xOffset ?? 0;
|
||||
let offsetY = this.component.yOffset ?? 0;
|
||||
let offsetX = this.component.xOffset ?? 0;
|
||||
|
||||
return this.overlayPositionBuilder.flexibleConnectedTo(this.elementRef).withPositions([
|
||||
{ originX, originY, overlayX, overlayY, offsetY, offsetX },
|
||||
18
apps/ui/common/src/lib/overlay-trigger/overlay-trigger.ts
Normal file
18
apps/ui/common/src/lib/overlay-trigger/overlay-trigger.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { ElementRef, TemplateRef } from '@angular/core';
|
||||
import { UiOverlayPositionX, UiOverlayPositionY } from './overlay-positions';
|
||||
|
||||
export interface UiOverlayTrigger {
|
||||
templateRef: TemplateRef<any>;
|
||||
|
||||
content: ElementRef;
|
||||
|
||||
xPosition: UiOverlayPositionX;
|
||||
|
||||
yPosition: UiOverlayPositionY;
|
||||
|
||||
xOffset: number;
|
||||
|
||||
yOffset: number;
|
||||
|
||||
close: () => void;
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
import {
|
||||
ConnectedPosition,
|
||||
HorizontalConnectionPos,
|
||||
Overlay,
|
||||
OverlayPositionBuilder,
|
||||
OverlayRef,
|
||||
VerticalConnectionPos,
|
||||
} from '@angular/cdk/overlay';
|
||||
import { TemplatePortal } from '@angular/cdk/portal';
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
ComponentRef,
|
||||
Directive,
|
||||
ElementRef,
|
||||
EmbeddedViewRef,
|
||||
HostBinding,
|
||||
HostListener,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
SimpleChanges,
|
||||
TemplateRef,
|
||||
ViewContainerRef,
|
||||
} from '@angular/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { UiDatepickerComponent } from './datepicker.component';
|
||||
|
||||
@Directive({ selector: '[uiDatepickerTrigger]', exportAs: 'uiDatepickerTrigger' })
|
||||
export class UiDatepickerTriggerDirective implements OnInit, OnDestroy, OnChanges {
|
||||
@Input('uiDatepickerTrigger')
|
||||
datepicker: UiDatepickerComponent;
|
||||
|
||||
@Input()
|
||||
@HostBinding('disabled')
|
||||
disabled: boolean;
|
||||
|
||||
private overlayRef: OverlayRef;
|
||||
private datepickerRef: EmbeddedViewRef<any>;
|
||||
private subscription = new Subscription();
|
||||
|
||||
get opened() {
|
||||
return !!this.datepickerRef;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private overlayPositionBuilder: OverlayPositionBuilder,
|
||||
private viewContainerRef: ViewContainerRef,
|
||||
private elementRef: ElementRef,
|
||||
private overlay: Overlay,
|
||||
private cdr: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
ngOnChanges({ position }: SimpleChanges): void {
|
||||
if (position) {
|
||||
this.updatePosition();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.createOverlay();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.overlayRef.dispose();
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
|
||||
@HostListener('click')
|
||||
toggle() {
|
||||
if (this.datepickerRef) {
|
||||
this.close();
|
||||
} else {
|
||||
this.open();
|
||||
}
|
||||
}
|
||||
|
||||
open() {
|
||||
const dropdownPortal = new TemplatePortal(this.datepicker.templateRef, this.viewContainerRef);
|
||||
|
||||
if (!!this.datepickerRef) {
|
||||
this.close();
|
||||
}
|
||||
|
||||
this.updatePositionStrategy();
|
||||
|
||||
this.datepickerRef = this.overlayRef.attach(dropdownPortal);
|
||||
this.datepicker.close = () => this.close();
|
||||
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
close() {
|
||||
this.datepickerRef?.destroy();
|
||||
this.overlayRef.detach();
|
||||
delete this.datepickerRef;
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
createOverlay() {
|
||||
this.overlayRef = this.overlay.create({
|
||||
positionStrategy: this.getPositionStrategy(),
|
||||
hasBackdrop: true,
|
||||
backdropClass: 'backdrop-transparent',
|
||||
});
|
||||
this.subscription.add(this.overlayRef.backdropClick().subscribe(() => this.close()));
|
||||
}
|
||||
|
||||
updatePositionStrategy() {
|
||||
this.overlayRef.updatePositionStrategy(this.getPositionStrategy());
|
||||
}
|
||||
|
||||
getPositionStrategy() {
|
||||
let [originX, originFallbackX]: HorizontalConnectionPos[] = [this.datepicker.xPosition === 'before' ? 'start' : 'end', 'start'];
|
||||
|
||||
let [originY, overlayFallbackY]: VerticalConnectionPos[] = [this.datepicker.yPosition === 'above' ? 'top' : 'bottom', 'top'];
|
||||
|
||||
let [overlayY, originFallbackY]: VerticalConnectionPos[] = [originY === 'bottom' ? 'top' : 'bottom', overlayFallbackY];
|
||||
let [overlayX, overlayFallbackX] = [originX, originFallbackX];
|
||||
let offsetY = this.datepicker.yOffset ?? 0;
|
||||
let offsetX = this.datepicker.xOffset ?? 0;
|
||||
|
||||
return this.overlayPositionBuilder.flexibleConnectedTo(this.elementRef).withPositions([
|
||||
{ originX, originY, overlayX, overlayY, offsetY, offsetX },
|
||||
{ originX: originFallbackX, originY, overlayX: overlayFallbackX, overlayY, offsetY, offsetX },
|
||||
{
|
||||
originX,
|
||||
originY: originFallbackY,
|
||||
overlayX,
|
||||
overlayY: overlayFallbackY,
|
||||
offsetY: -offsetY,
|
||||
offsetX: -offsetX,
|
||||
},
|
||||
{
|
||||
originX: originFallbackX,
|
||||
originY: originFallbackY,
|
||||
overlayX: overlayFallbackX,
|
||||
overlayY: overlayFallbackY,
|
||||
offsetY: -offsetY,
|
||||
offsetX: -offsetX,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
updatePosition() {
|
||||
this.overlayRef?.updatePositionStrategy(this.getPositionStrategy());
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
import { Datepicker } from './datepicker';
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { DateAdapter } from '@ui/common';
|
||||
import { DateAdapter, UiOverlayTrigger } from '@ui/common';
|
||||
import { DatepickerPositionX, DatepickerPositionY } from './datepicker-positions';
|
||||
|
||||
@Component({
|
||||
@@ -27,7 +27,7 @@ import { DatepickerPositionX, DatepickerPositionY } from './datepicker-positions
|
||||
],
|
||||
exportAs: 'uiDatepicker',
|
||||
})
|
||||
export class UiDatepickerComponent extends Datepicker implements ControlValueAccessor, OnInit, OnDestroy {
|
||||
export class UiDatepickerComponent extends Datepicker implements UiOverlayTrigger, ControlValueAccessor, OnInit, OnDestroy {
|
||||
@ViewChild(TemplateRef) templateRef: TemplateRef<any>;
|
||||
|
||||
@ContentChild('content') content: ElementRef;
|
||||
|
||||
@@ -5,19 +5,11 @@ import { CommonModule } from '@angular/common';
|
||||
import { UiDatepickerBodyComponent, UiDatepickerCellDirective, GetCellNamePipe } from './body';
|
||||
import { UiDatepickerHeaderComponent } from './header';
|
||||
import { IconModule } from '@libs/ui';
|
||||
import { UiDatepickerTriggerDirective } from './datepicker-trigger.directive';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, IconModule],
|
||||
exports: [UiDatepickerComponent, UiDatepickerTriggerDirective],
|
||||
declarations: [
|
||||
UiDatepickerComponent,
|
||||
UiDatepickerBodyComponent,
|
||||
UiDatepickerHeaderComponent,
|
||||
UiDatepickerCellDirective,
|
||||
GetCellNamePipe,
|
||||
UiDatepickerTriggerDirective,
|
||||
],
|
||||
exports: [UiDatepickerComponent],
|
||||
declarations: [UiDatepickerComponent, UiDatepickerBodyComponent, UiDatepickerHeaderComponent, UiDatepickerCellDirective, GetCellNamePipe],
|
||||
providers: [],
|
||||
})
|
||||
export class UiDatepickerModule {}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Component, Input, OnInit, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { Component, ElementRef, Input, OnInit, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { UiOverlayTrigger } from '@ui/common';
|
||||
import { DropdownPositionX, DropdownPositionY } from './dropdown-positions';
|
||||
|
||||
@Component({
|
||||
@@ -8,9 +9,11 @@ import { DropdownPositionX, DropdownPositionY } from './dropdown-positions';
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
styleUrls: ['./dropdown.scss'],
|
||||
})
|
||||
export class UiDropdownComponent implements OnInit {
|
||||
export class UiDropdownComponent implements UiOverlayTrigger, OnInit {
|
||||
@ViewChild(TemplateRef) templateRef: TemplateRef<any>;
|
||||
|
||||
content: ElementRef<any>;
|
||||
|
||||
@Input()
|
||||
xPosition: DropdownPositionX;
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { UiDropdownItemDirective } from './dropdown-item.directive';
|
||||
import { UiDropdownTriggerDirective } from './dropdown-trigger.directive';
|
||||
import { UiDropdownComponent } from './dropdown.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [UiDropdownComponent, UiDropdownItemDirective, UiDropdownTriggerDirective],
|
||||
declarations: [UiDropdownComponent, UiDropdownItemDirective],
|
||||
imports: [CommonModule],
|
||||
exports: [UiDropdownComponent, UiDropdownItemDirective, UiDropdownTriggerDirective],
|
||||
exports: [UiDropdownComponent, UiDropdownItemDirective],
|
||||
})
|
||||
export class UiDropdownModule {}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// start:ng42.barrel
|
||||
export * from './dropdown-item.directive';
|
||||
export * from './dropdown-trigger.directive';
|
||||
export * from './dropdown.component';
|
||||
export * from './dropdown.module';
|
||||
// end:ng42.barrel
|
||||
|
||||
@@ -16,15 +16,10 @@
|
||||
</ui-autocomplete>
|
||||
</ui-searchbox>
|
||||
<ng-container *ngIf="showDescription && uiInput?.description">
|
||||
<button
|
||||
[uiTooltip]="tooltipContent"
|
||||
[uiTooltipPosition]="{ offsetX: 20, offsetY: -20, originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom' }"
|
||||
class="info-tooltip-button"
|
||||
type="button"
|
||||
>
|
||||
<button [uiOverlayTrigger]="tooltipContent" #tooltip="uiOverlayTrigger" class="info-tooltip-button" type="button">
|
||||
i
|
||||
</button>
|
||||
<ng-template #tooltipContent>
|
||||
<ui-tooltip #tooltipContent yPosition="above" xPosition="after" [yOffset]="-16">
|
||||
{{ uiInput.description }}
|
||||
</ng-template>
|
||||
</ui-tooltip>
|
||||
</ng-container>
|
||||
|
||||
@@ -5,9 +5,10 @@ import { UiFilterInputGroupMainComponent } from './filter-input-group-main.compo
|
||||
import { UiAutocompleteModule } from '@ui/autocomplete';
|
||||
import { UiSearchboxNextModule } from '@ui/searchbox';
|
||||
import { UiTooltipModule } from 'apps/ui/tooltip/src/public-api';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, UiSearchboxNextModule, UiAutocompleteModule, UiTooltipModule],
|
||||
imports: [CommonModule, UiCommonModule, UiSearchboxNextModule, UiAutocompleteModule, UiTooltipModule],
|
||||
exports: [UiFilterInputGroupMainComponent],
|
||||
declarations: [UiFilterInputGroupMainComponent],
|
||||
})
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<button
|
||||
class="cta-picker"
|
||||
[class.open]="dpStartTrigger?.opened"
|
||||
[uiDatepickerTrigger]="dpStart"
|
||||
#dpStartTrigger="uiDatepickerTrigger"
|
||||
[uiOverlayTrigger]="dpStart"
|
||||
#dpStartTrigger="uiOverlayTrigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
@@ -32,8 +32,8 @@
|
||||
<button
|
||||
class="cta-picker"
|
||||
[class.open]="dpStopTrigger?.opened"
|
||||
[uiDatepickerTrigger]="dpStop"
|
||||
#dpStopTrigger="uiDatepickerTrigger"
|
||||
[uiOverlayTrigger]="dpStop"
|
||||
#dpStopTrigger="uiOverlayTrigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
.tooltip-caret {
|
||||
@apply relative bg-dark-cerulean origin-center transform rotate-45;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ui-tooltip-caret',
|
||||
template: '<div class="tooltip-caret"></div>',
|
||||
styleUrls: ['tooltip-caret.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class TooltipCaretComponent implements OnInit {
|
||||
constructor() {}
|
||||
|
||||
ngOnInit() {}
|
||||
}
|
||||
10
apps/ui/tooltip/src/lib/tooltip.component.html
Normal file
10
apps/ui/tooltip/src/lib/tooltip.component.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<ng-template>
|
||||
<div [class]="classList">
|
||||
<svg viewBox="0 25 50 25" class="triangle">
|
||||
<polygon points="0,50 50,50 25,25"></polygon>
|
||||
</svg>
|
||||
<div class="tooltip-box">
|
||||
<p><ng-content></ng-content></p>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
@@ -7,3 +7,53 @@
|
||||
@apply m-0 text-white font-bold text-base;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-tooltip-panel {
|
||||
.triangle {
|
||||
width: 30px;
|
||||
z-index: -1;
|
||||
polygon {
|
||||
fill: #1f466c;
|
||||
}
|
||||
}
|
||||
|
||||
/* triangle top right*/
|
||||
&.x-position-after.y-position-below {
|
||||
margin-top: 3px;
|
||||
.triangle {
|
||||
@apply absolute;
|
||||
top: -10px;
|
||||
right: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
/* triangle top left*/
|
||||
&.x-position-before.y-position-below {
|
||||
margin-top: 3px;
|
||||
.triangle {
|
||||
@apply absolute;
|
||||
top: -10px;
|
||||
left: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
/* triangle bottom left*/
|
||||
&.x-position-before.y-position-above {
|
||||
margin-bottom: 3px;
|
||||
.triangle {
|
||||
@apply absolute transform rotate-180 origin-center;
|
||||
bottom: -10px;
|
||||
left: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
/* triangle bottom right*/
|
||||
&.x-position-after.y-position-above {
|
||||
margin-bottom: 3px;
|
||||
.triangle {
|
||||
@apply absolute transform rotate-180 origin-center;
|
||||
bottom: -10px;
|
||||
right: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,38 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnInit, TemplateRef } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, ContentChild, ElementRef, Input, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { UiOverlayPositionX, UiOverlayPositionY, UiOverlayTrigger } from '@ui/common';
|
||||
|
||||
@Component({
|
||||
selector: 'ui-tooltip',
|
||||
template: `<div class="tooltip-box">
|
||||
<p><ng-template *ngTemplateOutlet="content"></ng-template></p>
|
||||
</div>`,
|
||||
templateUrl: 'tooltip.component.html',
|
||||
styleUrls: ['tooltip.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class TooltipComponent implements OnInit {
|
||||
@Input() content: string | TemplateRef<any> = '';
|
||||
export class TooltipComponent implements UiOverlayTrigger, OnInit {
|
||||
constructor() {}
|
||||
|
||||
@ViewChild(TemplateRef)
|
||||
templateRef: TemplateRef<any>;
|
||||
|
||||
@ContentChild('content')
|
||||
content: ElementRef<any>;
|
||||
|
||||
@Input()
|
||||
xPosition: UiOverlayPositionX;
|
||||
|
||||
@Input()
|
||||
yPosition: UiOverlayPositionY;
|
||||
|
||||
@Input()
|
||||
xOffset: number;
|
||||
|
||||
@Input()
|
||||
yOffset: number;
|
||||
|
||||
close: () => void;
|
||||
|
||||
get classList() {
|
||||
return ['ui-tooltip-panel', `x-position-${this.xPosition}`, `y-position-${this.yPosition}`];
|
||||
}
|
||||
|
||||
ngOnInit(): void {}
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import { Overlay, OverlayModule } from '@angular/cdk/overlay';
|
||||
import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator';
|
||||
import { TooltipDirective } from './tooltip.directive';
|
||||
|
||||
describe('TooltipDirective', () => {
|
||||
let spectator: SpectatorDirective<TooltipDirective>;
|
||||
|
||||
const createDirective = createDirectiveFactory({
|
||||
directive: TooltipDirective,
|
||||
imports: [OverlayModule],
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
spectator = createDirective(
|
||||
`<div uiTooltip="Unit Test" [uiTooltipPosition]="{ offsetX: 0, offsetY: 0, originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom' }">Testing Tooltip</div>`
|
||||
);
|
||||
});
|
||||
|
||||
it('should get the instance', () => {
|
||||
expect(spectator.directive).toBeDefined();
|
||||
});
|
||||
|
||||
it('should call overlayRef.attach and caretOverlayRef.attach on mouseenter', () => {
|
||||
const overlayRef = spectator.directive['overlayRef'];
|
||||
const caretOverlayRef = spectator.directive['caretOverlayRef'];
|
||||
spyOn(overlayRef, 'attach');
|
||||
spyOn(caretOverlayRef, 'attach');
|
||||
spectator.dispatchMouseEvent(spectator.element, 'mouseenter');
|
||||
expect(overlayRef.attach).toHaveBeenCalled();
|
||||
expect(caretOverlayRef.attach).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call overlayRef.detach and caretOverlayRef.detach on mouseout', () => {
|
||||
const overlayRef = spectator.directive['overlayRef'];
|
||||
const caretOverlayRef = spectator.directive['caretOverlayRef'];
|
||||
spyOn(overlayRef, 'detach');
|
||||
spyOn(caretOverlayRef, 'detach');
|
||||
spectator.dispatchMouseEvent(spectator.element, 'mouseout');
|
||||
expect(overlayRef.detach).toHaveBeenCalled();
|
||||
expect(caretOverlayRef.detach).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('ngOnInit', () => {
|
||||
it('should create overlay and caretOverlay', () => {
|
||||
const overlay = spectator.inject(Overlay);
|
||||
const overlaySpy = spyOn(overlay, 'create');
|
||||
spectator.directive.ngOnInit();
|
||||
expect(overlaySpy).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,68 +0,0 @@
|
||||
import { ConnectedPosition, Overlay, OverlayPositionBuilder, OverlayRef } from '@angular/cdk/overlay';
|
||||
import { ComponentPortal } from '@angular/cdk/portal';
|
||||
import { ComponentRef, Directive, ElementRef, HostListener, Input, OnInit, TemplateRef } from '@angular/core';
|
||||
import { TooltipCaretComponent } from './tooltip-caret.component';
|
||||
import { TooltipComponent } from './tooltip.component';
|
||||
|
||||
@Directive({ selector: '[uiTooltip]' })
|
||||
export class TooltipDirective implements OnInit {
|
||||
@Input('uiTooltip') content: string | TemplateRef<any>;
|
||||
|
||||
@Input('uiTooltipPosition') position: ConnectedPosition = {
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
originX: 'center',
|
||||
originY: 'top',
|
||||
overlayX: 'center',
|
||||
overlayY: 'bottom',
|
||||
};
|
||||
|
||||
private overlayRef: OverlayRef;
|
||||
private caretOverlayRef: OverlayRef;
|
||||
|
||||
constructor(private overlayPositionBuilder: OverlayPositionBuilder, private elementRef: ElementRef, private overlay: Overlay) {}
|
||||
|
||||
@HostListener('mouseenter')
|
||||
show() {
|
||||
const tooltipPortal = new ComponentPortal(TooltipComponent);
|
||||
const tooltipCaretPortal = new ComponentPortal(TooltipCaretComponent);
|
||||
this.caretOverlayRef.attach(tooltipCaretPortal);
|
||||
|
||||
const tooltipRef: ComponentRef<TooltipComponent> = this.overlayRef.attach(tooltipPortal);
|
||||
|
||||
tooltipRef.instance.content = this.content;
|
||||
}
|
||||
|
||||
@HostListener('mouseout')
|
||||
hide() {
|
||||
this.overlayRef.detach();
|
||||
this.caretOverlayRef.detach();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
const positionStrategy = this.overlayPositionBuilder.flexibleConnectedTo(this.elementRef).withPositions([
|
||||
{
|
||||
offsetX: this.position.offsetX,
|
||||
offsetY: this.position.offsetY,
|
||||
originX: this.position.originX,
|
||||
originY: this.position.originY,
|
||||
overlayX: this.position.overlayX,
|
||||
overlayY: this.position.overlayY,
|
||||
},
|
||||
]);
|
||||
|
||||
const caretPositionStrategy = this.overlayPositionBuilder.flexibleConnectedTo(this.elementRef).withPositions([
|
||||
{
|
||||
offsetX: 0,
|
||||
offsetY: this.position.offsetY * 0.5,
|
||||
originX: 'center',
|
||||
originY: this.position.originY,
|
||||
overlayX: 'center',
|
||||
overlayY: this.position.overlayY,
|
||||
},
|
||||
]);
|
||||
|
||||
this.overlayRef = this.overlay.create({ positionStrategy });
|
||||
this.caretOverlayRef = this.overlay.create({ positionStrategy: caretPositionStrategy });
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { TooltipComponent } from './tooltip.component';
|
||||
import { TooltipDirective } from './tooltip.directive';
|
||||
import { OverlayModule } from '@angular/cdk/overlay';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { TooltipCaretComponent } from './tooltip-caret.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [TooltipComponent, TooltipDirective, TooltipCaretComponent],
|
||||
declarations: [TooltipComponent],
|
||||
imports: [CommonModule, OverlayModule],
|
||||
exports: [TooltipComponent, TooltipDirective, TooltipCaretComponent],
|
||||
exports: [TooltipComponent],
|
||||
})
|
||||
export class UiTooltipModule {}
|
||||
|
||||
@@ -2,7 +2,5 @@
|
||||
* Public API Surface of tooltip
|
||||
*/
|
||||
|
||||
export * from './lib/tooltip.directive';
|
||||
export * from './lib/tooltip.component';
|
||||
export * from './lib/tooltip-caret.component';
|
||||
export * from './lib/tooltip.module';
|
||||
|
||||
Reference in New Issue
Block a user