Skip to content

Commit

Permalink
fix error tooltip cutoffs
Browse files Browse the repository at this point in the history
  • Loading branch information
wouter-willems committed Apr 5, 2024
1 parent 5afa9cd commit 1ef008c
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 10 deletions.
3 changes: 2 additions & 1 deletion projects/demo/src/app/demo/demo.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
<div style="height: 520px; overflow: auto; display: flex; gap: 30px">
<app-deep-input *ngIf="false" >
</app-deep-input>
<div style="flex: 1 1 0px; margin-top: 5rem; margin-bottom: 10rem;">

<div style="flex: 1 1 0px; margin-top: 5rem; margin-bottom: 10rem; margin-left: 20rem; max-width: 16rem;">
<klp-form-element caption="Name Je weet erfjwefwewkfjewerfjwefjewerfjwefwewkfjewerfjwefjew" spaceDistribution="34-66" direction="vertical">
<klp-form-text-input formControlName="name" [clearable]="true">
</klp-form-text-input>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
</div>
<ng-container [ngTemplateOutlet]="captionTpl"></ng-container>
<ng-container *ngIf="direction === 'vertical' && getErrorLocation() === 'belowCaption' && !errorMessageAsTooltip" [ngTemplateOutlet]="errorRef"></ng-container>
<div class="inputContainer">
<div class="inputContainer" (mouseenter)="setErrorTooltipOffset()">
<ng-container *ngIf="errorMessageAsTooltip && shouldShowErrorMessages() && getErrorToShow()">
<div class="errorTooltipTriangle"></div>
<div class="errorTooltipTriangleWhite"></div>
Expand Down Expand Up @@ -77,13 +77,19 @@
<ng-container *ngIf="hasHoverableErrorTooltip() || shouldShowErrorTooltipOpened()">
<div class="errorTooltipTriangle"></div>
<div class="errorTooltipTriangleWhite"></div>
<div class="errorTooltip" [ngClass]="{noPointerEvents: !shouldShowErrorTooltipOpened()}">
<div class="errorTooltipInner">
<i class="closeBtn" (click)="closePopup();">×</i>
<ng-container *ngIf="getErrorToShow()" [ngTemplateOutlet]="errorRef"></ng-container>
<div *ngIf="!getErrorToShow() && shouldShowWarningPopup()">{{getWarningToShow()}}</div>

<div class="absoluteAnchor" #absoluteAnchor></div>
<div class="fixedAnchor" #fixedAnchor></div>
<div class="fixedWrapper" #fixedWrapper>
<div class="errorTooltip" [ngClass]="{noPointerEvents: !shouldShowErrorTooltipOpened()}">
<div class="errorTooltipInner">
<i class="closeBtn" (click)="closePopup();">×</i>
<ng-container *ngIf="getErrorToShow()" [ngTemplateOutlet]="errorRef"></ng-container>
<div *ngIf="!getErrorToShow() && shouldShowWarningPopup()">{{getWarningToShow()}}</div>
</div>
</div>
</div>

