@ngx-kit/core
Version:
ngx-kit - core module
1,823 lines (1,790 loc) • 246 kB
JavaScript
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,