UNPKG

ng-hub-ui-table

Version:

Highly customizable Angular table and list components with pagination, sorting and filtering support.

862 lines (847 loc) 202 kB
import * as i0 from '@angular/core'; import { signal, effect, InjectionToken, Injectable, Optional, Inject, inject, NgModule, Component, ChangeDetectionStrategy, Input, Directive, Pipe, model, input, TemplateRef, ContentChild, ChangeDetectorRef, ElementRef, ViewContainerRef, ViewChild, HostListener, forwardRef, HostBinding, EventEmitter, Output, computed, contentChild, contentChildren, viewChildren } from '@angular/core'; import { Subject, Observable, isObservable, BehaviorSubject, debounceTime, distinctUntilChanged, of, fromEvent } from 'rxjs'; import * as i1 from '@angular/common'; import { NgClass, CommonModule, NgStyle, KeyValuePipe, UpperCasePipe, NgTemplateOutlet, AsyncPipe, DOCUMENT } from '@angular/common'; import * as i1$1 from '@angular/forms'; import { FormBuilder, NG_VALUE_ACCESSOR, ReactiveFormsModule, FormsModule, UntypedFormBuilder, FormGroup } from '@angular/forms'; import { trigger, transition, style, animate } from '@angular/animations'; import { toSignal } from '@angular/core/rxjs-interop'; import { tap, switchMap, map, distinctUntilChanged as distinctUntilChanged$1, takeUntil } from 'rxjs/operators'; const locale$1 = { lang: 'en', data: { LOADING: 'loading', SEARCH: 'search', ROWS_PER_PAGE: 'rows per page', SHOWING_X_OF_Y_ROWS: 'Showing {{ amount }} of {{ total }} rows', NO_RESULTS_FOUND: 'No results found', BATCH_ACTIONS: 'Batch actions', APPLY: 'Apply', ERROR_OCURRED: 'Error ocurred getting data.', FROM: 'from', TO: 'to', SAVE: 'save', NEW_VIEW: 'new view', ADD_VIEW: 'add view', EDIT_VIEW: 'edit view', DELETE_VIEW: 'delete view', SAVE_FILTERS: 'save filters', CANCEL: 'cancel', NO_VIEWS_CREATED: 'No views have been created', SELECT_VIEW: 'Select a view', CLEAN_FILTERS: 'Clean filters', NAME: 'name', A_VIEW_WILL_BE_CREATED: 'A view will be created with the following configuration', MUST_PROVIDE_A_VIEW_NAME: 'Must provide a view name.', MUST_PROVIDE_VIEW_CONDITIONS: 'Must provide the conditions of the view.', TODAY: 'today', YESTERDAY: 'yesterday', CURRENT_WEEK: 'current_week', LAST_WEEK: 'last_week', CURRENT_MONTH: 'current_month', LAST_MONTH: 'last_month', CURRENT_YEAR: 'current_year', LAST_YEAR: 'last_year', NO_ELEMENTS_FOUND: 'No elements found', STAR_TYPING: 'Start typing to begin your search', OPERATIONS: { EQUAL_TO: 'Equal to', VARIABLE: 'Variable' }, CLEAR: 'clean', ADD_RULE: 'add condition', REMOVE_RULE: 'remove condition', MATCH_ALL: 'match all', MATCH_ANY: 'match any', MATCH_MODES: { GREATERTHAN: 'Greater than', GREATERTHANOREQUAL: 'Greater than or equal to', LESSTHAN: 'Less than', LESSTHANOREQUAL: 'Less than or equal to', EQUALS: 's', NOTEQUALS: 'Not equal to', EQUAL: 'Equal to', NOTEQUAL: 'Not equal to', BEFORE: 'Before', BEFOREOREQUAL: 'Before or equal to', AFTER: 'After', AFTEROREQUAL: 'After or equal to', STARTSWITH: 'Starts with', CONTAINS: 'Contains', NOTCONTAINS: 'Does not contain', ENDSWITH: 'Ends with' } }, TRUE: 'true', FALSE: 'false' }; const locale = { lang: 'es', data: { LOADING: 'cargando', SEARCH: 'buscar', ROWS_PER_PAGE: 'Filas por página', SHOWING_X_OF_Y_ROWS: 'Mostrando {{ amount }} de {{ total }} resultados', NO_RESULTS_FOUND: 'No se han encontrado resultados', BATCH_ACTIONS: 'Acciones en lote', APPLY: 'Aplicar', ERROR_OCURRED: 'Ha ocurrido un error obteniendo los datos.', FROM: 'desde', TO: 'hasta', SAVE: 'guardar', NEW_VIEW: 'nueva vista', ADD_VIEW: 'añadir vista', EDIT_VIEW: 'editar vista', DELETE_VIEW: 'eliminar vista', SAVE_FILTERS: 'guardar filtros', CANCEL: 'cancelar', NO_VIEWS_CREATED: 'No hay vistas creadas', SELECT_VIEW: 'Selecciona una vista', CLEAN_FILTERS: 'Limpiar filtros', NAME: 'Nombre', A_VIEW_WILL_BE_CREATED: 'La vista se creará con las siguientes condiciones', MUST_PROVIDE_A_VIEW_NAME: 'El nombre de la vista es requerido.', MUST_PROVIDE_VIEW_CONDITIONS: 'Debe añadir condiciones a la vista.', TODAY: 'hoy', YESTERDAY: 'ayer', CURRENT_WEEK: 'semana actual', LAST_WEEK: 'semana pasada', CURRENT_MONTH: 'mes actual', LAST_MONTH: 'mes pasado', CURRENT_YEAR: 'año actual', LAST_YEAR: 'año pasado', NO_ELEMENTS_FOUND: 'No se han encontrado elementos', STAR_TYPING: 'Comienza a escribir para iniciar la búsqueda', OPERATIONS: { EQUAL_TO: 'Igual a', VARIABLE: 'Variable' }, CLEAR: 'Limpiar', ADD_RULE: 'Añadir condición', REMOVE_RULE: 'eliminar condición', MATCH_ALL: 'Coincidir todo', MATCH_ANY: 'Coincidir algo', MATCH_MODES: { GREATERTHAN: 'Mayor que', GREATERTHANOREQUAL: 'Mayor o igual que', LESSTHAN: 'Menor que', LESSTHANOREQUAL: 'Menor o igual que', EQUALS: 'Igual a', NOTEQUALS: 'No igual a', EQUAL: 'Igual a', NOTEQUAL: 'No igual a', BEFORE: 'Antes de', BEFOREOREQUAL: 'Antes o igual a', AFTER: 'Después de', AFTEROREQUAL: 'Después o igual a', STARTSWITH: 'Comienza con', CONTAINS: 'Contiene', NOTCONTAINS: 'No contiene', ENDSWITH: 'Termina con' } }, TRUE: 'verdadero', FALSE: 'falso' }; const DEFAULT_LANGUAGE = navigator.language.split('-').at(0) ?? 'en'; const DEFAULT_PAGINABLE_CONFIG = { theme: null, mapping: { currentPage: 'currentPage', lastPage: 'lastPage', data: 'data', total: 'total' }, views: { key: 'paginable-table_view_' }, language: DEFAULT_LANGUAGE }; /** * Creates a debounced version of a given signal. The returned signal updates its value * only after the specified debounce time has elapsed since the last change in the source signal. * * @template T - The type of the signal's value. * @param sourceSignal - The source signal to debounce. * @param debounceDelay - Either a number (ms) or a signal<number> indicating debounce delay. * @returns A new signal that emits debounced values from the source signal. */ function debouncedSignal(sourceSignal, debounceDelay = 0) { const debounced = signal(sourceSignal()); effect((onCleanup) => { const delay = typeof debounceDelay === 'number' ? debounceDelay : debounceDelay(); const value = sourceSignal(); const timeout = setTimeout(() => { debounced.set(value); }, delay); onCleanup(() => clearTimeout(timeout)); }); return debounced; } /** * Determines if two objects or two values are equivalent. * * Two objects or values are considered equivalent if at least one of the following is true: * * * Both objects or values pass `===` comparison. * * Both objects or values are of the same type and all of their properties are equal by * comparing them with `equals`. * * @param o1 Object or value to compare. * @param o2 Object or value to compare. * @returns true if arguments are equal. */ function equals(o1, o2) { if (o1 === o2) { return true; } if (o1 === null || o2 === null) { return false; } if (o1 !== o1 && o2 !== o2) { return true; } // NaN === NaN let t1 = typeof o1, t2 = typeof o2, length, key, keySet; if (t1 == t2 && t1 == 'object') { if (Array.isArray(o1)) { if (!Array.isArray(o2)) { return false; } if ((length = o1.length) == o2.length) { for (key = 0; key < length; key++) { if (!equals(o1[key], o2[key])) { return false; } } return true; } } else { if (Array.isArray(o2)) { return false; } keySet = Object.create(null); for (key in o1) { if (!equals(o1[key], o2[key])) { return false; } keySet[key] = true; } for (key in o2) { if (!(key in keySet) && typeof o2[key] !== 'undefined') { return false; } } return true; } } return false; } /** * Checks if a value is defined and not null. * * @param {any} value - is of type `any`, which means it can accept any data type. * * @returns a boolean value. */ function isDefined(value) { return typeof value !== 'undefined' && value !== null; } /** * Checks if the given item is an object and not an array. * * @param {any} item - is of type "any", which means it can be any data type. * * @returns a boolean value. */ function isObject(item) { return item && typeof item === 'object' && !Array.isArray(item); } /** * Recursively merges two objects, combining their properties into a new object. * * @param {any} target - is the object that you want to merge the `source` object into. * @param {any} source - is an object that contains the properties and values that you want to merge into the `target` object. * * @returns the merged object, which is the result of merging the `target` and `source` objects. */ function mergeDeep(target, source) { let output = Object.assign({}, target); if (isObject(target) && isObject(source)) { Object.keys(source).forEach((key) => { if (isObject(source[key])) { if (!(key in target)) { Object.assign(output, { [key]: source[key] }); } else { output[key] = mergeDeep(target[key], source[key]); } } else { Object.assign(output, { [key]: source[key] }); } }); } return output; } /** * Generates a unique ID of a specified length by randomly selecting characters from a predefined set of characters. * * @param {number} length - is the desired length of the generated unique ID. * * @returns a unique id string. */ function generateUniqueId(length) { const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; const charactersLength = characters.length; let result = ''; for (let i = 0; i < length; i++) { const randomIndex = Math.floor(Math.random() * charactersLength); result += characters.charAt(randomIndex); } return result; } /** * Replaces placeholders in a string with corresponding values from a given object. * * @param {string} expr - a string that represents the expression to be interpolated. It may contain * placeholders that will be replaced with values from the `params` object. * @param {any} [params] - an optional object that contains the values to be interpolated into the `expr` * string. It is used to replace placeholders in the `expr` string with actual values. * * @returns returns the interpolated string. If `params` is not provided, it returns the original * `expr` string. If `params` is provided, it replaces placeholders in the `expr` string with corresponding values from `params` * and returns the interpolated string. */ function interpolateString(expr = '', params = {}, templateMatcher = /{{\s?([^{}\s]*)\s?}}/g) { if (!params) { return expr; } return expr.replace(templateMatcher, (substring, b) => { let r = getValue(params, b); return isDefined(r) ? r : substring; }); } /** * Retrieves the value of a nested property from an object using dot notation. * * @param {any} target - the object from which you want to retrieve a value. * @param {string} key - a string that represents the property or nested properties of the `target` object that you want to * retrieve the value from. The `key` can be a single property name or a dot-separated string representing a nested property path. * * @returns the value of the specified key in the target object. */ function getValue(target, key) { let keys = typeof key === 'string' ? key.split('.') : [key]; key = ''; do { key += keys.shift(); if (isDefined(target) && isDefined(target[key]) && (typeof target[key] === 'object' || !keys.length)) { target = target[key]; key = ''; } else if (!keys.length) { target = undefined; } else { key += '.'; } } while (keys.length); return target; } /** * This is not a real service, but it looks like it from the outside. * It's just an InjectionTToken used to import the config object, provided from the outside */ // tslint:disable-next-line: variable-name const PaginableConfigService = new InjectionToken('PaginableConfig'); class PaginableService { get mapping() { return this.config.mapping; } constructor(_config = DEFAULT_PAGINABLE_CONFIG) { this._config = _config; this.initialize(); } initialize() { this.config = mergeDeep(DEFAULT_PAGINABLE_CONFIG, this._config); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: PaginableService, deps: [{ token: PaginableConfigService, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: PaginableService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: PaginableService, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [PaginableConfigService] }] }] }); class PaginableTranslationService { #paginableSvc; constructor() { this.#paginableSvc = inject(PaginableService); this.defaultTranslations = [locale$1, locale].reduce((acc, c) => { acc[c.lang] = c.data; return acc; }, {}); this.translationSource = new Subject(); this.translationObserver = this.translationSource.asObservable(); this.initialize(); } initialize() { this.setTranslations(this.defaultTranslations[this.#paginableSvc.config.language] ?? this.defaultTranslations['en']); } /** * Retrieves a value from a translations object based on a given key. * * @param {string} key - The `key` parameter in the `getTranslation` function is a string that represents the unique identifier or * key for the translation you want to retrieve from the translations object. This key is used to look up the corresponding * translation value in the translations object. * * @returns The value associated with the provided `key` from the `translations` object. */ getTranslation(key) { return getValue(this.translations, key); } /** * Merges the default English translations with the provided translations and updates the translation source. * * @param {Record<string, string> | any} translations - The `translations` parameter in the `setTranslation` method is a parameter that accepts * either an object of type `Record<string, string>` or any other type. If no value is provided, it defaults to an empty object * `{}`. */ setTranslations(translations = {}) { this.translations = { ...locale$1, ...translations }; this.translationSource.next(this.translations); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: PaginableTranslationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: PaginableTranslationService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: PaginableTranslationService, decorators: [{ type: Injectable }], ctorParameters: () => [] }); // import { PaginableTableComponent } from './components/paginable-table/paginable-table.component'; // import { PaginableTableHeaderDirective } from './directives/paginable-table-header.directive'; // import { PaginableTableRowDirective } from './directives/paginable-table-row.directive'; // import { PaginableTableCellDirective } from './directives/paginable-table-cell.directive'; // import { PaginableTableLoadingDirective } from './directives/paginable-table-loading.directive'; // import { PaginableTableErrorDirective } from './directives/paginable-table-error.directive'; // import { PaginableTableExpandingRowDirective } from './directives/paginable-table-expanding-row.directive'; // import { PaginableTableFilterDirective } from './directives/paginable-table-filter.directive'; class TableModule { static forRoot(config) { return { ngModule: TableModule, providers: [ { provide: PaginableConfigService, useValue: config }, PaginableService, PaginableTranslationService ] }; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: TableModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); } static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.8", ngImport: i0, type: TableModule }); } static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: TableModule }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: TableModule, decorators: [{ type: NgModule, args: [{ imports: [ // PaginableTableComponent, // PaginableTableHeaderDirective, // PaginableTableRowDirective, // PaginableTableCellDirective, // PaginableTableLoadingDirective, // PaginableTableErrorDirective, // PaginableTableExpandingRowDirective, // PaginableTableFilterDirective ], exports: [ // PaginableTableComponent, // PaginableTableHeaderDirective, // PaginableTableRowDirective, // PaginableTableCellDirective, // PaginableTableLoadingDirective, // PaginableTableErrorDirective, // PaginableTableExpandingRowDirective, // PaginableTableFilterDirective ], providers: [] }] }] }); /** * Checks if any Font Awesome base class is present in the input string. * * @param {string} input - The `containsFontAwesomeClass` function takes a string input and checks if it contains any of the Font * Awesome base classes such as 'fa-solid', 'fa-regular', 'fa-light', 'fa-duotone', 'fa-thin', or 'fa-brands'. * * @returns A boolean value - `true` if any of the Font Awesome base classes are * present in the input string, and `false` otherwise. */ function containsFontAwesomeClass(input) { // Define an array of Font Awesome base classes to check for const faClasses = [ 'fa-solid', 'fa-regular', 'fa-light', 'fa-duotone', 'fa-thin', 'fa-brands' ]; // Use a regular expression to check if any of the Font Awesome classes are present in the input string const faClassRegex = new RegExp(`\\b(${faClasses.join('|')})\\b`, 'i'); // Return true if any class matches, false otherwise return faClassRegex.test(input); } /** * Checks if a string contains the 'bi' class using a regular expression in TypeScript. * * @param {string} input - The `input` parameter in the `containsBootstrapIconsClass` function is a string that represents a CSS * class or a list of CSS classes. The function checks if the input string contains the Bootstrap Icons class 'bi'. * * @returns returns a boolean value - `true` if the input string contains the 'bi' * class, and `false` otherwise. */ function containsBootstrapIconsClass(input) { // Define a regular expression pattern to match 'bi' class const biClassRegex = /\bbi\b/; // Return true if the class matches, false otherwise return biClassRegex.test(input); } /** * @file icon.component.ts * @description Angular component for rendering icons with support for different icon libraries. */ /** * @component HubIconComponent * @selector hub-icon * @description A versatile icon component that supports multiple icon libraries. * * @example * <hub-icon [config]="iconConfig"></hub-icon> */ class HubIconComponent { /** * @input config * @description Sets the icon configuration. Can be a string or an Icon object. * @type {string | Icon} */ set config(value) { if (!value) { value = ''; } if (typeof value === 'string') { this.value = value; } else { this.type = value.type ?? null; this.variant = value.variant ?? ''; this.value = value.value ?? null; } } /** * @getter classlist * @description Computes the CSS classes for the icon based on its type and value. * @returns {string | null} The computed CSS class string or null if no value is set. */ get classlist() { if (!this.value) { return null; } if (!this.type) { return this.value; } const classlist = []; if (['font-awesome', 'bootstrap'].includes(this.type)) { if (Array.isArray(this.value)) { classlist.push(...this.value); } else { classlist.push(...this.value.split(' ')); } } switch (this.type) { case 'font-awesome': if (!containsFontAwesomeClass(classlist.join(' '))) { classlist.push('fa'); } break; case 'bootstrap': if (!containsFontAwesomeClass(classlist.join(' '))) { classlist.push('bs'); } break; case 'material': classlist.push('material-symbols-outlined'); break; } return classlist.join(' '); } /** * @getter content * @description Gets the content for material icons. * @returns {string | null} The icon content for material icons, or null for other types. */ get content() { return ['material'].includes(this.type) ? this.value : null; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: HubIconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.8", type: HubIconComponent, isStandalone: true, selector: "hub-icon, ng-hub-ui-icon", inputs: { config: "config" }, ngImport: i0, template: "@if (value) {\n <i [ngClass]=\"classlist\">\n {{ content }}\n </i>\n}\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: HubIconComponent, decorators: [{ type: Component, args: [{ selector: 'hub-icon, ng-hub-ui-icon', standalone: true, imports: [NgClass], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (value) {\n <i [ngClass]=\"classlist\">\n {{ content }}\n </i>\n}\n" }] }], propDecorators: { config: [{ type: Input, args: [{ required: true }] }] } }); class PaginableListItemDirective { constructor(template) { this.template = template; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: PaginableListItemDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.8", type: PaginableListItemDirective, isStandalone: true, selector: "[listItemTpt]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: PaginableListItemDirective, decorators: [{ type: Directive, args: [{ selector: '[listItemTpt]' }] }], ctorParameters: () => [{ type: i0.TemplateRef }] }); class PaginableTableNotFoundDirective { constructor(template) { this.template = template; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: PaginableTableNotFoundDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.8", type: PaginableTableNotFoundDirective, isStandalone: true, selector: "[noDataTpt], [paginableTableNotFound]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: PaginableTableNotFoundDirective, decorators: [{ type: Directive, args: [{ selector: '[noDataTpt], [paginableTableNotFound]', standalone: true }] }], ctorParameters: () => [{ type: i0.TemplateRef }] }); class UcfirstPipe { transform(value = '') { return value.charAt(0).toUpperCase() + value.slice(1); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: UcfirstPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.8", ngImport: i0, type: UcfirstPipe, isStandalone: true, name: "ucfirst" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: UcfirstPipe, decorators: [{ type: Pipe, args: [{ name: 'ucfirst', standalone: true }] }] }); class PaginatorComponent { constructor() { this.page = model(1); this.numberOfPages = input(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: PaginatorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.8", type: PaginatorComponent, isStandalone: true, selector: "hub-paginator, paginable-table-paginator", inputs: { page: { classPropertyName: "page", publicName: "page", isSignal: true, isRequired: false, transformFunction: null }, numberOfPages: { classPropertyName: "numberOfPages", publicName: "numberOfPages", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { page: "pageChange" }, ngImport: i0, template: "<nav class=\"pager\" aria-label=\"Page navigation\">\n\t<ul class=\"pager pagination pager__list\">\n\t\t<li\n\t\t\tclass=\"page-item pager__item\"\n\t\t\t[class.pager__item--disabled]=\"page() === 1\"\n\t\t\t[class.disabled]=\"page() === 1\"\n\t\t>\n\t\t\t<a\n\t\t\t\trole=\"button\"\n\t\t\t\tclass=\"page-link pager__link\"\n\t\t\t\thref=\"javascript:{}\"\n\t\t\t\t(click)=\"page.set(1)\"\n\t\t\t>\n\t\t\t\t<i class=\"pager__icon fa fa-angle-double-left\"></i>\n\t\t\t</a>\n\t\t</li>\n\t\t<li\n\t\t\tclass=\"page-item pager__item\"\n\t\t\t[class.pager__item--disabled]=\"page() <= 1\"\n\t\t\t[class.disabled]=\"page() <= 1\"\n\t\t>\n\t\t\t<a\n\t\t\t\trole=\"button\"\n\t\t\t\tclass=\"page-link pager__link\"\n\t\t\t\thref=\"javascript:{}\"\n\t\t\t\taria-label=\"Previous\"\n\t\t\t\t(click)=\"page.set(page() - 1)\"\n\t\t\t>\n\t\t\t\t<span class=\"pager__icon-wrapper\" aria-hidden=\"true\">\n\t\t\t\t\t<i class=\"pager__icon fa fa-angle-left\"></i>\n\t\t\t\t</span>\n\t\t\t</a>\n\t\t</li>\n\t\t@if (page() - 1 > 1) {\n\t\t\t<li class=\"page-item pager__item pager__item--disabled disabled\">\n\t\t\t\t<a\n\t\t\t\t\trole=\"button\"\n\t\t\t\t\tclass=\"page-link pager__link\"\n\t\t\t\t\thref=\"javascript:{}\"\n\t\t\t\t\t>...</a\n\t\t\t\t>\n\t\t\t</li>\n\t\t}\n\t\t<li class=\"page-item pager__item\">\n\t\t\t@if (page() - 1 >= 1) {\n\t\t\t\t<a\n\t\t\t\t\trole=\"button\"\n\t\t\t\t\tclass=\"page-link pager__link\"\n\t\t\t\t\thref=\"javascript:{}\"\n\t\t\t\t\t(click)=\"page.set(page() - 1)\"\n\t\t\t\t\t>{{ page() - 1 }}</a\n\t\t\t\t>\n\t\t\t}\n\t\t</li>\n\t\t<li class=\"page-item pager__item pager__item--active active\">\n\t\t\t<a\n\t\t\t\trole=\"button\"\n\t\t\t\tclass=\"page-link pager__link\"\n\t\t\t\thref=\"javascript:{}\"\n\t\t\t\t>{{ page() }}</a\n\t\t\t>\n\t\t</li>\n\t\t@if (numberOfPages()) {\n\t\t\t<li class=\"page-item pager__item\">\n\t\t\t\t@if (page() + 1 <= numberOfPages()!) {\n\t\t\t\t\t<a\n\t\t\t\t\t\trole=\"button\"\n\t\t\t\t\t\tclass=\"page-link pager__link\"\n\t\t\t\t\t\thref=\"javascript:{}\"\n\t\t\t\t\t\t(click)=\"page.set(page() + 1)\"\n\t\t\t\t\t\t>{{ page() + 1 }}</a\n\t\t\t\t\t>\n\t\t\t\t}\n\t\t\t</li>\n\t\t\t@if (page() + 1 < numberOfPages()!) {\n\t\t\t\t<li\n\t\t\t\t\tclass=\"page-item pager__item pager__item--disabled disabled\"\n\t\t\t\t>\n\t\t\t\t\t<a\n\t\t\t\t\t\trole=\"button\"\n\t\t\t\t\t\tclass=\"page-link pager__link\"\n\t\t\t\t\t\thref=\"javascript:{}\"\n\t\t\t\t\t\t>...</a\n\t\t\t\t\t>\n\t\t\t\t</li>\n\t\t\t}\n\t\t\t<li\n\t\t\t\tclass=\"page-item pager__item\"\n\t\t\t\t[class.pager__item--disabled]=\"page() >= numberOfPages()!\"\n\t\t\t\t[class.disabled]=\"page() >= numberOfPages()!\"\n\t\t\t>\n\t\t\t\t<a\n\t\t\t\t\trole=\"button\"\n\t\t\t\t\tclass=\"page-link pager__link\"\n\t\t\t\t\thref=\"javascript:{}\"\n\t\t\t\t\taria-label=\"Next\"\n\t\t\t\t\t(click)=\"page.set(page() + 1)\"\n\t\t\t\t>\n\t\t\t\t\t<span class=\"pager__icon-wrapper\" aria-hidden=\"true\">\n\t\t\t\t\t\t<i class=\"pager__icon fa fa-angle-right\"></i>\n\t\t\t\t\t</span>\n\t\t\t\t</a>\n\t\t\t</li>\n\t\t\t<li\n\t\t\t\tclass=\"page-item pager__item\"\n\t\t\t\t[class.pager__item--disabled]=\"page() === numberOfPages()\"\n\t\t\t\t[class.disabled]=\"page() === numberOfPages()\"\n\t\t\t>\n\t\t\t\t<a\n\t\t\t\t\trole=\"button\"\n\t\t\t\t\tclass=\"page-link pager__link\"\n\t\t\t\t\thref=\"javascript:{}\"\n\t\t\t\t\t(click)=\"page.set(numberOfPages() ?? 1)\"\n\t\t\t\t>\n\t\t\t\t\t<i class=\"pager__icon fa fa-angle-double-right\"></i>\n\t\t\t\t</a>\n\t\t\t</li>\n\t\t} @else {\n\t\t\t<li class=\"page-item pager__item\">\n\t\t\t\t<a\n\t\t\t\t\trole=\"button\"\n\t\t\t\t\tclass=\"page-link pager__link\"\n\t\t\t\t\thref=\"javascript:{}\"\n\t\t\t\t\taria-label=\"Next\"\n\t\t\t\t\t(click)=\"page.set(page() + 1)\"\n\t\t\t\t>\n\t\t\t\t\t<span class=\"pager__icon-wrapper\" aria-hidden=\"true\">\n\t\t\t\t\t\t<i class=\"pager__icon fa fa-angle-right\"></i>\n\t\t\t\t\t</span>\n\t\t\t\t</a>\n\t\t\t</li>\n\t\t}\n\t</ul>\n</nav>\n", styles: [".pager .pager__list{margin-bottom:0}\n"] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: PaginatorComponent, decorators: [{ type: Component, args: [{ selector: 'hub-paginator, paginable-table-paginator', standalone: true, template: "<nav class=\"pager\" aria-label=\"Page navigation\">\n\t<ul class=\"pager pagination pager__list\">\n\t\t<li\n\t\t\tclass=\"page-item pager__item\"\n\t\t\t[class.pager__item--disabled]=\"page() === 1\"\n\t\t\t[class.disabled]=\"page() === 1\"\n\t\t>\n\t\t\t<a\n\t\t\t\trole=\"button\"\n\t\t\t\tclass=\"page-link pager__link\"\n\t\t\t\thref=\"javascript:{}\"\n\t\t\t\t(click)=\"page.set(1)\"\n\t\t\t>\n\t\t\t\t<i class=\"pager__icon fa fa-angle-double-left\"></i>\n\t\t\t</a>\n\t\t</li>\n\t\t<li\n\t\t\tclass=\"page-item pager__item\"\n\t\t\t[class.pager__item--disabled]=\"page() <= 1\"\n\t\t\t[class.disabled]=\"page() <= 1\"\n\t\t>\n\t\t\t<a\n\t\t\t\trole=\"button\"\n\t\t\t\tclass=\"page-link pager__link\"\n\t\t\t\thref=\"javascript:{}\"\n\t\t\t\taria-label=\"Previous\"\n\t\t\t\t(click)=\"page.set(page() - 1)\"\n\t\t\t>\n\t\t\t\t<span class=\"pager__icon-wrapper\" aria-hidden=\"true\">\n\t\t\t\t\t<i class=\"pager__icon fa fa-angle-left\"></i>\n\t\t\t\t</span>\n\t\t\t</a>\n\t\t</li>\n\t\t@if (page() - 1 > 1) {\n\t\t\t<li class=\"page-item pager__item pager__item--disabled disabled\">\n\t\t\t\t<a\n\t\t\t\t\trole=\"button\"\n\t\t\t\t\tclass=\"page-link pager__link\"\n\t\t\t\t\thref=\"javascript:{}\"\n\t\t\t\t\t>...</a\n\t\t\t\t>\n\t\t\t</li>\n\t\t}\n\t\t<li class=\"page-item pager__item\">\n\t\t\t@if (page() - 1 >= 1) {\n\t\t\t\t<a\n\t\t\t\t\trole=\"button\"\n\t\t\t\t\tclass=\"page-link pager__link\"\n\t\t\t\t\thref=\"javascript:{}\"\n\t\t\t\t\t(click)=\"page.set(page() - 1)\"\n\t\t\t\t\t>{{ page() - 1 }}</a\n\t\t\t\t>\n\t\t\t}\n\t\t</li>\n\t\t<li class=\"page-item pager__item pager__item--active active\">\n\t\t\t<a\n\t\t\t\trole=\"button\"\n\t\t\t\tclass=\"page-link pager__link\"\n\t\t\t\thref=\"javascript:{}\"\n\t\t\t\t>{{ page() }}</a\n\t\t\t>\n\t\t</li>\n\t\t@if (numberOfPages()) {\n\t\t\t<li class=\"page-item pager__item\">\n\t\t\t\t@if (page() + 1 <= numberOfPages()!) {\n\t\t\t\t\t<a\n\t\t\t\t\t\trole=\"button\"\n\t\t\t\t\t\tclass=\"page-link pager__link\"\n\t\t\t\t\t\thref=\"javascript:{}\"\n\t\t\t\t\t\t(click)=\"page.set(page() + 1)\"\n\t\t\t\t\t\t>{{ page() + 1 }}</a\n\t\t\t\t\t>\n\t\t\t\t}\n\t\t\t</li>\n\t\t\t@if (page() + 1 < numberOfPages()!) {\n\t\t\t\t<li\n\t\t\t\t\tclass=\"page-item pager__item pager__item--disabled disabled\"\n\t\t\t\t>\n\t\t\t\t\t<a\n\t\t\t\t\t\trole=\"button\"\n\t\t\t\t\t\tclass=\"page-link pager__link\"\n\t\t\t\t\t\thref=\"javascript:{}\"\n\t\t\t\t\t\t>...</a\n\t\t\t\t\t>\n\t\t\t\t</li>\n\t\t\t}\n\t\t\t<li\n\t\t\t\tclass=\"page-item pager__item\"\n\t\t\t\t[class.pager__item--disabled]=\"page() >= numberOfPages()!\"\n\t\t\t\t[class.disabled]=\"page() >= numberOfPages()!\"\n\t\t\t>\n\t\t\t\t<a\n\t\t\t\t\trole=\"button\"\n\t\t\t\t\tclass=\"page-link pager__link\"\n\t\t\t\t\thref=\"javascript:{}\"\n\t\t\t\t\taria-label=\"Next\"\n\t\t\t\t\t(click)=\"page.set(page() + 1)\"\n\t\t\t\t>\n\t\t\t\t\t<span class=\"pager__icon-wrapper\" aria-hidden=\"true\">\n\t\t\t\t\t\t<i class=\"pager__icon fa fa-angle-right\"></i>\n\t\t\t\t\t</span>\n\t\t\t\t</a>\n\t\t\t</li>\n\t\t\t<li\n\t\t\t\tclass=\"page-item pager__item\"\n\t\t\t\t[class.pager__item--disabled]=\"page() === numberOfPages()\"\n\t\t\t\t[class.disabled]=\"page() === numberOfPages()\"\n\t\t\t>\n\t\t\t\t<a\n\t\t\t\t\trole=\"button\"\n\t\t\t\t\tclass=\"page-link pager__link\"\n\t\t\t\t\thref=\"javascript:{}\"\n\t\t\t\t\t(click)=\"page.set(numberOfPages() ?? 1)\"\n\t\t\t\t>\n\t\t\t\t\t<i class=\"pager__icon fa fa-angle-double-right\"></i>\n\t\t\t\t</a>\n\t\t\t</li>\n\t\t} @else {\n\t\t\t<li class=\"page-item pager__item\">\n\t\t\t\t<a\n\t\t\t\t\trole=\"button\"\n\t\t\t\t\tclass=\"page-link pager__link\"\n\t\t\t\t\thref=\"javascript:{}\"\n\t\t\t\t\taria-label=\"Next\"\n\t\t\t\t\t(click)=\"page.set(page() + 1)\"\n\t\t\t\t>\n\t\t\t\t\t<span class=\"pager__icon-wrapper\" aria-hidden=\"true\">\n\t\t\t\t\t\t<i class=\"pager__icon fa fa-angle-right\"></i>\n\t\t\t\t\t</span>\n\t\t\t\t</a>\n\t\t\t</li>\n\t\t}\n\t</ul>\n</nav>\n", styles: [".pager .pager__list{margin-bottom:0}\n"] }] }] }); class PaginableListComponent { constructor() { this.#fb = inject(FormBuilder); this.bindLabel = 'label'; this.bindChildren = 'children'; this._options = { cursor: 'default', hoverableRows: false, striped: null, variant: null, searchable: false, collapsed: true }; this.form = this.#fb.array([]); this.value = []; // NOTE: Otros this.isDisabled = false; this.onChange = () => { }; this.onTouch = () => { }; // NOTE: Filters this.searchFG = this.#fb.control({}); // NOTE: Batch actions /** * Collection of actions for items * * @type {PaginableTableRowAction[]} * @memberof PaginableTableComponent */ this._batchActions = []; } #fb; get options() { return this._options; } set options(v) { this._options = v; this.buildForm(this.form, this._items); } get items() { return this._items; } set items(v) { this._items = v ?? []; this.form.clear(); this.buildForm(this.form, this._items); } get batchActions() { return this._batchActions; } set batchActions(v) { this._batchActions = v.map((b) => { if (b.buttons) { b = { fill: null, position: 'start', color: 'light', ...b }; } return b; }); } // NOTE: Control access value writeValue(value = []) { // this.value = this.buildValue(value); } registerOnChange(fn) { this.onChange = fn; } registerOnTouched(fn) { this.onTouch = fn; } setDisabledState(isDisabled) { this.isDisabled = isDisabled; } /** * Handles the action to be executed in a batch * * @param {Event} event * @memberof PaginableTableComponent */ handleBatchAction(event) { event.handler(this.value); } buildForm(form, items) { form.clear(); for (const index in items) { if (Object.prototype.hasOwnProperty.call(items, index)) { const item = items[index]; const group = this.#fb.group({ selected: [true], collapsed: [this.options.collapsed], data: [item], children: this.#fb.array([]) }); group.patchValue(item); if (item[this.bindChildren]?.length) { this.buildForm(group.get('children'), item[this.bindChildren]); // newItem['children'] = this.buildValue(children); } form.push(group); } } } buildValue(items) { const value = []; for (const item of items) { const { children, ...newItem } = item; if (children?.length) { newItem['children'] = this.buildValue(children); } value.push({ ...newItem, collapsed: true }); } return value; } toggleCollapsed(control) { control.patchValue(!control.value); } /** * Emits a structured click event for the clicked list item, including metadata and state. * * This method is typically called when an item in the list is clicked. It extracts contextual * information such as depth, index, selection state, and expansion state, then passes it to * the user-defined `clickFn` callback. * * If a `bindLabel` is configured, the emitted `value` will be derived from that property; * otherwise, the full item will be passed as `value`. * * @param item - The list item object, including `selected` and `collapsed` state. * @param depth - The nesting depth of the item within a tree structure (0 = root level). * @param index - The position of the item in the current visible list or page. * @param event - The native `MouseEvent` that triggered the click. * * @remarks * If the `clickFn` callback is not defined, the method exits early and no event is emitted. */ onItemClick({ collapsed, selected, ...item }, depth, index, event) { if (!this.clickFn) { return; } this.clickFn({ depth, index, selected, collapsed, value: this.bindLabel ? getValue(item, this.bindLabel) : item, item: item, mouseEvent: event }); } onPageClicked(page) { // if (!this.data) { // return; // } // this.data.currentPage = page; // this.triggerTheParamChanges(); } filter() { // if (!this.data) { // return; // } // this.data.currentPage = 1; // this.filterChange.emit({ // searchText: this.searchFG?.value ?? null, // specificSearch: this.specificSearchFG?.value ?? null // }); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: PaginableListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.8", type: PaginableListComponent, isStandalone: true, selector: "hub-list, hub-ui-list, hub-paginable-list", inputs: { bindValue: "bindValue", bindLabel: "bindLabel", bindChildren: "bindChildren", selectable: "selectable", options: "options", items: "items", clickFn: "clickFn", batchActions: "batchActions" }, host: { classAttribute: "d-flex flex-column gap-4" }, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: PaginableListComponent, multi: true } ], queries: [{ propertyName: "itemTpt", first: true, predicate: PaginableListItemDirective, descendants: true, read: TemplateRef }, { propertyName: "noDataTpt", first: true, predicate: PaginableTableNotFoundDirective, descendants: true, read: TemplateRef }], ngImport: i0, template: "@if (batchActions.length || options.searchable) {\n\t<div class=\"d-flex justify-content-between gap-3\">\n\t\t<div class=\"ms-auto\">\n\t\t\t<div class=\"d-flex justify-content-end gap-2\">\n\t\t\t\t@if (options.searchable) {\n\t\t\t\t\t<div class=\"input-group search_input-group\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\tclass=\"form-control border border-light search_input\"\n\t\t\t\t\t\t\t[formControl]=\"searchFG\"\n\t\t\t\t\t\t\t[placeholder]=\"'SEARCH' | ucfirst\"\n\t\t\t\t\t\t\t(keyup.enter)=\"filter()\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<div class=\"input-group-append\">\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\tclass=\"btn btn-light search_button\"\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t(click)=\"filter()\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<i class=\"fa fa-search\" aria-hidden=\"true\"></i>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t</div>\n\t\t</div>\n\t</div>\n}\n\n<ng-container\n\t[ngTemplateOutlet]=\"containerTpt\"\n\t[ngTemplateOutletContext]=\"{\n\t\tformArray: form,\n\t\tisRoot: true,\n\t\titems: items,\n\t\tdepth: 0\n\t}\"\n></ng-container>\n\n<ng-template\n\t#containerTpt\n\tlet-formArray=\"formArray\"\n\tlet-isRoot=\"isRoot\"\n\tlet-items=\"items\"\n\tlet-depth=\"depth\"\n>\n\t<ul class=\"list-group list-group-flush tree-list\" [class.root]=\"isRoot\">\n\t\t@for (\n\t\t\tformGroup of formArray.controls;\n\t\t\ttrack formGroup;\n\t\t\tlet index = $index\n\t\t) {\n\t\t\t<li\n\t\t\t\tclass=\"list-group-item tree-list__node\"\n\t\t\t\t[class.clickable-item]=\"clickFn\"\n\t\t\t\t[formGroup]=\"formGroup\"\n\t\t\t\t(click)=\"\n\t\t\t\t\tonItemClick(formGroup.value, depth, index, $event);\n\t\t\t\t\t$event.preventDefault();\n\t\t\t\t\t$event.stopPropagation()\n\t\t\t\t\"\n\t\t\t>\n\t\t\t\t@if (formGroup.value; as item) {\n\t\t\t\t\t<div\n\t\t\t\t\t\tclass=\"d-flex justify-content-between align-items-center gap-2\"\n\t\t\t\t\t>\n\t\t\t\t\t\t@if (selectable === 'multiple') {\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\tclass=\"form-check-input tree-list__node-checkbox\"\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tformControlName=\"selected\"\n\t\t\t\t\t\t\t\t(click)=\"$event.stopPropagation()\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t<div class=\"tree-list__node-label\">\n\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t[ngTemplateOutlet]=\"itemTpt || defaultItemtTpt\"\n\t\t\t\t\t\t\t\t[ngTemplateOutletContext]=\"{\n\t\t\t\t\t\t\t\t\tdata: items[index],\n\t\t\t\t\t\t\t\t\tdepth,\n\t\t\t\t\t\t\t\t\tindex,\n\t\t\t\t\t\t\t\t\tcollapsed:\n\t\t\t\t\t\t\t\t\t\t!!formGroup.get('collapsed').value,\n\t\t\t\t\t\t\t\t\tselected: formGroup.get('selected').value\n\t\t\t\t\t\t\t\t}\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclass=\"tree-list__node-buttons d-flex align-items-center gap-2\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t@if (item[bindChildren]?.length) {\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\tclass=\"btn\"\n\t\t\t\t\t\t\t\t\t(click)=\"\n\t\t\t\t\t\t\t\t\t\ttoggleCollapsed(\n\t\t\t\t\t\t\t\t\t\t\tformGroup.get('collapsed')\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t$event.preventDefault();\n\t\t\t\t\t\t\t\t\t\t$event.stopPropagation()\n\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<i\n\t\t\t\t\t\t\t\t\t\tclass=\"fas\"\n\t\t\t\t\t\t\t\t\t\t[ngClass]=\"\n\t\t\t\t\t\t\t\t\t\t\titem.collapsed\n\t\t\t\t\t\t\t\t\t\t\t\t? 'fa-chevron-down'\n\t\t\t\t\t\t\t\t\t\t\t\t: 'fa-chevron-up'\n\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t></i>\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t@if (item[bindChildren]?.length && !item.collapsed) {\n\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t[ngTemplateOutlet]=\"containerTpt\"\n\t\t\t\t\t\t\t[ngTemplateOutletContext]=\"{\n\t\t\t\t\t\t\t\tformArray: formGroup.get('children'),\n\t\t\t\t\t\t\t\titems: items[index][bindChildren],\n\t\t\t\t\t\t\t\tdepth: depth + 1\n\t\t\t\t\t\t\t}\"\n\t\t\t\t\t\t></ng-container>\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t</li>\n\t\t}\n\t</ul>\n</ng-template>\n\n<ng-template #noDataRowTpt>\n\t<li class=\"list-group-item\">\n\t\t<ng-container [ngTemplateOutlet]=\"noDataTpt || defaultNoDataTpt\">\n\t\t</ng-container>\n\t</li>\n</ng-template>\n\n<ng-template #defaultNoDataTpt>\n\t<div class=\"alert alert-info d-flex align-items-center m-4\" role=\"alert\">\n\t\t<i class=\"fa fa-info fa-2x me-4 mr-4\" aria-hidden=\"true\"></i>\n\t\t{{ 'NO_RESULTS_FOUND' | ucfirst }}\n\t</div>\n</ng-template>\n\n<ng-template #defaultItemtTpt let-data=\"data\">\n\t{{ data[bindLabel] }}\n</ng-template>\n", styles: [":host{display:block;overflow:auto}.tree-list:not(.root)>.tree-list__node{padding-right:0}.tree-list__node-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-grow:1}.clickable-item{cursor:pointer}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "pipe", type: UcfirstPipe, name: "ucfirst" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: PaginableListComponent, decorators: [{ type: Component, args: [{ selector: 'hub-list, hub-ui-list, hub-paginable-list', host: { class: 'd-flex flex-column gap-4' }, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: PaginableListComponent, multi: true } ], imports: [ CommonModule, ReactiveFormsModule, PaginatorComponent, PaginableTableNotF