</ng-container>
<klp-form-warning-icon variant="fill" *ngIf="getErrorToShow()" (click)="togglePopup()"></klp-form-warning-icon>
<klp-form-warning-icon variant="line" *ngIf="!getErrorToShow() && getWarningToShow()" (click)="togglePopup()"></klp-form-warning-icon>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
.caption {
font-weight: bold;
flex: 0 0 auto;
overflow: hidden;
&.percentageSpacing {
flex: 40 1 0px;
&.d30-70 {
Expand Down Expand Up @@ -179,6 +180,16 @@ $triangleSize: 8px;
klp-form-warning-icon {
cursor: pointer;
}
.absoluteAnchor {
position: absolute;
}
.fixedAnchor {
position: fixed;
}
// using a fixed wrapper here to avoid the overflow hidden properties of a parent container, which could cut off the message tooltip
.fixedWrapper {
position: fixed;
}
}
.errorTooltipTriangle {
display: none;
Expand Down Expand Up @@ -212,7 +223,7 @@ $triangleSize: 8px;
}
position: absolute;
top: -0.6rem;
right: -$spacing-small;
right: -$spacing-large;

transform: translateY(-100%);
width: 20rem;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {CustomErrorMessages, FormErrorMessages} from '../../types';
import {isValueSet, stringIsSetAndFilled} from '../../util/values';
import {FormComponent} from '../form.component';
import {awaitableForNextCycle} from '../../util/angular';
import {getAllLimitingContainers} from '../../util/dom';


export const FORM_ERROR_MESSAGES = new InjectionToken<CustomErrorMessages>('form.error.messages');
Expand Down Expand Up @@ -49,6 +50,9 @@ export class FormElementComponent implements AfterViewInit {
@ViewChild('internalComponentRef') public internalComponentRef: ElementRef;
@ViewChild('tailTpl') public tailTpl: TemplateRef<any>;
@ViewChild('captionDummyForSpaceCalculation') public captionDummyForSpaceCalculation: ElementRef;
@ViewChild('absoluteAnchor') public absoluteAnchor: ElementRef;
@ViewChild('fixedAnchor') public fixedAnchor: ElementRef;
@ViewChild('fixedWrapper') public fixedWrapper: ElementRef;
@ContentChild(NG_VALUE_ACCESSOR) fieldInput: ValueAccessorBase<any>;

public captionRef: TemplateRef<any>;
Expand All @@ -61,6 +65,7 @@ export class FormElementComponent implements AfterViewInit {
constructor(
@Optional() private parent: FormComponent,
@Inject(FORM_ERROR_MESSAGES) @Optional() private customMessages: CustomErrorMessages,
private elRef: ElementRef,
) {
}

Expand All @@ -70,6 +75,8 @@ export class FormElementComponent implements AfterViewInit {
this.fieldInput?.onTouch.asObservable().subscribe((e) => {
this.determinePopupState();
});

[...getAllLimitingContainers(this.elRef.nativeElement), window].forEach(e => e.addEventListener('scroll', this.setErrorTooltipOffset));
}

public shouldShowErrorMessages(): boolean {
Expand Down Expand Up @@ -136,7 +143,7 @@ export class FormElementComponent implements AfterViewInit {
if (this.attachedControl?.touched !== true) {
return null;
}
if (!this.attachedControl?.errors) {
if (!this.attachedControl?.errors) {
return null;
}
return firstError;
Expand All @@ -161,7 +168,7 @@ export class FormElementComponent implements AfterViewInit {
}
}

scrollTo(): void{
scrollTo(): void {
this.internalComponentRef.nativeElement.scrollIntoView(true);
// to give some breathing room, we scroll 100px more to the top
this.getScrollableParent(this.internalComponentRef.nativeElement)?.scrollBy(0, -100);
Expand Down Expand Up @@ -237,4 +244,14 @@ export class FormElementComponent implements AfterViewInit {
this.popupState = 'lockedOpen';
}
}

public setErrorTooltipOffset = (): void => {
if (this.popupState !== 'lockedOpen' && this.popupState !== 'onHover') {
return;
}
const popupOffsetY = this.absoluteAnchor?.nativeElement.getBoundingClientRect().top - this.fixedAnchor?.nativeElement.getBoundingClientRect().top;
if (this.fixedWrapper?.nativeElement) {
this.fixedWrapper.nativeElement.style.transform = `translateY(${popupOffsetY}px)`;
}
};
}
15 changes: 15 additions & 0 deletions projects/klippa/ngx-enhancy-forms/src/lib/util/dom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export function getAllLimitingContainers(element: Element): Array<HTMLElement> {
const result = [];
let current = element;
while (current.parentElement) {
if (isLimitingContainer(current.parentElement)) {
result.push(current.parentElement);
}
current = current.parentElement;
}
return result;
}

export function isLimitingContainer(element: Element): boolean {
return element.scrollHeight > element.clientHeight;
}

0 comments on commit 1ef008c

Please sign in to comment.