UNPKG

novo-elements

Version:

Bullhorn's NOVO Element Repository for Angular 2

848 lines 52.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); // NG2 var core_1 = require("@angular/core"); var forms_1 = require("@angular/forms"); // Vendor var dateFns = require("date-fns"); // APP var novo_label_service_1 = require("../../services/novo-label-service"); var Helpers_1 = require("../../utils/Helpers"); var FormUtils_1 = require("../../utils/form-utils/FormUtils"); var FormControls_1 = require("./../form/FormControls"); var CollectionEvent_1 = require("../../services/data-provider/CollectionEvent"); var PagedArrayCollection_1 = require("../../services/data-provider/PagedArrayCollection"); // TODO - support (1) clicking cell to edit, (2) clicking row to edit, (3) button to trigger full table to edit var NovoTableMode; (function (NovoTableMode) { NovoTableMode[NovoTableMode["VIEW"] = 1] = "VIEW"; NovoTableMode[NovoTableMode["EDIT"] = 2] = "EDIT"; })(NovoTableMode = exports.NovoTableMode || (exports.NovoTableMode = {})); var NovoTableElement = (function () { function NovoTableElement(labels, formUtils, builder) { this.labels = labels; this.formUtils = formUtils; this.builder = builder; this.config = {}; this.skipSortAndFilterClear = false; this.mode = NovoTableMode.VIEW; this.editable = false; this.onRowClick = new core_1.EventEmitter(); this.onRowSelect = new core_1.EventEmitter(); this.onTableChange = new core_1.EventEmitter(); this._rows = []; this.selected = []; this.activeId = 0; this.master = false; this.expandAll = false; this.indeterminate = false; this.lastPage = 0; this.selectedPageCount = 0; this.showSelectAllMessage = false; this.pagedData = []; // Map to keep track of what dropdowns are toggled // Used to properly *ngIf the <list> so that the keepFilterFocused Directive // will properly fire the ngAfterViewInit event this.toggledDropdownMap = {}; this.NovoTableMode = NovoTableMode; this.tableForm = new forms_1.FormGroup({}); this.footers = []; this.grossFlagToAvoidTheTableFromBeingUglyWhenHidingTheToast = false; this.loading = false; } Object.defineProperty(NovoTableElement.prototype, "rows", { get: function () { return this._rows; }, set: function (rows) { this.dataProvider = rows; if (rows && rows.length > 0) { this.setupColumnDefaults(); } // this is a temporary/hacky fix until async dataloading is handled within the table if (!this.skipSortAndFilterClear) { this.clearAllSortAndFilters(); } }, enumerable: true, configurable: true }); Object.defineProperty(NovoTableElement.prototype, "dataProvider", { get: function () { return this._dataProvider; }, set: function (dp) { var _this = this; this._dataProvider = Array.isArray(dp) ? new PagedArrayCollection_1.PagedArrayCollection(dp) : dp; this._dataProvider.dataChange.debounceTime(100).subscribe(function (event) { switch (event.type) { case CollectionEvent_1.CollectionEvent.CHANGE: _this._rows = event.data; // Setup form _this.tableForm = _this.builder.group({ rows: _this.builder.array([]) }); // Remove all selection on sort change if selection is on if (_this.config.rowSelectionStyle === 'checkbox') { _this.pagedData = event.data; _this.pageSelected = _this.pagedData.filter(function (r) { return r._selected; }); _this.rowSelectHandler(); } // Find that columns we might need to sum up via the footer var columnsToSum_1 = []; var columnSums_1 = {}; if (_this.config.footers) { _this.config.footers.forEach(function (config) { columnsToSum_1.push.apply(columnsToSum_1, config.columns); }); // Only have unique columns, filter out duplicates columnsToSum_1 = columnsToSum_1.filter(function (item, index, array) { return array.indexOf(item) === index; }); } // Make a form for each row var tableFormRows_1 = _this.tableForm.controls['rows']; _this._rows.forEach(function (row, index) { var rowControls = []; row.controls = {}; row._editing = {}; row._expanded = _this.config.expandAll; row.rowId = _this._rows.length; _this.columns.forEach(function (column) { // Use the control passed or use a ReadOnlyControl so that the form has the values var control = column.editorConfig ? FormControls_1.ControlFactory.create(column.editorType, column.editorConfig) : new FormControls_1.ReadOnlyControl({ key: column.name }); row.controls[column.name] = control; rowControls.push(control); }); _this.formUtils.setInitialValues(rowControls, row, false); tableFormRows_1.push(_this.formUtils.toFormGroup(rowControls)); // Setup the total footer if configured // Array of keys to total if (columnsToSum_1.length !== 0) { columnsToSum_1.forEach(function (column) { if (Helpers_1.Helpers.isBlank(columnSums_1[column])) { columnSums_1[column] = 0; } columnSums_1[column] += row[column]; }); } }); if (_this.mode === NovoTableMode.EDIT) { _this.setTableEdit(); } // Setup the footers (if any) if (_this.config.footers) { _this.footers = []; _this.config.footers.forEach(function (footerConfig, footerConfigIndex) { var footer = {}; footer[footerConfig.labelColumn] = footerConfig.label; footerConfig.columns.forEach(function (column) { if (footerConfig.method === 'AVG' && _this._rows.length !== 0) { footer[column] = columnSums_1[column] / _this._rows.length; } else { footer[column] = columnSums_1[column]; } }); _this.footers.push(footer); }); } break; default: break; } }); if (this.config.paging) { this._dataProvider.page = this.config.paging.current; this._dataProvider.pageSize = this.config.paging.itemsPerPage; } else { // Paging turned off, return basically all of the data this._dataProvider.page = 1; this._dataProvider.pageSize = 500; } if (dp && dp.length > 0) { this.setupColumnDefaults(); } this._dataProvider.refresh(); }, enumerable: true, configurable: true }); Object.defineProperty(NovoTableElement.prototype, "editing", { get: function () { return this.mode === NovoTableMode.EDIT; }, enumerable: true, configurable: true }); Object.defineProperty(NovoTableElement.prototype, "formValue", { get: function () { return this.tableForm.value; }, enumerable: true, configurable: true }); NovoTableElement.prototype.onDropdownToggled = function (event, column) { this.toggledDropdownMap[column] = event; }; NovoTableElement.prototype.onPageChange = function (event) { //this.dataProvider.page = event.page; //this.dataProvider.pageSize = event.itemsPerPage; }; NovoTableElement.prototype.getOptionDataAutomationId = function (option) { if (!Helpers_1.Helpers.isBlank(option.value)) { return option.value; } return option; }; /** * @name setupColumnDefaults */ NovoTableElement.prototype.setupColumnDefaults = function () { var _this = this; // Check columns for cell option types this.columns.forEach(function (column) { if (column && column.type) { switch (column.type) { case 'date': // Set options based on dates if there are none column.options = (column.options || _this.getDefaultOptions(column)); break; default: break; } } }); }; /** * @name ngDoCheck */ NovoTableElement.prototype.ngDoCheck = function () { if (this.config.paging && this.config.paging.current !== this.lastPage) { this.rowSelectHandler(); this.showSelectAllMessage = false; } this.lastPage = this.config.paging ? this.config.paging.current : 1; }; /** * @name getPageStart * @returns {number} */ NovoTableElement.prototype.getPageStart = function () { return this.config.paging ? (this.dataProvider.page - 1) * this.dataProvider.pageSize : 0; }; /** * @name getPageEnd * @returns {*} */ NovoTableElement.prototype.getPageEnd = function () { return this.config.paging && this.dataProvider.pageSize > -1 ? this.getPageStart() + this.dataProvider.pageSize : this.rows.length; }; /** * @name onFilterClick * @param column * @param filter */ NovoTableElement.prototype.onFilterClick = function (column, filter) { if (filter.range && !column.calendarShow) { column.calenderShow = true; return; } if (Array.isArray(column.filter) && column.multiple) { if (~column.filter.indexOf(filter)) { // Remove filter column.filter.splice(column.filter.indexOf(filter), 1); if (filter.range) { column.calenderShow = false; } if (column.filter.length === 0) { column.filter = null; } } else { // Add filter column.filter.push(filter); } } else { column.filter = Helpers_1.Helpers.isBlank(filter.value) ? filter : filter.value; } this.onFilterChange(); }; /** * @name onFilterClear * @param column */ NovoTableElement.prototype.onFilterClear = function (column) { var _this = this; setTimeout(function () { column.filter = null; column.freetextFilter = null; _this.onFilterChange(); if (column.originalOptions) { column.options = column.originalOptions; } }); }; NovoTableElement.prototype.clearAllSortAndFilters = function () { if (this.config.filtering) { this.columns.forEach(function (column) { column.filter = null; column.sort = null; }); } }; /** * @name onFilterChange * * @description This method updates the row data to reflect the active filters. */ NovoTableElement.prototype.onFilterChange = function () { if (this.config.filtering) { // Array of filters var filters = this.columns.filter(function (col) { return !Helpers_1.Helpers.isEmpty(col.filter); }); if (filters.length) { var query = {}; var _loop_1 = function (column) { if (Helpers_1.Helpers.isFunction(column.match)) { query[column.name] = function (value, record) { return column.match(record, column.filter); }; } else if (column.preFilter && Helpers_1.Helpers.isFunction(column.preFilter)) { query = Object.assign({}, query, column.preFilter(this_1.escapeCharacters(column.filter))); } else if (Array.isArray(column.filter)) { // The filters are an array (multi-select), check value var options = column.filter; // We have an array of {value: '', labels: ''} if (options[0].value || options[0].label) { options = column.filter.map(function (opt) { return opt.value; }); } query[column.name] = { any: options }; } else if (column.type && column.type === 'date') { if (column.filter.startDate && column.filter.endDate) { query[column.name] = { min: dateFns.startOfDay(column.filter.startDate), max: dateFns.startOfDay(dateFns.addDays(dateFns.startOfDay(column.filter.endDate), 1)) }; } else { query[column.name] = { min: column.filter.min ? dateFns.addDays(dateFns.startOfToday(), column.filter.min) : dateFns.startOfToday(), max: column.filter.max ? dateFns.addDays(dateFns.startOfTomorrow(), column.filter.max) : dateFns.startOfTomorrow() }; } } else { query[column.name] = column.filter; } }; var this_1 = this; for (var _i = 0, filters_1 = filters; _i < filters_1.length; _i++) { var column = filters_1[_i]; _loop_1(column); } if (Helpers_1.Helpers.isFunction(this.config.filtering)) { this.config.filtering(query); } else { this._dataProvider.filter = query; } } else { this._dataProvider.filter = {}; } // Trickle down to keep sort // this.onSortChange(this.currentSortColumn); // If paging, reset page if (this.config.paging) { this.config.paging.current = 1; } // Remove all selection on sort change if selection is on if (this.config.rowSelectionStyle === 'checkbox') { this.selectAll(false); } } }; NovoTableElement.prototype.escapeCharacters = function (filter) { if (typeof (filter) === 'string') { return filter.replace(/'/g, '\'\''); } return filter; }; /** * @name isFilterActive * @param column * @param filter * @returns {boolean} * * @description */ NovoTableElement.prototype.isFilterActive = function (column, filter) { //TODO: This needs to be refactored var isActive = false; if (column && !Helpers_1.Helpers.isBlank(column.filter) && !Helpers_1.Helpers.isBlank(filter)) { if (Array.isArray(column.filter)) { if (typeof (filter) !== 'string') { isActive = column.filter.some(function (item) { return item.label === filter.label; }); } else { isActive = column.filter.includes(filter); } } else { if (typeof (column.filter) === typeof (filter)) { isActive = (column.filter === filter); } else { isActive = (column.filter === filter.value); } } } return isActive; }; /** * @name onSortChange * @param newSortColumn */ NovoTableElement.prototype.onSortChange = function (column) { var _this = this; this.currentSortColumn = column; var sortedColumns = this.columns.filter(function (thisColumn) { return thisColumn.sort && thisColumn !== _this.currentSortColumn; }); for (var _i = 0, sortedColumns_1 = sortedColumns; _i < sortedColumns_1.length; _i++) { var sortedColumn = sortedColumns_1[_i]; sortedColumn.sort = null; } if (column) { if (Helpers_1.Helpers.isFunction(this.config.sorting)) { this.config.sorting(); } else if (Helpers_1.Helpers.isFunction(column.preSort)) { this._dataProvider.sort = [].concat(column.preSort(column)); } else { this._dataProvider.sort = [{ field: (column.compare || column.name), reverse: column.sort === 'desc' }]; } } // Fire table change event // this.fireTableChangeEvent(); // If paging, reset page if (this.config.paging) { this.config.paging.current = 1; } // Remove all selection on sort change if selection is on if (this.config.rowSelectionStyle === 'checkbox') { this.selectAll(false); } }; /** * @name fireTableChangeEvent */ NovoTableElement.prototype.fireTableChangeEvent = function () { // Construct a table change object var onTableChange = {}; var filters = this.columns.filter(function (col) { return col.filter && col.filter.length; }); onTableChange.filter = filters.length ? filters : false; onTableChange.sort = this.currentSortColumn ? this.currentSortColumn : false; onTableChange.rows = this.rows; // Emit event this.onTableChange.emit(onTableChange); }; /** * @name findColumnIndex * @param value * @returns {*} */ NovoTableElement.prototype.findColumnIndex = function (value) { for (var i = 0; i < this.columns.length; i += 1) { if (this.columns[i].name === value) { return i; } } return null; }; /** * @name onOrderChange * @param event */ NovoTableElement.prototype.onOrderChange = function (event) { var oldIndex = this.findColumnIndex(event.first.name); var newIndex = this.findColumnIndex(event.second.name); this.columns.splice(newIndex, 0, this.columns.splice(oldIndex, 1)[0]); this.onSortChange(this.currentSortColumn); }; /** * @name selectPage */ NovoTableElement.prototype.expandAllOnPage = function (expanded) { this.config.expandAll = !expanded; for (var _i = 0, _a = this.dataProvider.list; _i < _a.length; _i++) { var row = _a[_i]; row._expanded = this.config.expandAll; } }; /** * @name selectPage */ NovoTableElement.prototype.selectPage = function () { if (!this.master) { this.selectAll(false); // Only show the select all message when there is only one new page selected at a time this.selectedPageCount = this.selectedPageCount > 0 ? this.selectedPageCount - 1 : 0; this.showSelectAllMessage = false; } else { this.indeterminate = false; //this.pagedData = this.rows.slice(this.getPageStart(), this.getPageEnd()); for (var _i = 0, _a = this.pagedData; _i < _a.length; _i++) { var row = _a[_i]; row._selected = this.master; } this.selected = this.dataProvider.list.filter(function (r) { return r._selected; }); this.pageSelected = this.pagedData.filter(function (r) { return r._selected; }); this.emitSelected(this.selected); // Only show the select all message when there is only one new page selected at a time this.selectedPageCount++; this.showSelectAllMessage = this.selectedPageCount === 1 && this.selected.length !== this.dataProvider.total; } }; /** * @name selectAll */ NovoTableElement.prototype.selectAll = function (value) { this.master = value; this.indeterminate = false; for (var _i = 0, _a = this.dataProvider.list; _i < _a.length; _i++) { var row = _a[_i]; row._selected = value; } this.selected = value ? this.dataProvider.list : []; this.showSelectAllMessage = false; this.selectedPageCount = this.selectedPageCount > 0 ? this.selectedPageCount - 1 : 0; this.rowSelectHandler(); }; /** * @name rowSelectHandler */ NovoTableElement.prototype.rowSelectHandler = function () { //this.pagedData = this.rows.slice(this.getPageStart(), this.getPageEnd()); this.pageSelected = this.pagedData.filter(function (r) { return r._selected; }); this.selected = this.dataProvider.list.filter(function (r) { return r._selected; }); if (this.pageSelected.length === 0) { this.master = false; this.indeterminate = false; } else if (this.pageSelected.length === this.pagedData.length) { this.master = true; this.indeterminate = false; } else { this.master = false; this.indeterminate = true; // Breaking the selected page count this.showSelectAllMessage = false; this.selectedPageCount = this.selectedPageCount > 0 ? this.selectedPageCount - 1 : 0; } this.emitSelected(this.selected); }; /** * @name emitSelected * @param selected */ NovoTableElement.prototype.emitSelected = function (selected) { this.onRowSelect.emit({ length: selected.length, selected: selected }); }; /** * @name rowClickHandler * @param row */ NovoTableElement.prototype.rowClickHandler = function (row) { if (this.config.rowSelect) { this.activeId = row.id || 0; this.onRowClick.emit(row); } }; /** * @name setDateOptions * @returns {Array} */ NovoTableElement.prototype.getDefaultOptions = function (column) { // TODO - needs to come from label service - https://github.com/bullhorn/novo-elements/issues/116 var opts = [ { label: this.labels.past1Day, min: -1, max: 0 }, { label: this.labels.past7Days, min: -7, max: 0 }, { label: this.labels.past30Days, min: -30, max: 0 }, { label: this.labels.past90Days, min: -90, max: 0 }, { label: this.labels.past1Year, min: -366, max: 0 }, { label: this.labels.next1Day, min: 0, max: 1 }, { label: this.labels.next7Days, min: 0, max: 7 }, { label: this.labels.next30Days, min: 0, max: 30 }, { label: this.labels.next90Days, min: 0, max: 90 }, { label: this.labels.next1Year, min: 0, max: 366 } ]; if (column && column.range) { opts.push({ label: this.labels.customDateRange, range: true }); } return opts; }; NovoTableElement.prototype.onCalenderSelect = function (column, event) { var _this = this; setTimeout(function () { if (event.startDate && event.endDate) { _this.onFilterChange(); } }, 10); }; NovoTableElement.prototype.onFilterKeywords = function (config) { if (config && config.filtering && config.filtering.freetextFilter) { var filterKeywords_1 = config.filtering.freetextFilter.toLowerCase(); if (!config.filtering.originalOptions) { config.filtering.originalOptions = config.filtering.options; } var newOptions = config.filtering.originalOptions.filter(function (option) { var value = option && option.label ? option.label : option; value = value.toLowerCase() ? value.toLowerCase() : value; if (value === filterKeywords_1) { return true; } else if (~value.indexOf(filterKeywords_1) || ~value.indexOf(filterKeywords_1)) { return true; } return false; }); config.filtering.options = newOptions; config.filtering.filter = config.filtering.freetextFilter; } else { config.filtering.options = config.filtering.originalOptions; } this.onFilterChange(); }; /** * @name setTableEdit * @description Sets the Table into EDIT mode, based on the row/column passed you can enter in a few states * (1) setTableEdit() - don't pass any to put the FULL table into edit mode * (2) setTableEdit(1) - pass only row to put that FULL row of the table into edit mode * (3) setTableEdit(1, 1) - pass row and column to put that column of the row of the table into edit mode * @param {number} [rowNumber] * @param {number} [columnNumber] * @memberOf NovoTableElement */ NovoTableElement.prototype.setTableEdit = function (rowNumber, columnNumber) { var _this = this; this.mode = NovoTableMode.EDIT; this._dataProvider.edit(); this._rows.forEach(function (row, rowIndex) { row._editing = row._editing || {}; _this.columns.forEach(function (column, columnIndex) { if (column.viewOnly) { row._editing[column.name] = false; } else if (Helpers_1.Helpers.isEmpty(rowNumber) && Helpers_1.Helpers.isEmpty(columnNumber)) { row._editing[column.name] = true; } else if (!Helpers_1.Helpers.isEmpty(rowNumber) && rowIndex === Number(rowNumber) && Helpers_1.Helpers.isEmpty(columnNumber)) { row._editing[column.name] = true; } else if (!Helpers_1.Helpers.isEmpty(rowNumber) && !Helpers_1.Helpers.isEmpty(columnNumber) && rowIndex === Number(rowNumber) && columnIndex === Number(columnNumber)) { row._editing[column.name] = true; } else { row._editing[column.name] = false; } }); }); }; /** * @name leaveEditMode * @description Leaves edit mode for the Table and puts everything back to VIEW only * @memberOf NovoTableElement * @param {cancel} [boolean] - whether or not to save data or undo */ NovoTableElement.prototype.leaveEditMode = function (cancel) { var _this = this; this.mode = NovoTableMode.VIEW; this._rows.forEach(function (row) { row._editing = row._editing || {}; _this.columns.forEach(function (column) { row._editing[column.name] = false; }); }); if (cancel) { this._dataProvider.undo(); } else { this._dataProvider.commit(); } this.hideToastMessage(); }; /** * @name addEditableRow * @description Adds a new row into the table to be edited, can be called from a local reference of the table in your template * @param {*} [defaultValue={}] * @memberOf NovoTableElement */ NovoTableElement.prototype.addEditableRow = function (defaultValue) { if (defaultValue === void 0) { defaultValue = {}; } var tableFormRows = this.tableForm.controls['rows']; var row = {}; var rowControls = []; row.controls = {}; row._editing = {}; row.rowId = this._rows.length + 1; this.columns.forEach(function (column) { // Use the control passed or use a ReadOnlyControl so that the form has the values var control = column.editorConfig ? FormControls_1.ControlFactory.create(column.editorType, column.editorConfig) : new FormControls_1.ReadOnlyControl({ key: column.name }); control.value = null; //remove copied column value row.controls[column.name] = control; row._editing[column.name] = !column.viewOnly; rowControls.push(control); }); this.formUtils.setInitialValues(rowControls, defaultValue, false); tableFormRows.push(this.formUtils.toFormGroup(rowControls)); this._rows.push(row); }; /** * @name validateAndGetUpdatedData * @description Validates the Form inside of the Table, if there are errors it will display/return the errors for each row. * If there are no errors, then it will return ONLY the changed data for each row, the data returned will be in the form: * { id: ID_OF_RECORD, key: value } -- data that was updated * { id: undefined, key: value } -- data that was added * @returns {{ changed?: any[], errors?: { errors: any, row: any, index: number }[] }} - either the changed data or errors! * @memberOf NovoTableElement */ NovoTableElement.prototype.validateAndGetUpdatedData = function () { var _this = this; if (this.tableForm && this.tableForm.controls && this.tableForm.controls['rows']) { var changedRows_1 = []; var errors_1 = []; // Go over the FormArray's controls this.tableForm.controls['rows'].controls.forEach(function (formGroup, index) { var changedRow = null; var error = null; // Go over the form group controls Object.keys(formGroup.controls).forEach(function (key) { var control = formGroup.controls[key]; // Handle value changing if (control && control.dirty && !control.errors) { if (!changedRow) { // Append the ID, so we have some key to save against changedRow = {}; if (_this._rows[index].id) { changedRow.id = _this._rows[index].id; } } // If dirty, grab value off the form changedRow[key] = _this.tableForm.value['rows'][index][key]; // Set value back to row (should be already done via the server call, but do it anyway) _this._rows[index][key] = changedRow[key]; } else if (control && control.errors) { // Handle errors if (!error) { error = {}; } error[key] = control.errors; control.markAsDirty(); control.markAsTouched(); } }); if (changedRow) { changedRows_1.push(changedRow); } if (error) { errors_1.push({ errors: error, row: _this._rows[index], index: index }); } }); var ret = {}; // Return errors if any, otherwise return the changed rows if (errors_1.length === 0) { return { changed: changedRows_1 }; } return { errors: errors_1 }; } }; /** * @name cancelEditing * @description Refresh the data provider and leave edit mode * @memberOf NovoTableElement */ NovoTableElement.prototype.cancelEditing = function () { this.leaveEditMode(true); }; /** * @name saveChanges * @description Refresh the data provider and leave edit mode * @memberOf NovoTableElement */ NovoTableElement.prototype.saveChanges = function () { this.leaveEditMode(false); }; /** * @name displayToastMessage * @description Displays a toast message inside of the table * @param {{ icon: string, theme: string, message: string }} toast * @param {number} [hideDelay] * @memberOf NovoTableElement */ NovoTableElement.prototype.displayToastMessage = function (toast, hideDelay) { var _this = this; this.loading = false; this.toast = toast; if (hideDelay) { setTimeout(function () { return _this.hideToastMessage(); }, hideDelay); } }; /** * @name hideToastMessage * @description Force hide the toast message * @memberOf NovoTableElement */ NovoTableElement.prototype.hideToastMessage = function () { var _this = this; this.toast = null; // Hack to make the table display properly after hiding the toast this.grossFlagToAvoidTheTableFromBeingUglyWhenHidingTheToast = true; setTimeout(function () { _this.grossFlagToAvoidTheTableFromBeingUglyWhenHidingTheToast = false; }); }; /** * @name toggleLoading * @description display the loading overlay on the table * @param {boolean} show * @memberOf NovoTableElement */ NovoTableElement.prototype.toggleLoading = function (show) { this.loading = show; }; /** * @name isColumnHidden * @description hide a column in edit or view mode * @param {column meta} column * @returns {boolean} * @memberOf NovoTableElement */ NovoTableElement.prototype.isColumnHidden = function (column) { return this.editing ? !!column.hideColumnOnEdit : !!column.hideColumnOnView; }; return NovoTableElement; }()); NovoTableElement.decorators = [ { type: core_1.Component, args: [{ selector: 'novo-table', host: { '[attr.theme]': 'theme', '[class.editing]': 'mode === NovoTableMode.EDIT', '[class.novo-table-loading]': 'loading' }, // directives: [], template: "\n <header *ngIf=\"columns.length\">\n <ng-content select=\"novo-table-header\"></ng-content>\n <div class=\"header-actions\">\n <novo-pagination *ngIf=\"config.paging && !(dataProvider.isEmpty() && !dataProvider.isFiltered())\"\n [rowOptions]=\"config.paging.rowOptions\"\n [disablePageSelection]=\"config.paging.disablePageSelection\"\n [(page)]=\"dataProvider.page\"\n [(itemsPerPage)]=\"dataProvider.pageSize\"\n [totalItems]=\"dataProvider.total\"\n (onPageChange)=\"onPageChange($event)\">\n </novo-pagination>\n <ng-content select=\"novo-table-actions\"></ng-content>\n </div>\n </header>\n <div class=\"novo-table-loading-overlay\" *ngIf=\"loading\">\n <novo-loading></novo-loading>\n </div>\n <novo-toast *ngIf=\"toast\" [theme]=\"toast?.theme\" [icon]=\"toast?.icon\" [message]=\"toast?.message\"></novo-toast>\n <div class=\"table-container\" *ngIf=\"!grossFlagToAvoidTheTableFromBeingUglyWhenHidingTheToast\">\n <novo-form hideHeader=\"true\" [form]=\"tableForm\">\n <table class=\"table table-striped dataTable\" [class.table-details]=\"config.hasDetails\" role=\"grid\">\n <!-- skipSortAndFilterClear is a hack right now, will be removed once Canvas is refactored -->\n <thead *ngIf=\"columns.length && (!dataProvider.isEmpty() || dataProvider.isFiltered() || skipSortAndFilterClear || editing)\">\n <tr role=\"row\">\n <!-- DETAILS -->\n <th class=\"row-actions\" *ngIf=\"config.hasDetails\">\n <button theme=\"icon\" icon=\"next\" (click)=\"expandAllOnPage(config.expandAll)\" *ngIf=\"!config.expandAll\" data-automation-id=\"expand-all\"></button>\n <button theme=\"icon\" icon=\"sort-desc\" (click)=\"expandAllOnPage(config.expandAll)\" *ngIf=\"config.expandAll\" data-automation-id=\"collapse-all\"></button>\n </th>\n <!-- CHECKBOX -->\n <th class=\"row-actions checkbox mass-action\" *ngIf=\"config.rowSelectionStyle === 'checkbox'\">\n <novo-checkbox [(ngModel)]=\"master\" [indeterminate]=\"pageSelected.length > 0 && pageSelected.length < pagedData.length\" (ngModelChange)=\"selectPage($event)\" data-automation-id=\"select-all-checkbox\" [tooltip]=\"master ? labels.deselectAll : labels.selectAllOnPage\" tooltipPosition=\"right\"></novo-checkbox>\n </th>\n <!-- TABLE HEADERS -->\n <th *ngFor=\"let column of columns\" [ngClass]=\"{ 'mass-action': config?.rowSelectionStyle === 'checkbox', 'actions': column?.actions?.items?.length > 0, 'preview': column?.name === 'preview' }\" [novoThOrderable]=\"column\" (onOrderChange)=\"onOrderChange($event)\" [hidden]=\"isColumnHidden(column)\">\n <div class=\"th-group\" [attr.data-automation-id]=\"column.id || column.name\" *ngIf=\"!column.hideHeader\">\n <!-- LABEL & SORT ARROWS -->\n <div class=\"th-title\" [ngClass]=\"(config.sorting !== false && column.sorting !== false) ? 'sortable' : ''\" [novoThSortable]=\"config\" [column]=\"column\" (onSortChange)=\"onSortChange($event)\">\n <label>{{ column.title || column.label }}</label>\n <div class=\"table-sort-icons\" tooltipPosition=\"bottom\" [tooltip]=\"labels.sort\" [ngClass]=\"column.sort || ''\" *ngIf=\"config.sorting !== false && column.sorting !== false\">\n <i class=\"bhi-arrow-up\"></i>\n <i class=\"bhi-arrow-down\"></i>\n </div>\n </div>\n <!-- FILTER DROP-DOWN -->\n <novo-dropdown side=\"right\" *ngIf=\"config.filtering !== false && column.filtering !== false\" class=\"column-filters\" (toggled)=\"onDropdownToggled($event, column.name)\" appendToBody=\"true\" parentScrollSelector=\".table-container\" containerClass=\"table-dropdown\">\n <button type=\"button\" theme=\"icon\" icon=\"filter\" tooltipPosition=\"bottom\" [tooltip]=\"labels.filters\" [class.filtered]=\"column.filter || column.filter===false\"></button>\n <!-- FILTER OPTIONS LIST -->\n <list *ngIf=\"(column?.options?.length || column?.originalOptions?.length) && column?.type !== 'date' && toggledDropdownMap[column.name]\">\n <item class=\"filter-search\">\n <div class=\"header\">\n <span>{{ labels.filters }}</span>\n <button theme=\"dialogue\" color=\"negative\" icon=\"times\" (click)=\"onFilterClear(column)\" *ngIf=\"column.filter || column.filter===false\">{{ labels.clear }}</button>\n </div>\n <input type=\"text\" *ngIf=\"!!column.allowCustomTextOption\" [attr.id]=\"column.name + '-input'\" [novoTableFilter]=\"column\" (onFilterChange)=\"onFilterKeywords($event)\" [(ngModel)]=\"column.freetextFilter\" keepFilterFocused/>\n </item>\n <item [ngClass]=\"{ active: isFilterActive(column, option) }\" *ngFor=\"let option of column.options\" (click)=\"onFilterClick(column, option)\" [attr.data-automation-id]=\"getOptionDataAutomationId(option)\">\n <span>{{ option?.label || option }}</span> <i class=\"bhi-check\" *ngIf=\"isFilterActive(column, option)\"></i>\n </item>\n </list>\n <!-- FILTER SEARCH INPUT -->\n <list *ngIf=\"!(column?.options?.length || column?.originalOptions?.length) && toggledDropdownMap[column.name]\">\n <item class=\"filter-search\">\n <div class=\"header\">\n <span>{{ labels.filters }}</span>\n <button theme=\"dialogue\" color=\"negative\" icon=\"times\" (click)=\"onFilterClear(column)\" *ngIf=\"column.filter\">{{ labels.clear }}</button>\n </div>\n <input type=\"text\" [attr.id]=\"column.name + '-input'\" [novoTableFilter]=\"column\" (onFilterChange)=\"onFilterChange($event)\" [(ngModel)]=\"column.filter\" keepFilterFocused/>\n </item>\n </list>\n <!-- FILTER DATE OPTIONS -->\n <list *ngIf=\"column?.options?.length && column?.type === 'date' && toggledDropdownMap[column.name]\">\n <item class=\"filter-search\" *ngIf=\"!column.calenderShow\">\n <div class=\"header\">\n <span>{{ labels.filters }}</span>\n <button theme=\"dialogue\" color=\"negative\" icon=\"times\" (click)=\"onFilterClear(column)\" *ngIf=\"column.filter\">{{ labels.clear }}</button>\n </div>\n </item>\n <item [ngClass]=\"{ active: isFilterActive(column, option) }\" *ngFor=\"let option of column.options\" (click)=\"onFilterClick(column, option)\" [keepOpen]=\"option.range\" [hidden]=\"column.calenderShow\" [attr.data-automation-id]=\"(option?.label || option)\">\n {{ option?.label || option }} <i class=\"bhi-check\" *ngIf=\"isFilterActive(column, option)\"></i>\n </item>\n <div class=\"calender-container\" [hidden]=\"!column.calenderShow\">\n <div (click)=\"column.calenderShow=false\"><i class=\"bhi-previous\"></i>{{ labels.backToPresetFilters }}</div>\n <novo-date-picker #rangePicker (onSelect)=\"onCalenderSelect(column, $event)\" [(ngModel)]=\"column.filter\" range=\"true\"></novo-date-picker>\n </div>\n </list>\n </novo-dropdown>\n </div>\n </th>\n </tr>\n </thead>\n <!-- TABLE DATA -->\n <tbody *ngIf=\"!dataProvider.isEmpty() || editing\">\n <tr class=\"table-selection-row\" *ngIf=\"config.rowSelectionStyle === 'checkbox' && showSelectAllMessage && config.selectAllEnabled\" data-automation-id=\"table-selection-row\">\n <td colspan=\"100%\">\n {{labels.selectedRecords(selected.length)}} <a (click)=\"selectAll(true)\" data-automation-id=\"all-matching-records\">{{labels.totalRecords(dataProvider.total)}}</a>\n </td>\n </tr>\n <ng-template ngFor let-row=\"$implicit\" let-i=\"index\" [ngForOf]=\"rows\">\n <tr class=\"table-row\" [ngClass]=\"row.customClass || ''\" [attr.data-automation-id]=\"row.id\" (click)=\"rowClickHandler(row)\" [class.active]=\"row.id === activeId\">\n <td class=\"row-actions\" *ngIf=\"config.hasDetails\">\n <button theme=\"icon\" icon=\"next\" (click)=\"row._expanded=!row._expanded\" *ngIf=\"!row._expanded\"></button>\n <button theme=\"icon\" icon=\"sort-desc\" (click)=\"row._expanded=!row._expanded\" *ngIf=\"row._expanded\"></button>\n </td>\n <td class=\"row-actions checkbox\" *ngIf=\"config.rowSelectionStyle === 'checkbox'\">\n <novo-checkbox [(ngModel)]=\"row._selected\" (ngModelChange)=\"rowSelectHandler(row)\" data-automation-id=\"select-row-checkbox\"></novo-checkbox>\n </td>\n <td *ngFor=\"let column of columns\" [attr.data-automation-id]=\"column.id || column.name\" [class.novo-form-row]=\"editable\" [hidden]=\"isColumnHidden(column)\">\n <novo-table-cell *ngIf=\"row._editing && !row._editing[column.name]\" [hasEditor]=\"editable\" [column]=\"column\" [row]=\"row\" [form]=\"tableForm.controls.rows.controls[i]\"></novo-table-cell>\n <novo-control *ngIf=\"row._editing && row._editing[column.name]\" condensed=\"true\" [form]=\"tableForm.controls.rows.controls[i]\" [control]=\"row.controls[column.name]\"></novo-control>\n </td>\n </tr>\n <tr class=\"details-row\" *ngIf=\"config.hasDetails\" [hidden]=\"!row._expanded\" [attr.data-automation-id]=\"'details-row-'+row.id\">\n <td class=\"row-actions\"></td>\n <td [attr.colspan]=\"columns.length\">\n <novo-row-details [data]=\"row\" [renderer]=\"config.detailsRenderer\"></novo-row-details>\n </td>\n </tr>\n </ng-template>\n </tbody>\n <!-- NO TABLE DATA PLACEHOLDER -->\n <tbody class=\"table-message\" *ngIf=\"dataProvider.isEmpty() && !dataProvider.isFiltered() && !editing\" data-automation-id=\"empty-table\">\n <tr>\n <td colspan=\"100%\">\n <div #emptymessage><ng-content select=\"[table-empty-message]\"></ng-content></div>\n <div class=\"table-empty-message\" *ngIf=\"emptymessage.childNodes.length == 0\">\n <h4><i class=\"bhi-search-question\"></i> {{ labels.emptyTableMessage }}</h4>\n </div>\n </td>\n </tr>\n </tbody>\n <!-- NO MATCHING RECORDS -->\n <tbody class=\"table-message\" *ngIf=\"dataProvider.isEmpty() && dataProvider.isFiltered()\" data-automation-id=\"empty-table\">\n <tr>\n <td colspan=\"100%\">\n <div #nomatchmessage><ng-content select=\"[table-no-matching-records-message]\"></ng-content></div>\n <div class=\"no-matching-records\" *ngIf=\"nomatchmessage.childNodes.length == 0\">\n <h4><i class=\"bhi-search-question\"></i> {{ labels.noMatchingRecordsMessage }}</h4>\n </div>\n </td>\n </tr>\n </tbody>\n <!-- TABLE DATA ERROR PLACEHOLDER -->\n <tbody class=\"table-message\" *ngIf=\"dataProvider.hasErrors()\" data-automation-id=\"table-errors\">\n <tr>\n <td colspan=\"100%\">\n <div #errormessage><ng-content select=\"[table-error-message]\"></ng-content></div>\n <div class=\"table-error-message\" *ngIf=\"errormessage.childNodes.length == 0\">\n <h4><i class=\"bhi-caution\"></i> {{ labels.erroredTableMessage }}<