UNPKG

ng-halfmoon

Version:

Angular Library to build upon the halfmoon-framework

668 lines (647 loc) 21.7 kB
import { Directive, ElementRef, Renderer2, Input, Injectable, RendererFactory2, Optional, Self, HostBinding, HostListener, Component, Injector, ContentChild, EventEmitter, InjectionToken, ApplicationRef, ComponentFactoryResolver, NgModule } from '@angular/core'; import * as halfmoon from 'halfmoon'; import { BehaviorSubject, Subject } from 'rxjs'; import { NgControl } from '@angular/forms'; import { distinctUntilChanged } from 'rxjs/operators'; import { CommonModule } from '@angular/common'; class Applier { constructor(el, renderer, baseClass) { this.el = el; this.renderer = renderer; this.baseClass = baseClass; } applyChange(change, el) { if (Applier.hasValueChanged(change)) { return; } if (!change.isFirstChange()) { this.removeClass(`${this.baseClass}-${change.previousValue}`, el); } this.addClass(`${this.baseClass}-${change.currentValue}`, el); } static hasValueChanged(change) { return !change.currentValue || change.previousValue === change.currentValue; } addClass(cls, el) { this.renderer.addClass(el.nativeElement, cls); } removeClass(cls, el) { this.renderer.removeClass(el.nativeElement, cls); } applyAttribute(change, attribute) { if (change.previousValue === change.currentValue) { return; } if (!change.currentValue) { this.renderer.removeAttribute(this.el.nativeElement, attribute); return; } this.renderer.setAttribute(this.el.nativeElement, attribute, ''); } } class ButtonDirective extends Applier { constructor(el, renderer) { super(el, renderer, 'btn'); this.addClass('btn', this.el); } ngOnChanges(changes) { if (changes.appearance) { this.applyChange(changes.appearance, this.el); } if (changes.size) { this.applyChange(changes.size, this.el); } if (changes.shape) { this.applyChange(changes.shape, this.el); } if (changes.disabled) { this.applyDisabled(changes.disabled); this.applyAttribute(changes.disabled, 'disabled'); } } applyDisabled(change) { if (change.previousValue === change.currentValue) { return; } if (!change.currentValue) { this.removeClass('disabled', this.el); return; } this.addClass('disabled', this.el); } } ButtonDirective.decorators = [ { type: Directive, args: [{ selector: '[hmButton]' },] } ]; ButtonDirective.ctorParameters = () => [ { type: ElementRef }, { type: Renderer2 } ]; ButtonDirective.propDecorators = { appearance: [{ type: Input }], size: [{ type: Input }], shape: [{ type: Input }], disabled: [{ type: Input }] }; class DarkModeService { constructor(rendererFactory) { this.darkModeOn = false; this._darkModeEnabled$ = new BehaviorSubject(false); this.cookieName = 'halfmoon_preferredMode'; this.renderer = rendererFactory.createRenderer(document.body, null); const cookie = halfmoon.readCookie(this.cookieName); if (cookie === 'light-mode') { this.darkModeOn = false; this._darkModeEnabled$.next(this.darkModeOn); } else if (cookie === 'dark-mode') { this.darkModeOn = true; this._darkModeEnabled$.next(this.darkModeOn); } this.toggleClass(); } get darkModeEnabled$() { return this._darkModeEnabled$.asObservable(); } toggleDarkMode() { this.darkModeOn = !this.darkModeOn; this._darkModeEnabled$.next(this.darkModeOn); this.toggleCookie(); this.toggleClass(); } toggleCookie() { halfmoon.createCookie(this.cookieName, this.darkModeOn ? 'dark-mode' : 'light-mode', 365); } toggleClass() { if (this.darkModeOn) { this.renderer.addClass(document.body, 'dark-mode'); } else { this.renderer.removeClass(document.body, 'dark-mode'); } } } DarkModeService.decorators = [ { type: Injectable } ]; DarkModeService.ctorParameters = () => [ { type: RendererFactory2 } ]; /** * This logic with managing the control-status is adopted from Clarity Design. All props go to their team and contributors */ var ControlStatus; (function (ControlStatus) { ControlStatus["NONE"] = "NONE"; ControlStatus["INVALID"] = "INVALID"; ControlStatus["VALID"] = "VALID"; })(ControlStatus || (ControlStatus = {})); class ControlService { constructor() { this._currentStatus$ = new Subject(); } get currentStatus$() { return this._currentStatus$.asObservable(); } ngOnDestroy() { var _a; (_a = this.subscription) === null || _a === void 0 ? void 0 : _a.unsubscribe(); } init(control) { var _a; this.currentControl = control; this.subscription = (_a = this.currentControl.statusChanges) === null || _a === void 0 ? void 0 : _a.pipe(distinctUntilChanged()).subscribe(() => { this.triggerStatusChange(); }); } triggerStatusChange() { if ((this.currentControl.touched || this.currentControl.dirty) && [ControlStatus.INVALID, ControlStatus.VALID].includes(this.currentControl.status)) { this._currentStatus$.next(this.currentControl.status); } else { this._currentStatus$.next(ControlStatus.NONE); } } } ControlService.decorators = [ { type: Injectable } ]; ControlService.ctorParameters = () => []; class InputDirective extends Applier { constructor(ngControl, controlService, el, renderer) { super(el, renderer, 'form-control'); this.ngControl = ngControl; this.controlService = controlService; this.sizing = undefined; this.isInvalid = false; this.addClass('form-control', this.el); } ngOnInit() { this.setupControlService(); } ngOnChanges(changes) { if (changes.sizing) { this.applyChange(changes.sizing, this.el); } } ngDoCheck() { if (!this.controlService && this.ngControl) { this.isInvalid = !!(this.ngControl.invalid && (this.ngControl.touched || this.ngControl.dirty)); } } ngOnDestroy() { if (!this.subscription) { return; } this.subscription.unsubscribe(); } changeStatus() { if (!this.controlService) { return; } this.controlService.triggerStatusChange(); } setupControlService() { if (!this.controlService) { return; } this.controlService.init(this.ngControl.control); this.subscription = this.controlService.currentStatus$.subscribe((value) => { this.isInvalid = value === ControlStatus.INVALID; }); } } InputDirective.decorators = [ { type: Directive, args: [{ selector: '[hmInput]' },] } ]; InputDirective.ctorParameters = () => [ { type: NgControl, decorators: [{ type: Optional }, { type: Self }] }, { type: ControlService, decorators: [{ type: Optional }] }, { type: ElementRef }, { type: Renderer2 } ]; InputDirective.propDecorators = { sizing: [{ type: Input }], isInvalid: [{ type: HostBinding, args: ['class.is-invalid',] }], changeStatus: [{ type: HostListener, args: ['blur',] }] }; class SelectDirective extends Applier { constructor(ngControl, controlService, el, renderer) { super(el, renderer, 'form-control'); this.ngControl = ngControl; this.controlService = controlService; this.sizing = undefined; this.isInvalid = false; this.addClass('form-control', this.el); } ngOnInit() { this.setupControlService(); } ngOnChanges(changes) { if (changes.sizing) { this.applyChange(changes.sizing, this.el); } } ngDoCheck() { if (this.ngControl) { this.isInvalid = !!(this.ngControl.invalid && (this.ngControl.touched || this.ngControl.dirty)); } } ngOnDestroy() { if (!this.subscription) { return; } this.subscription.unsubscribe(); } changeStatus() { if (!this.controlService) { return; } this.controlService.triggerStatusChange(); } setupControlService() { if (!this.controlService) { return; } this.controlService.init(this.ngControl.control); this.subscription = this.controlService.currentStatus$.subscribe((value) => { this.isInvalid = value === ControlStatus.INVALID; }); } } SelectDirective.decorators = [ { type: Directive, args: [{ selector: '[hmSelect]' },] } ]; SelectDirective.ctorParameters = () => [ { type: NgControl, decorators: [{ type: Optional }, { type: Self }] }, { type: ControlService, decorators: [{ type: Optional }] }, { type: ElementRef }, { type: Renderer2 } ]; SelectDirective.propDecorators = { sizing: [{ type: Input }], isInvalid: [{ type: HostBinding, args: ['class.is-invalid',] }], changeStatus: [{ type: HostListener, args: ['blur',] }] }; class HintComponent { constructor() { this.baseClass = true; } } HintComponent.decorators = [ { type: Component, args: [{ selector: 'hm-hint', template: "<ng-content></ng-content>\n", styles: [""] },] } ]; HintComponent.propDecorators = { baseClass: [{ type: HostBinding, args: ['class.form-text',] }] }; class ErrorComponent { constructor() { this.baseClass = true; } } ErrorComponent.decorators = [ { type: Component, args: [{ selector: 'hm-error', template: "<ng-content></ng-content>\n", styles: [":host{display:block}"] },] } ]; ErrorComponent.propDecorators = { baseClass: [{ type: HostBinding, args: ['class.invalid-feedback',] }] }; class FormGroupDirective { constructor(el, renderer, injector) { this.el = el; this.renderer = renderer; this.injector = injector; this.baseClass = true; this.isInvalid = false; this.controlService = injector.get(ControlService); this.subscription = this.controlService.currentStatus$.subscribe((status) => { this.isInvalid = status === ControlStatus.INVALID; }); } ngOnInit() { this.controlLabel = this.el.nativeElement.firstChild; } ngOnDestroy() { this.subscription.unsubscribe(); } updateLabelPostfix(isRequired) { if (isRequired && !this.controlLabel.classList.contains('required')) { this.renderer.addClass(this.controlLabel, 'required'); } else if (!isRequired && this.controlLabel.classList.contains('required')) { this.renderer.removeClass(this.controlLabel, 'required'); } } get canShowError() { return this.isInvalid; } } FormGroupDirective.decorators = [ { type: Directive } ]; FormGroupDirective.ctorParameters = () => [ { type: ElementRef }, { type: Renderer2 }, { type: Injector } ]; FormGroupDirective.propDecorators = { baseClass: [{ type: HostBinding, args: ['class.form-group',] }], isInvalid: [{ type: HostBinding, args: ['class.is-invalid',] }], input: [{ type: ContentChild, args: [InputDirective,] }] }; class InputContainerComponent extends FormGroupDirective { constructor(el, renderer, injector) { super(el, renderer, injector); } ngOnInit() { super.ngOnInit(); this.control = this.el.nativeElement.childNodes[2]; } ngAfterViewChecked() { this.updateLabelPostfix(this.control.required); } } InputContainerComponent.decorators = [ { type: Component, args: [{ selector: 'hm-input-container', template: "<ng-content select=\"label\"></ng-content>\n<ng-content select=\"hm-error\" *ngIf=\"canShowError\"></ng-content>\n<ng-content select=\"input\"></ng-content>\n<ng-content select=\"hm-hint\"></ng-content>\n", providers: [ControlService], styles: [":host{display:block}"] },] } ]; InputContainerComponent.ctorParameters = () => [ { type: ElementRef }, { type: Renderer2 }, { type: Injector } ]; class SelectContainerComponent extends FormGroupDirective { constructor(el, renderer, injector) { super(el, renderer, injector); } ngOnInit() { super.ngOnInit(); this.control = this.el.nativeElement.childNodes[2]; } ngAfterViewChecked() { this.updateLabelPostfix(this.control.required); } } SelectContainerComponent.decorators = [ { type: Component, args: [{ selector: 'hm-select-container', template: ` <ng-content select="label"></ng-content> <ng-content select="hm-error" *ngIf="canShowError"></ng-content> <ng-content select="select"></ng-content> <ng-content select="hm-hint"></ng-content> `, providers: [ControlService], styles: [` :host { display: block; } `] },] } ]; SelectContainerComponent.ctorParameters = () => [ { type: ElementRef }, { type: Renderer2 }, { type: Injector } ]; class AlertComponent extends Applier { constructor(el, renderer) { super(el, renderer, 'alert'); this.alertClass = true; this.dismissable = false; } ngOnInit() { this.renderer.setAttribute(this.el.nativeElement, 'role', 'alert'); } ngOnChanges(changes) { if (changes.appearance) { this.applyChange(changes.appearance, this.el); } } } AlertComponent.decorators = [ { type: Component, args: [{ selector: 'hm-alert', template: ` <button class="close" data-dismiss="alert" type="button" aria-label="Close" *ngIf="dismissable" > <span aria-hidden="true">&times;</span> </button> <ng-content select="h4"></ng-content> <ng-content></ng-content> `, styles: [` :host { display: block; } `] },] } ]; AlertComponent.ctorParameters = () => [ { type: ElementRef }, { type: Renderer2 } ]; AlertComponent.propDecorators = { alertClass: [{ type: HostBinding, args: ['class.alert',] }], appearance: [{ type: Input }], dismissable: [{ type: Input }] }; class ModalRef { constructor() { this.afterClosed = new EventEmitter(); } } ModalRef.decorators = [ { type: Injectable } ]; class ModalContainerComponent { constructor(modalRef) { this.modalRef = modalRef; this.baseClass = true; this.role = 'dialog'; this.tabindex = '-1'; this.overlayDismissal = 'true'; this.escDismissal = 'true'; this.isShown = false; } onClick(event) { if (event.target.classList.contains('modal-dialog')) { this.close(); } } onEsc() { this.close(); } close() { this.modalRef.close(); } } ModalContainerComponent.decorators = [ { type: Component, args: [{ selector: 'hm-modal-container', template: ` <div class="modal-dialog" role="document"> <div class="modal-content"> <button class="close" type="button" aria-label="Close" *ngIf="defaultDismiss" (click)="close()" > <span aria-hidden="true">&times;</span> </button> <ng-content></ng-content> </div> </div> ` },] } ]; ModalContainerComponent.ctorParameters = () => [ { type: ModalRef } ]; ModalContainerComponent.propDecorators = { baseClass: [{ type: HostBinding, args: ['class.modal',] }], role: [{ type: HostBinding, args: ['attr.role',] }], tabindex: [{ type: HostBinding, args: ['attr.tabindex',] }], overlayDismissal: [{ type: HostBinding, args: ['attr.data-overlay-dismissal-disabled',] }], escDismissal: [{ type: HostBinding, args: ['attr.data-esc-dismissal-disabled',] }], isShown: [{ type: HostBinding, args: ['class.show',] }], id: [{ type: HostBinding, args: ['attr.id',] }], onClick: [{ type: HostListener, args: ['click', ['$event'],] }], onEsc: [{ type: HostListener, args: ['window:keydown.esc',] }] }; const MODAL_DATA = new InjectionToken('modalData'); class ModalService { constructor(_applicationRef, _componentFactoryResolver, _injector) { this._applicationRef = _applicationRef; this._componentFactoryResolver = _componentFactoryResolver; this._injector = _injector; this.counter = 0; this.body = 'body'; } createModal(component, config) { var _a, _b, _c; this.modalRef = new ModalRef(); this.contentRef = this.createContent(component, config); this._applicationRef.attachView(this.contentRef.hostView); this.containerRef = this.createContainer(); this.containerRef.instance.id = (_a = config === null || config === void 0 ? void 0 : config.id) !== null && _a !== void 0 ? _a : `modal-${this.counter}`; this.containerRef.instance.defaultDismiss = (_b = config === null || config === void 0 ? void 0 : config.defaultDismiss) !== null && _b !== void 0 ? _b : false; this.modalRef.modalId = (_c = config === null || config === void 0 ? void 0 : config.id) !== null && _c !== void 0 ? _c : `modal-${this.counter}`; this.modalRef.close = (data) => { this.containerRef.instance.isShown = false; // timeout to not interrupt the closing animation setTimeout(() => { this.removeModal(); this.modalRef.afterClosed.emit(data); }, 100); }; this._applicationRef.attachView(this.containerRef.hostView); const selectedElement = document.querySelector(this.body); selectedElement === null || selectedElement === void 0 ? void 0 : selectedElement.appendChild(this.containerRef.location.nativeElement); // Trigger toggle a 1ms later to achieve the correct animation setTimeout(() => { this.containerRef.instance.isShown = true; }, 1); this.counter++; return this.modalRef; } createContainer() { const factory = this._componentFactoryResolver.resolveComponentFactory(ModalContainerComponent); const injector = Injector.create({ parent: this._injector, providers: [{ provide: ModalRef, useValue: this.modalRef }] }); return factory.create(injector, [[this.contentRef.location.nativeElement]]); } createContent(component, config) { const contentCmptFactory = this._componentFactoryResolver.resolveComponentFactory(component); const modalContentInjector = Injector.create({ providers: [ { provide: ModalRef, useValue: this.modalRef }, { provide: MODAL_DATA, useValue: config === null || config === void 0 ? void 0 : config.data } ], parent: this._injector }); return contentCmptFactory.create(modalContentInjector); } removeModal() { this.contentRef.destroy(); this.containerRef.destroy(); } } ModalService.decorators = [ { type: Injectable } ]; ModalService.ctorParameters = () => [ { type: ApplicationRef }, { type: ComponentFactoryResolver }, { type: Injector } ]; class ModalConfig { } class NgHalfmoonModule { } NgHalfmoonModule.decorators = [ { type: NgModule, args: [{ declarations: [ ButtonDirective, InputDirective, SelectDirective, HintComponent, ErrorComponent, InputContainerComponent, SelectContainerComponent, AlertComponent, ModalContainerComponent ], providers: [DarkModeService, ModalService], imports: [CommonModule], exports: [ ButtonDirective, InputDirective, SelectDirective, HintComponent, ErrorComponent, InputContainerComponent, SelectContainerComponent, AlertComponent, ModalContainerComponent ] },] } ]; /* * Public API Surface of ng-halfmoon */ /** * Generated bundle index. Do not edit. */ export { AlertComponent, ButtonDirective, DarkModeService, ErrorComponent, HintComponent, InputContainerComponent, InputDirective, MODAL_DATA, ModalConfig, ModalContainerComponent, ModalRef, ModalService, NgHalfmoonModule, SelectContainerComponent, SelectDirective, ButtonDirective as ɵa, InputDirective as ɵb, SelectDirective as ɵc, HintComponent as ɵd, ErrorComponent as ɵe, InputContainerComponent as ɵf, SelectContainerComponent as ɵg, AlertComponent as ɵh, ModalContainerComponent as ɵi, DarkModeService as ɵj, ModalService as ɵk }; //# sourceMappingURL=ng-halfmoon.js.map