UNPKG

@ipi-soft/ng-components

Version:

Custom Angular Components

529 lines (528 loc) 58.6 kB
import * as i0 from '@angular/core'; import { EventEmitter, Component, ViewChild, ViewChildren, Input, Output, HostListener } from '@angular/core'; import { formatDate, NgStyle, NgClass, DecimalPipe, NgTemplateOutlet } from '@angular/common'; import * as i2 from '@angular/forms'; import { Validators } from '@angular/forms'; import { IpiChipComponent } from '@ipi-soft/ng-components/chip'; import { IpiImageComponent } from '@ipi-soft/ng-components/image'; import { IpiCheckboxComponent } from '@ipi-soft/ng-components/checkbox'; import { IpiSelectComponent } from '@ipi-soft/ng-components/select'; import * as i1 from '@ipi-soft/ng-components/services'; import { MobileOS } from '@ipi-soft/ng-components/services'; import { TooltipPosition, IpiTooltipDirective } from '@ipi-soft/ng-components/tooltip'; var IpiTableColumnType; (function (IpiTableColumnType) { IpiTableColumnType[IpiTableColumnType["Text"] = 0] = "Text"; IpiTableColumnType[IpiTableColumnType["Date"] = 1] = "Date"; IpiTableColumnType[IpiTableColumnType["Currency"] = 2] = "Currency"; IpiTableColumnType[IpiTableColumnType["Chip"] = 3] = "Chip"; IpiTableColumnType[IpiTableColumnType["Number"] = 4] = "Number"; IpiTableColumnType[IpiTableColumnType["NumberMath"] = 5] = "NumberMath"; IpiTableColumnType[IpiTableColumnType["Actions"] = 6] = "Actions"; IpiTableColumnType[IpiTableColumnType["Checkbox"] = 7] = "Checkbox"; })(IpiTableColumnType || (IpiTableColumnType = {})); class IpiTableComponent { constructor(osService, formBuilder, changeDetectorRef) { this.osService = osService; this.formBuilder = formBuilder; this.changeDetectorRef = changeDetectorRef; this.DropdownElement = null; this.sortIndex = 0; this.sortable = true; this.sortDirection = 'asc'; this.pageSize = 10; this.currentPage = 0; this.pageable = false; this.pageSizeOptions = [10, 25, 50, 100, 1000]; this.tableChange = new EventEmitter(); this.tooltipPosition = TooltipPosition; this.TableColumnType = IpiTableColumnType; this.isDropdown = false; changeDetectorRef.detach(); } onClick(event) { // TO DO: When two tables exists the dropdown from first table is not closed if (event.target !== this.DropdownElement?.nativeElement && event.target) { if (this.currentActiveRow) { this.currentActiveRow.isDropdown = false; } this.changeDetectorRef.detectChanges(); } } onScroll() { for (let item of this.moreActionsList) { if (item.nativeElement.classList.contains('opened') && this.DropdownElement) { let actionPos = item.nativeElement.getBoundingClientRect(); if (this.osService.mobileOS === MobileOS.iOS) { actionPos.y += window.visualViewport.offsetTop; } this.DropdownElement.nativeElement.style.top = `${actionPos.y + actionPos.height}px`; break; } } } onResize() { for (let item of this.moreActionsList) { if (item.nativeElement.classList.contains('opened') && this.DropdownElement) { let actionPos = item.nativeElement.getBoundingClientRect(); if (this.osService.mobileOS === MobileOS.iOS) { actionPos.x += window.visualViewport.offsetLeft; } const dropdownWidth = this.DropdownElement.nativeElement.offsetWidth; const left = actionPos.x + dropdownWidth / 2 < window.innerWidth - 10 ? actionPos.x : window.innerWidth - 10 - dropdownWidth; this.DropdownElement.nativeElement.style.transform = left === actionPos.x ? 'translateX(-50%)' : ''; this.DropdownElement.nativeElement.style.left = left + 'px'; break; } } } ngAfterViewInit() { const from = this.currentPage * this.pageSize; const to = from + this.pageSize; if (this.serverSide && !this.data) { this.tableChange.emit({ from, to, sort: { columnValue: this.columns[this.sortIndex].value, sortDirection: this.sortDirection } }); } if (!this.serverSide) { this.buildPaginationSelectDependecies(); } } ngOnChanges(changes) { if (changes['isLoading'] && changes['isLoading'].currentValue === false) { if (this.data) { this._dataLength = this.data.length; } } if (changes['data'] && changes['data'].currentValue) { if (this.serverSide) { this.filteredData = this.data; this.preprocessData(); } else { this.filteredData = this.filter ? this.filterData() : this.data; this.preprocessData(); setTimeout(() => { this.sortColumn(this.columns[this.sortIndex], false); }); } this._dataLength = this.dataLength ? this.dataLength : this.filteredData.length; } if (changes['filter'] && !changes['filter'].firstChange) { // The two ways of filtering data -> local and serverSide if (!this.serverSide) { this.filteredData = this.filterData(); this._dataLength = this.filteredData.length; setTimeout(() => { this.goToFirstPage(); }); } else { setTimeout(() => { this.currentPage = 0; this.tableChange.emit({ from: 0, to: 0 + this.pageSize, filter: this.filter, sort: { columnValue: this.columns[this.sortIndex].value, sortDirection: this.sortDirection } }); }); } } if (changes['pageable'] && changes['pageable'].currentValue !== undefined) { this.buildPaginationSelectDependecies(); } const from = this.currentPage * this.pageSize; const to = Math.min(this._dataLength, from + this.pageSize); this.calculateTotalPages(); this.updateVisibleRange(from, to); this.changeDetectorRef.detectChanges(); } getPageData() { if (!this.filteredData) { return []; } if (!this.pageable) { return this.filteredData; } if (!this.serverSide) { const start = this.currentPage * this.pageSize; const end = start + this.pageSize; return this.filteredData.slice(start, end); } return this.filteredData; } updateVisibleRange(start, end) { this.visibleRange = `Showing ${start + 1} to ${end} of ${this._dataLength} results`; this.changeDetectorRef.detectChanges(); } calculateTotalPages() { this.totalPages = Math.ceil(this._dataLength / this.pageSize); } goToPage(page) { this.currentPage = page; this.calculatePages(); } goToPreviousPage() { this.currentPage--; this.calculatePages(); } goToNextPage() { this.currentPage++; this.calculatePages(); } goToFirstPage() { this.currentPage = 0; this.calculatePages(); } goToLastPage() { this.currentPage = this.totalPages - 1; this.calculatePages(); } cellEdit(event, column, currentPageRowIndex) { const dataIndex = currentPageRowIndex + (this.currentPage * this.pageSize); if (column.indexOf('.') === -1) { this.filteredData[dataIndex][column] = event.target.innerText; } else { // Nested properties let data = this.filteredData[dataIndex]; const fullPath = column.split('.'); for (let i = 0; i < fullPath.length; i++) { if (i < fullPath.length - 1) { data = data[fullPath[i]]; } else { data[fullPath[i]] = event.target.innerText; } } } } formatDate(dateAsString, formatDateOptions = { format: 'shortDate', locale: 'en-US', timezone: undefined }) { return formatDate(dateAsString, formatDateOptions.format, formatDateOptions.locale, formatDateOptions.timezone); } getClass(row, column) { if (!column.class) { return ''; } if (typeof column.class === 'string') { return column.class; } return { [column.class[this.getDescendantProp(row, column)]]: true }; } getDescendantProp(row, column) { if (!column.value || column.value === null) { return column.label; } // Case only on preprocessing - returns any numbers that have been passed in the column.value string if (isFinite(Number(column.value))) { return parseFloat(column.value); } if (column.type === this.TableColumnType.NumberMath) { return row[column.value]; } return column.value.split('.') .reduce((prev, curr) => { if (prev && curr in prev) { return prev[curr]; } else { return undefined; } }, row); } getChipLabel(row, column) { if (!column.chipLabel) { return column.label ? column.label : this.getDescendantProp(row, column); } return column.chipLabel[this.getDescendantProp(row, column)]; } getCheckboxState(row, column) { let state = this.getDescendantProp(row, column); if (typeof state !== 'boolean') { return false; } return state; } handleActionShow(action, row, column) { if (!action.showOn || action.showOn.length === 0) { return true; } return action.showOn.every((condition) => { const rowValue = this.getDescendantProp(row, { value: condition.property }); const ifValueExists = !!rowValue; if (condition.exists != null && condition.exists === ifValueExists) { return ifValueExists; } return condition.values.includes(rowValue); }); } sortColumn(column, changeSortDirection = true) { if (!this.sortable) { return; } if (!column.value) { return; } if (changeSortDirection) { if (this.sortIndex == this.columns.indexOf(column)) { switch (this.sortDirection) { case 'asc': this.sortDirection = 'desc'; break; case 'desc': this.sortDirection = 'asc'; break; default: this.sortDirection = 'asc'; break; } } else { this.sortDirection = 'asc'; this.sortIndex = this.columns.indexOf(column); } } if (this.serverSide) { this.currentPage = 0; const start = this.currentPage * this.pageSize; const end = Math.min(this._dataLength, start + this.pageSize); this.tableChange.emit({ from: start, to: end, filter: this.filter, sort: { columnValue: this.columns[this.sortIndex].value, sortDirection: this.sortDirection } }); } else { this.goToFirstPage(); this.sortData(this.filteredData, column); } if (this.currentActiveRow) { this.currentActiveRow.isDropdown = false; } this.changeDetectorRef.detectChanges(); } async toggleActionDropdown(row, event) { // Prevent the event to be processed by the Hostlistener event.stopPropagation(); let actionPos; if (this.currentActiveRow) { this.currentActiveRow.isDropdown = false; } row.isDropdown = !row.isDropdown; this.currentActiveRow = row; this.changeDetectorRef.detectChanges(); for (let i of this.moreActionsList) { if (i.nativeElement.classList.contains('opened')) { actionPos = i.nativeElement.getBoundingClientRect(); } } if (this.DropdownElement) { if (this.osService.mobileOS === MobileOS.iOS) { actionPos.y += window.visualViewport.offsetTop; actionPos.x += window.visualViewport.offsetLeft; } const dropdownWidth = this.DropdownElement.nativeElement.offsetWidth; const left = actionPos.x + dropdownWidth / 2 < window.innerWidth - 10 ? actionPos.x : window.innerWidth - 10 - dropdownWidth; this.DropdownElement.nativeElement.style.transform = left === actionPos.x ? 'translateX(-50%)' : ''; this.DropdownElement.nativeElement.style.left = left + 'px'; this.DropdownElement.nativeElement.style.top = `${actionPos.y + actionPos.height}px`; } } preprocessData() { if (!this.data || this.data.length === 0) { return; } this.columns.forEach(column => { if (!column.value || column.type !== this.TableColumnType.NumberMath) { return; } this.data.forEach(row => { const result = this.calculateNumberMathCell(row, column); row[column.value] = result; }); }); } calculateNumberMathCell(row, column) { const expression = column.value.replace(/([a-zA-Z_][a-zA-Z0-9_.]*|\d*\.?\d+)/g, (match) => { let value = this.getDescendantProp(row, { value: match }); return (typeof value === 'number') ? value : parseFloat(value) || 0; }); let fn = new Function(`return ${expression};`); let result = fn(); if (Number.isFinite(result)) { return result; } return this.getDescendantProp(row, column); } filterData() { if (!this.data || this.data.length === 0) { return []; } let arrayToReturn = []; for (let row of this.data) { for (let column of this.columns) { if (!column.value) { continue; } let access = row[column.value]; if (column.value.indexOf('.') !== -1 && column.type !== this.TableColumnType.NumberMath) { access = this.getAccessIfColumnValueIsObject(row, column); } if (access === undefined) { break; } const rowPropAsString = JSON.stringify(access).toLowerCase(); if (rowPropAsString.includes(this.filter.toLowerCase())) { arrayToReturn.push(row); break; } } } return arrayToReturn; } sortData(data, column) { if (!data || data.length === 0 || !Array.isArray(data)) { return []; } if (column) { this.sortByProperty(data, column); } return data; } sortByProperty(arr, column) { if (this.sortDirection === 'desc') { this.sortReverse(arr, column); return; } return arr.sort((a, b) => { let valueA = a[column.value]; let valueB = b[column.value]; let access = column.value; if (access.indexOf('.') !== -1 && column.type !== this.TableColumnType.NumberMath) { valueA = this.getAccessIfColumnValueIsObject(a, column); valueB = this.getAccessIfColumnValueIsObject(b, column); } else { if (typeof valueA === 'string' && valueB !== undefined) { valueA = valueA.toLowerCase(); valueB = valueB.toLowerCase(); } } if (typeof valueA === 'number' && typeof valueB === 'number') { return valueA - valueB; } if (!valueA && valueB) return -1; if (valueA && !valueB) return 1; if (!valueA && !valueB) return 0; if (valueA < valueB) return -1; if (valueA > valueB) return 1; return 0; }); } sortReverse(arr, column) { return arr.sort((a, b) => { let valueA = a[column.value]; let valueB = b[column.value]; let access = column.value; if (access.indexOf('.') !== -1 && column.type !== this.TableColumnType.NumberMath) { valueA = this.getAccessIfColumnValueIsObject(a, column); valueB = this.getAccessIfColumnValueIsObject(b, column); } else { if (typeof valueA === 'string' && valueB !== undefined) { valueA = valueA.toLowerCase(); valueB = valueB.toLowerCase(); } } if (typeof valueA === 'number' && typeof valueB === 'number') { return valueB - valueA; } if (!valueA && valueB) return 1; if (valueA && !valueB) return -1; if (!valueA && !valueB) return 0; if (valueA < valueB) return 1; if (valueA > valueB) return -1; return 0; }); } getAccessIfColumnValueIsObject(row, column) { if (!column.value || column.value === null) { return undefined; } const keys = column.value.split('.'); let access = row[keys[0]]; for (let i = 1; i < keys.length; i++) { if (access && keys[i] in access) { access = access[keys[i]]; } else { return undefined; } } return access; } buildPaginationSelectDependecies() { const formControls = { pageSize: this.formBuilder.control({ label: this.pageSize.toString(), value: this.pageSize }, { nonNullable: true, validators: [Validators.required] }) }; this.formGroup = this.formBuilder.group(formControls); const selectData = this.pageSizeOptions.map(item => ({ label: item.toString(), value: item })); this.pageSizeSelectOptions = { label: '', data: selectData, formGroup: this.formGroup, formControlName: 'pageSize' }; this.subscribeToFormValueChanges(); } subscribeToFormValueChanges() { this.formGroup.controls.pageSize.valueChanges.subscribe((value) => { if (this.pageSize === value.value) { return; } const currentPageFirstRowIndex = this.currentPage * this.pageSize; this.pageSize = value.value; this.currentPage = Math.floor(currentPageFirstRowIndex / this.pageSize); const start = this.currentPage * this.pageSize; const end = Math.min(this._dataLength, start + this.pageSize); if (this.serverSide) { this.tableChange.emit({ from: start, to: end, filter: this.filter, sort: { columnValue: this.columns[this.sortIndex].value, sortDirection: this.sortDirection } }); } this.calculateTotalPages(); this.updateVisibleRange(start, end); setTimeout(() => { this.changeDetectorRef.detectChanges(); }); }); } calculatePages() { const start = this.currentPage * this.pageSize; const end = Math.min(this._dataLength, start + this.pageSize); if (!this.serverSide) { this.updateVisibleRange(start, end); } else { this.tableChange.emit({ from: start, to: end, filter: this.filter, sort: { columnValue: this.columns[this.sortIndex].value, sortDirection: this.sortDirection } }); } this.changeDetectorRef.detectChanges(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.4", ngImport: i0, type: IpiTableComponent, deps: [{ token: i1.OSService }, { token: i2.FormBuilder }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.4", type: IpiTableComponent, isStandalone: true, selector: "ipi-table", inputs: { data: "data", dataLength: "dataLength", serverSide: "serverSide", isLoading: "isLoading", columns: "columns", filter: "filter", sortIndex: "sortIndex", sortable: "sortable", sortDirection: "sortDirection", pageSize: "pageSize", currentPage: "currentPage", pageable: "pageable", pageSizeOptions: "pageSizeOptions" }, outputs: { tableChange: "tableChange" }, host: { listeners: { "document:click": "onClick($event)", "document:scroll": "onScroll($event)", "window:resize": "onResize()" } }, viewQueries: [{ propertyName: "DropdownElement", first: true, predicate: ["dropdownElement"], descendants: true }, { propertyName: "moreActionsList", predicate: ["moreActionsList"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"container\">\n <table>\n <thead>\n <tr>\n @for (column of columns; track $index) {\n <th [ngStyle]=\"{ width: column.width ? column.width : '' }\">\n <div class=\"cell\">\n <span>{{ column.label }}</span>\n \n @if (column.type !== TableColumnType.Actions && sortable) {\n <div class=\"arranger\" (click)=\"sortColumn(column)\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 12 12\" fill=\"none\">\n <path d=\"M3.11445 5.50005H8.88795C9.31895 5.50005 9.54795 4.99005 9.26145 4.66755L6.37495 1.42005C6.32813 1.3672 6.27063 1.32489 6.20624 1.29591C6.14186 1.26694 6.07205 1.25195 6.00145 1.25195C5.93084 1.25195 5.86104 1.26694 5.79666 1.29591C5.73227 1.32489 5.67477 1.3672 5.62795 1.42005L2.74045 4.66755C2.45395 4.99005 2.68295 5.50005 3.11445 5.50005ZM5.62745 10.5795C5.67427 10.6324 5.73177 10.6747 5.79616 10.7037C5.86054 10.7327 5.93034 10.7476 6.00095 10.7476C6.07155 10.7476 6.14136 10.7327 6.20574 10.7037C6.27013 10.6747 6.32763 10.6324 6.37445 10.5795L9.26095 7.33205C9.54795 7.01005 9.31895 6.50005 8.88745 6.50005H3.11445C2.68345 6.50005 2.45445 7.01005 2.74095 7.33255L5.62745 10.5795Z\" fill=\"#C6C6C6\"/>\n </svg>\n </div>\n }\n </div>\n </th>\n }\n </tr>\n </thead>\n\n <tbody [ngClass]=\"{ 'is-loading': this.isLoading }\">\n @for (row of getPageData(); track $index) { \n <tr>\n @for (column of columns; track $index) {\n <td tabindex=\"-1\">\n @switch (column.type) {\n @case (undefined) {\n <ng-container *ngTemplateOutlet=\"textCell; context: { row, column, $index }\"></ng-container>\n }\n\n @case (TableColumnType.Number) {\n <ng-container *ngTemplateOutlet=\"numberCell; context: { row, column }\"></ng-container>\n }\n\n @case (TableColumnType.Currency) {\n <ng-container *ngTemplateOutlet=\"currencyCell; context: { row, column }\"></ng-container>\n }\n\n @case (TableColumnType.NumberMath) {\n <ng-container *ngTemplateOutlet=\"numberMathCell; context: { row, column }\"></ng-container>\n }\n\n @case (TableColumnType.Date) {\n <ng-container *ngTemplateOutlet=\"dateCell; context: { row, column }\"></ng-container>\n }\n\n @case (TableColumnType.Chip) {\n <ng-container *ngTemplateOutlet=\"chipCell; context: { row, column }\"></ng-container>\n }\n\n @case (TableColumnType.Checkbox) {\n <ng-container *ngTemplateOutlet=\"checkboxCell; context: { row, column }\"></ng-container>\n }\n\n @case (TableColumnType.Actions) {\n <ng-container *ngTemplateOutlet=\"actionCell; context: { row, column }\"></ng-container>\n }\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n\n @if (filter && filteredData && filteredData.length === 0) {\n <div class=\"row no-data\">\n <span>No data matching the filter:</span>\n\n <span>\"{{ filter }}\"</span>\n </div>\n }\n\n @if (!filter && data && data.length === 0) {\n <div class=\"row no-data\">No data available.</div>\n }\n\n @if (pageable && getPageData().length !== 0) {\n <div class=\"paging\">\n <div class=\"visible-range\">{{ visibleRange }}</div>\n\n <ng-container *ngTemplateOutlet=\"pagePicker; context: { totalPages: totalPages }\"></ng-container>\n\n <div class=\"page-size-option-picker\">\n <span>Items per page:</span>\n\n <ipi-select class=\"table\" [options]=\"pageSizeSelectOptions\"></ipi-select>\n </div>\n </div>\n }\n</div>\n\n<ng-template #textCell let-row=\"row\" let-column=\"column\" let-index=\"$index\">\n <div class=\"cell\"\n [ngClass]=\"getClass(row, column)\"\n [attr.contenteditable]=\"column.editable\"\n (keyup)=\"cellEdit($event, column.value, index)\">\n\n {{ column.prefix ? column.prefix : '' }}{{ getDescendantProp(row, column) }}{{ column.suffix ? column.suffix : '' }}\n </div>\n</ng-template>\n\n<ng-template #dateCell let-row=\"row\" let-column=\"column\">\n <div class=\"cell\"\n [ngClass]=\"getClass(row, column)\">\n {{ formatDate(getDescendantProp(row, column), column.dateFormat) }}\n </div>\n</ng-template>\n\n<ng-template #currencyCell let-row=\"row\" let-column=\"column\">\n <div class=\"cell\"\n [ngClass]=\"getClass(row, column)\"\n [ngStyle]=\"{ width: column.width ? column.width : '' }\">\n\n @if (getDescendantProp(row, column) !== undefined) {\n {{ column.prefix ? column.prefix : '' }}{{ (getDescendantProp(row, column) / 100) | number }}\n }\n </div>\n</ng-template>\n\n<ng-template #numberCell let-row=\"row\" let-column=\"column\" let-index=\"i\">\n <div class=\"cell\"\n [ngClass]=\"getClass(row, column)\"\n [attr.contenteditable]=\"column.editable\"\n (keyup)=\"cellEdit($event, column.value, index)\">\n\n {{ column.prefix ? column.prefix : '' }}{{ getDescendantProp(row, column) | number}}{{ column.suffix ? column.suffix : '' }}\n </div>\n</ng-template>\n\n<ng-template #numberMathCell let-row=\"row\" let-column=\"column\" let-index=\"i\">\n <div class=\"cell\"\n [ngClass]=\"getClass(row, column)\"\n [attr.contenteditable]=\"column.editable\"\n (keyup)=\"cellEdit($event, column.value, index)\">\n\n {{ column.prefix ? column.prefix : '' }}{{ getDescendantProp(row, column) | number}}{{ column.suffix ? column.suffix : '' }}\n </div>\n</ng-template>\n\n<ng-template #actionCell let-row=\"row\" let-column=\"column\">\n <div class=\"cell actions\">\n\n @for (action of column.singleActions; track $index) {\n @if (handleActionShow(action, row, column)) {\n <div IpiTooltip (click)=\"action.execute && action.execute(row)\" [ipiTooltip]=\"action.label\" [tooltipPosition]=\"tooltipPosition.Above\" class=\"action-icon-wrapper\" [ngClass]=\"{ 'disabled': action.disabled }\">\n <ipi-img [src]=\" 'assets/img/' + action.icon\" [ngClass]=\"action.class\" [ariaLabel]=\"'Action icon for ' + action.label\"></ipi-img>\n </div>\n }\n }\n\n @if (column.multipleActions) {\n <div class=\"action-icon-wrapper\" #moreActionsList [ngClass]=\"{ 'opened': row.isDropdown }\" (click)=\"toggleActionDropdown(row, $event)\">\n <svg class=\"multiple-action-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 17 16\" fill=\"none\">\n <path d=\"M10.3047 8C10.3047 8.34612 10.2021 8.68446 10.0098 8.97225C9.81747 9.26004 9.54416 9.48434 9.22438 9.61679C8.90461 9.74924 8.55275 9.7839 8.21328 9.71638C7.87381 9.64885 7.56199 9.48218 7.31725 9.23744C7.07251 8.9927 6.90584 8.68088 6.83831 8.34141C6.77079 8.00194 6.80545 7.65008 6.9379 7.33031C7.07035 7.01053 7.29465 6.73722 7.58244 6.54493C7.87023 6.35264 8.20857 6.25 8.55469 6.25C9.01868 6.25046 9.46353 6.43498 9.79162 6.76307C10.1197 7.09116 10.3042 7.53601 10.3047 8ZM8.55469 4.75C8.90081 4.75 9.23915 4.64737 9.52694 4.45507C9.81472 4.26278 10.039 3.98947 10.1715 3.6697C10.3039 3.34993 10.3386 2.99806 10.2711 2.65859C10.2035 2.31913 10.0369 2.00731 9.79213 1.76256C9.54738 1.51782 9.23556 1.35115 8.8961 1.28363C8.55663 1.2161 8.20476 1.25076 7.88499 1.38321C7.56522 1.51566 7.29191 1.73997 7.09962 2.02775C6.90732 2.31554 6.80469 2.65388 6.80469 3C6.80515 3.46399 6.98967 3.90884 7.31776 4.23693C7.64585 4.56502 8.0907 4.74954 8.55469 4.75ZM8.55469 11.25C8.20857 11.25 7.87023 11.3526 7.58244 11.5449C7.29465 11.7372 7.07035 12.0105 6.9379 12.3303C6.80545 12.6501 6.77079 13.0019 6.83831 13.3414C6.90584 13.6809 7.07251 13.9927 7.31725 14.2374C7.56199 14.4822 7.87381 14.6489 8.21328 14.7164C8.55275 14.7839 8.90461 14.7492 9.22438 14.6168C9.54416 14.4843 9.81747 14.26 10.0098 13.9722C10.2021 13.6845 10.3047 13.3461 10.3047 13C10.3042 12.536 10.1197 12.0912 9.79162 11.7631C9.46353 11.435 9.01868 11.2505 8.55469 11.25Z\" fill=\"#5D6068\"/>\n </svg>\n </div>\n }\n\n </div>\n\n @if (row.isDropdown) {\n <ng-container class=\"dropdown-element\" *ngTemplateOutlet=\"multipleActionsDropdown; context: { row, column }\"></ng-container>\n }\n</ng-template>\n\n<ng-template #chipCell let-row=\"row\" let-column=\"column\">\n <div class=\"cell chip\">\n @if (getChipLabel(row, column)) {\n <ipi-chip [ngClass]=\"getClass(row, column)\"> {{ getChipLabel(row, column) }}</ipi-chip>\n }\n </div>\n</ng-template>\n\n<ng-template #checkboxCell let-row=\"row\" let-column=\"column\">\n <div class=\"cell checkbox\">\n <ipi-checkbox [checked]=\"getCheckboxState(row, column)\" [disabled]=\"true\"></ipi-checkbox>\n </div>\n</ng-template>\n\n<ng-template #multipleActionsDropdown let-row=\"row\" let-column=\"column\">\n <div #multipleActionsDropdownElement #dropdownElement class=\"more-actions-dropdown\">\n @for (action of column.multipleActions; track $index) {\n\n @if (handleActionShow(action, row, column)) {\n <div class=\"dropdown-row\" (click)=\"action.execute && action.execute(row)\">\n <ipi-img [src]=\"'assets/img/' + action.icon\" [ngClass]=\"action.class\" [ariaLabel]=\"'Action icon for ' + action.label\"></ipi-img>\n\n <span>{{ action.label }}</span>\n </div>\n }\n\n }\n </div>\n</ng-template>\n\n<ng-template #pagePicker let-totalPages=\"totalPages\">\n <div class=\"page-picker\">\n <!-- If page one should be visible -->\n @if (currentPage + 1 >= 3) {\n <span (click)=\"goToPage(0)\">1</span>\n <span class=\"style-excluded\">...</span>\n }\n\n <!-- Middle part of the page-picker -->\n <ng-container *ngTemplateOutlet=\"page; context: { page: currentPage - 1 } \"></ng-container>\n <ng-container *ngTemplateOutlet=\"page; context: { page: currentPage, class: 'highlighted' } \"></ng-container>\n <ng-container *ngTemplateOutlet=\"page; context: { page: currentPage + 1 } \"></ng-container>\n\n <!-- If last page should be visible -->\n @if (currentPage + 1 <= totalPages - 2) {\n <span class=\"style-excluded\">...</span>\n <span (click)=\"goToPage(totalPages - 1)\">{{ totalPages }}</span>\n }\n\n </div>\n</ng-template>\n\n<ng-template #page let-page=\"page\" let-class=\"class\">\n @if (page >= 0 && page < totalPages) {\n <span [ngClass]=\"class\" (click)=\"goToPage(page)\">{{ page + 1 }}</span>\n }\n</ng-template>\n", styles: [":host{display:block;touch-action:manipulation}.container{width:100%;position:relative;overflow:auto;padding:24px 0}table{width:100%;text-align:center;table-layout:fixed;border-collapse:collapse}thead tr{border:none}tr{height:42px;vertical-align:middle;border-bottom:1px solid var(--ipi-table-row-bottom-border-color, #E9E9E9)}th{min-width:60px;-webkit-user-select:none;user-select:none;font-weight:500;font-size:14px;color:var(--ipi-table-first-row-text-color, #5D6068)}th .cell{display:flex;justify-content:center;align-items:center}td{scrollbar-width:thin;overflow:auto;text-overflow:clip;font-size:14px}td:focus{outline:none}td .cell{text-wrap:nowrap}ipi-chip{--ipi-chip-background-color: #FFF2EF;--ipi-chip-color: #F96138}.cell{line-height:16px;box-sizing:border-box}.cell span{margin-right:4px}.cell.checkbox{display:flex;justify-content:center;align-items:center;--ipi-checkbox-disabled-opacity: 1}.cell.actions{display:flex;justify-content:center;align-items:center;gap:4px}.cell.actions ipi-img{width:16px;height:16px}.cell.chip{display:flex;justify-content:center;align-items:center}.cell.chip .primary{--ipi-chip-background-color: var(--ipi-table-chip-background-color);--ipi-chip-color: var(--ipi-table-chip-color)}.cell.chip .secondary{--ipi-chip-background-color: var(--ipi-table-chip-background-color-secondary);--ipi-chip-color: var(--ipi-table-chip-color-secondary)}.no-data{display:flex;justify-content:center;font-size:14px;margin-top:16px}.more-actions-dropdown{min-width:184px;width:fit-content;position:fixed;background-color:var(--ipi-table-more-actions-dropdown-background-color, #FFFFFF);border-radius:8px;box-shadow:0 4px 8px #d6d6d659;z-index:10}.dropdown-row{height:44px;display:flex;align-items:center;justify-content:flex-start;font-size:14px;font-weight:500;box-sizing:border-box;overflow:visible;cursor:pointer;background-color:var(--ipi-table-more-actions-dropdown-background-color, #FFFFFF);padding:0 12px}.dropdown-row:hover{background-color:var(--ipi-table-more-actions-dropdown-hover-background-color, #F3F3F3)}.dropdown-row ipi-img{width:20px;height:20px}.dropdown-row ipi-img path{fill:var(--ipi-table-dropdown-action-icon-fill, #5D6068);stroke:var(--ipi-table-dropdown-action-icon-stroke, #FFFFFF00)}.dropdown-row:hover ipi-img path{fill:var(--ipi-table-action-icon-hover-fill, #F96138);stroke:var(--ipi-table-action-icon-hover-stroke, #FFFFFF00)}.dropdown-row span{margin:0 12px;text-wrap:nowrap}.is-loading{opacity:.5;pointer-events:none;overflow:hidden}.is-loading:after{width:0px;height:2px;content:\" \";position:absolute;left:-50%;background-color:var(--ipi-table-loading-bar-color, #FF7F50);animation:lineAnimation 1s linear infinite;border-radius:20px}.action-icon-wrapper{width:36px;height:36px;display:flex;align-items:center;justify-content:center;cursor:pointer}.action-icon-wrapper:not(.disabled):hover{border-radius:4px;background:var(--ipi-table-action-hover-background-color, #F7F8FB)}.action-icon-wrapper ipi-img path{fill:var(--ipi-table-action-icon-fill, #5D6068);stroke:var(--ipi-table-action-icon-stroke, #FFFFFF00)}.action-icon-wrapper:not(.disabled):hover ipi-img path{fill:var(--ipi-table-action-icon-hover-fill, #F96138);stroke:var(--ipi-table-action-icon-hover-stroke, #FFFFFF00)}.action-icon-wrapper:not(.disabled):hover svg path,.opened .multiple-action-icon path{fill:var(--ipi-table-action-more-icon-fill, #F96138);stroke:var(--ipi-table-action-more-icon-stroke, #FFFFFF00)}.action-icon-wrapper ipi-img.secondary path{fill:var(--ipi-table-action-secondary-icon-fill, #F96138);stroke:var(--ipi-table-action-secondary-icon-stroke, #FFFFFF00)}.action-icon-wrapper:not(.disabled):hover ipi-img.secondary path{fill:var(--ipi-table-action-secondary-icon-hover-fill, #5D6068);stroke:var(--ipi-table-action-secondary-icon-hover-stroke, #FFFFFF00)}.arranger{width:14px;height:14px;cursor:pointer}.arranger svg:hover{transform:scale(1.15)}.paging{display:flex;justify-content:space-between;align-items:center;font-size:14px;font-weight:600;-webkit-user-select:none;user-select:none;margin-top:12px}.paging .items-per-page{line-height:10px;margin-right:8px}.mobile .visible-range{margin:0 12px}.page-picker{display:flex;justify-content:center;gap:14px}.page-picker span{min-width:24px;height:24px;display:flex;flex-direction:column;justify-content:center;align-items:center;text-align:center;border-radius:24px;gap:10px;transition:color .25s,background-color .25s}.page-picker span.highlighted,.page-picker span:hover:not(.style-excluded){cursor:pointer;color:var(--ipi-table-page-picker-text-color, #FFF);background:var(--ipi-table-page-picker-background, #F96138)}.page-size-option-picker{display:flex;align-items:center}.page-size-option-picker span{text-wrap:nowrap;margin-right:8px}@media (max-width: 940px){.paging{flex-direction:column;row-gap:16px;margin-top:24px}.visible-range{order:1}}@keyframes lineAnimation{0%{width:0px}50%{left:20%;width:80%}to{left:100%;width:100%}}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: DecimalPipe, name: "number" }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IpiChipComponent, selector: "ipi-chip", inputs: ["closeIcon"], outputs: ["closeChange"] }, { kind: "component", type: IpiImageComponent, selector: "ipi-img", inputs: ["src", "ariaLabel"] }, { kind: "component", type: IpiSelectComponent, selector: "ipi-select", inputs: ["options"], outputs: ["selectChange", "helperTextChange"] }, { kind: "directive", type: IpiTooltipDirective, selector: "[ipiTooltip]", inputs: ["ipiTooltip", "tooltipPosition"] }, { kind: "component", type: IpiCheckboxComponent, selector: "ipi-checkbox", inputs: ["checked", "disabled", "tooltip", "options"], outputs: ["clickChange"] }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.4", ngImport: i0, type: IpiTableComponent, decorators: [{ type: Component, args: [{ standalone: true, selector: 'ipi-table', imports: [ NgStyle, NgClass, DecimalPipe, NgTemplateOutlet, IpiChipComponent, IpiImageComponent, IpiSelectComponent, IpiTooltipDirective, IpiCheckboxComponent, ], template: "<div class=\"container\">\n <table>\n <thead>\n <tr>\n @for (column of columns; track $index) {\n <th [ngStyle]=\"{ width: column.width ? column.width : '' }\">\n <div class=\"cell\">\n <span>{{ column.label }}</span>\n \n @if (column.type !== TableColumnType.Actions && sortable) {\n <div class=\"arranger\" (click)=\"sortColumn(column)\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 12 12\" fill=\"none\">\n <path d=\"M3.11445 5.50005H8.88795C9.31895 5.50005 9.54795 4.99005 9.26145 4.66755L6.37495 1.42005C6.32813 1.3672 6.27063 1.32489 6.20624 1.29591C6.14186 1.26694 6.07205 1.25195 6.00145 1.25195C5.93084 1.25195 5.86104 1.26694 5.79666 1.29591C5.73227 1.32489 5.67477 1.3672 5.62795 1.42005L2.74045 4.66755C2.45395 4.99005 2.68295 5.50005 3.11445 5.50005ZM5.62745 10.5795C5.67427 10.6324 5.73177 10.6747 5.79616 10.7037C5.86054 10.7327 5.93034 10.7476 6.00095 10.7476C6.07155 10.7476 6.14136 10.7327 6.20574 10.7037C6.27013 10.6747 6.32763 10.6324 6.37445 10.5795L9.26095 7.33205C9.54795 7.01005 9.31895 6.50005 8.88745 6.50005H3.11445C2.68345 6.50005 2.45445 7.01005 2.74095 7.33255L5.62745 10.5795Z\" fill=\"#C6C6C6\"/>\n </svg>\n </div>\n }\n </div>\n </th>\n }\n </tr>\n </thead>\n\n <tbody [ngClass]=\"{ 'is-loading': this.isLoading }\">\n @for (row of getPageData(); track $index) { \n <tr>\n @for (column of columns; track $index) {\n <td tabindex=\"-1\">\n @switch (column.type) {\n @case (undefined) {\n <ng-container *ngTemplateOutlet=\"textCell; context: { row, column, $index }\"></ng-container>\n }\n\n @case (TableColumnType.Number) {\n <ng-container *ngTemplateOutlet=\"numberCell; context: { row, column }\"></ng-container>\n }\n\n @case (TableColumnType.Currency) {\n <ng-container *ngTemplateOutlet=\"currencyCell; context: { row, column }\"></ng-container>\n }\n\n @case (TableColumnType.NumberMath) {\n <ng-container *ngTemplateOutlet=\"numberMathCell; context: { row, column }\"></ng-container>\n }\n\n @case (TableColumnType.Date) {\n <ng-container *ngTemplateOutlet=\"dateCell; context: { row, column }\"></ng-container>\n }\n\n @case (TableColumnType.Chip) {\n <ng-container *ngTemplateOutlet=\"chipCell; context: { row, column }\"></ng-container>\n }\n\n @case (TableColumnType.Checkbox) {\n <ng-container *ngTemplateOutlet=\"checkboxCell; context: { row, column }\"></ng-container>\n }\n\n @case (TableColumnType.Actions) {\n <ng-container *ngTemplateOutlet=\"actionCell; context: { row, column }\"></ng-container>\n }\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n\n @if (filter && filteredData && filteredData.length === 0) {\n <div class=\"row no-data\">\n <span>No data matching the filter:</span>\n\n <span>\"{{ filter }}\"</span>\n </div>\n }\n\n @if (!filter && data && data.length === 0) {\n <div class=\"row no-data\">No data available.</div>\n }\n\n @if (pageable && getPageData().length !== 0) {\n <div class=\"paging\">\n <div class=\"visible-range\">{{ visibleRange }}</div>\n\n <ng-container *ngTemplateOutlet=\"pagePicker; context: { totalPages: totalPages }\"></ng-container>\n\n <div class=\"page-size-option-picker\">\n <span>Items per page:</span>\n\n <ipi-select class=\"table\" [options]=\"pageSizeSelectOptions\"></ipi-select>\n </div>\n </div>\n }\n</div>\n\n<ng-template #textCell let-row=\"row\" let-column=\"column\" let-index=\"$index\">\n <div class=\"cell\"\n [ngClass]=\"getClass(row, column)\"\n [attr.contenteditable]=\"column.editable\"\n (keyup)=\"cellEdit($event, column.value, index)\">\n\n {{ column.prefix ? column.prefix : '' }}{{ getDescendantProp(row, column) }}{{ column.suffix ? column.suffix : '' }}\n </div>\n</ng-template>\n\n<ng-template #dateCell let-row=\"row\" let-column=\"column\">\n <div class=\"cell\"\n [ngClass]=\"getClass(row, column)\">\n {{ formatDate(getDescendantProp(row, column), column.dateFormat) }}\n </div>\n</ng-template>\n\n<ng-template #currencyCell let-row=\"row\" let-column=\"column\">\n <div class=\"cell\"\n [ngClass]=\"getClass(row, column)\"\n [ngStyle]=\"{ width: column.width ? column.width : '' }\">\n\n @if (getDescendantProp(row, column) !== undefined) {\n {{ column.prefix ? column.prefix : '' }}{{ (getDescendantProp(row, column) / 100) | number }}\n }\n </div>\n</ng-template>\n\n<ng-template #numberCell let-row=\"row\" let-column=\"column\" let-index=\"i\">\n <div class=\"cell\"\n [ngClass]=\"getClass(row, column)\"\n [attr.contenteditable]=\"column.editable\"\n (keyup)=\"cellEdit($event, column.value, index)\">\n\n {{ column.prefix ? column.prefix : '' }}{{ getDescendantProp(row, column) | number}}{{ column.suffix ? column.suffix : '' }}\n </div>\n</ng-template>\n\n<ng-template #numberMathCell let-row=\"row\" let-column=\"column\" let-index=\"i\">\n <div class=\"cell\"\n [ngClass]=\"getClass(row, column)\"\n [attr.contenteditable]=\"column.editable\"\n (keyup)=\"cellEdit($event, column.value, index)\">\n\n {{ column.prefix ? column.prefix : '' }}{{ getDescendantProp(row, column) | number}}{{ column.suffix ? column.suffix : '' }}\n </div>\n</ng-template>\n\n<ng-template #actionCell let-row=\"row\" let-column=\"column\">\n <div class=\"cell actions\">\n\n @for (action of column.singleActions; track $index) {\n @if (handleActionShow(action, row, column)) {\n <div IpiTooltip (click)=\"action.execute && action.execute(row)\" [ipiTooltip]=\"action.label\" [tooltipPosition]=\"tooltipPosition.Above\" class=\"action-icon-wrapper\" [ngClass]=\"{ 'disabled': action.disabled }\">\n <ipi-img [src]=\" 'assets/img/' + action.icon\" [ngClass]=\"action.class\" [ariaLabel]=\"'Action icon for ' + action.label\"></ipi-img>\n </div>\n }\n }\n\n @if (column.multipleActions) {\n <div class=\"action-icon-wrapper\" #moreActionsList [ngClass]=\"{ 'opened': row.isDropdown }\" (click)=\"toggleActionDropdown(row, $event)\">\n <svg class=\"multiple-action-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 17 16\" fill=\"none\">\n <path d=\"M10.3047 8C10.3047 8.34612 10.2021 8.68446 10.0098 8.97225C9.81747 9.26004 9.54416 9.48434 9.22438 9.61679C8.90461 9.74924 8.55275 9.7839 8.21328 9.71638C7.87381 9.64885 7.56199 9.48218 7.31725 9.23744C7.07251 8.9927 6.90584 8.68088 6.83831 8.34141C6.77079 8.00194 6.80545 7.65008 6.9379 7.33031C7.07035 7.01053 7.29465 6.73722 7.58244 6.54493C7.87023 6.35264 8.20857 6.25 8.55469 6.25C9.01868 6.25046 9.46353 6.43498 9.79162 6.76307C10.1197 7.09116 10.3042 7.53601 10.3047 8ZM8.55469 4.75C8.90081 4.75 9.23915 4.64737 9.52694 4.45507C9.81472 4.26278 10.039 3.98947 10.1715 3.6697C10.3039 3.34993 10.3386 2.99806 10.2711 2.65859C10.2035 2.31913 10.0369 2.00731 9.79213 1.76256C9.54738 1.51782 9.23556 1.35115 8.8961 1.28363C8.55663 1.2161 8.20476 1.25076 7.88499 1.38321C7.56522 1.51566 7.29191 1.73997 7.09962 2.02775C6.90732 2.31554 6.80469 2.65388 6.80469 3C6.80515 3.46399 6.98967 3.90884 7.31776 4.23693C7.64585 4.56502 8.0907 4.74954 8.55469 4.75ZM8.55469 11.25C8.20857 11.25 7.87023 11.3526 7.58244 11.5449C7.29465 11.7372 7.07035 12.0105 6.9379 12.3303C6.80545 12.6501 6.77079 13.0019 6.83831 13.3414C6.90584 13.6809 7.07251 13.9927 7.31725 14.2374C7.56199 14.4822 7.87381 14.6489 8.21328 14.7164C8.55275 14.7839 8.90461 14.7492 9.22438 14.6168C9.54416 14.4843 9.81747 14.26 10.0098 13.9722C10.2021 13.6845 10.3047 13.3461 10.3047 13C10.3042 12.536 10.1197 12.0912 9.79162 11.7631C9.46353 11.435 9.01868 11.2505 8.55469 11.25Z\" fill=\"#5D6068\"/>\n </svg>\n </div>\n }\n\n </div>\n\n @if (row.isDropdown) {\n <ng-container class=\"dropdown-element\" *ngTemplateOutlet=\"multipleActionsDropdown; context: { row, column }\"></ng-container>\n }\n</ng-template>\n\n<ng-template #chipCell let-row=\"row\" let-column=\"column\">\n <div class=\"cell chip\">\n @if (getChipLabel(row, column)) {\n <ipi-chip [ngClass]=\"getClass(row, column)\"> {{ getChipLabel(row, column) }}</ipi-chip>\n }\n </div>\n</ng-template>\n\n<ng-template #checkboxCell let-row=\"row\" let-column=\"column\">\n <div class=\"cell ch