import _ from "lodash"; import moment from "moment"; import kbn from "app/core/utils/kbn"; export class TableRenderer { formatters: any[]; colorState: any; constructor( private panel, private table, private isUtc, private sanitize, private templateSrv ) { this.initColumns(); } setTable(table) { this.table = table; this.initColumns(); } initColumns() { this.formatters = []; this.colorState = {}; for (let colIndex = 0; colIndex < this.table.columns.length; colIndex++) { let column = this.table.columns[colIndex]; column.title = column.text; for (let i = 0; i < this.panel.styles.length; i++) { let style = this.panel.styles[i]; var regex = kbn.stringToJsRegex(style.pattern); if (column.text.match(regex)) { column.style = style; if (style.alias) { column.title = column.text.replace(regex, style.alias); } break; } } this.formatters[colIndex] = this.createColumnFormatter(column); } } getColorForValue(value, style) { if (!style.thresholds) { return null; } for (var i = style.thresholds.length; i > 0; i--) { if (value >= style.thresholds[i - 1]) { return style.colors[i]; } } return _.first(style.colors); } defaultCellFormatter(v, style) { if (v === null || v === void 0 || v === undefined) { return ""; } if (_.isArray(v)) { v = v.join(", "); } if (style && style.sanitize) { return this.sanitize(v); } else { return _.escape(v); } } createColumnFormatter(column) { if (!column.style) { return this.defaultCellFormatter; } if (column.style.type === "hidden") { return v => { return undefined; }; } if (column.style.type === "date") { return v => { if (v === undefined || v === null) { return "-"; } if (_.isArray(v)) { v = v[0]; } var date = moment(v); if (this.isUtc) { date = date.utc(); } return date.format(column.style.dateFormat); }; } if (column.style.type === "number") { let valueFormatter = kbn.valueFormats[column.unit || column.style.unit]; return v => { if (v === null || v === void 0) { return "-"; } if (_.isString(v) || _.isArray(v)) { return this.defaultCellFormatter(v, column.style); } if (column.style.colorMode) { this.colorState[column.style.colorMode] = this.getColorForValue( v, column.style ); } return valueFormatter(v, column.style.decimals, null); }; } return value => { return this.defaultCellFormatter(value, column.style); }; } renderRowVariables(rowIndex) { let scopedVars = {}; let cell_variable; let row = this.table.rows[rowIndex]; for (let i = 0; i < row.length; i++) { cell_variable = `__cell_${i}`; scopedVars[cell_variable] = { value: row[i] }; } return scopedVars; } formatColumnValue(colIndex, value) { return this.formatters[colIndex] ? this.formatters[colIndex](value) : value; } renderCell(columnIndex, rowIndex, value, addWidthHack = false) { value = this.formatColumnValue(columnIndex, value); var column = this.table.columns[columnIndex]; var style = ""; var cellClasses = []; var cellClass = ""; if (this.colorState.cell) { style = ' style="background-color:' + this.colorState.cell + ';color: white"'; this.colorState.cell = null; } else if (this.colorState.value) { style = ' style="color:' + this.colorState.value + '"'; this.colorState.value = null; } // because of the fixed table headers css only solution // there is an issue if header cell is wider the cell // this hack adds header content to cell (not visible) var columnHtml = ""; if (addWidthHack) { columnHtml = '
' + this.table.columns[columnIndex].title + "
"; } if (value === undefined) { style = ' style="display:none;"'; column.hidden = true; } else { column.hidden = false; } if (column.style && column.style.preserveFormat) { cellClasses.push("table-panel-cell-pre"); } if (column.style && column.style.link) { // Render cell as link var scopedVars = this.renderRowVariables(rowIndex); scopedVars["__cell"] = { value: value }; var cellLink = this.templateSrv.replace(column.style.linkUrl, scopedVars); var cellLinkTooltip = this.templateSrv.replace( column.style.linkTooltip, scopedVars ); var cellTarget = column.style.linkTargetBlank ? "_blank" : ""; cellClasses.push("table-panel-cell-link"); columnHtml += ` ${value} `; } else { columnHtml += value; } if (column.filterable) { cellClasses.push("table-panel-cell-filterable"); columnHtml += ` `; } if (cellClasses.length) { cellClass = ' class="' + cellClasses.join(" ") + '"'; } columnHtml = "" + columnHtml + ""; return columnHtml; } render(page) { let pageSize = this.panel.pageSize || 100; let startPos = page * pageSize; let endPos = Math.min(startPos + pageSize, this.table.rows.length); var html = ""; for (var y = startPos; y < endPos; y++) { let row = this.table.rows[y]; let cellHtml = ""; let rowStyle = ""; for (var i = 0; i < this.table.columns.length; i++) { cellHtml += this.renderCell(i, y, row[i], y === startPos); } if (this.colorState.row) { rowStyle = ' style="background-color:' + this.colorState.row + ';color: white"'; this.colorState.row = null; } html += "" + cellHtml + ""; } return html; } render_values() { let rows = []; for (var y = 0; y < this.table.rows.length; y++) { let row = this.table.rows[y]; let new_row = []; for (var i = 0; i < this.table.columns.length; i++) { new_row.push(this.formatColumnValue(i, row[i])); } rows.push(new_row); } return { columns: this.table.columns, rows: rows }; } }