Skip to content

Commit

Permalink
Merge branch 'main' into main-v16
Browse files Browse the repository at this point in the history
# Conflicts:
#	projects/klippa/ngx-enhancy-forms/package.json
  • Loading branch information
MarkDoggen committed Jul 29, 2024
2 parents e01ae8a + e8319bb commit f506986
Show file tree
Hide file tree
Showing 15 changed files with 198 additions and 37 deletions.
2 changes: 2 additions & 0 deletions projects/demo/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { DemoComponent } from './demo/demo.component';
import { OnSubmitErrorsComponent } from './on-submit-errors/on-submit-errors.component';
import { CustomFormComponent } from './custom-form-component/custom-form.component';
import { MySuperFormComponent } from './custom-form-component/my-super-form/my-super-form.component';
import SimpleComponent from "./simple-comp/simple.component";

const routes: Routes = [
{
Expand Down Expand Up @@ -46,6 +47,7 @@ const routes: Routes = [
OnSubmitErrorsComponent,
CustomFormComponent,
MySuperFormComponent,
SimpleComponent,
],
providers: [],
bootstrap: [AppComponent]
Expand Down
20 changes: 18 additions & 2 deletions projects/demo/src/app/demo/demo.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@
<klp-form-error error="async"></klp-form-error>
</klp-form-element>

<klp-form-element caption="Password" spaceDistribution="34-66" direction="vertical">
<klp-form-text-input type="password" formControlName="name" [clearable]="true" [passwordPeekIcon]="peekIcon">
</klp-form-text-input>
<ng-template #peekIcon>E</ng-template>
<klp-form-error error="async"></klp-form-error>
</klp-form-element>

<klp-form-element caption="Name aap noot mies" spaceDistribution="30-70" direction="horizontal">
<klp-form-text-input formControlName="name2" [clearable]="true">
</klp-form-text-input>
Expand All @@ -35,8 +42,8 @@
<klp-form-error error="async"></klp-form-error>
</klp-form-element>

<klp-form-element caption="Yes or no?" spaceDistribution="fixedInputWidth">
<klp-form-toggle formControlName="yesno"></klp-form-toggle>
<klp-form-element caption="Yes or no toggle?" spaceDistribution="fixedInputWidth">
<klp-form-toggle formControlName="yesno" [transparentBackground]="false"></klp-form-toggle>
</klp-form-element>
</div>
<div style="flex: 3 1 0px; margin-top: 15rem; margin-bottom: 10rem; max-width: 250px">
Expand Down Expand Up @@ -72,6 +79,14 @@
I am a small piece of text
</klp-form-button>

<klp-form-button klpWithTooltip="black" [tooltipTemplate]="tooltipTpl" position="bottom" style="margin-left: 300px">
Tooltip with template
<ng-template #tooltipTpl>
<app-simple></app-simple>
</ng-template>
</klp-form-button>


<div>
<klp-form-element caption="radioButtons classic" spaceDistribution="34-66" verticalAlignment="top">
<klp-form-radio formControlName="radioOption" [options]="radioOptionsClassic" orientation="column">
Expand Down Expand Up @@ -208,3 +223,4 @@

<klp-form-submit-button [submitCallback]="saveSimple">Save</klp-form-submit-button>
</klp-form>

1 change: 1 addition & 0 deletions projects/demo/src/app/simple-comp/simple.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello {{text}}!
3 changes: 3 additions & 0 deletions projects/demo/src/app/simple-comp/simple.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:host {
background: orange;
}
17 changes: 17 additions & 0 deletions projects/demo/src/app/simple-comp/simple.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Component} from "@angular/core";

@Component({
selector: 'app-simple',
templateUrl: './simple.component.html',
styleUrls: ['./simple.component.scss'],
})
export default class SimpleComponent {
public text = 'world';

ngOnInit(): void {
setTimeout(() => {
console.log('update plz');
this.text = 'hello again!';
}, 1000);
}
}
2 changes: 1 addition & 1 deletion projects/klippa/ngx-enhancy-forms/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@klippa/ngx-enhancy-forms",
"version": "16.21.0",
"version": "16.22.12",
"publishConfig": {
"access": "public"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<i class="ti-search" *ngIf="icon === 'search'"></i>
</ng-container>
<input
[type]="type"
[type]="getType()"
[(ngModel)]="innerValue"
[ngClass]="{showErrors: isInErrorState(), hasIcon: icon?.length > 0, hasClearButton: clearable, noBorderLeft: !hasBorderLeft, noBorderRight: !hasBorderRight}"
(input)="setInnerValueAndNotify($event.target.value)"
Expand All @@ -15,5 +15,8 @@
<div class="tail">
<ng-container [ngTemplateOutlet]="getTailTpl()"></ng-container>
<div class="clearIcon" *ngIf="clearable && innerValue?.length > 0" (click)="resetToNull()">×</div>
<button class="peakBtn" (click)="togglePeakPassword()" *ngIf="passwordPeekIcon && type === 'password'">
<ng-container [ngTemplateOutlet]="passwordPeekIcon"></ng-container>
</button>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ input {
}
}

.peakBtn {
border: none;
background: transparent;
height: 100%;
padding: 0;
}

.showErrors {
border-color: $default-warning;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Component, EventEmitter, Input, Output} from '@angular/core';
import {Component, EventEmitter, Input, Output, TemplateRef} from '@angular/core';
import {NG_VALUE_ACCESSOR} from '@angular/forms';
import {ValueAccessorBase} from '../value-accessor-base/value-accessor-base.component';

Expand All @@ -9,11 +9,25 @@ import {ValueAccessorBase} from '../value-accessor-base/value-accessor-base.comp
providers: [{provide: NG_VALUE_ACCESSOR, useExisting: TextInputComponent, multi: true}],
})
export class TextInputComponent extends ValueAccessorBase<string> {
private isPeekingPassword = false;

@Input() placeholder: string;
@Input() type: 'text' | 'password' = 'text';
@Input() clearable = false;
@Input() icon: 'search';
@Input() hasBorderLeft = true;
@Input() hasBorderRight = true;
@Input() passwordPeekIcon: TemplateRef<any>;
@Output() onBlur = new EventEmitter<void>();

public togglePeakPassword(): void {
this.isPeekingPassword = !this.isPeekingPassword;
}

public getType(): 'text' | 'password' {
if (this.type === 'text') {
return 'text';
}
return this.isPeekingPassword ? 'text' : 'password';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
[ngClass]="{showErrors: isInErrorState()}"
#nativeInputRef
/>
<div class="toggleVisual"></div>
<div class="toggleVisual" [ngClass]="{transparentBackground: transparentBackground}"></div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ $height: 20px;
height: $height;
border: 1px solid $border-color;
border-radius: $width;
background: #EAECF0;

&.transparentBackground {
background: transparent;
}

&:before {
content: '';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ import {ValueAccessorBase} from '../value-accessor-base/value-accessor-base.comp
providers: [{provide: NG_VALUE_ACCESSOR, useExisting: ToggleComponent, multi: true}],
})
export class ToggleComponent extends ValueAccessorBase<boolean> {
@Input() transparentBackground = true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
ElementRef,
Inject,
InjectionToken,
Input,
Input, OnDestroy,
Optional,
TemplateRef,
ViewChild
Expand All @@ -17,6 +17,7 @@ import {isValueSet, stringIsSetAndFilled} from '../../util/values';
import {FormComponent} from '../form.component';
import {awaitableForNextCycle} from '../../util/angular';
import {getAllLimitingContainers} from '../../util/dom';
import {Subscription} from "rxjs";


export const FORM_ERROR_MESSAGES = new InjectionToken<CustomErrorMessages>('form.error.messages');
Expand All @@ -33,12 +34,14 @@ export const DEFAULT_ERROR_MESSAGES: FormErrorMessages = {
date: 'Enter a valid date',
};

type PopupState = 'onHover' | 'lockedOpen';

@Component({
selector: 'klp-form-element',
templateUrl: './form-element.component.html',
styleUrls: ['./form-element.component.scss'],
})
export class FormElementComponent implements AfterViewInit {
export class FormElementComponent implements AfterViewInit, OnDestroy {
public attachedControl: AbstractControl;
@Input() public caption: string;
@Input() public direction: 'horizontal' | 'vertical' = 'horizontal';
Expand All @@ -60,7 +63,8 @@ export class FormElementComponent implements AfterViewInit {
public customErrorHandlers: Array<{ error: string; templateRef: TemplateRef<any> }> = [];
private input: ValueAccessorBase<any>;
public errorFullyVisible: boolean;
private popupState: 'lockedOpen' | 'lockedClosed' | 'onHover' = 'onHover';
private popupState: PopupState = 'onHover';
private subscriptions: Array<Subscription> = [];

constructor(
@Optional() private parent: FormComponent,
Expand All @@ -72,11 +76,12 @@ export class FormElementComponent implements AfterViewInit {
async ngAfterViewInit(): Promise<void> {
await awaitableForNextCycle();
this.fieldInput?.setTailTpl(this.tailTpl);
this.fieldInput?.onTouch.asObservable().subscribe((e) => {
const subscription = this.fieldInput?.onTouch.asObservable().subscribe(() => {
this.determinePopupState();
});

[...getAllLimitingContainers(this.elRef.nativeElement), window].forEach(e => e.addEventListener('scroll', this.setErrorTooltipOffset));
if (isValueSet(subscription)) {
this.subscriptions.push(subscription);
}
}

public shouldShowErrorMessages(): boolean {
Expand All @@ -95,22 +100,40 @@ export class FormElementComponent implements AfterViewInit {
this.input = input;


this.attachedControl.statusChanges.subscribe((e) => {
const subscription = this.attachedControl.statusChanges.subscribe(() => {
this.determinePopupState();
});
this.subscriptions.push(subscription);
this.determinePopupState();
}

public determinePopupState(): void {
const prevState = this.popupState;
if (stringIsSetAndFilled(this.getErrorToShow())) {
this.popupState = 'onHover';
return;
}
if (isValueSet(this.getWarningToShow())) {
} else if (isValueSet(this.getWarningToShow())) {
this.popupState = 'lockedOpen';
} else {
this.popupState = 'onHover';
}

this.setUpErrorTooltipListeners(prevState, this.popupState);
}

private setUpErrorTooltipListeners(prev: PopupState, current: PopupState): void {
if (prev === current) {
return;
}
this.popupState = 'onHover';
const containers = [...getAllLimitingContainers(this.elRef.nativeElement), window];
if (current === 'lockedOpen') {
containers.forEach(e => {
e.addEventListener('scroll', this.setErrorTooltipOffset);
});
} else {
containers.forEach(e => {
e.removeEventListener('scroll', this.setErrorTooltipOffset);
});
}
}

public unregisterControl(formControl: UntypedFormControl): void {
Expand Down Expand Up @@ -169,20 +192,28 @@ export class FormElementComponent implements AfterViewInit {
}

getScrollableParent(node): any {
if (node == null) {
return null;
if (node === window.document.documentElement) {
return window.document.documentElement;
}
if (node.scrollHeight > node.clientHeight) {
const overflowY = getComputedStyle(node).overflowY;
if (node.clientHeight < node.scrollHeight && (overflowY === 'auto' || overflowY === 'scroll')) {
return node;
} else {
return this.getScrollableParent(node.parentNode);
}
}

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);
const parent = this.getScrollableParent(this.internalComponentRef.nativeElement);
const parentTop = parent === window.document.documentElement ? 0 : parent.getBoundingClientRect().top;
const elementTop = this.internalComponentRef.nativeElement.getBoundingClientRect().top;
const parentScrollTop = parent.scrollTop;
const answer = elementTop - parentTop + parentScrollTop;

parent.scrollTo({
top: answer - 30,
behavior: 'smooth'
});
}

isRequired(): boolean {
Expand Down Expand Up @@ -242,7 +273,9 @@ export class FormElementComponent implements AfterViewInit {
}

public closePopup(): void {
const prevState = this.popupState;
this.popupState = 'onHover';
this.setUpErrorTooltipListeners(prevState, this.popupState);
}

public togglePopup(): void {
Expand All @@ -252,11 +285,13 @@ export class FormElementComponent implements AfterViewInit {
if (this.errorFullyVisible) {
return;
}
const prevState = this.popupState;
if (this.popupState === 'lockedOpen') {
this.popupState = 'onHover';
} else {
this.popupState = 'lockedOpen';
}
this.setUpErrorTooltipListeners(prevState, this.popupState);
}

public setErrorTooltipOffset = (): void => {
Expand All @@ -268,4 +303,8 @@ export class FormElementComponent implements AfterViewInit {
this.fixedWrapper.nativeElement.style.transform = `translateY(${popupOffsetY}px)`;
}
};

ngOnDestroy(): void {
this.subscriptions.forEach(e => e.unsubscribe());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ export class FormComponent implements OnInit, OnDestroy, OnChanges {
formGroupValue): Promise<any>
{
if (this.formGroup.invalid) {
this.activeControls.find((e) => !e.formControl.valid)?.formElement?.scrollTo();
this.activeControls.find((e) => e.formControl.invalid)?.formElement?.scrollTo();
this.setDisabledStatesForAllControls(originalDisabledStates);
return Promise.reject(invalidFieldsSymbol);
} else {
Expand Down
Loading

0 comments on commit f506986

Please sign in to comment.