-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
55 changed files
with
2,640 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"lib": { | ||
"entryFile": "src/index.ts" | ||
} | ||
} |
105 changes: 105 additions & 0 deletions
105
packages/components-angular/common/src/directives/error.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/* eslint-disable @angular-eslint/directive-class-suffix */ | ||
import { AfterViewInit, ChangeDetectorRef, Directive, HostBinding, Inject, Injector, Input } from '@angular/core' | ||
import { AbstractControl, ControlContainer } from '@angular/forms' | ||
import { BehaviorSubject } from 'rxjs' | ||
|
||
import type { BaloiseDesignSystemAngularConfig } from '../utils/config' | ||
import { raf } from '../utils/utils' | ||
import { BalTokenConfig } from '../utils/token' | ||
|
||
// @Component({ | ||
// selector: 'bal-ng-error', | ||
// template: `<ng-content *ngIf="(ready | async) && hasError"></ng-content>`, | ||
// styles: [ | ||
// ` | ||
// :host { | ||
// display: inline-block; | ||
// } | ||
// `, | ||
// ], | ||
// }) | ||
|
||
@Directive({ | ||
selector: 'bal-ng-error', | ||
}) | ||
export class BalNgErrorComponent implements AfterViewInit { | ||
/** | ||
* The name of form validator to show. | ||
*/ | ||
@Input() error?: string | ||
|
||
/** | ||
* The name of the form control, which is registered in the form group. | ||
*/ | ||
@HostBinding('attr.controlname') | ||
@Input() | ||
controlName?: string | ||
|
||
constructor( | ||
@Inject(Injector) protected injector: Injector, | ||
@Inject(ChangeDetectorRef) protected cd: ChangeDetectorRef, | ||
) {} | ||
|
||
private controlContainer?: ControlContainer | ||
private control?: AbstractControl | null | ||
private config?: BaloiseDesignSystemAngularConfig | ||
private invalidateOn: 'dirty' | 'touched' = 'touched' | ||
ready = new BehaviorSubject(false) | ||
|
||
ngAfterViewInit(): void { | ||
raf(() => { | ||
try { | ||
this.controlContainer = this.injector.get<ControlContainer>(ControlContainer) | ||
} catch { | ||
/* No ControlContainer provided */ | ||
} | ||
|
||
if (!this.controlContainer) { | ||
return | ||
} | ||
|
||
try { | ||
this.config = this.injector.get<BaloiseDesignSystemAngularConfig>(BalTokenConfig) | ||
} catch { | ||
/* No config provided */ | ||
} | ||
|
||
this.invalidateOn = this.config?.forms?.invalidateOn || this.invalidateOn | ||
|
||
if (this.controlName) { | ||
this.control = this.controlContainer.control?.get(this.controlName) | ||
if (!this.control) { | ||
console.warn('[BalNgErrorComponent] Could not find the given controlName in the form control container') | ||
} else { | ||
this.ready.next(true) | ||
this.cd.detectChanges() | ||
} | ||
} else { | ||
console.warn('[BalNgErrorComponent] Please provide a controlName') | ||
} | ||
}) | ||
} | ||
|
||
get hasError(): boolean { | ||
if (this.controlName && this.controlContainer && this.config && this.control) { | ||
if (!this.control[this.invalidateOn]) { | ||
return false | ||
} | ||
|
||
if (!this.error) { | ||
return this.control.invalid | ||
} | ||
|
||
if (this.control.errors) { | ||
const validationErrorKeys = Object.keys(this.control.errors).filter(k => k !== 'errorType') | ||
const hasValidationErrors = validationErrorKeys.length > 0 | ||
|
||
if (hasValidationErrors) { | ||
return validationErrorKeys[0] === this.error // isFirstKeyOurError | ||
} | ||
} | ||
} | ||
|
||
return false | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
packages/components-angular/common/src/directives/focus.directive.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/* eslint-disable @angular-eslint/directive-class-suffix */ | ||
import { AfterViewInit, Directive, ElementRef } from '@angular/core' | ||
|
||
@Directive({ | ||
selector: '[balAutoFocus]', | ||
}) | ||
export class BalAutoFocus implements AfterViewInit { | ||
constructor(protected elementRef: ElementRef) {} | ||
|
||
ngAfterViewInit() { | ||
this.setFocus() | ||
} | ||
|
||
setFocus() { | ||
const el = this.elementRef.nativeElement | ||
if (el) { | ||
if (el.setFocus) { | ||
el.setFocus() | ||
} else { | ||
el.focus() | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
export type { BaloiseDesignSystemAngularConfig } from './utils/config' | ||
export type { ProxyComponent } from './utils/utils' | ||
|
||
export { AngularDelegate } from './providers/angular-delegate' | ||
export { BalBreakpointsService } from './providers/breakpoints.service' | ||
export { BalConfigService } from './providers/config.service' | ||
export { BalModalService } from './providers/modal.service' | ||
export { BalOrientationService } from './providers/orientation.service' | ||
export { BalSnackbarService } from './providers/snackbar.service' | ||
export { BalToastService } from './providers/toast.service' | ||
|
||
export { BalNgErrorComponent } from './directives/error.component' | ||
export { BalAutoFocus } from './directives/focus.directive' | ||
|
||
export { | ||
BalTokenUserConfig, | ||
BalTokenConfig, | ||
BalTokenToast, | ||
BalTokenSnackbar, | ||
BalTokenModal, | ||
BalTokenBreakpoints, | ||
BalTokenBreakpointSubject, | ||
BalTokenDevice, | ||
BalTokenOrientationSubject, | ||
} from './utils/token' | ||
|
||
export { raf, parseCustomEvent, element } from './utils/utils' |
108 changes: 108 additions & 0 deletions
108
packages/components-angular/common/src/providers/angular-delegate.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import { ApplicationRef, ComponentFactoryResolver, Injectable, Injector, NgZone, ViewContainerRef } from '@angular/core' | ||
import type { FrameworkDelegate } from '@baloise/ds-components' | ||
|
||
@Injectable() | ||
export class AngularDelegate { | ||
constructor( | ||
private zone: NgZone, | ||
private appRef: ApplicationRef, | ||
) {} | ||
|
||
create(resolver: ComponentFactoryResolver, injector: Injector, location?: ViewContainerRef) { | ||
return new AngularFrameworkDelegate(resolver, injector, location, this.appRef, this.zone) | ||
} | ||
} | ||
|
||
export class AngularFrameworkDelegate implements FrameworkDelegate { | ||
private elRefMap = new WeakMap<HTMLElement, any>() | ||
private elEventsMap = new WeakMap<HTMLElement, () => void>() | ||
|
||
constructor( | ||
private resolver: ComponentFactoryResolver, | ||
private injector: Injector, | ||
private location: ViewContainerRef | undefined, | ||
private appRef: ApplicationRef, | ||
private zone: NgZone, | ||
) {} | ||
|
||
attachViewToDom(container: any, component: any, params?: any, cssClasses?: string[]): Promise<any> { | ||
return this.zone.run(() => { | ||
return new Promise(resolve => { | ||
const el = attachView( | ||
this.zone, | ||
this.resolver, | ||
this.injector, | ||
this.location, | ||
this.appRef, | ||
this.elRefMap, | ||
this.elEventsMap, | ||
container, | ||
component, | ||
params, | ||
cssClasses, | ||
) | ||
resolve(el) | ||
}) | ||
}) | ||
} | ||
|
||
removeViewFromDom(_container: any, component: any): Promise<void> { | ||
return this.zone.run(() => { | ||
return new Promise(resolve => { | ||
const componentRef = this.elRefMap.get(component) | ||
if (componentRef) { | ||
componentRef.destroy() | ||
this.elRefMap.delete(component) | ||
const unbindEvents = this.elEventsMap.get(component) | ||
if (unbindEvents) { | ||
unbindEvents() | ||
this.elEventsMap.delete(component) | ||
} | ||
} | ||
resolve() | ||
}) | ||
}) | ||
} | ||
} | ||
|
||
export const attachView = ( | ||
_zone: NgZone, | ||
resolver: ComponentFactoryResolver, | ||
injector: Injector, | ||
location: ViewContainerRef | undefined, | ||
appRef: ApplicationRef, | ||
elRefMap: WeakMap<HTMLElement, any>, | ||
_elEventsMap: WeakMap<HTMLElement, () => void>, | ||
container: any, | ||
component: any, | ||
params: any, | ||
cssClasses: string[] | undefined, | ||
) => { | ||
const factory = resolver.resolveComponentFactory(component) | ||
const childInjector = Injector.create({ | ||
providers: [], | ||
parent: injector, | ||
}) | ||
const componentRef = location | ||
? location.createComponent(factory, location.length, childInjector) | ||
: factory.create(childInjector) | ||
|
||
const instance = componentRef.instance | ||
const hostElement = componentRef.location.nativeElement | ||
if (params) { | ||
Object.assign(instance as any, params) | ||
} | ||
if (cssClasses) { | ||
for (const clazz of cssClasses) { | ||
hostElement.classList.add(clazz) | ||
} | ||
} | ||
container.appendChild(hostElement) | ||
|
||
if (!location) { | ||
appRef.attachView(componentRef.hostView) | ||
} | ||
componentRef.changeDetectorRef.reattach() | ||
elRefMap.set(hostElement, componentRef) | ||
return hostElement | ||
} |
63 changes: 63 additions & 0 deletions
63
packages/components-angular/common/src/providers/breakpoints.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { ApplicationRef, Inject, Injectable } from '@angular/core' | ||
import { BehaviorSubject, Observable } from 'rxjs' | ||
import { map } from 'rxjs/operators' | ||
|
||
import type { | ||
BalBreakpointObserver, | ||
BalBreakpoints, | ||
BalBreakpointSubject, | ||
BalBreakpointsUtil, | ||
} from '@baloise/ds-components' | ||
|
||
import { BalTokenBreakpointSubject, BalTokenBreakpoints } from '../utils/token' | ||
|
||
@Injectable({ | ||
providedIn: 'root', | ||
}) | ||
export class BalBreakpointsService implements BalBreakpointObserver { | ||
private _breakpoints$!: BehaviorSubject<BalBreakpoints> | ||
|
||
state$: Observable<BalBreakpoints> | ||
mobile$: Observable<boolean> | ||
tablet$: Observable<boolean> | ||
touch$: Observable<boolean> | ||
desktop$: Observable<boolean> | ||
highDefinition$: Observable<boolean> | ||
widescreen$: Observable<boolean> | ||
fullhd$: Observable<boolean> | ||
|
||
constructor( | ||
private app: ApplicationRef, | ||
@Inject(BalTokenBreakpoints) private breakpoints: BalBreakpointsUtil, | ||
@Inject(BalTokenBreakpointSubject) private breakpointSubject: BalBreakpointSubject, | ||
) { | ||
this._breakpoints$ = new BehaviorSubject<BalBreakpoints>(this.breakpoints.toObject()) | ||
|
||
this.state$ = this._breakpoints$.asObservable() | ||
this.mobile$ = this._breakpoints$.asObservable().pipe(map(breakpoints => breakpoints.mobile)) | ||
this.tablet$ = this._breakpoints$.asObservable().pipe(map(breakpoints => breakpoints.tablet)) | ||
this.touch$ = this._breakpoints$.asObservable().pipe(map(breakpoints => breakpoints.touch)) | ||
this.desktop$ = this._breakpoints$.asObservable().pipe(map(breakpoints => breakpoints.desktop)) | ||
this.highDefinition$ = this._breakpoints$.asObservable().pipe(map(breakpoints => breakpoints.highDefinition)) | ||
this.widescreen$ = this._breakpoints$.asObservable().pipe(map(breakpoints => breakpoints.widescreen)) | ||
this.fullhd$ = this._breakpoints$.asObservable().pipe(map(breakpoints => breakpoints.fullhd)) | ||
|
||
this.breakpointSubject.attach(this) | ||
} | ||
|
||
breakpointListener(breakpoints: BalBreakpoints): void { | ||
this._breakpoints$.next(breakpoints) | ||
this.app.tick() | ||
} | ||
|
||
ngOnDestroy() { | ||
this.breakpointSubject.detach(this) | ||
} | ||
|
||
get value(): BalBreakpoints { | ||
if (this._breakpoints$) { | ||
return this._breakpoints$.getValue() | ||
} | ||
return this.breakpoints.toObject() | ||
} | ||
} |
Oops, something went wrong.