UNPKG

med-table

Version:
918 lines (885 loc) 77.1 kB
import * as i1 from 'med-dynamic-form'; import { PATTERN_TYPES, FIELD_TYPES, MedDynamicFormModule } from 'med-dynamic-form'; export { FIELD_TYPES, PATTERN_TYPES } from 'med-dynamic-form'; import * as i0 from '@angular/core'; import { Directive, Input, Injectable, Component, ContentChildren, ViewChild, EventEmitter, Output, forwardRef, NgModule } from '@angular/core'; import { get, isEqual, set } from 'lodash'; import * as i1$1 from 'primeng/api'; import * as XLSX from 'xlsx-js-style'; import * as i2 from 'primeng/table'; import { TableModule } from 'primeng/table'; import * as i10 from '@angular/common'; import * as i3 from 'primeng/multiselect'; import { MultiSelectModule } from 'primeng/multiselect'; import * as i5 from '@angular/forms'; import { NG_VALUE_ACCESSOR, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import * as i1$2 from 'primeng/calendar'; import { CalendarModule } from 'primeng/calendar'; import * as i11 from 'primeng/inputtext'; import { InputTextModule } from 'primeng/inputtext'; import * as i12 from 'primeng/button'; import { ButtonModule } from 'primeng/button'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; var CELL_TYPES; (function (CELL_TYPES) { CELL_TYPES["TEXT"] = "text"; CELL_TYPES["LINK"] = "link"; })(CELL_TYPES || (CELL_TYPES = {})); var FILTER_TYPES; (function (FILTER_TYPES) { FILTER_TYPES["TEXT"] = "text"; FILTER_TYPES["SELECT"] = "select"; FILTER_TYPES["DATE"] = "date"; FILTER_TYPES["CHECKBOX"] = "checkbox"; })(FILTER_TYPES || (FILTER_TYPES = {})); class MedTemplateDirective { constructor(template) { this.template = template; } getType() { return this.name; } } MedTemplateDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: MedTemplateDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); MedTemplateDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.2.7", type: MedTemplateDirective, selector: "[mTemplate]", inputs: { type: "type", name: ["mTemplate", "name"] }, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: MedTemplateDirective, decorators: [{ type: Directive, args: [{ selector: '[mTemplate]' }] }], ctorParameters: function () { return [{ type: i0.TemplateRef }]; }, propDecorators: { type: [{ type: Input }], name: [{ type: Input, args: ['mTemplate'] }] } }); class MedTableService { constructor(dynamicFormService) { this.dynamicFormService = dynamicFormService; this._filters = {}; this._filterSelectData = {}; this._data = []; } setFilterSelectData(data, key) { this._filterSelectData[key] = data; } getFilterSelectData(key) { return this._filterSelectData[key] || []; } setSelectData(data, key) { this.dynamicFormService.setSelectData(data, key); } setDatalist(data, key) { this.dynamicFormService.setDatalist(data, key); } get filters() { return this._filters; } set filters(data) { this._filters = data; } get data() { return this._data; } set data(data) { this._data = data; } } MedTableService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: MedTableService, deps: [{ token: i1.MedDynamicFormService }], target: i0.ɵɵFactoryTarget.Injectable }); MedTableService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: MedTableService, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: MedTableService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: function () { return [{ type: i1.MedDynamicFormService }]; } }); const APP_SELECTOR = 'med-table'; const SELECTOR_AFTER_WEB_COMPONENT = 'div'; // web component doesn't have a height const FOOTER_SELECTOR = 'body footer'; const TABLE_SELECTOR = `${APP_SELECTOR} p-table .p-datatable-wrapper`; const PAGINATOR_SELECTOR = `${APP_SELECTOR} p-paginator ${SELECTOR_AFTER_WEB_COMPONENT}`; const UP_SCROLL_SELECTOR = `${APP_SELECTOR} .med-table__up-scroll`; class StickyHeader { /** * should call in "ngAfterViewInit" hook */ get tableHeight() { const MARGINS = 20; // 20px margin app in prod const contentHeight = this.headerHeight() + this.footerHeight() + this.paginatorHeight() + this.upScrollHeight() + MARGINS; return `calc(100vh - ${contentHeight}px)`; } headerHeight() { const app = document.querySelector(TABLE_SELECTOR); if (!app) return 0; return app.getBoundingClientRect().top; } paginatorHeight() { return this.getElementHeight(PAGINATOR_SELECTOR); } footerHeight() { return this.getElementHeight(FOOTER_SELECTOR); } upScrollHeight() { return this.getElementHeight(UP_SCROLL_SELECTOR); } getElementHeight(selector) { const block = document.querySelector(selector); if (!block) return 0; return block.offsetHeight; } } class TemplatesMixin { ngAfterContentInit() { this.templates.forEach((item) => { switch (item.getType()) { case 'toolbar': this.toolbarTemplate = item.template; break; case 'caption': this.captionTemplate = item.template; break; case 'tableHead': this.tableHeadTemplate = item.template; break; case 'tableData': this.tableDataTemplate = item.template; break; case 'paginator': this.paginatorTemplate = item.template; break; case 'rowExpansion': this.rowExpansion = item.template; break; } }); } } TemplatesMixin.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: TemplatesMixin, deps: [], target: i0.ɵɵFactoryTarget.Component }); TemplatesMixin.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: TemplatesMixin, selector: "ng-component", queries: [{ propertyName: "templates", predicate: MedTemplateDirective }], ngImport: i0, template: '', isInline: true }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: TemplatesMixin, decorators: [{ type: Component, args: [{ template: '', }] }], propDecorators: { templates: [{ type: ContentChildren, args: [MedTemplateDirective] }] } }); const PRIMENG_TRANSLATIONS = { 'dayNames': ['Неділя', 'Понеділок', 'Вівторок', 'Середа', 'Четвер', 'П\'ятниця', 'Субота'], 'dayNamesShort': ['Нд', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'], 'dayNamesMin': ['Нд', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'], 'monthNames': ['Січень', 'Лютий', 'Березень', 'Квітень', 'Травень', 'Червень', 'Липень', 'Серпень', 'Вересень', 'Жовтень', 'Листопад', 'Грудень'], 'monthNamesShort': ['Січ.', 'Лют.', 'Бер.', 'Квіт.', 'Трав.', 'Черв.', 'Лип.', 'Серп.', 'Вер.', 'Жовт.', 'Лис.', 'Гру.'], }; class FilterMethods { static dateRangeHandler(value, filter) { const END_DATE = { HOURS: 23, MINUTES: 59 }; const [startDate, endDate] = filter || []; if (!startDate) return true; if (!value) return false; const currentDate = new Date(value).getTime(); if (endDate) { return startDate.getTime() <= currentDate && currentDate < endDate.setHours(END_DATE.HOURS, END_DATE.MINUTES); } return startDate.getTime() <= currentDate; } static selectHandler(value, filter) { return !filter ? true : filter.includes(value || ''); } static checkboxHandler(value, filter) { if (filter === null) { return true; } return Boolean(value) === filter; } static textHandler(value, filter) { const regexp = new RegExp(`${filter?.trim()}`, 'i'); return regexp.test(value || ''); } } class PrimengConfigMixin extends TemplatesMixin { constructor(primeConfig, filterService) { super(); this.primeConfig = primeConfig; this.filterService = filterService; } ngOnInit() { this.primeConfig.setTranslation(PRIMENG_TRANSLATIONS); this.filterService.register('custom-contains', FilterMethods.textHandler); } } PrimengConfigMixin.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: PrimengConfigMixin, deps: [{ token: i1$1.PrimeNGConfig }, { token: i1$1.FilterService }], target: i0.ɵɵFactoryTarget.Component }); PrimengConfigMixin.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: PrimengConfigMixin, selector: "ng-component", usesInheritance: true, ngImport: i0, template: '', isInline: true }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: PrimengConfigMixin, decorators: [{ type: Component, args: [{ template: '', }] }], ctorParameters: function () { return [{ type: i1$1.PrimeNGConfig }, { type: i1$1.FilterService }]; } }); const CONFIG_KEY_CHILDREN = 'children'; class CellConfigsFactory { build(list) { return this.flatChildren(list).map(el => this.cellBuilder(el)); } flatChildren(list) { const newList = list.map(el => el.children ? el.children : el).flat(); const hasChildren = newList.some(el => el.children); if (hasChildren) { return this.flatChildren(newList); } return newList; } cellBuilder(el) { return { ...el, key: el.key || '', children: undefined, }; } } const DEFAULT_TABLE_SETTINGS = { rows: 25, lazy: false, sticky: true, filterDelay: 0, totalRecords: 0, paginator: true, scrollHeight: 'flex', colMinWidth: '15rem', colMaxWidth: '20rem', expandedDataKey: '', doubleScrollbar: false, showCurrentPageReport: true, showGlobalSearchFilter: true, emptyMessage: 'Дані відсутні.', rowsPerPageOptions: [10, 25, 50, 100], currentPageReportTemplate: 'Показано {first} – {last} із {totalRecords} записів', }; const FONTS = { TIMES: 'Times New Roman', }; const FONT_SIZES = { SMALL: 12, MEDIUM: 14, LARGE: 16, EXTRA_LARGE: 18, }; const CELL_TYPE_STRING = 's'; const DEFAULT_CELL_STYLE = { size: FONT_SIZES.SMALL, bold: false, }; class CellBuilder { createEmptyCell() { return this.createTextCell(''); } createTextCell(v, style) { const { size, bold } = style || DEFAULT_CELL_STYLE; return { v, t: CELL_TYPE_STRING, s: { font: { name: FONTS.TIMES, sz: size, bold, }, alignment: { wrapText: true, vertical: 'top', }, }, }; } createLinkCell(v, link) { return { v, t: CELL_TYPE_STRING, l: { Target: link }, s: { font: { name: FONTS.TIMES, sz: FONT_SIZES.SMALL, underline: true, color: { rgb: '0563c1', }, }, alignment: { wrapText: true, vertical: 'top', }, }, }; } } const STRING = { EMPTY: '', SPACE: ' ', DOT: '.', ZERO: '0', HASHTAG: '#', SEMICOLON: ';', HYPHEN: '–', DASH: '-', LINE_BREAK: '\n', DELIMITER: { HYPHEN_WITH_SPACE: ' – ', COMMA: ', ', }, }; const DEFAULT_VIEW_HANDLER = (data) => data; class RowBuilder { constructor() { this.cellBuilder = new CellBuilder(); } createTableRow(config, row) { return config.map((item) => this.cellBuilder.createTextCell(this.getField(row, item))); } getField(data, config) { const { key, sortKey, defaultValue = STRING.HYPHEN, viewHandler = DEFAULT_VIEW_HANDLER } = config; const field = viewHandler(get(data, sortKey || key)); return field || defaultValue; } } class WorkSheetSettings { constructor() { this._rowIndex = 0; this._merges = []; } get merges() { return this._merges; } updateRowIndex(count) { this._rowIndex += count || 1; } addColMerge(start, end) { // s - start, e - end; r - row, c - column this._merges.push({ s: { r: this._rowIndex, c: start }, e: { r: this._rowIndex, c: end }, }); } addRowMerge(count, colIndex) { // s - start, e - end; r - row, c - column this._merges.push({ s: { r: this._rowIndex, c: colIndex }, e: { r: this._rowIndex + (count - 1), c: colIndex }, }); } } const isExist = (value) => value !== undefined && value !== null && value !== '' || Array.isArray(value) && value.length; const deepClone = (data) => JSON.parse(JSON.stringify(data)); const getMaxDeepKeyLevel = (data, key, deep = 1) => { const STEP = 1; const levels = data.map(({ [key]: prop }) => prop ? getMaxDeepKeyLevel(prop, key, deep + STEP) : deep); return Math.max(...levels); }; const getNestedList = (data, key, deep) => { const STEP = 1; if (!deep) return data; const mapHandler = (data) => data.map(el => el ? el[key] : el).flat(deep); return deep - STEP ? mapHandler(getNestedList(data, key, deep - STEP)) : mapHandler(data); }; const getColspanByKey = (column, key) => { const ONE_COL = 1; const { [key]: prop } = column; if (!prop || !prop.length) return ONE_COL; let total = 0; prop.forEach(el => { total += el[key] ? getColspanByKey(el, key) : ONE_COL; }); return total; }; const getRowspanByLevelAndKey = (data, key, deepLevel, currentLevel) => { const DELTA = 1; const { [key]: prop = [] } = data; const levelsToDown = getMaxDeepKeyLevel(prop, key); const emptyRows = !currentLevel ? deepLevel - (levelsToDown + DELTA) : 0; if (!isFinite(levelsToDown)) return deepLevel - currentLevel; return deepLevel - (levelsToDown + currentLevel + emptyRows); }; const getObjectsByLevel = (list, level, childKey) => { if (!level) return list; const newList = list.map(el => el[childKey] ? el[childKey] : el).flat(); const hasChildren = newList.some(el => el[childKey]); if (hasChildren) { return getObjectsByLevel(newList, --level, childKey); } return newList; }; const getColumnIndex = (list, obj, childKey) => { const index = list.findIndex((el) => isEqual(obj, el)); if (index < 0) return 0; const elementsBeforeCurrent = list.slice(0, index); return elementsBeforeCurrent .map(({ [childKey]: children }) => children ? children.length : 1) .reduce((acc, cur) => acc + cur, 0); }; const createArrayByNumber = (length) => Array(length).fill(0).map((x, i) => i); class TableHeaderBuilder { constructor(sheetSettings, columnsConfig) { this.sheetSettings = sheetSettings; this.columnsConfig = columnsConfig; this.cellBuilder = new CellBuilder(); } get maxDeepKeyLevel() { return getMaxDeepKeyLevel(this.columnsConfig, CONFIG_KEY_CHILDREN); } get tableHeadLevels() { return createArrayByNumber(this.maxDeepKeyLevel); } getHeaderRows() { return this.tableHeadLevels.map((level, index) => { if (index) this.sheetSettings.updateRowIndex(); const allColumnsBeforeLevel = this.getAllColumnsBeforeLevel(level); const visibleColumns = this.getVisibleColumns(level); return allColumnsBeforeLevel.map((column, index) => { const cols = []; const range = this.getColspan(column); const isVisibleColumn = Boolean(visibleColumns.find(col => isEqual(col, column))); const startIndex = getColumnIndex(allColumnsBeforeLevel, column, CONFIG_KEY_CHILDREN); this.sheetSettings.addColMerge(startIndex, startIndex + range); if (isVisibleColumn) { cols.push(this.cellBuilder.createTextCell(column.label, { bold: true })); } else { cols.push(this.cellBuilder.createEmptyCell()); } createArrayByNumber(range).map(() => { cols.push(this.cellBuilder.createEmptyCell()); }); return cols; }).flat(); }); } getAllColumnsBeforeLevel(level) { return getObjectsByLevel(this.columnsConfig, level, CONFIG_KEY_CHILDREN); } getVisibleColumns(level) { return getNestedList(this.columnsConfig, CONFIG_KEY_CHILDREN, level).filter(Boolean); } getColspan(column) { const DELTA = 1; return getColspanByKey(column, CONFIG_KEY_CHILDREN) - DELTA; } } class SheetsGenerator { constructor(data, config) { this.data = data; this.rowBuilder = new RowBuilder(); this.settings = new WorkSheetSettings(); const filteredColumns = this.hidePrivateFields(config); this.headerBuilder = new TableHeaderBuilder(this.settings, filteredColumns); this.dataRowConfig = new CellConfigsFactory().build(filteredColumns); } generate(fileName = 'document') { const wb = XLSX.utils.book_new(); const ws = XLSX.utils.aoa_to_sheet([ this.headerBuilder.getHeaderRows(), this.createTableData(), ].flat()); ws['!merges'] = this.settings.merges; XLSX.utils.book_append_sheet(wb, ws); XLSX.writeFile(wb, `${fileName}.xlsx`); } hidePrivateFields(list) { return list.filter(({ hideExport }) => !hideExport).map(column => { if (column.children) { return { ...column, children: this.hidePrivateFields(column.children), }; } return column; }); } createTableData() { return this.data.map(row => this.rowBuilder.createTableRow(this.dataRowConfig, row)); } } const DEFAULT_HANDLER = (data) => data; class TableCell { constructor() { this._item = {}; this._config = { key: '', label: '' }; } get fieldData() { return this.getValue(this._item, this._config.key); } get previewData() { const { viewHandler = DEFAULT_HANDLER, key, sortKey } = this._config; const value = this.getValue(this._item, sortKey || key); return value ? viewHandler(value) : this.defaultValue; } get defaultValue() { const { defaultValue, filterType } = this._config; if (defaultValue !== undefined) return defaultValue; if (filterType === FILTER_TYPES.CHECKBOX) return 0; return STRING.HYPHEN; } getValue(data, key) { return get(data, key) || ''; } setItem(data) { this._item = data; } setConfig(config) { this._config = config; } } const filterMethodsMap = new Map() .set(FILTER_TYPES.TEXT, FilterMethods.textHandler) .set(FILTER_TYPES.SELECT, FilterMethods.selectHandler) .set(FILTER_TYPES.CHECKBOX, FilterMethods.checkboxHandler) .set(FILTER_TYPES.DATE, FilterMethods.dateRangeHandler); class FilterDataHandler { constructor() { this._data = []; this._config = []; } filter(filters, exitKey) { return this._data.filter(row => this._config.every((col) => { const key = col.sortKey || col.key; const { value } = filters[key] || { matchMode: '' }; if (this.isEmptyValue(value, col.filterType) || exitKey === key) return true; const filterType = col.filterType || FILTER_TYPES.TEXT; const tableCell = new TableCell(); tableCell.setItem(row); tableCell.setConfig(col); const filterFn = filterMethodsMap.get(filterType); return filterFn ? filterFn(tableCell.previewData, value) : true; })); } setData(data) { this._data = data; } setConfig(config) { this._config = config; } isEmptyValue(value, filterType) { if (filterType === FILTER_TYPES.SELECT || filterType === FILTER_TYPES.DATE) { return !value || Array.isArray(value) && !value.length; } if (filterType === FILTER_TYPES.CHECKBOX) { return false; } return !value; } } class TableHeadCellComponent { get isEmptyTemplate() { // TODO should show default value if slot "template" is empty const { nativeElement } = this.contentRef || {}; return !(nativeElement && nativeElement.innerText); } } TableHeadCellComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: TableHeadCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); TableHeadCellComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: TableHeadCellComponent, selector: "table-head-cell", inputs: { config: "config", template: "template" }, viewQueries: [{ propertyName: "contentRef", first: true, predicate: ["contentRef"], descendants: true }], ngImport: i0, template: "<span>\n <span #contentRef>\n <ng-container *ngTemplateOutlet=\"\n template || defaultTemplate;\n context: { $implicit: config }\"\n ></ng-container>\n </span>\n\n <ng-container\n *ngIf=\"isEmptyTemplate\"\n [ngTemplateOutlet]=\"defaultTemplate\"\n ></ng-container>\n</span>\n\n<p-sortIcon\n *ngIf=\"config.label && config.key && !config.hideSortIcon\"\n [field]=\"config.sortKey || config.key\"\n></p-sortIcon>\n\n<ng-template #defaultTemplate>\n {{ config.label }}\n</ng-template>\n", styles: [""], components: [{ type: i2.SortIcon, selector: "p-sortIcon", inputs: ["field"] }], directives: [{ type: i10.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: i10.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: TableHeadCellComponent, decorators: [{ type: Component, args: [{ selector: 'table-head-cell', template: "<span>\n <span #contentRef>\n <ng-container *ngTemplateOutlet=\"\n template || defaultTemplate;\n context: { $implicit: config }\"\n ></ng-container>\n </span>\n\n <ng-container\n *ngIf=\"isEmptyTemplate\"\n [ngTemplateOutlet]=\"defaultTemplate\"\n ></ng-container>\n</span>\n\n<p-sortIcon\n *ngIf=\"config.label && config.key && !config.hideSortIcon\"\n [field]=\"config.sortKey || config.key\"\n></p-sortIcon>\n\n<ng-template #defaultTemplate>\n {{ config.label }}\n</ng-template>\n", styles: [""] }] }], propDecorators: { config: [{ type: Input }], template: [{ type: Input }], contentRef: [{ type: ViewChild, args: ['contentRef'] }] } }); class FilterSelectComponent { constructor(store, cd) { this.store = store; this.cd = cd; } get filterSelectOptions() { if (this.settings.lazy) { return this.store.getFilterSelectData(this.key) || []; } const filteredData = this.filterDataHandler.filter(this.store.filters, this.key); const options = filteredData.map(obj => get(obj, this.key)).filter(Boolean); return [...new Set(options)]; // delete duplicates } ngAfterViewInit() { this.cd.detectChanges(); } onFilter({ filter }) { const regexp = new RegExp(filter, 'i'); this.selectRef._filteredOptions = this.filterSelectOptions.filter((value) => regexp.test(value)); } } FilterSelectComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: FilterSelectComponent, deps: [{ token: MedTableService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); FilterSelectComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: FilterSelectComponent, selector: "filter-select", inputs: { key: "key", settings: "settings", filterDataHandler: "filterDataHandler" }, viewQueries: [{ propertyName: "selectRef", first: true, predicate: ["selectRef"], descendants: true }], ngImport: i0, template: "<p-columnFilter [field]=\"key\" matchMode=\"in\" [showMenu]=\"false\">\n <ng-template pTemplate=\"filter\" let-value let-filter=\"filterCallback\">\n <p-multiSelect\n #selectRef\n placeholder=\"\u0412\u0441\u0456\"\n styleClass=\"filter-select\"\n [ngModel]=\"value\"\n [options]=\"filterSelectOptions\"\n (onChange)=\"filter($event.value)\"\n (onFilter)=\"onFilter($event)\"\n >\n </p-multiSelect>\n </ng-template>\n</p-columnFilter>\n", styles: [":host ::ng-deep .filter-select .p-inputtext{min-width:180px}:host ::ng-deep .filter-select .p-multiselect-items{max-width:360px}:host ::ng-deep .filter-select .p-multiselect-item{white-space:break-spaces}\n"], components: [{ type: i2.ColumnFilter, selector: "p-columnFilter", inputs: ["field", "type", "display", "showMenu", "matchMode", "operator", "showOperator", "showClearButton", "showApplyButton", "showMatchModes", "showAddButton", "hideOnClear", "placeholder", "matchModeOptions", "maxConstraints", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "locale", "localeMatcher", "currency", "currencyDisplay", "useGrouping"] }, { type: i3.MultiSelect, selector: "p-multiSelect", inputs: ["style", "styleClass", "panelStyle", "panelStyleClass", "inputId", "disabled", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "appendTo", "dataKey", "name", "label", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "autoZIndex", "baseZIndex", "filterBy", "virtualScroll", "itemSize", "showTransitionOptions", "hideTransitionOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "scrollHeight", "defaultLabel", "placeholder", "options", "filterValue"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onPanelShow", "onPanelHide"] }], directives: [{ type: i1$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i5.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: FilterSelectComponent, decorators: [{ type: Component, args: [{ selector: 'filter-select', template: "<p-columnFilter [field]=\"key\" matchMode=\"in\" [showMenu]=\"false\">\n <ng-template pTemplate=\"filter\" let-value let-filter=\"filterCallback\">\n <p-multiSelect\n #selectRef\n placeholder=\"\u0412\u0441\u0456\"\n styleClass=\"filter-select\"\n [ngModel]=\"value\"\n [options]=\"filterSelectOptions\"\n (onChange)=\"filter($event.value)\"\n (onFilter)=\"onFilter($event)\"\n >\n </p-multiSelect>\n </ng-template>\n</p-columnFilter>\n", styles: [":host ::ng-deep .filter-select .p-inputtext{min-width:180px}:host ::ng-deep .filter-select .p-multiselect-items{max-width:360px}:host ::ng-deep .filter-select .p-multiselect-item{white-space:break-spaces}\n"] }] }], ctorParameters: function () { return [{ type: MedTableService }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { key: [{ type: Input }], settings: [{ type: Input }], filterDataHandler: [{ type: Input }], selectRef: [{ type: ViewChild, args: ['selectRef'] }] } }); class FormInputMixin { constructor() { this._value = ''; this.leave = new EventEmitter(); this.inputId = ''; } set value(val) { this._value = val; this.onChange(this._value); } get value() { return this._value; } ngAfterViewInit() { if (this.autoFocus) { setTimeout(() => { const { nativeElement, el } = this.inputEl; if (el === undefined) { return nativeElement.focus(); } el.nativeElement.querySelector('input').focus(); }, 0); } } onChange(_) { } writeValue(value) { this.value = value; } registerOnChange(fn) { this.onChange = fn; } registerOnTouched() { } } FormInputMixin.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: FormInputMixin, deps: [], target: i0.ɵɵFactoryTarget.Component }); FormInputMixin.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: FormInputMixin, selector: "ng-component", inputs: { handler: "handler", autoFocus: "autoFocus", inputId: "inputId", value: "value" }, outputs: { leave: "leave" }, viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["input"], descendants: true }], ngImport: i0, template: '', isInline: true }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: FormInputMixin, decorators: [{ type: Component, args: [{ template: '', }] }], propDecorators: { inputEl: [{ type: ViewChild, args: ['input'] }], leave: [{ type: Output }], handler: [{ type: Input }], autoFocus: [{ type: Input }], inputId: [{ type: Input }], value: [{ type: Input }] } }); class DateRangeComponent extends FormInputMixin { constructor() { super(...arguments); this._value = []; this.select = new EventEmitter(); } } DateRangeComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: DateRangeComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); DateRangeComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: DateRangeComponent, selector: "date-range", outputs: { select: "select" }, providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => DateRangeComponent), multi: true }], usesInheritance: true, ngImport: i0, template: "<p-calendar\n #input\n [touchUI]=\"true\"\n [showIcon]=\"true\"\n [inputId]=\"inputId\"\n [(ngModel)]=\"value\"\n dateFormat=\"dd.mm.yy\"\n selectionMode=\"range\"\n appendTo=\"body\"\n (focusout)=\"leave.emit()\"\n (keydown.enter)=\"leave.emit()\"\n (onClose)=\"select.emit(value)\"\n [panelStyle]=\"{'mim-width': '0', 'max-width': '360px'}\"\n></p-calendar>\n\n", styles: [""], components: [{ type: i1$2.Calendar, selector: "p-calendar", inputs: ["style", "styleClass", "inputStyle", "inputId", "name", "inputStyleClass", "placeholder", "ariaLabelledBy", "iconAriaLabel", "disabled", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "appendTo", "readonlyInput", "shortYearCutoff", "monthNavigator", "yearNavigator", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "required", "showOnFocus", "showWeek", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "view", "defaultDate", "minDate", "maxDate", "disabledDates", "disabledDays", "yearRange", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "locale"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }], directives: [{ type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i5.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: DateRangeComponent, decorators: [{ type: Component, args: [{ selector: 'date-range', providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => DateRangeComponent), multi: true }], template: "<p-calendar\n #input\n [touchUI]=\"true\"\n [showIcon]=\"true\"\n [inputId]=\"inputId\"\n [(ngModel)]=\"value\"\n dateFormat=\"dd.mm.yy\"\n selectionMode=\"range\"\n appendTo=\"body\"\n (focusout)=\"leave.emit()\"\n (keydown.enter)=\"leave.emit()\"\n (onClose)=\"select.emit(value)\"\n [panelStyle]=\"{'mim-width': '0', 'max-width': '360px'}\"\n></p-calendar>\n\n", styles: [""] }] }], propDecorators: { select: [{ type: Output }] } }); const dateRangeFilterName = 'date-range'; class FilterDateComponent { constructor(filterService) { this.filterService = filterService; this.matchModeOptions = [ { label: 'Date range', value: dateRangeFilterName }, ]; } ngOnInit() { this.filterService.register(dateRangeFilterName, FilterMethods.dateRangeHandler); } } FilterDateComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: FilterDateComponent, deps: [{ token: i1$1.FilterService }], target: i0.ɵɵFactoryTarget.Component }); FilterDateComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: FilterDateComponent, selector: "filter-date", inputs: { key: "key" }, ngImport: i0, template: "<p-columnFilter\n type=\"date\"\n matchMode=\"date-range\"\n [showMenu]=\"false\"\n [field]=\"key\"\n [matchModeOptions]=\"matchModeOptions\"\n>\n <ng-template pTemplate=\"filter\" let-value let-filter=\"filterCallback\">\n <date-range\n [ngModel]=\"value\"\n (select)=\"filter($event)\"\n ></date-range>\n </ng-template>\n</p-columnFilter>\n", styles: [""], components: [{ type: i2.ColumnFilter, selector: "p-columnFilter", inputs: ["field", "type", "display", "showMenu", "matchMode", "operator", "showOperator", "showClearButton", "showApplyButton", "showMatchModes", "showAddButton", "hideOnClear", "placeholder", "matchModeOptions", "maxConstraints", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "locale", "localeMatcher", "currency", "currencyDisplay", "useGrouping"] }, { type: DateRangeComponent, selector: "date-range", outputs: ["select"] }], directives: [{ type: i1$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i5.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: FilterDateComponent, decorators: [{ type: Component, args: [{ selector: 'filter-date', template: "<p-columnFilter\n type=\"date\"\n matchMode=\"date-range\"\n [showMenu]=\"false\"\n [field]=\"key\"\n [matchModeOptions]=\"matchModeOptions\"\n>\n <ng-template pTemplate=\"filter\" let-value let-filter=\"filterCallback\">\n <date-range\n [ngModel]=\"value\"\n (select)=\"filter($event)\"\n ></date-range>\n </ng-template>\n</p-columnFilter>\n", styles: [""] }] }], ctorParameters: function () { return [{ type: i1$1.FilterService }]; }, propDecorators: { key: [{ type: Input }] } }); const checkboxFilterName = 'bool-exact'; class FilterCheckboxComponent { constructor(filterService) { this.filterService = filterService; this.matchModeOptions = [ { label: 'Checkbox', value: checkboxFilterName }, ]; } ngOnInit() { this.filterService.register(checkboxFilterName, FilterMethods.checkboxHandler); } } FilterCheckboxComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: FilterCheckboxComponent, deps: [{ token: i1$1.FilterService }], target: i0.ɵɵFactoryTarget.Component }); FilterCheckboxComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: FilterCheckboxComponent, selector: "filter-checkbox", inputs: { key: "key" }, ngImport: i0, template: "<p-columnFilter\n type=\"boolean\"\n matchMode=\"bool-exact\"\n [field]=\"key\"\n [matchModeOptions]=\"matchModeOptions\"\n></p-columnFilter>\n", styles: [""], components: [{ type: i2.ColumnFilter, selector: "p-columnFilter", inputs: ["field", "type", "display", "showMenu", "matchMode", "operator", "showOperator", "showClearButton", "showApplyButton", "showMatchModes", "showAddButton", "hideOnClear", "placeholder", "matchModeOptions", "maxConstraints", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "locale", "localeMatcher", "currency", "currencyDisplay", "useGrouping"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: FilterCheckboxComponent, decorators: [{ type: Component, args: [{ selector: 'filter-checkbox', template: "<p-columnFilter\n type=\"boolean\"\n matchMode=\"bool-exact\"\n [field]=\"key\"\n [matchModeOptions]=\"matchModeOptions\"\n></p-columnFilter>\n", styles: [""] }] }], ctorParameters: function () { return [{ type: i1$1.FilterService }]; }, propDecorators: { key: [{ type: Input }] } }); class FilterTextComponent { } FilterTextComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: FilterTextComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); FilterTextComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: FilterTextComponent, selector: "filter-text", inputs: { key: "key" }, ngImport: i0, template: "<p-columnFilter\n type=\"text\"\n matchMode=\"contains\"\n [field]=\"key\"\n [showMenu]=\"false\"\n [showClearButton]=\"false\"\n></p-columnFilter>\n", styles: [""], components: [{ type: i2.ColumnFilter, selector: "p-columnFilter", inputs: ["field", "type", "display", "showMenu", "matchMode", "operator", "showOperator", "showClearButton", "showApplyButton", "showMatchModes", "showAddButton", "hideOnClear", "placeholder", "matchModeOptions", "maxConstraints", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "locale", "localeMatcher", "currency", "currencyDisplay", "useGrouping"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: FilterTextComponent, decorators: [{ type: Component, args: [{ selector: 'filter-text', template: "<p-columnFilter\n type=\"text\"\n matchMode=\"contains\"\n [field]=\"key\"\n [showMenu]=\"false\"\n [showClearButton]=\"false\"\n></p-columnFilter>\n", styles: [""] }] }], propDecorators: { key: [{ type: Input }] } }); class EditorBuilder { constructor(options) { this.onLeave = () => { }; this.autoFocus = true; this.wrappers = []; this.inputMask = ''; this.fieldWrapperSelector = '.table-cell__content'; this.pattern = PATTERN_TYPES.NONE; this.key = options.key; this.onLeave = options.onLeave; this.editorType = options.editorType || FIELD_TYPES.TEXT; this.inputMask = options.inputMask || this.inputMask; this.pattern = options.pattern || this.pattern; } } const DEFAULT_VISIBLE_EDITOR_HANDLER = () => true; class TableCellComponent { constructor(cd, fb) { this.cd = cd; this.fb = fb; this.update = new EventEmitter(); this.onFocus = new EventEmitter(); this.cellType = CELL_TYPES; this.fields = []; this.form = new FormGroup({}); this._item = {}; this._config = { key: '', label: '' }; this._tableCell = new TableCell(); } set config(newValue) { this._tableCell.setConfig(newValue); this._config = newValue; } get config() { return this._config; } set item(newValue) { this._tableCell.setItem(newValue); this._item = newValue; } get item() { return this._item; } get fieldData() { return this._tableCell.fieldData; } get previewData() { return this._tableCell.previewData; } get linkUrl() { const { url, keyParam } = this.config.linkOptions || {}; if (!url) return STRING.EMPTY; if (keyParam) { const param = this._tableCell.getValue(this.item, keyParam); return `${url}?${keyParam}=${param}`; } return url; } get linkTarget() { const { linkOptions: { target } = {} } = this.config; return target ? target : '_self'; } get isEditable() { const { editorType, visibleEditorHandler = DEFAULT_VISIBLE_EDITOR_HANDLER } = this.config; return Boolean(editorType) && visibleEditorHandler(this.item); } get isEmptyTemplate() { // TODO should show default value if slot "template" is empty const { nativeElement } = this.contentRef || {}; return !(nativeElement && nativeElement.innerText); } ngOnInit() { const { key } = this.config; const keyList = key.split(STRING.DOT); this.form.addControl(...this.setFormControl(keyList)); } ngAfterViewInit() { this.cd.detectChanges(); } setEditor() { if (!this.isEditable) { return; } const field = new EditorBuilder({ ...this.config, onLeave: this.onLeave.bind(this), }); this.onFocus.emit({ item: this.item, key: this.config.key }); this.fields.push(field); } onLeave() { const { key } = this.config; const { value } = this.form; if (this._tableCell.getValue(value, key) !== this.fieldData) { const item = set(deepClone(this.item), key, this._tableCell.getValue(value, key)); this.update.emit({ item, key }); } this.fields = []; } setFormControl([firstKey, ...keys]) { if (!keys.length) return [firstKey, this.fb.control(this.fieldData)]; const [newKey, control] = this.setFormControl(keys); return [firstKey, this.fb.group({ [newKey]: control })]; } } TableCellComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: TableCellComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i5.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); TableCellComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: TableCellComponent, selector: "table-cell", inputs: { template: "template", config: "config", item: "item" }, outputs: { update: "update", onFocus: "onFocus" }, viewQueries: [{ propertyName: "contentRef", first: true, predicate: ["contentRef"], descendants: true }], ngImport: i0, template: "<div class=\"table-cell__item\">\n <span class=\"table-cell__column-title\">\n {{ config.label }}\n </span>\n\n <span class=\"table-cell__content\">\n <med-dynamic-form\n *ngIf=\"fields.length\"\n [config]=\"fields\"\n [form]=\"form\"\n ></med-dynamic-form>\n\n <div\n [hidden]=\"fields.length\"\n [className]=\"isEditable ? 'table-cell__editable' : ''\"\n (click)=\"setEditor()\"\n >\n <span #contentRef>\n <ng-container *ngTemplateOutlet=\"\n template || defaultTemplate;\n context: { $implicit: previewData, item: item }\"\n ></ng-container>\n </span>\n\n <ng-container\n *ngIf=\"isEmptyTemplate\"\n [ngTemplateOutlet]=\"config.cellType === cellType.LINK ? linkTemplate : defaultTemplate\"\n ></ng-container>\n </div>\n </span>\n</div>\n\n<ng-template #linkTemplate>\n <a [attr.href]=\"linkUrl\" [attr.target]=\"linkTarget\" class=\"table-cell__link\">{{ previewData }}</a>\n</ng-template>\n\n<ng-template #defaultTemplate>\n {{ previewData }}\n</ng-template>\n", styles: [":host{width:100%}.table-cell__item{display:flex;width:100%;justify-content:flex-start!important}@media screen and (max-width: 960px){.table-cell__item{font-size:14px}}.table-cell__column-title{display:none;padding:5px;font-weight:700;flex:0 0 40%}@media screen and (max-width: 960px){.table-cell__column-title{display:block}}.table-cell__content{width:100%;height:100%;padding:5px;overflow-wrap:break-word;word-wrap:break-word;-webkit-hyphens:auto;hyphens:auto;word-break:break-all}.table-cell__editable{cursor:pointer;width:100%;height:100%;color:var(--green-700)}.table-cell__editable:hover{color:var(--green-400)}.table-cell__link{cursor:pointer;width:100%;height:100%;color:#3b82f6;text-decoration:none}.table-cell__link:hover{color:#3b6bff;text-decoration:underline}\n"], components: [{ type: i1.MedDynamicFormComponent, selector: "med-dynamic-form", inputs: ["config", "form"] }], directives: [{ type: i10.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i10.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: TableCellComponent, decorators: [{ type: Component, args: [{ selector: 'table-cell', template: "<div class=\"table-cell__item\">\n <span class=\"table-cell__column-title\">\n {{ config.label }}\n </span>\n\n <span class=\"table-cell__content\">\n <med-dynamic-form\n *ngIf=\"fields.length\"\n [config]=\"fields\"\n [form]=\"form\"\n ></med-dynamic-form>\n\n <div\n [hidden]=\"fields.length\"\n [className]=\"isEditable ? 'table-cell__editable' : ''\"\n (click)=\"setEditor()\"\n >\n <span #contentRef>\n <ng-container *ngTemplateOutlet=\"\n template || defaultTemplate;\n context: { $implicit: previewData, item: item }\"\n ></ng-container>\n </span>\n\n <ng-container\n *ngIf=\"isEmptyTemplate\"\n [ngTemplateOutlet]=\"config.cellType === cellType.LINK ? linkTemplate : defaultTemplate\"\n ></ng-container>\n </div>\n </span>\n</div>\n\n<ng-template #linkTemplate>\n <a [attr.href]=\"linkUrl\" [attr.target]=\"linkTarget\" class=\"table-cell__link\">{{ previewData }}</a>\n</ng-template>\n\n<ng-template #defaultTemplate>\n {{ previewData }}\n</ng-template>\n", styles: [":host{width:100%}.table-cell__item{display:flex;width:100%;justify-content:flex-start!important}@media screen and (max-width: 960px){.table-cell__item{font-size:14px}}.table-cell__column-title{display:none;padding:5px;font-weight:700;flex:0 0 40%}@media screen and (max-width: 960px){.table-cell__column-title{display:block}}.table-cell__content{width:100%;height:100%;padding:5px;overflow-wrap:break-word;word-wrap:break-word;-webkit-hyphens:auto;hyphens:auto;word-break:break-all}.table-cell__editable{cursor:pointer;width:100%;height:100%;color:var(--green-700)}.table-cell__editable:hover{color:var(--green-400)}.table-cell__link{cursor:pointer;width:100%;height:100%;color:#3b82f6;text-decoration:none}.table-cell__link:hover{color:#3b6bff;text-decoration:underline}\n"] }] }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i5.FormBuilder }]; }, propDecorators: { template: [{ type: Input }], config: [{ type: Input }],