UNPKG

@ngx-kit/core

Version:

ngx-kit - core module

1,823 lines (1,790 loc) 246 kB
import { HttpClient, HttpClientModule } from '@angular/common/http'; import { NG_VALUE_ACCESSOR, NgControl, ControlContainer, FormGroup } from '@angular/forms'; import { NavigationEnd, NavigationStart, Router } from '@angular/router'; import { animate, style, transition, trigger } from '@angular/animations'; import { BehaviorSubject as BehaviorSubject$1 } from 'rxjs/internal/BehaviorSubject'; import { EventManager, HammerGestureConfig } from '@angular/platform-browser'; import { Subject, BehaviorSubject, from, Observable, fromEvent, merge, timer } from 'rxjs'; import { takeUntil, take, filter, map, tap, mapTo, switchMap, debounce, debounceTime, distinctUntilChanged } from 'rxjs/operators'; import { CommonModule, isPlatformBrowser, isPlatformServer, DOCUMENT } from '@angular/common'; import { Directive, ElementRef, Input, NgModule, Injectable, IterableDiffers, Renderer2, forwardRef, Host, HostListener, ChangeDetectorRef, TemplateRef, ViewContainerRef, KeyValueDiffers, Optional, Inject, PLATFORM_ID, ChangeDetectionStrategy, Component, NgZone, SimpleChange, InjectionToken, EventEmitter, Output, Self, ContentChild, HostBinding, defineInjectable, inject, Injector, ComponentFactoryResolver, SkipSelf, INJECTOR } from '@angular/core'; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Returns a reference to `elementRef` of any element. * * If you define a template reference on the element that Angular views as a host element of the component, you will * get a reference to the component instance. `kitAnchor` resolves the problem. * * * ### Usage * * ```html * <any-component kitAnchor #anchor="anchor"></any-component> * <some-other-component [anchor]="anchor"></some-other-component> * ``` */ class KitAnchorDirective { /** * @param {?} _elementRef */ constructor(_elementRef) { this._elementRef = _elementRef; } /** * Get reference to anchored element. * @return {?} */ get elementRef() { return this._elementRef; } /** * Get anchored html-element. * @return {?} */ get nativeEl() { return this._elementRef.nativeElement; } } KitAnchorDirective.decorators = [ { type: Directive, args: [{ selector: '[kitAnchor]', exportAs: 'anchor', },] } ]; /** @nocollapse */ KitAnchorDirective.ctorParameters = () => [ { type: ElementRef } ]; KitAnchorDirective.propDecorators = { kitAnchor: [{ type: Input }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class KitAnchorModule { } KitAnchorModule.decorators = [ { type: NgModule, args: [{ imports: [ CommonModule, ], declarations: [ KitAnchorDirective, ], exports: [ KitAnchorDirective, ], },] } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @param {?} x * @return {?} */ function isString(x) { return typeof x === 'string'; } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Apply classes to an element. * * Must be provided on a component or directive. * * * ### Usage * * ```typescript * constructor(private kitClass: KitClassService) {} * ... * this.kitClass.apply({color: 'red', active: true, primary: false}); * ``` * * Adds to element: `class="color-red active"` * * * ### Example * * * collection:button - [sources](https://github.com/ngx-kit/ngx-kit/tree/master/packages/collection/lib/ui-button), * [demo](http://ngx-kit.com/collection/module/ui-button) */ class KitClassService { /** * @param {?} renderer * @param {?} el * @param {?} differs */ constructor(renderer, el, differs) { this.renderer = renderer; this.el = el; this.differs = differs; this._state = {}; } /** * Override class declaration state. * @param {?} setterRaw * @return {?} */ set state(setterRaw) { /** @type {?} */ const newState = Object.assign({}, setterRaw); this.process(newState); } /** * Merge to class declaration state. * @param {?} setter * @return {?} */ apply(setter) { /** @type {?} */ const newState = Object.assign({}, this._state, setter); this.process(newState); } /** * @private * @param {?} newState * @return {?} */ process(newState) { /** @type {?} */ const classList = this.processObj(newState); if (!this._differ) { this._differ = this.differs.find(classList).create(); } /** @type {?} */ const changes = this._differ.diff(classList); if (changes) { this.applyChanges(changes); this._state = newState; } } /** * @private * @param {?} changes * @return {?} */ applyChanges(changes) { changes.forEachRemovedItem((/** * @param {?} record * @return {?} */ (record) => this.renderer.removeClass(this.el.nativeElement, record.item))); changes.forEachAddedItem((/** * @param {?} record * @return {?} */ (record) => this.renderer.addClass(this.el.nativeElement, record.item))); } /** * @private * @param {?} obj * @return {?} */ processObj(obj) { return Object.keys(obj) .map((/** * @param {?} key * @return {?} */ (key) => { /** @type {?} */ const value = obj[key]; return isString(value) ? `${key}-${value}` : !!value ? key : null; })) .filter(isString); } } KitClassService.decorators = [ { type: Injectable } ]; /** @nocollapse */ KitClassService.ctorParameters = () => [ { type: Renderer2 }, { type: ElementRef }, { type: IterableDiffers } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @param {?} val * @return {?} */ function isDefined(val) { return val !== null && val !== undefined; } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ const KIT_CHECK_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef((/** * @return {?} */ () => KitCheckDirective)), multi: true, }; /** * Adds to any element ValueAccessor and checkbox/radio behavior. * * When is checked - adds class "checked" to the element. * * For a value changing the directive listen click event. Directive implements `ControlValueAccessor` interface and * changes `ngModel` value on click event. * * * ### Usage * * #### Emit checkboxes behavior: * * ```html * <button kitCheck [(ngModel)]="buttonModel1">Checkbox button 1</button> * <button kitCheck [(ngModel)]="buttonModel2">Checkbox button 2</button> * <button kitCheck [(ngModel)]="buttonModel3">Checkbox button 3</button> * ``` * * #### Emit radio behavior * * ```html * <button kitCheck [(ngModel)]="buttonModel" [value]="1">Radio button 1</button> * <button kitCheck [(ngModel)]="buttonModel" [value]="2">Radio button 2</button> * <button kitCheck [(ngModel)]="buttonModel" [value]="3">Radio button 3</button> * ``` * * * ### Example * * * collection:button - [sources](https://github.com/ngx-kit/ngx-kit/tree/master/packages/collection/lib/ui-button), * [demo](http://ngx-kit.com/collection/module/ui-button) */ class KitCheckDirective { /** * @param {?} kitClass */ constructor(kitClass) { this.kitClass = kitClass; /** * Class applied when active. */ this.checkedClass = 'checked'; this.changes = new Subject(); this.touches = new Subject(); } /** * @param {?} changes * @return {?} */ ngOnChanges(changes) { } /** * Listen to mouse clicks on element. * * \@internal * @return {?} */ clickListener() { if (isDefined(this.value)) { // radio-mode this.checked = true; this.changes.next(this.value); } else { // checkbox-mode this.checked = !this.checked; this.changes.next(this.checked); } this.touches.next(); this.applyClass(); } /** * \@internal * @param {?} fn * @return {?} */ registerOnChange(fn) { this.changes.subscribe(fn); } /** * \@internal * @param {?} fn * @return {?} */ registerOnTouched(fn) { this.touches.subscribe(fn); } /** * \@internal * @param {?} isDisabled * @return {?} */ setDisabledState(isDisabled) { this.disabled = isDisabled; } /** * \@internal * @param {?} value * @return {?} */ writeValue(value) { if (isDefined(this.value)) { // radio-mode this.checked = this.value === value; } else { // checkbox-mode this.checked = value; } this.applyClass(); } /** * @private * @return {?} */ applyClass() { this.kitClass.apply({ checked: this.checked, }); } } KitCheckDirective.decorators = [ { type: Directive, args: [{ selector: '[kitCheck]', providers: [ KIT_CHECK_VALUE_ACCESSOR, KitClassService, ], },] } ]; /** @nocollapse */ KitCheckDirective.ctorParameters = () => [ { type: KitClassService, decorators: [{ type: Host }] } ]; KitCheckDirective.propDecorators = { checkedClass: [{ type: Input }], kitCheck: [{ type: Input }], value: [{ type: Input }], clickListener: [{ type: HostListener, args: ['click',] }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class KitCheckModule { } KitCheckModule.decorators = [ { type: NgModule, args: [{ imports: [ CommonModule, ], exports: [ KitCheckDirective, ], declarations: [ KitCheckDirective, ], },] } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Provides `KitClassService` and pass input value to `KitClassService.apply` method. * * * ### Usage * * ```html * <div [kitClass]="{color: 'red', active: true, primary: false}"> * <!--<div class="color-red active">--> * ``` */ class KitClassDirective { /** * @param {?} service */ constructor(service) { this.service = service; } /** * @param {?} changes * @return {?} */ ngOnChanges(changes) { if (changes['kitClass']) { this.service.apply(this.kitClass); } } } KitClassDirective.decorators = [ { type: Directive, args: [{ selector: '[kitClass]', providers: [KitClassService], },] } ]; /** @nocollapse */ KitClassDirective.ctorParameters = () => [ { type: KitClassService } ]; KitClassDirective.propDecorators = { kitClass: [{ type: Input }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class KitClassModule { } KitClassModule.decorators = [ { type: NgModule, args: [{ declarations: [ KitClassDirective, ], exports: [ KitClassDirective, ], },] } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Controls set of collapsible items. * * Should be provided on component or directive. */ class KitCollapseHostService { constructor() { this.multiple = false; this._active = new BehaviorSubject(new Set()); this.ids = new Set(); } /** * Get `Observable` with activated items. * @return {?} */ get activeChanges() { return this._active.asObservable(); } /** * Get Set with activated items. * @return {?} */ get active() { return new Set(this._active.value); } /** * Activate item with id. * @param {?} id * @return {?} */ activate(id) { /** @type {?} */ const current = this._active.value; if (!current.has(id)) { if (this.multiple) { this._active.next(new Set(current).add(id)); } else { this._active.next(new Set().add(id)); } } } /** * Activate first registered item. * @return {?} */ activateFirst() { this.activate(this.ids.values().next().value); } /** * Add item. * @param {?} id * @return {?} */ addId(id) { this.ids.add(id); } /** * Deactivate item. * @param {?} id * @return {?} */ deactivate(id) { /** @type {?} */ const current = this._active.value; if (current.has(id)) { this._active.next(new Set(Array.from(current).filter((/** * @param {?} i * @return {?} */ i => i !== id)))); } } /** * Delete item. * @param {?} id * @return {?} */ deleteId(id) { this.ids.delete(id); } /** * Is item activated. * @param {?} id * @return {?} */ isActive(id) { /** @type {?} */ const current = this._active.value; return current.has(id); } /** * Change item activation state. * @param {?} id * @return {?} */ toggle(id) { /** @type {?} */ const current = this._active.value; if (current.has(id)) { this.deactivate(id); } else { this.activate(id); } } } KitCollapseHostService.decorators = [ { type: Injectable } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Random id generator. * * Generates GUID-like string, does not follow RFC4122. * @return {?} */ function uuid() { /** * @return {?} */ function s4() { return Math.floor((1 + Math.random()) * 0x10000) .toString(16) .substring(1); } return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Controls state of a collapsible item. * * Should be provided on component or directive. */ class KitCollapseItemService { /** * @param {?} host */ constructor(host) { this.host = host; this.id = uuid(); } /** * Is item activated. * @return {?} */ get active() { return this.host.isActive(this._id); } /** * Set activation state. * @param {?} active * @return {?} */ set active(active) { if (active) { this.host.activate(this._id); } else { this.host.deactivate(this._id); } } /** * Get item id. * @return {?} */ get id() { return this._id; } /** * Set item id. * @param {?} id * @return {?} */ set id(id) { if (id) { this.host.deleteId(this._id); this.host.addId(id); this._id = id; } else { throw new Error('id is empty'); } } /** * @return {?} */ ngOnDestroy() { this.host.deleteId(this._id); } /** * Toggle activation state. * @return {?} */ toggle() { this.host.toggle(this._id); } } KitCollapseItemService.decorators = [ { type: Injectable } ]; /** @nocollapse */ KitCollapseItemService.ctorParameters = () => [ { type: KitCollapseHostService } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Structure directive that collapsing. * * State based on `KitCollapseItemService` provided on a parent. */ class KitCollapseDirective { /** * @param {?} vcr * @param {?} template * @param {?} host * @param {?} item * @param {?} cdr */ constructor(vcr, template, host, item, cdr) { this.vcr = vcr; this.template = template; this.host = host; this.item = item; this.cdr = cdr; this.destroy = new Subject(); this.displayed = false; } /** * @return {?} */ ngOnDestroy() { this.destroy.next(); } /** * @return {?} */ ngOnInit() { this.host.activeChanges .pipe(takeUntil(this.destroy)) .subscribe((/** * @param {?} ids * @return {?} */ ids => { if (ids.has(this.item.id)) { if (!this.viewRef) { this.viewRef = this.vcr.createEmbeddedView(this.template); this.cdr.detectChanges(); } } else { if (this.viewRef) { this.vcr.clear(); this.viewRef = undefined; this.cdr.detectChanges(); } } })); } } KitCollapseDirective.decorators = [ { type: Directive, args: [{ selector: '[kitCollapse]', },] } ]; /** @nocollapse */ KitCollapseDirective.ctorParameters = () => [ { type: ViewContainerRef }, { type: TemplateRef }, { type: KitCollapseHostService }, { type: KitCollapseItemService }, { type: ChangeDetectorRef } ]; KitCollapseDirective.propDecorators = { kitCollapse: [{ type: Input }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class KitCollapseModule { } KitCollapseModule.decorators = [ { type: NgModule, args: [{ imports: [ CommonModule, ], exports: [ KitCollapseDirective, ], declarations: [ KitCollapseDirective, ], },] } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Binds CSS variable. * * ### Usage * * ```html * <div [kitCssVar]="{myBg: red}:"></div> * ``` * * Use variable in styles: * * ```css * div { * background: var(--myBg); * } * ``` */ class KitCssVarDirective { /** * @param {?} differs * @param {?} elRef * @param {?} renderer */ constructor(differs, elRef, renderer) { this.differs = differs; this.elRef = elRef; this.renderer = renderer; } /** * @return {?} */ ngDoCheck() { if (this.differ) { /** @type {?} */ const changes = this.differ.diff(this._cssVar); if (changes) { this.applyChanges(changes); } } } /** * @param {?} values * @return {?} */ set kitCssVar(values) { this._cssVar = values; if (!this.differ && values) { this.differ = this.differs.find(values).create(); } } /** * @private * @param {?} changes * @return {?} */ applyChanges(changes) { changes.forEachRemovedItem((/** * @param {?} record * @return {?} */ (record) => this.remove(record.key))); changes.forEachAddedItem((/** * @param {?} record * @return {?} */ (record) => this.set(record.key, record.currentValue))); changes.forEachChangedItem((/** * @param {?} record * @return {?} */ (record) => this.set(record.key, record.currentValue))); } /** * @private * @param {?} name * @return {?} */ remove(name) { /** @type {?} */ const styles = this.elRef.nativeElement.style; styles.removeProperty('--' + name); } /** * @private * @param {?} name * @param {?} value * @return {?} */ set(name, value) { /** @type {?} */ const styles = this.elRef.nativeElement.style; styles.setProperty('--' + name, value + ''); } } KitCssVarDirective.decorators = [ { type: Directive, args: [{ selector: '[kitCssVar]', },] } ]; /** @nocollapse */ KitCssVarDirective.ctorParameters = () => [ { type: KeyValueDiffers }, { type: ElementRef }, { type: Renderer2 } ]; KitCssVarDirective.propDecorators = { kitCssVar: [{ type: Input }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class KitCssVarModule { } KitCssVarModule.decorators = [ { type: NgModule, args: [{ imports: [ CommonModule, ], declarations: [ KitCssVarDirective, ], exports: [ KitCssVarDirective, ], },] } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Service encapsulates complex date-picker grid logic. * * * ### Example * * * collection:date-picker - * [sources](https://github.com/ngx-kit/ngx-kit/tree/master/packages/collection/lib/ui-date-picker), * [demo](https://ngx-kit.com/collection/module/ui-date-picker) */ class KitDatePickerService { /** * @param {?} renderer */ constructor(renderer) { this.renderer = renderer; this.moveHandlerUnsubs = []; this._grid = new BehaviorSubject([]); this._monthCursor = new BehaviorSubject(null); this._pick = new Subject(); } /** * Get active date. * @return {?} */ get active() { return new Date(); } /** * Set active date. * @param {?} date * @return {?} */ set active(date) { this._active = new Date(date); this._focus = new Date(date); this.updateGrid(); } /** * Observable with grid state. * @return {?} */ get gridChanges() { return this._grid.asObservable(); } /** * Observable with month cursor state. * @return {?} */ get monthCursorChanges() { return this._monthCursor.asObservable(); } /** * Observable with pick date events. * @return {?} */ get pick() { return this._pick.asObservable(); } /** * Weekdays array. * @return {?} */ get weekdays() { /** @type {?} */ const weekdays = []; /** @type {?} */ const cursor = this.startOfWeek(new Date()); for (let i = 0; i < 7; i++) { weekdays.push(new Date(cursor)); cursor.setDate(cursor.getDate() + 1); } return weekdays; } /** * @return {?} */ ngOnDestroy() { this.moveHandlerUnsubs.forEach((/** * @param {?} u * @return {?} */ u => u())); } /** * Focus date (open correspondent month). * @param {?} date * @return {?} */ focus(date) { this._focus = new Date(date); this.updateGrid(); } /** * Modify opened month. * @param {?} modifier * @return {?} */ modMonth(modifier) { this._focus.setMonth(this._focus.getMonth() + modifier); this.updateGrid(); } /** * Modify opened year. * @param {?} modifier * @return {?} */ modYear(modifier) { this._focus.setFullYear(this._focus.getFullYear() + modifier); this.updateGrid(); } /** * Handle keyboard movement. * @param {?} target * @return {?} */ handleMove(target) { if (this.renderer) { this.moveHandlerUnsubs = [ this.renderer.listen(target, 'keydown.ArrowRight', (/** * @param {?} e * @return {?} */ e => { e.preventDefault(); this._focus.setDate(this._focus.getDate() + 1); this.updateGrid(); })), this.renderer.listen(target, 'keydown.ArrowLeft', (/** * @param {?} e * @return {?} */ e => { e.preventDefault(); this._focus.setDate(this._focus.getDate() - 1); this.updateGrid(); })), this.renderer.listen(target, 'keydown.ArrowUp', (/** * @param {?} e * @return {?} */ e => { e.preventDefault(); this._focus.setDate(this._focus.getDate() - 7); this.updateGrid(); })), this.renderer.listen(target, 'keydown.ArrowDown', (/** * @param {?} e * @return {?} */ e => { e.preventDefault(); this._focus.setDate(this._focus.getDate() + 7); this.updateGrid(); })), this.renderer.listen(target, 'keydown.Home', (/** * @param {?} e * @return {?} */ e => { e.preventDefault(); this._focus.setDate(1); this.updateGrid(); })), this.renderer.listen(target, 'keydown.End', (/** * @param {?} e * @return {?} */ e => { e.preventDefault(); this._focus.setMonth(this._focus.getMonth() + 1, 0); this.updateGrid(); })), this.renderer.listen(target, 'keydown.PageUp', (/** * @param {?} e * @return {?} */ e => { e.preventDefault(); this.modMonth(-1); })), this.renderer.listen(target, 'keydown.PageDown', (/** * @param {?} e * @return {?} */ e => { e.preventDefault(); this.modMonth(1); })), this.renderer.listen(target, 'keydown.Alt.PageUp', (/** * @param {?} e * @return {?} */ e => { e.preventDefault(); this.modYear(-1); })), this.renderer.listen(target, 'keydown.Alt.PageDown', (/** * @param {?} e * @return {?} */ e => { e.preventDefault(); this.modYear(1); })), this.renderer.listen(target, 'keydown.Enter', (/** * @param {?} e * @return {?} */ e => { e.preventDefault(); this._pick.next(new Date(this._focus)); })), this.renderer.listen(target, 'keydown.Space', (/** * @param {?} e * @return {?} */ e => { e.preventDefault(); this._pick.next(new Date(this._focus)); })), ]; } } /** * Compare two dates. * @private * @param {?} x * @param {?} y * @return {?} */ isDatesEqual(x, y) { if (x && y) { // @todo improve performance: cache xp, yp /** @type {?} */ const xp = new Date(x); xp.setHours(0, 0, 0, 0); /** @type {?} */ const yp = new Date(y); yp.setHours(0, 0, 0, 0); return +xp === +yp; } else { throw new Error('isDatesEqual params error'); } } /** * Start of month of passed date. * @private * @param {?} curr * @return {?} */ startOfMonth(curr) { return new Date(curr.getFullYear(), curr.getMonth(), 1); } /** * Start of week of passed date. * @private * @param {?} curr * @return {?} */ startOfWeek(curr) { /** @type {?} */ const date = new Date(curr); /** @type {?} */ const day = date.getDay() || 7; if (day !== 1) { date.setHours(-24 * (day - 1)); } return date; } /** * Redraw grid based on monthCursor and current date. * @private * @return {?} */ updateGrid() { if (this._monthCursor.value && this.isDatesEqual(this.startOfMonth(this._focus), this.startOfMonth(this._monthCursor.value))) { // update current grid /** @type {?} */ const grid = this._grid.value; grid.forEach((/** * @param {?} r * @return {?} */ r => r.forEach((/** * @param {?} c * @return {?} */ c => { c.active = this.isDatesEqual(c.date, this._active); c.focus = this.isDatesEqual(c.date, this._focus); })))); this._grid.next(grid); } else { // recompile grid /** @type {?} */ const month = this.startOfMonth(this._focus); /** @type {?} */ const grid = []; /** @type {?} */ const cursor = this.startOfWeek(month); for (let row = 0; row < this.weeksInMonth(month); row++) { /** @type {?} */ const line = []; for (let col = 0; col < 7; col++) { /** @type {?} */ const date = new Date(cursor); line.push({ active: this.isDatesEqual(date, this._active), date, focus: this.isDatesEqual(date, this._focus), outside: date.getMonth() !== month.getMonth(), }); cursor.setDate(cursor.getDate() + 1); } grid.push(line); } this._monthCursor.next(month); this._grid.next(grid); } } /** * Calc number of weeks in month. * @private * @param {?} curr * @return {?} */ weeksInMonth(curr) { /** @type {?} */ const firstOfMonth = new Date(curr.getFullYear(), curr.getMonth(), 1); /** @type {?} */ let day = firstOfMonth.getDay() || 6; day = day === 1 ? 0 : day; if (day) { day--; } /** @type {?} */ let diff = 7 - day; /** @type {?} */ const lastOfMonth = new Date(curr.getFullYear(), curr.getMonth() + 1, 0); /** @type {?} */ const lastDate = lastOfMonth.getDate(); if (lastOfMonth.getDay() === 1) { diff--; } return Math.ceil((lastDate - diff) / 7) + 1; } } KitDatePickerService.decorators = [ { type: Injectable } ]; /** @nocollapse */ KitDatePickerService.ctorParameters = () => [ { type: Renderer2, decorators: [{ type: Optional }] } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ // Whether the current platform supports the V8 Break Iterator. The V8 check // is necessary to detect all Blink based browsers. /** @type {?} */ const hasV8BreakIterator = (typeof Intl !== 'undefined' && ((/** @type {?} */ (Intl))).v8BreakIterator); /** * Platform specific helpers. */ class KitPlatformService { /** * @param {?} platformId */ constructor(platformId) { this.platformId = platformId; } /** * Shortcut to `isPlatformBrowser` method. * @return {?} */ isBrowser() { return isPlatformBrowser(this.platformId); } /** * Shortcut to `isPlatformServer` method. * @return {?} */ isServer() { return isPlatformServer(this.platformId); } /** * Calc native scroll width. * @return {?} */ getScrollbarWidth() { if (this.isBrowser()) { if (typeof document === 'undefined') { return 0; } /** @type {?} */ const body = document.body; /** @type {?} */ const box = document.createElement('div'); /** @type {?} */ const boxStyle = box.style; /** @type {?} */ let width; // Init test div boxStyle.position = 'absolute'; boxStyle.position = boxStyle.position = '-9999px'; boxStyle.height = boxStyle.width = '100px'; boxStyle.overflow = 'scroll'; body.appendChild(box); // Calc width = box.offsetWidth - box.clientWidth; // Cleanup body.removeChild(box); return width; } else { return 0; } } /** * @return {?} */ isBrowserEdge() { return this.isBrowser() && /(edge)/i.test(navigator.userAgent); } /** * @return {?} */ isBrowserTrident() { return this.isBrowser() && /(msie|trident)/i.test(navigator.userAgent); } /** * @return {?} */ isBrowserBlink() { return this.isBrowser() && (!!(((/** @type {?} */ (window))).chrome || hasV8BreakIterator) && typeof CSS !== 'undefined' && !this.isBrowserEdge() && !this.isBrowserTrident()); } /** * @return {?} */ isBrowserWebkit() { return this.isBrowser() && /AppleWebKit/i.test(navigator.userAgent) && !this.isBrowserBlink() && !this.isBrowserEdge() && !this.isBrowserTrident(); } /** * @return {?} */ isBrowserIos() { return this.isBrowser() && /iPad|iPhone|iPod/.test(navigator.userAgent) && !((/** @type {?} */ (window))).MSStream; } /** * @return {?} */ isBrowserFirefox() { return this.isBrowser() && /(firefox|minefield)/i.test(navigator.userAgent); } /** * @return {?} */ isBrowserAndroid() { return this.isBrowser() && /android/i.test(navigator.userAgent) && !this.isBrowserTrident(); } /** * @return {?} */ isBrowserSafari() { return this.isBrowser() && /safari/i.test(navigator.userAgent) && this.isBrowserWebkit(); } } KitPlatformService.decorators = [ { type: Injectable, args: [{ providedIn: 'root', },] } ]; /** @nocollapse */ KitPlatformService.ctorParameters = () => [ { type: Object, decorators: [{ type: Inject, args: [PLATFORM_ID,] }] } ]; /** @nocollapse */ KitPlatformService.ngInjectableDef = defineInjectable({ factory: function KitPlatformService_Factory() { return new KitPlatformService(inject(PLATFORM_ID)); }, token: KitPlatformService, providedIn: "root" }); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Service for global events handling. * * ### Example * * Handle `esc` keydown globally: * * ```typescript * import { keyEscape, KitEventManagerService } from '\@ngx-kit/core'; * ... * constructor(private em: KitEventManagerService) { * } * ... * // subscribe * const escUnsub = this.em.listenGlobal('keydown', (event: KeyboardEvent) => { * if (event.keyCode === keyEscape) { * // do the job * } * }, true); * ... * // unsubscribe * escUnsub(); * ``` */ class KitEventManagerService { /** * @param {?} platform */ constructor(platform) { this.platform = platform; } /** * Listen event on the global root object. * * Reason: native Angular EventManager does not provide event listener with useCapture param. * @param {?} eventName * @param {?} handler * @param {?=} useCapture * @return {?} */ listenGlobal(eventName, handler, useCapture) { if (this.platform.isBrowser()) { window.addEventListener(eventName, (/** @type {?} */ (handler)), useCapture); return (/** * @return {?} */ () => window.removeEventListener(eventName, (/** @type {?} */ (handler)), useCapture)); } else { return (/** * @return {?} */ () => { }); } } /** * Get array of objects visited by event. * @param {?} event * @return {?} */ getEventPath(event) { if (this.platform.isBrowser()) { /** @type {?} */ const path = []; /** @type {?} */ let node = event.target; while (node && node !== document.body) { path.push(node); node = node['parentNode']; } return path; } else { return []; } } } KitEventManagerService.decorators = [ { type: Injectable, args: [{ providedIn: 'root', },] } ]; /** @nocollapse */ KitEventManagerService.ctorParameters = () => [ { type: KitPlatformService } ]; /** @nocollapse */ KitEventManagerService.ngInjectableDef = defineInjectable({ factory: function KitEventManagerService_Factory() { return new KitEventManagerService(inject(KitPlatformService)); }, token: KitEventManagerService, providedIn: "root" }); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @deprecated kayCode is deprecated * @type {?} */ const keyArrowUp = 38; /** * @deprecated kayCode is deprecated * @type {?} */ const keyArrowDown = 40; /** * @deprecated kayCode is deprecated * @type {?} */ const keyArrowRight = 39; /** * @deprecated kayCode is deprecated * @type {?} */ const keyArrowLeft = 37; /** * @deprecated kayCode is deprecated * @type {?} */ const keyPageUp = 33; /** * @deprecated kayCode is deprecated * @type {?} */ const keyPageDown = 34; /** * @deprecated kayCode is deprecated * @type {?} */ const keyHome = 36; /** * @deprecated kayCode is deprecated * @type {?} */ const keyEnd = 35; /** * @deprecated kayCode is deprecated * @type {?} */ const keyEnter = 13; /** * @deprecated kayCode is deprecated * @type {?} */ const keySpace = 32; /** * @deprecated kayCode is deprecated * @type {?} */ const keyTab = 9; /** * @deprecated kayCode is deprecated * @type {?} */ const keyEscape = 27; /** * @deprecated kayCode is deprecated * @type {?} */ const keyBackspace = 8; /** * @deprecated kayCode is deprecated * @type {?} */ const keyDelete = 46; /** * @deprecated kayCode is deprecated * @type {?} */ const keyShift = 16; /** * @deprecated kayCode is deprecated * @type {?} */ const keyCtrl = 17; /** * @deprecated kayCode is deprecated * @type {?} */ const keyAlt = 18; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Handles focus/blur from a group of elements. * * If focus moves among elements in a defined group, blur event will not be fired. * * Be aware that click on non-focusable elements will cause blur event (focus switch to `<body>`). * * Use `[kitSkipBlur]` when you need to add overlayed (`*kitOverlay`) element to a group. * * * ### Example * * In `ui-autocomplete` we heed to omit blur event, if user click on a suggestion. * * We provide `KitFocusListenerService` in the directive, register main input and `ui-autocomplete-options` element in * the service and subscribe on `blur` event. * * * collection:modal - * [sources](https://github.com/ngx-kit/ngx-kit/tree/master/packages/collection/lib/ui-autocomplete), * [demo](https://ngx-kit.com/collection/module/ui-autocomplete) */ class KitFocusListenerService { /** * @param {?} em */ constructor(em) { this.em = em; this._focused = false; this._focus = new Subject(); this._focusWithin = new Subject(); this._blur = new Subject(); this.elements = []; } /** * Emits, if user focuses one of registered element. * @return {?} */ get focus() { return this._focus.asObservable(); } /** * Emits, if user focuses one of registered element or move focus among registered elements. * @return {?} */ get focusWithin() { return this._focusWithin.asObservable(); } /** * Emits, if focus leave one of registered element and target node is not one of registered element (or it's child). * @return {?} */ get blur() { return this._blur.asObservable(); } /** * Is one of registered element focused now. * @return {?} */ get focused() { return this._focused; } /** * @param {?} el * @return {?} */ add(el) { this.elements.push({ el: el, focus: this.em.addEventListener(el, 'focusin', (/** * @param {?} event * @return {?} */ (event) => { this._focusWithin.next(event); if (!this._focused) { this._focused = true; this._focus.next(event); } })), blur: this.em.addEventListener(el, 'focusout', (/** * @param {?} event * @return {?} */ (event) => { this.checkLeave(event); })), }); } /** * @param {?} el * @return {?} */ remove(el) { /** @type {?} */ const index = this.elements.findIndex((/** * @param {?} e * @return {?} */ e => e.el === el)); if (index) { /** @type {?} */ const element = this.elements[index]; if (element) { // void handlers element.focus(); element.blur(); } // remove from stack this.elements.splice(index, 1); } else { throw new Error('Element has not been registered in KitFocusListenerService'); } } /** * @private * @param {?=} event * @return {?} */ checkLeave(event) { /** @type {?} */ let leave = true; /** @type {?} */ const relatedTarget = event.relatedTarget || event.explicitOriginalTarget || document.activeElement; this.elements.forEach((/** * @param {?} el * @return {?} */ el => { if (el.el && el.el.contains(relatedTarget)) { leave = false; } })); if (leave) { this._focused = false; this._blur.next(event); } } } KitFocusListenerService.decorators = [ { type: Injectable } ]; /** @nocollapse */ KitFocusListenerService.ctorParameters = () => [ { type: EventManager } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * Registers element in `KitFocusListener` to avoid emitting blur if focus moves to the current element. * * Important when you use `*kitOverlay`, because items are placed in overlay-container outside of a current component. */ class KitSkipBlurDirective { /** * @param {?} focusListener * @param {?} elementRef * @param {?} platform */ constructor(focusListener, elementRef, platform) { this.focusListener = focusListener; this.elementRef = elementRef; this.platform = platform; if (!this.focusListener) { throw new Error(`KitSkipBlurDirective: should be used under KitFocusListenerService,