UNPKG

right-angled

Version:

Lightweight and easy to use angular data grids. Integrates with your markup and styles rather than generating its own.

1 lines 260 kB
{"version":3,"file":"right-angled.mjs","sources":["../../../projects/right-angled/src/lib/core/operation-status.ts","../../../projects/right-angled/src/lib/core/sort-parameter.ts","../../../projects/right-angled/src/lib/utilities.ts","../../../projects/right-angled/src/lib/filters/filters.service.ts","../../../projects/right-angled/src/lib/filters/register-as-filter.directive.ts","../../../projects/right-angled/src/lib/filters/filter.annotation.ts","../../../projects/right-angled/src/lib/filters/filters.module.ts","../../../projects/right-angled/src/lib/lists/providers/sortings.service.ts","../../../projects/right-angled/src/lib/lists/providers/async-subscriber.ts","../../../projects/right-angled/src/lib/lists/providers/null-object-pager.ts","../../../projects/right-angled/src/lib/lists/providers/state.service.ts","../../../projects/right-angled/src/lib/lists/providers/list.ts","../../../projects/right-angled/src/lib/lists/list.directive.ts","../../../projects/right-angled/src/lib/lists/status/status-done.component.ts","../../../projects/right-angled/src/lib/lists/status/status-failed.component.ts","../../../projects/right-angled/src/lib/lists/status/status-in-progress.component.ts","../../../projects/right-angled/src/lib/lists/status/status-initial.component.ts","../../../projects/right-angled/src/lib/lists/status/status-no-data.component.ts","../../../projects/right-angled/src/lib/lists/status/status-request-cancelled.component.ts","../../../projects/right-angled/src/lib/lists/row-number.pipe.ts","../../../projects/right-angled/src/lib/lists/sort.directive.ts","../../../projects/right-angled/src/lib/lists/providers/buffered-pager.ts","../../../projects/right-angled/src/lib/lists/paging/buffered-pager.component.ts","../../../projects/right-angled/src/lib/lists/paging/infinite.directive.ts","../../../projects/right-angled/src/lib/lists/providers/paged-pager.ts","../../../projects/right-angled/src/lib/lists/paging/page-number.directive.ts","../../../projects/right-angled/src/lib/lists/paging/page-size.directive.ts","../../../projects/right-angled/src/lib/lists/paging/paged-pager.component.ts","../../../projects/right-angled/src/lib/lists/paging/row-count.directive.ts","../../../projects/right-angled/src/lib/lists/lists.module.ts","../../../projects/right-angled/src/lib/misc/focus-on-render.directive.ts","../../../projects/right-angled/src/lib/misc/events-attacher.base.ts","../../../projects/right-angled/src/lib/misc/prevent-defaults.directive.ts","../../../projects/right-angled/src/lib/misc/select-on-focus.directive.ts","../../../projects/right-angled/src/lib/misc/stop-events.directive.ts","../../../projects/right-angled/src/lib/misc/misc.module.ts","../../../projects/right-angled/src/lib/selection/providers/selection.service.ts","../../../projects/right-angled/src/lib/selection/providers/selection-events-helper.ts","../../../projects/right-angled/src/lib/selection/selection-area.directive.ts","../../../projects/right-angled/src/lib/selection/selectable.directive.ts","../../../projects/right-angled/src/lib/selection/selection-checkbox-for.directive.ts","../../../projects/right-angled/src/lib/selection/selection.module.ts","../../../projects/right-angled/src/lib/right-angled.module.ts","../../../projects/right-angled/src/public_api.ts","../../../projects/right-angled/src/right-angled.ts"],"sourcesContent":["/**\r\n * Represents possible values for operation status (e.g. of request to the server).\r\n */\r\nexport enum OperationStatus {\r\n /**\r\n * Nothing was performed before.\r\n */\r\n Initial = 0,\r\n /**\r\n * Last operation completed successfully.\r\n */\r\n Done = 1,\r\n /**\r\n * Operation is performing right now.\r\n */\r\n Progress = 2,\r\n /**\r\n * Last operation completed with failure.\r\n */\r\n Fail = 3,\r\n /**\r\n * Last operation was cancelled.\r\n */\r\n Cancelled = 4,\r\n /**\r\n * Last operation doesn't return any data.\r\n */\r\n NoData = 5\r\n}\r\n","/**\r\n * Represents sort direction that applied as parameter by {@link SortParameter} class.\r\n */\r\nexport enum SortDirection {\r\n /**\r\n * Ascending sort order.\r\n */\r\n Asc = 0,\r\n /**\r\n * Descending sort order.\r\n */\r\n Desc = 1\r\n}\r\n\r\nexport type SortDirectionStr = 'Asc' | 'Desc';\r\n\r\n/**\r\n * Represents sorting parameter applied to the server request by {@link RTSortingsService}.\r\n */\r\nexport interface SortParameter {\r\n /**\r\n * Sort direction.\r\n */\r\n direction: SortDirection;\r\n /**\r\n * Name of the field by which sorting must be performed.\r\n */\r\n fieldName: string;\r\n}\r\n","/**\r\n * Copies values of all properties from passed object to the new object literal.\r\n *\r\n * If any of the properties of passed object is also a complex object then {@link cloneAsLiteral} will be called recursively.\r\n *\r\n * Function declarations are ignored.\r\n * @param value value to clone.\r\n * @returns resulted literal.\r\n */\r\nexport function cloneAsLiteral(value: any): any {\r\n if (value === null) {\r\n return null;\r\n }\r\n if (typeof value === 'undefined') {\r\n return undefined;\r\n }\r\n if (Array.isArray(value)) {\r\n return value.map(cloneAsLiteral);\r\n }\r\n if (typeof value === 'object') {\r\n const result: { [id: string]: any } = {};\r\n for (const index in value) {\r\n if (Object.prototype.hasOwnProperty.call(value, index) && typeof value[index] !== 'function') {\r\n result[index] = cloneAsLiteral(value[index]);\r\n }\r\n }\r\n return result;\r\n }\r\n return value;\r\n}\r\n\r\n/**\r\n * Set of key-value pairs which is used by {@link coerceValue} method to coerce specific values.\r\n */\r\n// tslint:disable-next-line: object-literal-key-quotes\r\nexport const coerceTypes: any = { true: !0, false: !1, null: null };\r\n\r\n/**\r\n * Coerce type of passed value.\r\n *\r\n * For example if you pass string with value 'null' it returns `null`, if you pass 'true' it returns boolean value `true`, if you pass '1.0' it returns number `1.0` etc.\r\n *\r\n * If passed value is complex object or array this method will be called for each property or array item.\r\n * @param value value to coerce.\r\n * @returns resulted value.\r\n * @see {@link coerceTypes}\r\n */\r\nexport function coerceValue(value: any): any {\r\n let result = value;\r\n if (result === null) {\r\n return null;\r\n }\r\n if (typeof result === 'undefined') {\r\n return undefined;\r\n }\r\n if (typeof result === 'object' || Array.isArray(result)) {\r\n for (const index in result) {\r\n if (Object.prototype.hasOwnProperty.call(result, index)) {\r\n result[index] = coerceValue(result[index]);\r\n }\r\n }\r\n } else if (result && result !== true && !isNaN(result)) {\r\n result = +result;\r\n } else if (result === 'undefined') {\r\n result = undefined;\r\n } else if (typeof coerceTypes[result] !== 'undefined') {\r\n result = coerceTypes[result];\r\n }\r\n return result;\r\n}\r\n/**\r\n * Cleaning up passed array by calling `splice` function.\r\n *\r\n * Next, each element of passed array will be checked for existence of `destroy` method and if it exists it will be called.\r\n * @param collection array of elements to destroy.\r\n * @param async if `true` then iterating over array and `destroy` methods calling will be executed via setTimeout (,0).\r\n */\r\nexport function destroyAll(collection: any[], async = true) {\r\n if (!Array.isArray(collection)) {\r\n return;\r\n }\r\n let items = collection.splice(0, collection.length);\r\n\r\n if (async) {\r\n setTimeout(() => {\r\n items.forEach((item: any) => {\r\n if (item && item.destroy) {\r\n item.destroy();\r\n }\r\n });\r\n items = null;\r\n }, 0);\r\n } else {\r\n items.forEach((item: any) => {\r\n if (item && item.destroy) {\r\n item.destroy();\r\n }\r\n });\r\n }\r\n}\r\n","import { Injectable } from '@angular/core';\r\nimport { FilterConfig } from '../core/filter-config';\r\nimport { cloneAsLiteral, coerceValue } from '../utilities';\r\nimport { BehaviorSubject, Subject } from 'rxjs';\r\n/**\r\n * Used for declarative building of objects which represent valuable state of `target object`.\r\n *\r\n * Since this declaration is pretty abstract and hard to understand, let's look at a specific sample.\r\n *\r\n * Typical usage of this service is something like this:\r\n * ```JavaScript\r\n * class EndUserClass {\r\n * @filter\r\n * public parameter1 = 'Hey';\r\n * @filter\r\n * public parameter2 = 'There';\r\n * }\r\n * let endUserClassInstance = new EndUserClass();\r\n * let filterService = new FilterService(endUserClassInstance);\r\n * ```\r\n * Now we can use created `filtersService` instance for several cases:\r\n * - by calling {@link getRequestState} we can get serializable representation of object state\r\n * ```JavaScript\r\n * {\r\n * parameter1: 'Hey',\r\n * parameter2: 'There'\r\n * }\r\n * ```\r\n * - by calling {@link resetValues} we can reset values of annotated properties to initial values (or to what was specified in {@link FilterConfig.defaultValue} property).\r\n * - by calling {@link applyParams} we can automatically apply any set of values to annotated properties (we can pass queryString object to automatically apply values from it, for example).\r\n * - by calling {@link getRequestState} with some filters we can \"query\" the state of filters and save it to the settings storage, for example.\r\n * - by calling {@link registerFilterTarget} you can add any count of additional objects and get their\r\n * composed state via {@link getRequestState} method as well as process them all with {@link resetValues} and {@link applyParams}.\r\n */\r\n@Injectable()\r\nexport class RTFiltersService {\r\n /**\r\n * Global collection of all filters configuration.\r\n *\r\n * Used to build {@link appliedFiltersMap} for concrete set of objects that were registered as `target objects` via {@link registerFilterTarget} for concrete service instance.\r\n */\r\n public static filterPropertiesMap: Map<any, FilterConfig[]> = new Map<any, FilterConfig[]>();\r\n /**\r\n * Collection of {@link FilterConfig} settings that matches configuration for all objects that were registered in current service instance via {@link registerFilterTarget} method.\r\n *\r\n * This collection is also filled up with configurations of passed `target objects` base classes since config applicability is determined by `instanceof` check.\r\n *\r\n * This collection is \"lazy\" and will be filled up on first call of {@link resetValues}, {@link applyParams} or {@link getRequestState} method.\r\n */\r\n public readonly appliedFiltersMap: Map<object, FilterConfig[]> = new Map<object, FilterConfig[]>();\r\n /**\r\n * @param target `target object` that will be registered with {@link registerFilterTarget} method.\r\n */\r\n /**\r\n * Used to register type property as `target property` with specified filter config for later usage with {@link FiltersService}.\r\n *\r\n * This method is called by {@link filter} annotation, for example.\r\n * @param targetType type definition that contains specified property declaration.\r\n * @param propertyConfig configuration for property as a filter.\r\n */\r\n public static registerFilterConfig(targetType: object, propertyConfig: FilterConfig): void {\r\n const typeConfigs = RTFiltersService.filterPropertiesMap.has(targetType)\r\n ? RTFiltersService.filterPropertiesMap.get(targetType)\r\n : new Array<FilterConfig>();\r\n typeConfigs.push(propertyConfig);\r\n RTFiltersService.filterPropertiesMap.set(targetType, typeConfigs);\r\n }\r\n /**\r\n * Used to build resulted value of `target property` based on specified {@link FilterConfig}.\r\n *\r\n * This method is used by {@link getRequestState} and also calls oneself for the case of array values.\r\n *\r\n *\r\n * In addition to {@link FilterConfig} configuration, this method checks if `target property` has method `toRequest()`. If so this method will be used to get serialized value.\r\n *\r\n * This convention has sense in several scenarios:\r\n * - Serialization of complex object can be performed by it's own method which was declared once instead of copy-paste it in {@link FilterConfig.serializeFormatter} declarations.\r\n * - Very tricky but sometimes useful usage of this convention is to declare `toRequest` in `Date` prototype.\r\n *\r\n * This gives ability to easily send `Date` objects to the server in appropriate format which server can apply or, for example, always send UTC-dates.\r\n *\r\n * But be accurate with this approach. There's a lot of problems with extending of embedded types.\r\n *\r\n * @param target `target` object which holds specified `target property`. Used as `this` scope for {@link FilterConfig.serializeFormatter} method.\r\n * @param value raw value of `target` property.\r\n * @param config filter configuration for `target` property.\r\n */\r\n private static buildFilterValue(target: object, value: any, config: FilterConfig): object {\r\n let result = value;\r\n if (config && config.serializeFormatter) {\r\n return config.serializeFormatter.call(target, result);\r\n }\r\n\r\n result = config && config.emptyIsNull ? result || null : result;\r\n result = config && config.coerce ? coerceValue(result) : result;\r\n if (result && result.toRequest) {\r\n return result.toRequest();\r\n }\r\n if (Array.isArray(result)) {\r\n const temp = [];\r\n for (let i = 0; i < result.length; i++) {\r\n temp[i] = RTFiltersService.buildFilterValue(target, result[i], null);\r\n }\r\n return temp;\r\n }\r\n return result;\r\n }\r\n\r\n /**\r\n * Performs service destroy.\r\n */\r\n public destroy(): void {\r\n this.appliedFiltersMap.clear();\r\n }\r\n /**\r\n * Goes through all `target properties` of all `target objects` and sets their values to configured default.\r\n *\r\n * Default value will be determined as:\r\n * - If value for {@link FilterConfig.defaultValue} is specified it will be applied.\r\n * - Otherwise this service writes to {@link FilterConfig.defaultValue} value of `target property` at the moment of first call of\r\n * {@link resetValues}, {@link applyParams} or {@link getRequestState}.\r\n *\r\n * This method performs next actions:\r\n * - If value specified as {@link FilterConfig.defaultValue} is function it will be called with `target object` as `this` scope. If specified value is not a function it will be used by itself.\r\n * - Result of previous step will be cloned by {@link cloneLiteral} function to avoid possible reference types problems.\r\n * - If {@link FilterConfig.parseFormatter} method is specified it will be called with previous step result as parameter.\r\n * - Result of previous steps is applied as value to `target property`.\r\n */\r\n public resetValues(): void {\r\n this.appliedFiltersMap.forEach((targetConfig: FilterConfig[], target: { [id: string]: any }) => {\r\n for (const config of targetConfig) {\r\n const defaultValue = typeof config.defaultValue === 'function' ? (config.defaultValue as () => any).call(target) : config.defaultValue;\r\n const clonedObject = cloneAsLiteral({ defaultValue });\r\n this.setPropertyValue(\r\n target,\r\n config,\r\n config.parseFormatter ? config.parseFormatter.call(target, clonedObject.defaultValue) : clonedObject.defaultValue\r\n );\r\n }\r\n });\r\n }\r\n /**\r\n * Goes through all `target properties` of all `target objects` and tries to find property within passed object which name is equal\r\n * to configured {@link FilterConfig.parameterName}.\r\n *\r\n * If match is found, value of the property from passed object is applied to `target property` in accordance with\r\n * {@link FilterConfig.ignoreOnAutoMap}, {@link FilterConfig.emptyIsNull}, {@link FilterConfig.coerce}, {@link FilterConfig.parseFormatter}.\r\n * @param params - object with values to apply.\r\n */\r\n public applyParams(params: { [id: string]: any }): void {\r\n this.appliedFiltersMap.forEach((targetConfig: FilterConfig[], target: { [id: string]: any }) => {\r\n for (const config of targetConfig) {\r\n if (params && Object.prototype.hasOwnProperty.call(params, config.parameterName) && false === config.ignoreOnAutoMap) {\r\n let proposedVal = config.emptyIsNull ? params[config.parameterName] || null : params[config.parameterName];\r\n proposedVal = config.coerce ? coerceValue(proposedVal) : proposedVal;\r\n this.setPropertyValue(target, config, config.parseFormatter ? config.parseFormatter.call(target, proposedVal, params) : proposedVal);\r\n }\r\n }\r\n });\r\n }\r\n /**\r\n * Goes through all `target properties` of all `target objects` and applies their values to one resulted object literal.\r\n *\r\n * Typical usage of this method is building request to send it to the server.\r\n *\r\n * Names of properties in result object depends on {@link FilterConfig.parameterName}. Final values would be constructed by {@link buildFilterValue} method.\r\n * @param filterFn - optional function to filter applied values.\r\n * @returns resulted object literal.\r\n */\r\n public getRequestState(filterFn?: (config: FilterConfig, proposedValue: any, targetObject: object) => boolean): any {\r\n const result: { [id: string]: any } = {};\r\n this.appliedFiltersMap.forEach((targetConfig: FilterConfig[], target: { [id: string]: any }) => {\r\n for (let config of targetConfig) {\r\n config = { ...config };\r\n const proposedVal = this.getPropertyValue(target, config);\r\n if (filterFn ? filterFn(config, proposedVal, target) : true) {\r\n const resultValue = RTFiltersService.buildFilterValue(target, proposedVal, config);\r\n if (!config.omitIfNullOrUndefined || (resultValue !== null && typeof resultValue !== 'undefined')) {\r\n result[config.parameterName] = resultValue;\r\n }\r\n }\r\n }\r\n });\r\n return result;\r\n }\r\n\r\n /**\r\n * Registers passed object as `target object` for current service instance.\r\n *\r\n * {@link getRequestState} method will compose result from objects that were registered by this method.\r\n *\r\n * {@link applyParams} and {@link resetValues} methods processes registered objects that were registered by this method.\r\n * @param targets object(s) to register as `target object`.\r\n */\r\n public registerFilterTarget(...targets: object[]): void {\r\n targets.forEach((target: object) => {\r\n if (target === null || target === undefined) {\r\n return;\r\n }\r\n this.appliedFiltersMap.set(target, null);\r\n this.buildFilterTargetMap(target);\r\n });\r\n }\r\n /**\r\n * Removes passed object from `target objects` collection for current service instance.\r\n *\r\n * This means that {@link getRequestState}, {@link applyParams} and {@link resetValues} methods stops to process this objects.\r\n * @param targets object(s) to remove from collection of `target object`.\r\n */\r\n public removeFilterTarget(...targets: object[]): void {\r\n targets.forEach((target: object) => {\r\n this.appliedFiltersMap.delete(target);\r\n });\r\n }\r\n /**\r\n * Builds map of settings for passed `target` object.\r\n */\r\n private buildFilterTargetMap(target: { [id: string]: any }): void {\r\n let targetConfig = new Array<FilterConfig>();\r\n RTFiltersService.filterPropertiesMap.forEach((typeConfig: FilterConfig[], type: any) => {\r\n if (target instanceof type) {\r\n targetConfig = targetConfig.concat(typeConfig);\r\n for (const config of targetConfig) {\r\n if (typeof config.defaultValue === 'undefined') {\r\n config.defaultValue = cloneAsLiteral({\r\n defaultValue: this.getPropertyValue(target, config)\r\n }).defaultValue;\r\n }\r\n }\r\n }\r\n });\r\n if (targetConfig.length > 0) {\r\n this.appliedFiltersMap.set(target, targetConfig);\r\n } else {\r\n this.appliedFiltersMap.delete(target);\r\n }\r\n }\r\n private getPropertyValue(target: { [id: string]: any }, config: FilterConfig) {\r\n if (target[config.propertyName] instanceof BehaviorSubject) {\r\n return (target[config.propertyName] as BehaviorSubject<any>).getValue();\r\n }\r\n // duck type reactive form control\r\n if (!!target[config.propertyName] && !!target[config.propertyName].setValue && !!target[config.propertyName].status) {\r\n return target[config.propertyName].value;\r\n }\r\n return target[config.propertyName];\r\n }\r\n private setPropertyValue(target: { [id: string]: any }, config: FilterConfig, value: any) {\r\n if (!!target[config.propertyName] && target[config.propertyName] instanceof Subject) {\r\n (target[config.propertyName] as Subject<any>).next(value);\r\n return;\r\n }\r\n // duck type reactive form control\r\n if (!!target[config.propertyName] && target[config.propertyName].setValue) {\r\n target[config.propertyName].setValue(value);\r\n return;\r\n }\r\n target[config.propertyName] = value;\r\n }\r\n}\r\n","import { Directive, Input, OnDestroy, OnInit } from '@angular/core';\r\nimport { RTFiltersService } from './filters.service';\r\n@Directive({\r\n selector: '[rtRegisterAsFilter]'\r\n})\r\nexport class RegisterAsFilterDirective implements OnInit, OnDestroy {\r\n @Input('rtRegisterAsFilter') public filterTarget: any;\r\n constructor(public filtersService: RTFiltersService) {}\r\n public ngOnInit(): void {\r\n this.filtersService.registerFilterTarget(this.filterTarget);\r\n }\r\n public ngOnDestroy(): void {\r\n this.filtersService.removeFilterTarget(this.filterTarget);\r\n }\r\n}\r\n","import { RTFiltersService } from './filters.service';\r\nimport { FilterConfig } from '../core/filter-config';\r\nimport { cloneAsLiteral } from '../utilities';\r\n\r\n/**\r\n * Object literal used by {@link getDefaultFilterConfig} function to build filter configuration.\r\n * This object can be used to change default values of filter configs globally.\r\n * By default it has next values:\r\n * ```Javascript\r\n * {\r\n * coerce: true,\r\n * defaultValue: undefined,\r\n * emptyIsNull: false,\r\n * ignoreOnAutoMap: false,\r\n * omitIfNullOrUndefined: false,\r\n * parameterName: <value of 'propertyName' parameter>,\r\n * parseFormatter: undefined,\r\n * propertyName: <value of 'propertyName' parameter>,\r\n * serializeFormatter: undefined\r\n * }\r\n * ```\r\n */\r\nexport const DefaultFilterConfig = {\r\n coerce: true,\r\n defaultValue: undefined,\r\n emptyIsNull: false,\r\n ignoreOnAutoMap: false,\r\n omitIfNullOrUndefined: false,\r\n parseFormatter: undefined,\r\n serializeFormatter: undefined\r\n} as FilterConfig;\r\n\r\n/**\r\n * Returns filter configuration based on {@link DefaultFilterConfig} values with applied `parameterName` and `propertyName` properties values.\r\n * @param propertyName name of the property in `target type`, for which configuration is created. This value will be used to set {@link FilterConfig.propertyName} and {@link FilterConfig.parameterName} values.\r\n * @see {@link FilterConfig}\r\n */\r\nexport function getDefaultFilterConfig(propertyName: string): FilterConfig {\r\n return {\r\n parameterName: propertyName,\r\n propertyName,\r\n ...cloneAsLiteral(DefaultFilterConfig)\r\n } as FilterConfig;\r\n}\r\n/**\r\n * Annotation that can be used to configure type property as filter to use with {@link FiltersService}\r\n * @param targetOrNameOrConfig\r\n * - if annotation is applied without any parameters then result of {@link getDefaultFilterConfig} function will be used. Value of {@link FilterConfig.parameterName} property will be equal to annotated property name.\r\n * - if annotation is applied with string parameter then result of {@link getDefaultFilterConfig} function will be used. Value of {@link FilterConfig.parameterName} property will be equal to applied parameter value.\r\n * - if annotation is applied with object as parameter then result of {@link getDefaultFilterConfig} will be used and all properties which were specified in passed object would be applied to resulting configuration via Object.assign.\r\n * @param key specified by TypeScript automatically.\r\n * @see {@link FilterConfig}\r\n */\r\nexport function filter(targetOrNameOrConfig?: string | FilterConfig, key?: string): any {\r\n const decorateWithConfig = (target: object, key2: string): void => {\r\n const config = getDefaultFilterConfig(key2);\r\n if (typeof targetOrNameOrConfig === 'string') {\r\n config.parameterName = targetOrNameOrConfig;\r\n } else {\r\n Object.assign(config, targetOrNameOrConfig);\r\n }\r\n RTFiltersService.registerFilterConfig(target.constructor, config);\r\n };\r\n\r\n if (key) {\r\n const targetTemp = targetOrNameOrConfig;\r\n // tslint:disable-next-line:no-parameter-reassignment\r\n targetOrNameOrConfig = null;\r\n decorateWithConfig(targetTemp as object, key);\r\n return;\r\n }\r\n return decorateWithConfig;\r\n}\r\n","import { CommonModule } from '@angular/common';\r\nimport { NgModule } from '@angular/core';\r\nimport { RegisterAsFilterDirective } from './register-as-filter.directive';\r\n\r\n@NgModule({\r\n declarations: [RegisterAsFilterDirective],\r\n exports: [RegisterAsFilterDirective],\r\n imports: [CommonModule]\r\n})\r\nexport class RTFiltersModule {}\r\nexport { RegisterAsFilterDirective } from './register-as-filter.directive';\r\nexport { RTFiltersService } from './filters.service';\r\nexport { filter, DefaultFilterConfig } from './filter.annotation';\r\n","import { filter } from '../../filters/filter.annotation';\r\nimport { SortDirection, SortDirectionStr, SortParameter } from '../../core/sort-parameter';\r\nimport { FilterConfig } from '../../core/filter-config';\r\nimport { Injectable } from '@angular/core';\r\n\r\n/**\r\n * Provides sorting functionality.\r\n * @note This type is configured to use with {@link FiltersService}.\r\n */\r\n@Injectable()\r\nexport class RTSortingsService {\r\n /**\r\n * Sortings that were selected by the user and must be applied on next request of data.\r\n *\r\n * @note This property is ready to use with {@link FiltersService} since it has {@link filter} annotation.\r\n */\r\n @filter({\r\n defaultValue(this: RTSortingsService): SortParameter[] {\r\n return this.cloneDefaultSortings();\r\n },\r\n parameterName: 'sortings',\r\n parseFormatter(rawValue: any): object[] {\r\n return Array.isArray(rawValue)\r\n ? rawValue.map((sort: SortParameter) => ({\r\n direction: sort.direction * 1,\r\n fieldName: sort.fieldName\r\n }))\r\n : [];\r\n },\r\n serializeFormatter(this: RTSortingsService): object {\r\n return this.sortings.map((sort: SortParameter) => ({\r\n direction: sort.direction,\r\n fieldName: sort.fieldName\r\n }));\r\n }\r\n } as FilterConfig)\r\n public sortings: SortParameter[] = new Array<SortParameter>();\r\n private defaultSortingsInternal: SortParameter[] = new Array<SortParameter>();\r\n\r\n /**\r\n * Default sortings that will be used by service.\r\n */\r\n public get defaultSortings(): SortParameter[] {\r\n return this.defaultSortingsInternal;\r\n }\r\n\r\n /**\r\n * If called when {@link sortings} is empty then applied value will be copied to {@link sortings} immediately.\r\n */\r\n public set defaultSortings(value: SortParameter[]) {\r\n this.defaultSortingsInternal = value || [];\r\n if (this.sortings.length === 0) {\r\n this.sortings = this.cloneDefaultSortings();\r\n }\r\n }\r\n\r\n /**\r\n * Sets {@link sortings} according to specified parameters.\r\n * @param fieldName name of the field by which sorting must be executed on server. This value will be used as {@link SortParameter.fieldName}.\r\n *\r\n * In case when sorting with the same field name is already specified, direction of this sorting will be toggled to reversed value and this sorting will be pushed to the end of {@link sortings} array.\r\n * So it will be applied last.\r\n * @param savePrevious `true` to keep previously applied sortings in {@link sortings} array.\r\n *\r\n * @param startDirectionStr - sort direction\r\n */\r\n public setSort(fieldName: string, savePrevious: boolean, startDirectionStr: SortDirectionStr = 'Asc'): void {\r\n const startDirection: SortDirection = SortDirection[startDirectionStr] !== undefined ? SortDirection[startDirectionStr] : SortDirection.Asc;\r\n let newSort = { direction: startDirection, fieldName };\r\n for (let i = 0; i < this.sortings.length; i++) {\r\n if (this.sortings[i].fieldName === fieldName) {\r\n const existedSort = this.sortings.splice(i, 1)[0];\r\n newSort = {\r\n direction: existedSort.direction,\r\n fieldName: existedSort.fieldName\r\n };\r\n newSort.direction = newSort.direction === SortDirection.Asc ? SortDirection.Desc : SortDirection.Asc;\r\n break;\r\n }\r\n }\r\n if (savePrevious) {\r\n this.sortings.push(newSort);\r\n } else {\r\n this.sortings.length = 0;\r\n this.sortings.push(newSort);\r\n }\r\n }\r\n\r\n /**\r\n * Removes sort with specified field name from {@link sortings} array.\r\n * @param fieldName name of the sort to remove.\r\n */\r\n public removeSort(fieldName: string): void {\r\n for (let i = 0; i < this.sortings.length; i++) {\r\n if (this.sortings[i].fieldName === fieldName) {\r\n this.sortings.splice(i, 1);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Removes all sortings from {@link sortings} array.\r\n */\r\n public removeAllSortings(): void {\r\n this.sortings.length = 0;\r\n }\r\n\r\n /**\r\n * Performs service destroy.\r\n */\r\n public destroy(): void {\r\n this.defaultSortingsInternal.length = 0;\r\n this.sortings.length = 0;\r\n }\r\n\r\n /**\r\n * Internal method for default sortings cloning.\r\n * This method is used as {@link FilterConfig.defaultValue} as well as for copying to {@link sortings} when {@link defaultSortings} setter is used and {@link sortings} is empty.\r\n */\r\n private cloneDefaultSortings(): SortParameter[] {\r\n return this.defaultSortingsInternal.map((s: SortParameter) => ({\r\n direction: s.direction,\r\n fieldName: s.fieldName\r\n }));\r\n }\r\n}\r\n","import { Injectable } from '@angular/core';\r\n// tslint:disable:max-classes-per-file\r\n\r\n/**\r\n * Contract to implement abstracted subscription proxy which hides any details of underlying subscription\r\n */\r\nexport interface SubscriptionProxy {\r\n /**\r\n * Subscribes to passed object\r\n * @param target object to subscribe\r\n * @param completeAction action to call on underlying subscription successful completion\r\n * @param errorAction action to call on underlying subscription error\r\n */\r\n\r\n attach(target: any, completeAction: any, errorAction: any): any;\r\n\r\n /**\r\n * Detaches from underlying subscription\r\n */\r\n detach(subscription: any): void;\r\n}\r\n\r\n/**\r\n * Implementation of {@link SubscriptionProxy} to work with any objects with `subscribe/unsubscribe` contracts. This contract is suitable for Observable, for example.\r\n */\r\nexport class PushBasedSubscriptionProxy implements SubscriptionProxy {\r\n /**\r\n * Returns `true` if this proxy type can subscribe to passed object. `false` otherwise.\r\n */\r\n public static isAcceptable(target: any): boolean {\r\n return !!target.subscribe;\r\n }\r\n /**\r\n * @inheritdoc\r\n */\r\n public attach(target: any, completeAction: any, errorAction?: (error: any) => any): any {\r\n return target.subscribe({ error: errorAction, next: completeAction });\r\n }\r\n /**\r\n * @inheritdoc\r\n */\r\n public detach(subscription: any): void {\r\n subscription.unsubscribe();\r\n }\r\n}\r\n\r\n/**\r\n * Implementation of {@link SubscriptionProxy} which works with Promise and adds ability to unsubscribe from it.\r\n */\r\nexport class PromiseSubscriptionProxy implements SubscriptionProxy {\r\n private isAlive = true;\r\n /**\r\n * Returns `true` if this proxy type can subscribe to passed object. `false` otherwise.\r\n */\r\n public static isAcceptable(target: any): boolean {\r\n return target instanceof Promise;\r\n }\r\n /**\r\n * @inheritdoc\r\n */\r\n public attach(target: Promise<any>, completeAction: (value: any) => any, errorAction?: (error: any) => any): any {\r\n return target.then(\r\n (value: any) => {\r\n if (this.isAlive) {\r\n completeAction(value);\r\n }\r\n },\r\n (error: any) => {\r\n if (this.isAlive) {\r\n errorAction(error);\r\n }\r\n }\r\n );\r\n }\r\n /**\r\n * @inheritdoc\r\n */\r\n public detach(subscription: any): void {\r\n this.isAlive = false;\r\n }\r\n}\r\n\r\n/**\r\n * Service to manage async subscriptions which acts as mediator to {@link SubscriptionProxy} contract implementations.\r\n */\r\n@Injectable()\r\nexport class AsyncSubscriber {\r\n private proxy: SubscriptionProxy = null;\r\n private lastTarget: any = null;\r\n private subscription: any = null;\r\n\r\n /**\r\n * @see {@link SubscriptionProxy.attach}\r\n */\r\n public attach(target: any, completeAction: (value: any) => any, errorAction?: (error: any) => any): void {\r\n if (this.lastTarget !== null) {\r\n this.destroy();\r\n }\r\n this.lastTarget = target;\r\n this.proxy = this.getProxy(target);\r\n this.subscription = this.proxy.attach(target, completeAction, errorAction);\r\n }\r\n /**\r\n * Detaches from underlying subscription and destroys all internal objects.\r\n */\r\n public destroy(): void {\r\n if (this.proxy) {\r\n this.proxy.detach(this.subscription);\r\n }\r\n this.proxy = null;\r\n this.lastTarget = null;\r\n this.subscription = null;\r\n }\r\n /**\r\n * @see {@link SubscriptionProxy.detach}\r\n */\r\n public detach(): void {\r\n if (this.proxy) {\r\n this.proxy.detach(this.subscription);\r\n }\r\n }\r\n private getProxy(target: any): SubscriptionProxy {\r\n if (PromiseSubscriptionProxy.isAcceptable(target)) {\r\n return new PromiseSubscriptionProxy();\r\n }\r\n if (PushBasedSubscriptionProxy.isAcceptable(target)) {\r\n return new PushBasedSubscriptionProxy();\r\n }\r\n throw new Error('Passed object is not subscribable');\r\n }\r\n}\r\n","import { Pager } from '../../core/pager';\r\nimport { ListResponse } from '../../core/list-response';\r\n\r\n/**\r\n * Implements {@link Pager} contract and represents list without any paging mechanics.\r\n * @note This type is configured to use with {@link FiltersService}.\r\n */\r\nexport class NullObjectPager implements Pager {\r\n /**\r\n * @inheritdoc\r\n */\r\n public appendedOnLoad = false;\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n public totalCount = 0;\r\n /**\r\n * @inheritdoc\r\n */\r\n public loadedCount = 0;\r\n /**\r\n * @inheritdoc\r\n */\r\n public processResponse(response: ListResponse<any> | any[]): void {\r\n let alignedResponse: ListResponse<any>;\r\n if (Array.isArray(response)) {\r\n alignedResponse = {\r\n items: response,\r\n loadedCount: response.length,\r\n totalCount: response.length\r\n } as ListResponse<any>;\r\n } else {\r\n alignedResponse = response;\r\n }\r\n\r\n this.loadedCount = alignedResponse.loadedCount || (alignedResponse.items && alignedResponse.items.length ? alignedResponse.items.length : 0);\r\n this.totalCount = alignedResponse.totalCount || 0;\r\n }\r\n /**\r\n * @inheritdoc\r\n */\r\n public reset(): void {\r\n this.totalCount = 0;\r\n this.loadedCount = 0;\r\n }\r\n}\r\n","import { RTFiltersService } from '../../filters/filters.service';\r\n\r\n/**\r\n * Base contract for state management services.\r\n */\r\nexport abstract class RTStateService {\r\n /**\r\n * This method must get required state from passed filterService instance and persist it in any way.\r\n * @param filtersService service to request state.\r\n */\r\n public abstract persistState(filtersService: RTFiltersService): void;\r\n /**\r\n * This method will be called during {@link RTList} initialization and must return settings saved previously.\r\n */\r\n public abstract getState(): any;\r\n}\r\n","import { EventEmitter, Inject, Injectable, InjectionToken, Optional, SkipSelf } from '@angular/core';\r\nimport { Observable, BehaviorSubject } from 'rxjs';\r\nimport { RTFiltersService } from '../../filters/filters.service';\r\nimport { destroyAll } from '../../utilities';\r\nimport { RTStateService } from './state.service';\r\nimport { RTSortingsService } from './sortings.service';\r\nimport { OperationStatus } from '../../core/operation-status';\r\nimport { ListResponse } from '../../core/list-response';\r\nimport { AsyncSubscriber } from './async-subscriber';\r\nimport { NullObjectPager } from './null-object-pager';\r\nimport { Pager } from '../../core/pager';\r\nimport { FilterConfig } from '../../core/filter-config';\r\nimport { map } from 'rxjs/operators';\r\nexport const RTFilterTarget = new InjectionToken<any>('RTFilterTarget');\r\n\r\nexport class OperationStatusStream {\r\n public status$: Observable<OperationStatus>;\r\n}\r\n\r\n@Injectable()\r\nexport class RTList {\r\n /**\r\n * Global settings of list..\r\n *\r\n * These settings are static and their values are copied to the properties of the same name for each instance of {@link RTList} type.\r\n *\r\n * So, changing of this settings will affect all instances of {@link RTList} type that will be created after such changes.\r\n * If you want to change settings of concrete object you can use it the same name properties.\r\n */\r\n public static settings = {\r\n /**\r\n * @see {@link RTList.keepRecordsOnLoad}\r\n */\r\n keepRecordsOnLoad: false\r\n };\r\n public loadStarted: EventEmitter<void> = new EventEmitter<void>();\r\n public loadSucceed: EventEmitter<ListResponse<any> | any[]> = new EventEmitter<ListResponse<any> | any[]>();\r\n public loadFailed: EventEmitter<any> = new EventEmitter<any>();\r\n private filterTargets: object[] = [];\r\n constructor(\r\n private asyncSubscriber: AsyncSubscriber,\r\n @Optional() stateServices: RTStateService, // | RTStateService[],\r\n @SkipSelf()\r\n @Optional()\r\n @Inject(RTFilterTarget)\r\n filterTargets: any,\r\n private sortingsService: RTSortingsService,\r\n private filtersService: RTFiltersService\r\n ) {\r\n if (stateServices != null) {\r\n if (Array.isArray(stateServices)) {\r\n this.stateServices.push(...stateServices);\r\n } else {\r\n this.stateServices.push(stateServices);\r\n }\r\n }\r\n this.pager = new NullObjectPager();\r\n if (filterTargets != null) {\r\n if (Array.isArray(filterTargets)) {\r\n this.filterTargets.push(...filterTargets);\r\n } else {\r\n this.filterTargets.push(filterTargets);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Specifies that list must destroy previously loaded records immediately or keep them until data request is completed.\r\n */\r\n public keepRecordsOnLoad: boolean = RTList.settings.keepRecordsOnLoad;\r\n /**\r\n * Can be used with `Observable` data sourceto specify that list must keep previously loaded records or destroy them when new value published\r\n */\r\n public appendStreamedData: boolean | null = null;\r\n /**\r\n * Method for getting data. This parameter is required and its configuration is necessary.\r\n *\r\n * This method get one parameter with the settings of the request implementing {@link ListRequest} contract for the simple lists and {@link PagedListRequest} one for the paged lists.\r\n * The return value of this method should be any subscribable object which can be handled by {@link AsyncSubscriber}.\r\n * For the simple lists the response should contain array with the records. As for the paged ones, it should implement {@link ListResponse} contract.\r\n */\r\n public fetchMethod: (requestParams: any) => any;\r\n /**\r\n * Array of elements transferred in the {@link ListResponse.items} property.\r\n */\r\n public get items(): any[] {\r\n return (this.items$ as BehaviorSubject<any[]>).getValue();\r\n }\r\n /**\r\n * Array of registered {@link RTStateService} instances.\r\n */\r\n public stateServices: RTStateService[] = new Array<RTStateService>();\r\n /**\r\n * Async implementation of @see RTList.status\r\n */\r\n public status$: Observable<OperationStatus> = new BehaviorSubject(OperationStatus.Initial);\r\n /**\r\n * Async implementation of @see RTList.destroyed\r\n */\r\n public destroyed$: Observable<boolean> = new BehaviorSubject(false);\r\n /**\r\n * Async implementation of @see RTList.inited\r\n */\r\n public inited$: Observable<boolean> = new BehaviorSubject(false);\r\n /**\r\n * Async implementation of @see RTList.items\r\n */\r\n public items$: Observable<any[]> = new BehaviorSubject([]);\r\n /**\r\n * Async implementation of @see RTList.busy\r\n */\r\n public busy$: Observable<boolean> = this.status$.pipe(map(status => status === OperationStatus.Progress));\r\n /**\r\n * Async implementation of @see RTList.ready\r\n */\r\n public ready$: Observable<boolean> = this.status$.pipe(map(status => status !== OperationStatus.Progress));\r\n\r\n private pagerInternal: Pager;\r\n /**\r\n * Configured {@link Pager} service.\r\n */\r\n public get pager(): Pager {\r\n return this.pagerInternal;\r\n }\r\n public set pager(value: Pager) {\r\n this.filtersService.removeFilterTarget(this.pagerInternal);\r\n this.pagerInternal = value;\r\n this.filtersService.registerFilterTarget(this.pagerInternal);\r\n }\r\n /**\r\n * True if the service was already destroyed via {@link destroy} call.\r\n */\r\n public get destroyed(): boolean {\r\n return (this.destroyed$ as BehaviorSubject<boolean>).getValue();\r\n }\r\n /**\r\n * True if the service was already initialized via {@link init} call.\r\n */\r\n public get inited(): boolean {\r\n return (this.inited$ as BehaviorSubject<boolean>).getValue();\r\n }\r\n /**\r\n * Current execution status of the list.\r\n */\r\n public get status(): OperationStatus {\r\n return (this.status$ as BehaviorSubject<OperationStatus>).getValue();\r\n }\r\n /**\r\n * returns `true`, if there is a data request executed at the moment (i.e. {@link state} is equal to {@link ProgressState.Progress})\r\n */\r\n public get busy(): boolean {\r\n return this.status === OperationStatus.Progress;\r\n }\r\n /**\r\n * returns `true`, if there is no data request executed at the moment (i.e. {@link state} is NOT equal to {@link ProgressState.Progress})\r\n */\r\n public get ready(): boolean {\r\n return this.status !== OperationStatus.Progress;\r\n }\r\n /**\r\n * Performs initialization logic of the service. This method must be called before first use of the service.\r\n */\r\n public init(): void {\r\n if (this.inited) {\r\n return;\r\n }\r\n this.filtersService.registerFilterTarget(...this.filterTargets);\r\n this.filtersService.registerFilterTarget(this.pager, this.sortingsService);\r\n const restoredState = {};\r\n Object.assign(restoredState, ...this.stateServices.map((service: RTStateService) => service.getState() || {}));\r\n this.filtersService.applyParams(restoredState);\r\n (this.inited$ as BehaviorSubject<boolean>).next(true);\r\n }\r\n /**\r\n * Performs destroy logic of the list itself and all of the inner services.\r\n */\r\n public destroy(): void {\r\n this.asyncSubscriber.destroy();\r\n this.filtersService.destroy();\r\n this.sortingsService.destroy();\r\n this.clearData();\r\n (this.destroyed$ as BehaviorSubject<boolean>).next(true);\r\n }\r\n /**\r\n * Registers passed object(s) as state service to manage the list state.\r\n */\r\n public registerStateService(...services: RTStateService[]): void {\r\n services.forEach((service: RTStateService) => {\r\n this.stateServices.push(service);\r\n });\r\n }\r\n /**\r\n * Removes passed object(s) from state services collection of the list.\r\n */\r\n public removeStateService(...services: RTStateService[]): void {\r\n services.forEach((service: RTStateService) => {\r\n const index = this.stateServices.findIndex((s: RTStateService) => s === service);\r\n if (index !== -1) {\r\n this.stateServices.splice(index, 1);\r\n }\r\n });\r\n }\r\n /**\r\n * Registers passed object(s) as filter targets in underlying {@link FiltersService} to include their configured properties as parameters to the data request.\r\n * @see {@link FiltersService.registerFilterTarget}\r\n */\r\n public registerFilterTarget(...targets: object[]): void {\r\n this.filtersService.registerFilterTarget(...targets);\r\n }\r\n /**\r\n * @see {@link FiltersService.removeFilterTarget}\r\n */\r\n public removeFilterTarget(...targets: object[]): void {\r\n this.filtersService.removeFilterTarget(...targets);\r\n }\r\n /**\r\n * @see {@link FiltersService.getRequestState}\r\n */\r\n public getRequestState(filterFn?: (config: FilterConfig, proposedValue: any, targetObject: object) => boolean): any {\r\n return this.filtersService.getRequestState(filterFn);\r\n }\r\n /**\r\n * Resets the list parameters (sortings, paging, filters) to their default values.\r\n */\r\n public resetSettings(): void {\r\n this.asyncSubscriber.detach();\r\n this.filtersService.resetValues();\r\n this.pager.reset();\r\n this.clearData();\r\n (this.status$ as BehaviorSubject<OperationStatus>).next(OperationStatus.Initial);\r\n }\r\n /**\r\n * Cancels the request executed at the moment.\r\n */\r\n public cancelRequests(): void {\r\n if (this.busy || this.appendStreamedData !== null) {\r\n this.asyncSubscriber.detach();\r\n (this.status$ as BehaviorSubject<OperationStatus>).next(OperationStatus.Cancelled);\r\n this.clearData();\r\n }\r\n }\r\n\r\n /**\r\n * Clears {@link items} array. Calls {@link destroyAll} method for {@link items} array to perform optional destroy logic of the elements.\r\n * {@see destroyAll}\r\n */\r\n public clearData(): void {\r\n destroyAll(this.items);\r\n (this.items$ as BehaviorSubject<any[]>).next([]);\r\n }\r\n\r\n /**\r\n * Performs data loading by calling specified {@link fetchMethod} delegate.\r\n * @return result of {@link fetchMethod} execution.\r\n */\r\n public loadData(): Observable<any> | Promise<any> | EventEmitter<any> {\r\n if (this.busy) {\r\n return null;\r\n }\r\n this.loadStarted.emit();\r\n const subscribable = this.beginRequest();\r\n this.tryCleanItemsOnLoad(false);\r\n this.asyncSubscriber.attach(subscribable, this.loadDataSuccessCallback, this.loadDataFailCallback);\r\n this.stateServices.forEach((service: RTStateService) => {\r\n service.persistState(this.filtersService);\r\n });\r\n return subscribable;\r\n }\r\n /**\r\n * Resets paging parameters and performs data loading by calling {@link loadData} if list not in {@link OperationStatus.Progress} state.\r\n * @return result of {@link fetchMethod} if it was called. `null` otherwise.\r\n */\r\n public reloadData(): Observable<any> | Promise<any> | EventEmitter<any> {\r\n if (this.busy) {\r\n return null;\r\n }\r\n this.loadStarted.emit();\r\n this.pager.reset();\r\n const subscribable = this.beginRequest();\r\n this.tryCleanItemsOnReload(false);\r\n this.asyncSubscriber.attach(subscribable, this.reloadDataSuccessCallback, this.reloadDataFailCallback);\r\n this.stateServices.forEach((service: RTStateService) => {\r\n service.persistState(this.filtersService);\r\n });\r\n return subscribable;\r\n }\r\n /**\r\n * Callback which is executed if {@link fetchMethod} execution finished successfully.\r\n */\r\n public loadSuccessCallback(response: ListResponse<any> | any[]): ListResponse<any> | any[] {\r\n const items = Array.isArray(response) ? response : response.