ng-halfmoon
Version:
Angular Library to build upon the halfmoon-framework
668 lines (647 loc) • 21.7 kB
JavaScript
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">×</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">×</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